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 = ViewColor(); 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 BRect labelRect(Bounds()); 117 labelRect.left = checkBoxRect.right + 1 118 + be_control_look->DefaultLabelSpacing(); 119 120 const BBitmap* icon = IconBitmap( 121 B_INACTIVE_ICON_BITMAP | (IsEnabled() ? 0 : B_DISABLED_ICON_BITMAP)); 122 const BAlignment alignment = BAlignment(B_ALIGN_LEFT, B_ALIGN_VERTICAL_CENTER); 123 124 be_control_look->DrawLabel(this, Label(), icon, labelRect, updateRect, base, flags, alignment); 125 } 126 127 128 void 129 BCheckBox::AttachedToWindow() 130 { 131 BControl::AttachedToWindow(); 132 } 133 134 135 void 136 BCheckBox::DetachedFromWindow() 137 { 138 BControl::DetachedFromWindow(); 139 } 140 141 142 void 143 BCheckBox::AllAttached() 144 { 145 BControl::AllAttached(); 146 } 147 148 149 void 150 BCheckBox::AllDetached() 151 { 152 BControl::AllDetached(); 153 } 154 155 156 void 157 BCheckBox::FrameMoved(BPoint newPosition) 158 { 159 BControl::FrameMoved(newPosition); 160 } 161 162 163 void 164 BCheckBox::FrameResized(float newWidth, float newHeight) 165 { 166 BControl::FrameResized(newWidth, newHeight); 167 } 168 169 170 void 171 BCheckBox::WindowActivated(bool active) 172 { 173 BControl::WindowActivated(active); 174 } 175 176 177 void 178 BCheckBox::MessageReceived(BMessage* message) 179 { 180 BControl::MessageReceived(message); 181 } 182 183 184 void 185 BCheckBox::KeyDown(const char* bytes, int32 numBytes) 186 { 187 if (*bytes == B_ENTER || *bytes == B_SPACE) { 188 if (!IsEnabled()) 189 return; 190 191 SetValue(_NextState()); 192 Invoke(); 193 } else { 194 // skip the BControl implementation 195 BView::KeyDown(bytes, numBytes); 196 } 197 } 198 199 200 void 201 BCheckBox::MouseDown(BPoint where) 202 { 203 if (!IsEnabled()) 204 return; 205 206 fOutlined = true; 207 208 if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) { 209 Invalidate(); 210 SetTracking(true); 211 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); 212 } else { 213 BRect bounds = Bounds(); 214 uint32 buttons; 215 216 Invalidate(); 217 Window()->UpdateIfNeeded(); 218 219 do { 220 snooze(40000); 221 222 GetMouse(&where, &buttons, true); 223 224 bool inside = bounds.Contains(where); 225 if (fOutlined != inside) { 226 fOutlined = inside; 227 Invalidate(); 228 Window()->UpdateIfNeeded(); 229 } 230 } while (buttons != 0); 231 232 if (fOutlined) { 233 fOutlined = false; 234 SetValue(_NextState()); 235 Invoke(); 236 } else { 237 Invalidate(); 238 Window()->UpdateIfNeeded(); 239 } 240 } 241 } 242 243 244 void 245 BCheckBox::MouseUp(BPoint where) 246 { 247 if (!IsTracking()) 248 return; 249 250 bool inside = Bounds().Contains(where); 251 252 if (fOutlined != inside) { 253 fOutlined = inside; 254 Invalidate(); 255 } 256 257 if (fOutlined) { 258 fOutlined = false; 259 SetValue(_NextState()); 260 Invoke(); 261 } else { 262 Invalidate(); 263 } 264 265 SetTracking(false); 266 } 267 268 269 void 270 BCheckBox::MouseMoved(BPoint where, uint32 code, 271 const BMessage* dragMessage) 272 { 273 if (!IsTracking()) 274 return; 275 276 bool inside = Bounds().Contains(where); 277 278 if (fOutlined != inside) { 279 fOutlined = inside; 280 Invalidate(); 281 } 282 } 283 284 285 // #pragma mark - 286 287 288 void 289 BCheckBox::GetPreferredSize(float* _width, float* _height) 290 { 291 _ValidatePreferredSize(); 292 293 if (_width) 294 *_width = fPreferredSize.width; 295 296 if (_height) 297 *_height = fPreferredSize.height; 298 } 299 300 301 void 302 BCheckBox::ResizeToPreferred() 303 { 304 BControl::ResizeToPreferred(); 305 } 306 307 308 BSize 309 BCheckBox::MinSize() 310 { 311 return BLayoutUtils::ComposeSize(ExplicitMinSize(), 312 _ValidatePreferredSize()); 313 } 314 315 316 BSize 317 BCheckBox::MaxSize() 318 { 319 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), 320 _ValidatePreferredSize()); 321 } 322 323 324 BSize 325 BCheckBox::PreferredSize() 326 { 327 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), 328 _ValidatePreferredSize()); 329 } 330 331 332 BAlignment 333 BCheckBox::LayoutAlignment() 334 { 335 return BLayoutUtils::ComposeAlignment(ExplicitAlignment(), 336 BAlignment(B_ALIGN_LEFT, B_ALIGN_VERTICAL_CENTER)); 337 } 338 339 340 // #pragma mark - 341 342 343 void 344 BCheckBox::MakeFocus(bool focused) 345 { 346 BControl::MakeFocus(focused); 347 } 348 349 350 void 351 BCheckBox::SetValue(int32 value) 352 { 353 // We only accept three possible values. 354 switch (value) { 355 case B_CONTROL_OFF: 356 case B_CONTROL_ON: 357 case B_CONTROL_PARTIALLY_ON: 358 break; 359 default: 360 value = B_CONTROL_ON; 361 break; 362 } 363 364 if (value != Value()) { 365 BControl::SetValueNoUpdate(value); 366 Invalidate(_CheckBoxFrame()); 367 } 368 } 369 370 371 status_t 372 BCheckBox::Invoke(BMessage* message) 373 { 374 return BControl::Invoke(message); 375 } 376 377 378 BHandler* 379 BCheckBox::ResolveSpecifier(BMessage* message, int32 index, 380 BMessage* specifier, int32 what, const char* property) 381 { 382 return BControl::ResolveSpecifier(message, index, specifier, what, 383 property); 384 } 385 386 387 status_t 388 BCheckBox::GetSupportedSuites(BMessage* message) 389 { 390 return BControl::GetSupportedSuites(message); 391 } 392 393 394 status_t 395 BCheckBox::Perform(perform_code code, void* _data) 396 { 397 switch (code) { 398 case PERFORM_CODE_MIN_SIZE: 399 ((perform_data_min_size*)_data)->return_value 400 = BCheckBox::MinSize(); 401 return B_OK; 402 case PERFORM_CODE_MAX_SIZE: 403 ((perform_data_max_size*)_data)->return_value 404 = BCheckBox::MaxSize(); 405 return B_OK; 406 case PERFORM_CODE_PREFERRED_SIZE: 407 ((perform_data_preferred_size*)_data)->return_value 408 = BCheckBox::PreferredSize(); 409 return B_OK; 410 case PERFORM_CODE_LAYOUT_ALIGNMENT: 411 ((perform_data_layout_alignment*)_data)->return_value 412 = BCheckBox::LayoutAlignment(); 413 return B_OK; 414 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH: 415 ((perform_data_has_height_for_width*)_data)->return_value 416 = BCheckBox::HasHeightForWidth(); 417 return B_OK; 418 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH: 419 { 420 perform_data_get_height_for_width* data 421 = (perform_data_get_height_for_width*)_data; 422 BCheckBox::GetHeightForWidth(data->width, &data->min, &data->max, 423 &data->preferred); 424 return B_OK; 425 } 426 case PERFORM_CODE_SET_LAYOUT: 427 { 428 perform_data_set_layout* data = (perform_data_set_layout*)_data; 429 BCheckBox::SetLayout(data->layout); 430 return B_OK; 431 } 432 case PERFORM_CODE_LAYOUT_INVALIDATED: 433 { 434 perform_data_layout_invalidated* data 435 = (perform_data_layout_invalidated*)_data; 436 BCheckBox::LayoutInvalidated(data->descendants); 437 return B_OK; 438 } 439 case PERFORM_CODE_DO_LAYOUT: 440 { 441 BCheckBox::DoLayout(); 442 return B_OK; 443 } 444 case PERFORM_CODE_SET_ICON: 445 { 446 perform_data_set_icon* data = (perform_data_set_icon*)_data; 447 return BCheckBox::SetIcon(data->icon, data->flags); 448 } 449 } 450 451 return BControl::Perform(code, _data); 452 } 453 454 455 status_t 456 BCheckBox::SetIcon(const BBitmap* icon, uint32 flags) 457 { 458 return BControl::SetIcon(icon, flags | B_CREATE_DISABLED_ICON_BITMAPS); 459 } 460 461 462 void 463 BCheckBox::LayoutInvalidated(bool descendants) 464 { 465 // invalidate cached preferred size 466 fPreferredSize.Set(B_SIZE_UNSET, B_SIZE_UNSET); 467 } 468 469 470 bool 471 BCheckBox::IsPartialStateToOff() const 472 { 473 return fPartialToOff; 474 } 475 476 477 void 478 BCheckBox::SetPartialStateToOff(bool partialToOff) 479 { 480 fPartialToOff = partialToOff; 481 } 482 483 484 // #pragma mark - FBC padding 485 486 487 void BCheckBox::_ReservedCheckBox1() {} 488 void BCheckBox::_ReservedCheckBox2() {} 489 void BCheckBox::_ReservedCheckBox3() {} 490 491 492 BRect 493 BCheckBox::_CheckBoxFrame(const font_height& fontHeight) const 494 { 495 return BRect(0.0f, 2.0f, ceilf(3.0f + fontHeight.ascent), 496 ceilf(5.0f + fontHeight.ascent)); 497 } 498 499 500 BRect 501 BCheckBox::_CheckBoxFrame() const 502 { 503 font_height fontHeight; 504 GetFontHeight(&fontHeight); 505 return _CheckBoxFrame(fontHeight); 506 } 507 508 509 BSize 510 BCheckBox::_ValidatePreferredSize() 511 { 512 if (!fPreferredSize.IsWidthSet()) { 513 font_height fontHeight; 514 GetFontHeight(&fontHeight); 515 516 BRect rect(_CheckBoxFrame(fontHeight)); 517 float width = rect.right + rect.left; 518 float height = rect.bottom + rect.top; 519 520 const BBitmap* icon = IconBitmap(B_INACTIVE_ICON_BITMAP); 521 if (icon != NULL) { 522 width += be_control_look->DefaultLabelSpacing() 523 + icon->Bounds().Width() + 1; 524 height = std::max(height, icon->Bounds().Height()); 525 } 526 527 if (const char* label = Label()) { 528 width += be_control_look->DefaultLabelSpacing() 529 + ceilf(StringWidth(label)); 530 height = std::max(height, 531 ceilf(6.0f + fontHeight.ascent + fontHeight.descent)); 532 } 533 534 fPreferredSize.Set(width, height); 535 536 ResetLayoutInvalidation(); 537 } 538 539 return fPreferredSize; 540 } 541 542 543 int32 544 BCheckBox::_NextState() const 545 { 546 switch (Value()) { 547 case B_CONTROL_OFF: 548 return B_CONTROL_ON; 549 case B_CONTROL_PARTIALLY_ON: 550 return fPartialToOff ? B_CONTROL_OFF : B_CONTROL_ON; 551 case B_CONTROL_ON: 552 default: 553 return B_CONTROL_OFF; 554 } 555 } 556 557 558 BCheckBox & 559 BCheckBox::operator=(const BCheckBox &) 560 { 561 return *this; 562 } 563 564 565 extern "C" void 566 B_IF_GCC_2(InvalidateLayout__9BCheckBoxb, _ZN9BCheckBox16InvalidateLayoutEb)( 567 BCheckBox* box, bool descendants) 568 { 569 perform_data_layout_invalidated data; 570 data.descendants = descendants; 571 572 box->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data); 573 } 574 575