diff --git a/configure.ac b/configure.ac
index 710d55d..f81040d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -41,6 +41,8 @@ GEOCLUE_REQUIRED=0.11
 WEBKIT_REQUIRED=1.1.7
 KEYRING_REQUIRED=2.22
 NETWORK_MANAGER_REQUIRED=0.7.0
+INDICATE_REQUIRED=0.2.0
+INDICATE_GTK_REQUIRED=0.2.0
 
 # Use --enable-maintainer-mode to disabled deprecated symbols
 GNOME_MAINTAINER_MODE_DEFINES
@@ -380,6 +382,34 @@ fi
 AM_CONDITIONAL(HAVE_NOTHERE, test "x$have_nothere" = "xyes")
 
 # -----------------------------------------------------------
+# libindicate
+# -----------------------------------------------------------
+AC_ARG_ENABLE(libindicate,
+              AS_HELP_STRING([--enable-libindicate=@<:@no/yes/auto@:>@],
+                             [build libindicate support]), ,
+                             enable_libindicate=auto)
+
+if test "x$enable_libindicate" != "xno"; then
+   PKG_CHECK_MODULES(INDICATE, 
+   [
+     indicate >= $INDICATE_REQUIRED,
+     indicate-gtk >= $INDICATE_GTK_REQUIRED
+   ], have_libindicate="yes", have_libindicate="no")
+
+   if test "x$have_libindicate" = "xyes"; then
+      AC_DEFINE(HAVE_LIBINDICATE, 1, [Define if you have libindicate])
+   fi
+else
+   have_libindicate=no
+fi
+
+if test "x$enable_libindicate" = "xyes" -a "x$have_libindicate" != "xyes"; then
+   AC_MSG_ERROR([Couldn't find libindicate.])
+fi
+
+AM_CONDITIONAL(HAVE_LIBINDICATE, test "x$have_libindicate" = "xyes")
+
+# -----------------------------------------------------------
 # Tests
 # -----------------------------------------------------------
 AC_ARG_ENABLE(tests,
@@ -487,26 +517,27 @@ AC_OUTPUT([
 echo "
 Configure summary:
 
-	Compiler....................:  ${CC}
-	Compiler Flags..............:  ${CFLAGS}
-	Prefix......................:  ${prefix}
-	Shaved build................:  ${enable_shave}
-	Tests.......................:  ${have_check}
-	Coding style checks.........:  ${ENABLE_CODING_STYLE_CHECKS}
+	Compiler...............................:  ${CC}
+	Compiler Flags.........................:  ${CFLAGS}
+	Prefix.................................:  ${prefix}
+	Shaved build...........................:  ${enable_shave}
+	Tests..................................:  ${have_check}
+	Coding style checks....................:  ${ENABLE_CODING_STYLE_CHECKS}
 
     Features:
-	Spell checking (enchant)....:  ${have_enchant}
-	Display maps (libchamplain).:  ${have_libchamplain}
-	Location awareness (Geoclue):  ${have_geoclue}
-	Adium themes (Webkit).......:  ${have_webkit}
+	Spell checking (enchant)...............:  ${have_enchant}
+	Display maps (libchamplain)............:  ${have_libchamplain}
+	Location awareness (Geoclue)...........:  ${have_geoclue}
+	Adium themes (Webkit)..................:  ${have_webkit}
+	Message indicator support (libindicate):  ${have_libindicate}
 
     Connectivity:
-	NetworkManager integration..:  ${have_nm}
-	ConnMan integration.........:  ${have_connman}
+	NetworkManager integration.............:  ${have_nm}
+	ConnMan integration....................:  ${have_connman}
 
     Extras:
-	Documentation...............:  ${enable_gtk_doc}
-	Python bindings.............:  ${have_python}
-	Megaphone applet............:  ${have_megaphone}
-	Nothere applet..............:  ${have_nothere}
+	Documentation..........................:  ${enable_gtk_doc}
+	Python bindings........................:  ${have_python}
+	Megaphone applet.......................:  ${have_megaphone}
+	Nothere applet.........................:  ${have_nothere}
 "
diff --git a/data/empathy.schemas.in b/data/empathy.schemas.in
index 6aa7f8a..0b31678 100644
--- a/data/empathy.schemas.in
+++ b/data/empathy.schemas.in
@@ -291,6 +291,21 @@
     </schema>
 
     <schema>
+      <key>/schemas/apps/empathy/notifications/use_libindicate</key>
+      <applyto>/apps/empathy/notifications/use_libindicate</applyto>
+      <owner>empathy</owner>
+      <type>bool</type>
+      <default>true</default>
+      <locale name="C">
+         <short>Use the messaging indicator</short>
+         <long>
+         Whether or not to use the messaging indicator, if false
+	 the icon in the notification area will be displayed.
+         </long>
+      </locale>
+    </schema>
+
+    <schema>
        <key>/schemas/apps/empathy/ui/separate_chat_windows</key>
        <applyto>/apps/empathy/ui/separate_chat_windows</applyto>
        <owner>empathy</owner>
diff --git a/libempathy-gtk/empathy-conf.h b/libempathy-gtk/empathy-conf.h
index d8e34f6..ca4ade5 100644
--- a/libempathy-gtk/empathy-conf.h
+++ b/libempathy-gtk/empathy-conf.h
@@ -74,6 +74,7 @@ struct _EmpathyConfClass {
 #define EMPATHY_PREFS_UI_AVATAR_DIRECTORY          EMPATHY_PREFS_PATH "/ui/avatar_directory"
 #define EMPATHY_PREFS_UI_SHOW_AVATARS              EMPATHY_PREFS_PATH "/ui/show_avatars"
 #define EMPATHY_PREFS_UI_COMPACT_CONTACT_LIST      EMPATHY_PREFS_PATH "/ui/compact_contact_list"
+#define EMPATHY_PREFS_UI_USE_LIBINDICATE           EMPATHY_PREFS_PATH "/notifications/use_libindicate"
 #define EMPATHY_PREFS_CONTACTS_SHOW_OFFLINE        EMPATHY_PREFS_PATH "/contacts/show_offline"
 #define EMPATHY_PREFS_CONTACTS_SORT_CRITERIUM      EMPATHY_PREFS_PATH "/contacts/sort_criterium"
 #define EMPATHY_PREFS_HINTS_CLOSE_MAIN_WINDOW      EMPATHY_PREFS_PATH "/hints/close_main_window"
diff --git a/libempathy-gtk/empathy-ui-utils.c b/libempathy-gtk/empathy-ui-utils.c
index aeb665c..2ca004d 100644
--- a/libempathy-gtk/empathy-ui-utils.c
+++ b/libempathy-gtk/empathy-ui-utils.c
@@ -1269,24 +1269,35 @@ empathy_window_iconify (GtkWindow *window, GtkStatusIcon *status_icon)
 	Display      *dpy;
 	GdkWindow    *gdk_window;
 
-	gtk_status_icon_get_geometry (status_icon, NULL, &icon_location, NULL);
-	gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
-	dpy = gdk_x11_drawable_get_xdisplay (gdk_window);
-
-	data[0] = icon_location.x;
-	data[1] = icon_location.y;
-	data[2] = icon_location.width;
-	data[3] = icon_location.height;
-
-	XChangeProperty (dpy,
-			 GDK_WINDOW_XID (gdk_window),
-			 gdk_x11_get_xatom_by_name_for_display (gdk_drawable_get_display (gdk_window),
-			 "_NET_WM_ICON_GEOMETRY"),
-			 XA_CARDINAL, 32, PropModeReplace,
-			 (guchar *)&data, 4);
+	// If the status icon isn't visible (because indicators are used) then
+	// attempting to change the properties of the status icon doesn't work.
+	if (gtk_status_icon_get_visible (status_icon)) {
+		gtk_status_icon_get_geometry (status_icon, NULL, &icon_location, NULL);
+		gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
+		dpy = gdk_x11_drawable_get_xdisplay (gdk_window);
+
+		data[0] = icon_location.x;
+		data[1] = icon_location.y;
+		data[2] = icon_location.width;
+		data[3] = icon_location.height;
+
+		XChangeProperty (dpy,
+				 GDK_WINDOW_XID (gdk_window),
+				 gdk_x11_get_xatom_by_name_for_display (gdk_drawable_get_display (gdk_window),
+				 "_NET_WM_ICON_GEOMETRY"),
+				 XA_CARDINAL, 32, PropModeReplace,
+				 (guchar *)&data, 4);
+	}
 
 	gtk_window_set_skip_taskbar_hint (window, TRUE);
-	gtk_window_iconify (window);
+	// If the status icon isn't present then the WM will probably choose to
+	// iconfy to the taskbar, which doesn't look as good as the taskbar
+	// entry has just been removed. Just hide instead.
+	if (gtk_status_icon_get_visible (status_icon)) {
+		gtk_window_iconify (window);
+	} else {
+		gtk_widget_hide (GTK_WIDGET(window));
+	}
 }
 
 /* Takes care of moving the window to the current workspace. */
diff --git a/po/POTFILES.in b/po/POTFILES.in
index bf356f0..17cb9fc 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -79,6 +79,8 @@ src/empathy-import-widget.c
 [type: gettext/glade]src/empathy-import-dialog.ui
 src/empathy-import-widget.c
 src/empathy-import-mc4-accounts.c
+src/empathy-indicator.c
+src/empathy-indicator-manager.c
 src/empathy-main-window.c
 [type: gettext/glade]src/empathy-main-window.ui
 src/empathy-new-chatroom-dialog.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 980f002..96c2d82 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -4,8 +4,10 @@ include $(top_srcdir)/tools/flymake.mk
 AM_CPPFLAGS =						\
 	-I$(top_srcdir)                          	\
 	-DG_LOG_DOMAIN=\"empathy\"			\
+	-DDESKTOPDIR=\"$(datadir)/applications\"	\
 	$(EMPATHY_CFLAGS)				\
 	$(LIBNOTIFY_CFLAGS)				\
+	$(INDICATE_CFLAGS)				\
 	$(LIBCHAMPLAIN_CFLAGS)				\
 	$(DISABLE_DEPRECATED)				\
 	$(WEBKIT_CFLAGS)				\
@@ -16,6 +18,7 @@ LDADD =								\
 	$(top_builddir)/libempathy/libempathy.la		\
 	$(top_builddir)/extensions/libemp-extensions.la		\
 	$(LIBNOTIFY_LIBS)					\
+	$(INDICATE_LIBS)					\
 	$(EMPATHY_LIBS)						\
 	$(LIBCHAMPLAIN_LIBS)					\
 	$(WEBKIT_LIBS)
@@ -85,6 +88,18 @@ EXTRA_DIST =			\
 	$(autostart_DATA)	\
 	$(ui_DATA)
 
+if HAVE_LIBINDICATE
+empathy_handwritten_source += 				\
+	empathy-indicator-manager.c		\
+	empathy-indicator-manager.h 		\
+	empathy-indicator.c empathy-indicator.h
+else
+EXTRA_DIST += 					\
+	empathy-indicator-manager.c		\
+	empathy-indicator-manager.h		\
+	empathy-indicator.c empathy-indicator.h
+endif
+
 if HAVE_LIBCHAMPLAIN
 empathy_handwritten_source += 				\
 	empathy-map-view.c			\
diff --git a/src/empathy-chat-window.c b/src/empathy-chat-window.c
index 9e89f75..f5d0d9e 100644
--- a/src/empathy-chat-window.c
+++ b/src/empathy-chat-window.c
@@ -56,6 +56,11 @@
 #include "empathy-about-dialog.h"
 #include "empathy-misc.h"
 
+#ifdef HAVE_LIBINDICATE
+#include "empathy-indicator.h"
+#include "empathy-indicator-manager.h"
+#endif
+
 #define DEBUG_FLAG EMPATHY_DEBUG_CHAT
 #include <libempathy/empathy-debug.h>
 
@@ -78,6 +83,11 @@ typedef struct {
 	GtkWidget   *notebook;
 	NotifyNotification *notification;
 	NotificationData *notification_data;
+#ifdef HAVE_LIBINDICATE
+	EmpathyIndicatorManager *indicator_manager;
+	/* EmpathyChat -> EmpathyIndicator for that chat, if any */
+	GHashTable  *indicators;
+#endif
 
 	/* Menu items. */
 	GtkUIManager *ui_manager;
@@ -943,6 +953,76 @@ chat_window_notification_closed_cb (NotifyNotification *notify,
 	priv->notification_data = NULL;
 }
 
+#ifdef HAVE_LIBINDICATE
+static void
+chat_window_indicator_activate_cb (EmpathyIndicator *indicator,
+				    NotificationData *cb_data)
+{
+	empathy_chat_window_present_chat (cb_data->chat);
+
+	empathy_indicator_hide (indicator);
+	g_object_unref (cb_data->chat);
+	g_slice_free (NotificationData, cb_data);
+}
+
+static void
+chat_window_add_indicator (EmpathyChatWindow *window,
+					 EmpathyMessage *message,
+					 EmpathyChat    *chat)
+{
+	EmpathyChatWindowPriv *priv = GET_PRIV (window);
+	EmpathyContact *sender;
+	const char *body;
+	NotificationData *cb_data;
+	gboolean use_libindicate;
+	EmpathyIndicator *indicator;
+
+	empathy_conf_get_bool (empathy_conf_get (),
+			       EMPATHY_PREFS_UI_USE_LIBINDICATE,
+		               &use_libindicate);
+
+	if (!use_libindicate) {
+		return;
+	}
+
+	cb_data = g_slice_new0 (NotificationData);
+	cb_data->chat = g_object_ref (chat);
+	cb_data->window = window;
+
+	sender = empathy_message_get_sender (message);
+	body = empathy_message_get_body (message);
+
+	indicator = g_hash_table_lookup (priv->indicators, chat);
+	if (indicator) {
+		empathy_indicator_update (indicator, body);
+	} else {
+		indicator = empathy_indicator_manager_create_indicator (priv->indicator_manager,
+			sender, body);
+		g_signal_connect (indicator, "activate",
+				  G_CALLBACK (chat_window_indicator_activate_cb), cb_data);
+		g_hash_table_insert (priv->indicators, chat, indicator);
+	}
+	empathy_indicator_show (indicator);
+}
+
+static void
+chat_window_remove_indicator (EmpathyChatWindow *window, EmpathyChat *chat)
+{
+	EmpathyIndicator *indicator;
+	EmpathyChatWindowPriv *priv = GET_PRIV (window);
+
+	indicator = g_hash_table_lookup (priv->indicators, chat);
+
+	if (!indicator) {
+		return;
+	}
+
+	empathy_indicator_hide (indicator);
+	g_hash_table_remove (priv->indicators, chat);
+	g_object_unref (indicator);
+}
+#endif
+
 static void
 chat_window_show_or_update_notification (EmpathyChatWindow *window,
 					 EmpathyMessage *message,
@@ -1076,6 +1156,9 @@ chat_window_new_message_cb (EmpathyChat       *chat,
 		empathy_sound_play (GTK_WIDGET (priv->dialog),
 		    EMPATHY_SOUND_MESSAGE_INCOMING);
 		chat_window_show_or_update_notification (window, message, chat);
+#ifdef HAVE_LIBINDICATE
+		chat_window_add_indicator (window, message, chat);
+#endif
 	}
 }
 
@@ -1135,6 +1218,10 @@ chat_window_page_switched_cb (GtkNotebook      *notebook,
 	priv->chats_new_msg = g_list_remove (priv->chats_new_msg, chat);
 
 	chat_window_update_chat_tab (chat);
+
+#ifdef HAVE_LIBINDICATE
+	chat_window_remove_indicator (window, chat);
+#endif
 }
 
 static void
@@ -1245,6 +1332,11 @@ chat_window_focus_in_event_cb (GtkWidget        *widget,
 	/* Update the title, since we now mark all unread messages as read. */
 	chat_window_update_chat_tab (priv->current_chat);
 
+#ifdef HAVE_LIBINDICATE
+	/* Remove the indicator for the active chat */
+	chat_window_remove_indicator (window, priv->current_chat);
+#endif
+
 	return FALSE;
 }
 
@@ -1476,6 +1568,11 @@ empathy_chat_window_init (EmpathyChatWindow *window)
 	g_object_unref (gui);
 
 	priv->chatroom_manager = empathy_chatroom_manager_dup_singleton (NULL);
+#ifdef HAVE_LIBINDICATE
+	priv->indicator_manager = empathy_indicator_manager_dup_singleton ();
+	priv->indicators = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+			      NULL, g_object_unref);
+#endif
 
 	priv->notebook = gtk_notebook_new ();
 	gtk_notebook_set_group (GTK_NOTEBOOK (priv->notebook), "EmpathyChatWindow");
diff --git a/src/empathy-indicator-manager.c b/src/empathy-indicator-manager.c
new file mode 100644
index 0000000..c812a3f
--- /dev/null
+++ b/src/empathy-indicator-manager.c
@@ -0,0 +1,438 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY 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, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA  02110-1301  USA
+ *
+ * Authors: James Westby <james.westby@ubuntu.com>
+ *
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+
+#include <libempathy/empathy-contact.h>
+#include <libempathy/empathy-dispatcher.h>
+#include <libempathy/empathy-utils.h>
+
+#include <libempathy-gtk/empathy-conf.h>
+#include <libempathy-gtk/empathy-ui-utils.h>
+
+#include <telepathy-glib/util.h>
+
+#include "empathy-event-manager.h"
+#include "empathy-indicator.h"
+#include "empathy-indicator-manager.h"
+#include "empathy-misc.h"
+
+#include <libindicate/server.h>
+
+#define INDICATOR_LOGIN_TIMEOUT 15
+#define EMPATHY_DESKTOP_PATH DESKTOPDIR "/empathy.desktop"
+
+#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyIndicatorManager)
+
+enum {
+  SERVER_ACTIVATE,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+typedef struct {
+  EmpathyEventManager *event_manager;
+  IndicateServer      *indicate_server;
+  GSList              *indicator_events;
+  GHashTable          *login_timeouts;
+} EmpathyIndicatorManagerPriv;
+
+typedef struct {
+  EmpathyIndicator    *indicator;
+  EmpathyEvent        *event;
+} IndicatorEvent;
+
+typedef struct {
+  EmpathyIndicatorManager *manager;
+  EmpathyIndicator        *indicator;
+} LoginData;
+
+G_DEFINE_TYPE (EmpathyIndicatorManager, empathy_indicator_manager, G_TYPE_OBJECT);
+
+static EmpathyIndicatorManager *manager_singleton = NULL;
+
+
+static IndicatorEvent *
+indicator_event_new (EmpathyIndicator *indicator,
+    EmpathyEvent *event)
+{
+  IndicatorEvent *indicator_event;
+
+  indicator_event = g_slice_new0 (IndicatorEvent);
+  indicator_event->indicator = g_object_ref (indicator);
+  indicator_event->event = g_object_ref (event);
+
+  return indicator_event;
+}
+
+
+static void
+indicator_event_free (IndicatorEvent *indicator_event)
+{
+  g_object_unref (indicator_event->indicator);
+  g_object_unref (indicator_event->event);
+  g_free (indicator_event);
+}
+
+
+static void
+indicate_server_activate (IndicateServer *server,
+    EmpathyIndicatorManager *manager)
+{
+  g_signal_emit (manager, signals[SERVER_ACTIVATE], 0);
+}
+
+
+static void
+indicate_show_cb (EmpathyIndicator *indicator,
+    EmpathyEvent *event)
+{
+  empathy_event_activate (event);
+}
+
+
+static void
+indicator_manager_event_added_cb (EmpathyEventManager *event_manager,
+    EmpathyEvent *event,
+    EmpathyIndicatorManager *manager)
+{
+  EmpathyIndicator *indicator;
+  EmpathyIndicatorManagerPriv *priv;
+  IndicatorEvent *indicator_event;
+
+  priv = GET_PRIV (manager);
+
+  if (event->contact == NULL)
+    return;
+  indicator = empathy_indicator_new (event->contact, event->message, "im");
+  empathy_indicator_show (indicator);
+  g_signal_connect (G_OBJECT(indicator), "activate",
+      G_CALLBACK (indicate_show_cb),
+      event);
+  indicator_event = indicator_event_new (indicator, event);
+  g_object_unref (indicator);
+  priv->indicator_events = g_slist_prepend (priv->indicator_events,
+      indicator_event);
+}
+
+
+static void
+indicator_manager_event_removed_cb (EmpathyEventManager *event_manager,
+    EmpathyEvent *event,
+    EmpathyIndicatorManager *manager)
+{
+  EmpathyIndicatorManagerPriv *priv;
+  GSList *l;
+
+  priv = GET_PRIV (manager);
+
+  for (l = priv->indicator_events; l; l = l->next)
+  {
+    IndicatorEvent *indicator_event;
+    indicator_event = l->data;
+    if (indicator_event->event == event) {
+      priv->indicator_events = g_slist_remove (priv->indicator_events,
+          indicator_event);
+      empathy_indicator_hide (indicator_event->indicator);
+      indicator_event_free (indicator_event);
+      return;
+    }
+  }
+}
+
+
+static void
+indicator_manager_event_updated_cb (EmpathyEventManager *event_manager,
+    EmpathyEvent *event,
+    EmpathyIndicatorManager *manager)
+{
+  EmpathyIndicatorManagerPriv *priv;
+  GSList *l;
+
+  priv = GET_PRIV (manager);
+
+  for (l = priv->indicator_events; l; l = l->next)
+  {
+    IndicatorEvent *indicator_event;
+    indicator_event = l->data;
+    if (indicator_event->event == event) {
+      empathy_indicator_update (indicator_event->indicator,
+          event->message);
+      return;
+    }
+  }
+}
+
+
+/* Remove the login indicator when it times out */
+static gboolean
+indicate_login_timeout (gpointer data)
+{
+  LoginData *login_data;
+  EmpathyIndicator *e_indicator;
+  EmpathyIndicatorManager *manager;
+  EmpathyIndicatorManagerPriv *priv;
+
+  login_data = (LoginData *) data;
+  e_indicator = login_data->indicator;
+  manager = login_data->manager;
+  priv = GET_PRIV (manager);
+
+  empathy_indicator_hide (e_indicator);
+  g_hash_table_remove (priv->login_timeouts, e_indicator);
+
+  return FALSE;
+}
+
+
+static void
+indicate_login_cb (EmpathyIndicator *e_indicator,
+    EmpathyIndicatorManager *manager)
+{
+  EmpathyIndicatorManagerPriv *priv;
+  GSList *events, *l;
+  EmpathyContact *contact;
+
+  priv = GET_PRIV (manager);
+
+  empathy_indicator_hide (e_indicator);
+  g_hash_table_remove (priv->login_timeouts, e_indicator);
+
+  contact = empathy_indicator_get_contact (e_indicator);
+  /* If the contact has an event activate it, otherwise the
+   * default handler of row-activated will be called. */
+  events = empathy_event_manager_get_events (priv->event_manager);
+  for (l = events; l; l = l->next) {
+    EmpathyEvent *event;
+
+    event = l->data;
+    if (event->contact == contact) {
+      empathy_event_activate (event);
+      return;
+    }
+  }
+
+  /* Else start a new conversation */
+  empathy_dispatcher_chat_with_contact (contact, NULL, NULL);
+}
+
+
+EmpathyIndicatorManager *
+empathy_indicator_manager_dup_singleton (void)
+{
+  return g_object_new (EMPATHY_TYPE_INDICATOR_MANAGER, NULL);
+}
+
+
+static void
+indicator_manager_dispose (GObject *object)
+{
+  EmpathyIndicatorManagerPriv *priv;
+
+  priv = GET_PRIV (object);
+
+  if (priv->indicator_events) {
+    g_slist_foreach (priv->indicator_events, (GFunc) indicator_event_free,
+        NULL);
+    g_slist_free (priv->indicator_events);
+    priv->indicator_events = NULL;
+  }
+  if (priv->event_manager) {
+    g_object_unref (priv->event_manager);
+    priv->event_manager = NULL;
+  }
+  if (priv->indicate_server) {
+    g_object_unref (priv->indicate_server);
+    priv->indicate_server = NULL;
+  }
+  if (priv->login_timeouts) {
+    g_hash_table_unref (priv->login_timeouts);
+    priv->login_timeouts = NULL;
+  }
+
+  G_OBJECT_CLASS (empathy_indicator_manager_parent_class)->dispose (object);
+}
+
+
+static GObject *
+indicator_manager_constructor (GType type,
+    guint n_props,
+    GObjectConstructParam *props)
+{
+  GObject *retval;
+
+  if (manager_singleton != NULL) {
+    retval = g_object_ref (manager_singleton);
+  } else {
+    retval = G_OBJECT_CLASS (empathy_indicator_manager_parent_class)->constructor
+      (type, n_props, props);
+
+    manager_singleton = EMPATHY_INDICATOR_MANAGER (retval);
+    g_object_add_weak_pointer (retval, (gpointer) &manager_singleton);
+  }
+
+  return retval;
+}
+
+
+static void
+empathy_indicator_manager_class_init (EmpathyIndicatorManagerClass *klass)
+{
+  GObjectClass *object_class;
+
+  object_class = G_OBJECT_CLASS (klass);
+  object_class->dispose = indicator_manager_dispose;
+  object_class->constructor = indicator_manager_constructor;
+
+  signals[SERVER_ACTIVATE] =
+      g_signal_new ("server-activate",
+          G_TYPE_FROM_CLASS (klass),
+          G_SIGNAL_RUN_LAST,
+          0,
+          NULL, NULL,
+          g_cclosure_marshal_VOID__VOID,
+          G_TYPE_NONE,
+          0);
+
+  g_type_class_add_private (object_class, sizeof (EmpathyIndicatorManagerPriv));
+}
+
+
+static void
+empathy_indicator_manager_init (EmpathyIndicatorManager *manager)
+{
+  EmpathyIndicatorManagerPriv *priv;
+
+  priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
+       EMPATHY_TYPE_INDICATOR_MANAGER, EmpathyIndicatorManagerPriv);
+  manager->priv = priv;
+
+  priv->event_manager = empathy_event_manager_dup_singleton ();
+  priv->login_timeouts = g_hash_table_new_full (NULL, NULL,
+      (GDestroyNotify) g_object_unref, (GDestroyNotify) g_source_unref);
+  priv->indicate_server = indicate_server_ref_default ();
+  indicate_server_set_type (priv->indicate_server, "message.instant");
+  indicate_server_set_desktop_file (priv->indicate_server,
+      EMPATHY_DESKTOP_PATH);
+
+  g_signal_connect (priv->indicate_server,
+      INDICATE_SERVER_SIGNAL_SERVER_DISPLAY,
+      G_CALLBACK (indicate_server_activate),
+      manager);
+
+  g_signal_connect (priv->event_manager, "event-added",
+      G_CALLBACK (indicator_manager_event_added_cb),
+      manager);
+  g_signal_connect (priv->event_manager, "event-removed",
+      G_CALLBACK (indicator_manager_event_removed_cb),
+      manager);
+  g_signal_connect (priv->event_manager, "event-updated",
+      G_CALLBACK (indicator_manager_event_updated_cb),
+      manager);
+}
+
+
+void
+empathy_indicator_manager_set_server_visible (EmpathyIndicatorManager *manager,
+    gboolean visible)
+{
+  EmpathyIndicatorManagerPriv *priv;
+
+  priv = GET_PRIV (manager);
+  if (visible) {
+    indicate_server_show (priv->indicate_server);
+  } else {
+    /* Causes a crash on next show currently due to object not being
+       unregistered from dbus
+    indicate_server_hide (priv->indicate_server);
+    */
+  }
+}
+
+
+EmpathyIndicator *
+empathy_indicator_manager_create_indicator (EmpathyIndicatorManager *manager,
+    EmpathyContact *sender,
+    const gchar *body)
+{
+  return empathy_indicator_new (sender, body, "im");
+}
+
+
+static LoginData *
+login_data_new (EmpathyIndicator *e_indicator,
+    EmpathyIndicatorManager *manager)
+{
+  LoginData *login_data;
+
+  login_data = g_slice_new0 (LoginData);
+  login_data->manager = g_object_ref (manager);
+  login_data->indicator = g_object_ref (e_indicator);
+
+  return login_data;
+}
+
+
+static void
+indicator_destroy_login_data (gpointer data)
+{
+  LoginData *login_data;
+
+  login_data = (LoginData *) data;
+
+  g_object_unref (login_data->manager);
+  g_object_unref (login_data->indicator);
+  g_slice_free (LoginData, login_data);
+}
+
+
+/* Add an indicator for someone logging in. This will be displayed for
+ * a short period only.
+ */
+void
+empathy_indicator_manager_add_login_indicator (EmpathyIndicatorManager *manager,
+    EmpathyContact *contact)
+{
+  EmpathyIndicatorManagerPriv *priv;
+  GSource *timeout;
+  EmpathyIndicator *e_indicator;
+  LoginData *login_data;
+
+  priv = GET_PRIV (manager);
+  e_indicator = empathy_indicator_new (contact, NULL, "login");
+  login_data = login_data_new (e_indicator, manager);
+
+  timeout = g_timeout_source_new_seconds (INDICATOR_LOGIN_TIMEOUT);
+  g_source_set_callback (timeout, indicate_login_timeout, login_data,
+      indicator_destroy_login_data);
+  g_source_attach (timeout, NULL);
+
+  g_hash_table_insert (priv->login_timeouts, e_indicator, timeout);
+
+  g_signal_connect (e_indicator, "activate",
+      G_CALLBACK (indicate_login_cb), manager);
+  empathy_indicator_show (e_indicator);
+}
diff --git a/src/empathy-indicator-manager.h b/src/empathy-indicator-manager.h
new file mode 100644
index 0000000..a6261d3
--- /dev/null
+++ b/src/empathy-indicator-manager.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY 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, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA  02110-1301  USA
+ *
+ * Authors: James Westby <james.westby@ubuntu.com>
+ *
+ */
+
+#ifndef __EMPATHY_INDICATOR_MANAGER_H__
+#define __EMPATHY_INDICATOR_MANAGER_H__
+
+#include <glib.h>
+
+#include <libempathy/empathy-contact.h>
+#include "empathy-indicator.h"
+
+G_BEGIN_DECLS
+
+#define EMPATHY_TYPE_INDICATOR_MANAGER \
+  (empathy_indicator_manager_get_type ())
+#define EMPATHY_INDICATOR_MANAGER(o) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_INDICATOR_MANAGER, \
+                               EmpathyIndicatorManager))
+#define EMPATHY_INDICATOR_MANAGER_CLASS(k) \
+  (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_INDICATOR_MANAGER, \
+                           EmpathyIndicatorManagerClass))
+#define EMPATHY_IS_INDICATOR_MANAGER(o) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_INDICATOR_MANAGER))
+#define EMPATHY_IS_INDICATOR_MANAGER_CLASS(k) \
+  (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_INDICATOR_MANAGER))
+#define EMPATHY_INDICATOR_MANAGER_GET_CLASS(o) \
+  (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_INDICATOR_MANAGER, \
+                              EmpathyIndicatorManagerClass))
+
+typedef struct _EmpathyIndicatorManager      EmpathyIndicatorManager;
+typedef struct _EmpathyIndicatorManagerClass EmpathyIndicatorManagerClass;
+
+struct _EmpathyIndicatorManager {
+  GObject parent;
+  gpointer priv;
+};
+
+struct _EmpathyIndicatorManagerClass {
+  GObjectClass parent_class;
+};
+
+GType empathy_indicator_manager_get_type (void) G_GNUC_CONST;
+EmpathyIndicatorManager *empathy_indicator_manager_dup_singleton (void);
+void empathy_indicator_manager_set_server_visible (
+    EmpathyIndicatorManager *manager,
+    gboolean visible);
+EmpathyIndicator *empathy_indicator_manager_create_indicator (
+    EmpathyIndicatorManager *manager,
+    EmpathyContact *sender,
+    const gchar *body);
+void empathy_indicator_manager_add_login_indicator (
+    EmpathyIndicatorManager *manager,
+    EmpathyContact *contact);
+
+G_END_DECLS
+
+#endif /* __EMPATHY_INDICATOR_MANAGER_H__ */
diff --git a/src/empathy-indicator.c b/src/empathy-indicator.c
new file mode 100644
index 0000000..cbdecb0
--- /dev/null
+++ b/src/empathy-indicator.c
@@ -0,0 +1,309 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY 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, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA  02110-1301  USA
+ *
+ * Authors: James Westby <james.westby@ubuntu.com>
+ *
+ */
+
+#include <gtk/gtk.h>
+
+#include <libempathy/empathy-contact.h>
+#include <libempathy/empathy-utils.h>
+
+#include "empathy-indicator.h"
+#include "empathy-misc.h"
+
+#include <libindicate/indicator.h>
+#include <libindicate-gtk/indicator.h>
+
+#define GET_PRIV(obj) EMPATHY_GET_PRIV ((obj), EmpathyIndicator)
+
+enum {
+  PROP_0,
+  PROP_CONTACT,
+  PROP_BODY,
+  PROP_SUBTYPE,
+  PROP_VISIBLE
+};
+
+enum {
+  ACTIVATE,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+typedef struct {
+  IndicateIndicator *indicator;
+  EmpathyContact    *contact;
+  gchar             *body;
+  gchar             *subtype;
+} EmpathyIndicatorPriv;
+
+G_DEFINE_TYPE (EmpathyIndicator, empathy_indicator, G_TYPE_OBJECT)
+
+
+static void
+empathy_indicator_dispose (GObject *object)
+{
+  EmpathyIndicatorPriv *priv;
+
+  priv = GET_PRIV (object);
+
+  if (priv->indicator) {
+    g_object_unref (priv->indicator);
+    priv->indicator = NULL;
+  }
+  if (priv->contact) {
+    g_object_unref (priv->contact);
+    priv->contact = NULL;
+  }
+
+  G_OBJECT_CLASS (empathy_indicator_parent_class)->dispose (object);
+}
+
+
+static void
+empathy_indicator_finalize (GObject *object)
+{
+  EmpathyIndicatorPriv *priv;
+
+  priv = GET_PRIV (object);
+
+  g_free (priv->body);
+  g_free (priv->subtype);
+
+  G_OBJECT_CLASS (empathy_indicator_parent_class)->finalize (object);
+}
+
+static void
+indicate_show_cb (IndicateIndicator *indicator,
+    EmpathyIndicator *e_indicator)
+{
+  g_signal_emit (e_indicator, signals[ACTIVATE], 0);
+}
+
+
+static IndicateIndicator *
+empathy_indicator_get_indicator (EmpathyIndicator *e_indicator)
+{
+  EmpathyIndicatorPriv *priv;
+
+  priv = GET_PRIV (e_indicator);
+  if (priv->indicator)
+    return priv->indicator;
+
+  priv->indicator = indicate_indicator_new ();
+  g_assert (priv->indicator);
+  g_signal_connect (G_OBJECT (priv->indicator),
+      INDICATE_INDICATOR_SIGNAL_DISPLAY,
+      G_CALLBACK (indicate_show_cb),
+      e_indicator);
+
+  return priv->indicator;
+}
+
+
+static void
+empathy_indicator_set_property (GObject *object,
+    guint param_id,
+    const GValue *value,
+    GParamSpec *pspec)
+{
+  EmpathyIndicator *e_indicator;
+  EmpathyIndicatorPriv *priv;
+  IndicateIndicator *indicator;
+  GdkPixbuf *pixbuf;
+  GTimeVal time;
+
+  e_indicator = EMPATHY_INDICATOR (object);
+  priv = GET_PRIV (e_indicator);
+
+  indicator = empathy_indicator_get_indicator (e_indicator);
+
+  switch (param_id) {
+  case PROP_CONTACT:
+    priv->contact = g_object_ref (g_value_get_object (value));
+    g_assert (priv->contact);
+    indicate_indicator_set_property (indicator, "sender",
+        empathy_contact_get_name (priv->contact));
+    pixbuf = empathy_misc_get_pixbuf_for_indicator (priv->contact);
+    if (pixbuf) {
+      indicate_indicator_set_property_icon (priv->indicator, "icon", pixbuf);
+      g_object_unref (pixbuf);
+    }
+    break;
+  case PROP_BODY:
+    if (priv->body)
+      g_free (priv->body);
+    priv->body = g_strdup (g_value_get_string (value));
+    indicate_indicator_set_property (indicator, "body", priv->body);
+    g_get_current_time (&time);
+    indicate_indicator_set_property_time (indicator, "time", &time);
+    break;
+  case PROP_SUBTYPE:
+    if (priv->subtype)
+      g_free (priv->subtype);
+    priv->subtype = g_strdup (g_value_get_string (value));
+    indicate_indicator_set_property (indicator, "subtype", priv->subtype);
+    break;
+  case PROP_VISIBLE:
+    if (g_value_get_boolean (value))
+      indicate_indicator_show (indicator);
+    else
+      indicate_indicator_hide (indicator);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+    break;
+  }
+}
+
+
+static void
+empathy_indicator_get_property (GObject *object,
+    guint param_id,
+    GValue *value,
+    GParamSpec *pspec)
+{
+  EmpathyIndicator *e_indicator;
+  EmpathyIndicatorPriv *priv;
+
+  e_indicator = EMPATHY_INDICATOR (object);
+  priv = GET_PRIV (e_indicator);
+
+  switch (param_id) {
+  case PROP_CONTACT:
+    g_value_set_object (value, priv->contact);
+    break;
+  case PROP_BODY:
+    g_value_set_string (value, priv->body);
+    break;
+  case PROP_SUBTYPE:
+    g_value_set_string (value, priv->subtype);
+    break;
+  case PROP_VISIBLE:
+    g_value_set_boolean (value, indicate_indicator_is_visible (priv->indicator));
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+    break;
+  }
+}
+
+
+static void
+empathy_indicator_class_init (EmpathyIndicatorClass *klass)
+{
+  GObjectClass *object_class;
+
+  object_class = G_OBJECT_CLASS (klass);
+
+  object_class->set_property = empathy_indicator_set_property;
+  object_class->get_property = empathy_indicator_get_property;
+  object_class->dispose = empathy_indicator_dispose;
+  object_class->finalize = empathy_indicator_finalize;
+
+  g_object_class_install_property (object_class, PROP_CONTACT,
+      g_param_spec_object ("contact",
+          "Contact",
+          "The contact being indicated",
+          EMPATHY_TYPE_CONTACT,
+          (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));
+  g_object_class_install_property (object_class, PROP_BODY,
+      g_param_spec_string ("body",
+          "Body",
+          "The text for this contact",
+          NULL,
+          (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)));
+  g_object_class_install_property (object_class, PROP_SUBTYPE,
+      g_param_spec_string ("subtype",
+          "Subtype",
+          "The type of this indicator",
+          NULL,
+          (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)));
+  g_object_class_install_property (object_class, PROP_VISIBLE,
+      g_param_spec_boolean ("visible",
+          "Visible",
+          "The visibility of this indicator",
+          FALSE,
+          G_PARAM_READWRITE));
+
+  signals[ACTIVATE] =
+      g_signal_new ("activate",
+          G_TYPE_FROM_CLASS (klass),
+          G_SIGNAL_RUN_LAST,
+          0,
+          NULL, NULL,
+          g_cclosure_marshal_VOID__VOID,
+          G_TYPE_NONE,
+          0);
+
+  g_type_class_add_private (object_class, sizeof (EmpathyIndicatorPriv));
+}
+
+
+static void
+empathy_indicator_init (EmpathyIndicator *e_indicator)
+{
+  e_indicator->priv = G_TYPE_INSTANCE_GET_PRIVATE (e_indicator,
+      EMPATHY_TYPE_INDICATOR,
+      EmpathyIndicatorPriv);
+}
+
+
+EmpathyIndicator *
+empathy_indicator_new (EmpathyContact *sender,
+    const gchar *body,
+    const gchar *subtype)
+{
+  return g_object_new (EMPATHY_TYPE_INDICATOR, "contact", sender, "body", body,
+      "subtype", subtype, NULL);
+}
+
+
+void
+empathy_indicator_show (EmpathyIndicator *e_indicator)
+{
+  g_object_set (e_indicator, "visible", TRUE, NULL);
+}
+
+
+void
+empathy_indicator_hide (EmpathyIndicator *e_indicator)
+{
+  g_object_set (e_indicator, "visible", FALSE, NULL);
+}
+
+
+void
+empathy_indicator_update (EmpathyIndicator *e_indicator,
+    const gchar *body)
+{
+  g_object_set (e_indicator, "body", body, NULL);
+}
+
+
+EmpathyContact *
+empathy_indicator_get_contact (EmpathyIndicator *e_indicator)
+{
+  EmpathyContact *contact;
+  g_object_get (e_indicator, "contact", &contact, NULL);
+  return contact;
+}
diff --git a/src/empathy-indicator.h b/src/empathy-indicator.h
new file mode 100644
index 0000000..05e2be1
--- /dev/null
+++ b/src/empathy-indicator.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY 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, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA  02110-1301  USA
+ *
+ * Authors: James Westby <james.westby@ubuntu.com>
+ *
+ */
+
+#ifndef __EMPATHY_INDICATOR_H__
+#define __EMPATHY_INDICATOR_H__
+
+#include <glib.h>
+
+#include <libempathy/empathy-contact.h>
+
+G_BEGIN_DECLS
+
+#define EMPATHY_TYPE_INDICATOR         (empathy_indicator_get_type ())
+#define EMPATHY_INDICATOR(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_INDICATOR, EmpathyIndicator))
+#define EMPATHY_INDICATOR_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_INDICATOR, EmpathyIndicatorClass))
+#define EMPATHY_IS_INDICATOR(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_INDICATOR))
+#define EMPATHY_IS_INDICATOR_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_INDICATOR))
+#define EMPATHY_INDICATOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_INDICATOR, EmpathyIndicatorClass))
+
+typedef struct _EmpathyIndicator      EmpathyIndicator;
+typedef struct _EmpathyIndicatorClass EmpathyIndicatorClass;
+
+struct _EmpathyIndicator {
+  GObject parent;
+  gpointer priv;
+};
+
+struct _EmpathyIndicatorClass {
+  GObjectClass parent_class;
+};
+
+GType empathy_indicator_get_type (void) G_GNUC_CONST;
+EmpathyIndicator *empathy_indicator_new (EmpathyContact *sender,
+    const gchar *body,
+    const gchar *type);
+void empathy_indicator_show (EmpathyIndicator *e_indicator);
+void empathy_indicator_hide (EmpathyIndicator *e_indicator);
+void empathy_indicator_update (EmpathyIndicator *e_indicator,
+    const gchar *body);
+EmpathyContact *empathy_indicator_get_contact (EmpathyIndicator *e_indicator);
+
+G_END_DECLS
+
+
+#endif /* __EMPATHY-INDICATOR_H__ */
diff --git a/src/empathy-main-window.c b/src/empathy-main-window.c
index 8fb127b..95a62e4 100644
--- a/src/empathy-main-window.c
+++ b/src/empathy-main-window.c
@@ -61,6 +61,10 @@
 #include "empathy-event-manager.h"
 #include "empathy-ft-manager.h"
 
+#ifdef HAVE_LIBINDICATE
+#include "empathy-indicator-manager.h"
+#endif
+
 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
 #include <libempathy/empathy-debug.h>
 
@@ -111,6 +115,10 @@ typedef struct {
 
 	/* Actions that are enabled when there are connected accounts */
 	GList                  *actions_connected;
+#ifdef HAVE_LIBINDICATE
+	EmpathyIndicatorManager *indicator_manager;
+	GHashTable              *indicator_timeouts;
+#endif
 } EmpathyMainWindow;
 
 static EmpathyMainWindow *window = NULL;
@@ -600,6 +608,10 @@ main_window_contact_presence_changed_cb (EmpathyContactMonitor *monitor,
         /* someone is logging in */
         empathy_sound_play (GTK_WIDGET (window->window),
           EMPATHY_SOUND_CONTACT_CONNECTED);
+#ifdef HAVE_LIBINDICATE
+        empathy_indicator_manager_add_login_indicator (window->indicator_manager,
+          contact);
+#endif
     }
 }
 
@@ -664,6 +676,10 @@ main_window_destroy_cb (GtkWidget         *widget,
 	g_object_unref (window->event_manager);
 	g_object_unref (window->ui_manager);
 
+#ifdef HAVE_LIBINDICATE
+	g_object_unref (window->indicator_manager);
+#endif
+
 	g_free (window);
 }
 
@@ -1238,6 +1254,10 @@ empathy_main_window_show (void)
 			      "help_contents", "activate", main_window_help_contents_cb,
 			      NULL);
 
