From 78edb4ee5c8a585986fa092ee4ec81fad442f64f Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Fri, 15 Jun 2012 14:59:15 +0200 Subject: Add first version of a libmessaging-menu client library --- libmessaging-menu/messaging-menu.c | 746 +++++++++++++++++++++++++++++++++++++ 1 file changed, 746 insertions(+) create mode 100644 libmessaging-menu/messaging-menu.c (limited to 'libmessaging-menu/messaging-menu.c') diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c new file mode 100644 index 0000000..5b786af --- /dev/null +++ b/libmessaging-menu/messaging-menu.c @@ -0,0 +1,746 @@ +/* + * Copyright 2012 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Lars Uebernickel + */ + +#include "messaging-menu.h" +#include "indicator-messages-service.h" + +#include + +/** + * SECTION:messagingmenuapp + * @title: MessagingMenuApp + * @short_description: An application section in the messaging menu + */ +struct _MessagingMenuApp +{ + GObject parent_instance; + + GDesktopAppInfo *appinfo; + int registered; /* -1 for unknown */ + MessagingMenuStatus status; + GSimpleActionGroup *source_actions; + GMenu *menu; + + IndicatorMessagesService *messages_service; + + GCancellable *cancellable; +}; + +G_DEFINE_TYPE (MessagingMenuApp, messaging_menu_app, G_TYPE_OBJECT); + +enum { + PROP_0, + PROP_DESKTOP_ID, + N_PROPERTIES +}; + +enum { + ACTIVATE_SOURCE, + N_SIGNALS +}; + +static GParamSpec *properties[N_PROPERTIES]; +static guint signals[N_SIGNALS]; + +static void +messaging_menu_app_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MessagingMenuApp *app = MESSAGING_MENU_APP (object); + + switch (prop_id) + { + case PROP_DESKTOP_ID: + app->appinfo = g_desktop_app_info_new (g_value_get_string (value)); + if (app->appinfo == NULL) + { + g_warning ("could not find the desktop file for '%s'", + g_value_get_string (value)); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +messaging_menu_app_finalize (GObject *object) +{ + G_OBJECT_CLASS (messaging_menu_app_parent_class)->finalize (object); +} + +static void +messaging_menu_app_dispose (GObject *object) +{ + MessagingMenuApp *app = MESSAGING_MENU_APP (object); + + if (app->cancellable) + { + g_cancellable_cancel (app->cancellable); + g_object_unref (app->cancellable); + app->cancellable = NULL; + } + + g_clear_object (&app->appinfo); + g_clear_object (&app->source_actions); + g_clear_object (&app->menu); + + G_OBJECT_CLASS (messaging_menu_app_parent_class)->dispose (object); +} + +static void +messaging_menu_app_class_init (MessagingMenuAppClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->set_property = messaging_menu_app_set_property; + object_class->finalize = messaging_menu_app_finalize; + object_class->dispose = messaging_menu_app_dispose; + + properties[PROP_DESKTOP_ID] = g_param_spec_string ("desktop-id", + "Desktop Id", + "The desktop id of the associated application", + NULL, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + signals[ACTIVATE_SOURCE] = g_signal_new ("activate-source", + MESSAGING_MENU_TYPE_APP, + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); +} + +static void +created_messages_service (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + MessagingMenuApp *app = user_data; + GError *error = NULL; + + app->messages_service = indicator_messages_service_proxy_new_finish (result, &error); + if (!app->messages_service) + { + g_warning ("unable to connect to the mesaging menu service: %s", error->message); + g_error_free (error); + return; + } + + /* sync current status */ + if (app->registered == TRUE) + messaging_menu_app_register (app); + else if (app->registered == FALSE) + messaging_menu_app_unregister (app); +} + +static void +got_session_bus (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + MessagingMenuApp *app = user_data; + GDBusConnection *bus; + GError *error = NULL; + guint id; + + bus = g_bus_get_finish (res, &error); + if (bus == NULL) + { + g_warning ("unable to connect to session bus: %s", error->message); + g_error_free (error); + return; + } + + id = g_dbus_connection_export_action_group (bus, + "/com/canonical/indicator/messages", + G_ACTION_GROUP (app->source_actions), + &error); + if (!id) + { + g_warning ("unable to export action group: %s", error->message); + g_error_free (error); + } + + id = g_dbus_connection_export_menu_model (bus, + "/com/canonical/indicator/messages", + G_MENU_MODEL (app->menu), + &error); + if (!id) + { + g_warning ("unable to export menu: %s", error->message); + g_error_free (error); + } + + indicator_messages_service_proxy_new (bus, + G_DBUS_PROXY_FLAGS_NONE, + "com.canonical.indicator.messages", + "/com/canonical/indicator/messages/service", + app->cancellable, + created_messages_service, + app); + + g_object_unref (bus); +} + +static void +messaging_menu_app_init (MessagingMenuApp *app) +{ + app->registered = -1; + app->status = MESSAGING_MENU_STATUS_OFFLINE; + + app->cancellable = g_cancellable_new (); + + app->source_actions = g_simple_action_group_new (); + app->menu = g_menu_new (); + + app->cancellable = g_cancellable_new (); + + + g_bus_get (G_BUS_TYPE_SESSION, + app->cancellable, + got_session_bus, + app); +} + +/** + * messaging_menu_new: + * @desktop_id: a desktop file id. See g_desktop_app_info_new() + * + * Creates a new #MessagingMenuApp for the application associated with + * @desktop_id. + * + * If the application is already registered with the messaging menu, it will be + * marked as "running". Otherwise, call messaging_menu_app_register(). + * + * The messaging menu will return to marking the application as not running as + * soon as the returned #MessagingMenuApp is destroyed. + * + * Returns: (transfer-full): a new #MessagingMenuApp + */ +MessagingMenuApp * +messaging_menu_app_new (const gchar *desktop_id) +{ + return g_object_new (MESSAGING_MENU_TYPE_APP, + "desktop-id", desktop_id, + NULL); +} + +/** + * messaging_menu_app_register: + * @app: a #MessagingMenuApp + * + * Registers @app with the messaging menu. + * + * The messaging menu will add a section with an app launcher and the shortcuts + * defined in its desktop file. + * + * The application will be marked as "running" as long as @app is alive or + * messaging_menu_app_unregister() is called. + */ +void +messaging_menu_app_register (MessagingMenuApp *app) +{ + app->registered = TRUE; + + /* state will be synced right after connecting to the service */ + if (!app->messages_service) + return; + + indicator_messages_service_call_register_application (app->messages_service, + g_app_info_get_id (G_APP_INFO (app->appinfo)), + "/com/canonical/indicator/messages", + app->cancellable, + NULL, NULL); +} + +/** + * messaging_menu_app_unregister: + * @app: a #MessagingMenuApp + * + * Completely removes the application associated with @desktop_id from the + * messaging menu. + * + * Note: @app will remain valid and usable after this call. + */ +void +messaging_menu_app_unregister (MessagingMenuApp *app) +{ + app->registered = FALSE; + + /* state will be synced right after connecting to the service */ + if (!app->messages_service) + return; + + indicator_messages_service_call_unregister_application (app->messages_service, + g_app_info_get_id (G_APP_INFO (app->appinfo)), + app->cancellable, + NULL, NULL); +} + +/** + * messaging_menu_app_set_status: + * @app: a #MessagingMenuApp + * @status: a #MessagingMenuStatus + */ +void +messaging_menu_app_set_status (MessagingMenuApp *app, + MessagingMenuStatus status) +{ + g_warning ("%s: not yet implemented", G_STRFUNC); +} + +static void +source_action_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + MessagingMenuApp *app = user_data; + + g_signal_emit (app, signals[ACTIVATE_SOURCE], 0, + g_action_get_name (G_ACTION (action))); +} + +static void +messaging_menu_app_insert_source_action (MessagingMenuApp *app, + gint position, + const gchar *id, + GIcon *icon, + const gchar *label, + GVariant *state) +{ + GSimpleAction *action; + GMenuItem *menuitem; + + 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); + if (icon) + { + gchar *icon_name = g_icon_to_string (icon); + g_menu_item_set_attribute (menuitem, "indicator-icon-name", icon_name); + g_free (icon_name); + } + g_menu_insert_item (app->menu, position, menuitem); + g_object_unref (menuitem); +} + +static void +messaging_menu_app_set_source_action (MessagingMenuApp *app, + const gchar *source_id, + GVariant *state) +{ + GAction *action; + + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + g_return_if_fail (source_id != 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); + return; + } + + g_simple_action_set_state (G_SIMPLE_ACTION (action), state); +} + +/** + * messaging_menu_app_insert_source: + * @app: a #MessagingMenuApp + * @position: the position at which to insert the source + * @id: a unique identifier for the source to be added + * @icon: the icon associated with the source + * @label: a user-visible string best describing the source + * + * Inserts a new message source into the section representing @app. Equivalent + * to calling messaging_menu_app_insert_source_with_time() with the current + * time. + * + * It is an error to insert a source with an @id which already exists. Use + * messaging_menu_app_has_source() to find out whether there is such a source. + */ +void +messaging_menu_app_insert_source (MessagingMenuApp *app, + gint position, + const gchar *id, + GIcon *icon, + const gchar *label) +{ + messaging_menu_app_insert_source_with_time (app, position, id, icon, label, + g_get_real_time ()); +} + +/** + * messaging_menu_app_append_source: + * @app: a #MessagingMenuApp + * @id: a unique identifier for the source to be added + * @icon: the icon associated with the source + * @label: a user-visible string best describing the source + * + * Appends a new message source to the end of the section representing @app. + * Equivalent to calling messaging_menu_app_append_source_with_time() with the + * current time. + * + * It is an error to add a source with an @id which already exists. Use + * messaging_menu_app_has_source() to find out whether there is such a source. + */ +void +messaging_menu_app_append_source (MessagingMenuApp *app, + const gchar *id, + GIcon *icon, + const gchar *label) +{ + messaging_menu_app_insert_source (app, -1, id, icon, label); +} + +/** + * messaging_menu_app_insert_source_with_count: + * @app: a #MessagingMenuApp + * @position: the position at which to insert the source + * @id: a unique identifier for the source to be added + * @icon: the icon associated with the source + * @label: a user-visible string best describing the source + * @count: the count for the source + * + * Inserts a new message source into the section representing @app and + * initializes it with @count. + * + * To update the count, use messaging_menu_app_set_source_count(). + * + * It is an error to insert a source with an @id which already exists. Use + * messaging_menu_app_has_source() to find out whether there is such a source. + */ +void +messaging_menu_app_insert_source_with_count (MessagingMenuApp *app, + gint position, + const gchar *id, + GIcon *icon, + const gchar *label, + guint count) +{ + messaging_menu_app_insert_source_action (app, position, id, icon, label, + g_variant_new ("(uxs)", count, 0, "")); +} + +/** + * messaging_menu_app_append_source_with_count: + * @app: a #MessagingMenuApp + * @id: a unique identifier for the source to be added + * @icon: the icon associated with the source + * @label: a user-visible string best describing the source + * @count: the count for the source + * + * Appends a new message source to the end of the section representing @app and + * initializes it with @count. + * + * To update the count, use messaging_menu_app_set_source_count(). + * + * It is an error to add a source with an @id which already exists. Use + * messaging_menu_app_has_source() to find out whether there is such a source. + */ +void messaging_menu_app_append_source_with_count (MessagingMenuApp *app, + const gchar *id, + GIcon *icon, + const gchar *label, + guint count) +{ + messaging_menu_app_insert_source_with_count (app, -1, id, icon, label, count); +} + +/** + * messaging_menu_app_insert_source_with_time: + * @app: a #MessagingMenuApp + * @position: the position at which to insert the source + * @id: a unique identifier for the source to be added + * @icon: the icon associated with the source + * @label: a user-visible string best describing the source + * @time: the time when the source was created + * + * Inserts a new message source into the section representing @app and + * initializes it with @time. + * + * To change the time, use messaging_menu_app_set_source_time(). + * + * It is an error to insert a source with an @id which already exists. Use + * messaging_menu_app_has_source() to find out whether there is such a source. + */ +void +messaging_menu_app_insert_source_with_time (MessagingMenuApp *app, + gint position, + const gchar *id, + GIcon *icon, + const gchar *label, + gint64 time) +{ + messaging_menu_app_insert_source_action (app, position, id, icon, label, + g_variant_new ("(uxs)", 0, time, "")); +} + +/** + * messaging_menu_app_append_source_with_time: + * @app: a #MessagingMenuApp + * @position: the position at which to insert the source + * @id: a unique identifier for the source to be added + * @icon: the icon associated with the source + * @label: a user-visible string best describing the source + * @time: the time when the source was created + * + * Appends a new message source to the end of the section representing @app and + * initializes it with @time. + * + * To change the time, use messaging_menu_app_set_source_time(). + * + * It is an error to insert a source with an @id which already exists. Use + * messaging_menu_app_has_source() to find out whether there is such a source. + */ +void +messaging_menu_app_append_source_with_time (MessagingMenuApp *app, + const gchar *id, + GIcon *icon, + const gchar *label, + gint64 time) +{ + messaging_menu_app_insert_source_with_time (app, -1, id, icon, label, time); +} + +/** + * messaging_menu_app_insert_source_with_string: + * @app: a #MessagingMenuApp + * @position: the position at which to insert the source + * @id: a unique identifier for the source to be added + * @icon: the icon associated with the source + * @label: a user-visible string best describing the source + * @str: a string associated with the source + * + * Inserts a new message source into the section representing @app and + * initializes it with @str. + * + * To update the string, use messaging_menu_app_set_source_string(). + * + * It is an error to insert a source with an @id which already exists. Use + * messaging_menu_app_has_source() to find out whether there is such a source. + */ +void +messaging_menu_app_insert_source_with_string (MessagingMenuApp *app, + gint position, + const gchar *id, + GIcon *icon, + const gchar *label, + const gchar *str) +{ + messaging_menu_app_insert_source_action (app, position, id, icon, label, + g_variant_new ("(uxs)", 0, 0, str)); +} + +/** + * messaging_menu_app_append_source_with_string: + * @app: a #MessagingMenuApp + * @position: the position at which to insert the source + * @id: a unique identifier for the source to be added + * @icon: the icon associated with the source + * @label: a user-visible string best describing the source + * @str: a string associated with the source + * + * Appends a new message source to the end of the section representing @app and + * initializes it with @str. + * + * To update the string, use messaging_menu_app_set_source_string(). + * + * It is an error to insert a source with an @id which already exists. Use + * messaging_menu_app_has_source() to find out whether there is such a source. + */ +void +messaging_menu_app_append_source_with_string (MessagingMenuApp *app, + const gchar *id, + GIcon *icon, + const gchar *label, + const gchar *str) +{ + messaging_menu_app_insert_source_with_string (app, -1, id, icon, label, str); +} + +/** + * messaging_menu_app_remove_source: + * @app: a #MessagingMenuApp + * @source_id: the id of the source to remove + * + * Removes the source corresponding to @source_id from the menu. + */ +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) + { + g_warning ("%s: a source with id '%s' doesn't exist", G_STRFUNC, source_id); + return; + } + + n_items = g_menu_model_get_n_items (G_MENU_MODEL (app->menu)); + for (i = 0; i < n_items; i++) + { + const gchar *action = NULL; + + 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_simple_action_group_remove (app->source_actions, source_id); +} + +/** + * messaging_menu_app_has_source: + * @app: a #MessagingMenuApp + * @source_id: a source id + * + * Returns: TRUE if there is a source associated with @source_id + */ +gboolean +messaging_menu_app_has_source (MessagingMenuApp *app, + const gchar *source_id) +{ + 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; +} + +/** + * messaging_menu_app_set_source_count: + * @app: a #MessagingMenuApp + * @source_id: a source id + * @count: the new count for the source + * + * Updates the count of @source_id to @count. + */ +void messaging_menu_app_set_source_count (MessagingMenuApp *app, + const gchar *source_id, + guint count) +{ + messaging_menu_app_set_source_action (app, source_id, + g_variant_new ("(uxs)", count, 0, "")); +} + +/** + * messaging_menu_app_set_source_time: + * @app: a #MessagingMenuApp + * @source_id: a source id + * @time: the new time for the source + * + * Updates the time of @source_id to @time. + * + * Note that the time is only displayed if the source does not also have a + * count associated with it. + */ +void +messaging_menu_app_set_source_time (MessagingMenuApp *app, + const gchar *source_id, + gint64 time) +{ + messaging_menu_app_set_source_action (app, source_id, + g_variant_new ("(uxs)", 0, time, "")); +} + +/** + * messaging_menu_app_set_source_string: + * @app: a #MessagingMenuApp + * @source_id: a source id + * @string: the new string for the source + * + * Updates the string displayed next to @source_id to @str. + * + * Note that the string is only displayed if the source does not also have a + * count or time associated with it. + */ +void +messaging_menu_app_set_source_string (MessagingMenuApp *app, + const gchar *source_id, + const gchar *str) +{ + messaging_menu_app_set_source_action (app, source_id, + g_variant_new ("(uxs)", 0, 0, str)); +} + +/** + * messaging_menu_app_draw_attention: + * @app: a #MessagingMenuApp + * @source_id: a source id + * + * Indicates that @source_id has important unread messages. Currently, this + * means that the messaging menu's envelope icon will turn blue. + * + * Use messaging_menu_app_remove_attention() to stop indicating that the source + * needs attention. + */ +void +messaging_menu_app_draw_attention (MessagingMenuApp *app, + const gchar *source_id) +{ + g_warning ("%s: not yet implemented", G_STRFUNC); +} + +/** + * messaging_menu_app_remove_attention: + * @app: a #MessagingMenuApp + * @source_id: a source id + * + * Stop indicating that @source_id needs attention. + * + * Use messaging_menu_app_draw_attention() to make @source_id draw attention + * again. + */ +void +messaging_menu_app_remove_attention (MessagingMenuApp *app, + const gchar *source_id) +{ + g_warning ("%s: not yet implemented", G_STRFUNC); +} -- cgit v1.2.3 From ac1a11bdf1df0efb426654cfe6b7ad4d5deb3815 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Fri, 15 Jun 2012 15:03:01 +0200 Subject: Check parameter types in public API --- libmessaging-menu/messaging-menu.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'libmessaging-menu/messaging-menu.c') diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c index 5b786af..6d2eab5 100644 --- a/libmessaging-menu/messaging-menu.c +++ b/libmessaging-menu/messaging-menu.c @@ -265,6 +265,8 @@ messaging_menu_app_new (const gchar *desktop_id) void messaging_menu_app_register (MessagingMenuApp *app) { + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + app->registered = TRUE; /* state will be synced right after connecting to the service */ @@ -290,6 +292,8 @@ messaging_menu_app_register (MessagingMenuApp *app) void messaging_menu_app_unregister (MessagingMenuApp *app) { + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + app->registered = FALSE; /* state will be synced right after connecting to the service */ -- cgit v1.2.3 From 40c73ac73cb2e45136c6820086f901ea1cf72874 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Fri, 15 Jun 2012 15:08:35 +0200 Subject: libmessaging-menu: add source name detail to "active-source" signal --- libmessaging-menu/messaging-menu.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'libmessaging-menu/messaging-menu.c') diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c index 6d2eab5..d848651 100644 --- a/libmessaging-menu/messaging-menu.c +++ b/libmessaging-menu/messaging-menu.c @@ -128,7 +128,8 @@ messaging_menu_app_class_init (MessagingMenuAppClass *class) signals[ACTIVATE_SOURCE] = g_signal_new ("activate-source", MESSAGING_MENU_TYPE_APP, - G_SIGNAL_RUN_FIRST, + G_SIGNAL_RUN_FIRST | + G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__STRING, @@ -324,9 +325,10 @@ source_action_activated (GSimpleAction *action, gpointer user_data) { MessagingMenuApp *app = user_data; + const gchar *name = g_action_get_name (G_ACTION (action)); + GQuark q = g_quark_from_string (name); - g_signal_emit (app, signals[ACTIVATE_SOURCE], 0, - g_action_get_name (G_ACTION (action))); + g_signal_emit (app, signals[ACTIVATE_SOURCE], q, name); } static void -- cgit v1.2.3 From 93db8c38f2252cb4d506d90721446c0ad524ca3b Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 27 Jun 2012 17:25:34 +0200 Subject: Add draws-attention flag to source actions AppSections watch those flags for associated sources and mux them into a draws-attention property for the whole section. --- libmessaging-menu/messaging-menu.c | 45 +++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 8 deletions(-) (limited to 'libmessaging-menu/messaging-menu.c') diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c index d848651..eff3b4f 100644 --- a/libmessaging-menu/messaging-menu.c +++ b/libmessaging-menu/messaging-menu.c @@ -463,7 +463,7 @@ messaging_menu_app_insert_source_with_count (MessagingMenuApp *app, guint count) { messaging_menu_app_insert_source_action (app, position, id, icon, label, - g_variant_new ("(uxs)", count, 0, "")); + g_variant_new ("(uxsb)", count, 0, "", FALSE)); } /** @@ -517,7 +517,7 @@ messaging_menu_app_insert_source_with_time (MessagingMenuApp *app, gint64 time) { messaging_menu_app_insert_source_action (app, position, id, icon, label, - g_variant_new ("(uxs)", 0, time, "")); + g_variant_new ("(uxsb)", 0, time, "", FALSE)); } /** @@ -573,7 +573,7 @@ messaging_menu_app_insert_source_with_string (MessagingMenuApp *app, const gchar *str) { messaging_menu_app_insert_source_action (app, position, id, icon, label, - g_variant_new ("(uxs)", 0, 0, str)); + g_variant_new ("(uxsb)", 0, 0, str, FALSE)); } /** @@ -673,7 +673,7 @@ void messaging_menu_app_set_source_count (MessagingMenuApp *app, guint count) { messaging_menu_app_set_source_action (app, source_id, - g_variant_new ("(uxs)", count, 0, "")); + g_variant_new ("(uxsb)", count, 0, "", FALSE)); } /** @@ -693,7 +693,7 @@ messaging_menu_app_set_source_time (MessagingMenuApp *app, gint64 time) { messaging_menu_app_set_source_action (app, source_id, - g_variant_new ("(uxs)", 0, time, "")); + g_variant_new ("(uxsb)", 0, time, "", FALSE)); } /** @@ -713,7 +713,36 @@ messaging_menu_app_set_source_string (MessagingMenuApp *app, const gchar *str) { messaging_menu_app_set_source_action (app, source_id, - g_variant_new ("(uxs)", 0, 0, str)); + g_variant_new ("(uxsb)", 0, 0, str, FALSE)); +} + +static void +messaging_menu_app_set_attention (MessagingMenuApp *app, + const gchar *source_id, + gboolean attention) +{ + GAction *action; + GVariant *state; + guint32 count; + gint64 time; + const gchar *str =""; + + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + g_return_if_fail (source_id != 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); + return; + } + + state = g_action_get_state (action); + g_variant_get (state, "(ux&sb)", &count, &time, &str, NULL); + g_variant_unref (state); + + g_simple_action_set_state (G_SIMPLE_ACTION (action), + g_variant_new ("(uxsb)", count, time, str, attention)); } /** @@ -731,7 +760,7 @@ void messaging_menu_app_draw_attention (MessagingMenuApp *app, const gchar *source_id) { - g_warning ("%s: not yet implemented", G_STRFUNC); + messaging_menu_app_set_attention (app, source_id, TRUE); } /** @@ -748,5 +777,5 @@ void messaging_menu_app_remove_attention (MessagingMenuApp *app, const gchar *source_id) { - g_warning ("%s: not yet implemented", G_STRFUNC); + messaging_menu_app_set_attention (app, source_id, FALSE); } -- cgit v1.2.3 From cc6cbf767baba6ab2b5f2ec32f0d9ff2a7f94cfa Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 27 Jun 2012 22:44:30 +0200 Subject: Sync chat status from and to clients --- libmessaging-menu/messaging-menu.c | 74 +++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) (limited to 'libmessaging-menu/messaging-menu.c') diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c index eff3b4f..7ee455a 100644 --- a/libmessaging-menu/messaging-menu.c +++ b/libmessaging-menu/messaging-menu.c @@ -52,12 +52,19 @@ enum { enum { ACTIVATE_SOURCE, + STATUS_CHANGED, N_SIGNALS }; static GParamSpec *properties[N_PROPERTIES]; static guint signals[N_SIGNALS]; +static const gchar *status_ids[] = { "available", "away", "busy", "invisible", "offline" }; + +static void global_status_changed (IndicatorMessagesService *service, + const gchar *status_str, + gpointer user_data); + static void messaging_menu_app_set_property (GObject *object, guint prop_id, @@ -100,6 +107,14 @@ messaging_menu_app_dispose (GObject *object) app->cancellable = NULL; } + if (app->messages_service) + { + g_signal_handlers_disconnect_by_func (app->messages_service, + global_status_changed, + app); + g_clear_object (&app->messages_service); + } + g_clear_object (&app->appinfo); g_clear_object (&app->source_actions); g_clear_object (&app->menu); @@ -134,6 +149,14 @@ messaging_menu_app_class_init (MessagingMenuAppClass *class) NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); + + signals[STATUS_CHANGED] = g_signal_new ("status-changed", + MESSAGING_MENU_TYPE_APP, + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, 1, G_TYPE_INT); } static void @@ -152,11 +175,15 @@ created_messages_service (GObject *source_object, return; } + g_signal_connect (app->messages_service, "status-changed", + G_CALLBACK (global_status_changed), app); + /* sync current status */ if (app->registered == TRUE) messaging_menu_app_register (app); else if (app->registered == FALSE) messaging_menu_app_unregister (app); + messaging_menu_app_set_status (app, app->status); } static void @@ -316,7 +343,52 @@ void messaging_menu_app_set_status (MessagingMenuApp *app, MessagingMenuStatus status) { - g_warning ("%s: not yet implemented", G_STRFUNC); + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + g_return_if_fail (status >= MESSAGING_MENU_STATUS_AVAILABLE && + status <= MESSAGING_MENU_STATUS_OFFLINE); + + app->status = status; + + /* state will be synced right after connecting to the service */ + if (!app->messages_service) + return; + + indicator_messages_service_call_set_status (app->messages_service, + status_ids [status], + app->cancellable, + NULL, NULL); +} + +static int +status_from_string (const gchar *s) +{ + int i; + + if (!s) + return -1; + + for (i = 0; i <= MESSAGING_MENU_STATUS_OFFLINE; i++) + { + if (g_str_equal (s, status_ids[i])) + return i; + } + + return -1; +} + +static void +global_status_changed (IndicatorMessagesService *service, + const gchar *status_str, + gpointer user_data) +{ + MessagingMenuApp *app = user_data; + int status; + + status = status_from_string (status_str); + g_return_if_fail (status >= 0); + + app->status = (MessagingMenuStatus)status; + g_signal_emit (app, signals[STATUS_CHANGED], 0, app->status); } static void -- cgit v1.2.3 From 8f2b343fc59a927961ee90f588f207d22c54924d Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 28 Jun 2012 16:07:17 +0200 Subject: libmessaging-menu: use the (newly added) GTupleAction instead of GSimpleAction GTupleAction is a bit simpler to handle when the action state contains a tuple of things that are independently modified most the time. It might be useful for other indicators as well. This implicitly also fixes the bug that libmessaging-menu did not preserve the other values in the action state when updating count, time, or string. --- libmessaging-menu/messaging-menu.c | 71 +++++++++++++++----------------------- 1 file changed, 27 insertions(+), 44 deletions(-) (limited to 'libmessaging-menu/messaging-menu.c') diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c index 7ee455a..22e4793 100644 --- a/libmessaging-menu/messaging-menu.c +++ b/libmessaging-menu/messaging-menu.c @@ -19,6 +19,7 @@ #include "messaging-menu.h" #include "indicator-messages-service.h" +#include "gtupleaction.h" #include @@ -44,6 +45,14 @@ struct _MessagingMenuApp G_DEFINE_TYPE (MessagingMenuApp, messaging_menu_app, G_TYPE_OBJECT); +enum +{ + INDEX_COUNT, + INDEX_TIME, + INDEX_STRING, + INDEX_DRAWS_ATTENTION +}; + enum { PROP_0, PROP_DESKTOP_ID, @@ -392,9 +401,9 @@ global_status_changed (IndicatorMessagesService *service, } static void -source_action_activated (GSimpleAction *action, - GVariant *parameter, - gpointer user_data) +source_action_activated (GTupleAction *action, + GVariant *parameter, + gpointer user_data) { MessagingMenuApp *app = user_data; const gchar *name = g_action_get_name (G_ACTION (action)); @@ -411,7 +420,7 @@ messaging_menu_app_insert_source_action (MessagingMenuApp *app, const gchar *label, GVariant *state) { - GSimpleAction *action; + GTupleAction *action; GMenuItem *menuitem; g_return_if_fail (MESSAGING_MENU_IS_APP (app)); @@ -423,7 +432,7 @@ messaging_menu_app_insert_source_action (MessagingMenuApp *app, return; } - action = g_simple_action_new_stateful (id, NULL, state); + action = g_tuple_action_new (id, state); g_signal_connect (action, "activate", G_CALLBACK (source_action_activated), app); g_simple_action_group_insert (app->source_actions, G_ACTION (action)); @@ -443,7 +452,8 @@ messaging_menu_app_insert_source_action (MessagingMenuApp *app, static void messaging_menu_app_set_source_action (MessagingMenuApp *app, const gchar *source_id, - GVariant *state) + gsize index, + GVariant *child) { GAction *action; @@ -457,7 +467,7 @@ messaging_menu_app_set_source_action (MessagingMenuApp *app, return; } - g_simple_action_set_state (G_SIMPLE_ACTION (action), state); + g_tuple_action_set_child (G_TUPLE_ACTION (action), index, child); } /** @@ -744,8 +754,8 @@ void messaging_menu_app_set_source_count (MessagingMenuApp *app, const gchar *source_id, guint count) { - messaging_menu_app_set_source_action (app, source_id, - g_variant_new ("(uxsb)", count, 0, "", FALSE)); + messaging_menu_app_set_source_action (app, source_id, INDEX_COUNT, + g_variant_new_uint32 (count)); } /** @@ -764,8 +774,8 @@ messaging_menu_app_set_source_time (MessagingMenuApp *app, const gchar *source_id, gint64 time) { - messaging_menu_app_set_source_action (app, source_id, - g_variant_new ("(uxsb)", 0, time, "", FALSE)); + messaging_menu_app_set_source_action (app, source_id, INDEX_TIME, + g_variant_new_int64 (time)); } /** @@ -784,37 +794,8 @@ messaging_menu_app_set_source_string (MessagingMenuApp *app, const gchar *source_id, const gchar *str) { - messaging_menu_app_set_source_action (app, source_id, - g_variant_new ("(uxsb)", 0, 0, str, FALSE)); -} - -static void -messaging_menu_app_set_attention (MessagingMenuApp *app, - const gchar *source_id, - gboolean attention) -{ - GAction *action; - GVariant *state; - guint32 count; - gint64 time; - const gchar *str =""; - - g_return_if_fail (MESSAGING_MENU_IS_APP (app)); - g_return_if_fail (source_id != 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); - return; - } - - state = g_action_get_state (action); - g_variant_get (state, "(ux&sb)", &count, &time, &str, NULL); - g_variant_unref (state); - - g_simple_action_set_state (G_SIMPLE_ACTION (action), - g_variant_new ("(uxsb)", count, time, str, attention)); + messaging_menu_app_set_source_action (app, source_id, INDEX_STRING, + g_variant_new_string (str)); } /** @@ -832,7 +813,8 @@ void messaging_menu_app_draw_attention (MessagingMenuApp *app, const gchar *source_id) { - messaging_menu_app_set_attention (app, source_id, TRUE); + messaging_menu_app_set_source_action (app, source_id, INDEX_DRAWS_ATTENTION, + g_variant_new_boolean (TRUE)); } /** @@ -849,5 +831,6 @@ void messaging_menu_app_remove_attention (MessagingMenuApp *app, const gchar *source_id) { - messaging_menu_app_set_attention (app, source_id, FALSE); + messaging_menu_app_set_source_action (app, source_id, INDEX_DRAWS_ATTENTION, + g_variant_new_boolean (FALSE)); } -- cgit v1.2.3 From 91eb0b1a17c5bc9d0087fe807f373d1b8a8eb87b Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 21 Aug 2012 01:41:26 +0200 Subject: Use a custom menu item for message source menu items --- libmessaging-menu/messaging-menu.c | 1 + 1 file changed, 1 insertion(+) (limited to 'libmessaging-menu/messaging-menu.c') diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c index 22e4793..ea33cd6 100644 --- a/libmessaging-menu/messaging-menu.c +++ b/libmessaging-menu/messaging-menu.c @@ -439,6 +439,7 @@ messaging_menu_app_insert_source_action (MessagingMenuApp *app, 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 *icon_name = g_icon_to_string (icon); -- cgit v1.2.3 From 7d036b65aac90b646eb7845cfc8e229464f372f0 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 21 Aug 2012 11:40:47 +0200 Subject: Show icons in application and source menu items Everthing goes through GIcon now, using g_icon_{to,new_for}_string to set a string attribute on the menu item. The attribute is prefixed x-canonical- for now. --- libmessaging-menu/messaging-menu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'libmessaging-menu/messaging-menu.c') diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c index ea33cd6..336e89c 100644 --- a/libmessaging-menu/messaging-menu.c +++ b/libmessaging-menu/messaging-menu.c @@ -442,9 +442,9 @@ messaging_menu_app_insert_source_action (MessagingMenuApp *app, g_menu_item_set_attribute (menuitem, "x-canonical-type", "s", "ImSourceMenuItem"); if (icon) { - gchar *icon_name = g_icon_to_string (icon); - g_menu_item_set_attribute (menuitem, "indicator-icon-name", icon_name); - g_free (icon_name); + 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); -- cgit v1.2.3