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