From 78edb4ee5c8a585986fa092ee4ec81fad442f64f Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Fri, 15 Jun 2012 14:59:15 +0200 Subject: Add first version of a libmessaging-menu client library --- libmessaging-menu/Makefile.am | 35 ++ libmessaging-menu/messaging-menu.c | 746 +++++++++++++++++++++++++++++++++ libmessaging-menu/messaging-menu.h | 130 ++++++ libmessaging-menu/messaging-menu.pc.in | 11 + 4 files changed, 922 insertions(+) create mode 100644 libmessaging-menu/Makefile.am create mode 100644 libmessaging-menu/messaging-menu.c create mode 100644 libmessaging-menu/messaging-menu.h create mode 100644 libmessaging-menu/messaging-menu.pc.in (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/Makefile.am b/libmessaging-menu/Makefile.am new file mode 100644 index 0000000..a0da51e --- /dev/null +++ b/libmessaging-menu/Makefile.am @@ -0,0 +1,35 @@ + +lib_LTLIBRARIES = libmessaging-menu.la + +libmessaging_menu_ladir = $(includedir)/messaging-menu + +libmessaging_menu_la_SOURCES = \ + messaging-menu.c \ + $(BUILT_SOURCES) + +libmessaging_menu_la_HEADERS = \ + messaging-menu.h + +libmessaging_menu_la_LIBADD = $(GIO_LIBS) + +libmessaging_menu_la_CFLAGS = \ + $(GIO_CFLAGS) \ + -Wall + +BUILT_SOURCES = \ + indicator-messages-service.c \ + indicator-messages-service.h + +CLEANFILES = $(BUILT_SOURCES) + +indicator-messages-service.c: $(top_srcdir)/src/messages-service.xml + $(AM_V_GEN) gdbus-codegen \ + --interface-prefix com.canonical.indicator.messages. \ + --generate-c-code indicator-messages-service \ + --c-namespace IndicatorMessages \ + $^ +indicator-messages-service.h: indicator-messages-service.c + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = messaging-menu.pc + diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c new file mode 100644 index 0000000..5b786af --- /dev/null +++ b/libmessaging-menu/messaging-menu.c @@ -0,0 +1,746 @@ +/* + * Copyright 2012 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Lars Uebernickel + */ + +#include "messaging-menu.h" +#include "indicator-messages-service.h" + +#include + +/** + * SECTION:messagingmenuapp + * @title: MessagingMenuApp + * @short_description: An application section in the messaging menu + */ +struct _MessagingMenuApp +{ + GObject parent_instance; + + GDesktopAppInfo *appinfo; + int registered; /* -1 for unknown */ + MessagingMenuStatus status; + GSimpleActionGroup *source_actions; + GMenu *menu; + + IndicatorMessagesService *messages_service; + + GCancellable *cancellable; +}; + +G_DEFINE_TYPE (MessagingMenuApp, messaging_menu_app, G_TYPE_OBJECT); + +enum { + PROP_0, + PROP_DESKTOP_ID, + N_PROPERTIES +}; + +enum { + ACTIVATE_SOURCE, + N_SIGNALS +}; + +static GParamSpec *properties[N_PROPERTIES]; +static guint signals[N_SIGNALS]; + +static void +messaging_menu_app_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MessagingMenuApp *app = MESSAGING_MENU_APP (object); + + switch (prop_id) + { + case PROP_DESKTOP_ID: + app->appinfo = g_desktop_app_info_new (g_value_get_string (value)); + if (app->appinfo == NULL) + { + g_warning ("could not find the desktop file for '%s'", + g_value_get_string (value)); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +messaging_menu_app_finalize (GObject *object) +{ + G_OBJECT_CLASS (messaging_menu_app_parent_class)->finalize (object); +} + +static void +messaging_menu_app_dispose (GObject *object) +{ + MessagingMenuApp *app = MESSAGING_MENU_APP (object); + + if (app->cancellable) + { + g_cancellable_cancel (app->cancellable); + g_object_unref (app->cancellable); + app->cancellable = NULL; + } + + g_clear_object (&app->appinfo); + g_clear_object (&app->source_actions); + g_clear_object (&app->menu); + + G_OBJECT_CLASS (messaging_menu_app_parent_class)->dispose (object); +} + +static void +messaging_menu_app_class_init (MessagingMenuAppClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->set_property = messaging_menu_app_set_property; + object_class->finalize = messaging_menu_app_finalize; + object_class->dispose = messaging_menu_app_dispose; + + properties[PROP_DESKTOP_ID] = g_param_spec_string ("desktop-id", + "Desktop Id", + "The desktop id of the associated application", + NULL, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + signals[ACTIVATE_SOURCE] = g_signal_new ("activate-source", + MESSAGING_MENU_TYPE_APP, + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); +} + +static void +created_messages_service (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + MessagingMenuApp *app = user_data; + GError *error = NULL; + + app->messages_service = indicator_messages_service_proxy_new_finish (result, &error); + if (!app->messages_service) + { + g_warning ("unable to connect to the mesaging menu service: %s", error->message); + g_error_free (error); + return; + } + + /* sync current status */ + if (app->registered == TRUE) + messaging_menu_app_register (app); + else if (app->registered == FALSE) + messaging_menu_app_unregister (app); +} + +static void +got_session_bus (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + MessagingMenuApp *app = user_data; + GDBusConnection *bus; + GError *error = NULL; + guint id; + + bus = g_bus_get_finish (res, &error); + if (bus == NULL) + { + g_warning ("unable to connect to session bus: %s", error->message); + g_error_free (error); + return; + } + + id = g_dbus_connection_export_action_group (bus, + "/com/canonical/indicator/messages", + G_ACTION_GROUP (app->source_actions), + &error); + if (!id) + { + g_warning ("unable to export action group: %s", error->message); + g_error_free (error); + } + + id = g_dbus_connection_export_menu_model (bus, + "/com/canonical/indicator/messages", + G_MENU_MODEL (app->menu), + &error); + if (!id) + { + g_warning ("unable to export menu: %s", error->message); + g_error_free (error); + } + + indicator_messages_service_proxy_new (bus, + G_DBUS_PROXY_FLAGS_NONE, + "com.canonical.indicator.messages", + "/com/canonical/indicator/messages/service", + app->cancellable, + created_messages_service, + app); + + g_object_unref (bus); +} + +static void +messaging_menu_app_init (MessagingMenuApp *app) +{ + app->registered = -1; + app->status = MESSAGING_MENU_STATUS_OFFLINE; + + app->cancellable = g_cancellable_new (); + + app->source_actions = g_simple_action_group_new (); + app->menu = g_menu_new (); + + app->cancellable = g_cancellable_new (); + + + g_bus_get (G_BUS_TYPE_SESSION, + app->cancellable, + got_session_bus, + app); +} + +/** + * messaging_menu_new: + * @desktop_id: a desktop file id. See g_desktop_app_info_new() + * + * Creates a new #MessagingMenuApp for the application associated with + * @desktop_id. + * + * If the application is already registered with the messaging menu, it will be + * marked as "running". Otherwise, call messaging_menu_app_register(). + * + * The messaging menu will return to marking the application as not running as + * soon as the returned #MessagingMenuApp is destroyed. + * + * Returns: (transfer-full): a new #MessagingMenuApp + */ +MessagingMenuApp * +messaging_menu_app_new (const gchar *desktop_id) +{ + return g_object_new (MESSAGING_MENU_TYPE_APP, + "desktop-id", desktop_id, + NULL); +} + +/** + * messaging_menu_app_register: + * @app: a #MessagingMenuApp + * + * Registers @app with the messaging menu. + * + * The messaging menu will add a section with an app launcher and the shortcuts + * defined in its desktop file. + * + * The application will be marked as "running" as long as @app is alive or + * messaging_menu_app_unregister() is called. + */ +void +messaging_menu_app_register (MessagingMenuApp *app) +{ + app->registered = TRUE; + + /* state will be synced right after connecting to the service */ + if (!app->messages_service) + return; + + indicator_messages_service_call_register_application (app->messages_service, + g_app_info_get_id (G_APP_INFO (app->appinfo)), + "/com/canonical/indicator/messages", + app->cancellable, + NULL, NULL); +} + +/** + * messaging_menu_app_unregister: + * @app: a #MessagingMenuApp + * + * Completely removes the application associated with @desktop_id from the + * messaging menu. + * + * Note: @app will remain valid and usable after this call. + */ +void +messaging_menu_app_unregister (MessagingMenuApp *app) +{ + app->registered = FALSE; + + /* state will be synced right after connecting to the service */ + if (!app->messages_service) + return; + + indicator_messages_service_call_unregister_application (app->messages_service, + g_app_info_get_id (G_APP_INFO (app->appinfo)), + app->cancellable, + NULL, NULL); +} + +/** + * messaging_menu_app_set_status: + * @app: a #MessagingMenuApp + * @status: a #MessagingMenuStatus + */ +void +messaging_menu_app_set_status (MessagingMenuApp *app, + MessagingMenuStatus status) +{ + g_warning ("%s: not yet implemented", G_STRFUNC); +} + +static void +source_action_activated (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + MessagingMenuApp *app = user_data; + + g_signal_emit (app, signals[ACTIVATE_SOURCE], 0, + g_action_get_name (G_ACTION (action))); +} + +static void +messaging_menu_app_insert_source_action (MessagingMenuApp *app, + gint position, + const gchar *id, + GIcon *icon, + const gchar *label, + GVariant *state) +{ + GSimpleAction *action; + GMenuItem *menuitem; + + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + g_return_if_fail (id != NULL); + + if (g_simple_action_group_lookup (app->source_actions, id)) + { + g_warning ("a source with id '%s' already exists", id); + return; + } + + action = g_simple_action_new_stateful (id, NULL, state); + g_signal_connect (action, "activate", + G_CALLBACK (source_action_activated), app); + g_simple_action_group_insert (app->source_actions, G_ACTION (action)); + g_object_unref (action); + + menuitem = g_menu_item_new (label, id); + if (icon) + { + gchar *icon_name = g_icon_to_string (icon); + g_menu_item_set_attribute (menuitem, "indicator-icon-name", icon_name); + g_free (icon_name); + } + g_menu_insert_item (app->menu, position, menuitem); + g_object_unref (menuitem); +} + +static void +messaging_menu_app_set_source_action (MessagingMenuApp *app, + const gchar *source_id, + GVariant *state) +{ + GAction *action; + + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + g_return_if_fail (source_id != NULL); + + action = g_simple_action_group_lookup (app->source_actions, source_id); + if (action == NULL) + { + g_warning ("a source with id '%s' doesn't exist", source_id); + return; + } + + g_simple_action_set_state (G_SIMPLE_ACTION (action), state); +} + +/** + * messaging_menu_app_insert_source: + * @app: a #MessagingMenuApp + * @position: the position at which to insert the source + * @id: a unique identifier for the source to be added + * @icon: the icon associated with the source + * @label: a user-visible string best describing the source + * + * Inserts a new message source into the section representing @app. Equivalent + * to calling messaging_menu_app_insert_source_with_time() with the current + * time. + * + * It is an error to insert a source with an @id which already exists. Use + * messaging_menu_app_has_source() to find out whether there is such a source. + */ +void +messaging_menu_app_insert_source (MessagingMenuApp *app, + gint position, + const gchar *id, + GIcon *icon, + const gchar *label) +{ + messaging_menu_app_insert_source_with_time (app, position, id, icon, label, + g_get_real_time ()); +} + +/** + * messaging_menu_app_append_source: + * @app: a #MessagingMenuApp + * @id: a unique identifier for the source to be added + * @icon: the icon associated with the source + * @label: a user-visible string best describing the source + * + * Appends a new message source to the end of the section representing @app. + * Equivalent to calling messaging_menu_app_append_source_with_time() with the + * current time. + * + * It is an error to add a source with an @id which already exists. Use + * messaging_menu_app_has_source() to find out whether there is such a source. + */ +void +messaging_menu_app_append_source (MessagingMenuApp *app, + const gchar *id, + GIcon *icon, + const gchar *label) +{ + messaging_menu_app_insert_source (app, -1, id, icon, label); +} + +/** + * messaging_menu_app_insert_source_with_count: + * @app: a #MessagingMenuApp + * @position: the position at which to insert the source + * @id: a unique identifier for the source to be added + * @icon: the icon associated with the source + * @label: a user-visible string best describing the source + * @count: the count for the source + * + * Inserts a new message source into the section representing @app and + * initializes it with @count. + * + * To update the count, use messaging_menu_app_set_source_count(). + * + * It is an error to insert a source with an @id which already exists. Use + * messaging_menu_app_has_source() to find out whether there is such a source. + */ +void +messaging_menu_app_insert_source_with_count (MessagingMenuApp *app, + gint position, + const gchar *id, + GIcon *icon, + const gchar *label, + guint count) +{ + messaging_menu_app_insert_source_action (app, position, id, icon, label, + g_variant_new ("(uxs)", count, 0, "")); +} + +/** + * messaging_menu_app_append_source_with_count: + * @app: a #MessagingMenuApp + * @id: a unique identifier for the source to be added + * @icon: the icon associated with the source + * @label: a user-visible string best describing the source + * @count: the count for the source + * + * Appends a new message source to the end of the section representing @app and + * initializes it with @count. + * + * To update the count, use messaging_menu_app_set_source_count(). + * + * It is an error to add a source with an @id which already exists. Use + * messaging_menu_app_has_source() to find out whether there is such a source. + */ +void messaging_menu_app_append_source_with_count (MessagingMenuApp *app, + const gchar *id, + GIcon *icon, + const gchar *label, + guint count) +{ + messaging_menu_app_insert_source_with_count (app, -1, id, icon, label, count); +} + +/** + * messaging_menu_app_insert_source_with_time: + * @app: a #MessagingMenuApp + * @position: the position at which to insert the source + * @id: a unique identifier for the source to be added + * @icon: the icon associated with the source + * @label: a user-visible string best describing the source + * @time: the time when the source was created + * + * Inserts a new message source into the section representing @app and + * initializes it with @time. + * + * To change the time, use messaging_menu_app_set_source_time(). + * + * It is an error to insert a source with an @id which already exists. Use + * messaging_menu_app_has_source() to find out whether there is such a source. + */ +void +messaging_menu_app_insert_source_with_time (MessagingMenuApp *app, + gint position, + const gchar *id, + GIcon *icon, + const gchar *label, + gint64 time) +{ + messaging_menu_app_insert_source_action (app, position, id, icon, label, + g_variant_new ("(uxs)", 0, time, "")); +} + +/** + * messaging_menu_app_append_source_with_time: + * @app: a #MessagingMenuApp + * @position: the position at which to insert the source + * @id: a unique identifier for the source to be added + * @icon: the icon associated with the source + * @label: a user-visible string best describing the source + * @time: the time when the source was created + * + * Appends a new message source to the end of the section representing @app and + * initializes it with @time. + * + * To change the time, use messaging_menu_app_set_source_time(). + * + * It is an error to insert a source with an @id which already exists. Use + * messaging_menu_app_has_source() to find out whether there is such a source. + */ +void +messaging_menu_app_append_source_with_time (MessagingMenuApp *app, + const gchar *id, + GIcon *icon, + const gchar *label, + gint64 time) +{ + messaging_menu_app_insert_source_with_time (app, -1, id, icon, label, time); +} + +/** + * messaging_menu_app_insert_source_with_string: + * @app: a #MessagingMenuApp + * @position: the position at which to insert the source + * @id: a unique identifier for the source to be added + * @icon: the icon associated with the source + * @label: a user-visible string best describing the source + * @str: a string associated with the source + * + * Inserts a new message source into the section representing @app and + * initializes it with @str. + * + * To update the string, use messaging_menu_app_set_source_string(). + * + * It is an error to insert a source with an @id which already exists. Use + * messaging_menu_app_has_source() to find out whether there is such a source. + */ +void +messaging_menu_app_insert_source_with_string (MessagingMenuApp *app, + gint position, + const gchar *id, + GIcon *icon, + const gchar *label, + const gchar *str) +{ + messaging_menu_app_insert_source_action (app, position, id, icon, label, + g_variant_new ("(uxs)", 0, 0, str)); +} + +/** + * messaging_menu_app_append_source_with_string: + * @app: a #MessagingMenuApp + * @position: the position at which to insert the source + * @id: a unique identifier for the source to be added + * @icon: the icon associated with the source + * @label: a user-visible string best describing the source + * @str: a string associated with the source + * + * Appends a new message source to the end of the section representing @app and + * initializes it with @str. + * + * To update the string, use messaging_menu_app_set_source_string(). + * + * It is an error to insert a source with an @id which already exists. Use + * messaging_menu_app_has_source() to find out whether there is such a source. + */ +void +messaging_menu_app_append_source_with_string (MessagingMenuApp *app, + const gchar *id, + GIcon *icon, + const gchar *label, + const gchar *str) +{ + messaging_menu_app_insert_source_with_string (app, -1, id, icon, label, str); +} + +/** + * messaging_menu_app_remove_source: + * @app: a #MessagingMenuApp + * @source_id: the id of the source to remove + * + * Removes the source corresponding to @source_id from the menu. + */ +void +messaging_menu_app_remove_source (MessagingMenuApp *app, + const gchar *source_id) +{ + int n_items; + int i; + + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + g_return_if_fail (source_id != NULL); + + if (g_simple_action_group_lookup (app->source_actions, source_id) == NULL) + { + g_warning ("%s: a source with id '%s' doesn't exist", G_STRFUNC, source_id); + return; + } + + n_items = g_menu_model_get_n_items (G_MENU_MODEL (app->menu)); + for (i = 0; i < n_items; i++) + { + const gchar *action = NULL; + + g_menu_model_get_item_attribute (G_MENU_MODEL (app->menu), i, + "action", "&s", &action); + if (!g_strcmp0 (action, source_id)) + { + g_menu_remove (app->menu, i); + break; + } + } + + g_simple_action_group_remove (app->source_actions, source_id); +} + +/** + * messaging_menu_app_has_source: + * @app: a #MessagingMenuApp + * @source_id: a source id + * + * Returns: TRUE if there is a source associated with @source_id + */ +gboolean +messaging_menu_app_has_source (MessagingMenuApp *app, + const gchar *source_id) +{ + g_return_val_if_fail (MESSAGING_MENU_IS_APP (app), FALSE); + g_return_val_if_fail (source_id != NULL, FALSE); + + return g_simple_action_group_lookup (app->source_actions, source_id) != NULL; +} + +/** + * messaging_menu_app_set_source_count: + * @app: a #MessagingMenuApp + * @source_id: a source id + * @count: the new count for the source + * + * Updates the count of @source_id to @count. + */ +void messaging_menu_app_set_source_count (MessagingMenuApp *app, + const gchar *source_id, + guint count) +{ + messaging_menu_app_set_source_action (app, source_id, + g_variant_new ("(uxs)", count, 0, "")); +} + +/** + * messaging_menu_app_set_source_time: + * @app: a #MessagingMenuApp + * @source_id: a source id + * @time: the new time for the source + * + * Updates the time of @source_id to @time. + * + * Note that the time is only displayed if the source does not also have a + * count associated with it. + */ +void +messaging_menu_app_set_source_time (MessagingMenuApp *app, + const gchar *source_id, + gint64 time) +{ + messaging_menu_app_set_source_action (app, source_id, + g_variant_new ("(uxs)", 0, time, "")); +} + +/** + * messaging_menu_app_set_source_string: + * @app: a #MessagingMenuApp + * @source_id: a source id + * @string: the new string for the source + * + * Updates the string displayed next to @source_id to @str. + * + * Note that the string is only displayed if the source does not also have a + * count or time associated with it. + */ +void +messaging_menu_app_set_source_string (MessagingMenuApp *app, + const gchar *source_id, + const gchar *str) +{ + messaging_menu_app_set_source_action (app, source_id, + g_variant_new ("(uxs)", 0, 0, str)); +} + +/** + * messaging_menu_app_draw_attention: + * @app: a #MessagingMenuApp + * @source_id: a source id + * + * Indicates that @source_id has important unread messages. Currently, this + * means that the messaging menu's envelope icon will turn blue. + * + * Use messaging_menu_app_remove_attention() to stop indicating that the source + * needs attention. + */ +void +messaging_menu_app_draw_attention (MessagingMenuApp *app, + const gchar *source_id) +{ + g_warning ("%s: not yet implemented", G_STRFUNC); +} + +/** + * messaging_menu_app_remove_attention: + * @app: a #MessagingMenuApp + * @source_id: a source id + * + * Stop indicating that @source_id needs attention. + * + * Use messaging_menu_app_draw_attention() to make @source_id draw attention + * again. + */ +void +messaging_menu_app_remove_attention (MessagingMenuApp *app, + const gchar *source_id) +{ + g_warning ("%s: not yet implemented", G_STRFUNC); +} diff --git a/libmessaging-menu/messaging-menu.h b/libmessaging-menu/messaging-menu.h new file mode 100644 index 0000000..7ce2981 --- /dev/null +++ b/libmessaging-menu/messaging-menu.h @@ -0,0 +1,130 @@ +/* + * Copyright 2012 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Lars Uebernickel + */ + +#ifndef __messaging_menu_h__ +#define __messaging_menu_h__ + +#include + +G_BEGIN_DECLS + +#define MESSAGING_MENU_TYPE_APP messaging_menu_app_get_type() +#define MESSAGING_MENU_APP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MESSAGING_MENU_TYPE_APP, MessagingMenuApp)) +#define MESSAGING_MENU_APP_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), MESSAGING_MENU_TYPE_APP, MessagingMenuAppClass)) +#define MESSAGING_MENU_IS_APP(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MESSAGING_MENU_TYPE_APP)) + +typedef enum { + MESSAGING_MENU_STATUS_AVAILABLE, + MESSAGING_MENU_STATUS_AWAY, + MESSAGING_MENU_STATUS_BUSY, + MESSAGING_MENU_STATUS_HIDDEN, + MESSAGING_MENU_STATUS_OFFLINE +} MessagingMenuStatus; + + +typedef GObjectClass MessagingMenuAppClass; +typedef struct _MessagingMenuApp MessagingMenuApp; + +GType messaging_menu_app_get_type (void) G_GNUC_CONST; + +MessagingMenuApp * messaging_menu_app_new (const gchar *desktop_id); + +void messaging_menu_app_register (MessagingMenuApp *app); +void messaging_menu_app_unregister (MessagingMenuApp *app); + +void messaging_menu_app_set_status (MessagingMenuApp *app, + MessagingMenuStatus status); + +void messaging_menu_app_insert_source (MessagingMenuApp *app, + gint position, + const gchar *id, + GIcon *icon, + const gchar *label); + +void messaging_menu_app_append_source (MessagingMenuApp *app, + const gchar *id, + GIcon *icon, + const gchar *label); + +void messaging_menu_app_insert_source_with_count (MessagingMenuApp *app, + gint position, + const gchar *id, + GIcon *icon, + const gchar *label, + guint count); + +void messaging_menu_app_append_source_with_count (MessagingMenuApp *app, + const gchar *id, + GIcon *icon, + const gchar *label, + guint count); + +void messaging_menu_app_insert_source_with_time (MessagingMenuApp *app, + gint position, + const gchar *id, + GIcon *icon, + const gchar *label, + gint64 time); + +void messaging_menu_app_append_source_with_time (MessagingMenuApp *app, + const gchar *id, + GIcon *icon, + const gchar *label, + gint64 time); + +void messaging_menu_app_append_source_with_string (MessagingMenuApp *app, + const gchar *id, + GIcon *icon, + const gchar *label, + const gchar *str); + +void messaging_menu_app_insert_source_with_string (MessagingMenuApp *app, + gint position, + const gchar *id, + GIcon *icon, + const gchar *label, + const gchar *str); + +void messaging_menu_app_remove_source (MessagingMenuApp *app, + const gchar *source_id); + +gboolean messaging_menu_app_has_source (MessagingMenuApp *app, + const gchar *source_id); + +void messaging_menu_app_set_source_count (MessagingMenuApp *app, + const gchar *source_id, + guint count); + +void messaging_menu_app_set_source_time (MessagingMenuApp *app, + const gchar *source_id, + gint64 time); + +void messaging_menu_app_set_source_string (MessagingMenuApp *app, + const gchar *source_id, + const gchar *str); + +void messaging_menu_app_draw_attention (MessagingMenuApp *app, + const gchar *source_id); + +void messaging_menu_app_remove_attention (MessagingMenuApp *app, + const gchar *source_id); + +G_END_DECLS + +#endif diff --git a/libmessaging-menu/messaging-menu.pc.in b/libmessaging-menu/messaging-menu.pc.in new file mode 100644 index 0000000..486300f --- /dev/null +++ b/libmessaging-menu/messaging-menu.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@/messaging-menu + +Name: Messaging Menu Library +Description: Messaging Menu client library +Version: @VERSION@ +Requires: gio-unix-2.0 +Libs: -L${libdir} -lmessaging-menu +Cflags: -I${includedir} -- cgit v1.2.3 From ac1a11bdf1df0efb426654cfe6b7ad4d5deb3815 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Fri, 15 Jun 2012 15:03:01 +0200 Subject: Check parameter types in public API --- libmessaging-menu/messaging-menu.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c index 5b786af..6d2eab5 100644 --- a/libmessaging-menu/messaging-menu.c +++ b/libmessaging-menu/messaging-menu.c @@ -265,6 +265,8 @@ messaging_menu_app_new (const gchar *desktop_id) void messaging_menu_app_register (MessagingMenuApp *app) { + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + app->registered = TRUE; /* state will be synced right after connecting to the service */ @@ -290,6 +292,8 @@ messaging_menu_app_register (MessagingMenuApp *app) void messaging_menu_app_unregister (MessagingMenuApp *app) { + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + app->registered = FALSE; /* state will be synced right after connecting to the service */ -- cgit v1.2.3 From 40c73ac73cb2e45136c6820086f901ea1cf72874 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Fri, 15 Jun 2012 15:08:35 +0200 Subject: libmessaging-menu: add source name detail to "active-source" signal --- libmessaging-menu/messaging-menu.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c index 6d2eab5..d848651 100644 --- a/libmessaging-menu/messaging-menu.c +++ b/libmessaging-menu/messaging-menu.c @@ -128,7 +128,8 @@ messaging_menu_app_class_init (MessagingMenuAppClass *class) signals[ACTIVATE_SOURCE] = g_signal_new ("activate-source", MESSAGING_MENU_TYPE_APP, - G_SIGNAL_RUN_FIRST, + G_SIGNAL_RUN_FIRST | + G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__STRING, @@ -324,9 +325,10 @@ source_action_activated (GSimpleAction *action, gpointer user_data) { MessagingMenuApp *app = user_data; + const gchar *name = g_action_get_name (G_ACTION (action)); + GQuark q = g_quark_from_string (name); - g_signal_emit (app, signals[ACTIVATE_SOURCE], 0, - g_action_get_name (G_ACTION (action))); + g_signal_emit (app, signals[ACTIVATE_SOURCE], q, name); } static void -- cgit v1.2.3 From 93db8c38f2252cb4d506d90721446c0ad524ca3b Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 27 Jun 2012 17:25:34 +0200 Subject: Add draws-attention flag to source actions AppSections watch those flags for associated sources and mux them into a draws-attention property for the whole section. --- libmessaging-menu/messaging-menu.c | 45 +++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 8 deletions(-) (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c index d848651..eff3b4f 100644 --- a/libmessaging-menu/messaging-menu.c +++ b/libmessaging-menu/messaging-menu.c @@ -463,7 +463,7 @@ messaging_menu_app_insert_source_with_count (MessagingMenuApp *app, guint count) { messaging_menu_app_insert_source_action (app, position, id, icon, label, - g_variant_new ("(uxs)", count, 0, "")); + g_variant_new ("(uxsb)", count, 0, "", FALSE)); } /** @@ -517,7 +517,7 @@ messaging_menu_app_insert_source_with_time (MessagingMenuApp *app, gint64 time) { messaging_menu_app_insert_source_action (app, position, id, icon, label, - g_variant_new ("(uxs)", 0, time, "")); + g_variant_new ("(uxsb)", 0, time, "", FALSE)); } /** @@ -573,7 +573,7 @@ messaging_menu_app_insert_source_with_string (MessagingMenuApp *app, const gchar *str) { messaging_menu_app_insert_source_action (app, position, id, icon, label, - g_variant_new ("(uxs)", 0, 0, str)); + g_variant_new ("(uxsb)", 0, 0, str, FALSE)); } /** @@ -673,7 +673,7 @@ void messaging_menu_app_set_source_count (MessagingMenuApp *app, guint count) { messaging_menu_app_set_source_action (app, source_id, - g_variant_new ("(uxs)", count, 0, "")); + g_variant_new ("(uxsb)", count, 0, "", FALSE)); } /** @@ -693,7 +693,7 @@ messaging_menu_app_set_source_time (MessagingMenuApp *app, gint64 time) { messaging_menu_app_set_source_action (app, source_id, - g_variant_new ("(uxs)", 0, time, "")); + g_variant_new ("(uxsb)", 0, time, "", FALSE)); } /** @@ -713,7 +713,36 @@ messaging_menu_app_set_source_string (MessagingMenuApp *app, const gchar *str) { messaging_menu_app_set_source_action (app, source_id, - g_variant_new ("(uxs)", 0, 0, str)); + g_variant_new ("(uxsb)", 0, 0, str, FALSE)); +} + +static void +messaging_menu_app_set_attention (MessagingMenuApp *app, + const gchar *source_id, + gboolean attention) +{ + GAction *action; + GVariant *state; + guint32 count; + gint64 time; + const gchar *str =""; + + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + g_return_if_fail (source_id != NULL); + + action = g_simple_action_group_lookup (app->source_actions, source_id); + if (action == NULL) + { + g_warning ("a source with id '%s' doesn't exist", source_id); + return; + } + + state = g_action_get_state (action); + g_variant_get (state, "(ux&sb)", &count, &time, &str, NULL); + g_variant_unref (state); + + g_simple_action_set_state (G_SIMPLE_ACTION (action), + g_variant_new ("(uxsb)", count, time, str, attention)); } /** @@ -731,7 +760,7 @@ void messaging_menu_app_draw_attention (MessagingMenuApp *app, const gchar *source_id) { - g_warning ("%s: not yet implemented", G_STRFUNC); + messaging_menu_app_set_attention (app, source_id, TRUE); } /** @@ -748,5 +777,5 @@ void messaging_menu_app_remove_attention (MessagingMenuApp *app, const gchar *source_id) { - g_warning ("%s: not yet implemented", G_STRFUNC); + messaging_menu_app_set_attention (app, source_id, FALSE); } -- cgit v1.2.3 From cc6cbf767baba6ab2b5f2ec32f0d9ff2a7f94cfa Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Wed, 27 Jun 2012 22:44:30 +0200 Subject: Sync chat status from and to clients --- libmessaging-menu/messaging-menu.c | 74 +++++++++++++++++++++++++++++++++++++- libmessaging-menu/messaging-menu.h | 2 +- 2 files changed, 74 insertions(+), 2 deletions(-) (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c index eff3b4f..7ee455a 100644 --- a/libmessaging-menu/messaging-menu.c +++ b/libmessaging-menu/messaging-menu.c @@ -52,12 +52,19 @@ enum { enum { ACTIVATE_SOURCE, + STATUS_CHANGED, N_SIGNALS }; static GParamSpec *properties[N_PROPERTIES]; static guint signals[N_SIGNALS]; +static const gchar *status_ids[] = { "available", "away", "busy", "invisible", "offline" }; + +static void global_status_changed (IndicatorMessagesService *service, + const gchar *status_str, + gpointer user_data); + static void messaging_menu_app_set_property (GObject *object, guint prop_id, @@ -100,6 +107,14 @@ messaging_menu_app_dispose (GObject *object) app->cancellable = NULL; } + if (app->messages_service) + { + g_signal_handlers_disconnect_by_func (app->messages_service, + global_status_changed, + app); + g_clear_object (&app->messages_service); + } + g_clear_object (&app->appinfo); g_clear_object (&app->source_actions); g_clear_object (&app->menu); @@ -134,6 +149,14 @@ messaging_menu_app_class_init (MessagingMenuAppClass *class) NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); + + signals[STATUS_CHANGED] = g_signal_new ("status-changed", + MESSAGING_MENU_TYPE_APP, + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, 1, G_TYPE_INT); } static void @@ -152,11 +175,15 @@ created_messages_service (GObject *source_object, return; } + g_signal_connect (app->messages_service, "status-changed", + G_CALLBACK (global_status_changed), app); + /* sync current status */ if (app->registered == TRUE) messaging_menu_app_register (app); else if (app->registered == FALSE) messaging_menu_app_unregister (app); + messaging_menu_app_set_status (app, app->status); } static void @@ -316,7 +343,52 @@ void messaging_menu_app_set_status (MessagingMenuApp *app, MessagingMenuStatus status) { - g_warning ("%s: not yet implemented", G_STRFUNC); + g_return_if_fail (MESSAGING_MENU_IS_APP (app)); + g_return_if_fail (status >= MESSAGING_MENU_STATUS_AVAILABLE && + status <= MESSAGING_MENU_STATUS_OFFLINE); + + app->status = status; + + /* state will be synced right after connecting to the service */ + if (!app->messages_service) + return; + + indicator_messages_service_call_set_status (app->messages_service, + status_ids [status], + app->cancellable, + NULL, NULL); +} + +static int +status_from_string (const gchar *s) +{ + int i; + + if (!s) + return -1; + + for (i = 0; i <= MESSAGING_MENU_STATUS_OFFLINE; i++) + { + if (g_str_equal (s, status_ids[i])) + return i; + } + + return -1; +} + +static void +global_status_changed (IndicatorMessagesService *service, + const gchar *status_str, + gpointer user_data) +{ + MessagingMenuApp *app = user_data; + int status; + + status = status_from_string (status_str); + g_return_if_fail (status >= 0); + + app->status = (MessagingMenuStatus)status; + g_signal_emit (app, signals[STATUS_CHANGED], 0, app->status); } static void diff --git a/libmessaging-menu/messaging-menu.h b/libmessaging-menu/messaging-menu.h index 7ce2981..e767099 100644 --- a/libmessaging-menu/messaging-menu.h +++ b/libmessaging-menu/messaging-menu.h @@ -33,7 +33,7 @@ typedef enum { MESSAGING_MENU_STATUS_AVAILABLE, MESSAGING_MENU_STATUS_AWAY, MESSAGING_MENU_STATUS_BUSY, - MESSAGING_MENU_STATUS_HIDDEN, + MESSAGING_MENU_STATUS_INVISIBLE, MESSAGING_MENU_STATUS_OFFLINE } MessagingMenuStatus; -- cgit v1.2.3 From 8f2b343fc59a927961ee90f588f207d22c54924d Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Thu, 28 Jun 2012 16:07:17 +0200 Subject: libmessaging-menu: use the (newly added) GTupleAction instead of GSimpleAction GTupleAction is a bit simpler to handle when the action state contains a tuple of things that are independently modified most the time. It might be useful for other indicators as well. This implicitly also fixes the bug that libmessaging-menu did not preserve the other values in the action state when updating count, time, or string. --- libmessaging-menu/Makefile.am | 2 + libmessaging-menu/gtupleaction.c | 353 +++++++++++++++++++++++++++++++++++++ libmessaging-menu/gtupleaction.h | 40 +++++ libmessaging-menu/messaging-menu.c | 71 +++----- 4 files changed, 422 insertions(+), 44 deletions(-) create mode 100644 libmessaging-menu/gtupleaction.c create mode 100644 libmessaging-menu/gtupleaction.h (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/Makefile.am b/libmessaging-menu/Makefile.am index a0da51e..becaded 100644 --- a/libmessaging-menu/Makefile.am +++ b/libmessaging-menu/Makefile.am @@ -5,6 +5,8 @@ libmessaging_menu_ladir = $(includedir)/messaging-menu libmessaging_menu_la_SOURCES = \ messaging-menu.c \ + gtupleaction.c \ + gtupleaction.h \ $(BUILT_SOURCES) libmessaging_menu_la_HEADERS = \ diff --git a/libmessaging-menu/gtupleaction.c b/libmessaging-menu/gtupleaction.c new file mode 100644 index 0000000..f9e6fd7 --- /dev/null +++ b/libmessaging-menu/gtupleaction.c @@ -0,0 +1,353 @@ +/* + * Copyright 2012 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Lars Uebernickel + */ + +#include "gtupleaction.h" + +typedef GObjectClass GTupleActionClass; + +struct _GTupleAction +{ + GObject parent; + + gchar *name; + GVariantType *type; + gboolean enabled; + + gsize n_children; + GVariant **children; +}; + +static void action_interface_init (GActionInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (GTupleAction, g_tuple_action, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_ACTION, action_interface_init)); + +enum +{ + PROP_0, + PROP_NAME, + PROP_PARAMETER_TYPE, + PROP_ENABLED, + PROP_STATE_TYPE, + PROP_STATE, + N_PROPERTIES +}; + +enum +{ + SIGNAL_ACTIVATE, + N_SIGNALS +}; + +static GParamSpec *properties[N_PROPERTIES]; +static guint signal_ids[N_SIGNALS]; + +static const gchar * +g_tuple_action_get_name (GAction *action) +{ + GTupleAction *tuple = G_TUPLE_ACTION (action); + + return tuple->name; +} + +static const GVariantType * +g_tuple_action_get_parameter_type (GAction *action) +{ + return NULL; +} + +static const GVariantType * +g_tuple_action_get_state_type (GAction *action) +{ + GTupleAction *tuple = G_TUPLE_ACTION (action); + + return tuple->type; +} + +static GVariant * +g_tuple_action_get_state_hint (GAction *action) +{ + return NULL; +} + +static gboolean +g_tuple_action_get_enabled (GAction *action) +{ + GTupleAction *tuple = G_TUPLE_ACTION (action); + + return tuple->enabled; +} + +static GVariant * +g_tuple_action_get_state (GAction *action) +{ + GTupleAction *tuple = G_TUPLE_ACTION (action); + GVariant *result; + + result = g_variant_new_tuple (tuple->children, tuple->n_children); + return g_variant_ref_sink (result); +} + +static void +g_tuple_action_set_state (GTupleAction *tuple, + GVariant *state) +{ + int i; + + g_return_if_fail (g_variant_type_is_tuple (g_variant_get_type (state))); + + if (tuple->type == NULL) + { + tuple->type = g_variant_type_copy (g_variant_get_type (state)); + tuple->n_children = g_variant_n_children (state); + tuple->children = g_new0 (GVariant *, tuple->n_children); + } + + for (i = 0; i < tuple->n_children; i++) + { + if (tuple->children[i]) + g_variant_unref (tuple->children[i]); + tuple->children[i] = g_variant_get_child_value (state, i); + } + + g_object_notify_by_pspec (G_OBJECT (tuple), properties[PROP_STATE]); +} + +static void +g_tuple_action_change_state (GAction *action, + GVariant *value) +{ + GTupleAction *tuple = G_TUPLE_ACTION (action); + + g_return_if_fail (value != NULL); + g_return_if_fail (g_variant_is_of_type (value, tuple->type)); + + g_variant_ref_sink (value); + + /* TODO add a change-state signal similar to GSimpleAction */ + g_tuple_action_set_state (tuple, value); + + g_variant_unref (value); +} + +static void +g_tuple_action_activate (GAction *action, + GVariant *parameter) +{ + GTupleAction *tuple = G_TUPLE_ACTION (action); + + g_return_if_fail (parameter == NULL); + + if (tuple->enabled) + g_signal_emit (tuple, signal_ids[SIGNAL_ACTIVATE], 0, NULL); +} + +static void +g_tuple_action_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GAction *action = G_ACTION (object); + + switch (prop_id) + { + case PROP_NAME: + g_value_set_string (value, g_tuple_action_get_name (action)); + break; + + case PROP_PARAMETER_TYPE: + g_value_set_boxed (value, g_tuple_action_get_parameter_type (action)); + break; + + case PROP_ENABLED: + g_value_set_boolean (value, g_tuple_action_get_enabled (action)); + break; + + case PROP_STATE_TYPE: + g_value_set_boxed (value, g_tuple_action_get_state_type (action)); + break; + + case PROP_STATE: + g_value_take_variant (value, g_tuple_action_get_state (action)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +g_tuple_action_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GTupleAction *tuple = G_TUPLE_ACTION (object); + + switch (prop_id) + { + case PROP_NAME: + tuple->name = g_value_dup_string (value); + g_object_notify_by_pspec (object, properties[PROP_NAME]); + break; + + case PROP_ENABLED: + tuple->enabled = g_value_get_boolean (value); + g_object_notify_by_pspec (object, properties[PROP_ENABLED]); + break; + + case PROP_STATE: + g_tuple_action_set_state (tuple, g_value_get_variant (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +g_tuple_action_finalize (GObject *object) +{ + GTupleAction *tuple = G_TUPLE_ACTION (object); + int i; + + g_free (tuple->name); + g_variant_type_free (tuple->type); + + for (i = 0; i < tuple->n_children; i++) + g_variant_unref (tuple->children[i]); + + g_free (tuple->children); + + G_OBJECT_CLASS (g_tuple_action_parent_class)->finalize (object); +} + +static void +action_interface_init (GActionInterface *iface) +{ + iface->get_name = g_tuple_action_get_name; + iface->get_parameter_type = g_tuple_action_get_parameter_type; + iface->get_state_type = g_tuple_action_get_state_type; + iface->get_state_hint = g_tuple_action_get_state_hint; + iface->get_enabled = g_tuple_action_get_enabled; + iface->get_state = g_tuple_action_get_state; + iface->change_state = g_tuple_action_change_state; + iface->activate = g_tuple_action_activate; +} + +static void +g_tuple_action_class_init (GTupleActionClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + object_class->get_property = g_tuple_action_get_property; + object_class->set_property = g_tuple_action_set_property; + object_class->finalize = g_tuple_action_finalize; + + properties[PROP_NAME] = g_param_spec_string ("name", + "Name", + "The name of the action", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_PARAMETER_TYPE] = g_param_spec_boxed ("parameter-type", + "Parameter Type", + "The variant type passed to activate", + G_TYPE_VARIANT_TYPE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_ENABLED] = g_param_spec_boolean ("enabled", + "Enabled", + "Whether the action can be activated", + TRUE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_STATE_TYPE] = g_param_spec_boxed ("state-type", + "State Type", + "The variant type of the state, must be a tuple", + G_TYPE_VARIANT_TYPE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_STATE] = g_param_spec_variant ("state", + "State", + "The state of the action", + G_VARIANT_TYPE_TUPLE, + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); + + signal_ids[SIGNAL_ACTIVATE] = g_signal_new ("activate", + G_TYPE_TUPLE_ACTION, + G_SIGNAL_RUN_LAST | G_SIGNAL_MUST_COLLECT, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VARIANT, + G_TYPE_NONE, 1, + G_TYPE_VARIANT); +} + +static void +g_tuple_action_init (GTupleAction *action) +{ +} + +GTupleAction * +g_tuple_action_new (const gchar *name, + GVariant *initial_state) +{ + const GVariantType *type; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (initial_state != NULL, NULL); + + type = g_variant_get_type (initial_state); + g_return_val_if_fail (g_variant_type_is_tuple (type), NULL); + + return g_object_new (G_TYPE_TUPLE_ACTION, + "name", name, + "state", initial_state, + NULL); +} + +void +g_tuple_action_set_child (GTupleAction *action, + gsize index, + GVariant *value) +{ + const GVariantType *type; + + g_return_if_fail (G_IS_TUPLE_ACTION (action)); + g_return_if_fail (index < action->n_children); + g_return_if_fail (value != NULL); + + type = g_variant_get_type (value); + g_return_if_fail (g_variant_is_of_type (value, type)); + + g_variant_unref (action->children[index]); + action->children[index] = g_variant_ref_sink (value); + + g_object_notify_by_pspec (G_OBJECT (action), properties[PROP_STATE]); +} diff --git a/libmessaging-menu/gtupleaction.h b/libmessaging-menu/gtupleaction.h new file mode 100644 index 0000000..c447d71 --- /dev/null +++ b/libmessaging-menu/gtupleaction.h @@ -0,0 +1,40 @@ +/* + * Copyright 2012 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + * Authors: + * Lars Uebernickel + */ + +#ifndef __g_tuple_action_h__ +#define __g_tuple_action_h__ + +#include + +#define G_TYPE_TUPLE_ACTION (g_tuple_action_get_type ()) +#define G_TUPLE_ACTION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_TUPLE_ACTION, GTupleAction)) +#define G_IS_TUPLE_ACTION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_TUPLE_ACTION)) + +typedef struct _GTupleAction GTupleAction; + +GType g_tuple_action_get_type (void) G_GNUC_CONST; + +GTupleAction * g_tuple_action_new (const gchar *name, + GVariant *initial_state); + +void g_tuple_action_set_child (GTupleAction *action, + gsize index, + GVariant *value); + +#endif diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c index 7ee455a..22e4793 100644 --- a/libmessaging-menu/messaging-menu.c +++ b/libmessaging-menu/messaging-menu.c @@ -19,6 +19,7 @@ #include "messaging-menu.h" #include "indicator-messages-service.h" +#include "gtupleaction.h" #include @@ -44,6 +45,14 @@ struct _MessagingMenuApp G_DEFINE_TYPE (MessagingMenuApp, messaging_menu_app, G_TYPE_OBJECT); +enum +{ + INDEX_COUNT, + INDEX_TIME, + INDEX_STRING, + INDEX_DRAWS_ATTENTION +}; + enum { PROP_0, PROP_DESKTOP_ID, @@ -392,9 +401,9 @@ global_status_changed (IndicatorMessagesService *service, } static void -source_action_activated (GSimpleAction *action, - GVariant *parameter, - gpointer user_data) +source_action_activated (GTupleAction *action, + GVariant *parameter, + gpointer user_data) { MessagingMenuApp *app = user_data; const gchar *name = g_action_get_name (G_ACTION (action)); @@ -411,7 +420,7 @@ messaging_menu_app_insert_source_action (MessagingMenuApp *app, const gchar *label, GVariant *state) { - GSimpleAction *action; + GTupleAction *action; GMenuItem *menuitem; g_return_if_fail (MESSAGING_MENU_IS_APP (app)); @@ -423,7 +432,7 @@ messaging_menu_app_insert_source_action (MessagingMenuApp *app, return; } - action = g_simple_action_new_stateful (id, NULL, state); + action = g_tuple_action_new (id, state); g_signal_connect (action, "activate", G_CALLBACK (source_action_activated), app); g_simple_action_group_insert (app->source_actions, G_ACTION (action)); @@ -443,7 +452,8 @@ messaging_menu_app_insert_source_action (MessagingMenuApp *app, static void messaging_menu_app_set_source_action (MessagingMenuApp *app, const gchar *source_id, - GVariant *state) + gsize index, + GVariant *child) { GAction *action; @@ -457,7 +467,7 @@ messaging_menu_app_set_source_action (MessagingMenuApp *app, return; } - g_simple_action_set_state (G_SIMPLE_ACTION (action), state); + g_tuple_action_set_child (G_TUPLE_ACTION (action), index, child); } /** @@ -744,8 +754,8 @@ void messaging_menu_app_set_source_count (MessagingMenuApp *app, const gchar *source_id, guint count) { - messaging_menu_app_set_source_action (app, source_id, - g_variant_new ("(uxsb)", count, 0, "", FALSE)); + messaging_menu_app_set_source_action (app, source_id, INDEX_COUNT, + g_variant_new_uint32 (count)); } /** @@ -764,8 +774,8 @@ messaging_menu_app_set_source_time (MessagingMenuApp *app, const gchar *source_id, gint64 time) { - messaging_menu_app_set_source_action (app, source_id, - g_variant_new ("(uxsb)", 0, time, "", FALSE)); + messaging_menu_app_set_source_action (app, source_id, INDEX_TIME, + g_variant_new_int64 (time)); } /** @@ -784,37 +794,8 @@ messaging_menu_app_set_source_string (MessagingMenuApp *app, const gchar *source_id, const gchar *str) { - messaging_menu_app_set_source_action (app, source_id, - g_variant_new ("(uxsb)", 0, 0, str, FALSE)); -} - -static void -messaging_menu_app_set_attention (MessagingMenuApp *app, - const gchar *source_id, - gboolean attention) -{ - GAction *action; - GVariant *state; - guint32 count; - gint64 time; - const gchar *str =""; - - g_return_if_fail (MESSAGING_MENU_IS_APP (app)); - g_return_if_fail (source_id != NULL); - - action = g_simple_action_group_lookup (app->source_actions, source_id); - if (action == NULL) - { - g_warning ("a source with id '%s' doesn't exist", source_id); - return; - } - - state = g_action_get_state (action); - g_variant_get (state, "(ux&sb)", &count, &time, &str, NULL); - g_variant_unref (state); - - g_simple_action_set_state (G_SIMPLE_ACTION (action), - g_variant_new ("(uxsb)", count, time, str, attention)); + messaging_menu_app_set_source_action (app, source_id, INDEX_STRING, + g_variant_new_string (str)); } /** @@ -832,7 +813,8 @@ void messaging_menu_app_draw_attention (MessagingMenuApp *app, const gchar *source_id) { - messaging_menu_app_set_attention (app, source_id, TRUE); + messaging_menu_app_set_source_action (app, source_id, INDEX_DRAWS_ATTENTION, + g_variant_new_boolean (TRUE)); } /** @@ -849,5 +831,6 @@ void messaging_menu_app_remove_attention (MessagingMenuApp *app, const gchar *source_id) { - messaging_menu_app_set_attention (app, source_id, FALSE); + messaging_menu_app_set_source_action (app, source_id, INDEX_DRAWS_ATTENTION, + g_variant_new_boolean (FALSE)); } -- cgit v1.2.3 From e5c3665070ddd5a968082a618123dcd0b70d1059 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Mon, 30 Jul 2012 16:38:12 +0200 Subject: Add introspection support --- libmessaging-menu/Makefile.am | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/Makefile.am b/libmessaging-menu/Makefile.am index becaded..3799723 100644 --- a/libmessaging-menu/Makefile.am +++ b/libmessaging-menu/Makefile.am @@ -35,3 +35,28 @@ indicator-messages-service.h: indicator-messages-service.c pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = messaging-menu.pc + +-include $(INTROSPECTION_MAKEFILE) + +INTROSPECTION_GIRS = +INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir) +INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir) + +if HAVE_INTROSPECTION + +MessagingMenu-1.0.gir: libmessaging-menu.la +MessagingMenu_1_0_gir_NAMESPACE = MessagingMenu +MessagingMenu_1_0_gir_INCLUDES = GObject-2.0 Gio-2.0 +MessagingMenu_1_0_gir_CFLAGS = $(INCLUDES) $(GIO_CFLAGS) +MessagingMenu_1_0_gir_LIBS = libmessaging-menu.la +MessagingMenu_1_0_gir_FILES = $(libmessaging_menu_la_SOURCES) $(libmessaging_menu_la_HEADERS) +INTROSPECTION_GIRS += MessagingMenu-1.0.gir + +girdir = $(datadir)/gir-1.0 +gir_DATA = $(INTROSPECTION_GIRS) + +typelibdir = $(libdir)/girepository-1.0 +typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) + +CLEANFILES +=$(gir_DATA) $(typelib_DATA) +endif -- cgit v1.2.3 From b64ac4ede11db0741077b7a427b659a6a06f2c92 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Mon, 20 Aug 2012 23:09:54 +0200 Subject: gtupleaction: set enabled to TRUE by default --- libmessaging-menu/gtupleaction.c | 1 + 1 file changed, 1 insertion(+) (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/gtupleaction.c b/libmessaging-menu/gtupleaction.c index f9e6fd7..21bc003 100644 --- a/libmessaging-menu/gtupleaction.c +++ b/libmessaging-menu/gtupleaction.c @@ -312,6 +312,7 @@ g_tuple_action_class_init (GTupleActionClass *class) static void g_tuple_action_init (GTupleAction *action) { + action->enabled = TRUE; } GTupleAction * -- cgit v1.2.3 From 234c3cb933fcd422d9185b4bb01bed3223f0e791 Mon Sep 17 00:00:00 2001 From: Sebastien Bacher Date: Mon, 20 Aug 2012 23:16:04 +0200 Subject: libmessaging-menu: only export symbols tha t belong to the API --- libmessaging-menu/Makefile.am | 2 ++ 1 file changed, 2 insertions(+) (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/Makefile.am b/libmessaging-menu/Makefile.am index 3799723..187e6dc 100644 --- a/libmessaging-menu/Makefile.am +++ b/libmessaging-menu/Makefile.am @@ -18,6 +18,8 @@ libmessaging_menu_la_CFLAGS = \ $(GIO_CFLAGS) \ -Wall +libmessaging_menu_la_LDFLAGS = -export-symbols-regex="^messaging_menu_.*" + BUILT_SOURCES = \ indicator-messages-service.c \ indicator-messages-service.h -- cgit v1.2.3 From 91eb0b1a17c5bc9d0087fe807f373d1b8a8eb87b Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 21 Aug 2012 01:41:26 +0200 Subject: Use a custom menu item for message source menu items --- libmessaging-menu/messaging-menu.c | 1 + 1 file changed, 1 insertion(+) (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c index 22e4793..ea33cd6 100644 --- a/libmessaging-menu/messaging-menu.c +++ b/libmessaging-menu/messaging-menu.c @@ -439,6 +439,7 @@ messaging_menu_app_insert_source_action (MessagingMenuApp *app, g_object_unref (action); menuitem = g_menu_item_new (label, id); + g_menu_item_set_attribute (menuitem, "x-canonical-type", "s", "ImSourceMenuItem"); if (icon) { gchar *icon_name = g_icon_to_string (icon); -- cgit v1.2.3 From 7d036b65aac90b646eb7845cfc8e229464f372f0 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 21 Aug 2012 11:40:47 +0200 Subject: Show icons in application and source menu items Everthing goes through GIcon now, using g_icon_{to,new_for}_string to set a string attribute on the menu item. The attribute is prefixed x-canonical- for now. --- libmessaging-menu/messaging-menu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'libmessaging-menu') diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c index ea33cd6..336e89c 100644 --- a/libmessaging-menu/messaging-menu.c +++ b/libmessaging-menu/messaging-menu.c @@ -442,9 +442,9 @@ messaging_menu_app_insert_source_action (MessagingMenuApp *app, g_menu_item_set_attribute (menuitem, "x-canonical-type", "s", "ImSourceMenuItem"); if (icon) { - gchar *icon_name = g_icon_to_string (icon); - g_menu_item_set_attribute (menuitem, "indicator-icon-name", icon_name); - g_free (icon_name); + gchar *iconstr = g_icon_to_string (icon); + g_menu_item_set_attribute (menuitem, "x-canonical-icon", "s", iconstr); + g_free (iconstr); } g_menu_insert_item (app->menu, position, menuitem); g_object_unref (menuitem); -- cgit v1.2.3