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