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