xref: /haiku/src/servers/notification/NotificationView.cpp (revision 4a57f843964b2598ff06ba9a15e206a0a72a82ff)
1 /*
2  * Copyright 2010, Haiku, Inc. All Rights Reserved.
3  * Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
4  * Copyright 2004-2008, Michael Davidson. All Rights Reserved.
5  * Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
6  * Distributed under the terms of the MIT License.
7  *
8  * Authors:
9  *		Michael Davidson, slaad@bong.com.au
10  *		Mikael Eiman, mikael@eiman.tv
11  *		Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
12  *		Stephan Aßmus <superstippi@gmx.de>
13  */
14 
15 #include <stdlib.h>
16 
17 #include <Font.h>
18 #include <IconUtils.h>
19 #include <Messenger.h>
20 #include <Picture.h>
21 #include <PropertyInfo.h>
22 #include <Region.h>
23 #include <Resources.h>
24 #include <Roster.h>
25 #include <StringView.h>
26 #include <TranslationUtils.h>
27 
28 #include "NotificationView.h"
29 #include "NotificationWindow.h"
30 
31 const char* kSmallIconAttribute	= "BEOS:M:STD_ICON";
32 const char* kLargeIconAttribute	= "BEOS:L:STD_ICON";
33 const char* kIconAttribute		= "BEOS:ICON";
34 
35 property_info message_prop_list[] = {
36 	{ "type", {B_GET_PROPERTY, B_SET_PROPERTY, 0},
37 		{B_DIRECT_SPECIFIER, 0}, "get the notification type"},
38 	{ "app", {B_GET_PROPERTY, B_SET_PROPERTY, 0},
39 		{B_DIRECT_SPECIFIER, 0}, "get notification's app"},
40 	{ "title", {B_GET_PROPERTY, B_SET_PROPERTY, 0},
41 		{B_DIRECT_SPECIFIER, 0}, "get notification's title"},
42 	{ "content", {B_GET_PROPERTY, B_SET_PROPERTY, 0},
43 		{B_DIRECT_SPECIFIER, 0}, "get notification's contents"},
44 	{ "icon", {B_GET_PROPERTY, 0},
45 		{B_DIRECT_SPECIFIER, 0}, "get icon as an archived bitmap"},
46 	{ "progress", {B_GET_PROPERTY, B_SET_PROPERTY, 0},
47 		{B_DIRECT_SPECIFIER, 0}, "get the progress (between 0.0 and 1.0)"},
48 	{ NULL }
49 };
50 
51 
52 NotificationView::NotificationView(NotificationWindow* win,
53 	notification_type type, const char* app, const char* title, const char* text,
54 	BMessage* details)
55 	:
56 	BView(BRect(0, 0, win->ViewWidth(), 1), "NotificationView",
57 		B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE
58 		| B_FRAME_EVENTS),
59 	fParent(win),
60 	fType(type),
61 	fRunner(NULL),
62 	fProgress(0.0f),
63 	fMessageID(""),
64 	fDetails(details),
65 	fBitmap(NULL),
66 	fIsFirst(false),
67 	fIsLast(false)
68 {
69 	BMessage iconMsg;
70 	if (fDetails->FindMessage("icon", &iconMsg) == B_OK)
71 		fBitmap = new BBitmap(&iconMsg);
72 
73 	if (!fBitmap)
74 		_LoadIcon();
75 
76 	const char* messageID = NULL;
77 	if (fDetails->FindString("messageID", &messageID) == B_OK)
78 		fMessageID = messageID;
79 
80 	if (fDetails->FindFloat("progress", &fProgress) != B_OK)
81 		fProgress = 0.0f;
82 
83 	// Progress is between 0 and 1
84 	if (fProgress < 0.0f)
85 		fProgress = 0.0f;
86 	if (fProgress > 1.0f)
87 		fProgress = 1.0f;
88 
89 	SetText(app, title, text);
90 	ResizeToPreferred();
91 
92 	switch (type) {
93 		case B_IMPORTANT_NOTIFICATION:
94 			SetViewColor(255, 255, 255);
95 			SetLowColor(255, 255, 255);
96 			break;
97 		case B_ERROR_NOTIFICATION:
98 			SetViewColor(ui_color(B_FAILURE_COLOR));
99 			SetLowColor(ui_color(B_FAILURE_COLOR));
100 			break;
101 		default:
102 			SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
103 			SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
104 			break;
105 	}
106 }
107 
108 
109 NotificationView::~NotificationView()
110 {
111 	delete fRunner;
112 	delete fDetails;
113 	delete fBitmap;
114 
115 	LineInfoList::iterator lIt;
116 	for (lIt = fLines.begin(); lIt != fLines.end(); lIt++)
117 		delete (*lIt);
118 }
119 
120 
121 void
122 NotificationView::AttachedToWindow()
123 {
124 	BMessage msg(kRemoveView);
125 	msg.AddPointer("view", this);
126 	bigtime_t timeout = -1;
127 
128 	if (fDetails->FindInt64("timeout", &timeout) != B_OK)
129 		timeout = fParent->Timeout() * 1000000;
130 
131 	if (timeout > 0)
132 		fRunner = new BMessageRunner(BMessenger(Parent()), &msg, timeout, 1);
133 }
134 
135 
136 void
137 NotificationView::MessageReceived(BMessage* msg)
138 {
139 	switch (msg->what) {
140 		case B_GET_PROPERTY:
141 		{
142 			BMessage specifier;
143 			const char* property;
144 			BMessage reply(B_REPLY);
145 			bool msgOkay = true;
146 
147 			if (msg->FindMessage("specifiers", 0, &specifier) != B_OK)
148 				msgOkay = false;
149 			if (specifier.FindString("property", &property) != B_OK)
150 				msgOkay = false;
151 
152 			if (msgOkay) {
153 				if (strcmp(property, "type") == 0)
154 					reply.AddInt32("result", fType);
155 
156 				if (strcmp(property, "app") == 0)
157 					reply.AddString("result", fApp);
158 
159 				if (strcmp(property, "title") == 0)
160 					reply.AddString("result", fTitle);
161 
162 				if (strcmp(property, "content") == 0)
163 					reply.AddString("result", fText);
164 
165 				if (strcmp(property, "progress") == 0)
166 					reply.AddFloat("result", fProgress);
167 
168 				if ((strcmp(property, "icon") == 0) && fBitmap) {
169 					BMessage archive;
170 					if (fBitmap->Archive(&archive) == B_OK)
171 						reply.AddMessage("result", &archive);
172 				}
173 
174 				reply.AddInt32("error", B_OK);
175 			} else {
176 				reply.what = B_MESSAGE_NOT_UNDERSTOOD;
177 				reply.AddInt32("error", B_ERROR);
178 			}
179 
180 			msg->SendReply(&reply);
181 			break;
182 		}
183 		case B_SET_PROPERTY:
184 		{
185 			BMessage specifier;
186 			const char* property;
187 			BMessage reply(B_REPLY);
188 			bool msgOkay = true;
189 
190 			if (msg->FindMessage("specifiers", 0, &specifier) != B_OK)
191 				msgOkay = false;
192 			if (specifier.FindString("property", &property) != B_OK)
193 				msgOkay = false;
194 
195 			if (msgOkay) {
196 				if (strcmp(property, "app") == 0)
197 					msg->FindString("data", &fApp);
198 
199 				if (strcmp(property, "title") == 0)
200 					msg->FindString("data", &fTitle);
201 
202 				if (strcmp(property, "content") == 0)
203 					msg->FindString("data", &fText);
204 
205 				if (strcmp(property, "icon") == 0) {
206 					BMessage archive;
207 					if (msg->FindMessage("data", &archive) == B_OK) {
208 						delete fBitmap;
209 						fBitmap = new BBitmap(&archive);
210 					}
211 				}
212 
213 				SetText(Application(), Title(), Text());
214 				Invalidate();
215 
216 				reply.AddInt32("error", B_OK);
217 			} else {
218 				reply.what = B_MESSAGE_NOT_UNDERSTOOD;
219 				reply.AddInt32("error", B_ERROR);
220 			}
221 
222 			msg->SendReply(&reply);
223 			break;
224 		}
225 		case kRemoveView:
226 		{
227 			BMessage remove(kRemoveView);
228 			remove.AddPointer("view", this);
229 			BMessenger msgr(Window());
230 			msgr.SendMessage( &remove );
231 			break;
232 		}
233 		default:
234 			BView::MessageReceived(msg);
235 	}
236 }
237 
238 
239 void
240 NotificationView::GetPreferredSize(float* w, float* h)
241 {
242 	// Parent width, minus the edge padding, minus the pensize
243 	*w = fParent->ViewWidth() - (kEdgePadding * 2) - (kPenSize * 2);
244 	*h = fHeight;
245 
246 	if (fType == B_PROGRESS_NOTIFICATION) {
247 		font_height fh;
248 		be_plain_font->GetHeight(&fh);
249 		float fontHeight = fh.ascent + fh.descent + fh.leading;
250 		*h += (kSmallPadding * 2) + (kEdgePadding * 1) + fontHeight;
251 	}
252 }
253 
254 
255 void
256 NotificationView::Draw(BRect updateRect)
257 {
258 	BRect progRect;
259 
260 	// Draw progress background
261 	if (fType == B_PROGRESS_NOTIFICATION) {
262 		PushState();
263 
264 		font_height fh;
265 		be_plain_font->GetHeight(&fh);
266 		float fontHeight = fh.ascent + fh.descent + fh.leading;
267 
268 		progRect = Bounds();
269 		progRect.InsetBy(kEdgePadding, kEdgePadding);
270 		progRect.top = progRect.bottom - (kSmallPadding * 2) - fontHeight;
271 		StrokeRect(progRect);
272 
273 		BRect barRect = progRect;
274 		barRect.InsetBy(1.0, 1.0);
275 		barRect.right *= fProgress;
276 		SetHighColor(ui_color(B_CONTROL_HIGHLIGHT_COLOR));
277 		FillRect(barRect);
278 
279 		SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
280 
281 		BString label = "";
282 		label << (int)(fProgress * 100) << " %";
283 
284 		float labelWidth = be_plain_font->StringWidth(label.String());
285 		float labelX = progRect.left + (progRect.IntegerWidth() / 2) - (labelWidth / 2);
286 
287 		SetLowColor(B_TRANSPARENT_COLOR);
288 		SetDrawingMode(B_OP_ALPHA);
289 		DrawString(label.String(), label.Length(),
290 			BPoint(labelX, progRect.top + fh.ascent + fh.leading + kSmallPadding));
291 
292 		PopState();
293 	}
294 
295 	SetDrawingMode(B_OP_ALPHA);
296 	SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
297 
298 	// Icon size
299 	float iconSize = (float)fParent->IconSize();
300 
301 	// Rectangle for icon and overlay icon
302 	BRect iconRect(0, 0, 0, 0);
303 
304 	// Draw icon
305 	if (fBitmap) {
306 		LineInfo* appLine = fLines.back();
307 		font_height fh;
308 		appLine->font.GetHeight(&fh);
309 
310 		float title_bottom = appLine->location.y + fh.descent;
311 
312 		float ix = kEdgePadding;
313 		float iy = 0;
314 		if (fParent->Layout() == TitleAboveIcon)
315 			iy = title_bottom + kEdgePadding + (Bounds().Height() - title_bottom
316 				- kEdgePadding * 2 - iconSize) / 2;
317 		else
318 			iy = (Bounds().Height() - iconSize) / 2.0;
319 
320 		if (fType == B_PROGRESS_NOTIFICATION)
321 			// Move icon up by half progress bar height if it's present
322 			iy -= (progRect.Height() + kEdgePadding) / 2.0;
323 
324 		iconRect.left = ix;
325 		iconRect.top = iy;
326 		iconRect.right = ix + iconSize;
327 		iconRect.bottom = iy + iconSize;
328 
329 		DrawBitmapAsync(fBitmap, fBitmap->Bounds(),
330 			iconRect, B_FILTER_BITMAP_BILINEAR);
331 	}
332 
333 	// Draw content
334 	LineInfoList::iterator lIt;
335 	for (lIt = fLines.begin(); lIt != fLines.end(); lIt++) {
336 		LineInfo *l = (*lIt);
337 
338 		SetFont(&l->font);
339 		DrawString(l->text.String(), l->text.Length(), l->location);
340 	}
341 
342 	rgb_color detailCol = ui_color(B_CONTROL_BORDER_COLOR);
343 	detailCol = tint_color(detailCol, B_LIGHTEN_2_TINT);
344 
345 	// Draw the close widget
346 	BRect closeRect = Bounds();
347 	closeRect.InsetBy(kEdgePadding, kEdgePadding);
348 	closeRect.left = closeRect.right - kCloseSize;
349 	closeRect.bottom = closeRect.top + kCloseSize;
350 
351 	PushState();
352 		SetHighColor(detailCol);
353 		StrokeRoundRect(closeRect, kSmallPadding, kSmallPadding);
354 		BRect closeCross = closeRect.InsetByCopy(kSmallPadding, kSmallPadding);
355 		StrokeLine(closeCross.LeftTop(), closeCross.RightBottom());
356 		StrokeLine(closeCross.LeftBottom(), closeCross.RightTop());
357 	PopState();
358 
359 	Sync();
360 }
361 
362 
363 void
364 NotificationView::MouseDown(BPoint point)
365 {
366 	int32 buttons;
367 	Window()->CurrentMessage()->FindInt32("buttons", &buttons);
368 
369 	switch (buttons) {
370 		case B_PRIMARY_MOUSE_BUTTON:
371 		{
372 			BRect closeRect = Bounds().InsetByCopy(2,2);
373 			closeRect.left = closeRect.right - kCloseSize;
374 			closeRect.bottom = closeRect.top + kCloseSize;
375 
376 			if (!closeRect.Contains(point)) {
377 				entry_ref launchRef;
378 				BString launchString;
379 				BMessage argMsg(B_ARGV_RECEIVED);
380 				BMessage refMsg(B_REFS_RECEIVED);
381 				entry_ref appRef;
382 				bool useArgv = false;
383 				BList messages;
384 				entry_ref ref;
385 
386 				if (fDetails->FindString("onClickApp", &launchString) == B_OK)
387 					if (be_roster->FindApp(launchString.String(), &appRef) == B_OK)
388 						useArgv = true;
389 				if (fDetails->FindRef("onClickFile", &launchRef) == B_OK) {
390 					if (be_roster->FindApp(&launchRef, &appRef) == B_OK)
391 						useArgv = true;
392 				}
393 
394 				if (fDetails->FindRef("onClickRef", &ref) == B_OK) {
395 					for (int32 i = 0; fDetails->FindRef("onClickRef", i, &ref) == B_OK; i++)
396 						refMsg.AddRef("refs", &ref);
397 
398 					messages.AddItem((void*)&refMsg);
399 				}
400 
401 				if (useArgv) {
402 					type_code type;
403 					int32 argc = 0;
404 					BString arg;
405 
406 					BPath p(&appRef);
407 					argMsg.AddString("argv", p.Path());
408 
409 					fDetails->GetInfo("onClickArgv", &type, &argc);
410 					argMsg.AddInt32("argc", argc + 1);
411 
412 					for (int32 i = 0; fDetails->FindString("onClickArgv", i, &arg) == B_OK; i++)
413 						argMsg.AddString("argv", arg);
414 
415 					messages.AddItem((void*)&argMsg);
416 				}
417 
418 				BMessage tmp;
419 				for (int32 i = 0; fDetails->FindMessage("onClickMsg", i, &tmp) == B_OK; i++)
420 					messages.AddItem((void*)&tmp);
421 
422 				if (fDetails->FindString("onClickApp", &launchString) == B_OK)
423 					be_roster->Launch(launchString.String(), &messages);
424 				else
425 					be_roster->Launch(&launchRef, &messages);
426 			}
427 
428 			// Remove the info view after a click
429 			BMessage remove_msg(kRemoveView);
430 			remove_msg.AddPointer("view", this);
431 
432 			BMessenger msgr(Parent());
433 			msgr.SendMessage(&remove_msg);
434 			break;
435 		}
436 	}
437 }
438 
439 
440 void
441 NotificationView::FrameResized( float w, float /*h*/)
442 {
443 	SetText(Application(), Title(), Text());
444 }
445 
446 
447 BHandler*
448 NotificationView::ResolveSpecifier(BMessage* msg, int32 index, BMessage* spec, int32 form, const char* prop)
449 {
450 	BPropertyInfo prop_info(message_prop_list);
451 	if (prop_info.FindMatch(msg, index, spec, form, prop) >= 0) {
452 		msg->PopSpecifier();
453 		return this;
454 	}
455 
456 	return BView::ResolveSpecifier(msg, index, spec, form, prop);
457 }
458 
459 
460 status_t
461 NotificationView::GetSupportedSuites(BMessage* msg)
462 {
463 	msg->AddString("suites", "suite/x-vnd.Haiku-notification_server");
464 	BPropertyInfo prop_info(message_prop_list);
465 	msg->AddFlat("messages", &prop_info);
466 	return BView::GetSupportedSuites(msg);
467 }
468 
469 
470 const char*
471 NotificationView::Application() const
472 {
473 	return fApp.Length() > 0 ? fApp.String() : NULL;
474 }
475 
476 
477 const char*
478 NotificationView::Title() const
479 {
480 	return fTitle.Length() > 0 ? fTitle.String() : NULL;
481 }
482 
483 
484 const char*
485 NotificationView::Text() const
486 {
487 	return fText.Length() > 0 ? fText.String() : NULL;
488 }
489 
490 
491 void
492 NotificationView::SetText(const char* app, const char* title, const char* text,
493 	float newMaxWidth)
494 {
495 	if (newMaxWidth < 0)
496 		newMaxWidth = Bounds().Width() - (kEdgePadding * 2);
497 
498 	// Delete old lines
499 	LineInfoList::iterator lIt;
500 	for (lIt = fLines.begin(); lIt != fLines.end(); lIt++)
501 		delete (*lIt);
502 	fLines.clear();
503 
504 	fApp = app;
505 	fTitle = title;
506 	fText = text;
507 
508 	float iconRight = kEdgePadding + kEdgePadding;
509 	if (fBitmap != NULL)
510 		iconRight += fParent->IconSize();
511 
512 	font_height fh;
513 	be_bold_font->GetHeight(&fh);
514 	float fontHeight = ceilf(fh.leading) + ceilf(fh.descent)
515 		+ ceilf(fh.ascent);
516 	float y = fontHeight;
517 
518 	// Title
519 	LineInfo* titleLine = new LineInfo;
520 	titleLine->text = fTitle;
521 	titleLine->font = *be_bold_font;
522 
523 	if (fParent->Layout() == AllTextRightOfIcon)
524 		titleLine->location = BPoint(iconRight, y);
525 	else
526 		titleLine->location = BPoint(kEdgePadding, y);
527 
528 	fLines.push_front(titleLine);
529 	y += fontHeight;
530 
531 	// Rest of text is rendered with be_plain_font.
532 	be_plain_font->GetHeight(&fh);
533 	fontHeight = ceilf(fh.leading) + ceilf(fh.descent)
534 		+ ceilf(fh.ascent);
535 
536 	// Split text into chunks between certain characters and compose the lines.
537 	const char kSeparatorCharacters[] = " \n-\\/";
538 	BString textBuffer = fText;
539 	textBuffer.ReplaceAll("\t", "    ");
540 	const char* chunkStart = textBuffer.String();
541 	float maxWidth = newMaxWidth - kEdgePadding - iconRight;
542 	LineInfo* line = NULL;
543 	ssize_t length = textBuffer.Length();
544 	while (chunkStart - textBuffer.String() < length) {
545 		size_t chunkLength = strcspn(chunkStart, kSeparatorCharacters) + 1;
546 
547 		// Start a new line if either we didn't start one before,
548 		// the current offset
549 		BString tempText;
550 		if (line != NULL)
551 			tempText.SetTo(line->text);
552 		tempText.Append(chunkStart, chunkLength);
553 
554 		if (line == NULL || chunkStart[0] == '\n'
555 			|| StringWidth(tempText) > maxWidth) {
556 			line = new LineInfo;
557 			line->font = *be_plain_font;
558 			line->location = BPoint(iconRight + kEdgePadding, y);
559 
560 			fLines.push_front(line);
561 			y += fontHeight;
562 
563 			// Skip the eventual new-line character at the beginning of this
564 			// chunk.
565 			if (chunkStart[0] == '\n') {
566 				chunkStart++;
567 				chunkLength--;
568 			}
569 			// Skip more new-line characters and move the line further down.
570 			while (chunkStart[0] == '\n') {
571 				chunkStart++;
572 				chunkLength--;
573 				line->location.y += fontHeight;
574 				y += fontHeight;
575 			}
576 			// Strip space at beginning of a new line.
577 			while (chunkStart[0] == ' ') {
578 				chunkLength--;
579 				chunkStart++;
580 			}
581 		}
582 
583 		if (chunkStart[0] == '\0')
584 			break;
585 
586 		// Append the chunk to the current line, which was either a new
587 		// line or the one from the previous iteration.
588 		line->text.Append(chunkStart, chunkLength);
589 
590 		chunkStart += chunkLength;
591 	}
592 
593 	fHeight = y + (kEdgePadding * 2);
594 
595 	// Make sure icon fits
596 	if (fBitmap != NULL) {
597 		float minHeight = 0;
598 		if (fParent->Layout() == TitleAboveIcon) {
599 			LineInfo* appLine = fLines.back();
600 			font_height fh;
601 			appLine->font.GetHeight(&fh);
602 			minHeight = appLine->location.y + fh.descent;
603 		}
604 
605 		minHeight += fBitmap->Bounds().Height() + 2 * kEdgePadding;
606 		if (fHeight < minHeight)
607 			fHeight = minHeight;
608 	}
609 
610 	BMessenger messenger(Parent());
611 	messenger.SendMessage(kResizeToFit);
612 }
613 
614 
615 bool
616 NotificationView::HasMessageID(const char* id)
617 {
618 	return fMessageID == id;
619 }
620 
621 
622 const char*
623 NotificationView::MessageID()
624 {
625 	return fMessageID.String();
626 }
627 
628 
629 void
630 NotificationView::SetPosition(bool first, bool last)
631 {
632 	fIsFirst = first;
633 	fIsLast = last;
634 }
635 
636 
637 BBitmap*
638 NotificationView::_ReadNodeIcon(const char* fileName, icon_size size)
639 {
640 	BEntry entry(fileName, true);
641 
642 	entry_ref ref;
643 	entry.GetRef(&ref);
644 
645 	BNode node(BPath(&ref).Path());
646 
647 	BBitmap* ret = new BBitmap(BRect(0, 0, (float)size - 1, (float)size - 1), B_RGBA32);
648 	if (BIconUtils::GetIcon(&node, kIconAttribute, kSmallIconAttribute,
649 		kLargeIconAttribute, size, ret) != B_OK) {
650 		delete ret;
651 		ret = NULL;
652 	}
653 
654 	return ret;
655 }
656 
657 
658 void
659 NotificationView::_LoadIcon()
660 {
661 	// First try to get the icon from the caller application
662 	app_info info;
663 	BMessenger msgr = fDetails->ReturnAddress();
664 
665 	if (msgr.IsValid())
666 		be_roster->GetRunningAppInfo(msgr.Team(), &info);
667 	else if (fType == B_PROGRESS_NOTIFICATION)
668 		be_roster->GetAppInfo("application/x-vnd.Haiku-notification_server",
669 			&info);
670 
671 	BPath path;
672 	path.SetTo(&info.ref);
673 
674 	fBitmap = _ReadNodeIcon(path.Path(), fParent->IconSize());
675 	if (fBitmap)
676 		return;
677 
678 	// If that failed get icons from app_server
679 	if (find_directory(B_BEOS_SERVERS_DIRECTORY, &path) != B_OK)
680 		return;
681 
682 	path.Append("app_server");
683 
684 	BFile file(path.Path(), B_READ_ONLY);
685 	if (file.InitCheck() != B_OK)
686 		return;
687 
688 	BResources res(&file);
689 	if (res.InitCheck() != B_OK)
690 		return;
691 
692 	// Which one should we choose?
693 	const char* iconName = "";
694 	switch (fType) {
695 		case B_INFORMATION_NOTIFICATION:
696 			iconName = "info";
697 			break;
698 		case B_ERROR_NOTIFICATION:
699 			iconName = "stop";
700 			break;
701 		case B_IMPORTANT_NOTIFICATION:
702 			iconName = "warn";
703 			break;
704 		default:
705 			return;
706 	}
707 
708 	// Allocate the bitmap
709 	fBitmap = new BBitmap(BRect(0, 0, (float)B_LARGE_ICON - 1,
710 		(float)B_LARGE_ICON - 1), B_RGBA32);
711 	if (!fBitmap || fBitmap->InitCheck() != B_OK) {
712 		fBitmap = NULL;
713 		return;
714 	}
715 
716 	// Load raw icon data
717 	size_t size = 0;
718 	const uint8* data = (const uint8*)res.LoadResource(B_VECTOR_ICON_TYPE,
719 		iconName, &size);
720 	if ((data == NULL
721 		|| BIconUtils::GetVectorIcon(data, size, fBitmap) != B_OK))
722 		fBitmap = NULL;
723 }
724