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