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