+#ifdef HAVE_LIBINDICATE
+	window->indicator_manager = empathy_indicator_manager_dup_singleton ();
+#endif
+
 	/* Set up connection related widgets. */
 	main_window_connection_items_setup (window, gui);
 
diff --git a/src/empathy-misc.c b/src/empathy-misc.c
index a1a0847..147a882 100644
--- a/src/empathy-misc.c
+++ b/src/empathy-misc.c
@@ -30,6 +30,29 @@
 /* public methods */
 
 GdkPixbuf *
+empathy_misc_get_pixbuf_for_indicator (EmpathyContact *contact)
+{
+	GdkPixbuf *pixbuf = NULL;
+	GtkIconTheme *icon_theme;
+
+	if (contact != NULL) {
+		pixbuf = empathy_pixbuf_avatar_from_contact_scaled (contact,
+		                                                    16, 16);
+	}
+
+	if (!pixbuf) {
+		icon_theme = gtk_icon_theme_get_default ();
+		/* TODO: connect to icon theme changes */
+		if (icon_theme) {
+			pixbuf = gtk_icon_theme_load_icon (icon_theme, "stock_person",
+			                                   16, 0, NULL);
+		}
+	}
+
+	return pixbuf;
+}
+
+GdkPixbuf *
 empathy_misc_get_pixbuf_for_notification (EmpathyContact *contact,
 					  const char *icon_name)
 {
diff --git a/src/empathy-misc.h b/src/empathy-misc.h
index b3fe8fd..b705c47 100644
--- a/src/empathy-misc.h
+++ b/src/empathy-misc.h
@@ -41,6 +41,7 @@ typedef enum {
 } EmpathyNotificationClosedReason;
 
 gboolean    empathy_notification_is_enabled  (void);
+GdkPixbuf * empathy_misc_get_pixbuf_for_indicator (EmpathyContact *contact);
 GdkPixbuf * empathy_misc_get_pixbuf_for_notification (EmpathyContact *contact,
                                                       const char *icon_name);
 
diff --git a/src/empathy-preferences.c b/src/empathy-preferences.c
index d277273..728a323 100644
--- a/src/empathy-preferences.c
+++ b/src/empathy-preferences.c
@@ -53,6 +53,7 @@ typedef struct {
 
 	GtkWidget *checkbutton_show_smileys;
 	GtkWidget *checkbutton_show_contacts_in_rooms;
+	GtkWidget *checkbutton_use_libindicate;
 	GtkWidget *combobox_chat_theme;
 	GtkWidget *checkbutton_separate_chat_windows;
 	GtkWidget *checkbutton_autoconnect;
@@ -215,6 +216,14 @@ preferences_setup_widgets (EmpathyPreferences *preferences)
 					  EMPATHY_PREFS_CHAT_SHOW_SMILEYS,
 					  preferences->checkbutton_show_smileys);
 
+#ifdef HAVE_LIBINDICATE
+	preferences_hookup_toggle_button (preferences,
+					  EMPATHY_PREFS_UI_USE_LIBINDICATE,
+					  preferences->checkbutton_use_libindicate);
+#else
+	gtk_widget_hide (GTK_WIDGET (preferences->checkbutton_use_libindicate));
+#endif
+
 	preferences_hookup_toggle_button (preferences,
 					  EMPATHY_PREFS_CHAT_SHOW_CONTACTS_IN_ROOMS,
 					  preferences->checkbutton_show_contacts_in_rooms);
@@ -1137,6 +1146,7 @@ empathy_preferences_show (GtkWindow *parent)
 		"notebook", &preferences->notebook,
 		"checkbutton_show_smileys", &preferences->checkbutton_show_smileys,
 		"checkbutton_show_contacts_in_rooms", &preferences->checkbutton_show_contacts_in_rooms,
+		"checkbutton_use_libindicate", &preferences->checkbutton_use_libindicate,
 		"combobox_chat_theme", &preferences->combobox_chat_theme,
 		"checkbutton_separate_chat_windows", &preferences->checkbutton_separate_chat_windows,
 		"checkbutton_autoconnect", &preferences->checkbutton_autoconnect,
diff --git a/src/empathy-preferences.ui b/src/empathy-preferences.ui
index 1e3a425..044853f 100644
--- a/src/empathy-preferences.ui
+++ b/src/empathy-preferences.ui
@@ -250,6 +250,20 @@
                     <property name="position">1</property>
                   </packing>
                 </child>
+                <child>
+                  <object class="GtkCheckButton" id="checkbutton_use_libindicate">
+                    <property name="label" translatable="yes">Use message indicators</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
               </object>
               <packing>
                 <property name="position">1</property>
diff --git a/src/empathy-status-icon.c b/src/empathy-status-icon.c
index 430c933..3b3ec73 100644
--- a/src/empathy-status-icon.c
+++ b/src/empathy-status-icon.c
@@ -28,6 +28,7 @@
 #include <gtk/gtk.h>
 #include <gdk/gdkkeysyms.h>
 
+#include <libnotify/notify.h>
 #include <libnotify/notification.h>
 
 #include <libempathy/empathy-utils.h>
@@ -40,12 +41,18 @@
 #include <libempathy-gtk/empathy-images.h>
 #include <libempathy-gtk/empathy-new-message-dialog.h>
 
+#include <telepathy-glib/util.h>
+
 #include "empathy-accounts-dialog.h"
 #include "empathy-status-icon.h"
 #include "empathy-preferences.h"
 #include "empathy-event-manager.h"
 #include "empathy-misc.h"
 
+#ifdef HAVE_LIBINDICATE
+#include "empathy-indicator-manager.h"
+#endif
+
 #define DEBUG_FLAG EMPATHY_DEBUG_DISPATCHER
 #include <libempathy/empathy-debug.h>
 
@@ -69,6 +76,9 @@ typedef struct {
 	GtkAction           *show_window_item;
 	GtkAction           *new_message_item;
 	GtkAction           *status_item;
+#ifdef HAVE_LIBINDICATE
+	EmpathyIndicatorManager *indicator_manager;
+#endif
 } EmpathyStatusIconPriv;
 
 G_DEFINE_TYPE (EmpathyStatusIcon, empathy_status_icon, G_TYPE_OBJECT);
@@ -81,6 +91,33 @@ activate_event (EmpathyEvent *event)
 	return FALSE;
 }
 
