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