aboutsummaryrefslogtreecommitdiff
path: root/libmessaging-menu
diff options
context:
space:
mode:
authorLars Uebernickel <lars.uebernickel@canonical.com>2012-11-14 17:57:54 +0100
committerLars Uebernickel <lars.uebernickel@canonical.com>2012-11-14 17:57:54 +0100
commit5a9434c6652ef1117a50a9226f8609cea2ee53c0 (patch)
tree61f0c88e1439a04375e27e0cdf26acb1e60f46de /libmessaging-menu
parentebba9b86ef73743af028a82d53afa0a556017fe9 (diff)
downloadayatana-indicator-messages-5a9434c6652ef1117a50a9226f8609cea2ee53c0.tar.gz
ayatana-indicator-messages-5a9434c6652ef1117a50a9226f8609cea2ee53c0.tar.bz2
ayatana-indicator-messages-5a9434c6652ef1117a50a9226f8609cea2ee53c0.zip
Change application to service d-bus protocol
Previously, the protocol was simply a menu model and an action group of the currently active sources. The service inserted the menu as a section into the indicator menu. This doesn't work anymore, because applications can (soon) expose individual messages, and the messaging menu doesn't always display all of those at once. This patch introduces a more specific d-bus API. That API is still considered private: applications have to use libmessaging-menu.
Diffstat (limited to 'libmessaging-menu')
-rw-r--r--libmessaging-menu/messaging-menu-app.c511
1 files changed, 258 insertions, 253 deletions
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);
+ }
}
/**