+/* Query whether the notification server supports the "action"
+ * extension to the notifications specification, if it doesn't
+ * the sending actions has undefined results.
+ */
+static gboolean
+notification_server_supports_actions (void)
+{
+	GList *caps = notify_get_server_caps ();
+	GList *l;
+	gboolean ret = FALSE;
+
+	for (l = caps; l; l = l->next) {
+		gchar *cap = (gchar *) l->data;
+		if (!cap) {
+			continue;
+		}
+		if (!tp_strdiff (cap, "actions")) {
+			ret = TRUE;
+			break;
+		}
+	}
+	g_list_foreach (caps, (GFunc)g_free, NULL);
+	g_list_free (caps);
+
+	return ret;
+}
+
 static void
 status_icon_notification_closed_cb (NotifyNotification *notification,
 				    EmpathyStatusIcon  *icon)
@@ -110,8 +147,14 @@ status_icon_notification_closed_cb (NotifyNotification *notification,
 		 */
 		g_idle_add ((GSourceFunc) activate_event, priv->event);
 	} else {
-		/* inhibit other updates for this event */
-		empathy_event_inhibit_updates (priv->event);
+		/* inhibit other updates for this event, but only if
+		 * the server supports actions, using that as a proxy
+		 * for being click-through, as many notifications aren't
+		 * a problem then. Maybe not the right thing to do.
+		 */
+		if (notification_server_supports_actions ()) {
+			empathy_event_inhibit_updates (priv->event);
+		}
 	}
 }
 
