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