aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am1
-rw-r--r--common/Makefile.am32
-rw-r--r--common/com.canonical.indicator.messages.application.xml33
-rw-r--r--common/com.canonical.indicator.messages.service.xml (renamed from src/messages-service.xml)0
-rw-r--r--configure.ac1
-rw-r--r--doc/reference/Makefile.am3
-rw-r--r--libmessaging-menu/Makefile.am37
-rw-r--r--libmessaging-menu/gtupleaction.c354
-rw-r--r--libmessaging-menu/gtupleaction.h40
-rw-r--r--libmessaging-menu/messaging-menu-app.c (renamed from libmessaging-menu/messaging-menu.c)716
-rw-r--r--libmessaging-menu/messaging-menu-app.h160
-rw-r--r--libmessaging-menu/messaging-menu-message.c372
-rw-r--r--libmessaging-menu/messaging-menu-message.h64
-rw-r--r--libmessaging-menu/messaging-menu.h125
-rw-r--r--src/Makefile.am30
-rw-r--r--src/app-section.c424
-rw-r--r--src/dbus-data.h2
-rw-r--r--src/gactionmuxer.c8
-rw-r--r--src/gactionmuxer.h3
-rw-r--r--src/messages-service.c552
-rw-r--r--test/Makefile.am5
21 files changed, 1583 insertions, 1379 deletions
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..0b8bad4
--- /dev/null
+++ b/common/Makefile.am
@@ -0,0 +1,32 @@
+
+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
+
+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-application.c \
+ indicator-messages-application.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.application.xml b/common/com.canonical.indicator.messages.application.xml
new file mode 100644
index 0000000..fb9f079
--- /dev/null
+++ b/common/com.canonical.indicator.messages.application.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node name="/">
+ <interface name="com.canonical.indicator.messages.application">
+ <method name="ListSources">
+ <arg type="a(sssuxsb)" name="sources" direction="out" />
+ </method>
+ <method name="ListMessages">
+ <arg type="a(sssssxb)" name="message" direction="out" />
+ </method>
+ <method name="ActivateSource">
+ <arg type="s" name="source_id" direction="in" />
+ </method>
+ <method name="ActivateMessage">
+ <arg type="s" name="message_id" direction="in" />
+ </method>
+ <signal name="SourceAdded">
+ <arg type="u" name="position" direction="in" />
+ <arg type="(sssuxsb)" name="source" direction="in" />
+ </signal>
+ <signal name="SourceChanged">
+ <arg type="(sssuxsb)" name="source" direction="in" />
+ </signal>
+ <signal name="SourceRemoved">
+ <arg type="s" name="source_id" direction="in" />
+ </signal>
+ <signal name="MessageAdded">
+ <arg type="(sssssxb)" name="message" direction="in" />
+ </signal>
+ <signal name="MessageRemoved">
+ <arg type="s" name="message_id" direction="in" />
+ </signal>
+ </interface>
+</node>
diff --git a/src/messages-service.xml b/common/com.canonical.indicator.messages.service.xml
index 00ae154..00ae154 100644
--- a/src/messages-service.xml
+++ b/common/com.canonical.indicator.messages.service.xml
diff --git a/configure.ac b/configure.ac
index 6dad74c..b020d30 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/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 7a6ee31..d18538b 100644
--- a/libmessaging-menu/Makefile.am
+++ b/libmessaging-menu/Makefile.am
@@ -4,36 +4,25 @@ lib_LTLIBRARIES = libmessaging-menu.la
libmessaging_menu_ladir = $(includedir)/messaging-menu
libmessaging_menu_la_SOURCES = \
- messaging-menu.c \
- gtupleaction.c \
- gtupleaction.h \
- $(BUILT_SOURCES)
+ messaging-menu-app.c \
+ messaging-menu-message.c
libmessaging_menu_la_HEADERS = \
- messaging-menu.h
+ 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
@@ -52,7 +41,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.c messaging-menu.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
@@ -62,5 +55,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/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 <http://www.gnu.org/licenses/>.
- *
- * Authors:
- * Lars Uebernickel <lars.uebernickel@canonical.com>
- */
-
-#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 <http://www.gnu.org/licenses/>.
- *
- * Authors:
- * Lars Uebernickel <lars.uebernickel@canonical.com>
- */
-
-#ifndef __g_tuple_action_h__
-#define __g_tuple_action_h__
-
-#include <gio/gio.h>
-
-#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-app.c
index 38883f8..bc7e978 100644
--- a/libmessaging-menu/messaging-menu.c
+++ b/libmessaging-menu/messaging-menu-app.c
@@ -17,10 +17,12 @@
* Lars Uebernickel <lars.uebernickel@canonical.com>
*/
-#include "messaging-menu.h"
+#include "messaging-menu-app.h"
#include "indicator-messages-service.h"
+#include "indicator-messages-application.h"
#include <gio/gdesktopappinfo.h>
+#include <string.h>
/**
* SECTION:messaging-menu
@@ -102,14 +104,14 @@ struct _MessagingMenuApp
int registered; /* -1 for unknown */
MessagingMenuStatus status;
gboolean status_set;
- GSimpleActionGroup *source_actions;
- GMenu *menu;
GDBusConnection *bus;
+ GHashTable *messages;
+ GList *sources;
+ IndicatorMessagesApplication *app_interface;
+
IndicatorMessagesService *messages_service;
guint watch_id;
- guint action_export_id;
- guint menu_export_id;
GCancellable *cancellable;
};
@@ -124,6 +126,7 @@ enum {
enum {
ACTIVATE_SOURCE,
+ ACTIVATE_MESSAGE,
STATUS_CHANGED,
N_SIGNALS
};
@@ -133,10 +136,81 @@ 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 (gpointer data)
+{
+ Source *source = data;
+
+ 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 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)
{
@@ -155,18 +229,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 +245,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 +274,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 +308,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 +329,14 @@ 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);
- 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);
}
@@ -335,6 +386,27 @@ messaging_menu_app_class_init (MessagingMenuAppClass *class)
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
* @status: a #MessagingMenuStatus
@@ -416,6 +488,125 @@ 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_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,
+ 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 SourceRemoved */
+ 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 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)
{
@@ -423,15 +614,19 @@ 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);
+ 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",
@@ -604,123 +799,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;
-
- g_return_if_fail (MESSAGING_MENU_IS_APP (app));
- g_return_if_fail (id != NULL);
+ Source *source;
- if (g_simple_action_group_lookup (app->source_actions, id))
- {
- g_warning ("a source with id '%s' already exists", id);
- return;
- }
+ source = messaging_menu_app_lookup_source (app, id);
+ if (!source)
+ g_warning ("a source with id '%s' doesn't exist", id);
- 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);
-
- 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 +942,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 +996,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 +1052,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 +1092,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 +1113,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 +1129,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 +1157,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);
+ g_clear_object (&source->icon);
+ if (icon)
+ source->icon = g_object_ref (icon);
+ messaging_menu_app_notify_source_changed (app, source);
}
- else
- {
- g_menu_item_set_attribute_value (item, "x-canonical-icon", NULL);
- }
-
- g_menu_replace_item (app->menu, pos, item);
-
- g_object_unref (item);
}
/**
@@ -1120,7 +1184,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 +1210,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 +1236,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 +1265,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,5 +1296,106 @@ 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);
+ }
+}
+
+/**
+ * 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)
+{
+ const gchar *id;
+
+ g_return_if_fail (MESSAGING_MENU_IS_APP (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);
+ }
+ }
+}
+
+/**
+ * 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)
+{
+ messaging_menu_app_remove_message_by_id (app, messaging_menu_message_get_id (msg));
+}
+
+/**
+ * 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);
+
+ 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-app.h b/libmessaging-menu/messaging-menu-app.h
new file mode 100644
index 0000000..a2d27bc
--- /dev/null
+++ b/libmessaging-menu/messaging-menu-app.h
@@ -0,0 +1,160 @@
+/*
+ * 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_app_h__
+#define __messaging_menu_app_h__
+
+#include <gio/gio.h>
+#include "messaging-menu-message.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))
+
+/**
+ * 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);
+
+void messaging_menu_app_append_message (MessagingMenuApp *app,
+ MessagingMenuMessage *msg,
+ const gchar *source_id,
+ gboolean notify);
+
+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..f5cb18c
--- /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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Lars Uebernickel <lars.uebernickel@canonical.com>
+ */
+
+#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) (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
+ * @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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Lars Uebernickel <lars.uebernickel@canonical.com>
+ */
+
+#ifndef __messaging_menu_message_h__
+#define __messaging_menu_message_h__
+
+#include <gio/gio.h>
+
+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
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 <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))
-
-/**
- * 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
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/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 <http://www.gnu.org/licenses/>.
#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/src/dbus-data.h b/src/dbus-data.h
index 64747a9..ceeb546 100644
--- a/src/dbus-data.h
+++ b/src/dbus-data.h
@@ -3,7 +3,7 @@
#define __DBUS_DATA_H__ 1
#define INDICATOR_MESSAGES_DBUS_NAME "com.canonical.indicator.messages"
-#define INDICATOR_MESSAGES_DBUS_OBJECT "/com/canonical/indicator/messages/menu"
+#define INDICATOR_MESSAGES_DBUS_OBJECT "/com/canonical/indicator/messages"
#define INDICATOR_MESSAGES_DBUS_SERVICE_OBJECT "/com/canonical/indicator/messages/service"
#define INDICATOR_MESSAGES_DBUS_SERVICE_INTERFACE "com.canonical.indicator.messages.service"
diff --git a/src/gactionmuxer.c b/src/gactionmuxer.c
index 2b1d11a..0f3cda4 100644
--- a/src/gactionmuxer.c
+++ b/src/gactionmuxer.c
@@ -483,3 +483,11 @@ g_action_muxer_remove (GActionMuxer *muxer,
g_clear_object (&muxer->global_actions);
}
+GActionGroup *
+g_action_muxer_get_group (GActionMuxer *muxer,
+ const gchar *prefix)
+{
+ g_return_val_if_fail (G_IS_ACTION_MUXER (muxer), NULL);
+
+ return prefix ? g_hash_table_lookup (muxer->groups, prefix) : muxer->global_actions;
+}
diff --git a/src/gactionmuxer.h b/src/gactionmuxer.h
index 5c5e839..caf9ec7 100644
--- a/src/gactionmuxer.h
+++ b/src/gactionmuxer.h
@@ -40,5 +40,8 @@ void g_action_muxer_insert (GActionMuxer *muxer,
void g_action_muxer_remove (GActionMuxer *muxer,
const gchar *prefix);
+GActionGroup * g_action_muxer_get_group (GActionMuxer *muxer,
+ const gchar *prefix);
+
#endif
diff --git a/src/messages-service.c b/src/messages-service.c
index b36a0a2..81b7f0b 100644
--- a/src/messages-service.c
+++ b/src/messages-service.c
@@ -28,12 +28,12 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include <gio/gdesktopappinfo.h>
#include <glib/gi18n.h>
-#include "app-section.h"
#include "dbus-data.h"
#include "gactionmuxer.h"
#include "gsettingsstrv.h"
#include "gmenuutils.h"
#include "indicator-messages-service.h"
+#include "indicator-messages-application.h"
#define NUM_STATUSES 5
@@ -44,7 +44,8 @@ static GSimpleActionGroup *actions;
static GActionMuxer *action_muxer;
static GMenu *toplevel_menu;
static GMenu *menu;
-static GMenuModel *chat_section;
+static GMenu *messages_section;
+static GMenu *sources_section;
static GSettings *settings;
static gboolean draws_attention;
static const gchar *global_status[6]; /* max 5: available, away, busy, invisible, offline */
@@ -82,358 +83,172 @@ indicator_messages_get_icon_name ()
return iconstr;
}
-static void
-indicator_messages_update_icon ()
-{
- GSimpleAction *messages;
- gchar *icon;
-
- messages = G_SIMPLE_ACTION (g_simple_action_group_lookup (actions, "messages"));
- g_return_if_fail (messages != NULL);
-
- icon = indicator_messages_get_icon_name ();
- g_simple_action_set_state (messages, g_variant_new_string (icon));
-
- g_free (icon);
-}
-
-static gchar *
-g_app_info_get_simple_id (GAppInfo *appinfo)
-{
- const gchar *id;
-
- id = g_app_info_get_id (appinfo);
- if (!id)
- return NULL;
-
- if (g_str_has_suffix (id, ".desktop"))
- return g_strndup (id, strlen (id) - 8);
- else
- return g_strdup (id);
-}
-
-static void
-actions_changed (GObject *object,
- GParamSpec *pspec,
- gpointer user_data)
+static void
+service_shutdown (IndicatorService * service, gpointer user_data)
{
- AppSection *section = APP_SECTION (object);
- gchar *id;
- GActionGroup *actions;
-
- id = g_app_info_get_simple_id (app_section_get_app_info (section));
- actions = app_section_get_actions (section);
-
- g_action_muxer_insert (action_muxer, id, actions);
- g_free (id);
-}
-
+ GMainLoop *mainloop = user_data;
-static gboolean
-app_section_draws_attention (gpointer key,
- gpointer value,
- gpointer user_data)
-{
- AppSection *section = value;
- return app_section_get_draws_attention (section);
+ g_warning("Shutting down service!");
+ g_main_loop_quit(mainloop);
}
static void
-draws_attention_changed (GObject *object,
- GParamSpec *pspec,
- gpointer user_data)
-{
- GSimpleAction *clear;
-
- clear = G_SIMPLE_ACTION (g_simple_action_group_lookup (actions, "clear"));
- g_return_if_fail (clear != NULL);
-
- draws_attention = g_hash_table_find (applications, app_section_draws_attention, NULL) != NULL;
-
- g_simple_action_set_enabled (clear, draws_attention);
-
- indicator_messages_update_icon ();
-}
-
-static gboolean
-app_section_uses_chat (gpointer key,
- gpointer value,
+clear_action_activate (GSimpleAction *simple,
+ GVariant *param,
gpointer user_data)
{
- AppSection *section = value;
- return app_section_get_uses_chat_status (section);
+ /* TODO */
}
static void
-update_chat_section ()
+status_action_activate (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
- gboolean show_chat;
- GMenuModel *first_section;
-
- show_chat = g_hash_table_find (applications, app_section_uses_chat, NULL) != NULL;
-
- first_section = g_menu_model_get_item_link (G_MENU_MODEL (menu), 0, G_MENU_LINK_SECTION);
- if (first_section == chat_section) {
- if (!show_chat)
- g_menu_remove (menu, 0);
- }
- else {
- if (show_chat)
- g_menu_insert_section (menu, 0, NULL, chat_section);
- }
+ const gchar *status;
- if (first_section != NULL)
- g_object_unref (first_section);
+ status = g_variant_get_string (parameter, NULL);
- indicator_messages_update_icon ();
+ indicator_messages_service_emit_status_changed (messages_service, status);
}
static void
-uses_chat_status_changed (GObject *object,
- GParamSpec *pspec,
- gpointer user_data)
+sources_listed (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
{
- update_chat_section ();
-}
-
-static gboolean
-strv_contains (const gchar **strv,
- const gchar *needle)
-{
- const gchar **it;
-
- it = strv;
- while (*it != NULL && !g_str_equal (*it, needle))
- it++;
-
- return *it != NULL;
-}
+ gchar *app_id = user_data;
+ GVariant *sources;
+ GVariantIter iter;
+ const gchar *id;
+ const gchar *label;
+ const gchar *iconstr;
+ guint32 count;
+ gint64 time;
+ const gchar *string;
+ gboolean draws_attention;
+ GError *error = NULL;
-static void
-update_chat_status ()
-{
- GHashTableIter iter;
- AppSection *section;
- int pos;
- GAction *status;
-
- for (pos = 0; pos < G_N_ELEMENTS (global_status); pos++)
- global_status[pos] = NULL;
-
- pos = 0;
- g_hash_table_iter_init (&iter, applications);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer) &section) &&
- pos < G_N_ELEMENTS (global_status))
+ if (!indicator_messages_application_call_list_sources_finish (INDICATOR_MESSAGES_APPLICATION (source_object),
+ &sources, result, &error))
{
- const gchar *status_str = NULL;
-
- status_str = app_section_get_status (section);
- if (status_str != NULL && !strv_contains (global_status, status_str))
- global_status[pos++] = status_str;
- }
-
- if (pos == 0)
- global_status[0] = "offline";
-
- status = g_simple_action_group_lookup (actions, "status");
- g_return_if_fail (status != NULL);
-
- g_simple_action_set_state (G_SIMPLE_ACTION (status), g_variant_new_strv (global_status, -1));
-
- indicator_messages_update_icon ();
-}
-
-static void
-chat_status_changed (GObject *object,
- GParamSpec *pspec,
- gpointer user_data)
-{
- update_chat_status ();
-}
-
-static void
-remove_section (AppSection *section,
- const gchar *id)
-{
- int pos = g_menu_find_section (menu, app_section_get_menu (section));
- if (pos >= 0)
- g_menu_remove (menu, pos);
- g_action_muxer_remove (action_muxer, id);
-
- g_signal_handlers_disconnect_by_func (section, actions_changed, NULL);
- g_signal_handlers_disconnect_by_func (section, draws_attention_changed, NULL);
- g_signal_handlers_disconnect_by_func (section, uses_chat_status_changed, NULL);
- g_signal_handlers_disconnect_by_func (section, chat_status_changed, NULL);
- g_signal_handlers_disconnect_by_func (section, remove_section, NULL);
-
- g_hash_table_remove (applications, id);
-
- if (g_hash_table_size (applications) == 0 &&
- g_menu_model_get_n_items (G_MENU_MODEL (toplevel_menu)) == 1) {
- g_menu_remove (toplevel_menu, 0);
- }
-
- update_chat_status ();
- update_chat_section ();
-}
-
-static AppSection *
-add_application (const gchar *desktop_id)
-{
- GDesktopAppInfo *appinfo;
- gchar *id;
- AppSection *section;
-
- appinfo = g_desktop_app_info_new (desktop_id);
- if (!appinfo) {
- g_warning ("could not add '%s', there's no desktop file with that id", desktop_id);
- return NULL;
- }
-
- id = g_app_info_get_simple_id (G_APP_INFO (appinfo));
- section = g_hash_table_lookup (applications, id);
-
- if (!section) {
- GMenuItem *menuitem;
-
- section = app_section_new(appinfo);
- g_hash_table_insert (applications, g_strdup (id), section);
-
- g_action_muxer_insert (action_muxer, id, app_section_get_actions (section));
- g_signal_connect (section, "notify::actions",
- G_CALLBACK (actions_changed), NULL);
- g_signal_connect (section, "notify::draws-attention",
- G_CALLBACK (draws_attention_changed), NULL);
- g_signal_connect (section, "notify::uses-chat-status",
- G_CALLBACK (uses_chat_status_changed), NULL);
- g_signal_connect (section, "notify::chat-status",
- G_CALLBACK (chat_status_changed), NULL);
- g_signal_connect_data (section, "destroy",
- G_CALLBACK (remove_section),
- g_strdup (id),
- (GClosureNotify) g_free,
- 0);
-
- /* TODO insert it at the right position (alphabetically by application name) */
- menuitem = g_menu_item_new_section (NULL, app_section_get_menu (section));
- g_menu_item_set_attribute (menuitem, "action-namespace", "s", id);
- g_menu_insert_item (menu, g_menu_model_get_n_items (G_MENU_MODEL (menu)) -1, menuitem);
- g_object_unref (menuitem);
+ g_warning ("could not fetch the list of sources: %s", error->message);
+ g_error_free (error);
+ g_free (app_id);
+ return;
}
- if (g_menu_model_get_n_items (G_MENU_MODEL (toplevel_menu)) == 0) {
- GMenuItem *header;
-
- header = g_menu_item_new (NULL, "messages");
- g_menu_item_set_submenu (header, G_MENU_MODEL (menu));
- g_menu_item_set_attribute (header, "x-canonical-accessible-description", "s", _("Messages"));
- g_menu_append_item (toplevel_menu, header);
-
- g_object_unref (header);
+ g_variant_iter_init (&iter, sources);
+ while (g_variant_iter_next (&iter, "(&s&s&sux&sb)", &id, &label, &iconstr, &count,
+ &time, &string, &draws_attention))
+ {
+ GMenuItem *item;
+ GActionGroup *app_actions;
+ GSimpleAction *action;
+ gchar *action_name;
+
+ app_actions = g_action_muxer_get_group (action_muxer, app_id);
+ g_assert (app_actions);
+ action = g_simple_action_new_stateful (id, NULL, g_variant_new_uint32 (count));
+ g_simple_action_group_insert (G_SIMPLE_ACTION_GROUP (app_actions), G_ACTION (action));
+
+ action_name = g_strconcat (app_id, ".", id, NULL);
+ item = g_menu_item_new (label, action_name);
+ g_menu_item_set_attribute (item, "x-canonical-type", "s", "com.canonical.indicator.messages.sourceitem");
+ if (iconstr && *iconstr)
+ g_menu_item_set_attribute (item, "x-canonical-icon", "s", iconstr);
+ g_menu_append_item (sources_section, item);
+
+ g_object_unref (action);
+ g_free (action_name);
+ g_object_unref (item);
}
- g_free (id);
- g_object_unref (appinfo);
- return section;
+ g_variant_unref (sources);
+ g_free (app_id);
}
static void
-remove_application (const char *desktop_id)
+messages_listed (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
{
- GDesktopAppInfo *appinfo;
- gchar *id;
- AppSection *section;
-
- appinfo = g_desktop_app_info_new (desktop_id);
- if (!appinfo) {
- g_warning ("could not remove '%s', there's no desktop file with that id", desktop_id);
+ gchar *app_id = user_data;
+ GVariant *messages;
+ GError *error = NULL;
+ GVariantIter iter;
+ const gchar *id;
+ const gchar *iconstr;
+ const gchar *title;
+ const gchar *subtitle;
+ const gchar *body;
+ gint64 time;
+ gboolean draws_attention;
+
+ if (!indicator_messages_application_call_list_messages_finish (INDICATOR_MESSAGES_APPLICATION (source_object),
+ &messages, result, &error))
+ {
+ g_warning ("could not fetch the list of messages: %s", error->message);
+ g_error_free (error);
+ g_free (app_id);
return;
}
- id = g_app_info_get_simple_id (G_APP_INFO (appinfo));
-
- section = g_hash_table_lookup (applications, id);
- if (section) {
- remove_section (section, id);
- }
- else {
- g_warning ("could not remove '%s', it's not registered", desktop_id);
- }
-
- g_free (id);
- g_object_unref (appinfo);
-}
-
-/* This function turns a specific desktop id into a menu
- item and registers it appropriately with everyone */
-static gboolean
-build_launcher (gpointer data)
-{
- gchar *desktop_id = data;
-
- add_application (desktop_id);
-
- g_free (desktop_id);
- return FALSE;
-}
-
-/* This function goes through all the launchers that we're
- supposed to be grabbing and decides to show turn them
- into menu items or not. It doens't do the work, but it
- makes the decision. */
-static gboolean
-build_launchers (gpointer data)
-{
- gchar **applications = g_settings_get_strv (settings, "applications");
- gchar **app;
-
- g_return_val_if_fail (applications != NULL, FALSE);
-
- for (app = applications; *app; app++)
+ g_variant_iter_init (&iter, messages);
+ while (g_variant_iter_next (&iter, "(&s&s&s&s&sxb)", &id, &iconstr, &title, &subtitle, &body,
+ &time, &draws_attention))
{
- g_idle_add(build_launcher, g_strdup (*app));
+ GMenuItem *item;
+ GActionGroup *app_actions;
+ GSimpleAction *action;
+ gchar *action_name;
+
+ app_actions = g_action_muxer_get_group (action_muxer, app_id);
+ g_assert (app_actions);
+ action = g_simple_action_new (id, NULL);
+ g_simple_action_group_insert (G_SIMPLE_ACTION_GROUP (app_actions), G_ACTION (action));
+
+ action_name = g_strconcat (app_id, ".", id, NULL);
+ item = g_menu_item_new (title, action_name);
+ g_menu_item_set_attribute (item, "x-canonical-type", "s", "com.canonical.indicator.messages.messageitem");
+ g_menu_item_set_attribute (item, "x-canonical-message-id", "s", id);
+ g_menu_item_set_attribute (item, "x-canonical-subtitle", "s", subtitle);
+ g_menu_item_set_attribute (item, "x-canonical-text", "s", body);
+ g_menu_item_set_attribute (item, "x-canonical-time", "x", time);
+ if (iconstr && *iconstr)
+ g_menu_item_set_attribute (item, "x-canonical-icon", "s", iconstr);
+ g_menu_append_item (messages_section, item);
+
+ g_object_unref (action);
+ g_free (action_name);
+ g_object_unref (item);
}
- g_strfreev (applications);
- return FALSE;
-}
-
-static void
-service_shutdown (IndicatorService * service, gpointer user_data)
-{
- GMainLoop *mainloop = user_data;
-
- g_warning("Shutting down service!");
- g_main_loop_quit(mainloop);
+ g_variant_unref (messages);
+ g_free (app_id);
}
static void
-app_section_remove_attention (gpointer key,
- gpointer value,
- gpointer user_data)
+app_proxy_created (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
{
- AppSection *section = value;
- app_section_clear_draws_attention (section);
-}
-
-static void
-clear_action_activate (GSimpleAction *simple,
- GVariant *param,
- gpointer user_data)
-{
- g_hash_table_foreach (applications, app_section_remove_attention, NULL);
-}
+ gchar *desktop_id = user_data;
+ IndicatorMessagesApplication *app_proxy;
+ GError *error = NULL;
-static void
-status_action_activate (GSimpleAction *action,
- GVariant *parameter,
- gpointer user_data)
-{
- const gchar *status;
+ app_proxy = indicator_messages_application_proxy_new_finish (result, &error);
+ if (!app_proxy) {
+ g_warning ("could not create application proxy: %s", error->message);
+ g_error_free (error);
+ return;
+ }
- status = g_variant_get_string (parameter, NULL);
+ /* hash table takes ownership of desktop_id and app_proxy */
+ g_hash_table_insert (applications, desktop_id, app_proxy);
- indicator_messages_service_emit_status_changed (messages_service, status);
+ indicator_messages_application_call_list_sources (app_proxy, NULL, sources_listed, g_strdup (desktop_id));
+ indicator_messages_application_call_list_messages (app_proxy, NULL, messages_listed, g_strdup (desktop_id));
}
static void
@@ -443,21 +258,33 @@ register_application (IndicatorMessagesService *service,
const gchar *menu_path,
gpointer user_data)
{
- AppSection *section;
GDBusConnection *bus;
const gchar *sender;
+ GSimpleActionGroup *app_actions;
- section = add_application (desktop_id);
- if (!section)
+ if (g_hash_table_lookup (applications, desktop_id)) {
+ g_warning ("application with id '%s' already exists", desktop_id);
+ g_dbus_method_invocation_return_dbus_error (invocation,
+ "com.canonical.indicator.messages.ApplicationAlreadyRegistered",
+ "another application is already registered with this id");
return;
+ }
bus = g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (service));
sender = g_dbus_method_invocation_get_sender (invocation);
- app_section_set_object_path (section, bus, sender, menu_path);
+ indicator_messages_application_proxy_new (bus, G_DBUS_PROXY_FLAGS_NONE,
+ sender, menu_path, NULL,
+ app_proxy_created, g_strdup (desktop_id));
+
+ app_actions = g_simple_action_group_new ();
+ g_action_muxer_insert (action_muxer, desktop_id, G_ACTION_GROUP (app_actions));
+
g_settings_strv_append_unique (settings, "applications", desktop_id);
indicator_messages_service_complete_register_application (service, invocation);
+
+ g_object_unref (app_actions);
}
static void
@@ -466,44 +293,15 @@ unregister_application (IndicatorMessagesService *service,
const gchar *desktop_id,
gpointer user_data)
{
- remove_application (desktop_id);
- g_settings_strv_remove (settings, "applications", desktop_id);
+ if (g_hash_table_remove (applications, desktop_id)) {
- indicator_messages_service_complete_unregister_application (service, invocation);
-}
+ /* TODO remove menu items that refer to this application */
+ g_action_muxer_remove (action_muxer, desktop_id);
-static void
-set_status (IndicatorMessagesService *service,
- GDBusMethodInvocation *invocation,
- const gchar *desktop_id,
- const gchar *status_str,
- gpointer user_data)
-{
- GDesktopAppInfo *appinfo;
- gchar *id;
- AppSection *section;
-
- g_return_if_fail (g_str_equal (status_str, "available") ||
- g_str_equal (status_str, "away")||
- g_str_equal (status_str, "busy") ||
- g_str_equal (status_str, "invisible") ||
- g_str_equal (status_str, "offline"));
-
- appinfo = g_desktop_app_info_new (desktop_id);
- if (!appinfo) {
- g_warning ("could not set status for '%s', there's no desktop file with that id", desktop_id);
- return;
+ g_settings_strv_remove (settings, "applications", desktop_id);
}
- id = g_app_info_get_simple_id (G_APP_INFO (appinfo));
- section = g_hash_table_lookup (applications, id);
- if (section != NULL)
- app_section_set_status (section, status_str);
-
- indicator_messages_service_complete_set_status (service, invocation);
-
- g_free (id);
- g_object_unref (appinfo);
+ indicator_messages_service_complete_unregister_application (service, invocation);
}
static GSimpleActionGroup *
@@ -539,40 +337,6 @@ create_action_group (void)
return actions;
}
-static GMenuModel *
-create_status_section (void)
-{
- GMenu *menu;
- GMenuItem *item;
- struct status_item {
- gchar *label;
- gchar *action;
- gchar *icon_name;
- } status_items[] = {
- { _("Available"), "status::available", "user-available" },
- { _("Away"), "status::away", "user-away" },
- { _("Busy"), "status::busy", "user-busy" },
- { _("Invisible"), "status::invisible", "user-invisible" },
- { _("Offline"), "status::offline", "user-offline" }
- };
- int i;
-
- menu = g_menu_new ();
-
- item = g_menu_item_new (NULL, NULL);
- g_menu_item_set_attribute (item, "x-canonical-type", "s", "IdoMenuItem");
-
- for (i = 0; i < G_N_ELEMENTS (status_items); i++) {
- g_menu_item_set_label (item, status_items[i].label);
- g_menu_item_set_detailed_action (item, status_items[i].action);
- g_menu_item_set_attribute (item, "x-canonical-icon", "s", status_items[i].icon_name);
- g_menu_append_item (menu, item);
- }
-
- g_object_unref (item);
- return G_MENU_MODEL (menu);
-}
-
static void
got_bus (GObject *object,
GAsyncResult * res,
@@ -596,8 +360,8 @@ got_bus (GObject *object,
return;
}
- g_dbus_connection_export_menu_model (bus, INDICATOR_MESSAGES_DBUS_OBJECT,
- G_MENU_MODEL (toplevel_menu), &error);
+ g_dbus_connection_export_menu_model (bus, INDICATOR_MESSAGES_DBUS_OBJECT "/phone",
+ G_MENU_MODEL (menu), &error);
if (error) {
g_warning ("unable to export menu on dbus: %s", error->message);
g_error_free (error);
@@ -651,26 +415,24 @@ main (int argc, char ** argv)
G_CALLBACK (register_application), NULL);
g_signal_connect (messages_service, "handle-unregister-application",
G_CALLBACK (unregister_application), NULL);
- g_signal_connect (messages_service, "handle-set-status",
- G_CALLBACK (set_status), NULL);
menu = g_menu_new ();
- chat_section = create_status_section ();
- g_menu_append (menu, _("Clear"), "clear");
+ sources_section = g_menu_new ();
+ messages_section = g_menu_new ();
+ g_menu_append_section (menu, NULL, G_MENU_MODEL (sources_section));
+ g_menu_append_section (menu, NULL, G_MENU_MODEL (messages_section));
toplevel_menu = g_menu_new ();
+ g_menu_append_submenu (toplevel_menu, NULL, G_MENU_MODEL (menu));
settings = g_settings_new ("com.canonical.indicator.messages");
applications = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
- g_idle_add(build_launchers, NULL);
-
g_main_loop_run(mainloop);
/* Clean up */
g_object_unref (messages_service);
- g_object_unref (chat_section);
g_object_unref (settings);
g_hash_table_unref (applications);
return 0;
diff --git a/test/Makefile.am b/test/Makefile.am
index 4671446..8c8c160 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 \
@@ -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 \