aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Uebernickel <lars.uebernickel@canonical.com>2012-06-15 14:59:15 +0200
committerLars Uebernickel <lars.uebernickel@canonical.com>2012-06-15 14:59:15 +0200
commit78edb4ee5c8a585986fa092ee4ec81fad442f64f (patch)
tree748bc68e301d19801fb779551bbe242d1ab76367
parentbc9cd727c2bb0116c94e1bf258e257d5bff406f8 (diff)
downloadayatana-indicator-messages-78edb4ee5c8a585986fa092ee4ec81fad442f64f.tar.gz
ayatana-indicator-messages-78edb4ee5c8a585986fa092ee4ec81fad442f64f.tar.bz2
ayatana-indicator-messages-78edb4ee5c8a585986fa092ee4ec81fad442f64f.zip
Add first version of a libmessaging-menu client library
-rw-r--r--.bzrignore4
-rw-r--r--Makefile.am1
-rw-r--r--configure.ac4
-rw-r--r--libmessaging-menu/Makefile.am35
-rw-r--r--libmessaging-menu/messaging-menu.c746
-rw-r--r--libmessaging-menu/messaging-menu.h130
-rw-r--r--libmessaging-menu/messaging-menu.pc.in11
7 files changed, 931 insertions, 0 deletions
diff --git a/.bzrignore b/.bzrignore
index e89bdf7..431e715 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -43,4 +43,8 @@ stamp-*
/src/indicator-messages-status-provider-0.5.pc
/src/indicator-messages-status-provider-0.5.pc.in
+/libmessaging-menu/messaging-menu.pc
+/libmessaging-menu/indicator-messages-service.c
+/libmessaging-menu/indicator-messages-service.h
+
test/test-gactionmuxer
diff --git a/Makefile.am b/Makefile.am
index bef67bd..9bd3903 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,6 +1,7 @@
SUBDIRS = \
src \
+ libmessaging-menu \
data \
po
diff --git a/configure.ac b/configure.ac
index e3beaa2..9ef2438 100644
--- a/configure.ac
+++ b/configure.ac
@@ -52,6 +52,8 @@ PKG_CHECK_MODULES(APPLET, gtk+-3.0 >= $GTK_REQUIRED_VERSION
gmodule-2.0 >= $GLIB_REQUIRED_VERSION
menu-factory-gtk >= $MENU_FACTORY_REQUIRED_VERSION)
+PKG_CHECK_MODULES(GIO, gio-unix-2.0 >= $GIO_UNIX_REQUIRED_VERSION)
+
AC_SUBST(APPLET_CFLAGS)
AC_SUBST(APPLET_LIBS)
@@ -198,6 +200,8 @@ data/icons/scalable/status/Makefile
data/icons/scalable/categories/Makefile
po/Makefile.in
test/Makefile
+libmessaging-menu/Makefile
+libmessaging-menu/messaging-menu.pc
])
###########################
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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Lars Uebernickel <lars.uebernickel@canonical.com>
+ */
+
+#include "messaging-menu.h"
+#include "indicator-messages-service.h"
+
+#include <gio/gdesktopappinfo.h>
+
+/**
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Lars Uebernickel <lars.uebernickel@canonical.com>
+ */
+
+#ifndef __messaging_menu_h__
+#define __messaging_menu_h__
+
+#include <gio/gio.h>
+
+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}