nexmon – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /*
2 * Copyright © 2013 Lars Uebernickel
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General
15 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authors: Lars Uebernickel <lars@uebernic.de>
18 */
19  
20 #include "config.h"
21  
22 #include "gnotification-private.h"
23 #include "gdbusutils.h"
24 #include "gicon.h"
25 #include "gaction.h"
26 #include "gioenumtypes.h"
27  
28 /**
29 * SECTION:gnotification
30 * @short_description: User Notifications (pop up messages)
31 * @include: gio/gio.h
32 *
33 * #GNotification is a mechanism for creating a notification to be shown
34 * to the user -- typically as a pop-up notification presented by the
35 * desktop environment shell.
36 *
37 * The key difference between #GNotification and other similar APIs is
38 * that, if supported by the desktop environment, notifications sent
39 * with #GNotification will persist after the application has exited,
40 * and even across system reboots.
41 *
42 * Since the user may click on a notification while the application is
43 * not running, applications using #GNotification should be able to be
44 * started as a D-Bus service, using #GApplication.
45 *
46 * User interaction with a notification (either the default action, or
47 * buttons) must be associated with actions on the application (ie:
48 * "app." actions). It is not possible to route user interaction
49 * through the notification itself, because the object will not exist if
50 * the application is autostarted as a result of a notification being
51 * clicked.
52 *
53 * A notification can be sent with g_application_send_notification().
54 *
55 * Since: 2.40
56 **/
57  
58 /**
59 * GNotification:
60 *
61 * This structure type is private and should only be accessed using the
62 * public APIs.
63 *
64 * Since: 2.40
65 **/
66  
67 typedef GObjectClass GNotificationClass;
68  
69 struct _GNotification
70 {
71 GObject parent;
72  
73 gchar *title;
74 gchar *body;
75 GIcon *icon;
76 GNotificationPriority priority;
77 GPtrArray *buttons;
78 gchar *default_action;
79 GVariant *default_action_target;
80 };
81  
82 typedef struct
83 {
84 gchar *label;
85 gchar *action_name;
86 GVariant *target;
87 } Button;
88  
89 G_DEFINE_TYPE (GNotification, g_notification, G_TYPE_OBJECT);
90  
91 static void
92 button_free (gpointer data)
93 {
94 Button *button = data;
95  
96 g_free (button->label);
97 g_free (button->action_name);
98 if (button->target)
99 g_variant_unref (button->target);
100  
101 g_slice_free (Button, button);
102 }
103  
104 static void
105 g_notification_dispose (GObject *object)
106 {
107 GNotification *notification = G_NOTIFICATION (object);
108  
109 g_clear_object (&notification->icon);
110  
111 G_OBJECT_CLASS (g_notification_parent_class)->dispose (object);
112 }
113  
114 static void
115 g_notification_finalize (GObject *object)
116 {
117 GNotification *notification = G_NOTIFICATION (object);
118  
119 g_free (notification->title);
120 g_free (notification->body);
121 g_free (notification->default_action);
122 if (notification->default_action_target)
123 g_variant_unref (notification->default_action_target);
124 g_ptr_array_free (notification->buttons, TRUE);
125  
126 G_OBJECT_CLASS (g_notification_parent_class)->finalize (object);
127 }
128  
129 static void
130 g_notification_class_init (GNotificationClass *klass)
131 {
132 GObjectClass *object_class = G_OBJECT_CLASS (klass);
133  
134 object_class->dispose = g_notification_dispose;
135 object_class->finalize = g_notification_finalize;
136 }
137  
138 static void
139 g_notification_init (GNotification *notification)
140 {
141 notification->buttons = g_ptr_array_new_full (2, button_free);
142 }
143  
144 /**
145 * g_notification_new:
146 * @title: the title of the notification
147 *
148 * Creates a new #GNotification with @title as its title.
149 *
150 * After populating @notification with more details, it can be sent to
151 * the desktop shell with g_application_send_notification(). Changing
152 * any properties after this call will not have any effect until
153 * resending @notification.
154 *
155 * Returns: a new #GNotification instance
156 *
157 * Since: 2.40
158 */
159 GNotification *
160 g_notification_new (const gchar *title)
161 {
162 GNotification *notification;
163  
164 g_return_val_if_fail (title != NULL, NULL);
165  
166 notification = g_object_new (G_TYPE_NOTIFICATION, NULL);
167 notification->title = g_strdup (title);
168  
169 return notification;
170 }
171  
172 /*< private >
173 * g_notification_get_title:
174 * @notification: a #GNotification
175 *
176 * Gets the title of @notification.
177 *
178 * Returns: the title of @notification
179 *
180 * Since: 2.40
181 */
182 const gchar *
183 g_notification_get_title (GNotification *notification)
184 {
185 g_return_val_if_fail (G_IS_NOTIFICATION (notification), NULL);
186  
187 return notification->title;
188 }
189  
190 /**
191 * g_notification_set_title:
192 * @notification: a #GNotification
193 * @title: the new title for @notification
194 *
195 * Sets the title of @notification to @title.
196 *
197 * Since: 2.40
198 */
199 void
200 g_notification_set_title (GNotification *notification,
201 const gchar *title)
202 {
203 g_return_if_fail (G_IS_NOTIFICATION (notification));
204 g_return_if_fail (title != NULL);
205  
206 g_free (notification->title);
207  
208 notification->title = g_strdup (title);
209 }
210  
211 /*< private >
212 * g_notification_get_body:
213 * @notification: a #GNotification
214 *
215 * Gets the current body of @notification.
216 *
217 * Returns: (allow-none): the body of @notification
218 *
219 * Since: 2.40
220 */
221 const gchar *
222 g_notification_get_body (GNotification *notification)
223 {
224 g_return_val_if_fail (G_IS_NOTIFICATION (notification), NULL);
225  
226 return notification->body;
227 }
228  
229 /**
230 * g_notification_set_body:
231 * @notification: a #GNotification
232 * @body: (allow-none): the new body for @notification, or %NULL
233 *
234 * Sets the body of @notification to @body.
235 *
236 * Since: 2.40
237 */
238 void
239 g_notification_set_body (GNotification *notification,
240 const gchar *body)
241 {
242 g_return_if_fail (G_IS_NOTIFICATION (notification));
243 g_return_if_fail (body != NULL);
244  
245 g_free (notification->body);
246  
247 notification->body = g_strdup (body);
248 }
249  
250 /*< private >
251 * g_notification_get_icon:
252 * @notification: a #GNotification
253 *
254 * Gets the icon currently set on @notification.
255 *
256 * Returns: (transfer none): the icon associated with @notification
257 *
258 * Since: 2.40
259 */
260 GIcon *
261 g_notification_get_icon (GNotification *notification)
262 {
263 g_return_val_if_fail (G_IS_NOTIFICATION (notification), NULL);
264  
265 return notification->icon;
266 }
267  
268 /**
269 * g_notification_set_icon:
270 * @notification: a #GNotification
271 * @icon: the icon to be shown in @notification, as a #GIcon
272 *
273 * Sets the icon of @notification to @icon.
274 *
275 * Since: 2.40
276 */
277 void
278 g_notification_set_icon (GNotification *notification,
279 GIcon *icon)
280 {
281 g_return_if_fail (G_IS_NOTIFICATION (notification));
282  
283 if (notification->icon)
284 g_object_unref (notification->icon);
285  
286 notification->icon = g_object_ref (icon);
287 }
288  
289 /*< private >
290 * g_notification_get_priority:
291 * @notification: a #GNotification
292 *
293 * Returns the priority of @notification
294 *
295 * Since: 2.42
296 */
297 GNotificationPriority
298 g_notification_get_priority (GNotification *notification)
299 {
300 g_return_val_if_fail (G_IS_NOTIFICATION (notification), G_NOTIFICATION_PRIORITY_NORMAL);
301  
302 return notification->priority;
303 }
304  
305 /**
306 * g_notification_set_urgent:
307 * @notification: a #GNotification
308 * @urgent: %TRUE if @notification is urgent
309 *
310 * Deprecated in favor of g_notification_set_priority().
311 *
312 * Since: 2.40
313 */
314 void
315 g_notification_set_urgent (GNotification *notification,
316 gboolean urgent)
317 {
318 g_return_if_fail (G_IS_NOTIFICATION (notification));
319  
320 g_notification_set_priority (notification, G_NOTIFICATION_PRIORITY_URGENT);
321 }
322  
323 /**
324 * g_notification_set_priority:
325 * @notification: a #GNotification
326 * @priority: a #GNotificationPriority
327 *
328 * Sets the priority of @notification to @priority. See
329 * #GNotificationPriority for possible values.
330 */
331 void
332 g_notification_set_priority (GNotification *notification,
333 GNotificationPriority priority)
334 {
335 g_return_if_fail (G_IS_NOTIFICATION (notification));
336  
337 notification->priority = priority;
338 }
339  
340 /**
341 * g_notification_add_button:
342 * @notification: a #GNotification
343 * @label: label of the button
344 * @detailed_action: a detailed action name
345 *
346 * Adds a button to @notification that activates the action in
347 * @detailed_action when clicked. That action must be an
348 * application-wide action (starting with "app."). If @detailed_action
349 * contains a target, the action will be activated with that target as
350 * its parameter.
351 *
352 * See g_action_parse_detailed_name() for a description of the format
353 * for @detailed_action.
354 *
355 * Since: 2.40
356 */
357 void
358 g_notification_add_button (GNotification *notification,
359 const gchar *label,
360 const gchar *detailed_action)
361 {
362 gchar *action;
363 GVariant *target;
364 GError *error = NULL;
365  
366 g_return_if_fail (detailed_action != NULL);
367  
368 if (!g_action_parse_detailed_name (detailed_action, &action, &target, &error))
369 {
370 g_warning ("%s: %s", G_STRFUNC, error->message);
371 g_error_free (error);
372 return;
373 }
374  
375 g_notification_add_button_with_target_value (notification, label, action, target);
376  
377 g_free (action);
378 if (target)
379 g_variant_unref (target);
380 }
381  
382 /**
383 * g_notification_add_button_with_target: (skip)
384 * @notification: a #GNotification
385 * @label: label of the button
386 * @action: an action name
387 * @target_format: (allow-none): a #GVariant format string, or %NULL
388 * @...: positional parameters, as determined by @target_format
389 *
390 * Adds a button to @notification that activates @action when clicked.
391 * @action must be an application-wide action (it must start with "app.").
392 *
393 * If @target_format is given, it is used to collect remaining
394 * positional parameters into a #GVariant instance, similar to
395 * g_variant_new(). @action will be activated with that #GVariant as its
396 * parameter.
397 *
398 * Since: 2.40
399 */
400 void
401 g_notification_add_button_with_target (GNotification *notification,
402 const gchar *label,
403 const gchar *action,
404 const gchar *target_format,
405 ...)
406 {
407 va_list args;
408 GVariant *target = NULL;
409  
410 if (target_format)
411 {
412 va_start (args, target_format);
413 target = g_variant_new_va (target_format, NULL, &args);
414 va_end (args);
415 }
416  
417 g_notification_add_button_with_target_value (notification, label, action, target);
418 }
419  
420 /**
421 * g_notification_add_button_with_target_value: (rename-to g_notification_add_button_with_target)
422 * @notification: a #GNotification
423 * @label: label of the button
424 * @action: an action name
425 * @target: (allow-none): a #GVariant to use as @action's parameter, or %NULL
426 *
427 * Adds a button to @notification that activates @action when clicked.
428 * @action must be an application-wide action (it must start with "app.").
429 *
430 * If @target is non-%NULL, @action will be activated with @target as
431 * its parameter.
432 *
433 * Since: 2.40
434 */
435 void
436 g_notification_add_button_with_target_value (GNotification *notification,
437 const gchar *label,
438 const gchar *action,
439 GVariant *target)
440 {
441 Button *button;
442  
443 g_return_if_fail (G_IS_NOTIFICATION (notification));
444 g_return_if_fail (label != NULL);
445 g_return_if_fail (action != NULL && g_action_name_is_valid (action));
446  
447 if (!g_str_has_prefix (action, "app."))
448 {
449 g_warning ("%s: action '%s' does not start with 'app.'."
450 "This is unlikely to work properly.", G_STRFUNC, action);
451 }
452  
453 button = g_slice_new0 (Button);
454 button->label = g_strdup (label);
455 button->action_name = g_strdup (action);
456  
457 if (target)
458 button->target = g_variant_ref_sink (target);
459  
460 g_ptr_array_add (notification->buttons, button);
461 }
462  
463 /*< private >
464 * g_notification_get_n_buttons:
465 * @notification: a #GNotification
466 *
467 * Returns: the amount of buttons added to @notification.
468 */
469 guint
470 g_notification_get_n_buttons (GNotification *notification)
471 {
472 return notification->buttons->len;
473 }
474  
475 /*< private >
476 * g_notification_get_button:
477 * @notification: a #GNotification
478 * @index: index of the button
479 * @label: (): return location for the button's label
480 * @action: (): return location for the button's associated action
481 * @target: (): return location for the target @action should be
482 * activated with
483 *
484 * Returns a description of a button that was added to @notification
485 * with g_notification_add_button().
486 *
487 * @index must be smaller than the value returned by
488 * g_notification_get_n_buttons().
489 */
490 void
491 g_notification_get_button (GNotification *notification,
492 gint index,
493 gchar **label,
494 gchar **action,
495 GVariant **target)
496 {
497 Button *button;
498  
499 button = g_ptr_array_index (notification->buttons, index);
500  
501 if (label)
502 *label = g_strdup (button->label);
503  
504 if (action)
505 *action = g_strdup (button->action_name);
506  
507 if (target)
508 *target = button->target ? g_variant_ref (button->target) : NULL;
509 }
510  
511 /*< private >
512 * g_notification_get_button_with_action:
513 * @notification: a #GNotification
514 * @action: an action name
515 *
516 * Returns the index of the button in @notification that is associated
517 * with @action, or -1 if no such button exists.
518 */
519 gint
520 g_notification_get_button_with_action (GNotification *notification,
521 const gchar *action)
522 {
523 guint i;
524  
525 for (i = 0; i < notification->buttons->len; i++)
526 {
527 Button *button;
528  
529 button = g_ptr_array_index (notification->buttons, i);
530 if (g_str_equal (action, button->action_name))
531 return i;
532 }
533  
534 return -1;
535 }
536  
537  
538 /*< private >
539 * g_notification_get_default_action:
540 * @notification: a #GNotification
541 * @action: (allow-none): return location for the default action
542 * @target: (allow-none): return location for the target of the default action
543 *
544 * Gets the action and target for the default action of @notification.
545 *
546 * Returns: %TRUE if @notification has a default action
547 */
548 gboolean
549 g_notification_get_default_action (GNotification *notification,
550 gchar **action,
551 GVariant **target)
552 {
553 if (notification->default_action == NULL)
554 return FALSE;
555  
556 if (action)
557 *action = g_strdup (notification->default_action);
558  
559 if (target)
560 {
561 if (notification->default_action_target)
562 *target = g_variant_ref (notification->default_action_target);
563 else
564 *target = NULL;
565 }
566  
567 return TRUE;
568 }
569  
570 /**
571 * g_notification_set_default_action:
572 * @notification: a #GNotification
573 * @detailed_action: a detailed action name
574 *
575 * Sets the default action of @notification to @detailed_action. This
576 * action is activated when the notification is clicked on.
577 *
578 * The action in @detailed_action must be an application-wide action (it
579 * must start with "app."). If @detailed_action contains a target, the
580 * given action will be activated with that target as its parameter.
581 * See g_action_parse_detailed_name() for a description of the format
582 * for @detailed_action.
583 *
584 * When no default action is set, the application that the notification
585 * was sent on is activated.
586 *
587 * Since: 2.40
588 */
589 void
590 g_notification_set_default_action (GNotification *notification,
591 const gchar *detailed_action)
592 {
593 gchar *action;
594 GVariant *target;
595 GError *error = NULL;
596  
597 if (!g_action_parse_detailed_name (detailed_action, &action, &target, &error))
598 {
599 g_warning ("%s: %s", G_STRFUNC, error->message);
600 g_error_free (error);
601 return;
602 }
603  
604 g_notification_set_default_action_and_target_value (notification, action, target);
605  
606 g_free (action);
607 if (target)
608 g_variant_unref (target);
609 }
610  
611 /**
612 * g_notification_set_default_action_and_target: (skip)
613 * @notification: a #GNotification
614 * @action: an action name
615 * @target_format: (allow-none): a #GVariant format string, or %NULL
616 * @...: positional parameters, as determined by @target_format
617 *
618 * Sets the default action of @notification to @action. This action is
619 * activated when the notification is clicked on. It must be an
620 * application-wide action (it must start with "app.").
621 *
622 * If @target_format is given, it is used to collect remaining
623 * positional parameters into a #GVariant instance, similar to
624 * g_variant_new(). @action will be activated with that #GVariant as its
625 * parameter.
626 *
627 * When no default action is set, the application that the notification
628 * was sent on is activated.
629 *
630 * Since: 2.40
631 */
632 void
633 g_notification_set_default_action_and_target (GNotification *notification,
634 const gchar *action,
635 const gchar *target_format,
636 ...)
637 {
638 va_list args;
639 GVariant *target = NULL;
640  
641 if (target_format)
642 {
643 va_start (args, target_format);
644 target = g_variant_new_va (target_format, NULL, &args);
645 va_end (args);
646 }
647  
648 g_notification_set_default_action_and_target_value (notification, action, target);
649 }
650  
651 /**
652 * g_notification_set_default_action_and_target_value: (rename-to g_notification_set_default_action_and_target)
653 * @notification: a #GNotification
654 * @action: an action name
655 * @target: (allow-none): a #GVariant to use as @action's parameter, or %NULL
656 *
657 * Sets the default action of @notification to @action. This action is
658 * activated when the notification is clicked on. It must be an
659 * application-wide action (start with "app.").
660 *
661 * If @target is non-%NULL, @action will be activated with @target as
662 * its parameter.
663 *
664 * When no default action is set, the application that the notification
665 * was sent on is activated.
666 *
667 * Since: 2.40
668 */
669 void
670 g_notification_set_default_action_and_target_value (GNotification *notification,
671 const gchar *action,
672 GVariant *target)
673 {
674 g_return_if_fail (G_IS_NOTIFICATION (notification));
675 g_return_if_fail (action != NULL && g_action_name_is_valid (action));
676  
677 if (!g_str_has_prefix (action, "app."))
678 {
679 g_warning ("%s: action '%s' does not start with 'app.'."
680 "This is unlikely to work properly.", G_STRFUNC, action);
681 }
682  
683 g_free (notification->default_action);
684 g_clear_pointer (&notification->default_action_target, g_variant_unref);
685  
686 notification->default_action = g_strdup (action);
687  
688 if (target)
689 notification->default_action_target = g_variant_ref_sink (target);
690 }
691  
692 static GVariant *
693 g_notification_serialize_button (Button *button)
694 {
695 GVariantBuilder builder;
696  
697 g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
698  
699 g_variant_builder_add (&builder, "{sv}", "label", g_variant_new_string (button->label));
700 g_variant_builder_add (&builder, "{sv}", "action", g_variant_new_string (button->action_name));
701  
702 if (button->target)
703 g_variant_builder_add (&builder, "{sv}", "target", button->target);
704  
705 return g_variant_builder_end (&builder);
706 }
707  
708 static GVariant *
709 g_notification_get_priority_nick (GNotification *notification)
710 {
711 GEnumClass *enum_class;
712 GEnumValue *value;
713 GVariant *nick;
714  
715 enum_class = g_type_class_ref (G_TYPE_NOTIFICATION_PRIORITY);
716 value = g_enum_get_value (enum_class, g_notification_get_priority (notification));
717 g_assert (value != NULL);
718 nick = g_variant_new_string (value->value_nick);
719 g_type_class_unref (enum_class);
720  
721 return nick;
722 }
723  
724 /*< private >
725 * g_notification_serialize:
726 *
727 * Serializes @notification into an floating variant of type a{sv}.
728 *
729 * Returns: the serialized @notification as a floating variant.
730 */
731 GVariant *
732 g_notification_serialize (GNotification *notification)
733 {
734 GVariantBuilder builder;
735  
736 g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
737  
738 if (notification->title)
739 g_variant_builder_add (&builder, "{sv}", "title", g_variant_new_string (notification->title));
740  
741 if (notification->body)
742 g_variant_builder_add (&builder, "{sv}", "body", g_variant_new_string (notification->body));
743  
744 if (notification->icon)
745 {
746 GVariant *serialized_icon;
747  
748 if ((serialized_icon = g_icon_serialize (notification->icon)))
749 {
750 g_variant_builder_add (&builder, "{sv}", "icon", serialized_icon);
751 g_variant_unref (serialized_icon);
752 }
753 }
754  
755 g_variant_builder_add (&builder, "{sv}", "priority", g_notification_get_priority_nick (notification));
756  
757 if (notification->default_action)
758 {
759 g_variant_builder_add (&builder, "{sv}", "default-action",
760 g_variant_new_string (notification->default_action));
761  
762 if (notification->default_action_target)
763 g_variant_builder_add (&builder, "{sv}", "default-action-target",
764 notification->default_action_target);
765 }
766  
767 if (notification->buttons->len > 0)
768 {
769 GVariantBuilder actions_builder;
770 guint i;
771  
772 g_variant_builder_init (&actions_builder, G_VARIANT_TYPE ("aa{sv}"));
773  
774 for (i = 0; i < notification->buttons->len; i++)
775 {
776 Button *button = g_ptr_array_index (notification->buttons, i);
777 g_variant_builder_add (&actions_builder, "@a{sv}", g_notification_serialize_button (button));
778 }
779  
780 g_variant_builder_add (&builder, "{sv}", "buttons", g_variant_builder_end (&actions_builder));
781 }
782  
783 return g_variant_builder_end (&builder);
784 }