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