From 97d0cafcae1f64107f786796ead442c0c5401f15 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 1 Nov 2012 17:53:29 +0100 Subject: libmessaging-menu: messaging-menu.[ch] -> messaing-menu-app.[ch] --- libmessaging-menu/Makefile.am | 5 +- libmessaging-menu/messaging-menu-app.c | 1141 ++++++++++++++++++++++++++++++++ libmessaging-menu/messaging-menu-app.h | 148 +++++ libmessaging-menu/messaging-menu.c | 1141 -------------------------------- libmessaging-menu/messaging-menu.h | 125 +--- 5 files changed, 1293 insertions(+), 1267 deletions(-) create mode 100644 libmessaging-menu/messaging-menu-app.c create mode 100644 libmessaging-menu/messaging-menu-app.h delete mode 100644 libmessaging-menu/messaging-menu.c (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/Makefile.am b/libmessaging-menu/Makefile.am index 7a6ee31..ed5655f 100644 --- a/libmessaging-menu/Makefile.am +++ b/libmessaging-menu/Makefile.am @@ -4,12 +4,13 @@ lib_LTLIBRARIES = libmessaging-menu.la libmessaging_menu_ladir = $(includedir)/messaging-menu libmessaging_menu_la_SOURCES = \ - messaging-menu.c \ + messaging-menu-app.c \ gtupleaction.c \ gtupleaction.h \ $(BUILT_SOURCES) libmessaging_menu_la_HEADERS = \ + messaging-menu-app.h \ messaging-menu.h libmessaging_menu_la_LIBADD = $(GIO_LIBS) @@ -52,7 +53,7 @@ MessagingMenu_1_0_gir_INCLUDES = GObject-2.0 Gio-2.0 MessagingMenu_1_0_gir_CFLAGS = $(INCLUDES) $(GIO_CFLAGS) MessagingMenu_1_0_gir_SCANNERFLAGS = --c-include="messaging-menu.h" MessagingMenu_1_0_gir_LIBS = libmessaging-menu.la -MessagingMenu_1_0_gir_FILES = messaging-menu.c messaging-menu.h +MessagingMenu_1_0_gir_FILES = messaging-menu-app.c messaging-menu-app.h MessagingMenu_1_0_gir_EXPORT_PACKAGES = messaging-menu INTROSPECTION_GIRS += MessagingMenu-1.0.gir diff --git a/libmessaging-menu/messaging-menu-app.c b/libmessaging-menu/messaging-menu-app.c new file mode 100644 index 0000000..c6a5561 --- /dev/null +++ b/libmessaging-menu/messaging-menu-app.c @@ -0,0 +1,1141 @@ +/* + * 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-app.h" +#include "indicator-messages-service.h" +#include "gtupleaction.h" + +#include + +/** + * SECTION:messaging-menu + * @title: MessagingMenuApp + * @short_description: An application section in the messaging menu + * @include: messaging-menu.h + * + * A #MessagingMenuApp represents an application section in the + * Messaging Menu. An application section is tied to an installed + * application through a desktop file id, which must be passed to + * messaging_menu_app_new(). + * + * To register the application with the Messaging Menu, call + * messaging_menu_app_register(). This signifies that the application + * should be present in the menu and be marked as "running". + * + * The first menu item in an application section represents the + * application itself, using the name and icon found in the associated + * desktop file. Activating this item starts the application. + * + * Following the application item, the Messaging Menu inserts all + * shortcut actions found in the desktop file. Actions whose + * NotShowIn keyword contains "Messaging Menu" or whose + * OnlyShowIn keyword does not contain "Messaging Menu" + * will not appear (the + * desktop file specification contains a detailed explanation of + * shortcut actions.) An application cannot add, remove, or change + * these shortcut items while it is running. + * + * Next, an application section contains menu items for message sources. + * What exactly constitutes a message source depends on the type of + * application: an email client's message sources are folders + * containing new messages, while those of a chat program are persons + * that have contacted the user. + * + * A message source is represented in the menu by a label and optionally + * also an icon. It can be associated with either a count, a time, or + * an arbitrary string, which will appear on the right side of the menu + * item. + * + * When the user activates a source, the source is immediately removed + * from the menu and the "activate-source" signal is emitted. + * + * Applications should always expose all the message sources available. + * However, the Messaging Menu might limit the amount of sources it + * displays to the user. + * + * The Messaging Menu offers users a way to set their chat status + * (available, away, busy, invisible, or offline) for multiple + * applications at once. Applications that appear in the Messaging Menu + * can integrate with this by setting the + * "X-MessagingMenu-UsesChatSection" key in their desktop file to True. + * Use messaging_menu_app_set_status() to signify that the application's + * chat status has changed. When the user changes status through the + * Messaging Menu, the ::status-changed signal will be emitted. + * + * If the application stops running without calling + * messaging_menu_app_unregister(), it will be marked as "not running". + * Its application and shortcut items stay in the menu, but all message + * sources are removed. If messaging_menu_app_unregister() is called, + * the application section is removed completely. + * + * More information about the design and recommended usage of the + * Messaging Menu is available at https://wiki.ubuntu.com/MessagingMenu. + */ + +/** + * MessagingMenuApp: + * + * #MessagingMenuApp is an opaque structure. + */ +struct _MessagingMenuApp +{ + GObject parent_instance; + + GDesktopAppInfo *appinfo; + int registered; /* -1 for unknown */ + MessagingMenuStatus status; + gboolean status_set; + GSimpleActionGroup *source_actions; + GMenu *menu; + + IndicatorMessagesService *messages_service; + guint watch_id; + + GCancellable *cancellable; +}; + +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, + N_PROPERTIES +}; + +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 gchar * +messaging_menu_app_get_dbus_object_path (MessagingMenuApp *app) +{ + gchar *path; + + if (!app->appinfo) + return NULL; + + path = g_strconcat ("/com/canonical/indicator/messages/", + g_app_info_get_id (G_APP_INFO (app->appinfo)), + NULL); + + g_strcanon (path, "/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", '_'); + + return path; +} + +static void +export_menus_and_actions (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + MessagingMenuApp *app = user_data; + GDBusConnection *bus; + GError *error = NULL; + guint id; + gchar *object_path; + + object_path = messaging_menu_app_get_dbus_object_path (app); + if (!object_path) + return; + + 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, + object_path, + 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, + object_path, + G_MENU_MODEL (app->menu), + &error); + if (!id) + { + g_warning ("unable to export menu: %s", error->message); + g_error_free (error); + } + + g_object_unref (bus); + g_free (object_path); +} + +static void +messaging_menu_app_set_desktop_id (MessagingMenuApp *app, + const gchar *desktop_id) +{ + g_return_if_fail (desktop_id != NULL); + + /* no need to clean up, it's construct only */ + app->appinfo = g_desktop_app_info_new (desktop_id); + if (app->appinfo == NULL) + { + g_warning ("could not find the desktop file for '%s'", + desktop_id); + } + + g_bus_get (G_BUS_TYPE_SESSION, + app->cancellable, + export_menus_and_actions, + app); +} + +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: + messaging_menu_app_set_desktop_id (app, 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->watch_id > 0) + { + g_bus_unwatch_name (app->watch_id); + app->watch_id = 0; + } + + if (app->cancellable) + { + g_cancellable_cancel (app->cancellable); + g_object_unref (app->cancellable); + 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); + + 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; + + /** + * MessagingMenuApp:desktop-id: + * + * The desktop id of the application associated with this application + * section. Must be given when the #MessagingMenuApp is created. + */ + 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); + + /** + * MessagingMenuApp::activate-source: + * @mmapp: the #MessagingMenuApp + * @source_id: the source id that was activated + * + * Emitted when the user has activated the message source with id + * @source_id. The source is immediately removed from the menu, + * handlers of this signal do not need to call + * messaging_menu_app_remove_source(). + */ + signals[ACTIVATE_SOURCE] = g_signal_new ("activate-source", + MESSAGING_MENU_TYPE_APP, + G_SIGNAL_RUN_FIRST | + G_SIGNAL_DETAILED, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); + + /** + * MessagingMenuApp::status-changed: + * @mmapp: the #MessagingMenuApp + * @status: a #MessagingMenuStatus + * + * Emitted when the chat status is changed through the messaging menu. + * + * Applications which are registered to use the chat status should + * change their status to @status upon receiving this signal. Call + * messaging_menu_app_set_status() to acknowledge that the application + * changed its status. + */ + 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 +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; + } + + 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); + if (app->status_set) + messaging_menu_app_set_status (app, app->status); +} + +static void +indicator_messages_appeared (GDBusConnection *bus, + const gchar *name, + const gchar *name_owner, + gpointer user_data) +{ + MessagingMenuApp *app = user_data; + + 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); +} + +static void +indicator_messages_vanished (GDBusConnection *bus, + const gchar *name, + gpointer user_data) +{ + MessagingMenuApp *app = user_data; + + if (app->messages_service) + { + g_signal_handlers_disconnect_by_func (app->messages_service, + global_status_changed, + app); + g_clear_object (&app->messages_service); + } +} + +static void +messaging_menu_app_init (MessagingMenuApp *app) +{ + app->registered = -1; + app->status_set = FALSE; + + app->cancellable = g_cancellable_new (); + + app->source_actions = g_simple_action_group_new (); + app->menu = g_menu_new (); + + app->cancellable = g_cancellable_new (); + + app->watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION, + "com.canonical.indicator.messages", + G_BUS_NAME_WATCHER_FLAGS_NONE, + indicator_messages_appeared, + indicator_messages_vanished, + app, + NULL); +} + +/** + * 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. + * + * The application will not show up (nor be marked as "running") in the + * Messaging Menu before messaging_menu_app_register() has been called. + * + * 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. + * + * If the application doesn't already have a section in the Messaging + * Menu, one will be created for it. The application will also be + * marked as "running". + * + * The application will be marked as "not running" as soon as @app is + * destroyed. The application launcher as well as shortcut actions will + * remain in the menu. To completely remove the application section + * from the Messaging Menu, call messaging_menu_app_unregister(). + */ +void +messaging_menu_app_register (MessagingMenuApp *app) +{ + gchar *object_path; + + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + + app->registered = TRUE; + + /* state will be synced right after connecting to the service */ + if (!app->messages_service) + return; + + object_path = messaging_menu_app_get_dbus_object_path (app); + if (!object_path) + return; + + indicator_messages_service_call_register_application (app->messages_service, + g_app_info_get_id (G_APP_INFO (app->appinfo)), + object_path, + app->cancellable, + NULL, NULL); + + g_free (object_path); +} + +/** + * messaging_menu_app_unregister: + * @app: a #MessagingMenuApp + * + * Completely removes the @app from the Messaging Menu. If the + * application's launcher and shortcut actions should remain in the + * menu, destroying @app with g_object_unref() suffices. + * + * Note: @app will remain valid and usable after this call. + */ +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 */ + if (!app->messages_service) + return; + + if (!app->appinfo) + 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 + * + * Notify the Messaging Menu that the chat status of @app has changed to + * @status. + * + * Connect to the ::status-changed signal to receive notification about + * the user changing their global chat status through the Messaging + * Menu. + * + * This function does nothing for applications whose desktop file does + * not include X-MessagingMenu-UsesChatSection. + */ +void +messaging_menu_app_set_status (MessagingMenuApp *app, + MessagingMenuStatus status) +{ + 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; + app->status_set = TRUE; + + /* state will be synced right after connecting to the service */ + if (!app->messages_service) + return; + + if (!app->appinfo) + return; + + indicator_messages_service_call_set_status (app->messages_service, + g_app_info_get_id (G_APP_INFO (app->appinfo)), + 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); + + g_signal_emit (app, signals[STATUS_CHANGED], 0, status); +} + +static void +source_action_activated (GTupleAction *action, + GVariant *parameter, + gpointer user_data) +{ + MessagingMenuApp *app = user_data; + const gchar *name = g_action_get_name (G_ACTION (action)); + GQuark q = g_quark_from_string (name); + + messaging_menu_app_remove_source (app, name); + + g_signal_emit (app, signals[ACTIVATE_SOURCE], q, name); +} + +static void +messaging_menu_app_insert_source_action (MessagingMenuApp *app, + gint position, + const gchar *id, + GIcon *icon, + const gchar *label, + GVariant *state) +{ + GTupleAction *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_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)); + g_object_unref (action); + + menuitem = g_menu_item_new (label, id); + g_menu_item_set_attribute (menuitem, "x-canonical-type", "s", "ImSourceMenuItem"); + if (icon) + { + gchar *iconstr = g_icon_to_string (icon); + g_menu_item_set_attribute (menuitem, "x-canonical-icon", "s", iconstr); + g_free (iconstr); + } + g_menu_insert_item (app->menu, position, menuitem); + g_object_unref (menuitem); +} + +static void +messaging_menu_app_set_source_action (MessagingMenuApp *app, + const gchar *source_id, + gsize index, + GVariant *child) +{ + 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_tuple_action_set_child (G_TUPLE_ACTION (action), index, child); +} + +/** + * 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: (allow-none): 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: (allow-none): 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 ("(uxsb)", count, 0, "", FALSE)); +} + +/** + * messaging_menu_app_append_source_with_count: + * @app: a #MessagingMenuApp + * @id: a unique identifier for the source to be added + * @icon: (allow-none): 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: (allow-none): the icon associated with the source + * @label: a user-visible string best describing the source + * @time: the time when the source was created, in microseconds + * + * Inserts a new message source into the section representing @app and + * initializes it with @time. Use messaging_menu_app_insert_source() to + * insert a source with the current 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 ("(uxsb)", 0, time, "", FALSE)); +} + +/** + * messaging_menu_app_append_source_with_time: + * @app: a #MessagingMenuApp + * @id: a unique identifier for the source to be added + * @icon: (allow-none): the icon associated with the source + * @label: a user-visible string best describing the source + * @time: the time when the source was created, in microseconds + * + * Appends a new message source to the end of the section representing + * @app and initializes it with @time. Use + * messaging_menu_app_append_source() to append a source with the + * current 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: (allow-none): 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 ("(uxsb)", 0, 0, str, FALSE)); +} + +/** + * messaging_menu_app_append_source_with_string: + * @app: a #MessagingMenuApp + * @id: a unique identifier for the source to be added + * @icon: (allow-none): 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) + return; + + n_items = g_menu_model_get_n_items (G_MENU_MODEL (app->menu)); + for (i = 0; i < n_items; i++) + { + gchar *action; + + if (g_menu_model_get_item_attribute (G_MENU_MODEL (app->menu), i, + "action", "s", &action)) + { + if (!g_strcmp0 (action, source_id)) + { + g_menu_remove (app->menu, i); + break; + } + + g_free (action); + } + } + + g_simple_action_group_remove (app->source_actions, source_id); +} + +/** + * 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; +} + +static GMenuItem * +g_menu_find_item_with_action (GMenu *menu, + const gchar *action, + gint *out_pos) +{ + gint i; + gint n_elements; + GMenuItem *item = NULL; + + n_elements = g_menu_model_get_n_items (G_MENU_MODEL (menu)); + + for (i = 0; i < n_elements && item == NULL; i++) + { + GVariant *attr; + + item = g_menu_item_new_from_model (G_MENU_MODEL (menu), i); + attr = g_menu_item_get_attribute_value (item, G_MENU_ATTRIBUTE_ACTION, G_VARIANT_TYPE_STRING); + + if (!g_str_equal (action, g_variant_get_string (attr, NULL))) + g_clear_object (&item); + + g_variant_unref (attr); + } + + if (item && out_pos) + *out_pos = i - 1; + + return item; +} + +static void +g_menu_replace_item (GMenu *menu, + gint pos, + GMenuItem *item) +{ + g_menu_remove (menu, pos); + g_menu_insert_item (menu, pos, item); +} + +/** + * messaging_menu_app_set_source_label: + * @app: a #MessagingMenuApp + * @source_id: a source id + * @label: the new label for the source + * + * Changes the label of @source_id to @label. + */ +void +messaging_menu_app_set_source_label (MessagingMenuApp *app, + const gchar *source_id, + const gchar *label) +{ + gint pos; + GMenuItem *item; + + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + g_return_if_fail (source_id != NULL); + g_return_if_fail (label != NULL); + + item = g_menu_find_item_with_action (app->menu, source_id, &pos); + if (item == NULL) + return; + + g_menu_item_set_attribute (item, G_MENU_ATTRIBUTE_LABEL, "s", label); + g_menu_replace_item (app->menu, pos, item); + + g_object_unref (item); +} + +/** + * messaging_menu_app_set_source_icon: + * @app: a #MessagingMenuApp + * @source_id: a source id + * @icon: the new icon for the source + * + * Changes the icon of @source_id to @icon. + */ +void +messaging_menu_app_set_source_icon (MessagingMenuApp *app, + const gchar *source_id, + GIcon *icon) +{ + gint pos; + GMenuItem *item; + gchar *iconstr; + + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + g_return_if_fail (source_id != NULL); + + item = g_menu_find_item_with_action (app->menu, source_id, &pos); + if (item == NULL) + return; + + iconstr = icon ? g_icon_to_string (icon) : NULL; + g_menu_item_set_attribute (item, "x-canonical-icon", "s", iconstr); + g_menu_replace_item (app->menu, pos, item); + + g_free (iconstr); + g_object_unref (item); +} + +/** + * 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, INDEX_COUNT, + g_variant_new_uint32 (count)); +} + +/** + * messaging_menu_app_set_source_time: + * @app: a #MessagingMenuApp + * @source_id: a source id + * @time: the new time for the source, in microseconds + * + * 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, INDEX_TIME, + g_variant_new_int64 (time)); +} + +/** + * messaging_menu_app_set_source_string: + * @app: a #MessagingMenuApp + * @source_id: a source id + * @str: 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, INDEX_STRING, + g_variant_new_string (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) +{ + messaging_menu_app_set_source_action (app, source_id, INDEX_DRAWS_ATTENTION, + g_variant_new_boolean (TRUE)); +} + +/** + * messaging_menu_app_remove_attention: + * @app: a #MessagingMenuApp + * @source_id: a source id + * + * Stop indicating that @source_id needs attention. + * + * This function does not need to be called when the source is removed + * with messaging_menu_app_remove_source() or the user has activated the + * source. + * + * 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) +{ + messaging_menu_app_set_source_action (app, source_id, INDEX_DRAWS_ATTENTION, + g_variant_new_boolean (FALSE)); +} diff --git a/libmessaging-menu/messaging-menu-app.h b/libmessaging-menu/messaging-menu-app.h new file mode 100644 index 0000000..b9415ff --- /dev/null +++ b/libmessaging-menu/messaging-menu-app.h @@ -0,0 +1,148 @@ +/* + * 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_app_h__ +#define __messaging_menu_app_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)) + +/** + * MessagingMenuStatus: + * @MESSAGING_MENU_STATUS_AVAILABLE: available + * @MESSAGING_MENU_STATUS_AWAY: away + * @MESSAGING_MENU_STATUS_BUSY: busy + * @MESSAGING_MENU_STATUS_INVISIBLE: invisible + * @MESSAGING_MENU_STATUS_OFFLINE: offline + * + * An enumeration for the possible chat statuses the messaging menu can be in. + */ +typedef enum { + MESSAGING_MENU_STATUS_AVAILABLE, + MESSAGING_MENU_STATUS_AWAY, + MESSAGING_MENU_STATUS_BUSY, + MESSAGING_MENU_STATUS_INVISIBLE, + 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_label (MessagingMenuApp *app, + const gchar *source_id, + const gchar *label); + +void messaging_menu_app_set_source_icon (MessagingMenuApp *app, + const gchar *source_id, + GIcon *icon); + +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.c b/libmessaging-menu/messaging-menu.c deleted file mode 100644 index 70861d3..0000000 --- a/libmessaging-menu/messaging-menu.c +++ /dev/null @@ -1,1141 +0,0 @@ -/* - * 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 "gtupleaction.h" - -#include - -/** - * SECTION:messaging-menu - * @title: MessagingMenuApp - * @short_description: An application section in the messaging menu - * @include: messaging-menu.h - * - * A #MessagingMenuApp represents an application section in the - * Messaging Menu. An application section is tied to an installed - * application through a desktop file id, which must be passed to - * messaging_menu_app_new(). - * - * To register the application with the Messaging Menu, call - * messaging_menu_app_register(). This signifies that the application - * should be present in the menu and be marked as "running". - * - * The first menu item in an application section represents the - * application itself, using the name and icon found in the associated - * desktop file. Activating this item starts the application. - * - * Following the application item, the Messaging Menu inserts all - * shortcut actions found in the desktop file. Actions whose - * NotShowIn keyword contains "Messaging Menu" or whose - * OnlyShowIn keyword does not contain "Messaging Menu" - * will not appear (the - * desktop file specification contains a detailed explanation of - * shortcut actions.) An application cannot add, remove, or change - * these shortcut items while it is running. - * - * Next, an application section contains menu items for message sources. - * What exactly constitutes a message source depends on the type of - * application: an email client's message sources are folders - * containing new messages, while those of a chat program are persons - * that have contacted the user. - * - * A message source is represented in the menu by a label and optionally - * also an icon. It can be associated with either a count, a time, or - * an arbitrary string, which will appear on the right side of the menu - * item. - * - * When the user activates a source, the source is immediately removed - * from the menu and the "activate-source" signal is emitted. - * - * Applications should always expose all the message sources available. - * However, the Messaging Menu might limit the amount of sources it - * displays to the user. - * - * The Messaging Menu offers users a way to set their chat status - * (available, away, busy, invisible, or offline) for multiple - * applications at once. Applications that appear in the Messaging Menu - * can integrate with this by setting the - * "X-MessagingMenu-UsesChatSection" key in their desktop file to True. - * Use messaging_menu_app_set_status() to signify that the application's - * chat status has changed. When the user changes status through the - * Messaging Menu, the ::status-changed signal will be emitted. - * - * If the application stops running without calling - * messaging_menu_app_unregister(), it will be marked as "not running". - * Its application and shortcut items stay in the menu, but all message - * sources are removed. If messaging_menu_app_unregister() is called, - * the application section is removed completely. - * - * More information about the design and recommended usage of the - * Messaging Menu is available at https://wiki.ubuntu.com/MessagingMenu. - */ - -/** - * MessagingMenuApp: - * - * #MessagingMenuApp is an opaque structure. - */ -struct _MessagingMenuApp -{ - GObject parent_instance; - - GDesktopAppInfo *appinfo; - int registered; /* -1 for unknown */ - MessagingMenuStatus status; - gboolean status_set; - GSimpleActionGroup *source_actions; - GMenu *menu; - - IndicatorMessagesService *messages_service; - guint watch_id; - - GCancellable *cancellable; -}; - -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, - N_PROPERTIES -}; - -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 gchar * -messaging_menu_app_get_dbus_object_path (MessagingMenuApp *app) -{ - gchar *path; - - if (!app->appinfo) - return NULL; - - path = g_strconcat ("/com/canonical/indicator/messages/", - g_app_info_get_id (G_APP_INFO (app->appinfo)), - NULL); - - g_strcanon (path, "/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", '_'); - - return path; -} - -static void -export_menus_and_actions (GObject *source, - GAsyncResult *res, - gpointer user_data) -{ - MessagingMenuApp *app = user_data; - GDBusConnection *bus; - GError *error = NULL; - guint id; - gchar *object_path; - - object_path = messaging_menu_app_get_dbus_object_path (app); - if (!object_path) - return; - - 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, - object_path, - 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, - object_path, - G_MENU_MODEL (app->menu), - &error); - if (!id) - { - g_warning ("unable to export menu: %s", error->message); - g_error_free (error); - } - - g_object_unref (bus); - g_free (object_path); -} - -static void -messaging_menu_app_set_desktop_id (MessagingMenuApp *app, - const gchar *desktop_id) -{ - g_return_if_fail (desktop_id != NULL); - - /* no need to clean up, it's construct only */ - app->appinfo = g_desktop_app_info_new (desktop_id); - if (app->appinfo == NULL) - { - g_warning ("could not find the desktop file for '%s'", - desktop_id); - } - - g_bus_get (G_BUS_TYPE_SESSION, - app->cancellable, - export_menus_and_actions, - app); -} - -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: - messaging_menu_app_set_desktop_id (app, 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->watch_id > 0) - { - g_bus_unwatch_name (app->watch_id); - app->watch_id = 0; - } - - if (app->cancellable) - { - g_cancellable_cancel (app->cancellable); - g_object_unref (app->cancellable); - 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); - - 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; - - /** - * MessagingMenuApp:desktop-id: - * - * The desktop id of the application associated with this application - * section. Must be given when the #MessagingMenuApp is created. - */ - 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); - - /** - * MessagingMenuApp::activate-source: - * @mmapp: the #MessagingMenuApp - * @source_id: the source id that was activated - * - * Emitted when the user has activated the message source with id - * @source_id. The source is immediately removed from the menu, - * handlers of this signal do not need to call - * messaging_menu_app_remove_source(). - */ - signals[ACTIVATE_SOURCE] = g_signal_new ("activate-source", - MESSAGING_MENU_TYPE_APP, - G_SIGNAL_RUN_FIRST | - G_SIGNAL_DETAILED, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, 1, G_TYPE_STRING); - - /** - * MessagingMenuApp::status-changed: - * @mmapp: the #MessagingMenuApp - * @status: a #MessagingMenuStatus - * - * Emitted when the chat status is changed through the messaging menu. - * - * Applications which are registered to use the chat status should - * change their status to @status upon receiving this signal. Call - * messaging_menu_app_set_status() to acknowledge that the application - * changed its status. - */ - 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 -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; - } - - 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); - if (app->status_set) - messaging_menu_app_set_status (app, app->status); -} - -static void -indicator_messages_appeared (GDBusConnection *bus, - const gchar *name, - const gchar *name_owner, - gpointer user_data) -{ - MessagingMenuApp *app = user_data; - - 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); -} - -static void -indicator_messages_vanished (GDBusConnection *bus, - const gchar *name, - gpointer user_data) -{ - MessagingMenuApp *app = user_data; - - if (app->messages_service) - { - g_signal_handlers_disconnect_by_func (app->messages_service, - global_status_changed, - app); - g_clear_object (&app->messages_service); - } -} - -static void -messaging_menu_app_init (MessagingMenuApp *app) -{ - app->registered = -1; - app->status_set = FALSE; - - app->cancellable = g_cancellable_new (); - - app->source_actions = g_simple_action_group_new (); - app->menu = g_menu_new (); - - app->cancellable = g_cancellable_new (); - - app->watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION, - "com.canonical.indicator.messages", - G_BUS_NAME_WATCHER_FLAGS_NONE, - indicator_messages_appeared, - indicator_messages_vanished, - app, - NULL); -} - -/** - * 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. - * - * The application will not show up (nor be marked as "running") in the - * Messaging Menu before messaging_menu_app_register() has been called. - * - * 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. - * - * If the application doesn't already have a section in the Messaging - * Menu, one will be created for it. The application will also be - * marked as "running". - * - * The application will be marked as "not running" as soon as @app is - * destroyed. The application launcher as well as shortcut actions will - * remain in the menu. To completely remove the application section - * from the Messaging Menu, call messaging_menu_app_unregister(). - */ -void -messaging_menu_app_register (MessagingMenuApp *app) -{ - gchar *object_path; - - g_return_if_fail (MESSAGING_MENU_IS_APP (app)); - - app->registered = TRUE; - - /* state will be synced right after connecting to the service */ - if (!app->messages_service) - return; - - object_path = messaging_menu_app_get_dbus_object_path (app); - if (!object_path) - return; - - indicator_messages_service_call_register_application (app->messages_service, - g_app_info_get_id (G_APP_INFO (app->appinfo)), - object_path, - app->cancellable, - NULL, NULL); - - g_free (object_path); -} - -/** - * messaging_menu_app_unregister: - * @app: a #MessagingMenuApp - * - * Completely removes the @app from the Messaging Menu. If the - * application's launcher and shortcut actions should remain in the - * menu, destroying @app with g_object_unref() suffices. - * - * Note: @app will remain valid and usable after this call. - */ -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 */ - if (!app->messages_service) - return; - - if (!app->appinfo) - 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 - * - * Notify the Messaging Menu that the chat status of @app has changed to - * @status. - * - * Connect to the ::status-changed signal to receive notification about - * the user changing their global chat status through the Messaging - * Menu. - * - * This function does nothing for applications whose desktop file does - * not include X-MessagingMenu-UsesChatSection. - */ -void -messaging_menu_app_set_status (MessagingMenuApp *app, - MessagingMenuStatus status) -{ - 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; - app->status_set = TRUE; - - /* state will be synced right after connecting to the service */ - if (!app->messages_service) - return; - - if (!app->appinfo) - return; - - indicator_messages_service_call_set_status (app->messages_service, - g_app_info_get_id (G_APP_INFO (app->appinfo)), - 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); - - g_signal_emit (app, signals[STATUS_CHANGED], 0, status); -} - -static void -source_action_activated (GTupleAction *action, - GVariant *parameter, - gpointer user_data) -{ - MessagingMenuApp *app = user_data; - const gchar *name = g_action_get_name (G_ACTION (action)); - GQuark q = g_quark_from_string (name); - - messaging_menu_app_remove_source (app, name); - - g_signal_emit (app, signals[ACTIVATE_SOURCE], q, name); -} - -static void -messaging_menu_app_insert_source_action (MessagingMenuApp *app, - gint position, - const gchar *id, - GIcon *icon, - const gchar *label, - GVariant *state) -{ - GTupleAction *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_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)); - g_object_unref (action); - - menuitem = g_menu_item_new (label, id); - g_menu_item_set_attribute (menuitem, "x-canonical-type", "s", "ImSourceMenuItem"); - if (icon) - { - gchar *iconstr = g_icon_to_string (icon); - g_menu_item_set_attribute (menuitem, "x-canonical-icon", "s", iconstr); - g_free (iconstr); - } - g_menu_insert_item (app->menu, position, menuitem); - g_object_unref (menuitem); -} - -static void -messaging_menu_app_set_source_action (MessagingMenuApp *app, - const gchar *source_id, - gsize index, - GVariant *child) -{ - 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_tuple_action_set_child (G_TUPLE_ACTION (action), index, child); -} - -/** - * 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: (allow-none): 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: (allow-none): 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 ("(uxsb)", count, 0, "", FALSE)); -} - -/** - * messaging_menu_app_append_source_with_count: - * @app: a #MessagingMenuApp - * @id: a unique identifier for the source to be added - * @icon: (allow-none): 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: (allow-none): the icon associated with the source - * @label: a user-visible string best describing the source - * @time: the time when the source was created, in microseconds - * - * Inserts a new message source into the section representing @app and - * initializes it with @time. Use messaging_menu_app_insert_source() to - * insert a source with the current 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 ("(uxsb)", 0, time, "", FALSE)); -} - -/** - * messaging_menu_app_append_source_with_time: - * @app: a #MessagingMenuApp - * @id: a unique identifier for the source to be added - * @icon: (allow-none): the icon associated with the source - * @label: a user-visible string best describing the source - * @time: the time when the source was created, in microseconds - * - * Appends a new message source to the end of the section representing - * @app and initializes it with @time. Use - * messaging_menu_app_append_source() to append a source with the - * current 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: (allow-none): 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 ("(uxsb)", 0, 0, str, FALSE)); -} - -/** - * messaging_menu_app_append_source_with_string: - * @app: a #MessagingMenuApp - * @id: a unique identifier for the source to be added - * @icon: (allow-none): 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) - return; - - n_items = g_menu_model_get_n_items (G_MENU_MODEL (app->menu)); - for (i = 0; i < n_items; i++) - { - gchar *action; - - if (g_menu_model_get_item_attribute (G_MENU_MODEL (app->menu), i, - "action", "s", &action)) - { - if (!g_strcmp0 (action, source_id)) - { - g_menu_remove (app->menu, i); - break; - } - - g_free (action); - } - } - - g_simple_action_group_remove (app->source_actions, source_id); -} - -/** - * 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; -} - -static GMenuItem * -g_menu_find_item_with_action (GMenu *menu, - const gchar *action, - gint *out_pos) -{ - gint i; - gint n_elements; - GMenuItem *item = NULL; - - n_elements = g_menu_model_get_n_items (G_MENU_MODEL (menu)); - - for (i = 0; i < n_elements && item == NULL; i++) - { - GVariant *attr; - - item = g_menu_item_new_from_model (G_MENU_MODEL (menu), i); - attr = g_menu_item_get_attribute_value (item, G_MENU_ATTRIBUTE_ACTION, G_VARIANT_TYPE_STRING); - - if (!g_str_equal (action, g_variant_get_string (attr, NULL))) - g_clear_object (&item); - - g_variant_unref (attr); - } - - if (item && out_pos) - *out_pos = i - 1; - - return item; -} - -static void -g_menu_replace_item (GMenu *menu, - gint pos, - GMenuItem *item) -{ - g_menu_remove (menu, pos); - g_menu_insert_item (menu, pos, item); -} - -/** - * messaging_menu_app_set_source_label: - * @app: a #MessagingMenuApp - * @source_id: a source id - * @label: the new label for the source - * - * Changes the label of @source_id to @label. - */ -void -messaging_menu_app_set_source_label (MessagingMenuApp *app, - const gchar *source_id, - const gchar *label) -{ - gint pos; - GMenuItem *item; - - g_return_if_fail (MESSAGING_MENU_IS_APP (app)); - g_return_if_fail (source_id != NULL); - g_return_if_fail (label != NULL); - - item = g_menu_find_item_with_action (app->menu, source_id, &pos); - if (item == NULL) - return; - - g_menu_item_set_attribute (item, G_MENU_ATTRIBUTE_LABEL, "s", label); - g_menu_replace_item (app->menu, pos, item); - - g_object_unref (item); -} - -/** - * messaging_menu_app_set_source_icon: - * @app: a #MessagingMenuApp - * @source_id: a source id - * @icon: the new icon for the source - * - * Changes the icon of @source_id to @icon. - */ -void -messaging_menu_app_set_source_icon (MessagingMenuApp *app, - const gchar *source_id, - GIcon *icon) -{ - gint pos; - GMenuItem *item; - gchar *iconstr; - - g_return_if_fail (MESSAGING_MENU_IS_APP (app)); - g_return_if_fail (source_id != NULL); - - item = g_menu_find_item_with_action (app->menu, source_id, &pos); - if (item == NULL) - return; - - iconstr = icon ? g_icon_to_string (icon) : NULL; - g_menu_item_set_attribute (item, "x-canonical-icon", "s", iconstr); - g_menu_replace_item (app->menu, pos, item); - - g_free (iconstr); - g_object_unref (item); -} - -/** - * 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, INDEX_COUNT, - g_variant_new_uint32 (count)); -} - -/** - * messaging_menu_app_set_source_time: - * @app: a #MessagingMenuApp - * @source_id: a source id - * @time: the new time for the source, in microseconds - * - * 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, INDEX_TIME, - g_variant_new_int64 (time)); -} - -/** - * messaging_menu_app_set_source_string: - * @app: a #MessagingMenuApp - * @source_id: a source id - * @str: 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, INDEX_STRING, - g_variant_new_string (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) -{ - messaging_menu_app_set_source_action (app, source_id, INDEX_DRAWS_ATTENTION, - g_variant_new_boolean (TRUE)); -} - -/** - * messaging_menu_app_remove_attention: - * @app: a #MessagingMenuApp - * @source_id: a source id - * - * Stop indicating that @source_id needs attention. - * - * This function does not need to be called when the source is removed - * with messaging_menu_app_remove_source() or the user has activated the - * source. - * - * 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) -{ - messaging_menu_app_set_source_action (app, source_id, INDEX_DRAWS_ATTENTION, - g_variant_new_boolean (FALSE)); -} diff --git a/libmessaging-menu/messaging-menu.h b/libmessaging-menu/messaging-menu.h index 6c405c7..929b229 100644 --- a/libmessaging-menu/messaging-menu.h +++ b/libmessaging-menu/messaging-menu.h @@ -20,129 +20,6 @@ #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)) - -/** - * MessagingMenuStatus: - * @MESSAGING_MENU_STATUS_AVAILABLE: available - * @MESSAGING_MENU_STATUS_AWAY: away - * @MESSAGING_MENU_STATUS_BUSY: busy - * @MESSAGING_MENU_STATUS_INVISIBLE: invisible - * @MESSAGING_MENU_STATUS_OFFLINE: offline - * - * An enumeration for the possible chat statuses the messaging menu can be in. - */ -typedef enum { - MESSAGING_MENU_STATUS_AVAILABLE, - MESSAGING_MENU_STATUS_AWAY, - MESSAGING_MENU_STATUS_BUSY, - MESSAGING_MENU_STATUS_INVISIBLE, - 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_label (MessagingMenuApp *app, - const gchar *source_id, - const gchar *label); - -void messaging_menu_app_set_source_icon (MessagingMenuApp *app, - const gchar *source_id, - GIcon *icon); - -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 +#include "messaging-menu-app.h" #endif -- cgit v1.2.3 From 50d8e7cc08546d778634e6be463a4851cfb8267b Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 1 Nov 2012 17:54:11 +0100 Subject: libmessaging-menu: add MessagingMenuMessage MessagingMenuMessage allows adding individual messages with titles and body previews to the messaging menu with messaging_menu_app_append_message. This only adds the new API, messages are not actually sent to the messaging menu yet. --- libmessaging-menu/Makefile.am | 10 +- libmessaging-menu/messaging-menu-app.c | 63 +++++ libmessaging-menu/messaging-menu-app.h | 11 + libmessaging-menu/messaging-menu-message.c | 372 +++++++++++++++++++++++++++++ libmessaging-menu/messaging-menu-message.h | 64 +++++ 5 files changed, 518 insertions(+), 2 deletions(-) create mode 100644 libmessaging-menu/messaging-menu-message.c create mode 100644 libmessaging-menu/messaging-menu-message.h (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/Makefile.am b/libmessaging-menu/Makefile.am index ed5655f..15d0c9b 100644 --- a/libmessaging-menu/Makefile.am +++ b/libmessaging-menu/Makefile.am @@ -5,13 +5,15 @@ libmessaging_menu_ladir = $(includedir)/messaging-menu libmessaging_menu_la_SOURCES = \ messaging-menu-app.c \ + messaging-menu-message.c \ gtupleaction.c \ gtupleaction.h \ $(BUILT_SOURCES) libmessaging_menu_la_HEADERS = \ messaging-menu-app.h \ - messaging-menu.h + messaging-menu.h \ + messaging-menu-message.h libmessaging_menu_la_LIBADD = $(GIO_LIBS) @@ -53,7 +55,11 @@ MessagingMenu_1_0_gir_INCLUDES = GObject-2.0 Gio-2.0 MessagingMenu_1_0_gir_CFLAGS = $(INCLUDES) $(GIO_CFLAGS) MessagingMenu_1_0_gir_SCANNERFLAGS = --c-include="messaging-menu.h" MessagingMenu_1_0_gir_LIBS = libmessaging-menu.la -MessagingMenu_1_0_gir_FILES = messaging-menu-app.c messaging-menu-app.h +MessagingMenu_1_0_gir_FILES = \ + messaging-menu-app.c \ + messaging-menu-app.h \ + messaging-menu-message.c \ + messaging-menu-message.h MessagingMenu_1_0_gir_EXPORT_PACKAGES = messaging-menu INTROSPECTION_GIRS += MessagingMenu-1.0.gir diff --git a/libmessaging-menu/messaging-menu-app.c b/libmessaging-menu/messaging-menu-app.c index c6a5561..5e14d65 100644 --- a/libmessaging-menu/messaging-menu-app.c +++ b/libmessaging-menu/messaging-menu-app.c @@ -1139,3 +1139,66 @@ messaging_menu_app_remove_attention (MessagingMenuApp *app, messaging_menu_app_set_source_action (app, source_id, INDEX_DRAWS_ATTENTION, g_variant_new_boolean (FALSE)); } + +/** + * messaging_menu_app_append_message: + * @app: a #MessagingMenuApp + * @msg: the #MessagingMenuMessage to append + * @source_id: (allow-none): the source id to which @msg is added, or NULL + * @notify: whether a notification bubble should be shown for this + * message + * + * Appends @msg to the source with id @source_id of @app. The messaging + * menu might not display this message immediately if other messages are + * queued before this one. + * + * If @source_id has a count associated with it, that count will be + * increased by one. + * + * If @source_id is %NULL, @msg won't be associated with a source. + */ +void +messaging_menu_app_append_message (MessagingMenuApp *app, + MessagingMenuMessage *msg, + const gchar *source_id, + gboolean notify) +{ + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + g_return_if_fail (MESSAGING_MENU_IS_MESSAGE (app)); +} + +/** + * messaging_menu_app_remove_message: + * @app: a #MessagingMenuApp + * @msg: the #MessagingMenuMessage to remove + * + * Removes @msg from @app. + * + * If @source_id has a count associated with it, that count will be + * decreased by one. + */ +void +messaging_menu_app_remove_message (MessagingMenuApp *app, + MessagingMenuMessage *msg) +{ + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + g_return_if_fail (MESSAGING_MENU_IS_MESSAGE (app)); +} + +/** + * messaging_menu_app_remove_message_by_id: + * @app: a #MessagingMenuApp + * @id: the unique id of @msg + * + * Removes the message with the id @id from @app. + * + * If @source_id has a count associated with it, that count will be + * decreased by one. + */ +void +messaging_menu_app_remove_message_by_id (MessagingMenuApp *app, + const gchar *id) +{ + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + g_return_if_fail (id != NULL); +} diff --git a/libmessaging-menu/messaging-menu-app.h b/libmessaging-menu/messaging-menu-app.h index b9415ff..aca46ec 100644 --- a/libmessaging-menu/messaging-menu-app.h +++ b/libmessaging-menu/messaging-menu-app.h @@ -21,6 +21,7 @@ #define __messaging_menu_app_h__ #include +#include "messaging-menu-message.h" G_BEGIN_DECLS @@ -143,6 +144,16 @@ void messaging_menu_app_draw_attention (MessagingMenuA void messaging_menu_app_remove_attention (MessagingMenuApp *app, const gchar *source_id); +void messaging_menu_app_append_message (MessagingMenuApp *app, + MessagingMenuMessage *msg, + const gchar *source_id); + +void messaging_menu_app_remove_message (MessagingMenuApp *app, + MessagingMenuMessage *msg); + +void messaging_menu_app_remove_message_by_id (MessagingMenuApp *app, + const gchar *id); + G_END_DECLS #endif diff --git a/libmessaging-menu/messaging-menu-message.c b/libmessaging-menu/messaging-menu-message.c new file mode 100644 index 0000000..631786a --- /dev/null +++ b/libmessaging-menu/messaging-menu-message.c @@ -0,0 +1,372 @@ +/* + * 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-message.h" + +typedef GObjectClass MessagingMenuMessageClass; + +struct _MessagingMenuMessage +{ + GObject parent; + + gchar *id; + GIcon *icon; + gchar *title; + gchar *subtitle; + gchar *body; + gint64 time; + gboolean draws_attention; +}; + +G_DEFINE_TYPE (MessagingMenuMessage, messaging_menu_message, G_TYPE_OBJECT); + +enum +{ + PROP_0, + PROP_ID, + PROP_ICON, + PROP_TITLE, + PROP_SUBTITLE, + PROP_BODY, + PROP_TIME, + PROP_DRAWS_ATTENTION, + NUM_PROPERTIES +}; + +static GParamSpec *properties[NUM_PROPERTIES]; + +static void +messaging_menu_message_dispose (GObject *object) +{ + MessagingMenuMessage *msg = MESSAGING_MENU_MESSAGE (object); + + g_clear_object (&msg->icon); + + G_OBJECT_CLASS (messaging_menu_message_parent_class)->dispose (object); +} + +static void +messaging_menu_message_finalize (GObject *object) +{ + MessagingMenuMessage *msg = MESSAGING_MENU_MESSAGE (object); + + g_free (msg->id); + g_free (msg->title); + g_free (msg->subtitle); + g_free (msg->body); + + G_OBJECT_CLASS (messaging_menu_message_parent_class)->finalize (object); +} + +static void +messaging_menu_message_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + MessagingMenuMessage *msg = MESSAGING_MENU_MESSAGE (object); + + switch (property_id) + { + case PROP_ID: + g_value_set_string (value, msg->id); + break; + + case PROP_ICON: + g_value_set_object (value, msg->icon); + break; + + case PROP_TITLE: + g_value_set_string (value, msg->title); + break; + + case PROP_SUBTITLE: + g_value_set_string (value, msg->subtitle); + break; + + case PROP_BODY: + g_value_set_string (value, msg->body); + + case PROP_TIME: + g_value_set_int64 (value, msg->time); + break; + + case PROP_DRAWS_ATTENTION: + g_value_set_boolean (value, msg->draws_attention); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +messaging_menu_message_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + MessagingMenuMessage *msg = MESSAGING_MENU_MESSAGE (object); + + switch (property_id) + { + case PROP_ID: + msg->id = g_value_dup_string (value); + break; + + case PROP_ICON: + msg->icon = g_value_dup_object (value); + break; + + case PROP_TITLE: + msg->title = g_value_dup_string (value); + break; + + case PROP_SUBTITLE: + msg->subtitle = g_value_dup_string (value); + break; + + case PROP_BODY: + msg->body = g_value_dup_string (value); + + case PROP_TIME: + msg->time = g_value_get_int64 (value); + break; + + case PROP_DRAWS_ATTENTION: + messaging_menu_message_set_draws_attention (msg, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +messaging_menu_message_class_init (MessagingMenuMessageClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = messaging_menu_message_dispose; + object_class->finalize = messaging_menu_message_finalize; + object_class->get_property = messaging_menu_message_get_property; + object_class->set_property = messaging_menu_message_set_property; + + properties[PROP_ID] = g_param_spec_string ("id", "Id", + "Unique id of the message", + NULL, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_ICON] = g_param_spec_object ("icon", "Icon", + "Icon of the message", + G_TYPE_ICON, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_TITLE] = g_param_spec_string ("title", "Title", + "Title of the message", + NULL, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_SUBTITLE] = g_param_spec_string ("subtitle", "Subtitle", + "Subtitle of the message", + NULL, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_BODY] = g_param_spec_string ("body", "Body", + "First lines of the body of the message", + NULL, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_TIME] = g_param_spec_int64 ("time", "Time", + "Time the message was sent, in microseconds", 0, G_MAXINT64, 0, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_DRAWS_ATTENTION] = g_param_spec_boolean ("draws-attention", "Draws attention", + "Whether the message should draw attention", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (klass, NUM_PROPERTIES, properties); +} + +static void +messaging_menu_message_init (MessagingMenuMessage *self) +{ +} + +/** + * messaging_menu_message_new: + * @id: unique id of the message + * @icon: (transfer full): a #GIcon representing the message + * @title: the title of the message + * @subtitle: (allow-none): the subtitle of the message + * @body: (allow-none): the message body + * @time: the time the message was received + * + * Creates a new #MessagingMenuMessage. + * + * Returns: (transfer full): a new #MessagingMenuMessage + */ +MessagingMenuMessage * +messaging_menu_message_new (const gchar *id, + GIcon *icon, + const gchar *title, + const gchar *subtitle, + const gchar *body, + gint64 time) +{ + g_return_val_if_fail (id != NULL, NULL); + g_return_val_if_fail (title != NULL, NULL); + + return g_object_new (MESSAGING_MENU_TYPE_MESSAGE, + "id", id, + "icon", icon, + "title", title, + "subtitle", subtitle, + "body", body, + "time", time, + NULL); +} + +/** + * messaging_menu_message_get_id: + * @msg: a #MessagingMenuMessage + * + * Returns: the unique id of @msg + */ +const gchar * +messaging_menu_message_get_id (MessagingMenuMessage *msg) +{ + g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), NULL); + + return msg->id; +} + +/** + * messaging_menu_message_get_icon: + * @msg: a #MessagingMenuMessage + * + * Returns: (transfer none): the icon of @msg + */ +GIcon * +messaging_menu_message_get_icon (MessagingMenuMessage *msg) +{ + g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), NULL); + + return msg->icon; +} + +/** + * messaging_menu_message_get_title: + * @msg: a #MessagingMenuMessage + * + * Returns: the title of @msg + */ +const gchar * +messaging_menu_message_get_title (MessagingMenuMessage *msg) +{ + g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), NULL); + + return msg->title; +} + +/** + * messaging_menu_message_get_subtitle: + * @msg: a #MessagingMenuMessage + * + * Returns: the subtitle of @msg + */ +const gchar * +messaging_menu_message_get_subtitle (MessagingMenuMessage *msg) +{ + g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), NULL); + + return msg->subtitle; +} + +/** + * messaging_menu_message_get_body: + * @msg: a #MessagingMenuMessage + * + * Returns: the body of @msg + */ +const gchar * +messaging_menu_message_get_body (MessagingMenuMessage *msg) +{ + g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), NULL); + + return msg->body; +} + +/** + * messaging_menu_message_get_time: + * @msg: a #MessagingMenuMessage + * + * Returns: the time at which @msg was received + */ +gint64 +messaging_menu_message_get_time (MessagingMenuMessage *msg) +{ + g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), 0); + + return msg->time; +} + +/** + * messaging_menu_message_get_draws_attention: + * @msg: a #MessagingMenuMessage + * + * Returns: whether @msg is drawing attention + */ +gboolean +messaging_menu_message_get_draws_attention (MessagingMenuMessage *msg) +{ + g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), FALSE); + + return msg->draws_attention; +} + +/** + * messaging_menu_message_set_draws_attention: + * @msg: a #MessagingMenuMessage + * @draws_attention: whether @msg should draw attention + * + * Sets whether @msg is drawing attention. + */ +void +messaging_menu_message_set_draws_attention (MessagingMenuMessage *msg, + gboolean draws_attention) +{ + g_return_if_fail (MESSAGING_MENU_IS_MESSAGE (msg)); + + msg->draws_attention = draws_attention; + g_object_notify_by_pspec (G_OBJECT (msg), properties[PROP_DRAWS_ATTENTION]); +} diff --git a/libmessaging-menu/messaging-menu-message.h b/libmessaging-menu/messaging-menu-message.h new file mode 100644 index 0000000..068247b --- /dev/null +++ b/libmessaging-menu/messaging-menu-message.h @@ -0,0 +1,64 @@ +/* + * 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_message_h__ +#define __messaging_menu_message_h__ + +#include + +G_BEGIN_DECLS + +#define MESSAGING_MENU_TYPE_MESSAGE (messaging_menu_message_get_type ()) +#define MESSAGING_MENU_MESSAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MESSAGING_MENU_TYPE_MESSAGE, MessagingMenuMessage)) +#define MESSAGING_MENU_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MESSAGING_MENU_TYPE_MESSAGE, MessagingMenuMessageClass)) +#define MESSAGING_MENU_IS_MESSAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MESSAGING_MENU_TYPE_MESSAGE)) +#define MESSAGING_MENU_IS_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MESSAGING_MENU_TYPE_MESSAGE)) +#define MESSAGING_MENU_MESSAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MESSAGING_MENU_TYPE_MESSAGE, MessagingMenuMessageClass)) + +typedef struct _MessagingMenuMessage MessagingMenuMessage; + +GType messaging_menu_message_get_type (void) G_GNUC_CONST; + +MessagingMenuMessage * messaging_menu_message_new (const gchar *id, + GIcon *icon, + const gchar *title, + const gchar *subtitle, + const gchar *body, + gint64 time); + +const gchar * messaging_menu_message_get_id (MessagingMenuMessage *msg); + +GIcon * messaging_menu_message_get_icon (MessagingMenuMessage *msg); + +const gchar * messaging_menu_message_get_title (MessagingMenuMessage *msg); + +const gchar * messaging_menu_message_get_subtitle (MessagingMenuMessage *msg); + +const gchar * messaging_menu_message_get_body (MessagingMenuMessage *msg); + +gint64 messaging_menu_message_get_time (MessagingMenuMessage *msg); + +gboolean messaging_menu_message_get_draws_attention (MessagingMenuMessage *msg); + +void messaging_menu_message_set_draws_attention (MessagingMenuMessage *msg, + gboolean draws_attention); + +G_END_DECLS + +#endif -- cgit v1.2.3 From ef6b3b8daaef62a502c60e0faa22c3a4b10c0399 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 13 Nov 2012 13:32:15 +0100 Subject: Move service .xml into a common directory Both the service (in src/) and the client library (in libmessaging-menu/) need access to the dbus interface description file. Until now, it resided in src, with both Makefiles calling gdbus-codegen on it. This patch moves the file to common/ and builds a convenience library that contains only the generated code. --- Makefile.am | 1 + common/Makefile.am | 22 ++++++++++++++++ .../com.canonical.indicator.messages.service.xml | 24 +++++++++++++++++ configure.ac | 1 + libmessaging-menu/Makefile.am | 24 +++++------------ src/Makefile.am | 30 +++++----------------- src/messages-service.xml | 24 ----------------- test/Makefile.am | 4 +-- 8 files changed, 62 insertions(+), 68 deletions(-) create mode 100644 common/Makefile.am create mode 100644 common/com.canonical.indicator.messages.service.xml delete mode 100644 src/messages-service.xml (limited to 'libmessaging-menu') diff --git a/Makefile.am b/Makefile.am index daeb2b7..f8141a8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,6 @@ SUBDIRS = \ + common \ src \ libmessaging-menu \ data \ diff --git a/common/Makefile.am b/common/Makefile.am new file mode 100644 index 0000000..5bd7e20 --- /dev/null +++ b/common/Makefile.am @@ -0,0 +1,22 @@ + +noinst_LTLIBRARIES = libmessaging-common.la + +indicator-messages-service.c: com.canonical.indicator.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 + +BUILT_SOURCES = \ + indicator-messages-service.c \ + indicator-messages-service.h + +libmessaging_common_la_SOURCES = \ + $(BUILT_SOURCES) + +libmessaging_common_la_CFLAGS = $(GIO_CFLAGS) +libmessaging_common_la_LIBADD = $(GIO_LIBS) + +CLEANFILES = $(BUILT_SOURCES) diff --git a/common/com.canonical.indicator.messages.service.xml b/common/com.canonical.indicator.messages.service.xml new file mode 100644 index 0000000..00ae154 --- /dev/null +++ b/common/com.canonical.indicator.messages.service.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/configure.ac b/configure.ac index 8aa2e8a..b5af4d1 100644 --- a/configure.ac +++ b/configure.ac @@ -156,6 +156,7 @@ AM_GLIB_GNU_GETTEXT AC_OUTPUT([ Makefile src/Makefile +common/Makefile data/Makefile data/icons/Makefile data/icons/16x16/Makefile diff --git a/libmessaging-menu/Makefile.am b/libmessaging-menu/Makefile.am index 15d0c9b..411f7ff 100644 --- a/libmessaging-menu/Makefile.am +++ b/libmessaging-menu/Makefile.am @@ -7,36 +7,24 @@ libmessaging_menu_la_SOURCES = \ messaging-menu-app.c \ messaging-menu-message.c \ gtupleaction.c \ - gtupleaction.h \ - $(BUILT_SOURCES) + gtupleaction.h libmessaging_menu_la_HEADERS = \ messaging-menu-app.h \ messaging-menu.h \ messaging-menu-message.h -libmessaging_menu_la_LIBADD = $(GIO_LIBS) +libmessaging_menu_la_LIBADD = \ + $(GIO_LIBS) \ + $(top_builddir)/common/libmessaging-common.la libmessaging_menu_la_CFLAGS = \ + -I$(top_builddir)/common \ $(GIO_CFLAGS) \ -Wall libmessaging_menu_la_LDFLAGS = -export-symbols-regex "^messaging_menu_.*" -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 @@ -69,5 +57,5 @@ gir_DATA = $(INTROSPECTION_GIRS) typelibdir = $(libdir)/girepository-1.0 typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) -CLEANFILES +=$(gir_DATA) $(typelib_DATA) +CLEANFILES = $(gir_DATA) $(typelib_DATA) endif diff --git a/src/Makefile.am b/src/Makefile.am index 1df80e5..a7bfe66 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,8 +1,5 @@ -BUILT_SOURCES = EXTRA_DIST = -CLEANFILES = -DISTCLEANFILES = libexec_PROGRAMS = indicator-messages-service @@ -23,19 +20,20 @@ libmessaging_la_SOURCES = \ im-source-menu-item.h \ ido-detail-label.c \ ido-detail-label.h \ - indicator-messages-service.c \ - indicator-messages-service.h dbus-data.h libmessaging_la_CFLAGS = \ $(APPLET_CFLAGS) \ $(COVERAGE_CFLAGS) \ + -I$(top_builddir)/common \ -Wall \ -Wl,-Bsymbolic-functions \ -Wl,-z,defs \ -Wl,--as-needed \ -Werror \ -DG_LOG_DOMAIN=\"Indicator-Messages\" -libmessaging_la_LIBADD = $(APPLET_LIBS) -lm +libmessaging_la_LIBADD = \ + $(top_builddir)/common/libmessaging-common.la \ + $(APPLET_LIBS) -lm libmessaging_la_LDFLAGS = \ $(COVERAGE_LDFLAGS) \ -module -avoid-version @@ -46,8 +44,6 @@ libmessaging_la_LDFLAGS = \ indicator_messages_service_SOURCES = \ messages-service.c \ - indicator-messages-service.c \ - indicator-messages-service.h \ app-section.c \ app-section.h \ dbus-data.h \ @@ -61,6 +57,7 @@ indicator_messages_service_SOURCES = \ indicator_messages_service_CFLAGS = \ $(APPLET_CFLAGS) \ $(COVERAGE_CFLAGS) \ + -I$(top_builddir)/common \ -Wall \ -Wl,-Bsymbolic-functions \ -Wl,-z,defs \ @@ -69,26 +66,11 @@ indicator_messages_service_CFLAGS = \ -DG_LOG_DOMAIN=\"Indicator-Messages\" indicator_messages_service_LDADD = \ + $(top_builddir)/common/libmessaging-common.la \ $(APPLET_LIBS) indicator_messages_service_LDFLAGS = \ $(COVERAGE_LDFLAGS) -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 - -BUILT_SOURCES += \ - indicator-messages-service.c \ - indicator-messages-service.h - EXTRA_DIST += \ messages-service.xml - -CLEANFILES += \ - $(BUILT_SOURCES) - diff --git a/src/messages-service.xml b/src/messages-service.xml deleted file mode 100644 index 00ae154..0000000 --- a/src/messages-service.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/test/Makefile.am b/test/Makefile.am index 4671446..ee7cb3e 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -38,8 +38,8 @@ noinst_LTLIBRARIES = \ libindicator-messages-service.la libindicator_messages_service_la_SOURCES = \ - $(top_builddir)/src/indicator-messages-service.c \ - $(top_builddir)/src/indicator-messages-service.h \ + $(top_builddir)/common/indicator-messages-service.c \ + $(top_builddir)/common/indicator-messages-service.h \ $(top_srcdir)/src/app-section.c \ $(top_srcdir)/src/app-section.h \ $(top_srcdir)/src/gactionmuxer.c \ -- cgit v1.2.3 From ebba9b86ef73743af028a82d53afa0a556017fe9 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 13 Nov 2012 13:40:12 +0100 Subject: Remove gtupleaction, it isn't used anymore --- doc/reference/Makefile.am | 3 +- libmessaging-menu/Makefile.am | 4 +- libmessaging-menu/gtupleaction.c | 354 --------------------------------------- libmessaging-menu/gtupleaction.h | 40 ----- 4 files changed, 2 insertions(+), 399 deletions(-) delete mode 100644 libmessaging-menu/gtupleaction.c delete mode 100644 libmessaging-menu/gtupleaction.h (limited to 'libmessaging-menu') diff --git a/doc/reference/Makefile.am b/doc/reference/Makefile.am index 023f1e7..3ea08f2 100644 --- a/doc/reference/Makefile.am +++ b/doc/reference/Makefile.am @@ -12,8 +12,7 @@ HFILE_GLOB = $(top_srcdir)/libmessaging-menu/*.h CFILE_GLOB = $(top_srcdir)/libmessaging-menu/*.c IGNORE_HFILES= \ - indicator-messages-service.h \ - gtupleaction.h + indicator-messages-service.h INCLUDES=-I$(top_srcdir)/libmessaging-menu $(GIO_CFLAGS) GTKDOC_LIBS=$(top_builddir)/libmessaging-menu/libmessaging-menu.la diff --git a/libmessaging-menu/Makefile.am b/libmessaging-menu/Makefile.am index 411f7ff..d18538b 100644 --- a/libmessaging-menu/Makefile.am +++ b/libmessaging-menu/Makefile.am @@ -5,9 +5,7 @@ libmessaging_menu_ladir = $(includedir)/messaging-menu libmessaging_menu_la_SOURCES = \ messaging-menu-app.c \ - messaging-menu-message.c \ - gtupleaction.c \ - gtupleaction.h + messaging-menu-message.c libmessaging_menu_la_HEADERS = \ messaging-menu-app.h \ diff --git a/libmessaging-menu/gtupleaction.c b/libmessaging-menu/gtupleaction.c deleted file mode 100644 index 21bc003..0000000 --- a/libmessaging-menu/gtupleaction.c +++ /dev/null @@ -1,354 +0,0 @@ -/* - * 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) -{ - action->enabled = TRUE; -} - -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 deleted file mode 100644 index c447d71..0000000 --- a/libmessaging-menu/gtupleaction.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 -- cgit v1.2.3 From 5a9434c6652ef1117a50a9226f8609cea2ee53c0 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 14 Nov 2012 17:57:54 +0100 Subject: Change application to service d-bus protocol Previously, the protocol was simply a menu model and an action group of the currently active sources. The service inserted the menu as a section into the indicator menu. This doesn't work anymore, because applications can (soon) expose individual messages, and the messaging menu doesn't always display all of those at once. This patch introduces a more specific d-bus API. That API is still considered private: applications have to use libmessaging-menu. --- common/Makefile.am | 12 +- ...om.canonical.indicator.messages.application.xml | 21 + libmessaging-menu/messaging-menu-app.c | 511 +++++++++++---------- src/app-section.c | 424 ++++++++++------- test/Makefile.am | 1 + 5 files changed, 555 insertions(+), 414 deletions(-) create mode 100644 common/com.canonical.indicator.messages.application.xml (limited to 'libmessaging-menu') diff --git a/common/Makefile.am b/common/Makefile.am index 5bd7e20..0b8bad4 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -9,9 +9,19 @@ indicator-messages-service.c: com.canonical.indicator.messages.service.xml $^ indicator-messages-service.h: indicator-messages-service.c +indicator-messages-application.c: com.canonical.indicator.messages.application.xml + $(AM_V_GEN) gdbus-codegen \ + --interface-prefix com.canonical.indicator.messages. \ + --generate-c-code indicator-messages-application \ + --c-namespace IndicatorMessages \ + $^ +indicator-messages-application.h: indicator-messages-application.c + BUILT_SOURCES = \ indicator-messages-service.c \ - indicator-messages-service.h + indicator-messages-service.h \ + indicator-messages-application.c \ + indicator-messages-application.h libmessaging_common_la_SOURCES = \ $(BUILT_SOURCES) diff --git a/common/com.canonical.indicator.messages.application.xml b/common/com.canonical.indicator.messages.application.xml new file mode 100644 index 0000000..ec095dd --- /dev/null +++ b/common/com.canonical.indicator.messages.application.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/libmessaging-menu/messaging-menu-app.c b/libmessaging-menu/messaging-menu-app.c index 377aea0..20d9474 100644 --- a/libmessaging-menu/messaging-menu-app.c +++ b/libmessaging-menu/messaging-menu-app.c @@ -19,8 +19,10 @@ #include "messaging-menu-app.h" #include "indicator-messages-service.h" +#include "indicator-messages-application.h" #include +#include /** * SECTION:messaging-menu @@ -102,14 +104,13 @@ struct _MessagingMenuApp int registered; /* -1 for unknown */ MessagingMenuStatus status; gboolean status_set; - GSimpleActionGroup *source_actions; - GMenu *menu; GDBusConnection *bus; + GList *sources; + IndicatorMessagesApplication *app_interface; + IndicatorMessagesService *messages_service; guint watch_id; - guint action_export_id; - guint menu_export_id; GCancellable *cancellable; }; @@ -133,10 +134,56 @@ static guint signals[N_SIGNALS]; static const gchar *status_ids[] = { "available", "away", "busy", "invisible", "offline" }; +typedef struct +{ + gchar *id; + GIcon *icon; + gchar *label; + + guint32 count; + gint64 time; + gchar *string; + gboolean draws_attention; +} Source; + static void global_status_changed (IndicatorMessagesService *service, const gchar *status_str, gpointer user_data); +static void +source_free (Source *source) +{ + if (source) + { + g_free (source->id); + g_clear_object (&source->icon); + g_free (source->label); + g_free (source->string); + g_slice_free (Source, source); + } +} + +static GVariant * +source_to_variant (Source *source) +{ + GVariant *v; + gchar *iconstr; + + iconstr = source->icon ? g_icon_to_string (source->icon) : NULL; + + v = g_variant_new ("(sssuxsb)", source->id, + source->label, + iconstr ? iconstr : "", + source->count, + source->time, + source->string ? source->string : "", + source->draws_attention); + + g_free (iconstr); + + return v; +} + static gchar * messaging_menu_app_get_dbus_object_path (MessagingMenuApp *app) { @@ -155,18 +202,14 @@ messaging_menu_app_get_dbus_object_path (MessagingMenuApp *app) } static void -export_menus_and_actions (GObject *source, - GAsyncResult *res, - gpointer user_data) +messaging_menu_app_got_bus (GObject *source, + GAsyncResult *res, + gpointer user_data) { MessagingMenuApp *app = user_data; GError *error = NULL; gchar *object_path; - object_path = messaging_menu_app_get_dbus_object_path (app); - if (!object_path) - return; - app->bus = g_bus_get_finish (res, &error); if (app->bus == NULL) { @@ -175,23 +218,13 @@ export_menus_and_actions (GObject *source, return; } - app->action_export_id = g_dbus_connection_export_action_group (app->bus, - object_path, - G_ACTION_GROUP (app->source_actions), - &error); - if (!app->action_export_id) - { - g_warning ("unable to export action group: %s", error->message); - g_clear_error (&error); - } + object_path = messaging_menu_app_get_dbus_object_path (app); - app->menu_export_id = g_dbus_connection_export_menu_model (app->bus, - object_path, - G_MENU_MODEL (app->menu), - &error); - if (!app->menu_export_id) + if (object_path && + !g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (app->app_interface), + app->bus, object_path, &error)) { - g_warning ("unable to export menu: %s", error->message); + g_warning ("unable to export application interface: %s", error->message); g_clear_error (&error); } @@ -214,7 +247,7 @@ messaging_menu_app_set_desktop_id (MessagingMenuApp *app, g_bus_get (G_BUS_TYPE_SESSION, app->cancellable, - export_menus_and_actions, + messaging_menu_app_got_bus, app); } @@ -248,20 +281,6 @@ messaging_menu_app_dispose (GObject *object) { MessagingMenuApp *app = MESSAGING_MENU_APP (object); - if (app->bus) - { - if (app->action_export_id > 0) - g_dbus_connection_unexport_action_group (app->bus, app->action_export_id); - - if (app->menu_export_id > 0) - g_dbus_connection_unexport_menu_model (app->bus, app->menu_export_id); - - app->action_export_id = 0; - app->menu_export_id = 0; - g_object_unref (app->bus); - app->bus = NULL; - } - if (app->watch_id > 0) { g_bus_unwatch_name (app->watch_id); @@ -283,9 +302,9 @@ messaging_menu_app_dispose (GObject *object) g_clear_object (&app->messages_service); } + g_clear_object (&app->app_interface); g_clear_object (&app->appinfo); - g_clear_object (&app->source_actions); - g_clear_object (&app->menu); + g_clear_object (&app->bus); G_OBJECT_CLASS (messaging_menu_app_parent_class)->dispose (object); } @@ -416,6 +435,72 @@ indicator_messages_vanished (GDBusConnection *bus, } } +static gboolean +messaging_menu_app_list_sources (IndicatorMessagesApplication *app_interface, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + MessagingMenuApp *app = user_data; + GVariantBuilder builder; + GList *it; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sssuxsb)")); + + for (it = app->sources; it; it = it->next) + g_variant_builder_add_value (&builder, source_to_variant (it->data)); + + indicator_messages_application_complete_list_sources (app_interface, + invocation, + g_variant_builder_end (&builder)); + + return TRUE; +} + +static gint +compare_source_id (gconstpointer a, + gconstpointer b) +{ + const Source *source = a; + const gchar *id = b; + + return strcmp (source->id, id); +} + +static gboolean +messaging_menu_app_remove_source_internal (MessagingMenuApp *app, + const gchar *source_id) +{ + GList *node; + + node = g_list_find_custom (app->sources, source_id, compare_source_id); + if (node) + { + source_free (node->data); + app->sources = g_list_delete_link (app->sources, node); + return TRUE; + } + + return FALSE; +} + +static gboolean +messaging_menu_app_activate_source (IndicatorMessagesApplication *app_interface, + GDBusMethodInvocation *invocation, + const gchar *source_id, + gpointer user_data) +{ + MessagingMenuApp *app = user_data; + GQuark q = g_quark_from_string (source_id); + + /* Activate implies removing the source, no need for SourcesChanged */ + if (messaging_menu_app_remove_source_internal (app, source_id)) + g_signal_emit (app, signals[ACTIVATE_SOURCE], q, source_id); + + indicator_messages_application_complete_activate_source (app_interface, invocation); + + return TRUE; +} + static void messaging_menu_app_init (MessagingMenuApp *app) { @@ -423,13 +508,13 @@ messaging_menu_app_init (MessagingMenuApp *app) app->status_set = FALSE; app->bus = NULL; - app->action_export_id = 0; - app->menu_export_id = 0; - app->cancellable = g_cancellable_new (); - app->source_actions = g_simple_action_group_new (); - app->menu = g_menu_new (); + app->app_interface = indicator_messages_application_skeleton_new (); + g_signal_connect (app->app_interface, "handle-list-sources", + G_CALLBACK (messaging_menu_app_list_sources), app); + g_signal_connect (app->app_interface, "handle-activate-source", + G_CALLBACK (messaging_menu_app_activate_source), app); app->cancellable = g_cancellable_new (); @@ -604,123 +689,73 @@ global_status_changed (IndicatorMessagesService *service, g_signal_emit (app, signals[STATUS_CHANGED], 0, status); } -static void -source_action_activated (GSimpleAction *action, - GVariant *parameter, - gpointer user_data) +static Source * +messaging_menu_app_lookup_source (MessagingMenuApp *app, + const gchar *id) { - MessagingMenuApp *app = user_data; - const gchar *name = g_action_get_name (G_ACTION (action)); - GQuark q = g_quark_from_string (name); + GList *node; - messaging_menu_app_remove_source (app, name); + node = g_list_find_custom (app->sources, id, compare_source_id); - g_signal_emit (app, signals[ACTIVATE_SOURCE], q, name); + return node ? node->data : NULL; } -static void -messaging_menu_app_insert_source_action (MessagingMenuApp *app, - gint position, - const gchar *id, - GIcon *icon, - const gchar *label, - GVariant *state) +static Source * +messaging_menu_app_get_source (MessagingMenuApp *app, + const gchar *id) { - GSimpleAction *action; - GMenuItem *menuitem; + Source *source; - g_return_if_fail (MESSAGING_MENU_IS_APP (app)); - g_return_if_fail (id != NULL); - - if (g_simple_action_group_lookup (app->source_actions, id)) - { - g_warning ("a source with id '%s' already exists", id); - return; - } - - action = g_simple_action_new_stateful (id, NULL, state); - g_signal_connect (action, "activate", - G_CALLBACK (source_action_activated), app); - g_simple_action_group_insert (app->source_actions, G_ACTION (action)); - g_object_unref (action); - - menuitem = g_menu_item_new (label, id); - g_menu_item_set_attribute (menuitem, "x-canonical-type", "s", "ImSourceMenuItem"); - if (icon) - { - gchar *iconstr = g_icon_to_string (icon); - g_menu_item_set_attribute (menuitem, "x-canonical-icon", "s", iconstr); - g_free (iconstr); - } - g_menu_insert_item (app->menu, position, menuitem); - g_object_unref (menuitem); -} - -static GSimpleAction * -messaging_menu_app_get_source_action (MessagingMenuApp *app, - const gchar *source_id) - -{ - GAction *action; - - g_return_val_if_fail (MESSAGING_MENU_IS_APP (app), NULL); - g_return_val_if_fail (source_id != NULL, NULL); - - action = g_simple_action_group_lookup (app->source_actions, source_id); - if (action == NULL) - g_warning ("a source with id '%s' doesn't exist", source_id); + source = messaging_menu_app_lookup_source (app, id); + if (!source) + g_warning ("a source with id '%s' doesn't exist", id); - return G_SIMPLE_ACTION (action); + return source; } static void -messaging_menu_app_set_source_action (MessagingMenuApp *app, - const gchar *source_id, - guint count, - gint64 time, - const gchar *string) +messaging_menu_app_notify_source_changed (MessagingMenuApp *app, + Source *source) { - GSimpleAction *action; - GVariant *state; - gboolean draws_attention; - GVariant *new_state; - - action = messaging_menu_app_get_source_action (app, source_id); - if (!action) - return; - - state = g_action_get_state (G_ACTION (action)); - g_variant_get_child (state, 3, "b", &draws_attention); - - new_state = g_variant_new ("(uxsb)", count, time, string, draws_attention); - g_simple_action_set_state (action, new_state); - - g_variant_unref (state); + indicator_messages_application_emit_source_changed (app->app_interface, + source_to_variant (source)); } static void -messaging_menu_app_set_draws_attention (MessagingMenuApp *app, - const gchar *source_id, - gboolean draws_attention) +messaging_menu_app_insert_source_internal (MessagingMenuApp *app, + gint position, + const gchar *id, + GIcon *icon, + const gchar *label, + guint count, + gint64 time, + const gchar *string) { - GSimpleAction *action; - GVariant *state; - guint count; - gint64 time; - const gchar *string; - GVariant *new_state; + Source *source; - action = messaging_menu_app_get_source_action (app, source_id); - if (!action) - return; - - state = g_action_get_state (G_ACTION (action)); - g_variant_get (state, "(ux&sb)", &count, &time, &string); + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + g_return_if_fail (id != NULL); + g_return_if_fail (label != NULL); - new_state = g_variant_new ("(uxsb)", count, time, string, TRUE); - g_simple_action_set_state (action, new_state); + if (messaging_menu_app_lookup_source (app, id)) + { + g_warning ("a source with id '%s' already exists", id); + return; + } - g_variant_unref (state); + source = g_slice_new0 (Source); + source->id = g_strdup (id); + source->label = g_strdup (label); + if (icon) + source->icon = g_object_ref (icon); + source->count = count; + source->time = time; + source->string = g_strdup (string); + app->sources = g_list_insert (app->sources, source, position); + + indicator_messages_application_emit_source_added (app->app_interface, + position, + source_to_variant (source)); } /** @@ -797,8 +832,7 @@ messaging_menu_app_insert_source_with_count (MessagingMenuApp *app, const gchar *label, guint count) { - messaging_menu_app_insert_source_action (app, position, id, icon, label, - g_variant_new ("(uxsb)", count, 0, "", FALSE)); + messaging_menu_app_insert_source_internal (app, position, id, icon, label, count, 0, ""); } /** @@ -852,8 +886,7 @@ messaging_menu_app_insert_source_with_time (MessagingMenuApp *app, const gchar *label, gint64 time) { - messaging_menu_app_insert_source_action (app, position, id, icon, label, - g_variant_new ("(uxsb)", 0, time, "", FALSE)); + messaging_menu_app_insert_source_internal (app, position, id, icon, label, 0, time, ""); } /** @@ -909,8 +942,7 @@ messaging_menu_app_insert_source_with_string (MessagingMenuApp *app, const gchar *label, const gchar *str) { - messaging_menu_app_insert_source_action (app, position, id, icon, label, - g_variant_new ("(uxsb)", 0, 0, str, FALSE)); + messaging_menu_app_insert_source_internal (app, position, id, icon, label, 0, 0, str); } /** @@ -950,34 +982,11 @@ void messaging_menu_app_remove_source (MessagingMenuApp *app, const gchar *source_id) { - int n_items; - int i; - g_return_if_fail (MESSAGING_MENU_IS_APP (app)); g_return_if_fail (source_id != NULL); - if (g_simple_action_group_lookup (app->source_actions, source_id) == NULL) - return; - - n_items = g_menu_model_get_n_items (G_MENU_MODEL (app->menu)); - for (i = 0; i < n_items; i++) - { - gchar *action; - - if (g_menu_model_get_item_attribute (G_MENU_MODEL (app->menu), i, - "action", "s", &action)) - { - if (!g_strcmp0 (action, source_id)) - { - g_menu_remove (app->menu, i); - break; - } - - g_free (action); - } - } - - g_simple_action_group_remove (app->source_actions, source_id); + if (messaging_menu_app_remove_source_internal (app, source_id)) + indicator_messages_application_emit_source_removed (app->app_interface, source_id); } /** @@ -994,46 +1003,7 @@ messaging_menu_app_has_source (MessagingMenuApp *app, g_return_val_if_fail (MESSAGING_MENU_IS_APP (app), FALSE); g_return_val_if_fail (source_id != NULL, FALSE); - return g_simple_action_group_lookup (app->source_actions, source_id) != NULL; -} - -static GMenuItem * -g_menu_find_item_with_action (GMenu *menu, - const gchar *action, - gint *out_pos) -{ - gint i; - gint n_elements; - GMenuItem *item = NULL; - - n_elements = g_menu_model_get_n_items (G_MENU_MODEL (menu)); - - for (i = 0; i < n_elements && item == NULL; i++) - { - GVariant *attr; - - item = g_menu_item_new_from_model (G_MENU_MODEL (menu), i); - attr = g_menu_item_get_attribute_value (item, G_MENU_ATTRIBUTE_ACTION, G_VARIANT_TYPE_STRING); - - if (!g_str_equal (action, g_variant_get_string (attr, NULL))) - g_clear_object (&item); - - g_variant_unref (attr); - } - - if (item && out_pos) - *out_pos = i - 1; - - return item; -} - -static void -g_menu_replace_item (GMenu *menu, - gint pos, - GMenuItem *item) -{ - g_menu_remove (menu, pos); - g_menu_insert_item (menu, pos, item); + return messaging_menu_app_lookup_source (app, source_id) != NULL; } /** @@ -1049,21 +1019,19 @@ messaging_menu_app_set_source_label (MessagingMenuApp *app, const gchar *source_id, const gchar *label) { - gint pos; - GMenuItem *item; + Source *source; g_return_if_fail (MESSAGING_MENU_IS_APP (app)); g_return_if_fail (source_id != NULL); g_return_if_fail (label != NULL); - item = g_menu_find_item_with_action (app->menu, source_id, &pos); - if (item == NULL) - return; - - g_menu_item_set_attribute (item, G_MENU_ATTRIBUTE_LABEL, "s", label); - g_menu_replace_item (app->menu, pos, item); - - g_object_unref (item); + source = messaging_menu_app_get_source (app, source_id); + if (source) + { + g_free (source->label); + source->label = g_strdup (label); + messaging_menu_app_notify_source_changed (app, source); + } } /** @@ -1079,33 +1047,19 @@ messaging_menu_app_set_source_icon (MessagingMenuApp *app, const gchar *source_id, GIcon *icon) { - gint pos; - GMenuItem *item; + Source *source; g_return_if_fail (MESSAGING_MENU_IS_APP (app)); g_return_if_fail (source_id != NULL); - item = g_menu_find_item_with_action (app->menu, source_id, &pos); - if (item == NULL) - return; - - if (icon) + source = messaging_menu_app_get_source (app, source_id); + if (source) { - gchar *iconstr; - - iconstr = g_icon_to_string (icon); - g_menu_item_set_attribute (item, "x-canonical-icon", "s", iconstr); - - g_free (iconstr); - } - else - { - g_menu_item_set_attribute_value (item, "x-canonical-icon", NULL); + g_clear_object (&source->icon); + if (icon) + source->icon = g_object_ref (icon); + messaging_menu_app_notify_source_changed (app, source); } - - g_menu_replace_item (app->menu, pos, item); - - g_object_unref (item); } /** @@ -1120,7 +1074,17 @@ void messaging_menu_app_set_source_count (MessagingMenuApp *app, const gchar *source_id, guint count) { - messaging_menu_app_set_source_action (app, source_id, count, 0, ""); + Source *source; + + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + g_return_if_fail (source_id != NULL); + + source = messaging_menu_app_get_source (app, source_id); + if (source) + { + source->count = count; + messaging_menu_app_notify_source_changed (app, source); + } } /** @@ -1136,7 +1100,17 @@ messaging_menu_app_set_source_time (MessagingMenuApp *app, const gchar *source_id, gint64 time) { - messaging_menu_app_set_source_action (app, source_id, 0, time, ""); + Source *source; + + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + g_return_if_fail (source_id != NULL); + + source = messaging_menu_app_get_source (app, source_id); + if (source) + { + source->time = time; + messaging_menu_app_notify_source_changed (app, source); + } } /** @@ -1152,7 +1126,18 @@ messaging_menu_app_set_source_string (MessagingMenuApp *app, const gchar *source_id, const gchar *str) { - messaging_menu_app_set_source_action (app, source_id, 0, 0, str); + Source *source; + + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + g_return_if_fail (source_id != NULL); + + source = messaging_menu_app_get_source (app, source_id); + if (source) + { + g_free (source->string); + source->string = g_strdup (str); + messaging_menu_app_notify_source_changed (app, source); + } } /** @@ -1170,7 +1155,17 @@ void messaging_menu_app_draw_attention (MessagingMenuApp *app, const gchar *source_id) { - messaging_menu_app_set_draws_attention (app, source_id, TRUE); + Source *source; + + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + g_return_if_fail (source_id != NULL); + + source = messaging_menu_app_get_source (app, source_id); + if (source) + { + source->draws_attention = TRUE; + messaging_menu_app_notify_source_changed (app, source); + } } /** @@ -1191,7 +1186,17 @@ void messaging_menu_app_remove_attention (MessagingMenuApp *app, const gchar *source_id) { - messaging_menu_app_set_draws_attention (app, source_id, TRUE); + Source *source; + + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + g_return_if_fail (source_id != NULL); + + source = messaging_menu_app_get_source (app, source_id); + if (source) + { + source->draws_attention = FALSE; + messaging_menu_app_notify_source_changed (app, source); + } } /** diff --git a/src/app-section.c b/src/app-section.c index 6aac52a..19532f2 100644 --- a/src/app-section.c +++ b/src/app-section.c @@ -33,6 +33,7 @@ with this program. If not, see . #include "dbus-data.h" #include "gmenuutils.h" #include "gactionmuxer.h" +#include "indicator-messages-application.h" struct _AppSectionPrivate { @@ -41,11 +42,14 @@ struct _AppSectionPrivate IndicatorDesktopShortcuts * ids; + GCancellable *app_proxy_cancellable; + IndicatorMessagesApplication *app_proxy; + GMenu *menu; - GMenuModel *source_menu; + GMenu *source_menu; GSimpleActionGroup *static_shortcuts; - GActionGroup *source_actions; + GSimpleActionGroup *source_actions; GActionMuxer *muxer; gboolean draws_attention; @@ -89,19 +93,6 @@ static void launch_action_change_state (GSimpleAction *action, gpointer user_data); static void app_section_set_app_info (AppSection *self, GDesktopAppInfo *appinfo); -static gboolean any_action_draws_attention (GActionGroup *group, - const gchar *ignored_action); -static void action_added (GActionGroup *group, - const gchar *action_name, - gpointer user_data); -static void action_state_changed (GActionGroup *group, - const gchar *action_name, - GVariant *value, - gpointer user_data); -static void action_removed (GActionGroup *group, - const gchar *action_name, - gpointer user_data); -static gboolean action_draws_attention (GVariant *state); static void desktop_file_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, @@ -169,6 +160,7 @@ static void app_section_init (AppSection *self) { AppSectionPrivate *priv; + GMenuItem *item; self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, APP_SECTION_TYPE, @@ -178,10 +170,19 @@ app_section_init (AppSection *self) priv->appinfo = NULL; priv->menu = g_menu_new (); + + priv->source_menu = g_menu_new (); + item = g_menu_item_new_section (NULL, G_MENU_MODEL (priv->source_menu)); + g_menu_item_set_attribute (item, "action-namespace", "s", "source"); + g_menu_append_item (priv->menu, item); + g_object_unref (item); + priv->static_shortcuts = g_simple_action_group_new (); + priv->source_actions = g_simple_action_group_new (); priv->muxer = g_action_muxer_new (); g_action_muxer_insert (priv->muxer, NULL, G_ACTION_GROUP (priv->static_shortcuts)); + g_action_muxer_insert (priv->muxer, "source", G_ACTION_GROUP (priv->source_actions)); priv->draws_attention = FALSE; @@ -248,32 +249,30 @@ app_section_dispose (GObject *object) AppSection * self = APP_SECTION(object); AppSectionPrivate * priv = self->priv; + if (priv->app_proxy_cancellable) { + g_cancellable_cancel (priv->app_proxy_cancellable); + g_clear_object (&priv->app_proxy_cancellable); + } + if (priv->desktop_file_monitor) { g_signal_handlers_disconnect_by_func (priv->desktop_file_monitor, desktop_file_changed_cb, self); g_clear_object (&priv->desktop_file_monitor); } + g_clear_object (&priv->app_proxy); + g_clear_object (&priv->menu); + g_clear_object (&priv->source_menu); g_clear_object (&priv->static_shortcuts); + g_clear_object (&priv->source_actions); if (priv->name_watch_id) { g_bus_unwatch_name (priv->name_watch_id); priv->name_watch_id = 0; } - if (priv->source_actions) { - g_action_muxer_remove (priv->muxer, "source"); - g_object_disconnect (priv->source_actions, - "any_signal::action-added", action_added, self, - "any_signal::action-state-changed", action_state_changed, self, - "any_signal::action-removed", action_removed, self, - NULL); - g_clear_object (&priv->source_actions); - } - g_clear_object (&priv->muxer); - g_clear_object (&priv->source_menu); g_clear_object (&priv->ids); g_clear_object (&priv->appinfo); @@ -429,6 +428,11 @@ app_section_update_menu (AppSection *self) g_free(name); } + item = g_menu_item_new_section (NULL, G_MENU_MODEL (priv->source_menu)); + g_menu_item_set_attribute (item, "action-namespace", "s", "source"); + g_menu_append_item (priv->menu, item); + g_object_unref (item); + keyfile = g_file_new_for_path (g_desktop_app_info_get_filename (priv->appinfo)); g_file_load_contents_async (keyfile, NULL, keyfile_loaded, self); @@ -564,49 +568,242 @@ app_section_get_draws_attention (AppSection *self) void app_section_clear_draws_attention (AppSection *self) { - AppSectionPrivate * priv = self->priv; - gchar **action_names; + self->priv->draws_attention = FALSE; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]); +} + +static void +application_vanished (GDBusConnection *bus, + const gchar *name, + gpointer user_data) +{ + AppSection *self = user_data; + + app_section_unset_object_path (self); +} + +static void +update_draws_attention (AppSection *self) +{ + AppSectionPrivate *priv = self->priv; + gchar **actions; gchar **it; + gboolean draws_attention = FALSE; + + actions = g_action_group_list_actions (G_ACTION_GROUP (priv->source_actions)); + + for (it = actions; *it; it++) { + GVariant *state; + + state = g_action_group_get_action_state (G_ACTION_GROUP (priv->source_actions), *it); + if (state) { + gboolean b; + g_variant_get (state, "(uxsb)", NULL, NULL, NULL, &b); + draws_attention = b || draws_attention; + g_variant_unref (state); + } + + if (draws_attention) + break; + } + + if (draws_attention != priv->draws_attention) { + priv->draws_attention = draws_attention; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]); + } + + g_strfreev (actions); +} + +static void +remove_source (AppSection *self, + const gchar *id) +{ + AppSectionPrivate *priv = self->priv; + guint n_items; + guint i; + + n_items = g_menu_model_get_n_items (G_MENU_MODEL (priv->source_menu)); + for (i = 0; i < n_items; i++) { + gchar *action; + gboolean found = FALSE; + + if (g_menu_model_get_item_attribute (G_MENU_MODEL (priv->source_menu), i, + G_MENU_ATTRIBUTE_ACTION, "s", &action)) { + found = g_str_equal (action, id); + g_free (action); + } + + if (found) { + g_menu_remove (priv->source_menu, i); + break; + } + } + + g_simple_action_group_remove (priv->source_actions, id); + update_draws_attention (self); +} + +static void +source_action_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + AppSection *self = APP_SECTION (user_data); + AppSectionPrivate *priv = APP_SECTION (user_data)->priv; + + g_return_if_fail (priv->app_proxy != NULL); + + indicator_messages_application_call_activate_source (priv->app_proxy, + g_action_get_name (G_ACTION (action)), + priv->app_proxy_cancellable, + NULL, NULL); + + remove_source (self, g_action_get_name (G_ACTION (action))); +} + +static void +sources_listed (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + AppSection *self = user_data; + AppSectionPrivate *priv = self->priv; + GVariant *sources = NULL; + GError *error = NULL; + GVariantIter iter; + const gchar *id; + const gchar *label; + const gchar *iconstr; + guint32 count; + gint64 time; + const gchar *string; + gboolean draws_attention; - if (priv->source_actions == NULL) + if (!indicator_messages_application_call_list_sources_finish (INDICATOR_MESSAGES_APPLICATION (source_object), + &sources, result, &error)) + { + g_warning ("could not fetch the list of sources: %s", error->message); + g_error_free (error); return; + } - action_names = g_action_group_list_actions (priv->source_actions); + g_menu_clear (priv->source_menu); + g_simple_action_group_clear (priv->source_actions); + priv->draws_attention = FALSE; - for (it = action_names; *it; it++) { + g_variant_iter_init (&iter, sources); + while (g_variant_iter_next (&iter, "(&s&s&sux&sb)", &id, &label, &iconstr, + &count, &time, &string, &draws_attention)) + { GVariant *state; + GSimpleAction *action; + GMenuItem *item; - state = g_action_group_get_action_state (priv->source_actions, *it); - if (!state) - continue; + state = g_variant_new ("(uxsb)", count, time, string, draws_attention); + action = g_simple_action_new_stateful (id, NULL, state); + g_signal_connect (action, "activate", G_CALLBACK (source_action_activated), self); + g_simple_action_group_insert (priv->source_actions, G_ACTION (action)); - /* clear draws-attention while preserving other state */ - if (action_draws_attention (state)) { - guint32 count; - gint64 time; - const gchar *str; - GVariant *new_state; + item = g_menu_item_new (label, id); + g_menu_item_set_attribute (item, "x-canonical-type", "s", "ImSourceMenuItem"); + g_menu_append_item (priv->source_menu, item); - g_variant_get (state, "(ux&sb)", &count, &time, &str, NULL); + priv->draws_attention = priv->draws_attention || draws_attention; - new_state = g_variant_new ("(uxsb)", count, time, str, FALSE); - g_action_group_change_action_state (priv->source_actions, *it, new_state); - } + g_object_unref (item); + g_object_unref (action); + } + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]); + + g_variant_unref (sources); +} + +static void +source_added (IndicatorMessagesApplication *app, + const gchar *id, + const gchar *label, + const gchar *iconstr, + guint count, + gint64 time, + const gchar *string, + gboolean draws_attention, + gpointer user_data) +{ + AppSection *self = user_data; + AppSectionPrivate *priv = self->priv; + GVariant *state; + GSimpleAction *action; + + /* TODO put label and icon into the action as well */ - g_variant_unref (state); + state = g_variant_new ("(uxsb)", count, time, string, draws_attention); + action = g_simple_action_new_stateful (id, NULL, state); + + g_simple_action_group_insert (priv->source_actions, G_ACTION (action)); + + if (draws_attention && !priv->draws_attention) { + priv->draws_attention = TRUE; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]); } - g_strfreev (action_names); + g_object_unref (action); +} +static void +source_changed (IndicatorMessagesApplication *app, + const gchar *id, + const gchar *label, + const gchar *iconstr, + guint count, + gint64 time, + const gchar *string, + gboolean draws_attention, + gpointer user_data) +{ + AppSection *self = user_data; + AppSectionPrivate *priv = self->priv; + GVariant *state; + + /* TODO put label and icon into the action as well */ + + state = g_variant_new ("(uxsb)", count, time, string, draws_attention); + g_action_group_change_action_state (G_ACTION_GROUP (priv->source_actions), id, state); + + update_draws_attention (self); } static void -application_vanished (GDBusConnection *bus, - const gchar *name, - gpointer user_data) +source_removed (IndicatorMessagesApplication *app, + const gchar *id, + gpointer user_data) { AppSection *self = user_data; - app_section_unset_object_path (self); + remove_source (self, id); +} + +static void +app_proxy_created (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + AppSectionPrivate *priv = APP_SECTION (user_data)->priv; + GError *error = NULL; + + priv->app_proxy = indicator_messages_application_proxy_new_finish (result, &error); + if (!priv->app_proxy) { + g_warning ("could not create application proxy: %s", error->message); + g_error_free (error); + return; + } + + indicator_messages_application_call_list_sources (priv->app_proxy, priv->app_proxy_cancellable, + sources_listed, user_data); + + g_signal_connect (priv->app_proxy, "source-added", G_CALLBACK (source_added), user_data); + g_signal_connect (priv->app_proxy, "source-changed", G_CALLBACK (source_changed), user_data); + g_signal_connect (priv->app_proxy, "source-removed", G_CALLBACK (source_removed), user_data); } /* @@ -627,27 +824,20 @@ app_section_set_object_path (AppSection *self, const gchar *object_path) { AppSectionPrivate *priv = self->priv; - GMenuItem *item; g_object_freeze_notify (G_OBJECT (self)); app_section_unset_object_path (self); - priv->source_actions = G_ACTION_GROUP (g_dbus_action_group_get (bus, bus_name, object_path)); - g_action_muxer_insert (priv->muxer, "source", priv->source_actions); - - priv->draws_attention = any_action_draws_attention (priv->source_actions, NULL); - g_object_connect (priv->source_actions, - "signal::action-added", action_added, self, - "signal::action-state-changed", action_state_changed, self, - "signal::action-removed", action_removed, self, - NULL); + priv->app_proxy_cancellable = g_cancellable_new (); + indicator_messages_application_proxy_new (bus, + G_DBUS_PROXY_FLAGS_NONE, + bus_name, + object_path, + priv->app_proxy_cancellable, + app_proxy_created, + self); - priv->source_menu = G_MENU_MODEL (g_dbus_menu_model_get (bus, bus_name, object_path)); - - item = g_menu_item_new_section (NULL, priv->source_menu); - g_menu_item_set_attribute (item, "action-namespace", "s", "source"); - g_menu_append_item (priv->menu, item); - g_object_unref (item); + priv->draws_attention = FALSE; priv->name_watch_id = g_bus_watch_name_on_connection (bus, bus_name, 0, NULL, application_vanished, @@ -675,26 +865,19 @@ app_section_unset_object_path (AppSection *self) { AppSectionPrivate *priv = self->priv; + if (priv->app_proxy_cancellable) { + g_cancellable_cancel (priv->app_proxy_cancellable); + g_clear_object (&priv->app_proxy_cancellable); + } + g_clear_object (&priv->app_proxy); + if (priv->name_watch_id) { g_bus_unwatch_name (priv->name_watch_id); priv->name_watch_id = 0; } - if (priv->source_actions) { - g_object_disconnect (priv->source_actions, - "any_signal::action-added", action_added, self, - "any_signal::action-state-changed", action_state_changed, self, - "any_signal::action-removed", action_removed, self, - NULL); - g_clear_object (&priv->source_actions); - } - - if (priv->source_menu) { - /* the last menu item points is linked to the app's menumodel */ - gint n_items = g_menu_model_get_n_items (G_MENU_MODEL (priv->menu)); - g_menu_remove (priv->menu, n_items -1); - g_clear_object (&priv->source_menu); - } + g_simple_action_group_clear (priv->source_actions); + g_menu_clear (priv->source_menu); priv->draws_attention = FALSE; g_clear_pointer (&priv->chat_status, g_free); @@ -708,85 +891,6 @@ app_section_unset_object_path (AppSection *self) "launch", g_variant_new_boolean (FALSE)); } -static gboolean -action_draws_attention (GVariant *state) -{ - gboolean attention; - - if (state && g_variant_is_of_type (state, G_VARIANT_TYPE ("(uxsb)"))) - g_variant_get_child (state, 3, "b", &attention); - else - attention = FALSE; - - return attention; -} - -static gboolean -any_action_draws_attention (GActionGroup *group, - const gchar *ignored_action) -{ - gchar **actions; - gchar **it; - gboolean attention = FALSE; - - actions = g_action_group_list_actions (group); - - for (it = actions; *it && !attention; it++) { - GVariant *state; - - if (ignored_action && g_str_equal (ignored_action, *it)) - continue; - - state = g_action_group_get_action_state (group, *it); - if (state) { - attention = action_draws_attention (state); - g_variant_unref (state); - } - } - - g_strfreev (actions); - return attention; -} - -static void -action_added (GActionGroup *group, - const gchar *action_name, - gpointer user_data) -{ - AppSection *self = user_data; - GVariant *state; - - state = g_action_group_get_action_state (group, action_name); - if (state) { - self->priv->draws_attention |= action_draws_attention (state); - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]); - g_variant_unref (state); - } -} - -static void -action_state_changed (GActionGroup *group, - const gchar *action_name, - GVariant *value, - gpointer user_data) -{ - AppSection *self = user_data; - - self->priv->draws_attention = any_action_draws_attention (group, NULL); - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]); -} - -static void -action_removed (GActionGroup *group, - const gchar *action_name, - gpointer user_data) -{ - AppSection *self = user_data; - - self->priv->draws_attention = any_action_draws_attention (group, action_name); - g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]); -} - gboolean app_section_get_uses_chat_status (AppSection *self) { diff --git a/test/Makefile.am b/test/Makefile.am index ee7cb3e..8c8c160 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -53,6 +53,7 @@ libindicator_messages_service_la_CFLAGS = \ $(APPLET_CFLAGS) \ $(COVERAGE_CFLAGS) \ -I$(top_builddir)/src \ + -I$(top_builddir)/common \ -Wall \ -Wl,-Bsymbolic-functions \ -Wl,-z,defs \ -- cgit v1.2.3 From e60843df6c318ba7281067d11744e182ff739c72 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 14 Nov 2012 22:37:29 +0100 Subject: MessagingMenuApp: fix leak (source list) --- libmessaging-menu/messaging-menu-app.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/messaging-menu-app.c b/libmessaging-menu/messaging-menu-app.c index 20d9474..d793d6b 100644 --- a/libmessaging-menu/messaging-menu-app.c +++ b/libmessaging-menu/messaging-menu-app.c @@ -151,8 +151,10 @@ static void global_status_changed (IndicatorMessagesService *service, gpointer user_data); static void -source_free (Source *source) +source_free (gpointer data) { + Source *source = data; + if (source) { g_free (source->id); @@ -302,6 +304,8 @@ messaging_menu_app_dispose (GObject *object) g_clear_object (&app->messages_service); } + g_list_free_full (app->sources, source_free); + g_clear_object (&app->app_interface); g_clear_object (&app->appinfo); g_clear_object (&app->bus); -- cgit v1.2.3 From a44f4a7f0d1918809452da175c5b0585ecd6d951 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 15 Nov 2012 14:00:14 +0100 Subject: Add support for individual messages to MessagingMenuApp This is not exposed in the indicator menu yet. --- ...om.canonical.indicator.messages.application.xml | 12 ++ libmessaging-menu/messaging-menu-app.c | 144 ++++++++++++++++++++- libmessaging-menu/messaging-menu-message.c | 2 +- 3 files changed, 152 insertions(+), 6 deletions(-) (limited to 'libmessaging-menu') diff --git a/common/com.canonical.indicator.messages.application.xml b/common/com.canonical.indicator.messages.application.xml index ec095dd..33aed7c 100644 --- a/common/com.canonical.indicator.messages.application.xml +++ b/common/com.canonical.indicator.messages.application.xml @@ -4,9 +4,15 @@ + + + + + + @@ -17,5 +23,11 @@ + + + + + + diff --git a/libmessaging-menu/messaging-menu-app.c b/libmessaging-menu/messaging-menu-app.c index d793d6b..bc7e978 100644 --- a/libmessaging-menu/messaging-menu-app.c +++ b/libmessaging-menu/messaging-menu-app.c @@ -106,6 +106,7 @@ struct _MessagingMenuApp gboolean status_set; GDBusConnection *bus; + GHashTable *messages; GList *sources; IndicatorMessagesApplication *app_interface; @@ -125,6 +126,7 @@ enum { enum { ACTIVATE_SOURCE, + ACTIVATE_MESSAGE, STATUS_CHANGED, N_SIGNALS }; @@ -186,6 +188,29 @@ source_to_variant (Source *source) return v; } +static GVariant * +messaging_menu_message_to_variant (MessagingMenuMessage *message) +{ + GVariant *v; + GIcon *icon; + gchar *iconstr; + + icon = messaging_menu_message_get_icon (message); + iconstr = icon ? g_icon_to_string (icon) : NULL; + + v = g_variant_new ("(sssssxb)", messaging_menu_message_get_id (message), + iconstr ? iconstr : "", + messaging_menu_message_get_title (message), + messaging_menu_message_get_subtitle (message), + messaging_menu_message_get_body (message), + messaging_menu_message_get_time (message), + messaging_menu_message_get_draws_attention (message)); + + g_free (iconstr); + + return v; +} + static gchar * messaging_menu_app_get_dbus_object_path (MessagingMenuApp *app) { @@ -304,7 +329,10 @@ messaging_menu_app_dispose (GObject *object) g_clear_object (&app->messages_service); } + g_clear_pointer (&app->messages, g_hash_table_unref); + g_list_free_full (app->sources, source_free); + app->sources = NULL; g_clear_object (&app->app_interface); g_clear_object (&app->appinfo); @@ -357,6 +385,27 @@ messaging_menu_app_class_init (MessagingMenuAppClass *class) g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); + /** + * MessagingMenuApp::activate-message: + * @mmapp: the #MessagingMenuApp + * @message: the activated #MessagingMenuMessage + * + * Emitted when the user has activated a message. The message is + * immediately removed from the application's menu, handlers of this + * signal do not need to call messaging_menu_app_remove_message(). + * + * To get notified about the activation of a specific message, set the + * signal's detail to the message id. + */ + signals[ACTIVATE_MESSAGE] = g_signal_new ("activate-message", + MESSAGING_MENU_TYPE_APP, + G_SIGNAL_RUN_FIRST | + G_SIGNAL_DETAILED, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, MESSAGING_MENU_TYPE_MESSAGE); + /** * MessagingMenuApp::status-changed: * @mmapp: the #MessagingMenuApp @@ -487,6 +536,13 @@ messaging_menu_app_remove_source_internal (MessagingMenuApp *app, return FALSE; } +static gboolean +messaging_menu_app_remove_message_internal (MessagingMenuApp *app, + const gchar *message_id) +{ + return g_hash_table_remove (app->messages, message_id); +} + static gboolean messaging_menu_app_activate_source (IndicatorMessagesApplication *app_interface, GDBusMethodInvocation *invocation, @@ -496,7 +552,7 @@ messaging_menu_app_activate_source (IndicatorMessagesApplication *app_interface, MessagingMenuApp *app = user_data; GQuark q = g_quark_from_string (source_id); - /* Activate implies removing the source, no need for SourcesChanged */ + /* Activate implies removing the source, no need for SourceRemoved */ if (messaging_menu_app_remove_source_internal (app, source_id)) g_signal_emit (app, signals[ACTIVATE_SOURCE], q, source_id); @@ -505,6 +561,52 @@ messaging_menu_app_activate_source (IndicatorMessagesApplication *app_interface, return TRUE; } +static gboolean +messaging_menu_app_list_messages (IndicatorMessagesApplication *app_interface, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + MessagingMenuApp *app = user_data; + GVariantBuilder builder; + GHashTableIter iter; + MessagingMenuMessage *message; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sssssxb)")); + + g_hash_table_iter_init (&iter, app->messages); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &message)) + g_variant_builder_add_value (&builder, messaging_menu_message_to_variant (message)); + + indicator_messages_application_complete_list_messages (app_interface, + invocation, + g_variant_builder_end (&builder)); + + return TRUE; +} + +static gboolean +messaging_menu_app_activate_message (IndicatorMessagesApplication *app_interface, + GDBusMethodInvocation *invocation, + const gchar *message_id, + gpointer user_data) +{ + MessagingMenuApp *app = user_data; + MessagingMenuMessage *msg; + + msg = g_hash_table_lookup (app->messages, message_id); + if (msg) + { + g_signal_emit (app, signals[ACTIVATE_MESSAGE], g_quark_from_string (message_id), msg); + + /* Activate implies removing the message, no need for MessageRemoved */ + messaging_menu_app_remove_message_internal (app, message_id); + } + + indicator_messages_application_complete_activate_message (app_interface, invocation); + + return TRUE; +} + static void messaging_menu_app_init (MessagingMenuApp *app) { @@ -519,8 +621,12 @@ messaging_menu_app_init (MessagingMenuApp *app) G_CALLBACK (messaging_menu_app_list_sources), app); g_signal_connect (app->app_interface, "handle-activate-source", G_CALLBACK (messaging_menu_app_activate_source), app); + g_signal_connect (app->app_interface, "handle-list-messages", + G_CALLBACK (messaging_menu_app_list_messages), app); + g_signal_connect (app->app_interface, "handle-activate-message", + G_CALLBACK (messaging_menu_app_activate_message), app); - app->cancellable = g_cancellable_new (); + app->messages = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); app->watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION, "com.canonical.indicator.messages", @@ -1226,8 +1332,34 @@ messaging_menu_app_append_message (MessagingMenuApp *app, const gchar *source_id, gboolean notify) { + const gchar *id; + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); - g_return_if_fail (MESSAGING_MENU_IS_MESSAGE (app)); + g_return_if_fail (MESSAGING_MENU_IS_MESSAGE (msg)); + + id = messaging_menu_message_get_id (msg); + + if (g_hash_table_lookup (app->messages, id)) + { + g_warning ("a message with id '%s' already exists", id); + return; + } + + g_hash_table_insert (app->messages, g_strdup (id), g_object_ref (msg)); + indicator_messages_application_emit_message_added (app->app_interface, + messaging_menu_message_to_variant (msg)); + + if (source_id) + { + Source *source; + + source = messaging_menu_app_get_source (app, source_id); + if (source && source->count >= 0) + { + source->count++; + messaging_menu_app_notify_source_changed (app, source); + } + } } /** @@ -1244,8 +1376,7 @@ void messaging_menu_app_remove_message (MessagingMenuApp *app, MessagingMenuMessage *msg) { - g_return_if_fail (MESSAGING_MENU_IS_APP (app)); - g_return_if_fail (MESSAGING_MENU_IS_MESSAGE (app)); + messaging_menu_app_remove_message_by_id (app, messaging_menu_message_get_id (msg)); } /** @@ -1264,4 +1395,7 @@ messaging_menu_app_remove_message_by_id (MessagingMenuApp *app, { g_return_if_fail (MESSAGING_MENU_IS_APP (app)); g_return_if_fail (id != NULL); + + if (messaging_menu_app_remove_message_internal (app, id)) + indicator_messages_application_emit_source_removed (app->app_interface, id); } diff --git a/libmessaging-menu/messaging-menu-message.c b/libmessaging-menu/messaging-menu-message.c index 631786a..f5cb18c 100644 --- a/libmessaging-menu/messaging-menu-message.c +++ b/libmessaging-menu/messaging-menu-message.c @@ -225,7 +225,7 @@ messaging_menu_message_init (MessagingMenuMessage *self) /** * messaging_menu_message_new: * @id: unique id of the message - * @icon: (transfer full): a #GIcon representing the message + * @icon: (transfer full) (allow-none): a #GIcon representing the message * @title: the title of the message * @subtitle: (allow-none): the subtitle of the message * @body: (allow-none): the message body -- cgit v1.2.3 From 15f2eef7082f9a68b1511f0868cdd60558b3a4f6 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 20 Nov 2012 23:15:32 +0100 Subject: libmessaging-menu: emit the right signal when a message is removed --- libmessaging-menu/messaging-menu-app.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/messaging-menu-app.c b/libmessaging-menu/messaging-menu-app.c index bc7e978..61547c5 100644 --- a/libmessaging-menu/messaging-menu-app.c +++ b/libmessaging-menu/messaging-menu-app.c @@ -1397,5 +1397,5 @@ messaging_menu_app_remove_message_by_id (MessagingMenuApp *app, g_return_if_fail (id != NULL); if (messaging_menu_app_remove_message_internal (app, id)) - indicator_messages_application_emit_source_removed (app->app_interface, id); + indicator_messages_application_emit_message_removed (app->app_interface, id); } -- cgit v1.2.3 From 9287c2e6577e71d8f68d9c9e7d393a4f8524ab10 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Mon, 26 Nov 2012 20:12:13 +0100 Subject: Tell applications when messages and sources are dismissed Right now, this is only used to clean up internal data structures in libmessaging-menu. It's not exposed to the application itself. --- ...om.canonical.indicator.messages.application.xml | 4 +++ libmessaging-menu/messaging-menu-app.c | 21 +++++++++++++ src/im-application-list.c | 35 +++++++++++++++++----- 3 files changed, 53 insertions(+), 7 deletions(-) (limited to 'libmessaging-menu') diff --git a/common/com.canonical.indicator.messages.application.xml b/common/com.canonical.indicator.messages.application.xml index fb9f079..552b6a4 100644 --- a/common/com.canonical.indicator.messages.application.xml +++ b/common/com.canonical.indicator.messages.application.xml @@ -13,6 +13,10 @@ + + + + diff --git a/libmessaging-menu/messaging-menu-app.c b/libmessaging-menu/messaging-menu-app.c index 61547c5..d037da9 100644 --- a/libmessaging-menu/messaging-menu-app.c +++ b/libmessaging-menu/messaging-menu-app.c @@ -607,6 +607,25 @@ messaging_menu_app_activate_message (IndicatorMessagesApplication *app_interface return TRUE; } +static gboolean +messaging_menu_app_dismiss (IndicatorMessagesApplication *app_interface, + GDBusMethodInvocation *invocation, + const gchar * const *sources, + const gchar * const *messages, + gpointer user_data) +{ + MessagingMenuApp *app = user_data; + const gchar * const *it; + + for (it = sources; *it; it++) + messaging_menu_app_remove_source_internal (app, *it); + + for (it = messages; *it; it++) + messaging_menu_app_remove_message_internal (app, *it); + + return TRUE; +} + static void messaging_menu_app_init (MessagingMenuApp *app) { @@ -625,6 +644,8 @@ messaging_menu_app_init (MessagingMenuApp *app) G_CALLBACK (messaging_menu_app_list_messages), app); g_signal_connect (app->app_interface, "handle-activate-message", G_CALLBACK (messaging_menu_app_activate_message), app); + g_signal_connect (app->app_interface, "handle-dismiss", + G_CALLBACK (messaging_menu_app_dismiss), app); app->messages = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); diff --git a/src/im-application-list.c b/src/im-application-list.c index c3be647..d766f24 100644 --- a/src/im-application-list.c +++ b/src/im-application-list.c @@ -118,6 +118,13 @@ im_application_list_source_activated (GSimpleAction *action, app->cancellable, NULL, NULL); } + else + { + const gchar *sources[] = { source_id, NULL }; + const gchar *messages[] = { NULL }; + indicator_messages_application_call_dismiss (app->proxy, sources, messages, + app->cancellable, NULL, NULL); + } im_application_list_source_removed (app, source_id); } @@ -148,6 +155,13 @@ im_application_list_message_activated (GSimpleAction *action, app->cancellable, NULL, NULL); } + else + { + const gchar *sources[] = { NULL }; + const gchar *messages[] = { message_id, NULL }; + indicator_messages_application_call_dismiss (app->proxy, sources, messages, + app->cancellable, NULL, NULL); + } im_application_list_message_removed (app, message_id); } @@ -164,18 +178,25 @@ im_application_list_remove_all (GSimpleAction *action, g_hash_table_iter_init (&iter, list->applications); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &app)) { - gchar **actions; + gchar **source_actions; + gchar **message_actions; gchar **it; - actions = g_action_group_list_actions (G_ACTION_GROUP (app->source_actions)); - for (it = actions; *it; it++) + source_actions = g_action_group_list_actions (G_ACTION_GROUP (app->source_actions)); + for (it = source_actions; *it; it++) im_application_list_source_removed (app, *it); - g_strfreev (actions); - actions = g_action_group_list_actions (G_ACTION_GROUP (app->message_actions)); - for (it = actions; *it; it++) + message_actions = g_action_group_list_actions (G_ACTION_GROUP (app->message_actions)); + for (it = message_actions; *it; it++) im_application_list_message_removed (app, *it); - g_strfreev (actions); + + indicator_messages_application_call_dismiss (app->proxy, + (const gchar * const *) source_actions, + (const gchar * const *) message_actions, + app->cancellable, NULL, NULL); + + g_strfreev (source_actions); + g_strfreev (message_actions); } } -- cgit v1.2.3 From e8e99703ef0c565f07a9d1eba03c1003d82ff697 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 27 Nov 2012 15:14:26 +0100 Subject: Allow adding actions to MessagingMenuMessage --- ...om.canonical.indicator.messages.application.xml | 4 +- libmessaging-menu/messaging-menu-app.c | 28 +--- libmessaging-menu/messaging-menu-message.c | 160 +++++++++++++++++++++ libmessaging-menu/messaging-menu-message.h | 6 + src/im-application-list.c | 6 +- 5 files changed, 176 insertions(+), 28 deletions(-) (limited to 'libmessaging-menu') diff --git a/common/com.canonical.indicator.messages.application.xml b/common/com.canonical.indicator.messages.application.xml index 552b6a4..f37cd25 100644 --- a/common/com.canonical.indicator.messages.application.xml +++ b/common/com.canonical.indicator.messages.application.xml @@ -5,7 +5,7 @@ - + @@ -28,7 +28,7 @@ - + diff --git a/libmessaging-menu/messaging-menu-app.c b/libmessaging-menu/messaging-menu-app.c index d037da9..491c123 100644 --- a/libmessaging-menu/messaging-menu-app.c +++ b/libmessaging-menu/messaging-menu-app.c @@ -152,6 +152,9 @@ static void global_status_changed (IndicatorMessagesService *service, const gchar *status_str, gpointer user_data); +/* in messaging-menu-message.c */ +GVariant * messaging_menu_message_to_variant (MessagingMenuMessage *msg); + static void source_free (gpointer data) { @@ -188,29 +191,6 @@ source_to_variant (Source *source) return v; } -static GVariant * -messaging_menu_message_to_variant (MessagingMenuMessage *message) -{ - GVariant *v; - GIcon *icon; - gchar *iconstr; - - icon = messaging_menu_message_get_icon (message); - iconstr = icon ? g_icon_to_string (icon) : NULL; - - v = g_variant_new ("(sssssxb)", messaging_menu_message_get_id (message), - iconstr ? iconstr : "", - messaging_menu_message_get_title (message), - messaging_menu_message_get_subtitle (message), - messaging_menu_message_get_body (message), - messaging_menu_message_get_time (message), - messaging_menu_message_get_draws_attention (message)); - - g_free (iconstr); - - return v; -} - static gchar * messaging_menu_app_get_dbus_object_path (MessagingMenuApp *app) { @@ -571,7 +551,7 @@ messaging_menu_app_list_messages (IndicatorMessagesApplication *app_interface, GHashTableIter iter; MessagingMenuMessage *message; - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sssssxb)")); + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sssssxa(ssgav)b)")); g_hash_table_iter_init (&iter, app->messages); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &message)) diff --git a/libmessaging-menu/messaging-menu-message.c b/libmessaging-menu/messaging-menu-message.c index f5cb18c..cf050d3 100644 --- a/libmessaging-menu/messaging-menu-message.c +++ b/libmessaging-menu/messaging-menu-message.c @@ -32,6 +32,8 @@ struct _MessagingMenuMessage gchar *body; gint64 time; gboolean draws_attention; + + GSList *actions; }; G_DEFINE_TYPE (MessagingMenuMessage, messaging_menu_message, G_TYPE_OBJECT); @@ -51,6 +53,31 @@ enum static GParamSpec *properties[NUM_PROPERTIES]; +typedef struct +{ + gchar *id; + gchar *label; + GVariantType *parameter_type; + GVariant *parameter_hint; +} Action; + +static void +action_free (gpointer data) +{ + Action *action = data; + + g_free (action->id); + g_free (action->label); + + if (action->parameter_type) + g_variant_type_free (action->parameter_type); + + if (action->parameter_hint) + g_variant_unref (action->parameter_hint); + + g_slice_free (Action, action); +} + static void messaging_menu_message_dispose (GObject *object) { @@ -71,6 +98,9 @@ messaging_menu_message_finalize (GObject *object) g_free (msg->subtitle); g_free (msg->body); + g_slist_free_full (msg->actions, action_free); + msg->actions = NULL; + G_OBJECT_CLASS (messaging_menu_message_parent_class)->finalize (object); } @@ -370,3 +400,133 @@ messaging_menu_message_set_draws_attention (MessagingMenuMessage *msg, msg->draws_attention = draws_attention; g_object_notify_by_pspec (G_OBJECT (msg), properties[PROP_DRAWS_ATTENTION]); } + +/** + * messaging_menu_message_add_action: + * @msg: a #MessagingMenuMessage + * @id: unique id of the action + * @label: (allow-none): label of the action + * @parameter_type: (allow-none): a #GVariantType + * @parameter_hint: (allow-none): a #GVariant suggesting a valid range + * for parameters + * + * Adds an action with @id and @label to @message. Actions are an + * alternative way for users to activate a message. Note that messages + * can still be activated without an action. + * + * If @parameter_type is non-%NULL, the action is able to receive user + * input in addition to simply activating the action. Currently, only + * string parameters are supported. + * + * A list of predefined parameters can be supplied as a #GVariant array + * of @parameter_type in @parameter_hint. If @parameter_hint is + * floating, it will be consumed. + * + * It is recommended to add at most two actions to a message. + */ +void +messaging_menu_message_add_action (MessagingMenuMessage *msg, + const gchar *id, + const gchar *label, + const GVariantType *parameter_type, + GVariant *parameter_hint) +{ + Action *action; + + g_return_if_fail (MESSAGING_MENU_IS_MESSAGE (msg)); + g_return_if_fail (id != NULL); + + action = g_slice_new (Action); + action->id = g_strdup (id); + action->label = g_strdup (label); + action->parameter_type = parameter_type ? g_variant_type_copy (parameter_type) : NULL; + action->parameter_hint = parameter_hint ? g_variant_ref_sink (parameter_hint) : NULL; + + msg->actions = g_slist_append (msg->actions, action); +} + +static GVariant * +action_to_variant (Action *action) +{ + GVariantBuilder builder; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("(ssgav)")); + + g_variant_builder_add (&builder, "s", action->id); + g_variant_builder_add (&builder, "s", action->label ? action->label : ""); + + if (action->parameter_type) + { + gchar *type = g_variant_type_dup_string (action->parameter_type); + g_variant_builder_add (&builder, "g", type); + g_free (type); + } + else + g_variant_builder_add (&builder, "g", ""); + + g_variant_builder_open (&builder, G_VARIANT_TYPE ("av")); + if (action->parameter_hint) + g_variant_builder_add (&builder, "v", action->parameter_hint); + g_variant_builder_close (&builder); + + return g_variant_builder_end (&builder); +} + +/* + * messaging_menu_message_to_variant: + * @msg: a #MessagingMenuMessage + * + * Serializes @msg to a #GVariant of the form (sssssxa(ssgav)b): + * + * id + * icon + * title + * subtitle + * body + * time + * array of actions: id + * label + * parameter_type + * parameter_hint (0 or 1 elements) + * draws_attention + * + * Returns: a new floating #GVariant instance + */ +GVariant * +messaging_menu_message_to_variant (MessagingMenuMessage *msg) +{ + GVariantBuilder builder; + GSList *it; + + g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), NULL); + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("(sssssxa(ssgav)b)")); + + g_variant_builder_add (&builder, "s", msg->id); + + if (msg->icon) + { + gchar *iconstr; + + iconstr = g_icon_to_string (msg->icon); + g_variant_builder_add (&builder, "s", iconstr); + + g_free (iconstr); + } + else + g_variant_builder_add (&builder, "s", ""); + + g_variant_builder_add (&builder, "s", msg->title ? msg->title : ""); + g_variant_builder_add (&builder, "s", msg->subtitle ? msg->subtitle : ""); + g_variant_builder_add (&builder, "s", msg->body ? msg->body : ""); + g_variant_builder_add (&builder, "x", msg->time); + + g_variant_builder_open (&builder, G_VARIANT_TYPE ("a(ssgav)")); + for (it = msg->actions; it; it = it->next) + g_variant_builder_add_value (&builder, action_to_variant (it->data)); + g_variant_builder_close (&builder); + + g_variant_builder_add (&builder, "b", msg->draws_attention); + + return g_variant_builder_end (&builder); +} diff --git a/libmessaging-menu/messaging-menu-message.h b/libmessaging-menu/messaging-menu-message.h index 068247b..4708246 100644 --- a/libmessaging-menu/messaging-menu-message.h +++ b/libmessaging-menu/messaging-menu-message.h @@ -59,6 +59,12 @@ gboolean messaging_menu_message_get_draws_attention (MessagingMe void messaging_menu_message_set_draws_attention (MessagingMenuMessage *msg, gboolean draws_attention); +void messaging_menu_message_add_action (MessagingMenuMessage *msg, + const gchar *id, + const gchar *label, + const GVariantType *parameter_type, + GVariant *parameter_hint); + G_END_DECLS #endif diff --git a/src/im-application-list.c b/src/im-application-list.c index d766f24..4eaaed0 100644 --- a/src/im-application-list.c +++ b/src/im-application-list.c @@ -509,13 +509,14 @@ im_application_list_message_added (Application *app, const gchar *subtitle; const gchar *body; gint64 time; + GVariantIter *action_iter; gboolean draws_attention; GSimpleAction *action; GIcon *app_icon; gchar *app_iconstr; - g_variant_get (message, "(&s&s&s&s&sxb)", - &id, &iconstr, &title, &subtitle, &body, &time, &draws_attention); + g_variant_get (message, "(&s&s&s&s&sxa(ssgav)b)", + &id, &iconstr, &title, &subtitle, &body, &time, &action_iter, &draws_attention); app_icon = g_app_info_get_icon (G_APP_INFO (app->info)); app_iconstr = app_icon ? g_icon_to_string (app_icon) : NULL; @@ -528,6 +529,7 @@ im_application_list_message_added (Application *app, g_signal_emit (app->list, signals[MESSAGE_ADDED], 0, app->id, app_iconstr, id, iconstr, title, subtitle, body, time, draws_attention); + g_variant_iter_free (action_iter); g_free (app_iconstr); g_object_unref (action); } -- cgit v1.2.3 From 7c7c408e9fe4e1cddedae9a6882c714ac3e161d3 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 27 Nov 2012 16:18:18 +0100 Subject: Move 'activate' signal into MessagingMenuMessage And add parameters 'action' and 'parameter' (though they are not set yet). --- libmessaging-menu/messaging-menu-app.c | 24 +----------------------- libmessaging-menu/messaging-menu-message.c | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 23 deletions(-) (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/messaging-menu-app.c b/libmessaging-menu/messaging-menu-app.c index 491c123..cea0eb9 100644 --- a/libmessaging-menu/messaging-menu-app.c +++ b/libmessaging-menu/messaging-menu-app.c @@ -126,7 +126,6 @@ enum { enum { ACTIVATE_SOURCE, - ACTIVATE_MESSAGE, STATUS_CHANGED, N_SIGNALS }; @@ -365,27 +364,6 @@ messaging_menu_app_class_init (MessagingMenuAppClass *class) g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); - /** - * MessagingMenuApp::activate-message: - * @mmapp: the #MessagingMenuApp - * @message: the activated #MessagingMenuMessage - * - * Emitted when the user has activated a message. The message is - * immediately removed from the application's menu, handlers of this - * signal do not need to call messaging_menu_app_remove_message(). - * - * To get notified about the activation of a specific message, set the - * signal's detail to the message id. - */ - signals[ACTIVATE_MESSAGE] = g_signal_new ("activate-message", - MESSAGING_MENU_TYPE_APP, - G_SIGNAL_RUN_FIRST | - G_SIGNAL_DETAILED, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, MESSAGING_MENU_TYPE_MESSAGE); - /** * MessagingMenuApp::status-changed: * @mmapp: the #MessagingMenuApp @@ -576,7 +554,7 @@ messaging_menu_app_activate_message (IndicatorMessagesApplication *app_interface msg = g_hash_table_lookup (app->messages, message_id); if (msg) { - g_signal_emit (app, signals[ACTIVATE_MESSAGE], g_quark_from_string (message_id), msg); + g_signal_emit_by_name (msg, "activate", NULL, NULL); /* Activate implies removing the message, no need for MessageRemoved */ messaging_menu_app_remove_message_internal (app, message_id); diff --git a/libmessaging-menu/messaging-menu-message.c b/libmessaging-menu/messaging-menu-message.c index cf050d3..a17cdb6 100644 --- a/libmessaging-menu/messaging-menu-message.c +++ b/libmessaging-menu/messaging-menu-message.c @@ -245,6 +245,26 @@ messaging_menu_message_class_init (MessagingMenuMessageClass *klass) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (klass, NUM_PROPERTIES, properties); + + /** + * MessagingMenuMessage::activate: + * @msg: the #MessagingMenuMessage + * @action: (allow-none): the id of activated action, or %NULL + * @parameter: (allow-none): activation parameter, or %NULL + * + * Emitted when the user has activated the message. The message is + * immediately removed from the application's menu, handlers of this + * signal do not need to call messaging_menu_app_remove_message(). + */ + g_signal_new ("activate", + MESSAGING_MENU_TYPE_MESSAGE, + G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, 2, + G_TYPE_STRING, + G_TYPE_VARIANT); } static void -- cgit v1.2.3 From ced173aa7ea78c76922e07c84743d5b7f76c265e Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 27 Nov 2012 16:40:38 +0100 Subject: Pass message action and its parameter through to the application --- ...om.canonical.indicator.messages.application.xml | 2 ++ libmessaging-menu/messaging-menu-app.c | 23 +++++++++++++++++++++- src/im-application-list.c | 2 ++ 3 files changed, 26 insertions(+), 1 deletion(-) (limited to 'libmessaging-menu') diff --git a/common/com.canonical.indicator.messages.application.xml b/common/com.canonical.indicator.messages.application.xml index f37cd25..5e99780 100644 --- a/common/com.canonical.indicator.messages.application.xml +++ b/common/com.canonical.indicator.messages.application.xml @@ -12,6 +12,8 @@ + + diff --git a/libmessaging-menu/messaging-menu-app.c b/libmessaging-menu/messaging-menu-app.c index cea0eb9..8b9f76b 100644 --- a/libmessaging-menu/messaging-menu-app.c +++ b/libmessaging-menu/messaging-menu-app.c @@ -546,6 +546,8 @@ static gboolean messaging_menu_app_activate_message (IndicatorMessagesApplication *app_interface, GDBusMethodInvocation *invocation, const gchar *message_id, + const gchar *action_id, + GVariant *params, gpointer user_data) { MessagingMenuApp *app = user_data; @@ -554,7 +556,26 @@ messaging_menu_app_activate_message (IndicatorMessagesApplication *app_interface msg = g_hash_table_lookup (app->messages, message_id); if (msg) { - g_signal_emit_by_name (msg, "activate", NULL, NULL); + if (*action_id) + { + gchar *signal; + + signal = g_strconcat ("activate::", action_id, NULL); + + if (g_variant_n_children (params)) + { + GVariant *param = g_variant_get_child_value (params, 0); + g_signal_emit_by_name (msg, signal, action_id, param); + g_variant_unref (param); + } + else + g_signal_emit_by_name (msg, signal, action_id, NULL); + + g_free (signal); + } + else + g_signal_emit_by_name (msg, "activate", NULL, NULL); + /* Activate implies removing the message, no need for MessageRemoved */ messaging_menu_app_remove_message_internal (app, message_id); diff --git a/src/im-application-list.c b/src/im-application-list.c index 4eaaed0..7c53e69 100644 --- a/src/im-application-list.c +++ b/src/im-application-list.c @@ -152,6 +152,8 @@ im_application_list_message_activated (GSimpleAction *action, { indicator_messages_application_call_activate_message (app->proxy, message_id, + "", + g_variant_new_array (G_VARIANT_TYPE_VARIANT, NULL, 0), app->cancellable, NULL, NULL); } -- cgit v1.2.3 From 235a7edc5c2fc2435c837f8a3d427dd0755f966d Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 28 Nov 2012 17:24:31 +0100 Subject: Use aa{sv} instead of a(ssgav) to communicate actions to the service --- ...om.canonical.indicator.messages.application.xml | 4 ++-- libmessaging-menu/messaging-menu-app.c | 2 +- libmessaging-menu/messaging-menu-message.c | 27 +++++++++------------- src/im-application-list.c | 8 +++---- 4 files changed, 18 insertions(+), 23 deletions(-) (limited to 'libmessaging-menu') diff --git a/common/com.canonical.indicator.messages.application.xml b/common/com.canonical.indicator.messages.application.xml index 5e99780..6f038e6 100644 --- a/common/com.canonical.indicator.messages.application.xml +++ b/common/com.canonical.indicator.messages.application.xml @@ -5,7 +5,7 @@ - + @@ -30,7 +30,7 @@ - + diff --git a/libmessaging-menu/messaging-menu-app.c b/libmessaging-menu/messaging-menu-app.c index 8b9f76b..3747705 100644 --- a/libmessaging-menu/messaging-menu-app.c +++ b/libmessaging-menu/messaging-menu-app.c @@ -529,7 +529,7 @@ messaging_menu_app_list_messages (IndicatorMessagesApplication *app_interface, GHashTableIter iter; MessagingMenuMessage *message; - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sssssxa(ssgav)b)")); + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sssssxaa{sv}b)")); g_hash_table_iter_init (&iter, app->messages); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &message)) diff --git a/libmessaging-menu/messaging-menu-message.c b/libmessaging-menu/messaging-menu-message.c index a17cdb6..886c552 100644 --- a/libmessaging-menu/messaging-menu-message.c +++ b/libmessaging-menu/messaging-menu-message.c @@ -470,24 +470,22 @@ action_to_variant (Action *action) { GVariantBuilder builder; - g_variant_builder_init (&builder, G_VARIANT_TYPE ("(ssgav)")); + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); - g_variant_builder_add (&builder, "s", action->id); - g_variant_builder_add (&builder, "s", action->label ? action->label : ""); + g_variant_builder_add (&builder, "{sv}", "name", g_variant_new_string (action->id)); + + if (action->label) + g_variant_builder_add (&builder, "{sv}", "label", g_variant_new_string (action->label)); if (action->parameter_type) { gchar *type = g_variant_type_dup_string (action->parameter_type); - g_variant_builder_add (&builder, "g", type); + g_variant_builder_add (&builder, "{sv}", "parameter-type", g_variant_new_signature (type)); g_free (type); } - else - g_variant_builder_add (&builder, "g", ""); - g_variant_builder_open (&builder, G_VARIANT_TYPE ("av")); if (action->parameter_hint) - g_variant_builder_add (&builder, "v", action->parameter_hint); - g_variant_builder_close (&builder); + g_variant_builder_add (&builder, "{sv}", "parameter-hint", action->parameter_hint); return g_variant_builder_end (&builder); } @@ -496,7 +494,7 @@ action_to_variant (Action *action) * messaging_menu_message_to_variant: * @msg: a #MessagingMenuMessage * - * Serializes @msg to a #GVariant of the form (sssssxa(ssgav)b): + * Serializes @msg to a #GVariant of the form (sssssxaa{sv}b): * * id * icon @@ -504,10 +502,7 @@ action_to_variant (Action *action) * subtitle * body * time - * array of actions: id - * label - * parameter_type - * parameter_hint (0 or 1 elements) + * array of action dictionaries * draws_attention * * Returns: a new floating #GVariant instance @@ -520,7 +515,7 @@ messaging_menu_message_to_variant (MessagingMenuMessage *msg) g_return_val_if_fail (MESSAGING_MENU_IS_MESSAGE (msg), NULL); - g_variant_builder_init (&builder, G_VARIANT_TYPE ("(sssssxa(ssgav)b)")); + g_variant_builder_init (&builder, G_VARIANT_TYPE ("(sssssxaa{sv}b)")); g_variant_builder_add (&builder, "s", msg->id); @@ -541,7 +536,7 @@ messaging_menu_message_to_variant (MessagingMenuMessage *msg) g_variant_builder_add (&builder, "s", msg->body ? msg->body : ""); g_variant_builder_add (&builder, "x", msg->time); - g_variant_builder_open (&builder, G_VARIANT_TYPE ("a(ssgav)")); + g_variant_builder_open (&builder, G_VARIANT_TYPE ("aa{sv}")); for (it = msg->actions; it; it = it->next) g_variant_builder_add_value (&builder, action_to_variant (it->data)); g_variant_builder_close (&builder); diff --git a/src/im-application-list.c b/src/im-application-list.c index 7c53e69..1f901ec 100644 --- a/src/im-application-list.c +++ b/src/im-application-list.c @@ -511,14 +511,14 @@ im_application_list_message_added (Application *app, const gchar *subtitle; const gchar *body; gint64 time; - GVariantIter *action_iter; + GVariant *actions; gboolean draws_attention; GSimpleAction *action; GIcon *app_icon; gchar *app_iconstr; - g_variant_get (message, "(&s&s&s&s&sxa(ssgav)b)", - &id, &iconstr, &title, &subtitle, &body, &time, &action_iter, &draws_attention); + g_variant_get (message, "(&s&s&s&s&sx@aa{sv}b)", + &id, &iconstr, &title, &subtitle, &body, &time, &actions, &draws_attention); app_icon = g_app_info_get_icon (G_APP_INFO (app->info)); app_iconstr = app_icon ? g_icon_to_string (app_icon) : NULL; @@ -531,7 +531,7 @@ im_application_list_message_added (Application *app, g_signal_emit (app->list, signals[MESSAGE_ADDED], 0, app->id, app_iconstr, id, iconstr, title, subtitle, body, time, draws_attention); - g_variant_iter_free (action_iter); + g_variant_unref (actions); g_free (app_iconstr); g_object_unref (action); } -- cgit v1.2.3 From 1d9ee577e7950edfc58f74888167ed2ace39e4e5 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Fri, 30 Nov 2012 18:47:20 +0100 Subject: Don't export the messaging_menu_message_to_variant symbol --- libmessaging-menu/messaging-menu-app.c | 6 +++--- libmessaging-menu/messaging-menu-message.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/messaging-menu-app.c b/libmessaging-menu/messaging-menu-app.c index 3747705..7e61324 100644 --- a/libmessaging-menu/messaging-menu-app.c +++ b/libmessaging-menu/messaging-menu-app.c @@ -152,7 +152,7 @@ static void global_status_changed (IndicatorMessagesService *service, gpointer user_data); /* in messaging-menu-message.c */ -GVariant * messaging_menu_message_to_variant (MessagingMenuMessage *msg); +GVariant * _messaging_menu_message_to_variant (MessagingMenuMessage *msg); static void source_free (gpointer data) @@ -533,7 +533,7 @@ messaging_menu_app_list_messages (IndicatorMessagesApplication *app_interface, g_hash_table_iter_init (&iter, app->messages); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &message)) - g_variant_builder_add_value (&builder, messaging_menu_message_to_variant (message)); + g_variant_builder_add_value (&builder, _messaging_menu_message_to_variant (message)); indicator_messages_application_complete_list_messages (app_interface, invocation, @@ -1347,7 +1347,7 @@ messaging_menu_app_append_message (MessagingMenuApp *app, g_hash_table_insert (app->messages, g_strdup (id), g_object_ref (msg)); indicator_messages_application_emit_message_added (app->app_interface, - messaging_menu_message_to_variant (msg)); + _messaging_menu_message_to_variant (msg)); if (source_id) { diff --git a/libmessaging-menu/messaging-menu-message.c b/libmessaging-menu/messaging-menu-message.c index 886c552..b81cbea 100644 --- a/libmessaging-menu/messaging-menu-message.c +++ b/libmessaging-menu/messaging-menu-message.c @@ -491,7 +491,7 @@ action_to_variant (Action *action) } /* - * messaging_menu_message_to_variant: + * _messaging_menu_message_to_variant: * @msg: a #MessagingMenuMessage * * Serializes @msg to a #GVariant of the form (sssssxaa{sv}b): @@ -508,7 +508,7 @@ action_to_variant (Action *action) * Returns: a new floating #GVariant instance */ GVariant * -messaging_menu_message_to_variant (MessagingMenuMessage *msg) +_messaging_menu_message_to_variant (MessagingMenuMessage *msg) { GVariantBuilder builder; GSList *it; -- cgit v1.2.3 From 3a1cc9edfe053851fe205aec6632101b7de05e41 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 4 Dec 2012 21:04:03 +0000 Subject: Add messaging_menu_app_get_message --- libmessaging-menu/messaging-menu-app.c | 21 +++++++++++++++++++++ libmessaging-menu/messaging-menu-app.h | 3 +++ 2 files changed, 24 insertions(+) (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/messaging-menu-app.c b/libmessaging-menu/messaging-menu-app.c index 7e61324..7fc9106 100644 --- a/libmessaging-menu/messaging-menu-app.c +++ b/libmessaging-menu/messaging-menu-app.c @@ -1362,6 +1362,27 @@ messaging_menu_app_append_message (MessagingMenuApp *app, } } +/** + * messaging_menu_app_get_message: + * @app: a #MessagingMenuApp + * @id: id of the message to retrieve + * + * Retrieves the message with @id, that was added with + * messaging_menu_app_append_message(). + * + * Returns: (transfer none) (allow-none): the #MessagingMenuApp with + * @id, or %NULL + */ +MessagingMenuMessage * +messaging_menu_app_get_message (MessagingMenuApp *app, + const gchar *id) +{ + g_return_val_if_fail (MESSAGING_MENU_IS_APP (app), NULL); + g_return_val_if_fail (id != NULL, NULL); + + return g_hash_table_lookup (app->messages, id); +} + /** * messaging_menu_app_remove_message: * @app: a #MessagingMenuApp diff --git a/libmessaging-menu/messaging-menu-app.h b/libmessaging-menu/messaging-menu-app.h index a2d27bc..c8097e1 100644 --- a/libmessaging-menu/messaging-menu-app.h +++ b/libmessaging-menu/messaging-menu-app.h @@ -149,6 +149,9 @@ void messaging_menu_app_append_message (MessagingMenuA const gchar *source_id, gboolean notify); +MessagingMenuMessage * messaging_menu_app_get_message (MessagingMenuApp *app, + const gchar *id); + void messaging_menu_app_remove_message (MessagingMenuApp *app, MessagingMenuMessage *msg); -- cgit v1.2.3 From b1671dd0ebee569741d176edf042aa7df4dc5a95 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 11 Dec 2012 14:26:56 +0100 Subject: Remove variant wrapper from 'parameter' argument of the "activate" signal --- libmessaging-menu/messaging-menu-app.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/messaging-menu-app.c b/libmessaging-menu/messaging-menu-app.c index 7fc9106..8352a4f 100644 --- a/libmessaging-menu/messaging-menu-app.c +++ b/libmessaging-menu/messaging-menu-app.c @@ -564,8 +564,11 @@ messaging_menu_app_activate_message (IndicatorMessagesApplication *app_interface if (g_variant_n_children (params)) { - GVariant *param = g_variant_get_child_value (params, 0); + GVariant *param; + + g_variant_get_child (params, 0, "v", ¶m); g_signal_emit_by_name (msg, signal, action_id, param); + g_variant_unref (param); } else -- cgit v1.2.3 From 871848b6e856244b3396999cf07e24d2397142e5 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Fri, 14 Dec 2012 19:03:23 +0100 Subject: Make messaging_menu_app_remove_message() work for messages with a ref count of 1 This can happen if noone outside of MessagingMenuApp holds a reference to the message and somebody calls remove_message() with a pointer they got from _get_message() (which doesn't return a ref). messaging_menu_app_remove_message_by_id() really wants a valid 'id' pointer during its lifetime. --- libmessaging-menu/messaging-menu-app.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/messaging-menu-app.c b/libmessaging-menu/messaging-menu-app.c index 7fc9106..a0180cb 100644 --- a/libmessaging-menu/messaging-menu-app.c +++ b/libmessaging-menu/messaging-menu-app.c @@ -1397,7 +1397,11 @@ void messaging_menu_app_remove_message (MessagingMenuApp *app, MessagingMenuMessage *msg) { + /* take a ref of @msg here to make sure the pointer returned by + * _get_id() is valid for the duration of remove_message_by_id. */ + g_object_ref (msg); messaging_menu_app_remove_message_by_id (app, messaging_menu_message_get_id (msg)); + g_object_unref (msg); } /** -- cgit v1.2.3 From 5b0ea10ca057ffe594c17e8f96b08c5293baa570 Mon Sep 17 00:00:00 2001 From: Ken VanDine Date: Thu, 11 Jul 2013 16:13:37 -0400 Subject: generate vapi --- autogen.sh | 14 ++++++++++---- configure.ac | 17 +++++++++++++++++ libmessaging-menu/Makefile.am | 18 ++++++++++++++++++ 3 files changed, 45 insertions(+), 4 deletions(-) (limited to 'libmessaging-menu') diff --git a/autogen.sh b/autogen.sh index ab2f513..1abf3a7 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,5 +1,11 @@ -#!/bin/sh -e +#!/bin/sh -gtkdocize -autoreconf -i -f -intltoolize +PKG_NAME="indicator-messages" + +which gnome-autogen.sh || { + echo "You need gnome-common from GNOME SVN" + exit 1 +} + +USE_GNOME2_MACROS=1 \ +. gnome-autogen.sh diff --git a/configure.ac b/configure.ac index b020d30..b699b4f 100644 --- a/configure.ac +++ b/configure.ac @@ -122,6 +122,22 @@ else fi AC_SUBST(DBUSSERVICEDIR) +########################### +# Vala API Generation +########################### + +AC_ARG_ENABLE([vala], + AC_HELP_STRING([--disable-vala], [Disable vala]), + [enable_vala=$enableval], [enable_vala=yes]) + +AS_IF([test "x$enable_vala" != "xno"],[ + AM_COND_IF([HAVE_INTROSPECTION],,[ + AC_MSG_ERROR([Vala bindings require introspection support, please --enable-introspection]) + ]) +AC_PATH_PROG([VALA_API_GEN], [vapigen]) +]) +AM_CONDITIONAL([HAVE_VALA], [test -n "$VALA_API_GEN"]) + ############################## # Custom Junk ############################## @@ -197,5 +213,6 @@ Messaging Indicator Configuration: gtest: $enable_tests gcov: $use_gcov introspecion: $enable_introspection + Vala bindings: $enable_vala documentation: $enable_gtk_doc ]) diff --git a/libmessaging-menu/Makefile.am b/libmessaging-menu/Makefile.am index d18538b..4840582 100644 --- a/libmessaging-menu/Makefile.am +++ b/libmessaging-menu/Makefile.am @@ -56,4 +56,22 @@ typelibdir = $(libdir)/girepository-1.0 typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) CLEANFILES = $(gir_DATA) $(typelib_DATA) + + +######################### +# VAPI Files +######################### + +if HAVE_VALA + +vapidir = $(datadir)/vala/vapi +vapi_DATA = MessagingMenu-1.0.vapi + +MessagingMenu-1.0.vapi: MessagingMenu-1.0.gir + $(VALA_API_GEN) --library=MessagingMenu-1.0 $< + +CLEANFILES += $(vapi_DATA) + +endif + endif -- cgit v1.2.3 From 1d280a9153c569e96354e75398f83390db99c68a Mon Sep 17 00:00:00 2001 From: Ken VanDine Date: Fri, 12 Jul 2013 11:10:10 -0400 Subject: added gio-2.0 to vapigen --- libmessaging-menu/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/Makefile.am b/libmessaging-menu/Makefile.am index 4840582..1042d9f 100644 --- a/libmessaging-menu/Makefile.am +++ b/libmessaging-menu/Makefile.am @@ -68,7 +68,7 @@ vapidir = $(datadir)/vala/vapi vapi_DATA = MessagingMenu-1.0.vapi MessagingMenu-1.0.vapi: MessagingMenu-1.0.gir - $(VALA_API_GEN) --library=MessagingMenu-1.0 $< + $(VALA_API_GEN) --pkg gio-2.0 --library=MessagingMenu-1.0 $< CLEANFILES += $(vapi_DATA) -- cgit v1.2.3