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