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 24BRadioButton::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 38BRadioButton::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 47BRadioButton::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 55BRadioButton::BRadioButton(BMessage* data) 56 : 57 BControl(data), 58 fOutlined(false) 59{ 60} 61 62 63BRadioButton::~BRadioButton() 64{ 65} 66 67 68BArchivable* 69BRadioButton::Instantiate(BMessage* data) 70{ 71 if (validate_instantiation(data, "BRadioButton")) 72 return new BRadioButton(data); 73 74 return NULL; 75} 76 77 78status_t 79BRadioButton::Archive(BMessage* data, bool deep) const 80{ 81 return BControl::Archive(data, deep); 82} 83 84 85void 86BRadioButton::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 118void 119BRadioButton::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 158void 159BRadioButton::AttachedToWindow() 160{ 161 BControl::AttachedToWindow(); 162} 163 164 165void 166BRadioButton::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 189void 190BRadioButton::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 247void 248BRadioButton::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 279void 280BRadioButton::ResizeToPreferred() 281{ 282 BControl::ResizeToPreferred(); 283} 284 285 286status_t 287BRadioButton::Invoke(BMessage* message) 288{ 289 return BControl::Invoke(message); 290} 291 292 293void 294BRadioButton::MessageReceived(BMessage* message) 295{ 296 BControl::MessageReceived(message); 297} 298 299 300void 301BRadioButton::WindowActivated(bool active) 302{ 303 BControl::WindowActivated(active); 304} 305 306 307void 308BRadioButton::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 327void 328BRadioButton::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 343void 344BRadioButton::DetachedFromWindow() 345{ 346 BControl::DetachedFromWindow(); 347} 348 349 350void 351BRadioButton::FrameMoved(BPoint newPosition) 352{ 353 BControl::FrameMoved(newPosition); 354} 355 356 357void 358BRadioButton::FrameResized(float newWidth, float newHeight) 359{ 360 Invalidate(); 361 BControl::FrameResized(newWidth, newHeight); 362} 363 364 365BHandler* 366BRadioButton::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 374void 375BRadioButton::MakeFocus(bool focus) 376{ 377 BControl::MakeFocus(focus); 378} 379 380 381void 382BRadioButton::AllAttached() 383{ 384 BControl::AllAttached(); 385} 386 387 388void 389BRadioButton::AllDetached() 390{ 391 BControl::AllDetached(); 392} 393 394 395status_t 396BRadioButton::GetSupportedSuites(BMessage* message) 397{ 398 return BControl::GetSupportedSuites(message); 399} 400 401 402status_t 403BRadioButton::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 472BSize 473BRadioButton::MaxSize() 474{ 475 float width, height; 476 GetPreferredSize(&width, &height); 477 478 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), 479 BSize(width, height)); 480} 481 482 483BAlignment 484BRadioButton::LayoutAlignment() 485{ 486 return BLayoutUtils::ComposeAlignment(ExplicitAlignment(), 487 BAlignment(B_ALIGN_LEFT, B_ALIGN_VERTICAL_UNSET)); 488} 489 490 491status_t 492BRadioButton::SetIcon(const BBitmap* icon, uint32 flags) 493{ 494 return BControl::SetIcon(icon, flags | B_CREATE_DISABLED_ICON_BITMAPS); 495} 496 497 498void BRadioButton::_ReservedRadioButton1() {} 499void BRadioButton::_ReservedRadioButton2() {} 500 501 502BRadioButton& 503BRadioButton::operator=(const BRadioButton &) 504{ 505 return *this; 506} 507 508 509BRect 510BRadioButton::_KnobFrame() const 511{ 512 font_height fontHeight; 513 GetFontHeight(&fontHeight); 514 return _KnobFrame(fontHeight); 515} 516 517 518BRect 519BRadioButton::_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 527void 528BRadioButton::_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