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