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.c424
1 files changed, 264 insertions, 160 deletions
diff --git a/src/app-section.c b/src/app-section.c
index 6aac52a..19532f2 100644
--- a/src/app-section.c
+++ b/src/app-section.c
@@ -33,6 +33,7 @@ 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
{
@@ -41,11 +42,14 @@ struct _AppSectionPrivate
IndicatorDesktopShortcuts * ids;
+ GCancellable *app_proxy_cancellable;
+ IndicatorMessagesApplication *app_proxy;
+
GMenu *menu;
- GMenuModel *source_menu;
+ GMenu *source_menu;
GSimpleActionGroup *static_shortcuts;
- GActionGroup *source_actions;
+ GSimpleActionGroup *source_actions;
GActionMuxer *muxer;
gboolean draws_attention;
@@ -89,19 +93,6 @@ 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,
@@ -169,6 +160,7 @@ static void
app_section_init (AppSection *self)
{
AppSectionPrivate *priv;
+ GMenuItem *item;
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
APP_SECTION_TYPE,
@@ -178,10 +170,19 @@ 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;
@@ -248,32 +249,30 @@ 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);
@@ -429,6 +428,11 @@ 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);
@@ -564,49 +568,242 @@ app_section_get_draws_attention (AppSection *self)
void
app_section_clear_draws_attention (AppSection *self)
{
- AppSectionPrivate * priv = self->priv;
- gchar **action_names;
+ 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;
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 (priv->source_actions == NULL)
+ 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);
return;
+ }
- action_names = g_action_group_list_actions (priv->source_actions);
+ g_menu_clear (priv->source_menu);
+ g_simple_action_group_clear (priv->source_actions);
+ priv->draws_attention = FALSE;
- for (it = action_names; *it; it++) {
+ g_variant_iter_init (&iter, sources);
+ while (g_variant_iter_next (&iter, "(&s&s&sux&sb)", &id, &label, &iconstr,
+ &count, &time, &string, &draws_attention))
+ {
GVariant *state;
+ GSimpleAction *action;
+ GMenuItem *item;
- state = g_action_group_get_action_state (priv->source_actions, *it);
- if (!state)
- continue;
+ 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));
- /* clear draws-attention while preserving other state */
- if (action_draws_attention (state)) {
- guint32 count;
- gint64 time;
- const gchar *str;
- GVariant *new_state;
+ 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);
- g_variant_get (state, "(ux&sb)", &count, &time, &str, NULL);
+ priv->draws_attention = priv->draws_attention || draws_attention;
- new_state = g_variant_new ("(uxsb)", count, time, str, FALSE);
- g_action_group_change_action_state (priv->source_actions, *it, new_state);
- }
+ g_object_unref (item);
+ g_object_unref (action);
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]);
+
+ g_variant_unref (sources);
+}
+
+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 */
- g_variant_unref (state);
+ 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));
+
+ if (draws_attention && !priv->draws_attention) {
+ priv->draws_attention = TRUE;
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]);
}
- g_strfreev (action_names);
+ 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);
}
static void
-application_vanished (GDBusConnection *bus,
- const gchar *name,
- gpointer user_data)
+source_removed (IndicatorMessagesApplication *app,
+ const gchar *id,
+ gpointer user_data)
{
AppSection *self = user_data;
- app_section_unset_object_path (self);
+ 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);
}
/*
@@ -627,27 +824,20 @@ 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->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 = 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->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_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->draws_attention = FALSE;
priv->name_watch_id = g_bus_watch_name_on_connection (bus, bus_name, 0,
NULL, application_vanished,
@@ -675,26 +865,19 @@ 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;
}
- 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);
- }
+ g_simple_action_group_clear (priv->source_actions);
+ g_menu_clear (priv->source_menu);
priv->draws_attention = FALSE;
g_clear_pointer (&priv->chat_status, g_free);
@@ -708,85 +891,6 @@ 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)
{