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