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