xref: /haiku/src/kits/interface/Alert.cpp (revision 81f5654c124bf46fba0fd251f208e2d88d81e1ce)
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