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