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