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: Alert.cpp 23 // Author: Erik Jaesler (erik@cgsoftware.com) 24 // Description: BAlert displays a modal alert window. 25 //------------------------------------------------------------------------------ 26 27 // Standard Includes ----------------------------------------------------------- 28 #include <string.h> 29 30 // System Includes ------------------------------------------------------------- 31 #include <Invoker.h> 32 #include <Looper.h> 33 #include <Message.h> 34 #include <MessageFilter.h> 35 36 #include <Alert.h> 37 #include <Bitmap.h> 38 #include <Button.h> 39 #include <Screen.h> 40 #include <TextView.h> 41 #include <View.h> 42 43 #include <File.h> 44 #include <FindDirectory.h> 45 #include <Path.h> 46 #include <Resources.h> 47 #include <Beep.h> 48 49 #include <Autolock.h> 50 51 //#define DEBUG_ALERT 52 #ifdef DEBUG_ALERT 53 #define FTRACE(x) fprintf(x) 54 #else 55 #define FTRACE(x) /* nothing */ 56 #endif 57 58 // Default size of the Alert window. 59 #define DEFAULT_RECT BRect(0, 0, 310, 75) 60 #define max(LHS, RHS) ((LHS) > (RHS) ? (LHS) : (RHS)) 61 62 // Globals --------------------------------------------------------------------- 63 static const unsigned int kAlertButtonMsg = 'ALTB'; 64 static const int kSemTimeOut = 50000; 65 66 static const int kButtonBottomOffset = 9; 67 static const int kDefButtonBottomOffset = 6; 68 static const int kButtonRightOffset = 6; 69 static const int kButtonSpaceOffset = 6; 70 static const int kButtonOffsetSpaceOffset = 26; 71 static const int kButtonMinOffsetSpaceOffset = kButtonOffsetSpaceOffset / 2; 72 static const int kButtonLeftOffset = 62; 73 static const int kButtonUsualWidth = 75; 74 75 static const int kWindowIconOffset = 27; 76 static const int kWindowMinWidth = 310; 77 static const int kWindowMinOffset = 12; 78 static const int kWindowOffsetMinWidth = 335; 79 80 static const int kIconStripeWidth = 30; 81 82 static const int kTextLeftOffset = 10; 83 static const int kTextIconOffset = kWindowIconOffset + kIconStripeWidth - 2; 84 static const int kTextTopOffset = 6; 85 static const int kTextRightOffset = 10; 86 static const int kTextBottomOffset = 45; 87 88 //------------------------------------------------------------------------------ 89 class TAlertView : public BView { 90 public: 91 TAlertView(BRect frame); 92 TAlertView(BMessage* archive); 93 ~TAlertView(); 94 95 static TAlertView* Instantiate(BMessage* archive); 96 status_t Archive(BMessage* archive, bool deep = true); 97 98 virtual void Draw(BRect updateRect); 99 100 // These functions (or something analogous) are missing from libbe.so's 101 // dump. I can only assume that the bitmap is a public var in the 102 // original implementation -- or BAlert is a friend of TAlertView. 103 // Neither one is necessary, since I can just add these. 104 void SetBitmap(BBitmap* Icon) { fIconBitmap = Icon; } 105 BBitmap* Bitmap() { return fIconBitmap; } 106 107 private: 108 BBitmap* fIconBitmap; 109 }; 110 111 //------------------------------------------------------------------------------ 112 // I'm making a guess based on the name and TextEntryAlert's implementation that 113 // this is a BMessageFilter. I'm not sure, but I think I actually prefer how 114 // TextEntryAlert does it, but there are clearly no message filtering functions 115 // on BAlert so here we go. 116 class _BAlertFilter_ : public BMessageFilter { 117 public: 118 _BAlertFilter_(BAlert* Alert); 119 ~_BAlertFilter_(); 120 121 virtual filter_result Filter(BMessage* msg, BHandler** target); 122 123 private: 124 BAlert* fAlert; 125 }; 126 127 128 static float 129 width_from_label(BButton *button) 130 { 131 // BButton::GetPreferredSize() does not return the minimum width 132 // required to fit the label. Thus, the width is computed here. 133 return button->StringWidth(button->Label()) + 20.0f; 134 } 135 136 137 // #pragma mark - BAlert 138 139 140 BAlert::BAlert(const char *title, const char *text, const char *button1, 141 const char *button2, const char *button3, button_width width, 142 alert_type type) 143 : BWindow(DEFAULT_RECT, title, B_MODAL_WINDOW, 144 B_NOT_CLOSABLE | B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS) 145 { 146 InitObject(text, button1, button2, button3, width, B_EVEN_SPACING, type); 147 } 148 149 150 BAlert::BAlert(const char *title, const char *text, const char *button1, 151 const char *button2, const char *button3, button_width width, 152 button_spacing spacing, alert_type type) 153 : BWindow(DEFAULT_RECT, title, B_MODAL_WINDOW, 154 B_NOT_CLOSABLE | B_NOT_RESIZABLE | B_ASYNCHRONOUS_CONTROLS) 155 { 156 InitObject(text, button1, button2, button3, width, spacing, type); 157 } 158 159 160 BAlert::~BAlert() 161 { 162 // Probably not necessary, but it makes me feel better. 163 if (fAlertSem >= B_OK) 164 delete_sem(fAlertSem); 165 } 166 167 168 BAlert::BAlert(BMessage* data) 169 : BWindow(data) 170 { 171 fInvoker = NULL; 172 fAlertSem = -1; 173 fAlertVal = -1; 174 175 fTextView = (BTextView*)FindView("_tv_"); 176 177 fButtons[0] = (BButton*)FindView("_b0_"); 178 fButtons[1] = (BButton*)FindView("_b1_"); 179 fButtons[2] = (BButton*)FindView("_b2_"); 180 181 if (fButtons[2]) 182 SetDefaultButton(fButtons[2]); 183 else if (fButtons[1]) 184 SetDefaultButton(fButtons[1]); 185 else if (fButtons[0]) 186 SetDefaultButton(fButtons[0]); 187 188 TAlertView* master = (TAlertView*)FindView("_master_"); 189 if (master) 190 master->SetBitmap(InitIcon()); 191 192 // Get keys 193 char key; 194 for (int32 i = 0; i < 3; ++i) { 195 if (data->FindInt8("_but_key", i, (int8*)&key) == B_OK) 196 fKeys[i] = key; 197 } 198 199 int32 temp; 200 // Get alert type 201 if (data->FindInt32("_atype", &temp) == B_OK) 202 fMsgType = (alert_type)temp; 203 204 // Get button width 205 if (data->FindInt32("_but_width", &temp) == B_OK) 206 fButtonWidth = (button_width)temp; 207 208 AddCommonFilter(new _BAlertFilter_(this)); 209 } 210 211 212 BArchivable* 213 BAlert::Instantiate(BMessage* data) 214 { 215 if (!validate_instantiation(data, "BAlert")) 216 return NULL; 217 218 return new BAlert(data); 219 } 220 221 222 status_t 223 BAlert::Archive(BMessage* data, bool deep) const 224 { 225 BWindow::Archive(data, deep); 226 227 // Stow the text 228 data->AddString("_text", fTextView->Text()); 229 230 // Stow the alert type 231 data->AddInt32("_atype", fMsgType); 232 233 // Stow the button width 234 data->AddInt32("_but_width", fButtonWidth); 235 236 // Stow the shortcut keys 237 if (fKeys[0] || fKeys[1] || fKeys[2]) { 238 // If we have any to save, we must save something for everyone so it 239 // doesn't get confusing on the unarchive. 240 data->AddInt8("_but_key", fKeys[0]); 241 data->AddInt8("_but_key", fKeys[1]); 242 data->AddInt8("_but_key", fKeys[2]); 243 } 244 245 return B_OK; 246 } 247 248 249 void 250 BAlert::SetShortcut(int32 index, char key) 251 { 252 if (index >= 0 && index < 3) 253 fKeys[index] = key; 254 } 255 256 257 char 258 BAlert::Shortcut(int32 index) const 259 { 260 if (index >= 0 && index < 3) 261 return fKeys[index]; 262 263 return 0; 264 } 265 266 267 int32 268 BAlert::Go() 269 { 270 fAlertSem = create_sem(0, "AlertSem"); 271 if (fAlertSem < B_OK) { 272 Quit(); 273 return -1; 274 } 275 276 // Get the originating window, if it exists 277 BWindow* window = 278 dynamic_cast<BWindow*>(BLooper::LooperForThread(find_thread(NULL))); 279 280 Show(); 281 282 // Heavily modified from TextEntryAlert code; the original didn't let the 283 // blocked window ever draw. 284 if (window) { 285 status_t err; 286 for (;;) { 287 do { 288 err = acquire_sem_etc(fAlertSem, 1, B_RELATIVE_TIMEOUT, 289 kSemTimeOut); 290 // We've (probably) had our time slice taken away from us 291 } while (err == B_INTERRUPTED); 292 293 if (err == B_BAD_SEM_ID) { 294 // Semaphore was finally nuked in MessageReceived 295 break; 296 } 297 window->UpdateIfNeeded(); 298 } 299 } else { 300 // No window to update, so just hang out until we're done. 301 while (acquire_sem(fAlertSem) == B_INTERRUPTED) { 302 } 303 } 304 305 // Have to cache the value since we delete on Quit() 306 int32 value = fAlertVal; 307 if (Lock()) 308 Quit(); 309 310 return value; 311 } 312 313 314 status_t 315 BAlert::Go(BInvoker* invoker) 316 { 317 fInvoker = invoker; 318 Show(); 319 return B_OK; 320 } 321 322 323 void 324 BAlert::MessageReceived(BMessage* msg) 325 { 326 if (msg->what != kAlertButtonMsg) 327 return BWindow::MessageReceived(msg); 328 329 int32 which; 330 if (msg->FindInt32("which", &which) == B_OK) { 331 if (fAlertSem < B_OK) { 332 // Semaphore hasn't been created; we're running asynchronous 333 if (fInvoker) { 334 BMessage* out = fInvoker->Message(); 335 if (out && (out->ReplaceInt32("which", which) == B_OK 336 || out->AddInt32("which", which) == B_OK)) 337 fInvoker->Invoke(); 338 } 339 PostMessage(B_QUIT_REQUESTED); 340 } else { 341 // Created semaphore means were running synchronously 342 fAlertVal = which; 343 344 // TextAlertVar does release_sem() below, and then sets the 345 // member var. That doesn't make much sense to me, since we 346 // want to be able to clean up at some point. Better to just 347 // nuke the semaphore now; we don't need it any more and this 348 // lets synchronous Go() continue just as well. 349 delete_sem(fAlertSem); 350 fAlertSem = -1; 351 } 352 } 353 } 354 355 356 void 357 BAlert::FrameResized(float newWidth, float newHeight) 358 { 359 BWindow::FrameResized(newWidth, newHeight); 360 } 361 362 363 BButton* 364 BAlert::ButtonAt(int32 index) const 365 { 366 if (index >= 0 && index < 3) 367 return fButtons[index]; 368 369 return NULL; 370 } 371 372 373 BTextView* 374 BAlert::TextView() const 375 { 376 return fTextView; 377 } 378 379 380 BHandler* 381 BAlert::ResolveSpecifier(BMessage* msg, int32 index, 382 BMessage* specifier, int32 form, const char* property) 383 { 384 return BWindow::ResolveSpecifier(msg, index, specifier, form, property); 385 } 386 387 388 status_t 389 BAlert::GetSupportedSuites(BMessage* data) 390 { 391 return BWindow::GetSupportedSuites(data); 392 } 393 394 395 void 396 BAlert::DispatchMessage(BMessage* msg, BHandler* handler) 397 { 398 BWindow::DispatchMessage(msg, handler); 399 } 400 401 402 void 403 BAlert::Quit() 404 { 405 BWindow::Quit(); 406 } 407 408 409 bool 410 BAlert::QuitRequested() 411 { 412 return BWindow::QuitRequested(); 413 } 414 415 416 BPoint 417 BAlert::AlertPosition(float width, float height) 418 { 419 BPoint result(100, 100); 420 421 BWindow* window = 422 dynamic_cast<BWindow*>(BLooper::LooperForThread(find_thread(NULL))); 423 424 BScreen screen(window); 425 BRect screenRect(0, 0, 640, 480); 426 if (screen.IsValid()) 427 screenRect = screen.Frame(); 428 429 // Horizontally, we're smack in the middle 430 result.x = (screenRect.Width() / 2.0) - (width / 2.0); 431 432 // This is probably sooo wrong, but it looks right on 1024 x 768 433 result.y = (screenRect.Height() / 4.0) - ceil(height / 3.0); 434 435 return result; 436 } 437 438 439 status_t 440 BAlert::Perform(perform_code d, void* arg) 441 { 442 return BWindow::Perform(d, arg); 443 } 444 445 446 void BAlert::_ReservedAlert1() {} 447 void BAlert::_ReservedAlert2() {} 448 void BAlert::_ReservedAlert3() {} 449 450 451 void 452 BAlert::InitObject(const char* text, const char* button0, const char* button1, 453 const char* button2, button_width width, button_spacing spacing, 454 alert_type type) 455 { 456 fInvoker = NULL; 457 fAlertSem = -1; 458 fAlertVal = -1; 459 fButtons[0] = fButtons[1] = fButtons[2] = NULL; 460 fTextView = NULL; 461 fKeys[0] = fKeys[1] = fKeys[2] = 0; 462 fMsgType = type; 463 fButtonWidth = width; 464 465 // Set up the "_master_" view 466 TAlertView* masterView = new TAlertView(Bounds()); 467 AddChild(masterView); 468 masterView->SetBitmap(InitIcon()); 469 470 // Must have at least one button 471 if (button0 == NULL) { 472 debugger("BAlert's must have at least one button."); 473 button0 = ""; 474 } 475 476 BMessage ProtoMsg(kAlertButtonMsg); 477 ProtoMsg.AddInt32("which", 0); 478 // Set up the buttons 479 int buttonCount = 0; 480 fButtons[buttonCount] = new BButton(BRect(0, 0, 0, 0), "_b0_", button0, 481 new BMessage(ProtoMsg), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); 482 masterView->AddChild(fButtons[buttonCount]); 483 ++buttonCount; 484 485 if (button1) { 486 ProtoMsg.ReplaceInt32("which", 1); 487 fButtons[buttonCount] = new BButton(BRect(0, 0, 0, 0), "_b1_", button1, 488 new BMessage(ProtoMsg), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); 489 490 masterView->AddChild(fButtons[buttonCount]); 491 ++buttonCount; 492 } 493 if (button2) { 494 ProtoMsg.ReplaceInt32("which", 2); 495 fButtons[buttonCount] = new BButton(BRect(0, 0, 0, 0), "_b2_", button2, 496 new BMessage(ProtoMsg), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); 497 498 masterView->AddChild(fButtons[buttonCount]); 499 ++buttonCount; 500 } 501 502 SetDefaultButton(fButtons[buttonCount - 1]); 503 504 // Find the widest button only if the widest value needs to be known. 505 float maxWidth = 0; 506 if (fButtonWidth == B_WIDTH_FROM_WIDEST) { 507 for (int i = 0; i < buttonCount; ++i) { 508 float temp = width_from_label(fButtons[i]); 509 maxWidth = max(maxWidth, temp); 510 } 511 } 512 513 for (int i = buttonCount - 1; i >= 0; --i) { 514 // Determine the button's size 515 float buttonWidth = 0, buttonHeight = 0; 516 fButtons[i]->GetPreferredSize(&buttonWidth, &buttonHeight); 517 if (fButtonWidth == B_WIDTH_FROM_WIDEST) 518 buttonWidth = maxWidth; 519 else if (fButtonWidth == B_WIDTH_FROM_LABEL) 520 buttonWidth = width_from_label(fButtons[i]); 521 else // B_WIDTH_AS_USUAL 522 buttonWidth = max(buttonWidth, kButtonUsualWidth); 523 fButtons[i]->ResizeTo(buttonWidth, buttonHeight); 524 525 // Determine the button's placement 526 float buttonX, buttonY; 527 buttonY = Bounds().bottom - buttonHeight; 528 if (i == buttonCount - 1) { 529 // The right-most button 530 buttonX = Bounds().right - fButtons[i]->Frame().Width() - 531 kButtonRightOffset; 532 buttonY -= kDefButtonBottomOffset; 533 } else { 534 buttonX = fButtons[i + 1]->Frame().left - 535 fButtons[i]->Frame().Width() - kButtonSpaceOffset; 536 buttonY -= kButtonBottomOffset; 537 if (i == 0) { 538 if (spacing == B_OFFSET_SPACING) { 539 if (buttonCount == 3) 540 buttonX -= kButtonOffsetSpaceOffset; 541 else { 542 // If there are two buttons, the left wall of 543 // button0 needs to line up with the left wall 544 // of the TextView. 545 buttonX = (masterView->Bitmap()) ? 546 kTextIconOffset : kTextLeftOffset; 547 if (fButtons[i + 1]->Frame().left - 548 (buttonX + fButtons[i]->Frame().Width()) < 549 kButtonMinOffsetSpaceOffset) { 550 // Recompute buttonX using min offset space 551 // if using the current buttonX would not 552 // provide enough space or cause an overlap. 553 buttonX = fButtons[i + 1]->Frame().left - 554 fButtons[i]->Frame().Width() - 555 kButtonMinOffsetSpaceOffset; 556 } 557 } 558 } else if (buttonCount == 3) 559 buttonX -= 3; 560 } 561 } 562 fButtons[i]->MoveTo(buttonX, buttonY); 563 } // for (int i = buttonCount - 1; i >= 0; --i) 564 565 // Adjust the window's width, if necessary 566 float totalWidth = kButtonRightOffset; 567 totalWidth += fButtons[buttonCount - 1]->Frame().right - 568 fButtons[0]->Frame().left; 569 if (masterView->Bitmap()) 570 totalWidth += kIconStripeWidth + kWindowIconOffset; 571 else 572 totalWidth += kWindowMinOffset; 573 574 if (spacing == B_OFFSET_SPACING) { 575 totalWidth -= 2; 576 if (buttonCount == 3) 577 totalWidth = max(kWindowOffsetMinWidth, totalWidth); 578 else 579 totalWidth = max(kWindowMinWidth, totalWidth); 580 } else { 581 totalWidth += 5; 582 totalWidth = max(kWindowMinWidth, totalWidth); 583 } 584 ResizeTo(totalWidth, Bounds().Height()); 585 586 // Set up the text view 587 BRect TextViewRect(kTextLeftOffset, kTextTopOffset, 588 Bounds().right - kTextRightOffset, 589 Bounds().bottom - kTextBottomOffset); 590 if (masterView->Bitmap()) 591 TextViewRect.left = kTextIconOffset; 592 593 fTextView = new BTextView(TextViewRect, "_tv_", TextViewRect, 594 B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW); 595 masterView->AddChild(fTextView); 596 597 fTextView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 598 fTextView->SetText(text, strlen(text)); 599 fTextView->MakeEditable(false); 600 fTextView->MakeSelectable(false); 601 fTextView->SetWordWrap(true); 602 603 // Now resize the TextView vertically so that all the text is visible 604 float textHeight = fTextView->TextHeight(0, fTextView->CountLines()); 605 TextViewRect.OffsetTo(0, 0); 606 textHeight -= TextViewRect.Height(); 607 ResizeBy(0, textHeight); 608 fTextView->ResizeBy(0, textHeight); 609 TextViewRect.bottom += textHeight; 610 fTextView->SetTextRect(TextViewRect); 611 612 AddCommonFilter(new _BAlertFilter_(this)); 613 614 MoveTo(AlertPosition(Frame().Width(), Frame().Height())); 615 } 616 617 618 BBitmap* 619 BAlert::InitIcon() 620 { 621 // After a bit of a search, I found the icons in app_server. =P 622 BBitmap* icon = NULL; 623 BPath path; 624 status_t status = find_directory(B_BEOS_SERVERS_DIRECTORY, &path); 625 if (status >= B_OK) { 626 path.Append("app_server"); 627 BFile file; 628 status = file.SetTo(path.Path(), B_READ_ONLY); 629 if (status >= B_OK) { 630 BResources resources; 631 status = resources.SetTo(&file); 632 if (status >= B_OK) { 633 // Which icon are we trying to load? 634 const char* iconName = ""; // Don't want any seg faults 635 switch (fMsgType) { 636 case B_INFO_ALERT: 637 iconName = "info"; 638 break; 639 case B_IDEA_ALERT: 640 iconName = "idea"; 641 break; 642 case B_WARNING_ALERT: 643 iconName = "warn"; 644 break; 645 case B_STOP_ALERT: 646 iconName = "stop"; 647 break; 648 649 default: 650 // Alert type is either invalid or B_EMPTY_ALERT; 651 // either way, we're not going to load an icon 652 return NULL; 653 } 654 655 // Load the raw icon data 656 size_t size; 657 const void* rawIcon = 658 resources.LoadResource('ICON', iconName, &size); 659 660 if (rawIcon) { 661 // Now build the bitmap 662 icon = new BBitmap(BRect(0, 0, 31, 31), 0, B_CMAP8); 663 icon->SetBits(rawIcon, size, 0, B_CMAP8); 664 } else { 665 FTRACE((stderr, "BAlert::InitIcon() - Icon resource not found\n")); 666 } 667 } else { 668 FTRACE((stderr, "BAlert::InitIcon() - BResources init failed: %s\n", strerror(status))); 669 } 670 } else { 671 FTRACE((stderr, "BAlert::InitIcon() - BFile init failed: %s\n", strerror(status))); 672 } 673 } else { 674 FTRACE((stderr, "BAlert::InitIcon() - find_directory failed: %s\n", strerror(status))); 675 } 676 677 if (!icon) { 678 // If there's no icon, it's an empty alert indeed. 679 fMsgType = B_EMPTY_ALERT; 680 } 681 682 return icon; 683 } 684 685 686 //------------------------------------------------------------------------------ 687 // #pragma mark - TAlertView 688 689 690 TAlertView::TAlertView(BRect frame) 691 : BView(frame, "TAlertView", B_FOLLOW_ALL_SIDES, B_WILL_DRAW), 692 fIconBitmap(NULL) 693 { 694 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 695 } 696 697 698 TAlertView::TAlertView(BMessage* archive) 699 : BView(archive), 700 fIconBitmap(NULL) 701 { 702 } 703 704 705 TAlertView::~TAlertView() 706 { 707 delete fIconBitmap; 708 } 709 710 711 TAlertView* 712 TAlertView::Instantiate(BMessage* archive) 713 { 714 if (!validate_instantiation(archive, "TAlertView")) 715 return NULL; 716 717 return new TAlertView(archive); 718 } 719 720 721 status_t 722 TAlertView::Archive(BMessage* archive, bool deep) 723 { 724 return BView::Archive(archive, deep); 725 } 726 727 728 void 729 TAlertView::Draw(BRect updateRect) 730 { 731 // Here's the fun stuff 732 if (fIconBitmap) { 733 BRect StripeRect = Bounds(); 734 StripeRect.right = kIconStripeWidth; 735 SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT)); 736 FillRect(StripeRect); 737 738 SetDrawingMode(B_OP_OVER); 739 DrawBitmapAsync(fIconBitmap, BPoint(18, 6)); 740 SetDrawingMode(B_OP_COPY); 741 } 742 } 743 744 745 //------------------------------------------------------------------------------ 746 // #pragma mark - _BAlertFilter_ 747 748 749 _BAlertFilter_::_BAlertFilter_(BAlert* Alert) 750 : BMessageFilter(B_KEY_DOWN), 751 fAlert(Alert) 752 { 753 } 754 755 756 _BAlertFilter_::~_BAlertFilter_() 757 { 758 } 759 760 761 filter_result 762 _BAlertFilter_::Filter(BMessage* msg, BHandler** target) 763 { 764 if (msg->what == B_KEY_DOWN) { 765 char byte; 766 if (msg->FindInt8("byte", (int8*)&byte) == B_OK) { 767 for (int i = 0; i < 3; ++i) { 768 if (byte == fAlert->Shortcut(i) && fAlert->ButtonAt(i)) { 769 char space = ' '; 770 fAlert->ButtonAt(i)->KeyDown(&space, 1); 771 772 return B_SKIP_MESSAGE; 773 } 774 } 775 } 776 } 777 778 return B_DISPATCH_MESSAGE; 779 } 780