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