1/*
2 * Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
7 *		Stephan A��mus, superstippi@gmx.de
8 *		Brian Hill, supernova@tycho.email
9 */
10
11
12#include <Notification.h>
13
14#include <new>
15
16#include <stdlib.h>
17#include <string.h>
18
19#include <notification/Notifications.h>
20
21#include <Bitmap.h>
22#include <Message.h>
23#include <NodeInfo.h>
24#include <Path.h>
25#include <Roster.h>
26
27
28BNotification::BNotification(notification_type type)
29	:
30	BArchivable(),
31	fInitStatus(B_OK),
32	fType(type),
33	fProgress(0.f),
34	fFile(NULL),
35	fBitmap(NULL)
36{
37	team_info teamInfo;
38	get_team_info(B_CURRENT_TEAM, &teamInfo);
39	app_info appInfo;
40	be_roster->GetRunningAppInfo(teamInfo.team, &appInfo);
41
42	int32 iconSize = B_LARGE_ICON;
43	fBitmap = new BBitmap(BRect(0, 0, iconSize - 1, iconSize - 1), 0, B_RGBA32);
44	if (fBitmap) {
45		if (BNodeInfo::GetTrackerIcon(&appInfo.ref, fBitmap,
46			icon_size(iconSize)) != B_OK) {
47			delete fBitmap;
48			fBitmap = NULL;
49		}
50	}
51	fSourceSignature = appInfo.signature;
52	BPath path(&appInfo.ref);
53	if (path.InitCheck() == B_OK)
54		fSourceName = path.Leaf();
55}
56
57
58BNotification::BNotification(BMessage* archive)
59	:
60	BArchivable(archive),
61	fInitStatus(B_OK),
62	fProgress(0.0f),
63	fFile(NULL),
64	fBitmap(NULL)
65{
66	BString appName;
67	if (archive->FindString("_appname", &appName) == B_OK)
68		fSourceName = appName;
69
70	BString signature;
71	if (archive->FindString("_signature", &signature) == B_OK)
72		fSourceSignature = signature;
73
74	int32 type;
75	if (archive->FindInt32("_type", &type) == B_OK)
76		fType = (notification_type)type;
77	else
78		fInitStatus = B_ERROR;
79
80	BString group;
81	if (archive->FindString("_group", &group) == B_OK)
82		SetGroup(group);
83
84	BString title;
85	if (archive->FindString("_title", &title) == B_OK)
86		SetTitle(title);
87
88	BString content;
89	if (archive->FindString("_content", &content) == B_OK)
90		SetContent(content);
91
92	BString messageID;
93	if (archive->FindString("_messageID", &messageID) == B_OK)
94		SetMessageID(messageID);
95
96	float progress;
97	if (type == B_PROGRESS_NOTIFICATION
98		&& archive->FindFloat("_progress", &progress) == B_OK)
99		SetProgress(progress);
100
101	BString onClickApp;
102	if (archive->FindString("_onClickApp", &onClickApp) == B_OK)
103		SetOnClickApp(onClickApp);
104
105	entry_ref onClickFile;
106	if (archive->FindRef("_onClickFile", &onClickFile) == B_OK)
107		SetOnClickFile(&onClickFile);
108
109	entry_ref onClickRef;
110	int32 index = 0;
111	while (archive->FindRef("_onClickRef", index++, &onClickRef) == B_OK)
112		AddOnClickRef(&onClickRef);
113
114	BString onClickArgv;
115	index = 0;
116	while (archive->FindString("_onClickArgv", index++, &onClickArgv) == B_OK)
117		AddOnClickArg(onClickArgv);
118
119	status_t ret = B_OK;
120	BMessage icon;
121	if ((ret = archive->FindMessage("_icon", &icon)) == B_OK) {
122		BBitmap bitmap(&icon);
123		ret = bitmap.InitCheck();
124		if (ret == B_OK)
125			ret = SetIcon(&bitmap);
126	}
127}
128
129
130BNotification::~BNotification()
131{
132	delete fFile;
133	delete fBitmap;
134
135	for (int32 i = fRefs.CountItems() - 1; i >= 0; i--)
136		delete (entry_ref*)fRefs.ItemAtFast(i);
137
138	for (int32 i = fArgv.CountItems() - 1; i >= 0; i--)
139		free(fArgv.ItemAtFast(i));
140}
141
142
143/*! \brief Returns initialization status.
144 */
145status_t
146BNotification::InitCheck() const
147{
148	return fInitStatus;
149}
150
151
152/*! \brief Returns a new BNotification object from @archive.
153
154	Returns a new BNotification object, allocated by new and created
155	with the version of the constructor that takes BMessage archive.
156	However, if the message doesn't contain an archived data for a
157	BNotification object, this method returns NULL.
158
159	\return BNotification object from @archive or NULL if it doesn't
160			contain a valid BNotification object.
161*/
162BArchivable*
163BNotification::Instantiate(BMessage* archive)
164{
165	if (validate_instantiation(archive, "BNotification"))
166		return new(std::nothrow) BNotification(archive);
167
168	return NULL;
169}
170
171
172/*! \brief Archives the BNotification in the BMessages @archive.
173
174	\sa BArchivable::Archive(), Instantiate() static function.
175	\return
176	- \c B_OK: Everything went fine.
177	- \c Other errors: Archiving has failed.
178*/
179status_t
180BNotification::Archive(BMessage* archive, bool deep) const
181{
182	status_t status = BArchivable::Archive(archive, deep);
183
184	if (status == B_OK)
185		status = archive->AddString("_appname", fSourceName);
186
187	if (status == B_OK)
188		status = archive->AddString("_signature", fSourceSignature);
189
190	if (status == B_OK)
191		status = archive->AddInt32("_type", (int32)fType);
192
193	if (status == B_OK && Group() != NULL)
194		status = archive->AddString("_group", Group());
195
196	if (status == B_OK && Title() != NULL)
197		status = archive->AddString("_title", Title());
198
199	if (status == B_OK && Content() != NULL)
200		status = archive->AddString("_content", Content());
201
202	if (status == B_OK && MessageID() != NULL)
203		status = archive->AddString("_messageID", MessageID());
204
205	if (status == B_OK && Type() == B_PROGRESS_NOTIFICATION)
206		status = archive->AddFloat("_progress", Progress());
207
208	if (status == B_OK && OnClickApp() != NULL)
209		status = archive->AddString("_onClickApp", OnClickApp());
210
211	if (status == B_OK && OnClickFile() != NULL)
212		status = archive->AddRef("_onClickFile", OnClickFile());
213
214	if (status == B_OK) {
215		for (int32 i = 0; i < CountOnClickRefs(); i++) {
216			status = archive->AddRef("_onClickRef", OnClickRefAt(i));
217			if (status != B_OK)
218				break;
219		}
220	}
221
222	if (status == B_OK) {
223		for (int32 i = 0; i < CountOnClickArgs(); i++) {
224			status = archive->AddString("_onClickArgv", OnClickArgAt(i));
225			if (status != B_OK)
226				break;
227		}
228	}
229
230	if (status == B_OK) {
231		const BBitmap* icon = Icon();
232		if (icon != NULL) {
233			BMessage iconArchive;
234			status = icon->Archive(&iconArchive);
235			if (status == B_OK)
236				archive->AddMessage("_icon", &iconArchive);
237		}
238	}
239
240	return status;
241}
242
243
244/*! \brief Returns source application signature.
245
246	\return Source application signature.
247*/
248const char*
249BNotification::SourceSignature() const
250{
251	return fSourceSignature;
252}
253
254
255/*! \brief Returns source application name.
256
257	\return Source application name.
258*/
259const char*
260BNotification::SourceName() const
261{
262	return fSourceName;
263}
264
265
266/*! \brief Notification's type.
267
268	\return A value of the notification_type enum that represents
269			notification type.
270*/
271notification_type
272BNotification::Type() const
273{
274	return fType;
275}
276
277
278/*! \brief Returns notification's group.
279
280	\return Notification's group.
281*/
282const char*
283BNotification::Group() const
284{
285	if (fGroup == "")
286		return NULL;
287	return fGroup;
288}
289
290
291/*! \brief Sets notification's group.
292
293	Notifications can be grouped together setting the same group.
294*/
295void
296BNotification::SetGroup(const BString& group)
297{
298	fGroup = group;
299}
300
301
302/*! \brief Returns notification's title.
303
304	\return Notification's title.
305*/
306const char*
307BNotification::Title() const
308{
309	if (fTitle == "")
310		return NULL;
311	return fTitle;
312}
313
314
315/*! \brief Set notification's title.
316*/
317void
318BNotification::SetTitle(const BString& title)
319{
320	fTitle = title;
321}
322
323
324/*! \brief Returns notification's message.
325
326	\return Notification's message.
327*/
328const char*
329BNotification::Content() const
330{
331	if (fContent == "")
332		return NULL;
333	return fContent;
334}
335
336
337/*! \brief Sets notification's message.
338*/
339void
340BNotification::SetContent(const BString& content)
341{
342	fContent = content;
343}
344
345
346/*! \brief Returns notification's message identifier.
347
348	\return Notification's message identifier.
349*/
350const char*
351BNotification::MessageID() const
352{
353	if (fID == "")
354		return NULL;
355	return fID;
356}
357
358
359/*! \brief Sets notification's message identifier.
360*/
361void
362BNotification::SetMessageID(const BString& id)
363{
364	fID = id;
365}
366
367
368/*! \brief Returns progress information.
369
370	If notification's type is \c B_PROGRESS_NOTIFICATION, returns a value
371	between 0.0 and 1.0 that represent progress percentage.
372
373	If notification's type is not \c B_PROGRESS_NOTIFICATION, returns -1.
374
375	\return Percentage if notification's type is B_PROGRESS_NOTIFICATION
376			or otherwise -1.
377*/
378float
379BNotification::Progress() const
380{
381	if (fType != B_PROGRESS_NOTIFICATION)
382		return -1;
383	return fProgress;
384}
385
386
387/*! \brief Sets progress information.
388
389	Sets progress percentage, this information will be used only
390	if notification's type is \c B_PROGRESS_NOTIFICATION.
391
392	The value of @progress must be between 0.0 and 1.0.
393*/
394void
395BNotification::SetProgress(float progress)
396{
397	if (progress < 0)
398		fProgress = 0;
399	else if (progress > 1)
400		fProgress = 1;
401	else
402		fProgress = progress;
403}
404
405
406const char*
407BNotification::OnClickApp() const
408{
409	if (fApp == "")
410		return NULL;
411	return fApp;
412}
413
414
415void
416BNotification::SetOnClickApp(const BString& app)
417{
418	fApp = app;
419}
420
421
422const entry_ref*
423BNotification::OnClickFile() const
424{
425	return fFile;
426}
427
428
429status_t
430BNotification::SetOnClickFile(const entry_ref* file)
431{
432	delete fFile;
433
434	if (file != NULL) {
435		fFile = new(std::nothrow) entry_ref(*file);
436		if (fFile == NULL)
437			return B_NO_MEMORY;
438	} else
439		fFile = NULL;
440
441	return B_OK;
442}
443
444
445status_t
446BNotification::AddOnClickRef(const entry_ref* ref)
447{
448	if (ref == NULL)
449		return B_BAD_VALUE;
450
451	entry_ref* clonedRef = new(std::nothrow) entry_ref(*ref);
452	if (clonedRef == NULL || !fRefs.AddItem(clonedRef))
453		return B_NO_MEMORY;
454
455	return B_OK;
456}
457
458
459int32
460BNotification::CountOnClickRefs() const
461{
462	return fRefs.CountItems();
463}
464
465
466const entry_ref*
467BNotification::OnClickRefAt(int32 index) const
468{
469	return (entry_ref*)fRefs.ItemAt(index);
470}
471
472
473status_t
474BNotification::AddOnClickArg(const BString& arg)
475{
476	char* clonedArg = strdup(arg.String());
477	if (clonedArg == NULL || !fArgv.AddItem(clonedArg))
478		return B_NO_MEMORY;
479
480	return B_OK;
481}
482
483
484int32
485BNotification::CountOnClickArgs() const
486{
487	return fArgv.CountItems();
488}
489
490
491const char*
492BNotification::OnClickArgAt(int32 index) const
493{
494	return (char*)fArgv.ItemAt(index);
495}
496
497
498/*! \brief Notification's icon.
499
500	\return Notification's icon.
501*/
502const BBitmap*
503BNotification::Icon() const
504{
505	return fBitmap;
506}
507
508
509/*! \brief Sets notification's icon.
510
511	Sets notification's icon.
512	This method does not assume ownership of @icon.
513
514	\param icon Icon
515	\return
516	- \c B_OK: Everything went fine.
517	- \c B_NO_MEMORY: Allocation of @icon copy has failed.
518	- \c Other errors: Creation of @icon copy failed for some reason.
519*/
520status_t
521BNotification::SetIcon(const BBitmap* icon)
522{
523	delete fBitmap;
524
525	if (icon != NULL) {
526		fBitmap = new(std::nothrow) BBitmap(icon);
527		if (fBitmap == NULL)
528			return B_NO_MEMORY;
529		return fBitmap->InitCheck();
530	}
531
532	fBitmap = NULL;
533	return B_OK;
534}
535
536
537/*! \brief Sends a notification to the notification_server.
538
539	The notification is delivered asynchronously to the notification_server,
540	which will display it according to its settings and filters.
541
542	\param timeout Microseconds after the message fades out.
543	\return
544	- \c B_OK: Everything went fine.
545	- \c B_BAD_PORT_ID: A connection to notification_server could not be
546	  established or the server is not up and running anymore.
547	- \c Other errors: Building the message from the notification failed.
548*/
549status_t
550BNotification::Send(bigtime_t timeout)
551{
552	BMessage msg(kNotificationMessage);
553
554	// Archive notification
555	status_t ret = Archive(&msg);
556
557	// Custom time out
558	if (ret == B_OK && timeout > 0)
559		ret = msg.AddInt64("timeout", timeout);
560
561	// Send message
562	if (ret == B_OK) {
563		BMessenger server(kNotificationServerSignature);
564		ret = server.SendMessage(&msg);
565	}
566
567	return ret;
568}
569
570
571void BNotification::_ReservedNotification1() {}
572void BNotification::_ReservedNotification2() {}
573void BNotification::_ReservedNotification3() {}
574void BNotification::_ReservedNotification4() {}
575void BNotification::_ReservedNotification5() {}
576void BNotification::_ReservedNotification6() {}
577void BNotification::_ReservedNotification7() {}
578void BNotification::_ReservedNotification8() {}
579