summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVictor Kareh <[email protected]>2025-08-27 21:14:05 -0400
committerLuke from DC <[email protected]>2025-10-10 02:11:17 +0000
commit8b88e829580b71f67a904266a08f0b5aef8f75bb (patch)
treeef65a7f0a739fcf858818cc6a06675150942dc48
parentfcde65c52c3ceb4c97af33679ead210b1b1d58e2 (diff)
downloadmate-notification-daemon-8b88e829580b71f67a904266a08f0b5aef8f75bb.tar.bz2
mate-notification-daemon-8b88e829580b71f67a904266a08f0b5aef8f75bb.tar.xz
capplet: Add notification history context module
Adds the basis for a notification management system using the D-Bus context to display notification history in a popup. Fixes https://github.com/mate-desktop/mate-notification-daemon/issues/74
-rw-r--r--src/capplet/Makefile.am5
-rw-r--r--src/capplet/mate-notification-applet-history.c372
-rw-r--r--src/capplet/mate-notification-applet-history.h59
-rw-r--r--src/capplet/mate-notification-applet-menu.xml5
-rw-r--r--src/capplet/mate-notification-applet.c135
5 files changed, 574 insertions, 2 deletions
diff --git a/src/capplet/Makefile.am b/src/capplet/Makefile.am
index 4126d3f..0017ab6 100644
--- a/src/capplet/Makefile.am
+++ b/src/capplet/Makefile.am
@@ -47,6 +47,8 @@ libmate_notification_applet_la_SOURCES = \
mate-notification-applet.c \
mate-notification-applet-dbus.h \
mate-notification-applet-dbus.c \
+ mate-notification-applet-history.h \
+ mate-notification-applet-history.c \
$(NULL)
libmate_notification_applet_la_CFLAGS = \
@@ -68,6 +70,8 @@ mate_notification_applet_SOURCES = \
mate-notification-applet.c \
mate-notification-applet-dbus.h \
mate-notification-applet-dbus.c \
+ mate-notification-applet-history.h \
+ mate-notification-applet-history.c \
$(NULL)
mate_notification_applet_CFLAGS = \
@@ -109,6 +113,7 @@ EXTRA_DIST = \
mate-notification-applet-menu.xml \
mate-notification-properties.ui \
mate-notification-applet-dbus.h \
+ mate-notification-applet-history.h \
$(NULL)
-include $(top_srcdir)/git.mk
diff --git a/src/capplet/mate-notification-applet-history.c b/src/capplet/mate-notification-applet-history.c
new file mode 100644
index 0000000..cca03d6
--- /dev/null
+++ b/src/capplet/mate-notification-applet-history.c
@@ -0,0 +1,372 @@
+/* mate-notification-applet.c - MATE Notification Applet - History
+ *
+ * Copyright (C) 2025 MATE Developers
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "mate-notification-applet-history.h"
+#include "../common/constants.h"
+
+#define IMAGE_SIZE 48
+
+static GtkWidget *
+create_notification_icon (const gchar *icon_name)
+{
+ GdkPixbuf *pixbuf = NULL;
+ GtkWidget *image;
+
+ if (icon_name && *icon_name) {
+ if (g_path_is_absolute (icon_name)) {
+ pixbuf = gdk_pixbuf_new_from_file_at_scale (icon_name, IMAGE_SIZE, IMAGE_SIZE, TRUE, NULL);
+ } else {
+ GtkIconTheme *theme = gtk_icon_theme_get_default ();
+ pixbuf = gtk_icon_theme_load_icon (theme, icon_name, IMAGE_SIZE, GTK_ICON_LOOKUP_USE_BUILTIN, NULL);
+ }
+ }
+
+ if (pixbuf) {
+ image = gtk_image_new_from_pixbuf (pixbuf);
+ g_object_unref (pixbuf);
+ } else {
+ image = gtk_image_new_from_icon_name ("mate-notification-properties", GTK_ICON_SIZE_DIALOG);
+ }
+
+ gtk_widget_set_valign (image, GTK_ALIGN_CENTER);
+ return image;
+}
+
+static GtkWidget *
+create_popup_window (void)
+{
+ GtkWidget *popup = gtk_window_new (GTK_WINDOW_POPUP);
+ g_object_set (popup,
+ "type-hint", GDK_WINDOW_TYPE_HINT_POPUP_MENU,
+ "skip-taskbar-hint", TRUE,
+ "skip-pager-hint", TRUE,
+ "decorated", FALSE,
+ "resizable", FALSE,
+ NULL);
+ gtk_container_set_border_width (GTK_CONTAINER (popup), 1);
+ return popup;
+}
+
+static void
+popup_destroyed_cb (GtkWidget *popup,
+ MateNotificationHistoryContext *context)
+{
+ (void) popup;
+ context->history_popup = NULL;
+}
+
+static GtkWidget *
+create_notification_row (guint id, const gchar *app_name, const gchar *app_icon,
+ const gchar *summary, const gchar *body, gint64 timestamp)
+{
+ GtkWidget *row, *hbox, *icon_image, *content_box;
+ GtkWidget *title_label, *body_label, *time_label;
+ GDateTime *dt;
+ gchar *time_str, *markup;
+
+ /* Format timestamp */
+ dt = g_date_time_new_from_unix_local (timestamp / G_TIME_SPAN_SECOND);
+ time_str = g_date_time_format (dt, "%H:%M");
+ g_date_time_unref (dt);
+
+ /* Create row container for the entire notification */
+ row = gtk_list_box_row_new ();
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
+
+ icon_image = create_notification_icon (app_icon);
+
+ content_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 3);
+
+ /* Title */
+ markup = g_markup_printf_escaped ("<b>%s</b>", summary ? summary : _("(No summary)"));
+ title_label = gtk_label_new (NULL);
+ gtk_label_set_markup (GTK_LABEL (title_label), markup);
+ gtk_widget_set_halign (title_label, GTK_ALIGN_START);
+ gtk_label_set_ellipsize (GTK_LABEL (title_label), PANGO_ELLIPSIZE_END);
+ gtk_label_set_max_width_chars (GTK_LABEL (title_label), 40);
+ g_free (markup);
+
+ /* Body */
+ body_label = NULL;
+ if (body && *body) {
+ body_label = gtk_label_new (NULL);
+ gtk_label_set_markup (GTK_LABEL (body_label), body);
+ gtk_widget_set_halign (body_label, GTK_ALIGN_START);
+ gtk_label_set_ellipsize (GTK_LABEL (body_label), PANGO_ELLIPSIZE_END);
+ gtk_label_set_max_width_chars (GTK_LABEL (body_label), 40);
+
+ /* Set tooltip in case the body overflows */
+ gchar *tooltip_text = g_strdup_printf ("%s\n%s", summary, body);
+ gtk_widget_set_tooltip_text (row, tooltip_text);
+ g_free (tooltip_text);
+ }
+
+ /* Time and app label */
+ markup = g_markup_printf_escaped ("<small>%s - %s</small>",
+ app_name ? app_name : _("Unknown"),
+ time_str);
+ time_label = gtk_label_new (NULL);
+ gtk_label_set_markup (GTK_LABEL (time_label), markup);
+ gtk_widget_set_halign (time_label, GTK_ALIGN_START);
+ g_free (markup);
+ g_free (time_str);
+
+ /* Pack content box */
+ gtk_box_pack_start (GTK_BOX (content_box), title_label, FALSE, FALSE, 0);
+ if (body_label)
+ gtk_box_pack_start (GTK_BOX (content_box), body_label, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (content_box), time_label, FALSE, FALSE, 0);
+
+ /* Pack horizontal box */
+ gtk_box_pack_start (GTK_BOX (hbox), icon_image, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), content_box, TRUE, TRUE, 0);
+
+ gtk_container_add (GTK_CONTAINER (row), hbox);
+
+ return row;
+}
+
+static void
+clear_history (GtkWidget *button,
+ MateNotificationHistoryContext *context)
+{
+ (void) button;
+
+ /* Clear the notification history */
+ if (dbus_context_clear_notification_history (context->dbus_context)) {
+ /* Trigger count update after clearing */
+ if (context->count_update_callback) {
+ ((void (*)(gpointer)) context->count_update_callback) (context->count_update_user_data);
+ }
+ }
+
+ /* Close the popup */
+ if (context->history_popup) {
+ gtk_widget_destroy (context->history_popup);
+ }
+}
+
+static void
+dnd_toggle_clicked (GtkToggleButton *toggle,
+ MateNotificationHistoryContext *context)
+{
+ gboolean active = gtk_toggle_button_get_active (toggle);
+
+ if (context->settings) {
+ g_settings_set_boolean (context->settings, "do-not-disturb", active);
+ }
+}
+
+
+MateNotificationHistoryContext *
+history_context_new (MateNotificationDBusContext *dbus_context,
+ GtkWidget *main_widget,
+ GCallback count_update_callback,
+ gpointer count_update_user_data,
+ GSettings *settings)
+{
+ MateNotificationHistoryContext *context = g_new0 (MateNotificationHistoryContext, 1);
+ context->dbus_context = dbus_context;
+ context->main_widget = main_widget;
+ context->history_popup = NULL;
+ context->count_update_callback = count_update_callback;
+ context->count_update_user_data = count_update_user_data;
+ context->settings = settings;
+ return context;
+}
+
+void
+history_context_free (MateNotificationHistoryContext *context)
+{
+ if (context) {
+ if (context->history_popup) {
+ gtk_widget_destroy (context->history_popup);
+ }
+ g_free (context);
+ }
+}
+
+void
+history_context_update_dbus (MateNotificationHistoryContext *context,
+ MateNotificationDBusContext *dbus_context)
+{
+ if (context) {
+ context->dbus_context = dbus_context;
+ }
+}
+
+void
+show_notification_history (MateNotificationHistoryContext *context)
+{
+ GtkWidget *popup;
+ GtkWidget *vbox;
+ GtkWidget *scrolled_window;
+ GtkWidget *list_box;
+ GtkWidget *button_box;
+ GtkWidget *dnd_toggle;
+ GtkWidget *clear_button;
+ GtkWidget *close_button;
+ GVariant *result;
+ GVariantIter *iter;
+ guint id, urgency;
+ gchar *app_name, *app_icon, *summary, *body;
+ gint64 timestamp, closed_timestamp;
+ guint reason;
+ gboolean read;
+ gint x, y;
+ GdkWindow *window;
+
+ if (!dbus_context_is_available (context->dbus_context)) {
+ g_warning ("Cannot show history: daemon not available");
+ return;
+ }
+
+ /* If popup already exists, destroy it (basically toggle off) */
+ if (context->history_popup) {
+ gtk_widget_destroy (context->history_popup);
+ context->history_popup = NULL;
+ return;
+ }
+
+ /* Get notification history from daemon */
+ result = dbus_context_get_notification_history (context->dbus_context);
+
+ if (!result) {
+ g_warning ("Failed to get notification history");
+ return;
+ }
+
+ /* Trigger count update since accessing history marks all as read */
+ if (context->count_update_callback) {
+ ((void (*)(gpointer)) context->count_update_callback) (context->count_update_user_data);
+ }
+
+ popup = create_popup_window ();
+
+ context->history_popup = popup;
+ g_signal_connect (popup, "destroy", G_CALLBACK (popup_destroyed_cb), context);
+
+ /* Create main container */
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_container_add (GTK_CONTAINER (popup), vbox);
+
+ /* Create list box for all notifications */
+ list_box = gtk_list_box_new ();
+ gtk_list_box_set_selection_mode (GTK_LIST_BOX (list_box), GTK_SELECTION_NONE);
+
+ /* Get history data and add to list */
+ gint notification_count = 0;
+ if (result) {
+ g_variant_get (result, "(a(ussssxxuub))", &iter);
+
+ while (g_variant_iter_loop (iter, "(ussssxxuub)",
+ &id, &app_name, &app_icon, &summary, &body,
+ &timestamp, &closed_timestamp, &reason, &urgency, &read)) {
+ notification_count++;
+ /* Add each notification as a new row in the list */
+ GtkWidget *row = create_notification_row (id, app_name, app_icon, summary, body, timestamp);
+ gtk_list_box_insert (GTK_LIST_BOX (list_box), row, -1);
+ }
+
+ g_variant_iter_free (iter);
+ g_variant_unref (result);
+ }
+
+ /* Add message if list is empty */
+ if (gtk_list_box_get_row_at_index (GTK_LIST_BOX (list_box), 0) == NULL) {
+ GtkWidget *row = gtk_list_box_row_new ();
+ GtkWidget *label = gtk_label_new (_("No notifications"));
+ gtk_widget_set_sensitive (label, FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (row), 12);
+ gtk_container_add (GTK_CONTAINER (row), label);
+ gtk_list_box_insert (GTK_LIST_BOX (list_box), row, -1);
+ }
+
+ /* Add this window for the list of notifications */
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+
+ /* Calculate to ensure the popup is the right height:
+ * 80% of screen height, with some room for buttons */
+ GdkScreen *screen = gtk_widget_get_screen (context->main_widget);
+ gint max_content_height = (gdk_screen_get_height (screen) * 0.8) - 100;
+
+ gint content_height = 100; /* 100px minimum */
+ if (notification_count > 0)
+ content_height = MIN(notification_count * content_height, max_content_height);
+
+ /* Now force the scrollable window to be the calculated height */
+ gtk_scrolled_window_set_max_content_height (GTK_SCROLLED_WINDOW (scrolled_window), max_content_height);
+ gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (scrolled_window), content_height);
+ gtk_container_add (GTK_CONTAINER (scrolled_window), list_box);
+
+ gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
+
+ /* Add a box for action buttons */
+ button_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (button_box), 6);
+
+ /* DND toggle first */
+ dnd_toggle = gtk_check_button_new_with_label (_("Do not disturb"));
+ gtk_widget_set_halign (dnd_toggle, GTK_ALIGN_START);
+
+ if (context->settings) {
+ gboolean dnd_enabled = g_settings_get_boolean (context->settings, "do-not-disturb");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dnd_toggle), dnd_enabled);
+ }
+
+ g_signal_connect (dnd_toggle, "toggled", G_CALLBACK (dnd_toggle_clicked), context);
+ gtk_box_pack_start (GTK_BOX (button_box), dnd_toggle, FALSE, FALSE, 0);
+
+ /* Spacer to push buttons to the right */
+ GtkWidget *spacer = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_pack_start (GTK_BOX (button_box), spacer, TRUE, TRUE, 0);
+
+ /* Then the action buttons */
+ clear_button = gtk_button_new_with_label (_("Clear All"));
+ close_button = gtk_button_new_with_label (_("Close"));
+
+ g_signal_connect (clear_button, "clicked", G_CALLBACK (clear_history), context);
+ g_signal_connect_swapped (close_button, "clicked", G_CALLBACK (gtk_widget_destroy), popup);
+
+ gtk_box_pack_end (GTK_BOX (button_box), close_button, FALSE, FALSE, 0);
+ gtk_box_pack_end (GTK_BOX (button_box), clear_button, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), button_box, FALSE, FALSE, 0);
+
+ /* Place popup window below the applet */
+ window = gtk_widget_get_window (context->main_widget);
+ if (window) {
+ gdk_window_get_origin (window, &x, &y);
+ gtk_window_move (GTK_WINDOW (popup), x, y + gtk_widget_get_allocated_height (context->main_widget));
+ }
+
+ /* Set popup size based on content (with space for buttons) */
+ gtk_window_set_default_size (GTK_WINDOW (popup), 450, content_height + 100);
+ gtk_widget_show_all (popup);
+}
diff --git a/src/capplet/mate-notification-applet-history.h b/src/capplet/mate-notification-applet-history.h
new file mode 100644
index 0000000..37d0c31
--- /dev/null
+++ b/src/capplet/mate-notification-applet-history.h
@@ -0,0 +1,59 @@
+/* mate-notification-applet.c - MATE Notification Applet - History
+ *
+ * Copyright (C) 2025 MATE Developers
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __MATE_NOTIFICATION_APPLET_HISTORY_H__
+#define __MATE_NOTIFICATION_APPLET_HISTORY_H__
+
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+#include <mate-panel-applet.h>
+
+#include "mate-notification-applet-dbus.h"
+
+G_BEGIN_DECLS
+
+/* History context structure */
+typedef struct {
+ MateNotificationDBusContext *dbus_context;
+ GtkWidget *main_widget;
+ GtkWidget *history_popup;
+ GCallback count_update_callback;
+ gpointer count_update_user_data;
+ GSettings *settings;
+} MateNotificationHistoryContext;
+
+/* History popup functions */
+void show_notification_history (MateNotificationHistoryContext *context);
+
+/* Context creation/cleanup */
+MateNotificationHistoryContext *history_context_new (MateNotificationDBusContext *dbus_context,
+ GtkWidget *main_widget,
+ GCallback count_update_callback,
+ gpointer count_update_user_data,
+ GSettings *settings);
+void history_context_free (MateNotificationHistoryContext *context);
+
+/* Context update functions */
+void history_context_update_dbus (MateNotificationHistoryContext *context,
+ MateNotificationDBusContext *dbus_context);
+
+G_END_DECLS
+
+#endif /* __MATE_NOTIFICATION_APPLET_HISTORY_H__ */
diff --git a/src/capplet/mate-notification-applet-menu.xml b/src/capplet/mate-notification-applet-menu.xml
index cab30ac..4b41d57 100644
--- a/src/capplet/mate-notification-applet-menu.xml
+++ b/src/capplet/mate-notification-applet-menu.xml
@@ -1,3 +1,8 @@
<menuitem name="DoNotDisturb Item" action="DoNotDisturb" />
+<separator />
+<menuitem name="ShowHistory Item" action="ShowHistory" />
+<menuitem name="ClearHistory Item" action="ClearHistory" />
+<menuitem name="MarkAllRead Item" action="MarkAllRead" />
+<separator />
<menuitem name="Preferences Item" action="Preferences" />
<menuitem name="About Item" action="About" />
diff --git a/src/capplet/mate-notification-applet.c b/src/capplet/mate-notification-applet.c
index f7d02e0..c9f18e5 100644
--- a/src/capplet/mate-notification-applet.c
+++ b/src/capplet/mate-notification-applet.c
@@ -32,6 +32,7 @@
#include "constants.h"
#include "mate-notification-applet-dbus.h"
+#include "mate-notification-applet-history.h"
typedef struct
{
@@ -41,8 +42,11 @@ typedef struct
GtkWidget *image_off;
GtkActionGroup *action_group;
GSettings *settings;
+ guint unread_count;
+ guint update_timer_id;
MateNotificationDBusContext *dbus_context;
+ MateNotificationHistoryContext *history_context;
} MateNotificationApplet;
static void
@@ -51,11 +55,37 @@ show_about (GtkAction *action,
static void
call_properties (GtkAction *action,
MateNotificationApplet *applet);
+static void
+call_show_history (GtkAction *action,
+ MateNotificationApplet *applet);
+void
+call_clear_history (GtkAction *action,
+ MateNotificationApplet *applet);
+static void
+call_mark_all_read (GtkAction *action,
+ MateNotificationApplet *applet);
+/* D-Bus and notification history functions */
static void
setup_daemon_connection (MateNotificationApplet *applet);
+static void
+update_unread_count (MateNotificationApplet *applet);
+static gboolean
+periodic_update_count (MateNotificationApplet *applet);
+
+/* Click handler functions */
+static gboolean
+applet_button_press_cb (GtkWidget *widget,
+ GdkEventButton *event,
+ MateNotificationApplet *applet);
static const GtkActionEntry applet_menu_actions [] = {
+ { "ShowHistory", "document-open-recent", N_("_Show History"),
+ NULL, NULL, G_CALLBACK (call_show_history) },
+ { "ClearHistory", "edit-clear", N_("_Clear History"),
+ NULL, NULL, G_CALLBACK (call_clear_history) },
+ { "MarkAllRead", "edit-select-all", N_("_Mark All as Read"),
+ NULL, NULL, G_CALLBACK (call_mark_all_read) },
{ "Preferences", "document-properties", N_("_Preferences"),
NULL, NULL, G_CALLBACK (call_properties) },
{ "About", "help-about", N_("_About"),
@@ -72,16 +102,54 @@ applet_destroy (MatePanelApplet *applet_widget,
{
g_assert (applet);
+ if (applet->update_timer_id > 0)
+ g_source_remove (applet->update_timer_id);
+
if (applet->dbus_context) {
dbus_context_free (applet->dbus_context);
}
+ if (applet->history_context) {
+ history_context_free (applet->history_context);
+ }
+
g_object_unref (applet->settings);
g_object_unref (applet->action_group);
g_free (applet);
}
static void
+call_show_history (GtkAction *action,
+ MateNotificationApplet *applet)
+{
+ (void) action;
+
+ if (applet->history_context)
+ show_notification_history (applet->history_context);
+}
+
+void
+call_clear_history (GtkAction *action,
+ MateNotificationApplet *applet)
+{
+ (void) action;
+
+ if (dbus_context_clear_notification_history (applet->dbus_context))
+ update_unread_count (applet);
+}
+
+static void
+call_mark_all_read (GtkAction *action,
+ MateNotificationApplet *applet)
+{
+ (void) action;
+
+ /* Update count after marking all as read */
+ if (dbus_context_mark_all_notifications_as_read (applet->dbus_context))
+ update_unread_count (applet);
+}
+
+static void
call_properties (GtkAction *action,
MateNotificationApplet *applet)
{
@@ -175,7 +243,52 @@ applet_draw_icon (MatePanelApplet *applet_widget,
static void
setup_daemon_connection (MateNotificationApplet *applet)
{
- dbus_context_connect (applet->dbus_context);
+ if (dbus_context_connect (applet->dbus_context)) {
+ update_unread_count (applet);
+ } else {
+ applet->unread_count = 0;
+ }
+
+ /* Update history context with new daemon connection */
+ if (applet->history_context) {
+ history_context_update_dbus (applet->history_context, applet->dbus_context);
+ }
+}
+
+static void
+update_unread_count (MateNotificationApplet *applet)
+{
+ applet->unread_count = dbus_context_get_notification_count (applet->dbus_context);
+}
+
+static gboolean
+applet_button_press_cb (GtkWidget *widget,
+ GdkEventButton *event,
+ MateNotificationApplet *applet)
+{
+ switch (event->button) {
+ case 1: /* Left click */
+ if (applet->history_context) {
+ show_notification_history (applet->history_context);
+ }
+ return TRUE;
+
+ case 2: /* Middle click */
+ case 3: /* Right click handled by context menu */
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+periodic_update_count (MateNotificationApplet *applet)
+{
+ if (applet && dbus_context_is_available (applet->dbus_context)) {
+ update_unread_count (applet);
+ }
+ return TRUE; /* Continue periodic updates */
}
static MateNotificationApplet*
@@ -193,8 +306,10 @@ applet_main (MatePanelApplet *applet_widget)
applet->applet = applet_widget;
applet->settings = g_settings_new (GSETTINGS_SCHEMA);
- /* Initialize D-Bus context */
+ /* Initialize D-Bus context and notification state */
applet->dbus_context = dbus_context_new ();
+ applet->unread_count = 0;
+ applet->update_timer_id = 0;
#ifndef ENABLE_IN_PROCESS
/* needed to clamp ourselves to the panel size */
@@ -220,6 +335,11 @@ applet_main (MatePanelApplet *applet_widget)
g_settings_get_boolean (applet->settings,
GSETTINGS_KEY_DO_NOT_DISTURB));
+ /* click handling */
+ gtk_widget_add_events (GTK_WIDGET (applet_widget), GDK_BUTTON_PRESS_MASK);
+ g_signal_connect (G_OBJECT (applet_widget), "button-press-event",
+ G_CALLBACK (applet_button_press_cb), applet);
+
/* set up context menu */
applet->action_group = gtk_action_group_new ("Do Not Disturb Actions");
#ifdef ENABLE_NLS
@@ -250,8 +370,19 @@ applet_main (MatePanelApplet *applet_widget)
g_signal_connect (G_OBJECT (applet->settings), "changed::" GSETTINGS_KEY_DO_NOT_DISTURB,
G_CALLBACK (settings_changed), applet);
+ /* Create new history context */
+ applet->history_context = history_context_new (applet->dbus_context,
+ GTK_WIDGET (applet->applet),
+ G_CALLBACK (update_unread_count),
+ applet,
+ applet->settings);
+
setup_daemon_connection (applet);
+ /* Set up periodic updates every few seconds */
+#define NOTIFICATION_UPDATE_COUNT 5
+ applet->update_timer_id = g_timeout_add_seconds (NOTIFICATION_UPDATE_COUNT, (GSourceFunc) periodic_update_count, applet);
+
return applet;
}