diff options
Diffstat (limited to 'src/core/gtd-notification.c')
| -rw-r--r-- | src/core/gtd-notification.c | 441 |
1 files changed, 441 insertions, 0 deletions
diff --git a/src/core/gtd-notification.c b/src/core/gtd-notification.c new file mode 100644 index 0000000..eee3e46 --- /dev/null +++ b/src/core/gtd-notification.c @@ -0,0 +1,441 @@ +/* gtd-notification.c + * + * Copyright (C) 2015 Georges Basile Stavracas Neto <georges.stavracas@gmail.com> + * Copyright (C) 2022 Jamie Murphy <hello@itsjamie.dev> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#define G_LOG_DOMAIN "GtdNotification" + +#include "gtd-notification.h" +#include "gtd-object.h" + +#include <glib/gi18n.h> + +/** + * 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); + } +} |
