diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | configure.ac | 21 | ||||
-rw-r--r-- | data/Makefile.am | 6 | ||||
-rw-r--r-- | data/indicator-messages.service.in | 2 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/app-menu-item.c | 134 | ||||
-rw-r--r-- | src/dbus-data.h | 11 | ||||
-rw-r--r-- | src/im-menu-item.c | 227 | ||||
-rw-r--r-- | src/im-menu-item.h | 7 | ||||
-rw-r--r-- | src/indicator-messages.c | 91 | ||||
-rw-r--r-- | src/messages-service.c | 329 |
11 files changed, 596 insertions, 236 deletions
diff --git a/Makefile.am b/Makefile.am index 7220eba..7d58c6b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,3 +3,5 @@ SUBDIRS = \ src \ data +DISTCHECK_CONFIGURE_FLAGS = --enable-localinstall + diff --git a/configure.ac b/configure.ac index a3d937b..e00a725 100644 --- a/configure.ac +++ b/configure.ac @@ -34,23 +34,37 @@ PKG_CHECK_MODULES(APPLET, gtk+-2.0 >= $GTK_REQUIRED_VERSION gio-unix-2.0 >= $GIO_UNIX_REQUIRED_VERSION indicator >= $INDICATOR_REQUIRED_VERSION indicate >= $INDICATE_REQUIRED_VERSION - indicate-gtk >= $INDICATE_REQUIRED_VERSION dbusmenu-gtk >= $DBUSMENUGTK_REQUIRED_VERSION) AC_SUBST(APPLET_CFLAGS) AC_SUBST(APPLET_LIBS) ########################### +# Check to see if we're local +########################### + +with_localinstall="no" +AC_ARG_ENABLE(localinstall, AS_HELP_STRING([--enable-localinstall], [install all of the files localy instead of system directories (for distcheck)]), with_localinstall=$enableval, with_localinstall=no) + +########################### # Indicator Info ########################### -INDICATORDIR=`$PKG_CONFIG --variable=indicatordir indicator` +if test "x$with_localinstall" = "xyes"; then + INDICATORDIR="${libdir}/indicators/2/" +else + INDICATORDIR=`$PKG_CONFIG --variable=indicatordir indicator` +fi AC_SUBST(INDICATORDIR) ########################### # DBus Service Info ########################### -DBUSSERVICEDIR=`$PKG_CONFIG --variable=session_bus_services_dir dbus-1` +if test "x$with_localinstall" = "xyes"; then + DBUSSERVICEDIR="${datadir}/dbus-1/services/" +else + DBUSSERVICEDIR=`$PKG_CONFIG --variable=session_bus_services_dir dbus-1` +fi AC_SUBST(DBUSSERVICEDIR) ########################### @@ -61,7 +75,6 @@ AC_OUTPUT([ Makefile src/Makefile data/Makefile -data/indicator-messages.service data/icons/Makefile data/icons/16x16/Makefile data/icons/16x16/status/Makefile diff --git a/data/Makefile.am b/data/Makefile.am index 6177c6d..094a434 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -2,3 +2,9 @@ SUBDIRS = icons dbus_servicesdir = $(DBUSSERVICEDIR) dbus_services_DATA = indicator-messages.service + +%.service: %.service.in + sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@ + +EXTRA_DIST = indicator-messages.service.in + diff --git a/data/indicator-messages.service.in b/data/indicator-messages.service.in index 9ae3960..573a2d6 100644 --- a/data/indicator-messages.service.in +++ b/data/indicator-messages.service.in @@ -1,3 +1,3 @@ [D-BUS Service] Name=org.ayatana.indicator.messages -Exec=@prefix@/bin/indicator-messages-service +Exec=@libexecdir@/indicator-messages-service diff --git a/src/Makefile.am b/src/Makefile.am index 38787a1..01741d0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,5 +1,5 @@ -bin_PROGRAMS = indicator-messages-service +libexec_PROGRAMS = indicator-messages-service ###################################### # Building the messages indicator diff --git a/src/app-menu-item.c b/src/app-menu-item.c index feb780d..aa0b60c 100644 --- a/src/app-menu-item.c +++ b/src/app-menu-item.c @@ -27,6 +27,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #include <glib/gi18n.h> #include <gio/gdesktopappinfo.h> #include "app-menu-item.h" +#include "dbus-data.h" enum { COUNT_CHANGED, @@ -47,7 +48,6 @@ struct _AppMenuItemPrivate GAppInfo * appinfo; gchar * desktop; guint unreadcount; - gboolean count_on_label; }; #define APP_MENU_ITEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), APP_MENU_ITEM_TYPE, AppMenuItemPrivate)) @@ -58,14 +58,12 @@ 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, gpointer data); -static void type_cb (IndicateListener * listener, IndicateListenerServer * server, gchar * value, 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 desktop_cb (IndicateListener * listener, IndicateListenerServer * server, gchar * value, gpointer data); -static void indicator_added_cb (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * type, gpointer data); -static void indicator_removed_cb (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * type, gpointer data); static void update_label (AppMenuItem * self); - - +/* GObject Boilerplate */ G_DEFINE_TYPE (AppMenuItem, app_menu_item, DBUSMENU_TYPE_MENUITEM); static void @@ -108,8 +106,6 @@ app_menu_item_init (AppMenuItem *self) priv->appinfo = NULL; priv->desktop = NULL; priv->unreadcount = 0; - priv->count_on_label = FALSE; - return; } @@ -120,9 +116,6 @@ app_menu_item_dispose (GObject *object) AppMenuItem * self = APP_MENU_ITEM(object); AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self); - g_signal_handlers_disconnect_by_func(G_OBJECT(priv->listener), G_CALLBACK(indicator_added_cb), self); - g_signal_handlers_disconnect_by_func(G_OBJECT(priv->listener), G_CALLBACK(indicator_removed_cb), self); - g_object_unref(priv->listener); G_OBJECT_CLASS (app_menu_item_parent_class)->dispose (object); @@ -158,75 +151,82 @@ app_menu_item_new (IndicateListener * listener, IndicateListenerServer * server) 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)); - priv->server = server; + /* Can not ref as not real GObject */ + priv->server = server; - g_signal_connect(G_OBJECT(listener), INDICATE_LISTENER_SIGNAL_INDICATOR_ADDED, G_CALLBACK(indicator_added_cb), self); - g_signal_connect(G_OBJECT(listener), INDICATE_LISTENER_SIGNAL_INDICATOR_REMOVED, G_CALLBACK(indicator_removed_cb), self); + /* Set up listener signals */ + g_signal_connect(G_OBJECT(listener), INDICATE_LISTENER_SIGNAL_SERVER_COUNT_CHANGED, G_CALLBACK(count_changed), self); - indicate_listener_server_get_type(listener, server, type_cb, 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); 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 -type_cb (IndicateListener * listener, IndicateListenerServer * server, gchar * value, gpointer data) +static void +update_label (AppMenuItem * self) { - AppMenuItem * self = APP_MENU_ITEM(data); AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self); - if (priv->type != NULL) { - g_free(priv->type); - priv->type = NULL; - } - - if (value == NULL) { - g_warning("Type value is NULL, that shouldn't really happen"); - return; - } - - priv->type = g_strdup(value); - - if (!(!g_strcmp0(priv->type, "message.instant") || !g_strcmp0(priv->type, "message.micro") || !g_strcmp0(priv->type, "message.im"))) { - /* For IM and Microblogging we want the individual items, not a count */ - priv->count_on_label = TRUE; - update_label(self); - - indicate_listener_server_show_interest(listener, server, INDICATE_INTEREST_INDICATOR_COUNT); + 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)"), app_menu_item_get_name(self), priv->unreadcount); + dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_LABEL, label); + g_free(label); } else { - indicate_listener_server_show_interest(listener, server, INDICATE_INTEREST_INDICATOR_DISPLAY); - indicate_listener_server_show_interest(listener, server, INDICATE_INTEREST_INDICATOR_SIGNAL); + dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_LABEL, app_menu_item_get_name(self)); } 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 -update_label (AppMenuItem * self) +count_changed (IndicateListener * listener, IndicateListenerServer * server, guint count, gpointer data) { + AppMenuItem * self = APP_MENU_ITEM(data); AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self); - if (priv->count_on_label && !priv->unreadcount < 1) { - /* 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), DBUSMENU_MENUITEM_PROP_LABEL, label); - g_free(label); - } else { - dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_LABEL, app_menu_item_get_name(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. */ static void desktop_cb (IndicateListener * listener, IndicateListenerServer * server, gchar * value, gpointer data) { @@ -268,46 +268,6 @@ activate_cb (AppMenuItem * self, gpointer data) return; } -static void -indicator_added_cb (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * type, gpointer data) -{ - g_return_if_fail(IS_APP_MENU_ITEM(data)); - AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(data); - - if (g_strcmp0(INDICATE_LISTENER_SERVER_DBUS_NAME(server), INDICATE_LISTENER_SERVER_DBUS_NAME(priv->server))) { - /* Not us */ - return; - } - - priv->unreadcount++; - - update_label(APP_MENU_ITEM(data)); - g_signal_emit(G_OBJECT(data), signals[COUNT_CHANGED], 0, priv->unreadcount, TRUE); - - return; -} - -static void -indicator_removed_cb (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * type, gpointer data) -{ - AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(data); - - if (g_strcmp0(INDICATE_LISTENER_SERVER_DBUS_NAME(server), INDICATE_LISTENER_SERVER_DBUS_NAME(priv->server))) { - /* Not us */ - return; - } - - /* Should never happen, but let's have some protection on that */ - if (priv->unreadcount > 0) { - priv->unreadcount--; - } - - update_label(APP_MENU_ITEM(data)); - g_signal_emit(G_OBJECT(data), signals[COUNT_CHANGED], 0, priv->unreadcount, TRUE); - - return; -} - guint app_menu_item_get_count (AppMenuItem * appitem) { diff --git a/src/dbus-data.h b/src/dbus-data.h index db59003..9f53f94 100644 --- a/src/dbus-data.h +++ b/src/dbus-data.h @@ -12,4 +12,15 @@ #define LAUNCHER_MENUITEM_PROP_APP_NAME "application-name" #define LAUNCHER_MENUITEM_PROP_APP_DESC "application-description" +#define APPLICATION_MENUITEM_TYPE "application-item" +#define APPLICATION_MENUITEM_PROP_NAME "app-name" +#define APPLICATION_MENUITEM_PROP_COUNT "app-count" + +#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 MAX_NUMBER_OF_INDICATORS 7 + #endif /* __DBUS_DATA_H__ */ diff --git a/src/im-menu-item.c b/src/im-menu-item.c index 6f58ebd..f97436e 100644 --- a/src/im-menu-item.c +++ b/src/im-menu-item.c @@ -25,12 +25,15 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #include <glib/gi18n.h> #include <libdbusmenu-glib/client.h> -#include <libindicate-gtk/indicator.h> -#include <libindicate-gtk/listener.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 }; @@ -45,8 +48,10 @@ struct _ImMenuItemPrivate IndicateListenerIndicator * indicator; glong seconds; - gboolean show_time; + gchar * count; gulong indicator_changed; + gboolean attention; + gboolean show; guint time_update_min; }; @@ -81,7 +86,6 @@ static void activate_cb (ImMenuItem * self, static void indicator_modified_cb (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, - gchar * type, gchar * property, ImMenuItem * self); @@ -104,6 +108,13 @@ im_menu_item_class_init (ImMenuItemClass *klass) 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; } @@ -147,20 +158,25 @@ 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, gchar * propertydata, gpointer data) { - dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(data), DBUSMENU_MENUITEM_PROP_ICON_DATA, propertydata); + dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(data), INDICATOR_MENUITEM_PROP_ICON, propertydata); 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); - if (!priv->show_time) { - dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), "right-column", ""); + if (priv->count != NULL) { return; } @@ -191,13 +207,15 @@ update_time (ImMenuItem * self) } if (timestring != NULL) { - dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), "right-column", ""); + 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) { @@ -208,6 +226,10 @@ time_update_cb (gpointer data) 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, GTimeVal * propertydata, gpointer data) { @@ -238,26 +260,106 @@ time_cb (IndicateListener * listener, IndicateListenerServer * server, IndicateL return; } +/* 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, gchar * propertydata, gpointer data) { - g_debug("Got Sender Information"); + g_debug("Got Sender Information: %s", propertydata); ImMenuItem * self = IM_MENU_ITEM(data); - if (self == NULL) { - g_error("Menu Item callback called without a menu item"); + + /* 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; } - if (property == NULL || g_strcmp0(property, "sender")) { - g_warning("Sender callback called without being sent the sender. We got '%s' with value '%s'.", property, propertydata); + dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), INDICATOR_MENUITEM_PROP_LABEL, propertydata); + + 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, 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); + } return; } - dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_LABEL, propertydata); + 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); 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, gchar * 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 (propertydata == NULL || propertydata[0] == '\0' || !g_strcmp0(propertydata, "false")) { + wantit = FALSE; + } else { + wantit = TRUE; + } + + 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, gpointer data) { @@ -266,8 +368,10 @@ activate_cb (ImMenuItem * self, gpointer data) indicate_listener_display(priv->listener, priv->server, priv->indicator); } +/* 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 * type, gchar * property, ImMenuItem * self) +indicator_modified_cb (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * property, ImMenuItem * self) { ImMenuItemPrivate * priv = IM_MENU_ITEM_GET_PRIVATE(self); @@ -275,19 +379,29 @@ indicator_modified_cb (IndicateListener * listener, IndicateListenerServer * ser if (INDICATE_LISTENER_INDICATOR_ID(indicator) != INDICATE_LISTENER_INDICATOR_ID(priv->indicator)) return; if (server != priv->server) return; - if (!g_strcmp0(property, "sender")) { + /* 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(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); - } 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(listener, server, indicator, "icon", icon_cb, self); } return; } ImMenuItem * -im_menu_item_new (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gboolean show_time) +im_menu_item_new (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator) { ImMenuItem * self = g_object_new(IM_MENU_ITEM_TYPE, NULL); @@ -296,14 +410,21 @@ im_menu_item_new (IndicateListener * listener, IndicateListenerServer * server, priv->listener = listener; priv->server = server; priv->indicator = indicator; - priv->show_time = show_time; + priv->count = NULL; priv->time_update_min = 0; + priv->attention = FALSE; + priv->show = TRUE; - dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), "type", DBUSMENU_CLIENT_TYPES_IMAGE); + dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), "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(listener, server, indicator, INDICATE_INDICATOR_MESSAGES_PROP_ATTENTION, attention_cb, self); 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(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); @@ -311,9 +432,65 @@ im_menu_item_new (IndicateListener * listener, IndicateListenerServer * server, 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); 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 index 51414de..4279c2e 100644 --- a/src/im-menu-item.h +++ b/src/im-menu-item.h @@ -38,6 +38,7 @@ G_BEGIN_DECLS #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; @@ -46,6 +47,7 @@ struct _ImMenuItemClass { DbusmenuMenuitemClass parent_class; void (*time_changed) (glong seconds); + void (*attention_changed) (gboolean requestit); }; struct _ImMenuItem { @@ -53,8 +55,11 @@ struct _ImMenuItem { }; GType im_menu_item_get_type (void); -ImMenuItem * im_menu_item_new (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gboolean show_time); +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 diff --git a/src/indicator-messages.c b/src/indicator-messages.c index c410ef7..a3f22aa 100644 --- a/src/indicator-messages.c +++ b/src/indicator-messages.c @@ -24,6 +24,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #include <glib.h> #include <gtk/gtk.h> #include <libdbusmenu-gtk/menu.h> +#include <libdbusmenu-gtk/menuitem.h> #include <dbus/dbus-glib.h> #include <dbus/dbus-glib-bindings.h> @@ -41,6 +42,8 @@ static GtkIconSize design_team_size; static DBusGProxy * icon_proxy = NULL; +static GtkSizeGroup * indicator_right_group = NULL; + static void attention_changed_cb (DBusGProxy * proxy, gboolean dot, gpointer userdata) { @@ -137,6 +140,91 @@ setup_icon_proxy (gpointer userdata) return FALSE; } +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, gchar * 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), value); + } else if (!g_strcmp0(prop, INDICATOR_MENUITEM_PROP_RIGHT)) { + /* Set the right label */ + gtk_label_set_text(GTK_LABEL(mi_data->right), value); + } 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) { + gtk_image_set_from_pixbuf(GTK_IMAGE(mi_data->icon), pixbuf); + } + } else { + g_warning("Indicator Item property '%s' unknown", prop); + } + + 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) +{ + indicator_item_t * mi_data = g_new0(indicator_item_t, 1); + + 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 * hbox = gtk_hbox_new(FALSE, 4); + + /* Icon, probably someone's face or avatar on an IM */ + mi_data->icon = gtk_image_new(); + GdkPixbuf * pixbuf = dbusmenu_menuitem_property_get_image(newitem, INDICATOR_MENUITEM_PROP_ICON); + if (pixbuf != NULL) { + gtk_image_set_from_pixbuf(GTK_IMAGE(mi_data->icon), 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); + 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)); + gtk_size_group_add_widget(indicator_right_group, mi_data->right); + 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, 0); + 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_signal_connect(G_OBJECT(newitem), "destroyed", G_CALLBACK(g_free), mi_data); + + return TRUE; +} + static gboolean new_launcher_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client) { @@ -207,12 +295,15 @@ get_menu (void) return NULL; } + indicator_right_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + 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); + dbusmenu_client_add_type_handler(DBUSMENU_CLIENT(client), INDICATOR_MENUITEM_TYPE, new_indicator_item); return GTK_MENU(menu); } diff --git a/src/messages-service.c b/src/messages-service.c index 754021b..f7b869e 100644 --- a/src/messages-service.c +++ b/src/messages-service.c @@ -49,7 +49,7 @@ static void server_count_changed (AppMenuItem * appitem, guint count, gpointer d 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 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); @@ -71,6 +71,8 @@ typedef struct _serverList_t serverList_t; struct _serverList_t { IndicateListenerServer * server; AppMenuItem * menuitem; + gboolean attention; + guint count; GList * imList; }; @@ -112,6 +114,7 @@ struct _imList_t { IndicateListenerIndicator * indicator; DbusmenuMenuitem * menuitem; gulong timechange_cb; + gulong attentionchange_cb; }; static gboolean @@ -390,6 +393,56 @@ blacklist_dir_changed (GFileMonitor * monitor, GFile * file, GFile * other_file, * 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) +{ + 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; +} + +/* 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; + } + + /* 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; + } + } + + /* Nope, no one */ + slt->attention = FALSE; + return; +} + +/* 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) { @@ -413,26 +466,34 @@ server_added (IndicateListener * listener, IndicateListenerServer * server, gcha return; } + /* Build the Menu item */ 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); + /* 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); } + /* 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); + dbusmenu_menuitem_child_append(menushell, DBUSMENU_MENUITEM(menuitem)); /* Should be prepend ^ */ @@ -442,6 +503,10 @@ server_added (IndicateListener * listener, IndicateListenerServer * server, gcha return; } +/* 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) { @@ -451,52 +516,37 @@ server_name_changed (AppMenuItem * appitem, gchar * name, gpointer data) return; } +/* 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) { - 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; + 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 (!we_have_indicators) { - g_debug("Setting image to boring"); - showing_new_icon = FALSE; - message_service_dbus_set_attention(dbus_interface, FALSE); + if (count != 0 && !slt->attention) { + slt->attention = TRUE; + /* Let's tell everyone about us! */ + message_service_dbus_set_attention(dbus_interface, TRUE); } return; } +/* 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) { @@ -506,6 +556,26 @@ im_time_changed (ImMenuItem * imitem, glong seconds, gpointer data) return; } +/* 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) +{ + 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; +} + static void server_removed (IndicateListener * listener, IndicateListenerServer * server, gchar * type, gpointer data) { @@ -525,7 +595,7 @@ server_removed (IndicateListener * listener, IndicateListenerServer * server, gc while (sltp->imList) { imList_t * imitem = (imList_t *)sltp->imList->data; - indicator_removed(listener, server, imitem->indicator, "message", data); + indicator_removed(listener, server, imitem->indicator, data); } serverList = g_list_remove(serverList, sltp); @@ -536,10 +606,14 @@ server_removed (IndicateListener * listener, IndicateListenerServer * server, gc g_object_unref(G_OBJECT(sltp->menuitem)); } + if (sltp->attention) { + /* Check to see if this was the server causing the menu item to + be lit up. */ + check_attention(); + } + g_free(sltp); - /* Simulate a server saying zero to recalculate icon */ - server_count_changed(NULL, 0, NULL); check_hidden(); return; @@ -646,8 +720,11 @@ resort_menu (DbusmenuMenuitem * menushell) 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 -subtype_cb (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gchar * property, gchar * propertydata, gpointer data) +indicator_added (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gpointer data) { DbusmenuMenuitem * menushell = DBUSMENU_MENUITEM(data); if (menushell == NULL) { @@ -655,105 +732,93 @@ subtype_cb (IndicateListener * listener, IndicateListenerServer * server, Indica 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; - } + imList_t * listItem = g_new0(imList_t, 1); + listItem->server = server; + listItem->indicator = indicator; - 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; - } + /* Building the IM Menu Item which is a subclass + of DBus Menuitem */ + ImMenuItem * menuitem = im_menu_item_new(listener, server, indicator); + g_object_ref(G_OBJECT(menuitem)); + listItem->menuitem = DBUSMENU_MENUITEM(menuitem); - 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; - } + /* 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; + serverList_t * sl_item = NULL; + sl_item_local.server = server; + GList * serverentry = g_list_find_custom(serverList, &sl_item_local, serverList_equal); - 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); + 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; + sl_item->attention = FALSE; + sl_item->count = 0; - g_debug("Placing in Shell"); - menushell_location_t msl; - msl.found = FALSE; - msl.position = 0; - msl.server = server; + serverList = g_list_insert_sorted(serverList, sl_item, serverList_sort); + } else { + sl_item = (serverList_t *)serverentry->data; + } - 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)); + /* 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); } } - 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; + /* 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)); } - g_debug("Got a message"); - indicate_listener_get_property(listener, server, indicator, "subtype", subtype_cb, data); return; } +/* 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, gchar * type, gpointer data) +indicator_removed (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, 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; + /* Find the server that was related to this item */ 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) { + /* We didn't care about that server */ return; } sl_item = (serverList_t *)serverentry->data; @@ -771,11 +836,41 @@ indicator_removed (IndicateListener * listener, IndicateListenerServer * server, 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(menuitem, DBUSMENU_MENUITEM_PROP_VISIBLE, "false"); dbusmenu_menuitem_child_delete(DBUSMENU_MENUITEM(data), menuitem); removed = TRUE; |