1 /* 2 * Copyright 2001-2013, Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Marc Flerackers, mflerackers@androme.be 7 * Ingo Weinhold, ingo_weinhold@gmx.de 8 */ 9 10 /*! BControl is the base class for user-event handling objects. */ 11 12 13 #include <stdlib.h> 14 #include <string.h> 15 16 #include <Control.h> 17 #include <PropertyInfo.h> 18 #include <Window.h> 19 20 #include <binary_compatibility/Interface.h> 21 #include <Icon.h> 22 23 24 static property_info sPropertyList[] = { 25 { 26 "Enabled", 27 { B_GET_PROPERTY, B_SET_PROPERTY }, 28 { B_DIRECT_SPECIFIER }, 29 NULL, 0, 30 { B_BOOL_TYPE } 31 }, 32 { 33 "Label", 34 { B_GET_PROPERTY, B_SET_PROPERTY }, 35 { B_DIRECT_SPECIFIER }, 36 NULL, 0, 37 { B_STRING_TYPE } 38 }, 39 { 40 "Value", 41 { B_GET_PROPERTY, B_SET_PROPERTY }, 42 { B_DIRECT_SPECIFIER }, 43 NULL, 0, 44 { B_INT32_TYPE } 45 }, 46 {} 47 }; 48 49 50 BControl::BControl(BRect frame, const char *name, const char *label, 51 BMessage *message, uint32 resizingMode, uint32 flags) 52 : BView(frame, name, resizingMode, flags) 53 { 54 InitData(NULL); 55 56 SetLabel(label); 57 SetMessage(message); 58 } 59 60 61 BControl::BControl(const char *name, const char *label, BMessage *message, 62 uint32 flags) 63 : BView(name, flags) 64 { 65 InitData(NULL); 66 67 SetLabel(label); 68 SetMessage(message); 69 } 70 71 72 BControl::~BControl() 73 { 74 free(fLabel); 75 delete fIcon; 76 SetMessage(NULL); 77 } 78 79 80 BControl::BControl(BMessage *archive) 81 : BView(archive) 82 { 83 InitData(archive); 84 85 BMessage message; 86 if (archive->FindMessage("_msg", &message) == B_OK) 87 SetMessage(new BMessage(message)); 88 89 const char *label; 90 if (archive->FindString("_label", &label) == B_OK) 91 SetLabel(label); 92 93 int32 value; 94 if (archive->FindInt32("_val", &value) == B_OK) 95 SetValue(value); 96 97 bool toggle; 98 if (archive->FindBool("_disable", &toggle) == B_OK) 99 SetEnabled(!toggle); 100 101 if (archive->FindBool("be:wants_nav", &toggle) == B_OK) 102 fWantsNav = toggle; 103 } 104 105 106 BArchivable * 107 BControl::Instantiate(BMessage *archive) 108 { 109 if (validate_instantiation(archive, "BControl")) 110 return new BControl(archive); 111 112 return NULL; 113 } 114 115 116 status_t 117 BControl::Archive(BMessage *archive, bool deep) const 118 { 119 status_t status = BView::Archive(archive, deep); 120 121 if (status == B_OK && Message()) 122 status = archive->AddMessage("_msg", Message()); 123 124 if (status == B_OK && fLabel) 125 status = archive->AddString("_label", fLabel); 126 127 if (status == B_OK && fValue != B_CONTROL_OFF) 128 status = archive->AddInt32("_val", fValue); 129 130 if (status == B_OK && !fEnabled) 131 status = archive->AddBool("_disable", true); 132 133 return status; 134 } 135 136 137 void 138 BControl::WindowActivated(bool active) 139 { 140 BView::WindowActivated(active); 141 142 if (IsFocus()) 143 Invalidate(); 144 } 145 146 147 void 148 BControl::AttachedToWindow() 149 { 150 rgb_color color; 151 152 BView* parent = Parent(); 153 if (parent != NULL) { 154 // inherit the color from parent 155 color = parent->ViewColor(); 156 if (color == B_TRANSPARENT_COLOR) 157 color = ui_color(B_PANEL_BACKGROUND_COLOR); 158 } else 159 color = ui_color(B_PANEL_BACKGROUND_COLOR); 160 161 SetViewColor(color); 162 SetLowColor(color); 163 164 if (!Messenger().IsValid()) 165 SetTarget(Window()); 166 167 BView::AttachedToWindow(); 168 } 169 170 171 void 172 BControl::DetachedFromWindow() 173 { 174 BView::DetachedFromWindow(); 175 } 176 177 178 void 179 BControl::AllAttached() 180 { 181 BView::AllAttached(); 182 } 183 184 185 void 186 BControl::AllDetached() 187 { 188 BView::AllDetached(); 189 } 190 191 192 void 193 BControl::MessageReceived(BMessage *message) 194 { 195 if (message->what == B_GET_PROPERTY || message->what == B_SET_PROPERTY) { 196 BMessage reply(B_REPLY); 197 bool handled = false; 198 199 BMessage specifier; 200 int32 index; 201 int32 form; 202 const char *property; 203 if (message->GetCurrentSpecifier(&index, &specifier, &form, &property) == B_OK) { 204 if (strcmp(property, "Label") == 0) { 205 if (message->what == B_GET_PROPERTY) { 206 reply.AddString("result", fLabel); 207 handled = true; 208 } else { 209 // B_SET_PROPERTY 210 const char *label; 211 if (message->FindString("data", &label) == B_OK) { 212 SetLabel(label); 213 reply.AddInt32("error", B_OK); 214 handled = true; 215 } 216 } 217 } else if (strcmp(property, "Value") == 0) { 218 if (message->what == B_GET_PROPERTY) { 219 reply.AddInt32("result", fValue); 220 handled = true; 221 } else { 222 // B_SET_PROPERTY 223 int32 value; 224 if (message->FindInt32("data", &value) == B_OK) { 225 SetValue(value); 226 reply.AddInt32("error", B_OK); 227 handled = true; 228 } 229 } 230 } else if (strcmp(property, "Enabled") == 0) { 231 if (message->what == B_GET_PROPERTY) { 232 reply.AddBool("result", fEnabled); 233 handled = true; 234 } else { 235 // B_SET_PROPERTY 236 bool enabled; 237 if (message->FindBool("data", &enabled) == B_OK) { 238 SetEnabled(enabled); 239 reply.AddInt32("error", B_OK); 240 handled = true; 241 } 242 } 243 } 244 } 245 246 if (handled) { 247 message->SendReply(&reply); 248 return; 249 } 250 } 251 252 BView::MessageReceived(message); 253 } 254 255 256 void 257 BControl::MakeFocus(bool focused) 258 { 259 if (focused == IsFocus()) 260 return; 261 262 BView::MakeFocus(focused); 263 264 if (Window()) { 265 fFocusChanging = true; 266 Invalidate(Bounds()); 267 Flush(); 268 fFocusChanging = false; 269 } 270 } 271 272 273 void 274 BControl::KeyDown(const char *bytes, int32 numBytes) 275 { 276 if (*bytes == B_ENTER || *bytes == B_SPACE) { 277 if (!fEnabled) 278 return; 279 280 SetValue(Value() ? B_CONTROL_OFF : B_CONTROL_ON); 281 Invoke(); 282 } else 283 BView::KeyDown(bytes, numBytes); 284 } 285 286 287 void 288 BControl::MouseDown(BPoint point) 289 { 290 BView::MouseDown(point); 291 } 292 293 294 void 295 BControl::MouseUp(BPoint point) 296 { 297 BView::MouseUp(point); 298 } 299 300 301 void 302 BControl::MouseMoved(BPoint point, uint32 transit, const BMessage *message) 303 { 304 BView::MouseMoved(point, transit, message); 305 } 306 307 308 void 309 BControl::SetLabel(const char *label) 310 { 311 if (label != NULL && !label[0]) 312 label = NULL; 313 314 // Has the label been changed? 315 if ((fLabel && label && !strcmp(fLabel, label)) 316 || ((fLabel == NULL || !fLabel[0]) && label == NULL)) 317 return; 318 319 free(fLabel); 320 fLabel = label ? strdup(label) : NULL; 321 322 InvalidateLayout(); 323 Invalidate(); 324 } 325 326 327 const char * 328 BControl::Label() const 329 { 330 return fLabel; 331 } 332 333 334 void 335 BControl::SetValue(int32 value) 336 { 337 if (value == fValue) 338 return; 339 340 fValue = value; 341 Invalidate(); 342 } 343 344 345 void 346 BControl::SetValueNoUpdate(int32 value) 347 { 348 fValue = value; 349 } 350 351 352 int32 353 BControl::Value() const 354 { 355 return fValue; 356 } 357 358 359 void 360 BControl::SetEnabled(bool enabled) 361 { 362 if (fEnabled == enabled) 363 return; 364 365 fEnabled = enabled; 366 367 if (fEnabled && fWantsNav) 368 SetFlags(Flags() | B_NAVIGABLE); 369 else if (!fEnabled && (Flags() & B_NAVIGABLE)) { 370 fWantsNav = true; 371 SetFlags(Flags() & ~B_NAVIGABLE); 372 } else 373 fWantsNav = false; 374 375 if (Window()) { 376 Invalidate(Bounds()); 377 Flush(); 378 } 379 } 380 381 382 bool 383 BControl::IsEnabled() const 384 { 385 return fEnabled; 386 } 387 388 389 void 390 BControl::GetPreferredSize(float *_width, float *_height) 391 { 392 BView::GetPreferredSize(_width, _height); 393 } 394 395 396 void 397 BControl::ResizeToPreferred() 398 { 399 BView::ResizeToPreferred(); 400 } 401 402 403 status_t 404 BControl::Invoke(BMessage *message) 405 { 406 bool notify = false; 407 uint32 kind = InvokeKind(¬ify); 408 409 if (!message && !notify) 410 message = Message(); 411 412 BMessage clone(kind); 413 414 if (!message) { 415 if (!IsWatched()) 416 return B_BAD_VALUE; 417 } else 418 clone = *message; 419 420 clone.AddInt64("when", (int64)system_time()); 421 clone.AddPointer("source", this); 422 clone.AddInt32("be:value", fValue); 423 clone.AddMessenger("be:sender", BMessenger(this)); 424 425 // ToDo: is this correct? If message == NULL (even if IsWatched()), we always return B_BAD_VALUE 426 status_t err; 427 if (message) 428 err = BInvoker::Invoke(&clone); 429 else 430 err = B_BAD_VALUE; 431 432 // TODO: asynchronous messaging 433 SendNotices(kind, &clone); 434 435 return err; 436 } 437 438 439 BHandler * 440 BControl::ResolveSpecifier(BMessage *message, int32 index, 441 BMessage *specifier, int32 what, const char *property) 442 { 443 BPropertyInfo propInfo(sPropertyList); 444 445 if (propInfo.FindMatch(message, 0, specifier, what, property) >= B_OK) 446 return this; 447 448 return BView::ResolveSpecifier(message, index, specifier, what, 449 property); 450 } 451 452 453 status_t 454 BControl::GetSupportedSuites(BMessage *message) 455 { 456 message->AddString("suites", "suite/vnd.Be-control"); 457 458 BPropertyInfo propInfo(sPropertyList); 459 message->AddFlat("messages", &propInfo); 460 461 return BView::GetSupportedSuites(message); 462 } 463 464 465 status_t 466 BControl::Perform(perform_code code, void* _data) 467 { 468 switch (code) { 469 case PERFORM_CODE_MIN_SIZE: 470 ((perform_data_min_size*)_data)->return_value 471 = BControl::MinSize(); 472 return B_OK; 473 case PERFORM_CODE_MAX_SIZE: 474 ((perform_data_max_size*)_data)->return_value 475 = BControl::MaxSize(); 476 return B_OK; 477 case PERFORM_CODE_PREFERRED_SIZE: 478 ((perform_data_preferred_size*)_data)->return_value 479 = BControl::PreferredSize(); 480 return B_OK; 481 case PERFORM_CODE_LAYOUT_ALIGNMENT: 482 ((perform_data_layout_alignment*)_data)->return_value 483 = BControl::LayoutAlignment(); 484 return B_OK; 485 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH: 486 ((perform_data_has_height_for_width*)_data)->return_value 487 = BControl::HasHeightForWidth(); 488 return B_OK; 489 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH: 490 { 491 perform_data_get_height_for_width* data 492 = (perform_data_get_height_for_width*)_data; 493 BControl::GetHeightForWidth(data->width, &data->min, &data->max, 494 &data->preferred); 495 return B_OK; 496 } 497 case PERFORM_CODE_SET_LAYOUT: 498 { 499 perform_data_set_layout* data = (perform_data_set_layout*)_data; 500 BControl::SetLayout(data->layout); 501 return B_OK; 502 } 503 case PERFORM_CODE_LAYOUT_INVALIDATED: 504 { 505 perform_data_layout_invalidated* data 506 = (perform_data_layout_invalidated*)_data; 507 BControl::LayoutInvalidated(data->descendants); 508 return B_OK; 509 } 510 case PERFORM_CODE_DO_LAYOUT: 511 { 512 BControl::DoLayout(); 513 return B_OK; 514 } 515 case PERFORM_CODE_SET_ICON: 516 { 517 perform_data_set_icon* data = (perform_data_set_icon*)_data; 518 return BControl::SetIcon(data->icon, data->flags); 519 } 520 } 521 522 return BView::Perform(code, _data); 523 } 524 525 526 status_t 527 BControl::SetIcon(const BBitmap* bitmap, uint32 flags) 528 { 529 status_t error = BIcon::UpdateIcon(bitmap, flags, fIcon); 530 531 if (error == B_OK) { 532 InvalidateLayout(); 533 Invalidate(); 534 } 535 536 return error; 537 } 538 539 540 status_t 541 BControl::SetIconBitmap(const BBitmap* bitmap, uint32 which, uint32 flags) 542 { 543 status_t error = BIcon::SetIconBitmap(bitmap, which, flags, fIcon); 544 545 if (error != B_OK) { 546 InvalidateLayout(); 547 Invalidate(); 548 } 549 550 return error; 551 } 552 553 554 const BBitmap* 555 BControl::IconBitmap(uint32 which) const 556 { 557 return fIcon != NULL ? fIcon->Bitmap(which) : NULL; 558 } 559 560 561 bool 562 BControl::IsFocusChanging() const 563 { 564 return fFocusChanging; 565 } 566 567 568 bool 569 BControl::IsTracking() const 570 { 571 return fTracking; 572 } 573 574 575 void 576 BControl::SetTracking(bool state) 577 { 578 fTracking = state; 579 } 580 581 582 extern "C" status_t 583 B_IF_GCC_2(_ReservedControl1__8BControl, _ZN8BControl17_ReservedControl1Ev)( 584 BControl* control, const BBitmap* icon, uint32 flags) 585 { 586 // SetIcon() 587 perform_data_set_icon data; 588 data.icon = icon; 589 data.flags = flags; 590 return control->Perform(PERFORM_CODE_SET_ICON, &data); 591 } 592 593 594 void BControl::_ReservedControl2() {} 595 void BControl::_ReservedControl3() {} 596 void BControl::_ReservedControl4() {} 597 598 599 BControl & 600 BControl::operator=(const BControl &) 601 { 602 return *this; 603 } 604 605 606 void 607 BControl::InitData(BMessage *data) 608 { 609 fLabel = NULL; 610 SetLabel(B_EMPTY_STRING); 611 fValue = B_CONTROL_OFF; 612 fEnabled = true; 613 fFocusChanging = false; 614 fTracking = false; 615 fWantsNav = Flags() & B_NAVIGABLE; 616 fIcon = NULL; 617 618 if (data && data->HasString("_fname")) 619 SetFont(be_plain_font, B_FONT_FAMILY_AND_STYLE); 620 } 621 622