xref: /haiku/src/kits/interface/Alert.cpp (revision adb0d19d561947362090081e81d90dde59142026)
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 BPoint
400 BAlert::AlertPosition(float width, float height)
401 {
402 	BPoint result(100, 100);
403 
404 	BWindow* window =
405 		dynamic_cast<BWindow*>(BLooper::LooperForThread(find_thread(NULL)));
406 
407 	BScreen screen(window);
408  	BRect screenFrame(0, 0, 640, 480);
409  	if (screen.IsValid())
410  		screenFrame = screen.Frame();
411 
412 	// Horizontally, we're smack in the middle
413 	result.x = screenFrame.left + (screenFrame.Width() / 2.0) - (width / 2.0);
414 
415 	// This is probably sooo wrong, but it looks right on 1024 x 768
416 	result.y = screenFrame.top + (screenFrame.Height() / 4.0) - ceil(height / 3.0);
417 
418 	return result;
419 }
420 
421 
422 status_t
423 BAlert::Perform(perform_code code, void* _data)
424 {
425 	switch (code) {
426 		case PERFORM_CODE_SET_LAYOUT:
427 		{
428 			perform_data_set_layout* data = (perform_data_set_layout*)_data;
429 			BAlert::SetLayout(data->layout);
430 			return B_OK;
431 }
432 	}
433 
434 	return BWindow::Perform(code, _data);
435 }
436 
437 
438 void BAlert::_ReservedAlert1() {}
439 void BAlert::_ReservedAlert2() {}
440 void BAlert::_ReservedAlert3() {}
441 
442 
443 void
444 BAlert::_InitObject(const char* text, const char* button0, const char* button1,
445 	const char* button2, button_width buttonWidth, button_spacing spacing,
446 	alert_type type)
447 {
448 	fInvoker = NULL;
449 	fAlertSem = -1;
450 	fAlertValue = -1;
451 	fButtons[0] = fButtons[1] = fButtons[2] = NULL;
452 	fTextView = NULL;
453 	fKeys[0] = fKeys[1] = fKeys[2] = 0;
454 	fMsgType = type;
455 	fButtonWidth = buttonWidth;
456 
457 	// Set up the "_master_" view
458 	TAlertView* view = new(std::nothrow) TAlertView(Bounds());
459 	if (view == NULL)
460 		return;
461 
462 	AddChild(view);
463 	view->SetBitmap(_InitIcon());
464 
465 	// Must have at least one button
466 	if (button0 == NULL) {
467 		debugger("BAlert's must have at least one button.");
468 		button0 = "";
469 	}
470 
471 	// Set up the buttons
472 
473 	int32 buttonCount = 1;
474 	view->AddChild(fButtons[0] = _CreateButton(0, button0));
475 
476 	if (button1 != NULL) {
477 		view->AddChild(fButtons[1] = _CreateButton(1, button1));
478 		buttonCount++;
479 	}
480 
481 	if (button2 != NULL) {
482 		view->AddChild(fButtons[2] = _CreateButton(2, button2));
483 		buttonCount++;
484 	}
485 
486 	// Find the widest button only if the widest value needs to be known.
487 
488 	if (fButtonWidth == B_WIDTH_FROM_WIDEST) {
489 		float maxWidth = 0;
490 		for (int i = 0; i < buttonCount; i++) {
491 			float width = fButtons[i]->Bounds().Width();
492 			if (width > maxWidth)
493 				maxWidth = width;
494 		}
495 
496 		// resize buttons
497 		for (int i = 0; i < buttonCount; i++) {
498 			fButtons[i]->ResizeTo(maxWidth, fButtons[i]->Bounds().Height());
499 		}
500 	}
501 
502 	float defaultButtonFrameWidth = -fButtons[buttonCount - 1]->Bounds().Width() / 2.0f;
503 	SetDefaultButton(fButtons[buttonCount - 1]);
504 	defaultButtonFrameWidth += fButtons[buttonCount - 1]->Bounds().Width() / 2.0f;
505 
506 	// Layout buttons
507 
508 	float fontFactor = be_plain_font->Size() / 11.0f;
509 
510 	for (int i = buttonCount - 1; i >= 0; --i) {
511 		float x = -fButtons[i]->Bounds().Width();
512 		if (i + 1 == buttonCount)
513 			x += Bounds().right - kRightOffset + defaultButtonFrameWidth;
514 		else
515 			x += fButtons[i + 1]->Frame().left - kButtonSpacing;
516 
517 		if (buttonCount > 1 && i == 0 && spacing == B_OFFSET_SPACING)
518 			x -= kButtonOffsetSpacing * fontFactor;
519 
520 		fButtons[i]->MoveTo(x, fButtons[i]->Frame().top);
521 	}
522 
523 	// Adjust the window's width, if necessary
524 
525 	int32 iconLayoutScale = icon_layout_scale();
526 	float totalWidth = kRightOffset + fButtons[buttonCount - 1]->Frame().right
527 		- defaultButtonFrameWidth - fButtons[0]->Frame().left;
528 	if (view->Bitmap()) {
529 		totalWidth += (kIconStripeWidth + kWindowIconOffset) * iconLayoutScale;
530 	} else
531 		totalWidth += kWindowMinOffset;
532 
533 	float width = (spacing == B_OFFSET_SPACING
534 		? kWindowOffsetMinWidth : kWindowMinWidth) * fontFactor;
535 
536 	ResizeTo(max_c(totalWidth, width), Bounds().Height());
537 
538 	// Set up the text view
539 
540 	BRect textViewRect(kLeftOffset, kTopOffset,
541 		Bounds().right - kRightOffset,
542 		fButtons[0]->Frame().top - kTextButtonOffset);
543 	if (view->Bitmap())
544 		textViewRect.left = (kWindowIconOffset
545 			+ kIconStripeWidth) * iconLayoutScale - 2;
546 
547 	fTextView = new(std::nothrow) BTextView(textViewRect, "_tv_",
548 		textViewRect.OffsetByCopy(B_ORIGIN),
549 		B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW);
550 	if (fTextView == NULL)
551 		return;
552 
553 	fTextView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
554 	rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR);
555 	fTextView->SetFontAndColor(be_plain_font, B_FONT_ALL, &textColor);
556 	fTextView->SetText(text, strlen(text));
557 	fTextView->MakeEditable(false);
558 	fTextView->MakeSelectable(false);
559 	fTextView->SetWordWrap(true);
560 	view->AddChild(fTextView);
561 
562 	// Now resize the TextView vertically so that all the text is visible
563 	float textHeight = fTextView->TextHeight(0, fTextView->CountLines());
564 	textViewRect.OffsetTo(0, 0);
565 	textHeight -= textViewRect.Height();
566 	ResizeBy(0, textHeight);
567 	fTextView->ResizeBy(0, textHeight);
568 	textViewRect.bottom += textHeight;
569 	fTextView->SetTextRect(textViewRect);
570 
571 	AddCommonFilter(new(std::nothrow) _BAlertFilter_(this));
572 
573 	MoveTo(AlertPosition(Frame().Width(), Frame().Height()));
574 }
575 
576 
577 BBitmap*
578 BAlert::_InitIcon()
579 {
580 	// Save the desired alert type and set it to "empty" until
581 	// loading the icon was successful
582 	alert_type alertType = fMsgType;
583 	fMsgType = B_EMPTY_ALERT;
584 
585 	// After a bit of a search, I found the icons in app_server. =P
586 	BBitmap* icon = NULL;
587 	BPath path;
588 	status_t status = find_directory(B_BEOS_SERVERS_DIRECTORY, &path);
589 	if (status < B_OK) {
590 		FTRACE((stderr, "BAlert::_InitIcon() - find_directory failed: %s\n",
591 			strerror(status)));
592 		return NULL;
593 	}
594 
595 	path.Append("app_server");
596 	BFile file;
597 	status = file.SetTo(path.Path(), B_READ_ONLY);
598 	if (status < B_OK) {
599 		FTRACE((stderr, "BAlert::_InitIcon() - BFile init failed: %s\n",
600 			strerror(status)));
601 		return NULL;
602 	}
603 
604 	BResources resources;
605 	status = resources.SetTo(&file);
606 	if (status < B_OK) {
607 		FTRACE((stderr, "BAlert::_InitIcon() - BResources init failed: %s\n",
608 			strerror(status)));
609 		return NULL;
610 	}
611 
612 	// Which icon are we trying to load?
613 	const char* iconName = "";	// Don't want any seg faults
614 	switch (alertType) {
615 		case B_INFO_ALERT:
616 			iconName = "info";
617 			break;
618 		case B_IDEA_ALERT:
619 			iconName = "idea";
620 			break;
621 		case B_WARNING_ALERT:
622 			iconName = "warn";
623 			break;
624 		case B_STOP_ALERT:
625 			iconName = "stop";
626 			break;
627 
628 		default:
629 			// Alert type is either invalid or B_EMPTY_ALERT;
630 			// either way, we're not going to load an icon
631 			return NULL;
632 	}
633 
634 	int32 iconSize = 32 * icon_layout_scale();
635 	// Allocate the icon bitmap
636 	icon = new(std::nothrow) BBitmap(BRect(0, 0, iconSize - 1, iconSize - 1),
637 		0, B_RGBA32);
638 	if (icon == NULL || icon->InitCheck() < B_OK) {
639 		FTRACE((stderr, "BAlert::_InitIcon() - No memory for bitmap\n"));
640 		delete icon;
641 		return NULL;
642 	}
643 
644 	// Load the raw icon data
645 	size_t size = 0;
646 	const uint8* rawIcon;
647 
648 #ifdef __HAIKU__
649 	// Try to load vector icon
650 	rawIcon = (const uint8*)resources.LoadResource(B_VECTOR_ICON_TYPE,
651 		iconName, &size);
652 	if (rawIcon != NULL
653 		&& BIconUtils::GetVectorIcon(rawIcon, size, icon) == B_OK) {
654 		// We have an icon, restore the saved alert type
655 		fMsgType = alertType;
656 		return icon;
657 	}
658 #endif
659 
660 	// Fall back to bitmap icon
661 	rawIcon = (const uint8*)resources.LoadResource(B_LARGE_ICON_TYPE,
662 		iconName, &size);
663 	if (rawIcon == NULL) {
664 		FTRACE((stderr, "BAlert::_InitIcon() - Icon resource not found\n"));
665 		delete icon;
666 		return NULL;
667 	}
668 
669 	// Handle color space conversion
670 #ifdef __HAIKU__
671 	if (icon->ColorSpace() != B_CMAP8) {
672 		BIconUtils::ConvertFromCMAP8(rawIcon, iconSize, iconSize,
673 			iconSize, icon);
674 	}
675 #else
676 	icon->SetBits(rawIcon, iconSize, 0, B_CMAP8);
677 #endif
678 
679 	// We have an icon, restore the saved alert type
680 	fMsgType = alertType;
681 
682 	return icon;
683 }
684 
685 
686 BButton*
687 BAlert::_CreateButton(int32 which, const char* label)
688 {
689 	BMessage* message = new BMessage(kAlertButtonMsg);
690 	if (message == NULL)
691 		return NULL;
692 
693 	message->AddInt32("which", which);
694 
695 	BRect rect;
696 	rect.top = Bounds().bottom - kBottomOffset;
697 	rect.bottom = rect.top;
698 
699 	char name[32];
700 	snprintf(name, sizeof(name), "_b%ld_", which);
701 
702 	BButton* button = new(std::nothrow) BButton(rect, name, label, message,
703 		B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
704 	if (button == NULL)
705 		return NULL;
706 
707 	float width, height;
708 	button->GetPreferredSize(&width, &height);
709 
710 	if (fButtonWidth == B_WIDTH_AS_USUAL) {
711 		float fontFactor = be_plain_font->Size() / 11.0f;
712 		width = max_c(width, kButtonUsualWidth * fontFactor);
713 	}
714 
715 	button->ResizeTo(width, height);
716 	button->MoveBy(0.0f, -height);
717 	return button;
718 }
719 
720 
721 //	#pragma mark - TAlertView
722 
723 
724 TAlertView::TAlertView(BRect frame)
725 	:	BView(frame, "TAlertView", B_FOLLOW_ALL_SIDES, B_WILL_DRAW),
726 		fIconBitmap(NULL)
727 {
728 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
729 }
730 
731 
732 TAlertView::TAlertView(BMessage* archive)
733 	:	BView(archive),
734 		fIconBitmap(NULL)
735 {
736 }
737 
738 
739 TAlertView::~TAlertView()
740 {
741 	delete fIconBitmap;
742 }
743 
744 
745 TAlertView*
746 TAlertView::Instantiate(BMessage* archive)
747 {
748 	if (!validate_instantiation(archive, "TAlertView"))
749 		return NULL;
750 
751 	return new(std::nothrow) TAlertView(archive);
752 }
753 
754 
755 status_t
756 TAlertView::Archive(BMessage* archive, bool deep) const
757 {
758 	return BView::Archive(archive, deep);
759 }
760 
761 
762 void
763 TAlertView::Draw(BRect updateRect)
764 {
765 	if (!fIconBitmap)
766 		return;
767 
768 	// Here's the fun stuff
769 	BRect stripeRect = Bounds();
770 	int32 iconLayoutScale = icon_layout_scale();
771 	stripeRect.right = kIconStripeWidth * iconLayoutScale;
772 	SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT));
773 	FillRect(stripeRect);
774 
775 	SetDrawingMode(B_OP_ALPHA);
776 	SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
777 	DrawBitmapAsync(fIconBitmap, BPoint(18 * iconLayoutScale,
778 		6 * iconLayoutScale));
779 
780 }
781 
782 
783 //------------------------------------------------------------------------------
784 //	#pragma mark - _BAlertFilter_
785 
786 
787 _BAlertFilter_::_BAlertFilter_(BAlert* alert)
788 	: BMessageFilter(B_KEY_DOWN),
789 	fAlert(alert)
790 {
791 }
792 
793 
794 _BAlertFilter_::~_BAlertFilter_()
795 {
796 }
797 
798 
799 filter_result
800 _BAlertFilter_::Filter(BMessage* msg, BHandler** target)
801 {
802 	if (msg->what == B_KEY_DOWN) {
803 		char byte;
804 		if (msg->FindInt8("byte", (int8*)&byte) == B_OK) {
805 			for (int i = 0; i < 3; ++i) {
806 				if (byte == fAlert->Shortcut(i) && fAlert->ButtonAt(i)) {
807 					char space = ' ';
808 					fAlert->ButtonAt(i)->KeyDown(&space, 1);
809 
810 					return B_SKIP_MESSAGE;
811 				}
812 			}
813 		}
814 	}
815 
816 	return B_DISPATCH_MESSAGE;
817 }
818 
819