aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTed Gould <ted@canonical.com>2009-09-04 09:11:59 -0500
committerTed Gould <ted@canonical.com>2009-09-04 09:11:59 -0500
commit506e943af17db275f1c83c7a4726d2d15897a09e (patch)
tree16028144fa8f3bddb4a176ed093fc093ecabd654
parent10327267daa9e4ba36ace96f637beaa9045e0895 (diff)
parenta04a5e11668a7068f1307b28911df1097ab983cf (diff)
downloadayatana-indicator-messages-506e943af17db275f1c83c7a4726d2d15897a09e.tar.gz
ayatana-indicator-messages-506e943af17db275f1c83c7a4726d2d15897a09e.tar.bz2
ayatana-indicator-messages-506e943af17db275f1c83c7a4726d2d15897a09e.zip
Merging in the dbusmenu011 branch. I think I got other trunk updates as well.
-rw-r--r--.bzrignore2
-rw-r--r--configure.ac6
-rw-r--r--data/indicator-messages.service.in2
-rw-r--r--src/Makefile.am52
-rw-r--r--src/app-menu-item.c31
-rw-r--r--src/app-menu-item.h1
-rw-r--r--src/dbus-data.h11
-rw-r--r--src/dirs.h3
-rw-r--r--src/im-menu-item.c16
-rw-r--r--src/indicator-messages.c146
-rw-r--r--src/indicator-service.c505
-rw-r--r--src/launcher-menu-item.c212
-rw-r--r--src/launcher-menu-item.h66
-rw-r--r--src/messages-service-dbus.c184
-rw-r--r--src/messages-service-dbus.h62
-rw-r--r--src/messages-service.c1065
-rw-r--r--src/messages-service.xml25
17 files changed, 1863 insertions, 526 deletions
diff --git a/.bzrignore b/.bzrignore
index 42bdaa3..1d6767c 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -19,3 +19,5 @@ 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
diff --git a/configure.ac b/configure.ac
index 9093512..a3d937b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4,7 +4,7 @@ AC_INIT(src/indicator-messages.c)
AC_PREREQ(2.53)
AM_CONFIG_HEADER(config.h)
-AM_INIT_AUTOMAKE(indicator-messages, 0.2.0dev)
+AM_INIT_AUTOMAKE(indicator-messages, 0.2.0)
AM_MAINTAINER_MODE
@@ -17,6 +17,8 @@ AC_PROG_LIBTOOL
AC_SUBST(VERSION)
AC_CONFIG_MACRO_DIR([m4])
+m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
+
###########################
# Dependencies
###########################
@@ -26,7 +28,7 @@ GIO_UNIX_REQUIRED_VERSION=2.18
PANEL_REQUIRED_VERSION=2.0.0
INDICATE_REQUIRED_VERSION=0.2.0
INDICATOR_REQUIRED_VERSION=0.2.0
-DBUSMENUGTK_REQUIRED_VERSION=0.0.0
+DBUSMENUGTK_REQUIRED_VERSION=0.1.1
PKG_CHECK_MODULES(APPLET, gtk+-2.0 >= $GTK_REQUIRED_VERSION
gio-unix-2.0 >= $GIO_UNIX_REQUIRED_VERSION
diff --git a/data/indicator-messages.service.in b/data/indicator-messages.service.in
index 908079a..9ae3960 100644
--- a/data/indicator-messages.service.in
+++ b/data/indicator-messages.service.in
@@ -1,3 +1,3 @@
[D-BUS Service]
-Name=com.ubuntu.indicator.messages
+Name=org.ayatana.indicator.messages
Exec=@prefix@/bin/indicator-messages-service
diff --git a/src/Makefile.am b/src/Makefile.am
index 74db2df..38787a1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,13 +1,61 @@
bin_PROGRAMS = indicator-messages-service
+######################################
+# Building the messages indicator
+######################################
+
messaginglibdir = $(INDICATORDIR)
messaginglib_LTLIBRARIES = libmessaging.la
-libmessaging_la_SOURCES = indicator-messages.c im-menu-item.c im-menu-item.h app-menu-item.c app-menu-item.h
+libmessaging_la_SOURCES = \
+ indicator-messages.c \
+ messages-service-client.h \
+ dbus-data.h
libmessaging_la_CFLAGS = $(APPLET_CFLAGS) -Wall -Wl,-Bsymbolic-functions -Wl,-z,defs -Wl,--as-needed -Werror
libmessaging_la_LIBADD = $(APPLET_LIBS)
libmessaging_la_LDFLAGS = -module -avoid-version
-indicator_messages_service_SOURCES = indicator-service.c im-menu-item.c im-menu-item.h app-menu-item.c app-menu-item.h
+######################################
+# Building the messages service
+######################################
+
+indicator_messages_service_SOURCES = \
+ messages-service.c \
+ messages-service-server.h \
+ messages-service-dbus.c \
+ messages-service-dbus.h \
+ im-menu-item.c \
+ im-menu-item.h \
+ app-menu-item.c \
+ app-menu-item.h \
+ launcher-menu-item.c \
+ launcher-menu-item.h \
+ dirs.h \
+ dbus-data.h
indicator_messages_service_CFLAGS = $(APPLET_CFLAGS) -Wall -Wl,-Bsymbolic-functions -Wl,-z,defs -Wl,--as-needed -Werror
indicator_messages_service_LDADD = $(APPLET_LIBS)
+
+messages-service-client.h: $(srcdir)/messages-service.xml
+ dbus-binding-tool \
+ --prefix=_messages_service_client \
+ --mode=glib-client \
+ --output=messages-service-client.h \
+ $(srcdir)/messages-service.xml
+
+messages-service-server.h: $(srcdir)/messages-service.xml
+ dbus-binding-tool \
+ --prefix=_messages_service_server \
+ --mode=glib-server \
+ --output=messages-service-server.h \
+ $(srcdir)/messages-service.xml
+
+BUILT_SOURCES = \
+ messages-service-client.h \
+ messages-service-server.h
+
+CLEANFILES = \
+ $(BUILT_SOURCES)
+
+EXTRA_DIST = \
+ messages-service.xml
+
diff --git a/src/app-menu-item.c b/src/app-menu-item.c
index 2212be5..feb780d 100644
--- a/src/app-menu-item.c
+++ b/src/app-menu-item.c
@@ -45,6 +45,7 @@ struct _AppMenuItemPrivate
gchar * type;
GAppInfo * appinfo;
+ gchar * desktop;
guint unreadcount;
gboolean count_on_label;
};
@@ -105,6 +106,7 @@ app_menu_item_init (AppMenuItem *self)
priv->server = NULL;
priv->type = NULL;
priv->appinfo = NULL;
+ priv->desktop = NULL;
priv->unreadcount = 0;
priv->count_on_label = FALSE;
@@ -136,6 +138,10 @@ app_menu_item_finalize (GObject *object)
g_free(priv->type);
}
+ if (priv->desktop != NULL) {
+ g_free(priv->desktop);
+ }
+
if (priv->appinfo != NULL) {
g_object_unref(priv->appinfo);
}
@@ -212,10 +218,10 @@ update_label (AppMenuItem * self)
/* 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)"), app_menu_item_get_name(self), priv->unreadcount);
- dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), "label", label);
+ dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_LABEL, label);
g_free(label);
} else {
- dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), "label", app_menu_item_get_name(self));
+ dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_LABEL, app_menu_item_get_name(self));
}
return;
@@ -229,15 +235,23 @@ desktop_cb (IndicateListener * listener, IndicateListenerServer * server, gchar
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;
}
-
+
priv->appinfo = G_APP_INFO(g_desktop_app_info_new_from_filename(value));
g_return_if_fail(priv->appinfo != NULL);
+ priv->desktop = g_strdup(value);
+
update_label(self);
g_signal_emit(G_OBJECT(self), signals[NAME_CHANGED], 0, app_menu_item_get_name(self), TRUE);
@@ -297,6 +311,7 @@ indicator_removed_cb (IndicateListener * listener, IndicateListenerServer * serv
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;
@@ -304,6 +319,7 @@ app_menu_item_get_count (AppMenuItem * appitem)
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;
@@ -312,6 +328,7 @@ app_menu_item_get_server (AppMenuItem * appitem) {
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) {
@@ -320,3 +337,11 @@ app_menu_item_get_name (AppMenuItem * appitem)
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;
+}
diff --git a/src/app-menu-item.h b/src/app-menu-item.h
index dda4765..583d50d 100644
--- a/src/app-menu-item.h
+++ b/src/app-menu-item.h
@@ -59,6 +59,7 @@ AppMenuItem * app_menu_item_new (IndicateListener * listener, IndicateListenerSe
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);
G_END_DECLS
diff --git a/src/dbus-data.h b/src/dbus-data.h
index 4aeedf0..db59003 100644
--- a/src/dbus-data.h
+++ b/src/dbus-data.h
@@ -2,7 +2,14 @@
#ifndef __DBUS_DATA_H__
#define __DBUS_DATA_H__ 1
-#define INDICATOR_MESSAGES_DBUS_NAME "com.ubuntu.indicator.messages"
-#define INDICATOR_MESSAGES_DBUS_OBJECT "/com/ubuntu/indicator/messages"
+#define INDICATOR_MESSAGES_DBUS_NAME "org.ayatana.indicator.messages"
+#define INDICATOR_MESSAGES_DBUS_OBJECT "/org/ayatana/indicator/messages/menu"
+
+#define INDICATOR_MESSAGES_DBUS_SERVICE_OBJECT "/org/ayatana/indicator/messages/service"
+#define INDICATOR_MESSAGES_DBUS_SERVICE_INTERFACE "org.ayatana.indicator.messages.service"
+
+#define LAUNCHER_MENUITEM_TYPE "launcher-item"
+#define LAUNCHER_MENUITEM_PROP_APP_NAME "application-name"
+#define LAUNCHER_MENUITEM_PROP_APP_DESC "application-description"
#endif /* __DBUS_DATA_H__ */
diff --git a/src/dirs.h b/src/dirs.h
new file mode 100644
index 0000000..a994ff5
--- /dev/null
+++ b/src/dirs.h
@@ -0,0 +1,3 @@
+#define SYSTEM_APPS_DIR "/etc/indicators/messages/applications"
+#define USER_APPS_DIR "indicators/messages/applications"
+#define USER_BLACKLIST_DIR "indicators/messages/applications-blacklist"
diff --git a/src/im-menu-item.c b/src/im-menu-item.c
index d35684e..6f58ebd 100644
--- a/src/im-menu-item.c
+++ b/src/im-menu-item.c
@@ -24,6 +24,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#endif
#include <glib/gi18n.h>
+#include <libdbusmenu-glib/client.h>
#include <libindicate-gtk/indicator.h>
#include <libindicate-gtk/listener.h>
#include "im-menu-item.h"
@@ -73,7 +74,7 @@ static void icon_cb (IndicateListener * listener,
IndicateListenerServer * server,
IndicateListenerIndicator * indicator,
gchar * property,
- GdkPixbuf * propertydata,
+ gchar * propertydata,
gpointer data);
static void activate_cb (ImMenuItem * self,
gpointer data);
@@ -147,10 +148,9 @@ im_menu_item_finalize (GObject *object)
}
static void
-icon_cb (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * property, GdkPixbuf * propertydata, gpointer data)
+icon_cb (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * property, gchar * propertydata, gpointer data)
{
- /* dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), "icon", propertydata); */
-
+ dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(data), DBUSMENU_MENUITEM_PROP_ICON_DATA, propertydata);
return;
}
@@ -253,7 +253,7 @@ sender_cb (IndicateListener * listener, IndicateListenerServer * server, Indicat
return;
}
- dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), "label", propertydata);
+ dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_LABEL, propertydata);
return;
}
@@ -280,7 +280,7 @@ indicator_modified_cb (IndicateListener * listener, IndicateListenerServer * ser
} else if (!g_strcmp0(property, "time")) {
indicate_listener_get_property_time(listener, server, indicator, "time", time_cb, self);
} else if (!g_strcmp0(property, "icon")) {
- indicate_listener_get_property_icon(listener, server, indicator, "icon", icon_cb, self);
+ indicate_listener_get_property(listener, server, indicator, "icon", icon_cb, self);
}
return;
@@ -299,9 +299,11 @@ im_menu_item_new (IndicateListener * listener, IndicateListenerServer * server,
priv->show_time = show_time;
priv->time_update_min = 0;
+ dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), "type", DBUSMENU_CLIENT_TYPES_IMAGE);
+
indicate_listener_get_property(listener, server, indicator, "sender", sender_cb, self);
indicate_listener_get_property_time(listener, server, indicator, "time", time_cb, self);
- indicate_listener_get_property_icon(listener, server, indicator, "icon", icon_cb, self);
+ indicate_listener_get_property(listener, server, indicator, "icon", icon_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);
diff --git a/src/indicator-messages.c b/src/indicator-messages.c
index efb52b2..c410ef7 100644
--- a/src/indicator-messages.c
+++ b/src/indicator-messages.c
@@ -21,6 +21,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
+#include <glib.h>
#include <gtk/gtk.h>
#include <libdbusmenu-gtk/menu.h>
#include <dbus/dbus-glib.h>
@@ -31,12 +32,144 @@ INDICATOR_SET_VERSION
INDICATOR_SET_NAME("messages")
#include "dbus-data.h"
+#include "messages-service-client.h"
-static GtkWidget * main_image;
+static GtkWidget * main_image = NULL;
#define DESIGN_TEAM_SIZE design_team_size
static GtkIconSize design_team_size;
+static DBusGProxy * icon_proxy = NULL;
+
+static void
+attention_changed_cb (DBusGProxy * proxy, gboolean dot, gpointer userdata)
+{
+ if (dot) {
+ gtk_image_set_from_icon_name(GTK_IMAGE(main_image), "indicator-messages-new", DESIGN_TEAM_SIZE);
+ } else {
+ gtk_image_set_from_icon_name(GTK_IMAGE(main_image), "indicator-messages", DESIGN_TEAM_SIZE);
+ }
+ return;
+}
+
+static void
+icon_changed_cb (DBusGProxy * proxy, gboolean hidden, gpointer userdata)
+{
+ if (hidden) {
+ gtk_widget_hide(main_image);
+ } else {
+ gtk_widget_show(main_image);
+ }
+ return;
+}
+
+static void
+watch_cb (DBusGProxy * proxy, GError * error, gpointer userdata)
+{
+ if (error != NULL) {
+ g_warning("Watch failed! %s", error->message);
+ g_error_free(error);
+ }
+ return;
+}
+
+static void
+attention_cb (DBusGProxy * proxy, gboolean dot, GError * error, gpointer userdata)
+{
+ if (error != NULL) {
+ g_warning("Unable to get attention status: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ return attention_changed_cb(proxy, dot, userdata);
+}
+
+static void
+icon_cb (DBusGProxy * proxy, gboolean hidden, GError * error, gpointer userdata)
+{
+ if (error != NULL) {
+ g_warning("Unable to get icon visibility: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ return icon_changed_cb(proxy, hidden, userdata);
+}
+
+static gboolean
+setup_icon_proxy (gpointer userdata)
+{
+ DBusGConnection * connection = dbus_g_bus_get(DBUS_BUS_SESSION, NULL);
+ if (connection == NULL) {
+ g_warning("Unable to get session bus");
+ return FALSE; /* TRUE? */
+ }
+
+ icon_proxy = dbus_g_proxy_new_for_name(connection,
+ INDICATOR_MESSAGES_DBUS_NAME,
+ INDICATOR_MESSAGES_DBUS_SERVICE_OBJECT,
+ INDICATOR_MESSAGES_DBUS_SERVICE_INTERFACE);
+ if (icon_proxy == NULL) {
+ g_warning("Unable to get messages service interface.");
+ return FALSE;
+ }
+
+ org_ayatana_indicator_messages_service_watch_async(icon_proxy, watch_cb, NULL);
+
+ dbus_g_proxy_add_signal(icon_proxy, "AttentionChanged", G_TYPE_BOOLEAN, G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal(icon_proxy,
+ "AttentionChanged",
+ G_CALLBACK(attention_changed_cb),
+ NULL,
+ NULL);
+
+ dbus_g_proxy_add_signal(icon_proxy, "IconChanged", G_TYPE_BOOLEAN, G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal(icon_proxy,
+ "IconChanged",
+ G_CALLBACK(icon_changed_cb),
+ NULL,
+ NULL);
+
+ org_ayatana_indicator_messages_service_attention_requested_async(icon_proxy, attention_cb, NULL);
+ org_ayatana_indicator_messages_service_icon_shown_async(icon_proxy, icon_cb, NULL);
+
+ return FALSE;
+}
+
+static gboolean
+new_launcher_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client)
+{
+ 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 */
+
+ GtkMenuItem * gmi = GTK_MENU_ITEM(gtk_menu_item_new());
+
+ GtkWidget * vbox = gtk_vbox_new(TRUE, 2);
+
+ GtkWidget * app_label = gtk_label_new(dbusmenu_menuitem_property_get(newitem, LAUNCHER_MENUITEM_PROP_APP_NAME));
+ gtk_misc_set_alignment(GTK_MISC(app_label), 0.0, 0.5);
+ GtkWidget * dsc_label = gtk_label_new("");
+ gtk_misc_set_alignment(GTK_MISC(dsc_label), 0.05, 0.5);
+ gtk_label_set_ellipsize(GTK_LABEL(dsc_label), PANGO_ELLIPSIZE_END);
+ gchar * markup = g_markup_printf_escaped("<span font-size=\"smaller\">%s</span>", dbusmenu_menuitem_property_get(newitem, LAUNCHER_MENUITEM_PROP_APP_DESC));
+ gtk_label_set_markup(GTK_LABEL(dsc_label), markup);
+ g_free(markup);
+
+ gtk_box_pack_start(GTK_BOX(vbox), app_label, FALSE, FALSE, 0);
+ gtk_widget_show(app_label);
+ gtk_box_pack_start(GTK_BOX(vbox), dsc_label, FALSE, FALSE, 0);
+ gtk_widget_show(dsc_label);
+
+ gtk_container_add(GTK_CONTAINER(gmi), GTK_WIDGET(vbox));
+ gtk_widget_show(GTK_WIDGET(vbox));
+
+ dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, gmi, parent);
+
+ return TRUE;
+}
+
GtkLabel *
get_label (void)
{
@@ -51,8 +184,6 @@ get_icon (void)
main_image = gtk_image_new_from_icon_name("indicator-messages", DESIGN_TEAM_SIZE);
gtk_widget_show(main_image);
- /* Need a proxy here to figure out when the icon changes */
-
return GTK_IMAGE(main_image);
}
@@ -76,6 +207,13 @@ get_menu (void)
return NULL;
}
- return GTK_MENU(dbusmenu_gtkmenu_new(INDICATOR_MESSAGES_DBUS_NAME, INDICATOR_MESSAGES_DBUS_OBJECT));
+ g_idle_add(setup_icon_proxy, NULL);
+
+ 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), LAUNCHER_MENUITEM_TYPE, new_launcher_item);
+
+ return GTK_MENU(menu);
}
diff --git a/src/indicator-service.c b/src/indicator-service.c
deleted file mode 100644
index cb7e23a..0000000
--- a/src/indicator-service.c
+++ /dev/null
@@ -1,505 +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/>.
-*/
-
-#include <string.h>
-#include <dbus/dbus-glib-bindings.h>
-#include <libindicate/listener.h>
-
-#include <libdbusmenu-glib/server.h>
-
-#include "im-menu-item.h"
-#include "app-menu-item.h"
-#include "dbus-data.h"
-
-static IndicateListener * listener;
-static GList * serverList;
-
-static DbusmenuMenuitem * root_menuitem = NULL;
-static GMainLoop * mainloop = NULL;
-
-
-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 reconsile_list_and_menu (GList * serverlist, DbusmenuMenuitem * menushell);
-static void indicator_removed (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * type, gpointer data);
-
-typedef struct _serverList_t serverList_t;
-struct _serverList_t {
- IndicateListenerServer * server;
- AppMenuItem * menuitem;
- GList * imList;
-};
-
-static gint
-serverList_equal (gconstpointer a, gconstpointer b)
-{
- serverList_t * pa, * pb;
-
- pa = (serverList_t *)a;
- pb = (serverList_t *)b;
-
- const gchar * pas = INDICATE_LISTENER_SERVER_DBUS_NAME(pa->server);
- const gchar * pbs = INDICATE_LISTENER_SERVER_DBUS_NAME(pb->server);
-
- return g_strcmp0(pas, pbs);
-}
-
-static gint
-serverList_sort (gconstpointer a, gconstpointer b)
-{
- serverList_t * pa, * pb;
-
- pa = (serverList_t *)a;
- pb = (serverList_t *)b;
-
- 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);
-}
-
-typedef struct _imList_t imList_t;
-struct _imList_t {
- IndicateListenerServer * server;
- IndicateListenerIndicator * indicator;
- DbusmenuMenuitem * menuitem;
- gulong timechange_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)));
-}
-
-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;
- }
-
- AppMenuItem * menuitem = app_menu_item_new(listener, server);
- g_signal_connect(G_OBJECT(menuitem), APP_MENU_ITEM_SIGNAL_COUNT_CHANGED, G_CALLBACK(server_count_changed), NULL);
- g_signal_connect(G_OBJECT(menuitem), APP_MENU_ITEM_SIGNAL_NAME_CHANGED, G_CALLBACK(server_name_changed), menushell);
-
- serverList_t * sl_item = g_new0(serverList_t, 1);
- sl_item->server = server;
- sl_item->menuitem = menuitem;
- sl_item->imList = NULL;
-
- /* Incase we got an indicator first */
- GList * alreadythere = g_list_find_custom(serverList, sl_item, serverList_equal);
- if (alreadythere != NULL) {
- g_free(sl_item);
- sl_item = (serverList_t *)alreadythere->data;
- sl_item->menuitem = menuitem;
- serverList = g_list_sort(serverList, serverList_sort);
- } else {
- serverList = g_list_insert_sorted(serverList, sl_item, serverList_sort);
- }
-
- dbusmenu_menuitem_child_append(menushell, DBUSMENU_MENUITEM(menuitem));
- /* Should be prepend ^ */
-
- reconsile_list_and_menu(serverList, menushell);
-
- return;
-}
-
-static void
-server_name_changed (AppMenuItem * appitem, gchar * name, gpointer data)
-{
- serverList = g_list_sort(serverList, serverList_sort);
- reconsile_list_and_menu(serverList, DBUSMENU_MENUITEM(data));
- return;
-}
-
-static void
-server_count_changed (AppMenuItem * appitem, guint count, gpointer data)
-{
- static gboolean showing_new_icon = FALSE;
-
- /* Quick check for a common case */
- if (count != 0 && showing_new_icon) {
- return;
- }
-
- /* Odd that we'd get a signal in this case, but let's
- take it out of the mix too */
- if (count == 0 && !showing_new_icon) {
- return;
- }
-
- if (count != 0) {
- g_debug("Setting image to 'new'");
- showing_new_icon = TRUE;
- /* gtk_image_set_from_icon_name(GTK_IMAGE(main_image), "indicator-messages-new", DESIGN_TEAM_SIZE); */
- return;
- }
-
- /* Okay, now at this point the count is zero and it
- might result in a switching of the icon back to being
- the plain one. Let's check. */
-
- gboolean we_have_indicators = FALSE;
- GList * appitems = serverList;
- for (; appitems != NULL; appitems = appitems->next) {
- AppMenuItem * appitem = ((serverList_t *)appitems->data)->menuitem;
- if (app_menu_item_get_count(appitem) != 0) {
- we_have_indicators = TRUE;
- break;
- }
- }
-
- if (!we_have_indicators) {
- g_debug("Setting image to boring");
- showing_new_icon = FALSE;
- /* gtk_image_set_from_icon_name(GTK_IMAGE(main_image), "indicator-messages", DESIGN_TEAM_SIZE); */
- }
-
- return;
-}
-
-static void
-im_time_changed (ImMenuItem * imitem, glong seconds, gpointer data)
-{
- serverList_t * sl = (serverList_t *)data;
- sl->imList = g_list_sort(sl->imList, imList_sort);
- reconsile_list_and_menu(serverList, root_menuitem);
- return;
-}
-
-static void
-server_removed (IndicateListener * listener, IndicateListenerServer * server, gchar * type, gpointer data)
-{
- g_debug("Removing server: %s", INDICATE_LISTENER_SERVER_DBUS_NAME(server));
- serverList_t slt;
- slt.server = server;
- GList * lookup = g_list_find_custom(serverList, &slt, serverList_equal);
-
- if (lookup == NULL) {
- g_debug("\tUnable to find server: %s", INDICATE_LISTENER_SERVER_DBUS_NAME(server));
- return;
- }
-
- serverList_t * sltp = (serverList_t *)lookup->data;
-
- while (sltp->imList) {
- imList_t * imitem = (imList_t *)sltp->imList->data;
- indicator_removed(listener, server, imitem->indicator, "message", data);
- }
-
- serverList = g_list_remove(serverList, sltp);
-
- if (sltp->menuitem != NULL) {
- dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(sltp->menuitem), "visibile", "false");
- dbusmenu_menuitem_child_delete(DBUSMENU_MENUITEM(data), DBUSMENU_MENUITEM(sltp->menuitem));
- g_object_unref(G_OBJECT(sltp->menuitem));
- }
-
- g_free(sltp);
-
- /* Simulate a server saying zero to recalculate icon */
- server_count_changed(NULL, 0, NULL);
-
- return;
-}
-
-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;
-
- msl->position++;
-
- if (!IS_APP_MENU_ITEM(data_mi)) {
- 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;
-}
-
-static void
-reconsile_list_and_menu (GList * serverlist, DbusmenuMenuitem * menushell)
-{
- guint position = 0;
- GList * serverentry;
-
- g_debug("Reordering Menu:");
-
- for (serverentry = serverList; serverentry != NULL; serverentry = serverentry->next) {
- serverList_t * si = (serverList_t *)serverentry->data;
- 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++;
- }
-
- 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);
- dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(imi->menuitem), position);
- position++;
- }
- }
- }
-
- return;
-}
-
-static void
-subtype_cb (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * property, gchar * propertydata, gpointer data)
-{
- DbusmenuMenuitem * menushell = DBUSMENU_MENUITEM(data);
- if (menushell == NULL) {
- g_error("Data in callback is not a menushell");
- return;
- }
-
- if (property == NULL || g_strcmp0(property, "subtype")) {
- /* We should only ever get subtypes, but just in case */
- g_warning("Subtype callback got a property '%s'", property);
- return;
- }
-
- if (propertydata == NULL || propertydata[0] == '\0') {
- /* It's possible that this message didn't have a subtype. That's
- * okay, but we don't want to display those */
- g_debug("No subtype");
- return;
- }
-
- g_debug("Message subtype: %s", propertydata);
-
- if (!g_strcmp0(propertydata, "im") || !g_strcmp0(propertydata, "login")) {
- imList_t * listItem = g_new0(imList_t, 1);
- listItem->server = server;
- listItem->indicator = indicator;
-
- g_debug("Building IM Item");
- ImMenuItem * menuitem = im_menu_item_new(listener, server, indicator, !g_strcmp0(propertydata, "im"));
- g_object_ref(G_OBJECT(menuitem));
- listItem->menuitem = DBUSMENU_MENUITEM(menuitem);
-
- g_debug("Finding the server entry");
- serverList_t sl_item_local;
- 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 */
- serverList_t * sl_item = g_new0(serverList_t, 1);
- sl_item->server = server;
- sl_item->menuitem = NULL;
- sl_item->imList = NULL;
-
- serverList = g_list_insert_sorted(serverList, sl_item, serverList_sort);
- } else {
- sl_item = (serverList_t *)serverentry->data;
- }
-
- g_debug("Adding to 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);
-
- g_debug("Placing in Shell");
- 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));
- }
- }
-
- return;
-}
-
-static void
-indicator_added (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * type, gpointer data)
-{
- if (type == NULL || g_strcmp0(type, "message")) {
- /* We only care about message type indicators
- all of the others can go to the bit bucket */
- g_debug("Ignoreing indicator of type '%s'", type);
- return;
- }
- g_debug("Got a message");
-
- indicate_listener_get_property(listener, server, indicator, "subtype", subtype_cb, data);
- return;
-}
-
-static void
-indicator_removed (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * type, gpointer data)
-{
- g_debug("Removing %s %d", INDICATE_LISTENER_SERVER_DBUS_NAME(server), INDICATE_LISTENER_INDICATOR_ID(indicator));
- if (type == NULL || g_strcmp0(type, "message")) {
- /* We only care about message type indicators
- all of the others can go to the bit bucket */
- g_debug("Ignoreing indicator of type '%s'", type);
- return;
- }
-
- gboolean removed = FALSE;
-
- serverList_t sl_item_local;
- 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) {
- return;
- }
- sl_item = (serverList_t *)serverentry->data;
-
- /* Look in the IM Hash Table */
- imList_t listData;
- 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 (!removed && menuitem != NULL) {
- sl_item->imList = g_list_remove(sl_item->imList, ilt);
- g_signal_handler_disconnect(menuitem, ilt->timechange_cb);
- g_free(ilt);
-
- dbusmenu_menuitem_property_set(menuitem, "visibile", "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;
-}
-
-int
-main (int argc, char ** argv)
-{
- g_type_init();
-
- DBusGConnection * connection = dbus_g_bus_get(DBUS_BUS_SESSION, NULL);
- DBusGProxy * bus_proxy = dbus_g_proxy_new_for_name(connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
- GError * error = NULL;
- guint nameret = 0;
-
- if (!org_freedesktop_DBus_request_name(bus_proxy, INDICATOR_MESSAGES_DBUS_NAME, 0, &nameret, &error)) {
- g_error("Unable to call to request name");
- return 1;
- }
-
- if (nameret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
- g_error("Unable to get name");
- return 1;
- }
-
- listener = indicate_listener_ref_default();
- serverList = NULL;
-
- root_menuitem = dbusmenu_menuitem_new();
- DbusmenuServer * server = dbusmenu_server_new(INDICATOR_MESSAGES_DBUS_OBJECT);
- dbusmenu_server_set_root(server, root_menuitem);
-
- 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);
-
- mainloop = g_main_loop_new(NULL, FALSE);
- g_main_loop_run(mainloop);
-
- return 0;
-}
diff --git a/src/launcher-menu-item.c b/src/launcher-menu-item.c
new file mode 100644
index 0000000..822196b
--- /dev/null
+++ b/src/launcher-menu-item.c
@@ -0,0 +1,212 @@
+/*
+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 "launcher-menu-item.h"
+#include "dbus-data.h"
+
+enum {
+ NAME_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+typedef struct _LauncherMenuItemPrivate LauncherMenuItemPrivate;
+struct _LauncherMenuItemPrivate
+{
+ GAppInfo * appinfo;
+ gchar * desktop;
+};
+
+#define LAUNCHER_MENU_ITEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), LAUNCHER_MENU_ITEM_TYPE, LauncherMenuItemPrivate))
+
+/* 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, 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;
+
+ return;
+}
+
+static void
+launcher_menu_item_dispose (GObject *object)
+{
+ // LauncherMenuItem * self = LAUNCHER_MENU_ITEM(object);
+ // LauncherMenuItemPrivate * priv = LAUNCHER_MENU_ITEM_GET_PRIVATE(self);
+
+ 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->appinfo != NULL) {
+ g_object_unref(priv->appinfo);
+ priv->appinfo = NULL;
+ }
+
+ 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);
+
+ priv->appinfo = G_APP_INFO(g_desktop_app_info_new_from_filename(desktop_file));
+ priv->desktop = g_strdup(desktop_file);
+
+ g_debug("\tName: %s", launcher_menu_item_get_name(self));
+ dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), "type", LAUNCHER_MENUITEM_TYPE);
+ dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), LAUNCHER_MENUITEM_PROP_APP_NAME, launcher_menu_item_get_name(self));
+ dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), LAUNCHER_MENUITEM_PROP_APP_DESC, launcher_menu_item_get_description(self));
+
+ g_signal_connect(G_OBJECT(self), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(activate_cb), NULL);
+
+ 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);
+ }
+}
+
+/* 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, 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);
+}
+
+/* Hides the menu item based on whether it is eclipsed
+ or not. */
+void
+launcher_menu_item_set_eclipsed (LauncherMenuItem * li, gboolean eclipsed)
+{
+ g_debug("Laucher '%s' is %s", launcher_menu_item_get_name(li), eclipsed ? "now eclipsed" : "shown again");
+ dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(li), DBUSMENU_MENUITEM_PROP_VISIBLE, eclipsed ? "false" : "true");
+ return;
+}
+
+gboolean
+launcher_menu_item_get_eclipsed (LauncherMenuItem * li)
+{
+ const gchar * show = dbusmenu_menuitem_property_get(DBUSMENU_MENUITEM(li), DBUSMENU_MENUITEM_PROP_VISIBLE);
+ if (show == NULL) {
+ return FALSE;
+ }
+ g_debug("Launcher check eclipse: %s", show);
+ if (!g_strcmp0(show, "false")) {
+ return TRUE;
+ }
+ return FALSE;
+}
diff --git a/src/launcher-menu-item.h b/src/launcher-menu-item.h
new file mode 100644
index 0000000..3e031d5
--- /dev/null
+++ b/src/launcher-menu-item.h
@@ -0,0 +1,66 @@
+/*
+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);
+void launcher_menu_item_set_eclipsed (LauncherMenuItem * li, gboolean eclipsed);
+gboolean launcher_menu_item_get_eclipsed (LauncherMenuItem * li);
+
+G_END_DECLS
+
+#endif /* __LAUNCHER_MENU_ITEM_H__ */
+
diff --git a/src/messages-service-dbus.c b/src/messages-service-dbus.c
new file mode 100644
index 0000000..d9c0e8d
--- /dev/null
+++ b/src/messages-service-dbus.c
@@ -0,0 +1,184 @@
+/*
+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 <dbus/dbus-glib.h>
+#include "messages-service-dbus.h"
+#include "dbus-data.h"
+
+enum {
+ ATTENTION_CHANGED,
+ ICON_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+typedef struct _MessageServiceDbusPrivate MessageServiceDbusPrivate;
+
+struct _MessageServiceDbusPrivate
+{
+ 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 _messages_service_server_watch (void);
+static gboolean _messages_service_server_attention_requested (MessageServiceDbus * self, gboolean * dot, GError ** error);
+static gboolean _messages_service_server_icon_shown (MessageServiceDbus * self, gboolean * hidden, GError ** error);
+
+#include "messages-service-server.h"
+
+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);
+
+
+ dbus_g_object_type_install_info(MESSAGE_SERVICE_DBUS_TYPE, &dbus_glib__messages_service_server_object_info);
+
+ return;
+}
+
+static void
+message_service_dbus_init (MessageServiceDbus *self)
+{
+ DBusGConnection * connection = dbus_g_bus_get(DBUS_BUS_SESSION, NULL);
+ dbus_g_connection_register_g_object(connection,
+ INDICATOR_MESSAGES_DBUS_SERVICE_OBJECT,
+ G_OBJECT(self));
+
+ MessageServiceDbusPrivate * priv = MESSAGE_SERVICE_DBUS_GET_PRIVATE(self);
+
+ priv->dot = FALSE;
+ priv->hidden = FALSE;
+
+ return;
+}
+
+static void
+message_service_dbus_dispose (GObject *object)
+{
+
+
+ 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));
+}
+
+/* DBus function to say that someone is watching */
+static void
+_messages_service_server_watch (void)
+{
+
+}
+
+/* DBus interface to request the private variable to know
+ whether there is a green dot. */
+static gboolean
+_messages_service_server_attention_requested (MessageServiceDbus * self, gboolean * dot, GError ** error)
+{
+ MessageServiceDbusPrivate * priv = MESSAGE_SERVICE_DBUS_GET_PRIVATE(self);
+ *dot = priv->dot;
+ return TRUE;
+}
+
+/* DBus interface to request the private variable to know
+ whether the icon is hidden. */
+static gboolean
+_messages_service_server_icon_shown (MessageServiceDbus * self, gboolean * hidden, GError ** error)
+{
+ MessageServiceDbusPrivate * priv = MESSAGE_SERVICE_DBUS_GET_PRIVATE(self);
+ *hidden = priv->hidden;
+ return TRUE;
+}
+
+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);
+ }
+ 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);
+ }
+ return;
+}
diff --git a/src/messages-service-dbus.h b/src/messages-service-dbus.h
new file mode 100644
index 0000000..7a8574e
--- /dev/null
+++ b/src/messages-service-dbus.h
@@ -0,0 +1,62 @@
+/*
+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
new file mode 100644
index 0000000..754021b
--- /dev/null
+++ b/src/messages-service.c
@@ -0,0 +1,1065 @@
+/*
+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/>.
+*/
+
+#include <string.h>
+#include <pango/pango-utils.h>
+#include <dbus/dbus-glib-bindings.h>
+#include <libindicate/listener.h>
+#include <gio/gio.h>
+
+#include <libdbusmenu-glib/server.h>
+
+#include "im-menu-item.h"
+#include "app-menu-item.h"
+#include "launcher-menu-item.h"
+#include "dbus-data.h"
+#include "dirs.h"
+#include "messages-service-dbus.h"
+
+static IndicateListener * listener;
+static GList * serverList = NULL;
+static GList * launcherList = NULL;
+
+static DbusmenuMenuitem * root_menuitem = NULL;
+static GMainLoop * mainloop = NULL;
+
+static MessageServiceDbus * dbus_interface = NULL;
+
+
+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, gchar * type, gpointer data);
+static void check_eclipses (AppMenuItem * ai);
+static void remove_eclipses (AppMenuItem * ai);
+static gboolean build_launcher (gpointer data);
+static gboolean build_launchers (gpointer data);
+static gboolean blacklist_init (gpointer data);
+static gboolean blacklist_add (gpointer data);
+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);
+
+
+/*
+ * Server List
+ */
+
+typedef struct _serverList_t serverList_t;
+struct _serverList_t {
+ IndicateListenerServer * server;
+ AppMenuItem * menuitem;
+ GList * imList;
+};
+
+static gint
+serverList_equal (gconstpointer a, gconstpointer b)
+{
+ serverList_t * pa, * pb;
+
+ pa = (serverList_t *)a;
+ pb = (serverList_t *)b;
+
+ const gchar * pas = INDICATE_LISTENER_SERVER_DBUS_NAME(pa->server);
+ const gchar * pbs = INDICATE_LISTENER_SERVER_DBUS_NAME(pb->server);
+
+ return g_strcmp0(pas, pbs);
+}
+
+static gint
+serverList_sort (gconstpointer a, gconstpointer b)
+{
+ serverList_t * pa, * pb;
+
+ pa = (serverList_t *)a;
+ pb = (serverList_t *)b;
+
+ 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);
+}
+
+/*
+ * Item List
+ */
+
+typedef struct _imList_t imList_t;
+struct _imList_t {
+ IndicateListenerServer * server;
+ IndicateListenerIndicator * indicator;
+ DbusmenuMenuitem * menuitem;
+ gulong timechange_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;
+ GList * appdiritems;
+};
+
+static gint
+launcherList_sort (gconstpointer a, gconstpointer b)
+{
+ 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);
+}
+
+static void
+launcherList_count_helper (gpointer data, 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;
+ }
+
+ 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);
+ g_idle_add(blacklist_add, path);
+ }
+
+ g_dir_close(dir);
+ g_free(blacklistdir);
+
+ return FALSE;
+}
+
+/* Add a definition file into the black list and eclipse
+ and launchers that have the same file. */
+static gboolean
+blacklist_add (gpointer udata)
+{
+ gchar * definition_file = (gchar *)udata;
+ /* Dump the file */
+ gchar * desktop;
+ g_file_get_contents(definition_file, &desktop, NULL, NULL);
+ if (desktop == NULL) {
+ g_warning("Couldn't get data out of: %s", definition_file);
+ return FALSE;
+ }
+
+ /* Clean up the data */
+ gchar * trimdesktop = pango_trim_string(desktop);
+ g_free(desktop);
+
+ /* Check for conflicts */
+ gpointer data = g_hash_table_lookup(blacklist, trimdesktop);
+ if (data != NULL) {
+ gchar * oldfile = (gchar *)data;
+ if (!g_strcmp0(oldfile, definition_file)) {
+ g_warning("Already added file '%s'", oldfile);
+ } else {
+ g_warning("Already have desktop file '%s' in blacklist file '%s' not adding from '%s'", trimdesktop, oldfile, definition_file);
+ }
+
+ g_free(trimdesktop);
+ g_free(definition_file);
+ return FALSE;
+ }
+
+ /* Actually blacklist this thing */
+ g_hash_table_insert(blacklist, trimdesktop, definition_file);
+ g_debug("Adding Blacklist item '%s' for desktop '%s'", definition_file, trimdesktop);
+
+ /* Go through and eclipse folks */
+ GList * launcher;
+ for (launcher = launcherList; launcher != NULL; launcher = launcher->next) {
+ launcherList_t * item = (launcherList_t *)launcher->data;
+ if (!g_strcmp0(trimdesktop, launcher_menu_item_get_desktop(item->menuitem))) {
+ launcher_menu_item_set_eclipsed(item->menuitem, TRUE);
+ }
+ }
+
+ check_hidden();
+
+ return FALSE;
+}
+
+/* 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;
+ }
+ }
+
+ if (!found) {
+ g_debug("\tNot found!");
+ return FALSE;
+ }
+
+ 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);
+ }
+ }
+ }
+
+ if (!g_hash_table_remove(blacklist, key)) {
+ g_warning("Unable to remove '%s' with value '%s'", definition_file, (gchar *)key);
+ }
+
+ check_hidden();
+
+ return FALSE;
+}
+
+/* Check to see if a particular desktop file is
+ in the blacklist. */
+static gboolean
+blacklist_check (const gchar * desktop_file)
+{
+ g_debug("Checking blacklist for: %s", desktop_file);
+ if (blacklist == NULL) return FALSE;
+
+ if (g_hash_table_lookup(blacklist, desktop_file)) {
+ g_debug("\tFound!");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* 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;
+}
+
+/*
+ * More code
+ */
+
+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;
+ }
+
+ AppMenuItem * menuitem = app_menu_item_new(listener, server);
+ g_signal_connect(G_OBJECT(menuitem), APP_MENU_ITEM_SIGNAL_COUNT_CHANGED, G_CALLBACK(server_count_changed), NULL);
+ g_signal_connect(G_OBJECT(menuitem), APP_MENU_ITEM_SIGNAL_NAME_CHANGED, G_CALLBACK(server_name_changed), menushell);
+
+ serverList_t * sl_item = g_new0(serverList_t, 1);
+ sl_item->server = server;
+ sl_item->menuitem = menuitem;
+ sl_item->imList = NULL;
+
+ /* Incase we got an indicator first */
+ GList * alreadythere = g_list_find_custom(serverList, sl_item, serverList_equal);
+ if (alreadythere != NULL) {
+ g_free(sl_item);
+ sl_item = (serverList_t *)alreadythere->data;
+ sl_item->menuitem = menuitem;
+ serverList = g_list_sort(serverList, serverList_sort);
+ } else {
+ serverList = g_list_insert_sorted(serverList, sl_item, serverList_sort);
+ }
+
+ dbusmenu_menuitem_child_append(menushell, DBUSMENU_MENUITEM(menuitem));
+ /* Should be prepend ^ */
+
+ resort_menu(menushell);
+ check_hidden();
+
+ return;
+}
+
+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;
+}
+
+static void
+server_count_changed (AppMenuItem * appitem, guint count, gpointer data)
+{
+ static gboolean showing_new_icon = FALSE;
+
+ /* Quick check for a common case */
+ if (count != 0 && showing_new_icon) {
+ return;
+ }
+
+ /* Odd that we'd get a signal in this case, but let's
+ take it out of the mix too */
+ if (count == 0 && !showing_new_icon) {
+ return;
+ }
+
+ if (count != 0) {
+ g_debug("Setting image to 'new'");
+ showing_new_icon = TRUE;
+ message_service_dbus_set_attention(dbus_interface, TRUE);
+ return;
+ }
+
+ /* Okay, now at this point the count is zero and it
+ might result in a switching of the icon back to being
+ the plain one. Let's check. */
+
+ gboolean we_have_indicators = FALSE;
+ GList * appitems = serverList;
+ for (; appitems != NULL; appitems = appitems->next) {
+ AppMenuItem * appitem = ((serverList_t *)appitems->data)->menuitem;
+ if (app_menu_item_get_count(appitem) != 0) {
+ we_have_indicators = TRUE;
+ break;
+ }
+ }
+
+ if (!we_have_indicators) {
+ g_debug("Setting image to boring");
+ showing_new_icon = FALSE;
+ message_service_dbus_set_attention(dbus_interface, FALSE);
+ }
+
+ return;
+}
+
+static void
+im_time_changed (ImMenuItem * imitem, glong seconds, gpointer data)
+{
+ serverList_t * sl = (serverList_t *)data;
+ sl->imList = g_list_sort(sl->imList, imList_sort);
+ resort_menu(root_menuitem);
+ return;
+}
+
+static void
+server_removed (IndicateListener * listener, IndicateListenerServer * server, gchar * type, gpointer data)
+{
+ g_debug("Removing server: %s", INDICATE_LISTENER_SERVER_DBUS_NAME(server));
+ serverList_t slt;
+ slt.server = server;
+ GList * lookup = g_list_find_custom(serverList, &slt, serverList_equal);
+
+ if (lookup == NULL) {
+ g_debug("\tUnable to find server: %s", INDICATE_LISTENER_SERVER_DBUS_NAME(server));
+ return;
+ }
+
+ serverList_t * sltp = (serverList_t *)lookup->data;
+
+ remove_eclipses(sltp->menuitem);
+
+ while (sltp->imList) {
+ imList_t * imitem = (imList_t *)sltp->imList->data;
+ indicator_removed(listener, server, imitem->indicator, "message", data);
+ }
+
+ serverList = g_list_remove(serverList, sltp);
+
+ if (sltp->menuitem != NULL) {
+ dbusmenu_menuitem_property_set(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));
+ }
+
+ g_free(sltp);
+
+ /* Simulate a server saying zero to recalculate icon */
+ server_count_changed(NULL, 0, NULL);
+ check_hidden();
+
+ return;
+}
+
+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;
+
+ msl->position++;
+
+ if (!IS_APP_MENU_ITEM(data_mi)) {
+ 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;
+}
+
+static void
+check_hidden (void)
+{
+ 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;
+ }
+ }
+
+ message_service_dbus_set_icon(dbus_interface, hide);
+ return;
+}
+
+static void
+resort_menu (DbusmenuMenuitem * menushell)
+{
+ guint position = 0;
+ GList * serverentry;
+ GList * launcherentry = launcherList;
+
+ g_debug("Reordering Menu:");
+
+ for (serverentry = serverList; serverentry != NULL; serverentry = serverentry->next) {
+ serverList_t * si = (serverList_t *)serverentry->data;
+
+ 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) {
+ 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++;
+ launcherentry = launcherentry->next;
+ if (launcherentry != NULL) {
+ li = (launcherList_t *)launcherentry->data;
+ }
+ }
+ }
+
+ 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++;
+ }
+
+ 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);
+ dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(imi->menuitem), position);
+ position++;
+ }
+ }
+ }
+
+ while (launcherentry != NULL) {
+ launcherList_t * li = (launcherList_t *)launcherentry->data;
+ 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++;
+ launcherentry = launcherentry->next;
+ }
+
+ return;
+}
+
+static void
+subtype_cb (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * property, gchar * propertydata, gpointer data)
+{
+ DbusmenuMenuitem * menushell = DBUSMENU_MENUITEM(data);
+ if (menushell == NULL) {
+ g_error("Data in callback is not a menushell");
+ return;
+ }
+
+ if (property == NULL || g_strcmp0(property, "subtype")) {
+ /* We should only ever get subtypes, but just in case */
+ g_warning("Subtype callback got a property '%s'", property);
+ return;
+ }
+
+ if (propertydata == NULL || propertydata[0] == '\0') {
+ /* It's possible that this message didn't have a subtype. That's
+ * okay, but we don't want to display those */
+ g_debug("No subtype");
+ return;
+ }
+
+ g_debug("Message subtype: %s", propertydata);
+
+ if (!g_strcmp0(propertydata, "im") || !g_strcmp0(propertydata, "login")) {
+ imList_t * listItem = g_new0(imList_t, 1);
+ listItem->server = server;
+ listItem->indicator = indicator;
+
+ g_debug("Building IM Item");
+ ImMenuItem * menuitem = im_menu_item_new(listener, server, indicator, !g_strcmp0(propertydata, "im"));
+ g_object_ref(G_OBJECT(menuitem));
+ listItem->menuitem = DBUSMENU_MENUITEM(menuitem);
+
+ g_debug("Finding the server entry");
+ serverList_t sl_item_local;
+ 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 */
+ serverList_t * sl_item = g_new0(serverList_t, 1);
+ sl_item->server = server;
+ sl_item->menuitem = NULL;
+ sl_item->imList = NULL;
+
+ serverList = g_list_insert_sorted(serverList, sl_item, serverList_sort);
+ } else {
+ sl_item = (serverList_t *)serverentry->data;
+ }
+
+ g_debug("Adding to 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);
+
+ g_debug("Placing in Shell");
+ 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));
+ }
+ }
+
+ return;
+}
+
+static void
+indicator_added (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * type, gpointer data)
+{
+ if (type == NULL || g_strcmp0(type, "message")) {
+ /* We only care about message type indicators
+ all of the others can go to the bit bucket */
+ g_debug("Ignoreing indicator of type '%s'", type);
+ return;
+ }
+ g_debug("Got a message");
+
+ indicate_listener_get_property(listener, server, indicator, "subtype", subtype_cb, data);
+ return;
+}
+
+static void
+indicator_removed (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * type, gpointer data)
+{
+ g_debug("Removing %s %d", INDICATE_LISTENER_SERVER_DBUS_NAME(server), INDICATE_LISTENER_INDICATOR_ID(indicator));
+ if (type == NULL || g_strcmp0(type, "message")) {
+ /* We only care about message type indicators
+ all of the others can go to the bit bucket */
+ g_debug("Ignoreing indicator of type '%s'", type);
+ return;
+ }
+
+ gboolean removed = FALSE;
+
+ serverList_t sl_item_local;
+ 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) {
+ return;
+ }
+ sl_item = (serverList_t *)serverentry->data;
+
+ /* Look in the IM Hash Table */
+ imList_t listData;
+ 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 (!removed && menuitem != NULL) {
+ sl_item->imList = g_list_remove(sl_item->imList, ilt);
+ g_signal_handler_disconnect(menuitem, ilt->timechange_cb);
+ g_free(ilt);
+
+ dbusmenu_menuitem_property_set(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;
+}
+
+static void
+app_dir_changed (GFileMonitor * monitor, GFile * file, GFile * other_file, GFileMonitorEvent event_type, 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);
+ g_idle_add(build_launcher, path);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return;
+}
+
+/* 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);
+ break;
+ }
+ }
+
+ return;
+}
+
+/* Remove any eclipses that might have been caused
+ by this app item that is now retiring */
+static void
+remove_eclipses (AppMenuItem * ai)
+{
+ 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);
+ break;
+ }
+ }
+
+ 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;
+ }
+ }
+
+ if (listitem == NULL) {
+ g_warning("Removed '%s' by the way of it not seeming to exist anywhere.", appdirentry);
+ return 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;
+ }
+
+ /* Full Destroy */
+ g_free(appdir);
+ g_list_free(li->appdiritems);
+
+ if (li->menuitem != NULL) {
+ dbusmenu_menuitem_property_set(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;
+ }
+
+ launcherList = g_list_remove(launcherList, li);
+ g_free(li);
+
+ return FALSE;
+}
+
+/* This function turns a specific file into a menu
+ item and registers it appropriately with everyone */
+static gboolean
+build_launcher (gpointer data)
+{
+ /* 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) {
+ return FALSE;
+ }
+
+ gchar * trimdesktop = pango_trim_string(desktop);
+ g_free(desktop);
+ g_debug("\tcontents: %s", trimdesktop);
+
+ /* 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), trimdesktop)) {
+ break;
+ }
+ }
+
+ if (listitem == NULL) {
+ /* If not */
+ /* Build the item */
+ launcherList_t * ll = g_new0(launcherList_t, 1);
+ ll->menuitem = launcher_menu_item_new(trimdesktop);
+ g_free(trimdesktop);
+ ll->appdiritems = g_list_append(NULL, path);
+
+ /* Add it to the list */
+ launcherList = g_list_insert_sorted(launcherList, ll, launcherList_sort);
+
+ /* Add it to the menu */
+ dbusmenu_menuitem_child_append(root_menuitem, DBUSMENU_MENUITEM(ll->menuitem));
+ resort_menu(root_menuitem);
+
+ if (blacklist_check(launcher_menu_item_get_desktop(ll->menuitem))) {
+ launcher_menu_item_set_eclipsed(ll->menuitem, TRUE);
+ }
+ } else {
+ /* If so add ourselves */
+ launcherList_t * ll = (launcherList_t *)listitem->data;
+ ll->appdiritems = g_list_append(ll->appdiritems, path);
+ }
+
+ return FALSE;
+}
+
+/* This function goes through all the launchers that we're
+ supposed to be grabbing and decides to show turn them
+ into menu items or not. It doens't do the work, but it
+ makes the decision. */
+static gboolean
+build_launchers (gpointer data)
+{
+ gchar * directory = (gchar *)data;
+
+ if (!g_file_test(directory, G_FILE_TEST_IS_DIR)) {
+ return FALSE;
+ }
+
+ 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);
+ }
+
+ 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;
+ }
+
+ 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);
+ g_idle_add(build_launcher, path);
+ }
+
+ g_dir_close(dir);
+ launcherList = g_list_sort(launcherList, launcherList_sort);
+ return FALSE;
+}
+
+/* Oh, if you don't know what main() is for
+ we really shouldn't be talking. */
+int
+main (int argc, char ** argv)
+{
+ g_type_init();
+
+ DBusGConnection * connection = dbus_g_bus_get(DBUS_BUS_SESSION, NULL);
+ DBusGProxy * bus_proxy = dbus_g_proxy_new_for_name(connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
+ GError * error = NULL;
+ guint nameret = 0;
+
+ if (!org_freedesktop_DBus_request_name(bus_proxy, INDICATOR_MESSAGES_DBUS_NAME, 0, &nameret, &error)) {
+ g_error("Unable to call to request name");
+ return 1;
+ }
+
+ if (nameret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+ g_error("Unable to get name");
+ return 1;
+ }
+
+ dbus_interface = message_service_dbus_new();
+
+ listener = indicate_listener_ref_default();
+ serverList = NULL;
+
+ root_menuitem = dbusmenu_menuitem_new();
+ DbusmenuServer * server = dbusmenu_server_new(INDICATOR_MESSAGES_DBUS_OBJECT);
+ dbusmenu_server_set_root(server, root_menuitem);
+
+ 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);
+
+ g_idle_add(blacklist_init, NULL);
+ g_idle_add(build_launchers, SYSTEM_APPS_DIR);
+ gchar * userdir = g_build_filename(g_get_user_config_dir(), USER_APPS_DIR, NULL);
+ g_idle_add(build_launchers, userdir);
+
+ mainloop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(mainloop);
+
+ g_free(userdir);
+
+ return 0;
+}
diff --git a/src/messages-service.xml b/src/messages-service.xml
new file mode 100644
index 0000000..f991179
--- /dev/null
+++ b/src/messages-service.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node name="/">
+ <interface name="org.ayatana.indicator.messages.service">
+
+<!-- Methods -->
+ <method name="Watch">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value="true" />
+ </method>
+ <method name="AttentionRequested">
+ <arg type="b" name="dot" direction="out" />
+ </method>
+ <method name="IconShown">
+ <arg type="b" name="hidden" direction="out" />
+ </method>
+
+<!-- Signals -->
+ <signal name="AttentionChanged">
+ <arg type="b" name="dot" direction="out" />
+ </signal>
+ <signal name="IconChanged">
+ <arg type="b" name="hidden" direction="out" />
+ </signal>
+
+ </interface>
+</node>