diff options
Diffstat (limited to 'src/capplet/mate-notification-applet.c')
| -rw-r--r-- | src/capplet/mate-notification-applet.c | 378 |
1 files changed, 324 insertions, 54 deletions
diff --git a/src/capplet/mate-notification-applet.c b/src/capplet/mate-notification-applet.c index 7402707..dd2f4e1 100644 --- a/src/capplet/mate-notification-applet.c +++ b/src/capplet/mate-notification-applet.c @@ -25,20 +25,29 @@ #include <glib/gi18n-lib.h> #include <gtk/gtk.h> #include <mate-panel-applet.h> +#include <gio/gio.h> #define MATE_DESKTOP_USE_UNSTABLE_API #include <libmate-desktop/mate-desktop-utils.h> #include "constants.h" +#include "mate-notification-applet-dbus.h" +#include "mate-notification-applet-history.h" typedef struct { MatePanelApplet *applet; - GtkWidget *image_on; - GtkWidget *image_off; + GtkWidget *status_image; + GtkWidget *overlay; + GtkWidget *count_label; GtkActionGroup *action_group; GSettings *settings; + guint unread_count; + guint update_timer_id; + + MateNotificationDBusContext *dbus_context; + MateNotificationHistoryContext *history_context; } MateNotificationApplet; static void @@ -47,8 +56,43 @@ 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 void +update_applet_display (MateNotificationApplet *applet); +static void +update_count_badge (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 void +toggle_do_not_disturb (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"), @@ -65,12 +109,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) { @@ -95,6 +181,7 @@ show_about (GtkAction *action, MateNotificationApplet *applet) { static const char *authors[] = { + "MATE Developers", "Robert Buj <[email protected]>", NULL }; @@ -102,10 +189,10 @@ show_about (GtkAction *action, (void) applet; gtk_show_about_dialog (NULL, - "title", _("About Do Not Disturb"), + "title", _("About Notification Status"), "version", VERSION, "copyright", _("Copyright \xc2\xa9 2021 MATE developers"), - "comments", _("Activate the do not disturb mode quickly."), + "comments", _("Monitor and control notification status."), "authors", authors, "translator-credits", _("translator-credits"), "logo_icon_name", "mate-notification-properties", @@ -114,13 +201,36 @@ show_about (GtkAction *action, static void set_status_image (MateNotificationApplet *applet, - gboolean active) + gboolean dnd_active, + gboolean history_enabled) { + const char *icon_name; + gint size, scale; + cairo_surface_t *surface; + + if (dnd_active && history_enabled) { + icon_name = "user-busy"; + } else if (dnd_active && !history_enabled) { + icon_name = "user-offline"; + } else if (!dnd_active && !history_enabled) { + icon_name = "user-invisible"; + } else { + icon_name = "user-available"; + } + + size = (gint) mate_panel_applet_get_size (applet->applet); + scale = gtk_widget_get_scale_factor (GTK_WIDGET (applet->applet)); + + surface = gtk_icon_theme_load_surface (gtk_icon_theme_get_default (), + icon_name, + size, scale, + NULL, 0, NULL); + if (surface) { + gtk_image_set_from_surface (GTK_IMAGE (applet->status_image), surface); + cairo_surface_destroy (surface); + } + gtk_widget_show_all (GTK_WIDGET (applet->applet)); - if (active) - gtk_widget_hide (applet->image_on); - else - gtk_widget_hide (applet->image_off); } static void @@ -128,47 +238,146 @@ settings_changed (GSettings *settings, gchar *key, MateNotificationApplet *applet) { - if (g_strcmp0 (GSETTINGS_KEY_DO_NOT_DISTURB, key) == 0) - set_status_image (applet, - g_settings_get_boolean (settings, key)); + if (g_strcmp0 (GSETTINGS_KEY_DO_NOT_DISTURB, key) == 0 || + g_strcmp0 (GSETTINGS_KEY_HISTORY_ENABLED, key) == 0) + update_applet_display (applet); } static void -applet_draw_icon (MatePanelApplet *applet_widget, - int arg1, - MateNotificationApplet *applet) +applet_size_changed (MatePanelApplet *applet_widget, + int arg1, + MateNotificationApplet *applet) { - gint size, scale; + update_applet_display (applet); +} - g_assert (applet); +static void +setup_daemon_connection (MateNotificationApplet *applet) +{ + if (dbus_context_connect (applet->dbus_context)) { + update_unread_count (applet); + } else { + applet->unread_count = 0; + } - size = (gint) mate_panel_applet_get_size (applet_widget); - scale = gtk_widget_get_scale_factor (GTK_WIDGET (applet_widget)); + /* 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); + update_applet_display (applet); +} + +static void +update_applet_display (MateNotificationApplet *applet) +{ + gchar *tooltip_text; + gboolean dnd_active; + gboolean history_enabled; + + dnd_active = g_settings_get_boolean (applet->settings, GSETTINGS_KEY_DO_NOT_DISTURB); + history_enabled = g_settings_get_boolean (applet->settings, GSETTINGS_KEY_HISTORY_ENABLED); + + /* Update tooltip based on number of unread notifications and user state */ + if (!history_enabled) + tooltip_text = g_strdup (_("(privacy mode)")); + else if (applet->unread_count > 99) + tooltip_text = g_strdup (_("(99+ unread)")); + else if (applet->unread_count > 0) + tooltip_text = g_strdup_printf ("(%d unread)", applet->unread_count); + else + tooltip_text = g_strdup (""); + + if (dnd_active) + tooltip_text = g_strdup_printf ("Do Not Disturb %s", tooltip_text); + else if (!dbus_context_is_available (applet->dbus_context)) + tooltip_text = g_strdup (_("Notifications (daemon unavailable)")); + else + tooltip_text = g_strdup_printf ("Notifications %s", tooltip_text); + + gtk_widget_set_tooltip_text (GTK_WIDGET (applet->applet), tooltip_text); + g_free (tooltip_text); + + set_status_image (applet, dnd_active, history_enabled); + update_count_badge (applet); +} + +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 */ + toggle_do_not_disturb (applet); + return TRUE; + + case 3: /* Right click handled by context menu */ + default: + break; + } + + return FALSE; +} + +static void +toggle_do_not_disturb (MateNotificationApplet *applet) +{ + gboolean current_state; - cairo_surface_t *image_on = gtk_icon_theme_load_surface (gtk_icon_theme_get_default (), - "user-available", - size, scale, - NULL, 0, NULL); - cairo_surface_t *image_off = gtk_icon_theme_load_surface (gtk_icon_theme_get_default (), - "user-invisible", - size, scale, - NULL, 0, NULL); + current_state = g_settings_get_boolean (applet->settings, GSETTINGS_KEY_DO_NOT_DISTURB); + g_settings_set_boolean (applet->settings, GSETTINGS_KEY_DO_NOT_DISTURB, !current_state); +} - gtk_image_set_from_surface (GTK_IMAGE (applet->image_on), image_on); - gtk_image_set_from_surface (GTK_IMAGE (applet->image_off), image_off); +static void +update_count_badge (MateNotificationApplet *applet) +{ + gchar *count_text; + gboolean history_enabled = g_settings_get_boolean (applet->settings, GSETTINGS_KEY_HISTORY_ENABLED); + + /* Only show count badges when history is enabled and there are notifications */ + if (history_enabled && applet->unread_count > 0) { + if (applet->unread_count > 99) { + count_text = g_strdup ("99+"); + } else { + count_text = g_strdup_printf ("%u", applet->unread_count); + } + gtk_label_set_text (GTK_LABEL (applet->count_label), count_text); + gtk_widget_show (applet->count_label); + g_free (count_text); + } else { + /* Hide badge when no unread notifications */ + gtk_widget_hide (applet->count_label); + } +} - cairo_surface_destroy (image_on); - cairo_surface_destroy (image_off); +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* applet_main (MatePanelApplet *applet_widget) { MateNotificationApplet *applet; - GtkWidget *box; #ifndef ENABLE_IN_PROCESS - g_set_application_name (_("Do Not Disturb")); + g_set_application_name (_("Notification Status")); #endif gtk_window_set_default_icon_name ("mate-notification-properties"); @@ -176,32 +385,67 @@ applet_main (MatePanelApplet *applet_widget) applet->applet = applet_widget; applet->settings = g_settings_new (GSETTINGS_SCHEMA); + /* 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 */ - mate_panel_applet_set_flags (MATE_PANEL_APPLET (applet), MATE_PANEL_APPLET_EXPAND_MINOR); + mate_panel_applet_set_flags (MATE_PANEL_APPLET (applet->applet), MATE_PANEL_APPLET_EXPAND_MINOR); #endif - box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); - - applet->image_on = gtk_image_new (); - applet->image_off = gtk_image_new (); - applet_draw_icon (applet_widget, 0, applet); - - gtk_widget_set_tooltip_text (applet->image_off, _("Do Not Disturb")); - gtk_widget_set_tooltip_text (applet->image_on, _("Notifications Enabled")); - - gtk_box_pack_start (GTK_BOX (box), applet->image_on, - TRUE, TRUE, 0); - gtk_box_pack_start (GTK_BOX (box), applet->image_off, - TRUE, TRUE, 0); - gtk_container_add (GTK_CONTAINER (applet_widget), box); + /* Create overlay for icons and count badge */ + applet->overlay = gtk_overlay_new (); + + /* Create status icon */ + applet->status_image = gtk_image_new (); + + /* Add status icon as main overlay child */ + gtk_container_add (GTK_CONTAINER (applet->overlay), applet->status_image); + + /* Create count badge label */ + applet->count_label = gtk_label_new (""); + gtk_widget_set_name (applet->count_label, "notification-count-badge"); + gtk_widget_set_halign (applet->count_label, GTK_ALIGN_END); + gtk_widget_set_valign (applet->count_label, GTK_ALIGN_END); + GtkCssProvider *css_provider = gtk_css_provider_new (); + const gchar *css_data = + "#notification-count-badge {" + " background-color: rgba(255,255,255,0.9);" + " color: #000000;" + " border-radius: 8px;" + " min-width: 12px;" + " min-height: 4px;" + " padding: 1px 1px;" + " font-size: 10px;" + " font-weight: bold;" + " text-shadow: none;" + " border: 1px solid rgba(0,0,0,0.1);" + "}"; + gtk_css_provider_load_from_data (css_provider, css_data, -1, NULL); + gtk_style_context_add_provider (gtk_widget_get_style_context (applet->count_label), + GTK_STYLE_PROVIDER (css_provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + g_object_unref (css_provider); + + /* Add count label as overlay */ + gtk_overlay_add_overlay (GTK_OVERLAY (applet->overlay), applet->count_label); + + /* Add overlay to applet */ + gtk_container_add (GTK_CONTAINER (applet_widget), applet->overlay); set_status_image (applet, - g_settings_get_boolean (applet->settings, - GSETTINGS_KEY_DO_NOT_DISTURB)); + g_settings_get_boolean (applet->settings, GSETTINGS_KEY_DO_NOT_DISTURB), + g_settings_get_boolean (applet->settings, GSETTINGS_KEY_HISTORY_ENABLED)); + + /* 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"); + applet->action_group = gtk_action_group_new ("Notification Status Actions"); #ifdef ENABLE_NLS gtk_action_group_set_translation_domain (applet->action_group, GETTEXT_PACKAGE); #endif /* ENABLE_NLS */ @@ -215,6 +459,13 @@ applet_main (MatePanelApplet *applet_widget) gtk_action_group_add_action (applet->action_group, GTK_ACTION (do_not_disturb_toggle_action)); + GtkToggleAction *history_toggle_action = + gtk_toggle_action_new ("HistoryEnabled", _("_Enable History"), + _("Enable/Disable notification history."), NULL); + + gtk_action_group_add_action (applet->action_group, + GTK_ACTION (history_toggle_action)); + mate_panel_applet_setup_menu_from_resource (applet->applet, RESOURCE_PATH "menu.xml", applet->action_group); @@ -223,12 +474,31 @@ applet_main (MatePanelApplet *applet_widget) do_not_disturb_toggle_action, "active", G_SETTINGS_BIND_DEFAULT); + g_settings_bind (applet->settings, "history-enabled", + history_toggle_action, "active", + G_SETTINGS_BIND_DEFAULT); + g_signal_connect (G_OBJECT (applet->applet), "destroy", G_CALLBACK (applet_destroy), applet); - /* GSettings callback */ + /* GSettings callbacks */ g_signal_connect (G_OBJECT (applet->settings), "changed::" GSETTINGS_KEY_DO_NOT_DISTURB, G_CALLBACK (settings_changed), applet); + g_signal_connect (G_OBJECT (applet->settings), "changed::" GSETTINGS_KEY_HISTORY_ENABLED, + 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; } @@ -245,7 +515,7 @@ applet_factory (MatePanelApplet *applet_widget, applet = applet_main (applet_widget); g_signal_connect (G_OBJECT (applet_widget), "change_size", - G_CALLBACK (applet_draw_icon), + G_CALLBACK (applet_size_changed), (gpointer) applet); return TRUE; @@ -256,6 +526,6 @@ applet_factory (MatePanelApplet *applet_widget, PANEL_APPLET_FACTORY ("MateNotificationAppletFactory", PANEL_TYPE_APPLET, - "Do Not Disturb Applet", + "Notification Status Applet", applet_factory, NULL) |