@@ -318,6 +361,28 @@ status_icon_set_visibility (EmpathyStatusIcon *icon,
 	}
 }
 
+#ifdef HAVE_LIBINDICATE
+static void
+status_icon_set_use_libindicate (EmpathyStatusIcon *icon,
+			    gboolean           use_libindicate)
+{
+	EmpathyStatusIconPriv *priv = GET_PRIV (icon);
+
+	if (use_libindicate) {
+		empathy_indicator_manager_set_server_visible (priv->indicator_manager,
+				TRUE);
+		/* Hide the status icon so there are not two ways to access empathy.
+		 * Should use libindicate's "interest" to confirm someone is listening.
+		 */
+		gtk_status_icon_set_visible (priv->icon, FALSE);
+	} else {
+		empathy_indicator_manager_set_server_visible (priv->indicator_manager,
+				FALSE);
+		gtk_status_icon_set_visible (priv->icon, TRUE);
+	}
+}
+#endif
+
 static void
 status_icon_notify_visibility_cb (EmpathyConf *conf,
 				  const gchar *key,
@@ -331,16 +396,56 @@ status_icon_notify_visibility_cb (EmpathyConf *conf,
 	}
 }
 
+#ifdef HAVE_LIBINDICATE
+static void
+status_icon_notify_libindicate_cb (EmpathyConf *conf,
+				  const gchar *key,
+				  gpointer     user_data)
+{
+	EmpathyStatusIcon *icon = user_data;
+	gboolean           use_libindicate = FALSE;
+
+	if (empathy_conf_get_bool (conf, key, &use_libindicate)) {
+		status_icon_set_use_libindicate (icon, use_libindicate);
+	}
+}
+#endif
+
 static void
 status_icon_toggle_visibility (EmpathyStatusIcon *icon)
 {
 	EmpathyStatusIconPriv *priv = GET_PRIV (icon);
 	gboolean               visible;
+#ifdef HAVE_LIBINDICATE
+	gboolean               use_libindicate;
+#endif
 
 	visible = gtk_window_is_active (priv->window);
+#ifdef HAVE_LIBINDICATE
+	empathy_conf_get_bool (empathy_conf_get (),
+			       EMPATHY_PREFS_UI_USE_LIBINDICATE,
+		               &use_libindicate);
+	if (use_libindicate) {
+		/* If indicators are used then we may very well not be active
+		 * when toggled, as they are usually windows themselves. This
+		 * makes it damn hard to toggle, so we just look at whether
+		 * we are visible.
+		 */
+		visible = GTK_WIDGET_VISIBLE (priv->window);
+	}
+#endif
 	status_icon_set_visibility (icon, !visible, TRUE);
 }
 
