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