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