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