(root)/
glib-2.79.0/
gio/
gnotification.c
       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  G_DEFINE_TYPE (GNotification, g_notification, G_TYPE_OBJECT)
     106  
     107  static void
     108  button_free (gpointer data)
     109  {
     110    Button *button = data;
     111  
     112    g_free (button->label);
     113    g_free (button->action_name);
     114    if (button->target)
     115      g_variant_unref (button->target);
     116  
     117    g_slice_free (Button, button);
     118  }
     119  
     120  static void
     121  g_notification_dispose (GObject *object)
     122  {
     123    GNotification *notification = G_NOTIFICATION (object);
     124  
     125    g_clear_object (&notification->icon);
     126  
     127    G_OBJECT_CLASS (g_notification_parent_class)->dispose (object);
     128  }
     129  
     130  static void
     131  g_notification_finalize (GObject *object)
     132  {
     133    GNotification *notification = G_NOTIFICATION (object);
     134  
     135    g_free (notification->title);
     136    g_free (notification->body);
     137    g_free (notification->category);
     138    g_free (notification->default_action);
     139    if (notification->default_action_target)
     140      g_variant_unref (notification->default_action_target);
     141    g_ptr_array_free (notification->buttons, TRUE);
     142  
     143    G_OBJECT_CLASS (g_notification_parent_class)->finalize (object);
     144  }
     145  
     146  static void
     147  g_notification_class_init (GNotificationClass *klass)
     148  {
     149    GObjectClass *object_class = G_OBJECT_CLASS (klass);
     150  
     151    object_class->dispose = g_notification_dispose;
     152    object_class->finalize = g_notification_finalize;
     153  }
     154  
     155  static void
     156  g_notification_init (GNotification *notification)
     157  {
     158    notification->buttons = g_ptr_array_new_full (2, button_free);
     159  }
     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  g_notification_new (const gchar *title)
     178  {
     179    GNotification *notification;
     180  
     181    g_return_val_if_fail (title != NULL, NULL);
     182  
     183    notification = g_object_new (G_TYPE_NOTIFICATION, NULL);
     184    notification->title = g_strdup (title);
     185  
     186    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  g_notification_get_title (GNotification *notification)
     201  {
     202    g_return_val_if_fail (G_IS_NOTIFICATION (notification), NULL);
     203  
     204    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  g_notification_set_title (GNotification *notification,
     218                            const gchar   *title)
     219  {
     220    g_return_if_fail (G_IS_NOTIFICATION (notification));
     221    g_return_if_fail (title != NULL);
     222  
     223    g_free (notification->title);
     224  
     225    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  g_notification_get_body (GNotification *notification)
     240  {
     241    g_return_val_if_fail (G_IS_NOTIFICATION (notification), NULL);
     242  
     243    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  g_notification_set_body (GNotification *notification,
     257                           const gchar   *body)
     258  {
     259    g_return_if_fail (G_IS_NOTIFICATION (notification));
     260    g_return_if_fail (body != NULL);
     261  
     262    g_free (notification->body);
     263  
     264    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  g_notification_get_icon (GNotification *notification)
     279  {
     280    g_return_val_if_fail (G_IS_NOTIFICATION (notification), NULL);
     281  
     282    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  g_notification_set_icon (GNotification *notification,
     296                           GIcon         *icon)
     297  {
     298    g_return_if_fail (G_IS_NOTIFICATION (notification));
     299  
     300    if (notification->icon)
     301      g_object_unref (notification->icon);
     302  
     303    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  g_notification_get_priority (GNotification *notification)
     316  {
     317    g_return_val_if_fail (G_IS_NOTIFICATION (notification), G_NOTIFICATION_PRIORITY_NORMAL);
     318  
     319    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  g_notification_set_urgent (GNotification *notification,
     335                             gboolean       urgent)
     336  {
     337    g_return_if_fail (G_IS_NOTIFICATION (notification));
     338  
     339    notification->priority = urgent ?
     340        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  g_notification_get_category (GNotification *notification)
     358  {
     359    g_return_val_if_fail (G_IS_NOTIFICATION (notification), NULL);
     360  
     361    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  g_notification_set_category (GNotification *notification,
     380                               const gchar   *category)
     381  {
     382    g_return_if_fail (G_IS_NOTIFICATION (notification));
     383    g_return_if_fail (category == NULL || *category != '\0');
     384  
     385    g_free (notification->category);
     386  
     387    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  g_notification_set_priority (GNotification         *notification,
     400                               GNotificationPriority  priority)
     401  {
     402    g_return_if_fail (G_IS_NOTIFICATION (notification));
     403  
     404    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  g_notification_add_button (GNotification *notification,
     426                             const gchar   *label,
     427                             const gchar   *detailed_action)
     428  {
     429    gchar *action;
     430    GVariant *target;
     431    GError *error = NULL;
     432  
     433    g_return_if_fail (detailed_action != NULL);
     434  
     435    if (!g_action_parse_detailed_name (detailed_action, &action, &target, &error))
     436      {
     437        g_warning ("%s: %s", G_STRFUNC, error->message);
     438        g_error_free (error);
     439        return;
     440      }
     441  
     442    g_notification_add_button_with_target_value (notification, label, action, target);
     443  
     444    g_free (action);
     445    if (target)
     446      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  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    GVariant *target = NULL;
     476  
     477    if (target_format)
     478      {
     479        va_start (args, target_format);
     480        target = g_variant_new_va (target_format, NULL, &args);
     481        va_end (args);
     482      }
     483  
     484    g_notification_add_button_with_target_value (notification, label, action, target);
     485  }
     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  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    g_return_if_fail (G_IS_NOTIFICATION (notification));
     511    g_return_if_fail (label != NULL);
     512    g_return_if_fail (action != NULL && g_action_name_is_valid (action));
     513  
     514    if (!g_str_has_prefix (action, "app."))
     515      {
     516        g_warning ("%s: action '%s' does not start with 'app.'."
     517                   "This is unlikely to work properly.", G_STRFUNC, action);
     518      }
     519  
     520    button =  g_slice_new0 (Button);
     521    button->label = g_strdup (label);
     522    button->action_name = g_strdup (action);
     523  
     524    if (target)
     525      button->target = g_variant_ref_sink (target);
     526  
     527    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  g_notification_get_n_buttons (GNotification *notification)
     538  {
     539    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  g_notification_get_button (GNotification  *notification,
     559                             gint            index,
     560                             gchar         **label,
     561                             gchar         **action,
     562                             GVariant      **target)
     563  {
     564    Button *button;
     565  
     566    button = g_ptr_array_index (notification->buttons, index);
     567  
     568    if (label)
     569      *label = g_strdup (button->label);
     570  
     571    if (action)
     572      *action = g_strdup (button->action_name);
     573  
     574    if (target)
     575      *target = button->target ? g_variant_ref (button->target) : NULL;
     576  }
     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  g_notification_get_button_with_action (GNotification *notification,
     588                                         const gchar   *action)
     589  {
     590    guint i;
     591  
     592    for (i = 0; i < notification->buttons->len; i++)
     593      {
     594        Button *button;
     595  
     596        button = g_ptr_array_index (notification->buttons, i);
     597        if (g_str_equal (action, button->action_name))
     598          return i;
     599      }
     600  
     601    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  g_notification_get_default_action (GNotification  *notification,
     623                                     gchar         **action,
     624                                     GVariant      **target)
     625  {
     626    if (notification->default_action == NULL)
     627      return FALSE;
     628  
     629    if (action)
     630      *action = g_strdup (notification->default_action);
     631  
     632    if (target)
     633      {
     634        if (notification->default_action_target)
     635          *target = g_variant_ref (notification->default_action_target);
     636        else
     637          *target = NULL;
     638      }
     639  
     640    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  g_notification_set_default_action (GNotification *notification,
     664                                     const gchar   *detailed_action)
     665  {
     666    gchar *action;
     667    GVariant *target;
     668    GError *error = NULL;
     669  
     670    if (!g_action_parse_detailed_name (detailed_action, &action, &target, &error))
     671      {
     672        g_warning ("%s: %s", G_STRFUNC, error->message);
     673        g_error_free (error);
     674        return;
     675      }
     676  
     677    g_notification_set_default_action_and_target_value (notification, action, target);
     678  
     679    g_free (action);
     680    if (target)
     681      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  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    GVariant *target = NULL;
     713  
     714    if (target_format)
     715      {
     716        va_start (args, target_format);
     717        target = g_variant_new_va (target_format, NULL, &args);
     718        va_end (args);
     719      }
     720  
     721    g_notification_set_default_action_and_target_value (notification, action, target);
     722  }
     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  g_notification_set_default_action_and_target_value (GNotification *notification,
     744                                                      const gchar   *action,
     745                                                      GVariant      *target)
     746  {
     747    g_return_if_fail (G_IS_NOTIFICATION (notification));
     748    g_return_if_fail (action != NULL && g_action_name_is_valid (action));
     749  
     750    if (!g_str_has_prefix (action, "app."))
     751      {
     752        g_warning ("%s: action '%s' does not start with 'app.'."
     753                   "This is unlikely to work properly.", G_STRFUNC, action);
     754      }
     755  
     756    g_free (notification->default_action);
     757    g_clear_pointer (&notification->default_action_target, g_variant_unref);
     758  
     759    notification->default_action = g_strdup (action);
     760  
     761    if (target)
     762      notification->default_action_target = g_variant_ref_sink (target);
     763  }
     764  
     765  static GVariant *
     766  g_notification_serialize_button (Button *button)
     767  {
     768    GVariantBuilder builder;
     769  
     770    g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
     771  
     772    g_variant_builder_add (&builder, "{sv}", "label", g_variant_new_string (button->label));
     773    g_variant_builder_add (&builder, "{sv}", "action", g_variant_new_string (button->action_name));
     774  
     775    if (button->target)
     776      g_variant_builder_add (&builder, "{sv}", "target", button->target);
     777  
     778    return g_variant_builder_end (&builder);
     779  }
     780  
     781  static GVariant *
     782  g_notification_get_priority_nick (GNotification *notification)
     783  {
     784    GEnumClass *enum_class;
     785    GEnumValue *value;
     786    GVariant *nick;
     787  
     788    enum_class = g_type_class_ref (G_TYPE_NOTIFICATION_PRIORITY);
     789    value = g_enum_get_value (enum_class, g_notification_get_priority (notification));
     790    g_assert (value != NULL);
     791    nick = g_variant_new_string (value->value_nick);
     792    g_type_class_unref (enum_class);
     793  
     794    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  g_notification_serialize (GNotification *notification)
     806  {
     807    GVariantBuilder builder;
     808  
     809    g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
     810  
     811    if (notification->title)
     812      g_variant_builder_add (&builder, "{sv}", "title", g_variant_new_string (notification->title));
     813  
     814    if (notification->body)
     815      g_variant_builder_add (&builder, "{sv}", "body", g_variant_new_string (notification->body));
     816  
     817    if (notification->icon)
     818      {
     819        GVariant *serialized_icon;
     820  
     821        if ((serialized_icon = g_icon_serialize (notification->icon)))
     822          {
     823            g_variant_builder_add (&builder, "{sv}", "icon", serialized_icon);
     824            g_variant_unref (serialized_icon);
     825          }
     826      }
     827  
     828    g_variant_builder_add (&builder, "{sv}", "priority", g_notification_get_priority_nick (notification));
     829  
     830    if (notification->default_action)
     831      {
     832        g_variant_builder_add (&builder, "{sv}", "default-action",
     833                                                 g_variant_new_string (notification->default_action));
     834  
     835        if (notification->default_action_target)
     836          g_variant_builder_add (&builder, "{sv}", "default-action-target",
     837                                                    notification->default_action_target);
     838      }
     839  
     840    if (notification->buttons->len > 0)
     841      {
     842        GVariantBuilder actions_builder;
     843        guint i;
     844  
     845        g_variant_builder_init (&actions_builder, G_VARIANT_TYPE ("aa{sv}"));
     846  
     847        for (i = 0; i < notification->buttons->len; i++)
     848          {
     849            Button *button = g_ptr_array_index (notification->buttons, i);
     850            g_variant_builder_add (&actions_builder, "@a{sv}", g_notification_serialize_button (button));
     851          }
     852  
     853        g_variant_builder_add (&builder, "{sv}", "buttons", g_variant_builder_end (&actions_builder));
     854      }
     855  
     856    return g_variant_builder_end (&builder);
     857  }