aboutsummaryrefslogtreecommitdiff
path: root/src/app-section.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/app-section.c')
-rw-r--r--src/app-section.c434
1 files changed, 165 insertions, 269 deletions
diff --git a/src/app-section.c b/src/app-section.c
index aad994d..1106c62 100644
--- a/src/app-section.c
+++ b/src/app-section.c
@@ -34,7 +34,6 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include "dbus-data.h"
#include "gmenuutils.h"
#include "gactionmuxer.h"
-#include "indicator-messages-application.h"
struct _AppSectionPrivate
{
@@ -43,14 +42,11 @@ struct _AppSectionPrivate
IndicatorDesktopShortcuts * ids;
- GCancellable *app_proxy_cancellable;
- IndicatorMessagesApplication *app_proxy;
-
GMenu *menu;
- GMenu *source_menu;
+ GMenuModel *source_menu;
GSimpleActionGroup *static_shortcuts;
- GSimpleActionGroup *source_actions;
+ GActionGroup *source_actions;
GActionMuxer *muxer;
gboolean draws_attention;
@@ -94,6 +90,19 @@ static void launch_action_change_state (GSimpleAction *action,
gpointer user_data);
static void app_section_set_app_info (AppSection *self,
GDesktopAppInfo *appinfo);
+static gboolean any_action_draws_attention (GActionGroup *group,
+ const gchar *ignored_action);
+static void action_added (GActionGroup *group,
+ const gchar *action_name,
+ gpointer user_data);
+static void action_state_changed (GActionGroup *group,
+ const gchar *action_name,
+ GVariant *value,
+ gpointer user_data);
+static void action_removed (GActionGroup *group,
+ const gchar *action_name,
+ gpointer user_data);
+static gboolean action_draws_attention (GVariant *state);
static void desktop_file_changed_cb (GFileMonitor *monitor,
GFile *file,
GFile *other_file,
@@ -161,7 +170,6 @@ static void
app_section_init (AppSection *self)
{
AppSectionPrivate *priv;
- GMenuItem *item;
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
APP_SECTION_TYPE,
@@ -171,19 +179,10 @@ app_section_init (AppSection *self)
priv->appinfo = NULL;
priv->menu = g_menu_new ();
-
- priv->source_menu = g_menu_new ();
- item = g_menu_item_new_section (NULL, G_MENU_MODEL (priv->source_menu));
- g_menu_item_set_attribute (item, "action-namespace", "s", "source");
- g_menu_append_item (priv->menu, item);
- g_object_unref (item);
-
priv->static_shortcuts = g_simple_action_group_new ();
- priv->source_actions = g_simple_action_group_new ();
priv->muxer = g_action_muxer_new ();
g_action_muxer_insert (priv->muxer, NULL, G_ACTION_GROUP (priv->static_shortcuts));
- g_action_muxer_insert (priv->muxer, "source", G_ACTION_GROUP (priv->source_actions));
priv->draws_attention = FALSE;
@@ -250,30 +249,32 @@ app_section_dispose (GObject *object)
AppSection * self = APP_SECTION(object);
AppSectionPrivate * priv = self->priv;
- if (priv->app_proxy_cancellable) {
- g_cancellable_cancel (priv->app_proxy_cancellable);
- g_clear_object (&priv->app_proxy_cancellable);
- }
-
if (priv->desktop_file_monitor) {
g_signal_handlers_disconnect_by_func (priv->desktop_file_monitor, desktop_file_changed_cb, self);
g_clear_object (&priv->desktop_file_monitor);
}
- g_clear_object (&priv->app_proxy);
-
g_clear_object (&priv->menu);
- g_clear_object (&priv->source_menu);
g_clear_object (&priv->static_shortcuts);
- g_clear_object (&priv->source_actions);
if (priv->name_watch_id) {
g_bus_unwatch_name (priv->name_watch_id);
priv->name_watch_id = 0;
}
+ if (priv->source_actions) {
+ g_action_muxer_remove (priv->muxer, "source");
+ g_object_disconnect (priv->source_actions,
+ "any_signal::action-added", action_added, self,
+ "any_signal::action-state-changed", action_state_changed, self,
+ "any_signal::action-removed", action_removed, self,
+ NULL);
+ g_clear_object (&priv->source_actions);
+ }
+
g_clear_object (&priv->muxer);
+ g_clear_object (&priv->source_menu);
g_clear_object (&priv->ids);
g_clear_object (&priv->appinfo);
@@ -313,12 +314,14 @@ nick_activate_cb (GSimpleAction *action,
g_return_if_fail(priv->ids != NULL);
- if (!indicator_desktop_shortcuts_nick_exec_with_context(priv->ids, nick, NULL)) {
+ GAppLaunchContext *context = get_launch_context (g_variant_get_uint32 (param));
+
+ if (!indicator_desktop_shortcuts_nick_exec_with_context(priv->ids, nick, context)) {
g_warning("Unable to execute nick '%s' for desktop file '%s'",
nick, g_desktop_app_info_get_filename (priv->appinfo));
}
- return;
+ g_object_unref (context);
}
static void
@@ -411,9 +414,7 @@ app_section_update_menu (AppSection *self)
item = g_menu_item_new (g_app_info_get_name (G_APP_INFO (priv->appinfo)), "launch");
g_menu_item_set_attribute (item, "x-canonical-type", "s", "ImAppMenuItem");
iconstr = g_icon_to_string (g_app_info_get_icon (G_APP_INFO (priv->appinfo)));
- if (iconstr != NULL) {
- g_menu_item_set_attribute (item, "x-canonical-icon", "s", iconstr);
- }
+ g_menu_item_set_attribute (item, "x-canonical-icon", "s", iconstr);
g_free (iconstr);
g_menu_append_item (priv->menu, item);
@@ -444,11 +445,6 @@ app_section_update_menu (AppSection *self)
g_free(name);
}
- item = g_menu_item_new_section (NULL, G_MENU_MODEL (priv->source_menu));
- g_menu_item_set_attribute (item, "action-namespace", "s", "source");
- g_menu_append_item (priv->menu, item);
- g_object_unref (item);
-
keyfile = g_file_new_for_path (g_desktop_app_info_get_filename (priv->appinfo));
g_file_load_contents_async (keyfile, NULL, keyfile_loaded, self);
@@ -587,242 +583,49 @@ app_section_get_draws_attention (AppSection *self)
void
app_section_clear_draws_attention (AppSection *self)
{
- self->priv->draws_attention = FALSE;
- g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]);
-}
-
-static void
-application_vanished (GDBusConnection *bus,
- const gchar *name,
- gpointer user_data)
-{
- AppSection *self = user_data;
-
- app_section_unset_object_path (self);
-}
-
-static void
-update_draws_attention (AppSection *self)
-{
- AppSectionPrivate *priv = self->priv;
- gchar **actions;
+ AppSectionPrivate * priv = self->priv;
+ gchar **action_names;
gchar **it;
- gboolean draws_attention = FALSE;
-
- actions = g_action_group_list_actions (G_ACTION_GROUP (priv->source_actions));
-
- for (it = actions; *it; it++) {
- GVariant *state;
-
- state = g_action_group_get_action_state (G_ACTION_GROUP (priv->source_actions), *it);
- if (state) {
- gboolean b;
- g_variant_get (state, "(uxsb)", NULL, NULL, NULL, &b);
- draws_attention = b || draws_attention;
- g_variant_unref (state);
- }
-
- if (draws_attention)
- break;
- }
-
- if (draws_attention != priv->draws_attention) {
- priv->draws_attention = draws_attention;
- g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]);
- }
-
- g_strfreev (actions);
-}
-
-static void
-remove_source (AppSection *self,
- const gchar *id)
-{
- AppSectionPrivate *priv = self->priv;
- guint n_items;
- guint i;
-
- n_items = g_menu_model_get_n_items (G_MENU_MODEL (priv->source_menu));
- for (i = 0; i < n_items; i++) {
- gchar *action;
- gboolean found = FALSE;
-
- if (g_menu_model_get_item_attribute (G_MENU_MODEL (priv->source_menu), i,
- G_MENU_ATTRIBUTE_ACTION, "s", &action)) {
- found = g_str_equal (action, id);
- g_free (action);
- }
-
- if (found) {
- g_menu_remove (priv->source_menu, i);
- break;
- }
- }
-
- g_simple_action_group_remove (priv->source_actions, id);
- update_draws_attention (self);
-}
-
-static void
-source_action_activated (GSimpleAction *action,
- GVariant *parameter,
- gpointer user_data)
-{
- AppSection *self = APP_SECTION (user_data);
- AppSectionPrivate *priv = APP_SECTION (user_data)->priv;
-
- g_return_if_fail (priv->app_proxy != NULL);
-
- indicator_messages_application_call_activate_source (priv->app_proxy,
- g_action_get_name (G_ACTION (action)),
- priv->app_proxy_cancellable,
- NULL, NULL);
-
- remove_source (self, g_action_get_name (G_ACTION (action)));
-}
-static void
-sources_listed (GObject *source_object,
- GAsyncResult *result,
- gpointer user_data)
-{
- AppSection *self = user_data;
- AppSectionPrivate *priv = self->priv;
- GVariant *sources = NULL;
- GError *error = NULL;
- GVariantIter iter;
- const gchar *id;
- const gchar *label;
- const gchar *iconstr;
- guint32 count;
- gint64 time;
- const gchar *string;
- gboolean draws_attention;
-
- if (!indicator_messages_application_call_list_sources_finish (INDICATOR_MESSAGES_APPLICATION (source_object),
- &sources, result, &error))
- {
- g_warning ("could not fetch the list of sources: %s", error->message);
- g_error_free (error);
+ if (priv->source_actions == NULL)
return;
- }
- g_menu_clear (priv->source_menu);
- g_simple_action_group_clear (priv->source_actions);
- priv->draws_attention = FALSE;
+ action_names = g_action_group_list_actions (priv->source_actions);
- g_variant_iter_init (&iter, sources);
- while (g_variant_iter_next (&iter, "(&s&s&sux&sb)", &id, &label, &iconstr,
- &count, &time, &string, &draws_attention))
- {
+ for (it = action_names; *it; it++) {
GVariant *state;
- GSimpleAction *action;
- GMenuItem *item;
- state = g_variant_new ("(uxsb)", count, time, string, draws_attention);
- action = g_simple_action_new_stateful (id, NULL, state);
- g_signal_connect (action, "activate", G_CALLBACK (source_action_activated), self);
- g_simple_action_group_insert (priv->source_actions, G_ACTION (action));
+ state = g_action_group_get_action_state (priv->source_actions, *it);
+ if (!state)
+ continue;
- item = g_menu_item_new (label, id);
- g_menu_item_set_attribute (item, "x-canonical-type", "s", "ImSourceMenuItem");
- g_menu_append_item (priv->source_menu, item);
+ /* clear draws-attention while preserving other state */
+ if (action_draws_attention (state)) {
+ guint32 count;
+ gint64 time;
+ const gchar *str;
+ GVariant *new_state;
- priv->draws_attention = priv->draws_attention || draws_attention;
-
- g_object_unref (item);
- g_object_unref (action);
- }
-
- g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]);
-
- g_variant_unref (sources);
-}
+ g_variant_get (state, "(ux&sb)", &count, &time, &str, NULL);
-static void
-source_added (IndicatorMessagesApplication *app,
- const gchar *id,
- const gchar *label,
- const gchar *iconstr,
- guint count,
- gint64 time,
- const gchar *string,
- gboolean draws_attention,
- gpointer user_data)
-{
- AppSection *self = user_data;
- AppSectionPrivate *priv = self->priv;
- GVariant *state;
- GSimpleAction *action;
-
- /* TODO put label and icon into the action as well */
-
- state = g_variant_new ("(uxsb)", count, time, string, draws_attention);
- action = g_simple_action_new_stateful (id, NULL, state);
-
- g_simple_action_group_insert (priv->source_actions, G_ACTION (action));
+ new_state = g_variant_new ("(uxsb)", count, time, str, FALSE);
+ g_action_group_change_action_state (priv->source_actions, *it, new_state);
+ }
- if (draws_attention && !priv->draws_attention) {
- priv->draws_attention = TRUE;
- g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]);
+ g_variant_unref (state);
}
- g_object_unref (action);
-}
-static void
-source_changed (IndicatorMessagesApplication *app,
- const gchar *id,
- const gchar *label,
- const gchar *iconstr,
- guint count,
- gint64 time,
- const gchar *string,
- gboolean draws_attention,
- gpointer user_data)
-{
- AppSection *self = user_data;
- AppSectionPrivate *priv = self->priv;
- GVariant *state;
-
- /* TODO put label and icon into the action as well */
-
- state = g_variant_new ("(uxsb)", count, time, string, draws_attention);
- g_action_group_change_action_state (G_ACTION_GROUP (priv->source_actions), id, state);
-
- update_draws_attention (self);
+ g_strfreev (action_names);
}
static void
-source_removed (IndicatorMessagesApplication *app,
- const gchar *id,
- gpointer user_data)
+application_vanished (GDBusConnection *bus,
+ const gchar *name,
+ gpointer user_data)
{
AppSection *self = user_data;
- remove_source (self, id);
-}
-
-static void
-app_proxy_created (GObject *source_object,
- GAsyncResult *result,
- gpointer user_data)
-{
- AppSectionPrivate *priv = APP_SECTION (user_data)->priv;
- GError *error = NULL;
-
- priv->app_proxy = indicator_messages_application_proxy_new_finish (result, &error);
- if (!priv->app_proxy) {
- g_warning ("could not create application proxy: %s", error->message);
- g_error_free (error);
- return;
- }
-
- indicator_messages_application_call_list_sources (priv->app_proxy, priv->app_proxy_cancellable,
- sources_listed, user_data);
-
- g_signal_connect (priv->app_proxy, "source-added", G_CALLBACK (source_added), user_data);
- g_signal_connect (priv->app_proxy, "source-changed", G_CALLBACK (source_changed), user_data);
- g_signal_connect (priv->app_proxy, "source-removed", G_CALLBACK (source_removed), user_data);
+ app_section_unset_object_path (self);
}
/*
@@ -843,20 +646,27 @@ app_section_set_object_path (AppSection *self,
const gchar *object_path)
{
AppSectionPrivate *priv = self->priv;
+ GMenuItem *item;
g_object_freeze_notify (G_OBJECT (self));
app_section_unset_object_path (self);
- priv->app_proxy_cancellable = g_cancellable_new ();
- indicator_messages_application_proxy_new (bus,
- G_DBUS_PROXY_FLAGS_NONE,
- bus_name,
- object_path,
- priv->app_proxy_cancellable,
- app_proxy_created,
- self);
+ priv->source_actions = G_ACTION_GROUP (g_dbus_action_group_get (bus, bus_name, object_path));
+ g_action_muxer_insert (priv->muxer, "source", priv->source_actions);
- priv->draws_attention = FALSE;
+ priv->draws_attention = any_action_draws_attention (priv->source_actions, NULL);
+ g_object_connect (priv->source_actions,
+ "signal::action-added", action_added, self,
+ "signal::action-state-changed", action_state_changed, self,
+ "signal::action-removed", action_removed, self,
+ NULL);
+
+ priv->source_menu = G_MENU_MODEL (g_dbus_menu_model_get (bus, bus_name, object_path));
+
+ item = g_menu_item_new_section (NULL, priv->source_menu);
+ g_menu_item_set_attribute (item, "action-namespace", "s", "source");
+ g_menu_append_item (priv->menu, item);
+ g_object_unref (item);
priv->name_watch_id = g_bus_watch_name_on_connection (bus, bus_name, 0,
NULL, application_vanished,
@@ -884,19 +694,26 @@ app_section_unset_object_path (AppSection *self)
{
AppSectionPrivate *priv = self->priv;
- if (priv->app_proxy_cancellable) {
- g_cancellable_cancel (priv->app_proxy_cancellable);
- g_clear_object (&priv->app_proxy_cancellable);
- }
- g_clear_object (&priv->app_proxy);
-
if (priv->name_watch_id) {
g_bus_unwatch_name (priv->name_watch_id);
priv->name_watch_id = 0;
}
- g_simple_action_group_clear (priv->source_actions);
- g_menu_clear (priv->source_menu);
+ if (priv->source_actions) {
+ g_object_disconnect (priv->source_actions,
+ "any_signal::action-added", action_added, self,
+ "any_signal::action-state-changed", action_state_changed, self,
+ "any_signal::action-removed", action_removed, self,
+ NULL);
+ g_clear_object (&priv->source_actions);
+ }
+
+ if (priv->source_menu) {
+ /* the last menu item points is linked to the app's menumodel */
+ gint n_items = g_menu_model_get_n_items (G_MENU_MODEL (priv->menu));
+ g_menu_remove (priv->menu, n_items -1);
+ g_clear_object (&priv->source_menu);
+ }
priv->draws_attention = FALSE;
g_clear_pointer (&priv->chat_status, g_free);
@@ -910,6 +727,85 @@ app_section_unset_object_path (AppSection *self)
"launch", g_variant_new_boolean (FALSE));
}
+static gboolean
+action_draws_attention (GVariant *state)
+{
+ gboolean attention;
+
+ if (state && g_variant_is_of_type (state, G_VARIANT_TYPE ("(uxsb)")))
+ g_variant_get_child (state, 3, "b", &attention);
+ else
+ attention = FALSE;
+
+ return attention;
+}
+
+static gboolean
+any_action_draws_attention (GActionGroup *group,
+ const gchar *ignored_action)
+{
+ gchar **actions;
+ gchar **it;
+ gboolean attention = FALSE;
+
+ actions = g_action_group_list_actions (group);
+
+ for (it = actions; *it && !attention; it++) {
+ GVariant *state;
+
+ if (ignored_action && g_str_equal (ignored_action, *it))
+ continue;
+
+ state = g_action_group_get_action_state (group, *it);
+ if (state) {
+ attention = action_draws_attention (state);
+ g_variant_unref (state);
+ }
+ }
+
+ g_strfreev (actions);
+ return attention;
+}
+
+static void
+action_added (GActionGroup *group,
+ const gchar *action_name,
+ gpointer user_data)
+{
+ AppSection *self = user_data;
+ GVariant *state;
+
+ state = g_action_group_get_action_state (group, action_name);
+ if (state) {
+ self->priv->draws_attention |= action_draws_attention (state);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]);
+ g_variant_unref (state);
+ }
+}
+
+static void
+action_state_changed (GActionGroup *group,
+ const gchar *action_name,
+ GVariant *value,
+ gpointer user_data)
+{
+ AppSection *self = user_data;
+
+ self->priv->draws_attention = any_action_draws_attention (group, NULL);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]);
+}
+
+static void
+action_removed (GActionGroup *group,
+ const gchar *action_name,
+ gpointer user_data)
+{
+ AppSection *self = user_data;
+
+ self->priv->draws_attention = any_action_draws_attention (group, action_name);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]);
+}
+
gboolean
app_section_get_uses_chat_status (AppSection *self)
{