aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/Makefile.am12
-rw-r--r--common/com.canonical.indicator.messages.application.xml21
-rw-r--r--libmessaging-menu/messaging-menu-app.c511
-rw-r--r--src/app-section.c424
-rw-r--r--test/Makefile.am1
5 files changed, 555 insertions, 414 deletions
diff --git a/common/Makefile.am b/common/Makefile.am
index 5bd7e20..0b8bad4 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -9,9 +9,19 @@ indicator-messages-service.c: com.canonical.indicator.messages.service.xml
$^
indicator-messages-service.h: indicator-messages-service.c
+indicator-messages-application.c: com.canonical.indicator.messages.application.xml
+ $(AM_V_GEN) gdbus-codegen \
+ --interface-prefix com.canonical.indicator.messages. \
+ --generate-c-code indicator-messages-application \
+ --c-namespace IndicatorMessages \
+ $^
+indicator-messages-application.h: indicator-messages-application.c
+
BUILT_SOURCES = \
indicator-messages-service.c \
- indicator-messages-service.h
+ indicator-messages-service.h \
+ indicator-messages-application.c \
+ indicator-messages-application.h
libmessaging_common_la_SOURCES = \
$(BUILT_SOURCES)
diff --git a/common/com.canonical.indicator.messages.application.xml b/common/com.canonical.indicator.messages.application.xml
new file mode 100644
index 0000000..ec095dd
--- /dev/null
+++ b/common/com.canonical.indicator.messages.application.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node name="/">
+ <interface name="com.canonical.indicator.messages.application">
+ <method name="ListSources">
+ <arg type="a(sssuxsb)" name="sources" direction="out" />
+ </method>
+ <method name="ActivateSource">
+ <arg type="s" name="source_id" direction="in" />
+ </method>
+ <signal name="SourceAdded">
+ <arg type="u" name="position" direction="in" />
+ <arg type="(sssuxsb)" name="source" direction="in" />
+ </signal>
+ <signal name="SourceChanged">
+ <arg type="(sssuxsb)" name="source" direction="in" />
+ </signal>
+ <signal name="SourceRemoved">
+ <arg type="s" name="source_id" direction="in" />
+ </signal>
+ </interface>
+</node>
diff --git a/libmessaging-menu/messaging-menu-app.c b/libmessaging-menu/messaging-menu-app.c
index 377aea0..20d9474 100644
--- a/libmessaging-menu/messaging-menu-app.c
+++ b/libmessaging-menu/messaging-menu-app.c
@@ -19,8 +19,10 @@
#include "messaging-menu-app.h"
#include "indicator-messages-service.h"
+#include "indicator-messages-application.h"
#include <gio/gdesktopappinfo.h>
+#include <string.h>
/**
* SECTION:messaging-menu
@@ -102,14 +104,13 @@ struct _MessagingMenuApp
int registered; /* -1 for unknown */
MessagingMenuStatus status;
gboolean status_set;
- GSimpleActionGroup *source_actions;
- GMenu *menu;
GDBusConnection *bus;
+ GList *sources;
+ IndicatorMessagesApplication *app_interface;
+
IndicatorMessagesService *messages_service;
guint watch_id;
- guint action_export_id;
- guint menu_export_id;
GCancellable *cancellable;
};
@@ -133,10 +134,56 @@ static guint signals[N_SIGNALS];
static const gchar *status_ids[] = { "available", "away", "busy", "invisible", "offline" };
+typedef struct
+{
+ gchar *id;
+ GIcon *icon;
+ gchar *label;
+
+ guint32 count;
+ gint64 time;
+ gchar *string;
+ gboolean draws_attention;
+} Source;
+
static void global_status_changed (IndicatorMessagesService *service,
const gchar *status_str,
gpointer user_data);
+static void
+source_free (Source *source)
+{
+ if (source)
+ {
+ g_free (source->id);
+ g_clear_object (&source->icon);
+ g_free (source->label);
+ g_free (source->string);
+ g_slice_free (Source, source);
+ }
+}
+
+static GVariant *
+source_to_variant (Source *source)
+{
+ GVariant *v;
+ gchar *iconstr;
+
+ iconstr = source->icon ? g_icon_to_string (source->icon) : NULL;
+
+ v = g_variant_new ("(sssuxsb)", source->id,
+ source->label,
+ iconstr ? iconstr : "",
+ source->count,
+ source->time,
+ source->string ? source->string : "",
+ source->draws_attention);
+
+ g_free (iconstr);
+
+ return v;
+}
+
static gchar *
messaging_menu_app_get_dbus_object_path (MessagingMenuApp *app)
{
@@ -155,18 +202,14 @@ messaging_menu_app_get_dbus_object_path (MessagingMenuApp *app)
}
static void
-export_menus_and_actions (GObject *source,
- GAsyncResult *res,
- gpointer user_data)
+messaging_menu_app_got_bus (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
{
MessagingMenuApp *app = user_data;
GError *error = NULL;
gchar *object_path;
- object_path = messaging_menu_app_get_dbus_object_path (app);
- if (!object_path)
- return;
-
app->bus = g_bus_get_finish (res, &error);
if (app->bus == NULL)
{
@@ -175,23 +218,13 @@ export_menus_and_actions (GObject *source,
return;
}
- app->action_export_id = g_dbus_connection_export_action_group (app->bus,
- object_path,
- G_ACTION_GROUP (app->source_actions),
- &error);
- if (!app->action_export_id)
- {
- g_warning ("unable to export action group: %s", error->message);
- g_clear_error (&error);
- }
+ object_path = messaging_menu_app_get_dbus_object_path (app);
- app->menu_export_id = g_dbus_connection_export_menu_model (app->bus,
- object_path,
- G_MENU_MODEL (app->menu),
- &error);
- if (!app->menu_export_id)
+ if (object_path &&
+ !g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (app->app_interface),
+ app->bus, object_path, &error))
{
- g_warning ("unable to export menu: %s", error->message);
+ g_warning ("unable to export application interface: %s", error->message);
g_clear_error (&error);
}
@@ -214,7 +247,7 @@ messaging_menu_app_set_desktop_id (MessagingMenuApp *app,
g_bus_get (G_BUS_TYPE_SESSION,
app->cancellable,
- export_menus_and_actions,
+ messaging_menu_app_got_bus,
app);
}
@@ -248,20 +281,6 @@ messaging_menu_app_dispose (GObject *object)
{
MessagingMenuApp *app = MESSAGING_MENU_APP (object);
- if (app->bus)
- {
- if (app->action_export_id > 0)
- g_dbus_connection_unexport_action_group (app->bus, app->action_export_id);
-
- if (app->menu_export_id > 0)
- g_dbus_connection_unexport_menu_model (app->bus, app->menu_export_id);
-
- app->action_export_id = 0;
- app->menu_export_id = 0;
- g_object_unref (app->bus);
- app->bus = NULL;
- }
-
if (app->watch_id > 0)
{
g_bus_unwatch_name (app->watch_id);
@@ -283,9 +302,9 @@ messaging_menu_app_dispose (GObject *object)
g_clear_object (&app->messages_service);
}
+ g_clear_object (&app->app_interface);
g_clear_object (&app->appinfo);
- g_clear_object (&app->source_actions);
- g_clear_object (&app->menu);
+ g_clear_object (&app->bus);
G_OBJECT_CLASS (messaging_menu_app_parent_class)->dispose (object);
}
@@ -416,6 +435,72 @@ indicator_messages_vanished (GDBusConnection *bus,
}
}
+static gboolean
+messaging_menu_app_list_sources (IndicatorMessagesApplication *app_interface,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ MessagingMenuApp *app = user_data;
+ GVariantBuilder builder;
+ GList *it;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sssuxsb)"));
+
+ for (it = app->sources; it; it = it->next)
+ g_variant_builder_add_value (&builder, source_to_variant (it->data));
+
+ indicator_messages_application_complete_list_sources (app_interface,
+ invocation,
+ g_variant_builder_end (&builder));
+
+ return TRUE;
+}
+
+static gint
+compare_source_id (gconstpointer a,
+ gconstpointer b)
+{
+ const Source *source = a;
+ const gchar *id = b;
+
+ return strcmp (source->id, id);
+}
+
+static gboolean
+messaging_menu_app_remove_source_internal (MessagingMenuApp *app,
+ const gchar *source_id)
+{
+ GList *node;
+
+ node = g_list_find_custom (app->sources, source_id, compare_source_id);
+ if (node)
+ {
+ source_free (node->data);
+ app->sources = g_list_delete_link (app->sources, node);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+messaging_menu_app_activate_source (IndicatorMessagesApplication *app_interface,
+ GDBusMethodInvocation *invocation,
+ const gchar *source_id,
+ gpointer user_data)
+{
+ MessagingMenuApp *app = user_data;
+ GQuark q = g_quark_from_string (source_id);
+
+ /* Activate implies removing the source, no need for SourcesChanged */
+ if (messaging_menu_app_remove_source_internal (app, source_id))
+ g_signal_emit (app, signals[ACTIVATE_SOURCE], q, source_id);
+
+ indicator_messages_application_complete_activate_source (app_interface, invocation);
+
+ return TRUE;
+}
+
static void
messaging_menu_app_init (MessagingMenuApp *app)
{
@@ -423,13 +508,13 @@ messaging_menu_app_init (MessagingMenuApp *app)
app->status_set = FALSE;
app->bus = NULL;
- app->action_export_id = 0;
- app->menu_export_id = 0;
-
app->cancellable = g_cancellable_new ();
- app->source_actions = g_simple_action_group_new ();
- app->menu = g_menu_new ();
+ app->app_interface = indicator_messages_application_skeleton_new ();
+ g_signal_connect (app->app_interface, "handle-list-sources",
+ G_CALLBACK (messaging_menu_app_list_sources), app);
+ g_signal_connect (app->app_interface, "handle-activate-source",
+ G_CALLBACK (messaging_menu_app_activate_source), app);
app->cancellable = g_cancellable_new ();
@@ -604,123 +689,73 @@ global_status_changed (IndicatorMessagesService *service,
g_signal_emit (app, signals[STATUS_CHANGED], 0, status);
}
-static void
-source_action_activated (GSimpleAction *action,
- GVariant *parameter,
- gpointer user_data)
+static Source *
+messaging_menu_app_lookup_source (MessagingMenuApp *app,
+ const gchar *id)
{
- MessagingMenuApp *app = user_data;
- const gchar *name = g_action_get_name (G_ACTION (action));
- GQuark q = g_quark_from_string (name);
+ GList *node;
- messaging_menu_app_remove_source (app, name);
+ node = g_list_find_custom (app->sources, id, compare_source_id);
- g_signal_emit (app, signals[ACTIVATE_SOURCE], q, name);
+ return node ? node->data : NULL;
}
-static void
-messaging_menu_app_insert_source_action (MessagingMenuApp *app,
- gint position,
- const gchar *id,
- GIcon *icon,
- const gchar *label,
- GVariant *state)
+static Source *
+messaging_menu_app_get_source (MessagingMenuApp *app,
+ const gchar *id)
{
- GSimpleAction *action;
- GMenuItem *menuitem;
+ Source *source;
- g_return_if_fail (MESSAGING_MENU_IS_APP (app));
- g_return_if_fail (id != NULL);
-
- if (g_simple_action_group_lookup (app->source_actions, id))
- {
- g_warning ("a source with id '%s' already exists", id);
- return;
- }
-
- action = g_simple_action_new_stateful (id, NULL, state);
- g_signal_connect (action, "activate",
- G_CALLBACK (source_action_activated), app);
- g_simple_action_group_insert (app->source_actions, G_ACTION (action));
- g_object_unref (action);
-
- menuitem = g_menu_item_new (label, id);
- g_menu_item_set_attribute (menuitem, "x-canonical-type", "s", "ImSourceMenuItem");
- if (icon)
- {
- gchar *iconstr = g_icon_to_string (icon);
- g_menu_item_set_attribute (menuitem, "x-canonical-icon", "s", iconstr);
- g_free (iconstr);
- }
- g_menu_insert_item (app->menu, position, menuitem);
- g_object_unref (menuitem);
-}
-
-static GSimpleAction *
-messaging_menu_app_get_source_action (MessagingMenuApp *app,
- const gchar *source_id)
-
-{
- GAction *action;
-
- g_return_val_if_fail (MESSAGING_MENU_IS_APP (app), NULL);
- g_return_val_if_fail (source_id != NULL, NULL);
-
- action = g_simple_action_group_lookup (app->source_actions, source_id);
- if (action == NULL)
- g_warning ("a source with id '%s' doesn't exist", source_id);
+ source = messaging_menu_app_lookup_source (app, id);
+ if (!source)
+ g_warning ("a source with id '%s' doesn't exist", id);
- return G_SIMPLE_ACTION (action);
+ return source;
}
static void
-messaging_menu_app_set_source_action (MessagingMenuApp *app,
- const gchar *source_id,
- guint count,
- gint64 time,
- const gchar *string)
+messaging_menu_app_notify_source_changed (MessagingMenuApp *app,
+ Source *source)
{
- GSimpleAction *action;
- GVariant *state;
- gboolean draws_attention;
- GVariant *new_state;
-
- action = messaging_menu_app_get_source_action (app, source_id);
- if (!action)
- return;
-
- state = g_action_get_state (G_ACTION (action));
- g_variant_get_child (state, 3, "b", &draws_attention);
-
- new_state = g_variant_new ("(uxsb)", count, time, string, draws_attention);
- g_simple_action_set_state (action, new_state);
-
- g_variant_unref (state);
+ indicator_messages_application_emit_source_changed (app->app_interface,
+ source_to_variant (source));
}
static void
-messaging_menu_app_set_draws_attention (MessagingMenuApp *app,
- const gchar *source_id,
- gboolean draws_attention)
+messaging_menu_app_insert_source_internal (MessagingMenuApp *app,
+ gint position,
+ const gchar *id,
+ GIcon *icon,
+ const gchar *label,
+ guint count,
+ gint64 time,
+ const gchar *string)
{
- GSimpleAction *action;
- GVariant *state;
- guint count;
- gint64 time;
- const gchar *string;
- GVariant *new_state;
+ Source *source;
- action = messaging_menu_app_get_source_action (app, source_id);
- if (!action)
- return;
-
- state = g_action_get_state (G_ACTION (action));
- g_variant_get (state, "(ux&sb)", &count, &time, &string);
+ g_return_if_fail (MESSAGING_MENU_IS_APP (app));
+ g_return_if_fail (id != NULL);
+ g_return_if_fail (label != NULL);
- new_state = g_variant_new ("(uxsb)", count, time, string, TRUE);
- g_simple_action_set_state (action, new_state);
+ if (messaging_menu_app_lookup_source (app, id))
+ {
+ g_warning ("a source with id '%s' already exists", id);
+ return;
+ }
- g_variant_unref (state);
+ source = g_slice_new0 (Source);
+ source->id = g_strdup (id);
+ source->label = g_strdup (label);
+ if (icon)
+ source->icon = g_object_ref (icon);
+ source->count = count;
+ source->time = time;
+ source->string = g_strdup (string);
+ app->sources = g_list_insert (app->sources, source, position);
+
+ indicator_messages_application_emit_source_added (app->app_interface,
+ position,
+ source_to_variant (source));
}
/**
@@ -797,8 +832,7 @@ messaging_menu_app_insert_source_with_count (MessagingMenuApp *app,
const gchar *label,
guint count)
{
- messaging_menu_app_insert_source_action (app, position, id, icon, label,
- g_variant_new ("(uxsb)", count, 0, "", FALSE));
+ messaging_menu_app_insert_source_internal (app, position, id, icon, label, count, 0, "");
}
/**
@@ -852,8 +886,7 @@ messaging_menu_app_insert_source_with_time (MessagingMenuApp *app,
const gchar *label,
gint64 time)
{
- messaging_menu_app_insert_source_action (app, position, id, icon, label,
- g_variant_new ("(uxsb)", 0, time, "", FALSE));
+ messaging_menu_app_insert_source_internal (app, position, id, icon, label, 0, time, "");
}
/**
@@ -909,8 +942,7 @@ messaging_menu_app_insert_source_with_string (MessagingMenuApp *app,
const gchar *label,
const gchar *str)
{
- messaging_menu_app_insert_source_action (app, position, id, icon, label,
- g_variant_new ("(uxsb)", 0, 0, str, FALSE));
+ messaging_menu_app_insert_source_internal (app, position, id, icon, label, 0, 0, str);
}
/**
@@ -950,34 +982,11 @@ void
messaging_menu_app_remove_source (MessagingMenuApp *app,
const gchar *source_id)
{
- int n_items;
- int i;
-
g_return_if_fail (MESSAGING_MENU_IS_APP (app));
g_return_if_fail (source_id != NULL);
- if (g_simple_action_group_lookup (app->source_actions, source_id) == NULL)
- return;
-
- n_items = g_menu_model_get_n_items (G_MENU_MODEL (app->menu));
- for (i = 0; i < n_items; i++)
- {
- gchar *action;
-
- if (g_menu_model_get_item_attribute (G_MENU_MODEL (app->menu), i,
- "action", "s", &action))
- {
- if (!g_strcmp0 (action, source_id))
- {
- g_menu_remove (app->menu, i);
- break;
- }
-
- g_free (action);
- }
- }
-
- g_simple_action_group_remove (app->source_actions, source_id);
+ if (messaging_menu_app_remove_source_internal (app, source_id))
+ indicator_messages_application_emit_source_removed (app->app_interface, source_id);
}
/**
@@ -994,46 +1003,7 @@ messaging_menu_app_has_source (MessagingMenuApp *app,
g_return_val_if_fail (MESSAGING_MENU_IS_APP (app), FALSE);
g_return_val_if_fail (source_id != NULL, FALSE);
- return g_simple_action_group_lookup (app->source_actions, source_id) != NULL;
-}
-
-static GMenuItem *
-g_menu_find_item_with_action (GMenu *menu,
- const gchar *action,
- gint *out_pos)
-{
- gint i;
- gint n_elements;
- GMenuItem *item = NULL;
-
- n_elements = g_menu_model_get_n_items (G_MENU_MODEL (menu));
-
- for (i = 0; i < n_elements && item == NULL; i++)
- {
- GVariant *attr;
-
- item = g_menu_item_new_from_model (G_MENU_MODEL (menu), i);
- attr = g_menu_item_get_attribute_value (item, G_MENU_ATTRIBUTE_ACTION, G_VARIANT_TYPE_STRING);
-
- if (!g_str_equal (action, g_variant_get_string (attr, NULL)))
- g_clear_object (&item);
-
- g_variant_unref (attr);
- }
-
- if (item && out_pos)
- *out_pos = i - 1;
-
- return item;
-}
-
-static void
-g_menu_replace_item (GMenu *menu,
- gint pos,
- GMenuItem *item)
-{
- g_menu_remove (menu, pos);
- g_menu_insert_item (menu, pos, item);
+ return messaging_menu_app_lookup_source (app, source_id) != NULL;
}
/**
@@ -1049,21 +1019,19 @@ messaging_menu_app_set_source_label (MessagingMenuApp *app,
const gchar *source_id,
const gchar *label)
{
- gint pos;
- GMenuItem *item;
+ Source *source;
g_return_if_fail (MESSAGING_MENU_IS_APP (app));
g_return_if_fail (source_id != NULL);
g_return_if_fail (label != NULL);
- item = g_menu_find_item_with_action (app->menu, source_id, &pos);
- if (item == NULL)
- return;
-
- g_menu_item_set_attribute (item, G_MENU_ATTRIBUTE_LABEL, "s", label);
- g_menu_replace_item (app->menu, pos, item);
-
- g_object_unref (item);
+ source = messaging_menu_app_get_source (app, source_id);
+ if (source)
+ {
+ g_free (source->label);
+ source->label = g_strdup (label);
+ messaging_menu_app_notify_source_changed (app, source);
+ }
}
/**
@@ -1079,33 +1047,19 @@ messaging_menu_app_set_source_icon (MessagingMenuApp *app,
const gchar *source_id,
GIcon *icon)
{
- gint pos;
- GMenuItem *item;
+ Source *source;
g_return_if_fail (MESSAGING_MENU_IS_APP (app));
g_return_if_fail (source_id != NULL);
- item = g_menu_find_item_with_action (app->menu, source_id, &pos);
- if (item == NULL)
- return;
-
- if (icon)
+ source = messaging_menu_app_get_source (app, source_id);
+ if (source)
{
- gchar *iconstr;
-
- iconstr = g_icon_to_string (icon);
- g_menu_item_set_attribute (item, "x-canonical-icon", "s", iconstr);
-
- g_free (iconstr);
- }
- else
- {
- g_menu_item_set_attribute_value (item, "x-canonical-icon", NULL);
+ g_clear_object (&source->icon);
+ if (icon)
+ source->icon = g_object_ref (icon);
+ messaging_menu_app_notify_source_changed (app, source);
}
-
- g_menu_replace_item (app->menu, pos, item);
-
- g_object_unref (item);
}
/**
@@ -1120,7 +1074,17 @@ void messaging_menu_app_set_source_count (MessagingMenuApp *app,
const gchar *source_id,
guint count)
{
- messaging_menu_app_set_source_action (app, source_id, count, 0, "");
+ Source *source;
+
+ g_return_if_fail (MESSAGING_MENU_IS_APP (app));
+ g_return_if_fail (source_id != NULL);
+
+ source = messaging_menu_app_get_source (app, source_id);
+ if (source)
+ {
+ source->count = count;
+ messaging_menu_app_notify_source_changed (app, source);
+ }
}
/**
@@ -1136,7 +1100,17 @@ messaging_menu_app_set_source_time (MessagingMenuApp *app,
const gchar *source_id,
gint64 time)
{
- messaging_menu_app_set_source_action (app, source_id, 0, time, "");
+ Source *source;
+
+ g_return_if_fail (MESSAGING_MENU_IS_APP (app));
+ g_return_if_fail (source_id != NULL);
+
+ source = messaging_menu_app_get_source (app, source_id);
+ if (source)
+ {
+ source->time = time;
+ messaging_menu_app_notify_source_changed (app, source);
+ }
}
/**
@@ -1152,7 +1126,18 @@ messaging_menu_app_set_source_string (MessagingMenuApp *app,
const gchar *source_id,
const gchar *str)
{
- messaging_menu_app_set_source_action (app, source_id, 0, 0, str);
+ Source *source;
+
+ g_return_if_fail (MESSAGING_MENU_IS_APP (app));
+ g_return_if_fail (source_id != NULL);
+
+ source = messaging_menu_app_get_source (app, source_id);
+ if (source)
+ {
+ g_free (source->string);
+ source->string = g_strdup (str);
+ messaging_menu_app_notify_source_changed (app, source);
+ }
}
/**
@@ -1170,7 +1155,17 @@ void
messaging_menu_app_draw_attention (MessagingMenuApp *app,
const gchar *source_id)
{
- messaging_menu_app_set_draws_attention (app, source_id, TRUE);
+ Source *source;
+
+ g_return_if_fail (MESSAGING_MENU_IS_APP (app));
+ g_return_if_fail (source_id != NULL);
+
+ source = messaging_menu_app_get_source (app, source_id);
+ if (source)
+ {
+ source->draws_attention = TRUE;
+ messaging_menu_app_notify_source_changed (app, source);
+ }
}
/**
@@ -1191,7 +1186,17 @@ void
messaging_menu_app_remove_attention (MessagingMenuApp *app,
const gchar *source_id)
{
- messaging_menu_app_set_draws_attention (app, source_id, TRUE);
+ Source *source;
+
+ g_return_if_fail (MESSAGING_MENU_IS_APP (app));
+ g_return_if_fail (source_id != NULL);
+
+ source = messaging_menu_app_get_source (app, source_id);
+ if (source)
+ {
+ source->draws_attention = FALSE;
+ messaging_menu_app_notify_source_changed (app, source);
+ }
}
/**
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)
{
diff --git a/test/Makefile.am b/test/Makefile.am
index ee7cb3e..8c8c160 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -53,6 +53,7 @@ libindicator_messages_service_la_CFLAGS = \
$(APPLET_CFLAGS) \
$(COVERAGE_CFLAGS) \
-I$(top_builddir)/src \
+ -I$(top_builddir)/common \
-Wall \
-Wl,-Bsymbolic-functions \
-Wl,-z,defs \