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 BPoint 400 BAlert::AlertPosition(float width, float height) 401 { 402 BPoint result(100, 100); 403 404 BWindow* window = 405 dynamic_cast<BWindow*>(BLooper::LooperForThread(find_thread(NULL))); 406 407 BScreen screen(window); 408 BRect screenFrame(0, 0, 640, 480); 409 if (screen.IsValid()) 410 screenFrame = screen.Frame(); 411 412 // Horizontally, we're smack in the middle 413 result.x = screenFrame.left + (screenFrame.Width() / 2.0) - (width / 2.0); 414 415 // This is probably sooo wrong, but it looks right on 1024 x 768 416 result.y = screenFrame.top + (screenFrame.Height() / 4.0) - ceil(height / 3.0); 417 418 return result; 419 } 420 421 422 status_t 423 BAlert::Perform(perform_code code, void* _data) 424 { 425 switch (code) { 426 case PERFORM_CODE_SET_LAYOUT: 427 { 428 perform_data_set_layout* data = (perform_data_set_layout*)_data; 429 BAlert::SetLayout(data->layout); 430 return B_OK; 431 } 432 } 433 434 return BWindow::Perform(code, _data); 435 } 436 437 438 void BAlert::_ReservedAlert1() {} 439 void BAlert::_ReservedAlert2() {} 440 void BAlert::_ReservedAlert3() {} 441 442 443 void 444 BAlert::_InitObject(const char* text, const char* button0, const char* button1, 445 const char* button2, button_width buttonWidth, button_spacing spacing, 446 alert_type type) 447 { 448 fInvoker = NULL; 449 fAlertSem = -1; 450 fAlertValue = -1; 451 fButtons[0] = fButtons[1] = fButtons[2] = NULL; 452 fTextView = NULL; 453 fKeys[0] = fKeys[1] = fKeys[2] = 0; 454 fMsgType = type; 455 fButtonWidth = buttonWidth; 456 457 // Set up the "_master_" view 458 TAlertView* view = new(std::nothrow) TAlertView(Bounds()); 459 if (view == NULL) 460 return; 461 462 AddChild(view); 463 view->SetBitmap(_InitIcon()); 464 465 // Must have at least one button 466 if (button0 == NULL) { 467 debugger("BAlert's must have at least one button."); 468 button0 = ""; 469 } 470 471 // Set up the buttons 472 473 int32 buttonCount = 1; 474 view->AddChild(fButtons[0] = _CreateButton(0, button0)); 475 476 if (button1 != NULL) { 477 view->AddChild(fButtons[1] = _CreateButton(1, button1)); 478 buttonCount++; 479 } 480 481 if (button2 != NULL) { 482 view->AddChild(fButtons[2] = _CreateButton(2, button2)); 483 buttonCount++; 484 } 485 486 // Find the widest button only if the widest value needs to be known. 487 488 if (fButtonWidth == B_WIDTH_FROM_WIDEST) { 489 float maxWidth = 0; 490 for (int i = 0; i < buttonCount; i++) { 491 float width = fButtons[i]->Bounds().Width(); 492 if (width > maxWidth) 493 maxWidth = width; 494 } 495 496 // resize buttons 497 for (int i = 0; i < buttonCount; i++) { 498 fButtons[i]->ResizeTo(maxWidth, fButtons[i]->Bounds().Height()); 499 } 500 } 501 502 float defaultButtonFrameWidth = -fButtons[buttonCount - 1]->Bounds().Width() / 2.0f; 503 SetDefaultButton(fButtons[buttonCount - 1]); 504 defaultButtonFrameWidth += fButtons[buttonCount - 1]->Bounds().Width() / 2.0f; 505 506 // Layout buttons 507 508 float fontFactor = be_plain_font->Size() / 11.0f; 509 510 for (int i = buttonCount - 1; i >= 0; --i) { 511 float x = -fButtons[i]->Bounds().Width(); 512 if (i + 1 == buttonCount) 513 x += Bounds().right - kRightOffset + defaultButtonFrameWidth; 514 else 515 x += fButtons[i + 1]->Frame().left - kButtonSpacing; 516 517 if (buttonCount > 1 && i == 0 && spacing == B_OFFSET_SPACING) 518 x -= kButtonOffsetSpacing * fontFactor; 519 520 fButtons[i]->MoveTo(x, fButtons[i]->Frame().top); 521 } 522 523 // Adjust the window's width, if necessary 524 525 int32 iconLayoutScale = icon_layout_scale(); 526 float totalWidth = kRightOffset + fButtons[buttonCount - 1]->Frame().right 527 - defaultButtonFrameWidth - fButtons[0]->Frame().left; 528 if (view->Bitmap()) { 529 totalWidth += (kIconStripeWidth + kWindowIconOffset) * iconLayoutScale; 530 } else 531 totalWidth += kWindowMinOffset; 532 533 float width = (spacing == B_OFFSET_SPACING 534 ? kWindowOffsetMinWidth : kWindowMinWidth) * fontFactor; 535 536 ResizeTo(max_c(totalWidth, width), Bounds().Height()); 537 538 // Set up the text view 539 540 BRect textViewRect(kLeftOffset, kTopOffset, 541 Bounds().right - kRightOffset, 542 fButtons[0]->Frame().top - kTextButtonOffset); 543 if (view->Bitmap()) 544 textViewRect.left = (kWindowIconOffset 545 + kIconStripeWidth) * iconLayoutScale - 2; 546 547 fTextView = new(std::nothrow) BTextView(textViewRect, "_tv_", 548 textViewRect.OffsetByCopy(B_ORIGIN), 549 B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW); 550 if (fTextView == NULL) 551 return; 552 553 fTextView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 554 rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR); 555 fTextView->SetFontAndColor(be_plain_font, B_FONT_ALL, &textColor); 556 fTextView->SetText(text, strlen(text)); 557 fTextView->MakeEditable(false); 558 fTextView->MakeSelectable(false); 559 fTextView->SetWordWrap(true); 560 view->AddChild(fTextView); 561 562 // Now resize the TextView vertically so that all the text is visible 563 float textHeight = fTextView->TextHeight(0, fTextView->CountLines()); 564 textViewRect.OffsetTo(0, 0); 565 textHeight -= textViewRect.Height(); 566 ResizeBy(0, textHeight); 567 fTextView->ResizeBy(0, textHeight); 568 textViewRect.bottom += textHeight; 569 fTextView->SetTextRect(textViewRect); 570 571 AddCommonFilter(new(std::nothrow) _BAlertFilter_(this)); 572 573 MoveTo(AlertPosition(Frame().Width(), Frame().Height())); 574 } 575 576 577 BBitmap* 578 BAlert::_InitIcon() 579 { 580 // Save the desired alert type and set it to "empty" until 581 // loading the icon was successful 582 alert_type alertType = fMsgType; 583 fMsgType = B_EMPTY_ALERT; 584 585 // After a bit of a search, I found the icons in app_server. =P 586 BBitmap* icon = NULL; 587 BPath path; 588 status_t status = find_directory(B_BEOS_SERVERS_DIRECTORY, &path); 589 if (status < B_OK) { 590 FTRACE((stderr, "BAlert::_InitIcon() - find_directory failed: %s\n", 591 strerror(status))); 592 return NULL; 593 } 594 595 path.Append("app_server"); 596 BFile file; 597 status = file.SetTo(path.Path(), B_READ_ONLY); 598 if (status < B_OK) { 599 FTRACE((stderr, "BAlert::_InitIcon() - BFile init failed: %s\n", 600 strerror(status))); 601 return NULL; 602 } 603 604 BResources resources; 605 status = resources.SetTo(&file); 606 if (status < B_OK) { 607 FTRACE((stderr, "BAlert::_InitIcon() - BResources init failed: %s\n", 608 strerror(status))); 609 return NULL; 610 } 611 612 // Which icon are we trying to load? 613 const char* iconName = ""; // Don't want any seg faults 614 switch (alertType) { 615 case B_INFO_ALERT: 616 iconName = "info"; 617 break; 618 case B_IDEA_ALERT: 619 iconName = "idea"; 620 break; 621 case B_WARNING_ALERT: 622 iconName = "warn"; 623 break; 624 case B_STOP_ALERT: 625 iconName = "stop"; 626 break; 627 628 default: 629 // Alert type is either invalid or B_EMPTY_ALERT; 630 // either way, we're not going to load an icon 631 return NULL; 632 } 633 634 int32 iconSize = 32 * icon_layout_scale(); 635 // Allocate the icon bitmap 636 icon = new(std::nothrow) BBitmap(BRect(0, 0, iconSize - 1, iconSize - 1), 637 0, B_RGBA32); 638 if (icon == NULL || icon->InitCheck() < B_OK) { 639 FTRACE((stderr, "BAlert::_InitIcon() - No memory for bitmap\n")); 640 delete icon; 641 return NULL; 642 } 643 644 // Load the raw icon data 645 size_t size = 0; 646 const uint8* rawIcon; 647 648 #ifdef __HAIKU__ 649 // Try to load vector icon 650 rawIcon = (const uint8*)resources.LoadResource(B_VECTOR_ICON_TYPE, 651 iconName, &size); 652 if (rawIcon != NULL 653 && BIconUtils::GetVectorIcon(rawIcon, size, icon) == B_OK) { 654 // We have an icon, restore the saved alert type 655 fMsgType = alertType; 656 return icon; 657 } 658 #endif 659 660 // Fall back to bitmap icon 661 rawIcon = (const uint8*)resources.LoadResource(B_LARGE_ICON_TYPE, 662 iconName, &size); 663 if (rawIcon == NULL) { 664 FTRACE((stderr, "BAlert::_InitIcon() - Icon resource not found\n")); 665 delete icon; 666 return NULL; 667 } 668 669 // Handle color space conversion 670 #ifdef __HAIKU__ 671 if (icon->ColorSpace() != B_CMAP8) { 672 BIconUtils::ConvertFromCMAP8(rawIcon, iconSize, iconSize, 673 iconSize, icon); 674 } 675 #else 676 icon->SetBits(rawIcon, iconSize, 0, B_CMAP8); 677 #endif 678 679 // We have an icon, restore the saved alert type 680 fMsgType = alertType; 681 682 return icon; 683 } 684 685 686 BButton* 687 BAlert::_CreateButton(int32 which, const char* label) 688 { 689 BMessage* message = new BMessage(kAlertButtonMsg); 690 if (message == NULL) 691 return NULL; 692 693 message->AddInt32("which", which); 694 695 BRect rect; 696 rect.top = Bounds().bottom - kBottomOffset; 697 rect.bottom = rect.top; 698 699 char name[32]; 700 snprintf(name, sizeof(name), "_b%ld_", which); 701 702 BButton* button = new(std::nothrow) BButton(rect, name, label, message, 703 B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); 704 if (button == NULL) 705 return NULL; 706 707 float width, height; 708 button->GetPreferredSize(&width, &height); 709 710 if (fButtonWidth == B_WIDTH_AS_USUAL) { 711 float fontFactor = be_plain_font->Size() / 11.0f; 712 width = max_c(width, kButtonUsualWidth * fontFactor); 713 } 714 715 button->ResizeTo(width, height); 716 button->MoveBy(0.0f, -height); 717 return button; 718 } 719 720 721 // #pragma mark - TAlertView 722 723 724 TAlertView::TAlertView(BRect frame) 725 : BView(frame, "TAlertView", B_FOLLOW_ALL_SIDES, B_WILL_DRAW), 726 fIconBitmap(NULL) 727 { 728 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 729 } 730 731 732 TAlertView::TAlertView(BMessage* archive) 733 : BView(archive), 734 fIconBitmap(NULL) 735 { 736 } 737 738 739 TAlertView::~TAlertView() 740 { 741 delete fIconBitmap; 742 } 743 744 745 TAlertView* 746 TAlertView::Instantiate(BMessage* archive) 747 { 748 if (!validate_instantiation(archive, "TAlertView")) 749 return NULL; 750 751 return new(std::nothrow) TAlertView(archive); 752 } 753 754 755 status_t 756 TAlertView::Archive(BMessage* archive, bool deep) const 757 { 758 return BView::Archive(archive, deep); 759 } 760 761 762 void 763 TAlertView::Draw(BRect updateRect) 764 { 765 if (!fIconBitmap) 766 return; 767 768 // Here's the fun stuff 769 BRect stripeRect = Bounds(); 770 int32 iconLayoutScale = icon_layout_scale(); 771 stripeRect.right = kIconStripeWidth * iconLayoutScale; 772 SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT)); 773 FillRect(stripeRect); 774 775 SetDrawingMode(B_OP_ALPHA); 776 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 777 DrawBitmapAsync(fIconBitmap, BPoint(18 * iconLayoutScale, 778 6 * iconLayoutScale)); 779 780 } 781 782 783 //------------------------------------------------------------------------------ 784 // #pragma mark - _BAlertFilter_ 785 786 787 _BAlertFilter_::_BAlertFilter_(BAlert* alert) 788 : BMessageFilter(B_KEY_DOWN), 789 fAlert(alert) 790 { 791 } 792 793 794 _BAlertFilter_::~_BAlertFilter_() 795 { 796 } 797 798 799 filter_result 800 _BAlertFilter_::Filter(BMessage* msg, BHandler** target) 801 { 802 if (msg->what == B_KEY_DOWN) { 803 char byte; 804 if (msg->FindInt8("byte", (int8*)&byte) == B_OK) { 805 for (int i = 0; i < 3; ++i) { 806 if (byte == fAlert->Shortcut(i) && fAlert->ButtonAt(i)) { 807 char space = ' '; 808 fAlert->ButtonAt(i)->KeyDown(&space, 1); 809 810 return B_SKIP_MESSAGE; 811 } 812 } 813 } 814 } 815 816 return B_DISPATCH_MESSAGE; 817 } 818 819