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