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