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