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/Makefile.am | 35 ++ libmessaging-menu/messaging-menu.c | 746 +++++++++++++++++++++++++++++++++ libmessaging-menu/messaging-menu.h | 130 ++++++ libmessaging-menu/messaging-menu.pc.in | 11 + 4 files changed, 922 insertions(+) create mode 100644 libmessaging-menu/Makefile.am create mode 100644 libmessaging-menu/messaging-menu.c create mode 100644 libmessaging-menu/messaging-menu.h create mode 100644 libmessaging-menu/messaging-menu.pc.in (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/Makefile.am b/libmessaging-menu/Makefile.am new file mode 100644 index 0000000..a0da51e --- /dev/null +++ b/libmessaging-menu/Makefile.am @@ -0,0 +1,35 @@ + +lib_LTLIBRARIES = libmessaging-menu.la + +libmessaging_menu_ladir = $(includedir)/messaging-menu + +libmessaging_menu_la_SOURCES = \ + messaging-menu.c \ + $(BUILT_SOURCES) + +libmessaging_menu_la_HEADERS = \ + messaging-menu.h + +libmessaging_menu_la_LIBADD = $(GIO_LIBS) + +libmessaging_menu_la_CFLAGS = \ + $(GIO_CFLAGS) \ + -Wall + +BUILT_SOURCES = \ + indicator-messages-service.c \ + indicator-messages-service.h + +CLEANFILES = $(BUILT_SOURCES) + +indicator-messages-service.c: $(top_srcdir)/src/messages-service.xml + $(AM_V_GEN) gdbus-codegen \ + --interface-prefix com.canonical.indicator.messages. \ + --generate-c-code indicator-messages-service \ + --c-namespace IndicatorMessages \ + $^ +indicator-messages-service.h: indicator-messages-service.c + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = messaging-menu.pc + 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); +} diff --git a/libmessaging-menu/messaging-menu.h b/libmessaging-menu/messaging-menu.h new file mode 100644 index 0000000..7ce2981 --- /dev/null +++ b/libmessaging-menu/messaging-menu.h @@ -0,0 +1,130 @@ +/* + * 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 + */ + +#ifndef __messaging_menu_h__ +#define __messaging_menu_h__ + +#include + +G_BEGIN_DECLS + +#define MESSAGING_MENU_TYPE_APP messaging_menu_app_get_type() +#define MESSAGING_MENU_APP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MESSAGING_MENU_TYPE_APP, MessagingMenuApp)) +#define MESSAGING_MENU_APP_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), MESSAGING_MENU_TYPE_APP, MessagingMenuAppClass)) +#define MESSAGING_MENU_IS_APP(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MESSAGING_MENU_TYPE_APP)) + +typedef enum { + MESSAGING_MENU_STATUS_AVAILABLE, + MESSAGING_MENU_STATUS_AWAY, + MESSAGING_MENU_STATUS_BUSY, + MESSAGING_MENU_STATUS_HIDDEN, + MESSAGING_MENU_STATUS_OFFLINE +} MessagingMenuStatus; + + +typedef GObjectClass MessagingMenuAppClass; +typedef struct _MessagingMenuApp MessagingMenuApp; + +GType messaging_menu_app_get_type (void) G_GNUC_CONST; + +MessagingMenuApp * messaging_menu_app_new (const gchar *desktop_id); + +void messaging_menu_app_register (MessagingMenuApp *app); +void messaging_menu_app_unregister (MessagingMenuApp *app); + +void messaging_menu_app_set_status (MessagingMenuApp *app, + MessagingMenuStatus status); + +void messaging_menu_app_insert_source (MessagingMenuApp *app, + gint position, + const gchar *id, + GIcon *icon, + const gchar *label); + +void messaging_menu_app_append_source (MessagingMenuApp *app, + const gchar *id, + GIcon *icon, + const gchar *label); + +void messaging_menu_app_insert_source_with_count (MessagingMenuApp *app, + gint position, + const gchar *id, + GIcon *icon, + const gchar *label, + guint count); + +void messaging_menu_app_append_source_with_count (MessagingMenuApp *app, + const gchar *id, + GIcon *icon, + const gchar *label, + guint count); + +void messaging_menu_app_insert_source_with_time (MessagingMenuApp *app, + gint position, + const gchar *id, + GIcon *icon, + const gchar *label, + gint64 time); + +void messaging_menu_app_append_source_with_time (MessagingMenuApp *app, + const gchar *id, + GIcon *icon, + const gchar *label, + gint64 time); + +void messaging_menu_app_append_source_with_string (MessagingMenuApp *app, + const gchar *id, + GIcon *icon, + const gchar *label, + const gchar *str); + +void messaging_menu_app_insert_source_with_string (MessagingMenuApp *app, + gint position, + const gchar *id, + GIcon *icon, + const gchar *label, + const gchar *str); + +void messaging_menu_app_remove_source (MessagingMenuApp *app, + const gchar *source_id); + +gboolean messaging_menu_app_has_source (MessagingMenuApp *app, + const gchar *source_id); + +void messaging_menu_app_set_source_count (MessagingMenuApp *app, + const gchar *source_id, + guint count); + +void messaging_menu_app_set_source_time (MessagingMenuApp *app, + const gchar *source_id, + gint64 time); + +void messaging_menu_app_set_source_string (MessagingMenuApp *app, + const gchar *source_id, + const gchar *str); + +void messaging_menu_app_draw_attention (MessagingMenuApp *app, + const gchar *source_id); + +void messaging_menu_app_remove_attention (MessagingMenuApp *app, + const gchar *source_id); + +G_END_DECLS + +#endif diff --git a/libmessaging-menu/messaging-menu.pc.in b/libmessaging-menu/messaging-menu.pc.in new file mode 100644 index 0000000..486300f --- /dev/null +++ b/libmessaging-menu/messaging-menu.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@/messaging-menu + +Name: Messaging Menu Library +Description: Messaging Menu client library +Version: @VERSION@ +Requires: gio-unix-2.0 +Libs: -L${libdir} -lmessaging-menu +Cflags: -I${includedir} -- 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') 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') 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 +++++++++-- src/app-section.c | 151 ++++++++++++++++++++++++++++++++++++- src/app-section.h | 1 + src/messages-service.c | 43 +++++++---- 4 files changed, 217 insertions(+), 23 deletions(-) (limited to 'libmessaging-menu') 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); } diff --git a/src/app-section.c b/src/app-section.c index 472fb6c..35a842f 100644 --- a/src/app-section.c +++ b/src/app-section.c @@ -45,6 +45,8 @@ struct _AppSectionPrivate GMenuModel *remote_menu; GActionGroup *actions; + gboolean draws_attention; + guint name_watch_id; }; @@ -52,6 +54,7 @@ enum { PROP_0, PROP_APPINFO, PROP_ACTIONS, + PROP_DRAWS_ATTENTION, NUM_PROPERTIES }; @@ -74,6 +77,18 @@ static void activate_cb (GSimpleAction *action, gpointer userdata); 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); /* GObject Boilerplate */ G_DEFINE_TYPE (AppSection, app_section, G_TYPE_OBJECT); @@ -101,6 +116,12 @@ app_section_class_init (AppSectionClass *klass) G_TYPE_ACTION_GROUP, G_PARAM_READABLE); + properties[PROP_DRAWS_ATTENTION] = g_param_spec_boolean ("draws-attention", + "Draws attention", + "Whether the section currently draws attention", + FALSE, + G_PARAM_READABLE); + g_object_class_install_properties (object_class, NUM_PROPERTIES, properties); } @@ -120,6 +141,8 @@ app_section_init (AppSection *self) priv->menu = g_menu_new (); priv->static_shortcuts = g_simple_action_group_new (); + priv->draws_attention = FALSE; + return; } @@ -137,6 +160,10 @@ app_section_get_property (GObject *object, g_value_set_object (value, app_section_get_app_info (self)); break; + case PROP_DRAWS_ATTENTION: + g_value_set_boolean (value, app_section_get_draws_attention (self)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -174,7 +201,15 @@ app_section_dispose (GObject *object) priv->name_watch_id = 0; } - g_clear_object (&priv->actions); + if (priv->actions) { + g_object_disconnect (priv->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->actions); + } + g_clear_object (&priv->remote_menu); if (priv->ids != NULL) { @@ -334,6 +369,13 @@ app_section_get_app_info (AppSection *self) return G_APP_INFO (priv->appinfo); } +gboolean +app_section_get_draws_attention (AppSection *self) +{ + AppSectionPrivate * priv = self->priv; + return priv->draws_attention; +} + static void application_vanished (GDBusConnection *bus, const gchar *name, @@ -367,6 +409,14 @@ app_section_set_object_path (AppSection *self, app_section_unset_object_path (self); priv->actions = G_ACTION_GROUP (g_dbus_action_group_get (bus, bus_name, object_path)); + + priv->draws_attention = any_action_draws_attention (priv->actions, NULL); + g_object_connect (priv->actions, + "signal::action-added", action_added, self, + "signal::action-state-changed", action_state_changed, self, + "signal::action-removed", action_removed, self, + NULL); + priv->remote_menu = G_MENU_MODEL (g_dbus_menu_model_get (bus, bus_name, object_path)); g_menu_append_section (priv->menu, NULL, priv->remote_menu); @@ -376,6 +426,7 @@ app_section_set_object_path (AppSection *self, self, NULL); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIONS]); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]); g_object_thaw_notify (G_OBJECT (self)); } @@ -396,7 +447,15 @@ app_section_unset_object_path (AppSection *self) g_bus_unwatch_name (priv->name_watch_id); priv->name_watch_id = 0; } - g_clear_object (&priv->actions); + + if (priv->actions) { + g_object_disconnect (priv->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->actions); + } if (priv->remote_menu) { /* the last menu item points is linked to the app's menumodel */ @@ -405,6 +464,94 @@ app_section_unset_object_path (AppSection *self) g_clear_object (&priv->remote_menu); } + priv->draws_attention = FALSE; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIONS]); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]); } +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; + GVariant *state; + + state = g_action_group_get_action_state (group, action_name); + if (!state) + return; + + self->priv->draws_attention = any_action_draws_attention (group, action_name); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]); + + g_variant_unref (state); +} diff --git a/src/app-section.h b/src/app-section.h index 36e091d..935f7a2 100644 --- a/src/app-section.h +++ b/src/app-section.h @@ -56,6 +56,7 @@ const gchar * app_section_get_desktop (AppSection * appitem); GActionGroup * app_section_get_actions (AppSection *self); GMenuModel * app_section_get_menu (AppSection *appitem); GAppInfo * app_section_get_app_info (AppSection *appitem); +gboolean app_section_get_draws_attention (AppSection *appitem); void app_section_set_object_path (AppSection *self, GDBusConnection *bus, const gchar *bus_name, diff --git a/src/messages-service.c b/src/messages-service.c index 8a08423..209abe5 100644 --- a/src/messages-service.c +++ b/src/messages-service.c @@ -73,6 +73,31 @@ actions_changed (GObject *object, g_free (id); } + +static gboolean +app_section_draws_attention (gpointer key, + gpointer value, + gpointer user_data) +{ + AppSection *section = value; + return app_section_get_draws_attention (section); +} + +static void +draws_attention_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + GSimpleAction *clear; + gboolean attention; + + clear = G_SIMPLE_ACTION (g_simple_action_group_lookup (actions, "clear")); + g_return_if_fail (clear != NULL); + + attention = g_hash_table_find (applications, app_section_draws_attention, NULL) != NULL; + g_simple_action_set_enabled (clear, attention); +} + static AppSection * add_application (const gchar *desktop_id) { @@ -98,6 +123,8 @@ add_application (const gchar *desktop_id) g_action_muxer_insert (action_muxer, id, app_section_get_actions (section)); g_signal_connect (section, "notify::actions", G_CALLBACK (actions_changed), NULL); + g_signal_connect (section, "notify::draws-attention", + G_CALLBACK (draws_attention_changed), NULL); /* TODO insert it at the right position (alphabetically by application name) */ menuitem = g_menu_item_new_section (NULL, app_section_get_menu (section)); @@ -132,6 +159,9 @@ remove_application (const char *desktop_id) if (pos >= 0) g_menu_remove (menu, pos); g_action_muxer_remove (action_muxer, id); + + g_signal_handlers_disconnect_by_func (section, actions_changed, NULL); + g_signal_handlers_disconnect_by_func (section, draws_attention_changed, NULL); } else { g_warning ("could not remove '%s', it's not registered", desktop_id); @@ -210,15 +240,6 @@ change_status (GSimpleAction *action, g_message ("changing status to %s", g_variant_get_string (value, NULL)); } -static void -clear_action_handler (MessageServiceDbus *msd, - gboolean attention, - gpointer user_data) -{ - GSimpleAction *action = user_data; - g_simple_action_set_enabled (action, attention); -} - static void register_application (MessageServiceDbus *msd, const gchar *sender, @@ -336,10 +357,6 @@ main (int argc, char ** argv) action_muxer = g_action_muxer_new (); g_action_muxer_insert (action_muxer, NULL, G_ACTION_GROUP (actions)); - g_signal_connect (dbus_interface, MESSAGE_SERVICE_DBUS_SIGNAL_ATTENTION_CHANGED, - G_CALLBACK(clear_action_handler), - g_action_map_lookup_action (G_ACTION_MAP (actions), "clear")); - g_signal_connect (dbus_interface, MESSAGE_SERVICE_DBUS_SIGNAL_REGISTER_APPLICATION, G_CALLBACK (register_application), NULL); g_signal_connect (dbus_interface, MESSAGE_SERVICE_DBUS_SIGNAL_UNREGISTER_APPLICATION, -- 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 +++++++++++++++++++++++++++++++++++++- libmessaging-menu/messaging-menu.h | 2 +- src/messages-service.c | 50 ++++++++++++++++++++++---- src/messages-service.xml | 9 +++++ 4 files changed, 126 insertions(+), 9 deletions(-) (limited to 'libmessaging-menu') 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 diff --git a/libmessaging-menu/messaging-menu.h b/libmessaging-menu/messaging-menu.h index 7ce2981..e767099 100644 --- a/libmessaging-menu/messaging-menu.h +++ b/libmessaging-menu/messaging-menu.h @@ -33,7 +33,7 @@ typedef enum { MESSAGING_MENU_STATUS_AVAILABLE, MESSAGING_MENU_STATUS_AWAY, MESSAGING_MENU_STATUS_BUSY, - MESSAGING_MENU_STATUS_HIDDEN, + MESSAGING_MENU_STATUS_INVISIBLE, MESSAGING_MENU_STATUS_OFFLINE } MessagingMenuStatus; diff --git a/src/messages-service.c b/src/messages-service.c index ec36335..a12847f 100644 --- a/src/messages-service.c +++ b/src/messages-service.c @@ -246,10 +246,26 @@ radio_item_activate (GSimpleAction *action, g_action_change_state (G_ACTION (action), parameter); } +static gboolean +g_action_state_equal (GAction *action, + GVariant *value) +{ + GVariant *state; + gboolean eq; + + state = g_action_get_state (action); + g_return_val_if_fail (state != NULL, FALSE); + + eq = g_variant_equal (state, value); + + g_variant_unref (state); + return eq; +} + static void -change_status (GSimpleAction *action, - GVariant *value, - gpointer user_data) +change_status_action (GSimpleAction *action, + GVariant *value, + gpointer user_data) { const gchar *status; @@ -261,9 +277,11 @@ change_status (GSimpleAction *action, g_str_equal (status, "invisible") || g_str_equal (status, "offline")); - g_simple_action_set_state (action, value); - - g_message ("changing status to %s", g_variant_get_string (value, NULL)); + if (!g_action_state_equal (G_ACTION (action), value)) { + g_message ("%s", status); + g_simple_action_set_state (action, value); + indicator_messages_service_emit_status_changed (messages_service, status); + } } static void @@ -302,6 +320,22 @@ unregister_application (IndicatorMessagesService *service, indicator_messages_service_complete_unregister_application (service, invocation); } +static void +set_status (IndicatorMessagesService *service, + GDBusMethodInvocation *invocation, + const gchar *status_str, + gpointer user_data) +{ + GAction *status; + + status = g_simple_action_group_lookup (actions, "status"); + g_return_if_fail (status != NULL); + + g_action_change_state (status, g_variant_new_string (status_str)); + + indicator_messages_service_complete_set_status (service, invocation); +} + GSimpleActionGroup * create_action_group () { @@ -319,7 +353,7 @@ create_action_group () status = g_simple_action_new_stateful ("status", G_VARIANT_TYPE ("s"), g_variant_new ("s", "offline")); g_signal_connect (status, "activate", G_CALLBACK (radio_item_activate), NULL); - g_signal_connect (status, "change-state", G_CALLBACK (change_status), NULL); + g_signal_connect (status, "change-state", G_CALLBACK (change_status_action), NULL); clear = g_simple_action_new ("clear", NULL); g_simple_action_set_enabled (clear, FALSE); @@ -427,6 +461,8 @@ main (int argc, char ** argv) G_CALLBACK (register_application), NULL); g_signal_connect (messages_service, "handle-unregister-application", G_CALLBACK (unregister_application), NULL); + g_signal_connect (messages_service, "handle-set-status", + G_CALLBACK (set_status), NULL); menu = g_menu_new (); status_items = create_status_section (); diff --git a/src/messages-service.xml b/src/messages-service.xml index 450d1fa..edd47c7 100644 --- a/src/messages-service.xml +++ b/src/messages-service.xml @@ -6,9 +6,18 @@ + + + + + + + + + -- 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/Makefile.am | 2 + libmessaging-menu/gtupleaction.c | 353 +++++++++++++++++++++++++++++++++++++ libmessaging-menu/gtupleaction.h | 40 +++++ libmessaging-menu/messaging-menu.c | 71 +++----- 4 files changed, 422 insertions(+), 44 deletions(-) create mode 100644 libmessaging-menu/gtupleaction.c create mode 100644 libmessaging-menu/gtupleaction.h (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/Makefile.am b/libmessaging-menu/Makefile.am index a0da51e..becaded 100644 --- a/libmessaging-menu/Makefile.am +++ b/libmessaging-menu/Makefile.am @@ -5,6 +5,8 @@ libmessaging_menu_ladir = $(includedir)/messaging-menu libmessaging_menu_la_SOURCES = \ messaging-menu.c \ + gtupleaction.c \ + gtupleaction.h \ $(BUILT_SOURCES) libmessaging_menu_la_HEADERS = \ diff --git a/libmessaging-menu/gtupleaction.c b/libmessaging-menu/gtupleaction.c new file mode 100644 index 0000000..f9e6fd7 --- /dev/null +++ b/libmessaging-menu/gtupleaction.c @@ -0,0 +1,353 @@ +/* + * 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 "gtupleaction.h" + +typedef GObjectClass GTupleActionClass; + +struct _GTupleAction +{ + GObject parent; + + gchar *name; + GVariantType *type; + gboolean enabled; + + gsize n_children; + GVariant **children; +}; + +static void action_interface_init (GActionInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (GTupleAction, g_tuple_action, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_ACTION, action_interface_init)); + +enum +{ + PROP_0, + PROP_NAME, + PROP_PARAMETER_TYPE, + PROP_ENABLED, + PROP_STATE_TYPE, + PROP_STATE, + N_PROPERTIES +}; + +enum +{ + SIGNAL_ACTIVATE, + N_SIGNALS +}; + +static GParamSpec *properties[N_PROPERTIES]; +static guint signal_ids[N_SIGNALS]; + +static const gchar * +g_tuple_action_get_name (GAction *action) +{ + GTupleAction *tuple = G_TUPLE_ACTION (action); + + return tuple->name; +} + +static const GVariantType * +g_tuple_action_get_parameter_type (GAction *action) +{ + return NULL; +} + +static const GVariantType * +g_tuple_action_get_state_type (GAction *action) +{ + GTupleAction *tuple = G_TUPLE_ACTION (action); + + return tuple->type; +} + +static GVariant * +g_tuple_action_get_state_hint (GAction *action) +{ + return NULL; +} + +static gboolean +g_tuple_action_get_enabled (GAction *action) +{ + GTupleAction *tuple = G_TUPLE_ACTION (action); + + return tuple->enabled; +} + +static GVariant * +g_tuple_action_get_state (GAction *action) +{ + GTupleAction *tuple = G_TUPLE_ACTION (action); + GVariant *result; + + result = g_variant_new_tuple (tuple->children, tuple->n_children); + return g_variant_ref_sink (result); +} + +static void +g_tuple_action_set_state (GTupleAction *tuple, + GVariant *state) +{ + int i; + + g_return_if_fail (g_variant_type_is_tuple (g_variant_get_type (state))); + + if (tuple->type == NULL) + { + tuple->type = g_variant_type_copy (g_variant_get_type (state)); + tuple->n_children = g_variant_n_children (state); + tuple->children = g_new0 (GVariant *, tuple->n_children); + } + + for (i = 0; i < tuple->n_children; i++) + { + if (tuple->children[i]) + g_variant_unref (tuple->children[i]); + tuple->children[i] = g_variant_get_child_value (state, i); + } + + g_object_notify_by_pspec (G_OBJECT (tuple), properties[PROP_STATE]); +} + +static void +g_tuple_action_change_state (GAction *action, + GVariant *value) +{ + GTupleAction *tuple = G_TUPLE_ACTION (action); + + g_return_if_fail (value != NULL); + g_return_if_fail (g_variant_is_of_type (value, tuple->type)); + + g_variant_ref_sink (value); + + /* TODO add a change-state signal similar to GSimpleAction */ + g_tuple_action_set_state (tuple, value); + + g_variant_unref (value); +} + +static void +g_tuple_action_activate (GAction *action, + GVariant *parameter) +{ + GTupleAction *tuple = G_TUPLE_ACTION (action); + + g_return_if_fail (parameter == NULL); + + if (tuple->enabled) + g_signal_emit (tuple, signal_ids[SIGNAL_ACTIVATE], 0, NULL); +} + +static void +g_tuple_action_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GAction *action = G_ACTION (object); + + switch (prop_id) + { + case PROP_NAME: + g_value_set_string (value, g_tuple_action_get_name (action)); + break; + + case PROP_PARAMETER_TYPE: + g_value_set_boxed (value, g_tuple_action_get_parameter_type (action)); + break; + + case PROP_ENABLED: + g_value_set_boolean (value, g_tuple_action_get_enabled (action)); + break; + + case PROP_STATE_TYPE: + g_value_set_boxed (value, g_tuple_action_get_state_type (action)); + break; + + case PROP_STATE: + g_value_take_variant (value, g_tuple_action_get_state (action)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +g_tuple_action_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GTupleAction *tuple = G_TUPLE_ACTION (object); + + switch (prop_id) + { + case PROP_NAME: + tuple->name = g_value_dup_string (value); + g_object_notify_by_pspec (object, properties[PROP_NAME]); + break; + + case PROP_ENABLED: + tuple->enabled = g_value_get_boolean (value); + g_object_notify_by_pspec (object, properties[PROP_ENABLED]); + break; + + case PROP_STATE: + g_tuple_action_set_state (tuple, g_value_get_variant (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +g_tuple_action_finalize (GObject *object) +{ + GTupleAction *tuple = G_TUPLE_ACTION (object); + int i; + + g_free (tuple->name); + g_variant_type_free (tuple->type); + + for (i = 0; i < tuple->n_children; i++) + g_variant_unref (tuple->children[i]); + + g_free (tuple->children); + + G_OBJECT_CLASS (g_tuple_action_parent_class)->finalize (object); +} + +static void +action_interface_init (GActionInterface *iface) +{ + iface->get_name = g_tuple_action_get_name; + iface->get_parameter_type = g_tuple_action_get_parameter_type; + iface->get_state_type = g_tuple_action_get_state_type; + iface->get_state_hint = g_tuple_action_get_state_hint; + iface->get_enabled = g_tuple_action_get_enabled; + iface->get_state = g_tuple_action_get_state; + iface->change_state = g_tuple_action_change_state; + iface->activate = g_tuple_action_activate; +} + +static void +g_tuple_action_class_init (GTupleActionClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->get_property = g_tuple_action_get_property; + object_class->set_property = g_tuple_action_set_property; + object_class->finalize = g_tuple_action_finalize; + + properties[PROP_NAME] = g_param_spec_string ("name", + "Name", + "The name of the action", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_PARAMETER_TYPE] = g_param_spec_boxed ("parameter-type", + "Parameter Type", + "The variant type passed to activate", + G_TYPE_VARIANT_TYPE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_ENABLED] = g_param_spec_boolean ("enabled", + "Enabled", + "Whether the action can be activated", + TRUE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_STATE_TYPE] = g_param_spec_boxed ("state-type", + "State Type", + "The variant type of the state, must be a tuple", + G_TYPE_VARIANT_TYPE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_STATE] = g_param_spec_variant ("state", + "State", + "The state of the action", + G_VARIANT_TYPE_TUPLE, + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + signal_ids[SIGNAL_ACTIVATE] = g_signal_new ("activate", + G_TYPE_TUPLE_ACTION, + G_SIGNAL_RUN_LAST | G_SIGNAL_MUST_COLLECT, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VARIANT, + G_TYPE_NONE, 1, + G_TYPE_VARIANT); +} + +static void +g_tuple_action_init (GTupleAction *action) +{ +} + +GTupleAction * +g_tuple_action_new (const gchar *name, + GVariant *initial_state) +{ + const GVariantType *type; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (initial_state != NULL, NULL); + + type = g_variant_get_type (initial_state); + g_return_val_if_fail (g_variant_type_is_tuple (type), NULL); + + return g_object_new (G_TYPE_TUPLE_ACTION, + "name", name, + "state", initial_state, + NULL); +} + +void +g_tuple_action_set_child (GTupleAction *action, + gsize index, + GVariant *value) +{ + const GVariantType *type; + + g_return_if_fail (G_IS_TUPLE_ACTION (action)); + g_return_if_fail (index < action->n_children); + g_return_if_fail (value != NULL); + + type = g_variant_get_type (value); + g_return_if_fail (g_variant_is_of_type (value, type)); + + g_variant_unref (action->children[index]); + action->children[index] = g_variant_ref_sink (value); + + g_object_notify_by_pspec (G_OBJECT (action), properties[PROP_STATE]); +} diff --git a/libmessaging-menu/gtupleaction.h b/libmessaging-menu/gtupleaction.h new file mode 100644 index 0000000..c447d71 --- /dev/null +++ b/libmessaging-menu/gtupleaction.h @@ -0,0 +1,40 @@ +/* + * 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 + */ + +#ifndef __g_tuple_action_h__ +#define __g_tuple_action_h__ + +#include + +#define G_TYPE_TUPLE_ACTION (g_tuple_action_get_type ()) +#define G_TUPLE_ACTION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_TUPLE_ACTION, GTupleAction)) +#define G_IS_TUPLE_ACTION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_TUPLE_ACTION)) + +typedef struct _GTupleAction GTupleAction; + +GType g_tuple_action_get_type (void) G_GNUC_CONST; + +GTupleAction * g_tuple_action_new (const gchar *name, + GVariant *initial_state); + +void g_tuple_action_set_child (GTupleAction *action, + gsize index, + GVariant *value); + +#endif 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 e5c3665070ddd5a968082a618123dcd0b70d1059 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Mon, 30 Jul 2012 16:38:12 +0200 Subject: Add introspection support --- Makefile.am | 2 +- configure.ac | 7 ++++++- libmessaging-menu/Makefile.am | 25 +++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) (limited to 'libmessaging-menu') diff --git a/Makefile.am b/Makefile.am index 9bd3903..ef08e41 100644 --- a/Makefile.am +++ b/Makefile.am @@ -17,7 +17,7 @@ endif -DISTCHECK_CONFIGURE_FLAGS = --enable-localinstall --enable-deprecations +DISTCHECK_CONFIGURE_FLAGS = --enable-localinstall --enable-deprecations --enable-introspection dist-hook: @if test -d "$(top_srcdir)/.bzr"; \ diff --git a/configure.ac b/configure.ac index d02dffa..dc56181 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ AC_INIT(indicator-messages, 0.6.0) -AC_PREREQ(2.53) +AC_PREREQ(2.62) AM_CONFIG_HEADER(config.h) AM_INIT_AUTOMAKE() @@ -43,6 +43,7 @@ GIO_UNIX_REQUIRED_VERSION=2.18 PANEL_REQUIRED_VERSION=2.0.0 INDICATOR_REQUIRED_VERSION=0.3.19 GLIB_REQUIRED_VERSION=2.31.20 +INTROSPECTION_REQUIRED_VERSION=1.32.0 PKG_CHECK_MODULES(APPLET, gtk+-3.0 >= $GTK_REQUIRED_VERSION gio-unix-2.0 >= $GIO_UNIX_REQUIRED_VERSION @@ -57,6 +58,10 @@ AC_SUBST(APPLET_LIBS) GLIB_GSETTINGS +GTK_DOC_CHECK([1.18], []) + +GOBJECT_INTROSPECTION_CHECK([$INTROSPECTION_REQUIRED_VERSION]) + ########################### # gcov coverage reporting ########################### diff --git a/libmessaging-menu/Makefile.am b/libmessaging-menu/Makefile.am index becaded..3799723 100644 --- a/libmessaging-menu/Makefile.am +++ b/libmessaging-menu/Makefile.am @@ -35,3 +35,28 @@ indicator-messages-service.h: indicator-messages-service.c pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = messaging-menu.pc + +-include $(INTROSPECTION_MAKEFILE) + +INTROSPECTION_GIRS = +INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir) +INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir) + +if HAVE_INTROSPECTION + +MessagingMenu-1.0.gir: libmessaging-menu.la +MessagingMenu_1_0_gir_NAMESPACE = MessagingMenu +MessagingMenu_1_0_gir_INCLUDES = GObject-2.0 Gio-2.0 +MessagingMenu_1_0_gir_CFLAGS = $(INCLUDES) $(GIO_CFLAGS) +MessagingMenu_1_0_gir_LIBS = libmessaging-menu.la +MessagingMenu_1_0_gir_FILES = $(libmessaging_menu_la_SOURCES) $(libmessaging_menu_la_HEADERS) +INTROSPECTION_GIRS += MessagingMenu-1.0.gir + +girdir = $(datadir)/gir-1.0 +gir_DATA = $(INTROSPECTION_GIRS) + +typelibdir = $(libdir)/girepository-1.0 +typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) + +CLEANFILES +=$(gir_DATA) $(typelib_DATA) +endif -- cgit v1.2.3 From b64ac4ede11db0741077b7a427b659a6a06f2c92 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Mon, 20 Aug 2012 23:09:54 +0200 Subject: gtupleaction: set enabled to TRUE by default --- libmessaging-menu/gtupleaction.c | 1 + 1 file changed, 1 insertion(+) (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/gtupleaction.c b/libmessaging-menu/gtupleaction.c index f9e6fd7..21bc003 100644 --- a/libmessaging-menu/gtupleaction.c +++ b/libmessaging-menu/gtupleaction.c @@ -312,6 +312,7 @@ g_tuple_action_class_init (GTupleActionClass *class) static void g_tuple_action_init (GTupleAction *action) { + action->enabled = TRUE; } GTupleAction * -- cgit v1.2.3 From 234c3cb933fcd422d9185b4bb01bed3223f0e791 Mon Sep 17 00:00:00 2001 From: Sebastien Bacher Date: Mon, 20 Aug 2012 23:16:04 +0200 Subject: libmessaging-menu: only export symbols tha t belong to the API --- libmessaging-menu/Makefile.am | 2 ++ 1 file changed, 2 insertions(+) (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/Makefile.am b/libmessaging-menu/Makefile.am index 3799723..187e6dc 100644 --- a/libmessaging-menu/Makefile.am +++ b/libmessaging-menu/Makefile.am @@ -18,6 +18,8 @@ libmessaging_menu_la_CFLAGS = \ $(GIO_CFLAGS) \ -Wall +libmessaging_menu_la_LDFLAGS = -export-symbols-regex="^messaging_menu_.*" + BUILT_SOURCES = \ indicator-messages-service.c \ indicator-messages-service.h -- 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 + src/Makefile.am | 2 + src/im-source-menu-item.c | 278 +++++++++++++++++++++++++++++++++++++ src/im-source-menu-item.h | 54 +++++++ src/indicator-messages.c | 2 + 5 files changed, 337 insertions(+) create mode 100644 src/im-source-menu-item.c create mode 100644 src/im-source-menu-item.h (limited to 'libmessaging-menu') 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); diff --git a/src/Makefile.am b/src/Makefile.am index 37009b7..403a289 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -17,6 +17,8 @@ libmessaging_la_SOURCES = \ indicator-messages.c \ im-app-menu-item.c \ im-app-menu-item.h \ + im-source-menu-item.c \ + im-source-menu-item.h \ indicator-messages-service.c \ indicator-messages-service.h dbus-data.h diff --git a/src/im-source-menu-item.c b/src/im-source-menu-item.c new file mode 100644 index 0000000..82d553f --- /dev/null +++ b/src/im-source-menu-item.c @@ -0,0 +1,278 @@ +/* + * 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 "im-source-menu-item.h" + +struct _ImSourceMenuItemPrivate +{ + GActionGroup *action_group; + gchar *action; + + GtkWidget *label; +}; + +enum +{ + PROP_0, + PROP_MENU_ITEM, + PROP_ACTION_GROUP, + NUM_PROPERTIES +}; + +static GParamSpec *properties[NUM_PROPERTIES]; + +G_DEFINE_TYPE (ImSourceMenuItem, im_source_menu_item, GTK_TYPE_MENU_ITEM); + +static void +im_source_menu_item_constructed (GObject *object) +{ + ImSourceMenuItemPrivate *priv = IM_SOURCE_MENU_ITEM (object)->priv; + GtkWidget *grid; + gint icon_width; + + gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &icon_width, NULL); + + priv->label = g_object_ref (gtk_label_new ("")); + gtk_widget_set_margin_left (priv->label, icon_width + 2); + + grid = gtk_grid_new (); + gtk_grid_attach (GTK_GRID (grid), priv->label, 0, 0, 1, 1); + + gtk_container_add (GTK_CONTAINER (object), grid); + gtk_widget_show_all (grid); + + G_OBJECT_CLASS (im_source_menu_item_parent_class)->constructed (object); +} + +static void +im_source_menu_item_set_action_name (ImSourceMenuItem *self, + const gchar *action_name) +{ + ImSourceMenuItemPrivate *priv = self->priv; + gboolean enabled = FALSE; + GVariant *state; + + if (priv->action != NULL) + g_free (priv->action); + + priv->action = g_strdup (action_name); + + if (priv->action_group != NULL && priv->action != NULL && + g_action_group_query_action (priv->action_group, priv->action, + &enabled, NULL, NULL, NULL, &state)) + { + if (!state || !g_variant_is_of_type (state, G_VARIANT_TYPE ("(uxsb)"))) + enabled = FALSE; + + if (state) + g_variant_unref (state); + } + + gtk_widget_set_sensitive (GTK_WIDGET (self), enabled); +} + +static void +im_source_menu_item_action_added (GActionGroup *action_group, + gchar *action_name, + gpointer user_data) +{ + ImSourceMenuItem *self = user_data; + + if (g_strcmp0 (self->priv->action, action_name) == 0) + im_source_menu_item_set_action_name (self, action_name); +} + +static void +im_source_menu_item_action_removed (GActionGroup *action_group, + gchar *action_name, + gpointer user_data) +{ + ImSourceMenuItem *self = user_data; + + if (g_strcmp0 (self->priv->action, action_name) == 0) + { + gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE); + } +} + +static void +im_source_menu_item_action_enabled_changed (GActionGroup *action_group, + gchar *action_name, + gboolean enabled, + gpointer user_data) +{ + ImSourceMenuItem *self = user_data; + + if (g_strcmp0 (self->priv->action, action_name) == 0) + gtk_widget_set_sensitive (GTK_WIDGET (self), enabled); +} + +static void +im_source_menu_item_action_state_changed (GActionGroup *action_group, + gchar *action_name, + GVariant *value, + gpointer user_data) +{ + ImSourceMenuItem *self = user_data; + + if (g_strcmp0 (self->priv->action, action_name) == 0) + g_return_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE ("(uxsb)"))); +} + +static void +im_source_menu_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + ImSourceMenuItem *self = IM_SOURCE_MENU_ITEM (object); + + switch (property_id) + { + case PROP_MENU_ITEM: + im_source_menu_item_set_menu_item (self, G_MENU_ITEM (g_value_get_object (value))); + break; + + case PROP_ACTION_GROUP: + im_source_menu_item_set_action_group (self, G_ACTION_GROUP (g_value_get_object (value))); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +im_source_menu_item_dispose (GObject *object) +{ + ImSourceMenuItem *self = IM_SOURCE_MENU_ITEM (object); + + if (self->priv->action_group) + im_source_menu_item_set_action_group (self, NULL); + + G_OBJECT_CLASS (im_source_menu_item_parent_class)->dispose (object); +} + +static void +im_source_menu_item_finalize (GObject *object) +{ + ImSourceMenuItemPrivate *priv = IM_SOURCE_MENU_ITEM (object)->priv; + + g_free (priv->action); + + G_OBJECT_CLASS (im_source_menu_item_parent_class)->finalize (object); +} + +static void +im_source_menu_item_activate (GtkMenuItem *item) +{ + ImSourceMenuItemPrivate *priv = IM_SOURCE_MENU_ITEM (item)->priv; + + if (priv->action && priv->action_group) + g_action_group_activate_action (priv->action_group, priv->action, NULL); +} + +static void +im_source_menu_item_class_init (ImSourceMenuItemClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkMenuItemClass *menu_item_class = GTK_MENU_ITEM_CLASS (klass); + + g_type_class_add_private (klass, sizeof (ImSourceMenuItemPrivate)); + + object_class->constructed = im_source_menu_item_constructed; + object_class->set_property = im_source_menu_set_property; + object_class->dispose = im_source_menu_item_dispose; + object_class->finalize = im_source_menu_item_finalize; + + menu_item_class->activate = im_source_menu_item_activate; + + properties[PROP_MENU_ITEM] = g_param_spec_object ("menu-item", + "Menu item", + "The model GMenuItem for this menu item", + G_TYPE_MENU_ITEM, + G_PARAM_WRITABLE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_ACTION_GROUP] = g_param_spec_object ("action-group", + "Action group", + "The action group associated with this menu item", + G_TYPE_ACTION_GROUP, + G_PARAM_WRITABLE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, NUM_PROPERTIES, properties); +} + +static void +im_source_menu_item_init (ImSourceMenuItem *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + IM_TYPE_SOURCE_MENU_ITEM, + ImSourceMenuItemPrivate); + g_message (G_STRFUNC); +} + +void +im_source_menu_item_set_menu_item (ImSourceMenuItem *self, + GMenuItem *menuitem) +{ + gchar *label; + gchar *action = NULL; + + g_menu_item_get_attribute (menuitem, "label", "s", &label); + gtk_label_set_label (GTK_LABEL (self->priv->label), label ? label : ""); + + g_menu_item_get_attribute (menuitem, "action", "s", &action); + im_source_menu_item_set_action_name (self, action); + + g_free (label); + g_free (action); +} + +void +im_source_menu_item_set_action_group (ImSourceMenuItem *self, + GActionGroup *action_group) +{ + ImSourceMenuItemPrivate *priv = self->priv; + + if (priv->action_group != NULL) + { + g_signal_handlers_disconnect_by_func (priv->action_group, im_source_menu_item_action_added, self); + g_signal_handlers_disconnect_by_func (priv->action_group, im_source_menu_item_action_removed, self); + g_signal_handlers_disconnect_by_func (priv->action_group, im_source_menu_item_action_enabled_changed, self); + g_signal_handlers_disconnect_by_func (priv->action_group, im_source_menu_item_action_state_changed, self); + + g_clear_object (&priv->action_group); + } + + if (action_group != NULL) + { + priv->action_group = g_object_ref (action_group); + + g_signal_connect (priv->action_group, "action-added", + G_CALLBACK (im_source_menu_item_action_added), self); + g_signal_connect (priv->action_group, "action-removed", + G_CALLBACK (im_source_menu_item_action_removed), self); + g_signal_connect (priv->action_group, "action-enabled-changed", + G_CALLBACK (im_source_menu_item_action_enabled_changed), self); + g_signal_connect (priv->action_group, "action-state-changed", + G_CALLBACK (im_source_menu_item_action_state_changed), self); + } +} diff --git a/src/im-source-menu-item.h b/src/im-source-menu-item.h new file mode 100644 index 0000000..c359b94 --- /dev/null +++ b/src/im-source-menu-item.h @@ -0,0 +1,54 @@ +/* + * 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 + */ + +#ifndef __IM_SOURCE_MENU_ITEM_H__ +#define __IM_SOURCE_MENU_ITEM_H__ + +#include + +#define IM_TYPE_SOURCE_MENU_ITEM (im_source_menu_item_get_type ()) +#define IM_SOURCE_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IM_TYPE_SOURCE_MENU_ITEM, ImSourceMenuItem)) +#define IM_SOURCE_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), IM_TYPE_SOURCE_MENU_ITEM, ImSourceMenuItemClass)) +#define IS_IM_SOURCE_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IM_TYPE_SOURCE_MENU_ITEM)) +#define IS_IM_SOURCE_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), IM_TYPE_SOURCE_MENU_ITEM)) +#define IM_SOURCE_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), IM_TYPE_SOURCE_MENU_ITEM, ImSourceMenuItemClass)) + +typedef struct _ImSourceMenuItem ImSourceMenuItem; +typedef struct _ImSourceMenuItemClass ImSourceMenuItemClass; +typedef struct _ImSourceMenuItemPrivate ImSourceMenuItemPrivate; + +struct _ImSourceMenuItemClass +{ + GtkMenuItemClass parent_class; +}; + +struct _ImSourceMenuItem +{ + GtkMenuItem parent; + ImSourceMenuItemPrivate *priv; +}; + +GType im_source_menu_item_get_type (void); + +void im_source_menu_item_set_menu_item (ImSourceMenuItem *item, + GMenuItem *menuitem); +void im_source_menu_item_set_action_group (ImSourceMenuItem *self, + GActionGroup *action_group); + +#endif diff --git a/src/indicator-messages.c b/src/indicator-messages.c index 4e8701a..d898ad6 100644 --- a/src/indicator-messages.c +++ b/src/indicator-messages.c @@ -37,6 +37,7 @@ with this program. If not, see . #include "dbus-data.h" #include "im-app-menu-item.h" +#include "im-source-menu-item.h" #define INDICATOR_MESSAGES_TYPE (indicator_messages_get_type ()) #define INDICATOR_MESSAGES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_MESSAGES_TYPE, IndicatorMessages)) @@ -146,6 +147,7 @@ indicator_messages_init (IndicatorMessages *self) /* make sure custom menu item types are registered (so that * gtk_model_new_from_menu can pick them up */ im_app_menu_item_get_type (); + im_source_menu_item_get_type (); } /* Unref stuff */ -- 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 ++--- src/app-section.c | 8 +++---- src/dbus-data.h | 3 --- src/gmenuutils.c | 2 +- src/im-app-menu-item.c | 47 +++++++++++++++++++++++++++++++++++++- src/im-source-menu-item.c | 28 ++++++++++++++++++++--- src/indicator-messages.c | 25 +++++++++++++++----- src/messages-service.c | 11 +++++++-- 8 files changed, 107 insertions(+), 23 deletions(-) (limited to 'libmessaging-menu') 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); diff --git a/src/app-section.c b/src/app-section.c index 1602ac6..70bf21e 100644 --- a/src/app-section.c +++ b/src/app-section.c @@ -312,7 +312,7 @@ app_section_set_app_info (AppSection *self, GSimpleAction *launch; GFile *keyfile; GMenuItem *item; - gchar *iconname; + gchar *iconstr; g_return_if_fail (priv->appinfo == NULL); @@ -330,9 +330,9 @@ app_section_set_app_info (AppSection *self, item = g_menu_item_new (g_app_info_get_name (G_APP_INFO (priv->appinfo)), "launch"); g_menu_item_set_attribute (item, "x-canonical-type", "s", "ImAppMenuItem"); - iconname = g_icon_to_string (g_app_info_get_icon (G_APP_INFO (priv->appinfo))); - g_menu_item_set_attribute (item, INDICATOR_MENU_ATTRIBUTE_ICON_NAME, "s", iconname); - g_free (iconname); + iconstr = g_icon_to_string (g_app_info_get_icon (G_APP_INFO (priv->appinfo))); + g_menu_item_set_attribute (item, "x-canonical-icon", "s", iconstr); + g_free (iconstr); g_menu_append_item (priv->menu, item); g_object_unref (item); diff --git a/src/dbus-data.h b/src/dbus-data.h index 3c72b81..64747a9 100644 --- a/src/dbus-data.h +++ b/src/dbus-data.h @@ -8,7 +8,4 @@ #define INDICATOR_MESSAGES_DBUS_SERVICE_OBJECT "/com/canonical/indicator/messages/service" #define INDICATOR_MESSAGES_DBUS_SERVICE_INTERFACE "com.canonical.indicator.messages.service" -#define INDICATOR_MENU_ATTRIBUTE_ICON_NAME "indicator-icon-name" -#define INDICATOR_MENU_ATTRIBUTE_ACCESSIBLE_DESCRIPTION "indicator-accessible-description" - #endif /* __DBUS_DATA_H__ */ diff --git a/src/gmenuutils.c b/src/gmenuutils.c index 056e75f..f3ceba7 100644 --- a/src/gmenuutils.c +++ b/src/gmenuutils.c @@ -71,7 +71,7 @@ g_menu_append_with_icon_name (GMenu *menu, GMenuItem *item; item = g_menu_item_new (label, detailed_action); - g_menu_item_set_attribute (item, INDICATOR_MENU_ATTRIBUTE_ICON_NAME, "s", icon_name); + g_menu_item_set_attribute (item, "x-canonical-icon", "s", icon_name); g_menu_append_item (menu, item); diff --git a/src/im-app-menu-item.c b/src/im-app-menu-item.c index f4430be..eddf562 100644 --- a/src/im-app-menu-item.c +++ b/src/im-app-menu-item.c @@ -24,6 +24,9 @@ struct _ImAppMenuItemPrivate GActionGroup *action_group; gchar *action; gboolean is_running; + + GtkWidget *icon; + GtkWidget *label; }; enum @@ -38,6 +41,26 @@ static GParamSpec *properties[NUM_PROPERTIES]; G_DEFINE_TYPE (ImAppMenuItem, im_app_menu_item, GTK_TYPE_MENU_ITEM); +static void +im_app_menu_item_constructed (GObject *object) +{ + ImAppMenuItemPrivate *priv = IM_APP_MENU_ITEM (object)->priv; + GtkWidget *grid; + + priv->icon = g_object_ref (gtk_image_new ()); + + priv->label = g_object_ref (gtk_label_new ("")); + + grid = gtk_grid_new (); + gtk_grid_attach (GTK_GRID (grid), priv->icon, 0, 0, 1, 1); + gtk_grid_attach (GTK_GRID (grid), priv->label, 1, 0, 1, 1); + + gtk_container_add (GTK_CONTAINER (object), grid); + gtk_widget_show_all (grid); + + G_OBJECT_CLASS (im_app_menu_item_parent_class)->constructed (object); +} + static void im_app_menu_item_set_action_name (ImAppMenuItem *self, const gchar *action_name) @@ -156,6 +179,9 @@ im_app_menu_item_dispose (GObject *object) if (self->priv->action_group) im_app_menu_item_set_action_group (self, NULL); + g_clear_object (&self->priv->icon); + g_clear_object (&self->priv->label); + G_OBJECT_CLASS (im_app_menu_item_parent_class)->dispose (object); } @@ -223,6 +249,7 @@ im_app_menu_item_class_init (ImAppMenuItemClass *klass) g_type_class_add_private (klass, sizeof (ImAppMenuItemPrivate)); + object_class->constructed = im_app_menu_item_constructed; object_class->set_property = im_app_menu_set_property; object_class->dispose = im_app_menu_item_dispose; object_class->finalize = im_app_menu_item_finalize; @@ -260,15 +287,33 @@ void im_app_menu_item_set_menu_item (ImAppMenuItem *self, GMenuItem *menuitem) { + gchar *iconstr = NULL; + GIcon *icon = NULL; gchar *label; gchar *action = NULL; + if (g_menu_item_get_attribute (menuitem, "x-canonical-icon", "s", &iconstr)) + { + GError *error; + + icon = g_icon_new_for_string (iconstr, &error); + if (icon == NULL) + { + g_warning ("unable to set icon: %s", error->message); + g_error_free (error); + } + g_free (iconstr); + } + gtk_image_set_from_gicon (GTK_IMAGE (self->priv->icon), icon, GTK_ICON_SIZE_MENU); + g_menu_item_get_attribute (menuitem, "label", "s", &label); - gtk_menu_item_set_label (GTK_MENU_ITEM (self), label ? label : ""); + gtk_label_set_label (GTK_LABEL (self->priv->label), label ? label : ""); g_menu_item_get_attribute (menuitem, "action", "s", &action); im_app_menu_item_set_action_name (self, action); + if (icon) + g_object_unref (icon); g_free (label); g_free (action); } diff --git a/src/im-source-menu-item.c b/src/im-source-menu-item.c index b0c55f4..269c75d 100644 --- a/src/im-source-menu-item.c +++ b/src/im-source-menu-item.c @@ -26,6 +26,7 @@ struct _ImSourceMenuItemPrivate GActionGroup *action_group; gchar *action; + GtkWidget *icon; GtkWidget *label; GtkWidget *detail; }; @@ -51,8 +52,10 @@ im_source_menu_item_constructed (GObject *object) gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &icon_width, NULL); + priv->icon = g_object_ref (gtk_image_new ()); + gtk_widget_set_margin_left (priv->icon, icon_width + 2); + priv->label = g_object_ref (gtk_label_new ("")); - gtk_widget_set_margin_left (priv->label, icon_width + 2); priv->detail = g_object_ref (gtk_label_new ("")); gtk_widget_set_halign (priv->detail, GTK_ALIGN_END); @@ -61,8 +64,9 @@ im_source_menu_item_constructed (GObject *object) gtk_style_context_add_class (gtk_widget_get_style_context (priv->detail), "accelerator"); grid = gtk_grid_new (); - gtk_grid_attach (GTK_GRID (grid), priv->label, 0, 0, 1, 1); - gtk_grid_attach (GTK_GRID (grid), priv->detail, 1, 0, 1, 1); + gtk_grid_attach (GTK_GRID (grid), priv->icon, 0, 0, 1, 1); + gtk_grid_attach (GTK_GRID (grid), priv->label, 1, 0, 1, 1); + gtk_grid_attach (GTK_GRID (grid), priv->detail, 2, 0, 1, 1); gtk_container_add (GTK_CONTAINER (object), grid); gtk_widget_show_all (grid); @@ -274,6 +278,7 @@ im_source_menu_item_dispose (GObject *object) if (self->priv->action_group) im_source_menu_item_set_action_group (self, NULL); + g_clear_object (&self->priv->icon); g_clear_object (&self->priv->label); g_clear_object (&self->priv->detail); @@ -343,15 +348,32 @@ void im_source_menu_item_set_menu_item (ImSourceMenuItem *self, GMenuItem *menuitem) { + gchar *iconstr = NULL; + GIcon *icon = NULL; gchar *label; gchar *action = NULL; + if (g_menu_item_get_attribute (menuitem, "x-canonical-icon", "s", &iconstr)) + { + GError *error; + icon = g_icon_new_for_string (iconstr, &error); + if (icon == NULL) + { + g_warning ("unable to set icon: %s", error->message); + g_error_free (error); + } + g_free (iconstr); + } + gtk_image_set_from_gicon (GTK_IMAGE (self->priv->icon), icon, GTK_ICON_SIZE_MENU); + g_menu_item_get_attribute (menuitem, "label", "s", &label); gtk_label_set_label (GTK_LABEL (self->priv->label), label ? label : ""); g_menu_item_get_attribute (menuitem, "action", "s", &action); im_source_menu_item_set_action_name (self, action); + if (icon) + g_object_unref (icon); g_free (label); g_free (action); } diff --git a/src/indicator-messages.c b/src/indicator-messages.c index d898ad6..2824b0c 100644 --- a/src/indicator-messages.c +++ b/src/indicator-messages.c @@ -232,21 +232,34 @@ indicator_messages_accessible_desc_updated (IndicatorMessages *self) static void update_root_item (IndicatorMessages * self) { - const gchar *icon_name; + gchar *iconstr; if (g_menu_model_get_n_items (self->menu) == 0) return; - g_menu_model_get_item_attribute (self->menu, 0, INDICATOR_MENU_ATTRIBUTE_ICON_NAME, - "&s", &icon_name); + if (g_menu_model_get_item_attribute (self->menu, 0, "x-canonical-icon", "s", &iconstr)) { + GIcon *icon; + GError *error; + + icon = g_icon_new_for_string (iconstr, &error); + if (icon) { + gtk_image_set_from_gicon (GTK_IMAGE (self->image), icon, GTK_ICON_SIZE_MENU); + g_object_unref (icon); + } + else { + g_warning ("unable to load icon: %s", error->message); + g_error_free (error); + } + + g_free (iconstr); + } g_free (self->accessible_desc); + self->accessible_desc = NULL; - g_menu_model_get_item_attribute (self->menu, 0, INDICATOR_MENU_ATTRIBUTE_ACCESSIBLE_DESCRIPTION, + g_menu_model_get_item_attribute (self->menu, 0, "x-canonical-accessible-description", "s", &self->accessible_desc); indicator_messages_accessible_desc_updated (self); - - gtk_image_set_from_icon_name (GTK_IMAGE (self->image), icon_name, GTK_ICON_SIZE_MENU); } static void diff --git a/src/messages-service.c b/src/messages-service.c index aade829..7654e1f 100644 --- a/src/messages-service.c +++ b/src/messages-service.c @@ -464,6 +464,8 @@ main (int argc, char ** argv) GMainLoop * mainloop = NULL; IndicatorService * service = NULL; GMenuItem *header; + GIcon *icon; + gchar *iconstr; /* Glib init */ g_type_init(); @@ -501,11 +503,14 @@ main (int argc, char ** argv) chat_section = create_status_section (); g_menu_append (menu, _("Clear"), "clear"); + icon = g_themed_icon_new ("indicator-messages"); + iconstr = g_icon_to_string (icon); + toplevel_menu = g_menu_new (); header = g_menu_item_new (NULL, "messages"); g_menu_item_set_submenu (header, G_MENU_MODEL (menu)); - g_menu_item_set_attribute (header, INDICATOR_MENU_ATTRIBUTE_ICON_NAME, "s", "indicator-messages"); - g_menu_item_set_attribute (header, INDICATOR_MENU_ATTRIBUTE_ACCESSIBLE_DESCRIPTION, "s", _("Messages")); + g_menu_item_set_attribute (header, "x-canonical-icon", "s", iconstr); + g_menu_item_set_attribute (header, "x-canonical-accessible-description", "s", _("Messages")); g_menu_append_item (toplevel_menu, header); g_object_unref (header); @@ -518,6 +523,8 @@ main (int argc, char ** argv) g_main_loop_run(mainloop); /* Clean up */ + g_free (iconstr); + g_object_unref (icon); g_object_unref (messages_service); g_object_unref (chat_section); g_object_unref (settings); -- cgit v1.2.3