xref: /haiku/src/kits/interface/Alert.cpp (revision 51978af14a173e7fae0563b562be5603bc652aeb)
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 // TODO: Fix
40 // This is hacked in because something in Accelerant.h or SupportDefs.h is lame!
41 typedef unsigned int			uint;
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 
51 #include <Autolock.h>
52 
53 // Project Includes ------------------------------------------------------------
54 
55 // Local Includes --------------------------------------------------------------
56 
57 // Local Defines ---------------------------------------------------------------
58 #define DEFAULT_RECT	BRect(100, 100, 100, 100)
59 #define max(LHS, RHS)	((LHS) > (RHS) ? (LHS) : (RHS))
60 
61 // Globals ---------------------------------------------------------------------
62 const unsigned int kAlertButtonMsg	= 'ALTB';
63 const int kSemTimeOut				= 50000;
64 
65 const int kButtonBottomOffset		=   9;
66 const int kDefButtonBottomOffset	=   6;
67 const int kButtonRightOffset		=   6;
68 const int kButtonSpaceOffset		=   6;
69 const int kButtonOffsetSpaceOffset	=  26;
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 	// TODO: Add sound?
259 	fAlertSem = create_sem(0, "AlertSem");
260 	if (fAlertSem < B_OK)
261 	{
262 		Quit();
263 		return -1;
264 	}
265 
266 	// Get the originating window, if it exists
267 	BWindow* Window =
268 		dynamic_cast<BWindow*>(BLooper::LooperForThread(find_thread(NULL)));
269 
270 	Show();
271 
272 	// Heavily modified from TextEntryAlert code; the original didn't let the
273 	// blocked window ever draw.
274 	if (Window)
275 	{
276 		status_t err;
277 		for (;;)
278 		{
279 			do
280 			{
281 				err = acquire_sem_etc(fAlertSem, 1, B_RELATIVE_TIMEOUT,
282 									  kSemTimeOut);
283 				// We've (probably) had our time slice taken away from us
284 			} while (err == B_INTERRUPTED);
285 			if (err == B_BAD_SEM_ID)
286 			{
287 				// Semaphore was finally nuked in MessageReceived
288 				break;
289 			}
290 			Window->UpdateIfNeeded();
291 		}
292 	}
293 	else
294 	{
295 		// No window to update, so just hang out until we're done.
296 		while (acquire_sem(fAlertSem) == B_INTERRUPTED)
297 		{
298 			;
299 		}
300 	}
301 
302 	// Have to cache the value since we delete on Quit()
303 	int32 value = fAlertVal;
304 	if (Lock())
305 	{
306 		Quit();
307 	}
308 
309 	return value;
310 }
311 //------------------------------------------------------------------------------
312 status_t 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 void BAlert::MessageReceived(BMessage* msg)
323 {
324 	if (msg->what == kAlertButtonMsg)
325 	{
326 		int32 which;
327 		if (msg->FindInt32("which", &which) == B_OK)
328 		{
329 			if (fAlertSem < B_OK)
330 			{
331 				// Semaphore hasn't been created; we're running asynchronous
332 				if (fInvoker)
333 				{
334 					BMessage* out = fInvoker->Message();
335 					if (out && (out->AddInt32("which", which) == B_OK ||
336 						out->ReplaceInt32("which", which) == B_OK))
337 					{
338 						fInvoker->Invoke();
339 					}
340 				}
341 			}
342 			else
343 			{
344 				// Created semaphore means were running synchronously
345 				fAlertVal = which;
346 
347 				// TextAlertVar does release_sem() below, and then sets the
348 				// member var.  That doesn't make much sense to me, since we
349 				// want to be able to clean up at some point.  Better to just
350 				// nuke the semaphore now; we don't need it any more and this
351 				// lets synchronous Go() continue just as well.
352 				delete_sem(fAlertSem);
353 				fAlertSem = -1;
354 			}
355 		}
356 	}
357 }
358 //------------------------------------------------------------------------------
359 void BAlert::FrameResized(float new_width, float new_height)
360 {
361 	// Nothing in documentation; will have to test
362 	// TODO: Implement?
363 	BWindow::FrameResized(new_width, new_height);
364 }
365 //------------------------------------------------------------------------------
366 BButton* BAlert::ButtonAt(int32 index) const
367 {
368 	BButton* Button = NULL;
369 	if (index >= 0 && index < 3)
370 		Button = fButtons[index];
371 
372 	return Button;
373 }
374 //------------------------------------------------------------------------------
375 BTextView* BAlert::TextView() const
376 {
377 	return fTextView;
378 }
379 //------------------------------------------------------------------------------
380 BHandler* BAlert::ResolveSpecifier(BMessage* msg, int32 index,
381 								   BMessage* specifier, int32 form,
382 								   const char* property)
383 {
384 	// Nothing in documentation; will have to test
385 	// TODO: Implement?
386 	return BWindow::ResolveSpecifier(msg, index, specifier, form, property);
387 }
388 //------------------------------------------------------------------------------
389 status_t BAlert::GetSupportedSuites(BMessage* data)
390 {
391 	// Nothing in documentation; will have to test
392 	// TODO: Implement?
393 	return BWindow::GetSupportedSuites(data);
394 }
395 //------------------------------------------------------------------------------
396 void BAlert::DispatchMessage(BMessage* msg, BHandler* handler)
397 {
398 	// Nothing in documentation; will have to test
399 	// TODO: Implement?
400 	BWindow::DispatchMessage(msg, handler);
401 }
402 //------------------------------------------------------------------------------
403 void BAlert::Quit()
404 {
405 	// Nothing in documentation; will have to test
406 	// TODO: Implement?
407 	BWindow::Quit();
408 }
409 //------------------------------------------------------------------------------
410 bool BAlert::QuitRequested()
411 {
412 	// Nothing in documentation; will have to test
413 	// TODO: Implement?
414 	return BWindow::QuitRequested();
415 }
416 //------------------------------------------------------------------------------
417 BPoint BAlert::AlertPosition(float width, float height)
418 {
419 	BPoint result(100, 100);
420 
421 	BWindow* Window =
422 		dynamic_cast<BWindow*>(BLooper::LooperForThread(find_thread(NULL)));
423 
424 	BScreen Screen(Window);
425 	if (!Screen.IsValid())
426 	{
427 		// ... then we're in deep trouble
428 		// But what to say about it?
429 		// debugger(???);
430 	}
431 
432 	BRect screenRect = Screen.Frame();
433 
434 	// Horizontally, we're smack in the middle
435 	result.x = (screenRect.Width() / 2.0) - (width / 2.0);
436 
437 	// This is probably sooo wrong, but it looks right on 1024 x 768
438 	result.y = (screenRect.Height() / 4.0) - ceil(height / 3.0);
439 
440 	return result;
441 }
442 //------------------------------------------------------------------------------
443 status_t BAlert::Perform(perform_code d, void* arg)
444 {
445 	return BWindow::Perform(d, arg);
446 }
447 //------------------------------------------------------------------------------
448 void BAlert::_ReservedAlert1()
449 {
450 	;
451 }
452 //------------------------------------------------------------------------------
453 void BAlert::_ReservedAlert2()
454 {
455 	;
456 }
457 //------------------------------------------------------------------------------
458 void BAlert::_ReservedAlert3()
459 {
460 	;
461 }
462 //------------------------------------------------------------------------------
463 void BAlert::InitObject(const char* text, const char* button0,
464 						const char* button1, const char* button2,
465 						button_width width, button_spacing spacing,
466 						alert_type type)
467 {
468 	BAutolock Autolock(this);
469 	if (Autolock.IsLocked())
470 	{
471 		fInvoker = NULL;
472 		fAlertSem = -1;
473 		fAlertVal = -1;
474 		fButtons[0] = fButtons[1] = fButtons[2] = NULL;
475 		fTextView = NULL;
476 		fKeys[0] = fKeys[1] = fKeys[2] = 0;
477 		fMsgType = type;
478 		fButtonWidth = width;
479 
480 		// Set up the "_master_" view
481 		TAlertView* MasterView = new TAlertView(Bounds());
482 		MasterView->SetBitmap(InitIcon());
483 		AddChild(MasterView);
484 
485 		// Set up the buttons
486 		int buttonCount = 0;
487 
488 		// Have to have at least one button
489 		if (button0 == NULL)
490 		{
491 			debugger("BAlert's must have at least one button.");
492 			button0 = "";
493 		}
494 
495 		BMessage ProtoMsg(kAlertButtonMsg);
496 		ProtoMsg.AddInt32("which", 0);
497 		fButtons[0] = new BButton(BRect(0, 0, 0, 0), "_b0_", button0,
498 								  new BMessage(ProtoMsg),
499 								  B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
500 		++buttonCount;
501 
502 		if (button1)
503 		{
504 			ProtoMsg.ReplaceInt32("which", 1);
505 			fButtons[buttonCount] = new BButton(BRect(0, 0, 0, 0), "_b1_", button1,
506 												new BMessage(ProtoMsg),
507 												B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
508 			++buttonCount;
509 		}
510 
511 		if (button2)
512 		{
513 			ProtoMsg.ReplaceInt32("which", 2);
514 			fButtons[buttonCount] = new BButton(BRect(0, 0, 0, 0), "_b2_", button2,
515 												new BMessage(ProtoMsg),
516 												B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
517 			++buttonCount;
518 		}
519 
520 		SetDefaultButton(fButtons[buttonCount - 1]);
521 
522 		float buttonWidth = 0;
523 		float buttonHeight = 0;
524 		for (int i = 0; i < buttonCount; ++i)
525 		{
526 			float temp;
527 			fButtons[i]->GetPreferredSize(&temp, &buttonHeight);
528 			buttonWidth = max(buttonWidth, temp);
529 		}
530 
531 		// Add first, because the buttons will ResizeToPreferred()
532 		// in AttachedToWindow()
533 		for (int i = 0; i < buttonCount; ++i)
534 		{
535 			MasterView->AddChild(fButtons[i]);
536 		}
537 
538 		for (int i = buttonCount - 1; i >= 0; --i)
539 		{
540 			switch (fButtonWidth)
541 			{
542 				case B_WIDTH_FROM_WIDEST:
543 					fButtons[i]->ResizeTo(buttonWidth, buttonHeight);
544 					break;
545 
546 				case B_WIDTH_FROM_LABEL:
547 					fButtons[i]->ResizeToPreferred();
548 					break;
549 
550 				default:	// B_WIDTH_AS_USUAL
551 					fButtons[i]->GetPreferredSize(&buttonWidth, &buttonHeight);
552 					buttonWidth = max(buttonWidth, kButtonUsualWidth);
553 					fButtons[i]->ResizeTo(buttonWidth, buttonHeight);
554 					break;
555 			}
556 
557 			float buttonX;
558 			float buttonY;
559 			float temp;
560 
561 			fButtons[i]->GetPreferredSize(&temp, &buttonHeight);
562 			buttonY = Bounds().bottom - buttonHeight;
563 
564 			if (i == buttonCount - 1)	// the right-most button
565 			{
566 				buttonX = Bounds().right - fButtons[i]->Frame().Width() -
567 						  kButtonRightOffset;
568 				buttonY -= kDefButtonBottomOffset;
569 			}
570 			else
571 			{
572 				buttonX = fButtons[i + 1]->Frame().left -
573 						  fButtons[i]->Frame().Width() -
574 						  kButtonSpaceOffset;
575 
576 				if (i == 0)
577 				{
578 					if (spacing == B_OFFSET_SPACING)
579 					{
580 						buttonX -= kButtonOffsetSpaceOffset;
581 					}
582 					else if (buttonCount == 3)
583 					{
584 						buttonX -= 3;
585 					}
586 				}
587 				buttonY -= kButtonBottomOffset;
588 			}
589 
590 			fButtons[i]->MoveTo(buttonX, buttonY);
591 		}
592 
593 
594 		// Resize the window, if necessary
595 		float totalWidth = kButtonRightOffset;
596 		totalWidth += fButtons[buttonCount - 1]->Frame().right -
597 					  fButtons[0]->Frame().left;
598 		if (MasterView->Bitmap())
599 		{
600 			totalWidth += kIconStripeWidth + kWindowIconOffset;
601 		}
602 		else
603 		{
604 			totalWidth += kWindowMinOffset;
605 		}
606 
607 		if (spacing == B_OFFSET_SPACING)
608 		{
609 			totalWidth = max(kWindowOffsetMinWidth, totalWidth);
610 		}
611 		else
612 		{
613 			totalWidth += 5;
614 			totalWidth = max(kWindowMinWidth, totalWidth);
615 		}
616 		ResizeTo(totalWidth, Bounds().Height());
617 
618 		// Set up the text view
619 		BRect TextViewRect(kTextLeftOffset, kTextTopOffset,
620 						   Bounds().right - kTextRightOffset,
621 						   Bounds().bottom - kTextBottomOffset);
622 		if (MasterView->Bitmap())
623 		{
624 			TextViewRect.left = kTextIconOffset;
625 		}
626 
627 		fTextView = new BTextView(TextViewRect, "_tv_",
628 								  TextViewRect,
629 								  B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW);
630 		fTextView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
631 		fTextView->SetText(text, strlen(text));
632 		fTextView->MakeEditable(false);
633 		fTextView->MakeSelectable(false);
634 		fTextView->SetWordWrap(true);
635 
636 		// Now resize the window vertically so that all the text is visible
637 		float textHeight = fTextView->TextHeight(0, fTextView->CountLines());
638 		TextViewRect.OffsetTo(0, 0);
639 		textHeight -= TextViewRect.Height();
640 		ResizeBy(0, textHeight);
641 		fTextView->ResizeBy(0, textHeight);
642 		TextViewRect.bottom += textHeight;
643 		fTextView->SetTextRect(TextViewRect);
644 
645 		MasterView->AddChild(fTextView);
646 
647 		AddCommonFilter(new _BAlertFilter_(this));
648 
649 		MoveTo(AlertPosition(Frame().Width(), Frame().Height()));
650 	}
651 }
652 //------------------------------------------------------------------------------
653 BBitmap* BAlert::InitIcon()
654 {
655 	// After a bit of a search, I found the icons in app_server. =P
656 	BBitmap* Icon = NULL;
657 	BPath Path;
658 	if (find_directory(B_BEOS_SERVERS_DIRECTORY, &Path) == B_OK)
659 	{
660 		Path.Append("app_server");
661 		BFile File;
662 		if (File.SetTo(Path.Path(), B_READ_ONLY) == B_OK)
663 		{
664 			BResources Resources;
665 			if (Resources.SetTo(&File) == B_OK)
666 			{
667 				// Which icon are we trying to load?
668 				const char* iconName = "";	// Don't want any seg faults
669 				switch (fMsgType)
670 				{
671 					case B_INFO_ALERT:
672 						iconName = "info";
673 						break;
674 
675 					case B_IDEA_ALERT:
676 						iconName = "idea";
677 						break;
678 
679 					case B_WARNING_ALERT:
680 						iconName = "warn";
681 						break;
682 
683 					case B_STOP_ALERT:
684 						iconName = "stop";
685 						break;
686 
687 					default:
688 						// Alert type is either invalid or B_EMPTY_ALERT;
689 						// either way, we're not going to load an icon
690 						return Icon;
691 				}
692 
693 				// Load the raw icon data
694 				size_t size;
695 				const void* rawIcon =
696 					Resources.LoadResource('ICON', iconName, &size);
697 
698 				if (rawIcon)
699 				{
700 					// Now build the bitmap
701 					Icon = new BBitmap(BRect(0, 0, 31, 31), B_CMAP8);
702 					Icon->SetBits(rawIcon, size, 0, B_CMAP8);
703 				}
704 			}
705 		}
706 	}
707 
708 	if (!Icon)
709 	{
710 		// If there's no icon, it's an empty alert indeed.
711 		fMsgType = B_EMPTY_ALERT;
712 	}
713 
714 	return Icon;
715 }
716 //------------------------------------------------------------------------------
717 
718 
719 //------------------------------------------------------------------------------
720 //	#pragma mark -
721 //	#pragma mark TAlertView
722 //	#pragma mark -
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 TAlertView::TAlertView(BMessage* archive)
732 	:	BView(archive),
733 		fIconBitmap(NULL)
734 {
735 }
736 //------------------------------------------------------------------------------
737 TAlertView::~TAlertView()
738 {
739 	if (fIconBitmap)
740 	{
741 		delete fIconBitmap;
742 	}
743 }
744 //------------------------------------------------------------------------------
745 TAlertView* TAlertView::Instantiate(BMessage* archive)
746 {
747 	if (!validate_instantiation(archive, "TAlertView"))
748 	{
749 		return NULL;
750 	}
751 
752 	return new TAlertView(archive);
753 }
754 //------------------------------------------------------------------------------
755 status_t TAlertView::Archive(BMessage* archive, bool deep)
756 {
757 	return BView::Archive(archive, deep);
758 }
759 //------------------------------------------------------------------------------
760 void TAlertView::Draw(BRect updateRect)
761 {
762 	// Here's the fun stuff
763 	if (fIconBitmap)
764 	{
765 		BRect StripeRect = Bounds();
766 		StripeRect.right = kIconStripeWidth;
767 		SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT));
768 		FillRect(StripeRect);
769 
770 		SetDrawingMode(B_OP_OVER);
771 		DrawBitmapAsync(fIconBitmap, BPoint(18, 6));
772 		SetDrawingMode(B_OP_COPY);
773 	}
774 }
775 //------------------------------------------------------------------------------
776 
777 
778 //------------------------------------------------------------------------------
779 //	#pragma mark -
780 //	#pragma mark _BAlertFilter_
781 //	#pragma mark -
782 //------------------------------------------------------------------------------
783 _BAlertFilter_::_BAlertFilter_(BAlert* Alert)
784 	:	BMessageFilter(B_KEY_DOWN),
785 		fAlert(Alert)
786 {
787 }
788 //------------------------------------------------------------------------------
789 _BAlertFilter_::~_BAlertFilter_()
790 {
791 	;
792 }
793 //------------------------------------------------------------------------------
794 filter_result _BAlertFilter_::Filter(BMessage* msg, BHandler** target)
795 {
796 	if (msg->what == B_KEY_DOWN)
797 	{
798 		char byte;
799 		if (msg->FindInt8("byte", (int8*)&byte) == B_OK)
800 		{
801 			for (int i = 0; i < 3; ++i)
802 			{
803 				if (byte == fAlert->Shortcut(i) && fAlert->ButtonAt(i))
804 				{
805 					char space = ' ';
806 					fAlert->ButtonAt(i)->KeyDown(&space, 1);
807 
808 					return B_SKIP_MESSAGE;
809 				}
810 			}
811 		}
812 	}
813 
814 	return B_DISPATCH_MESSAGE;
815 }
816 //------------------------------------------------------------------------------
817 
818 /*
819  * $Log $
820  *
821  * $Id  $
822  *
823  */
824 
825