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 27 BCheckBox::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 45 BCheckBox::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 56 BCheckBox::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 66 BCheckBox::BCheckBox(BMessage* data) 67 : 68 BControl(data), 69 fOutlined(false), 70 fPartialToOff(false) 71 { 72 } 73 74 75 BCheckBox::~BCheckBox() 76 { 77 } 78 79 80 // #pragma mark - Archiving methods 81 82 83 BArchivable* 84 BCheckBox::Instantiate(BMessage* data) 85 { 86 if (validate_instantiation(data, "BCheckBox")) 87 return new(std::nothrow) BCheckBox(data); 88 89 return NULL; 90 } 91 92 93 status_t 94 BCheckBox::Archive(BMessage* data, bool deep) const 95 { 96 return BControl::Archive(data, deep); 97 } 98 99 100 // #pragma mark - Hook methods 101 102 103 void 104 BCheckBox::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 132 void 133 BCheckBox::AttachedToWindow() 134 { 135 BControl::AttachedToWindow(); 136 } 137 138 139 void 140 BCheckBox::DetachedFromWindow() 141 { 142 BControl::DetachedFromWindow(); 143 } 144 145 146 void 147 BCheckBox::AllAttached() 148 { 149 BControl::AllAttached(); 150 } 151 152 153 void 154 BCheckBox::AllDetached() 155 { 156 BControl::AllDetached(); 157 } 158 159 160 void 161 BCheckBox::FrameMoved(BPoint newPosition) 162 { 163 BControl::FrameMoved(newPosition); 164 } 165 166 167 void 168 BCheckBox::FrameResized(float newWidth, float newHeight) 169 { 170 BControl::FrameResized(newWidth, newHeight); 171 } 172 173 174 void 175 BCheckBox::WindowActivated(bool active) 176 { 177 BControl::WindowActivated(active); 178 } 179 180 181 void 182 BCheckBox::MessageReceived(BMessage* message) 183 { 184 BControl::MessageReceived(message); 185 } 186 187 188 void 189 BCheckBox::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 204 void 205 BCheckBox::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 248 void 249 BCheckBox::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 273 void 274 BCheckBox::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 292 void 293 BCheckBox::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 305 void 306 BCheckBox::ResizeToPreferred() 307 { 308 BControl::ResizeToPreferred(); 309 } 310 311 312 BSize 313 BCheckBox::MinSize() 314 { 315 return BLayoutUtils::ComposeSize(ExplicitMinSize(), 316 _ValidatePreferredSize()); 317 } 318 319 320 BSize 321 BCheckBox::MaxSize() 322 { 323 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), 324 _ValidatePreferredSize()); 325 } 326 327 328 BSize 329 BCheckBox::PreferredSize() 330 { 331 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), 332 _ValidatePreferredSize()); 333 } 334 335 336 BAlignment 337 BCheckBox::LayoutAlignment() 338 { 339 return BLayoutUtils::ComposeAlignment(ExplicitAlignment(), 340 BAlignment(B_ALIGN_LEFT, B_ALIGN_VERTICAL_CENTER)); 341 } 342 343 344 // #pragma mark - 345 346 347 void 348 BCheckBox::MakeFocus(bool focused) 349 { 350 BControl::MakeFocus(focused); 351 } 352 353 354 void 355 BCheckBox::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 375 status_t 376 BCheckBox::Invoke(BMessage* message) 377 { 378 return BControl::Invoke(message); 379 } 380 381 382 BHandler* 383 BCheckBox::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 391 status_t 392 BCheckBox::GetSupportedSuites(BMessage* message) 393 { 394 return BControl::GetSupportedSuites(message); 395 } 396 397 398 status_t 399 BCheckBox::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 459 status_t 460 BCheckBox::SetIcon(const BBitmap* icon, uint32 flags) 461 { 462 return BControl::SetIcon(icon, flags | B_CREATE_DISABLED_ICON_BITMAPS); 463 } 464 465 466 void 467 BCheckBox::LayoutInvalidated(bool descendants) 468 { 469 // invalidate cached preferred size 470 fPreferredSize.Set(B_SIZE_UNSET, B_SIZE_UNSET); 471 } 472 473 474 bool 475 BCheckBox::IsPartialStateToOff() const 476 { 477 return fPartialToOff; 478 } 479 480 481 void 482 BCheckBox::SetPartialStateToOff(bool partialToOff) 483 { 484 fPartialToOff = partialToOff; 485 } 486 487 488 // #pragma mark - FBC padding 489 490 491 void BCheckBox::_ReservedCheckBox1() {} 492 void BCheckBox::_ReservedCheckBox2() {} 493 void BCheckBox::_ReservedCheckBox3() {} 494 495 496 BRect 497 BCheckBox::_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 504 BRect 505 BCheckBox::_CheckBoxFrame() const 506 { 507 font_height fontHeight; 508 GetFontHeight(&fontHeight); 509 return _CheckBoxFrame(fontHeight); 510 } 511 512 513 BSize 514 BCheckBox::_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 547 int32 548 BCheckBox::_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 562 BCheckBox & 563 BCheckBox::operator=(const BCheckBox &) 564 { 565 return *this; 566 } 567 568 569 extern "C" void 570 B_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