1 //------------------------------------------------------------------------------ 2 // Copyright (c) 2001-2002, OpenBeOS 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a 5 // copy of this software and associated documentation files (the "Software"), 6 // to deal in the Software without restriction, including without limitation 7 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 // and/or sell copies of the Software, and to permit persons to whom the 9 // Software is furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 // DEALINGS IN THE SOFTWARE. 21 // 22 // File Name: Button.cpp 23 // Author: Marc Flerackers (mflerackers@androme.be) 24 // Description: BButton displays and controls a button in a window. 25 //------------------------------------------------------------------------------ 26 27 // Standard Includes ----------------------------------------------------------- 28 29 // System Includes ------------------------------------------------------------- 30 #include <Button.h> 31 #include <Window.h> 32 #include <Errors.h> 33 34 // Project Includes ------------------------------------------------------------ 35 36 // Local Includes -------------------------------------------------------------- 37 38 // Local Defines --------------------------------------------------------------- 39 40 // Globals --------------------------------------------------------------------- 41 42 //------------------------------------------------------------------------------ 43 BButton::BButton(BRect frame, const char *name, const char *label, BMessage *message, 44 uint32 resizingMode, uint32 flags) 45 : BControl(frame, name, label, message, resizingMode, flags), 46 fDrawAsDefault(false) 47 { 48 // Resize to minimum height if needed 49 font_height fh; 50 GetFontHeight(&fh); 51 float minHeight = 12.0f + (float)ceil(fh.ascent + fh.descent); 52 if (Bounds().Height() < minHeight) 53 ResizeTo(Bounds().Width(), minHeight); 54 } 55 //------------------------------------------------------------------------------ 56 BButton::~BButton() 57 { 58 } 59 //------------------------------------------------------------------------------ 60 BButton::BButton(BMessage *archive) 61 : BControl (archive) 62 { 63 if (archive->FindBool("_default", &fDrawAsDefault) != B_OK) 64 fDrawAsDefault = false; 65 } 66 //------------------------------------------------------------------------------ 67 BArchivable *BButton::Instantiate(BMessage *archive) 68 { 69 if (validate_instantiation(archive, "BButton")) 70 return new BButton(archive); 71 else 72 return NULL; 73 } 74 //------------------------------------------------------------------------------ 75 status_t BButton::Archive(BMessage* archive, bool deep) const 76 { 77 status_t err = BControl::Archive(archive, deep); 78 79 if (err != B_OK) 80 return err; 81 82 if (fDrawAsDefault) 83 err = archive->AddBool("_default", fDrawAsDefault); 84 85 return err; 86 } 87 //------------------------------------------------------------------------------ 88 void BButton::Draw(BRect updateRect) 89 { 90 font_height fh; 91 GetFontHeight(&fh); 92 93 BRect bounds(Bounds()); 94 95 // If the focus is changing, just redraw the focus indicator 96 if (IsFocusChanging()) 97 { 98 float x = bounds.right / 2 - StringWidth(Label()) / 2.0f; 99 float y = bounds.bottom - fh.descent - (IsDefault() ? 6.0f : 3.0f); 100 101 if (IsFocus()) 102 SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 103 else 104 SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 105 B_LIGHTEN_1_TINT)); 106 107 StrokeLine(BPoint(x, y), BPoint(x + StringWidth(Label()), y)); 108 109 return; 110 } 111 112 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR), 113 lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT), 114 lighten2 = tint_color(no_tint, B_LIGHTEN_2_TINT), 115 lightenmax = tint_color(no_tint, B_LIGHTEN_MAX_TINT), 116 darken2 = tint_color(no_tint, B_DARKEN_2_TINT), 117 darken4 = tint_color(no_tint, B_DARKEN_4_TINT), 118 darkenmax = tint_color(no_tint, B_DARKEN_MAX_TINT); 119 120 BRect rect(bounds); 121 122 if (IsDefault()) 123 rect = DrawDefault(rect, IsEnabled()); 124 else 125 rect.InsetBy(1,1); 126 127 if (IsEnabled()) 128 { 129 // This can be set outside draw 130 SetHighColor(darken4); 131 132 // Dark border 133 StrokeRect(rect); 134 135 BeginLineArray(8); 136 137 // Corners 138 AddLine(rect.LeftBottom(), rect.LeftBottom(), no_tint); 139 AddLine(rect.LeftTop(), rect.LeftTop(), no_tint); 140 AddLine(rect.RightTop(), rect.RightTop(), no_tint); 141 AddLine(rect.RightBottom(), rect.RightBottom(), no_tint); 142 143 rect.InsetBy(1, 1); 144 145 // First bevel 146 AddLine(BPoint(rect.left + 1.0f, rect.bottom), 147 BPoint(rect.right, rect.bottom), darken2); 148 AddLine(BPoint(rect.right, rect.bottom - 1.0f), 149 BPoint(rect.right, rect.top + 1.0f), darken2); 150 151 AddLine(BPoint(rect.left, rect.top), 152 BPoint(rect.left, rect.bottom), lighten1); 153 AddLine(BPoint(rect.left + 1.0f, rect.top), 154 BPoint(rect.right, rect.top), lighten1); 155 156 EndLineArray(); 157 158 rect.InsetBy(1, 1); 159 160 // Second bevel 161 SetHighColor(lightenmax); 162 FillRect(rect); 163 164 SetHighColor(no_tint); 165 StrokeLine(BPoint(rect.right, rect.top + 1.0f), 166 BPoint(rect.right, rect.bottom)); 167 StrokeLine(BPoint(rect.left + 1.0f, rect.bottom)); 168 169 rect.InsetBy(1, 1); 170 171 // Filling 172 rect.left += 1.0f; 173 rect.top += 1.0f; 174 SetHighColor(lighten1); 175 FillRect(rect); 176 177 if (Value()) 178 { 179 // Invert 180 rect.left -= 3; 181 rect.top -= 3; 182 rect.right += 2; 183 rect.bottom += 2; 184 SetDrawingMode(B_OP_INVERT); 185 SetHighColor(0, 0, 0); 186 FillRect(rect); 187 SetDrawingMode(B_OP_COPY); 188 } 189 190 // Label 191 float x = bounds.right / 2 - StringWidth(Label()) / 2.0f; 192 float y = bounds.bottom - fh.descent - (IsDefault() ? 8.0f : 5.0f); 193 194 if (Value()) 195 { 196 SetHighColor(lightenmax); 197 SetLowColor(darkenmax); 198 } 199 else 200 { 201 SetHighColor(darkenmax); 202 SetLowColor(lighten2); 203 } 204 205 DrawString(Label(), BPoint(x, y)); 206 207 // Focus 208 if (IsFocus()) 209 { 210 y += 2.0f; 211 SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 212 StrokeLine(BPoint(x, y), BPoint(x + StringWidth(Label()), y)); 213 } 214 } 215 else 216 { 217 // This can be set outside draw 218 SetHighColor(darken2); 219 220 // Dark border 221 StrokeRect(rect); 222 223 BeginLineArray(8); 224 225 // Corners 226 AddLine(rect.LeftBottom(), rect.LeftBottom(), no_tint); 227 AddLine(rect.LeftTop(), rect.LeftTop(), no_tint); 228 AddLine(rect.RightTop(), rect.RightTop(), no_tint); 229 AddLine(rect.RightBottom(), rect.RightBottom(), no_tint); 230 231 rect.InsetBy(1, 1); 232 233 // First bevel 234 AddLine(BPoint(rect.left + 1.0f, rect.bottom), 235 BPoint(rect.right, rect.bottom), no_tint); 236 AddLine(BPoint(rect.right, rect.bottom - 1.0f), 237 BPoint(rect.right, rect.top + 1.0f), no_tint); 238 239 AddLine(BPoint(rect.left, rect.top), 240 BPoint(rect.left, rect.bottom), lighten1); 241 AddLine(BPoint(rect.left + 1.0f, rect.top), 242 BPoint(rect.right, rect.top), lighten1); 243 244 EndLineArray(); 245 246 rect.InsetBy(1, 1); 247 248 // Second bevel 249 SetHighColor(lightenmax); 250 FillRect(rect); 251 252 SetHighColor(no_tint); 253 StrokeLine(BPoint(rect.right, rect.top + 1.0f), 254 BPoint(rect.right, rect.bottom)); 255 StrokeLine(BPoint(rect.left + 1.0f, rect.bottom)); 256 257 rect.InsetBy(1, 1); 258 259 // Filling 260 rect.left += 1.0f; 261 rect.top += 1.0f; 262 SetHighColor(lighten1); 263 FillRect(rect); 264 265 // Label 266 float x = bounds.right / 2 - StringWidth(Label()) / 2.0f; 267 float y = bounds.bottom - fh.descent - 5.0f; 268 269 SetHighColor(tint_color(no_tint, B_DISABLED_LABEL_TINT)); 270 SetLowColor(lighten2); 271 DrawString(Label(), BPoint(x, y)); 272 } 273 } 274 //------------------------------------------------------------------------------ 275 void BButton::MouseDown(BPoint point) 276 { 277 if (!IsEnabled()) 278 { 279 BControl::MouseDown(point); 280 return; 281 } 282 283 SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY | B_SUSPEND_VIEW_FOCUS); 284 285 SetValue(B_CONTROL_ON); 286 SetTracking(true); 287 } 288 //------------------------------------------------------------------------------ 289 void BButton::AttachedToWindow() 290 { 291 BControl::AttachedToWindow(); 292 293 if (IsDefault()) 294 Window()->SetDefaultButton(this); 295 } 296 //------------------------------------------------------------------------------ 297 void BButton::KeyDown ( const char *bytes, int32 numBytes ) 298 { 299 if (numBytes == 1) 300 { 301 switch (bytes[0]) 302 { 303 case B_ENTER: 304 case B_SPACE: 305 SetValue(B_CONTROL_ON); 306 snooze(50000); 307 SetValue(B_CONTROL_OFF); 308 Invoke(); 309 break; 310 311 default: 312 BControl::KeyDown(bytes, numBytes); 313 } 314 } 315 else 316 BControl::KeyDown(bytes, numBytes); 317 } 318 //------------------------------------------------------------------------------ 319 void BButton::MakeDefault(bool flag) 320 { 321 if (flag == IsDefault()) 322 return; 323 324 fDrawAsDefault = flag; 325 BWindow *window = Window(); 326 327 if (flag) 328 { 329 ResizeBy(6.0f, 6.0f); 330 MoveBy(-3.0f, -3.0f); 331 332 if (window) 333 window->SetDefaultButton((BButton*)this); 334 } 335 else 336 { 337 ResizeBy(-6.0f, -6.0f); 338 MoveBy(3.0f, 3.0f); 339 340 if (window) 341 window->SetDefaultButton(NULL); 342 } 343 } 344 //------------------------------------------------------------------------------ 345 void BButton::SetLabel(const char *string) 346 { 347 BControl::SetLabel(string); 348 } 349 //------------------------------------------------------------------------------ 350 bool BButton::IsDefault() const 351 { 352 return fDrawAsDefault; 353 } 354 //------------------------------------------------------------------------------ 355 void BButton::MessageReceived(BMessage *message) 356 { 357 BControl::MessageReceived(message); 358 } 359 //------------------------------------------------------------------------------ 360 void BButton::WindowActivated(bool active) 361 { 362 BControl::WindowActivated(active); 363 } 364 //------------------------------------------------------------------------------ 365 void BButton::MouseMoved(BPoint point, uint32 transit, const BMessage *message) 366 { 367 if (IsEnabled() && IsTracking()) 368 { 369 if (transit == B_EXITED_VIEW) 370 SetValue(B_CONTROL_OFF); 371 else if (transit == B_ENTERED_VIEW) 372 SetValue(B_CONTROL_ON); 373 } 374 else 375 BControl::MouseMoved(point, transit, message); 376 } 377 //------------------------------------------------------------------------------ 378 void BButton::MouseUp(BPoint point) 379 { 380 if (IsEnabled() && IsTracking()) 381 { 382 if (Bounds().Contains(point)) 383 { 384 if ( Value() == B_CONTROL_ON) 385 { 386 SetValue(B_CONTROL_OFF); 387 Invoke(); 388 } 389 } 390 391 SetTracking(false); 392 } 393 } 394 //------------------------------------------------------------------------------ 395 void BButton::DetachedFromWindow() 396 { 397 BControl::DetachedFromWindow(); 398 } 399 //------------------------------------------------------------------------------ 400 void BButton::SetValue(int32 value) 401 { 402 BControl::SetValue(value); 403 } 404 //------------------------------------------------------------------------------ 405 void BButton::GetPreferredSize(float *width, float *height) 406 { 407 font_height fh; 408 GetFontHeight(&fh); 409 410 *height = 12.0f + (float)ceil(fh.ascent + fh.descent); 411 *width = 20.0f + (float)ceil(StringWidth(Label())); 412 413 if (*width < 75.0f) 414 *width = 75.0f; 415 416 if (fDrawAsDefault) 417 { 418 *width += 6.0f; 419 *height += 6.0f; 420 } 421 } 422 //------------------------------------------------------------------------------ 423 void BButton::ResizeToPreferred() 424 { 425 BControl::ResizeToPreferred(); 426 } 427 //------------------------------------------------------------------------------ 428 status_t BButton::Invoke(BMessage *message) 429 { 430 return BControl::Invoke(message); 431 } 432 //------------------------------------------------------------------------------ 433 void BButton::FrameMoved(BPoint newLocation) 434 { 435 BControl::FrameMoved(newLocation); 436 } 437 //------------------------------------------------------------------------------ 438 void BButton::FrameResized(float width, float height) 439 { 440 BControl::FrameResized(width, height); 441 } 442 //------------------------------------------------------------------------------ 443 void BButton::MakeFocus(bool focused) 444 { 445 BControl::MakeFocus(focused); 446 } 447 //------------------------------------------------------------------------------ 448 void BButton::AllAttached() 449 { 450 BControl::AllAttached(); 451 } 452 //------------------------------------------------------------------------------ 453 void BButton::AllDetached() 454 { 455 BControl::AllDetached(); 456 } 457 //------------------------------------------------------------------------------ 458 BHandler *BButton::ResolveSpecifier(BMessage *message, int32 index, 459 BMessage *specifier, int32 what, 460 const char *property) 461 { 462 return BControl::ResolveSpecifier(message, index, specifier, what, property); 463 } 464 //------------------------------------------------------------------------------ 465 status_t BButton::GetSupportedSuites(BMessage *message) 466 { 467 return BControl::GetSupportedSuites(message); 468 } 469 //------------------------------------------------------------------------------ 470 status_t BButton::Perform(perform_code d, void *arg) 471 { 472 return B_ERROR; 473 } 474 475 //------------------------------------------------------------------------------ 476 void BButton::_ReservedButton1() {} 477 void BButton::_ReservedButton2() {} 478 void BButton::_ReservedButton3() {} 479 //------------------------------------------------------------------------------ 480 BButton &BButton::operator=(const BButton &) 481 { 482 return *this; 483 } 484 //------------------------------------------------------------------------------ 485 BRect BButton::DrawDefault(BRect bounds, bool enabled) 486 { 487 rgb_color no_tint = ui_color(B_PANEL_BACKGROUND_COLOR), 488 lighten1 = tint_color(no_tint, B_LIGHTEN_1_TINT), 489 darken1 = tint_color(no_tint, B_DARKEN_1_TINT), 490 darken4 = tint_color(no_tint, B_DARKEN_4_TINT); 491 492 if (enabled) 493 { 494 // Dark border 495 BeginLineArray(4); 496 AddLine(BPoint(bounds.left, bounds.bottom - 1.0f), 497 BPoint(bounds.left, bounds.top + 1.0f), darken4); 498 AddLine(BPoint(bounds.left + 1.0f, bounds.top), 499 BPoint(bounds.right - 1.0f, bounds.top), darken4); 500 AddLine(BPoint(bounds.right, bounds.top + 1.0f), 501 BPoint(bounds.right, bounds.bottom - 1.0f), darken4); 502 AddLine(BPoint(bounds.left + 1.0f, bounds.bottom), 503 BPoint(bounds.right - 1.0f, bounds.bottom), darken4); 504 EndLineArray(); 505 506 bounds.InsetBy(1.0f, 1.0f); 507 508 // Bevel 509 SetHighColor(darken1); 510 StrokeRect(bounds); 511 512 bounds.InsetBy(1.0f, 1.0f); 513 514 // Filling 515 SetHighColor(lighten1); 516 FillRect(bounds); 517 518 bounds.InsetBy(2.0f, 2.0f); 519 } 520 else 521 { 522 // Dark border 523 BeginLineArray(4); 524 AddLine(BPoint(bounds.left, bounds.bottom - 1.0f), 525 BPoint(bounds.left, bounds.top + 1.0f), darken1); 526 AddLine(BPoint(bounds.left + 1.0f, bounds.top), 527 BPoint(bounds.right - 1.0f, bounds.top), darken1); 528 AddLine(BPoint(bounds.right, bounds.top + 1.0f), 529 BPoint(bounds.right, bounds.bottom - 1.0f), darken1); 530 AddLine(BPoint(bounds.left + 1.0f, bounds.bottom), 531 BPoint(bounds.right - 1.0f, bounds.bottom), darken1); 532 EndLineArray(); 533 534 bounds.InsetBy(1.0f, 1.0f); 535 536 // Filling 537 SetHighColor(lighten1); 538 FillRect(bounds); 539 540 bounds.InsetBy(3.0f, 3.0f); 541 } 542 543 return bounds; 544 } 545 //------------------------------------------------------------------------------ 546 status_t BButton::Execute() 547 { 548 // TODO: Is there a use for this? Maybe visual feedback happens here? 549 return Invoke(); 550 } 551 //------------------------------------------------------------------------------ 552 553 /* 554 * $Log $ 555 * 556 * $Id $ 557 * 558 */ 559