/* gtd-notification.c * * Copyright (C) 2015 Georges Basile Stavracas Neto * Copyright (C) 2022 Jamie Murphy * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define G_LOG_DOMAIN "GtdNotification" #include "gtd-notification.h" #include "gtd-object.h" #include /** * SECTION: gtd-notification * @short_description: An auxiliary class around #AdwToast * @title: GtdNotification * @stability: Stable * * The #GtdNotification represents a notification shown at the top of * the window. It is an auxiliary class around #AdwToast, and is used only * to store toast data and callbacks. * * A notification may have a dismissal action that is called when the toast * is dismissed, whether by user interaction or a timeout * * Optionally, the notification may have a secondary action * (see gtd_notification_set_secondary_action()), shown as a button. * * A user should not create a UI for a #GtdNotification and should instead * pass it to a #GtdManager, via gtd_manager_send_notification() * * Example: * |[ * GtdNotification *notification; * * notification = gtd_notification_new ("Something happened!"); * * gtd_notification_set_dismissal_action (notification, * called_when_notification_is_dismissed, * self); * * gtd_notification_set_secondary_action (notification, * "Details", * show_details, * self); * [...] * ]| */ typedef struct { gchar *text; GtdNotificationActionFunc dismissal_action; gboolean has_dismissal_action; gpointer dismissal_action_data; GtdNotificationActionFunc secondary_action; gboolean has_secondary_action; gpointer secondary_action_data; gchar *secondary_action_name; } GtdNotificationPrivate; struct _GtdNotification { GtdObject parent; /*< private >*/ GtdNotificationPrivate *priv; }; G_DEFINE_TYPE_WITH_PRIVATE (GtdNotification, gtd_notification, GTD_TYPE_OBJECT) enum { PROP_0, PROP_HAS_DISMISSAL_ACTION, PROP_HAS_SECONDARY_ACTION, PROP_SECONDARY_ACTION_NAME, PROP_TEXT, LAST_PROP }; enum { EXECUTED, NUM_SIGNALS }; static guint signals[NUM_SIGNALS] = { 0, }; static void gtd_notification_finalize (GObject *object) { GtdNotification *self = (GtdNotification *)object; GtdNotificationPrivate *priv = gtd_notification_get_instance_private (self); g_clear_pointer (&priv->secondary_action_name, g_free); g_clear_pointer (&priv->text, g_free); G_OBJECT_CLASS (gtd_notification_parent_class)->finalize (object); } static void gtd_notification_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GtdNotification *self = GTD_NOTIFICATION (object); switch (prop_id) { case PROP_HAS_DISMISSAL_ACTION: g_value_set_boolean (value, self->priv->has_dismissal_action); break; case PROP_HAS_SECONDARY_ACTION: g_value_set_boolean (value, self->priv->has_secondary_action); break; case PROP_SECONDARY_ACTION_NAME: g_value_set_string (value, self->priv->secondary_action_name ? self->priv->secondary_action_name : ""); break; case PROP_TEXT: g_value_set_string (value, gtd_notification_get_text (self)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void gtd_notification_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GtdNotification *self = GTD_NOTIFICATION (object); switch (prop_id) { case PROP_SECONDARY_ACTION_NAME: gtd_notification_set_secondary_action (self, g_value_get_string (value), self->priv->secondary_action, self->priv->secondary_action_data); break; case PROP_TEXT: gtd_notification_set_text (self, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void gtd_notification_class_init (GtdNotificationClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gtd_notification_finalize; object_class->get_property = gtd_notification_get_property; object_class->set_property = gtd_notification_set_property; /** * GtdNotification::has-dismissal-action: * * @TRUE if the notification has a dismissal action or @FALSE otherwise. */ g_object_class_install_property ( object_class, PROP_HAS_DISMISSAL_ACTION, g_param_spec_boolean ("has-dismissal-action", "Whether the notification has a dismissal action", "Whether the notification has the dismissal action.", FALSE, G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY)); /** * GtdNotification::has-secondary-action: * * @TRUE if the notification has a secondary action or @FALSE otherwise. The * secondary action is triggered only by user explicit input. */ g_object_class_install_property ( object_class, PROP_HAS_SECONDARY_ACTION, g_param_spec_boolean ("has-secondary-action", "Whether the notification has a secondary action", "Whether the notification has the secondary action, activated by the user", FALSE, G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY)); /** * GtdNotification::secondary-action-name: * * The main text of the notification, usually a markuped text. */ g_object_class_install_property ( object_class, PROP_SECONDARY_ACTION_NAME, g_param_spec_string ("secondary-action-name", "Text of the secondary action button", "The text of the secondary action button", "", G_PARAM_READWRITE)); /** * GtdNotification::text: * * The main text of the notification, usually a markuped text. */ g_object_class_install_property ( object_class, PROP_TEXT, g_param_spec_string ("text", "Notification message", "The main message of the notification", "", G_PARAM_READWRITE)); /** * GtdNotification::executed: * * The ::executed signal is emitted after the primary or secondary * #GtdNotification action is executed. */ signals[EXECUTED] = g_signal_new ("executed", GTD_TYPE_NOTIFICATION, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } static void gtd_notification_init (GtdNotification *self) { self->priv = gtd_notification_get_instance_private (self); self->priv->secondary_action_name = NULL; self->priv->text = NULL; } /** * gtd_notification_new: * @text: (nullable): text of the notification * * Creates a new notification with @text. * * Returns: (transfer full): a new #GtdNotification */ GtdNotification* gtd_notification_new (const gchar *text) { return g_object_new (GTD_TYPE_NOTIFICATION, "text", text, NULL); } /** * gtd_notification_set_dismissal_action: * @notification: a #GtdNotification * @func: (closure user_data) (scope call) (nullable): the dismissal action function * @user_data: data passed to @func * * Sets the dismissal action of @notification */ void gtd_notification_set_dismissal_action (GtdNotification *notification, GtdNotificationActionFunc func, gpointer user_data) { GtdNotificationPrivate *priv; gboolean has_action; g_return_if_fail (GTD_IS_NOTIFICATION (notification)); priv = notification->priv; has_action = (func != NULL); if (has_action != priv->has_dismissal_action) { priv->has_dismissal_action = has_action; priv->dismissal_action = has_action ? func : NULL; priv->dismissal_action_data = has_action ? user_data : NULL; g_object_notify (G_OBJECT (notification), "has-dismissal-action"); } } /** * gtd_notification_set_secondary_action: * @notification: a #GtdNotification * @name: the name of the secondary action * @func: (closure user_data) (scope call) (nullable): the secondary action function * @user_data: data passed to @func * * Sets the secondary action of @notification, which is triggered * only on user explicit input. */ void gtd_notification_set_secondary_action (GtdNotification *notification, const gchar *name, GtdNotificationActionFunc func, gpointer user_data) { GtdNotificationPrivate *priv; gboolean has_action; g_return_if_fail (GTD_IS_NOTIFICATION (notification)); priv = notification->priv; has_action = (func != NULL); if (has_action != priv->has_secondary_action) { priv->has_secondary_action = has_action; priv->secondary_action = has_action ? func : NULL; priv->secondary_action_data = has_action ? user_data : NULL; if (priv->secondary_action_name != name) { g_clear_pointer (&priv->secondary_action_name, g_free); priv->secondary_action_name = g_strdup (name); g_object_notify (G_OBJECT (notification), "secondary-action-name"); } g_object_notify (G_OBJECT (notification), "has-secondary-action"); } } /** * gtd_notification_get_text: * @notification: a #GtdNotification * * Gets the text of @notification. * * Returns: (transfer none): the text of @notification. */ const gchar* gtd_notification_get_text (GtdNotification *notification) { g_return_val_if_fail (GTD_IS_NOTIFICATION (notification), NULL); return notification->priv->text ? notification->priv->text : ""; } /** * gtd_notification_set_text: * @notification: a #GtdNotification * @text: the user-visible text of @notification * * Sets the text of @notification to @text. */ void gtd_notification_set_text (GtdNotification *notification, const gchar *text) { GtdNotificationPrivate *priv; g_return_if_fail (GTD_IS_NOTIFICATION (notification)); priv = notification->priv; if (g_strcmp0 (priv->text, text) != 0) { g_clear_pointer (&priv->text, g_free); priv->text = g_strdup (text); g_object_notify (G_OBJECT (notification), "text"); } } /** * gtd_notification_execute_dismissal_action: * @notification: a #GtdNotification * * Executes the dismissal action of @notification if any. */ void gtd_notification_execute_dismissal_action (GtdNotification *notification) { GtdNotificationPrivate *priv; g_return_if_fail (GTD_IS_NOTIFICATION (notification)); priv = notification->priv; if (priv->dismissal_action) priv->dismissal_action (notification, priv->dismissal_action_data); g_signal_emit (notification, signals[EXECUTED], 0); } /** * gtd_notification_execute_secondary_action: * @notification: a #GtdNotification * * Executes the secondary action of @notification if any. */ void gtd_notification_execute_secondary_action (GtdNotification *notification) { GtdNotificationPrivate *priv; g_return_if_fail (GTD_IS_NOTIFICATION (notification)); priv = notification->priv; if (priv->secondary_action) { priv->secondary_action (notification, priv->secondary_action_data); g_signal_emit (notification, signals[EXECUTED], 0); } }