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