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