[gnome-todo] window: move notifications to dedicated subsystem
- From: Georges Basile Stavracas Neto <gbsneto src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-todo] window: move notifications to dedicated subsystem
- Date: Sat, 11 Jul 2015 01:33:18 +0000 (UTC)
commit 1c874c7279825757e0afe1dea457ca4a9ceb99f3
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date: Fri Jul 10 22:28:50 2015 -0300
window: move notifications to dedicated subsystem
Notifications are an important part of the application,
and deserve a better implementation than some queues throwed
away, like it was done.
Now it has it's own subclass, with a nice widget which
servers as an state machine for notifications.
data/Makefile.am | 1 +
data/todo.gresource.xml | 1 +
data/ui/window.ui | 76 +----
src/Makefile.am | 5 +
src/gtd-task-list-view.c | 43 ++-
src/gtd-types.h | 2 +
src/gtd-window.c | 265 ++------------
src/gtd-window.h | 15 +-
src/notification/gtd-notification-widget.c | 277 ++++++++++++++
src/notification/gtd-notification-widget.h | 42 +++
src/notification/gtd-notification.c | 544 ++++++++++++++++++++++++++++
src/notification/gtd-notification.h | 70 ++++
12 files changed, 1010 insertions(+), 331 deletions(-)
---
diff --git a/data/Makefile.am b/data/Makefile.am
index 8f3d8fe..45a72d7 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -29,6 +29,7 @@ EXTRA_DIST= \
ui/edit-pane.ui \
ui/initial-setup.ui \
ui/list-view.ui \
+ ui/notification.ui \
ui/storage-dialog.ui \
ui/storage-popover.ui \
ui/storage-row.ui \
diff --git a/data/todo.gresource.xml b/data/todo.gresource.xml
index 117d1ef..c81567a 100644
--- a/data/todo.gresource.xml
+++ b/data/todo.gresource.xml
@@ -5,6 +5,7 @@
<file compressed="true" preprocess="xml-stripblanks">ui/edit-pane.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/initial-setup.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/list-view.ui</file>
+ <file compressed="true" preprocess="xml-stripblanks">ui/notification.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/storage-dialog.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/storage-popover.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/storage-row.ui</file>
diff --git a/data/ui/window.ui b/data/ui/window.ui
index 6f38c01..b8e6e2d 100644
--- a/data/ui/window.ui
+++ b/data/ui/window.ui
@@ -21,83 +21,11 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<child type="overlay">
- <object class="GtkRevealer" id="notification_revealer">
+ <object class="GtdNotificationWidget" id="notification_widget">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="transition_type">slide-down</property>
<property name="halign">center</property>
<property name="valign">start</property>
- <child>
- <object class="GtkFrame" id="notification_frame">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkBox" id="notification_box">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkSpinner" id="notification_spinner">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="notification_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="notification_action_button">
- <property name="visible">False</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Undo</property>
- <signal name="clicked"
handler="gtd_window__notification_notification_button_clicked" object="GtdWindow" swapped="no" />
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="notification_close_button">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="relief">none</property>
- <signal name="clicked"
handler="gtd_window__notification_notification_button_clicked" object="GtdWindow" swapped="no" />
- <child>
- <object class="GtkImage" id="notification_close_button_icon">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">window-close-symbolic</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">3</property>
- </packing>
- </child>
- </object>
- </child>
- <style>
- <class name="app-notification" />
- </style>
- </object>
- </child>
</object>
</child>
<child>
@@ -105,6 +33,7 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="transition_type">crossfade</property>
+ <signal name="notify::visible-child" handler="gtd_window__stack_visible_child_cb"
object="GtdWindow" swapped="yes" />
<child>
<object class="GtkBox" id="main_box">
<property name="visible">True</property>
@@ -134,6 +63,7 @@
<property name="vexpand">True</property>
<property name="transition_duration">250</property>
<property name="transition_type">crossfade</property>
+ <signal name="notify::visible-child" handler="gtd_window__stack_visible_child_cb"
object="GtdWindow" swapped="yes" />
<child>
<object class="GtkScrolledWindow" id="lists_scrolled_window">
<property name="visible">True</property>
diff --git a/src/Makefile.am b/src/Makefile.am
index fbc235e..738eb84 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -5,6 +5,7 @@ AM_CPPFLAGS = \
-DUI_DATA_DIR=\""$(pkgdatadir)/style"\" \
-DGOA_API_IS_SUBJECT_TO_CHANGE \
-I$(srcdir)/storage \
+ -I$(srcdir)/notification \
$(GNOME_TODO_CFLAGS)
bin_PROGRAMS = gnome-todo
@@ -15,6 +16,10 @@ BUILT_SOURCES = \
gnome_todo_SOURCES = \
$(BUILT_SOURCES) \
+ notification/gtd-notification.c \
+ notification/gtd-notification.h \
+ notification/gtd-notification-widget.c \
+ notification/gtd-notification-widget.h \
storage/gtd-storage.c \
storage/gtd-storage.h \
storage/gtd-storage-dialog.c \
diff --git a/src/gtd-task-list-view.c b/src/gtd-task-list-view.c
index fe5c293..f61b607 100644
--- a/src/gtd-task-list-view.c
+++ b/src/gtd-task-list-view.c
@@ -20,6 +20,7 @@
#include "gtd-edit-pane.h"
#include "gtd-task-list-view.h"
#include "gtd-manager.h"
+#include "gtd-notification.h"
#include "gtd-task.h"
#include "gtd-task-list.h"
#include "gtd-task-row.h"
@@ -91,30 +92,27 @@ enum {
LAST_PROP
};
-static gboolean
-remove_task_action (RemoveTaskData *data)
+static void
+remove_task_action (GtdNotification *notification,
+ gpointer user_data)
{
- g_return_val_if_fail (data != NULL, G_SOURCE_REMOVE);
+ RemoveTaskData *data = user_data;
gtd_manager_remove_task (data->view->priv->manager, data->task);
g_free (data);
-
- return G_SOURCE_REMOVE;
}
-static gboolean
-undo_remove_task_action (RemoveTaskData *data)
+static void
+undo_remove_task_action (GtdNotification *notification,
+ gpointer user_data)
{
+ RemoveTaskData *data = user_data;
GtdTaskList *list = gtd_task_get_list (data->task);
- g_return_val_if_fail (data != NULL, G_SOURCE_REMOVE);
-
gtd_task_list_save_task (list, data->task);
g_free (data);
-
- return G_SOURCE_REMOVE;
}
static void
@@ -192,6 +190,7 @@ gtd_task_list_view__remove_task_cb (GtdEditPane *pane,
gpointer user_data)
{
GtdTaskListViewPrivate *priv;
+ GtdNotification *notification;
RemoveTaskData *data;
GtdWindow *window;
gchar *text;
@@ -211,15 +210,19 @@ gtd_task_list_view__remove_task_cb (GtdEditPane *pane,
gtk_revealer_set_reveal_child (priv->edit_revealer, FALSE);
- gtd_window_notify (window,
- 7500, //ms
- TASK_REMOVED_NOTIFICATION_ID,
- text,
- _("Undo"),
- (GSourceFunc) remove_task_action,
- (GSourceFunc) undo_remove_task_action,
- FALSE,
- data);
+ /* Notify about the removal */
+ notification = gtd_notification_new (text, 7500.0);
+
+ gtd_notification_set_primary_action (notification,
+ (GtdNotificationActionFunc) remove_task_action,
+ data);
+
+ gtd_notification_set_secondary_action (notification,
+ _("Undo"),
+ (GtdNotificationActionFunc) undo_remove_task_action,
+ data);
+
+ gtd_window_notify (window, notification);
g_free (text);
}
diff --git a/src/gtd-types.h b/src/gtd-types.h
index f4f8a2f..5bd651a 100644
--- a/src/gtd-types.h
+++ b/src/gtd-types.h
@@ -29,6 +29,8 @@ typedef struct _GtdApplication GtdApplication;
typedef struct _GtdInitialSetupWindow GtdInitialSetupWindow;
typedef struct _GtdListView GtdListView;
typedef struct _GtdManager GtdManager;
+typedef struct _GtdNotification GtdNotification;
+typedef struct _GtdNotificationWidget GtdNotificationWidget;
typedef struct _GtdObject GtdObject;
typedef struct _GtdStorage GtdStorage;
typedef struct _GtdStoragePopover GtdStoragePopover;
diff --git a/src/gtd-window.c b/src/gtd-window.c
index 9ae798f..271eef8 100644
--- a/src/gtd-window.c
+++ b/src/gtd-window.c
@@ -19,6 +19,8 @@
#include "gtd-application.h"
#include "gtd-task-list-view.h"
#include "gtd-manager.h"
+#include "gtd-notification.h"
+#include "gtd-notification-widget.h"
#include "gtd-storage-dialog.h"
#include "gtd-task.h"
#include "gtd-task-list.h"
@@ -35,15 +37,12 @@ typedef struct
GtkFlowBox *lists_flowbox;
GtkStack *main_stack;
GtkWidget *new_list_popover;
- GtkButton *notification_action_button;
- GtkButton *notification_close_button;
- GtkLabel *notification_label;
- GtkRevealer *notification_revealer;
- GtkSpinner *notification_spinner;
+ GtdNotificationWidget *notification_widget;
GtdTaskListView *scheduled_list_view;
GtkSearchBar *search_bar;
+ GtkToggleButton *search_button;
GtkSearchEntry *search_entry;
- GtkStackSwitcher *stack;
+ GtkStack *stack;
GtkStackSwitcher *stack_switcher;
GtdStorageDialog *storage_dialog;
GtdTaskListView *today_list_view;
@@ -52,12 +51,8 @@ typedef struct
/* mode */
GtdWindowMode mode;
- /*
- * A queue of the next operations.
- */
- GQueue *notification_queue;
- gint notification_delay_id;
- gboolean consuming_notifications;
+ /* loading notification */
+ GtdNotification *loading_notification;
GtdManager *manager;
} GtdWindowPrivate;
@@ -70,37 +65,10 @@ struct _GtdWindow
GtdWindowPrivate *priv;
};
-typedef struct
-{
- GtdWindow *window;
-
- gchar *id;
- gchar *text;
- gchar *label;
-
- gint delay;
- gboolean show_spinner;
-
- GSourceFunc primary_action;
- GSourceFunc secondary_action;
-
- gpointer data;
-} NotificationData;
-
-#define LOADING_LISTS_NOTIFICATION_ID "loading-lists-id"
-
static void gtd_window__change_storage_action (GSimpleAction *simple,
GVariant *parameter,
gpointer user_data);
-static gboolean gtd_window__execute_notification_data (NotificationData *data,
- gboolean primary_action);
-
-
-static void gtd_window_consume_notification (GtdWindow *window);
-
-void notification_data_free (NotificationData *data);
-
G_DEFINE_TYPE_WITH_PRIVATE (GtdWindow, gtd_window, GTK_TYPE_APPLICATION_WINDOW)
static const GActionEntry gtd_window_entries[] = {
@@ -114,6 +82,20 @@ enum {
};
static void
+gtd_window__stack_visible_child_cb (GtdWindow *window)
+{
+ GtdWindowPrivate *priv;
+
+ priv = window->priv;
+
+ gtk_widget_set_visible (
+ GTK_WIDGET (priv->search_button),
+ g_strcmp0 (gtk_stack_get_visible_child_name (priv->main_stack), "overview") == 0 &&
+ g_strcmp0 (gtk_stack_get_visible_child_name (priv->stack), "lists") == 0);
+
+}
+
+static void
gtd_window_update_list_counters (GtdTaskList *list,
GtdTask *task,
GtdWindow *window)
@@ -166,14 +148,6 @@ gtd_window_update_list_counters (GtdTaskList *list,
g_free (new_title);
}
-void
-notification_data_free (NotificationData *data)
-{
- g_free (data->id);
- g_free (data->text);
- g_free (data->label);
- g_free (data);
-}
static void
gtd_window__change_storage_action (GSimpleAction *simple,
@@ -190,93 +164,6 @@ gtd_window__change_storage_action (GSimpleAction *simple,
}
static void
-gtd_window__notification_notification_button_clicked (GtkButton *button,
- gpointer user_data)
-{
- GtdWindowPrivate *priv;
- NotificationData *data;
-
- g_return_if_fail (GTD_IS_WINDOW (user_data));
-
- priv = GTD_WINDOW (user_data)->priv;
- data = g_queue_peek_head (priv->notification_queue);
-
- /* Cancel any previouly set timeouts */
- if (priv->notification_delay_id > 0)
- {
- g_source_remove (priv->notification_delay_id);
- priv->notification_delay_id = 0;
- }
-
- gtd_window__execute_notification_data (data, button == priv->notification_close_button);
-}
-
-static void
-gtd_window_consume_notification (GtdWindow *window)
-{
- GtdWindowPrivate *priv;
- NotificationData *data;
-
- g_return_if_fail (GTD_IS_WINDOW (window));
-
- priv = window->priv;
-
- if (g_queue_is_empty (priv->notification_queue))
- {
- gtk_revealer_set_reveal_child (priv->notification_revealer, FALSE);
- priv->consuming_notifications = FALSE;
- return;
- }
-
- priv->consuming_notifications = TRUE;
-
- /* Keep the current operation until it is finished or canceled */
- data = g_queue_peek_head (priv->notification_queue);
-
- gtk_button_set_label (priv->notification_action_button, data->label ? data->label : "");
- gtk_label_set_markup (priv->notification_label, data->text);
- gtk_revealer_set_reveal_child (priv->notification_revealer, TRUE);
- gtk_widget_set_visible (GTK_WIDGET (priv->notification_action_button), data->label != NULL);
-
- /* spinner */
- gtk_widget_set_visible (GTK_WIDGET (priv->notification_spinner), data->show_spinner);
- g_object_set (priv->notification_spinner, "active", data->show_spinner, NULL);
-
- /* If there's a delay set, execute the action */
- if (data->delay)
- {
- priv->notification_delay_id = g_timeout_add (data->delay,
- (GSourceFunc) gtd_window__execute_notification_data,
- data);
- }
-}
-
-static gboolean
-gtd_window__execute_notification_data (NotificationData *data,
- gboolean primary_action)
-{
- GtdWindowPrivate *priv = data->window->priv;
- gboolean retval = G_SOURCE_REMOVE;
-
- if (primary_action && data->primary_action)
- retval = data->primary_action (data->data);
- else if (!primary_action && data->secondary_action)
- retval = data->secondary_action (data->data);
-
- /* Remove the current notification from queue */
- g_queue_pop_head (priv->notification_queue);
-
- /* Continue consuming notifications */
- gtd_window_consume_notification (data->window);
-
- notification_data_free (data);
-
- priv->notification_delay_id = 0;
-
- return retval;
-}
-
-static void
gtd_window__list_color_set (GtkColorChooser *button,
gpointer user_data)
{
@@ -384,23 +271,16 @@ gtd_window__manager_ready_changed (GObject *source,
gpointer user_data)
{
GtdWindowPrivate *priv = GTD_WINDOW (user_data)->priv;
+
g_return_if_fail (GTD_IS_WINDOW (user_data));
if (gtd_object_get_ready (GTD_OBJECT (source)))
{
- gtd_window_cancel_notification (GTD_WINDOW (user_data), LOADING_LISTS_NOTIFICATION_ID);
+ gtd_notification_widget_cancel (priv->notification_widget, priv->loading_notification);
}
else
{
- gtd_window_notify (GTD_WINDOW (user_data),
- 0,
- LOADING_LISTS_NOTIFICATION_ID,
- _("Loading your task lists…"),
- NULL,
- NULL,
- NULL,
- TRUE,
- NULL);
+ gtd_notification_widget_notify (priv->notification_widget, priv->loading_notification);
}
}
@@ -646,13 +526,11 @@ gtd_window_class_init (GtdWindowClass *klass)
gtk_widget_class_bind_template_child_private (widget_class, GtdWindow, list_view);
gtk_widget_class_bind_template_child_private (widget_class, GtdWindow, main_stack);
gtk_widget_class_bind_template_child_private (widget_class, GtdWindow, new_list_popover);
- gtk_widget_class_bind_template_child_private (widget_class, GtdWindow, notification_action_button);
- gtk_widget_class_bind_template_child_private (widget_class, GtdWindow, notification_label);
- gtk_widget_class_bind_template_child_private (widget_class, GtdWindow, notification_revealer);
- gtk_widget_class_bind_template_child_private (widget_class, GtdWindow, notification_spinner);
+ gtk_widget_class_bind_template_child_private (widget_class, GtdWindow, notification_widget);
gtk_widget_class_bind_template_child_private (widget_class, GtdWindow, scheduled_list_view);
gtk_widget_class_bind_template_child_private (widget_class, GtdWindow, stack);
gtk_widget_class_bind_template_child_private (widget_class, GtdWindow, search_bar);
+ gtk_widget_class_bind_template_child_private (widget_class, GtdWindow, search_button);
gtk_widget_class_bind_template_child_private (widget_class, GtdWindow, search_entry);
gtk_widget_class_bind_template_child_private (widget_class, GtdWindow, stack_switcher);
gtk_widget_class_bind_template_child_private (widget_class, GtdWindow, storage_dialog);
@@ -662,7 +540,7 @@ gtd_window_class_init (GtdWindowClass *klass)
gtk_widget_class_bind_template_callback (widget_class, gtd_window__list_color_set);
gtk_widget_class_bind_template_callback (widget_class, gtd_window__list_selected);
gtk_widget_class_bind_template_callback (widget_class, gtd_window__on_key_press_event);
- gtk_widget_class_bind_template_callback (widget_class,
gtd_window__notification_notification_button_clicked);
+ gtk_widget_class_bind_template_callback (widget_class, gtd_window__stack_visible_child_cb);
}
static void
@@ -670,7 +548,8 @@ gtd_window_init (GtdWindow *self)
{
self->priv = gtd_window_get_instance_private (self);
- self->priv->notification_queue = g_queue_new ();
+ self->priv->loading_notification = gtd_notification_new (_("Loading your task lists…"), 0);
+ gtd_object_set_ready (GTD_OBJECT (self->priv->loading_notification), FALSE);
/* add actions */
g_action_map_add_action_entries (G_ACTION_MAP (self),
@@ -700,111 +579,43 @@ gtd_window_get_manager (GtdWindow *window)
/**
* gtd_window_notify:
* @window: a #GtdWindow
- * @visible_time: time which the notification will be visible, 0 to show permanently
- * @text: text of the notification
- * @button_label: text to show on the notification button, %NULL to hide it
- * @primary_action: function to call when close button is clicked
- * @secondary_action: function to call when the alternative button is clicked
- * @show_spinner: whether show the spinner or not
- * @user_data: custom data
+ * @notification: a #GtdNotification
*
* Shows a notification on the top of the main window.
*
* Returns:
*/
void
-gtd_window_notify (GtdWindow *window,
- gint visible_time,
- const gchar *id,
- const gchar *text,
- const gchar *button_label,
- GSourceFunc primary_action,
- GSourceFunc secondary_action,
- gboolean show_spinner,
- gpointer user_data)
+gtd_window_notify (GtdWindow *window,
+ GtdNotification *notification)
{
GtdWindowPrivate *priv;
- NotificationData *data;
g_return_if_fail (GTD_IS_WINDOW (window));
priv = window->priv;
- data = g_new0 (NotificationData, 1);
- data->window = window;
- data->delay = visible_time;
- data->id = g_strdup (id);
- data->text = g_strdup (text);
- data->label = g_strdup (button_label);
- data->primary_action = primary_action;
- data->secondary_action = secondary_action;
- data->show_spinner = show_spinner;
- data->data = user_data;
-
- g_queue_push_tail (priv->notification_queue, data);
-
- /* If we're not consuming notifications, start it now */
- if (!priv->consuming_notifications)
- gtd_window_consume_notification (window);
+ gtd_notification_widget_notify (priv->notification_widget, notification);
}
/**
* gtd_window_cancel_notification:
* @window: a #GtdManager
- * @id: id of the given notification
+ * @notification: a #GtdNotification
*
- * Cancels the notification with @id as id.
+ * Cancels @notification.
*
* Returns:
*/
void
-gtd_window_cancel_notification (GtdWindow *window,
- const gchar *id)
+gtd_window_cancel_notification (GtdWindow *window,
+ GtdNotification *notification)
{
GtdWindowPrivate *priv;
- NotificationData *data;
- GList *head;
- GList *l;
- gint index;
g_return_if_fail (GTD_IS_WINDOW (window));
- g_return_if_fail (id != NULL);
- data = NULL;
priv = window->priv;
- index = 0;
-
- /* Search for a notification with the given id */
- head = priv->notification_queue->head;
-
- for (l = head; l != NULL; l = l->next)
- {
- NotificationData *tmp = l->data;
-
- if (tmp && g_strcmp0 (tmp->id, id) == 0)
- {
- data = l->data;
- break;
- }
-
- index++;
- }
-
- g_queue_remove (priv->notification_queue, data);
-
- /* If we're removing the current head, continue consuming the queue */
- if (index == 0)
- {
- /* Remove any remaining timeouts */
- if (priv->notification_delay_id > 0)
- {
- g_source_remove (priv->notification_delay_id);
- priv->notification_delay_id = 0;
- }
-
- gtd_window_consume_notification (window);
- }
- if (data)
- notification_data_free (data);
+ gtd_notification_widget_cancel (priv->notification_widget, notification);
}
diff --git a/src/gtd-window.h b/src/gtd-window.h
index 0f19f4f..901f2ce 100644
--- a/src/gtd-window.h
+++ b/src/gtd-window.h
@@ -35,17 +35,10 @@ GtkWidget* gtd_window_new (GtdApplication
GtdManager* gtd_window_get_manager (GtdWindow *window);
void gtd_window_notify (GtdWindow *window,
- gint visible_time,
- const gchar *id,
- const gchar *text,
- const gchar *button_label,
- GSourceFunc primary_action,
- GSourceFunc secondary_action,
- gboolean show_spinner,
- gpointer user_data);
-
-void gtd_window_cancel_notification (GtdWindow *window,
- const gchar *id);
+ GtdNotification *notification);
+
+void gtd_window_cancel_notification (GtdWindow *window,
+ GtdNotification *notification);
G_END_DECLS
diff --git a/src/notification/gtd-notification-widget.c b/src/notification/gtd-notification-widget.c
new file mode 100644
index 0000000..dbdcb14
--- /dev/null
+++ b/src/notification/gtd-notification-widget.c
@@ -0,0 +1,277 @@
+/* gtd-notification-widget.c
+ *
+ * Copyright (C) 2015 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * 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/>.
+ */
+
+#include "gtd-notification.h"
+#include "gtd-notification-widget.h"
+
+typedef enum
+{
+ STATE_IDLE,
+ STATE_EXECUTING
+} GtdExecutionState;
+
+typedef struct
+{
+ /* widgets */
+ GtkButton *secondary_button;
+ GtkSpinner *spinner;
+ GtkLabel *text_label;
+
+ /* internal data */
+ GQueue *queue;
+ GtdNotification *current_notification;
+ GtdExecutionState state;
+
+ /* bindings */
+ GBinding *has_secondary_action_binding;
+ GBinding *message_label_binding;
+ GBinding *ready_binding;
+ GBinding *secondary_label_binding;
+} GtdNotificationWidgetPrivate;
+
+struct _GtdNotificationWidget
+{
+ GtkRevealer parent;
+
+ /*< private >*/
+ GtdNotificationWidgetPrivate *priv;
+};
+
+/* Prototypes */
+static void gtd_notification_widget_execute_notification (GtdNotificationWidget *widget,
+ GtdNotification *notification);
+
+G_DEFINE_TYPE_WITH_PRIVATE (GtdNotificationWidget, gtd_notification_widget, GTK_TYPE_REVEALER)
+
+static void
+gtd_notification_widget_clear_bindings (GtdNotificationWidget *widget)
+{
+ GtdNotificationWidgetPrivate *priv = widget->priv;
+
+ g_clear_pointer (&priv->has_secondary_action_binding, g_binding_unbind);
+ g_clear_pointer (&priv->message_label_binding, g_binding_unbind);
+ g_clear_pointer (&priv->ready_binding, g_binding_unbind);
+ g_clear_pointer (&priv->secondary_label_binding, g_binding_unbind);
+}
+
+/*
+ * This method is called after a notification is dismissed
+ * or any action is taken, and it verifies if it should
+ * continue the execution of notifications.
+ */
+static void
+gtd_notification_widget_stop_or_run (GtdNotificationWidget *widget)
+{
+ GtdNotificationWidgetPrivate *priv = widget->priv;
+
+ g_clear_object (&priv->current_notification);
+ priv->current_notification = g_queue_pop_head (priv->queue);
+
+ if (priv->current_notification)
+ {
+ gtk_revealer_set_reveal_child (GTK_REVEALER (widget), TRUE);
+ gtd_notification_widget_execute_notification (widget, priv->current_notification);
+ priv->state = STATE_EXECUTING;
+ }
+ else
+ {
+ gtk_revealer_set_reveal_child (GTK_REVEALER (widget), FALSE);
+ priv->state = STATE_IDLE;
+ }
+}
+
+static void
+gtd_notification_widget__close_button_clicked_cb (GtdNotificationWidget *widget)
+{
+ GtdNotificationWidgetPrivate *priv = widget->priv;
+
+ gtd_notification_stop (priv->current_notification);
+ gtd_notification_execute_primary_action (priv->current_notification);
+}
+
+static void
+gtd_notification_widget__secondary_button_clicked_cb (GtdNotificationWidget *widget)
+{
+ GtdNotificationWidgetPrivate *priv = widget->priv;
+
+ gtd_notification_stop (priv->current_notification);
+ gtd_notification_execute_secondary_action (priv->current_notification);
+}
+
+static void
+gtd_notification_widget__notification_executed_cb (GtdNotification *notification,
+ GtdNotificationWidget *widget)
+{
+ gtd_notification_widget_clear_bindings (widget);
+ gtd_notification_widget_stop_or_run (widget);
+
+ g_signal_handlers_disconnect_by_func (notification,
+ gtd_notification_widget__notification_executed_cb,
+ widget);
+}
+
+static void
+gtd_notification_widget_execute_notification (GtdNotificationWidget *widget,
+ GtdNotification *notification)
+{
+ GtdNotificationWidgetPrivate *priv = widget->priv;
+
+ g_signal_connect (notification,
+ "executed",
+ G_CALLBACK (gtd_notification_widget__notification_executed_cb),
+ widget);
+
+ priv->has_secondary_action_binding =
+ g_object_bind_property (notification,
+ "has-secondary-action",
+ priv->secondary_button,
+ "visible",
+ G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
+
+ priv->message_label_binding =
+ g_object_bind_property (notification,
+ "text",
+ priv->text_label,
+ "label",
+ G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
+
+ priv->ready_binding =
+ g_object_bind_property (notification,
+ "ready",
+ priv->spinner,
+ "visible",
+ G_BINDING_DEFAULT | G_BINDING_INVERT_BOOLEAN | G_BINDING_SYNC_CREATE);
+
+ priv->secondary_label_binding =
+ g_object_bind_property (notification,
+ "secondary-action-name",
+ priv->secondary_button,
+ "label",
+ G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
+
+ gtd_notification_start (notification);
+}
+
+static void
+gtd_notification_widget_finalize (GObject *object)
+{
+ GtdNotificationWidget *self = (GtdNotificationWidget *)object;
+ GtdNotificationWidgetPrivate *priv = gtd_notification_widget_get_instance_private (self);
+
+ g_queue_free (priv->queue);
+
+ G_OBJECT_CLASS (gtd_notification_widget_parent_class)->finalize (object);
+}
+
+static void
+gtd_notification_widget_class_init (GtdNotificationWidgetClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = gtd_notification_widget_finalize;
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/todo/ui/notification.ui");
+
+ gtk_widget_class_bind_template_child_private (widget_class, GtdNotificationWidget, secondary_button);
+ gtk_widget_class_bind_template_child_private (widget_class, GtdNotificationWidget, spinner);
+ gtk_widget_class_bind_template_child_private (widget_class, GtdNotificationWidget, text_label);
+
+ gtk_widget_class_bind_template_callback (widget_class, gtd_notification_widget__close_button_clicked_cb);
+ gtk_widget_class_bind_template_callback (widget_class,
gtd_notification_widget__secondary_button_clicked_cb);
+}
+
+static void
+gtd_notification_widget_init (GtdNotificationWidget *self)
+{
+ self->priv = gtd_notification_widget_get_instance_private (self);
+ self->priv->queue = g_queue_new ();
+ self->priv->state = STATE_IDLE;
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+/**
+ * gtd_notification_widget_new:
+ *
+ * Creates a new #GtdNotificationWidget.
+ *
+ * Returns: (transger full): a new #GtdNotificationWidget
+ */
+GtkWidget*
+gtd_notification_widget_new (void)
+{
+ return g_object_new (GTD_TYPE_NOTIFICATION_WIDGET, NULL);
+}
+
+/**
+ * gtd_notification_widget_notify:
+ *
+ * Adds @notification to the queue of notifications, and eventually
+ * consume it.
+ *
+ * Returns:
+ */
+void
+gtd_notification_widget_notify (GtdNotificationWidget *widget,
+ GtdNotification *notification)
+{
+ GtdNotificationWidgetPrivate *priv;
+
+ g_return_if_fail (GTD_IS_NOTIFICATION_WIDGET (widget));
+
+ priv = widget->priv;
+
+ if (!g_queue_find (priv->queue, notification))
+ {
+ g_queue_push_tail (priv->queue, notification);
+
+ if (priv->state == STATE_IDLE)
+ gtd_notification_widget_stop_or_run (widget);
+ }
+}
+
+/**
+ * gtd_notification_widget_cancel:
+ *
+ * Cancel @notification from being displayed. If @notification is not
+ * queued, nothing happens.
+ *
+ * Returns:
+ */
+void
+gtd_notification_widget_cancel (GtdNotificationWidget *widget,
+ GtdNotification *notification)
+{
+ GtdNotificationWidgetPrivate *priv;
+ GList *l;
+
+ g_return_if_fail (GTD_IS_NOTIFICATION_WIDGET (widget));
+
+ priv = widget->priv;
+
+ if (notification == priv->current_notification)
+ {
+ gtd_notification_stop (notification);
+ gtd_notification_widget_stop_or_run (widget);
+ }
+ else if (g_queue_find (priv->queue, notification))
+ {
+ g_queue_remove (priv->queue, notification);
+ }
+}
diff --git a/src/notification/gtd-notification-widget.h b/src/notification/gtd-notification-widget.h
new file mode 100644
index 0000000..18b5882
--- /dev/null
+++ b/src/notification/gtd-notification-widget.h
@@ -0,0 +1,42 @@
+/* gtd-notification-widget.h
+ *
+ * Copyright (C) 2015 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * 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/>.
+ */
+
+#ifndef GTD_NOTIFICATION_WIDGET_H
+#define GTD_NOTIFICATION_WIDGET_H
+
+#include "gtd-types.h"
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTD_TYPE_NOTIFICATION_WIDGET (gtd_notification_widget_get_type())
+
+G_DECLARE_FINAL_TYPE (GtdNotificationWidget, gtd_notification_widget, GTD, NOTIFICATION_WIDGET, GtkRevealer)
+
+GtkWidget* gtd_notification_widget_new (void);
+
+void gtd_notification_widget_notify (GtdNotificationWidget *widget,
+ GtdNotification *notification);
+
+void gtd_notification_widget_cancel (GtdNotificationWidget *widget,
+ GtdNotification *notification);
+
+G_END_DECLS
+
+#endif /* GTD_NOTIFICATION_WIDGET_H */
diff --git a/src/notification/gtd-notification.c b/src/notification/gtd-notification.c
new file mode 100644
index 0000000..ebe931d
--- /dev/null
+++ b/src/notification/gtd-notification.c
@@ -0,0 +1,544 @@
+/* gtd-notification.c
+ *
+ * Copyright (C) 2015 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * 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/>.
+ */
+
+#include "gtd-notification.h"
+#include "gtd-object.h"
+
+#include <glib/gi18n.h>
+
+typedef struct
+{
+ gchar *text;
+
+ gdouble timeout;
+ gint timeout_id;
+
+ GtdNotificationActionFunc primary_action;
+ gboolean has_primary_action;
+ gpointer primary_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_PRIMARY_ACTION,
+ PROP_HAS_SECONDARY_ACTION,
+ PROP_SECONDARY_ACTION_NAME,
+ PROP_TEXT,
+ PROP_TIMEOUT,
+ LAST_PROP
+};
+
+enum
+{
+ EXECUTED,
+ NUM_SIGNALS
+};
+
+static guint signals[NUM_SIGNALS] = { 0, };
+
+static gboolean
+execute_action_cb (GtdNotification *notification)
+{
+ GtdNotificationPrivate *priv = notification->priv;
+
+ priv->timeout_id = 0;
+
+ gtd_notification_execute_primary_action (notification);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+gtd_notification_finalize (GObject *object)
+{
+ GtdNotification *self = (GtdNotification *)object;
+ GtdNotificationPrivate *priv = gtd_notification_get_instance_private (self);
+
+ if (priv->timeout_id > 0)
+ g_source_remove (priv->timeout_id);
+
+ 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_PRIMARY_ACTION:
+ g_value_set_boolean (value, self->priv->has_primary_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;
+
+ case PROP_TIMEOUT:
+ g_value_set_double (value, gtd_notification_get_timeout (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;
+
+ case PROP_TIMEOUT:
+ gtd_notification_set_timeout (self, g_value_get_double (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-primary-action:
+ *
+ * @TRUE if the notification has a primary action or @FALSE otherwise. The
+ * primary action is triggered on notification timeout or dismiss.
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_HAS_PRIMARY_ACTION,
+ g_param_spec_boolean ("has-primary-action",
+ _("Whether the notification has a primary action"),
+ _("Whether the notification has the primary action, activated on timeout or
dismiss"),
+ 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::timeout:
+ *
+ * The time the notification will be displayed.
+ */
+ g_object_class_install_property (
+ object_class,
+ PROP_TIMEOUT,
+ g_param_spec_double ("timeout",
+ _("Notification timeout"),
+ _("The time the notification is displayed"),
+ 0.0,
+ 30000.0,
+ 7500.00,
+ G_PARAM_READWRITE));
+
+ /**
+ * GtdNotification::executed:
+ *
+ * The ::executed signal is emmited 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;
+ self->priv->timeout = 7500.0;
+}
+
+/**
+ * gtd_notification_new:
+ *
+ * Creates a new notification with @text and @timeout. If @timeout is
+ * 0, the notification is indefinitely displayed.
+ *
+ * Returns: (transfer full): a new #GtdNotification
+ */
+GtdNotification*
+gtd_notification_new (const gchar *text,
+ gdouble timeout)
+{
+ return g_object_new (GTD_TYPE_NOTIFICATION,
+ "text", text,
+ "timeout", timeout,
+ NULL);
+}
+
+/**
+ * gtd_notification_set_primary_action:
+ *
+ * Sets the primary action of @notification, which is triggered
+ * on dismiss or timeout.
+ *
+ * Returns:
+ */
+void
+gtd_notification_set_primary_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_primary_action)
+ {
+ priv->has_primary_action = has_action;
+
+ priv->primary_action = has_action ? func : NULL;
+ priv->primary_action_data = has_action ? user_data : NULL;
+
+ g_object_notify (G_OBJECT (notification), "has-primary-action");
+ }
+}
+
+/**
+ * gtd_notification_set_secondary_action:
+ *
+ * Sets the secondary action of @notification, which is triggered
+ * only on user explicit input.
+ *
+ * Returns:
+ */
+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:
+ *
+ * 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:
+ *
+ * Sets the text of @notification to @text.
+ *
+ * Returns:
+ */
+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_get_timeout:
+ *
+ * Retrieves the timeout of @notification.
+ *
+ * Returns: the timeout of @notification.
+ */
+gdouble
+gtd_notification_get_timeout (GtdNotification *notification)
+{
+ g_return_val_if_fail (GTD_IS_NOTIFICATION (notification), 0.0);
+
+ return notification->priv->timeout;
+}
+
+/**
+ * gtd_notification_set_timeout:
+ *
+ * Sets the timeout of @notification to @timeout.
+ *
+ * Returns:
+ */
+void
+gtd_notification_set_timeout (GtdNotification *notification,
+ gdouble timeout)
+{
+ GtdNotificationPrivate *priv;
+
+ g_return_if_fail (GTD_IS_NOTIFICATION (notification));
+
+ priv = notification->priv;
+
+ if (priv->timeout != timeout)
+ {
+ priv->timeout = timeout;
+
+ g_object_notify (G_OBJECT (notification), "timeout");
+ }
+}
+
+/**
+ * gtd_notification_execute_primary_action:
+ *
+ * Executes the primary action of @notification if set.
+ *
+ * Returns:
+ */
+void
+gtd_notification_execute_primary_action (GtdNotification *notification)
+{
+ GtdNotificationPrivate *priv;
+
+ g_return_if_fail (GTD_IS_NOTIFICATION (notification));
+
+ priv = notification->priv;
+
+ if (priv->primary_action)
+ {
+ priv->primary_action (notification, priv->primary_action_data);
+
+ g_signal_emit (notification, signals[EXECUTED], 0);
+ }
+}
+
+/**
+ * gtd_notification_execute_secondary_action:
+ *
+ * Executes the secondary action of @notification if any.
+ *
+ * Returns:
+ */
+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);
+ }
+}
+
+/**
+ * gtd_notification_start:
+ *
+ * Starts the timeout of notification. Use @gtd_notification_stop
+ * to stop it.
+ *
+ * Returns:
+ */
+void
+gtd_notification_start (GtdNotification *notification)
+{
+ GtdNotificationPrivate *priv;
+
+ g_return_if_fail (GTD_IS_NOTIFICATION (notification));
+
+ priv = notification->priv;
+
+ if (priv->timeout != 0)
+ {
+ if (priv->timeout_id > 0)
+ {
+ g_source_remove (priv->timeout_id);
+ priv->timeout_id = 0;
+ }
+
+ priv->timeout_id = g_timeout_add (priv->timeout,
+ (GSourceFunc) execute_action_cb,
+ notification);
+ }
+}
+
+/**
+ * gtd_notification_stop:
+ *
+ * Stops the timeout of notification. Use @gtd_notification_start
+ * to start it.
+ *
+ * Returns:
+ */
+void
+gtd_notification_stop (GtdNotification *notification)
+{
+ GtdNotificationPrivate *priv;
+
+ g_return_if_fail (GTD_IS_NOTIFICATION (notification));
+
+ priv = notification->priv;
+
+ if (priv->timeout_id != 0)
+ {
+ g_source_remove (priv->timeout_id);
+ priv->timeout_id = 0;
+ }
+}
diff --git a/src/notification/gtd-notification.h b/src/notification/gtd-notification.h
new file mode 100644
index 0000000..2ae2298
--- /dev/null
+++ b/src/notification/gtd-notification.h
@@ -0,0 +1,70 @@
+/* gtd-notification.h
+ *
+ * Copyright (C) 2015 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * 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/>.
+ */
+
+#ifndef GTD_NOTIFICATION_H
+#define GTD_NOTIFICATION_H
+
+#include "gtd-object.h"
+#include "gtd-types.h"
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GTD_TYPE_NOTIFICATION (gtd_notification_get_type())
+
+G_DECLARE_FINAL_TYPE (GtdNotification, gtd_notification, GTD, NOTIFICATION, GtdObject)
+
+typedef void (*GtdNotificationActionFunc) (GtdNotification *notification,
+ gpointer user_data);
+
+
+GtdNotification* gtd_notification_new (const gchar *text,
+ gdouble timeout);
+
+void gtd_notification_execute_primary_action (GtdNotification *notification);
+
+void gtd_notification_execute_secondary_action (GtdNotification *notification);
+
+void gtd_notification_start (GtdNotification *notification);
+
+void gtd_notification_stop (GtdNotification *notification);
+
+void gtd_notification_set_primary_action (GtdNotification *notification,
+ GtdNotificationActionFunc func,
+ gpointer user_data);
+
+void gtd_notification_set_secondary_action (GtdNotification *notification,
+ const gchar *name,
+ GtdNotificationActionFunc func,
+ gpointer user_data);
+
+const gchar* gtd_notification_get_text (GtdNotification *notification);
+
+void gtd_notification_set_text (GtdNotification *notification,
+ const gchar *text);
+
+gdouble gtd_notification_get_timeout (GtdNotification *notification);
+
+void gtd_notification_set_timeout (GtdNotification *notification,
+ gdouble timeout);
+
+G_END_DECLS
+
+#endif /* GTD_NOTIFICATION_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]