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