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 // Default size of the Alert window. 57 #define DEFAULT_RECT BRect(0, 0, 310, 75) 58 #define max(LHS, RHS) ((LHS) > (RHS) ? (LHS) : (RHS)) 59 60 // Globals --------------------------------------------------------------------- 61 const unsigned int kAlertButtonMsg = 'ALTB'; 62 const int kSemTimeOut = 50000; 63 64 const int kButtonBottomOffset = 9; 65 const int kDefButtonBottomOffset = 6; 66 const int kButtonRightOffset = 6; 67 const int kButtonSpaceOffset = 6; 68 const int kButtonOffsetSpaceOffset = 26; 69 const int kButtonMinOffsetSpaceOffset = kButtonOffsetSpaceOffset / 2; 70 const int kButtonLeftOffset = 62; 71 const int kButtonUsualWidth = 75; 72 73 const int kWindowIconOffset = 27; 74 const int kWindowMinWidth = 310; 75 const int kWindowMinOffset = 12; 76 const int kWindowOffsetMinWidth = 335; 77 78 const int kIconStripeWidth = 30; 79 80 const int kTextLeftOffset = 10; 81 const int kTextIconOffset = kWindowIconOffset + kIconStripeWidth - 2; 82 const int kTextTopOffset = 6; 83 const int kTextRightOffset = 10; 84 const int kTextBottomOffset = 45; 85 86 //------------------------------------------------------------------------------ 87 class TAlertView : public BView 88 { 89 public: 90 TAlertView(BRect frame); 91 TAlertView(BMessage* archive); 92 ~TAlertView(); 93 94 static TAlertView* Instantiate(BMessage* archive); 95 status_t Archive(BMessage* archive, bool deep = true); 96 97 virtual void Draw(BRect updateRect); 98 99 // These functions (or something analogous) are missing from libbe.so's 100 // dump. I can only assume that the bitmap is a public var in the 101 // original implementation -- or BAlert is a friend of TAlertView. 102 // Neither one is necessary, since I can just add these. 103 void SetBitmap(BBitmap* Icon) { fIconBitmap = Icon; } 104 BBitmap* Bitmap() { return fIconBitmap; } 105 106 private: 107 BBitmap* fIconBitmap; 108 }; 109 //------------------------------------------------------------------------------ 110 // I'm making a guess based on the name and TextEntryAlert's implementation that 111 // this is a BMessageFilter. I'm not sure, but I think I actually prefer how 112 // TextEntryAlert does it, but there are clearly no message filtering functions 113 // on BAlert so here we go. 114 class _BAlertFilter_ : public BMessageFilter 115 { 116 public: 117 _BAlertFilter_(BAlert* Alert); 118 ~_BAlertFilter_(); 119 120 virtual filter_result Filter(BMessage* msg, BHandler** target); 121 122 private: 123 BAlert* fAlert; 124 }; 125 //------------------------------------------------------------------------------ 126 127 128 //------------------------------------------------------------------------------ 129 BAlert::BAlert(const char *title, const char *text, const char *button1, 130 const char *button2, const char *button3, button_width width, 131 alert_type type) 132 : BWindow(DEFAULT_RECT, title, B_MODAL_WINDOW, 133 B_NOT_CLOSABLE | B_NOT_RESIZABLE) 134 { 135 InitObject(text, button1, button2, button3, width, B_EVEN_SPACING, type); 136 } 137 //------------------------------------------------------------------------------ 138 BAlert::BAlert(const char *title, const char *text, const char *button1, 139 const char *button2, const char *button3, button_width width, 140 button_spacing spacing, alert_type type) 141 : BWindow(DEFAULT_RECT, title, B_MODAL_WINDOW, 142 B_NOT_CLOSABLE | B_NOT_RESIZABLE) 143 { 144 InitObject(text, button1, button2, button3, width, spacing, type); 145 } 146 //------------------------------------------------------------------------------ 147 BAlert::~BAlert() 148 { 149 // Probably not necessary, but it makes me feel better. 150 if (fAlertSem >= B_OK) 151 { 152 delete_sem(fAlertSem); 153 } 154 } 155 //------------------------------------------------------------------------------ 156 BAlert::BAlert(BMessage* data) 157 : BWindow(data) 158 { 159 BAutolock Autolock(this); 160 if (Autolock.IsLocked()) 161 { 162 fInvoker = NULL; 163 fAlertSem = -1; 164 fAlertVal = -1; 165 166 fTextView = (BTextView*)FindView("_tv_"); 167 168 fButtons[0] = (BButton*)FindView("_b0_"); 169 fButtons[1] = (BButton*)FindView("_b1_"); 170 fButtons[2] = (BButton*)FindView("_b2_"); 171 172 if (fButtons[2]) 173 SetDefaultButton(fButtons[2]); 174 else if (fButtons[1]) 175 SetDefaultButton(fButtons[1]); 176 else if (fButtons[0]) 177 SetDefaultButton(fButtons[0]); 178 179 TAlertView* Master = (TAlertView*)FindView("_master_"); 180 if (Master) 181 { 182 Master->SetBitmap(InitIcon()); 183 } 184 185 // Get keys 186 char key; 187 for (int32 i = 0; i < 3; ++i) 188 { 189 if (data->FindInt8("_but_key", i, (int8*)&key) == B_OK) 190 fKeys[i] = key; 191 } 192 193 int32 temp; 194 // Get alert type 195 if (data->FindInt32("_atype", &temp) == B_OK) 196 fMsgType = (alert_type)temp; 197 198 // Get button width 199 if (data->FindInt32("_but_width", &temp) == B_OK) 200 fButtonWidth = (button_width)temp; 201 202 AddCommonFilter(new _BAlertFilter_(this)); 203 } 204 } 205 //------------------------------------------------------------------------------ 206 BArchivable* BAlert::Instantiate(BMessage* data) 207 { 208 if (!validate_instantiation(data, "BAlert")) 209 { 210 return NULL; 211 } 212 213 return new BAlert(data); 214 } 215 //------------------------------------------------------------------------------ 216 status_t BAlert::Archive(BMessage* data, bool deep) const 217 { 218 BWindow::Archive(data, deep); 219 220 // Stow the text 221 data->AddString("_text", fTextView->Text()); 222 223 // Stow the alert type 224 data->AddInt32("_atype", fMsgType); 225 226 // Stow the button width 227 data->AddInt32("_but_width", fButtonWidth); 228 229 // Stow the shortcut keys 230 if (fKeys[0] || fKeys[1] || fKeys[2]) 231 { 232 // If we have any to save, we must save something for everyone so it 233 // doesn't get confusing on the unarchive. 234 data->AddInt8("_but_key", fKeys[0]); 235 data->AddInt8("_but_key", fKeys[1]); 236 data->AddInt8("_but_key", fKeys[2]); 237 } 238 239 return B_OK; 240 } 241 //------------------------------------------------------------------------------ 242 void BAlert::SetShortcut(int32 index, char key) 243 { 244 if (index >= 0 && index < 3) 245 fKeys[index] = key; 246 } 247 //------------------------------------------------------------------------------ 248 char BAlert::Shortcut(int32 index) const 249 { 250 if (index >= 0 && index < 3) 251 return fKeys[index]; 252 253 return 0; 254 } 255 //------------------------------------------------------------------------------ 256 int32 BAlert::Go() 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 BRect screenRect(0, 0, 640, 480); 420 if (Screen.IsValid()) 421 screenRect = Screen.Frame(); 422 423 // Horizontally, we're smack in the middle 424 result.x = (screenRect.Width() / 2.0) - (width / 2.0); 425 426 // This is probably sooo wrong, but it looks right on 1024 x 768 427 result.y = (screenRect.Height() / 4.0) - ceil(height / 3.0); 428 429 return result; 430 } 431 //------------------------------------------------------------------------------ 432 status_t BAlert::Perform(perform_code d, void* arg) 433 { 434 return BWindow::Perform(d, arg); 435 } 436 //------------------------------------------------------------------------------ 437 void BAlert::_ReservedAlert1() 438 { 439 ; 440 } 441 //------------------------------------------------------------------------------ 442 void BAlert::_ReservedAlert2() 443 { 444 ; 445 } 446 //------------------------------------------------------------------------------ 447 void BAlert::_ReservedAlert3() 448 { 449 ; 450 } 451 //------------------------------------------------------------------------------ 452 float width_from_label(BButton *button) 453 { 454 // BButton::GetPreferredSize() does not return the minimum width 455 // required to fit the label. Thus, the width is computed here. 456 return button->StringWidth(button->Label()) + 20.0f; 457 } 458 459 //------------------------------------------------------------------------------ 460 void 461 BAlert::InitObject(const char* text, const char* button0, const char* button1, 462 const char* button2, button_width width, button_spacing spacing, 463 alert_type type) 464 { 465 BAutolock Autolock(this); 466 if (!Autolock.IsLocked()) 467 // Bail out if a lock can't be acquired 468 return; 469 470 fInvoker = NULL; 471 fAlertSem = -1; 472 fAlertVal = -1; 473 fButtons[0] = fButtons[1] = fButtons[2] = NULL; 474 fTextView = NULL; 475 fKeys[0] = fKeys[1] = fKeys[2] = 0; 476 fMsgType = type; 477 fButtonWidth = width; 478 479 // Set up the "_master_" view 480 TAlertView* MasterView = new TAlertView(Bounds()); 481 AddChild(MasterView); 482 MasterView->SetBitmap(InitIcon()); 483 484 // Must have at least one button 485 if (button0 == NULL) { 486 debugger("BAlert's must have at least one button."); 487 button0 = ""; 488 } 489 490 BMessage ProtoMsg(kAlertButtonMsg); 491 ProtoMsg.AddInt32("which", 0); 492 // Set up the buttons 493 int buttonCount = 0; 494 fButtons[buttonCount] = new BButton(BRect(0, 0, 0, 0), "_b0_", button0, 495 new BMessage(ProtoMsg), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); 496 MasterView->AddChild(fButtons[buttonCount]); 497 ++buttonCount; 498 499 if (button1) { 500 ProtoMsg.ReplaceInt32("which", 1); 501 fButtons[buttonCount] = new BButton(BRect(0, 0, 0, 0), "_b1_", button1, 502 new BMessage(ProtoMsg), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); 503 504 MasterView->AddChild(fButtons[buttonCount]); 505 ++buttonCount; 506 } 507 if (button2) { 508 ProtoMsg.ReplaceInt32("which", 2); 509 fButtons[buttonCount] = new BButton(BRect(0, 0, 0, 0), "_b2_", button2, 510 new BMessage(ProtoMsg), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); 511 512 MasterView->AddChild(fButtons[buttonCount]); 513 ++buttonCount; 514 } 515 516 SetDefaultButton(fButtons[buttonCount - 1]); 517 518 // Find the widest button only if the widest value needs to be known. 519 float maxWidth = 0; 520 if (fButtonWidth == B_WIDTH_FROM_WIDEST) { 521 for (int i = 0; i < buttonCount; ++i) { 522 float temp = width_from_label(fButtons[i]); 523 maxWidth = max(maxWidth, temp); 524 } 525 } 526 527 for (int i = buttonCount - 1; i >= 0; --i) { 528 // Determine the button's size 529 float buttonWidth = 0, buttonHeight = 0; 530 fButtons[i]->GetPreferredSize(&buttonWidth, &buttonHeight); 531 if (fButtonWidth == B_WIDTH_FROM_WIDEST) 532 buttonWidth = maxWidth; 533 else if (fButtonWidth == B_WIDTH_FROM_LABEL) 534 buttonWidth = width_from_label(fButtons[i]); 535 else // B_WIDTH_AS_USUAL 536 buttonWidth = max(buttonWidth, kButtonUsualWidth); 537 fButtons[i]->ResizeTo(buttonWidth, buttonHeight); 538 539 // Determine the button's placement 540 float buttonX, buttonY; 541 buttonY = Bounds().bottom - buttonHeight; 542 if (i == buttonCount - 1) { 543 // The right-most button 544 buttonX = Bounds().right - fButtons[i]->Frame().Width() - 545 kButtonRightOffset; 546 buttonY -= kDefButtonBottomOffset; 547 } else { 548 buttonX = fButtons[i + 1]->Frame().left - 549 fButtons[i]->Frame().Width() - kButtonSpaceOffset; 550 buttonY -= kButtonBottomOffset; 551 if (i == 0) { 552 if (spacing == B_OFFSET_SPACING) { 553 if (buttonCount == 3) 554 buttonX -= kButtonOffsetSpaceOffset; 555 else { 556 // If there are two buttons, the left wall of 557 // button0 needs to line up with the left wall 558 // of the TextView. 559 buttonX = (MasterView->Bitmap()) ? 560 kTextIconOffset : kTextLeftOffset; 561 if (fButtons[i + 1]->Frame().left - 562 (buttonX + fButtons[i]->Frame().Width()) < 563 kButtonMinOffsetSpaceOffset) { 564 // Recompute buttonX using min offset space 565 // if using the current buttonX would not 566 // provide enough space or cause an overlap. 567 buttonX = fButtons[i + 1]->Frame().left - 568 fButtons[i]->Frame().Width() - 569 kButtonMinOffsetSpaceOffset; 570 } 571 } 572 } else if (buttonCount == 3) 573 buttonX -= 3; 574 } 575 } 576 fButtons[i]->MoveTo(buttonX, buttonY); 577 } // for (int i = buttonCount - 1; i >= 0; --i) 578 579 // Adjust the window's width, if necessary 580 float totalWidth = kButtonRightOffset; 581 totalWidth += fButtons[buttonCount - 1]->Frame().right - 582 fButtons[0]->Frame().left; 583 if (MasterView->Bitmap()) 584 totalWidth += kIconStripeWidth + kWindowIconOffset; 585 else 586 totalWidth += kWindowMinOffset; 587 588 if (spacing == B_OFFSET_SPACING) { 589 totalWidth -= 2; 590 if (buttonCount == 3) 591 totalWidth = max(kWindowOffsetMinWidth, totalWidth); 592 else 593 totalWidth = max(kWindowMinWidth, totalWidth); 594 } else { 595 totalWidth += 5; 596 totalWidth = max(kWindowMinWidth, totalWidth); 597 } 598 ResizeTo(totalWidth, Bounds().Height()); 599 600 // Set up the text view 601 BRect TextViewRect(kTextLeftOffset, kTextTopOffset, 602 Bounds().right - kTextRightOffset, 603 Bounds().bottom - kTextBottomOffset); 604 if (MasterView->Bitmap()) 605 TextViewRect.left = kTextIconOffset; 606 607 fTextView = new BTextView(TextViewRect, "_tv_", TextViewRect, 608 B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW); 609 MasterView->AddChild(fTextView); 610 611 fTextView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 612 fTextView->SetText(text, strlen(text)); 613 fTextView->MakeEditable(false); 614 fTextView->MakeSelectable(false); 615 fTextView->SetWordWrap(true); 616 617 // Now resize the TextView vertically so that all the text is visible 618 float textHeight = fTextView->TextHeight(0, fTextView->CountLines()); 619 TextViewRect.OffsetTo(0, 0); 620 textHeight -= TextViewRect.Height(); 621 ResizeBy(0, textHeight); 622 fTextView->ResizeBy(0, textHeight); 623 TextViewRect.bottom += textHeight; 624 fTextView->SetTextRect(TextViewRect); 625 626 AddCommonFilter(new _BAlertFilter_(this)); 627 628 MoveTo(AlertPosition(Frame().Width(), Frame().Height())); 629 } 630 //------------------------------------------------------------------------------ 631 BBitmap* BAlert::InitIcon() 632 { 633 // After a bit of a search, I found the icons in app_server. =P 634 BBitmap* Icon = NULL; 635 BPath Path; 636 if (find_directory(B_BEOS_SERVERS_DIRECTORY, &Path) == B_OK) 637 { 638 Path.Append("app_server"); 639 BFile File; 640 if (File.SetTo(Path.Path(), B_READ_ONLY) == B_OK) 641 { 642 BResources Resources; 643 if (Resources.SetTo(&File) == B_OK) 644 { 645 // Which icon are we trying to load? 646 const char* iconName = ""; // Don't want any seg faults 647 switch (fMsgType) 648 { 649 case B_INFO_ALERT: 650 iconName = "info"; 651 break; 652 653 case B_IDEA_ALERT: 654 iconName = "idea"; 655 break; 656 657 case B_WARNING_ALERT: 658 iconName = "warn"; 659 break; 660 661 case B_STOP_ALERT: 662 iconName = "stop"; 663 break; 664 665 default: 666 // Alert type is either invalid or B_EMPTY_ALERT; 667 // either way, we're not going to load an icon 668 return Icon; 669 } 670 671 // Load the raw icon data 672 size_t size; 673 const void* rawIcon = 674 Resources.LoadResource('ICON', iconName, &size); 675 676 if (rawIcon) 677 { 678 // Now build the bitmap 679 Icon = new BBitmap(BRect(0, 0, 31, 31), B_CMAP8); 680 Icon->SetBits(rawIcon, size, 0, B_CMAP8); 681 } 682 } 683 } 684 } 685 686 if (!Icon) 687 { 688 // If there's no icon, it's an empty alert indeed. 689 fMsgType = B_EMPTY_ALERT; 690 } 691 692 return Icon; 693 } 694 //------------------------------------------------------------------------------ 695 696 697 //------------------------------------------------------------------------------ 698 // #pragma mark - 699 // #pragma mark TAlertView 700 // #pragma mark - 701 //------------------------------------------------------------------------------ 702 TAlertView::TAlertView(BRect frame) 703 : BView(frame, "TAlertView", B_FOLLOW_ALL_SIDES, B_WILL_DRAW), 704 fIconBitmap(NULL) 705 { 706 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 707 } 708 //------------------------------------------------------------------------------ 709 TAlertView::TAlertView(BMessage* archive) 710 : BView(archive), 711 fIconBitmap(NULL) 712 { 713 } 714 //------------------------------------------------------------------------------ 715 TAlertView::~TAlertView() 716 { 717 if (fIconBitmap) 718 { 719 delete fIconBitmap; 720 } 721 } 722 //------------------------------------------------------------------------------ 723 TAlertView* TAlertView::Instantiate(BMessage* archive) 724 { 725 if (!validate_instantiation(archive, "TAlertView")) 726 { 727 return NULL; 728 } 729 730 return new TAlertView(archive); 731 } 732 //------------------------------------------------------------------------------ 733 status_t TAlertView::Archive(BMessage* archive, bool deep) 734 { 735 return BView::Archive(archive, deep); 736 } 737 //------------------------------------------------------------------------------ 738 void TAlertView::Draw(BRect updateRect) 739 { 740 // Here's the fun stuff 741 if (fIconBitmap) 742 { 743 BRect StripeRect = Bounds(); 744 StripeRect.right = kIconStripeWidth; 745 SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT)); 746 FillRect(StripeRect); 747 748 SetDrawingMode(B_OP_OVER); 749 DrawBitmapAsync(fIconBitmap, BPoint(18, 6)); 750 SetDrawingMode(B_OP_COPY); 751 } 752 } 753 //------------------------------------------------------------------------------ 754 755 756 //------------------------------------------------------------------------------ 757 // #pragma mark - 758 // #pragma mark _BAlertFilter_ 759 // #pragma mark - 760 //------------------------------------------------------------------------------ 761 _BAlertFilter_::_BAlertFilter_(BAlert* Alert) 762 : BMessageFilter(B_KEY_DOWN), 763 fAlert(Alert) 764 { 765 } 766 //------------------------------------------------------------------------------ 767 _BAlertFilter_::~_BAlertFilter_() 768 { 769 ; 770 } 771 //------------------------------------------------------------------------------ 772 filter_result _BAlertFilter_::Filter(BMessage* msg, BHandler** target) 773 { 774 if (msg->what == B_KEY_DOWN) 775 { 776 char byte; 777 if (msg->FindInt8("byte", (int8*)&byte) == B_OK) 778 { 779 for (int i = 0; i < 3; ++i) 780 { 781 if (byte == fAlert->Shortcut(i) && fAlert->ButtonAt(i)) 782 { 783 char space = ' '; 784 fAlert->ButtonAt(i)->KeyDown(&space, 1); 785 786 return B_SKIP_MESSAGE; 787 } 788 } 789 } 790 } 791 792 return B_DISPATCH_MESSAGE; 793 } 794 //------------------------------------------------------------------------------ 795 796 /* 797 * $Log $ 798 * 799 * $Id $ 800 * 801 */ 802 803