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 25static 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 52BControl::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 64BControl::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 76BControl::~BControl() 77{ 78 free(fLabel); 79 delete fIcon; 80 SetMessage(NULL); 81} 82 83 84BControl::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 111BArchivable* 112BControl::Instantiate(BMessage* data) 113{ 114 if (validate_instantiation(data, "BControl")) 115 return new BControl(data); 116 117 return NULL; 118} 119 120 121status_t 122BControl::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 142void 143BControl::WindowActivated(bool active) 144{ 145 BView::WindowActivated(active); 146 147 if (IsFocus()) 148 Invalidate(); 149} 150 151 152void 153BControl::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 179void 180BControl::DetachedFromWindow() 181{ 182 BView::DetachedFromWindow(); 183} 184 185 186void 187BControl::AllAttached() 188{ 189 BView::AllAttached(); 190} 191 192 193void 194BControl::AllDetached() 195{ 196 BView::AllDetached(); 197} 198 199 200void 201BControl::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 264void 265BControl::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 281void 282BControl::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 295void 296BControl::MouseDown(BPoint where) 297{ 298 BView::MouseDown(where); 299} 300 301 302void 303BControl::MouseUp(BPoint where) 304{ 305 BView::MouseUp(where); 306} 307 308 309void 310BControl::MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage) 311{ 312 BView::MouseMoved(where, code, dragMessage); 313} 314 315 316void 317BControl::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 335const char* 336BControl::Label() const 337{ 338 return fLabel; 339} 340 341 342void 343BControl::SetValue(int32 value) 344{ 345 if (value == fValue) 346 return; 347 348 fValue = value; 349 Invalidate(); 350} 351 352 353void 354BControl::SetValueNoUpdate(int32 value) 355{ 356 fValue = value; 357} 358 359 360int32 361BControl::Value() const 362{ 363 return fValue; 364} 365 366 367void 368BControl::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 390bool 391BControl::IsEnabled() const 392{ 393 return fEnabled; 394} 395 396 397void 398BControl::GetPreferredSize(float* _width, float* _height) 399{ 400 BView::GetPreferredSize(_width, _height); 401} 402 403 404void 405BControl::ResizeToPreferred() 406{ 407 BView::ResizeToPreferred(); 408} 409 410 411status_t 412BControl::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 447BHandler* 448BControl::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 461status_t 462BControl::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 473status_t 474BControl::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 534status_t 535BControl::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 548status_t 549BControl::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 562const BBitmap* 563BControl::IconBitmap(uint32 which) const 564{ 565 return fIcon != NULL ? fIcon->Bitmap(which) : NULL; 566} 567 568 569bool 570BControl::IsFocusChanging() const 571{ 572 return fFocusChanging; 573} 574 575 576bool 577BControl::IsTracking() const 578{ 579 return fTracking; 580} 581 582 583void 584BControl::SetTracking(bool state) 585{ 586 fTracking = state; 587} 588 589 590extern "C" status_t 591B_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 602void BControl::_ReservedControl2() {} 603void BControl::_ReservedControl3() {} 604void BControl::_ReservedControl4() {} 605 606 607BControl & 608BControl::operator=(const BControl &) 609{ 610 return *this; 611} 612 613 614void 615BControl::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