+#ifdef HAVE_LIBINDICATE
+static void
+indicate_server_activate_cb (EmpathyIndicatorManager *manager,
+				  EmpathyStatusIcon *icon)
+{
+	status_icon_toggle_visibility (icon);
+}
+#endif
+
 static void
 status_icon_idle_notify_cb (EmpathyStatusIcon *icon)
 {
@@ -521,6 +626,10 @@ status_icon_finalize (GObject *object)
 	g_object_unref (priv->account_manager);
 	g_object_unref (priv->event_manager);
 	g_object_unref (priv->ui_manager);
+
+#ifdef HAVE_LIBINDICATE
+	g_object_unref (priv->indicator_manager);
+#endif
 }
 
 static void
@@ -555,6 +664,13 @@ empathy_status_icon_init (EmpathyStatusIcon *icon)
 				 status_icon_notify_visibility_cb,
 				 icon);
 
+#ifdef HAVE_LIBINDICATE
+	empathy_conf_notify_add (empathy_conf_get (),
+				 EMPATHY_PREFS_UI_USE_LIBINDICATE,
+				 status_icon_notify_libindicate_cb,
+				 icon);
+#endif
+
 	status_icon_create_menu (icon);
 	status_icon_idle_notify_cb (icon);
 
@@ -584,6 +700,9 @@ empathy_status_icon_new (GtkWindow *window, gboolean hide_contact_list)
 	EmpathyStatusIconPriv *priv;
 	EmpathyStatusIcon     *icon;
 	gboolean               should_hide;
