diff options
Diffstat (limited to 'src/indicator-notifications.c')
-rw-r--r-- | src/indicator-notifications.c | 287 |
1 files changed, 260 insertions, 27 deletions
diff --git a/src/indicator-notifications.c b/src/indicator-notifications.c index 07ccd26..56ddd7a 100644 --- a/src/indicator-notifications.c +++ b/src/indicator-notifications.c @@ -61,8 +61,10 @@ struct _IndicatorNotificationsPrivate { GList *hidden_items; gboolean clear_on_middle_click; + gboolean do_not_disturb; gboolean have_unread; gboolean hide_indicator; + gboolean swap_clear_settings; gint max_items; @@ -75,7 +77,9 @@ struct _IndicatorNotificationsPrivate { DBusSpy *spy; - GHashTable *blacklist; + GHashTable *filter_list; + + GList *filter_list_hints; GSettings *settings; }; @@ -83,8 +87,12 @@ struct _IndicatorNotificationsPrivate { #include "settings.h" #define INDICATOR_ICON_SIZE 22 -#define INDICATOR_ICON_READ "ayatana-indicator-notification-read" -#define INDICATOR_ICON_UNREAD "ayatana-indicator-notification-unread" +#define INDICATOR_ICON_READ "ayatana-indicator-notification-read" +#define INDICATOR_ICON_UNREAD "ayatana-indicator-notification-unread" +#define INDICATOR_ICON_READ_DND "ayatana-indicator-notification-read-dnd" +#define INDICATOR_ICON_UNREAD_DND "ayatana-indicator-notification-unread-dnd" + +#define HINT_MAX 10 GType indicator_notifications_get_type(void); @@ -108,9 +116,16 @@ static void clear_menuitems(IndicatorNotifications *self); static void insert_menuitem(IndicatorNotifications *self, GtkWidget *item); static void remove_menuitem(IndicatorNotifications *self, GtkWidget *item); static void set_unread(IndicatorNotifications *self, gboolean unread); -static void update_blacklist(IndicatorNotifications *self); +static void update_unread(IndicatorNotifications *self); +static void update_filter_list(IndicatorNotifications *self); static void update_clear_item_markup(IndicatorNotifications *self); static void update_indicator_visibility(IndicatorNotifications *self); +static void load_filter_list_hints(IndicatorNotifications *self); +static void save_filter_list_hints(IndicatorNotifications *self); +static void update_filter_list_hints(IndicatorNotifications *self, Notification *notification); +static void update_do_not_disturb(IndicatorNotifications *self); +static void settings_try_set_boolean(const gchar *schema, const gchar *key, gboolean value); +static void swap_clear_settings_items(IndicatorNotifications *self); /* Callbacks */ static void clear_item_activated_cb(GtkMenuItem *menuitem, gpointer user_data); @@ -193,16 +208,24 @@ indicator_notifications_init(IndicatorNotifications *self) self->priv->spy = dbus_spy_new(); g_signal_connect(self->priv->spy, DBUS_SPY_SIGNAL_MESSAGE_RECEIVED, G_CALLBACK(message_received_cb), self); - /* Initialize an empty blacklist */ - self->priv->blacklist = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + /* Initialize an empty filter list */ + self->priv->filter_list = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); /* Connect to GSettings */ self->priv->settings = g_settings_new(NOTIFICATIONS_SCHEMA); self->priv->clear_on_middle_click = g_settings_get_boolean(self->priv->settings, NOTIFICATIONS_KEY_CLEAR_MC); + self->priv->do_not_disturb = g_settings_get_boolean(self->priv->settings, NOTIFICATIONS_KEY_DND); self->priv->hide_indicator = g_settings_get_boolean(self->priv->settings, NOTIFICATIONS_KEY_HIDE_INDICATOR); self->priv->max_items = g_settings_get_int(self->priv->settings, NOTIFICATIONS_KEY_MAX_ITEMS); - update_blacklist(self); + update_filter_list(self); + self->priv->swap_clear_settings = g_settings_get_boolean(self->priv->settings, NOTIFICATIONS_KEY_SWAP_CLEAR_SETTINGS); + if(self->priv->swap_clear_settings) + swap_clear_settings_items(self); g_signal_connect(self->priv->settings, "changed", G_CALLBACK(setting_changed_cb), self); + + /* Set up filter-list hints */ + self->priv->filter_list_hints = NULL; + load_filter_list_hints(self); } static void @@ -240,9 +263,14 @@ indicator_notifications_dispose(GObject *object) self->priv->settings = NULL; } - if(self->priv->blacklist != NULL) { - g_hash_table_unref(self->priv->blacklist); - self->priv->blacklist = NULL; + if(self->priv->filter_list != NULL) { + g_hash_table_unref(self->priv->filter_list); + self->priv->filter_list = NULL; + } + + if(self->priv->filter_list_hints != NULL) { + g_list_free_full(self->priv->filter_list_hints, g_free); + self->priv->filter_list_hints = NULL; } G_OBJECT_CLASS (indicator_notifications_parent_class)->dispose (object); @@ -263,7 +291,8 @@ get_image(IndicatorObject *io) if(self->priv->image == NULL) { self->priv->image = GTK_IMAGE(gtk_image_new()); - set_unread(self, FALSE); + /* We have to wait until the image is created to update do-not-disturb the first time */ + update_do_not_disturb(self); update_indicator_visibility(self); } @@ -417,35 +446,58 @@ set_unread(IndicatorNotifications *self, gboolean unread) { g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(self)); - if(unread) { - gtk_image_set_from_icon_name(self->priv->image, INDICATOR_ICON_UNREAD, GTK_ICON_SIZE_MENU); + self->priv->have_unread = unread; + update_unread(self); +} + +/** + * update_unread: + * @self: the indicator object + * + * Updates the indicator icons. + **/ +static void +update_unread(IndicatorNotifications *self) +{ + g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(self)); + + if(self->priv->have_unread) { + if (self->priv->do_not_disturb) { + gtk_image_set_from_icon_name(self->priv->image, INDICATOR_ICON_UNREAD_DND, GTK_ICON_SIZE_MENU); + } + else { + gtk_image_set_from_icon_name(self->priv->image, INDICATOR_ICON_UNREAD, GTK_ICON_SIZE_MENU); + } } else { - gtk_image_set_from_icon_name(self->priv->image, INDICATOR_ICON_READ, GTK_ICON_SIZE_MENU); + if (self->priv->do_not_disturb) { + gtk_image_set_from_icon_name(self->priv->image, INDICATOR_ICON_READ_DND, GTK_ICON_SIZE_MENU); + } + else { + gtk_image_set_from_icon_name(self->priv->image, INDICATOR_ICON_READ, GTK_ICON_SIZE_MENU); + } } - - self->priv->have_unread = unread; } /** - * update_blacklist: + * update_filter_list: * @self: the indicator object * - * Updates the blacklist from GSettings. This currently does not filter already + * Updates the filter list from GSettings. This currently does not filter already * allowed messages. It only applies to messages received in the future. **/ static void -update_blacklist(IndicatorNotifications *self) +update_filter_list(IndicatorNotifications *self) { g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(self)); - g_return_if_fail(self->priv->blacklist != NULL); + g_return_if_fail(self->priv->filter_list != NULL); - g_hash_table_remove_all(self->priv->blacklist); - gchar **items = g_settings_get_strv(self->priv->settings, NOTIFICATIONS_KEY_BLACKLIST); + g_hash_table_remove_all(self->priv->filter_list); + gchar **items = g_settings_get_strv(self->priv->settings, NOTIFICATIONS_KEY_FILTER_LIST); int i; for(i = 0; items[i] != NULL; i++) { - g_hash_table_insert(self->priv->blacklist, g_strdup(items[i]), NULL); + g_hash_table_insert(self->priv->filter_list, g_strdup(items[i]), NULL); } g_strfreev(items); @@ -501,6 +553,169 @@ update_indicator_visibility(IndicatorNotifications *self) } /** + * load_filter_list_hints: + * @self: the indicator object + * + * Loads the filter list hints from gsettings + **/ +static void +load_filter_list_hints(IndicatorNotifications *self) +{ + g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(self)); + g_return_if_fail(self->priv->filter_list_hints == NULL); + + gchar **items = g_settings_get_strv(self->priv->settings, NOTIFICATIONS_KEY_FILTER_LIST_HINTS); + int i; + + for (i = 0; items[i] != NULL; i++) { + self->priv->filter_list_hints = g_list_prepend(self->priv->filter_list_hints, items[i]); + } + + g_free(items); +} + +/** + * save_filter_list_hints: + * @self: the indicator object + * + * Saves the filter list hints to gsettings + **/ +static void +save_filter_list_hints(IndicatorNotifications *self) +{ + g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(self)); + + gchar *hints[HINT_MAX + 1]; + int i = 0; + + GList *l; + for (l = self->priv->filter_list_hints; (l != NULL) && (i < HINT_MAX); l = l->next, i++) { + hints[i] = (gchar *) l->data; + } + + hints[i] = NULL; + + g_settings_set_strv(self->priv->settings, NOTIFICATIONS_KEY_FILTER_LIST_HINTS, (const gchar **) hints); +} + +/** + * update_filter_list_hints: + * @self: the indicator object + * + * Adds an application name to the hints + **/ +static void +update_filter_list_hints(IndicatorNotifications *self, Notification *notification) +{ + g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(self)); + g_return_if_fail(IS_NOTIFICATION(notification)); + + const gchar *appname = notification_get_app_name(notification); + + /* Avoid duplicates */ + GList *l; + for (l = self->priv->filter_list_hints; l != NULL; l = l->next) { + if (g_strcmp0(appname, (const gchar *) l->data) == 0) + return; + } + + /* Add the appname */ + self->priv->filter_list_hints = g_list_prepend(self->priv->filter_list_hints, g_strdup(appname)); + + /* Keep only a reasonable number */ + while (g_list_length(self->priv->filter_list_hints) > HINT_MAX) { + GList *last = g_list_last(self->priv->filter_list_hints); + g_free(last->data); + self->priv->filter_list_hints = g_list_delete_link(self->priv->filter_list_hints, last); + } + + /* Save the hints */ + /* FIXME: maybe don't do this every update */ + save_filter_list_hints(self); +} + +/** + * update_do_not_disturb: + * @self: the indicator object + * + * Updates the icon with the do-not-disturb version and sets do-not-disturb options + * on external notification daemons that are supported. + **/ +static void +update_do_not_disturb(IndicatorNotifications *self) +{ + g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(self)); + + update_unread(self); + + /* Mate do-not-disturb support */ + settings_try_set_boolean(MATE_SCHEMA, MATE_KEY_DND, self->priv->do_not_disturb); +} + +/** + * settings_try_set_boolean: + * @schema: the GSettings schema + * @key: the GSettings key + * @value: the boolean value + * + * Checks to see if the schema and key exist before setting the value. + */ +static void +settings_try_set_boolean(const gchar *schema, const gchar *key, gboolean value) +{ + /* Check if we can access the schema */ + GSettingsSchemaSource *source = g_settings_schema_source_get_default(); + if (source == NULL) { + return; + } + + /* Lookup the schema */ + GSettingsSchema *source_schema = g_settings_schema_source_lookup(source, schema, TRUE); + + /* Couldn't find the schema */ + if (source_schema == NULL) { + return; + } + + /* Found the schema, make sure we have the key */ + if (g_settings_schema_has_key(source_schema, key)) { + /* Make sure the key is of boolean type */ + GSettingsSchemaKey *source_key = g_settings_schema_get_key(source_schema, key); + + if (g_variant_type_equal(g_settings_schema_key_get_value_type(source_key), G_VARIANT_TYPE_BOOLEAN)) { + /* Set the value */ + GSettings *settings = g_settings_new(schema); + g_settings_set_boolean(settings, key, value); + g_object_unref(settings); + } + + g_settings_schema_key_unref(source_key); + } + g_settings_schema_unref(source_schema); +} + +/** + * swap_clear_settings_items: + * @self: the indicator object + * + * Swaps the position of the clear and settings items. + **/ +static void +swap_clear_settings_items(IndicatorNotifications *self) +{ + g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(self)); + + // Pick which widget to move + GtkWidget *widget = self->priv->settings_item; + if(self->priv->swap_clear_settings) + widget = self->priv->clear_item; + + gtk_container_remove(GTK_CONTAINER(self->priv->menu), g_object_ref(widget)); + gtk_menu_shell_append(GTK_MENU_SHELL(self->priv->menu), widget); + g_object_unref(widget); +} + +/** * clear_item_activated_cb: * @menuitem: the clear menuitem * @user_data: the indicator object @@ -564,11 +779,19 @@ setting_changed_cb(GSettings *settings, gchar *key, gpointer user_data) self->priv->hide_indicator = g_settings_get_boolean(settings, NOTIFICATIONS_KEY_HIDE_INDICATOR); update_indicator_visibility(self); } + else if(g_strcmp0(key, NOTIFICATIONS_KEY_DND) == 0) { + self->priv->do_not_disturb = g_settings_get_boolean(settings, NOTIFICATIONS_KEY_DND); + update_do_not_disturb(self); + } else if(g_strcmp0(key, NOTIFICATIONS_KEY_CLEAR_MC) == 0) { self->priv->clear_on_middle_click = g_settings_get_boolean(self->priv->settings, NOTIFICATIONS_KEY_CLEAR_MC); } - else if(g_strcmp0(key, NOTIFICATIONS_KEY_BLACKLIST) == 0) { - update_blacklist(self); + else if(g_strcmp0(key, NOTIFICATIONS_KEY_FILTER_LIST) == 0) { + update_filter_list(self); + } + else if(g_strcmp0(key, NOTIFICATIONS_KEY_SWAP_CLEAR_SETTINGS) == 0) { + self->priv->swap_clear_settings = g_settings_get_boolean(self->priv->settings, NOTIFICATIONS_KEY_SWAP_CLEAR_SETTINGS); + swap_clear_settings_items(self); } /* TODO: Trim or extend the notifications list based on "max-items" key * (Currently requires a restart) */ @@ -612,19 +835,29 @@ message_received_cb(DBusSpy *spy, Notification *note, gpointer user_data) g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(user_data)); IndicatorNotifications *self = INDICATOR_NOTIFICATIONS(user_data); + /* Discard notifications if we are hidden */ + if(self->priv->hide_indicator) { + g_object_unref(note); + return; + } + /* Discard useless notifications */ if(notification_is_private(note) || notification_is_empty(note)) { g_object_unref(note); return; } - /* Discard notifications on the blacklist */ - if(self->priv->blacklist != NULL && g_hash_table_contains(self->priv->blacklist, + /* Discard notifications on the filter list */ + if(self->priv->filter_list != NULL && g_hash_table_contains(self->priv->filter_list, notification_get_app_name(note))) { g_object_unref(note); return; } + /* Save a hint for the appname */ + update_filter_list_hints(self, note); + + /* Create the menuitem */ GtkWidget *item = notification_menuitem_new(); notification_menuitem_set_from_notification(NOTIFICATION_MENUITEM(item), note); g_signal_connect(item, NOTIFICATION_MENUITEM_SIGNAL_CLICKED, G_CALLBACK(notification_clicked_cb), self); |