1 /* 2 * Copyright 2001-2009, Haiku Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Marc Flerackers (mflerackers@androme.be) 7 * Stephan Aßmus <superstippi@gmx.de> 8 */ 9 10 /*! BCheckBox displays an on/off control. */ 11 12 13 #include <CheckBox.h> 14 15 #include <new> 16 17 #include <ControlLook.h> 18 #include <LayoutUtils.h> 19 #include <Window.h> 20 21 #include <binary_compatibility/Interface.h> 22 23 24 BCheckBox::BCheckBox(BRect frame, const char *name, const char *label, 25 BMessage *message, uint32 resizingMode, uint32 flags) 26 : 27 BControl(frame, name, label, message, resizingMode, flags), 28 fPreferredSize(), 29 fOutlined(false) 30 { 31 // Resize to minimum height if needed 32 font_height fontHeight; 33 GetFontHeight(&fontHeight); 34 float minHeight = (float)ceil(6.0f + fontHeight.ascent 35 + fontHeight.descent); 36 if (Bounds().Height() < minHeight) 37 ResizeTo(Bounds().Width(), minHeight); 38 } 39 40 41 BCheckBox::BCheckBox(const char *name, const char *label, BMessage *message, 42 uint32 flags) 43 : 44 BControl(name, label, message, flags | B_WILL_DRAW | B_NAVIGABLE), 45 fPreferredSize(), 46 fOutlined(false) 47 { 48 } 49 50 51 BCheckBox::BCheckBox(const char *label, BMessage *message) 52 : 53 BControl(NULL, label, message, B_WILL_DRAW | B_NAVIGABLE), 54 fPreferredSize(), 55 fOutlined(false) 56 { 57 } 58 59 60 BCheckBox::BCheckBox(BMessage *archive) 61 : 62 BControl(archive), 63 fOutlined(false) 64 { 65 } 66 67 68 BCheckBox::~BCheckBox() 69 { 70 } 71 72 73 // #pragma mark - 74 75 76 BArchivable * 77 BCheckBox::Instantiate(BMessage *archive) 78 { 79 if (validate_instantiation(archive, "BCheckBox")) 80 return new(std::nothrow) BCheckBox(archive); 81 82 return NULL; 83 } 84 85 86 status_t 87 BCheckBox::Archive(BMessage *archive, bool deep) const 88 { 89 return BControl::Archive(archive, deep); 90 } 91 92 93 // #pragma mark - 94 95 96 void 97 BCheckBox::Draw(BRect updateRect) 98 { 99 if (be_control_look != NULL) { 100 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 101 102 uint32 flags = be_control_look->Flags(this); 103 if (fOutlined) 104 flags |= BControlLook::B_CLICKED; 105 106 BRect checkBoxRect(_CheckBoxFrame()); 107 BRect rect(checkBoxRect); 108 be_control_look->DrawCheckBox(this, rect, updateRect,base, flags); 109 110 BRect labelRect(Bounds()); 111 labelRect.left = checkBoxRect.right 112 + be_control_look->DefaultLabelSpacing(); 113 114 be_control_look->DrawLabel(this, Label(), labelRect, updateRect, 115 base, flags); 116 return; 117 } 118 119 font_height fontHeight; 120 GetFontHeight(&fontHeight); 121 122 // If the focus is changing, just redraw the focus indicator 123 if (IsFocusChanging()) { 124 float x = (float)ceil(10.0f + fontHeight.ascent); 125 float y = 5.0f + (float)ceil(fontHeight.ascent); 126 127 if (IsFocus()) 128 SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 129 else 130 SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 131 132 StrokeLine(BPoint(x, y), BPoint(x + StringWidth(Label()), y)); 133 return; 134 } 135 136 rgb_color noTint = ui_color(B_PANEL_BACKGROUND_COLOR); 137 rgb_color lighten1 = tint_color(noTint, B_LIGHTEN_1_TINT); 138 rgb_color lightenMax = tint_color(noTint, B_LIGHTEN_MAX_TINT); 139 rgb_color darken1 = tint_color(noTint, B_DARKEN_1_TINT); 140 rgb_color darken2 = tint_color(noTint, B_DARKEN_2_TINT); 141 rgb_color darken3 = tint_color(noTint, B_DARKEN_3_TINT); 142 rgb_color darken4 = tint_color(noTint, B_DARKEN_4_TINT); 143 rgb_color darkenmax = tint_color(noTint, B_DARKEN_MAX_TINT); 144 145 BRect rect = _CheckBoxFrame(); 146 147 if (IsEnabled()) { 148 // Filling 149 SetHighColor(lightenMax); 150 FillRect(rect); 151 152 // Box 153 if (fOutlined) { 154 SetHighColor(darken3); 155 StrokeRect(rect); 156 157 rect.InsetBy(1, 1); 158 159 BeginLineArray(6); 160 161 AddLine(BPoint(rect.left, rect.bottom), 162 BPoint(rect.left, rect.top), darken2); 163 AddLine(BPoint(rect.left, rect.top), 164 BPoint(rect.right, rect.top), darken2); 165 AddLine(BPoint(rect.left, rect.bottom), 166 BPoint(rect.right, rect.bottom), darken4); 167 AddLine(BPoint(rect.right, rect.bottom), 168 BPoint(rect.right, rect.top), darken4); 169 170 EndLineArray(); 171 } else { 172 BeginLineArray(6); 173 174 AddLine(BPoint(rect.left, rect.bottom), 175 BPoint(rect.left, rect.top), darken1); 176 AddLine(BPoint(rect.left, rect.top), 177 BPoint(rect.right, rect.top), darken1); 178 rect.InsetBy(1, 1); 179 AddLine(BPoint(rect.left, rect.bottom), 180 BPoint(rect.left, rect.top), darken4); 181 AddLine(BPoint(rect.left, rect.top), 182 BPoint(rect.right, rect.top), darken4); 183 AddLine(BPoint(rect.left + 1.0f, rect.bottom), 184 BPoint(rect.right, rect.bottom), noTint); 185 AddLine(BPoint(rect.right, rect.bottom), 186 BPoint(rect.right, rect.top + 1.0f), noTint); 187 188 EndLineArray(); 189 } 190 191 // Checkmark 192 if (Value() == B_CONTROL_ON) { 193 rect.InsetBy(3, 3); 194 195 SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 196 SetPenSize(2); 197 StrokeLine(BPoint(rect.left, rect.top), 198 BPoint(rect.right, rect.bottom)); 199 StrokeLine(BPoint(rect.left, rect.bottom), 200 BPoint(rect.right, rect.top)); 201 SetPenSize(1); 202 } 203 204 // Label 205 SetHighColor(darkenmax); 206 DrawString(Label(), BPoint((float)ceil(10.0f + fontHeight.ascent), 207 3.0f + (float)ceil(fontHeight.ascent))); 208 209 // Focus 210 if (IsFocus()) { 211 float x = (float)ceil(10.0f + fontHeight.ascent); 212 float y = 5.0f + (float)ceil(fontHeight.ascent); 213 214 SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 215 StrokeLine(BPoint(x, y), BPoint(x + StringWidth(Label()), y)); 216 } 217 } else { 218 // Filling 219 SetHighColor(lighten1); 220 FillRect(rect); 221 222 // Box 223 BeginLineArray(6); 224 225 AddLine(BPoint(rect.left, rect.bottom), 226 BPoint(rect.left, rect.top), noTint); 227 AddLine(BPoint(rect.left, rect.top), 228 BPoint(rect.right, rect.top), noTint); 229 rect.InsetBy(1, 1); 230 AddLine(BPoint(rect.left, rect.bottom), 231 BPoint(rect.left, rect.top), darken2); 232 AddLine(BPoint(rect.left, rect.top), 233 BPoint(rect.right, rect.top), darken2); 234 AddLine(BPoint(rect.left + 1.0f, rect.bottom), 235 BPoint(rect.right, rect.bottom), darken1); 236 AddLine(BPoint(rect.right, rect.bottom), 237 BPoint(rect.right, rect.top + 1.0f), darken1); 238 239 EndLineArray(); 240 241 // Checkmark 242 if (Value() == B_CONTROL_ON) { 243 rect.InsetBy(3, 3); 244 245 SetHighColor(tint_color(ui_color(B_KEYBOARD_NAVIGATION_COLOR), 246 B_DISABLED_MARK_TINT)); 247 SetPenSize(2); 248 StrokeLine(BPoint(rect.left, rect.top), 249 BPoint(rect.right, rect.bottom)); 250 StrokeLine(BPoint(rect.left, rect.bottom), 251 BPoint(rect.right, rect.top)); 252 SetPenSize(1); 253 } 254 255 // Label 256 SetHighColor(tint_color(noTint, B_DISABLED_LABEL_TINT)); 257 DrawString(Label(), BPoint((float)ceil(10.0f + fontHeight.ascent), 258 3.0f + (float)ceil(fontHeight.ascent))); 259 } 260 } 261 262 263 void 264 BCheckBox::AttachedToWindow() 265 { 266 BControl::AttachedToWindow(); 267 } 268 269 270 void 271 BCheckBox::DetachedFromWindow() 272 { 273 BControl::DetachedFromWindow(); 274 } 275 276 277 void 278 BCheckBox::AllAttached() 279 { 280 BControl::AllAttached(); 281 } 282 283 284 void 285 BCheckBox::AllDetached() 286 { 287 BControl::AllDetached(); 288 } 289 290 291 void 292 BCheckBox::FrameMoved(BPoint newLocation) 293 { 294 BControl::FrameMoved(newLocation); 295 } 296 297 298 void 299 BCheckBox::FrameResized(float width, float height) 300 { 301 BControl::FrameResized(width, height); 302 } 303 304 305 void 306 BCheckBox::WindowActivated(bool active) 307 { 308 BControl::WindowActivated(active); 309 } 310 311 312 // #pragma mark - 313 314 315 void 316 BCheckBox::MessageReceived(BMessage *message) 317 { 318 BControl::MessageReceived(message); 319 } 320 321 322 void 323 BCheckBox::KeyDown(const char *bytes, int32 numBytes) 324 { 325 BControl::KeyDown(bytes, numBytes); 326 } 327 328 329 void 330 BCheckBox::MouseDown(BPoint point) 331 { 332 if (!IsEnabled()) 333 return; 334 335 fOutlined = true; 336 337 if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) { 338 Invalidate(); 339 SetTracking(true); 340 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); 341 } else { 342 BRect bounds = Bounds(); 343 uint32 buttons; 344 345 Invalidate(); 346 Window()->UpdateIfNeeded(); 347 348 do { 349 snooze(40000); 350 351 GetMouse(&point, &buttons, true); 352 353 bool inside = bounds.Contains(point); 354 if (fOutlined != inside) { 355 fOutlined = inside; 356 Invalidate(); 357 Window()->UpdateIfNeeded(); 358 } 359 } while (buttons != 0); 360 361 if (fOutlined) { 362 fOutlined = false; 363 SetValue(!Value()); 364 Invoke(); 365 } else { 366 Invalidate(); 367 Window()->UpdateIfNeeded(); 368 } 369 } 370 } 371 372 373 void 374 BCheckBox::MouseUp(BPoint point) 375 { 376 if (!IsTracking()) 377 return; 378 379 bool inside = Bounds().Contains(point); 380 381 if (fOutlined != inside) { 382 fOutlined = inside; 383 Invalidate(); 384 } 385 386 if (fOutlined) { 387 fOutlined = false; 388 SetValue(!Value()); 389 Invoke(); 390 } else { 391 Invalidate(); 392 } 393 394 SetTracking(false); 395 } 396 397 398 void 399 BCheckBox::MouseMoved(BPoint point, uint32 transit, const BMessage *message) 400 { 401 if (!IsTracking()) 402 return; 403 404 bool inside = Bounds().Contains(point); 405 406 if (fOutlined != inside) { 407 fOutlined = inside; 408 Invalidate(); 409 } 410 } 411 412 413 // #pragma mark - 414 415 416 void 417 BCheckBox::GetPreferredSize(float* _width, float* _height) 418 { 419 font_height fontHeight; 420 GetFontHeight(&fontHeight); 421 422 if (_width) { 423 float width = 12.0f + fontHeight.ascent; 424 425 if (Label()) 426 width += StringWidth(Label()); 427 428 *_width = (float)ceil(width); 429 } 430 431 if (_height) 432 *_height = (float)ceil(6.0f + fontHeight.ascent + fontHeight.descent); 433 } 434 435 436 void 437 BCheckBox::ResizeToPreferred() 438 { 439 BControl::ResizeToPreferred(); 440 } 441 442 443 BSize 444 BCheckBox::MinSize() 445 { 446 return BLayoutUtils::ComposeSize(ExplicitMinSize(), 447 _ValidatePreferredSize()); 448 } 449 450 451 BSize 452 BCheckBox::MaxSize() 453 { 454 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), 455 _ValidatePreferredSize()); 456 } 457 458 459 BSize 460 BCheckBox::PreferredSize() 461 { 462 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), 463 _ValidatePreferredSize()); 464 } 465 466 467 BAlignment 468 BCheckBox::LayoutAlignment() 469 { 470 return BLayoutUtils::ComposeAlignment(ExplicitAlignment(), 471 BAlignment(B_ALIGN_LEFT, B_ALIGN_VERTICAL_CENTER)); 472 } 473 474 475 // #pragma mark - 476 477 478 void 479 BCheckBox::MakeFocus(bool focused) 480 { 481 BControl::MakeFocus(focused); 482 } 483 484 485 void 486 BCheckBox::SetValue(int32 value) 487 { 488 value = value ? B_CONTROL_ON : B_CONTROL_OFF; 489 // we only accept boolean values 490 491 if (value != Value()) { 492 BControl::SetValueNoUpdate(value); 493 Invalidate(_CheckBoxFrame()); 494 } 495 } 496 497 498 status_t 499 BCheckBox::Invoke(BMessage *message) 500 { 501 return BControl::Invoke(message); 502 } 503 504 505 BHandler * 506 BCheckBox::ResolveSpecifier(BMessage *message, int32 index, 507 BMessage *specifier, int32 what, const char *property) 508 { 509 return BControl::ResolveSpecifier(message, index, specifier, what, 510 property); 511 } 512 513 514 status_t 515 BCheckBox::GetSupportedSuites(BMessage *message) 516 { 517 return BControl::GetSupportedSuites(message); 518 } 519 520 521 status_t 522 BCheckBox::Perform(perform_code code, void* _data) 523 { 524 switch (code) { 525 case PERFORM_CODE_MIN_SIZE: 526 ((perform_data_min_size*)_data)->return_value 527 = BCheckBox::MinSize(); 528 return B_OK; 529 case PERFORM_CODE_MAX_SIZE: 530 ((perform_data_max_size*)_data)->return_value 531 = BCheckBox::MaxSize(); 532 return B_OK; 533 case PERFORM_CODE_PREFERRED_SIZE: 534 ((perform_data_preferred_size*)_data)->return_value 535 = BCheckBox::PreferredSize(); 536 return B_OK; 537 case PERFORM_CODE_LAYOUT_ALIGNMENT: 538 ((perform_data_layout_alignment*)_data)->return_value 539 = BCheckBox::LayoutAlignment(); 540 return B_OK; 541 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH: 542 ((perform_data_has_height_for_width*)_data)->return_value 543 = BCheckBox::HasHeightForWidth(); 544 return B_OK; 545 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH: 546 { 547 perform_data_get_height_for_width* data 548 = (perform_data_get_height_for_width*)_data; 549 BCheckBox::GetHeightForWidth(data->width, &data->min, &data->max, 550 &data->preferred); 551 return B_OK; 552 } 553 case PERFORM_CODE_SET_LAYOUT: 554 { 555 perform_data_set_layout* data = (perform_data_set_layout*)_data; 556 BCheckBox::SetLayout(data->layout); 557 return B_OK; 558 } 559 case PERFORM_CODE_LAYOUT_INVALIDATED: 560 { 561 perform_data_layout_invalidated* data 562 = (perform_data_layout_invalidated*)_data; 563 BCheckBox::LayoutInvalidated(data->descendants); 564 return B_OK; 565 } 566 case PERFORM_CODE_DO_LAYOUT: 567 { 568 BCheckBox::DoLayout(); 569 return B_OK; 570 } 571 } 572 573 return BControl::Perform(code, _data); 574 } 575 576 577 void 578 BCheckBox::LayoutInvalidated(bool descendants) 579 { 580 // invalidate cached preferred size 581 fPreferredSize.Set(B_SIZE_UNSET, B_SIZE_UNSET); 582 } 583 584 585 // #pragma mark - FBC padding 586 587 588 void BCheckBox::_ReservedCheckBox1() {} 589 void BCheckBox::_ReservedCheckBox2() {} 590 void BCheckBox::_ReservedCheckBox3() {} 591 592 593 BRect 594 BCheckBox::_CheckBoxFrame() const 595 { 596 font_height fontHeight; 597 GetFontHeight(&fontHeight); 598 599 return BRect(0.0f, 2.0f, ceilf(3.0f + fontHeight.ascent), 600 ceilf(5.0f + fontHeight.ascent)); 601 } 602 603 604 BSize 605 BCheckBox::_ValidatePreferredSize() 606 { 607 if (!fPreferredSize.IsWidthSet()) { 608 font_height fontHeight; 609 GetFontHeight(&fontHeight); 610 611 float width = 12.0f + fontHeight.ascent; 612 613 if (Label()) 614 width += StringWidth(Label()); 615 616 fPreferredSize.width = (float)ceil(width); 617 618 fPreferredSize.height = (float)ceil(6.0f + fontHeight.ascent 619 + fontHeight.descent); 620 621 ResetLayoutInvalidation(); 622 } 623 624 return fPreferredSize; 625 } 626 627 628 BCheckBox & 629 BCheckBox::operator=(const BCheckBox &) 630 { 631 return *this; 632 } 633 634 635 extern "C" void 636 B_IF_GCC_2(InvalidateLayout__9BCheckBoxb, _ZN9BCheckBox16InvalidateLayoutEb)( 637 BCheckBox* box, bool descendants) 638 { 639 perform_data_layout_invalidated data; 640 data.descendants = descendants; 641 642 box->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data); 643 } 644 645