+#ifdef HAVE_LIBINDICATE
+	gboolean               use_libindicate;
+#endif
 
 	g_return_val_if_fail (GTK_IS_WINDOW (window), NULL);
 
@@ -591,6 +710,12 @@ empathy_status_icon_new (GtkWindow *window, gboolean hide_contact_list)
 	priv = GET_PRIV (icon);
 
 	priv->window = g_object_ref (window);
+#ifdef HAVE_LIBINDICATE
+	priv->indicator_manager = empathy_indicator_manager_dup_singleton ();
+	g_signal_connect (priv->indicator_manager, "server-activate",
+			  G_CALLBACK (indicate_server_activate_cb),
+			  icon);
+#endif
 
 	g_signal_connect_after (priv->window, "key-press-event",
 			  G_CALLBACK (status_icon_key_press_event_cb),
@@ -608,6 +733,13 @@ empathy_status_icon_new (GtkWindow *window, gboolean hide_contact_list)
 		should_hide = TRUE;
 	}
 
+#ifdef HAVE_LIBINDICATE
+	empathy_conf_get_bool (empathy_conf_get (),
+			       EMPATHY_PREFS_UI_USE_LIBINDICATE,
+		               &use_libindicate);
+	status_icon_set_use_libindicate (icon, use_libindicate);
+#endif
+
 	if (gtk_window_is_active (priv->window) == should_hide) {
 		status_icon_set_visibility (icon, !should_hide, FALSE);
 	}
