From 5a9434c6652ef1117a50a9226f8609cea2ee53c0 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 14 Nov 2012 17:57:54 +0100 Subject: 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. --- libmessaging-menu/messaging-menu-app.c | 511 +++++++++++++++++---------------- 1 file changed, 258 insertions(+), 253 deletions(-) (limited to 'libmessaging-menu') 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 +#include /** * 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); + } } /** -- cgit v1.2.3