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(2, 2); 158 159 SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 160 SetPenSize(2); 161 SetDrawingMode(B_OP_OVER); 162 // needed because of anti-aliasing 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 SetDrawingMode(B_OP_COPY); 169 } 170 171 // Label 172 SetHighColor(darkenmax); 173 DrawString(Label(), BPoint((float)ceil(10.0f + fontHeight.ascent), 174 3.0f + (float)ceil(fontHeight.ascent))); 175 176 // Focus 177 if (IsFocus()) { 178 float x = (float)ceil(10.0f + fontHeight.ascent); 179 float y = 5.0f + (float)ceil(fontHeight.ascent); 180 181 SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 182 StrokeLine(BPoint(x, y), BPoint(x + StringWidth(Label()), y)); 183 } 184 } else { 185 // Filling 186 SetHighColor(lighten1); 187 FillRect(rect); 188 189 // Box 190 BeginLineArray(6); 191 192 AddLine(BPoint(rect.left, rect.bottom), 193 BPoint(rect.left, rect.top), noTint); 194 AddLine(BPoint(rect.left, rect.top), 195 BPoint(rect.right, rect.top), noTint); 196 rect.InsetBy(1, 1); 197 AddLine(BPoint(rect.left, rect.bottom), 198 BPoint(rect.left, rect.top), darken2); 199 AddLine(BPoint(rect.left, rect.top), 200 BPoint(rect.right, rect.top), darken2); 201 AddLine(BPoint(rect.left + 1.0f, rect.bottom), 202 BPoint(rect.right, rect.bottom), darken1); 203 AddLine(BPoint(rect.right, rect.bottom), 204 BPoint(rect.right, rect.top + 1.0f), darken1); 205 206 EndLineArray(); 207 208 // Checkmark 209 if (Value() == B_CONTROL_ON) { 210 rect.InsetBy(2, 2); 211 212 SetHighColor(tint_color(ui_color(B_KEYBOARD_NAVIGATION_COLOR), 213 B_DISABLED_MARK_TINT)); 214 SetPenSize(2); 215 SetDrawingMode(B_OP_OVER); 216 // needed because of anti-aliasing 217 StrokeLine(BPoint(rect.left, rect.top), 218 BPoint(rect.right, rect.bottom)); 219 StrokeLine(BPoint(rect.left, rect.bottom), 220 BPoint(rect.right, rect.top)); 221 SetPenSize(1); 222 SetDrawingMode(B_OP_COPY); 223 } 224 225 // Label 226 SetHighColor(tint_color(noTint, B_DISABLED_LABEL_TINT)); 227 DrawString(Label(), BPoint((float)ceil(10.0f + fontHeight.ascent), 228 3.0f + (float)ceil(fontHeight.ascent))); 229 } 230 } 231 232 233 void 234 BCheckBox::AttachedToWindow() 235 { 236 BControl::AttachedToWindow(); 237 } 238 239 240 void 241 BCheckBox::MouseDown(BPoint point) 242 { 243 if (!IsEnabled()) 244 return; 245 246 fOutlined = true; 247 248 if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) { 249 Invalidate(); 250 SetTracking(true); 251 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); 252 } else { 253 BRect bounds = Bounds(); 254 uint32 buttons; 255 256 Invalidate(); 257 Window()->UpdateIfNeeded(); 258 259 do { 260 snooze(40000); 261 262 GetMouse(&point, &buttons, true); 263 264 bool inside = bounds.Contains(point); 265 if (fOutlined != inside) { 266 fOutlined = inside; 267 Invalidate(); 268 Window()->UpdateIfNeeded(); 269 } 270 } while (buttons != 0); 271 272 if (fOutlined) { 273 fOutlined = false; 274 SetValue(!Value()); 275 Invoke(); 276 } else { 277 Invalidate(); 278 Window()->UpdateIfNeeded(); 279 } 280 } 281 } 282 283 284 void 285 BCheckBox::MessageReceived(BMessage *message) 286 { 287 BControl::MessageReceived(message); 288 } 289 290 291 void 292 BCheckBox::WindowActivated(bool active) 293 { 294 BControl::WindowActivated(active); 295 } 296 297 298 void 299 BCheckBox::KeyDown(const char *bytes, int32 numBytes) 300 { 301 BControl::KeyDown(bytes, numBytes); 302 } 303 304 305 void 306 BCheckBox::MouseUp(BPoint point) 307 { 308 if (!IsTracking()) 309 return; 310 311 bool inside = Bounds().Contains(point); 312 313 if (fOutlined != inside) { 314 fOutlined = inside; 315 Invalidate(); 316 } 317 318 if (fOutlined) { 319 fOutlined = false; 320 SetValue(!Value()); 321 Invoke(); 322 } else { 323 Invalidate(); 324 } 325 326 SetTracking(false); 327 } 328 329 330 void 331 BCheckBox::MouseMoved(BPoint point, uint32 transit, const BMessage *message) 332 { 333 if (!IsTracking()) 334 return; 335 336 bool inside = Bounds().Contains(point); 337 338 if (fOutlined != inside) { 339 fOutlined = inside; 340 Invalidate(); 341 } 342 } 343 344 345 void 346 BCheckBox::DetachedFromWindow() 347 { 348 BControl::DetachedFromWindow(); 349 } 350 351 352 void 353 BCheckBox::SetValue(int32 value) 354 { 355 value = value ? B_CONTROL_ON : B_CONTROL_OFF; 356 // we only accept boolean values 357 358 if (value != Value()) { 359 BControl::SetValueNoUpdate(value); 360 Invalidate(_CheckBoxFrame()); 361 } 362 } 363 364 365 void 366 BCheckBox::GetPreferredSize(float* _width, float* _height) 367 { 368 font_height fontHeight; 369 GetFontHeight(&fontHeight); 370 371 if (_width) { 372 float width = 12.0f + fontHeight.ascent; 373 374 if (Label()) 375 width += StringWidth(Label()); 376 377 *_width = (float)ceil(width); 378 } 379 380 if (_height) 381 *_height = (float)ceil(6.0f + fontHeight.ascent + fontHeight.descent); 382 } 383 384 385 void 386 BCheckBox::ResizeToPreferred() 387 { 388 BControl::ResizeToPreferred(); 389 } 390 391 392 status_t 393 BCheckBox::Invoke(BMessage *message) 394 { 395 return BControl::Invoke(message); 396 } 397 398 399 void 400 BCheckBox::FrameMoved(BPoint newLocation) 401 { 402 BControl::FrameMoved(newLocation); 403 } 404 405 406 void 407 BCheckBox::FrameResized(float width, float height) 408 { 409 BControl::FrameResized(width, height); 410 } 411 412 413 BHandler * 414 BCheckBox::ResolveSpecifier(BMessage *message, int32 index, 415 BMessage *specifier, int32 what, const char *property) 416 { 417 return BControl::ResolveSpecifier(message, index, specifier, what, 418 property); 419 } 420 421 422 status_t 423 BCheckBox::GetSupportedSuites(BMessage *message) 424 { 425 return BControl::GetSupportedSuites(message); 426 } 427 428 429 void 430 BCheckBox::MakeFocus(bool focused) 431 { 432 BControl::MakeFocus(focused); 433 } 434 435 436 void 437 BCheckBox::AllAttached() 438 { 439 BControl::AllAttached(); 440 } 441 442 443 void 444 BCheckBox::AllDetached() 445 { 446 BControl::AllDetached(); 447 } 448 449 450 status_t 451 BCheckBox::Perform(perform_code d, void *arg) 452 { 453 return BControl::Perform(d, arg); 454 } 455 456 457 void 458 BCheckBox::InvalidateLayout(bool descendants) 459 { 460 // invalidate cached preferred size 461 fPreferredSize.Set(B_SIZE_UNSET, B_SIZE_UNSET); 462 463 BControl::InvalidateLayout(descendants); 464 } 465 466 467 BSize 468 BCheckBox::MinSize() 469 { 470 return BLayoutUtils::ComposeSize(ExplicitMinSize(), 471 _ValidatePreferredSize()); 472 } 473 474 475 BSize 476 BCheckBox::MaxSize() 477 { 478 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), 479 BSize(B_SIZE_UNLIMITED, _ValidatePreferredSize().height)); 480 } 481 482 483 BSize 484 BCheckBox::PreferredSize() 485 { 486 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), 487 _ValidatePreferredSize()); 488 } 489 490 491 void BCheckBox::_ReservedCheckBox1() {} 492 void BCheckBox::_ReservedCheckBox2() {} 493 void BCheckBox::_ReservedCheckBox3() {} 494 495 496 BCheckBox & 497 BCheckBox::operator=(const BCheckBox &) 498 { 499 return *this; 500 } 501 502 503 BRect 504 BCheckBox::_CheckBoxFrame() const 505 { 506 font_height fontHeight; 507 GetFontHeight(&fontHeight); 508 509 return BRect(1.0f, 3.0f, ceilf(3.0f + fontHeight.ascent), 510 ceilf(5.0f + fontHeight.ascent)); 511 } 512 513 514 BSize 515 BCheckBox::_ValidatePreferredSize() 516 { 517 if (!fPreferredSize.IsWidthSet()) { 518 font_height fontHeight; 519 GetFontHeight(&fontHeight); 520 521 float width = 12.0f + fontHeight.ascent; 522 523 if (Label()) 524 width += StringWidth(Label()); 525 526 fPreferredSize.width = (float)ceil(width); 527 528 fPreferredSize.height = (float)ceil(6.0f + fontHeight.ascent 529 + fontHeight.descent); 530 } 531 532 return fPreferredSize; 533 } 534