1 //------------------------------------------------------------------------------ 2 // Copyright (c) 2001-2005, Haiku, Inc. 3 // 4 // Distributed under the terms of the MIT license. 5 // 6 // File Name: RadioButton.cpp 7 // Authors: Marc Flerackers (mflerackers@androme.be) 8 // Stephan Aßmus <superstippi@gmx.de> 9 // Description: BRadioButton represents a single on/off button. All 10 // sibling BRadioButton objects comprise a single 11 // "multiple choice" control. 12 //------------------------------------------------------------------------------ 13 14 // Standard Includes ----------------------------------------------------------- 15 16 // System Includes ------------------------------------------------------------- 17 #include <RadioButton.h> 18 #include <Errors.h> 19 #include <Box.h> 20 #include <Window.h> 21 22 // Project Includes ------------------------------------------------------------ 23 24 // Local Includes -------------------------------------------------------------- 25 26 // Local Defines --------------------------------------------------------------- 27 28 // Globals --------------------------------------------------------------------- 29 30 //------------------------------------------------------------------------------ 31 BRadioButton::BRadioButton(BRect frame, const char *name, const char *label, 32 BMessage *message, uint32 resizMask, uint32 flags) 33 : BControl(frame, name, label, message, resizMask, flags), 34 fOutlined(false) 35 { 36 // Resize to minimum height if needed 37 font_height fh; 38 GetFontHeight(&fh); 39 float minHeight = (float)ceil(6.0f + fh.ascent + fh.descent); 40 if (Bounds().Height() < minHeight) 41 ResizeTo(Bounds().Width(), minHeight); 42 } 43 //------------------------------------------------------------------------------ 44 BRadioButton::BRadioButton(BMessage *archive) 45 : BControl(archive), 46 fOutlined(false) 47 { 48 } 49 //------------------------------------------------------------------------------ 50 BRadioButton::~BRadioButton() 51 { 52 } 53 //------------------------------------------------------------------------------ 54 BArchivable *BRadioButton::Instantiate(BMessage *archive) 55 { 56 if (validate_instantiation(archive, "BRadioButton")) 57 return new BRadioButton(archive); 58 else 59 return NULL; 60 } 61 //------------------------------------------------------------------------------ 62 status_t BRadioButton::Archive(BMessage *archive, bool deep) const 63 { 64 return BControl::Archive(archive, deep); 65 } 66 //------------------------------------------------------------------------------ 67 void BRadioButton::Draw(BRect updateRect) 68 { 69 // NOTE: the commented out StrokeLine() calls appearently tweak the 70 // rendering on R5, but they are unnecessary under Haiku 71 72 font_height fh; 73 GetFontHeight(&fh); 74 75 // layout the rect for the dot 76 BRect rect = _KnobFrame(); 77 78 // its size depends on the text height 79 float textHeight = floorf(fh.ascent + fh.descent + 0.5); 80 81 BPoint labelPos(rect.right + floorf(textHeight / 2.0), 82 floorf((rect.top + rect.bottom + textHeight) / 2.0 - fh.descent + 0.5) + 1.0); 83 84 // If the focus is changing, just redraw the focus indicator 85 if (IsFocusChanging()) { 86 if (IsFocus()) 87 SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 88 else 89 SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 90 91 BPoint underLine = labelPos; 92 underLine.y += fh.descent; 93 StrokeLine(underLine, underLine + BPoint(StringWidth(Label()), 0.0)); 94 95 return; 96 } 97 98 // Placeholder until sBitmaps is filled in 99 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR), 100 lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT), 101 lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT), 102 darken1 = tint_color(no_tint, B_DARKEN_1_TINT), 103 darken2 = tint_color(no_tint, B_DARKEN_2_TINT), 104 darken3 = tint_color(no_tint, B_DARKEN_3_TINT), 105 //darken4 = tint_color(no_tint, B_DARKEN_4_TINT), 106 darkenmax = tint_color(no_tint, B_DARKEN_MAX_TINT); 107 108 // NOTE: this improves drawing a lot because of 109 // anti-aliased circles and arcs in Haiku 110 SetDrawingMode(B_OP_OVER); 111 112 if (IsEnabled()) { 113 // Dot 114 if (Value() == B_CONTROL_ON) { 115 rgb_color kb_color = ui_color(B_KEYBOARD_NAVIGATION_COLOR); 116 117 SetHighColor(tint_color(kb_color, B_DARKEN_3_TINT)); 118 FillEllipse(rect); 119 SetHighColor(kb_color); 120 FillEllipse(BRect(rect.left + 3, rect.top + 3, rect.right - 4, rect.bottom - 4)); 121 SetHighColor(tint_color(kb_color, B_DARKEN_3_TINT)); 122 StrokeLine(BPoint(rect.right - 5, rect.bottom - 4), 123 BPoint(rect.right - 4, rect.bottom - 5)); 124 SetHighColor(tint_color(kb_color, B_LIGHTEN_MAX_TINT)); 125 StrokeLine(BPoint(rect.left + 4, rect.top + 5), 126 BPoint(rect.left + 5, rect.top + 4)); 127 128 } else { 129 SetHighColor(lightenmax); 130 FillEllipse(rect); 131 } 132 133 rect.InsetBy(-1.0, -1.0); 134 135 // Outer circle 136 if (fOutlined) { 137 SetHighColor(darken3); 138 StrokeEllipse(rect); 139 } else { 140 SetHighColor(darken1); 141 StrokeArc(rect, 45.0f, 180.0f); 142 SetHighColor(lightenmax); 143 StrokeArc(rect, 45.0f, -180.0f); 144 } 145 146 rect.InsetBy(1, 1); 147 148 // Inner circle 149 SetHighColor(darken3); 150 StrokeArc(rect, 45.0f, 180.0f); 151 // StrokeLine(BPoint(rect.left + 1, rect.top + 1), 152 // BPoint(rect.left + 1, rect.top + 1)); 153 SetHighColor(no_tint); 154 StrokeArc(rect, 45.0f, -180.0f); 155 // StrokeLine(BPoint(rect.left + 1, rect.bottom - 1), 156 // BPoint(rect.left + 1, rect.bottom - 1)); 157 // StrokeLine(BPoint(rect.right - 1, rect.bottom - 1), 158 // BPoint(rect.right - 1, rect.bottom - 1)); 159 // StrokeLine(BPoint(rect.right - 1, rect.top + 1), 160 // BPoint(rect.right - 1, rect.top + 1)); 161 162 // For faster font rendering, we can restore B_OP_COPY 163 SetDrawingMode(B_OP_COPY); 164 165 // Label 166 SetHighColor(darkenmax); 167 DrawString(Label(), labelPos); 168 169 // Focus 170 if (IsFocus()) { 171 SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 172 BPoint underLine = labelPos; 173 underLine.y += fh.descent; 174 StrokeLine(underLine, underLine + BPoint(StringWidth(Label()), 0.0)); 175 } 176 } else { 177 // Dot 178 if (Value() == B_CONTROL_ON) { 179 rgb_color kb_color = ui_color(B_KEYBOARD_NAVIGATION_COLOR); 180 181 SetHighColor(tint_color(kb_color, B_LIGHTEN_2_TINT)); 182 FillEllipse(rect); 183 SetHighColor(tint_color(kb_color, B_LIGHTEN_MAX_TINT)); 184 StrokeLine(BPoint(rect.left + 4, rect.top + 5), 185 BPoint(rect.left + 5, rect.top + 4)); 186 SetHighColor(tint_color(kb_color, B_DARKEN_3_TINT)); 187 StrokeArc(BRect(rect.left + 2, rect.top + 2, rect.right - 2, rect.bottom - 2), 188 45.0f, -180.0f); 189 } else { 190 SetHighColor(lighten1); 191 FillEllipse(rect); 192 } 193 194 rect.InsetBy(-1.0, -1.0); 195 196 // Outer circle 197 SetHighColor(no_tint); 198 StrokeArc(rect, 45.0f, 180.0f); 199 SetHighColor(lighten1); 200 StrokeArc(rect, 45.0f, -180.0f); 201 202 rect.InsetBy(1, 1); 203 204 // Inner circle 205 SetHighColor(darken2); 206 StrokeArc(rect, 45.0f, 180.0f); 207 // StrokeLine(BPoint(rect.left + 1, rect.top + 1), 208 // BPoint(rect.left + 1, rect.top + 1)); 209 SetHighColor(no_tint); 210 StrokeArc(rect, 45.0f, -180.0f); 211 // StrokeLine(BPoint(rect.left + 1, rect.bottom - 1), 212 // BPoint(rect.left + 1, rect.bottom - 1)); 213 // StrokeLine(BPoint(rect.right - 1, rect.bottom - 1), 214 // BPoint(rect.right - 1, rect.bottom - 1)); 215 // StrokeLine(BPoint(rect.right - 1, rect.top + 1), 216 // BPoint(rect.right - 1, rect.top + 1)); 217 218 // Label 219 SetHighColor(tint_color(no_tint, B_DISABLED_LABEL_TINT)); 220 DrawString(Label(), labelPos); 221 } 222 } 223 //------------------------------------------------------------------------------ 224 void BRadioButton::MouseDown(BPoint point) 225 { 226 if (!IsEnabled()) 227 return; 228 229 fOutlined = true; 230 231 if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) { 232 Invalidate(); 233 SetTracking(true); 234 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); 235 } else { 236 Draw(Bounds()); 237 Flush(); 238 239 BRect bounds = Bounds(); 240 uint32 buttons; 241 242 do { 243 snooze(40000); 244 245 GetMouse(&point, &buttons, true); 246 247 bool inside = bounds.Contains(point); 248 249 if (fOutlined != inside) { 250 fOutlined = inside; 251 Draw(Bounds()); 252 Flush(); 253 } 254 } while (buttons != 0); 255 256 if (fOutlined) { 257 fOutlined = false; 258 Draw(Bounds()); 259 Flush(); 260 SetValue(B_CONTROL_ON); 261 Invoke(); 262 } else { 263 Draw(Bounds()); 264 Flush(); 265 } 266 } 267 268 } 269 //------------------------------------------------------------------------------ 270 void BRadioButton::AttachedToWindow() 271 { 272 BControl::AttachedToWindow(); 273 } 274 //------------------------------------------------------------------------------ 275 void BRadioButton::KeyDown(const char *bytes, int32 numBytes) 276 { 277 // TODO add select_next_button functionality 278 279 switch (bytes[0]) 280 { 281 case ' ': 282 { 283 if (IsEnabled() && !Value()) 284 { 285 SetValue(B_CONTROL_ON); 286 Invoke(); 287 } 288 289 break; 290 } 291 default: 292 BControl::KeyDown(bytes, numBytes); 293 } 294 } 295 //------------------------------------------------------------------------------ 296 void BRadioButton::SetValue(int32 value) 297 { 298 if (value != Value()) { 299 BControl::SetValue(value); 300 Invalidate(_KnobFrame()); 301 } 302 303 if (!value) 304 return; 305 306 BView *parent = Parent(); 307 BView *child = NULL; 308 309 if (parent) 310 { 311 // If the parent is a BBox, the group parent is the parent of the BBox 312 BBox *box = dynamic_cast<BBox*>(parent); 313 314 if (box && box->LabelView() == this) 315 parent = box->Parent(); 316 317 if (parent) 318 { 319 BBox *box = dynamic_cast<BBox*>(parent); 320 321 // If the parent is a BBox, skip the label if there is one 322 if (box && box->LabelView()) 323 child = parent->ChildAt(1); 324 else 325 child = parent->ChildAt(0); 326 } 327 else 328 child = Window()->ChildAt(0); 329 } 330 else if (Window()) 331 child = Window()->ChildAt(0); 332 333 while (child) 334 { 335 BRadioButton *radio = dynamic_cast<BRadioButton*>(child); 336 337 if (child != this && radio) 338 radio->SetValue(B_CONTROL_OFF); 339 else 340 { 341 // If the child is a BBox, check if the label is a radiobutton 342 BBox *box = dynamic_cast<BBox*>(child); 343 344 if (box && box->LabelView()) 345 { 346 radio = dynamic_cast<BRadioButton*>(box->LabelView()); 347 348 if (radio) 349 radio->SetValue(B_CONTROL_OFF); 350 } 351 } 352 353 child = child->NextSibling(); 354 } 355 } 356 //------------------------------------------------------------------------------ 357 void BRadioButton::GetPreferredSize(float *width, float *height) 358 { 359 font_height fh; 360 GetFontHeight(&fh); 361 362 *height = (float)ceil(fh.ascent + fh.descent) + 6.0f; 363 *width = 22.0f; // TODO: check if ascent is included 364 365 if (Label()) 366 *width += StringWidth(Label()); 367 368 *width = (float)ceil(*width); 369 } 370 //------------------------------------------------------------------------------ 371 void BRadioButton::ResizeToPreferred() 372 { 373 BControl::ResizeToPreferred(); 374 } 375 //------------------------------------------------------------------------------ 376 status_t BRadioButton::Invoke(BMessage *message) 377 { 378 return BControl::Invoke(message); 379 } 380 //------------------------------------------------------------------------------ 381 void BRadioButton::MessageReceived(BMessage *message) 382 { 383 BControl::MessageReceived(message); 384 } 385 //------------------------------------------------------------------------------ 386 void BRadioButton::WindowActivated(bool active) 387 { 388 BControl::WindowActivated(active); 389 } 390 //------------------------------------------------------------------------------ 391 void BRadioButton::MouseUp(BPoint point) 392 { 393 if (!IsTracking()) 394 return; 395 396 fOutlined = Bounds().Contains(point); 397 if (fOutlined) { 398 fOutlined = false; 399 SetValue(B_CONTROL_ON); 400 Invoke(); 401 } 402 Invalidate(); 403 404 SetTracking(false); 405 } 406 //------------------------------------------------------------------------------ 407 void BRadioButton::MouseMoved(BPoint point, uint32 transit, const BMessage *message) 408 { 409 if (!IsTracking()) 410 return; 411 412 bool inside = Bounds().Contains(point); 413 414 if (fOutlined != inside) { 415 fOutlined = inside; 416 Invalidate(); 417 } 418 } 419 //------------------------------------------------------------------------------ 420 void BRadioButton::DetachedFromWindow() 421 { 422 BControl::DetachedFromWindow(); 423 } 424 //------------------------------------------------------------------------------ 425 void BRadioButton::FrameMoved(BPoint newLocation) 426 { 427 BControl::FrameMoved(newLocation); 428 } 429 //------------------------------------------------------------------------------ 430 void BRadioButton::FrameResized(float width, float height) 431 { 432 BControl::FrameResized(width, height); 433 } 434 //------------------------------------------------------------------------------ 435 BHandler *BRadioButton::ResolveSpecifier(BMessage *message, int32 index, 436 BMessage *specifier, int32 what, 437 const char *property) 438 { 439 return BControl::ResolveSpecifier(message, index, specifier, what, 440 property); 441 } 442 //------------------------------------------------------------------------------ 443 void BRadioButton::MakeFocus(bool focused) 444 { 445 BControl::MakeFocus(focused); 446 } 447 //------------------------------------------------------------------------------ 448 void BRadioButton::AllAttached() 449 { 450 BControl::AllAttached(); 451 } 452 //------------------------------------------------------------------------------ 453 void BRadioButton::AllDetached() 454 { 455 BControl::AllDetached(); 456 } 457 //------------------------------------------------------------------------------ 458 status_t BRadioButton::GetSupportedSuites(BMessage *message) 459 { 460 return BControl::GetSupportedSuites(message); 461 } 462 //------------------------------------------------------------------------------ 463 status_t BRadioButton::Perform(perform_code d, void *arg) 464 { 465 return BControl::Perform(d, arg); 466 } 467 //------------------------------------------------------------------------------ 468 void BRadioButton::_ReservedRadioButton1() {} 469 void BRadioButton::_ReservedRadioButton2() {} 470 //------------------------------------------------------------------------------ 471 BRadioButton &BRadioButton::operator=(const BRadioButton &) 472 { 473 return *this; 474 } 475 //------------------------------------------------------------------------------ 476 BRect 477 BRadioButton::_KnobFrame() const 478 { 479 font_height fh; 480 GetFontHeight(&fh); 481 482 // layout the rect for the dot 483 BRect rect(Bounds()); 484 485 // its size depends on the text height 486 float textHeight = floorf(fh.ascent + fh.descent + 0.5); 487 float inset = -floorf(textHeight / 2 - 2); 488 489 rect.left -= (inset - 1); 490 rect.top = floorf((rect.top + rect.bottom) / 2.0); 491 rect.bottom = rect.top; 492 rect.right = rect.left; 493 rect.InsetBy(inset, inset); 494 495 return rect; 496 } 497 498 499 /* 500 * $Log $ 501 * 502 * $Id $ 503 * 504 */ 505