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