aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Uebernickel <lars.uebernickel@canonical.com>2012-08-21 12:07:19 +0200
committerLars Uebernickel <lars.uebernickel@canonical.com>2012-08-21 12:07:19 +0200
commit374fa7bb7eb2526f1cf19bf85edd5793d8ef569b (patch)
treeeae99c80f31d940226680a31100024cd2b4a71c4
parent13f2b1bb69fb69aadcdc838b2321aa2b01657e06 (diff)
parent5df53a2c98d806f3b266d0032986d4dab3beb7a3 (diff)
downloadayatana-indicator-messages-374fa7bb7eb2526f1cf19bf85edd5793d8ef569b.tar.gz
ayatana-indicator-messages-374fa7bb7eb2526f1cf19bf85edd5793d8ef569b.tar.bz2
ayatana-indicator-messages-374fa7bb7eb2526f1cf19bf85edd5793d8ef569b.zip
Merge lp:~larsu/indicator-messags/towards-q-redesign
This branch introduces libmessaging-menu, a new library for applications to integrate with the messaging menu. Libindicate is not supported anymore. libmessaging-menu uses GMenuModel to communicate with indicator-messages-service. In order to take advantage of GMenuModel's architecture (re-exporting menus on a different path), the service now also sends its menu as a GMenuModel to the panel plugin. The plugin uses gtk_menu_new_from_model to create the menu widgets. Custom menu items are created by a small gtk+ patch that watches for x-canonical-type attributes. The branch also contains most of the design changes for quantal.
-rw-r--r--.bzrignore99
-rw-r--r--Makefile.am3
-rwxr-xr-xautogen.sh14
-rw-r--r--configure.ac39
-rw-r--r--data/Makefile.am7
-rw-r--r--data/com.canonical.indicator.messages.gschema.xml12
-rw-r--r--libmessaging-menu/Makefile.am64
-rw-r--r--libmessaging-menu/gtupleaction.c354
-rw-r--r--libmessaging-menu/gtupleaction.h40
-rw-r--r--libmessaging-menu/messaging-menu.c837
-rw-r--r--libmessaging-menu/messaging-menu.h130
-rw-r--r--libmessaging-menu/messaging-menu.pc.in11
-rw-r--r--po/POTFILES.in6
-rw-r--r--src/Makefile.am247
-rw-r--r--src/app-menu-item.c579
-rw-r--r--src/app-menu-item.h72
-rw-r--r--src/app-section.c695
-rw-r--r--src/app-section.h72
-rw-r--r--src/dbus-data.h13
-rw-r--r--src/default-applications.c116
-rw-r--r--src/default-applications.h34
-rw-r--r--src/dirs.h4
-rw-r--r--src/gactionmuxer.c485
-rw-r--r--src/gactionmuxer.h44
-rw-r--r--src/gmenuutils.c79
-rw-r--r--src/gmenuutils.h38
-rw-r--r--src/gsettingsstrv.c106
-rw-r--r--src/gsettingsstrv.h33
-rw-r--r--src/im-app-menu-item.c350
-rw-r--r--src/im-app-menu-item.h54
-rw-r--r--src/im-menu-item.c586
-rw-r--r--src/im-menu-item.h67
-rw-r--r--src/im-source-menu-item.c410
-rw-r--r--src/im-source-menu-item.h54
-rw-r--r--src/indicator-messages-status-provider-0.5.pc.in.in15
-rw-r--r--src/indicator-messages.c736
-rw-r--r--src/launcher-menu-item.c395
-rw-r--r--src/launcher-menu-item.h68
-rw-r--r--src/messages-service-dbus.c472
-rw-r--r--src/messages-service-dbus.h62
-rw-r--r--src/messages-service.c1727
-rw-r--r--src/messages-service.xml24
-rw-r--r--src/seen-db.c177
-rw-r--r--src/seen-db.h31
-rw-r--r--src/status-items.c318
-rw-r--r--src/status-items.h38
-rw-r--r--src/status-provider-emesene.c348
-rw-r--r--src/status-provider-emesene.h56
-rw-r--r--src/status-provider-mc5.c308
-rw-r--r--src/status-provider-mc5.h56
-rw-r--r--src/status-provider-mc5.list1
-rw-r--r--src/status-provider-pidgin.c433
-rw-r--r--src/status-provider-pidgin.h56
-rw-r--r--src/status-provider-pidgin.list1
-rw-r--r--src/status-provider-telepathy.c385
-rw-r--r--src/status-provider-telepathy.h56
-rw-r--r--src/status-provider-telepathy.list1
-rw-r--r--src/status-provider.c101
-rw-r--r--src/status-provider.h81
-rw-r--r--test/Makefile.am59
-rw-r--r--test/test-gactionmuxer.cpp365
-rw-r--r--test/test-gtest.cpp12
62 files changed, 4828 insertions, 7308 deletions
diff --git a/.bzrignore b/.bzrignore
index 3ba685c..b3415fa 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -1,49 +1,50 @@
-compile
-m4
-po
-src/libmessaging.la
-src/libmessaging_la-indicator-messages.lo
-config.guess.cdbs-orig
-config.sub.cdbs-orig
-debian/compat
-debian/files
-debian/indicator-messages
-debian/indicator-messages.debhelper.log
-debian/indicator-messages.postinst.debhelper
-debian/indicator-messages.postrm.debhelper
-debian/indicator-messages.substvars
-debian/stamp-autotools-files
-debian/stamp-makefile-build
-libmessaging_la-im-menu-item.lo
-src/libmessaging_la-app-menu-item.lo
-data/indicator-messages.service
-indicator-messages-service
-indicator-messages-service-activate
-src/messages-service-client.h
-src/messages-service-server.h
-po/indicator-messages.pot
-src/libmessaging_la-app-gtk-menu-item.lo
-src/gen-messages-service.xml.c
-src/gen-messages-service.xml.h
-src/libemesene.la
-src/libemesene_la-status-provider-emesene.lo
-src/libmc5.la
-src/libmc5_la-status-provider-mc5-marshal.lo
-src/libmc5_la-status-provider-mc5.lo
-src/libmessaging_la-gen-messages-service.xml.lo
-src/libpidgin.la
-src/libpidgin_la-status-provider-pidgin-marshal.lo
-src/libpidgin_la-status-provider-pidgin.lo
-src/libtelepathy.la
-src/libtelepathy_la-status-provider-telepathy-marshal.lo
-src/libtelepathy_la-status-provider-telepathy.lo
-src/status-provider-mc5-marshal.c
-src/status-provider-mc5-marshal.h
-src/status-provider-pidgin-marshal.c
-src/status-provider-pidgin-marshal.h
-src/status-provider-telepathy-marshal.c
-src/status-provider-telepathy-marshal.h
-src/libindicator-messages-status-provider.la
-src/libindicator_messages_status_provider_la-status-provider.lo
-indicator-messages-status-provider-0.5.pc
-indicator-messages-status-provider-0.5.pc.in
+Makefile
+Makefile.in
+Makefile.in.in
+*.o
+*.lo
+*.la
+*.pot
+.deps
+.libs
+*.valid
+stamp-*
+*.gcno
+*.gcda
+
+/COPYING
+/INSTALL
+/compile
+/libtool
+/config.*
+/configure
+/aclocal.m4
+/ltmain.sh
+/install-sh
+/missing
+/autom4te.cache
+/depcomp
+/mkinstalldirs
+/coverage.info
+/coveragereport
+
+/m4/*
+/po/POTFILES
+/data/indicator-messages.service
+/src/indicator-messages-service
+/src/indicator-messages-service.c
+/src/indicator-messages-service.h
+/src/status-provider-mc5-marshal.c
+/src/status-provider-mc5-marshal.h
+/src/status-provider-pidgin-marshal.c
+/src/status-provider-pidgin-marshal.h
+/src/status-provider-telepathy-marshal.c
+/src/status-provider-telepathy-marshal.h
+/src/indicator-messages-status-provider-0.5.pc
+/src/indicator-messages-status-provider-0.5.pc.in
+
+/libmessaging-menu/messaging-menu.pc
+/libmessaging-menu/indicator-messages-service.c
+/libmessaging-menu/indicator-messages-service.h
+
+test/test-gactionmuxer
diff --git a/Makefile.am b/Makefile.am
index bef67bd..ef08e41 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,6 +1,7 @@
SUBDIRS = \
src \
+ libmessaging-menu \
data \
po
@@ -16,7 +17,7 @@ endif
-DISTCHECK_CONFIGURE_FLAGS = --enable-localinstall --enable-deprecations
+DISTCHECK_CONFIGURE_FLAGS = --enable-localinstall --enable-deprecations --enable-introspection
dist-hook:
@if test -d "$(top_srcdir)/.bzr"; \
diff --git a/autogen.sh b/autogen.sh
index 1abf3a7..a49cd41 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -1,11 +1,5 @@
-#!/bin/sh
+#!/bin/sh -e
-PKG_NAME="indicator-messages"
-
-which gnome-autogen.sh || {
- echo "You need gnome-common from GNOME SVN"
- exit 1
-}
-
-USE_GNOME2_MACROS=1 \
-. gnome-autogen.sh
+autoreconf -i -f
+intltoolize
+gtkdocize
diff --git a/configure.ac b/configure.ac
index ca879a1..dc56181 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,10 +1,10 @@
-AC_INIT(src/indicator-messages.c)
+AC_INIT(indicator-messages, 0.6.0)
-AC_PREREQ(2.53)
+AC_PREREQ(2.62)
AM_CONFIG_HEADER(config.h)
-AM_INIT_AUTOMAKE(indicator-messages, 0.6.0)
+AM_INIT_AUTOMAKE()
AM_MAINTAINER_MODE
@@ -41,43 +41,26 @@ AC_PROG_CXX
GTK_REQUIRED_VERSION=3.0
GIO_UNIX_REQUIRED_VERSION=2.18
PANEL_REQUIRED_VERSION=2.0.0
-INDICATE_REQUIRED_VERSION=0.6.90
INDICATOR_REQUIRED_VERSION=0.3.19
-DBUSMENUGTK_REQUIRED_VERSION=0.5.90
GLIB_REQUIRED_VERSION=2.31.20
+INTROSPECTION_REQUIRED_VERSION=1.32.0
PKG_CHECK_MODULES(APPLET, gtk+-3.0 >= $GTK_REQUIRED_VERSION
gio-unix-2.0 >= $GIO_UNIX_REQUIRED_VERSION
indicator3-0.4 >= $INDICATOR_REQUIRED_VERSION
- indicate-0.7 >= $INDICATE_REQUIRED_VERSION
- dbusmenu-gtk3-0.4 >= $DBUSMENUGTK_REQUIRED_VERSION
glib-2.0 >= $GLIB_REQUIRED_VERSION
gmodule-2.0 >= $GLIB_REQUIRED_VERSION)
+PKG_CHECK_MODULES(GIO, gio-unix-2.0 >= $GIO_UNIX_REQUIRED_VERSION)
+
AC_SUBST(APPLET_CFLAGS)
AC_SUBST(APPLET_LIBS)
-###########################
-# Status Provider Deps
-###########################
-
-PKG_CHECK_MODULES(STATUS_PROVIDER_PIDGIN, dbus-glib-1)
-AC_SUBST(STATUS_PROVIDER_PIDGIN_CFLAGS)
-AC_SUBST(STATUS_PROVIDER_PIDGIN_LIBS)
-
-PKG_CHECK_MODULES(STATUS_PROVIDER_TELEPATHY, dbus-glib-1)
-AC_SUBST(STATUS_PROVIDER_TELEPATHY_CFLAGS)
-AC_SUBST(STATUS_PROVIDER_TELEPATHY_LIBS)
+GLIB_GSETTINGS
-TELEPATHYGLIB_REQUIRED_VERSION=0.9.0
-PKG_CHECK_MODULES(STATUS_PROVIDER_MC5, dbus-glib-1
- telepathy-glib >= $TELEPATHYGLIB_REQUIRED_VERSION)
-AC_SUBST(STATUS_PROVIDER_MC5_CFLAGS)
-AC_SUBST(STATUS_PROVIDER_MC5_LIBS)
+GTK_DOC_CHECK([1.18], [])
-PKG_CHECK_MODULES(STATUS_PROVIDER_EMESENE, dbus-glib-1)
-AC_SUBST(STATUS_PROVIDER_EMESENE_CFLAGS)
-AC_SUBST(STATUS_PROVIDER_EMESENE_LIBS)
+GOBJECT_INTROSPECTION_CHECK([$INTROSPECTION_REQUIRED_VERSION])
###########################
# gcov coverage reporting
@@ -177,7 +160,6 @@ AM_GLIB_GNU_GETTEXT
AC_OUTPUT([
Makefile
src/Makefile
-src/indicator-messages-status-provider-0.5.pc.in
data/Makefile
data/icons/Makefile
data/icons/16x16/Makefile
@@ -198,6 +180,8 @@ data/icons/scalable/status/Makefile
data/icons/scalable/categories/Makefile
po/Makefile.in
test/Makefile
+libmessaging-menu/Makefile
+libmessaging-menu/messaging-menu.pc
])
###########################
@@ -212,4 +196,5 @@ Messaging Indicator Configuration:
Indicator Dir: $INDICATORDIR
gtest: $enable_tests
gcov: $use_gcov
+ introspecion: $enable_introspection
])
diff --git a/data/Makefile.am b/data/Makefile.am
index 351601b..488ce05 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -6,6 +6,11 @@ dbus_services_DATA = indicator-messages.service
%.service: %.service.in
sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@
-EXTRA_DIST = indicator-messages.service.in
+gsettings_SCHEMAS = com.canonical.indicator.messages.gschema.xml
+@GSETTINGS_RULES@
+
+EXTRA_DIST = \
+ $(gsettings_SCHEMAS) \
+ indicator-messages.service.in
CLEANFILES = indicator-messages.service
diff --git a/data/com.canonical.indicator.messages.gschema.xml b/data/com.canonical.indicator.messages.gschema.xml
new file mode 100644
index 0000000..d9165cd
--- /dev/null
+++ b/data/com.canonical.indicator.messages.gschema.xml
@@ -0,0 +1,12 @@
+<schemalist>
+ <schema id="com.canonical.indicator.messages" path="/com/canonical/indicator/messages/" gettext-domain="indicator-messages">
+ <key name="applications" type="as">
+ <summary>List of applications that are shown in the messaging menu</summary>
+ <description>
+ Applications corresponding to the desktop file ids in this list are shown in the messaging menu.
+ </description>
+ <default>[]</default>
+ </key>
+ </schema>
+</schemalist>
+
diff --git a/libmessaging-menu/Makefile.am b/libmessaging-menu/Makefile.am
new file mode 100644
index 0000000..187e6dc
--- /dev/null
+++ b/libmessaging-menu/Makefile.am
@@ -0,0 +1,64 @@
+
+lib_LTLIBRARIES = libmessaging-menu.la
+
+libmessaging_menu_ladir = $(includedir)/messaging-menu
+
+libmessaging_menu_la_SOURCES = \
+ messaging-menu.c \
+ gtupleaction.c \
+ gtupleaction.h \
+ $(BUILT_SOURCES)
+
+libmessaging_menu_la_HEADERS = \
+ messaging-menu.h
+
+libmessaging_menu_la_LIBADD = $(GIO_LIBS)
+
+libmessaging_menu_la_CFLAGS = \
+ $(GIO_CFLAGS) \
+ -Wall
+
+libmessaging_menu_la_LDFLAGS = -export-symbols-regex="^messaging_menu_.*"
+
+BUILT_SOURCES = \
+ indicator-messages-service.c \
+ indicator-messages-service.h
+
+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
+
+
+-include $(INTROSPECTION_MAKEFILE)
+
+INTROSPECTION_GIRS =
+INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir)
+INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir)
+
+if HAVE_INTROSPECTION
+
+MessagingMenu-1.0.gir: libmessaging-menu.la
+MessagingMenu_1_0_gir_NAMESPACE = MessagingMenu
+MessagingMenu_1_0_gir_INCLUDES = GObject-2.0 Gio-2.0
+MessagingMenu_1_0_gir_CFLAGS = $(INCLUDES) $(GIO_CFLAGS)
+MessagingMenu_1_0_gir_LIBS = libmessaging-menu.la
+MessagingMenu_1_0_gir_FILES = $(libmessaging_menu_la_SOURCES) $(libmessaging_menu_la_HEADERS)
+INTROSPECTION_GIRS += MessagingMenu-1.0.gir
+
+girdir = $(datadir)/gir-1.0
+gir_DATA = $(INTROSPECTION_GIRS)
+
+typelibdir = $(libdir)/girepository-1.0
+typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
+
+CLEANFILES +=$(gir_DATA) $(typelib_DATA)
+endif
diff --git a/libmessaging-menu/gtupleaction.c b/libmessaging-menu/gtupleaction.c
new file mode 100644
index 0000000..21bc003
--- /dev/null
+++ b/libmessaging-menu/gtupleaction.c
@@ -0,0 +1,354 @@
+/*
+ * 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
new file mode 100644
index 0000000..c447d71
--- /dev/null
+++ b/libmessaging-menu/gtupleaction.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2012 Canonical Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <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.c
new file mode 100644
index 0000000..336e89c
--- /dev/null
+++ b/libmessaging-menu/messaging-menu.c
@@ -0,0 +1,837 @@
+/*
+ * Copyright 2012 Canonical Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Lars Uebernickel <lars.uebernickel@canonical.com>
+ */
+
+#include "messaging-menu.h"
+#include "indicator-messages-service.h"
+#include "gtupleaction.h"
+
+#include <gio/gdesktopappinfo.h>
+
+/**
+ * SECTION:messagingmenuapp
+ * @title: MessagingMenuApp
+ * @short_description: An application section in the messaging menu
+ */
+struct _MessagingMenuApp
+{
+ GObject parent_instance;
+
+ GDesktopAppInfo *appinfo;
+ int registered; /* -1 for unknown */
+ MessagingMenuStatus status;
+ GSimpleActionGroup *source_actions;
+ GMenu *menu;
+
+ IndicatorMessagesService *messages_service;
+
+ GCancellable *cancellable;
+};
+
+G_DEFINE_TYPE (MessagingMenuApp, messaging_menu_app, G_TYPE_OBJECT);
+
+enum
+{
+ INDEX_COUNT,
+ INDEX_TIME,
+ INDEX_STRING,
+ INDEX_DRAWS_ATTENTION
+};
+
+enum {
+ PROP_0,
+ PROP_DESKTOP_ID,
+ N_PROPERTIES
+};
+
+enum {
+ ACTIVATE_SOURCE,
+ STATUS_CHANGED,
+ N_SIGNALS
+};
+
+static GParamSpec *properties[N_PROPERTIES];
+static guint signals[N_SIGNALS];
+
+static const gchar *status_ids[] = { "available", "away", "busy", "invisible", "offline" };
+
+static void global_status_changed (IndicatorMessagesService *service,
+ const gchar *status_str,
+ gpointer user_data);
+
+static void
+messaging_menu_app_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MessagingMenuApp *app = MESSAGING_MENU_APP (object);
+
+ switch (prop_id)
+ {
+ case PROP_DESKTOP_ID:
+ app->appinfo = g_desktop_app_info_new (g_value_get_string (value));
+ if (app->appinfo == NULL)
+ {
+ g_warning ("could not find the desktop file for '%s'",
+ g_value_get_string (value));
+ }
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+messaging_menu_app_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (messaging_menu_app_parent_class)->finalize (object);
+}
+
+static void
+messaging_menu_app_dispose (GObject *object)
+{
+ MessagingMenuApp *app = MESSAGING_MENU_APP (object);
+
+ if (app->cancellable)
+ {
+ g_cancellable_cancel (app->cancellable);
+ g_object_unref (app->cancellable);
+ app->cancellable = NULL;
+ }
+
+ if (app->messages_service)
+ {
+ g_signal_handlers_disconnect_by_func (app->messages_service,
+ global_status_changed,
+ app);
+ g_clear_object (&app->messages_service);
+ }
+
+ g_clear_object (&app->appinfo);
+ g_clear_object (&app->source_actions);
+ g_clear_object (&app->menu);
+
+ G_OBJECT_CLASS (messaging_menu_app_parent_class)->dispose (object);
+}
+
+static void
+messaging_menu_app_class_init (MessagingMenuAppClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ object_class->set_property = messaging_menu_app_set_property;
+ object_class->finalize = messaging_menu_app_finalize;
+ object_class->dispose = messaging_menu_app_dispose;
+
+ properties[PROP_DESKTOP_ID] = g_param_spec_string ("desktop-id",
+ "Desktop Id",
+ "The desktop id of the associated application",
+ NULL,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPERTIES, properties);
+
+ signals[ACTIVATE_SOURCE] = g_signal_new ("activate-source",
+ MESSAGING_MENU_TYPE_APP,
+ G_SIGNAL_RUN_FIRST |
+ G_SIGNAL_DETAILED,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+
+ signals[STATUS_CHANGED] = g_signal_new ("status-changed",
+ MESSAGING_MENU_TYPE_APP,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE, 1, G_TYPE_INT);
+}
+
+static void
+created_messages_service (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ MessagingMenuApp *app = user_data;
+ GError *error = NULL;
+
+ app->messages_service = indicator_messages_service_proxy_new_finish (result, &error);
+ if (!app->messages_service)
+ {
+ g_warning ("unable to connect to the mesaging menu service: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ g_signal_connect (app->messages_service, "status-changed",
+ G_CALLBACK (global_status_changed), app);
+
+ /* sync current status */
+ if (app->registered == TRUE)
+ messaging_menu_app_register (app);
+ else if (app->registered == FALSE)
+ messaging_menu_app_unregister (app);
+ messaging_menu_app_set_status (app, app->status);
+}
+
+static void
+got_session_bus (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ MessagingMenuApp *app = user_data;
+ GDBusConnection *bus;
+ GError *error = NULL;
+ guint id;
+
+ bus = g_bus_get_finish (res, &error);
+ if (bus == NULL)
+ {
+ g_warning ("unable to connect to session bus: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ id = g_dbus_connection_export_action_group (bus,
+ "/com/canonical/indicator/messages",
+ G_ACTION_GROUP (app->source_actions),
+ &error);
+ if (!id)
+ {
+ g_warning ("unable to export action group: %s", error->message);
+ g_error_free (error);
+ }
+
+ id = g_dbus_connection_export_menu_model (bus,
+ "/com/canonical/indicator/messages",
+ G_MENU_MODEL (app->menu),
+ &error);
+ if (!id)
+ {
+ g_warning ("unable to export menu: %s", error->message);
+ g_error_free (error);
+ }
+
+ indicator_messages_service_proxy_new (bus,
+ G_DBUS_PROXY_FLAGS_NONE,
+ "com.canonical.indicator.messages",
+ "/com/canonical/indicator/messages/service",
+ app->cancellable,
+ created_messages_service,
+ app);
+
+ g_object_unref (bus);
+}
+
+static void
+messaging_menu_app_init (MessagingMenuApp *app)
+{
+ app->registered = -1;
+ app->status = MESSAGING_MENU_STATUS_OFFLINE;
+
+ app->cancellable = g_cancellable_new ();
+
+ app->source_actions = g_simple_action_group_new ();
+ app->menu = g_menu_new ();
+
+ app->cancellable = g_cancellable_new ();
+
+
+ g_bus_get (G_BUS_TYPE_SESSION,
+ app->cancellable,
+ got_session_bus,
+ app);
+}
+
+/**
+ * messaging_menu_new:
+ * @desktop_id: a desktop file id. See g_desktop_app_info_new()
+ *
+ * Creates a new #MessagingMenuApp for the application associated with
+ * @desktop_id.
+ *
+ * If the application is already registered with the messaging menu, it will be
+ * marked as "running". Otherwise, call messaging_menu_app_register().
+ *
+ * The messaging menu will return to marking the application as not running as
+ * soon as the returned #MessagingMenuApp is destroyed.
+ *
+ * Returns: (transfer-full): a new #MessagingMenuApp
+ */
+MessagingMenuApp *
+messaging_menu_app_new (const gchar *desktop_id)
+{
+ return g_object_new (MESSAGING_MENU_TYPE_APP,
+ "desktop-id", desktop_id,
+ NULL);
+}
+
+/**
+ * messaging_menu_app_register:
+ * @app: a #MessagingMenuApp
+ *
+ * Registers @app with the messaging menu.
+ *
+ * The messaging menu will add a section with an app launcher and the shortcuts
+ * defined in its desktop file.
+ *
+ * The application will be marked as "running" as long as @app is alive or
+ * messaging_menu_app_unregister() is called.
+ */
+void
+messaging_menu_app_register (MessagingMenuApp *app)
+{
+ g_return_if_fail (MESSAGING_MENU_IS_APP (app));
+
+ app->registered = TRUE;
+
+ /* state will be synced right after connecting to the service */
+ if (!app->messages_service)
+ return;
+
+ indicator_messages_service_call_register_application (app->messages_service,
+ g_app_info_get_id (G_APP_INFO (app->appinfo)),
+ "/com/canonical/indicator/messages",
+ app->cancellable,
+ NULL, NULL);
+}
+
+/**
+ * messaging_menu_app_unregister:
+ * @app: a #MessagingMenuApp
+ *
+ * Completely removes the application associated with @desktop_id from the
+ * messaging menu.
+ *
+ * Note: @app will remain valid and usable after this call.
+ */
+void
+messaging_menu_app_unregister (MessagingMenuApp *app)
+{
+ g_return_if_fail (MESSAGING_MENU_IS_APP (app));
+
+ app->registered = FALSE;
+
+ /* state will be synced right after connecting to the service */
+ if (!app->messages_service)
+ return;
+
+ indicator_messages_service_call_unregister_application (app->messages_service,
+ g_app_info_get_id (G_APP_INFO (app->appinfo)),
+ app->cancellable,
+ NULL, NULL);
+}
+
+/**
+ * messaging_menu_app_set_status:
+ * @app: a #MessagingMenuApp
+ * @status: a #MessagingMenuStatus
+ */
+void
+messaging_menu_app_set_status (MessagingMenuApp *app,
+ MessagingMenuStatus status)
+{
+ g_return_if_fail (MESSAGING_MENU_IS_APP (app));
+ g_return_if_fail (status >= MESSAGING_MENU_STATUS_AVAILABLE &&
+ status <= MESSAGING_MENU_STATUS_OFFLINE);
+
+ app->status = status;
+
+ /* state will be synced right after connecting to the service */
+ if (!app->messages_service)
+ return;
+
+ indicator_messages_service_call_set_status (app->messages_service,
+ status_ids [status],
+ app->cancellable,
+ NULL, NULL);
+}
+
+static int
+status_from_string (const gchar *s)
+{
+ int i;
+
+ if (!s)
+ return -1;
+
+ for (i = 0; i <= MESSAGING_MENU_STATUS_OFFLINE; i++)
+ {
+ if (g_str_equal (s, status_ids[i]))
+ return i;
+ }
+
+ return -1;
+}
+
+static void
+global_status_changed (IndicatorMessagesService *service,
+ const gchar *status_str,
+ gpointer user_data)
+{
+ MessagingMenuApp *app = user_data;
+ int status;
+
+ status = status_from_string (status_str);
+ g_return_if_fail (status >= 0);
+
+ app->status = (MessagingMenuStatus)status;
+ g_signal_emit (app, signals[STATUS_CHANGED], 0, app->status);
+}
+
+static void
+source_action_activated (GTupleAction *action,
+ GVariant *parameter,
+ gpointer user_data)
+{
+ MessagingMenuApp *app = user_data;
+ const gchar *name = g_action_get_name (G_ACTION (action));
+ GQuark q = g_quark_from_string (name);
+
+ g_signal_emit (app, signals[ACTIVATE_SOURCE], q, name);
+}
+
+static void
+messaging_menu_app_insert_source_action (MessagingMenuApp *app,
+ gint position,
+ const gchar *id,
+ GIcon *icon,
+ const gchar *label,
+ GVariant *state)
+{
+ GTupleAction *action;
+ GMenuItem *menuitem;
+
+ g_return_if_fail (MESSAGING_MENU_IS_APP (app));
+ g_return_if_fail (id != NULL);
+
+ if (g_simple_action_group_lookup (app->source_actions, id))
+ {
+ g_warning ("a source with id '%s' already exists", id);
+ return;
+ }
+
+ action = g_tuple_action_new (id, state);
+ g_signal_connect (action, "activate",
+ G_CALLBACK (source_action_activated), app);
+ g_simple_action_group_insert (app->source_actions, G_ACTION (action));
+ g_object_unref (action);
+
+ menuitem = g_menu_item_new (label, id);
+ g_menu_item_set_attribute (menuitem, "x-canonical-type", "s", "ImSourceMenuItem");
+ if (icon)
+ {
+ gchar *iconstr = g_icon_to_string (icon);
+ g_menu_item_set_attribute (menuitem, "x-canonical-icon", "s", iconstr);
+ g_free (iconstr);
+ }
+ g_menu_insert_item (app->menu, position, menuitem);
+ g_object_unref (menuitem);
+}
+
+static void
+messaging_menu_app_set_source_action (MessagingMenuApp *app,
+ const gchar *source_id,
+ gsize index,
+ GVariant *child)
+{
+ GAction *action;
+
+ g_return_if_fail (MESSAGING_MENU_IS_APP (app));
+ g_return_if_fail (source_id != NULL);
+
+ action = g_simple_action_group_lookup (app->source_actions, source_id);
+ if (action == NULL)
+ {
+ g_warning ("a source with id '%s' doesn't exist", source_id);
+ return;
+ }
+
+ g_tuple_action_set_child (G_TUPLE_ACTION (action), index, child);
+}
+
+/**
+ * messaging_menu_app_insert_source:
+ * @app: a #MessagingMenuApp
+ * @position: the position at which to insert the source
+ * @id: a unique identifier for the source to be added
+ * @icon: the icon associated with the source
+ * @label: a user-visible string best describing the source
+ *
+ * Inserts a new message source into the section representing @app. Equivalent
+ * to calling messaging_menu_app_insert_source_with_time() with the current
+ * time.
+ *
+ * It is an error to insert a source with an @id which already exists. Use
+ * messaging_menu_app_has_source() to find out whether there is such a source.
+ */
+void
+messaging_menu_app_insert_source (MessagingMenuApp *app,
+ gint position,
+ const gchar *id,
+ GIcon *icon,
+ const gchar *label)
+{
+ messaging_menu_app_insert_source_with_time (app, position, id, icon, label,
+ g_get_real_time ());
+}
+
+/**
+ * messaging_menu_app_append_source:
+ * @app: a #MessagingMenuApp
+ * @id: a unique identifier for the source to be added
+ * @icon: the icon associated with the source
+ * @label: a user-visible string best describing the source
+ *
+ * Appends a new message source to the end of the section representing @app.
+ * Equivalent to calling messaging_menu_app_append_source_with_time() with the
+ * current time.
+ *
+ * It is an error to add a source with an @id which already exists. Use
+ * messaging_menu_app_has_source() to find out whether there is such a source.
+ */
+void
+messaging_menu_app_append_source (MessagingMenuApp *app,
+ const gchar *id,
+ GIcon *icon,
+ const gchar *label)
+{
+ messaging_menu_app_insert_source (app, -1, id, icon, label);
+}
+
+/**
+ * messaging_menu_app_insert_source_with_count:
+ * @app: a #MessagingMenuApp
+ * @position: the position at which to insert the source
+ * @id: a unique identifier for the source to be added
+ * @icon: the icon associated with the source
+ * @label: a user-visible string best describing the source
+ * @count: the count for the source
+ *
+ * Inserts a new message source into the section representing @app and
+ * initializes it with @count.
+ *
+ * To update the count, use messaging_menu_app_set_source_count().
+ *
+ * It is an error to insert a source with an @id which already exists. Use
+ * messaging_menu_app_has_source() to find out whether there is such a source.
+ */
+void
+messaging_menu_app_insert_source_with_count (MessagingMenuApp *app,
+ gint position,
+ const gchar *id,
+ GIcon *icon,
+ const gchar *label,
+ guint count)
+{
+ messaging_menu_app_insert_source_action (app, position, id, icon, label,
+ g_variant_new ("(uxsb)", count, 0, "", FALSE));
+}
+
+/**
+ * messaging_menu_app_append_source_with_count:
+ * @app: a #MessagingMenuApp
+ * @id: a unique identifier for the source to be added
+ * @icon: the icon associated with the source
+ * @label: a user-visible string best describing the source
+ * @count: the count for the source
+ *
+ * Appends a new message source to the end of the section representing @app and
+ * initializes it with @count.
+ *
+ * To update the count, use messaging_menu_app_set_source_count().
+ *
+ * It is an error to add a source with an @id which already exists. Use
+ * messaging_menu_app_has_source() to find out whether there is such a source.
+ */
+void messaging_menu_app_append_source_with_count (MessagingMenuApp *app,
+ const gchar *id,
+ GIcon *icon,
+ const gchar *label,
+ guint count)
+{
+ messaging_menu_app_insert_source_with_count (app, -1, id, icon, label, count);
+}
+
+/**
+ * messaging_menu_app_insert_source_with_time:
+ * @app: a #MessagingMenuApp
+ * @position: the position at which to insert the source
+ * @id: a unique identifier for the source to be added
+ * @icon: the icon associated with the source
+ * @label: a user-visible string best describing the source
+ * @time: the time when the source was created
+ *
+ * Inserts a new message source into the section representing @app and
+ * initializes it with @time.
+ *
+ * To change the time, use messaging_menu_app_set_source_time().
+ *
+ * It is an error to insert a source with an @id which already exists. Use
+ * messaging_menu_app_has_source() to find out whether there is such a source.
+ */
+void
+messaging_menu_app_insert_source_with_time (MessagingMenuApp *app,
+ gint position,
+ const gchar *id,
+ GIcon *icon,
+ const gchar *label,
+ gint64 time)
+{
+ messaging_menu_app_insert_source_action (app, position, id, icon, label,
+ g_variant_new ("(uxsb)", 0, time, "", FALSE));
+}
+
+/**
+ * messaging_menu_app_append_source_with_time:
+ * @app: a #MessagingMenuApp
+ * @position: the position at which to insert the source
+ * @id: a unique identifier for the source to be added
+ * @icon: the icon associated with the source
+ * @label: a user-visible string best describing the source
+ * @time: the time when the source was created
+ *
+ * Appends a new message source to the end of the section representing @app and
+ * initializes it with @time.
+ *
+ * To change the time, use messaging_menu_app_set_source_time().
+ *
+ * It is an error to insert a source with an @id which already exists. Use
+ * messaging_menu_app_has_source() to find out whether there is such a source.
+ */
+void
+messaging_menu_app_append_source_with_time (MessagingMenuApp *app,
+ const gchar *id,
+ GIcon *icon,
+ const gchar *label,
+ gint64 time)
+{
+ messaging_menu_app_insert_source_with_time (app, -1, id, icon, label, time);
+}
+
+/**
+ * messaging_menu_app_insert_source_with_string:
+ * @app: a #MessagingMenuApp
+ * @position: the position at which to insert the source
+ * @id: a unique identifier for the source to be added
+ * @icon: the icon associated with the source
+ * @label: a user-visible string best describing the source
+ * @str: a string associated with the source
+ *
+ * Inserts a new message source into the section representing @app and
+ * initializes it with @str.
+ *
+ * To update the string, use messaging_menu_app_set_source_string().
+ *
+ * It is an error to insert a source with an @id which already exists. Use
+ * messaging_menu_app_has_source() to find out whether there is such a source.
+ */
+void
+messaging_menu_app_insert_source_with_string (MessagingMenuApp *app,
+ gint position,
+ const gchar *id,
+ GIcon *icon,
+ const gchar *label,
+ const gchar *str)
+{
+ messaging_menu_app_insert_source_action (app, position, id, icon, label,
+ g_variant_new ("(uxsb)", 0, 0, str, FALSE));
+}
+
+/**
+ * messaging_menu_app_append_source_with_string:
+ * @app: a #MessagingMenuApp
+ * @position: the position at which to insert the source
+ * @id: a unique identifier for the source to be added
+ * @icon: the icon associated with the source
+ * @label: a user-visible string best describing the source
+ * @str: a string associated with the source
+ *
+ * Appends a new message source to the end of the section representing @app and
+ * initializes it with @str.
+ *
+ * To update the string, use messaging_menu_app_set_source_string().
+ *
+ * It is an error to insert a source with an @id which already exists. Use
+ * messaging_menu_app_has_source() to find out whether there is such a source.
+ */
+void
+messaging_menu_app_append_source_with_string (MessagingMenuApp *app,
+ const gchar *id,
+ GIcon *icon,
+ const gchar *label,
+ const gchar *str)
+{
+ messaging_menu_app_insert_source_with_string (app, -1, id, icon, label, str);
+}
+
+/**
+ * messaging_menu_app_remove_source:
+ * @app: a #MessagingMenuApp
+ * @source_id: the id of the source to remove
+ *
+ * Removes the source corresponding to @source_id from the menu.
+ */
+void
+messaging_menu_app_remove_source (MessagingMenuApp *app,
+ const gchar *source_id)
+{
+ int n_items;
+ int i;
+
+ g_return_if_fail (MESSAGING_MENU_IS_APP (app));
+ g_return_if_fail (source_id != NULL);
+
+ if (g_simple_action_group_lookup (app->source_actions, source_id) == NULL)
+ {
+ g_warning ("%s: a source with id '%s' doesn't exist", G_STRFUNC, source_id);
+ return;
+ }
+
+ n_items = g_menu_model_get_n_items (G_MENU_MODEL (app->menu));
+ for (i = 0; i < n_items; i++)
+ {
+ const gchar *action = NULL;
+
+ g_menu_model_get_item_attribute (G_MENU_MODEL (app->menu), i,
+ "action", "&s", &action);
+ if (!g_strcmp0 (action, source_id))
+ {
+ g_menu_remove (app->menu, i);
+ break;
+ }
+ }
+
+ g_simple_action_group_remove (app->source_actions, source_id);
+}
+
+/**
+ * messaging_menu_app_has_source:
+ * @app: a #MessagingMenuApp
+ * @source_id: a source id
+ *
+ * Returns: TRUE if there is a source associated with @source_id
+ */
+gboolean
+messaging_menu_app_has_source (MessagingMenuApp *app,
+ const gchar *source_id)
+{
+ g_return_val_if_fail (MESSAGING_MENU_IS_APP (app), FALSE);
+ g_return_val_if_fail (source_id != NULL, FALSE);
+
+ return g_simple_action_group_lookup (app->source_actions, source_id) != NULL;
+}
+
+/**
+ * messaging_menu_app_set_source_count:
+ * @app: a #MessagingMenuApp
+ * @source_id: a source id
+ * @count: the new count for the source
+ *
+ * Updates the count of @source_id to @count.
+ */
+void messaging_menu_app_set_source_count (MessagingMenuApp *app,
+ const gchar *source_id,
+ guint count)
+{
+ messaging_menu_app_set_source_action (app, source_id, INDEX_COUNT,
+ g_variant_new_uint32 (count));
+}
+
+/**
+ * messaging_menu_app_set_source_time:
+ * @app: a #MessagingMenuApp
+ * @source_id: a source id
+ * @time: the new time for the source
+ *
+ * Updates the time of @source_id to @time.
+ *
+ * Note that the time is only displayed if the source does not also have a
+ * count associated with it.
+ */
+void
+messaging_menu_app_set_source_time (MessagingMenuApp *app,
+ const gchar *source_id,
+ gint64 time)
+{
+ messaging_menu_app_set_source_action (app, source_id, INDEX_TIME,
+ g_variant_new_int64 (time));
+}
+
+/**
+ * messaging_menu_app_set_source_string:
+ * @app: a #MessagingMenuApp
+ * @source_id: a source id
+ * @string: the new string for the source
+ *
+ * Updates the string displayed next to @source_id to @str.
+ *
+ * Note that the string is only displayed if the source does not also have a
+ * count or time associated with it.
+ */
+void
+messaging_menu_app_set_source_string (MessagingMenuApp *app,
+ const gchar *source_id,
+ const gchar *str)
+{
+ messaging_menu_app_set_source_action (app, source_id, INDEX_STRING,
+ g_variant_new_string (str));
+}
+
+/**
+ * messaging_menu_app_draw_attention:
+ * @app: a #MessagingMenuApp
+ * @source_id: a source id
+ *
+ * Indicates that @source_id has important unread messages. Currently, this
+ * means that the messaging menu's envelope icon will turn blue.
+ *
+ * Use messaging_menu_app_remove_attention() to stop indicating that the source
+ * needs attention.
+ */
+void
+messaging_menu_app_draw_attention (MessagingMenuApp *app,
+ const gchar *source_id)
+{
+ messaging_menu_app_set_source_action (app, source_id, INDEX_DRAWS_ATTENTION,
+ g_variant_new_boolean (TRUE));
+}
+
+/**
+ * messaging_menu_app_remove_attention:
+ * @app: a #MessagingMenuApp
+ * @source_id: a source id
+ *
+ * Stop indicating that @source_id needs attention.
+ *
+ * Use messaging_menu_app_draw_attention() to make @source_id draw attention
+ * again.
+ */
+void
+messaging_menu_app_remove_attention (MessagingMenuApp *app,
+ const gchar *source_id)
+{
+ messaging_menu_app_set_source_action (app, source_id, INDEX_DRAWS_ATTENTION,
+ g_variant_new_boolean (FALSE));
+}
diff --git a/libmessaging-menu/messaging-menu.h b/libmessaging-menu/messaging-menu.h
new file mode 100644
index 0000000..e767099
--- /dev/null
+++ b/libmessaging-menu/messaging-menu.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2012 Canonical Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Lars Uebernickel <lars.uebernickel@canonical.com>
+ */
+
+#ifndef __messaging_menu_h__
+#define __messaging_menu_h__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define MESSAGING_MENU_TYPE_APP messaging_menu_app_get_type()
+#define MESSAGING_MENU_APP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MESSAGING_MENU_TYPE_APP, MessagingMenuApp))
+#define MESSAGING_MENU_APP_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), MESSAGING_MENU_TYPE_APP, MessagingMenuAppClass))
+#define MESSAGING_MENU_IS_APP(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MESSAGING_MENU_TYPE_APP))
+
+typedef enum {
+ MESSAGING_MENU_STATUS_AVAILABLE,
+ MESSAGING_MENU_STATUS_AWAY,
+ MESSAGING_MENU_STATUS_BUSY,
+ MESSAGING_MENU_STATUS_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_count (MessagingMenuApp *app,
+ const gchar *source_id,
+ guint count);
+
+void messaging_menu_app_set_source_time (MessagingMenuApp *app,
+ const gchar *source_id,
+ gint64 time);
+
+void messaging_menu_app_set_source_string (MessagingMenuApp *app,
+ const gchar *source_id,
+ const gchar *str);
+
+void messaging_menu_app_draw_attention (MessagingMenuApp *app,
+ const gchar *source_id);
+
+void messaging_menu_app_remove_attention (MessagingMenuApp *app,
+ const gchar *source_id);
+
+G_END_DECLS
+
+#endif
diff --git a/libmessaging-menu/messaging-menu.pc.in b/libmessaging-menu/messaging-menu.pc.in
new file mode 100644
index 0000000..486300f
--- /dev/null
+++ b/libmessaging-menu/messaging-menu.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@/messaging-menu
+
+Name: Messaging Menu Library
+Description: Messaging Menu client library
+Version: @VERSION@
+Requires: gio-unix-2.0
+Libs: -L${libdir} -lmessaging-menu
+Cflags: -I${includedir}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 051f8f3..edb0388 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,9 +1,3 @@
[encoding: UTF-8]
-src/messages-service-dbus.c
src/indicator-messages.c
src/messages-service.c
-src/default-applications.c
-src/launcher-menu-item.c
-src/im-menu-item.c
-src/app-menu-item.c
-src/status-items.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 6617b49..403a289 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -15,8 +15,12 @@ messaginglibdir = $(INDICATORDIR)
messaginglib_LTLIBRARIES = libmessaging.la
libmessaging_la_SOURCES = \
indicator-messages.c \
- gen-messages-service.xml.h \
- gen-messages-service.xml.c \
+ im-app-menu-item.c \
+ im-app-menu-item.h \
+ im-source-menu-item.c \
+ im-source-menu-item.h \
+ indicator-messages-service.c \
+ indicator-messages-service.h
dbus-data.h
libmessaging_la_CFLAGS = \
$(APPLET_CFLAGS) \
@@ -37,26 +41,18 @@ libmessaging_la_LDFLAGS = \
######################################
indicator_messages_service_SOURCES = \
- default-applications.h \
- default-applications.c \
messages-service.c \
- messages-service-dbus.c \
- messages-service-dbus.h \
- gen-messages-service.xml.h \
- gen-messages-service.xml.c \
- im-menu-item.c \
- im-menu-item.h \
- app-menu-item.c \
- app-menu-item.h \
- launcher-menu-item.c \
- launcher-menu-item.h \
- seen-db.c \
- seen-db.h \
- dirs.h \
+ indicator-messages-service.c \
+ indicator-messages-service.h \
+ app-section.c \
+ app-section.h \
dbus-data.h \
- \
- status-items.c \
- status-items.h
+ gactionmuxer.c \
+ gactionmuxer.h \
+ gsettingsstrv.c \
+ gsettingsstrv.h \
+ gmenuutils.c \
+ gmenuutils.h
indicator_messages_service_CFLAGS = \
$(APPLET_CFLAGS) \
@@ -66,218 +62,29 @@ indicator_messages_service_CFLAGS = \
-Wl,-z,defs \
-Wl,--as-needed \
-Werror \
- -DG_LOG_DOMAIN=\"Indicator-Messages\" \
- -DSTATUS_PROVIDER_DIR=\"$(STATUS_PROVIDER_DIR)\"
+ -DG_LOG_DOMAIN=\"Indicator-Messages\"
indicator_messages_service_LDADD = \
- $(APPLET_LIBS) \
- libindicator-messages-status-provider.la
+ $(APPLET_LIBS)
indicator_messages_service_LDFLAGS = \
$(COVERAGE_LDFLAGS)
-gen-%.xml.h: %.xml
- @echo "Building $@ from $<"
- @echo "extern const char * _$(subst -,_,$(subst .,_,$(basename $(notdir $<))));" > $@
-
-gen-%.xml.c: %.xml
- @echo "Building $@ from $<"
- echo "const char * _$(subst -,_,$(subst .,_,$(basename $(notdir $<)))) = " > $@
- @sed -e "s:\":\\\\\":g" -e s:^:\": -e s:\$$:\\\\n\": $< >> $@
- @echo ";" >> $@
+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 += \
- gen-messages-service.xml.h \
- gen-messages-service.xml.c
+ indicator-messages-service.c \
+ indicator-messages-service.h
EXTRA_DIST += \
messages-service.xml
-######################################
-# Status Provider Library
-######################################
-
-STATUS_PROVIDER_API_VERSION = 1
-STATUS_PROVIDER_DIR = $(libexecdir)/status-providers/$(STATUS_PROVIDER_API_VERSION)
-statusprovidersdir = $(STATUS_PROVIDER_DIR)
-statusproviders_LTLIBRARIES =
-
-EXTRA_DIST += \
- indicator-messages-status-provider-0.5.pc.in.in
-CLEANFILES += \
- indicator-messages-status-provider-0.5.pc
-
-pkgconfig_DATA = indicator-messages-status-provider-0.5.pc
-pkgconfigdir = $(libdir)/pkgconfig
-
-%.pc: %.pc.in
- sed \
- -e "s|\@status_provider_dir\@|$(STATUS_PROVIDER_DIR)|" \
- -e "s|\@status_provider_api_version\@|$(STATUS_PROVIDER_API_VERSION)|" \
- $< > $@
-
-lib_LTLIBRARIES = \
- libindicator-messages-status-provider.la
-
-libindicator_messages_status_provider_la_HEADERS = \
- status-provider.h
-
-libindicator_messages_status_provider_la_SOURCES = \
- $(libindicator_messages_status_provider_HEADERS) \
- status-provider.c
-
-libindicator_messages_status_provider_ladir = \
- $(includedir)/libindicator-messages-status-provider-$(STATUS_PROVIDER_API_VERSION)/
-
-libindicator_messages_status_provider_la_LDFLAGS = \
- -version-info $(STATUS_PROVIDER_API_VERSION):0:0 \
- -no-undefined \
- -export-symbols-regex "^[^_].*"
-
-libindicator_messages_status_provider_la_LIBADD = \
- $(APPLET_LIBS)
-
-libindicator_messages_status_provider_la_CFLAGS = \
- $(APPLET_CFLAGS) \
- -Wall -Werror
-
-######################################
-# Status provider: Pidgin
-######################################
-
-statusproviders_LTLIBRARIES += libpidgin.la
-libpidgin_la_SOURCES = \
- status-provider-pidgin.h \
- status-provider-pidgin.c \
- status-provider-pidgin-marshal.h \
- status-provider-pidgin-marshal.c
-libpidgin_la_CFLAGS = \
- $(APPLET_CFLAGS) \
- $(STATUS_PROVIDER_PIDGIN_CFLAGS) \
- -Wall -Werror \
- -DG_LOG_DOMAIN=\"Status-Provider-Pidgin\"
-libpidgin_la_LIBADD = \
- libindicator-messages-status-provider.la \
- $(APPLET_LIBS) \
- $(STATUS_PROVIDER_PIDGIN_LIBS)
-libpidgin_la_LDFLAGS = -module -avoid-version
-
-status-provider-pidgin-marshal.h: $(srcdir)/status-provider-pidgin.list
- glib-genmarshal --header \
- --prefix=_status_provider_pidgin_marshal $(srcdir)/status-provider-pidgin.list \
- > status-provider-pidgin-marshal.h
-
-status-provider-pidgin-marshal.c: $(srcdir)/status-provider-pidgin.list
- glib-genmarshal --body \
- --prefix=_status_provider_pidgin_marshal $(srcdir)/status-provider-pidgin.list \
- > status-provider-pidgin-marshal.c
-
-BUILT_SOURCES += \
- status-provider-pidgin-marshal.h \
- status-provider-pidgin-marshal.c
-
-EXTRA_DIST += \
- status-provider-pidgin.list
-
-######################################
-# Status provider: Mission Control 4
-######################################
-
-statusproviders_LTLIBRARIES += libtelepathy.la
-libtelepathy_la_SOURCES = \
- status-provider-telepathy.h \
- status-provider-telepathy.c \
- status-provider-telepathy-marshal.h \
- status-provider-telepathy-marshal.c
-libtelepathy_la_CFLAGS = \
- $(APPLET_CFLAGS) \
- $(STATUS_PROVIDER_TELEPATHY_CFLAGS) \
- -Wall -Werror \
- -DG_LOG_DOMAIN=\"Status-Provider-Telepathy\"
-libtelepathy_la_LIBADD = \
- libindicator-messages-status-provider.la \
- $(APPLET_LIBS) \
- $(STATUS_PROVIDER_TELEPATHY_LIBS)
-libtelepathy_la_LDFLAGS = -module -avoid-version
-
-status-provider-telepathy-marshal.h: $(srcdir)/status-provider-telepathy.list
- glib-genmarshal --header \
- --prefix=_status_provider_telepathy_marshal $(srcdir)/status-provider-telepathy.list \
- > status-provider-telepathy-marshal.h
-
-status-provider-telepathy-marshal.c: $(srcdir)/status-provider-telepathy.list
- glib-genmarshal --body \
- --prefix=_status_provider_telepathy_marshal $(srcdir)/status-provider-telepathy.list \
- > status-provider-telepathy-marshal.c
-
-BUILT_SOURCES += \
- status-provider-telepathy-marshal.h \
- status-provider-telepathy-marshal.c
-
-EXTRA_DIST += \
- status-provider-telepathy.list
-
-######################################
-# Status provider: Mission Control 5
-######################################
-
-statusproviders_LTLIBRARIES += libmc5.la
-libmc5_la_SOURCES = \
- status-provider-mc5.h \
- status-provider-mc5.c \
- status-provider-mc5-marshal.h \
- status-provider-mc5-marshal.c
-libmc5_la_CFLAGS = \
- $(APPLET_CFLAGS) \
- $(STATUS_PROVIDER_MC5_CFLAGS) \
- -Wall -Werror \
- -DG_LOG_DOMAIN=\"Status-Provider-MC5\"
-libmc5_la_LIBADD = \
- libindicator-messages-status-provider.la \
- $(APPLET_LIBS) \
- $(STATUS_PROVIDER_MC5_LIBS)
-libmc5_la_LDFLAGS = -module -avoid-version
-
-status-provider-mc5-marshal.h: $(srcdir)/status-provider-mc5.list
- glib-genmarshal --header \
- --prefix=_status_provider_mc5_marshal $(srcdir)/status-provider-mc5.list \
- > status-provider-mc5-marshal.h
-
-status-provider-mc5-marshal.c: $(srcdir)/status-provider-mc5.list
- glib-genmarshal --body \
- --prefix=_status_provider_mc5_marshal $(srcdir)/status-provider-mc5.list \
- > status-provider-mc5-marshal.c
-
-BUILT_SOURCES += \
- status-provider-mc5-marshal.h \
- status-provider-mc5-marshal.c
-
-EXTRA_DIST += \
- status-provider-mc5.list
-
-######################################
-# Status provider: Emesene
-######################################
-
-statusproviders_LTLIBRARIES += libemesene.la
-libemesene_la_SOURCES = \
- status-provider-emesene.h \
- status-provider-emesene.c
-libemesene_la_CFLAGS = \
- $(APPLET_CFLAGS) \
- $(STATUS_PROVIDER_EMESENE_CFLAGS) \
- -Wall -Werror \
- -DG_LOG_DOMAIN=\"Status-Provider-Emesene\"
-libemesene_la_LIBADD = \
- libindicator-messages-status-provider.la \
- $(APPLET_LIBS) \
- $(STATUS_PROVIDER_EMESENE_LIBS)
-libemesene_la_LDFLAGS = -module -avoid-version
-
-######################################
-# Extras
-######################################
-
CLEANFILES += \
$(BUILT_SOURCES)
diff --git a/src/app-menu-item.c b/src/app-menu-item.c
deleted file mode 100644
index ef3fbc0..0000000
--- a/src/app-menu-item.c
+++ /dev/null
@@ -1,579 +0,0 @@
-/*
-An indicator to show information that is in messaging applications
-that the user is using.
-
-Copyright 2009 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@canonical.com>
-
-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/>.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <glib/gi18n.h>
-#include <gio/gdesktopappinfo.h>
-#include <libdbusmenu-glib/client.h>
-#include <libdbusmenu-glib/menuitem-proxy.h>
-#include "app-menu-item.h"
-#include "dbus-data.h"
-#include "default-applications.h"
-#include "seen-db.h"
-
-enum {
- COUNT_CHANGED,
- NAME_CHANGED,
- SHORTCUT_ADDED,
- SHORTCUT_REMOVED,
- LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-typedef struct _AppMenuItemPrivate AppMenuItemPrivate;
-
-struct _AppMenuItemPrivate
-{
- IndicateListener * listener;
- IndicateListenerServer * server;
-
- gchar * type;
- GAppInfo * appinfo;
- GKeyFile * keyfile;
- gchar * desktop;
- guint unreadcount;
-
- DbusmenuClient * client;
- DbusmenuMenuitem * root;
- GList * shortcuts;
-};
-
-#define APP_MENU_ITEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), APP_MENU_ITEM_TYPE, AppMenuItemPrivate))
-
-/* Prototypes */
-static void app_menu_item_class_init (AppMenuItemClass *klass);
-static void app_menu_item_init (AppMenuItem *self);
-static void app_menu_item_dispose (GObject *object);
-static void app_menu_item_finalize (GObject *object);
-static void activate_cb (AppMenuItem * self, guint timestamp, gpointer data);
-static void count_changed (IndicateListener * listener, IndicateListenerServer * server, guint count, gpointer data);
-static void count_cb (IndicateListener * listener, IndicateListenerServer * server, guint value, gpointer data);
-static void menu_cb (IndicateListener * listener, IndicateListenerServer * server, const gchar * menupath, gpointer data);
-static void desktop_cb (IndicateListener * listener, IndicateListenerServer * server, const gchar * value, gpointer data);
-static void update_label (AppMenuItem * self);
-
-/* GObject Boilerplate */
-G_DEFINE_TYPE (AppMenuItem, app_menu_item, DBUSMENU_TYPE_MENUITEM);
-
-static void
-app_menu_item_class_init (AppMenuItemClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- g_type_class_add_private (klass, sizeof (AppMenuItemPrivate));
-
- object_class->dispose = app_menu_item_dispose;
- object_class->finalize = app_menu_item_finalize;
-
- signals[COUNT_CHANGED] = g_signal_new(APP_MENU_ITEM_SIGNAL_COUNT_CHANGED,
- G_TYPE_FROM_CLASS(klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (AppMenuItemClass, count_changed),
- NULL, NULL,
- g_cclosure_marshal_VOID__UINT,
- G_TYPE_NONE, 1, G_TYPE_UINT);
- signals[NAME_CHANGED] = g_signal_new(APP_MENU_ITEM_SIGNAL_NAME_CHANGED,
- G_TYPE_FROM_CLASS(klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (AppMenuItemClass, name_changed),
- NULL, NULL,
- g_cclosure_marshal_VOID__STRING,
- G_TYPE_NONE, 1, G_TYPE_STRING);
- signals[SHORTCUT_ADDED] = g_signal_new(APP_MENU_ITEM_SIGNAL_SHORTCUT_ADDED,
- G_TYPE_FROM_CLASS(klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (AppMenuItemClass, shortcut_added),
- NULL, NULL,
- g_cclosure_marshal_VOID__OBJECT,
- G_TYPE_NONE, 1, G_TYPE_OBJECT);
- signals[SHORTCUT_REMOVED] = g_signal_new(APP_MENU_ITEM_SIGNAL_SHORTCUT_REMOVED,
- G_TYPE_FROM_CLASS(klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (AppMenuItemClass, shortcut_removed),
- NULL, NULL,
- g_cclosure_marshal_VOID__OBJECT,
- G_TYPE_NONE, 1, G_TYPE_OBJECT);
-
- return;
-}
-
-static void
-app_menu_item_init (AppMenuItem *self)
-{
- g_debug("Building new App Menu Item");
- AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self);
-
- priv->listener = NULL;
- priv->server = NULL;
- priv->type = NULL;
- priv->appinfo = NULL;
- priv->keyfile = NULL;
- priv->desktop = NULL;
- priv->unreadcount = 0;
-
- priv->client = NULL;
- priv->root = NULL;
- priv->shortcuts = NULL;
-
- dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
-
- return;
-}
-
-/* A wrapper to make the prototypes work for GFunc */
-static void
-func_unref (gpointer data, gpointer user_data)
-{
- g_signal_emit(user_data, signals[SHORTCUT_REMOVED], 0, data, TRUE);
- g_object_unref(G_OBJECT(data));
- return;
-}
-
-/* Disconnect the count_changed signal and unref the listener */
-static void
-app_menu_item_dispose (GObject *object)
-{
- AppMenuItem * self = APP_MENU_ITEM(object);
- AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self);
-
- if (priv->listener != NULL) {
- g_signal_handlers_disconnect_by_func(G_OBJECT(priv->listener), count_changed, self);
- g_object_unref(priv->listener);
- priv->listener = NULL;
- }
-
- if (priv->shortcuts != NULL) {
- g_list_foreach(priv->shortcuts, func_unref, object);
- g_list_free(priv->shortcuts);
- priv->shortcuts = NULL;
- }
-
- if (priv->root != NULL) {
- g_object_unref(priv->root);
- priv->root = NULL;
- }
-
- if (priv->client != NULL) {
- g_object_unref(priv->client);
- priv->client = NULL;
- }
-
- if (priv->appinfo != NULL) {
- g_object_unref(priv->appinfo);
- priv->appinfo = NULL;
- }
-
- if (priv->keyfile != NULL) {
- g_object_unref(priv->keyfile);
- priv->keyfile = NULL;
- }
-
- G_OBJECT_CLASS (app_menu_item_parent_class)->dispose (object);
-}
-
-/* Free the memory used by our type, desktop file and application
- info structures. */
-static void
-app_menu_item_finalize (GObject *object)
-{
- AppMenuItem * self = APP_MENU_ITEM(object);
- AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self);
-
- if (priv->type != NULL) {
- g_free(priv->type);
- priv->type = NULL;
- }
-
- if (priv->desktop != NULL) {
- g_free(priv->desktop);
- priv->desktop = NULL;
- }
-
- G_OBJECT_CLASS (app_menu_item_parent_class)->finalize (object);
-
- return;
-}
-
-AppMenuItem *
-app_menu_item_new (IndicateListener * listener, IndicateListenerServer * server)
-{
- AppMenuItem * self = g_object_new(APP_MENU_ITEM_TYPE, NULL);
-
- AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self);
-
- /* Copy the listener so we can use it later */
- priv->listener = listener;
- g_object_ref(G_OBJECT(listener));
-
- /* Can not ref as not real GObject */
- priv->server = server;
-
- dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_TYPE, APPLICATION_MENUITEM_TYPE);
-
- /* Set up listener signals */
- g_signal_connect(G_OBJECT(listener), INDICATE_LISTENER_SIGNAL_SERVER_COUNT_CHANGED, G_CALLBACK(count_changed), self);
-
- /* Get the values we care about from the server */
- indicate_listener_server_get_desktop(listener, server, desktop_cb, self);
- indicate_listener_server_get_count(listener, server, count_cb, self);
- indicate_listener_server_get_menu(listener, server, menu_cb, self);
-
- g_signal_connect(G_OBJECT(self), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(activate_cb), NULL);
-
- indicate_listener_server_show_interest(listener, server, INDICATE_INTEREST_SERVER_DISPLAY);
- indicate_listener_server_show_interest(listener, server, INDICATE_INTEREST_SERVER_SIGNAL);
- indicate_listener_server_show_interest(listener, server, INDICATE_INTEREST_INDICATOR_COUNT);
- indicate_listener_server_show_interest(listener, server, INDICATE_INTEREST_INDICATOR_DISPLAY);
- indicate_listener_server_show_interest(listener, server, INDICATE_INTEREST_INDICATOR_SIGNAL);
- indicate_listener_set_server_max_indicators(listener, server, MAX_NUMBER_OF_INDICATORS);
-
- return self;
-}
-
-static void
-update_label (AppMenuItem * self)
-{
- AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self);
- const gchar * name = get_default_name(priv->desktop);
-
- if (name == NULL) {
- name = app_menu_item_get_name(self);
- }
-
- if (priv->unreadcount > 0) {
- /* TRANSLATORS: This is the name of the program and the number of indicators. So it
- would read something like "Mail Client (5)" */
- gchar * label = g_strdup_printf(_("%s (%d)"), _(name), priv->unreadcount);
- dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), APPLICATION_MENUITEM_PROP_NAME, label);
- g_free(label);
- } else {
- dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), APPLICATION_MENUITEM_PROP_NAME, _(name));
- }
-
- return;
-}
-
-/* Callback to the signal that the server count
- has changed to a new value. This checks to see if
- it's actually changed and if so signals everyone and
- updates the label. */
-static void
-count_changed (IndicateListener * listener, IndicateListenerServer * server, guint count, gpointer data)
-{
- g_return_if_fail(IS_APP_MENU_ITEM(data));
- AppMenuItem * self = APP_MENU_ITEM(data);
- AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self);
-
- if (priv->unreadcount != count) {
- priv->unreadcount = count;
- update_label(self);
- g_signal_emit(G_OBJECT(self), signals[COUNT_CHANGED], 0, priv->unreadcount, TRUE);
- }
-
- return;
-}
-
-/* Callback for getting the count property off
- of the server. */
-static void
-count_cb (IndicateListener * listener, IndicateListenerServer * server, guint value, gpointer data)
-{
- count_changed(listener, server, value, data);
- return;
-}
-
-/* Callback for when we ask the server for the path
- to it's desktop file. We then turn it into an
- app structure and start sucking data out of it.
- Mostly the name. And the icon. */
-static void
-desktop_cb (IndicateListener * listener, IndicateListenerServer * server, const gchar * value, gpointer data)
-{
- g_return_if_fail(IS_APP_MENU_ITEM(data));
- AppMenuItem * self = APP_MENU_ITEM(data);
- AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self);
-
- if (priv->appinfo != NULL) {
- g_object_unref(G_OBJECT(priv->appinfo));
- priv->appinfo = NULL;
- }
-
- if (priv->desktop != NULL) {
- g_free(priv->desktop);
- priv->desktop = NULL;
- }
-
- if (value == NULL || value[0] == '\0') {
- return;
- }
-
- seen_db_add(value);
-
- priv->appinfo = G_APP_INFO(g_desktop_app_info_new_from_filename(value));
- g_return_if_fail(priv->appinfo != NULL);
-
- priv->keyfile = g_key_file_new();
- g_key_file_load_from_file(priv->keyfile, value, G_KEY_FILE_NONE, NULL);
-
- priv->desktop = g_strdup(value);
-
- dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
- dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(self), APPLICATION_MENUITEM_PROP_RUNNING, TRUE);
-
- update_label(self);
-
- const gchar * def_icon = get_default_icon(priv->desktop);
- if (def_icon == NULL) {
- gchar * iconstr = NULL;
-
- /* Check for the over ride key and see if we should be using that
- icon. If we can't get it, then go back to the app info */
- if (g_key_file_has_key(priv->keyfile, G_KEY_FILE_DESKTOP_GROUP, ICON_KEY, NULL) && iconstr == NULL) {
- GError * error = NULL;
-
- iconstr = g_key_file_get_string(priv->keyfile, G_KEY_FILE_DESKTOP_GROUP, ICON_KEY, &error);
-
- if (error != NULL) {
- /* Can't figure out why this would happen, but sure, let's print something */
- g_warning("Error getting '" ICON_KEY "' from desktop file: %s", error->message);
- g_error_free(error);
- }
- }
-
- /* For some reason that didn't work, let's try the app info */
- if (iconstr == NULL) {
- GIcon * icon = g_app_info_get_icon(priv->appinfo);
- iconstr = g_icon_to_string(icon);
- }
-
- dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), APPLICATION_MENUITEM_PROP_ICON, iconstr);
- g_free(iconstr);
- } else {
- dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), APPLICATION_MENUITEM_PROP_ICON, def_icon);
- }
-
- g_signal_emit(G_OBJECT(self), signals[NAME_CHANGED], 0, app_menu_item_get_name(self), TRUE);
-
- return;
-}
-
-/* Relay this signal into causing a rebuild of the shortcuts
- from those above us. */
-static void
-child_added_cb (DbusmenuMenuitem * root, DbusmenuMenuitem * child, guint position, gpointer data)
-{
- g_return_if_fail(IS_APP_MENU_ITEM(data));
- AppMenuItem * self = APP_MENU_ITEM(data);
- AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self);
- DbusmenuMenuitemProxy * mip = dbusmenu_menuitem_proxy_new(child);
-
- priv->shortcuts = g_list_insert(priv->shortcuts, mip, position);
-
- g_signal_emit(G_OBJECT(data), signals[SHORTCUT_ADDED], 0, mip, TRUE);
- return;
-}
-
-/* Relay this signal into causing a rebuild of the shortcuts
- from those above us. */
-static void
-child_removed_cb (DbusmenuMenuitem * root, DbusmenuMenuitem * child, gpointer data)
-{
- g_return_if_fail(IS_APP_MENU_ITEM(data));
- AppMenuItem * self = APP_MENU_ITEM(data);
- AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self);
-
- GList * pitems = priv->shortcuts;
- while (pitems != NULL) {
- DbusmenuMenuitemProxy * mip = DBUSMENU_MENUITEM_PROXY(pitems->data);
-
- if (dbusmenu_menuitem_proxy_get_wrapped(mip) == child) {
- break;
- }
-
- pitems = g_list_next(pitems);
- }
-
- if (pitems != NULL) {
- DbusmenuMenuitemProxy * mip = DBUSMENU_MENUITEM_PROXY(pitems->data);
- priv->shortcuts = g_list_remove(priv->shortcuts, mip);
-
- g_signal_emit(G_OBJECT(data), signals[SHORTCUT_REMOVED], 0, mip, TRUE);
- g_object_unref(mip);
- }
-
- return;
-}
-
-/* Relay this signal into causing a rebuild of the shortcuts
- from those above us. */
-static void
-child_moved_cb (DbusmenuMenuitem * root, DbusmenuMenuitem * child, guint newpos, guint oldpos, gpointer data)
-{
- g_return_if_fail(IS_APP_MENU_ITEM(data));
- AppMenuItem * self = APP_MENU_ITEM(data);
- AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self);
-
- DbusmenuMenuitemProxy * mip = DBUSMENU_MENUITEM_PROXY(g_list_nth_data(priv->shortcuts, oldpos));
-
- if (mip != NULL) {
- if (dbusmenu_menuitem_proxy_get_wrapped(mip) != child) {
- mip = NULL;
- }
- }
-
- if (mip != NULL) {
- priv->shortcuts = g_list_remove(priv->shortcuts, mip);
- priv->shortcuts = g_list_insert(priv->shortcuts, mip, newpos);
- g_signal_emit(G_OBJECT(data), signals[SHORTCUT_ADDED], 0, NULL, TRUE);
- }
-
- return;
-}
-
-/* We've got a new root. We need to proxy it and handle it's children
- if that's a relevant thing to do. */
-static void
-root_changed (DbusmenuClient * client, DbusmenuMenuitem * newroot, gpointer data)
-{
- g_debug("Root Changed");
- AppMenuItem * self = APP_MENU_ITEM(data);
- AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self);
-
- if (priv->root != NULL) {
- if (dbusmenu_menuitem_get_children(DBUSMENU_MENUITEM(priv->root)) != NULL) {
- g_list_foreach(priv->shortcuts, func_unref, data);
- g_list_free(priv->shortcuts);
- priv->shortcuts = NULL;
- }
- g_object_unref(priv->root);
- priv->root = NULL;
- }
-
- /* We need to proxy the new root across to the old
- world of indicator land. */
- priv->root = newroot;
-
- if (priv->root != NULL) {
- g_object_ref(priv->root);
- g_signal_connect(G_OBJECT(priv->root), DBUSMENU_MENUITEM_SIGNAL_CHILD_ADDED, G_CALLBACK(child_added_cb), self);
- g_signal_connect(G_OBJECT(priv->root), DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED, G_CALLBACK(child_removed_cb), self);
- g_signal_connect(G_OBJECT(priv->root), DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED, G_CALLBACK(child_moved_cb), self);
-
- /* See if we have any menuitems to worry about,
- otherwise we'll just move along. */
- GList * children = dbusmenu_menuitem_get_children(DBUSMENU_MENUITEM(priv->root));
- if (children != NULL) {
- g_debug("\tProcessing %d children", g_list_length(children));
- while (children != NULL) {
- DbusmenuMenuitemProxy * mip = dbusmenu_menuitem_proxy_new(DBUSMENU_MENUITEM(children->data));
- priv->shortcuts = g_list_append(priv->shortcuts, mip);
- g_signal_emit(G_OBJECT(self), signals[SHORTCUT_ADDED], 0, mip, TRUE);
- children = g_list_next(children);
- }
- }
- }
-
- return;
-}
-
-/* Gets the path to menuitems if there are some. Now we need to
- make them special. */
-static void
-menu_cb (IndicateListener * listener, IndicateListenerServer * server, const gchar * menupath, gpointer data)
-{
- g_debug("Got Menu: %s", menupath);
- g_return_if_fail(IS_APP_MENU_ITEM(data));
- AppMenuItem * self = APP_MENU_ITEM(data);
- AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self);
-
- priv->client = dbusmenu_client_new(indicate_listener_server_get_dbusname(server), menupath);
- g_signal_connect(G_OBJECT(priv->client), DBUSMENU_CLIENT_SIGNAL_ROOT_CHANGED, G_CALLBACK(root_changed), self);
-
- DbusmenuMenuitem * root = dbusmenu_client_get_root(priv->client);
- if (root != NULL) {
- root_changed(priv->client, root, self);
- }
-
- return;
-}
-
-static void
-activate_cb (AppMenuItem * self, guint timestamp, gpointer data)
-{
- AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self);
-
- indicate_listener_display(priv->listener, priv->server, NULL, timestamp);
-
- return;
-}
-
-guint
-app_menu_item_get_count (AppMenuItem * appitem)
-{
- g_return_val_if_fail(IS_APP_MENU_ITEM(appitem), 0);
- AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(appitem);
-
- return priv->unreadcount;
-}
-
-IndicateListenerServer *
-app_menu_item_get_server (AppMenuItem * appitem) {
- g_return_val_if_fail(IS_APP_MENU_ITEM(appitem), NULL);
- AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(appitem);
-
- return priv->server;
-}
-
-const gchar *
-app_menu_item_get_name (AppMenuItem * appitem)
-{
- g_return_val_if_fail(IS_APP_MENU_ITEM(appitem), NULL);
- AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(appitem);
-
- if (priv->appinfo == NULL) {
- return INDICATE_LISTENER_SERVER_DBUS_NAME(priv->server);
- } else {
- return g_app_info_get_name(priv->appinfo);
- }
-}
-
-const gchar *
-app_menu_item_get_desktop (AppMenuItem * appitem)
-{
- g_return_val_if_fail(IS_APP_MENU_ITEM(appitem), NULL);
- AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(appitem);
- return priv->desktop;
-}
-
-/* Get the dynamic items added onto the end of
- and app entry. */
-GList *
-app_menu_item_get_items (AppMenuItem * appitem)
-{
- g_return_val_if_fail(IS_APP_MENU_ITEM(appitem), NULL);
- AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(appitem);
- return priv->shortcuts;
-}
diff --git a/src/app-menu-item.h b/src/app-menu-item.h
deleted file mode 100644
index d616856..0000000
--- a/src/app-menu-item.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
-An indicator to show information that is in messaging applications
-that the user is using.
-
-Copyright 2009 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@canonical.com>
-
-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/>.
-*/
-#ifndef __APP_MENU_ITEM_H__
-#define __APP_MENU_ITEM_H__
-
-#include <glib.h>
-#include <glib-object.h>
-
-#include <libdbusmenu-glib/menuitem.h>
-#include <libindicate/listener.h>
-
-G_BEGIN_DECLS
-
-#define APP_MENU_ITEM_TYPE (app_menu_item_get_type ())
-#define APP_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), APP_MENU_ITEM_TYPE, AppMenuItem))
-#define APP_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), APP_MENU_ITEM_TYPE, AppMenuItemClass))
-#define IS_APP_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), APP_MENU_ITEM_TYPE))
-#define IS_APP_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), APP_MENU_ITEM_TYPE))
-#define APP_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), APP_MENU_ITEM_TYPE, AppMenuItemClass))
-
-#define APP_MENU_ITEM_SIGNAL_COUNT_CHANGED "count-changed"
-#define APP_MENU_ITEM_SIGNAL_NAME_CHANGED "name-changed"
-#define APP_MENU_ITEM_SIGNAL_SHORTCUT_ADDED "shortcut-added"
-#define APP_MENU_ITEM_SIGNAL_SHORTCUT_REMOVED "shortcut-removed"
-
-typedef struct _AppMenuItem AppMenuItem;
-typedef struct _AppMenuItemClass AppMenuItemClass;
-
-struct _AppMenuItemClass {
- DbusmenuMenuitemClass parent_class;
-
- void (* count_changed) (guint count);
- void (* name_changed) (gchar * name);
- void (* shortcut_added) (DbusmenuMenuitem * mi);
- void (* shortcut_removed) (DbusmenuMenuitem * mi);
-};
-
-struct _AppMenuItem {
- DbusmenuMenuitem parent;
-};
-
-GType app_menu_item_get_type (void);
-AppMenuItem * app_menu_item_new (IndicateListener * listener, IndicateListenerServer * server);
-guint app_menu_item_get_count (AppMenuItem * appitem);
-IndicateListenerServer * app_menu_item_get_server (AppMenuItem * appitem);
-const gchar * app_menu_item_get_name (AppMenuItem * appitem);
-const gchar * app_menu_item_get_desktop (AppMenuItem * appitem);
-GList * app_menu_item_get_items (AppMenuItem * appitem);
-
-G_END_DECLS
-
-#endif /* __APP_MENU_ITEM_H__ */
-
diff --git a/src/app-section.c b/src/app-section.c
new file mode 100644
index 0000000..70bf21e
--- /dev/null
+++ b/src/app-section.c
@@ -0,0 +1,695 @@
+/*
+An indicator to show information that is in messaging applications
+that the user is using.
+
+Copyright 2012 Canonical Ltd.
+
+Authors:
+ Lars Uebernickel <lars.uebernickel@canonical.com>
+ Ted Gould <ted@canonical.com>
+
+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/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib/gi18n.h>
+#include <gio/gdesktopappinfo.h>
+#include <gio/gio.h>
+#include <libindicator/indicator-desktop-shortcuts.h>
+#include "app-section.h"
+#include "dbus-data.h"
+#include "gmenuutils.h"
+#include "gactionmuxer.h"
+
+struct _AppSectionPrivate
+{
+ GDesktopAppInfo * appinfo;
+ guint unreadcount;
+
+ IndicatorDesktopShortcuts * ids;
+
+ GMenu *menu;
+ GMenuModel *source_menu;
+
+ GSimpleActionGroup *static_shortcuts;
+ GActionGroup *source_actions;
+ GActionMuxer *muxer;
+
+ gboolean draws_attention;
+ gboolean uses_chat_status;
+
+ guint name_watch_id;
+};
+
+enum {
+ PROP_0,
+ PROP_APPINFO,
+ PROP_ACTIONS,
+ PROP_DRAWS_ATTENTION,
+ PROP_USES_CHAT_STATUS,
+ NUM_PROPERTIES
+};
+
+static GParamSpec *properties[NUM_PROPERTIES];
+
+/* Prototypes */
+static void app_section_class_init (AppSectionClass *klass);
+static void app_section_init (AppSection *self);
+static void app_section_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void app_section_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void app_section_dispose (GObject *object);
+static void activate_cb (GSimpleAction *action,
+ GVariant *param,
+ gpointer userdata);
+static void launch_action_change_state (GSimpleAction *action,
+ GVariant *value,
+ 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);
+
+/* GObject Boilerplate */
+G_DEFINE_TYPE (AppSection, app_section, G_TYPE_OBJECT);
+
+static void
+app_section_class_init (AppSectionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (AppSectionPrivate));
+
+ object_class->get_property = app_section_get_property;
+ object_class->set_property = app_section_set_property;
+ object_class->dispose = app_section_dispose;
+
+ properties[PROP_APPINFO] = g_param_spec_object ("app-info",
+ "AppInfo",
+ "The GAppInfo for the app that this menu represents",
+ G_TYPE_APP_INFO,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ properties[PROP_ACTIONS] = g_param_spec_object ("actions",
+ "Actions",
+ "The actions exported by this application",
+ G_TYPE_ACTION_GROUP,
+ G_PARAM_READABLE);
+
+ properties[PROP_DRAWS_ATTENTION] = g_param_spec_boolean ("draws-attention",
+ "Draws attention",
+ "Whether the section currently draws attention",
+ FALSE,
+ G_PARAM_READABLE);
+
+ properties[PROP_USES_CHAT_STATUS] = g_param_spec_boolean ("uses-chat-status",
+ "Uses chat status",
+ "Whether the section uses the global chat status",
+ FALSE,
+ G_PARAM_READABLE);
+
+ g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
+}
+
+static void
+app_section_init (AppSection *self)
+{
+ AppSectionPrivate *priv;
+
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ APP_SECTION_TYPE,
+ AppSectionPrivate);
+ priv = self->priv;
+
+ priv->appinfo = NULL;
+ priv->unreadcount = 0;
+
+ priv->menu = g_menu_new ();
+ priv->static_shortcuts = g_simple_action_group_new ();
+
+ priv->muxer = g_action_muxer_new ();
+ g_action_muxer_insert (priv->muxer, NULL, G_ACTION_GROUP (priv->static_shortcuts));
+
+ priv->draws_attention = FALSE;
+
+ return;
+}
+
+static void
+app_section_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ AppSection *self = APP_SECTION (object);
+
+ switch (property_id)
+ {
+ case PROP_APPINFO:
+ g_value_set_object (value, app_section_get_app_info (self));
+ break;
+
+ case PROP_DRAWS_ATTENTION:
+ g_value_set_boolean (value, app_section_get_draws_attention (self));
+ break;
+
+ case PROP_USES_CHAT_STATUS:
+ g_value_set_boolean (value, app_section_get_uses_chat_status (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+app_section_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ AppSection *self = APP_SECTION (object);
+
+ switch (property_id)
+ {
+ case PROP_APPINFO:
+ app_section_set_app_info (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+static void
+app_section_dispose (GObject *object)
+{
+ AppSection * self = APP_SECTION(object);
+ AppSectionPrivate * priv = self->priv;
+
+ g_clear_object (&priv->menu);
+ g_clear_object (&priv->static_shortcuts);
+
+ 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->source_menu);
+ g_clear_object (&priv->ids);
+ g_clear_object (&priv->appinfo);
+
+ G_OBJECT_CLASS (app_section_parent_class)->dispose (object);
+}
+
+/* Respond to one of the shortcuts getting clicked on. */
+static void
+nick_activate_cb (GSimpleAction *action,
+ GVariant *param,
+ gpointer userdata)
+{
+ const gchar * nick = g_action_get_name (G_ACTION (action));
+ AppSection * mi = APP_SECTION (userdata);
+ AppSectionPrivate * priv = mi->priv;
+
+ g_return_if_fail(priv->ids != NULL);
+
+ if (!indicator_desktop_shortcuts_nick_exec(priv->ids, nick)) {
+ g_warning("Unable to execute nick '%s' for desktop file '%s'",
+ nick, g_desktop_app_info_get_filename (priv->appinfo));
+ }
+}
+
+static void
+keyfile_loaded (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ AppSection *self = user_data;
+ gchar *contents;
+ gsize length;
+ GKeyFile *keyfile;
+ GError *error = NULL;
+
+ if (!g_file_load_contents_finish (G_FILE (source_object), result,
+ &contents, &length, NULL, &error)) {
+ g_warning ("could not read key file: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ keyfile = g_key_file_new ();
+ if (!g_key_file_load_from_data (keyfile, contents, length, 0, &error)) {
+ g_warning ("could not read key file: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ self->priv->uses_chat_status = g_key_file_get_boolean (keyfile,
+ G_KEY_FILE_DESKTOP_GROUP,
+ "X-MessagingMenu-UsesChatSection",
+ &error);
+ if (error) {
+ if (error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
+ g_warning ("could not read X-MessagingMenu-UsesChatSection: %s",
+ error->message);
+ }
+ g_error_free (error);
+ goto out;
+ }
+
+ if (self->priv->uses_chat_status)
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_USES_CHAT_STATUS]);
+
+out:
+ g_key_file_free (keyfile);
+ g_free (contents);
+}
+
+static void
+app_section_set_app_info (AppSection *self,
+ GDesktopAppInfo *appinfo)
+{
+ AppSectionPrivate *priv = self->priv;
+ GSimpleAction *launch;
+ GFile *keyfile;
+ GMenuItem *item;
+ gchar *iconstr;
+
+ g_return_if_fail (priv->appinfo == NULL);
+
+ if (appinfo == NULL) {
+ g_warning ("appinfo must not be NULL");
+ return;
+ }
+
+ priv->appinfo = g_object_ref (appinfo);
+
+ launch = g_simple_action_new_stateful ("launch", NULL, g_variant_new_boolean (FALSE));
+ g_signal_connect (launch, "activate", G_CALLBACK (activate_cb), self);
+ g_signal_connect (launch, "change-state", G_CALLBACK (launch_action_change_state), self);
+ g_simple_action_group_insert (priv->static_shortcuts, G_ACTION (launch));
+
+ item = g_menu_item_new (g_app_info_get_name (G_APP_INFO (priv->appinfo)), "launch");
+ g_menu_item_set_attribute (item, "x-canonical-type", "s", "ImAppMenuItem");
+ iconstr = g_icon_to_string (g_app_info_get_icon (G_APP_INFO (priv->appinfo)));
+ g_menu_item_set_attribute (item, "x-canonical-icon", "s", iconstr);
+ g_free (iconstr);
+
+ g_menu_append_item (priv->menu, item);
+ g_object_unref (item);
+
+ /* Start to build static shortcuts */
+ priv->ids = indicator_desktop_shortcuts_new(g_desktop_app_info_get_filename (priv->appinfo), "Messaging Menu");
+ const gchar ** nicks = indicator_desktop_shortcuts_get_nicks(priv->ids);
+ gint i;
+ for (i = 0; nicks[i] != NULL; i++) {
+ gchar *name;
+ GSimpleAction *action;
+
+ name = indicator_desktop_shortcuts_nick_get_name(priv->ids, nicks[i]);
+
+ action = g_simple_action_new (nicks[i], NULL);
+ g_signal_connect(action, "activate", G_CALLBACK (nick_activate_cb), self);
+ g_simple_action_group_insert (priv->static_shortcuts, G_ACTION (action));
+
+ g_menu_append (priv->menu, name, nicks[i]);
+
+ g_free(name);
+ }
+
+ keyfile = g_file_new_for_path (g_desktop_app_info_get_filename (priv->appinfo));
+ g_file_load_contents_async (keyfile, NULL, keyfile_loaded, self);
+ g_object_unref (keyfile);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_APPINFO]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIONS]);
+
+ g_object_unref (launch);
+}
+
+AppSection *
+app_section_new (GDesktopAppInfo *appinfo)
+{
+ return g_object_new (APP_SECTION_TYPE,
+ "app-info", appinfo,
+ NULL);
+}
+
+static void
+activate_cb (GSimpleAction *action,
+ GVariant *param,
+ gpointer userdata)
+{
+ AppSection * mi = APP_SECTION (userdata);
+ AppSectionPrivate * priv = mi->priv;
+ GError *error = NULL;
+
+ if (!g_app_info_launch (G_APP_INFO (priv->appinfo), NULL, NULL, &error)) {
+ g_warning("Unable to execute application for desktop file '%s'",
+ g_desktop_app_info_get_filename (priv->appinfo));
+ }
+}
+
+static void
+launch_action_change_state (GSimpleAction *action,
+ GVariant *value,
+ gpointer user_data)
+{
+ g_simple_action_set_state (action, value);
+}
+
+guint
+app_section_get_count (AppSection * self)
+{
+ AppSectionPrivate * priv = self->priv;
+
+ return priv->unreadcount;
+}
+
+const gchar *
+app_section_get_name (AppSection * self)
+{
+ AppSectionPrivate * priv = self->priv;
+
+ if (priv->appinfo) {
+ return g_app_info_get_name(G_APP_INFO(priv->appinfo));
+ }
+ return NULL;
+}
+
+const gchar *
+app_section_get_desktop (AppSection * self)
+{
+ AppSectionPrivate * priv = self->priv;
+ if (priv->appinfo)
+ return g_desktop_app_info_get_filename (priv->appinfo);
+ else
+ return NULL;
+}
+
+GActionGroup *
+app_section_get_actions (AppSection *self)
+{
+ AppSectionPrivate * priv = self->priv;
+ return G_ACTION_GROUP (priv->muxer);
+}
+
+GMenuModel *
+app_section_get_menu (AppSection *self)
+{
+ AppSectionPrivate * priv = self->priv;
+ return G_MENU_MODEL (priv->menu);
+}
+
+GAppInfo *
+app_section_get_app_info (AppSection *self)
+{
+ AppSectionPrivate * priv = self->priv;
+ return G_APP_INFO (priv->appinfo);
+}
+
+gboolean
+app_section_get_draws_attention (AppSection *self)
+{
+ AppSectionPrivate * priv = self->priv;
+ return priv->draws_attention;
+}
+
+void
+app_section_clear_draws_attention (AppSection *self)
+{
+ AppSectionPrivate * priv = self->priv;
+ gchar **action_names;
+ gchar **it;
+
+ if (priv->source_actions == NULL)
+ return;
+
+ action_names = g_action_group_list_actions (priv->source_actions);
+
+ for (it = action_names; *it; it++) {
+ GVariant *state;
+
+ state = g_action_group_get_action_state (priv->source_actions, *it);
+ if (!state)
+ continue;
+
+ /* clear draws-attention while preserving other state */
+ if (action_draws_attention (state)) {
+ guint32 count;
+ gint64 time;
+ const gchar *str;
+ GVariant *new_state;
+
+ g_variant_get (state, "(ux&sb)", &count, &time, &str, NULL);
+
+ new_state = g_variant_new ("(uxsb)", count, time, str, FALSE);
+ g_action_group_change_action_state (priv->source_actions, *it, new_state);
+ }
+
+ g_variant_unref (state);
+ }
+
+ g_strfreev (action_names);
+}
+
+static void
+application_vanished (GDBusConnection *bus,
+ const gchar *name,
+ gpointer user_data)
+{
+ AppSection *self = user_data;
+
+ app_section_unset_object_path (self);
+}
+
+/*
+ * app_section_set_object_path:
+ * @self: an #AppSection
+ * @bus: a #GDBusConnection
+ * @bus_name: the bus name of the application
+ * @object_path: the object path on which the app exports its actions and menus
+ *
+ * Sets the D-Bus object path exported by an instance of the application
+ * associated with @self. Actions and menus exported on that path will be
+ * shown in the section.
+ */
+void
+app_section_set_object_path (AppSection *self,
+ GDBusConnection *bus,
+ const gchar *bus_name,
+ 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->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->name_watch_id = g_bus_watch_name_on_connection (bus, bus_name, 0,
+ NULL, application_vanished,
+ self, NULL);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIONS]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_USES_CHAT_STATUS]);
+ g_object_thaw_notify (G_OBJECT (self));
+
+ g_action_group_change_action_state (G_ACTION_GROUP (priv->static_shortcuts),
+ "launch", g_variant_new_boolean (TRUE));
+}
+
+/*
+ * app_section_unset_object_path:
+ * @self: an #AppSection
+ *
+ * Unsets the object path set with app_section_set_object_path(). The section
+ * will return to only showing application name and static shortcuts in the
+ * menu.
+ */
+void
+app_section_unset_object_path (AppSection *self)
+{
+ AppSectionPrivate *priv = self->priv;
+
+ 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);
+ }
+
+ priv->draws_attention = FALSE;
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIONS]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_USES_CHAT_STATUS]);
+
+ g_action_group_change_action_state (G_ACTION_GROUP (priv->static_shortcuts),
+ "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;
+ GVariant *state;
+
+ state = g_action_group_get_action_state (group, action_name);
+ if (!state)
+ return;
+
+ self->priv->draws_attention = any_action_draws_attention (group, action_name);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]);
+
+ g_variant_unref (state);
+}
+
+gboolean
+app_section_get_uses_chat_status (AppSection *self)
+{
+ AppSectionPrivate * priv = self->priv;
+
+ /* chat status is only useful when the app is running */
+ return priv->uses_chat_status && priv->source_actions;
+}
diff --git a/src/app-section.h b/src/app-section.h
new file mode 100644
index 0000000..711fdc9
--- /dev/null
+++ b/src/app-section.h
@@ -0,0 +1,72 @@
+/*
+An indicator to show information that is in messaging applications
+that the user is using.
+
+Copyright 2012 Canonical Ltd.
+
+Authors:
+ Lars Uebernickel <lars.uebernickel@canonical.com>
+ Ted Gould <ted@canonical.com>
+
+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/>.
+*/
+#ifndef __APP_SECTION_H__
+#define __APP_SECTION_H__
+
+#include <gio/gio.h>
+#include <gio/gdesktopappinfo.h>
+
+G_BEGIN_DECLS
+
+#define APP_SECTION_TYPE (app_section_get_type ())
+#define APP_SECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), APP_SECTION_TYPE, AppSection))
+#define APP_SECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), APP_SECTION_TYPE, AppSectionClass))
+#define IS_APP_SECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), APP_SECTION_TYPE))
+#define IS_APP_SECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), APP_SECTION_TYPE))
+#define APP_SECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), APP_SECTION_TYPE, AppSectionClass))
+
+typedef struct _AppSection AppSection;
+typedef struct _AppSectionClass AppSectionClass;
+typedef struct _AppSectionPrivate AppSectionPrivate;
+
+
+struct _AppSectionClass {
+ GObjectClass parent_class;
+};
+
+struct _AppSection {
+ GObject parent;
+ AppSectionPrivate *priv;
+};
+
+GType app_section_get_type (void);
+AppSection * app_section_new (GDesktopAppInfo *appinfo);
+guint app_section_get_count (AppSection * appitem);
+const gchar * app_section_get_name (AppSection * appitem);
+const gchar * app_section_get_desktop (AppSection * appitem);
+GActionGroup * app_section_get_actions (AppSection *self);
+GMenuModel * app_section_get_menu (AppSection *appitem);
+GAppInfo * app_section_get_app_info (AppSection *appitem);
+gboolean app_section_get_draws_attention (AppSection *appitem);
+void app_section_clear_draws_attention (AppSection *appitem);
+void app_section_set_object_path (AppSection *self,
+ GDBusConnection *bus,
+ const gchar *bus_name,
+ const gchar *object_path);
+void app_section_unset_object_path (AppSection *self);
+gboolean app_section_get_uses_chat_status (AppSection *self);
+
+G_END_DECLS
+
+#endif /* __APP_SECTION_H__ */
+
diff --git a/src/dbus-data.h b/src/dbus-data.h
index fac732e..64747a9 100644
--- a/src/dbus-data.h
+++ b/src/dbus-data.h
@@ -8,17 +8,4 @@
#define INDICATOR_MESSAGES_DBUS_SERVICE_OBJECT "/com/canonical/indicator/messages/service"
#define INDICATOR_MESSAGES_DBUS_SERVICE_INTERFACE "com.canonical.indicator.messages.service"
-#define APPLICATION_MENUITEM_TYPE "application-item"
-#define APPLICATION_MENUITEM_PROP_NAME "label"
-#define APPLICATION_MENUITEM_PROP_ICON "icon-name"
-#define APPLICATION_MENUITEM_PROP_RUNNING "app-running"
-
-#define INDICATOR_MENUITEM_TYPE "indicator-item"
-#define INDICATOR_MENUITEM_PROP_LABEL "indicator-label"
-#define INDICATOR_MENUITEM_PROP_ICON "indicator-icon"
-#define INDICATOR_MENUITEM_PROP_RIGHT "right-side-text"
-#define INDICATOR_MENUITEM_PROP_RIGHT_IS_LOZENGE "right-is-lozenge"
-
-#define MAX_NUMBER_OF_INDICATORS 7
-
#endif /* __DBUS_DATA_H__ */
diff --git a/src/default-applications.c b/src/default-applications.c
deleted file mode 100644
index 0382e4d..0000000
--- a/src/default-applications.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
-Looking for the default applications. A quick lookup.
-
-Copyright 2010 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@canonical.com>
-
-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/>.
-*/
-
-#include <glib.h>
-#include <glib/gi18n.h>
-#include <gio/gio.h>
-#include <gio/gdesktopappinfo.h>
-#include "default-applications.h"
-
-struct default_db_t {
- const gchar * desktop_file;
- const gchar * uri_scheme;
- const gchar * name;
- const gchar * setupname;
- const gchar * icon;
-};
-
-struct default_db_t default_db[] = {
- {NULL, "mailto", N_("Mail"), N_("Set Up Mail..."), "applications-email-panel"},
- {"empathy.desktop", NULL, N_("Chat"), N_("Set Up Chat..."), "applications-chat-panel"},
- {"gwibber.desktop", NULL, N_("Broadcast"), N_("Set Up Broadcast Account..."), "applications-microblogging-panel"},
-};
-
-static struct default_db_t *
-get_default_helper (const gchar * desktop_path)
-{
- g_return_val_if_fail(desktop_path != NULL, NULL);
- gchar * basename = g_path_get_basename(desktop_path);
- g_return_val_if_fail(basename != NULL, NULL);
-
- gboolean found = FALSE;
- gint i;
- gint length = G_N_ELEMENTS(default_db);
- for (i = 0; i < length && !found; i++) {
- if (default_db[i].desktop_file) {
- if (g_strcmp0(default_db[i].desktop_file, basename) == 0) {
- found = TRUE;
- }
- } else if (default_db[i].uri_scheme) {
- GAppInfo *info = g_app_info_get_default_for_uri_scheme(default_db[i].uri_scheme);
- if (!info) {
- continue;
- }
-
- const gchar * filename = g_desktop_app_info_get_filename(G_DESKTOP_APP_INFO(info));
- if (!filename) {
- g_object_unref(info);
- continue;
- }
-
- gchar * default_basename = g_path_get_basename(filename);
- g_object_unref(info);
- if (g_strcmp0(default_basename, basename) == 0) {
- found = TRUE;
- }
-
- g_free(default_basename);
- }
- }
-
- g_free(basename);
-
- if (found) {
- return &default_db[i - 1];
- }
-
- return NULL;
-}
-
-const gchar *
-get_default_name (const gchar * desktop_path)
-{
- struct default_db_t * db = get_default_helper(desktop_path);
-
- if (db == NULL)
- return NULL;
- return db->name;
-}
-
-const gchar *
-get_default_setup (const gchar * desktop_path)
-{
- struct default_db_t * db = get_default_helper(desktop_path);
-
- if (db == NULL)
- return NULL;
- return db->setupname;
-}
-
-const gchar *
-get_default_icon (const gchar * desktop_path)
-{
- struct default_db_t * db = get_default_helper(desktop_path);
-
- if (db == NULL)
- return NULL;
- return db->icon;
-}
diff --git a/src/default-applications.h b/src/default-applications.h
deleted file mode 100644
index df52b38..0000000
--- a/src/default-applications.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
-Looking for the default applications. A quick lookup.
-
-Copyright 2010 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@canonical.com>
-
-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/>.
-*/
-
-#ifndef DEFAULT_APPLICATIONS_H__
-#define DEFAULT_APPLICATIONS_H__ 1
-
-/* Used for override icons in the normal case, but didn't
- have a better place to put it. */
-#define ICON_KEY "X-Ayatana-Messaging-Menu-Icon"
-
-const gchar * get_default_name (const gchar * desktop_path);
-const gchar * get_default_setup (const gchar * desktop_path);
-const gchar * get_default_icon (const gchar * desktop_path);
-
-#endif /* DEFAULT_APPLICATIONS_H__ */
-
diff --git a/src/dirs.h b/src/dirs.h
deleted file mode 100644
index ca322f0..0000000
--- a/src/dirs.h
+++ /dev/null
@@ -1,4 +0,0 @@
-#define SYSTEM_APPS_DIR "/usr/share/indicators/messages/applications"
-#define SYSTEM_APPS_DIR_OLD "/etc/indicators/messages/applications"
-#define USER_APPS_DIR "indicators/messages/applications"
-#define USER_BLACKLIST_DIR "indicators/messages/applications-blacklist"
diff --git a/src/gactionmuxer.c b/src/gactionmuxer.c
new file mode 100644
index 0000000..2b1d11a
--- /dev/null
+++ b/src/gactionmuxer.c
@@ -0,0 +1,485 @@
+/*
+ * 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>
+ * Ryan Lortie <desrt@desrt.ca>
+ */
+
+#include "gactionmuxer.h"
+
+#include <string.h>
+
+/*
+ * SECTION:gactionmuxer
+ * @short_description: Aggregate several action groups
+ *
+ * #GActionMuxer is a #GActionGroup that is capable of containing other
+ * #GActionGroup instances.
+ *
+ * The typical use is aggregating all of the actions applicable to a
+ * particular context into a single action group, with namespacing.
+ *
+ * Consider the case of two action groups -- one containing actions
+ * applicable to an entire application (such as 'quit') and one
+ * containing actions applicable to a particular window in the
+ * application (such as 'fullscreen').
+ *
+ * In this case, each of these action groups could be added to a
+ * #GActionMuxer with the prefixes "app" and "win", respectively. This
+ * would expose the actions as "app.quit" and "win.fullscreen" on the
+ * #GActionGroup interface presented by the #GActionMuxer.
+ *
+ * Activations and state change requests on the #GActionMuxer are wired
+ * through to the underlying action group in the expected way.
+ */
+
+typedef GObjectClass GActionMuxerClass;
+
+struct _GActionMuxer
+{
+ GObject parent;
+ GActionGroup *global_actions;
+ GHashTable *groups; /* prefix -> subgroup */
+ GHashTable *reverse; /* subgroup -> prefix */
+};
+
+
+static void g_action_muxer_group_init (GActionGroupInterface *iface);
+static void g_action_muxer_dispose (GObject *object);
+static void g_action_muxer_finalize (GObject *object);
+static void g_action_muxer_disconnect_group (GActionMuxer *muxer,
+ GActionGroup *subgroup);
+static gchar ** g_action_muxer_list_actions (GActionGroup *group);
+static void g_action_muxer_activate_action (GActionGroup *group,
+ const gchar *action_name,
+ GVariant *parameter);
+static void g_action_muxer_change_action_state (GActionGroup *group,
+ const gchar *action_name,
+ GVariant *value);
+static gboolean g_action_muxer_query_action (GActionGroup *group,
+ const gchar *action_name,
+ gboolean *enabled,
+ const GVariantType **parameter_type,
+ const GVariantType **state_type,
+ GVariant **state_hint,
+ GVariant **state);
+static void g_action_muxer_action_added (GActionGroup *group,
+ gchar *action_name,
+ gpointer user_data);
+static void g_action_muxer_action_removed (GActionGroup *group,
+ gchar *action_name,
+ gpointer user_data);
+static void g_action_muxer_action_state_changed (GActionGroup *group,
+ gchar *action_name,
+ GVariant *value,
+ gpointer user_data);
+static void g_action_muxer_action_enabled_changed (GActionGroup *group,
+ gchar *action_name,
+ gboolean enabled,
+ gpointer user_data);
+
+G_DEFINE_TYPE_WITH_CODE (GActionMuxer, g_action_muxer, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, g_action_muxer_group_init));
+
+
+static void
+g_action_muxer_class_init (GObjectClass *klass)
+{
+ klass->dispose = g_action_muxer_dispose;
+ klass->finalize = g_action_muxer_finalize;
+}
+
+static void
+g_action_muxer_init (GActionMuxer *muxer)
+{
+ muxer->global_actions = NULL;
+ muxer->groups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+ muxer->reverse = g_hash_table_new (g_direct_hash, g_direct_equal);
+}
+
+static void
+g_action_muxer_group_init (GActionGroupInterface *iface)
+{
+ iface->list_actions = g_action_muxer_list_actions;
+ iface->activate_action = g_action_muxer_activate_action;
+ iface->change_action_state = g_action_muxer_change_action_state;
+ iface->query_action = g_action_muxer_query_action;
+}
+
+static void
+g_action_muxer_dispose (GObject *object)
+{
+ GActionMuxer *muxer = G_ACTION_MUXER (object);
+ GHashTableIter it;
+ GActionGroup *subgroup;
+
+ if (muxer->global_actions)
+ {
+ g_action_muxer_disconnect_group (muxer, muxer->global_actions);
+ g_clear_object (&muxer->global_actions);
+ }
+
+ g_hash_table_iter_init (&it, muxer->groups);
+ while (g_hash_table_iter_next (&it, NULL, (gpointer *) &subgroup))
+ g_action_muxer_disconnect_group (muxer, subgroup);
+
+ g_hash_table_remove_all (muxer->groups);
+ g_hash_table_remove_all (muxer->reverse);
+}
+
+static void
+g_action_muxer_finalize (GObject *object)
+{
+ GActionMuxer *muxer = G_ACTION_MUXER (object);
+
+ g_hash_table_unref (muxer->groups);
+ g_hash_table_unref (muxer->reverse);
+
+ G_OBJECT_CLASS (g_action_muxer_parent_class)->finalize (object);
+}
+
+static GActionGroup *
+g_action_muxer_lookup_group (GActionMuxer *muxer,
+ const gchar *full_name,
+ const gchar **action_name)
+{
+ const gchar *sep;
+ GActionGroup *group;
+
+ sep = strchr (full_name, '.');
+
+ if (sep)
+ {
+ gchar *prefix;
+ prefix = g_strndup (full_name, sep - full_name);
+ group = g_hash_table_lookup (muxer->groups, prefix);
+ g_free (prefix);
+ if (action_name)
+ *action_name = sep + 1;
+ }
+ else
+ {
+ group = muxer->global_actions;
+ if (action_name)
+ *action_name = full_name;
+ }
+
+ return group;
+}
+
+static gchar *
+g_action_muxer_lookup_full_name (GActionMuxer *muxer,
+ GActionGroup *subgroup,
+ const gchar *action_name)
+{
+ gpointer prefix;
+
+ if (subgroup == muxer->global_actions)
+ return g_strdup (action_name);
+
+ if (g_hash_table_lookup_extended (muxer->reverse, subgroup, NULL, &prefix))
+ return g_strdup_printf ("%s.%s", (gchar *) prefix, action_name);
+
+ return NULL;
+}
+
+static void
+g_action_muxer_disconnect_group (GActionMuxer *muxer,
+ GActionGroup *subgroup)
+{
+ gchar **actions;
+ gchar **action;
+
+ actions = g_action_group_list_actions (subgroup);
+ for (action = actions; *action; action++)
+ g_action_muxer_action_removed (subgroup, *action, muxer);
+ g_strfreev (actions);
+
+ g_signal_handlers_disconnect_by_func (subgroup, g_action_muxer_action_added, muxer);
+ g_signal_handlers_disconnect_by_func (subgroup, g_action_muxer_action_removed, muxer);
+ g_signal_handlers_disconnect_by_func (subgroup, g_action_muxer_action_enabled_changed, muxer);
+ g_signal_handlers_disconnect_by_func (subgroup, g_action_muxer_action_state_changed, muxer);
+}
+
+static gchar **
+g_action_muxer_list_actions (GActionGroup *group)
+{
+ GActionMuxer *muxer = G_ACTION_MUXER (group);
+ GHashTableIter it;
+ GArray *all_actions;
+ gchar *prefix;
+ GActionGroup *subgroup;
+ gchar **actions;
+ gchar **a;
+
+ all_actions = g_array_sized_new (TRUE, FALSE, sizeof (gchar *), 8);
+
+ if (muxer->global_actions)
+ {
+ actions = g_action_group_list_actions (muxer->global_actions);
+ for (a = actions; *a; a++)
+ {
+ gchar *name = g_strdup (*a);
+ g_array_append_val (all_actions, name);
+ }
+ g_strfreev (actions);
+ }
+
+ g_hash_table_iter_init (&it, muxer->groups);
+ while (g_hash_table_iter_next (&it, (gpointer *) &prefix, (gpointer *) &subgroup))
+ {
+ actions = g_action_group_list_actions (subgroup);
+ for (a = actions; *a; a++)
+ {
+ gchar *full_name = g_strdup_printf ("%s.%s", prefix, *a);
+ g_array_append_val (all_actions, full_name);
+ }
+ g_strfreev (actions);
+ }
+
+ return (gchar **) g_array_free (all_actions, FALSE);
+}
+
+static void
+g_action_muxer_activate_action (GActionGroup *group,
+ const gchar *action_name,
+ GVariant *parameter)
+{
+ GActionMuxer *muxer = G_ACTION_MUXER (group);
+ GActionGroup *subgroup;
+ const gchar *action;
+
+ g_return_if_fail (action_name != NULL);
+
+ subgroup = g_action_muxer_lookup_group (muxer, action_name, &action);
+
+ if (subgroup)
+ g_action_group_activate_action (subgroup, action, parameter);
+}
+
+static void
+g_action_muxer_change_action_state (GActionGroup *group,
+ const gchar *action_name,
+ GVariant *value)
+{
+ GActionMuxer *muxer = G_ACTION_MUXER (group);
+ GActionGroup *subgroup;
+ const gchar *action;
+
+ g_return_if_fail (action_name != NULL);
+
+ subgroup = g_action_muxer_lookup_group (muxer, action_name, &action);
+
+ if (subgroup)
+ g_action_group_change_action_state (subgroup, action, value);
+}
+
+static gboolean
+g_action_muxer_query_action (GActionGroup *group,
+ const gchar *action_name,
+ gboolean *enabled,
+ const GVariantType **parameter_type,
+ const GVariantType **state_type,
+ GVariant **state_hint,
+ GVariant **state)
+{
+ GActionMuxer *muxer = G_ACTION_MUXER (group);
+ GActionGroup *subgroup;
+ const gchar *action;
+
+ g_return_val_if_fail (action_name != NULL, FALSE);
+
+ subgroup = g_action_muxer_lookup_group (muxer, action_name, &action);
+
+ if (!subgroup)
+ return FALSE;
+
+ return g_action_group_query_action (subgroup, action, enabled, parameter_type,
+ state_type, state_hint, state);
+}
+
+static void
+g_action_muxer_action_added (GActionGroup *group,
+ gchar *action_name,
+ gpointer user_data)
+{
+ GActionMuxer *muxer = user_data;
+ gchar *full_name;
+
+ full_name = g_action_muxer_lookup_full_name (muxer, group, action_name);
+
+ if (full_name)
+ {
+ g_action_group_action_added (G_ACTION_GROUP (muxer), full_name);
+ g_free (full_name);
+ }
+}
+
+static void
+g_action_muxer_action_removed (GActionGroup *group,
+ gchar *action_name,
+ gpointer user_data)
+{
+ GActionMuxer *muxer = user_data;
+ gchar *full_name;
+
+ full_name = g_action_muxer_lookup_full_name (muxer, group, action_name);
+
+ if (full_name)
+ {
+ g_action_group_action_removed (G_ACTION_GROUP (muxer), full_name);
+ g_free (full_name);
+ }
+}
+
+static void
+g_action_muxer_action_state_changed (GActionGroup *group,
+ gchar *action_name,
+ GVariant *value,
+ gpointer user_data)
+{
+ GActionMuxer *muxer = user_data;
+ gchar *full_name;
+
+ full_name = g_action_muxer_lookup_full_name (muxer, group, action_name);
+
+ if (full_name)
+ {
+ g_action_group_action_state_changed (G_ACTION_GROUP (muxer), full_name, value);
+ g_free (full_name);
+ }
+}
+
+static void
+g_action_muxer_action_enabled_changed (GActionGroup *group,
+ gchar *action_name,
+ gboolean enabled,
+ gpointer user_data)
+{
+ GActionMuxer *muxer = user_data;
+ gchar *full_name;
+
+ full_name = g_action_muxer_lookup_full_name (muxer, group, action_name);
+
+ if (full_name)
+ {
+ g_action_group_action_enabled_changed (G_ACTION_GROUP (muxer), full_name, enabled);
+ g_free (full_name);
+ }
+}
+
+/*
+ * g_action_muxer_new:
+ *
+ * Creates a new #GActionMuxer.
+ */
+GActionMuxer *
+g_action_muxer_new (void)
+{
+ return g_object_new (G_TYPE_ACTION_MUXER, NULL);
+}
+
+/*
+ * g_action_muxer_insert:
+ * @muxer: a #GActionMuxer
+ * @prefix: (allow-none): the prefix string for the action group, or NULL
+ * @group: (allow-none): a #GActionGroup, or NULL
+ *
+ * Adds the actions in @group to the list of actions provided by @muxer.
+ * @prefix is prefixed to each action name, such that for each action
+ * <varname>x</varname> in @group, there is an equivalent action
+ * @prefix<literal>.</literal><varname>x</varname> in @muxer.
+ *
+ * For example, if @prefix is "<literal>app</literal>" and @group contains an
+ * action called "<literal>quit</literal>", then @muxer will now contain an
+ * action called "<literal>app.quit</literal>".
+ *
+ * If @prefix is <literal>NULL</literal>, the actions in @group will be added
+ * to @muxer without prefix.
+ *
+ * If @group is <literal>NULL</literal>, this function has the same effect as
+ * calling g_action_muxer_remove() with @prefix.
+ *
+ * There may only be one group per prefix (including the
+ * <literal>NULL</literal>-prefix). If a group has been added with @prefix in
+ * a previous call to this function, it will be removed.
+ *
+ * @prefix must not contain a dot ('.').
+ */
+void
+g_action_muxer_insert (GActionMuxer *muxer,
+ const gchar *prefix,
+ GActionGroup *group)
+{
+ gchar *prefix_copy;
+ gchar **actions;
+ gchar **action;
+
+ g_return_if_fail (G_IS_ACTION_MUXER (muxer));
+ g_return_if_fail (G_IS_ACTION_GROUP (group));
+
+ g_action_muxer_remove (muxer, prefix);
+
+ if (prefix)
+ {
+ prefix_copy = g_strdup (prefix);
+ g_hash_table_insert (muxer->groups, prefix_copy, g_object_ref (group));
+ g_hash_table_insert (muxer->reverse, group, prefix_copy);
+ }
+ else
+ muxer->global_actions = g_object_ref (group);
+
+ actions = g_action_group_list_actions (group);
+ for (action = actions; *action; action++)
+ g_action_muxer_action_added (group, *action, muxer);
+ g_strfreev (actions);
+
+ g_signal_connect (group, "action-added", G_CALLBACK (g_action_muxer_action_added), muxer);
+ g_signal_connect (group, "action-removed", G_CALLBACK (g_action_muxer_action_removed), muxer);
+ g_signal_connect (group, "action-enabled-changed", G_CALLBACK (g_action_muxer_action_enabled_changed), muxer);
+ g_signal_connect (group, "action-state-changed", G_CALLBACK (g_action_muxer_action_state_changed), muxer);
+}
+
+/*
+ * g_action_muxer_remove:
+ * @muxer: a #GActionMuxer
+ * @prefix: (allow-none): the prefix of the action group to remove, or NULL
+ *
+ * Removes a #GActionGroup from the #GActionMuxer.
+ */
+void
+g_action_muxer_remove (GActionMuxer *muxer,
+ const gchar *prefix)
+{
+ GActionGroup *subgroup;
+
+ g_return_if_fail (G_IS_ACTION_MUXER (muxer));
+
+ subgroup = prefix ? g_hash_table_lookup (muxer->groups, prefix) : muxer->global_actions;
+ if (!subgroup)
+ return;
+
+ g_action_muxer_disconnect_group (muxer, subgroup);
+
+ if (prefix)
+ {
+ g_hash_table_remove (muxer->groups, prefix);
+ g_hash_table_remove (muxer->reverse, subgroup);
+ }
+ else
+ g_clear_object (&muxer->global_actions);
+}
+
diff --git a/src/gactionmuxer.h b/src/gactionmuxer.h
new file mode 100644
index 0000000..5c5e839
--- /dev/null
+++ b/src/gactionmuxer.h
@@ -0,0 +1,44 @@
+/*
+ * 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>
+ * Ryan Lortie <desrt@desrt.ca>
+ */
+
+#ifndef __G_ACTION_MUXER_H__
+#define __G_ACTION_MUXER_H__
+
+#include <gio/gio.h>
+
+#define G_TYPE_ACTION_MUXER (g_action_muxer_get_type ())
+#define G_ACTION_MUXER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_ACTION_MUXER, GActionMuxer))
+#define G_IS_ACTION_MUXER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_ACTION_MUXER))
+
+typedef struct _GActionMuxer GActionMuxer;
+
+GType g_action_muxer_get_type (void) G_GNUC_CONST;
+
+GActionMuxer * g_action_muxer_new (void);
+
+void g_action_muxer_insert (GActionMuxer *muxer,
+ const gchar *prefix,
+ GActionGroup *group);
+
+void g_action_muxer_remove (GActionMuxer *muxer,
+ const gchar *prefix);
+
+#endif
+
diff --git a/src/gmenuutils.c b/src/gmenuutils.c
new file mode 100644
index 0000000..f3ceba7
--- /dev/null
+++ b/src/gmenuutils.c
@@ -0,0 +1,79 @@
+/*
+ * 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 "gmenuutils.h"
+#include "dbus-data.h"
+
+/* g_menu_find_section:
+ * @menu: a #GMenu
+ * @section: the section to be found in @menu
+ *
+ * @Returns the index of the first menu item that is linked to #section, or -1
+ * if there's no such item.
+ */
+int
+g_menu_find_section (GMenu *menu,
+ GMenuModel *section)
+{
+ GMenuModel *model = G_MENU_MODEL (menu);
+ int n_items;
+ int i;
+
+ g_return_val_if_fail (G_IS_MENU_MODEL (section), -1);
+
+ n_items = g_menu_model_get_n_items (model);
+ for (i = 0; i < n_items; i++)
+ {
+ if (section == g_menu_model_get_item_link (model, i, G_MENU_LINK_SECTION))
+ return i;
+ }
+
+ return -1;
+}
+
+
+void
+g_menu_append_with_icon (GMenu *menu,
+ const gchar *label,
+ GIcon *icon,
+ const gchar *detailed_action)
+{
+ gchar *iconstr;
+
+ iconstr = g_icon_to_string (icon);
+ g_menu_append_with_icon_name (menu, label, iconstr, detailed_action);
+
+ g_free (iconstr);
+}
+
+void
+g_menu_append_with_icon_name (GMenu *menu,
+ const gchar *label,
+ const gchar *icon_name,
+ const gchar *detailed_action)
+{
+ GMenuItem *item;
+
+ item = g_menu_item_new (label, detailed_action);
+ g_menu_item_set_attribute (item, "x-canonical-icon", "s", icon_name);
+
+ g_menu_append_item (menu, item);
+
+ g_object_unref (item);
+}
diff --git a/src/gmenuutils.h b/src/gmenuutils.h
new file mode 100644
index 0000000..e00ac55
--- /dev/null
+++ b/src/gmenuutils.h
@@ -0,0 +1,38 @@
+/*
+ * 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_MENU_UTILS_H__
+#define __G_MENU_UTILS_H__
+
+#include <gio/gio.h>
+
+int g_menu_find_section (GMenu *menu,
+ GMenuModel *section);
+
+void g_menu_append_with_icon (GMenu *menu,
+ const gchar *label,
+ GIcon *icon,
+ const gchar *detailed_action);
+
+void g_menu_append_with_icon_name (GMenu *menu,
+ const gchar *label,
+ const gchar *icon_name,
+ const gchar *detailed_action);
+
+#endif
diff --git a/src/gsettingsstrv.c b/src/gsettingsstrv.c
new file mode 100644
index 0000000..cf889fa
--- /dev/null
+++ b/src/gsettingsstrv.c
@@ -0,0 +1,106 @@
+/*
+ * 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 "gsettingsstrv.h"
+
+/**
+ * g_settings_strv_append_unique:
+ * @settings: a #GSettings object
+ * @key: the key at which @settings contains a string array
+ * @item: the string to append
+ *
+ * Appends @item to the string array at @key if that string array doesn't
+ * contain @item yet.
+ *
+ * Returns: TRUE if @item was added to the list, FALSE if it already existed.
+ */
+gboolean
+g_settings_strv_append_unique (GSettings *settings,
+ const gchar *key,
+ const gchar *item)
+{
+ gchar **strv;
+ gchar **it;
+ gboolean add = TRUE;
+
+ g_return_val_if_fail (G_IS_SETTINGS (settings), FALSE);
+ g_return_val_if_fail (key != NULL, FALSE);
+ g_return_val_if_fail (item != NULL, FALSE);
+
+ strv = g_settings_get_strv (settings, key);
+
+ for (it = strv; *it; it++)
+ {
+ if (g_str_equal (*it, item))
+ {
+ add = FALSE;
+ break;
+ }
+ }
+
+ if (add)
+ {
+ GVariantBuilder builder;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
+
+ for (it = strv; *it; it++)
+ g_variant_builder_add (&builder, "s", *it);
+ g_variant_builder_add (&builder, "s", item);
+
+ g_settings_set_value (settings, key, g_variant_builder_end (&builder));
+ }
+
+ g_strfreev (strv);
+ return add;
+}
+
+/**
+ * g_settings_strv_remove:
+ * @settings: a #GSettings object
+ * @key: the key at which @settings contains a string array
+ * @item: the string to remove
+ *
+ * Removes all occurences of @item in @key.
+ */
+void
+g_settings_strv_remove (GSettings *settings,
+ const gchar *key,
+ const gchar *item)
+{
+ gchar **strv;
+ gchar **it;
+ GVariantBuilder builder;
+
+ g_return_if_fail (G_IS_SETTINGS (settings));
+ g_return_if_fail (key != NULL);
+ g_return_if_fail (item != NULL);
+
+ strv = g_settings_get_strv (settings, key);
+
+ g_variant_builder_init (&builder, (GVariantType *)"as");
+ for (it = strv; *it; it++)
+ {
+ if (!g_str_equal (*it, item))
+ g_variant_builder_add (&builder, "s", *it);
+ }
+ g_settings_set_value (settings, key, g_variant_builder_end (&builder));
+
+ g_strfreev (strv);
+}
diff --git a/src/gsettingsstrv.h b/src/gsettingsstrv.h
new file mode 100644
index 0000000..accebda
--- /dev/null
+++ b/src/gsettingsstrv.h
@@ -0,0 +1,33 @@
+/*
+ * 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_SETTINGS_STRV_H__
+#define __G_SETTINGS_STRV_H__
+
+#include <gio/gio.h>
+
+gboolean g_settings_strv_append_unique (GSettings *settings,
+ const gchar *key,
+ const gchar *item);
+
+void g_settings_strv_remove (GSettings *settings,
+ const gchar *key,
+ const gchar *item);
+
+#endif
diff --git a/src/im-app-menu-item.c b/src/im-app-menu-item.c
new file mode 100644
index 0000000..eddf562
--- /dev/null
+++ b/src/im-app-menu-item.c
@@ -0,0 +1,350 @@
+/*
+ * 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 "im-app-menu-item.h"
+
+struct _ImAppMenuItemPrivate
+{
+ GActionGroup *action_group;
+ gchar *action;
+ gboolean is_running;
+
+ GtkWidget *icon;
+ GtkWidget *label;
+};
+
+enum
+{
+ PROP_0,
+ PROP_MENU_ITEM,
+ PROP_ACTION_GROUP,
+ NUM_PROPERTIES
+};
+
+static GParamSpec *properties[NUM_PROPERTIES];
+
+G_DEFINE_TYPE (ImAppMenuItem, im_app_menu_item, GTK_TYPE_MENU_ITEM);
+
+static void
+im_app_menu_item_constructed (GObject *object)
+{
+ ImAppMenuItemPrivate *priv = IM_APP_MENU_ITEM (object)->priv;
+ GtkWidget *grid;
+
+ priv->icon = g_object_ref (gtk_image_new ());
+
+ priv->label = g_object_ref (gtk_label_new (""));
+
+ grid = gtk_grid_new ();
+ gtk_grid_attach (GTK_GRID (grid), priv->icon, 0, 0, 1, 1);
+ gtk_grid_attach (GTK_GRID (grid), priv->label, 1, 0, 1, 1);
+
+ gtk_container_add (GTK_CONTAINER (object), grid);
+ gtk_widget_show_all (grid);
+
+ G_OBJECT_CLASS (im_app_menu_item_parent_class)->constructed (object);
+}
+
+static void
+im_app_menu_item_set_action_name (ImAppMenuItem *self,
+ const gchar *action_name)
+{
+ ImAppMenuItemPrivate *priv = self->priv;
+ gboolean enabled = FALSE;
+ GVariant *state;
+
+ if (priv->action != NULL)
+ g_free (priv->action);
+
+ priv->action = g_strdup (action_name);
+
+ priv->is_running = FALSE;
+
+ if (priv->action_group != NULL && priv->action != NULL &&
+ g_action_group_query_action (priv->action_group, priv->action,
+ &enabled, NULL, NULL, NULL, &state))
+ {
+ if (state && g_variant_is_of_type (state, G_VARIANT_TYPE ("b")))
+ priv->is_running = g_variant_get_boolean (state);
+ else
+ enabled = FALSE;
+
+ if (state)
+ g_variant_unref (state);
+ }
+
+ gtk_widget_set_sensitive (GTK_WIDGET (self), enabled);
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+im_app_menu_item_action_added (GActionGroup *action_group,
+ gchar *action_name,
+ gpointer user_data)
+{
+ ImAppMenuItem *self = user_data;
+
+ if (g_strcmp0 (self->priv->action, action_name) == 0)
+ im_app_menu_item_set_action_name (self, action_name);
+}
+
+static void
+im_app_menu_item_action_removed (GActionGroup *action_group,
+ gchar *action_name,
+ gpointer user_data)
+{
+ ImAppMenuItem *self = user_data;
+
+ if (g_strcmp0 (self->priv->action, action_name) == 0)
+ {
+ gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
+ self->priv->is_running = FALSE;
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+ }
+}
+
+static void
+im_app_menu_item_action_enabled_changed (GActionGroup *action_group,
+ gchar *action_name,
+ gboolean enabled,
+ gpointer user_data)
+{
+ ImAppMenuItem *self = user_data;
+
+ if (g_strcmp0 (self->priv->action, action_name) == 0)
+ gtk_widget_set_sensitive (GTK_WIDGET (self), enabled);
+}
+
+static void
+im_app_menu_item_action_state_changed (GActionGroup *action_group,
+ gchar *action_name,
+ GVariant *value,
+ gpointer user_data)
+{
+ ImAppMenuItem *self = user_data;
+
+ if (g_strcmp0 (self->priv->action, action_name) == 0)
+ {
+ g_return_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE ("b")));
+
+ self->priv->is_running = g_variant_get_boolean (value);
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+ }
+}
+
+static void
+im_app_menu_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ImAppMenuItem *self = IM_APP_MENU_ITEM (object);
+
+ switch (property_id)
+ {
+ case PROP_MENU_ITEM:
+ im_app_menu_item_set_menu_item (self, G_MENU_ITEM (g_value_get_object (value)));
+ break;
+
+ case PROP_ACTION_GROUP:
+ im_app_menu_item_set_action_group (self, G_ACTION_GROUP (g_value_get_object (value)));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+im_app_menu_item_dispose (GObject *object)
+{
+ ImAppMenuItem *self = IM_APP_MENU_ITEM (object);
+
+ if (self->priv->action_group)
+ im_app_menu_item_set_action_group (self, NULL);
+
+ g_clear_object (&self->priv->icon);
+ g_clear_object (&self->priv->label);
+
+ G_OBJECT_CLASS (im_app_menu_item_parent_class)->dispose (object);
+}
+
+static void
+im_app_menu_item_finalize (GObject *object)
+{
+ ImAppMenuItemPrivate *priv = IM_APP_MENU_ITEM (object)->priv;
+
+ g_free (priv->action);
+
+ G_OBJECT_CLASS (im_app_menu_item_parent_class)->finalize (object);
+}
+
+static gboolean
+im_app_menu_item_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ ImAppMenuItemPrivate *priv = IM_APP_MENU_ITEM (widget)->priv;
+
+ GTK_WIDGET_CLASS (im_app_menu_item_parent_class)->draw (widget, cr);
+
+ if (priv->is_running)
+ {
+ const int arrow_width = 5;
+ const double half_arrow_height = 4.5;
+ GtkAllocation alloc;
+ GdkRGBA color;
+ double center;
+
+ gtk_widget_get_allocation (widget, &alloc);
+
+ gtk_style_context_get_color (gtk_widget_get_style_context (widget),
+ gtk_widget_get_state_flags (widget),
+ &color);
+ gdk_cairo_set_source_rgba (cr, &color);
+
+ center = alloc.height / 2 + 0.5;
+
+ cairo_move_to (cr, 0, center - half_arrow_height);
+ cairo_line_to (cr, 0, center + half_arrow_height);
+ cairo_line_to (cr, arrow_width, center);
+ cairo_close_path (cr);
+
+ cairo_fill (cr);
+ }
+
+ return FALSE;
+}
+
+static void
+im_app_menu_item_activate (GtkMenuItem *item)
+{
+ ImAppMenuItemPrivate *priv = IM_APP_MENU_ITEM (item)->priv;
+
+ if (priv->action && priv->action_group)
+ g_action_group_activate_action (priv->action_group, priv->action, NULL);
+}
+
+static void
+im_app_menu_item_class_init (ImAppMenuItemClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkMenuItemClass *menu_item_class = GTK_MENU_ITEM_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (ImAppMenuItemPrivate));
+
+ object_class->constructed = im_app_menu_item_constructed;
+ object_class->set_property = im_app_menu_set_property;
+ object_class->dispose = im_app_menu_item_dispose;
+ object_class->finalize = im_app_menu_item_finalize;
+
+ widget_class->draw = im_app_menu_item_draw;
+
+ menu_item_class->activate = im_app_menu_item_activate;
+
+ properties[PROP_MENU_ITEM] = g_param_spec_object ("menu-item",
+ "Menu item",
+ "The model GMenuItem for this menu item",
+ G_TYPE_MENU_ITEM,
+ G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_ACTION_GROUP] = g_param_spec_object ("action-group",
+ "Action group",
+ "The action group associated with this menu item",
+ G_TYPE_ACTION_GROUP,
+ G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
+}
+
+static void
+im_app_menu_item_init (ImAppMenuItem *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ IM_TYPE_APP_MENU_ITEM,
+ ImAppMenuItemPrivate);
+}
+
+void
+im_app_menu_item_set_menu_item (ImAppMenuItem *self,
+ GMenuItem *menuitem)
+{
+ gchar *iconstr = NULL;
+ GIcon *icon = NULL;
+ gchar *label;
+ gchar *action = NULL;
+
+ if (g_menu_item_get_attribute (menuitem, "x-canonical-icon", "s", &iconstr))
+ {
+ GError *error;
+
+ icon = g_icon_new_for_string (iconstr, &error);
+ if (icon == NULL)
+ {
+ g_warning ("unable to set icon: %s", error->message);
+ g_error_free (error);
+ }
+ g_free (iconstr);
+ }
+ gtk_image_set_from_gicon (GTK_IMAGE (self->priv->icon), icon, GTK_ICON_SIZE_MENU);
+
+ g_menu_item_get_attribute (menuitem, "label", "s", &label);
+ gtk_label_set_label (GTK_LABEL (self->priv->label), label ? label : "");
+
+ g_menu_item_get_attribute (menuitem, "action", "s", &action);
+ im_app_menu_item_set_action_name (self, action);
+
+ if (icon)
+ g_object_unref (icon);
+ g_free (label);
+ g_free (action);
+}
+
+void
+im_app_menu_item_set_action_group (ImAppMenuItem *self,
+ GActionGroup *action_group)
+{
+ ImAppMenuItemPrivate *priv = self->priv;
+
+ if (priv->action_group != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (priv->action_group, im_app_menu_item_action_added, self);
+ g_signal_handlers_disconnect_by_func (priv->action_group, im_app_menu_item_action_removed, self);
+ g_signal_handlers_disconnect_by_func (priv->action_group, im_app_menu_item_action_enabled_changed, self);
+ g_signal_handlers_disconnect_by_func (priv->action_group, im_app_menu_item_action_state_changed, self);
+
+ g_clear_object (&priv->action_group);
+ }
+
+ if (action_group != NULL)
+ {
+ priv->action_group = g_object_ref (action_group);
+
+ g_signal_connect (priv->action_group, "action-added",
+ G_CALLBACK (im_app_menu_item_action_added), self);
+ g_signal_connect (priv->action_group, "action-removed",
+ G_CALLBACK (im_app_menu_item_action_removed), self);
+ g_signal_connect (priv->action_group, "action-enabled-changed",
+ G_CALLBACK (im_app_menu_item_action_enabled_changed), self);
+ g_signal_connect (priv->action_group, "action-state-changed",
+ G_CALLBACK (im_app_menu_item_action_state_changed), self);
+ }
+}
diff --git a/src/im-app-menu-item.h b/src/im-app-menu-item.h
new file mode 100644
index 0000000..519de8d
--- /dev/null
+++ b/src/im-app-menu-item.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2012 Canonical Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Lars Uebernickel <lars.uebernickel@canonical.com>
+ */
+
+#ifndef __IM_APP_MENU_ITEM_H__
+#define __IM_APP_MENU_ITEM_H__
+
+#include <gtk/gtk.h>
+
+#define IM_TYPE_APP_MENU_ITEM (im_app_menu_item_get_type ())
+#define IM_APP_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IM_TYPE_APP_MENU_ITEM, ImAppMenuItem))
+#define IM_APP_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), IM_TYPE_APP_MENU_ITEM, ImAppMenuItemClass))
+#define IS_IM_APP_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IM_TYPE_APP_MENU_ITEM))
+#define IS_IM_APP_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), IM_TYPE_APP_MENU_ITEM))
+#define IM_APP_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), IM_TYPE_APP_MENU_ITEM, ImAppMenuItemClass))
+
+typedef struct _ImAppMenuItem ImAppMenuItem;
+typedef struct _ImAppMenuItemClass ImAppMenuItemClass;
+typedef struct _ImAppMenuItemPrivate ImAppMenuItemPrivate;
+
+struct _ImAppMenuItemClass
+{
+ GtkMenuItemClass parent_class;
+};
+
+struct _ImAppMenuItem
+{
+ GtkMenuItem parent;
+ ImAppMenuItemPrivate *priv;
+};
+
+GType im_app_menu_item_get_type (void);
+
+void im_app_menu_item_set_menu_item (ImAppMenuItem *item,
+ GMenuItem *menuitem);
+void im_app_menu_item_set_action_group (ImAppMenuItem *self,
+ GActionGroup *action_group);
+
+#endif
diff --git a/src/im-menu-item.c b/src/im-menu-item.c
deleted file mode 100644
index 7466d3e..0000000
--- a/src/im-menu-item.c
+++ /dev/null
@@ -1,586 +0,0 @@
-/*
-An indicator to show information that is in messaging applications
-that the user is using.
-
-Copyright 2009 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@canonical.com>
-
-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/>.
-*/
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <glib/gi18n.h>
-#include <libdbusmenu-glib/client.h>
-#include <libindicate/indicator.h>
-#include <libindicate/indicator-messages.h>
-#include <libindicate/listener.h>
-#include "im-menu-item.h"
-#include "dbus-data.h"
-
-enum {
- TIME_CHANGED,
- ATTENTION_CHANGED,
- LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-typedef struct _ImMenuItemPrivate ImMenuItemPrivate;
-
-struct _ImMenuItemPrivate
-{
- IndicateListener * listener;
- IndicateListenerServer * server;
- IndicateListenerIndicator * indicator;
-
- glong creation_seconds;
- glong seconds;
- gchar * count;
- gulong indicator_changed;
- gboolean attention;
- gboolean show;
-
- guint time_update_min;
-};
-
-#define IM_MENU_ITEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), IM_MENU_ITEM_TYPE, ImMenuItemPrivate))
-
-/* Prototypes */
-static void im_menu_item_class_init (ImMenuItemClass *klass);
-static void im_menu_item_init (ImMenuItem *self);
-static void im_menu_item_dispose (GObject *object);
-static void im_menu_item_finalize (GObject *object);
-static void sender_cb (IndicateListener * listener,
- IndicateListenerServer * server,
- IndicateListenerIndicator * indicator,
- gchar * property,
- const gchar * propertydata,
- gpointer data);
-static void time_cb (IndicateListener * listener,
- IndicateListenerServer * server,
- IndicateListenerIndicator * indicator,
- gchar * property,
- const GTimeVal * propertydata,
- gpointer data);
-static void icon_cb (IndicateListener * listener,
- IndicateListenerServer * server,
- IndicateListenerIndicator * indicator,
- gchar * property,
- const gchar * propertydata,
- gpointer data);
-static void activate_cb (ImMenuItem * self,
- guint timestamp,
- gpointer data);
-static void indicator_modified_cb (IndicateListener * listener,
- IndicateListenerServer * server,
- IndicateListenerIndicator * indicator,
- gchar * property,
- ImMenuItem * self);
-
-G_DEFINE_TYPE (ImMenuItem, im_menu_item, DBUSMENU_TYPE_MENUITEM);
-
-static void
-im_menu_item_class_init (ImMenuItemClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- g_type_class_add_private (klass, sizeof (ImMenuItemPrivate));
-
- object_class->dispose = im_menu_item_dispose;
- object_class->finalize = im_menu_item_finalize;
-
- signals[TIME_CHANGED] = g_signal_new(IM_MENU_ITEM_SIGNAL_TIME_CHANGED,
- G_TYPE_FROM_CLASS(klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (ImMenuItemClass, time_changed),
- NULL, NULL,
- g_cclosure_marshal_VOID__LONG,
- G_TYPE_NONE, 1, G_TYPE_LONG);
- signals[ATTENTION_CHANGED] = g_signal_new(IM_MENU_ITEM_SIGNAL_ATTENTION_CHANGED,
- G_TYPE_FROM_CLASS(klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (ImMenuItemClass, attention_changed),
- NULL, NULL,
- g_cclosure_marshal_VOID__BOOLEAN,
- G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
-
- return;
-}
-
-static void
-im_menu_item_init (ImMenuItem *self)
-{
- g_debug("Building new IM Menu Item");
- ImMenuItemPrivate * priv = IM_MENU_ITEM_GET_PRIVATE(self);
-
- /* Set the variables to NULL, but they should be
- configured further down the line. */
- priv->listener = NULL;
- priv->server = NULL;
- priv->indicator = NULL;
-
- /* A sane default, but look below */
- priv->creation_seconds = 0;
- priv->seconds = 0;
-
- /* Set the seconds to be the time when the item was
- created incase we're not given a better time. */
- GTimeVal current_time;
- g_get_current_time(&current_time);
- priv->creation_seconds = current_time.tv_sec;
-
- return;
-}
-
-static void
-im_menu_item_dispose (GObject *object)
-{
- G_OBJECT_CLASS (im_menu_item_parent_class)->dispose (object);
-
- ImMenuItem * self = IM_MENU_ITEM(object);
- ImMenuItemPrivate * priv = IM_MENU_ITEM_GET_PRIVATE(self);
-
- if (priv->time_update_min != 0) {
- g_source_remove(priv->time_update_min);
- }
-
- g_signal_handler_disconnect(priv->listener, priv->indicator_changed);
- priv->indicator_changed = 0;
-
- return;
-}
-
-static void
-im_menu_item_finalize (GObject *object)
-{
- G_OBJECT_CLASS (im_menu_item_parent_class)->finalize (object);
-}
-
-/* Call back for getting icon data. It just passes it along
- to the indicator so that it can visualize it. Not our problem. */
-static void
-icon_cb (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * property, const gchar * propertydata, gpointer data)
-{
- gsize len;
- guchar *icon;
- icon = g_base64_decode (propertydata, &len);
- dbusmenu_menuitem_property_set_byte_array(DBUSMENU_MENUITEM(data), INDICATOR_MENUITEM_PROP_ICON, icon, len);
- g_free (icon);
- return;
-}
-
-/* This function takes the time and turns it into the appropriate
- string to put on the right side of the menu item. Of course it
- doesn't do that if there is a count set. If there's a count then
- it gets that space. */
-static void
-update_time (ImMenuItem * self)
-{
- ImMenuItemPrivate * priv = IM_MENU_ITEM_GET_PRIVATE(self);
-
- /* Count has been set, so it takes priority. */
- if (priv->count != NULL) {
- return;
- }
-
- /* Seconds hasn't been set, so we just want to keep the time
- area blank. */
- if (priv->seconds == 0) {
- dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), INDICATOR_MENUITEM_PROP_RIGHT, "");
- return;
- }
-
- gchar * timestring = NULL;
-
- GTimeVal current_time;
- g_get_current_time(&current_time);
-
- guint elapsed_seconds = current_time.tv_sec - priv->seconds;
- guint elapsed_minutes = elapsed_seconds / 60;
-
- if (elapsed_seconds % 60 > 55) {
- /* We're using fuzzy timers, so we need fuzzy comparisons */
- elapsed_minutes += 1;
- }
-
- if (elapsed_minutes < 60) {
- /* TRANSLATORS: This string is used to represent the number of minutes
- since an IM has occured. It is in the right column
- of a menu so being brief is desirable, but one character
- is not a requirement. */
- timestring = g_strdup_printf(ngettext("%d m", "%d m", elapsed_minutes), elapsed_minutes);
- } else {
- guint elapsed_hours = elapsed_minutes / 60;
-
- if (elapsed_minutes % 60 > 55) {
- /* We're using fuzzy timers, so we need fuzzy comparisons */
- elapsed_hours += 1;
- }
-
- /* TRANSLATORS: This string is used to represent the number of hours
- since an IM has occured. It is in the right column
- of a menu so being brief is desirable, but one character
- is not a requirement. */
- timestring = g_strdup_printf(ngettext("%d h", "%d h", elapsed_hours), elapsed_hours);
- }
-
- if (timestring != NULL) {
- dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), INDICATOR_MENUITEM_PROP_RIGHT, timestring);
- g_free(timestring);
- }
-
- return;
-}
-
-/* This is a wrapper around update_time that matches the prototype
- needed to make this a timer callback. Silly. */
-static gboolean
-time_update_cb (gpointer data)
-{
- ImMenuItem * self = IM_MENU_ITEM(data);
-
- update_time(self);
-
- return TRUE;
-}
-
-/* Yet another time function. This one takes the time as formated as
- we get it from libindicate and turns it into the seconds that we're
- looking for. It should only be called once at the init with a new
- indicator and again when the value changes. */
-static void
-time_cb (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * property, const GTimeVal * propertydata, gpointer data)
-{
- g_debug("Got Time info");
- ImMenuItem * self = IM_MENU_ITEM(data);
- if (self == NULL) {
- g_error("Menu Item callback called without a menu item");
- return;
- }
-
- if (property == NULL || g_strcmp0(property, "time")) {
- g_warning("Time callback called without being sent the time.");
- return;
- }
-
- ImMenuItemPrivate * priv = IM_MENU_ITEM_GET_PRIVATE(self);
-
- priv->seconds = propertydata->tv_sec;
-
- update_time(self);
-
- if (priv->time_update_min == 0) {
- priv->time_update_min = g_timeout_add_seconds(60, time_update_cb, self);
- }
-
- g_signal_emit(G_OBJECT(self), signals[TIME_CHANGED], 0, priv->seconds, TRUE);
-
- return;
-}
-
-/* Returns a newly allocated string which is 'str' with all occurences of
- * consecutive whitespace collapsed into single space character. */
-static gchar *
-collapse_whitespace (const gchar *str)
-{
- GString *result;
- gboolean in_space = FALSE;
-
- if (!str)
- return NULL;
-
- result = g_string_sized_new (strlen (str));
-
- while (*str) {
- gunichar c = g_utf8_get_char_validated (str, -1);
-
- if (c < 0)
- break;
-
- if (!g_unichar_isspace (c)) {
- g_string_append_unichar (result, c);
- in_space = FALSE;
- }
- else if (!in_space) {
- g_string_append_c (result, ' ');
- in_space = TRUE;
- }
-
- str = g_utf8_next_char (str);
- }
-
- return g_string_free (result, FALSE);
-}
-
-/* Callback from libindicate that is for getting the sender information
- on a particular indicator. */
-static void
-sender_cb (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * property, const gchar * propertydata, gpointer data)
-{
- gchar *label;
-
- g_debug("Got Sender Information: %s", propertydata);
- ImMenuItem * self = IM_MENU_ITEM(data);
-
- /* Our data should be right */
- g_return_if_fail(self != NULL);
- /* We should have a property name */
- g_return_if_fail(property != NULL);
- /* The Property should be sender or name */
- g_return_if_fail(!g_strcmp0(property, "sender") || !g_strcmp0(property, INDICATE_INDICATOR_MESSAGES_PROP_NAME));
-
- /* We might get the sender variable returning a
- null string as it doesn't exist on newer clients
- but we don't want to listen to that. */
- if (!g_strcmp0(property, "sender") && propertydata[0] == '\0') {
- return;
- }
-
- label = collapse_whitespace (propertydata);
- dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), INDICATOR_MENUITEM_PROP_LABEL, label);
- g_free (label);
-
- return;
-}
-
-/* Callback saying that the count is updated, we need to either put
- that on the menu item or just remove it if the count is gone. If
- that's the case we can update time. */
-static void
-count_cb (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * property, const gchar * propertydata, gpointer data)
-{
- g_debug("Got Count Information");
- ImMenuItem * self = IM_MENU_ITEM(data);
-
- /* Our data should be right */
- g_return_if_fail(self != NULL);
- /* We should have a property name */
- g_return_if_fail(property != NULL);
- /* The Property should be count */
- g_return_if_fail(!g_strcmp0(property, INDICATE_INDICATOR_MESSAGES_PROP_COUNT));
-
- ImMenuItemPrivate * priv = IM_MENU_ITEM_GET_PRIVATE(self);
-
- if (propertydata == NULL || propertydata[0] == '\0') {
- /* The count is either being unset or it was never
- set in the first place. */
- if (priv->count != NULL) {
- g_free(priv->count);
- priv->count = NULL;
- update_time(self);
- dbusmenu_menuitem_property_set_bool (DBUSMENU_MENUITEM (self),
- INDICATOR_MENUITEM_PROP_RIGHT_IS_LOZENGE,
- FALSE);
- }
- return;
- }
-
- if (priv->count != NULL) {
- g_free(priv->count);
- }
-
- priv->count = g_strdup_printf("%s", propertydata);
- dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), INDICATOR_MENUITEM_PROP_RIGHT, priv->count);
- dbusmenu_menuitem_property_set_bool (DBUSMENU_MENUITEM (self),
- INDICATOR_MENUITEM_PROP_RIGHT_IS_LOZENGE,
- TRUE);
-
- return;
-}
-
-/* This is getting the attention variable that's looking at whether
- this indicator should be calling for attention or not. If we are,
- we need to signal that. */
-static void
-attention_cb (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * property, GVariant * propertydata, gpointer data)
-{
- g_debug("Got Attention Information");
- ImMenuItem * self = IM_MENU_ITEM(data);
-
- /* Our data should be right */
- g_return_if_fail(self != NULL);
- /* We should have a property name */
- g_return_if_fail(property != NULL);
- /* The Property should be count */
- g_return_if_fail(!g_strcmp0(property, INDICATE_INDICATOR_MESSAGES_PROP_ATTENTION));
-
- ImMenuItemPrivate * priv = IM_MENU_ITEM_GET_PRIVATE(self);
-
- gboolean wantit;
- if (g_variant_is_of_type(propertydata, G_VARIANT_TYPE_BOOLEAN)) {
- wantit = g_variant_get_boolean(propertydata);
- } else if (g_variant_is_of_type(propertydata, G_VARIANT_TYPE_STRING)) {
- const gchar * propstring = g_variant_get_string(propertydata, NULL);
-
- if (propstring == NULL || propstring[0] == '\0' || !g_strcmp0(propstring, "false")) {
- wantit = FALSE;
- } else {
- wantit = TRUE;
- }
- } else {
- g_warning("Got property '%s' of an unknown type.", property);
- return;
- }
-
- if (priv->attention != wantit) {
- priv->attention = wantit;
- g_signal_emit(G_OBJECT(self), signals[ATTENTION_CHANGED], 0, wantit, TRUE);
- }
-
- return;
-}
-
-/* Callback when the item gets clicked on from the Messaging Menu */
-static void
-activate_cb (ImMenuItem * self, guint timestamp, gpointer data)
-{
- ImMenuItemPrivate * priv = IM_MENU_ITEM_GET_PRIVATE(self);
-
- indicate_listener_display(priv->listener, priv->server, priv->indicator, timestamp);
-}
-
-/* Callback when a property gets modified. It figures out which one
- got modified and notifies the appropriate person. */
-void
-indicator_modified_cb (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * property, ImMenuItem * self)
-{
- ImMenuItemPrivate * priv = IM_MENU_ITEM_GET_PRIVATE(self);
-
- /* Not meant for us */
- if (INDICATE_LISTENER_INDICATOR_ID(indicator) != INDICATE_LISTENER_INDICATOR_ID(priv->indicator)) return;
- if (server != priv->server) return;
-
- /* Determine which property has been changed and request the
- value go to the appropriate callback. */
- if (!g_strcmp0(property, INDICATE_INDICATOR_MESSAGES_PROP_NAME)) {
- indicate_listener_get_property(listener, server, indicator, INDICATE_INDICATOR_MESSAGES_PROP_NAME, sender_cb, self);
- } else if (!g_strcmp0(property, INDICATE_INDICATOR_MESSAGES_PROP_TIME)) {
- indicate_listener_get_property_time(listener, server, indicator, INDICATE_INDICATOR_MESSAGES_PROP_TIME, time_cb, self);
- } else if (!g_strcmp0(property, INDICATE_INDICATOR_MESSAGES_PROP_ICON)) {
- indicate_listener_get_property(listener, server, indicator, INDICATE_INDICATOR_MESSAGES_PROP_ICON, icon_cb, self);
- } else if (!g_strcmp0(property, INDICATE_INDICATOR_MESSAGES_PROP_COUNT)) {
- indicate_listener_get_property(listener, server, indicator, INDICATE_INDICATOR_MESSAGES_PROP_COUNT, count_cb, self);
- } else if (!g_strcmp0(property, INDICATE_INDICATOR_MESSAGES_PROP_ATTENTION)) {
- indicate_listener_get_property_variant(listener, server, indicator, INDICATE_INDICATOR_MESSAGES_PROP_ATTENTION, attention_cb, self);
- } else if (!g_strcmp0(property, "sender")) {
- /* This is a compatibility string with v1 and should be removed */
- g_debug("Indicator is using 'sender' property which is a v1 string.");
- indicate_listener_get_property(listener, server, indicator, "sender", sender_cb, self);
- }
-
- return;
-}
-
-ImMenuItem *
-im_menu_item_new (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator)
-{
- ImMenuItem * self = g_object_new(IM_MENU_ITEM_TYPE, NULL);
-
- ImMenuItemPrivate * priv = IM_MENU_ITEM_GET_PRIVATE(self);
-
- priv->listener = listener;
- priv->server = server;
- priv->indicator = indicator;
- priv->count = NULL;
- priv->time_update_min = 0;
- priv->attention = FALSE;
- priv->show = TRUE;
-
- dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_TYPE, INDICATOR_MENUITEM_TYPE);
-
- indicate_listener_displayed(listener, server, indicator, TRUE);
-
- indicate_listener_get_property(listener, server, indicator, INDICATE_INDICATOR_MESSAGES_PROP_NAME, sender_cb, self);
- indicate_listener_get_property_time(listener, server, indicator, INDICATE_INDICATOR_MESSAGES_PROP_TIME, time_cb, self);
- indicate_listener_get_property(listener, server, indicator, INDICATE_INDICATOR_MESSAGES_PROP_ICON, icon_cb, self);
- indicate_listener_get_property(listener, server, indicator, INDICATE_INDICATOR_MESSAGES_PROP_COUNT, count_cb, self);
- indicate_listener_get_property_variant(listener, server, indicator, INDICATE_INDICATOR_MESSAGES_PROP_ATTENTION, attention_cb, self);
- indicate_listener_get_property(listener, server, indicator, "sender", sender_cb, self);
-
- g_signal_connect(G_OBJECT(self), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(activate_cb), NULL);
- priv->indicator_changed = g_signal_connect(G_OBJECT(listener), INDICATE_LISTENER_SIGNAL_INDICATOR_MODIFIED, G_CALLBACK(indicator_modified_cb), self);
-
- return self;
-}
-
-/* Gets the number of seconds for the creator
- of this item. */
-glong
-im_menu_item_get_seconds (ImMenuItem * menuitem)
-{
- g_return_val_if_fail(IS_IM_MENU_ITEM(menuitem), 0);
-
- ImMenuItemPrivate * priv = IM_MENU_ITEM_GET_PRIVATE(menuitem);
-
- if (priv->seconds == 0) {
- return priv->creation_seconds;
- } else {
- return priv->seconds;
- }
-}
-
-/* Gets whether or not this indicator item is
- asking for attention or not. */
-gboolean
-im_menu_item_get_attention (ImMenuItem * menuitem)
-{
- g_return_val_if_fail(IS_IM_MENU_ITEM(menuitem), FALSE);
-
- ImMenuItemPrivate * priv = IM_MENU_ITEM_GET_PRIVATE(menuitem);
- return priv->attention;
-}
-
-/* This takes care of items that need to be hidden, this is
- usually because they go over the count of allowed indicators.
- Which is more than a little bit silly. We shouldn't do that.
- But we need to enforce it to save users against bad apps. */
-void
-im_menu_item_show (ImMenuItem * menuitem, gboolean show)
-{
- g_return_if_fail(IS_IM_MENU_ITEM(menuitem));
-
- ImMenuItemPrivate * priv = IM_MENU_ITEM_GET_PRIVATE(menuitem);
-
- if (priv->show == show) {
- return;
- }
-
- priv->show = show;
- /* Tell the app what we're doing to it. If it's being
- punished it needs to know about it. */
- indicate_listener_displayed(priv->listener, priv->server, priv->indicator, priv->show);
- if (priv->attention) {
- /* If we were asking for attention we can ask for it
- again if we're being shown, otherwise no. */
- g_signal_emit(G_OBJECT(menuitem), signals[ATTENTION_CHANGED], 0, priv->show, TRUE);
- }
- dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(menuitem), DBUSMENU_MENUITEM_PROP_VISIBLE, priv->show ? "true" : "false");
-
- return;
-}
-
-/* Check to see if this item is shown. Accessor for the
- internal variable. */
-gboolean
-im_menu_item_shown (ImMenuItem * menuitem)
-{
- g_return_val_if_fail(IS_IM_MENU_ITEM(menuitem), FALSE);
-
- ImMenuItemPrivate * priv = IM_MENU_ITEM_GET_PRIVATE(menuitem);
-
- return priv->show;
-}
diff --git a/src/im-menu-item.h b/src/im-menu-item.h
deleted file mode 100644
index 4279c2e..0000000
--- a/src/im-menu-item.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
-An indicator to show information that is in messaging applications
-that the user is using.
-
-Copyright 2009 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@canonical.com>
-
-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/>.
-*/
-#ifndef __IM_MENU_ITEM_H__
-#define __IM_MENU_ITEM_H__
-
-#include <glib.h>
-#include <glib-object.h>
-
-#include <libdbusmenu-glib/menuitem.h>
-#include <libindicate/listener.h>
-
-G_BEGIN_DECLS
-
-#define IM_MENU_ITEM_TYPE (im_menu_item_get_type ())
-#define IM_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IM_MENU_ITEM_TYPE, ImMenuItem))
-#define IM_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), IM_MENU_ITEM_TYPE, ImMenuItemClass))
-#define IS_IM_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IM_MENU_ITEM_TYPE))
-#define IS_IM_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), IM_MENU_ITEM_TYPE))
-#define IM_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), IM_MENU_ITEM_TYPE, ImMenuItemClass))
-
-#define IM_MENU_ITEM_SIGNAL_TIME_CHANGED "time-changed"
-#define IM_MENU_ITEM_SIGNAL_ATTENTION_CHANGED "attention-changed"
-
-typedef struct _ImMenuItem ImMenuItem;
-typedef struct _ImMenuItemClass ImMenuItemClass;
-
-struct _ImMenuItemClass {
- DbusmenuMenuitemClass parent_class;
-
- void (*time_changed) (glong seconds);
- void (*attention_changed) (gboolean requestit);
-};
-
-struct _ImMenuItem {
- DbusmenuMenuitem parent;
-};
-
-GType im_menu_item_get_type (void);
-ImMenuItem * im_menu_item_new (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator);
-glong im_menu_item_get_seconds (ImMenuItem * menuitem);
-gboolean im_menu_item_get_attention (ImMenuItem * menuitem);
-void im_menu_item_show (ImMenuItem * menuitem, gboolean show);
-gboolean im_menu_item_shown (ImMenuItem * menuitem);
-
-G_END_DECLS
-
-#endif
-
diff --git a/src/im-source-menu-item.c b/src/im-source-menu-item.c
new file mode 100644
index 0000000..269c75d
--- /dev/null
+++ b/src/im-source-menu-item.c
@@ -0,0 +1,410 @@
+/*
+ * 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 "im-source-menu-item.h"
+
+#include <libintl.h>
+
+struct _ImSourceMenuItemPrivate
+{
+ GActionGroup *action_group;
+ gchar *action;
+
+ GtkWidget *icon;
+ GtkWidget *label;
+ GtkWidget *detail;
+};
+
+enum
+{
+ PROP_0,
+ PROP_MENU_ITEM,
+ PROP_ACTION_GROUP,
+ NUM_PROPERTIES
+};
+
+static GParamSpec *properties[NUM_PROPERTIES];
+
+G_DEFINE_TYPE (ImSourceMenuItem, im_source_menu_item, GTK_TYPE_MENU_ITEM);
+
+static void
+im_source_menu_item_constructed (GObject *object)
+{
+ ImSourceMenuItemPrivate *priv = IM_SOURCE_MENU_ITEM (object)->priv;
+ GtkWidget *grid;
+ gint icon_width;
+
+ gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &icon_width, NULL);
+
+ priv->icon = g_object_ref (gtk_image_new ());
+ gtk_widget_set_margin_left (priv->icon, icon_width + 2);
+
+ priv->label = g_object_ref (gtk_label_new (""));
+
+ priv->detail = g_object_ref (gtk_label_new (""));
+ gtk_widget_set_halign (priv->detail, GTK_ALIGN_END);
+ gtk_widget_set_hexpand (priv->detail, TRUE);
+ gtk_misc_set_alignment (GTK_MISC (priv->label), 1.0, 0.5);
+ gtk_style_context_add_class (gtk_widget_get_style_context (priv->detail), "accelerator");
+
+ grid = gtk_grid_new ();
+ gtk_grid_attach (GTK_GRID (grid), priv->icon, 0, 0, 1, 1);
+ gtk_grid_attach (GTK_GRID (grid), priv->label, 1, 0, 1, 1);
+ gtk_grid_attach (GTK_GRID (grid), priv->detail, 2, 0, 1, 1);
+
+ gtk_container_add (GTK_CONTAINER (object), grid);
+ gtk_widget_show_all (grid);
+
+ G_OBJECT_CLASS (im_source_menu_item_parent_class)->constructed (object);
+}
+
+/* collapse_whitespace:
+ * @str: the source string
+ *
+ * Collapses all occurences of consecutive whitespace charactes in @str
+ * into a single space.
+ *
+ * Returns: (transfer full): a newly-allocated string
+ */
+static gchar *
+collapse_whitespace (const gchar *str)
+{
+ GString *result;
+ gboolean in_space = FALSE;
+
+ if (str == NULL)
+ return NULL;
+
+ result = g_string_new ("");
+
+ while (*str)
+ {
+ gunichar c = g_utf8_get_char_validated (str, -1);
+
+ if (c < 0)
+ break;
+
+ if (!g_unichar_isspace (c))
+ {
+ g_string_append_unichar (result, c);
+ in_space = FALSE;
+ }
+ else if (!in_space)
+ {
+ g_string_append_c (result, ' ');
+ in_space = TRUE;
+ }
+
+ str = g_utf8_next_char (str);
+ }
+
+ return g_string_free (result, FALSE);
+}
+
+static gchar *
+im_source_menu_item_time_span_string (gint64 timestamp)
+{
+ gchar *str;
+ gint64 span;
+ gint hours;
+ gint minutes;
+
+ span = MAX (g_get_real_time () - timestamp, 0) / G_USEC_PER_SEC;
+ hours = span / 3600;
+ minutes = (span / 60) % 60;
+
+ if (hours == 0)
+ {
+ /* TRANSLATORS: number of minutes that have passed */
+ str = g_strdup_printf (ngettext ("%d m", "%d m", minutes), minutes);
+ }
+ else
+ {
+ /* TRANSLATORS: number of hours that have passed */
+ str = g_strdup_printf (ngettext ("%d h", "%d h", hours), hours);
+ }
+
+ return str;
+}
+
+static gboolean
+im_source_menu_item_set_state (ImSourceMenuItem *self,
+ GVariant *state)
+{
+ ImSourceMenuItemPrivate *priv = self->priv;
+ guint32 count;
+ gint64 time;
+ const gchar *str;
+ gchar *detail;
+
+ g_return_val_if_fail (g_variant_is_of_type (state, G_VARIANT_TYPE ("(uxsb)")), FALSE);
+
+ g_variant_get (state, "(ux&sb)", &count, &time, &str, NULL);
+
+ if (count != 0)
+ detail = g_strdup_printf ("%d", count);
+ else if (time != 0)
+ detail = im_source_menu_item_time_span_string (time);
+ else if (str != NULL && *str)
+ detail = collapse_whitespace (str);
+ else
+ detail = NULL;
+
+ gtk_label_set_text (GTK_LABEL (priv->detail), detail ? detail : "");
+
+ g_free (detail);
+ return TRUE;
+}
+
+static void
+im_source_menu_item_set_action_name (ImSourceMenuItem *self,
+ const gchar *action_name)
+{
+ ImSourceMenuItemPrivate *priv = self->priv;
+ gboolean enabled = FALSE;
+ GVariant *state;
+
+ if (priv->action != NULL)
+ g_free (priv->action);
+
+ priv->action = g_strdup (action_name);
+
+ if (priv->action_group != NULL && priv->action != NULL &&
+ g_action_group_query_action (priv->action_group, priv->action,
+ &enabled, NULL, NULL, NULL, &state))
+ {
+ if (!state || !im_source_menu_item_set_state (self, state))
+ enabled = FALSE;
+
+ if (state)
+ g_variant_unref (state);
+ }
+
+ gtk_widget_set_sensitive (GTK_WIDGET (self), enabled);
+}
+
+static void
+im_source_menu_item_action_added (GActionGroup *action_group,
+ gchar *action_name,
+ gpointer user_data)
+{
+ ImSourceMenuItem *self = user_data;
+
+ if (g_strcmp0 (self->priv->action, action_name) == 0)
+ im_source_menu_item_set_action_name (self, action_name);
+}
+
+static void
+im_source_menu_item_action_removed (GActionGroup *action_group,
+ gchar *action_name,
+ gpointer user_data)
+{
+ ImSourceMenuItem *self = user_data;
+
+ if (g_strcmp0 (self->priv->action, action_name) == 0)
+ {
+ gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
+ }
+}
+
+static void
+im_source_menu_item_action_enabled_changed (GActionGroup *action_group,
+ gchar *action_name,
+ gboolean enabled,
+ gpointer user_data)
+{
+ ImSourceMenuItem *self = user_data;
+
+ if (g_strcmp0 (self->priv->action, action_name) == 0)
+ gtk_widget_set_sensitive (GTK_WIDGET (self), enabled);
+}
+
+static void
+im_source_menu_item_action_state_changed (GActionGroup *action_group,
+ gchar *action_name,
+ GVariant *value,
+ gpointer user_data)
+{
+ ImSourceMenuItem *self = user_data;
+
+ if (g_strcmp0 (self->priv->action, action_name) == 0)
+ im_source_menu_item_set_state (self, value);
+}
+
+static void
+im_source_menu_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ ImSourceMenuItem *self = IM_SOURCE_MENU_ITEM (object);
+
+ switch (property_id)
+ {
+ case PROP_MENU_ITEM:
+ im_source_menu_item_set_menu_item (self, G_MENU_ITEM (g_value_get_object (value)));
+ break;
+
+ case PROP_ACTION_GROUP:
+ im_source_menu_item_set_action_group (self, G_ACTION_GROUP (g_value_get_object (value)));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+im_source_menu_item_dispose (GObject *object)
+{
+ ImSourceMenuItem *self = IM_SOURCE_MENU_ITEM (object);
+
+ if (self->priv->action_group)
+ im_source_menu_item_set_action_group (self, NULL);
+
+ g_clear_object (&self->priv->icon);
+ g_clear_object (&self->priv->label);
+ g_clear_object (&self->priv->detail);
+
+ G_OBJECT_CLASS (im_source_menu_item_parent_class)->dispose (object);
+}
+
+static void
+im_source_menu_item_finalize (GObject *object)
+{
+ ImSourceMenuItemPrivate *priv = IM_SOURCE_MENU_ITEM (object)->priv;
+
+ g_free (priv->action);
+
+ G_OBJECT_CLASS (im_source_menu_item_parent_class)->finalize (object);
+}
+
+static void
+im_source_menu_item_activate (GtkMenuItem *item)
+{
+ ImSourceMenuItemPrivate *priv = IM_SOURCE_MENU_ITEM (item)->priv;
+
+ if (priv->action && priv->action_group)
+ g_action_group_activate_action (priv->action_group, priv->action, NULL);
+}
+
+static void
+im_source_menu_item_class_init (ImSourceMenuItemClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkMenuItemClass *menu_item_class = GTK_MENU_ITEM_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (ImSourceMenuItemPrivate));
+
+ object_class->constructed = im_source_menu_item_constructed;
+ object_class->set_property = im_source_menu_set_property;
+ object_class->dispose = im_source_menu_item_dispose;
+ object_class->finalize = im_source_menu_item_finalize;
+
+ menu_item_class->activate = im_source_menu_item_activate;
+
+ properties[PROP_MENU_ITEM] = g_param_spec_object ("menu-item",
+ "Menu item",
+ "The model GMenuItem for this menu item",
+ G_TYPE_MENU_ITEM,
+ G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_ACTION_GROUP] = g_param_spec_object ("action-group",
+ "Action group",
+ "The action group associated with this menu item",
+ G_TYPE_ACTION_GROUP,
+ G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
+}
+
+static void
+im_source_menu_item_init (ImSourceMenuItem *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ IM_TYPE_SOURCE_MENU_ITEM,
+ ImSourceMenuItemPrivate);
+}
+
+void
+im_source_menu_item_set_menu_item (ImSourceMenuItem *self,
+ GMenuItem *menuitem)
+{
+ gchar *iconstr = NULL;
+ GIcon *icon = NULL;
+ gchar *label;
+ gchar *action = NULL;
+
+ if (g_menu_item_get_attribute (menuitem, "x-canonical-icon", "s", &iconstr))
+ {
+ GError *error;
+ icon = g_icon_new_for_string (iconstr, &error);
+ if (icon == NULL)
+ {
+ g_warning ("unable to set icon: %s", error->message);
+ g_error_free (error);
+ }
+ g_free (iconstr);
+ }
+ gtk_image_set_from_gicon (GTK_IMAGE (self->priv->icon), icon, GTK_ICON_SIZE_MENU);
+
+ g_menu_item_get_attribute (menuitem, "label", "s", &label);
+ gtk_label_set_label (GTK_LABEL (self->priv->label), label ? label : "");
+
+ g_menu_item_get_attribute (menuitem, "action", "s", &action);
+ im_source_menu_item_set_action_name (self, action);
+
+ if (icon)
+ g_object_unref (icon);
+ g_free (label);
+ g_free (action);
+}
+
+void
+im_source_menu_item_set_action_group (ImSourceMenuItem *self,
+ GActionGroup *action_group)
+{
+ ImSourceMenuItemPrivate *priv = self->priv;
+
+ if (priv->action_group != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (priv->action_group, im_source_menu_item_action_added, self);
+ g_signal_handlers_disconnect_by_func (priv->action_group, im_source_menu_item_action_removed, self);
+ g_signal_handlers_disconnect_by_func (priv->action_group, im_source_menu_item_action_enabled_changed, self);
+ g_signal_handlers_disconnect_by_func (priv->action_group, im_source_menu_item_action_state_changed, self);
+
+ g_clear_object (&priv->action_group);
+ }
+
+ if (action_group != NULL)
+ {
+ priv->action_group = g_object_ref (action_group);
+
+ g_signal_connect (priv->action_group, "action-added",
+ G_CALLBACK (im_source_menu_item_action_added), self);
+ g_signal_connect (priv->action_group, "action-removed",
+ G_CALLBACK (im_source_menu_item_action_removed), self);
+ g_signal_connect (priv->action_group, "action-enabled-changed",
+ G_CALLBACK (im_source_menu_item_action_enabled_changed), self);
+ g_signal_connect (priv->action_group, "action-state-changed",
+ G_CALLBACK (im_source_menu_item_action_state_changed), self);
+ }
+}
diff --git a/src/im-source-menu-item.h b/src/im-source-menu-item.h
new file mode 100644
index 0000000..c359b94
--- /dev/null
+++ b/src/im-source-menu-item.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2012 Canonical Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3, as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranties of
+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Lars Uebernickel <lars.uebernickel@canonical.com>
+ */
+
+#ifndef __IM_SOURCE_MENU_ITEM_H__
+#define __IM_SOURCE_MENU_ITEM_H__
+
+#include <gtk/gtk.h>
+
+#define IM_TYPE_SOURCE_MENU_ITEM (im_source_menu_item_get_type ())
+#define IM_SOURCE_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IM_TYPE_SOURCE_MENU_ITEM, ImSourceMenuItem))
+#define IM_SOURCE_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), IM_TYPE_SOURCE_MENU_ITEM, ImSourceMenuItemClass))
+#define IS_IM_SOURCE_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IM_TYPE_SOURCE_MENU_ITEM))
+#define IS_IM_SOURCE_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), IM_TYPE_SOURCE_MENU_ITEM))
+#define IM_SOURCE_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), IM_TYPE_SOURCE_MENU_ITEM, ImSourceMenuItemClass))
+
+typedef struct _ImSourceMenuItem ImSourceMenuItem;
+typedef struct _ImSourceMenuItemClass ImSourceMenuItemClass;
+typedef struct _ImSourceMenuItemPrivate ImSourceMenuItemPrivate;
+
+struct _ImSourceMenuItemClass
+{
+ GtkMenuItemClass parent_class;
+};
+
+struct _ImSourceMenuItem
+{
+ GtkMenuItem parent;
+ ImSourceMenuItemPrivate *priv;
+};
+
+GType im_source_menu_item_get_type (void);
+
+void im_source_menu_item_set_menu_item (ImSourceMenuItem *item,
+ GMenuItem *menuitem);
+void im_source_menu_item_set_action_group (ImSourceMenuItem *self,
+ GActionGroup *action_group);
+
+#endif
diff --git a/src/indicator-messages-status-provider-0.5.pc.in.in b/src/indicator-messages-status-provider-0.5.pc.in.in
deleted file mode 100644
index 3fe4cf2..0000000
--- a/src/indicator-messages-status-provider-0.5.pc.in.in
+++ /dev/null
@@ -1,15 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-bindir=@bindir@
-includedir=@includedir@
-
-statusproviderdir=@status_provider_dir@
-
-Cflags: -I${includedir}/indicator-messages-status-provider-0.@status_provider_api_version@
-Requires: gobject-2.0
-Libs: -L${libdir} -lindicator-messages-status-provider
-
-Name: indicator-messages-status-provider
-Description: Status providers for the indicator-messages menu.
-Version: @VERSION@
diff --git a/src/indicator-messages.c b/src/indicator-messages.c
index 32f97f7..2824b0c 100644
--- a/src/indicator-messages.c
+++ b/src/indicator-messages.c
@@ -29,16 +29,15 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include <glib/gi18n-lib.h>
#include <gtk/gtk.h>
-#include <libdbusmenu-gtk/menu.h>
-#include <libdbusmenu-gtk/menuitem.h>
-
#include <libindicator/indicator.h>
#include <libindicator/indicator-object.h>
#include <libindicator/indicator-image-helper.h>
#include <libindicator/indicator-service-manager.h>
#include "dbus-data.h"
-#include "gen-messages-service.xml.h"
+
+#include "im-app-menu-item.h"
+#include "im-source-menu-item.h"
#define INDICATOR_MESSAGES_TYPE (indicator_messages_get_type ())
#define INDICATOR_MESSAGES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_MESSAGES_TYPE, IndicatorMessages))
@@ -52,12 +51,16 @@ typedef struct _IndicatorMessagesClass IndicatorMessagesClass;
struct _IndicatorMessagesClass {
IndicatorObjectClass parent_class;
- void (*update_a11y_desc) (IndicatorServiceManager * service, gpointer * user_data);
};
struct _IndicatorMessages {
IndicatorObject parent;
IndicatorServiceManager * service;
+ GActionGroup *actions;
+ GMenu *menu_wrapper;
+ GMenuModel *menu;
+ GtkWidget *image;
+ gchar *accessible_desc;
};
GType indicator_messages_get_type (void);
@@ -66,53 +69,25 @@ GType indicator_messages_get_type (void);
INDICATOR_SET_VERSION
INDICATOR_SET_TYPE(INDICATOR_MESSAGES_TYPE)
-/* Globals */
-static GtkWidget * main_image = NULL;
-static GDBusProxy * icon_proxy = NULL;
-static GDBusNodeInfo * bus_node_info = NULL;
-static GDBusInterfaceInfo * bus_interface_info = NULL;
-static const gchar * accessible_desc = NULL;
-static IndicatorObject * indicator = NULL;
-
/* Prototypes */
static void indicator_messages_class_init (IndicatorMessagesClass *klass);
static void indicator_messages_init (IndicatorMessages *self);
static void indicator_messages_dispose (GObject *object);
static void indicator_messages_finalize (GObject *object);
-static GtkImage * get_icon (IndicatorObject * io);
+static GtkImage * get_image (IndicatorObject * io);
static GtkMenu * get_menu (IndicatorObject * io);
-static void indicator_messages_middle_click (IndicatorObject * io,
- IndicatorObjectEntry * entry,
- guint time, gpointer data);
static const gchar * get_accessible_desc (IndicatorObject * io);
static const gchar * get_name_hint (IndicatorObject * io);
-static void connection_change (IndicatorServiceManager * sm,
- gboolean connected,
- gpointer user_data);
+static void update_root_item (IndicatorMessages * self);
+static void update_menu (IndicatorMessages *self);
+static void menu_items_changed (GMenuModel *menu,
+ gint position,
+ gint removed,
+ gint added,
+ gpointer user_data);
G_DEFINE_TYPE (IndicatorMessages, indicator_messages, INDICATOR_OBJECT_TYPE);
-static void
-update_a11y_desc (void)
-{
- g_return_if_fail(IS_INDICATOR_MESSAGES(indicator));
-
- GList *entries = indicator_object_get_entries(indicator);
- IndicatorObjectEntry * entry = (IndicatorObjectEntry *)entries->data;
-
- entry->accessible_desc = get_accessible_desc(indicator);
-
- g_signal_emit(G_OBJECT(indicator),
- INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE_ID,
- 0,
- entry,
- TRUE);
-
- g_list_free(entries);
-
- return;
-}
-
/* Initialize the one-timers */
static void
indicator_messages_class_init (IndicatorMessagesClass *klass)
@@ -124,47 +99,55 @@ indicator_messages_class_init (IndicatorMessagesClass *klass)
IndicatorObjectClass * io_class = INDICATOR_OBJECT_CLASS(klass);
- io_class->get_image = get_icon;
+ io_class->get_image = get_image;
io_class->get_menu = get_menu;
io_class->get_accessible_desc = get_accessible_desc;
io_class->get_name_hint = get_name_hint;
- io_class->secondary_activate = indicator_messages_middle_click;
-
- if (bus_node_info == NULL) {
- GError * error = NULL;
-
- bus_node_info = g_dbus_node_info_new_for_xml(_messages_service, &error);
- if (error != NULL) {
- g_error("Unable to parse Messaging Menu Interface description: %s", error->message);
- g_error_free(error);
- }
- }
-
- if (bus_interface_info == NULL) {
- bus_interface_info = g_dbus_node_info_lookup_interface(bus_node_info, INDICATOR_MESSAGES_DBUS_SERVICE_INTERFACE);
-
- if (bus_interface_info == NULL) {
- g_error("Unable to find interface '" INDICATOR_MESSAGES_DBUS_SERVICE_INTERFACE "'");
- }
- }
-
- return;
}
/* Build up our per-instance variables */
static void
indicator_messages_init (IndicatorMessages *self)
{
+ GDBusConnection *bus;
+ GError *error = NULL;
+
/* Default values */
self->service = NULL;
/* Complex stuff */
self->service = indicator_service_manager_new_version(INDICATOR_MESSAGES_DBUS_NAME, 1);
- g_signal_connect(self->service, INDICATOR_SERVICE_MANAGER_SIGNAL_CONNECTION_CHANGE, G_CALLBACK(connection_change), self);
- indicator = INDICATOR_OBJECT(self);
+ bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+ if (!bus) {
+ g_warning ("error connecting to the session bus: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ self->actions = G_ACTION_GROUP (g_dbus_action_group_get (bus,
+ INDICATOR_MESSAGES_DBUS_NAME,
+ INDICATOR_MESSAGES_DBUS_OBJECT));
- return;
+ self->menu = G_MENU_MODEL (g_dbus_menu_model_get (bus,
+ INDICATOR_MESSAGES_DBUS_NAME,
+ INDICATOR_MESSAGES_DBUS_OBJECT));
+
+ g_signal_connect (self->menu, "items-changed", G_CALLBACK (menu_items_changed), self);
+
+ self->image = g_object_ref_sink (gtk_image_new ());
+ gtk_widget_show (self->image);
+ update_root_item (self);
+
+ self->menu_wrapper = g_menu_new ();
+ update_menu (self);
+
+ g_object_unref (bus);
+
+ /* make sure custom menu item types are registered (so that
+ * gtk_model_new_from_menu can pick them up */
+ im_app_menu_item_get_type ();
+ im_source_menu_item_get_type ();
}
/* Unref stuff */
@@ -174,10 +157,11 @@ indicator_messages_dispose (GObject *object)
IndicatorMessages * self = INDICATOR_MESSAGES(object);
g_return_if_fail(self != NULL);
- if (self->service != NULL) {
- g_object_unref(self->service);
- self->service = NULL;
- }
+ g_clear_object (&self->service);
+ g_clear_object (&self->menu_wrapper);
+ g_clear_object (&self->actions);
+ g_clear_object (&self->menu);
+ g_clear_object (&self->image);
G_OBJECT_CLASS (indicator_messages_parent_class)->dispose (object);
return;
@@ -187,6 +171,9 @@ indicator_messages_dispose (GObject *object)
static void
indicator_messages_finalize (GObject *object)
{
+ IndicatorMessages *self = INDICATOR_MESSAGES (object);
+
+ g_free (self->accessible_desc);
G_OBJECT_CLASS (indicator_messages_parent_class)->finalize (object);
return;
@@ -196,592 +183,121 @@ indicator_messages_finalize (GObject *object)
/* Functions */
-/* Signal off of the proxy */
-static void
-proxy_signal (GDBusProxy * proxy, const gchar * sender, const gchar * signal, GVariant * params, gpointer user_data)
+static GtkImage *
+get_image (IndicatorObject * io)
{
- gboolean prop = g_variant_get_boolean(g_variant_get_child_value(params, 0));
-
- if (g_strcmp0("AttentionChanged", signal) == 0) {
- if (prop) {
- indicator_image_helper_update(GTK_IMAGE(main_image), "indicator-messages-new");
- accessible_desc = _("New Messages");
- } else {
- indicator_image_helper_update(GTK_IMAGE(main_image), "indicator-messages");
- accessible_desc = _("Messages");
- }
- } else if (g_strcmp0("IconChanged", signal) == 0) {
- if (prop) {
- gtk_widget_hide(main_image);
- } else {
- gtk_widget_show(main_image);
- }
- } else {
- g_warning("Unknown signal %s", signal);
- }
-
- update_a11y_desc();
+ IndicatorMessages *self = INDICATOR_MESSAGES (io);
- return;
+ return GTK_IMAGE (self->image);
}
-/* Callback from getting the attention status from the service. */
-static void
-attention_cb (GObject * object, GAsyncResult * ares, gpointer user_data)
+static GtkMenu *
+get_menu (IndicatorObject * io)
{
- GError * error = NULL;
- GVariant * res = g_dbus_proxy_call_finish(G_DBUS_PROXY(object), ares, &error);
-
- if (error != NULL) {
- g_warning("Unable to get attention status: %s", error->message);
- g_error_free(error);
- return;
- }
-
- gboolean prop = g_variant_get_boolean(g_variant_get_child_value(res, 0));
+ IndicatorMessages *self = INDICATOR_MESSAGES (io);
+ GtkWidget *menu;
- if (prop) {
- indicator_image_helper_update(GTK_IMAGE(main_image), "indicator-messages-new");
- accessible_desc = _("New Messages");
- } else {
- indicator_image_helper_update(GTK_IMAGE(main_image), "indicator-messages");
- accessible_desc = _("Messages");
- }
+ menu = gtk_menu_new_from_model (G_MENU_MODEL (self->menu_wrapper));
+ gtk_widget_insert_action_group (menu, get_name_hint (io), self->actions);
- update_a11y_desc();
-
- return;
+ return GTK_MENU (menu);
}
-/* Change from getting the icon visibility from the service */
-static void
-icon_cb (GObject * object, GAsyncResult * ares, gpointer user_data)
+static const gchar *
+get_accessible_desc (IndicatorObject * io)
{
- GError * error = NULL;
- GVariant * res = g_dbus_proxy_call_finish(G_DBUS_PROXY(object), ares, &error);
-
- if (error != NULL) {
- g_warning("Unable to get icon visibility: %s", error->message);
- g_error_free(error);
- return;
- }
-
- gboolean prop = g_variant_get_boolean(g_variant_get_child_value(res, 0));
-
- if (prop) {
- gtk_widget_hide(main_image);
- } else {
- gtk_widget_show(main_image);
- }
-
- return;
+ IndicatorMessages *self = INDICATOR_MESSAGES (io);
+ return self->accessible_desc;
}
-static guint connection_drop_timeout = 0;
-
-/* Resets the icon to not having messages if we can't get a good
- answer on it from the service. */
-static gboolean
-connection_drop_cb (gpointer user_data)
+static const gchar *
+get_name_hint (IndicatorObject *io)
{
- if (main_image != NULL) {
- indicator_image_helper_update(GTK_IMAGE(main_image), "indicator-messages");
- }
- connection_drop_timeout = 0;
- return FALSE;
+ return PACKAGE;
}
-/* Proxy is setup now.. whoo! */
static void
-proxy_ready_cb (GObject * obj, GAsyncResult * res, gpointer user_data)
+indicator_messages_accessible_desc_updated (IndicatorMessages *self)
{
- GError * error = NULL;
- GDBusProxy * proxy = g_dbus_proxy_new_for_bus_finish(res, &error);
+ GList *entries;
- if (error != NULL) {
- g_warning("Unable to get proxy of service: %s", error->message);
- g_error_free(error);
- return;
- }
+ entries = indicator_object_get_entries (INDICATOR_OBJECT (self));
+ g_return_if_fail (entries != NULL);
- icon_proxy = proxy;
-
- g_signal_connect(G_OBJECT(proxy), "g-signal", G_CALLBACK(proxy_signal), user_data);
-
- g_dbus_proxy_call(icon_proxy,
- "AttentionRequested",
- NULL, /* params */
- G_DBUS_CALL_FLAGS_NONE,
- -1, /* timeout */
- NULL, /* cancel */
- attention_cb,
- user_data);
- g_dbus_proxy_call(icon_proxy,
- "IconShown",
- NULL, /* params */
- G_DBUS_CALL_FLAGS_NONE,
- -1, /* timeout */
- NULL, /* cancel */
- icon_cb,
- user_data);
+ g_signal_emit_by_name (self, INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE, entries->data);
- return;
+ g_list_free (entries);
}
-/* Sets up all the icon information in the proxy. */
-static void
-connection_change (IndicatorServiceManager * sm, gboolean connected, gpointer user_data)
+static void
+update_root_item (IndicatorMessages * self)
{
- if (connection_drop_timeout != 0) {
- g_source_remove(connection_drop_timeout);
- connection_drop_timeout = 0;
- }
+ gchar *iconstr;
- if (!connected) {
- /* Ensure that we're not saying there are messages
- when we don't have a connection. */
- connection_drop_timeout = g_timeout_add(400, connection_drop_cb, NULL);
+ if (g_menu_model_get_n_items (self->menu) == 0)
return;
- }
-
- if (icon_proxy == NULL) {
- g_dbus_proxy_new_for_bus(G_BUS_TYPE_SESSION,
- G_DBUS_PROXY_FLAGS_NONE,
- bus_interface_info,
- INDICATOR_MESSAGES_DBUS_NAME,
- INDICATOR_MESSAGES_DBUS_SERVICE_OBJECT,
- INDICATOR_MESSAGES_DBUS_SERVICE_INTERFACE,
- NULL, /* cancel */
- proxy_ready_cb,
- sm);
- } else {
- g_dbus_proxy_call(icon_proxy,
- "AttentionRequested",
- NULL, /* params */
- G_DBUS_CALL_FLAGS_NONE,
- -1, /* timeout */
- NULL, /* cancel */
- attention_cb,
- sm);
- g_dbus_proxy_call(icon_proxy,
- "IconShown",
- NULL, /* params */
- G_DBUS_CALL_FLAGS_NONE,
- -1, /* timeout */
- NULL, /* cancel */
- icon_cb,
- sm);
- }
- return;
-}
+ if (g_menu_model_get_item_attribute (self->menu, 0, "x-canonical-icon", "s", &iconstr)) {
+ GIcon *icon;
+ GError *error;
-/* Sets the icon when it changes. */
-static void
-application_icon_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant * value, gpointer user_data)
-{
- if (!g_strcmp0(prop, APPLICATION_MENUITEM_PROP_ICON)) {
- /* Set the main icon */
- if (GTK_IS_IMAGE(user_data)) {
- gtk_image_set_from_icon_name(GTK_IMAGE(user_data), g_variant_get_string(value, NULL), GTK_ICON_SIZE_MENU);
+ icon = g_icon_new_for_string (iconstr, &error);
+ if (icon) {
+ gtk_image_set_from_gicon (GTK_IMAGE (self->image), icon, GTK_ICON_SIZE_MENU);
+ g_object_unref (icon);
}
- }
-
- return;
-}
-
-/* Sets the label when it changes. */
-static void
-application_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant * value, gpointer user_data)
-{
- if (!g_strcmp0(prop, APPLICATION_MENUITEM_PROP_NAME)) {
- /* Set the main label */
- if (GTK_IS_LABEL(user_data)) {
- gtk_label_set_text(GTK_LABEL(user_data), g_variant_get_string(value, NULL));
+ else {
+ g_warning ("unable to load icon: %s", error->message);
+ g_error_free (error);
}
- }
- if (!g_strcmp0(prop, APPLICATION_MENUITEM_PROP_RUNNING)) {
- /* TODO: should hide/show the triangle live if the menu was open.
- In practice, this is rarely needed. */
+ g_free (iconstr);
}
- return;
-}
-
-/* Draws a triangle on the left, using fg[STATE_TYPE] color. */
-static gboolean
-application_triangle_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer data)
-{
- GtkAllocation allocation;
- GtkStyle *style;
- int x, y, arrow_width, arrow_height;
-
- if (!GTK_IS_WIDGET (widget)) return FALSE;
- if (!DBUSMENU_IS_MENUITEM (data)) return FALSE;
-
- /* render the triangle indicator only if the application is running */
- if (! dbusmenu_menuitem_property_get_bool (DBUSMENU_MENUITEM(data), APPLICATION_MENUITEM_PROP_RUNNING))
- return FALSE;;
-
- /* get style */
- style = gtk_widget_get_style (widget);
-
- /* set arrow position / dimensions */
- arrow_width = 5; /* the pixel-based reference triangle is 5x9 */
- arrow_height = 9;
- gtk_widget_get_allocation (widget, &allocation);
- x = 0;
- y = allocation.height/2.0 - (double)arrow_height/2.0;
-
- cairo_save (cr);
-
- /* set line width */
- cairo_set_line_width (cr, 1.0);
-
- /* cairo drawing code */
- cairo_move_to (cr, x, y);
- cairo_line_to (cr, x, y + arrow_height);
- cairo_line_to (cr, x + arrow_width, y + (double)arrow_height/2.0);
- cairo_close_path (cr);
- cairo_set_source_rgb (cr, style->fg[gtk_widget_get_state(widget)].red/65535.0,
- style->fg[gtk_widget_get_state(widget)].green/65535.0,
- style->fg[gtk_widget_get_state(widget)].blue/65535.0);
- cairo_fill (cr);
-
- cairo_restore (cr);
+ g_free (self->accessible_desc);
+ self->accessible_desc = NULL;
- return FALSE;
+ g_menu_model_get_item_attribute (self->menu, 0, "x-canonical-accessible-description",
+ "s", &self->accessible_desc);
+ indicator_messages_accessible_desc_updated (self);
}
-static gint
-gtk_widget_get_font_size (GtkWidget *widget)
-{
- const PangoFontDescription *font;
-
- font = gtk_style_context_get_font (gtk_widget_get_style_context (widget),
- gtk_widget_get_state_flags (widget));
-
- return pango_font_description_get_size (font) / PANGO_SCALE;
-}
-
-/* Custom function to draw rounded rectangle with max radius */
static void
-custom_cairo_rounded_rectangle (cairo_t *cr,
- double x, double y, double w, double h)
-{
- double radius = h / 2.0;
-
- cairo_move_to (cr, x+radius, y);
- cairo_arc (cr, x+w-radius, y+radius, radius, M_PI*1.5, M_PI*2);
- cairo_arc (cr, x+w-radius, y+h-radius, radius, 0, M_PI*0.5);
- cairo_arc (cr, x+radius, y+h-radius, radius, M_PI*0.5, M_PI);
- cairo_arc (cr, x+radius, y+radius, radius, M_PI, M_PI*1.5);
-}
-
-/* Draws a rounded rectangle with text inside. */
-static gboolean
-numbers_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer data)
-{
- GtkAllocation allocation;
- GtkStyle *style;
- double x, y, w, h;
- PangoLayout * layout;
- PangoRectangle layout_extents;
- gint font_size = gtk_widget_get_font_size (widget);
- gboolean is_lozenge = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "is-lozenge"));
-
- if (!GTK_IS_WIDGET (widget)) return FALSE;
-
- /* let the label handle the drawing if it's not a lozenge */
- if (!is_lozenge)
- return FALSE;
-
- /* get style */
- style = gtk_widget_get_style (widget);
-
- /* set arrow position / dimensions */
- gtk_widget_get_allocation (widget, &allocation);
- x = 0;
- y = 0;
- w = allocation.width;
- h = allocation.height;
-
- layout = gtk_label_get_layout (GTK_LABEL(widget));
- pango_layout_get_extents (layout, NULL, &layout_extents);
- pango_extents_to_pixels (&layout_extents, NULL);
-
- if (layout_extents.width == 0)
- return TRUE;
-
- cairo_save (cr);
-
- /* set line width */
- cairo_set_line_width (cr, 1.0);
-
- cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
-
- /* cairo drawing code */
- custom_cairo_rounded_rectangle (cr, x - font_size/2.0, y, w + font_size, h);
-
- cairo_set_source_rgba (cr, style->fg[gtk_widget_get_state(widget)].red/65535.0,
- style->fg[gtk_widget_get_state(widget)].green/65535.0,
- style->fg[gtk_widget_get_state(widget)].blue/65535.0, 0.5);
-
- x += (allocation.width - layout_extents.width) / 2.0;
- y += (allocation.height - layout_extents.height) / 2.0;
- cairo_move_to (cr, floor (x), floor (y));
- pango_cairo_layout_path (cr, layout);
- cairo_fill (cr);
-
- cairo_restore (cr);
-
- return TRUE;
-}
-
-/* Builds a menu item representing a running application in the
- messaging menu */
-static gboolean
-new_application_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data)
+update_menu (IndicatorMessages *self)
{
- g_debug ("%s (\"%s\")", __func__, dbusmenu_menuitem_property_get(newitem, APPLICATION_MENUITEM_PROP_NAME));
-
- GtkMenuItem * gmi = GTK_MENU_ITEM(gtk_menu_item_new());
-
- gint padding = 4;
- gtk_widget_style_get(GTK_WIDGET(gmi), "toggle-spacing", &padding, NULL);
+ GMenuModel *popup;
+ GMenuItem *item;
- GtkWidget * hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, padding);
-
- GtkWidget * icon = gtk_image_new_from_icon_name(dbusmenu_menuitem_property_get(newitem, APPLICATION_MENUITEM_PROP_ICON), GTK_ICON_SIZE_MENU);
- gtk_misc_set_alignment(GTK_MISC(icon), 1.0 /* right aligned */, 0.5);
- gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
-
- /* Application name in a label */
- GtkWidget * label = gtk_label_new(dbusmenu_menuitem_property_get(newitem, APPLICATION_MENUITEM_PROP_NAME));
- gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
- gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+ if (g_menu_model_get_n_items (self->menu) == 0)
+ return;
- gtk_container_add(GTK_CONTAINER(gmi), hbox);
- gtk_widget_show_all (GTK_WIDGET (gmi));
+ popup = g_menu_model_get_item_link (self->menu, 0, G_MENU_LINK_SUBMENU);
+ if (popup == NULL)
+ return;
- /* Attach some of the standard GTK stuff */
- dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, gmi, parent);
+ if (g_menu_model_get_n_items (G_MENU_MODEL (self->menu_wrapper)) == 1)
+ g_menu_remove (self->menu_wrapper, 0);
- /* Make sure we can handle the label changing */
- g_signal_connect(G_OBJECT(newitem), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(application_prop_change_cb), label);
- g_signal_connect(G_OBJECT(newitem), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(application_icon_change_cb), icon);
- g_signal_connect_after(G_OBJECT (gmi), "draw", G_CALLBACK (application_triangle_draw_cb), newitem);
+ item = g_menu_item_new_section (NULL, popup);
+ g_menu_item_set_attribute (item, "action-namespace",
+ "s", get_name_hint (INDICATOR_OBJECT (self)));
+ g_menu_append_item (self->menu_wrapper, item);
- return TRUE;
+ g_object_unref (item);
+ g_object_unref (popup);
}
-typedef struct _indicator_item_t indicator_item_t;
-struct _indicator_item_t {
- GtkWidget * icon;
- GtkWidget * label;
- GtkWidget * right;
-};
-
-/* Whenever we have a property change on a DbusmenuMenuitem
- we need to be responsive to that. */
static void
-indicator_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant * value, indicator_item_t * mi_data)
-{
- if (!g_strcmp0(prop, INDICATOR_MENUITEM_PROP_LABEL)) {
- /* Set the main label */
- gtk_label_set_text(GTK_LABEL(mi_data->label), g_variant_get_string(value, NULL));
- } else if (!g_strcmp0(prop, INDICATOR_MENUITEM_PROP_RIGHT)) {
- /* Set the right label */
- gtk_label_set_text(GTK_LABEL(mi_data->right), g_variant_get_string(value, NULL));
- } else if (!g_strcmp0(prop, INDICATOR_MENUITEM_PROP_RIGHT_IS_LOZENGE)) {
- g_object_set_data (G_OBJECT (mi_data->right), "is-lozenge", GINT_TO_POINTER (TRUE));
- gtk_widget_queue_draw (mi_data->right);
- } else if (!g_strcmp0(prop, INDICATOR_MENUITEM_PROP_ICON)) {
- /* We don't use the value here, which is probably less efficient,
- but it's easier to use the easy function. And since th value
- is already cached, shouldn't be a big deal really. */
- GdkPixbuf * pixbuf = dbusmenu_menuitem_property_get_image(mi, INDICATOR_MENUITEM_PROP_ICON);
- if (pixbuf != NULL) {
- /* If we've got a pixbuf we need to make sure it's of a reasonable
- size to fit in the menu. If not, rescale it. */
- GdkPixbuf * resized_pixbuf;
- gint width, height;
- gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
- if (gdk_pixbuf_get_width(pixbuf) > width ||
- gdk_pixbuf_get_height(pixbuf) > height) {
- g_debug("Resizing icon from %dx%d to %dx%d", gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf), width, height);
- resized_pixbuf = gdk_pixbuf_scale_simple(pixbuf,
- width,
- height,
- GDK_INTERP_BILINEAR);
- g_object_unref(pixbuf);
- } else {
- g_debug("Happy with icon sized %dx%d", gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf));
- resized_pixbuf = pixbuf;
- }
-
- gtk_image_set_from_pixbuf(GTK_IMAGE(mi_data->icon), resized_pixbuf);
-
- g_object_unref(resized_pixbuf);
-
- gtk_widget_show(mi_data->icon);
- } else {
- gtk_widget_hide(mi_data->icon);
- }
- }
-
- return;
-}
-
-/* We have a small little menuitem type that handles all
- of the fun stuff for indicators. Mostly this is the
- shifting over and putting the icon in with some right
- side text that'll be determined by the service. */
-static gboolean
-new_indicator_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data)
-{
- g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE);
- g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE);
- /* Note: not checking parent, it's reasonable for it to be NULL */
-
- indicator_item_t * mi_data = g_new0(indicator_item_t, 1);
-
- GtkMenuItem * gmi = GTK_MENU_ITEM(gtk_menu_item_new());
-
- gint padding = 4;
- gint font_size = gtk_widget_get_font_size (GTK_WIDGET (gmi));
- gtk_widget_style_get(GTK_WIDGET(gmi), "toggle-spacing", &padding, NULL);
-
- GtkWidget * hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, padding);
-
- /* Icon, probably someone's face or avatar on an IM */
- mi_data->icon = gtk_image_new();
-
- /* Set the minimum size, we always want it to take space */
- gint width, height;
- gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
- gtk_widget_set_size_request(GTK_WIDGET (gmi), -1, height + 4);
-
- gtk_widget_set_margin_left (hbox, width + padding);
-
- GdkPixbuf * pixbuf = dbusmenu_menuitem_property_get_image(newitem, INDICATOR_MENUITEM_PROP_ICON);
- if (pixbuf != NULL) {
- /* If we've got a pixbuf we need to make sure it's of a reasonable
- size to fit in the menu. If not, rescale it. */
- GdkPixbuf * resized_pixbuf;
- if (gdk_pixbuf_get_width(pixbuf) > width ||
- gdk_pixbuf_get_height(pixbuf) > height) {
- g_debug("Resizing icon from %dx%d to %dx%d", gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf), width, height);
- resized_pixbuf = gdk_pixbuf_scale_simple(pixbuf,
- width,
- height,
- GDK_INTERP_BILINEAR);
- g_object_unref(pixbuf);
- } else {
- g_debug("Happy with icon sized %dx%d", gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf));
- resized_pixbuf = pixbuf;
- }
-
- gtk_image_set_from_pixbuf(GTK_IMAGE(mi_data->icon), resized_pixbuf);
-
- g_object_unref(resized_pixbuf);
- }
- gtk_misc_set_alignment(GTK_MISC(mi_data->icon), 0.0, 0.5);
- gtk_box_pack_start(GTK_BOX(hbox), mi_data->icon, FALSE, FALSE, 0);
-
- if (pixbuf != NULL) {
- gtk_widget_show(mi_data->icon);
- }
-
- /* Label, probably a username, chat room or mailbox name */
- mi_data->label = gtk_label_new(dbusmenu_menuitem_property_get(newitem, INDICATOR_MENUITEM_PROP_LABEL));
- gtk_misc_set_alignment(GTK_MISC(mi_data->label), 0.0, 0.5);
- gtk_box_pack_start(GTK_BOX(hbox), mi_data->label, TRUE, TRUE, 0);
- gtk_widget_show(mi_data->label);
-
- /* Usually either the time or the count on the individual
- item. */
- mi_data->right = gtk_label_new(dbusmenu_menuitem_property_get(newitem, INDICATOR_MENUITEM_PROP_RIGHT));
- g_object_set_data (G_OBJECT (mi_data->right),
- "is-lozenge",
- GINT_TO_POINTER (dbusmenu_menuitem_property_get_bool (newitem, INDICATOR_MENUITEM_PROP_RIGHT_IS_LOZENGE)));
- /* install extra decoration overlay */
- g_signal_connect (G_OBJECT (mi_data->right), "draw",
- G_CALLBACK (numbers_draw_cb), NULL);
-
- gtk_misc_set_alignment(GTK_MISC(mi_data->right), 1.0, 0.5);
- gtk_box_pack_start(GTK_BOX(hbox), mi_data->right, FALSE, FALSE, padding + font_size/2.0);
- gtk_label_set_width_chars (GTK_LABEL (mi_data->right), 2);
- gtk_style_context_add_class (gtk_widget_get_style_context (mi_data->right),
- "accelerator");
- gtk_widget_show(mi_data->right);
-
- gtk_container_add(GTK_CONTAINER(gmi), hbox);
- gtk_widget_show(hbox);
-
- dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, gmi, parent);
-
- g_signal_connect(G_OBJECT(newitem), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(indicator_prop_change_cb), mi_data);
- g_object_weak_ref(G_OBJECT(newitem), (GWeakNotify)g_free, mi_data);
-
- return TRUE;
-}
-
-/* Builds the main image icon using the libindicator helper. */
-static GtkImage *
-get_icon (IndicatorObject * io)
-{
- main_image = GTK_WIDGET(indicator_image_helper("indicator-messages"));
- gtk_widget_show(main_image);
-
- return GTK_IMAGE(main_image);
-}
-
-/* Builds the menu for the indicator */
-static GtkMenu *
-get_menu (IndicatorObject * io)
-{
- DbusmenuGtkMenu * menu = dbusmenu_gtkmenu_new(INDICATOR_MESSAGES_DBUS_NAME, INDICATOR_MESSAGES_DBUS_OBJECT);
- DbusmenuGtkClient * client = dbusmenu_gtkmenu_get_client(menu);
-
- dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(client), INDICATOR_MENUITEM_TYPE, new_indicator_item);
- dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(client), APPLICATION_MENUITEM_TYPE, new_application_item);
-
- return GTK_MENU(menu);
-}
-
-/* Returns the accessible description of the indicator */
-static const gchar *
-get_accessible_desc (IndicatorObject * io)
-{
- return accessible_desc;
-}
-
-/* Returns the name hint of the indicator */
-static const gchar *
-get_name_hint (IndicatorObject *io)
+menu_items_changed (GMenuModel *menu,
+ gint position,
+ gint removed,
+ gint added,
+ gpointer user_data)
{
- return PACKAGE;
-}
+ IndicatorMessages *self = user_data;
-/* Hide the notifications on middle-click over the indicator-messages */
-static void
-indicator_messages_middle_click (IndicatorObject * io, IndicatorObjectEntry * entry,
- guint time, gpointer data)
-{
- if (icon_proxy == NULL) {
- return;
+ if (position == 0) {
+ update_root_item (self);
+ update_menu (self);
}
-
- g_dbus_proxy_call(icon_proxy,
- "ClearAttention",
- NULL, /* params */
- G_DBUS_CALL_FLAGS_NONE,
- -1, /* timeout */
- NULL, /* cancel */
- NULL,
- NULL);
-
- return;
}
diff --git a/src/launcher-menu-item.c b/src/launcher-menu-item.c
deleted file mode 100644
index 91cbbb8..0000000
--- a/src/launcher-menu-item.c
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
-An indicator to show information that is in messaging applications
-that the user is using.
-
-Copyright 2009 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@canonical.com>
-
-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/>.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <gdk/gdk.h>
-#include <glib/gi18n.h>
-#include <gio/gdesktopappinfo.h>
-#include <libindicator/indicator-desktop-shortcuts.h>
-#include "launcher-menu-item.h"
-#include "dbus-data.h"
-#include "default-applications.h"
-#include "seen-db.h"
-
-enum {
- NAME_CHANGED,
- LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-typedef struct _LauncherMenuItemPrivate LauncherMenuItemPrivate;
-struct _LauncherMenuItemPrivate
-{
- GAppInfo * appinfo;
- GKeyFile * keyfile;
- gchar * desktop;
- IndicatorDesktopShortcuts * ids;
- GList * shortcuts;
-};
-
-#define LAUNCHER_MENU_ITEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), LAUNCHER_MENU_ITEM_TYPE, LauncherMenuItemPrivate))
-
-#define NICK_DATA "ids-nick-data"
-
-/* Prototypes */
-static void launcher_menu_item_class_init (LauncherMenuItemClass *klass);
-static void launcher_menu_item_init (LauncherMenuItem *self);
-static void launcher_menu_item_dispose (GObject *object);
-static void launcher_menu_item_finalize (GObject *object);
-static void activate_cb (LauncherMenuItem * self, guint timestamp, gpointer data);
-static void nick_activate_cb (LauncherMenuItem * self, guint timestamp, gpointer data);
-
-
-G_DEFINE_TYPE (LauncherMenuItem, launcher_menu_item, DBUSMENU_TYPE_MENUITEM);
-
-static void
-launcher_menu_item_class_init (LauncherMenuItemClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- g_type_class_add_private (klass, sizeof (LauncherMenuItemPrivate));
-
- object_class->dispose = launcher_menu_item_dispose;
- object_class->finalize = launcher_menu_item_finalize;
-
- signals[NAME_CHANGED] = g_signal_new(LAUNCHER_MENU_ITEM_SIGNAL_NAME_CHANGED,
- G_TYPE_FROM_CLASS(klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (LauncherMenuItemClass, name_changed),
- NULL, NULL,
- g_cclosure_marshal_VOID__STRING,
- G_TYPE_NONE, 1, G_TYPE_STRING);
-
- return;
-}
-
-static void
-launcher_menu_item_init (LauncherMenuItem *self)
-{
- g_debug("Building new Launcher Menu Item");
- LauncherMenuItemPrivate * priv = LAUNCHER_MENU_ITEM_GET_PRIVATE(self);
-
- priv->appinfo = NULL;
- priv->desktop = NULL;
- priv->keyfile = NULL;
-
- priv->ids = NULL;
- priv->shortcuts = NULL;
-
- dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_TYPE, APPLICATION_MENUITEM_TYPE);
-
- return;
-}
-
-static void
-func_unref (gpointer data, gpointer user_data)
-{
- g_object_unref(G_OBJECT(data));
- return;
-}
-
-static void
-launcher_menu_item_dispose (GObject *object)
-{
- LauncherMenuItem * self = LAUNCHER_MENU_ITEM(object);
- LauncherMenuItemPrivate * priv = LAUNCHER_MENU_ITEM_GET_PRIVATE(self);
-
- if (priv->appinfo != NULL) {
- g_object_unref(priv->appinfo);
- priv->appinfo = NULL;
- }
-
- if (priv->keyfile != NULL) {
- g_object_unref(priv->keyfile);
- priv->keyfile = NULL;
- }
-
- if (priv->ids != NULL) {
- g_object_unref(priv->ids);
- priv->ids = NULL;
- }
-
- if (priv->shortcuts != NULL) {
- g_list_foreach(priv->shortcuts, func_unref, NULL);
- g_list_free(priv->shortcuts);
- priv->shortcuts = NULL;
- }
-
- G_OBJECT_CLASS (launcher_menu_item_parent_class)->dispose (object);
-}
-
-static void
-launcher_menu_item_finalize (GObject *object)
-{
- LauncherMenuItem * self = LAUNCHER_MENU_ITEM(object);
- LauncherMenuItemPrivate * priv = LAUNCHER_MENU_ITEM_GET_PRIVATE(self);
-
- if (priv->desktop != NULL) {
- g_free(priv->desktop);
- priv->desktop = NULL;
- }
-
- G_OBJECT_CLASS (launcher_menu_item_parent_class)->finalize (object);
-
- return;
-}
-
-LauncherMenuItem *
-launcher_menu_item_new (const gchar * desktop_file)
-{
- LauncherMenuItem * self = g_object_new(LAUNCHER_MENU_ITEM_TYPE, NULL);
- g_debug("\tDesktop file: %s", desktop_file);
-
- LauncherMenuItemPrivate * priv = LAUNCHER_MENU_ITEM_GET_PRIVATE(self);
-
- /* Parse the desktop file we've been given. */
- priv->appinfo = G_APP_INFO(g_desktop_app_info_new_from_filename(desktop_file));
- priv->keyfile = g_key_file_new();
- g_key_file_load_from_file(priv->keyfile, desktop_file, G_KEY_FILE_NONE, NULL);
- priv->desktop = g_strdup(desktop_file);
-
- /* Set the appropriate values on this menu item based on the
- app info that we've parsed */
- g_debug("\tName: %s", launcher_menu_item_get_name(self));
-
- const gchar * default_name = NULL;
-
- if (seen_db_seen(desktop_file)) {
- default_name = get_default_name(desktop_file);
- } else {
- default_name = get_default_setup(desktop_file);
- }
-
- if (default_name == NULL) {
- dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_LABEL, launcher_menu_item_get_name(self));
- } else {
- dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_LABEL, _(default_name));
- }
-
- gchar * iconstr;
- if (default_name == NULL) {
- iconstr = launcher_menu_item_get_icon(self);
- } else {
- iconstr = g_strdup(get_default_icon(desktop_file));
- }
- dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_ICON_NAME, iconstr);
- g_free(iconstr);
- dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
-
- g_signal_connect(G_OBJECT(self), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(activate_cb), NULL);
-
- /* Start to build static shortcuts */
- if (seen_db_seen(desktop_file)) {
- priv->ids = indicator_desktop_shortcuts_new(priv->desktop, "Messaging Menu");
- const gchar ** nicks = indicator_desktop_shortcuts_get_nicks(priv->ids);
- gint i;
- for (i = 0; nicks[i] != NULL; i++) {
- DbusmenuMenuitem * mi = dbusmenu_menuitem_new();
- dbusmenu_menuitem_property_set(mi, DBUSMENU_MENUITEM_PROP_TYPE, APPLICATION_MENUITEM_TYPE);
- g_object_set_data(G_OBJECT(mi), NICK_DATA, (gpointer)nicks[i]);
-
- gchar *name = indicator_desktop_shortcuts_nick_get_name(priv->ids, nicks[i]);
- dbusmenu_menuitem_property_set(mi, DBUSMENU_MENUITEM_PROP_LABEL, name);
- g_free(name);
-
- g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(nick_activate_cb), self);
-
- priv->shortcuts = g_list_append(priv->shortcuts, mi);
- }
- }
-
- /* Check to see if we should be eclipsed */
- if (priv->appinfo == NULL) {
- launcher_menu_item_set_eclipsed(self, TRUE);
- }
-
- return self;
-}
-
-const gchar *
-launcher_menu_item_get_name (LauncherMenuItem * appitem)
-{
- LauncherMenuItemPrivate * priv = LAUNCHER_MENU_ITEM_GET_PRIVATE(appitem);
-
- if (priv->appinfo == NULL) {
- return NULL;
- } else {
- return g_app_info_get_name(priv->appinfo);
- }
-}
-
-/* Respond to one of the shortcuts getting clicked on. */
-static void
-nick_activate_cb (LauncherMenuItem * self, guint timestamp, gpointer data)
-{
- gchar * nick = (gchar *)g_object_get_data(G_OBJECT(self), NICK_DATA);
- LauncherMenuItem * lmi = LAUNCHER_MENU_ITEM(data);
-
- g_return_if_fail(nick != NULL);
- g_return_if_fail(lmi != NULL);
-
- LauncherMenuItemPrivate * priv = LAUNCHER_MENU_ITEM_GET_PRIVATE(lmi);
-
- g_return_if_fail(priv->ids != NULL);
-
- if (!indicator_desktop_shortcuts_nick_exec(priv->ids, nick)) {
- g_warning("Unable to execute nick '%s' for desktop file '%s'", nick, priv->desktop);
- }
-
- return;
-}
-
-/* Figure out the appropriate icon for this launcher */
-gchar *
-launcher_menu_item_get_icon (LauncherMenuItem * appitem)
-{
- LauncherMenuItemPrivate * priv = LAUNCHER_MENU_ITEM_GET_PRIVATE(appitem);
- gchar * retval = NULL;
-
- /* Check to see if there is a specific icon for the messaging
- menu first. */
- if (g_key_file_has_key(priv->keyfile, G_KEY_FILE_DESKTOP_GROUP, ICON_KEY, NULL) && retval == NULL) {
- GError * error = NULL;
-
- retval = g_key_file_get_string(priv->keyfile, G_KEY_FILE_DESKTOP_GROUP, ICON_KEY, &error);
-
- if (error != NULL) {
- /* Can't figure out why this would happen, but sure, let's print something */
- g_warning("Error getting '" ICON_KEY "' from desktop file: %s", error->message);
- g_error_free(error);
- }
- }
-
- /* If there's not, or there is an error, we'll use the one
- from the application info */
- if (priv->appinfo != NULL && retval == NULL) {
- GIcon * icon = g_app_info_get_icon(priv->appinfo);
- retval = g_icon_to_string(icon);
- }
-
- return retval;
-}
-
-/* When the menu item is clicked on it tries to launch
- the application that is represented by the desktop file */
-static void
-activate_cb (LauncherMenuItem * self, guint timestamp, gpointer data)
-{
- LauncherMenuItemPrivate * priv = LAUNCHER_MENU_ITEM_GET_PRIVATE(self);
- g_return_if_fail(priv->appinfo != NULL);
-
- GError * error = NULL;
- if (!g_app_info_launch(priv->appinfo, NULL, NULL, &error)) {
- g_warning("Application failed to launch '%s' because: %s", launcher_menu_item_get_name(self), error->message);
- g_error_free(error);
- }
-
- return;
-}
-
-const gchar *
-launcher_menu_item_get_desktop (LauncherMenuItem * launchitem)
-{
- g_return_val_if_fail(IS_LAUNCHER_MENU_ITEM(launchitem), NULL);
- LauncherMenuItemPrivate * priv = LAUNCHER_MENU_ITEM_GET_PRIVATE(launchitem);
- return priv->desktop;
-}
-
-/* Gets the decription for the item that should
- go in the messaging menu */
-const gchar *
-launcher_menu_item_get_description (LauncherMenuItem * li)
-{
- g_return_val_if_fail(IS_LAUNCHER_MENU_ITEM(li), NULL);
- LauncherMenuItemPrivate * priv = LAUNCHER_MENU_ITEM_GET_PRIVATE(li);
- return g_app_info_get_description(priv->appinfo);
-}
-
-/* Apply the eclipse value to all the shortcuts */
-static void
-eclipse_shortcuts_cb (gpointer data, gpointer user_data)
-{
- DbusmenuMenuitem * mi = DBUSMENU_MENUITEM(data);
- g_return_if_fail(mi != NULL);
-
- gboolean eclipsed = GPOINTER_TO_UINT(user_data);
-
- dbusmenu_menuitem_property_set_bool(mi, DBUSMENU_MENUITEM_PROP_VISIBLE, !eclipsed);
- return;
-}
-
-/* Hides the menu item based on whether it is eclipsed
- or not. */
-void
-launcher_menu_item_set_eclipsed (LauncherMenuItem * li, gboolean eclipsed)
-{
- g_return_if_fail(IS_LAUNCHER_MENU_ITEM(li));
- LauncherMenuItemPrivate * priv = LAUNCHER_MENU_ITEM_GET_PRIVATE(li);
-
- g_debug("Laucher '%s' is %s", launcher_menu_item_get_name(li), eclipsed ? "now eclipsed" : "shown again");
- dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(li), DBUSMENU_MENUITEM_PROP_VISIBLE, !eclipsed);
-
- g_list_foreach(priv->shortcuts, eclipse_shortcuts_cb, GINT_TO_POINTER(eclipsed));
-
- /* If we're being reshown let's re-evaluate how we should be
- showing the label */
- if (!eclipsed) {
- const gchar * default_name = NULL;
-
- if (seen_db_seen(priv->desktop)) {
- default_name = get_default_name(priv->desktop);
- } else {
- default_name = get_default_setup(priv->desktop);
- }
-
- if (default_name == NULL) {
- dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(li), DBUSMENU_MENUITEM_PROP_LABEL, launcher_menu_item_get_name(li));
- } else {
- dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(li), DBUSMENU_MENUITEM_PROP_LABEL, _(default_name));
- }
- }
-
- return;
-}
-
-/* Check to see if this item is eclipsed */
-gboolean
-launcher_menu_item_get_eclipsed (LauncherMenuItem * li)
-{
- gboolean show = dbusmenu_menuitem_property_get_bool(DBUSMENU_MENUITEM(li), DBUSMENU_MENUITEM_PROP_VISIBLE);
- g_debug("Launcher check eclipse: %s", show ? "false" : "true");
- return !show;
-}
-
-/* Gets the shortcuts that are associated with this
- launcher. They're a list of DbusmenuMenuitems */
-GList *
-launcher_menu_item_get_items (LauncherMenuItem * li)
-{
- g_return_val_if_fail(IS_LAUNCHER_MENU_ITEM(li), NULL);
- LauncherMenuItemPrivate * priv = LAUNCHER_MENU_ITEM_GET_PRIVATE(li);
- return priv->shortcuts;
-}
diff --git a/src/launcher-menu-item.h b/src/launcher-menu-item.h
deleted file mode 100644
index ace1f85..0000000
--- a/src/launcher-menu-item.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
-An indicator to show information that is in messaging applications
-that the user is using.
-
-Copyright 2009 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@canonical.com>
-
-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/>.
-*/
-
-#ifndef __LAUNCHER_MENU_ITEM_H__
-#define __LAUNCHER_MENU_ITEM_H__
-
-#include <glib.h>
-#include <glib-object.h>
-
-#include <libdbusmenu-glib/menuitem.h>
-
-G_BEGIN_DECLS
-
-#define LAUNCHER_MENU_ITEM_TYPE (launcher_menu_item_get_type ())
-#define LAUNCHER_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LAUNCHER_MENU_ITEM_TYPE, LauncherMenuItem))
-#define LAUNCHER_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), LAUNCHER_MENU_ITEM_TYPE, LauncherMenuItemClass))
-#define IS_LAUNCHER_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LAUNCHER_MENU_ITEM_TYPE))
-#define IS_LAUNCHER_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), LAUNCHER_MENU_ITEM_TYPE))
-#define LAUNCHER_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), LAUNCHER_MENU_ITEM_TYPE, LauncherMenuItemClass))
-
-#define LAUNCHER_MENU_ITEM_SIGNAL_NAME_CHANGED "name-changed"
-
-typedef struct _LauncherMenuItem LauncherMenuItem;
-typedef struct _LauncherMenuItemClass LauncherMenuItemClass;
-
-struct _LauncherMenuItemClass {
- DbusmenuMenuitemClass parent_class;
-
- void (* name_changed) (gchar * name);
-};
-
-struct _LauncherMenuItem {
- DbusmenuMenuitem parent;
-};
-
-GType launcher_menu_item_get_type (void);
-LauncherMenuItem * launcher_menu_item_new (const gchar * desktop_file);
-const gchar * launcher_menu_item_get_name (LauncherMenuItem * appitem);
-const gchar * launcher_menu_item_get_desktop (LauncherMenuItem * launchitem);
-const gchar * launcher_menu_item_get_description (LauncherMenuItem * li);
-gchar * launcher_menu_item_get_icon (LauncherMenuItem * appitem);
-void launcher_menu_item_set_eclipsed (LauncherMenuItem * li, gboolean eclipsed);
-gboolean launcher_menu_item_get_eclipsed (LauncherMenuItem * li);
-GList * launcher_menu_item_get_items (LauncherMenuItem * li);
-
-G_END_DECLS
-
-#endif /* __LAUNCHER_MENU_ITEM_H__ */
-
diff --git a/src/messages-service-dbus.c b/src/messages-service-dbus.c
deleted file mode 100644
index 2b72f2e..0000000
--- a/src/messages-service-dbus.c
+++ /dev/null
@@ -1,472 +0,0 @@
-/*
-An indicator to show information that is in messaging applications
-that the user is using.
-
-Copyright 2009 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@canonical.com>
-
-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/>.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <gio/gio.h>
-#include "messages-service-dbus.h"
-#include "dbus-data.h"
-#include "gen-messages-service.xml.h"
-
-enum {
- ATTENTION_CHANGED,
- ICON_CHANGED,
- LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-typedef struct _MessageServiceDbusPrivate MessageServiceDbusPrivate;
-
-struct _MessageServiceDbusPrivate
-{
- GDBusConnection * connection;
- GCancellable * accounts_cancel;
- GDBusProxy * accounts_user;
- gboolean dot;
- gboolean hidden;
-};
-
-#define MESSAGE_SERVICE_DBUS_GET_PRIVATE(o) \
-(G_TYPE_INSTANCE_GET_PRIVATE ((o), MESSAGE_SERVICE_DBUS_TYPE, MessageServiceDbusPrivate))
-
-static void message_service_dbus_class_init (MessageServiceDbusClass *klass);
-static void message_service_dbus_init (MessageServiceDbus *self);
-static void message_service_dbus_dispose (GObject *object);
-static void message_service_dbus_finalize (GObject *object);
-static void bus_method_call (GDBusConnection * connection,
- const gchar * sender,
- const gchar * path,
- const gchar * interface,
- const gchar * method,
- GVariant * params,
- GDBusMethodInvocation * invocation,
- gpointer user_data);
-
-static GDBusNodeInfo * bus_node_info = NULL;
-static GDBusInterfaceInfo * bus_interface_info = NULL;
-static const GDBusInterfaceVTable bus_interface_table = {
- method_call: bus_method_call,
- get_property: NULL, /* No properties */
- set_property: NULL /* No properties */
-};
-
-G_DEFINE_TYPE (MessageServiceDbus, message_service_dbus, G_TYPE_OBJECT);
-
-
-static void
-message_service_dbus_class_init (MessageServiceDbusClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- g_type_class_add_private (klass, sizeof (MessageServiceDbusPrivate));
-
- object_class->dispose = message_service_dbus_dispose;
- object_class->finalize = message_service_dbus_finalize;
-
- signals[ATTENTION_CHANGED] = g_signal_new(MESSAGE_SERVICE_DBUS_SIGNAL_ATTENTION_CHANGED,
- G_TYPE_FROM_CLASS(klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (MessageServiceDbusClass, attention_changed),
- NULL, NULL,
- g_cclosure_marshal_VOID__BOOLEAN,
- G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
-
- signals[ICON_CHANGED] = g_signal_new(MESSAGE_SERVICE_DBUS_SIGNAL_ICON_CHANGED,
- G_TYPE_FROM_CLASS(klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (MessageServiceDbusClass, icon_changed),
- NULL, NULL,
- g_cclosure_marshal_VOID__BOOLEAN,
- G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
-
- if (bus_node_info == NULL) {
- GError * error = NULL;
-
- bus_node_info = g_dbus_node_info_new_for_xml(_messages_service, &error);
- if (error != NULL) {
- g_error("Unable to parse Messaging Menu Interface description: %s", error->message);
- g_error_free(error);
- }
- }
-
- if (bus_interface_info == NULL) {
- bus_interface_info = g_dbus_node_info_lookup_interface(bus_node_info, INDICATOR_MESSAGES_DBUS_SERVICE_INTERFACE);
-
- if (bus_interface_info == NULL) {
- g_error("Unable to find interface '" INDICATOR_MESSAGES_DBUS_SERVICE_INTERFACE "'");
- }
- }
-
- return;
-}
-
-static void
-connection_cb (GObject * object, GAsyncResult * res, gpointer user_data)
-{
- GError * error = NULL;
- GDBusConnection * connection = g_bus_get_finish(res, &error);
-
- if (error != NULL) {
- g_error("Unable to connect to the session bus: %s", error->message);
- g_error_free(error);
- return;
- }
-
- MessageServiceDbusPrivate * priv = MESSAGE_SERVICE_DBUS_GET_PRIVATE(user_data);
- priv->connection = connection;
-
- g_dbus_connection_register_object(connection,
- INDICATOR_MESSAGES_DBUS_SERVICE_OBJECT,
- bus_interface_info,
- &bus_interface_table,
- user_data,
- NULL, /* destroy */
- &error);
-
- if (error != NULL) {
- g_error("Unable to register on session bus: %s", error->message);
- g_error_free(error);
- return;
- }
-
- g_debug("Service on session bus");
-
- return;
-}
-
-static void
-accounts_notify_cb (GObject *source_object, GAsyncResult *res,
- gpointer user_data)
-{
- GError * error = NULL;
- GVariant * answer = g_dbus_proxy_call_finish(G_DBUS_PROXY(source_object), res, &error);
-
- if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
- g_error_free(error);
- return; /* Must exit before accessing freed memory */
- }
-
- MessageServiceDbusPrivate * priv = MESSAGE_SERVICE_DBUS_GET_PRIVATE(user_data);
-
- if (priv->accounts_cancel != NULL) {
- g_object_unref(priv->accounts_cancel);
- priv->accounts_cancel = NULL;
- }
-
- if (error != NULL) {
- g_warning("Unable to get notify accounts service of message status: %s", error->message);
- g_error_free(error);
- return;
- }
-
- g_variant_unref (answer);
-}
-
-static void
-accounts_notify (MessageServiceDbus *self)
-{
- MessageServiceDbusPrivate * priv = MESSAGE_SERVICE_DBUS_GET_PRIVATE(self);
-
- if (priv->accounts_user == NULL)
- return; /* We're not able to talk to accounts service */
-
- if (priv->accounts_cancel != NULL) {
- /* Cancel old notify before starting new one */
- g_cancellable_cancel(priv->accounts_cancel);
- g_object_unref(priv->accounts_cancel);
- priv->accounts_cancel = NULL;
- }
-
- priv->accounts_cancel = g_cancellable_new();
- g_dbus_proxy_call(priv->accounts_user,
- "SetXHasMessages",
- g_variant_new ("(b)", priv->dot),
- G_DBUS_CALL_FLAGS_NONE,
- -1, /* timeout */
- priv->accounts_cancel,
- accounts_notify_cb,
- self);
-}
-
-static void
-get_accounts_user_proxy_cb (GObject *source_object, GAsyncResult *res,
- gpointer user_data)
-{
- GError * error = NULL;
- GDBusProxy * proxy = g_dbus_proxy_new_for_bus_finish(res, &error);
-
- if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
- g_error_free(error);
- return; /* Must exit before accessing freed memory */
- }
-
- MessageServiceDbusPrivate * priv = MESSAGE_SERVICE_DBUS_GET_PRIVATE(user_data);
-
- if (priv->accounts_cancel != NULL) {
- g_object_unref(priv->accounts_cancel);
- priv->accounts_cancel = NULL;
- }
-
- if (error != NULL) {
- g_warning("Unable to get proxy of accountsservice: %s", error->message);
- g_error_free(error);
- return;
- }
-
- priv->accounts_user = proxy;
- accounts_notify (MESSAGE_SERVICE_DBUS (user_data));
-}
-
-static void
-get_accounts_user_find_user_cb (GObject *source_object, GAsyncResult *res,
- gpointer user_data)
-{
- GError * error = NULL;
- GVariant * answer = g_dbus_proxy_call_finish(G_DBUS_PROXY(source_object), res, &error);
-
- /* We're done with main accounts proxy now */
- g_object_unref (source_object);
-
- if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
- g_error_free(error);
- return; /* Must exit before accessing freed memory */
- }
-
- MessageServiceDbusPrivate * priv = MESSAGE_SERVICE_DBUS_GET_PRIVATE(user_data);
-
- if (priv->accounts_cancel != NULL) {
- g_object_unref(priv->accounts_cancel);
- priv->accounts_cancel = NULL;
- }
-
- if (error != NULL) {
- g_warning("Unable to get object name of user from accountsservice: %s", error->message);
- g_error_free(error);
- return;
- }
-
- if (!g_variant_is_of_type (answer, G_VARIANT_TYPE ("(o)"))) {
- g_warning("Unexpected type from FindUserByName: %s", g_variant_get_type_string (answer));
- g_variant_unref(answer);
- return;
- }
-
- const gchar *path;
- g_variant_get(answer, "(&o)", &path);
-
- priv->accounts_cancel = g_cancellable_new();
- g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM,
- G_DBUS_PROXY_FLAGS_NONE,
- NULL,
- "org.freedesktop.Accounts",
- path,
- "org.freedesktop.Accounts.User",
- priv->accounts_cancel,
- get_accounts_user_proxy_cb,
- user_data);
-
- g_variant_unref (answer);
-}
-
-static void
-get_accounts_proxy_cb (GObject *source_object, GAsyncResult *res,
- gpointer user_data)
-{
- GError * error = NULL;
- GDBusProxy * proxy = g_dbus_proxy_new_for_bus_finish(res, &error);
-
- if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
- g_error_free(error);
- return; /* Must exit before accessing freed memory */
- }
-
- MessageServiceDbusPrivate * priv = MESSAGE_SERVICE_DBUS_GET_PRIVATE(user_data);
-
- if (priv->accounts_cancel != NULL) {
- g_object_unref(priv->accounts_cancel);
- priv->accounts_cancel = NULL;
- }
-
- if (error != NULL) {
- g_warning("Unable to get proxy of accountsservice: %s", error->message);
- g_error_free(error);
- return;
- }
-
- priv->accounts_cancel = g_cancellable_new();
- g_dbus_proxy_call(proxy,
- "FindUserByName",
- g_variant_new ("(s)", g_get_user_name ()),
- G_DBUS_CALL_FLAGS_NONE,
- -1, /* timeout */
- priv->accounts_cancel,
- get_accounts_user_find_user_cb,
- user_data);
-}
-
-static void
-get_accounts_proxy (MessageServiceDbus *self)
-{
- MessageServiceDbusPrivate * priv = MESSAGE_SERVICE_DBUS_GET_PRIVATE(self);
-
- g_return_if_fail(priv->accounts_cancel == NULL);
-
- priv->accounts_cancel = g_cancellable_new();
- g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM,
- G_DBUS_PROXY_FLAGS_NONE,
- NULL,
- "org.freedesktop.Accounts",
- "/org/freedesktop/Accounts",
- "org.freedesktop.Accounts",
- priv->accounts_cancel,
- get_accounts_proxy_cb,
- self);
-}
-
-static void
-message_service_dbus_init (MessageServiceDbus *self)
-{
- g_bus_get(G_BUS_TYPE_SESSION, NULL, connection_cb, self);
- get_accounts_proxy (self);
-
- MessageServiceDbusPrivate * priv = MESSAGE_SERVICE_DBUS_GET_PRIVATE(self);
-
- priv->dot = FALSE;
- priv->hidden = FALSE;
-
- return;
-}
-
-static void
-message_service_dbus_dispose (GObject *object)
-{
- MessageServiceDbusPrivate * priv = MESSAGE_SERVICE_DBUS_GET_PRIVATE(object);
-
- if (priv->connection != NULL) {
- g_object_unref(priv->connection);
- priv->connection = NULL;
- }
-
- if (priv->accounts_cancel != NULL) {
- g_cancellable_cancel(priv->accounts_cancel);
- g_object_unref(priv->accounts_cancel);
- priv->accounts_cancel = NULL;
- }
-
- if (priv->accounts_user != NULL) {
- g_object_unref(priv->accounts_user);
- priv->accounts_user = NULL;
- }
-
- G_OBJECT_CLASS (message_service_dbus_parent_class)->dispose (object);
- return;
-}
-
-static void
-message_service_dbus_finalize (GObject *object)
-{
-
-
- G_OBJECT_CLASS (message_service_dbus_parent_class)->finalize (object);
- return;
-}
-
-MessageServiceDbus *
-message_service_dbus_new (void)
-{
- return MESSAGE_SERVICE_DBUS(g_object_new(MESSAGE_SERVICE_DBUS_TYPE, NULL));
-}
-
-/* Method request off of DBus */
-static void
-bus_method_call (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * method, GVariant * params, GDBusMethodInvocation * invocation, gpointer user_data)
-{
- MessageServiceDbus * ms = MESSAGE_SERVICE_DBUS(user_data);
- if (ms == NULL) { return; }
-
- MessageServiceDbusPrivate * priv = MESSAGE_SERVICE_DBUS_GET_PRIVATE(ms);
-
- if (g_strcmp0("AttentionRequested", method) == 0) {
- g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", priv->dot));
- return;
- } else if (g_strcmp0("IconShown", method) == 0) {
- g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", priv->hidden));
- return;
- } else if (g_strcmp0("ClearAttention", method) == 0) {
- message_service_dbus_set_attention(ms, FALSE);
- g_dbus_method_invocation_return_value(invocation, NULL);
- return;
- } else {
- g_warning("Unknown function call '%s'", method);
- }
-
- return;
-}
-
-void
-message_service_dbus_set_attention (MessageServiceDbus * self, gboolean attention)
-{
- MessageServiceDbusPrivate * priv = MESSAGE_SERVICE_DBUS_GET_PRIVATE(self);
- /* Do signal */
- if (attention != priv->dot) {
- priv->dot = attention;
- g_signal_emit(G_OBJECT(self), signals[ATTENTION_CHANGED], 0, priv->dot, TRUE);
-
- if (priv->connection != NULL) {
- g_dbus_connection_emit_signal(priv->connection,
- NULL,
- INDICATOR_MESSAGES_DBUS_SERVICE_OBJECT,
- INDICATOR_MESSAGES_DBUS_SERVICE_INTERFACE,
- "AttentionChanged",
- g_variant_new("(b)", priv->dot),
- NULL);
- }
-
- accounts_notify (self);
- }
- return;
-}
-
-void
-message_service_dbus_set_icon (MessageServiceDbus * self, gboolean hidden)
-{
- MessageServiceDbusPrivate * priv = MESSAGE_SERVICE_DBUS_GET_PRIVATE(self);
- /* Do signal */
- if (hidden != priv->hidden) {
- priv->hidden = hidden;
- g_signal_emit(G_OBJECT(self), signals[ICON_CHANGED], 0, priv->hidden, TRUE);
-
- if (priv->connection != NULL) {
- g_dbus_connection_emit_signal(priv->connection,
- NULL,
- INDICATOR_MESSAGES_DBUS_SERVICE_OBJECT,
- INDICATOR_MESSAGES_DBUS_SERVICE_INTERFACE,
- "IconChanged",
- g_variant_new("(b)", priv->hidden),
- NULL);
- }
- }
- return;
-}
diff --git a/src/messages-service-dbus.h b/src/messages-service-dbus.h
deleted file mode 100644
index 7a8574e..0000000
--- a/src/messages-service-dbus.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
-An indicator to show information that is in messaging applications
-that the user is using.
-
-Copyright 2009 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@canonical.com>
-
-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/>.
-*/
-
-#ifndef __MESSAGE_SERVICE_DBUS_H__
-#define __MESSAGE_SERVICE_DBUS_H__
-
-#include <glib.h>
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-#define MESSAGE_SERVICE_DBUS_TYPE (message_service_dbus_get_type ())
-#define MESSAGE_SERVICE_DBUS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MESSAGE_SERVICE_DBUS_TYPE, MessageServiceDbus))
-#define MESSAGE_SERVICE_DBUS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MESSAGE_SERVICE_DBUS_TYPE, MessageServiceDbusClass))
-#define IS_MESSAGE_SERVICE_DBUS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MESSAGE_SERVICE_DBUS_TYPE))
-#define IS_MESSAGE_SERVICE_DBUS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MESSAGE_SERVICE_DBUS_TYPE))
-#define MESSAGE_SERVICE_DBUS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MESSAGE_SERVICE_DBUS_TYPE, MessageServiceDbusClass))
-
-#define MESSAGE_SERVICE_DBUS_SIGNAL_ATTENTION_CHANGED "attention-changed"
-#define MESSAGE_SERVICE_DBUS_SIGNAL_ICON_CHANGED "icon-changed"
-
-typedef struct _MessageServiceDbus MessageServiceDbus;
-typedef struct _MessageServiceDbusClass MessageServiceDbusClass;
-
-struct _MessageServiceDbusClass {
- GObjectClass parent_class;
-
- void (*attention_changed) (gboolean dot);
- void (*icon_changed) (gboolean hidden);
-};
-
-struct _MessageServiceDbus {
- GObject parent;
-};
-
-GType message_service_dbus_get_type (void);
-MessageServiceDbus * message_service_dbus_new (void);
-void message_service_dbus_set_attention (MessageServiceDbus * self, gboolean attention);
-void message_service_dbus_set_icon (MessageServiceDbus * self, gboolean hidden);
-
-G_END_DECLS
-
-#endif
diff --git a/src/messages-service.c b/src/messages-service.c
index 078cee3..7654e1f 100644
--- a/src/messages-service.c
+++ b/src/messages-service.c
@@ -20,1512 +20,461 @@ You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <string.h>
-#include <locale.h>
-#include <libintl.h>
#include <config.h>
-#include <pango/pango-utils.h>
-#include <libindicate/listener.h>
+#include <locale.h>
#include <libindicator/indicator-service.h>
#include <gio/gio.h>
+#include <gio/gdesktopappinfo.h>
#include <glib/gi18n.h>
-#include <libdbusmenu-glib/client.h>
-#include <libdbusmenu-glib/server.h>
-
-#include "im-menu-item.h"
-#include "app-menu-item.h"
-#include "launcher-menu-item.h"
+#include "app-section.h"
#include "dbus-data.h"
-#include "dirs.h"
-#include "messages-service-dbus.h"
-#include "seen-db.h"
-#include "status-items.h"
-
-static IndicatorService * service = NULL;
-static IndicateListener * listener = NULL;
-static GList * serverList = NULL;
-static GList * launcherList = NULL;
-
-static DbusmenuMenuitem * root_menuitem = NULL;
-static DbusmenuMenuitem * status_separator = NULL;
-static DbusmenuMenuitem * clear_attention = NULL;
-static GMainLoop * mainloop = NULL;
-
-static MessageServiceDbus * dbus_interface = NULL;
-
-#define DESKTOP_FILE_GROUP "Messaging Menu"
-#define DESKTOP_FILE_KEY_DESKTOP "DesktopFile"
-
-static void server_shortcut_added (AppMenuItem * appitem, DbusmenuMenuitem * mi, gpointer data);
-static void server_shortcut_removed (AppMenuItem * appitem, DbusmenuMenuitem * mi, gpointer data);
-static void server_count_changed (AppMenuItem * appitem, guint count, gpointer data);
-static void server_name_changed (AppMenuItem * appitem, gchar * name, gpointer data);
-static void im_time_changed (ImMenuItem * imitem, glong seconds, gpointer data);
-static void resort_menu (DbusmenuMenuitem * menushell);
-static void indicator_removed (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gpointer data);
-static void check_eclipses (AppMenuItem * ai);
-static void remove_eclipses (AppMenuItem * ai);
-static gboolean build_launcher (gpointer data);
-static gboolean build_launcher_keyfile (gpointer data);
-static void build_launcher_core (const gchar * desktop);
-static gboolean build_launchers (gpointer data);
-static gboolean blacklist_init (gpointer data);
-static gboolean blacklist_add (gpointer data);
-static gchar * desktop_file_from_keyfile (const gchar * definition_file);
-static gboolean blacklist_keyfile_add (gpointer udata);
-static void blacklist_add_core (gchar * desktop, gchar * definition);
-static gboolean blacklist_remove (gpointer data);
-static void blacklist_dir_changed (GFileMonitor * monitor, GFile * file, GFile * other_file, GFileMonitorEvent event_type, gpointer user_data);
-static void app_dir_changed (GFileMonitor * monitor, GFile * file, GFile * other_file, GFileMonitorEvent event_type, gpointer user_data);
-static gboolean destroy_launcher (gpointer data);
-static void check_hidden (void);
+#include "gactionmuxer.h"
+#include "gsettingsstrv.h"
+#include "gmenuutils.h"
+#include "indicator-messages-service.h"
+static GHashTable *applications;
-/*
- * Server List
- */
-
-typedef struct _serverList_t serverList_t;
-struct _serverList_t {
- IndicateListenerServer * server;
- AppMenuItem * menuitem;
- DbusmenuMenuitem * separator;
- gboolean attention;
- guint count;
- GList * imList;
-};
+IndicatorMessagesService *messages_service;
+static GSimpleActionGroup *actions;
+static GActionMuxer *action_muxer;
+static GMenu *toplevel_menu;
+static GMenu *menu;
+static GMenuModel *chat_section;
+static GSettings *settings;
-static gint
-serverList_equal (gconstpointer a, gconstpointer b)
-{
- serverList_t * pa, * pb;
- pa = (serverList_t *)a;
- pb = (serverList_t *)b;
+static gchar *
+g_app_info_get_simple_id (GAppInfo *appinfo)
+{
+ const gchar *id;
- const gchar * pan = INDICATE_LISTENER_SERVER_DBUS_NAME(pa->server);
- const gchar * pbn = INDICATE_LISTENER_SERVER_DBUS_NAME(pb->server);
- const gchar * pap = indicate_listener_server_get_dbuspath(pa->server);
- const gchar * pbp = indicate_listener_server_get_dbuspath(pb->server);
+ id = g_app_info_get_id (appinfo);
+ if (!id)
+ return NULL;
- if (g_strcmp0(pan, pbn) == 0)
- return g_strcmp0(pap, pbp);
+ if (g_str_has_suffix (id, ".desktop"))
+ return g_strndup (id, strlen (id) - 8);
else
- return 1;
+ return g_strdup (id);
}
-static gint
-serverList_sort (gconstpointer a, gconstpointer b)
+static void
+actions_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
{
- serverList_t * pa, * pb;
+ AppSection *section = APP_SECTION (object);
+ gchar *id;
+ GActionGroup *actions;
- pa = (serverList_t *)a;
- pb = (serverList_t *)b;
+ id = g_app_info_get_simple_id (app_section_get_app_info (section));
+ actions = app_section_get_actions (section);
- const gchar * pan = app_menu_item_get_name(pa->menuitem);
- const gchar * pbn = app_menu_item_get_name(pb->menuitem);
-
- return g_strcmp0(pan, pbn);
+ g_action_muxer_insert (action_muxer, id, actions);
+ g_free (id);
}
-/*
- * Item List
- */
-
-typedef struct _imList_t imList_t;
-struct _imList_t {
- IndicateListenerServer * server;
- IndicateListenerIndicator * indicator;
- DbusmenuMenuitem * menuitem;
- gulong timechange_cb;
- gulong attentionchange_cb;
-};
static gboolean
-imList_equal (gconstpointer a, gconstpointer b)
-{
- imList_t * pa, * pb;
-
- pa = (imList_t *)a;
- pb = (imList_t *)b;
-
- const gchar * pas = INDICATE_LISTENER_SERVER_DBUS_NAME(pa->server);
- const gchar * pbs = INDICATE_LISTENER_SERVER_DBUS_NAME(pb->server);
-
- guint pai = INDICATE_LISTENER_INDICATOR_ID(pa->indicator);
- guint pbi = INDICATE_LISTENER_INDICATOR_ID(pb->indicator);
-
- g_debug("\tComparing (%s %d) to (%s %d)", pas, pai, pbs, pbi);
-
- return !((!g_strcmp0(pas, pbs)) && (pai == pbi));
-}
-
-static gint
-imList_sort (gconstpointer a, gconstpointer b)
-{
- imList_t * pa, * pb;
-
- pa = (imList_t *)a;
- pb = (imList_t *)b;
-
- return (gint)(im_menu_item_get_seconds(IM_MENU_ITEM(pb->menuitem)) - im_menu_item_get_seconds(IM_MENU_ITEM(pa->menuitem)));
-}
-
-/*
- * Launcher List
- */
-
-typedef struct _launcherList_t launcherList_t;
-struct _launcherList_t {
- LauncherMenuItem * menuitem;
- DbusmenuMenuitem * separator;
- GList * appdiritems;
-};
-
-static gint
-launcherList_sort (gconstpointer a, gconstpointer b)
+app_section_draws_attention (gpointer key,
+ gpointer value,
+ gpointer user_data)
{
- launcherList_t * pa, * pb;
-
- pa = (launcherList_t *)a;
- pb = (launcherList_t *)b;
-
- const gchar * pan = launcher_menu_item_get_name(pa->menuitem);
- const gchar * pbn = launcher_menu_item_get_name(pb->menuitem);
-
- return g_strcmp0(pan, pbn);
+ AppSection *section = value;
+ return app_section_get_draws_attention (section);
}
static void
-launcherList_count_helper (gpointer data, gpointer user_data)
+draws_attention_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
{
- guint * count = (guint *)user_data;
- launcherList_t * li = (launcherList_t *)data;
-
- if (!launcher_menu_item_get_eclipsed(li->menuitem)) {
- *count = *count + 1;
- }
-
- return;
-}
-
-static guint
-launcherList_count (void)
-{
- guint count = 0;
-
- g_list_foreach(launcherList, launcherList_count_helper, &count);
-
- return count;
-}
-
-/*
- * Black List
- */
-
-static GHashTable * blacklist = NULL;
-static GFileMonitor * blacklistdirmon = NULL;
-
-/* Initialize the black list and start to setup
- handlers for it. */
-static gboolean
-blacklist_init (gpointer data)
-{
- blacklist = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, g_free);
-
- gchar * blacklistdir = g_build_filename(g_get_user_config_dir(), USER_BLACKLIST_DIR, NULL);
- g_debug("Looking at blacklist: %s", blacklistdir);
- if (!g_file_test(blacklistdir, G_FILE_TEST_IS_DIR)) {
- g_free(blacklistdir);
- return FALSE;
- }
-
- GFile * filedir = g_file_new_for_path(blacklistdir);
- blacklistdirmon = g_file_monitor_directory(filedir, G_FILE_MONITOR_NONE, NULL, NULL);
- if (blacklistdirmon != NULL) {
- g_signal_connect(G_OBJECT(blacklistdirmon), "changed", G_CALLBACK(blacklist_dir_changed), NULL);
- }
-
- GError * error = NULL;
- GDir * dir = g_dir_open(blacklistdir, 0, &error);
- if (dir == NULL) {
- g_warning("Unable to open blacklist directory (%s): %s", blacklistdir, error == NULL ? "No Message" : error->message);
- g_error_free(error);
- g_free(blacklistdir);
- return FALSE;
- }
+ GSimpleAction *messages;
+ GSimpleAction *clear;
+ gboolean attention;
- const gchar * filename = NULL;
- while ((filename = g_dir_read_name(dir)) != NULL) {
- g_debug("Found file: %s", filename);
- gchar * path = g_build_filename(blacklistdir, filename, NULL);
- if (g_str_has_suffix(path, "keyfile")) {
- g_idle_add(blacklist_keyfile_add, path);
- } else {
- g_idle_add(blacklist_add, path);
- }
- }
+ messages = G_SIMPLE_ACTION (g_simple_action_group_lookup (actions, "messages"));
+ clear = G_SIMPLE_ACTION (g_simple_action_group_lookup (actions, "clear"));
+ g_return_if_fail (messages != NULL && clear != NULL);
- g_dir_close(dir);
- g_free(blacklistdir);
+ attention = g_hash_table_find (applications, app_section_draws_attention, NULL) != NULL;
- return FALSE;
+ g_simple_action_set_state (messages, g_variant_new_boolean (attention));
+ g_simple_action_set_enabled (clear, attention);
}
-/* Parses through a keyfile to find the desktop file entry and
- pushes them into the blacklist. */
static gboolean
-blacklist_keyfile_add (gpointer udata)
+app_section_uses_chat (gpointer key,
+ gpointer value,
+ gpointer user_data)
{
- gchar * definition_file = (gchar *)udata;
- gchar * desktopfile = desktop_file_from_keyfile(definition_file);
- if (desktopfile != NULL) {
- blacklist_add_core(desktopfile, definition_file);
- g_free(desktopfile);
- }
- return FALSE;
+ AppSection *section = value;
+ return app_section_get_uses_chat_status (section);
}
-/* Takes a keyfile and finds the desktop file in it for
- us. With some error handling. */
-static gchar *
-desktop_file_from_keyfile (const gchar * definition_file)
+static void
+uses_chat_status_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
{
- GKeyFile * keyfile = g_key_file_new();
- GError * error = NULL;
+ gboolean show_chat;
+ GMenuModel *first_section;
- if (!g_key_file_load_from_file(keyfile, definition_file, G_KEY_FILE_NONE, &error)) {
- g_warning("Unable to load keyfile '%s' because: %s", definition_file, error == NULL ? "unknown" : error->message);
- g_error_free(error);
- g_key_file_free(keyfile);
- return NULL;
- }
+ show_chat = g_hash_table_find (applications, app_section_uses_chat, NULL) != NULL;
- if (!g_key_file_has_group(keyfile, DESKTOP_FILE_GROUP)) {
- g_warning("Unable to use keyfile '%s' as it has no '" DESKTOP_FILE_GROUP "' group.", definition_file);
- g_key_file_free(keyfile);
- return 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);
}
-
- if (!g_key_file_has_key(keyfile, DESKTOP_FILE_GROUP, DESKTOP_FILE_KEY_DESKTOP, &error)) {
- g_warning("Unable to use keyfile '%s' as there is no key '" DESKTOP_FILE_KEY_DESKTOP "' in the group '" DESKTOP_FILE_GROUP "' because: %s", definition_file, error == NULL ? "unknown" : error->message);
- g_error_free(error);
- g_key_file_free(keyfile);
- return NULL;
+ else {
+ if (show_chat)
+ g_menu_insert_section (menu, 0, NULL, chat_section);
}
-
- gchar * desktopfile = g_key_file_get_string(keyfile, DESKTOP_FILE_GROUP, DESKTOP_FILE_KEY_DESKTOP, &error);
- g_key_file_free(keyfile);
- return desktopfile;
}
-/* Check if path is a symlink and return its target if it is */
-static gchar *
-get_symlink_target (const gchar *path)
+static AppSection *
+add_application (const gchar *desktop_id)
{
- GFile *file;
- GFileInfo *fileinfo;
- gchar *target = NULL;
-
- file = g_file_new_for_path (path);
+ GDesktopAppInfo *appinfo;
+ gchar *id;
+ AppSection *section;
- fileinfo = g_file_query_info (file, "standard::is-symlink",
- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
- NULL, NULL);
- g_object_unref (file);
-
- if (!fileinfo)
+ 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;
+ }
- if (g_file_info_get_is_symlink (fileinfo))
- target = g_strdup (g_file_info_get_symlink_target (fileinfo));
+ id = g_app_info_get_simple_id (G_APP_INFO (appinfo));
+ section = g_hash_table_lookup (applications, id);
- g_object_unref (fileinfo);
- return target;
-}
+ if (!section) {
+ GMenuItem *menuitem;
-/* Add a definition file into the black list and eclipse
- any launchers that have the same file. */
-static gboolean
-blacklist_add (gpointer udata)
-{
- gchar * definition_file = (gchar *)udata;
- gchar * symlink_target = get_symlink_target (definition_file);
- gchar * contents = NULL;
+ section = app_section_new(appinfo);
+ g_hash_table_insert (applications, g_strdup (id), section);
- if (symlink_target)
- {
- blacklist_add_core (symlink_target, definition_file);
- g_free (symlink_target);
- }
- else if (g_str_has_suffix (definition_file, ".desktop"))
- {
- blacklist_add_core(definition_file, definition_file);
- }
- else if (g_file_get_contents (definition_file, &contents, NULL, NULL))
- {
- gchar *trimmed = pango_trim_string (contents);
- blacklist_add_core (trimmed, definition_file);
- g_free (trimmed);
- g_free (contents);
+ 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);
+
+ /* 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);
}
- else
- g_warning ("invalid blacklist entry: %s", definition_file);
- return FALSE;
+ g_free (id);
+ g_object_unref (appinfo);
+ return section;
}
-/* This takes a desktop file and tries to add it to the black
- list for applications in the messaging menu. If it can,
- then the launcher item gets marked as eclipsed and hidden
- from the user. */
static void
-blacklist_add_core (gchar * desktop, gchar * definition)
+remove_application (const char *desktop_id)
{
- gchar *basename = g_path_get_basename(desktop);
-
- /* Check for conflicts */
- gpointer data = g_hash_table_lookup(blacklist, basename);
- if (data != NULL) {
- gchar * oldfile = (gchar *)data;
- if (!g_strcmp0(oldfile, definition)) {
- g_warning("Already added file '%s'", oldfile);
- } else {
- g_warning("Already have desktop file '%s' in blacklist file '%s' not adding from '%s'", desktop, oldfile, definition);
- }
-
- g_free(basename);
- return;
- }
+ GDesktopAppInfo *appinfo;
+ gchar *id;
+ AppSection *section;
- /* Actually blacklist this thing */
- g_hash_table_insert(blacklist, g_strdup(basename), g_strdup(definition));
- g_debug("Adding Blacklist item '%s' for desktop '%s'", definition, desktop);
-
- /* Go through and eclipse folks */
- GList * launcher;
- for (launcher = launcherList; launcher != NULL; launcher = launcher->next) {
- launcherList_t * item = (launcherList_t *)launcher->data;
- gchar * item_basename = g_path_get_basename(launcher_menu_item_get_desktop(item->menuitem));
- if (!g_strcmp0(basename, item_basename)) {
- launcher_menu_item_set_eclipsed(item->menuitem, TRUE);
- dbusmenu_menuitem_property_set_bool(item->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
- }
- g_free(item_basename);
+ 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);
+ return;
}
- check_hidden();
- /* Shouldn't need a resort here as hiding shouldn't cause things to
- move other than this item disappearing. */
+ id = g_app_info_get_simple_id (G_APP_INFO (appinfo));
- g_free(basename);
- return;
-}
+ section = g_hash_table_lookup (applications, id);
+ if (section) {
+ 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);
-/* Remove a black list item based on the definition file
- and uneclipse those launchers blocked by it. */
-static gboolean
-blacklist_remove (gpointer data)
-{
- gchar * definition_file = (gchar *)data;
- g_debug("Removing: %s", definition_file);
-
- GHashTableIter iter;
- gpointer key, value;
- gboolean found = FALSE;
-
- g_hash_table_iter_init(&iter, blacklist);
- while (g_hash_table_iter_next(&iter, &key, &value)) {
- if (!g_strcmp0((gchar *)value, definition_file)) {
- found = TRUE;
- break;
- }
+ 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);
}
-
- if (!found) {
- g_debug("\tNot found!");
- return FALSE;
+ else {
+ g_warning ("could not remove '%s', it's not registered", desktop_id);
}
-
- GList * launcheritem;
- for (launcheritem = launcherList; launcheritem != NULL; launcheritem = launcheritem->next) {
- launcherList_t * li = (launcherList_t *)launcheritem->data;
- if (!g_strcmp0(launcher_menu_item_get_desktop(li->menuitem), (gchar *)key)) {
- GList * serveritem;
- for (serveritem = serverList; serveritem != NULL; serveritem = serveritem->next) {
- serverList_t * si = (serverList_t *)serveritem->data;
- if (!g_strcmp0(app_menu_item_get_desktop(si->menuitem), (gchar *)key)) {
- break;
- }
- }
- if (serveritem == NULL) {
- launcher_menu_item_set_eclipsed(li->menuitem, FALSE);
- dbusmenu_menuitem_property_set_bool(li->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
- }
- }
- }
-
- if (!g_hash_table_remove(blacklist, key)) {
- g_warning("Unable to remove '%s' with value '%s'", definition_file, (gchar *)key);
- }
-
- check_hidden();
- resort_menu(root_menuitem);
-
- return FALSE;
+
+ g_hash_table_remove (applications, id);
+ g_free (id);
+ g_object_unref (appinfo);
}
-/* Check to see if a particular desktop file is
- in the blacklist. */
+/* This function turns a specific desktop id into a menu
+ item and registers it appropriately with everyone */
static gboolean
-blacklist_check (const gchar * desktop_file)
+build_launcher (gpointer data)
{
- gchar *basename = g_path_get_basename(desktop_file);
- gboolean found;
+ gchar *desktop_id = data;
- g_debug("Checking blacklist for: %s", basename);
+ add_application (desktop_id);
- if (blacklist && g_hash_table_lookup(blacklist, basename)) {
- g_debug("\tFound!");
- found = TRUE;
- }
- else
- found = FALSE;
-
- g_free(basename);
- return found;
-}
-
-/* A callback everytime the blacklist directory changes
- in some way. It needs to handle that. */
-static void
-blacklist_dir_changed (GFileMonitor * monitor, GFile * file, GFile * other_file, GFileMonitorEvent event_type, gpointer user_data)
-{
- g_debug("Blacklist directory changed!");
-
- switch (event_type) {
- case G_FILE_MONITOR_EVENT_DELETED: {
- gchar * path = g_file_get_path(file);
- g_debug("\tDelete: %s", path);
- g_idle_add(blacklist_remove, path);
- break;
- }
- case G_FILE_MONITOR_EVENT_CREATED: {
- gchar * path = g_file_get_path(file);
- g_debug("\tCreate: %s", path);
- g_idle_add(blacklist_add, path);
- break;
- }
- default:
- break;
- }
-
- return;
+ g_free (desktop_id);
+ return FALSE;
}
-/*
- * More code
- */
-
-/* Goes through all the servers and sees if any of them
- want attention. If they do, then well we'll give it
- to them. If they don't, let's not bother the user
- any, shall we? */
-static void
-check_attention (void)
+/* 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)
{
- GList * pointer;
- for (pointer = serverList; pointer != NULL; pointer = g_list_next(pointer)) {
- serverList_t * slt = (serverList_t *)pointer->data;
- if (slt->attention) {
- message_service_dbus_set_attention(dbus_interface, TRUE);
- return;
- }
- }
- message_service_dbus_set_attention(dbus_interface, FALSE);
- return;
-}
+ gchar **applications = g_settings_get_strv (settings, "applications");
+ gchar **app;
-/* This checks a server listing to see if it should
- have attention. It can get attention through it's
- count or by having an indicator that is requestion
- attention. */
-static void
-server_attention (serverList_t * slt)
-{
- /* Count, easy yes and out. */
- if (slt->count > 0) {
- slt->attention = TRUE;
- return;
- }
+ g_return_val_if_fail (applications != NULL, FALSE);
- /* Check to see if any of the indicators want attention */
- GList * pointer;
- for (pointer = slt->imList; pointer != NULL; pointer = g_list_next(pointer)) {
- imList_t * ilt = (imList_t *)pointer->data;
- if (im_menu_item_get_attention(IM_MENU_ITEM(ilt->menuitem))) {
- slt->attention = TRUE;
- return;
- }
+ for (app = applications; *app; app++)
+ {
+ g_idle_add(build_launcher, g_strdup (*app));
}
- /* Nope, no one */
- slt->attention = FALSE;
- return;
+ g_strfreev (applications);
+ return FALSE;
}
-/* A new server has been created on the indicate bus.
- We need to check to see if we like it. And build
- structures for it if so. */
static void
-server_added (IndicateListener * listener, IndicateListenerServer * server, gchar * type, gpointer data)
-{
- g_debug("Server Added '%s' of type '%s'.", INDICATE_LISTENER_SERVER_DBUS_NAME(server), type);
- if (type == NULL) {
- return;
- }
-
- if (type[0] == '\0') {
- return;
- }
-
- if (strncmp(type, "message", strlen("message"))) {
- g_debug("\tServer type '%s' is not a message based type.", type);
- return;
- }
-
- DbusmenuMenuitem * menushell = DBUSMENU_MENUITEM(data);
- if (menushell == NULL) {
- g_error("\tData in callback is not a menushell");
- return;
- }
-
- /* Build the Menu item */
- AppMenuItem * menuitem = app_menu_item_new(listener, server);
-
- /* Build a possible server structure */
- serverList_t * sl_item = g_new0(serverList_t, 1);
- sl_item->server = server;
- sl_item->menuitem = menuitem;
- sl_item->imList = NULL;
- sl_item->attention = FALSE;
- sl_item->count = 0;
-
- /* Incase we got an indicator first */
- GList * alreadythere = g_list_find_custom(serverList, sl_item, serverList_equal);
- if (alreadythere != NULL) {
- /* Use the one we already had */
- g_free(sl_item);
- sl_item = (serverList_t *)alreadythere->data;
- sl_item->menuitem = menuitem;
- serverList = g_list_sort(serverList, serverList_sort);
- } else {
- /* Insert the new one in the list */
- serverList = g_list_insert_sorted(serverList, sl_item, serverList_sort);
- }
-
- /* Build a separator */
- sl_item->separator = dbusmenu_menuitem_new();
- dbusmenu_menuitem_property_set(sl_item->separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR);
-
- /* Connect the signals up to the menu item */
- g_signal_connect(G_OBJECT(menuitem), APP_MENU_ITEM_SIGNAL_COUNT_CHANGED, G_CALLBACK(server_count_changed), sl_item);
- g_signal_connect(G_OBJECT(menuitem), APP_MENU_ITEM_SIGNAL_NAME_CHANGED, G_CALLBACK(server_name_changed), menushell);
- g_signal_connect(G_OBJECT(menuitem), APP_MENU_ITEM_SIGNAL_SHORTCUT_ADDED, G_CALLBACK(server_shortcut_added), menushell);
- g_signal_connect(G_OBJECT(menuitem), APP_MENU_ITEM_SIGNAL_SHORTCUT_REMOVED, G_CALLBACK(server_shortcut_removed), menushell);
-
- /* Put our new menu item in, with the separator behind it.
- resort_menu will take care of whether it should be hidden
- or not. */
- dbusmenu_menuitem_child_append(menushell, DBUSMENU_MENUITEM(menuitem));
-
- GList * shortcuts = app_menu_item_get_items(sl_item->menuitem);
- while (shortcuts != NULL) {
- DbusmenuMenuitem * mi = DBUSMENU_MENUITEM(shortcuts->data);
- g_debug("\tAdding shortcut: %s", dbusmenu_menuitem_property_get(mi, DBUSMENU_MENUITEM_PROP_LABEL));
- dbusmenu_menuitem_child_append(menushell, mi);
- shortcuts = g_list_next(shortcuts);
- }
-
- dbusmenu_menuitem_child_append(menushell, DBUSMENU_MENUITEM(sl_item->separator));
-
- resort_menu(menushell);
- check_hidden();
-
- return;
-}
-
-/* Server shortcut has been added */
-static void
-server_shortcut_added (AppMenuItem * appitem, DbusmenuMenuitem * mi, gpointer data)
-{
- g_debug("Application Shortcut added: %s", mi != NULL ? dbusmenu_menuitem_property_get(mi, DBUSMENU_MENUITEM_PROP_LABEL) : "none");
- DbusmenuMenuitem * shell = DBUSMENU_MENUITEM(data);
- if (mi != NULL) {
- dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_ICON_NAME, DBUSMENU_MENUITEM_ICON_NAME_BLANK);
- dbusmenu_menuitem_child_append(shell, mi);
- }
- resort_menu(shell);
- return;
-}
-
-/* Server shortcut has been removed */
-static void
-server_shortcut_removed (AppMenuItem * appitem, DbusmenuMenuitem * mi, gpointer data)
+service_shutdown (IndicatorService * service, gpointer user_data)
{
- g_debug("Application Shortcut removed: %s", mi != NULL ? dbusmenu_menuitem_property_get(mi, DBUSMENU_MENUITEM_PROP_LABEL) : "none");
- DbusmenuMenuitem * shell = DBUSMENU_MENUITEM(data);
- dbusmenu_menuitem_child_delete(shell, mi);
- return;
-}
+ GMainLoop *mainloop = user_data;
-/* The name of a server has changed, we probably
- need to reorder the menu to keep it in alphabetical
- order. This happens often after we read the destkop
- file from disk. */
-static void
-server_name_changed (AppMenuItem * appitem, gchar * name, gpointer data)
-{
- serverList = g_list_sort(serverList, serverList_sort);
- check_eclipses(appitem);
- resort_menu(DBUSMENU_MENUITEM(data));
- return;
+ g_warning("Shutting down service!");
+ g_main_loop_quit(mainloop);
}
-/* If the count on the server changes, we need to know
- whether that should be grabbing attention or not. If
- it is, we need to reevaluate whether the whole thing
- should be grabbing attention or not. */
static void
-server_count_changed (AppMenuItem * appitem, guint count, gpointer data)
+app_section_remove_attention (gpointer key,
+ gpointer value,
+ gpointer user_data)
{
- serverList_t * slt = (serverList_t *)data;
- slt->count = count;
-
- if (count == 0 && slt->attention) {
- /* Regen based on indicators if the count isn't going to cause it. */
- server_attention(slt);
- /* If we're dropping let's see if we're the last. */
- if (!slt->attention) {
- check_attention();
- }
- }
-
- if (count != 0 && !slt->attention) {
- slt->attention = TRUE;
- /* Let's tell everyone about us! */
- message_service_dbus_set_attention(dbus_interface, TRUE);
- }
-
- return;
+ AppSection *section = value;
+ app_section_clear_draws_attention (section);
}
-/* Respond to the IM entrie's time changing
- which results in it needing to resort the list
- and rebuild the menu to match. */
static void
-im_time_changed (ImMenuItem * imitem, glong seconds, gpointer data)
+clear_action_activate (GSimpleAction *simple,
+ GVariant *param,
+ gpointer user_data)
{
- serverList_t * sl = (serverList_t *)data;
- sl->imList = g_list_sort(sl->imList, imList_sort);
- resort_menu(root_menuitem);
- return;
+ g_hash_table_foreach (applications, app_section_remove_attention, NULL);
}
-/* The IM entrie's request for attention has changed
- so we need to pass that up the stack. */
static void
-im_attention_changed (ImMenuItem * imitem, gboolean requestit, gpointer data)
+radio_item_activate (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
- serverList_t * sl = (serverList_t *)data;
-
- if (requestit) {
- sl->attention = TRUE;
- message_service_dbus_set_attention(dbus_interface, TRUE);
- } else {
- server_attention(sl);
- if (!sl->attention) {
- check_attention();
- }
- }
-
- return;
+ g_action_change_state (G_ACTION (action), parameter);
}
-/* Run when a server is removed from the indicator bus. It figures
- out if we have it somewhere, and if so then we dump it out and
- clean up all of it's entries. */
-static void
-server_removed (IndicateListener * listener, IndicateListenerServer * server, gchar * type, gpointer data)
+static gboolean
+g_action_state_equal (GAction *action,
+ GVariant *value)
{
- /* Look for the server */
- g_debug("Removing server: %s", INDICATE_LISTENER_SERVER_DBUS_NAME(server));
- serverList_t slt = {0};
- slt.server = server;
- GList * lookup = g_list_find_custom(serverList, &slt, serverList_equal);
-
- /* If we don't have it, exit */
- if (lookup == NULL) {
- g_debug("\tUnable to find server: %s", INDICATE_LISTENER_SERVER_DBUS_NAME(server));
- return;
- }
-
- serverList_t * sltp = (serverList_t *)lookup->data;
-
- /* Removing indicators from this server */
- while (sltp->imList) {
- imList_t * imitem = (imList_t *)sltp->imList->data;
- indicator_removed(listener, server, imitem->indicator, data);
- }
+ GVariant *state;
+ gboolean eq;
- /* Remove from the server list */
- serverList = g_list_remove(serverList, sltp);
-
- /* Remove launchers this could be eclipsing */
- remove_eclipses(sltp->menuitem);
-
- /* If there is a menu item, let's get rid of it. */
- if (sltp->menuitem != NULL) {
- /* If there are shortcuts remove them */
- GList * shortcuts = app_menu_item_get_items(sltp->menuitem);
- while (shortcuts != NULL) {
- g_debug("\tRemoving shortcut: %s", dbusmenu_menuitem_property_get(DBUSMENU_MENUITEM(shortcuts->data), DBUSMENU_MENUITEM_PROP_LABEL));
- dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(shortcuts->data), DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
- dbusmenu_menuitem_child_delete(DBUSMENU_MENUITEM(data), DBUSMENU_MENUITEM(shortcuts->data));
- shortcuts = g_list_next(shortcuts);
- }
-
- g_debug("\tRemoving item");
- dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(sltp->menuitem), DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
- dbusmenu_menuitem_child_delete(DBUSMENU_MENUITEM(data), DBUSMENU_MENUITEM(sltp->menuitem));
- g_object_unref(G_OBJECT(sltp->menuitem));
- } else {
- g_debug("\tNo menuitem");
- }
-
- /* If there is a separator, let's get rid of it. */
- if (sltp->separator != NULL) {
- g_debug("\tRemoving separator");
- dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(sltp->separator), DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
- dbusmenu_menuitem_child_delete(DBUSMENU_MENUITEM(data), DBUSMENU_MENUITEM(sltp->separator));
- g_object_unref(G_OBJECT(sltp->separator));
- } else {
- g_debug("\tNo separator");
- }
-
- if (sltp->attention) {
- /* Check to see if this was the server causing the menu item to
- be lit up. */
- check_attention();
- }
+ state = g_action_get_state (action);
+ g_return_val_if_fail (state != NULL, FALSE);
- g_free(sltp);
+ eq = g_variant_equal (state, value);
- check_hidden();
-
- return;
+ g_variant_unref (state);
+ return eq;
}
-typedef struct _menushell_location menushell_location_t;
-struct _menushell_location {
- const IndicateListenerServer * server;
- gint position;
- gboolean found;
-};
-
static void
-menushell_foreach_cb (DbusmenuMenuitem * data_mi, gpointer data_ms) {
- menushell_location_t * msl = (menushell_location_t *)data_ms;
-
- if (msl->found) return;
-
- if (!IS_APP_MENU_ITEM(data_mi)) {
- msl->position++;
- return;
- }
-
- AppMenuItem * appmenu = APP_MENU_ITEM(data_mi);
- if (!g_strcmp0(INDICATE_LISTENER_SERVER_DBUS_NAME((IndicateListenerServer*)msl->server), INDICATE_LISTENER_SERVER_DBUS_NAME(app_menu_item_get_server(appmenu)))) {
- msl->found = TRUE;
- /* Return a position at the end of our shortcuts */
- msl->position += g_list_length(app_menu_item_get_items(appmenu));
- } else {
- msl->position++;
- }
-
- return;
-}
-
-static void
-check_hidden (void)
+change_status_action (GSimpleAction *action,
+ GVariant *value,
+ gpointer user_data)
{
- g_debug("Checking Hidden...");
- gboolean hide = FALSE;
- if (launcherList_count() == 0) {
- g_debug("\tZero Launchers");
- /* If we don't have visible launchers we need to look more */
- if (g_list_length(serverList) == 0) {
- g_debug("\tZero Applications");
- hide = TRUE;
- }
- }
+ const gchar *status;
- message_service_dbus_set_icon(dbus_interface, hide);
- return;
-}
+ g_variant_get (value, "&s", &status);
-/* This function takes care of putting the menu in the right order.
- It basically it rebuilds the order by looking through all the
- applications and launchers and puts them in the right place. The
- menu functions will handle the cases where they don't move so this
- is a good way to ensure everything is right. */
-static void
-resort_menu (DbusmenuMenuitem * menushell)
-{
- guint position = 0;
- GList * serverentry;
- GList * launcherentry = launcherList;
-
- g_debug("Reordering Menu:");
-
- if (DBUSMENU_IS_MENUITEM(status_separator)) {
- position = dbusmenu_menuitem_get_position(status_separator, root_menuitem) + 1;
- g_debug("\tPriming with location of status separator: %d", position);
- }
+ g_return_if_fail (g_str_equal (status, "available") ||
+ g_str_equal (status, "away")||
+ g_str_equal (status, "busy") ||
+ g_str_equal (status, "invisible") ||
+ g_str_equal (status, "offline"));
- for (serverentry = serverList; serverentry != NULL; serverentry = serverentry->next) {
- serverList_t * si = (serverList_t *)serverentry->data;
-
- /* Looking to see if there are any launchers we need to insert
- into the menu structure. We put as many as we need to. */
- if (launcherentry != NULL) {
- launcherList_t * li = (launcherList_t *)launcherentry->data;
- while (launcherentry != NULL && g_strcmp0(launcher_menu_item_get_name(li->menuitem), app_menu_item_get_name(si->menuitem)) < 0) {
- /* Putting the launcher item in */
- g_debug("\tMoving launcher '%s' to position %d", launcher_menu_item_get_name(li->menuitem), position);
- dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(li->menuitem), position);
- position++;
-
- /* Inserting the shortcuts from the launcher */
- GList * shortcuts = launcher_menu_item_get_items(li->menuitem);
- while (shortcuts != NULL) {
- g_debug("\t\tMoving shortcut to position %d", position);
- dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(shortcuts->data), position);
- position++;
- shortcuts = g_list_next(shortcuts);
- }
-
- /* Putting the launcher separator in */
- g_debug("\tMoving launcher separator to position %d", position);
- dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(li->separator), position);
- if (!launcher_menu_item_get_eclipsed(li->menuitem)) {
- /* Only clear the visiblity if we're not eclipsed */
- dbusmenu_menuitem_property_set_bool(li->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
- }
- position++;
-
- launcherentry = launcherentry->next;
- if (launcherentry != NULL) {
- li = (launcherList_t *)launcherentry->data;
- }
- }
- }
-
- /* Putting the app menu item in */
- if (si->menuitem != NULL) {
- g_debug("\tMoving app %s to position %d", INDICATE_LISTENER_SERVER_DBUS_NAME(si->server), position);
- dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(si->menuitem), position);
- position++;
-
- /* Inserting the shortcuts from the launcher */
- GList * shortcuts = app_menu_item_get_items(si->menuitem);
- while (shortcuts != NULL) {
- g_debug("\t\tMoving shortcut to position %d", position);
- dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(shortcuts->data), position);
- position++;
- shortcuts = g_list_next(shortcuts);
- }
- }
-
- /* Putting all the indicators that are related to this application
- after it. */
- GList * imentry;
- for (imentry = si->imList; imentry != NULL; imentry = imentry->next) {
- imList_t * imi = (imList_t *)imentry->data;
-
- if (imi->menuitem != NULL) {
- g_debug("\tMoving indicator on %s id %d to position %d", INDICATE_LISTENER_SERVER_DBUS_NAME(imi->server), INDICATE_LISTENER_INDICATOR_ID(imi->indicator), position);
-
- if (si->menuitem == NULL || !dbusmenu_menuitem_property_get_bool(DBUSMENU_MENUITEM(si->menuitem), DBUSMENU_MENUITEM_PROP_VISIBLE)) {
- dbusmenu_menuitem_property_set_bool(imi->menuitem, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
- } else {
- dbusmenu_menuitem_property_set_bool(imi->menuitem, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
- }
-
- dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(imi->menuitem), position);
- position++;
- }
- }
-
- /* Lastly putting the separator in */
- if (si->separator != NULL) {
- g_debug("\tMoving app %s separator to position %d", INDICATE_LISTENER_SERVER_DBUS_NAME(si->server), position);
-
- if (si->menuitem == NULL || !dbusmenu_menuitem_property_get_bool(DBUSMENU_MENUITEM(si->menuitem), DBUSMENU_MENUITEM_PROP_VISIBLE)) {
- dbusmenu_menuitem_property_set_bool(si->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
- /* Note, this isn't the last if we can't see it */
- } else {
- dbusmenu_menuitem_property_set_bool(si->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
- }
-
- dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(si->separator), position);
- position++;
- }
+ if (!g_action_state_equal (G_ACTION (action), value)) {
+ g_message ("%s", status);
+ g_simple_action_set_state (action, value);
+ indicator_messages_service_emit_status_changed (messages_service, status);
}
-
- /* Put any leftover launchers in at the end of the list. */
- while (launcherentry != NULL) {
- launcherList_t * li = (launcherList_t *)launcherentry->data;
-
- /* Putting the launcher in */
- g_debug("\tMoving launcher '%s' to position %d", launcher_menu_item_get_name(li->menuitem), position);
- dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(li->menuitem), position);
- position++;
-
- /* Inserting the shortcuts from the launcher */
- GList * shortcuts = launcher_menu_item_get_items(li->menuitem);
- while (shortcuts != NULL) {
- g_debug("\t\tMoving shortcut to position %d", position);
- dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(shortcuts->data), position);
- position++;
- shortcuts = g_list_next(shortcuts);
- }
-
- /* Putting the launcher separator in */
- g_debug("\tMoving launcher separator to position %d", position);
- dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(li->separator), position);
- if (!launcher_menu_item_get_eclipsed(li->menuitem)) {
- /* Only clear the visiblity if we're not eclipsed */
- dbusmenu_menuitem_property_set_bool(li->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
- }
- position++;
-
- launcherentry = launcherentry->next;
- }
-
- if (clear_attention != NULL) {
- dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), clear_attention, position);
- position++; /* Not needed, but reduce bugs on code tacked on here, compiler will remove */
- }
-
- return;
}
-/* Responding to a new indicator showing up on the bus. We
- need to create a menuitem for it and start populating the
- internal structures to track it. */
static void
-indicator_added (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gpointer data)
-{
- DbusmenuMenuitem * menushell = DBUSMENU_MENUITEM(data);
- if (menushell == NULL) {
- g_error("Data in callback is not a menushell");
+register_application (IndicatorMessagesService *service,
+ GDBusMethodInvocation *invocation,
+ const gchar *desktop_id,
+ const gchar *menu_path,
+ gpointer user_data)
+{
+ AppSection *section;
+ GDBusConnection *bus;
+ const gchar *sender;
+
+ section = add_application (desktop_id);
+ if (!section)
return;
- }
- imList_t * listItem = g_new0(imList_t, 1);
- listItem->server = server;
- listItem->indicator = indicator;
-
- /* Building the IM Menu Item which is a subclass
- of DBus Menuitem */
- ImMenuItem * menuitem = im_menu_item_new(listener, server, indicator);
- listItem->menuitem = DBUSMENU_MENUITEM(menuitem);
-
- /* Looking for a server entry to attach this indicator
- to. If we can't find one then we have to build one
- and attach the indicator to it. */
- serverList_t sl_item_local = {0};
- serverList_t * sl_item = NULL;
- sl_item_local.server = server;
- GList * serverentry = g_list_find_custom(serverList, &sl_item_local, serverList_equal);
-
- if (serverentry == NULL) {
- /* This sucks, we got an indicator before the server. I guess
- that's the joy of being asynchronous */
- sl_item = g_new0(serverList_t, 1);
- sl_item->server = server;
- sl_item->menuitem = NULL;
- sl_item->imList = NULL;
- sl_item->attention = FALSE;
- sl_item->count = 0;
- sl_item->separator = NULL;
-
- serverList = g_list_insert_sorted(serverList, sl_item, serverList_sort);
- } else {
- sl_item = (serverList_t *)serverentry->data;
- }
+ bus = g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (service));
+ sender = g_dbus_method_invocation_get_sender (invocation);
- /* Added a this entry into the IM list */
- sl_item->imList = g_list_insert_sorted(sl_item->imList, listItem, imList_sort);
- listItem->timechange_cb = g_signal_connect(G_OBJECT(menuitem), IM_MENU_ITEM_SIGNAL_TIME_CHANGED, G_CALLBACK(im_time_changed), sl_item);
- listItem->attentionchange_cb = g_signal_connect(G_OBJECT(menuitem), IM_MENU_ITEM_SIGNAL_ATTENTION_CHANGED, G_CALLBACK(im_attention_changed), sl_item);
-
- /* Check the length of the list. If we've got more inidactors
- than we allow. Well. Someone's gotta pay. Sorry. I didn't
- want to do this, but you did it to yourself. */
- if (g_list_length(sl_item->imList) > MAX_NUMBER_OF_INDICATORS) {
- GList * indicatoritem;
- gint count;
- for (indicatoritem = sl_item->imList, count = 0; indicatoritem != NULL; indicatoritem = g_list_next(indicatoritem), count++) {
- imList_t * im = (imList_t *)indicatoritem->data;
- im_menu_item_show(IM_MENU_ITEM(im->menuitem), count < MAX_NUMBER_OF_INDICATORS);
- }
- }
+ app_section_set_object_path (section, bus, sender, menu_path);
+ g_settings_strv_append_unique (settings, "applications", desktop_id);
- /* Placing the item into the shell. Look to see if
- we can find our server and slip in there. Otherwise
- we'll just append. */
- menushell_location_t msl;
- msl.found = FALSE;
- msl.position = 0;
- msl.server = server;
-
- dbusmenu_menuitem_foreach(DBUSMENU_MENUITEM(menushell), menushell_foreach_cb, &msl);
- if (msl.found) {
- dbusmenu_menuitem_child_add_position(menushell, DBUSMENU_MENUITEM(menuitem), msl.position);
- } else {
- g_warning("Unable to find server menu item");
- dbusmenu_menuitem_child_append(menushell, DBUSMENU_MENUITEM(menuitem));
- resort_menu (root_menuitem);
- }
-
- return;
+ indicator_messages_service_complete_register_application (service, invocation);
}
-/* Process and indicator getting removed from the system. We
- first need to ensure that it's one of ours and figure out
- where we put it. When we find all that out we can go through
- the process of removing the effect it had on the system. */
static void
-indicator_removed (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gpointer data)
+unregister_application (IndicatorMessagesService *service,
+ GDBusMethodInvocation *invocation,
+ const gchar *desktop_id,
+ gpointer user_data)
{
- g_debug("Removing %s %d", INDICATE_LISTENER_SERVER_DBUS_NAME(server), INDICATE_LISTENER_INDICATOR_ID(indicator));
-
- gboolean removed = FALSE;
+ remove_application (desktop_id);
+ g_settings_strv_remove (settings, "applications", desktop_id);
- /* Find the server that was related to this item */
- serverList_t sl_item_local = {0};
- serverList_t * sl_item = NULL;
- sl_item_local.server = server;
- GList * serverentry = g_list_find_custom(serverList, &sl_item_local, serverList_equal);
- if (serverentry == NULL) {
- /* We didn't care about that server */
- return;
- }
- sl_item = (serverList_t *)serverentry->data;
-
- /* Look in the IM Hash Table */
- imList_t listData = {0};
- listData.server = server;
- listData.indicator = indicator;
-
- GList * listItem = g_list_find_custom(sl_item->imList, &listData, imList_equal);
- DbusmenuMenuitem * menuitem = NULL;
- imList_t * ilt = NULL;
- if (listItem != NULL) {
- ilt = (imList_t *)listItem->data;
- menuitem = ilt->menuitem;
- }
-
- /* If we found a menu item and an imList_t item then
- we can go ahead and remove it. Otherwise we can
- skip this and exit. */
- if (!removed && menuitem != NULL) {
- sl_item->imList = g_list_remove(sl_item->imList, ilt);
- g_signal_handler_disconnect(menuitem, ilt->timechange_cb);
- g_signal_handler_disconnect(menuitem, ilt->attentionchange_cb);
- g_free(ilt);
-
- if (im_menu_item_get_attention(IM_MENU_ITEM(menuitem)) && im_menu_item_shown(IM_MENU_ITEM(menuitem))) {
- /* If the removed indicator menu item was asking for
- attention we need to see if this server should still
- be asking for attention. */
- server_attention(sl_item);
- /* If the server is no longer asking for attention then
- we need to check if the whole system should be. */
- if (!sl_item->attention) {
- check_attention();
- }
- }
-
- if (im_menu_item_shown(IM_MENU_ITEM(menuitem)) && g_list_length(sl_item->imList) >= MAX_NUMBER_OF_INDICATORS) {
- /* In this case we need to show a different indicator
- becasue a shown one has left. But we're going to be
- easy and set all the values. */
- GList * indicatoritem;
- gint count;
- for (indicatoritem = sl_item->imList, count = 0; indicatoritem != NULL; indicatoritem = g_list_next(indicatoritem), count++) {
- imList_t * im = (imList_t *)indicatoritem->data;
- im_menu_item_show(IM_MENU_ITEM(im->menuitem), count < MAX_NUMBER_OF_INDICATORS);
- }
- }
-
- /* Hide the item immediately, and then remove it
- which might take a little longer. */
- dbusmenu_menuitem_property_set_bool(menuitem, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
- dbusmenu_menuitem_child_delete(DBUSMENU_MENUITEM(data), menuitem);
- removed = TRUE;
- }
-
- if (!removed) {
- g_warning("We were asked to remove %s %d but we didn't.", INDICATE_LISTENER_SERVER_DBUS_NAME(server), INDICATE_LISTENER_INDICATOR_ID(indicator));
- }
-
- return;
+ indicator_messages_service_complete_unregister_application (service, invocation);
}
static void
-app_dir_changed (GFileMonitor * monitor, GFile * file, GFile * other_file, GFileMonitorEvent event_type, gpointer user_data)
+set_status (IndicatorMessagesService *service,
+ GDBusMethodInvocation *invocation,
+ const gchar *status_str,
+ gpointer user_data)
{
- gchar * directory = (gchar *)user_data;
- g_debug("Application directory changed: %s", directory);
-
- switch (event_type) {
- case G_FILE_MONITOR_EVENT_DELETED: {
- gchar * path = g_file_get_path(file);
- g_debug("\tDelete: %s", path);
- g_idle_add(destroy_launcher, path);
- break;
- }
- case G_FILE_MONITOR_EVENT_CREATED: {
- gchar * path = g_file_get_path(file);
- g_debug("\tCreate: %s", path);
- if (g_str_has_suffix(path, "keyfile")) {
- g_idle_add(build_launcher_keyfile, path);
- } else {
- g_idle_add(build_launcher, path);
- }
- break;
- }
- default:
- break;
- }
+ GAction *status;
- return;
-}
+ status = g_simple_action_group_lookup (actions, "status");
+ g_return_if_fail (status != NULL);
-/* Check to see if a new desktop file causes
- any of the launchers to be eclipsed by a running
- process */
-static void
-check_eclipses (AppMenuItem * ai)
-{
- g_debug("Checking eclipsing");
- const gchar * aidesktop = app_menu_item_get_desktop(ai);
- if (aidesktop == NULL) return;
- g_debug("\tApp desktop: %s", aidesktop);
-
- GList * llitem;
- for (llitem = launcherList; llitem != NULL; llitem = llitem->next) {
- launcherList_t * ll = (launcherList_t *)llitem->data;
- const gchar * lidesktop = launcher_menu_item_get_desktop(ll->menuitem);
- g_debug("\tLauncher desktop: %s", lidesktop);
-
- if (!g_strcmp0(aidesktop, lidesktop)) {
- launcher_menu_item_set_eclipsed(ll->menuitem, TRUE);
- dbusmenu_menuitem_property_set_bool(ll->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
- break;
- }
- }
+ g_action_change_state (status, g_variant_new_string (status_str));
- return;
+ indicator_messages_service_complete_set_status (service, invocation);
}
-/* Remove any eclipses that might have been caused
- by this app item that is now retiring */
-static void
-remove_eclipses (AppMenuItem * ai)
+static GSimpleActionGroup *
+create_action_group (void)
{
- const gchar * aidesktop = app_menu_item_get_desktop(ai);
- if (aidesktop == NULL) return;
-
- if (blacklist_check(aidesktop)) return;
-
- GList * llitem;
- for (llitem = launcherList; llitem != NULL; llitem = llitem->next) {
- launcherList_t * ll = (launcherList_t *)llitem->data;
- const gchar * lidesktop = launcher_menu_item_get_desktop(ll->menuitem);
-
- if (!g_strcmp0(aidesktop, lidesktop)) {
- launcher_menu_item_set_eclipsed(ll->menuitem, FALSE);
- dbusmenu_menuitem_property_set_bool(ll->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
- break;
- }
- }
+ GSimpleActionGroup *actions;
+ GSimpleAction *messages;
+ GSimpleAction *clear;
+ GSimpleAction *status;
- return;
-}
-
-/* Remove a launcher from the system. We need to figure
- out what it's up to! */
-static gboolean
-destroy_launcher (gpointer data)
-{
- gchar * appdirentry = (gchar *)data;
-
- GList * listitem;
- GList * direntry;
- launcherList_t * li;
- gchar * appdir;
-
- for (listitem = launcherList; listitem != NULL; listitem = listitem->next) {
- li = (launcherList_t *)listitem->data;
- for (direntry = li->appdiritems; direntry != NULL; direntry = direntry->next) {
- appdir = (gchar *)direntry->data;
- if (!g_strcmp0(appdir, appdirentry)) {
- break;
- }
- }
-
- if (direntry != NULL) {
- break;
- }
- }
+ actions = g_simple_action_group_new ();
- if (listitem == NULL) {
- g_warning("Removed '%s' by the way of it not seeming to exist anywhere.", appdirentry);
- return FALSE;
- }
+ /* state of the messages action mirrors "draws-attention" */
+ messages = g_simple_action_new_stateful ("messages", G_VARIANT_TYPE ("b"),
+ g_variant_new_boolean (FALSE));
- if (g_list_length(li->appdiritems) > 1) {
- /* Just remove this item, and we can move on */
- g_debug("Just removing file entry: %s", appdir);
- li->appdiritems = g_list_remove(li->appdiritems, appdir);
- g_free(appdir);
- return FALSE;
- }
+ status = g_simple_action_new_stateful ("status", G_VARIANT_TYPE ("s"),
+ g_variant_new ("s", "offline"));
+ g_signal_connect (status, "activate", G_CALLBACK (radio_item_activate), NULL);
+ g_signal_connect (status, "change-state", G_CALLBACK (change_status_action), NULL);
- /* Full Destroy */
- g_free(appdir);
- g_list_free(li->appdiritems);
-
- if (li->menuitem != NULL) {
- /* If there are shortcuts remove them */
- GList * shortcuts = launcher_menu_item_get_items(li->menuitem);
- while (shortcuts != NULL) {
- dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(shortcuts->data), DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
- dbusmenu_menuitem_child_delete(DBUSMENU_MENUITEM(data), DBUSMENU_MENUITEM(shortcuts->data));
- shortcuts = g_list_next(shortcuts);
- }
-
- dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(li->menuitem), DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
- dbusmenu_menuitem_child_delete(root_menuitem, DBUSMENU_MENUITEM(li->menuitem));
- g_object_unref(G_OBJECT(li->menuitem));
- li->menuitem = NULL;
- }
+ clear = g_simple_action_new ("clear", NULL);
+ g_simple_action_set_enabled (clear, FALSE);
+ g_signal_connect (clear, "activate", G_CALLBACK (clear_action_activate), NULL);
- launcherList = g_list_remove(launcherList, li);
- g_free(li);
+ g_simple_action_group_insert (actions, G_ACTION (messages));
+ g_simple_action_group_insert (actions, G_ACTION (status));
+ g_simple_action_group_insert (actions, G_ACTION (clear));
- return FALSE;
+ return actions;
}
-/* This function turns a specific file into a menu
- item and registers it appropriately with everyone */
-static gboolean
-build_launcher (gpointer data)
+static GMenuModel *
+create_status_section (void)
{
- /* Read the file get the data */
- gchar * path = (gchar *)data;
- g_debug("\tpath: %s", path);
- gchar * desktop = NULL;
-
- g_file_get_contents(path, &desktop, NULL, NULL);
-
- if (desktop == NULL) {
- g_free(path);
- return FALSE;
- }
+ GMenu *menu;
- gchar * trimdesktop = pango_trim_string(desktop);
- g_free(desktop);
- g_debug("\tcontents: %s", trimdesktop);
+ menu = g_menu_new ();
+ g_menu_append_with_icon_name (menu, _("Available"), "user-available", "status::available");
+ g_menu_append_with_icon_name (menu, _("Away"), "user-away", "status::away");
+ g_menu_append_with_icon_name (menu, _("Busy"), "user-busy", "status::busy");
+ g_menu_append_with_icon_name (menu, _("Invisible"), "user-invisible", "status::invisible");
+ g_menu_append_with_icon_name (menu, _("Offline"), "user-offline", "status::offline");
- build_launcher_core(trimdesktop);
- g_free(trimdesktop);
- g_free(path);
- return FALSE;
-}
-
-/* Use a key file to find the desktop file. */
-static gboolean
-build_launcher_keyfile (gpointer data)
-{
- gchar * path = (gchar *)data;
- gchar * desktop = desktop_file_from_keyfile (path);
- if (desktop != NULL) {
- build_launcher_core(desktop);
- g_free(desktop);
- }
- g_free(path);
- return FALSE;
+ return G_MENU_MODEL (menu);
}
-/* The core action of dealing with a desktop file that should
- be a launcher */
static void
-build_launcher_core (const gchar * desktop)
+got_bus (GObject *object,
+ GAsyncResult * res,
+ gpointer user_data)
{
- /* Check to see if we already have a launcher */
- GList * listitem;
- for (listitem = launcherList; listitem != NULL; listitem = listitem->next) {
- launcherList_t * li = (launcherList_t *)listitem->data;
- if (!g_strcmp0(launcher_menu_item_get_desktop(li->menuitem), desktop)) {
- break;
- }
- }
-
- if (listitem == NULL) {
- /* If not */
- /* Build the item */
- launcherList_t * ll = g_new0(launcherList_t, 1);
- ll->menuitem = launcher_menu_item_new(desktop);
- ll->appdiritems = g_list_append(NULL, g_strdup(desktop));
-
- /* Build a separator */
- ll->separator = dbusmenu_menuitem_new();
- dbusmenu_menuitem_property_set(ll->separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR);
-
- /* Add it to the list */
- launcherList = g_list_insert_sorted(launcherList, ll, launcherList_sort);
-
- /* Add it to the menu */
- dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(ll->menuitem), DBUSMENU_MENUITEM_PROP_TYPE, APPLICATION_MENUITEM_TYPE);
- dbusmenu_menuitem_child_append(root_menuitem, DBUSMENU_MENUITEM(ll->menuitem));
- GList * shortcuts = launcher_menu_item_get_items(ll->menuitem);
- while (shortcuts != NULL) {
- dbusmenu_menuitem_child_append(root_menuitem, DBUSMENU_MENUITEM(shortcuts->data));
- shortcuts = g_list_next(shortcuts);
- }
- dbusmenu_menuitem_child_append(root_menuitem, DBUSMENU_MENUITEM(ll->separator));
-
- /* If we're in the black list or we've gotten eclipsed
- by something else, hide the item and the separator. */
- if (blacklist_check(launcher_menu_item_get_desktop(ll->menuitem)) ||
- launcher_menu_item_get_eclipsed(ll->menuitem)) {
- launcher_menu_item_set_eclipsed(ll->menuitem, TRUE);
- dbusmenu_menuitem_property_set_bool(ll->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE);
- }
-
- /* Check to see if any of the current applications should
- be eclipsing us. */
- GList * server;
- for (server = serverList; server != NULL; server = g_list_next(server)) {
- serverList_t * slt = (serverList_t *)server->data;
- check_eclipses(slt->menuitem);
- }
-
- resort_menu(root_menuitem);
- check_hidden();
- } else {
- /* If so add ourselves */
- launcherList_t * ll = (launcherList_t *)listitem->data;
- ll->appdiritems = g_list_append(ll->appdiritems, g_strdup(desktop));
- }
-
- return;
-}
+ GDBusConnection *bus;
+ GError *error = NULL;
-/* 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 * directory = (gchar *)data;
-
- if (!g_file_test(directory, G_FILE_TEST_IS_DIR)) {
- return FALSE;
+ bus = g_bus_get_finish (res, &error);
+ if (!bus) {
+ g_warning ("unable to connect to the session bus: %s", error->message);
+ g_error_free (error);
+ return;
}
- GFile * filedir = g_file_new_for_path(directory);
- GFileMonitor * dirmon = g_file_monitor_directory(filedir, G_FILE_MONITOR_NONE, NULL, NULL);
- if (dirmon != NULL) {
- g_signal_connect(G_OBJECT(dirmon), "changed", G_CALLBACK(app_dir_changed), directory);
+ g_dbus_connection_export_action_group (bus, INDICATOR_MESSAGES_DBUS_OBJECT,
+ G_ACTION_GROUP (action_muxer), &error);
+ if (error) {
+ g_warning ("unable to export action group on dbus: %s", error->message);
+ g_error_free (error);
+ return;
}
- GError * error = NULL;
- GDir * dir = g_dir_open(directory, 0, &error);
- if (dir == NULL) {
- g_warning("Unable to open system apps directory: %s", error->message);
- g_error_free(error);
- return FALSE;
+ g_dbus_connection_export_menu_model (bus, INDICATOR_MESSAGES_DBUS_OBJECT,
+ G_MENU_MODEL (toplevel_menu), &error);
+ if (error) {
+ g_warning ("unable to export menu on dbus: %s", error->message);
+ g_error_free (error);
+ return;
}
- const gchar * filename = NULL;
- while ((filename = g_dir_read_name(dir)) != NULL) {
- g_debug("Found file: %s", filename);
- gchar * path = g_build_filename(directory, filename, NULL);
- if (g_str_has_suffix(path, "keyfile")) {
- g_idle_add(build_launcher_keyfile, path);
- } else {
- g_idle_add(build_launcher, path);
- }
+ g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (messages_service),
+ bus, INDICATOR_MESSAGES_DBUS_SERVICE_OBJECT,
+ &error);
+ if (error) {
+ g_warning ("unable to export messages service on dbus: %s", error->message);
+ g_error_free (error);
+ return;
}
- g_dir_close(dir);
- launcherList = g_list_sort(launcherList, launcherList_sort);
- return FALSE;
-}
-
-static void
-service_shutdown (IndicatorService * service, gpointer user_data)
-{
- g_warning("Shutting down service!");
- g_main_loop_quit(mainloop);
- return;
+ g_object_unref (bus);
}
-/* Respond to changing status by updating the icon that
- is on the panel */
-static void
-status_update_callback (void)
-{
- return;
-}
-
-/* The clear attention item has been clicked on, what to do? */
-static void
-clear_attention_activate (DbusmenuMenuitem * mi, guint timestamp, MessageServiceDbus * dbus)
-{
- message_service_dbus_set_attention(dbus, FALSE);
- return;
-}
-
-/* Handle an update of the active state to ensure that we're
- only enabled when we could do something. */
-static void
-clear_attention_handler (MessageServiceDbus * msd, gboolean attention, DbusmenuMenuitem * clearitem)
-{
- dbusmenu_menuitem_property_set_bool(clearitem, DBUSMENU_MENUITEM_PROP_ENABLED, attention);
- return;
-}
-
-/* Oh, if you don't know what main() is for
- we really shouldn't be talking. */
int
main (int argc, char ** argv)
{
+ GMainLoop * mainloop = NULL;
+ IndicatorService * service = NULL;
+ GMenuItem *header;
+ GIcon *icon;
+ gchar *iconstr;
+
/* Glib init */
g_type_init();
+ mainloop = g_main_loop_new (NULL, FALSE);
+
/* Create the Indicator Service interface */
service = indicator_service_new_version(INDICATOR_MESSAGES_DBUS_NAME, 1);
- g_signal_connect(service, INDICATOR_SERVICE_SIGNAL_SHUTDOWN, G_CALLBACK(service_shutdown), NULL);
+ g_signal_connect(service, INDICATOR_SERVICE_SIGNAL_SHUTDOWN, G_CALLBACK(service_shutdown), mainloop);
/* Setting up i18n and gettext. Apparently, we need
all of these. */
@@ -1533,58 +482,52 @@ main (int argc, char ** argv)
bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
textdomain (GETTEXT_PACKAGE);
- /* Create the Seen DB */
- seen_db_init();
-
/* Bring up the service DBus interface */
- dbus_interface = message_service_dbus_new();
-
- /* Build the base menu */
- root_menuitem = dbusmenu_menuitem_new();
- DbusmenuServer * server = dbusmenu_server_new(INDICATOR_MESSAGES_DBUS_OBJECT);
- dbusmenu_server_set_root(server, root_menuitem);
-
- /* Add status items */
- GList * statusitems = status_items_build(&status_update_callback);
- while (statusitems != NULL) {
- dbusmenu_menuitem_child_append(root_menuitem, DBUSMENU_MENUITEM(statusitems->data));
- statusitems = g_list_next(statusitems);
- }
- status_separator = dbusmenu_menuitem_new();
- dbusmenu_menuitem_property_set(status_separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR);
- dbusmenu_menuitem_child_append(root_menuitem, status_separator);
-
- /* Add in the clear attention item */
- clear_attention = dbusmenu_menuitem_new();
- dbusmenu_menuitem_property_set(clear_attention, DBUSMENU_MENUITEM_PROP_LABEL, _("Clear"));
- dbusmenu_menuitem_child_append(root_menuitem, clear_attention);
- g_signal_connect(G_OBJECT(dbus_interface), MESSAGE_SERVICE_DBUS_SIGNAL_ATTENTION_CHANGED, G_CALLBACK(clear_attention_handler), clear_attention);
- g_signal_connect(G_OBJECT(clear_attention), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(clear_attention_activate), dbus_interface);
-
- /* Start up the libindicate listener */
- listener = indicate_listener_ref_default();
- serverList = NULL;
-
- g_signal_connect(listener, INDICATE_LISTENER_SIGNAL_INDICATOR_ADDED, G_CALLBACK(indicator_added), root_menuitem);
- g_signal_connect(listener, INDICATE_LISTENER_SIGNAL_INDICATOR_REMOVED, G_CALLBACK(indicator_removed), root_menuitem);
- g_signal_connect(listener, INDICATE_LISTENER_SIGNAL_SERVER_ADDED, G_CALLBACK(server_added), root_menuitem);
- g_signal_connect(listener, INDICATE_LISTENER_SIGNAL_SERVER_REMOVED, G_CALLBACK(server_removed), root_menuitem);
-
- /* Find launchers by looking through the config directories
- in the idle loop */
- g_idle_add(blacklist_init, NULL);
- g_idle_add(build_launchers, SYSTEM_APPS_DIR);
- g_idle_add(build_launchers, SYSTEM_APPS_DIR_OLD);
- gchar * userdir = g_build_filename(g_get_user_config_dir(), USER_APPS_DIR, NULL);
- g_idle_add(build_launchers, userdir);
-
- /* Let's run a mainloop */
- mainloop = g_main_loop_new(NULL, FALSE);
+ messages_service = indicator_messages_service_skeleton_new ();
+
+ g_bus_get (G_BUS_TYPE_SESSION, NULL, got_bus, NULL);
+
+ actions = create_action_group ();
+
+ action_muxer = g_action_muxer_new ();
+ g_action_muxer_insert (action_muxer, NULL, G_ACTION_GROUP (actions));
+
+ g_signal_connect (messages_service, "handle-register-application",
+ 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");
+
+ icon = g_themed_icon_new ("indicator-messages");
+ iconstr = g_icon_to_string (icon);
+
+ toplevel_menu = g_menu_new ();
+ header = g_menu_item_new (NULL, "messages");
+ g_menu_item_set_submenu (header, G_MENU_MODEL (menu));
+ g_menu_item_set_attribute (header, "x-canonical-icon", "s", iconstr);
+ g_menu_item_set_attribute (header, "x-canonical-accessible-description", "s", _("Messages"));
+ g_menu_append_item (toplevel_menu, header);
+ g_object_unref (header);
+
+ 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 */
- status_items_cleanup();
- g_free(userdir);
-
+ g_free (iconstr);
+ g_object_unref (icon);
+ g_object_unref (messages_service);
+ g_object_unref (chat_section);
+ g_object_unref (settings);
+ g_hash_table_unref (applications);
return 0;
}
diff --git a/src/messages-service.xml b/src/messages-service.xml
index d79049e..edd47c7 100644
--- a/src/messages-service.xml
+++ b/src/messages-service.xml
@@ -2,21 +2,21 @@
<node name="/">
<interface name="com.canonical.indicator.messages.service">
-<!-- Methods -->
- <method name="AttentionRequested">
- <arg type="b" name="dot" direction="out" />
+ <method name="RegisterApplication">
+ <arg type="s" name="desktop_id" direction="in" />
+ <arg type="o" name="menu_path" direction="in" />
</method>
- <method name="IconShown">
- <arg type="b" name="hidden" direction="out" />
+
+ <method name="UnregisterApplication">
+ <arg type="s" name="desktop_id" direction="in" />
</method>
- <method name="ClearAttention" />
-<!-- Signals -->
- <signal name="AttentionChanged">
- <arg type="b" name="dot" direction="out" />
- </signal>
- <signal name="IconChanged">
- <arg type="b" name="hidden" direction="out" />
+ <method name="SetStatus">
+ <arg type="s" name="status" direction="in" />
+ </method>
+
+ <signal name="StatusChanged">
+ <arg type="s" name="status" direction="in" />
</signal>
</interface>
diff --git a/src/seen-db.c b/src/seen-db.c
deleted file mode 100644
index 57765cc..0000000
--- a/src/seen-db.c
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
-A small database of which desktop files we've seen.
-
-Copyright 2010 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@canonical.com>
-
-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/>.
-*/
-
-#include "seen-db.h"
-
-#define GROUP_NAME "Seen Database"
-#define KEY_NAME "DesktopFiles"
-
-GHashTable * seendb = NULL;
-gchar * filename = NULL;
-gchar * dirname = NULL;
-guint write_process = 0;
-
-/* Build the hashtable and then see if we have a keyfile that
- we can get the history of desktop files we've seen. */
-void
-seen_db_init(void)
-{
- g_return_if_fail(seendb == NULL);
-
- seendb = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
-
- /* Build the filename for the seen database. We're putting
- it in the cache directory because it could get deleted and
- it really wouldn't be a big deal. */
- if (dirname == NULL) {
- dirname = g_build_filename(g_get_user_cache_dir(), "indicators", "messages", NULL);
- }
- if (filename == NULL) {
- filename = g_build_filename(dirname, "seen-db.keyfile", NULL);
- }
-
- if (g_file_test(filename, G_FILE_TEST_EXISTS)) {
- GKeyFile * keyfile = g_key_file_new();
-
- /* Load from file */
- if (!g_key_file_load_from_file(keyfile, filename, G_KEY_FILE_NONE, NULL)) {
- g_key_file_free(keyfile);
- keyfile = NULL;
- }
-
- /* Check for keys */
- if (keyfile != NULL && !g_key_file_has_key(keyfile, GROUP_NAME, KEY_NAME, NULL)) {
- g_warning("Seen DB '%s' does not have key '%s' in group '%s'", filename, KEY_NAME, GROUP_NAME);
- g_key_file_free(keyfile);
- keyfile = NULL;
- }
-
- /* Grab them and put in DB */
- if (keyfile != NULL) {
- gchar ** desktops = g_key_file_get_string_list(keyfile, GROUP_NAME, KEY_NAME, NULL, NULL);
- gint i = 0;
-
- while (desktops[i] != NULL) {
- g_hash_table_insert(seendb,
- g_strdup(desktops[i]),
- GINT_TO_POINTER(TRUE));
- i++;
- }
-
- g_strfreev(desktops);
- }
-
- /* Clean up our file */
- if (keyfile != NULL) {
- g_key_file_free(keyfile);
- }
- }
-
- return;
-}
-
-/* A function to write out the seen database after it's been
- modified for a while. */
-static gboolean
-write_seen_db (gpointer user_data)
-{
- write_process = 0;
-
- /* Build up the key file */
- GKeyFile * keyfile = g_key_file_new();
- GArray * desktops = g_array_new(FALSE, FALSE, sizeof(gchar *));
-
- /* Get the keys from the hashtable and make them
- into an array */
- if (keyfile != NULL) {
- GList * desktop_keys = g_hash_table_get_keys(seendb);
- GList * head = NULL;
-
- for (head = desktop_keys; head != NULL; head = g_list_next(head)) {
- g_array_append_val(desktops, head->data);
- }
-
- g_list_free(desktop_keys);
- }
-
- /* Use the array to dump the strings into the keyfile */
- g_key_file_set_string_list(keyfile,
- GROUP_NAME,
- KEY_NAME,
- (const gchar * const *)desktops->data,
- desktops->len);
- g_array_free(desktops, TRUE);
-
- /* Dump the key file to string */
- gchar * keydump = NULL;
- gsize keydumplen = 0;
- keydump = g_key_file_to_data(keyfile, &keydumplen, NULL);
- g_key_file_free(keyfile);
-
- /* Ensure the directory exists */
- if (g_mkdir_with_parents(dirname, 0700) != 0) {
- g_warning("Unable to make directory: %s", dirname);
- g_free(keydump);
- return FALSE;
- }
-
- /* Dump out the file */
- GError * error = NULL;
- if (!g_file_set_contents(filename, keydump, keydumplen, &error)) {
- g_warning("Unable to write out file '%s': %s", filename, error->message);
- g_error_free(error);
- }
-
- /* Clean up */
- g_free(keydump);
-
- return FALSE;
-}
-
-/* Add a new desktop file to the seen database. Also sets up a timer
- to do the write out. */
-void
-seen_db_add (const gchar * desktop)
-{
- /* If this is a new one, let's set up the timer. If
- there's already one clear it. */
- if (!seen_db_seen(desktop)) {
- if (write_process != 0) {
- g_source_remove(write_process);
- write_process = 0;
- }
-
- write_process = g_timeout_add_seconds(60, write_seen_db, NULL);
- }
-
- g_hash_table_insert(seendb,
- g_strdup(desktop),
- GINT_TO_POINTER(TRUE));
-
- return;
-}
-
-/* Checks to see if a desktop file has been seen. */
-gboolean
-seen_db_seen (const gchar * desktop)
-{
- return GPOINTER_TO_INT(g_hash_table_lookup(seendb, desktop));
-}
diff --git a/src/seen-db.h b/src/seen-db.h
deleted file mode 100644
index a998ff0..0000000
--- a/src/seen-db.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
-A small database of which desktop files we've seen.
-
-Copyright 2010 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@canonical.com>
-
-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/>.
-*/
-
-#ifndef SEEN_DB_H__
-#define SEEN_DB_H__ 1
-
-#include <glib.h>
-
-void seen_db_init(void);
-void seen_db_add (const gchar * desktop);
-gboolean seen_db_seen (const gchar * desktop);
-
-#endif /* SEEN_DB_H__ */
diff --git a/src/status-items.c b/src/status-items.c
deleted file mode 100644
index 70a2ad9..0000000
--- a/src/status-items.c
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
-Code to build and maintain the status adjustment menuitems.
-
-Copyright 2011 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@canonical.com>
-
-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/>.
-*/
-
-#include <glib.h>
-#include <glib/gi18n.h>
-#include <gio/gio.h>
-#include <libdbusmenu-glib/dbusmenu-glib.h>
-
-#include "status-items.h"
-#include "status-provider.h"
-
-static const gchar * status_strings [STATUS_PROVIDER_STATUS_LAST] = {
- /* STATUS_PROVIDER_STATUS_ONLINE, */ N_("Available"),
- /* STATUS_PROVIDER_STATUS_AWAY, */ N_("Away"),
- /* STATUS_PROVIDER_STATUS_DND */ N_("Busy"),
- /* STATUS_PROVIDER_STATUS_INVISIBLE */ N_("Invisible"),
- /* STATUS_PROVIDER_STATUS_OFFLINE, */ N_("Offline"),
- /* STATUS_PROVIDER_STATUS_DISCONNECTED*/ N_("Offline")
-};
-
-static const gchar * status_icons[STATUS_PROVIDER_STATUS_LAST] = {
- /* STATUS_PROVIDER_STATUS_ONLINE, */ "user-available",
- /* STATUS_PROVIDER_STATUS_AWAY, */ "user-away",
- /* STATUS_PROVIDER_STATUS_DND, */ "user-busy",
- /* STATUS_PROVIDER_STATUS_INVISIBLE, */ "user-invisible",
- /* STATUS_PROVIDER_STATUS_OFFLINE */ "user-offline",
- /* STATUS_PROVIDER_STATUS_DISCONNECTED */ "user-offline-panel"
-};
-
-static const gchar * panel_icons[STATUS_PROVIDER_STATUS_LAST] = {
- /* STATUS_PROVIDER_STATUS_ONLINE, */ "indicator-messages-user-available",
- /* STATUS_PROVIDER_STATUS_AWAY, */ "indicator-messages-user-away",
- /* STATUS_PROVIDER_STATUS_DND, */ "indicator-messages-user-busy",
- /* STATUS_PROVIDER_STATUS_INVISIBLE, */ "indicator-messages-user-invisible",
- /* STATUS_PROVIDER_STATUS_OFFLINE */ "indicator-messages-user-offline",
- /* STATUS_PROVIDER_STATUS_DISCONNECTED */ "indicator-messages-user-disconnected"
-};
-
-static const gchar * panel_active_icons[STATUS_PROVIDER_STATUS_LAST] = {
- /* STATUS_PROVIDER_STATUS_ONLINE, */ "indicator-messages-new-user-available",
- /* STATUS_PROVIDER_STATUS_AWAY, */ "indicator-messages-new-user-away",
- /* STATUS_PROVIDER_STATUS_DND, */ "indicator-messages-new-user-busy",
- /* STATUS_PROVIDER_STATUS_INVISIBLE, */ "indicator-messages-new-user-invisible",
- /* STATUS_PROVIDER_STATUS_OFFLINE */ "indicator-messages-new-user-offline",
- /* STATUS_PROVIDER_STATUS_DISCONNECTED */ "indicator-messages-new-user-disconnected"
-};
-
-/* Prototypes */
-static gboolean provider_directory_parse (gpointer dir);
-static gboolean load_status_provider (gpointer dir);
-static void user_status_change (DbusmenuMenuitem * item, guint timestamp, gpointer pstatus);
-
-/* Globals */
-static StatusProviderStatus current_status = STATUS_PROVIDER_STATUS_DISCONNECTED;
-static GList * menuitems = NULL;
-static GList * status_providers = NULL;
-static StatusUpdateFunc update_func = NULL;
-
-/* Build the inital status items and start kicking off the async code
- for handling all the statuses */
-GList *
-status_items_build (StatusUpdateFunc status_update_func)
-{
- int i;
- for (i = STATUS_PROVIDER_STATUS_ONLINE; i < STATUS_PROVIDER_STATUS_DISCONNECTED; i++) {
- DbusmenuMenuitem * item = dbusmenu_menuitem_new();
-
- dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, _(status_strings[i]));
- dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_ICON_NAME, status_icons[i]);
-
- dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE);
- dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_ENABLED, FALSE);
-
- dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE, DBUSMENU_MENUITEM_TOGGLE_RADIO);
- dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED);
-
- g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(user_status_change), GINT_TO_POINTER(i));
-
- menuitems = g_list_append(menuitems, item);
- }
-
- update_func = status_update_func;
-
- const gchar * status_providers_env = g_getenv("INDICATOR_MESSAGES_STATUS_PROVIDER_DIR");
- if (status_providers_env == NULL) {
- g_idle_add(provider_directory_parse, STATUS_PROVIDER_DIR);
- } else {
- g_idle_add(provider_directory_parse, (gpointer)status_providers_env);
- }
-
- return menuitems;
-}
-
-/* Clean up our globals and stop with all this allocation
- of memory */
-void
-status_items_cleanup (void)
-{
- while (status_providers != NULL) {
- StatusProvider * sprovider = STATUS_PROVIDER(status_providers->data);
- g_object_unref(sprovider);
- status_providers = g_list_remove(status_providers, sprovider);
- }
-
- return;
-}
-
-/* Get the icon that should be shown on the panel */
-const gchar *
-status_current_panel_icon (gboolean alert)
-{
- if (alert) {
- return panel_active_icons[current_status];
- } else {
- return panel_icons[current_status];
- }
-}
-
-/* Update status from all the providers */
-static void
-update_status (void)
-{
- StatusProviderStatus status = STATUS_PROVIDER_STATUS_DISCONNECTED;
- GList * provider;
-
- for (provider = status_providers; provider != NULL; provider = g_list_next(provider)) {
- StatusProviderStatus localstatus = status_provider_get_status(STATUS_PROVIDER(provider->data));
-
- if (localstatus < status) {
- status = localstatus;
- }
- }
-
- if (status == current_status) {
- return;
- }
-
- current_status = status;
-
- if (update_func != NULL) {
- update_func();
- }
-
- GList * menu;
- int i;
- for (menu = menuitems, i = 0; menu != NULL && i < STATUS_PROVIDER_STATUS_DISCONNECTED; menu = g_list_next(menu), i++) {
- /* If we're the seleced status or if we're disconnected
- show the user that we're offline */
- if (i == current_status || (current_status == STATUS_PROVIDER_STATUS_DISCONNECTED && i == STATUS_PROVIDER_STATUS_OFFLINE)) {
- dbusmenu_menuitem_property_set_int(DBUSMENU_MENUITEM(menu->data), DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED);
- } else {
- dbusmenu_menuitem_property_set_int(DBUSMENU_MENUITEM(menu->data), DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED);
- }
-
- if (current_status == STATUS_PROVIDER_STATUS_DISCONNECTED) {
- dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(menu->data), DBUSMENU_MENUITEM_PROP_ENABLED, FALSE);
- } else {
- dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(menu->data), DBUSMENU_MENUITEM_PROP_ENABLED, TRUE);
- }
- }
-
- return;
-}
-
-/* Handle the user requesting a status change */
-static void
-user_status_change (DbusmenuMenuitem * item, guint timestamp, gpointer pstatus)
-{
- StatusProviderStatus status = GPOINTER_TO_INT(pstatus);
- GList * provider;
-
- /* Set each provider to this status */
- for (provider = status_providers; provider != NULL; provider = g_list_next(provider)) {
- status_provider_set_status(STATUS_PROVIDER(provider->data), status);
- }
-
- /* See what we really are now */
- update_status();
- return;
-}
-
-/* Start parsing a directory and setting up the entires in the idle loop */
-static gboolean
-provider_directory_parse (gpointer directory)
-{
- const gchar * dirname = (const gchar *)directory;
- g_debug("Looking for status providers in: %s", dirname);
-
- if (!g_file_test(dirname, G_FILE_TEST_EXISTS)) {
- return FALSE;
- }
-
- GDir * dir = g_dir_open(dirname, 0, NULL);
- if (dir == NULL) {
- return FALSE;
- }
-
- const gchar * name;
- while ((name = g_dir_read_name(dir)) != NULL) {
- if (!g_str_has_suffix(name, G_MODULE_SUFFIX)) {
- continue;
- }
-
- gchar * fullname = g_build_filename(dirname, name, NULL);
- g_idle_add(load_status_provider, fullname);
- }
-
- g_dir_close(dir);
-
- return FALSE;
-}
-
-/* Close the module as an idle function so that we know
- it's all cleaned up */
-static gboolean
-module_destroy_in_idle_helper (gpointer data)
-{
- GModule * module = (GModule *)data;
- if (module != NULL) {
- g_debug("Unloading module: %s", g_module_name(module));
- g_module_close(module);
- }
- return FALSE;
-}
-
-/* Set up an idle function to close the module */
-static void
-module_destroy_in_idle (gpointer data)
-{
- g_idle_add_full(G_PRIORITY_LOW, module_destroy_in_idle_helper, data, NULL);
- return;
-}
-
-/* Load a particular status provider */
-static gboolean
-load_status_provider (gpointer dir)
-{
- gchar * provider = dir;
-
- /* load the module */
- GModule * module = NULL;
- if (g_file_test(provider, G_FILE_TEST_EXISTS)) {
- g_debug("Loading status provider: %s", provider);
- module = g_module_open(provider, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
- if (module == NULL) {
- g_warning("Unable to open module: %s", provider);
- }
- }
-
- /* find the status provider's GType */
- GType provider_type = 0;
- if (module != NULL) {
- GType (*type_func) (void);
- if (!g_module_symbol(module, STATUS_PROVIDER_EXPORT_S, (gpointer *)&type_func)) {
- g_warning("Unable to find type symbol in: %s", provider);
- } else {
- provider_type = type_func();
- if (provider_type == 0) {
- g_warning("Unable to create type from: %s", provider);
- }
- }
- }
-
- /* instantiate the status provider */
- StatusProvider * sprovider = NULL;
- if (provider_type != 0) {
- sprovider = STATUS_PROVIDER(g_object_new(provider_type, NULL));
- if (sprovider == NULL) {
- g_warning("Unable to build provider from: %s", provider);
- }
- }
-
- /* use the provider */
- if (sprovider != NULL) {
- /* On update let's talk to all of them and create the aggregate
- value to export */
- g_signal_connect(G_OBJECT(sprovider),
- STATUS_PROVIDER_SIGNAL_STATUS_CHANGED,
- G_CALLBACK(update_status), NULL);
-
- /* Attach the module object to the status provider so
- that when the status provider is free'd the module
- is closed automatically. */
- g_object_set_data_full(G_OBJECT(sprovider),
- "status-provider-module",
- module, module_destroy_in_idle);
- module = NULL; /* don't close module in this func */
-
- status_providers = g_list_prepend(status_providers, sprovider);
-
- /* Force an update to ensure a consistent state */
- update_status();
- }
-
- /* cleanup */
- if (module != NULL)
- g_module_close(module);
- g_free(provider);
- return FALSE; /* only call this idle func once */
-}
diff --git a/src/status-items.h b/src/status-items.h
deleted file mode 100644
index fe7900c..0000000
--- a/src/status-items.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-Code to build and maintain the status adjustment menuitems.
-
-Copyright 2011 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@canonical.com>
-
-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/>.
-*/
-
-#ifndef __STATUS_ITEMS_H__
-#define __STATUS_ITEMS_H__
-
-#include <glib.h>
-
-G_BEGIN_DECLS
-
-typedef void (*StatusUpdateFunc) (void);
-
-GList * status_items_build (StatusUpdateFunc update_func);
-const gchar * status_current_panel_icon (gboolean alert);
-void status_items_cleanup (void);
-
-G_END_DECLS
-
-#endif /* __STATUS_ITEMS_H__ */
-
diff --git a/src/status-provider-emesene.c b/src/status-provider-emesene.c
deleted file mode 100644
index 5aa6698..0000000
--- a/src/status-provider-emesene.c
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
-A small wrapper utility to load indicators and put them as menu items
-into the gnome-panel using it's applet interface.
-
-Copyright 2011 Canonical Ltd.
-
-Authors:
- Stefano Candori <stefano.candori@gmail.com>
-
-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/>.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "status-provider.h"
-#include "status-provider-emesene.h"
-
-#include <dbus/dbus-glib.h>
-
-typedef enum {
- EM_STATUS_ONLINE,
- EM_STATUS_INVISIBLE,
- EM_STATUS_BUSY,
- EM_STATUS_AWAY,
- EM_STATUS_IDLE,
- EM_STATUS_OFFLINE
-} em_status_t;
-
-static const StatusProviderStatus em_to_sp_map[] = {
- /* EM_STATUS_ONLINE, */ STATUS_PROVIDER_STATUS_ONLINE,
- /* EM_STATUS_INVISIBLE, */ STATUS_PROVIDER_STATUS_INVISIBLE,
- /* EM_STATUS_BUSY, */ STATUS_PROVIDER_STATUS_DND,
- /* EM_STATUS_AWAY, */ STATUS_PROVIDER_STATUS_AWAY,
- /* EM_STATUS_IDLE, */ STATUS_PROVIDER_STATUS_AWAY,
- /* EM_STATUS_OFFLINE, */ STATUS_PROVIDER_STATUS_OFFLINE
-};
-
-static const em_status_t sp_to_em_map[STATUS_PROVIDER_STATUS_LAST] = {
- /* STATUS_PROVIDER_STATUS_ONLINE, */ EM_STATUS_ONLINE,
- /* STATUS_PROVIDER_STATUS_AWAY, */ EM_STATUS_AWAY,
- /* STATUS_PROVIDER_STATUS_DND */ EM_STATUS_BUSY,
- /* STATUS_PROVIDER_STATUS_INVISIBLE*/ EM_STATUS_INVISIBLE,
- /* STATUS_PROVIDER_STATUS_OFFLINE */ EM_STATUS_OFFLINE,
- /* STATUS_PROVIDER_STATUS_DISCONNECTED*/ EM_STATUS_OFFLINE
-};
-
-typedef struct _StatusProviderEmesenePrivate StatusProviderEmesenePrivate;
-struct _StatusProviderEmesenePrivate {
- DBusGProxy * proxy;
- DBusGProxy * dbus_proxy;
- em_status_t em_status;
-};
-
-#define STATUS_PROVIDER_EMESENE_GET_PRIVATE(o) \
-(G_TYPE_INSTANCE_GET_PRIVATE ((o), STATUS_PROVIDER_EMESENE_TYPE, StatusProviderEmesenePrivate))
-
-/* Prototypes */
-/* GObject stuff */
-static void status_provider_emesene_class_init (StatusProviderEmeseneClass *klass);
-static void status_provider_emesene_init (StatusProviderEmesene *self);
-static void status_provider_emesene_dispose (GObject *object);
-static void status_provider_emesene_finalize (GObject *object);
-/* Internal Funcs */
-static void set_status (StatusProvider * sp, StatusProviderStatus status);
-static StatusProviderStatus get_status (StatusProvider * sp);
-static void setup_emesene_proxy (StatusProviderEmesene * self);
-static void dbus_namechange (DBusGProxy * proxy, const gchar * name, const gchar * prev, const gchar * new, StatusProviderEmesene * self);
-
-G_DEFINE_TYPE (StatusProviderEmesene, status_provider_emesene, STATUS_PROVIDER_TYPE);
-
-STATUS_PROVIDER_EXPORT_TYPE(STATUS_PROVIDER_EMESENE_TYPE)
-
-static void
-status_provider_emesene_class_init (StatusProviderEmeseneClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- g_type_class_add_private (klass, sizeof (StatusProviderEmesenePrivate));
-
- object_class->dispose = status_provider_emesene_dispose;
- object_class->finalize = status_provider_emesene_finalize;
-
- StatusProviderClass * spclass = STATUS_PROVIDER_CLASS(klass);
-
- spclass->set_status = set_status;
- spclass->get_status = get_status;
-
- return;
-}
-
-static void
-status_cb (DBusGProxy * proxy, DBusGProxyCall * call, gpointer userdata)
-{
- StatusProviderEmesene * spe = STATUS_PROVIDER_EMESENE(userdata);
- StatusProviderEmesenePrivate * priv = STATUS_PROVIDER_EMESENE_GET_PRIVATE(spe);
-
- GError * error = NULL;
- gint status = 0;
- if (!dbus_g_proxy_end_call(proxy, call, &error, G_TYPE_INT, &status, G_TYPE_INVALID)) {
- g_warning("Unable to get status from Emesene: %s", error->message);
- g_error_free(error);
- return;
- }
-
- priv->em_status = status;
- g_signal_emit(G_OBJECT(spe), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, em_to_sp_map[priv->em_status], TRUE);
- return;
-}
-
-static void
-changed_status (DBusGProxy * proxy, gint status, StatusProviderEmesene * spe)
-{
- StatusProviderEmesenePrivate * priv = STATUS_PROVIDER_EMESENE_GET_PRIVATE(spe);
- g_debug("Emesene changed status to %d", status);
- priv->em_status = status;
- g_signal_emit(G_OBJECT(spe), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, em_to_sp_map[priv->em_status], TRUE);
- return;
-}
-
-static void
-proxy_destroy (DBusGProxy * proxy, StatusProviderEmesene * spe)
-{
- StatusProviderEmesenePrivate * priv = STATUS_PROVIDER_EMESENE_GET_PRIVATE(spe);
-
- priv->proxy = NULL;
- priv->em_status = EM_STATUS_OFFLINE;
-
- g_signal_emit(G_OBJECT(spe), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, em_to_sp_map[priv->em_status], TRUE);
- return;
-}
-
-static void
-status_provider_emesene_init (StatusProviderEmesene *self)
-{
- StatusProviderEmesenePrivate * priv = STATUS_PROVIDER_EMESENE_GET_PRIVATE(self);
-
- priv->proxy = NULL;
- priv->em_status = EM_STATUS_OFFLINE;
-
- DBusGConnection * bus = dbus_g_bus_get(DBUS_BUS_SESSION, NULL);
- g_return_if_fail(bus != NULL); /* Can't do anymore DBus stuff without this,
- all non-DBus stuff should be done */
-
- GError * error = NULL;
-
- /* Set up the dbus Proxy */
- priv->dbus_proxy = dbus_g_proxy_new_for_name_owner (bus,
- DBUS_SERVICE_DBUS,
- DBUS_PATH_DBUS,
- DBUS_INTERFACE_DBUS,
- &error);
- if (error != NULL) {
- g_warning("Unable to connect to DBus events: %s", error->message);
- g_error_free(error);
- return;
- }
-
- /* Configure the name owner changing */
- dbus_g_proxy_add_signal(priv->dbus_proxy, "NameOwnerChanged",
- G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
- G_TYPE_INVALID);
- dbus_g_proxy_connect_signal(priv->dbus_proxy, "NameOwnerChanged",
- G_CALLBACK(dbus_namechange),
- self, NULL);
-
- setup_emesene_proxy(self);
-
- return;
-}
-
-/* Watch to see if the Emesene comes up on Dbus */
-static void
-dbus_namechange (DBusGProxy * proxy, const gchar * name, const gchar * prev, const gchar * new, StatusProviderEmesene * self)
-{
- g_return_if_fail(name != NULL);
- g_return_if_fail(new != NULL);
-
- if (g_strcmp0(name, "org.emesene.Service") == 0) {
- setup_emesene_proxy(self);
- }
- return;
-}
-
-/* Setup the Emesene proxy so that we can talk to it
- and get signals from it. */
-static void
-setup_emesene_proxy (StatusProviderEmesene * self)
-{
- StatusProviderEmesenePrivate * priv = STATUS_PROVIDER_EMESENE_GET_PRIVATE(self);
-
- if (priv->proxy != NULL) {
- g_debug("Doh!We were asked to set up a Emesene proxy when we already had one.");
- return;
- }
-
- DBusGConnection * bus = dbus_g_bus_get(DBUS_BUS_SESSION, NULL);
- g_return_if_fail(bus != NULL); /* Can't do anymore DBus stuff without this,
- all non-DBus stuff should be done */
-
- GError * error = NULL;
-
- /* Set up the Emesene Proxy */
- priv->proxy = dbus_g_proxy_new_for_name_owner (bus,
- "org.emesene.Service",
- "/org/emesene/Service",
- "org.emesene.Service",
- &error);
- /* Report any errors */
- if (error != NULL) {
- g_debug("Unable to get Emesene proxy: %s", error->message);
- g_error_free(error);
- }
-
- /* If we have a proxy, let's start using it */
- if (priv->proxy != NULL) {
- /* Set the proxy to NULL if it's destroyed */
- g_object_add_weak_pointer (G_OBJECT(priv->proxy), (gpointer *)&priv->proxy);
- /* If it's destroyed, let's clean up as well */
- g_signal_connect(G_OBJECT(priv->proxy), "destroy",
- G_CALLBACK(proxy_destroy), self);
-
- /* Watching for the status change coming from the
- Emesene side of things. */
- g_debug("Adding Emesene Signals");
- dbus_g_proxy_add_signal (priv->proxy,
- "status_changed",
- G_TYPE_INT,
- G_TYPE_INVALID);
- dbus_g_proxy_connect_signal(priv->proxy,
- "status_changed",
- G_CALLBACK(changed_status),
- (void *)self,
- NULL);
-
- /* Get the current status to update our cached
- value of the status. */
- dbus_g_proxy_begin_call(priv->proxy,
- "get_status",
- status_cb,
- self,
- NULL,
- G_TYPE_INVALID);
- }
-
- return;
-}
-
-static void
-status_provider_emesene_dispose (GObject *object)
-{
- StatusProviderEmesenePrivate * priv = STATUS_PROVIDER_EMESENE_GET_PRIVATE(object);
-
- if (priv->proxy != NULL) {
- g_object_unref(priv->proxy);
- priv->proxy = NULL;
- }
-
- G_OBJECT_CLASS (status_provider_emesene_parent_class)->dispose (object);
- return;
-}
-
-static void
-status_provider_emesene_finalize (GObject *object)
-{
-
- G_OBJECT_CLASS (status_provider_emesene_parent_class)->finalize (object);
- return;
-}
-
-/**
- status_provider_emesene_new:
-
- Creates a new #StatusProviderEmesene object. No parameters or anything
- like that. Just a convience function.
-
- Return value: A new instance of #StatusProviderEmesene
-*/
-StatusProvider *
-status_provider_emesene_new (void)
-{
- return STATUS_PROVIDER(g_object_new(STATUS_PROVIDER_EMESENE_TYPE, NULL));
-}
-
-/* Takes the status provided generically for Status providers
- and turns it into a Emesene status and sends it to Emesene. */
-static void
-set_status (StatusProvider * sp, StatusProviderStatus status)
-{
- g_return_if_fail(IS_STATUS_PROVIDER_EMESENE(sp));
- StatusProviderEmesenePrivate * priv = STATUS_PROVIDER_EMESENE_GET_PRIVATE(sp);
-
- g_debug("Emesene set status to %d", status);
- if (priv->proxy == NULL) {
- return;
- }
-
- priv->em_status = sp_to_em_map[status];
-
- gboolean ret = FALSE;
- GError * error = NULL;
-
- ret = dbus_g_proxy_call(priv->proxy,
- "set_status", &error,
- G_TYPE_INT, priv->em_status,
- G_TYPE_INVALID,
- G_TYPE_INVALID);
-
- if (!ret) {
- if (error != NULL) {
- g_warning("Emesene unable to change to status: %s", error->message);
- g_error_free(error);
- } else {
- g_warning("Emesene unable to change to status");
- }
- error = NULL;
- }
-
- g_signal_emit(G_OBJECT(sp), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, em_to_sp_map[priv->em_status], TRUE);
- return;
-}
-
-/* Takes the cached Emesene status and makes it into the generic
- Status provider status. If there is no Emesene proxy then it
- returns the disconnected state. */
-static StatusProviderStatus
-get_status (StatusProvider * sp)
-{
- g_return_val_if_fail(IS_STATUS_PROVIDER_EMESENE(sp), STATUS_PROVIDER_STATUS_DISCONNECTED);
- StatusProviderEmesenePrivate * priv = STATUS_PROVIDER_EMESENE_GET_PRIVATE(sp);
-
- if (priv->proxy == NULL) {
- return STATUS_PROVIDER_STATUS_DISCONNECTED;
- }
-
- return em_to_sp_map[priv->em_status];
-}
diff --git a/src/status-provider-emesene.h b/src/status-provider-emesene.h
deleted file mode 100644
index 2309684..0000000
--- a/src/status-provider-emesene.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
-A small wrapper utility to load indicators and put them as menu items
-into the gnome-panel using it's applet interface.
-
-Copyright 20011 Canonical Ltd.
-
-Authors:
- Stefano Candori <stefano.candori@gmail.com>
-
-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/>.
-*/
-
-#ifndef __STATUS_PROVIDER_EMESENE_H__
-#define __STATUS_PROVIDER_EMESENE_H__
-
-#include <glib.h>
-#include <glib-object.h>
-
-#include "status-provider.h"
-
-G_BEGIN_DECLS
-
-#define STATUS_PROVIDER_EMESENE_TYPE (status_provider_emesene_get_type ())
-#define STATUS_PROVIDER_EMESENE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), STATUS_PROVIDER_EMESENE_TYPE, StatusProviderEmesene))
-#define STATUS_PROVIDER_EMESENE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), STATUS_PROVIDER_EMESENE_TYPE, StatusProviderEmeseneClass))
-#define IS_STATUS_PROVIDER_EMESENE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), STATUS_PROVIDER_EMESENE_TYPE))
-#define IS_STATUS_PROVIDER_EMESENE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), STATUS_PROVIDER_EMESENE_TYPE))
-#define STATUS_PROVIDER_EMESENE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), STATUS_PROVIDER_EMESENE_TYPE, StatusProviderEmeseneClass))
-
-
-typedef struct _StatusProviderEmeseneClass StatusProviderEmeseneClass;
-struct _StatusProviderEmeseneClass {
- StatusProviderClass parent_class;
-};
-
-typedef struct _StatusProviderEmesene StatusProviderEmesene;
-struct _StatusProviderEmesene {
- StatusProvider parent;
-};
-
-GType status_provider_emesene_get_type (void);
-StatusProvider * status_provider_emesene_new (void);
-
-G_END_DECLS
-
-#endif
diff --git a/src/status-provider-mc5.c b/src/status-provider-mc5.c
deleted file mode 100644
index 3f23565..0000000
--- a/src/status-provider-mc5.c
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
-A small wrapper utility to load indicators and put them as menu items
-into the gnome-panel using it's applet interface.
-
-Copyright 2009 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@canonical.com>
-
-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/>.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <telepathy-glib/account-manager.h>
-
-#include "status-provider.h"
-#include "status-provider-mc5.h"
-#include "status-provider-mc5-marshal.h"
-
-#include <dbus/dbus-glib.h>
-#include <dbus/dbus-glib-bindings.h>
-
-static gchar * sp_to_mc_map[STATUS_PROVIDER_STATUS_LAST] = {
- /* STATUS_PROVIDER_STATUS_ONLINE, */ "available",
- /* STATUS_PROVIDER_STATUS_AWAY, */ "away",
- /* STATUS_PROVIDER_STATUS_DND */ "busy",
- /* STATUS_PROVIDER_STATUS_INVISIBLE*/ "hidden",
- /* STATUS_PROVIDER_STATUS_OFFLINE */ "offline",
- /* STATUS_PROVIDER_STATUS_DISCONNECTED*/NULL
-};
-
-static TpConnectionPresenceType sp_to_tp_map[STATUS_PROVIDER_STATUS_LAST] = {
- /* STATUS_PROVIDER_STATUS_ONLINE, */ TP_CONNECTION_PRESENCE_TYPE_AVAILABLE,
- /* STATUS_PROVIDER_STATUS_AWAY, */ TP_CONNECTION_PRESENCE_TYPE_AWAY,
- /* STATUS_PROVIDER_STATUS_DND */ TP_CONNECTION_PRESENCE_TYPE_BUSY,
- /* STATUS_PROVIDER_STATUS_INVISIBLE*/ TP_CONNECTION_PRESENCE_TYPE_HIDDEN,
- /* STATUS_PROVIDER_STATUS_OFFLINE */ TP_CONNECTION_PRESENCE_TYPE_OFFLINE,
- /* STATUS_PROVIDER_STATUS_DISCONNECTED*/ TP_CONNECTION_PRESENCE_TYPE_UNSET
-};
-
-static StatusProviderStatus tp_to_sp_map[TP_CONNECTION_PRESENCE_TYPE_ERROR + 1] = {
- /* TP_CONNECTION_PRESENCE_TYPE_UNSET */ STATUS_PROVIDER_STATUS_DISCONNECTED,
- /* TP_CONNECTION_PRESENCE_TYPE_OFFLINE */ STATUS_PROVIDER_STATUS_OFFLINE,
- /* TP_CONNECTION_PRESENCE_TYPE_AVAILABLE */ STATUS_PROVIDER_STATUS_ONLINE,
- /* TP_CONNECTION_PRESENCE_TYPE_AWAY */ STATUS_PROVIDER_STATUS_AWAY,
- /* TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY */ STATUS_PROVIDER_STATUS_AWAY,
- /* TP_CONNECTION_PRESENCE_TYPE_HIDDEN */ STATUS_PROVIDER_STATUS_INVISIBLE,
- /* TP_CONNECTION_PRESENCE_TYPE_BUSY */ STATUS_PROVIDER_STATUS_DND,
- /* TP_CONNECTION_PRESENCE_TYPE_UNKNOWN */ STATUS_PROVIDER_STATUS_DISCONNECTED,
- /* TP_CONNECTION_PRESENCE_TYPE_ERROR */ STATUS_PROVIDER_STATUS_DISCONNECTED
-};
-
-typedef struct _StatusProviderMC5Private StatusProviderMC5Private;
-struct _StatusProviderMC5Private {
- TpAccountManager * manager;
- StatusProviderStatus status;
- DBusGProxy * dbus_proxy;
-};
-
-#define STATUS_PROVIDER_MC5_GET_PRIVATE(o) \
-(G_TYPE_INSTANCE_GET_PRIVATE ((o), STATUS_PROVIDER_MC5_TYPE, StatusProviderMC5Private))
-#define MC5_WELL_KNOWN_NAME "org.freedesktop.Telepathy.AccountManager"
-
-/* Prototypes */
-/* GObject stuff */
-static void status_provider_mc5_class_init (StatusProviderMC5Class *klass);
-static void status_provider_mc5_init (StatusProviderMC5 *self);
-static void status_provider_mc5_dispose (GObject *object);
-static void status_provider_mc5_finalize (GObject *object);
-/* Internal Funcs */
-static void set_status (StatusProvider * sp, StatusProviderStatus status);
-static StatusProviderStatus get_status (StatusProvider * sp);
-static void presence_changed (TpAccountManager * eam, guint type, const gchar * type_str, const gchar * message, StatusProviderMC5 * sp);
-static void dbus_namechange (DBusGProxy * proxy, const gchar * name, const gchar * prev, const gchar * new, StatusProviderMC5 * self);
-static void mc5_exists_cb (DBusGProxy * proxy, gboolean exists, GError * error, gpointer userdata);
-
-G_DEFINE_TYPE (StatusProviderMC5, status_provider_mc5, STATUS_PROVIDER_TYPE);
-
-STATUS_PROVIDER_EXPORT_TYPE(STATUS_PROVIDER_MC5_TYPE)
-
-/* Create the class. We over ride a few functions but nothing
- really shocking. Most interesting is the set and get status. */
-static void
-status_provider_mc5_class_init (StatusProviderMC5Class *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- g_type_class_add_private (klass, sizeof (StatusProviderMC5Private));
-
- object_class->dispose = status_provider_mc5_dispose;
- object_class->finalize = status_provider_mc5_finalize;
-
- StatusProviderClass * spclass = STATUS_PROVIDER_CLASS(klass);
-
- spclass->set_status = set_status;
- spclass->get_status = get_status;
-
- return;
-}
-
-/* Build our telepathy account manager instance if we don't
- have one. */
-static void
-build_eam (StatusProviderMC5 * self)
-{
- StatusProviderMC5Private * priv = STATUS_PROVIDER_MC5_GET_PRIVATE(self);
-
- if (priv->manager != NULL) {
- return;
- }
-
- priv->manager = tp_account_manager_dup();
- g_signal_connect(G_OBJECT(priv->manager), "most-available-presence-changed", G_CALLBACK(presence_changed), self);
-
- return;
-}
-
-/* Creating an instance of the status provider. We set the variables
- and create an TpAccountManager object. It does all the hard
- work in this module of tracking MissionControl and enumerating the
- accounts and all that jazz. */
-static void
-status_provider_mc5_init (StatusProviderMC5 *self)
-{
- StatusProviderMC5Private * priv = STATUS_PROVIDER_MC5_GET_PRIVATE(self);
-
- priv->status = STATUS_PROVIDER_STATUS_OFFLINE;
- priv->manager = NULL;
-
- DBusGConnection * bus = dbus_g_bus_get(DBUS_BUS_SESSION, NULL);
- g_return_if_fail(bus != NULL); /* Can't do anymore DBus stuff without this,
- all non-DBus stuff should be done */
-
- GError * error = NULL;
-
- /* Set up the dbus Proxy */
- priv->dbus_proxy = dbus_g_proxy_new_for_name_owner (bus,
- DBUS_SERVICE_DBUS,
- DBUS_PATH_DBUS,
- DBUS_INTERFACE_DBUS,
- &error);
- if (error != NULL) {
- g_warning("Unable to connect to DBus events: %s", error->message);
- g_error_free(error);
- return;
- }
-
- /* Configure the name owner changing */
- dbus_g_proxy_add_signal(priv->dbus_proxy, "NameOwnerChanged",
- G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
- G_TYPE_INVALID);
- dbus_g_proxy_connect_signal(priv->dbus_proxy, "NameOwnerChanged",
- G_CALLBACK(dbus_namechange),
- self, NULL);
-
- org_freedesktop_DBus_name_has_owner_async(priv->dbus_proxy, MC5_WELL_KNOWN_NAME, mc5_exists_cb, self);
-
- return;
-}
-
-/* Unref the account manager and move on. Sadly, we're
- leaving the show. */
-static void
-status_provider_mc5_dispose (GObject *object)
-{
- StatusProviderMC5Private * priv = STATUS_PROVIDER_MC5_GET_PRIVATE(object);
-
- if (priv->manager != NULL) {
- g_object_unref(priv->manager);
- priv->manager = NULL;
- }
-
- if (priv->dbus_proxy != NULL) {
- g_object_unref(priv->dbus_proxy);
- priv->dbus_proxy = NULL;
- }
-
- G_OBJECT_CLASS (status_provider_mc5_parent_class)->dispose (object);
- return;
-}
-
-/* Pass to superclass */
-static void
-status_provider_mc5_finalize (GObject *object)
-{
-
- G_OBJECT_CLASS (status_provider_mc5_parent_class)->finalize (object);
- return;
-}
-
-/* Watch for MC5 Coming on and off the bus. */
-static void
-dbus_namechange (DBusGProxy * proxy, const gchar * name, const gchar * prev, const gchar * new, StatusProviderMC5 * self)
-{
- /* g_debug("DBUS NAMECHANGE: %s %s %s", name, prev, new); */
-
- if (prev[0] == '\0' && g_strcmp0(name, MC5_WELL_KNOWN_NAME) == 0) {
- g_debug("MC5 Coming online");
- build_eam(self);
- }
- if (new[0] == '\0' && g_strcmp0(name, MC5_WELL_KNOWN_NAME) == 0) {
- g_debug("MC5 going offline");
- StatusProviderMC5Private * priv = STATUS_PROVIDER_MC5_GET_PRIVATE(self);
- if (priv->manager != NULL) {
- g_object_unref(priv->manager);
- priv->manager = NULL;
- }
-
- priv->status = STATUS_PROVIDER_STATUS_OFFLINE;
- g_signal_emit(G_OBJECT(self), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, priv->status, TRUE);
- }
-
- return;
-}
-
-/* Callback for the Dbus command to do HasOwner on
- the MC5 service. If it exists, we want to have an
- account manager. */
-static void
-mc5_exists_cb (DBusGProxy * proxy, gboolean exists, GError * error, gpointer userdata)
-{
- if (error) {
- g_warning("Unable to check if MC5 is running: %s", error->message);
- return;
- }
-
- if (exists) {
- build_eam(STATUS_PROVIDER_MC5(userdata));
- }
-
- return;
-}
-
-/**
- status_provider_mc5_new:
-
- Creates a new #StatusProviderMC5 object. No parameters or anything
- like that. Just a convience function.
-
- Return value: A new instance of #StatusProviderMC5
-*/
-StatusProvider *
-status_provider_mc5_new (void)
-{
- return STATUS_PROVIDER(g_object_new(STATUS_PROVIDER_MC5_TYPE, NULL));
-}
-
-/* Setting the status in the empathy account manager. We're
- basically requesting a global status. This may or may not
- get applied to all accounts. It's really the best we can
- hope to do. */
-static void
-set_status (StatusProvider * sp, StatusProviderStatus status)
-{
- StatusProviderMC5Private * priv = STATUS_PROVIDER_MC5_GET_PRIVATE(sp);
-
- build_eam(STATUS_PROVIDER_MC5(sp));
- tp_account_manager_set_all_requested_presences(priv->manager, sp_to_tp_map[status], sp_to_mc_map[status], "");
-
- return;
-}
-
-/* Gets the status, uses the cached value that we have. Asking
- would just be painful. */
-static StatusProviderStatus
-get_status (StatusProvider * sp)
-{
- g_return_val_if_fail(IS_STATUS_PROVIDER_MC5(sp), STATUS_PROVIDER_STATUS_DISCONNECTED);
- StatusProviderMC5Private * priv = STATUS_PROVIDER_MC5_GET_PRIVATE(sp);
-
- if (priv->manager == NULL) {
- return STATUS_PROVIDER_STATUS_OFFLINE;
- }
-
- return priv->status;
-}
-
-/* A signal handler for when the TpAccountManager believes
- that the global status has changed. It roughly calculates this
- by finding the most available of all accounts that are active. */
-static void
-presence_changed (TpAccountManager * eam, guint type, const gchar * type_str, const gchar * message, StatusProviderMC5 * sp)
-{
- StatusProviderMC5Private * priv = STATUS_PROVIDER_MC5_GET_PRIVATE(sp);
-
- g_debug("MC5 Status changed: %d %s %s", type, type_str, message);
-
- if (priv->status != tp_to_sp_map[type]) {
- priv->status = tp_to_sp_map[type];
- g_signal_emit(G_OBJECT(sp), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, priv->status, TRUE);
- }
-
- return;
-}
-
diff --git a/src/status-provider-mc5.h b/src/status-provider-mc5.h
deleted file mode 100644
index 4d5659d..0000000
--- a/src/status-provider-mc5.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
-A small wrapper utility to load indicators and put them as menu items
-into the gnome-panel using it's applet interface.
-
-Copyright 2009 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@canonical.com>
-
-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/>.
-*/
-
-#ifndef __STATUS_PROVIDER_MC5_H__
-#define __STATUS_PROVIDER_MC5_H__
-
-#include <glib.h>
-#include <glib-object.h>
-
-#include "status-provider.h"
-
-G_BEGIN_DECLS
-
-#define STATUS_PROVIDER_MC5_TYPE (status_provider_mc5_get_type ())
-#define STATUS_PROVIDER_MC5(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), STATUS_PROVIDER_MC5_TYPE, StatusProviderMC5))
-#define STATUS_PROVIDER_MC5_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), STATUS_PROVIDER_MC5_TYPE, StatusProviderMC5Class))
-#define IS_STATUS_PROVIDER_MC5(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), STATUS_PROVIDER_MC5_TYPE))
-#define IS_STATUS_PROVIDER_MC5_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), STATUS_PROVIDER_MC5_TYPE))
-#define STATUS_PROVIDER_MC5_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), STATUS_PROVIDER_MC5_TYPE, StatusProviderMC5Class))
-
-
-typedef struct _StatusProviderMC5Class StatusProviderMC5Class;
-struct _StatusProviderMC5Class {
- StatusProviderClass parent_class;
-};
-
-typedef struct _StatusProviderMC5 StatusProviderMC5;
-struct _StatusProviderMC5 {
- StatusProvider parent;
-};
-
-GType status_provider_mc5_get_type (void);
-StatusProvider * status_provider_mc5_new (void);
-
-G_END_DECLS
-
-#endif
diff --git a/src/status-provider-mc5.list b/src/status-provider-mc5.list
deleted file mode 100644
index 5ab45bf..0000000
--- a/src/status-provider-mc5.list
+++ /dev/null
@@ -1 +0,0 @@
-VOID:UINT,STRING
diff --git a/src/status-provider-pidgin.c b/src/status-provider-pidgin.c
deleted file mode 100644
index 6c73d56..0000000
--- a/src/status-provider-pidgin.c
+++ /dev/null
@@ -1,433 +0,0 @@
-/*
-A small wrapper utility to load indicators and put them as menu items
-into the gnome-panel using it's applet interface.
-
-Copyright 2009 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@canonical.com>
-
-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/>.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "status-provider.h"
-#include "status-provider-pidgin.h"
-#include "status-provider-pidgin-marshal.h"
-
-#include <dbus/dbus-glib.h>
-
-typedef enum {
- PG_STATUS_UNKNOWN,
- PG_STATUS_OFFLINE,
- PG_STATUS_AVAILABLE,
- PG_STATUS_UNAVAILABLE,
- PG_STATUS_INVISIBLE,
- PG_STATUS_AWAY,
- PG_STATUS_EXTENDEND_AWAY,
- PG_STATUS_MOBILE,
- PG_STATUS_TUNE
-} pg_status_t;
-
-static const StatusProviderStatus pg_to_sp_map[] = {
- /* PG_STATUS_UNKNOWN, */ STATUS_PROVIDER_STATUS_OFFLINE,
- /* PG_STATUS_OFFLINE, */ STATUS_PROVIDER_STATUS_OFFLINE,
- /* PG_STATUS_AVAILABLE, */ STATUS_PROVIDER_STATUS_ONLINE,
- /* PG_STATUS_UNAVAILABLE, */ STATUS_PROVIDER_STATUS_DND,
- /* PG_STATUS_INVISIBLE, */ STATUS_PROVIDER_STATUS_INVISIBLE,
- /* PG_STATUS_AWAY, */ STATUS_PROVIDER_STATUS_AWAY,
- /* PG_STATUS_EXTENDEND_AWAY, */ STATUS_PROVIDER_STATUS_AWAY,
- /* PG_STATUS_MOBILE, */ STATUS_PROVIDER_STATUS_OFFLINE,
- /* PG_STATUS_TUNE */ STATUS_PROVIDER_STATUS_OFFLINE
-};
-
-static const pg_status_t sp_to_pg_map[STATUS_PROVIDER_STATUS_LAST] = {
- /* STATUS_PROVIDER_STATUS_ONLINE, */ PG_STATUS_AVAILABLE,
- /* STATUS_PROVIDER_STATUS_AWAY, */ PG_STATUS_AWAY,
- /* STATUS_PROVIDER_STATUS_DND */ PG_STATUS_UNAVAILABLE,
- /* STATUS_PROVIDER_STATUS_INVISIBLE*/ PG_STATUS_INVISIBLE,
- /* STATUS_PROVIDER_STATUS_OFFLINE */ PG_STATUS_OFFLINE,
- /* STATUS_PROVIDER_STATUS_DISCONNECTED*/ PG_STATUS_OFFLINE
-};
-
-typedef struct _StatusProviderPidginPrivate StatusProviderPidginPrivate;
-struct _StatusProviderPidginPrivate {
- DBusGProxy * proxy;
- DBusGProxy * dbus_proxy;
- pg_status_t pg_status;
-};
-
-#define STATUS_PROVIDER_PIDGIN_GET_PRIVATE(o) \
-(G_TYPE_INSTANCE_GET_PRIVATE ((o), STATUS_PROVIDER_PIDGIN_TYPE, StatusProviderPidginPrivate))
-
-/* Prototypes */
-/* GObject stuff */
-static void status_provider_pidgin_class_init (StatusProviderPidginClass *klass);
-static void status_provider_pidgin_init (StatusProviderPidgin *self);
-static void status_provider_pidgin_dispose (GObject *object);
-static void status_provider_pidgin_finalize (GObject *object);
-/* Internal Funcs */
-static void set_status (StatusProvider * sp, StatusProviderStatus status);
-static StatusProviderStatus get_status (StatusProvider * sp);
-static void setup_pidgin_proxy (StatusProviderPidgin * self);
-static void dbus_namechange (DBusGProxy * proxy, const gchar * name, const gchar * prev, const gchar * new, StatusProviderPidgin * self);
-
-G_DEFINE_TYPE (StatusProviderPidgin, status_provider_pidgin, STATUS_PROVIDER_TYPE);
-
-STATUS_PROVIDER_EXPORT_TYPE(STATUS_PROVIDER_PIDGIN_TYPE)
-
-static void
-status_provider_pidgin_class_init (StatusProviderPidginClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- g_type_class_add_private (klass, sizeof (StatusProviderPidginPrivate));
-
- object_class->dispose = status_provider_pidgin_dispose;
- object_class->finalize = status_provider_pidgin_finalize;
-
- StatusProviderClass * spclass = STATUS_PROVIDER_CLASS(klass);
-
- spclass->set_status = set_status;
- spclass->get_status = get_status;
-
- return;
-}
-
-static void
-type_cb (DBusGProxy * proxy, DBusGProxyCall * call, gpointer userdata)
-{
- GError * error = NULL;
- gint status = 0;
- if (!dbus_g_proxy_end_call(proxy, call, &error, G_TYPE_INT, &status, G_TYPE_INVALID)) {
- g_warning("Unable to get type from Pidgin: %s", error->message);
- g_error_free(error);
- return;
- }
-
- StatusProviderPidginPrivate * priv = STATUS_PROVIDER_PIDGIN_GET_PRIVATE(userdata);
- if (status != priv->pg_status) {
- priv->pg_status = status;
-
- g_signal_emit(G_OBJECT(userdata), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, pg_to_sp_map[priv->pg_status], TRUE);
- }
-
- return;
-}
-
-static void
-saved_status_to_type (StatusProviderPidgin * spp, gint savedstatus)
-{
- StatusProviderPidginPrivate * priv = STATUS_PROVIDER_PIDGIN_GET_PRIVATE(spp);
-
- g_debug("Pidgin figuring out type for %d", savedstatus);
- dbus_g_proxy_begin_call(priv->proxy,
- "PurpleSavedstatusGetType", type_cb, spp, NULL,
- G_TYPE_INT, savedstatus, G_TYPE_INVALID);
-
- return;
-}
-
-static void
-savedstatus_cb (DBusGProxy * proxy, DBusGProxyCall * call, gpointer userdata)
-{
- GError * error = NULL;
- gint status = 0;
- if (!dbus_g_proxy_end_call(proxy, call, &error, G_TYPE_INT, &status, G_TYPE_INVALID)) {
- g_warning("Unable to get saved status from Pidgin: %s", error->message);
- g_error_free(error);
- return;
- }
-
- saved_status_to_type(STATUS_PROVIDER_PIDGIN(userdata), status);
- return;
-}
-
-
-static void
-changed_status (DBusGProxy * proxy, gint savedstatus, GError ** error, StatusProviderPidgin * spp)
-{
- saved_status_to_type(spp, savedstatus);
- return;
-}
-
-static void
-proxy_destroy (DBusGProxy * proxy, StatusProviderPidgin * spp)
-{
- StatusProviderPidginPrivate * priv = STATUS_PROVIDER_PIDGIN_GET_PRIVATE(spp);
-
- priv->proxy = NULL;
- priv->pg_status = PG_STATUS_OFFLINE;
-
- g_signal_emit(G_OBJECT(spp), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, pg_to_sp_map[priv->pg_status], TRUE);
- return;
-}
-
-static void
-status_provider_pidgin_init (StatusProviderPidgin *self)
-{
- StatusProviderPidginPrivate * priv = STATUS_PROVIDER_PIDGIN_GET_PRIVATE(self);
-
- priv->proxy = NULL;
- priv->pg_status = PG_STATUS_OFFLINE;
-
- DBusGConnection * bus = dbus_g_bus_get(DBUS_BUS_SESSION, NULL);
- g_return_if_fail(bus != NULL); /* Can't do anymore DBus stuff without this,
- all non-DBus stuff should be done */
-
- GError * error = NULL;
-
- /* Set up the dbus Proxy */
- priv->dbus_proxy = dbus_g_proxy_new_for_name_owner (bus,
- DBUS_SERVICE_DBUS,
- DBUS_PATH_DBUS,
- DBUS_INTERFACE_DBUS,
- &error);
- if (error != NULL) {
- g_warning("Unable to connect to DBus events: %s", error->message);
- g_error_free(error);
- return;
- }
-
- /* Configure the name owner changing */
- dbus_g_proxy_add_signal(priv->dbus_proxy, "NameOwnerChanged",
- G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
- G_TYPE_INVALID);
- dbus_g_proxy_connect_signal(priv->dbus_proxy, "NameOwnerChanged",
- G_CALLBACK(dbus_namechange),
- self, NULL);
-
- setup_pidgin_proxy(self);
-
- return;
-}
-
-/* Watch to see if the Pidgin comes up on Dbus */
-static void
-dbus_namechange (DBusGProxy * proxy, const gchar * name, const gchar * prev, const gchar * new, StatusProviderPidgin * self)
-{
- g_return_if_fail(name != NULL);
- g_return_if_fail(new != NULL);
-
- if (g_strcmp0(name, "im.pidgin.purple.PurpleService") == 0) {
- setup_pidgin_proxy(self);
- }
- return;
-}
-
-/* Setup the Pidgin proxy so that we can talk to it
- and get signals from it. */
-static void
-setup_pidgin_proxy (StatusProviderPidgin * self)
-{
- StatusProviderPidginPrivate * priv = STATUS_PROVIDER_PIDGIN_GET_PRIVATE(self);
-
- if (priv->proxy != NULL) {
- g_debug("Odd, we were asked to set up a Pidgin proxy when we already had one.");
- return;
- }
-
- DBusGConnection * bus = dbus_g_bus_get(DBUS_BUS_SESSION, NULL);
- g_return_if_fail(bus != NULL); /* Can't do anymore DBus stuff without this,
- all non-DBus stuff should be done */
-
- GError * error = NULL;
-
- /* Set up the Pidgin Proxy */
- priv->proxy = dbus_g_proxy_new_for_name_owner (bus,
- "im.pidgin.purple.PurpleService",
- "/im/pidgin/purple/PurpleObject",
- "im.pidgin.purple.PurpleInterface",
- &error);
- /* Report any errors */
- if (error != NULL) {
- g_debug("Unable to get Pidgin proxy: %s", error->message);
- g_error_free(error);
- }
-
- /* If we have a proxy, let's start using it */
- if (priv->proxy != NULL) {
- /* Set the proxy to NULL if it's destroyed */
- g_object_add_weak_pointer (G_OBJECT(priv->proxy), (gpointer *)&priv->proxy);
- /* If it's destroyed, let's clean up as well */
- g_signal_connect(G_OBJECT(priv->proxy), "destroy",
- G_CALLBACK(proxy_destroy), self);
-
- /* Watching for the status change coming from the
- Pidgin side of things. */
- g_debug("Adding Pidgin Signals");
- dbus_g_object_register_marshaller(_status_provider_pidgin_marshal_VOID__INT_INT,
- G_TYPE_NONE,
- G_TYPE_INT,
- G_TYPE_INT,
- G_TYPE_INVALID);
- dbus_g_proxy_add_signal (priv->proxy,
- "SavedstatusChanged",
- G_TYPE_INT,
- G_TYPE_INT,
- G_TYPE_INVALID);
- dbus_g_proxy_connect_signal(priv->proxy,
- "SavedstatusChanged",
- G_CALLBACK(changed_status),
- (void *)self,
- NULL);
-
- /* Get the current status to update our cached
- value of the status. */
- dbus_g_proxy_begin_call(priv->proxy,
- "PurpleSavedstatusGetCurrent",
- savedstatus_cb,
- self,
- NULL,
- G_TYPE_INVALID);
- }
-
- return;
-}
-
-static void
-status_provider_pidgin_dispose (GObject *object)
-{
- StatusProviderPidginPrivate * priv = STATUS_PROVIDER_PIDGIN_GET_PRIVATE(object);
-
- if (priv->proxy != NULL) {
- g_object_unref(priv->proxy);
- priv->proxy = NULL;
- }
-
- G_OBJECT_CLASS (status_provider_pidgin_parent_class)->dispose (object);
- return;
-}
-
-static void
-status_provider_pidgin_finalize (GObject *object)
-{
-
- G_OBJECT_CLASS (status_provider_pidgin_parent_class)->finalize (object);
- return;
-}
-
-/**
- status_provider_pidgin_new:
-
- Creates a new #StatusProviderPidgin object. No parameters or anything
- like that. Just a convience function.
-
- Return value: A new instance of #StatusProviderPidgin
-*/
-StatusProvider *
-status_provider_pidgin_new (void)
-{
- return STATUS_PROVIDER(g_object_new(STATUS_PROVIDER_PIDGIN_TYPE, NULL));
-}
-
-/* Takes the status provided generically for Status providers
- and turns it into a Pidgin status and sends it to Pidgin. */
-static void
-set_status (StatusProvider * sp, StatusProviderStatus status)
-{
- gchar * message = "";
-
- g_return_if_fail(IS_STATUS_PROVIDER_PIDGIN(sp));
- StatusProviderPidginPrivate * priv = STATUS_PROVIDER_PIDGIN_GET_PRIVATE(sp);
-
- g_debug("\tPidgin set status to %d", status);
- if (priv->proxy == NULL) {
- return;
- }
-
- priv->pg_status = sp_to_pg_map[status];
- gint status_val = 0;
- gboolean ret = FALSE;
- GError * error = NULL;
-
- ret = dbus_g_proxy_call(priv->proxy,
- "PurpleSavedstatusFindTransientByTypeAndMessage", &error,
- G_TYPE_INT, priv->pg_status,
- G_TYPE_STRING, message,
- G_TYPE_INVALID,
- G_TYPE_INT, &status_val,
- G_TYPE_INVALID);
-
- if (!ret) {
- if (error != NULL) {
- g_error_free(error);
- }
- error = NULL;
- status_val = 0;
- g_debug("No Pidgin saved status to apply");
- }
-
- if (status_val == 0) {
- ret = dbus_g_proxy_call(priv->proxy,
- "PurpleSavedstatusNew", &error,
- G_TYPE_STRING, message,
- G_TYPE_INT, priv->pg_status,
- G_TYPE_INVALID,
- G_TYPE_INT, &status_val,
- G_TYPE_INVALID);
-
- if (!ret) {
- status_val = 0;
- if (error != NULL) {
- g_warning("Unable to create Pidgin status for %d: %s", status, error->message);
- g_error_free(error);
- } else {
- g_warning("Unable to create Pidgin status for %d", status);
- }
- error = NULL;
- }
- }
-
- if (status_val == 0) {
- return;
- }
-
- ret = dbus_g_proxy_call(priv->proxy,
- "PurpleSavedstatusActivate", &error,
- G_TYPE_INT, status_val,
- G_TYPE_INVALID,
- G_TYPE_INVALID);
-
- if (!ret) {
- if (error != NULL) {
- g_warning("Pidgin unable to change to status: %s", error->message);
- g_error_free(error);
- } else {
- g_warning("Pidgin unable to change to status");
- }
- error = NULL;
- }
-
- g_signal_emit(G_OBJECT(sp), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, pg_to_sp_map[priv->pg_status], TRUE);
- return;
-}
-
-/* Takes the cached Pidgin status and makes it into the generic
- Status provider status. If there is no Pidgin proxy then it
- returns the disconnected state. */
-static StatusProviderStatus
-get_status (StatusProvider * sp)
-{
- g_return_val_if_fail(IS_STATUS_PROVIDER_PIDGIN(sp), STATUS_PROVIDER_STATUS_DISCONNECTED);
- StatusProviderPidginPrivate * priv = STATUS_PROVIDER_PIDGIN_GET_PRIVATE(sp);
-
- if (priv->proxy == NULL) {
- return STATUS_PROVIDER_STATUS_DISCONNECTED;
- }
-
- return pg_to_sp_map[priv->pg_status];
-}
diff --git a/src/status-provider-pidgin.h b/src/status-provider-pidgin.h
deleted file mode 100644
index 54b1718..0000000
--- a/src/status-provider-pidgin.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
-A small wrapper utility to load indicators and put them as menu items
-into the gnome-panel using it's applet interface.
-
-Copyright 2009 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@canonical.com>
-
-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/>.
-*/
-
-#ifndef __STATUS_PROVIDER_PIDGIN_H__
-#define __STATUS_PROVIDER_PIDGIN_H__
-
-#include <glib.h>
-#include <glib-object.h>
-
-#include "status-provider.h"
-
-G_BEGIN_DECLS
-
-#define STATUS_PROVIDER_PIDGIN_TYPE (status_provider_pidgin_get_type ())
-#define STATUS_PROVIDER_PIDGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), STATUS_PROVIDER_PIDGIN_TYPE, StatusProviderPidgin))
-#define STATUS_PROVIDER_PIDGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), STATUS_PROVIDER_PIDGIN_TYPE, StatusProviderPidginClass))
-#define IS_STATUS_PROVIDER_PIDGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), STATUS_PROVIDER_PIDGIN_TYPE))
-#define IS_STATUS_PROVIDER_PIDGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), STATUS_PROVIDER_PIDGIN_TYPE))
-#define STATUS_PROVIDER_PIDGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), STATUS_PROVIDER_PIDGIN_TYPE, StatusProviderPidginClass))
-
-
-typedef struct _StatusProviderPidginClass StatusProviderPidginClass;
-struct _StatusProviderPidginClass {
- StatusProviderClass parent_class;
-};
-
-typedef struct _StatusProviderPidgin StatusProviderPidgin;
-struct _StatusProviderPidgin {
- StatusProvider parent;
-};
-
-GType status_provider_pidgin_get_type (void);
-StatusProvider * status_provider_pidgin_new (void);
-
-G_END_DECLS
-
-#endif
diff --git a/src/status-provider-pidgin.list b/src/status-provider-pidgin.list
deleted file mode 100644
index 1f953dd..0000000
--- a/src/status-provider-pidgin.list
+++ /dev/null
@@ -1 +0,0 @@
-VOID:INT,INT
diff --git a/src/status-provider-telepathy.c b/src/status-provider-telepathy.c
deleted file mode 100644
index 948e965..0000000
--- a/src/status-provider-telepathy.c
+++ /dev/null
@@ -1,385 +0,0 @@
-/*
-A small wrapper utility to load indicators and put them as menu items
-into the gnome-panel using it's applet interface.
-
-Copyright 2009 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@canonical.com>
-
-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/>.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "status-provider.h"
-#include "status-provider-telepathy.h"
-#include "status-provider-telepathy-marshal.h"
-
-#include <dbus/dbus-glib.h>
-
-typedef enum {
- MC_STATUS_UNSET,
- MC_STATUS_OFFLINE,
- MC_STATUS_AVAILABLE,
- MC_STATUS_AWAY,
- MC_STATUS_EXTENDED_AWAY,
- MC_STATUS_HIDDEN,
- MC_STATUS_DND
-} mc_status_t;
-
-static StatusProviderStatus mc_to_sp_map[] = {
- /* MC_STATUS_UNSET, */ STATUS_PROVIDER_STATUS_OFFLINE,
- /* MC_STATUS_OFFLINE, */ STATUS_PROVIDER_STATUS_OFFLINE,
- /* MC_STATUS_AVAILABLE, */ STATUS_PROVIDER_STATUS_ONLINE,
- /* MC_STATUS_AWAY, */ STATUS_PROVIDER_STATUS_AWAY,
- /* MC_STATUS_EXTENDED_AWAY, */ STATUS_PROVIDER_STATUS_AWAY,
- /* MC_STATUS_HIDDEN, */ STATUS_PROVIDER_STATUS_INVISIBLE,
- /* MC_STATUS_DND */ STATUS_PROVIDER_STATUS_DND
-};
-
-static mc_status_t sp_to_mc_map[] = {
- /* STATUS_PROVIDER_STATUS_ONLINE, */ MC_STATUS_AVAILABLE,
- /* STATUS_PROVIDER_STATUS_AWAY, */ MC_STATUS_AWAY,
- /* STATUS_PROVIDER_STATUS_DND */ MC_STATUS_DND,
- /* STATUS_PROVIDER_STATUS_INVISIBLE*/ MC_STATUS_HIDDEN,
- /* STATUS_PROVIDER_STATUS_OFFLINE */ MC_STATUS_OFFLINE,
- /* STATUS_PROVIDER_STATUS_DISCONNECTED*/MC_STATUS_OFFLINE
-};
-
-typedef struct _StatusProviderTelepathyPrivate StatusProviderTelepathyPrivate;
-struct _StatusProviderTelepathyPrivate {
- DBusGProxy * proxy;
- DBusGProxy * dbus_proxy;
- mc_status_t mc_status;
-};
-
-#define STATUS_PROVIDER_TELEPATHY_GET_PRIVATE(o) \
-(G_TYPE_INSTANCE_GET_PRIVATE ((o), STATUS_PROVIDER_TELEPATHY_TYPE, StatusProviderTelepathyPrivate))
-
-/* Prototypes */
-/* GObject stuff */
-static void status_provider_telepathy_class_init (StatusProviderTelepathyClass *klass);
-static void status_provider_telepathy_init (StatusProviderTelepathy *self);
-static void status_provider_telepathy_dispose (GObject *object);
-static void status_provider_telepathy_finalize (GObject *object);
-/* Internal Funcs */
-static void build_telepathy_proxy (StatusProviderTelepathy * self);
-static void dbus_namechange (DBusGProxy * proxy, const gchar * name, const gchar * prev, const gchar * new, StatusProviderTelepathy * self);
-static void set_status (StatusProvider * sp, StatusProviderStatus status);
-static StatusProviderStatus get_status (StatusProvider * sp);
-static void changed_status (DBusGProxy * proxy, guint status, gchar * message, StatusProvider * sp);
-static void proxy_destroy (DBusGProxy * proxy, StatusProvider * sp);
-static void get_status_async (DBusGProxy * proxy, DBusGProxyCall * call, gpointer userdata);
-
-G_DEFINE_TYPE (StatusProviderTelepathy, status_provider_telepathy, STATUS_PROVIDER_TYPE);
-
-STATUS_PROVIDER_EXPORT_TYPE(STATUS_PROVIDER_TELEPATHY_TYPE)
-
-static void
-status_provider_telepathy_class_init (StatusProviderTelepathyClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- g_type_class_add_private (klass, sizeof (StatusProviderTelepathyPrivate));
-
- object_class->dispose = status_provider_telepathy_dispose;
- object_class->finalize = status_provider_telepathy_finalize;
-
- StatusProviderClass * spclass = STATUS_PROVIDER_CLASS(klass);
-
- spclass->set_status = set_status;
- spclass->get_status = get_status;
-
- return;
-}
-
-
-static void
-status_provider_telepathy_init (StatusProviderTelepathy *self)
-{
- StatusProviderTelepathyPrivate * priv = STATUS_PROVIDER_TELEPATHY_GET_PRIVATE(self);
-
- priv->proxy = NULL;
- priv->dbus_proxy = NULL;
- priv->mc_status = MC_STATUS_OFFLINE;
-
- GError * error = NULL;
-
- /* Grabbing the session bus */
- DBusGConnection * bus = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
- if (bus == NULL) {
- g_warning("Unable to connect to Session Bus: %s", error == NULL ? "No message" : error->message);
- g_error_free(error);
- return;
- }
-
- /* Set up the dbus Proxy */
- priv->dbus_proxy = dbus_g_proxy_new_for_name_owner (bus,
- DBUS_SERVICE_DBUS,
- DBUS_PATH_DBUS,
- DBUS_INTERFACE_DBUS,
- &error);
- if (error != NULL) {
- g_warning("Unable to connect to DBus events: %s", error->message);
- g_error_free(error);
- return;
- }
-
- /* Configure the name owner changing */
- dbus_g_proxy_add_signal(priv->dbus_proxy, "NameOwnerChanged",
- G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
- G_TYPE_INVALID);
- dbus_g_proxy_connect_signal(priv->dbus_proxy, "NameOwnerChanged",
- G_CALLBACK(dbus_namechange),
- self, NULL);
-
- build_telepathy_proxy(self);
-
- return;
-}
-
-/* Builds up the proxy to Mission Control and configures all of the
- signals for getting info from the proxy. Also does a call to get
- the inital value of the status. */
-static void
-build_telepathy_proxy (StatusProviderTelepathy * self)
-{
- g_debug("Building Telepathy Proxy");
- StatusProviderTelepathyPrivate * priv = STATUS_PROVIDER_TELEPATHY_GET_PRIVATE(self);
-
- if (priv->proxy != NULL) {
- g_debug("Hmm, being asked to build a proxy we alredy have.");
- return;
- }
-
- GError * error = NULL;
-
- /* Grabbing the session bus */
- DBusGConnection * session_bus = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
- if (session_bus == NULL) {
- g_warning("Unable to connect to Session Bus: %s", error == NULL ? "No message" : error->message);
- g_error_free(error);
- return;
- }
-
- /* Get the proxy to Mission Control */
- priv->proxy = dbus_g_proxy_new_for_name_owner(session_bus,
- "org.freedesktop.Telepathy.MissionControl",
- "/org/freedesktop/Telepathy/MissionControl",
- "org.freedesktop.Telepathy.MissionControl",
- &error);
-
- if (priv->proxy != NULL) {
- /* If it goes, we set the proxy to NULL */
- g_object_add_weak_pointer (G_OBJECT(priv->proxy), (gpointer *)&priv->proxy);
- /* And we clean up other variables associated */
- g_signal_connect(G_OBJECT(priv->proxy), "destroy",
- G_CALLBACK(proxy_destroy), self);
-
- /* Set up the signal handler for watching when status changes. */
- dbus_g_object_register_marshaller(_status_provider_telepathy_marshal_VOID__UINT_STRING,
- G_TYPE_NONE,
- G_TYPE_UINT,
- G_TYPE_STRING,
- G_TYPE_INVALID);
- dbus_g_proxy_add_signal (priv->proxy,
- "PresenceChanged",
- G_TYPE_UINT,
- G_TYPE_STRING,
- G_TYPE_INVALID);
- dbus_g_proxy_connect_signal(priv->proxy,
- "PresenceChanged",
- G_CALLBACK(changed_status),
- (void *)self,
- NULL);
-
- /* Do a get here, to init the status */
- dbus_g_proxy_begin_call(priv->proxy,
- "GetStatus",
- get_status_async,
- self,
- NULL,
- G_TYPE_INVALID);
- } else {
- g_warning("Unable to connect to Mission Control");
- if (error != NULL) {
- g_error_free(error);
- }
- }
-
- return;
-}
-
-/* Watch to see if the Mission Control comes up on Dbus */
-static void
-dbus_namechange (DBusGProxy * proxy, const gchar * name, const gchar * prev, const gchar * new, StatusProviderTelepathy * self)
-{
- g_return_if_fail(name != NULL);
- g_return_if_fail(new != NULL);
-
- if (g_strcmp0(name, "org.freedesktop.Telepathy.MissionControl") == 0) {
- build_telepathy_proxy(self);
- }
- return;
-}
-
-static void
-status_provider_telepathy_dispose (GObject *object)
-{
- StatusProviderTelepathyPrivate * priv = STATUS_PROVIDER_TELEPATHY_GET_PRIVATE(object);
-
- if (priv->proxy != NULL) {
- g_object_unref(priv->proxy);
- priv->proxy = NULL;
- }
-
- G_OBJECT_CLASS (status_provider_telepathy_parent_class)->dispose (object);
- return;
-}
-
-static void
-status_provider_telepathy_finalize (GObject *object)
-{
-
- G_OBJECT_CLASS (status_provider_telepathy_parent_class)->finalize (object);
- return;
-}
-
-/**
- status_provider_telepathy_new:
-
- Creates a new #StatusProviderTelepathy object. No parameters or anything
- like that. Just a convience function.
-
- Return value: A new instance of #StatusProviderTelepathy
-*/
-StatusProvider *
-status_provider_telepathy_new (void)
-{
- return STATUS_PROVIDER(g_object_new(STATUS_PROVIDER_TELEPATHY_TYPE, NULL));
-}
-
-static void
-set_status (StatusProvider * sp, StatusProviderStatus status)
-{
- StatusProviderTelepathyPrivate * priv = STATUS_PROVIDER_TELEPATHY_GET_PRIVATE(sp);
- if (priv->proxy == NULL) {
- priv->mc_status = MC_STATUS_OFFLINE;
- return;
- }
-
- priv->mc_status = sp_to_mc_map[status];
-
- guint mcstatus = MC_STATUS_UNSET;
- gboolean ret = FALSE;
- GError * error = NULL;
-
- ret = dbus_g_proxy_call(priv->proxy,
- "GetPresence", &error,
- G_TYPE_INVALID,
- G_TYPE_UINT, &mcstatus,
- G_TYPE_INVALID);
-
- /* If we can't get the get call to work, let's not set */
- if (!ret) {
- if (error != NULL) {
- g_error_free(error);
- }
- return;
- }
-
- /* If the get call doesn't return a status, that means that there
- are no clients connected. We don't want to connect them by telling
- MC that we're going online -- we'd like to be more passive than that. */
- if (mcstatus == MC_STATUS_UNSET) {
- return;
- }
-
- ret = dbus_g_proxy_call(priv->proxy,
- "SetPresence", &error,
- G_TYPE_UINT, priv->mc_status,
- G_TYPE_STRING, "",
- G_TYPE_INVALID,
- G_TYPE_INVALID);
-
- if (!ret) {
- if (error != NULL) {
- g_warning("Unable to set Mission Control Presence: %s", error->message);
- g_error_free(error);
- } else {
- g_warning("Unable to set Mission Control Presence");
- }
- return;
- }
-
- return;
-}
-
-static StatusProviderStatus
-get_status (StatusProvider * sp)
-{
- g_return_val_if_fail(IS_STATUS_PROVIDER_TELEPATHY(sp), STATUS_PROVIDER_STATUS_DISCONNECTED);
- StatusProviderTelepathyPrivate * priv = STATUS_PROVIDER_TELEPATHY_GET_PRIVATE(sp);
-
- if (priv->proxy == NULL) {
- return STATUS_PROVIDER_STATUS_DISCONNECTED;
- }
-
- return mc_to_sp_map[priv->mc_status];
-}
-
-static void
-changed_status (DBusGProxy * proxy, guint status, gchar * message, StatusProvider * sp)
-{
- StatusProviderTelepathyPrivate * priv = STATUS_PROVIDER_TELEPATHY_GET_PRIVATE(sp);
- priv->mc_status = status;
- g_signal_emit(G_OBJECT(sp), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, mc_to_sp_map[priv->mc_status], TRUE);
-}
-
-static void
-proxy_destroy (DBusGProxy * proxy, StatusProvider * sp)
-{
- g_debug("Signal: Mission Control proxy destroyed");
- g_signal_emit(G_OBJECT(sp), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, STATUS_PROVIDER_STATUS_OFFLINE, TRUE);
- return;
-}
-
-static void
-get_status_async (DBusGProxy * proxy, DBusGProxyCall * call, gpointer userdata)
-{
- GError * error = NULL;
- guint status = 0;
- if (!dbus_g_proxy_end_call(proxy, call, &error, G_TYPE_UINT, &status, G_TYPE_INVALID)) {
- g_warning("Unable to get type from Mission Control: %s", error->message);
- g_error_free(error);
- return;
- }
-
- StatusProviderTelepathyPrivate * priv = STATUS_PROVIDER_TELEPATHY_GET_PRIVATE(userdata);
-
- gboolean changed = FALSE;
- if (status != priv->mc_status) {
- changed = TRUE;
- }
-
- priv->mc_status = status;
-
- if (changed) {
- g_signal_emit(G_OBJECT(userdata), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, mc_to_sp_map[priv->mc_status], TRUE);
- }
-
- return;
-}
diff --git a/src/status-provider-telepathy.h b/src/status-provider-telepathy.h
deleted file mode 100644
index a67ee40..0000000
--- a/src/status-provider-telepathy.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
-A small wrapper utility to load indicators and put them as menu items
-into the gnome-panel using it's applet interface.
-
-Copyright 2009 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@canonical.com>
-
-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/>.
-*/
-
-#ifndef __STATUS_PROVIDER_TELEPATHY_H__
-#define __STATUS_PROVIDER_TELEPATHY_H__
-
-#include <glib.h>
-#include <glib-object.h>
-
-#include "status-provider.h"
-
-G_BEGIN_DECLS
-
-#define STATUS_PROVIDER_TELEPATHY_TYPE (status_provider_telepathy_get_type ())
-#define STATUS_PROVIDER_TELEPATHY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), STATUS_PROVIDER_TELEPATHY_TYPE, StatusProviderTelepathy))
-#define STATUS_PROVIDER_TELEPATHY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), STATUS_PROVIDER_TELEPATHY_TYPE, StatusProviderTelepathyClass))
-#define IS_STATUS_PROVIDER_TELEPATHY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), STATUS_PROVIDER_TELEPATHY_TYPE))
-#define IS_STATUS_PROVIDER_TELEPATHY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), STATUS_PROVIDER_TELEPATHY_TYPE))
-#define STATUS_PROVIDER_TELEPATHY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), STATUS_PROVIDER_TELEPATHY_TYPE, StatusProviderTelepathyClass))
-
-
-typedef struct _StatusProviderTelepathyClass StatusProviderTelepathyClass;
-struct _StatusProviderTelepathyClass {
- StatusProviderClass parent_class;
-};
-
-typedef struct _StatusProviderTelepathy StatusProviderTelepathy;
-struct _StatusProviderTelepathy {
- StatusProvider parent;
-};
-
-GType status_provider_telepathy_get_type (void);
-StatusProvider * status_provider_telepathy_new (void);
-
-G_END_DECLS
-
-#endif
diff --git a/src/status-provider-telepathy.list b/src/status-provider-telepathy.list
deleted file mode 100644
index 5ab45bf..0000000
--- a/src/status-provider-telepathy.list
+++ /dev/null
@@ -1 +0,0 @@
-VOID:UINT,STRING
diff --git a/src/status-provider.c b/src/status-provider.c
deleted file mode 100644
index 1139e54..0000000
--- a/src/status-provider.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
-A small wrapper utility to load indicators and put them as menu items
-into the gnome-panel using it's applet interface.
-
-Copyright 2009 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@canonical.com>
-
-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/>.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "status-provider.h"
-
-/* Signals */
-enum {
- STATUS_CHANGED,
- LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-/* GObject Boilerplate */
-static void status_provider_class_init (StatusProviderClass *klass);
-static void status_provider_init (StatusProvider *self);
-
-G_DEFINE_TYPE (StatusProvider, status_provider, G_TYPE_OBJECT);
-
-static void
-status_provider_class_init (StatusProviderClass *klass)
-{
- // GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- klass->status_changed = NULL;
-
- klass->set_status = NULL;
- klass->get_status = NULL;
-
- /**
- StatusProvider::status-changed:
- @arg0: The #StatusProvider object.
- @arg1: The new status #StatusProviderStatus
-
- Should be emitted by subclasses everytime that the status
- changes externally to us.
- */
- signals[STATUS_CHANGED] = g_signal_new(STATUS_PROVIDER_SIGNAL_STATUS_CHANGED,
- G_TYPE_FROM_CLASS(klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET(StatusProviderClass, status_changed),
- NULL, NULL,
- g_cclosure_marshal_VOID__UINT,
- G_TYPE_NONE, 1, G_TYPE_UINT);
-
- return;
-}
-
-static void
-status_provider_init (StatusProvider *self)
-{
-
- return;
-}
-
-void
-status_provider_set_status (StatusProvider * sp, StatusProviderStatus status)
-{
- g_return_if_fail(IS_STATUS_PROVIDER(sp));
-
- StatusProviderClass * class = STATUS_PROVIDER_GET_CLASS(sp);
- g_return_if_fail(class != NULL);
- g_return_if_fail(class->set_status != NULL);
-
- return class->set_status(sp, status);
-}
-
-StatusProviderStatus
-status_provider_get_status (StatusProvider * sp)
-{
- g_return_val_if_fail(IS_STATUS_PROVIDER(sp), STATUS_PROVIDER_STATUS_OFFLINE);
-
- StatusProviderClass * class = STATUS_PROVIDER_GET_CLASS(sp);
- g_return_val_if_fail(class->get_status != NULL, STATUS_PROVIDER_STATUS_OFFLINE);
-
- return class->get_status(sp);
-}
-
diff --git a/src/status-provider.h b/src/status-provider.h
deleted file mode 100644
index 45433fc..0000000
--- a/src/status-provider.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
-A small wrapper utility to load indicators and put them as menu items
-into the gnome-panel using it's applet interface.
-
-Copyright 2009 Canonical Ltd.
-
-Authors:
- Ted Gould <ted@canonical.com>
-
-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/>.
-*/
-
-#ifndef __STATUS_PROVIDER_H__
-#define __STATUS_PROVIDER_H__
-
-#include <glib.h>
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-#define STATUS_PROVIDER_EXPORT_TYPE(x) GType status_provider_export_type (void) { return (x); }
-#define STATUS_PROVIDER_EXPORT_S "status_provider_export_type"
-
-#define STATUS_PROVIDER_TYPE (status_provider_get_type ())
-#define STATUS_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), STATUS_PROVIDER_TYPE, StatusProvider))
-#define STATUS_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), STATUS_PROVIDER_TYPE, StatusProviderClass))
-#define IS_STATUS_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), STATUS_PROVIDER_TYPE))
-#define IS_STATUS_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), STATUS_PROVIDER_TYPE))
-#define STATUS_PROVIDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), STATUS_PROVIDER_TYPE, StatusProviderClass))
-
-typedef enum
-{
- STATUS_PROVIDER_STATUS_ONLINE,
- STATUS_PROVIDER_STATUS_AWAY,
- STATUS_PROVIDER_STATUS_DND,
- STATUS_PROVIDER_STATUS_INVISIBLE,
- STATUS_PROVIDER_STATUS_OFFLINE,
- STATUS_PROVIDER_STATUS_DISCONNECTED,
- /* Leave as last */
- STATUS_PROVIDER_STATUS_LAST
-}
-StatusProviderStatus;
-
-#define STATUS_PROVIDER_SIGNAL_STATUS_CHANGED "status-changed"
-#define STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID (g_signal_lookup(STATUS_PROVIDER_SIGNAL_STATUS_CHANGED, STATUS_PROVIDER_TYPE))
-
-typedef struct _StatusProvider StatusProvider;
-struct _StatusProvider {
- GObject parent;
-};
-
-typedef struct _StatusProviderClass StatusProviderClass;
-struct _StatusProviderClass {
- GObjectClass parent_class;
-
- /* Signals */
- void (*status_changed) (StatusProviderStatus newstatus);
-
- /* Virtual Functions */
- void (*set_status) (StatusProvider * sp, StatusProviderStatus newstatus);
- StatusProviderStatus (*get_status) (StatusProvider * sp);
-};
-
-GType status_provider_get_type (void);
-
-void status_provider_set_status (StatusProvider * sp, StatusProviderStatus status);
-StatusProviderStatus status_provider_get_status (StatusProvider * sp);
-
-G_END_DECLS
-
-#endif
diff --git a/test/Makefile.am b/test/Makefile.am
index f2c86d9..4671446 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -1,8 +1,8 @@
check_LIBRARIES = libgtest.a
-check_PROGRAMS = test-gtest
+check_PROGRAMS = test-gactionmuxer
-TESTS =
+TESTS = $(check_PROGRAMS)
AM_CPPFLAGS = $(GTEST_CPPFLAGS) \
-I${top_srcdir}/src
@@ -11,23 +11,22 @@ nodist_libgtest_a_SOURCES = \
$(GTEST_SOURCE)/src/gtest-all.cc \
$(GTEST_SOURCE)/src/gtest_main.cc
libgtest_a_CPPFLAGS = \
- $(GTEST_CPPFLAGS) -w
+ $(GTEST_CPPFLAGS) -w \
+ $(AM_CPPFLAGS)
libgtest_a_CXXFLAGS = \
$(AM_CXXFLAGS)
-libgtest_a_CPPFLAGS = \
- $(AM_CPPFLAGS)
-TESTS += test-gtest
-test_gtest_SOURCES = \
- test-gtest.cpp
-test_gtest_CPPFLAGS = \
+test_gactionmuxer_SOURCES = \
+ test-gactionmuxer.cpp
+
+test_gactionmuxer_CPPFLAGS = \
$(APPLET_CFLAGS) \
$(AM_CPPFLAGS)
-test_gtest_LDFLAGS = -pthread
-test_gtest_LDADD = \
+
+test_gactionmuxer_LDADD = \
$(APPLET_LIBS) \
- $(top_builddir)/src/.libs/libindicator-messages-status-provider.la \
+ libindicator-messages-service.la \
libgtest.a
@@ -35,31 +34,17 @@ test_gtest_LDADD = \
# Lib containing code under test
######################################
-lib_LTLIBRARIES = \
+noinst_LTLIBRARIES = \
libindicator-messages-service.la
-libindicator_messages_service_la_HEADERS = \
- $(top_srcdir)/src/default-applications.h \
- $(top_srcdir)/src/messages-service-dbus.h \
- $(top_builddir)/src/gen-messages-service.xml.h \
- $(top_srcdir)/src/im-menu-item.h \
- $(top_srcdir)/src/app-menu-item.h \
- $(top_srcdir)/src/launcher-menu-item.h \
- $(top_srcdir)/src/seen-db.h \
- $(top_srcdir)/src/dirs.h \
- $(top_srcdir)/src/dbus-data.h \
- $(top_srcdir)/src/status-items.h
-
libindicator_messages_service_la_SOURCES = \
- $(top_srcdir)/src/default-applications.c \
- $(top_srcdir)/src/messages-service.c \
- $(top_srcdir)/src/messages-service-dbus.c \
- $(top_builddir)/src/gen-messages-service.xml.c \
- $(top_srcdir)/src/im-menu-item.c \
- $(top_srcdir)/src/app-menu-item.c \
- $(top_srcdir)/src/launcher-menu-item.c \
- $(top_srcdir)/src/seen-db.c \
- $(top_srcdir)/src/status-items.c
+ $(top_builddir)/src/indicator-messages-service.c \
+ $(top_builddir)/src/indicator-messages-service.h \
+ $(top_srcdir)/src/app-section.c \
+ $(top_srcdir)/src/app-section.h \
+ $(top_srcdir)/src/gactionmuxer.c \
+ $(top_srcdir)/src/gactionmuxer.h
+ $(top_srcdir)/src/dbus-data.h
libindicator_messages_service_ladir = \
$(includedir)/libindicator-messages-service/
@@ -73,12 +58,10 @@ libindicator_messages_service_la_CFLAGS = \
-Wl,-z,defs \
-Wl,--as-needed \
-Werror \
- -DG_LOG_DOMAIN=\"Indicator-Messages\" \
- -DSTATUS_PROVIDER_DIR=\"$(STATUS_PROVIDER_DIR)\"
+ -DG_LOG_DOMAIN=\"Indicator-Messages\"
libindicator_messages_service_la_LIBADD = \
- $(APPLET_LIBS) \
- $(top_builddir)/src/.libs/libindicator-messages-status-provider.la
+ $(APPLET_LIBS)
libindicator_messages_service_la_LDFLAGS = \
$(COVERAGE_LDFLAGS)
diff --git a/test/test-gactionmuxer.cpp b/test/test-gactionmuxer.cpp
new file mode 100644
index 0000000..6304853
--- /dev/null
+++ b/test/test-gactionmuxer.cpp
@@ -0,0 +1,365 @@
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <gtest/gtest.h>
+
+extern "C" {
+#include "app-section.h"
+#include "gactionmuxer.h"
+}
+
+static gboolean
+strv_contains (gchar **str_array,
+ const gchar *str)
+{
+ gchar **it;
+
+ for (it = str_array; *it; it++) {
+ if (!g_strcmp0 (*it, str))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+TEST(GActionMuxerTest, Sanity) {
+ GActionMuxer *muxer;
+
+ g_type_init ();
+
+ g_action_muxer_insert (NULL, NULL, NULL);
+ g_action_muxer_remove (NULL, NULL);
+
+ muxer = g_action_muxer_new ();
+
+ g_action_muxer_insert (muxer, NULL, NULL);
+ g_action_muxer_remove (muxer, NULL);
+ EXPECT_FALSE (g_action_group_has_action (G_ACTION_GROUP (muxer), NULL));
+ EXPECT_FALSE (g_action_group_get_action_enabled (G_ACTION_GROUP (muxer), NULL));
+ EXPECT_FALSE (g_action_group_query_action (G_ACTION_GROUP (muxer), NULL, NULL, NULL, NULL, NULL, NULL));
+ g_action_group_activate_action (G_ACTION_GROUP (muxer), NULL, NULL);
+
+ g_object_unref (muxer);
+}
+
+TEST(GActionMuxerTest, Empty) {
+ GActionMuxer *muxer;
+ gchar **actions;
+
+ g_type_init ();
+
+ muxer = g_action_muxer_new ();
+
+ actions = g_action_group_list_actions (G_ACTION_GROUP (muxer));
+ EXPECT_EQ (0, g_strv_length (actions));
+
+ g_strfreev (actions);
+ g_object_unref (muxer);
+}
+
+TEST(GActionMuxerTest, AddAndRemove) {
+ const GActionEntry entries1[] = { { "one" }, { "two" }, { "three" } };
+ const GActionEntry entries2[] = { { "gb" }, { "es" }, { "fr" } };
+ const GActionEntry entries3[] = { { "foo" }, { "bar" } };
+ GSimpleActionGroup *group1;
+ GSimpleActionGroup *group2;
+ GSimpleActionGroup *group3;
+ GActionMuxer *muxer;
+ gchar **actions;
+
+ g_type_init ();
+
+ group1 = g_simple_action_group_new ();
+ g_simple_action_group_add_entries (group1,
+ entries1,
+ G_N_ELEMENTS (entries1),
+ NULL);
+
+ group2 = g_simple_action_group_new ();
+ g_simple_action_group_add_entries (group2,
+ entries2,
+ G_N_ELEMENTS (entries2),
+ NULL);
+
+ group3 = g_simple_action_group_new ();
+ g_simple_action_group_add_entries (group3,
+ entries3,
+ G_N_ELEMENTS (entries3),
+ NULL);
+
+ muxer = g_action_muxer_new ();
+ g_action_muxer_insert (muxer, "first", G_ACTION_GROUP (group1));
+ g_action_muxer_insert (muxer, "second", G_ACTION_GROUP (group2));
+ g_action_muxer_insert (muxer, NULL, G_ACTION_GROUP (group3));
+
+ actions = g_action_group_list_actions (G_ACTION_GROUP (muxer));
+ EXPECT_TRUE (g_action_group_has_action (G_ACTION_GROUP (muxer), "first.one"));
+ EXPECT_FALSE (g_action_group_has_action (G_ACTION_GROUP (muxer), "one"));
+ EXPECT_EQ (8, g_strv_length (actions));
+ EXPECT_TRUE (strv_contains (actions, "first.one"));
+ EXPECT_TRUE (strv_contains (actions, "first.two"));
+ EXPECT_TRUE (strv_contains (actions, "first.three"));
+ EXPECT_TRUE (strv_contains (actions, "second.gb"));
+ EXPECT_TRUE (strv_contains (actions, "second.es"));
+ EXPECT_TRUE (strv_contains (actions, "second.fr"));
+ EXPECT_TRUE (strv_contains (actions, "foo"));
+ EXPECT_TRUE (strv_contains (actions, "bar"));
+ g_strfreev (actions);
+
+ g_action_muxer_remove (muxer, NULL);
+ EXPECT_FALSE (g_action_group_has_action (G_ACTION_GROUP (muxer), "foo"));
+ EXPECT_TRUE (g_action_group_has_action (G_ACTION_GROUP (muxer), "first.one"));
+ actions = g_action_group_list_actions (G_ACTION_GROUP (muxer));
+ EXPECT_EQ (6, g_strv_length (actions));
+ EXPECT_FALSE (strv_contains (actions, "foo"));
+ EXPECT_TRUE (strv_contains (actions, "first.one"));
+ g_strfreev (actions);
+
+ g_action_muxer_remove (muxer, "first");
+ EXPECT_FALSE (g_action_group_has_action (G_ACTION_GROUP (muxer), "first.two"));
+ EXPECT_TRUE (g_action_group_has_action (G_ACTION_GROUP (muxer), "second.es"));
+ actions = g_action_group_list_actions (G_ACTION_GROUP (muxer));
+ EXPECT_EQ (3, g_strv_length (actions));
+ EXPECT_FALSE (strv_contains (actions, "first.two"));
+ EXPECT_TRUE (strv_contains (actions, "second.es"));
+ g_strfreev (actions);
+
+ g_action_muxer_insert (muxer, "second", G_ACTION_GROUP (group2));
+ actions = g_action_group_list_actions (G_ACTION_GROUP (muxer));
+ EXPECT_EQ (3, g_strv_length (actions));
+ g_strfreev (actions);
+
+ g_action_muxer_insert (muxer, NULL, G_ACTION_GROUP (group3));
+ actions = g_action_group_list_actions (G_ACTION_GROUP (muxer));
+ EXPECT_EQ (5, g_strv_length (actions));
+ g_strfreev (actions);
+
+ g_object_unref (muxer);
+ g_object_unref (group1);
+ g_object_unref (group2);
+ g_object_unref (group3);
+}
+
+static gboolean
+g_variant_equal0 (gconstpointer one,
+ gconstpointer two)
+{
+ if (one == NULL)
+ return two == NULL;
+ else
+ return g_variant_equal (one, two);
+}
+
+TEST(GActionMuxerTest, ActionAttributes) {
+ GSimpleActionGroup *group;
+ GSimpleAction *action;
+ GActionMuxer *muxer;
+ gboolean enabled[2];
+ const GVariantType *param_type[2];
+ const GVariantType *state_type[2];
+ GVariant *state_hint[2];
+ GVariant *state[2];
+
+ g_type_init ();
+
+ group = g_simple_action_group_new ();
+ action = g_simple_action_new ("one", G_VARIANT_TYPE_STRING);
+ g_simple_action_group_insert (group, G_ACTION (action));
+
+ muxer = g_action_muxer_new ();
+ g_action_muxer_insert (muxer, "first", G_ACTION_GROUP (group));
+
+ /* test two of the convenience functions */
+ EXPECT_TRUE (g_action_group_get_action_enabled (G_ACTION_GROUP (muxer), "first.one"));
+ g_simple_action_set_enabled (action, FALSE);
+ EXPECT_FALSE (g_action_group_get_action_enabled (G_ACTION_GROUP (muxer), "first.one"));
+
+ EXPECT_STREQ ((gchar *) g_action_group_get_action_parameter_type (G_ACTION_GROUP (muxer), "first.one"),
+ (gchar *) G_VARIANT_TYPE_STRING);
+
+ /* query_action */
+ g_action_group_query_action (G_ACTION_GROUP (group), "one",
+ &enabled[0], &param_type[0], &state_type[0], &state_hint[0], &state[0]);
+ g_action_group_query_action (G_ACTION_GROUP (muxer), "first.one",
+ &enabled[1], &param_type[1], &state_type[1], &state_hint[1], &state[1]);
+ EXPECT_EQ (enabled[0], enabled[1]);
+ EXPECT_STREQ ((gchar *) param_type[0], (gchar *) param_type[1]);
+ EXPECT_STREQ ((gchar *) state_type[0], (gchar *) state_type[1]);
+ EXPECT_TRUE (g_variant_equal0 ((gconstpointer) state_hint[0], (gconstpointer) state_hint[1]));
+ EXPECT_TRUE (g_variant_equal0 ((gconstpointer) state[0], (gconstpointer) state[1]));
+
+ g_object_unref (action);
+ g_object_unref (group);
+ g_object_unref (muxer);
+}
+
+typedef struct {
+ gboolean signal_ran;
+ const gchar *name;
+} TestSignalClosure;
+
+static void
+action_added (GActionGroup *group,
+ gchar *action_name,
+ gpointer user_data)
+{
+ TestSignalClosure *c = (TestSignalClosure *)user_data;
+ EXPECT_STREQ (c->name, action_name);
+ c->signal_ran = TRUE;
+}
+
+static void
+action_enabled_changed (GActionGroup *group,
+ gchar *action_name,
+ gboolean enabled,
+ gpointer user_data)
+{
+ TestSignalClosure *c = (TestSignalClosure *)user_data;
+ EXPECT_EQ (enabled, FALSE);
+ c->signal_ran = TRUE;
+}
+
+static void
+action_state_changed (GActionGroup *group,
+ gchar *action_name,
+ GVariant *value,
+ gpointer user_data)
+{
+ TestSignalClosure *c = (TestSignalClosure *)user_data;
+ EXPECT_STREQ (g_variant_get_string (value, NULL), "off");
+ c->signal_ran = TRUE;
+}
+
+static void
+action_removed (GActionGroup *group,
+ gchar *action_name,
+ gpointer user_data)
+{
+ TestSignalClosure *c = (TestSignalClosure *)user_data;
+ EXPECT_STREQ (c->name, action_name);
+ c->signal_ran = TRUE;
+}
+
+TEST(GActionMuxerTest, Signals) {
+ GSimpleActionGroup *group;
+ GSimpleAction *action;
+ GActionMuxer *muxer;
+ TestSignalClosure closure;
+
+ group = g_simple_action_group_new ();
+
+ action = g_simple_action_new ("one", G_VARIANT_TYPE_STRING);
+ g_simple_action_group_insert (group, G_ACTION (action));
+ g_object_unref (action);
+
+ muxer = g_action_muxer_new ();
+
+ g_signal_connect (muxer, "action-added",
+ G_CALLBACK (action_added), (gpointer) &closure);
+ g_signal_connect (muxer, "action-enabled-changed",
+ G_CALLBACK (action_enabled_changed), (gpointer) &closure);
+ g_signal_connect (muxer, "action-state-changed",
+ G_CALLBACK (action_state_changed), (gpointer) &closure);
+ g_signal_connect (muxer, "action-removed",
+ G_CALLBACK (action_removed), (gpointer) &closure);
+
+ /* add the group with "one" action and check whether the signal is emitted */
+ closure.signal_ran = FALSE;
+ closure.name = "first.one";
+ g_action_muxer_insert (muxer, "first", G_ACTION_GROUP (group));
+ EXPECT_TRUE (closure.signal_ran);
+
+ /* add a second action after the group was added to the muxer */
+ closure.signal_ran = FALSE;
+ closure.name = "first.two";
+ action = g_simple_action_new_stateful ("two", G_VARIANT_TYPE_STRING,
+ g_variant_new_string ("on"));
+ g_simple_action_group_insert (group, G_ACTION (action));
+ EXPECT_TRUE (closure.signal_ran);
+
+ /* disable the action */
+ closure.signal_ran = FALSE;
+ g_simple_action_set_enabled (action, FALSE);
+ EXPECT_TRUE (closure.signal_ran);
+
+ /* change its state */
+ closure.signal_ran = FALSE;
+ g_simple_action_set_state (action, g_variant_new_string ("off"));
+ EXPECT_TRUE (closure.signal_ran);
+ g_object_unref (action);
+
+ /* remove the first action */
+ closure.signal_ran = FALSE;
+ closure.name = "first.one";
+ g_simple_action_group_remove (group, "one");
+ EXPECT_TRUE (closure.signal_ran);
+
+ /* remove the whole group, should be notified about "first.two" */
+ closure.signal_ran = FALSE;
+ closure.name = "first.two";
+ g_action_muxer_remove (muxer, "first");
+ EXPECT_TRUE (closure.signal_ran);
+
+ g_object_unref (group);
+ g_object_unref (muxer);
+}
+
+static void
+action_activated (GSimpleAction *simple,
+ GVariant *parameter,
+ gpointer user_data)
+{
+ gboolean *signal_ran = (gboolean *)user_data;
+
+ EXPECT_STREQ (g_variant_get_string (parameter, NULL), "value");
+ *signal_ran = TRUE;
+}
+
+static void
+action_change_state (GSimpleAction *simple,
+ GVariant *value,
+ gpointer user_data)
+{
+ gboolean *signal_ran = (gboolean *)user_data;
+
+ EXPECT_STREQ (g_variant_get_string (value, NULL), "off");
+ *signal_ran = TRUE;
+}
+
+TEST(GActionMuxerTest, ActivateAction) {
+ GSimpleActionGroup *group;
+ GSimpleAction *action;
+ GActionMuxer *muxer;
+ gboolean signal_ran;
+
+ group = g_simple_action_group_new ();
+
+ action = g_simple_action_new ("one", G_VARIANT_TYPE_STRING);
+ g_simple_action_group_insert (group, G_ACTION (action));
+ g_signal_connect (action, "activate",
+ G_CALLBACK (action_activated), (gpointer) &signal_ran);
+ g_object_unref (action);
+
+ action = g_simple_action_new_stateful ("two", NULL,
+ g_variant_new_string ("on"));
+ g_simple_action_group_insert (group, G_ACTION (action));
+ g_signal_connect (action, "change-state",
+ G_CALLBACK (action_change_state), (gpointer) &signal_ran);
+ g_object_unref (action);
+
+ muxer = g_action_muxer_new ();
+ g_action_muxer_insert (muxer, "first", G_ACTION_GROUP (group));
+
+ signal_ran = FALSE;
+ g_action_group_activate_action (G_ACTION_GROUP (muxer), "first.one",
+ g_variant_new_string ("value"));
+ EXPECT_TRUE (signal_ran);
+
+ signal_ran = FALSE;
+ g_action_group_change_action_state (G_ACTION_GROUP (muxer), "first.two",
+ g_variant_new_string ("off"));
+ EXPECT_TRUE (signal_ran);
+
+ g_object_unref (group);
+ g_object_unref (muxer);
+}
diff --git a/test/test-gtest.cpp b/test/test-gtest.cpp
deleted file mode 100644
index 1cc97be..0000000
--- a/test/test-gtest.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-
-#include <glib.h>
-#include <gtest/gtest.h>
-
-extern "C" {
- #include "launcher-menu-item.h"
-}
-
-TEST(LauncherMenuItem, NameInitialized) {
- g_type_init();
- EXPECT_TRUE(true);
-}