[Evolution-hackers] Patch for bug #127546 - Gaim/Evolution presence integration.



Hi,
             Attached is a patch  for bug #127546,
http://bugzilla.gnome.org/show_bug.cgi?id=127546. The patch implements
all the required features as mentioned on the webpage
http://www.gnome.org/bounties/IM.html#127546 . I am posting the patch
here as a few issues are left unresolved and i would like to get some
feedback on the patch.

The following issues are unresolved and i would really like some help
solving them.

No presence information is added to the contact editor. I was not sure
where exactly to add. I asked this particular quetion on the list but
nobody replied. Also the screen names can be edited in the contact
editor and hence the presence information may not always be in sync
and i felt that it is really not needed as the information is
automatically updated in the addressbook view as soon as a contact is
modified/added/removed.

I could not make the columns showing presence info in addressbook
table default . Right now you have to right click on the table and add
the column "IM Presence manually".

The floating dialog which appears when you click on presence icons is
closed as soon as mouse leaves the dialog box. Ideally it should stay
on as long as the mouse is clicked outside the dialog box.
            Any pointers on solving the above two issues are
appreciated. Regarding the contact-editor issue can somebody tell me
where exactly to put the icons/text indicating presence.
 
Other than what is mentioned above everything mentioned on the webpage
is implemented. The patches for evolution are split as i could not run
the diff properly for the whole evolution directory.

The summary of the changes are --
 
gaim-remote plugin is now multithreaded so as not to block the gaim UI.
there is a separate library named libevogaimremote.so which can be
used to issue presence queries. the result is conveyed back using
callbacks. The library current has API applicable only to get back
buddy information and send gaim uri requests which were required for
evolution. I will probably implement all the request types as
mentioned in remote.h later.

EText is modified to put clickable icons at arbitrary places inside
the EText widget. It is used right now to put icons in TO:,CC:,BCC:
fields in the compoer and also for the Minicard view.

HTML display of messages is modified to include clickable buddy icons
indicating  the contact the message is from.

Addressbook list view and minicard view are modified to include
presence information.

TO, BCC, CC fields are modified to include presence icons.

Select names table is modified to include presence icons. 

How to apply patches.
               there should be 8 patch files.
                                evolution-addressbook.diff,
evolution-art.diff, evolution-configure.diff, evolution-e-util.diff,
evolutio-mail.diff, evolution-widgets-misc.diff, gal.diff, gaim.diff.
The patches can be applied from within the specific application
directories with "patch -p1 < xxx.diff". For evolution and gal the CVS
versions can be used. I tested them with CVS versions from yesterday.
For gaim please use the latest stable release gaim-0.82.1. The gaim
from CVS has changed quite a lot and i am not even able to compile
gaim cvs version on my computer. So the patch is for the latest stable
release. I will modify it when the gaim CVS becomes stable/when i can
compile it. Regarding icons, 4 of them are needed. buddy-online.png,
buddy-offline.png, buddy-away.png and buddy-none.png. The first three
can be gotten from the page which describes the bounty. The last one
is used for a contact who is online/offline but has no contact
photo/buddy icon associated. Please use any icon for that right now.
All the above four icons must be present in art subdirectory of
evolution.

If all the patches and icons and subdirectories are present in the
same toplevel directory a script is attached to patch the sources and
copy the icons.
 
                            Please test/verify the patch and post back
if there are any problems. Also could somebody tell me if the patch
can be posted to evolution-patches list.


Thanks,
Praveen.
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/component/select-names/Makefile.am evolution/addressbook/gui/component/select-names/Makefile.am
--- pristine/evolution/addressbook/gui/component/select-names/Makefile.am	2004-09-06 00:52:56.000000000 -0400
+++ evolution/addressbook/gui/component/select-names/Makefile.am	2004-09-05 23:37:00.000000000 -0400
@@ -40,7 +40,8 @@
 	-DEVOLUTION_ETSPECDIR=\""$(etspecdir)"\"	\
 	-DEVOLUTION_UIDIR=\""$(evolutionuidir)"\"	\
 	-DPREFIX=\""$(prefix)"\"			\
-	$(EVOLUTION_ADDRESSBOOK_CFLAGS)
+	$(EVOLUTION_ADDRESSBOOK_CFLAGS)			\
+	$(GAIM_CFLAGS)
 
 privlib_LTLIBRARIES = libeselectnames.la
 
@@ -65,10 +66,13 @@
 	e-select-names-text-model.c		\
 	e-select-names-text-model.h		\
 	e-select-names.c			\
-	e-select-names.h
+	e-select-names.h			\
+	e-select-names-table-adapter.c		\
+	e-select-names-table-adapter.h
 
 libeselectnames_la_LIBADD =					\
-	$(EVOLUTION_ADDRESSBOOK_LIBS)
+	$(EVOLUTION_ADDRESSBOOK_LIBS)				\
+	$(GAIM_LIBS)
 
 # We'll need these again when we split out the select names control
 # into its own shlib factory, but for now they're redundant.
@@ -80,7 +84,7 @@
 @EVO_MARSHAL_RULE@
 
 glade_DATA = select-names.glade
-etspec_DATA = e-select-names.etspec e-select-names-section.etspec
+etspec_DATA = e-select-names.etspec e-select-names-section.etspec esn-completion-table.etspec
 
 EXTRA_DIST = 			\
 	$(glade_DATA)		\
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/component/select-names/e-select-names-completion.c evolution/addressbook/gui/component/select-names/e-select-names-completion.c
--- pristine/evolution/addressbook/gui/component/select-names/e-select-names-completion.c	2004-09-06 00:52:56.000000000 -0400
+++ evolution/addressbook/gui/component/select-names/e-select-names-completion.c	2004-09-06 05:09:17.000000000 -0400
@@ -41,6 +41,8 @@
 
 #include <e-util/e-sexp.h>
 
+#include "e-util/e-impresence.h"
+
 typedef struct {
 	EBook *book;
 	guint book_view_tag;
@@ -71,6 +73,8 @@
 	gboolean match_contact_lists;
 
 	gint minimum_query_length;
+
+	int new_presence_id;
 };
 
 static void e_select_names_completion_class_init (ESelectNamesCompletionClass *);
@@ -709,6 +713,9 @@
 		g_free (comp->priv->waiting_query);
 		g_free (comp->priv->query_text);
 
+		if (comp->priv->new_presence_id)
+			g_signal_handler_disconnect (e_impresence_get_global (),
+						     comp->priv->new_presence_id);
 		g_free (comp->priv);
 		comp->priv = NULL;
 	}
@@ -838,12 +845,27 @@
 		/* Save the list of matching cards. */
 		while (cards) {
 			book_data->cached_cards = g_list_prepend (book_data->cached_cards, g_object_ref (cards->data));
+			/* Send presence query */
+			EContact *contact = E_CONTACT(cards->data);
+			e_impresence_query (contact, EIMPRESENCE_CACHE);
+
 			cards = g_list_next (cards);
 		}
 	}
 }
 
 static void
+e_select_names_new_presence_info (EIMPresence *empr, EContact *contact, GList *buddylist, ESelectNamesCompletion *comp)
+{
+	g_return_if_fail (comp != NULL);
+
+	ECompletion *ecomp = E_COMPLETION(comp);
+
+	e_table_model_pre_change (comp->model);
+	e_table_model_changed (comp->model);
+}
+
+static void
 e_select_names_completion_seq_complete_cb (EBookView *book_view, EBookViewStatus status, gpointer user_data)
 {
 	ESelectNamesCompletionBookData *book_data = user_data;
@@ -1178,6 +1200,11 @@
 	comp->priv->text_model = text_model;
 	g_object_ref (text_model);
 
+	comp->model = NULL;
+
+	/* connect new presence info */
+	comp->priv->new_presence_id = g_signal_connect (e_impresence_get_global(), "new_presence_info" , G_CALLBACK (e_select_names_new_presence_info), comp);
+
 	return E_COMPLETION (comp);
 }
 
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/component/select-names/e-select-names-completion.h evolution/addressbook/gui/component/select-names/e-select-names-completion.h
--- pristine/evolution/addressbook/gui/component/select-names/e-select-names-completion.h	2004-09-06 00:52:56.000000000 -0400
+++ evolution/addressbook/gui/component/select-names/e-select-names-completion.h	2004-09-05 23:37:00.000000000 -0400
@@ -28,6 +28,7 @@
 #define E_SELECT_NAMES_COMPLETION_H
 
 #include <gal/e-text/e-completion.h>
+#include <gal/e-table/e-table-model.h>
 #include <libebook/e-book.h>
 #include "e-select-names-text-model.h"
 
@@ -46,6 +47,8 @@
 struct _ESelectNamesCompletion {
 	ECompletion parent;
 
+	ETableModel *model;		/* This is a hack but how else do you convey
+					   the model that new completion info is present */
 	struct _ESelectNamesCompletionPrivate *priv;
 };
 
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/component/select-names/e-select-names-manager.c evolution/addressbook/gui/component/select-names/e-select-names-manager.c
--- pristine/evolution/addressbook/gui/component/select-names/e-select-names-manager.c	2004-09-06 00:52:56.000000000 -0400
+++ evolution/addressbook/gui/component/select-names/e-select-names-manager.c	2004-09-07 01:47:44.000000000 -0400
@@ -14,6 +14,8 @@
 #include <string.h>
 
 #include <gal/e-text/e-entry.h>
+#include <gal/e-table/e-cell-text.h>
+#include <gal/e-table/e-cell-toggle.h>
 
 #include <libgnome/gnome-i18n.h>
 #include "e-select-names-config.h"
@@ -23,12 +25,17 @@
 #include "e-select-names-text-model.h"
 #include "e-select-names.h"
 #include "e-select-names-completion.h"
+#include "e-select-names-table-adapter.h"
 #include "e-select-names-popup.h"
 #include <addressbook/util/eab-book-util.h>
 #include <addressbook/util/e-destination.h>
 #include "addressbook/gui/component/addressbook.h"
 #include <bonobo/bonobo-object.h>
 
+#include "widgets/misc/e-imsend.h"
+#include "e-util/e-icon-factory.h"
+#include "addressbook/gui/widgets/e-addressbook-table-adapter.h"
+
 #define DEFAULT_MINIMUM_QUERY_LENGTH 3
 
 enum {
@@ -58,6 +65,10 @@
 	ESelectNamesModel *model;
 	ECompletion *comp;
 	guint cleaning_tag;
+	gboolean icon;
+
+	ETableModel *adapter;
+	ETableExtras *extras;
 } ESelectNamesManagerEntry;
 
 static void e_select_names_manager_init (ESelectNamesManager *manager);
@@ -154,6 +165,37 @@
 }
 
 static void
+icon_clicked_cb (EEntry *eentry, GdkEventButton *ev, gint object_num, gint textofs, gpointer user_data)
+{
+	ESelectNamesTextModel *text_model;
+	ESelectNamesModel *model = NULL;
+	EDestination *dest;
+
+	g_object_get (eentry,
+		      "model", &text_model,
+		      NULL);
+        
+	g_return_if_fail (E_IS_SELECT_NAMES_TEXT_MODEL (text_model));
+	g_return_if_fail (ev);
+	g_return_if_fail (object_num >= 0);
+	g_return_if_fail (textofs >= 0);
+
+	model = text_model->source;
+	g_return_if_fail (E_IS_SELECT_NAMES_MODEL (model));
+
+        dest = (EDestination *)e_select_names_model_get_destination (model, object_num);
+        if (e_destination_empty (dest))
+                return;
+
+	
+	GList *blist = e_impresence_get_all_buddy_info (e_destination_get_contact_uid(dest));
+	if (!blist)
+		return;
+	
+	e_imsend_dialog (ev, e_destination_get_name(dest), blist, TRUE);
+}
+
+static void
 completion_handler (EEntry *entry, ECompletionMatch *match)
 {
 	ESelectNamesManagerEntry *mgr_entry;
@@ -184,12 +226,34 @@
 	e_entry_set_position (entry, start_pos+len);
 }
 
+static ETableExtras *
+create_table_view_extras (void)
+{
+	GdkPixbuf *images [4];
+	ETableExtras *extras;
+        
+	extras = e_table_extras_new ();
+        
+	images[0] = gdk_pixbuf_new_from_file (EVOLUTION_IMAGESDIR "/buddy-offline.png", NULL);
+	images[1] = gdk_pixbuf_new_from_file (EVOLUTION_IMAGESDIR "/buddy-online.png", NULL);
+	images[2] = gdk_pixbuf_new_from_file (EVOLUTION_IMAGESDIR "/buddy-away.png", NULL);
+	images[3] = e_icon_factory_get_icon  ( "stock_mail", E_ICON_SIZE_BUTTON);
+        
+	e_table_extras_add_pixbuf (extras, "presence",  images[0]);
+        
+	e_table_extras_add_cell (extras, "render_presence", e_cell_toggle_new (0, 4, images));
+	e_table_extras_add_cell (extras, "imstring", e_cell_text_new (NULL, GTK_JUSTIFY_RIGHT));
+        
+	return extras;
+}
+
 static ESelectNamesManagerEntry *
 e_select_names_manager_entry_new (ESelectNamesManager *manager, ESelectNamesModel *model, const gchar *id)
 {
 	ESelectNamesManagerEntry *entry;
 	ETextModel *text_model;
 	GList *l;
+	GtkWidget *table;
 
 	g_return_val_if_fail (E_IS_SELECT_NAMES_MANAGER (manager), NULL);
 	g_return_val_if_fail (E_IS_SELECT_NAMES_MODEL (model), NULL);
@@ -199,6 +263,8 @@
 	entry->id = g_strdup (id);
 
 	entry->entry = E_ENTRY (e_entry_new ());
+	entry->icon = FALSE;
+
 	text_model = e_select_names_text_model_new (model);
 	g_object_set(entry->entry,
 		     "model",          text_model, /* The entry takes ownership of the text model */
@@ -219,7 +285,18 @@
 	e_select_names_completion_set_minimum_query_length (E_SELECT_NAMES_COMPLETION(entry->comp),
 							    manager->minimum_query_length);
 
-	e_entry_enable_completion_full (entry->entry, entry->comp, 100, completion_handler);
+	/* Create the table to put for completion handler */
+	entry->adapter = E_TABLE_MODEL (esn_table_adapter_new (entry->comp));
+	entry->extras = create_table_view_extras();
+
+	table = e_table_scrolled_new_from_spec_file (entry->adapter,
+						     entry->extras,
+						     EVOLUTION_ETSPECDIR "/esn-completion-table.etspec",
+						     NULL);
+
+	e_entry_enable_completion_full (entry->entry, entry->comp, 100, completion_handler, table);
+	/* right justify the text in last column  -- this is done via extras */
+	E_SELECT_NAMES_COMPLETION (entry->comp)->model = entry->adapter;	
 		
 	entry->manager = manager;
 
@@ -231,6 +308,11 @@
 			  G_CALLBACK (populate_popup_cb),
 			  entry);
 			
+	g_signal_connect (entry->entry,
+			  "icon_clicked",
+			  G_CALLBACK (icon_clicked_cb),
+			  entry);
+
 	g_object_set_data (G_OBJECT (entry->entry), "entry_info", entry);
 	g_object_set_data (G_OBJECT (entry->entry), "select_names_model", model);
 	g_object_set_data (G_OBJECT (entry->entry), "select_names_text_model", text_model);
@@ -252,6 +334,11 @@
 	if (entry->cleaning_tag)
 		g_source_remove (entry->cleaning_tag);
 
+	if (entry->adapter)
+		g_object_unref (entry->adapter);
+	if (entry->extras)
+		g_object_unref (entry->extras);
+
 	g_free (entry);
 }
 
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/component/select-names/e-select-names-model.c evolution/addressbook/gui/component/select-names/e-select-names-model.c
--- pristine/evolution/addressbook/gui/component/select-names/e-select-names-model.c	2004-09-06 00:52:56.000000000 -0400
+++ evolution/addressbook/gui/component/select-names/e-select-names-model.c	2004-09-06 21:39:51.000000000 -0400
@@ -22,6 +22,8 @@
 #include "e-select-names-marshal.h"
 #include "eab-book-util.h"
 
+#include "e-util/e-impresence.h"
+
 enum {
 	E_SELECT_NAMES_MODEL_CHANGED,
 	E_SELECT_NAMES_MODEL_RESIZED,
@@ -46,6 +48,8 @@
 
 	gint freeze_count;
 	gboolean pending_changed;
+
+	gint im_handler_id;
 };
 
 static GObjectClass *parent_class = NULL;
@@ -55,6 +59,8 @@
 
 static void e_select_names_model_dispose (GObject *object);
 
+static void e_select_names_model_new_presence_info (EIMPresence *eimp, EContact *contact, GList *buddylist, ESelectNamesModel*model);
+
 GType
 e_select_names_model_get_type (void)
 {
@@ -122,6 +128,8 @@
 	model->priv = g_new0 (struct _ESelectNamesModelPrivate, 1);
 
 	model->priv->limit = -1;
+	
+	model->priv->im_handler_id = g_signal_connect (e_impresence_get_global(), "new_presence_info", G_CALLBACK(e_select_names_model_new_presence_info), model);
 }
 
 static void
@@ -136,6 +144,8 @@
 		g_list_foreach (model->priv->data, (GFunc) g_object_unref, NULL);
 		g_list_free (model->priv->data);
 
+		g_signal_handler_disconnect (e_impresence_get_global(), model->priv->im_handler_id);
+		
 		g_free (model->priv);
 		model->priv = NULL;
 	}
@@ -144,6 +154,37 @@
 		G_OBJECT_CLASS (parent_class)->dispose (object);
 }
 
+static void 
+e_select_names_model_new_presence_info (EIMPresence *eimp, EContact *contact, GList *buddylist, ESelectNamesModel *model)
+{
+	g_return_if_fail (model != NULL);
+	g_return_if_fail (model->priv != NULL);
+	if (!contact)
+		return;
+
+        g_signal_emit (model, e_select_names_model_signals[E_SELECT_NAMES_MODEL_CHANGED], 0);
+}
+
+static void
+e_select_names_model_presence_query (ESelectNamesModel *model)
+{
+	const EDestination *dest;
+	EContact *contact;
+	int i;
+	
+	for (i = 0; i < e_select_names_model_count(model); i++) {
+		dest = e_select_names_model_get_destination (model,i);
+		if (!dest || e_destination_empty(dest))
+			continue;
+		
+		contact = e_destination_get_contact (dest);
+		if (!contact)
+			continue;
+
+		e_impresence_query (contact, EIMPRESENCE_CACHE);
+	}
+}
+
 
 static void
 e_select_names_model_changed (ESelectNamesModel *model)
@@ -151,6 +192,9 @@
 	if (model->priv->freeze_count > 0) {
 		model->priv->pending_changed = TRUE;
 	} else {
+		/* The model has changed send presence info queries */
+		e_select_names_model_presence_query (model);		
+
 		g_signal_emit (model, e_select_names_model_signals[E_SELECT_NAMES_MODEL_CHANGED], 0);
 		model->priv->pending_changed = FALSE;
 	}
@@ -375,6 +419,34 @@
 	return dest ? e_destination_get_textrep (dest, FALSE) : "";
 }
 
+GdkPixbuf *
+e_select_names_model_get_icon (ESelectNamesModel *model, gint index)
+{
+        const EDestination *dest;
+
+        g_return_val_if_fail (model && E_IS_SELECT_NAMES_MODEL (model), NULL);
+        g_return_val_if_fail (index >= 0, NULL);
+	
+        g_return_val_if_fail (index < g_list_length (model->priv->data), NULL);
+
+        dest = e_select_names_model_get_destination (model, index);
+	
+	if (e_destination_empty (dest))
+		return NULL;	
+
+	const GRBuddyInfo *binfo = e_impresence_get_buddy_info (e_destination_get_contact_uid (dest));
+
+	if (!binfo)
+		return NULL;
+	else if (binfo->present == 0)
+		return gdk_pixbuf_new_from_file(EVOLUTION_IMAGESDIR "/buddy-offline.png", NULL);
+	else if ((binfo->present == 1) && (binfo->idle < 1))
+		return gdk_pixbuf_new_from_file(EVOLUTION_IMAGESDIR "/buddy-online.png", NULL);
+	else
+		return gdk_pixbuf_new_from_file(EVOLUTION_IMAGESDIR "/buddy-away.png", NULL);
+}
+
+
 gboolean
 e_select_names_model_contains (ESelectNamesModel *model, const EDestination *dest)
 {
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/component/select-names/e-select-names-model.h evolution/addressbook/gui/component/select-names/e-select-names-model.h
--- pristine/evolution/addressbook/gui/component/select-names/e-select-names-model.h	2004-09-06 00:52:56.000000000 -0400
+++ evolution/addressbook/gui/component/select-names/e-select-names-model.h	2004-09-05 23:37:00.000000000 -0400
@@ -13,6 +13,7 @@
 
 #include <time.h>
 #include <gtk/gtkobject.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
 #include <stdio.h>
 #include <e-util/e-list.h>
 #include <libebook/e-contact.h>
@@ -60,6 +61,7 @@
 							      gchar *destinationv);
 EContact           *e_select_names_model_get_contact         (ESelectNamesModel *model, gint index);
 const gchar        *e_select_names_model_get_string          (ESelectNamesModel *model, gint index);
+GdkPixbuf 	   *e_select_names_model_get_icon	     (ESelectNamesModel *model, gint index);
 
 gboolean      e_select_names_model_contains       (ESelectNamesModel *model, const EDestination *dest);
 
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/component/select-names/e-select-names-table-adapter.c evolution/addressbook/gui/component/select-names/e-select-names-table-adapter.c
--- pristine/evolution/addressbook/gui/component/select-names/e-select-names-table-adapter.c	1969-12-31 19:00:00.000000000 -0500
+++ evolution/addressbook/gui/component/select-names/e-select-names-table-adapter.c	2004-09-06 21:39:51.000000000 -0400
@@ -0,0 +1,279 @@
+/*
+ *  Authors:  Praveen Kumar <praveen9 gmail com>
+ *
+ *  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., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#include <config.h>
+#include "e-addressbook-model.h"
+#include "e-select-names-table-adapter.h"
+#include <gal/e-text/e-completion.h>
+
+#include "eab-gui-util.h"
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+#include <gnome.h>
+
+#include "e-util/e-impresence.h"
+
+struct _ESelectnamesTableAdapterPrivate {
+	ECompletion *comp;
+};
+
+#define PARENT_TYPE e_table_model_get_type()
+static ETableModelClass *parent_class;
+
+#define CONTACT_NAME_COL 2
+#define PRESENCE_INFO_COL 50
+
+static void
+unlink_model(ESelectnamesTableAdapter *adapter)
+{
+	ESelectnamesTableAdapterPrivate *priv = adapter->priv;
+
+	priv->comp = NULL;
+}
+
+static void
+selectnames_dispose(GObject *object)
+{
+	ESelectnamesTableAdapter *adapter = ESN_TABLE_ADAPTER(object);
+
+	if (adapter->priv) {
+		unlink_model(adapter);
+
+		g_free (adapter->priv);
+		adapter->priv = NULL;
+	}
+
+	if (G_OBJECT_CLASS (parent_class)->dispose)
+		(* G_OBJECT_CLASS (parent_class)->dispose) (object);
+}
+
+/* This function returns the number of columns in our ETableModel. */
+static int
+selectnames_col_count (ETableModel *etc)
+{
+	return 3;
+}
+
+/* This function returns the number of rows in our ETableModel. */
+static int
+selectnames_row_count (ETableModel *etc)
+{
+	ESelectnamesTableAdapter *adapter = ESN_TABLE_ADAPTER(etc);
+	ESelectnamesTableAdapterPrivate *priv = adapter->priv;
+
+	return e_completion_match_count (priv->comp);
+}
+
+/* This function returns the value at a particular point in our ETableModel. */
+static void *
+selectnames_value_at (ETableModel *etc, int col, int row)
+{
+	ESelectnamesTableAdapter *adapter = ESN_TABLE_ADAPTER(etc);
+	ESelectnamesTableAdapterPrivate *priv = adapter->priv;
+	const char *value;
+	const GRBuddyInfo *binfo;
+
+	if ( ((col != CONTACT_NAME_COL)  && (col != PRESENCE_INFO_COL) && (col != PRESENCE_INFO_COL+1)) || row >= e_completion_match_count (priv->comp) ) 
+		return NULL;
+
+	EDestination *dest = e_completion_match_at (priv->comp, row)->user_data;
+	if (col == PRESENCE_INFO_COL) {
+	        binfo = e_impresence_get_buddy_info (e_contact_get_const((EContact *)e_destination_get_contact (dest) , E_CONTACT_UID));
+	
+	        if (binfo == NULL)
+	                value = GINT_TO_POINTER(3);
+	        else if (binfo->present == 0)
+	                value = GINT_TO_POINTER(0);
+	        else if ((binfo->present == 1) && (binfo->idle < 1))
+	                value = GINT_TO_POINTER(1);
+	        else
+	                value = GINT_TO_POINTER(2);
+		return (void *)value;
+        } else if (col == PRESENCE_INFO_COL + 1) {
+                binfo = e_impresence_get_buddy_info (e_contact_get_const((EContact *)e_destination_get_contact (dest) , E_CONTACT_UID));
+
+                if (binfo == NULL)
+                        value = g_strdup ("");
+                else {
+                        char *imclient;
+                        if (binfo->protocol_id)
+                                imclient = g_strdup (gaim_remote_get_protocol (binfo->protocol_id));
+                        else
+                                imclient = g_strdup ("");
+                        if (binfo->present == 0)
+                                value = g_strdup_printf ("Offline, %s", imclient);
+                        else if ((binfo->present == 1) && (binfo->idle < 1))
+                                value = g_strdup_printf ("Online, %s", imclient);
+                        else
+                                value = g_strdup_printf ("Online (idle %s), %s", gaim_remote_seconds_to_string(binfo->idle), imclient);
+         		g_free (imclient);
+		 }
+		 return (void *)value;
+
+	} else {	
+		value = g_strdup(e_completion_match_get_menu_text(e_completion_match_at (priv->comp, row)));
+		return (void *)(value ? value : "");
+	}
+}
+
+/* This function returns whether a particular cell is editable. */
+static gboolean
+selectnames_is_cell_editable (ETableModel *etc, int col, int row)
+{
+#if 0
+	ESelectnamesTableAdapter *adapter = EAB_TABLE_ADAPTER(etc);
+	ESelectnamesTableAdapterPrivate *priv = adapter->priv;
+	const EContact *contact;
+
+	if (row >= 0 && row < eab_model_contact_count (priv->model))
+		contact = eab_model_contact_at (priv->model, row);
+	else
+		contact = NULL;
+
+	if (!eab_model_editable(priv->model))
+		return FALSE;
+	else if (contact && e_contact_get ((EContact *) contact, E_CONTACT_IS_LIST))
+		/* we only allow editing of the name and file as for
+                   lists */
+		return col == E_CONTACT_FULL_NAME || col == E_CONTACT_FILE_AS; 
+	else
+		return col < E_CONTACT_LAST_SIMPLE_STRING;
+#endif
+
+	return FALSE;
+}
+
+/* This function duplicates the value passed to it. */
+static void *
+selectnames_duplicate_value (ETableModel *etc, int col, const void *value)
+{
+	return g_strdup(value);
+}
+
+/* This function frees the value passed to it. */
+static void
+selectnames_free_value (ETableModel *etc, int col, void *value)
+{
+	if (col != PRESENCE_INFO_COL)
+		g_free(value);
+}
+
+static void *
+selectnames_initialize_value (ETableModel *etc, int col)
+{
+	if (col == PRESENCE_INFO_COL)
+		return GINT_TO_POINTER(3);
+	else
+		return g_strdup("");
+}
+
+static gboolean
+selectnames_value_is_empty (ETableModel *etc, int col, const void *value)
+{
+	if (col == PRESENCE_INFO_COL)
+		return !value;
+	else
+		return !(value && *(char *)value);
+}
+
+static char *
+selectnames_value_to_string (ETableModel *etc, int col, const void *value)
+{
+	if (col == PRESENCE_INFO_COL)
+		return g_strdup_printf ("%d", GPOINTER_TO_INT(value));
+	else
+		return g_strdup(value);
+}
+
+static void
+esn_table_adapter_class_init (GObjectClass *object_class)
+{
+	ETableModelClass *model_class = (ETableModelClass *) object_class;
+
+	parent_class = g_type_class_peek_parent (object_class);
+
+	object_class->dispose = selectnames_dispose;
+
+	model_class->column_count = selectnames_col_count;
+	model_class->row_count = selectnames_row_count;
+	model_class->value_at = selectnames_value_at;
+	model_class->is_cell_editable = selectnames_is_cell_editable;
+	model_class->duplicate_value = selectnames_duplicate_value;
+	model_class->free_value = selectnames_free_value;
+	model_class->initialize_value = selectnames_initialize_value;
+	model_class->value_is_empty = selectnames_value_is_empty;
+	model_class->value_to_string = selectnames_value_to_string;
+}
+
+static void
+esn_table_adapter_init (GObject *object)
+{
+	ESelectnamesTableAdapter *adapter = ESN_TABLE_ADAPTER(object);
+	ESelectnamesTableAdapterPrivate *priv;
+
+	priv = adapter->priv = g_new0 (ESelectnamesTableAdapterPrivate, 1);
+
+}
+
+GType
+esn_table_adapter_get_type (void)
+{
+	static GType type = 0;
+
+	if (!type) {
+		static const GTypeInfo info =  {
+			sizeof (ESelectnamesTableAdapterClass),
+			NULL,           /* base_init */
+			NULL,           /* base_finalize */
+			(GClassInitFunc) esn_table_adapter_class_init,
+			NULL,           /* class_finalize */
+			NULL,           /* class_data */
+			sizeof (ESelectnamesTableAdapter),
+			0,             /* n_preallocs */
+			(GInstanceInitFunc) esn_table_adapter_init,
+		};
+
+		type = g_type_register_static (PARENT_TYPE, "ESelectnamesTableAdapter", &info, 0);
+	}
+
+	return type;
+}
+
+void
+esn_table_adapter_construct (ESelectnamesTableAdapter *adapter,
+				       ECompletion *comp)
+{
+	ESelectnamesTableAdapterPrivate *priv = adapter->priv;
+
+	priv->comp = comp;
+}
+
+ETableModel *
+esn_table_adapter_new (ECompletion *comp)
+{
+	ESelectnamesTableAdapter *et;
+
+	et = g_object_new(E_TYPE_SN_TABLE_ADAPTER, NULL);
+
+	esn_table_adapter_construct (et, comp);
+
+	return E_TABLE_MODEL(et);
+}
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/component/select-names/e-select-names-table-adapter.h evolution/addressbook/gui/component/select-names/e-select-names-table-adapter.h
--- pristine/evolution/addressbook/gui/component/select-names/e-select-names-table-adapter.h	1969-12-31 19:00:00.000000000 -0500
+++ evolution/addressbook/gui/component/select-names/e-select-names-table-adapter.h	2004-09-06 01:48:55.000000000 -0400
@@ -0,0 +1,58 @@
+
+/*
+ *  Authors:  Praveen Kumar <praveen9 gmail com>
+ *
+ *  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., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _ESN_TABLE_ADAPTER_H_
+#define _ESN_TABLE_ADAPTER_H_
+
+#include <gal/e-table/e-table-model.h>
+#include <libebook/e-book.h>
+#include <libebook/e-book-view.h>
+#include <gal/e-text/e-completion.h>
+
+#define E_TYPE_SN_TABLE_ADAPTER                 (esn_table_adapter_get_type ())
+#define ESN_TABLE_ADAPTER(o)                    (G_TYPE_CHECK_INSTANCE_CAST ((o), E_TYPE_SN_TABLE_ADAPTER, ESelectnamesTableAdapter))
+#define ESN_TABLE_ADAPTER_CLASS(k)              (G_TYPE_CHECK_CLASS_CAST((k), E_TYPE_SN_TABLE_ADAPTER, ESelectnamesTableAdapterClass))
+#define E_IS_SELECTNAMES_TABLE_ADAPTER(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), E_TYPE_SN_TABLE_ADAPTER))
+#define E_IS_SELECTNAMES_TABLE_ADAPTER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), E_TYPE_SN_TABLE_ADAPTER))
+
+#define PRESENCE_INFO_COL 50
+
+typedef struct _ESelectnamesTableAdapter ESelectnamesTableAdapter;
+typedef struct _ESelectnamesTableAdapterPrivate ESelectnamesTableAdapterPrivate;
+typedef struct _ESelectnamesTableAdapterClass ESelectnamesTableAdapterClass;
+
+struct _ESelectnamesTableAdapter {
+	ETableModel parent;
+
+	ESelectnamesTableAdapterPrivate *priv;
+};
+
+
+struct _ESelectnamesTableAdapterClass {
+	ETableModelClass parent_class;
+};
+
+
+GType        esn_table_adapter_get_type (void);
+void         esn_table_adapter_construct (ESelectnamesTableAdapter *adapter,
+					  ECompletion *model);
+ETableModel *esn_table_adapter_new (ECompletion *model);
+
+#endif /* _ESN_TABLE_ADAPTER_H_ */
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/component/select-names/e-select-names-table-model.h evolution/addressbook/gui/component/select-names/e-select-names-table-model.h
--- pristine/evolution/addressbook/gui/component/select-names/e-select-names-table-model.h	2004-09-06 00:52:56.000000000 -0400
+++ evolution/addressbook/gui/component/select-names/e-select-names-table-model.h	2004-09-05 23:37:00.000000000 -0400
@@ -21,6 +21,8 @@
 #define E_IS_SELECT_NAMES_TABLE_MODEL(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_SELECT_NAMES_TABLE_MODEL))
 #define E_IS_SELECT_NAMES_TABLE_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_SELECT_NAMES_TABLE_MODEL))
 
+#define PRESENCE_INFO_COL 50
+
 typedef struct {
 	char *name;
 	char *email;
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/component/select-names/e-select-names-text-model.c evolution/addressbook/gui/component/select-names/e-select-names-text-model.c
--- pristine/evolution/addressbook/gui/component/select-names/e-select-names-text-model.c	2004-09-06 00:52:56.000000000 -0400
+++ evolution/addressbook/gui/component/select-names/e-select-names-text-model.c	2004-09-06 05:09:17.000000000 -0400
@@ -46,6 +46,7 @@
 static gint         e_select_names_text_model_obj_count   (ETextModel *model);
 static const gchar *e_select_names_text_model_get_nth_obj (ETextModel *model, gint n, gint *len);
 static void         e_select_names_text_model_activate_obj (ETextModel *model, gint n);
+static GdkPixbuf   *e_select_names_text_model_get_nth_obj_icon (ETextModel *model, gint n);
 
 
 static ETextModelClass *parent_class;
@@ -115,6 +116,7 @@
 	text_model_class->obj_count     = e_select_names_text_model_obj_count;
 	text_model_class->get_nth_obj   = e_select_names_text_model_get_nth_obj;
 	text_model_class->object_activated = e_select_names_text_model_activate_obj;
+	text_model_class->get_nth_obj_icon = e_select_names_text_model_get_nth_obj_icon;
 
 	if (getenv ("EVO_DEBUG_SELECT_NAMES_TEXT_MODEL")) {
 		out = fopen ("/tmp/evo-debug-select-names-text-model", "w");
@@ -852,5 +854,17 @@
 	g_object_unref (book);
 }
 
+static GdkPixbuf *
+e_select_names_text_model_get_nth_obj_icon (ETextModel *model, gint n)
+{
+	ESelectNamesTextModel *text_model = E_SELECT_NAMES_TEXT_MODEL (model);
+	ESelectNamesModel *source = text_model->source;
+	gint i;
+	
+	i = nth_obj_index (source, n);
+	if (i < 0)
+		return NULL;
 
+	return e_select_names_model_get_icon(source, i);
+}
 
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/component/select-names/e-select-names.c evolution/addressbook/gui/component/select-names/e-select-names.c
--- pristine/evolution/addressbook/gui/component/select-names/e-select-names.c	2004-09-06 00:52:56.000000000 -0400
+++ evolution/addressbook/gui/component/select-names/e-select-names.c	2004-09-06 05:09:17.000000000 -0400
@@ -27,8 +27,11 @@
 #include <gtk/gtkstock.h>
 #include <libgnome/gnome-i18n.h>
 
-#include <gal/e-table/e-table-simple.h>
-#include <gal/e-table/e-table-without.h>
+#include <gal/e-table/e-table.h>
+#include <gal/e-table/e-table-extras.h>
+#include <gal/e-table/e-cell-toggle.h>
+#include <gal/e-table/e-cell-text.h>
+
 #include <gal/widgets/e-popup-menu.h>
 
 #include <libebook/e-book.h>
@@ -47,6 +50,10 @@
 #include <e-util/e-categories-master-list-wombat.h>
 #include "e-util/e-sexp.h"
 
+#include "e-util/e-impresence.h"
+#include "widgets/misc/e-imsend.h"
+#include "art/empty.xpm"
+
 static void  e_select_names_init       (ESelectNames		 *names);
 static void  e_select_names_class_init (ESelectNamesClass	 *klass);
 static void  e_select_names_dispose    (GObject *object);
@@ -189,7 +196,7 @@
 static void
 sync_one_model (gpointer k, gpointer val, gpointer closure)
 {
-	ETableWithout *etw = E_TABLE_WITHOUT (closure);
+	ETable *et = E_TABLE (closure);
 	ESelectNamesChild *child = val;
 	ESelectNamesModel *model = child->source;
 	gint i, count;
@@ -197,21 +204,12 @@
 	void *key;
 	
 	count = e_select_names_model_count (model);
-	for (i = 0; i < count; ++i) {
-		contact = e_select_names_model_get_contact (model, i);
-		if (contact) {
-			key = contact_key (contact);
-			e_table_without_hide (etw, key);
-			g_free (key);
-		}
-	}
 }
 
 static void
 sync_table_and_models (ESelectNamesModel *triggering_model, ESelectNames *esl)
 {
-	e_table_without_show_all (E_TABLE_WITHOUT (esl->without));
-	g_hash_table_foreach (esl->children, sync_one_model, esl->without);
+	gtk_widget_show( GTK_WIDGET(esl->table) );
 }
 
 static void
@@ -221,11 +219,8 @@
 	ESelectNames *names = child->names;
 	const EContact *contact;
 	EDestination *dest = e_destination_new ();
-	gint mapped_row;
 
-	mapped_row = e_table_subset_view_to_model_row (E_TABLE_SUBSET (names->without), model_row);
-
-	contact = eab_model_contact_at (EAB_MODEL(names->model), mapped_row);
+	contact = eab_model_contact_at (EAB_MODEL(names->model), model_row);
 	
 	if (contact != NULL) {
 		e_destination_set_contact (dest, (EContact*)contact, 0);
@@ -274,41 +269,33 @@
 	g_hash_table_foreach (names->children, sensitize_button, &sensitive);
 }
 
-static void *
-esn_get_key_fn (ETableModel *source, int row, void *closure)
+static ETableExtras *
+create_table_view_extras (void)
 {
-	EABModel *model = EAB_MODEL (closure);
-	const EContact *contact = eab_model_contact_at (model, row);
-	void *key = contact_key (contact);
-	return key;
-}
+        GdkPixbuf *images [4];
+        ETableExtras *extras;
 
-static void *
-esn_dup_key_fn (const void *key, void *closure)
-{
-	void *dup = (void *) g_strdup ((const gchar *) key);
-	return dup;
-}
+        extras = e_table_extras_new ();
 
-static void
-esn_free_gotten_key_fn (void *key, void *closure)
-{
-	g_free (key);
-}
+        images[0] = gdk_pixbuf_new_from_file(EVOLUTION_IMAGESDIR "/buddy-offline.png", NULL);
+        images[1] = gdk_pixbuf_new_from_file(EVOLUTION_IMAGESDIR "/buddy-online.png", NULL);
+        images[2] = gdk_pixbuf_new_from_file(EVOLUTION_IMAGESDIR "/buddy-away.png", NULL);
+        images[3] = gdk_pixbuf_new_from_xpm_data ((const char **)empty_xpm);
 
-static void
-esn_free_duped_key_fn (void *key, void *closure)
-{
-	g_free (key);
+        e_table_extras_add_pixbuf (extras, "presence",  images[0]);
+
+        e_table_extras_add_cell (extras, "render_presence", e_cell_toggle_new (0, 4, images));
+
+        return extras;
 }
 
 GtkWidget *
 e_addressbook_create_ebook_table(char *name, char *string1, char *string2, int num1, int num2)
 {
 	ETableModel *adapter;
-	ETableModel *without;
 	EABModel *model;
 	GtkWidget *table;
+	ETableExtras *extras;
 
 	model = eab_model_new ();
 	adapter = E_TABLE_MODEL (eab_table_adapter_new (model));
@@ -317,22 +304,15 @@
 		     "editable", FALSE,
 		     NULL);
 
-	without = e_table_without_new (adapter,
-				       g_str_hash,
-				       g_str_equal,
-				       esn_get_key_fn,
-				       esn_dup_key_fn,
-				       esn_free_gotten_key_fn,
-				       esn_free_duped_key_fn,
-				       model);
+	extras = create_table_view_extras();
 
-	table = e_table_scrolled_new_from_spec_file (without,
-						     NULL,
+	table = e_table_scrolled_new_from_spec_file (adapter,
+						     extras,
 						     EVOLUTION_ETSPECDIR "/e-select-names.etspec",
 						     NULL);
 
 	g_object_set_data(G_OBJECT(table), "adapter", adapter);
-	g_object_set_data(G_OBJECT(table), "without", without);
+	g_object_set_data(G_OBJECT(table), "extras", extras);
 	g_object_set_data(G_OBJECT(table), "model", model);
 
 	return table;
@@ -422,16 +402,12 @@
 
 		table = e_table_scrolled_get_table (e_select_names->table);
 
-		count = e_table_model_row_count (e_select_names->without);
+		count = e_table_model_row_count (e_select_names->table->table->model);
 
 		for (i = 0; i < count; i++) {
-			int model_row = e_table_view_to_model_row (table, i);
 			char *row_strcoll_string =
-				g_utf8_collate_key (e_table_model_value_at (e_select_names->without,
-									    E_CONTACT_FULL_NAME,
-									    model_row),
-						    -1);
-			if (strcmp (select_strcoll_string, row_strcoll_string) <= 0) {
+				g_utf8_collate_key (e_table_model_value_at (e_select_names->table->table->model, E_CONTACT_FULL_NAME, i), -1);
+			if (g_strrstr (row_strcoll_string, select_strcoll_string)) {
 				g_free (row_strcoll_string);
 				break;
 			}
@@ -441,11 +417,9 @@
 		if (i == count)
 			i --;
 
-		if (count > 0) {
-			i = e_table_view_to_model_row (table, i);
+		if (count > 0)
 			e_table_set_cursor_row (table, i);
 		}
-	}
 }
 
 GtkWidget *e_select_names_create_categories (gchar *name,
@@ -474,6 +448,33 @@
 	*widget_ref = NULL;
 }
 
+static gint 
+e_select_names_click_cb (ETable *et, int row, int col, GdkEvent *event, ESelectNames *esn)
+{
+	g_return_val_if_fail (esn && esn->table, FALSE);
+        if (col != PRESENCE_INFO_COL)
+                return FALSE;
+
+	EABModel *model = g_object_get_data (G_OBJECT(esn->table), "model");
+
+	if (!model)
+		return FALSE;
+
+        EContact *contact = e_contact_duplicate(eab_model_contact_at (model, row));
+        if (!contact)
+                return FALSE;
+	
+        GList *blist = e_impresence_get_all_buddy_info (e_contact_get_const (contact, E_CONTACT_UID));
+        if (!blist)
+                return FALSE;
+
+        e_imsend_dialog (&(event->button), e_contact_get_const(contact, E_CONTACT_FULL_NAME), blist, TRUE);
+
+	g_object_unref (contact);
+
+	return TRUE;
+}
+
 static void
 e_select_names_init (ESelectNames *e_select_names)
 {
@@ -527,9 +528,11 @@
 					 GTK_RESPONSE_OK);
 
 	e_select_names->table = E_TABLE_SCROLLED(glade_xml_get_widget(gui, "table-source"));
+
+	g_signal_connect (e_table_scrolled_get_table (e_select_names->table), "click", G_CALLBACK (e_select_names_click_cb), e_select_names);
+			
 	e_select_names->model = g_object_get_data(G_OBJECT(e_select_names->table), "model");
 	e_select_names->adapter = g_object_get_data(G_OBJECT(e_select_names->table), "adapter");
-	e_select_names->without = g_object_get_data(G_OBJECT(e_select_names->table), "without");
 	gtk_widget_show (GTK_WIDGET (e_select_names->table));
 
 	e_select_names->status_message = glade_xml_get_widget (gui, "status-message");
@@ -634,10 +637,6 @@
 		e_select_names->children = NULL;
 	}
 
-	if (e_select_names->without) {
-		g_object_unref(e_select_names->without);
-		e_select_names->without = NULL;
-	}
 	if (e_select_names->adapter) {
 		g_object_unref(e_select_names->adapter);
 		e_select_names->adapter = NULL;
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/component/select-names/e-select-names.etspec evolution/addressbook/gui/component/select-names/e-select-names.etspec
--- pristine/evolution/addressbook/gui/component/select-names/e-select-names.etspec	2004-09-06 00:52:56.000000000 -0400
+++ evolution/addressbook/gui/component/select-names/e-select-names.etspec	2004-09-05 23:37:00.000000000 -0400
@@ -1,6 +1,8 @@
 <ETableSpecification no-headers="true" cursor-mode="line">
-  <ETableColumn model_col= "86" _title="Name" expansion="1.0" minimum_width="20" resizable="true" cell="string" compare="collate" search="string"/>
+  <ETableColumn model_col="2" _title="Name" expansion="1.0" minimum_width="20" resizable="true" cell="string" compare="string" search="string"/>
+  <ETableColumn model_col="50" _title="IM Presence"  pixbuf="presence" expansion="0.0" minimum_width="22" resizable="false" cell="render_presence" compare="integer" sortable="false"/>
     <ETableState>
+	<column source="1"/>
 	<column source="0"/>
 	    <grouping> <leaf column="0" ascending="true"/> </grouping>
     </ETableState>
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/component/select-names/e-select-names.h evolution/addressbook/gui/component/select-names/e-select-names.h
--- pristine/evolution/addressbook/gui/component/select-names/e-select-names.h	2004-09-06 00:52:56.000000000 -0400
+++ evolution/addressbook/gui/component/select-names/e-select-names.h	2004-09-06 00:03:31.000000000 -0400
@@ -66,7 +66,6 @@
 	int child_count;
 	ETableScrolled *table;
 	ETableModel *adapter;
-	ETableModel *without;
 	EABModel *model;
 	GtkWidget *categories;
 	GtkWidget *select_entry;
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/component/select-names/esn-completion-table.etspec evolution/addressbook/gui/component/select-names/esn-completion-table.etspec
--- pristine/evolution/addressbook/gui/component/select-names/esn-completion-table.etspec	1969-12-31 19:00:00.000000000 -0500
+++ evolution/addressbook/gui/component/select-names/esn-completion-table.etspec	2004-09-05 23:37:00.000000000 -0400
@@ -0,0 +1,11 @@
+<ETableSpecification no-headers="true" cursor-mode="line" draw-grid="false" alternating-row-colors="false">
+  <ETableColumn model_col="2" _title="Name" expansion="1.0" minimum_width="200" resizable="true" cell="string" compare="string" search="string"/>
+  <ETableColumn model_col="50" _title="IM Presence"  pixbuf="presence" expansion="0.0" minimum_width="22" resizable="false" cell="render_presence" compare="integer" sortable="false"/>
+  <ETableColumn model_col="51" _title="IM Presence 1"  expansion="1.0" minimum_width="20" resizable="true" cell="imstring" compare="string" sortable="false"/>
+    <ETableState>
+	<column source="1"/>
+	<column source="0"/>
+	<column source="2"/>
+	    <grouping> <leaf column="1" ascending="true"/> </grouping>
+    </ETableState>
+</ETableSpecification>
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/widgets/e-addressbook-model.c evolution/addressbook/gui/widgets/e-addressbook-model.c
--- pristine/evolution/addressbook/gui/widgets/e-addressbook-model.c	2004-09-06 00:52:56.000000000 -0400
+++ evolution/addressbook/gui/widgets/e-addressbook-model.c	2004-09-06 05:05:09.000000000 -0400
@@ -17,6 +17,8 @@
 #include <gal/widgets/e-gui-utils.h>
 #include "eab-gui-util.h"
 
+#include "e-util/e-impresence.h"
+
 #define PARENT_TYPE G_TYPE_OBJECT
 static GObjectClass *parent_class;
 
@@ -131,6 +133,10 @@
 		model->query = NULL;
 	}
 
+	if (model->new_presence_id)
+		g_signal_handler_disconnect (e_impresence_get_global(), 
+					     model->new_presence_id);
+
 	if (G_OBJECT_CLASS(parent_class)->dispose)
 		G_OBJECT_CLASS(parent_class)->dispose(object);
 }
@@ -166,6 +172,7 @@
 {
 	int old_count = model->data_count;
 	int length = g_list_length ((GList *)contacts);
+	GList *contactlist = NULL;
 
 	if (model->data_count + length > model->allocated_count) {
 		while (model->data_count + length > model->allocated_count)
@@ -175,9 +182,13 @@
 
 	for ( ; contacts; contacts = contacts->next) {
 		model->data[model->data_count++] = contacts->data;
+		contactlist = g_list_append(contactlist, contacts->data);
 		g_object_ref (contacts->data);
 	}
 
+	if (contactlist)	
+		e_impresence_query_list (contactlist, EIMPRESENCE_CACHE);
+	
 	g_signal_emit (model,
 		       eab_model_signals [CONTACT_ADDED], 0,
 		       old_count, model->data_count - old_count);
@@ -219,6 +230,7 @@
 	       const GList *contacts,
 	       EABModel *model)
 {
+	GList *contactlist = NULL;
 	for ( ; contacts; contacts = contacts->next) {
 		int i;
 		for ( i = 0; i < model->data_count; i++) {
@@ -226,6 +238,7 @@
 				     e_contact_get_const(E_CONTACT(contacts->data), E_CONTACT_UID)) ) {
 				g_object_unref (model->data[i]);
 				model->data[i] = e_contact_duplicate(E_CONTACT(contacts->data));
+				contactlist = g_list_append(contactlist, contacts->data);
 				g_signal_emit (model,
 					       eab_model_signals [CONTACT_CHANGED], 0,
 					       i);
@@ -233,6 +246,8 @@
 			}
 		}
 	}
+	if (contactlist)	
+		e_impresence_query_list (contactlist, EIMPRESENCE_RECACHE);
 }
 
 static void
@@ -437,6 +452,24 @@
 }
 
 static void
+model_new_presence_info (EIMPresence *empr, EContact *contact, GList *buddylist, EABModel *model)
+{
+	g_return_if_fail (E_IS_CONTACT(contact));
+	g_return_if_fail (model != NULL);
+
+	int i;
+	for ( i = 0; i < model->data_count; i++) {
+		if ( !strcmp(e_contact_get_const(model->data[i], E_CONTACT_UID),
+		     e_contact_get_const(E_CONTACT(contact), E_CONTACT_UID)) ) {
+			g_signal_emit (model,
+				       eab_model_signals [CONTACT_CHANGED], 0,
+				       i);
+			break;
+		}
+	}
+}
+
+static void
 book_view_loaded (EBook *book, EBookStatus status, EBookView *book_view, gpointer closure)
 {
 	EABModel *model = closure;
@@ -474,6 +507,13 @@
 						       model);
 
 	model->search_in_progress = TRUE;
+	
+	
+	model->new_presence_id = g_signal_connect (e_impresence_get_global(),
+						   "new_presence_info",
+						   G_CALLBACK(model_new_presence_info),
+						   model);
+
 	g_signal_emit (model,
 		       eab_model_signals [MODEL_CHANGED], 0);
 	g_signal_emit (model,
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/widgets/e-addressbook-model.h evolution/addressbook/gui/widgets/e-addressbook-model.h
--- pristine/evolution/addressbook/gui/widgets/e-addressbook-model.h	2004-09-06 00:52:56.000000000 -0400
+++ evolution/addressbook/gui/widgets/e-addressbook-model.h	2004-09-05 23:37:00.000000000 -0400
@@ -33,6 +33,7 @@
 	int create_contact_id, remove_contact_id, modify_contact_id;
 	int status_message_id, writable_status_id, sequence_complete_id;
 	int backend_died_id;
+	int new_presence_id;
 
 	guint search_in_progress : 1;
 	guint editable : 1;
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/widgets/e-addressbook-reflow-adapter.c evolution/addressbook/gui/widgets/e-addressbook-reflow-adapter.c
--- pristine/evolution/addressbook/gui/widgets/e-addressbook-reflow-adapter.c	2004-09-06 00:52:56.000000000 -0400
+++ evolution/addressbook/gui/widgets/e-addressbook-reflow-adapter.c	2004-09-05 23:37:00.000000000 -0400
@@ -160,6 +160,10 @@
 		}
 		g_free (string);
 	}
+
+	if (e_impresence_get_buddy_info (e_contact_get_const (contact, E_CONTACT_UID)))
+		height += text_height (layout, "Presence status:");
+
 	height += 2;
 
 	g_object_unref (layout);
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/widgets/e-addressbook-table-adapter.c evolution/addressbook/gui/widgets/e-addressbook-table-adapter.c
--- pristine/evolution/addressbook/gui/widgets/e-addressbook-table-adapter.c	2004-09-06 00:52:56.000000000 -0400
+++ evolution/addressbook/gui/widgets/e-addressbook-table-adapter.c	2004-09-06 21:39:51.000000000 -0400
@@ -10,6 +10,8 @@
 #include <libxml/xmlmemory.h>
 #include <gnome.h>
 
+#include "e-util/e-impresence.h"
+
 struct _EAddressbookTableAdapterPrivate {
 	EABModel *model;
 
@@ -21,6 +23,8 @@
 
 #define COLS (E_CONTACT_FIELD_LAST)
 
+#define PRESENCE_INFO_COL 50
+
 static void
 unlink_model(EAddressbookTableAdapter *adapter)
 {
@@ -89,9 +93,44 @@
 	if ( col >= COLS || row >= eab_model_contact_count (priv->model) )
 		return NULL;
 
-	value = e_contact_get_const((EContact*)eab_model_contact_at (priv->model, row), col);
+	if (col == PRESENCE_INFO_COL) {
+	        const GRBuddyInfo *binfo = e_impresence_get_buddy_info (e_contact_get_const((EContact *)eab_model_contact_at (priv->model, row) , E_CONTACT_UID));
+	
+	        if (binfo == NULL)
+	                value = GINT_TO_POINTER(3);
+	        else if (binfo->present == 0)
+	                value = GINT_TO_POINTER(0);
+	        else if ((binfo->present == 1) && (binfo->idle < 1))
+	                value = GINT_TO_POINTER(1);
+	        else
+	                value = GINT_TO_POINTER(2);
+		return (void *)value;
 
+	} else if (col == PRESENCE_INFO_COL + 1) {
+                const GRBuddyInfo *binfo = e_impresence_get_buddy_info (e_contact_get_const((EContact *)eab_model_contact_at (priv->model, row) , E_CONTACT_UID));
+
+                if (binfo == NULL)
+                        value = g_strdup ("");
+                else {
+			char *imclient;
+			if (binfo->protocol_id)	
+				imclient = g_strdup (gaim_remote_get_protocol (binfo->protocol_id));
+			else
+				imclient = g_strdup ("");
+			if (binfo->present == 0)
+                        	value = g_strdup_printf ("Offline, %s", imclient);
+	                else if ((binfo->present == 1) && (binfo->idle < 1))
+	                        value = g_strdup_printf ("Online, %s", imclient);
+	                else
+	                        value = g_strdup_printf ("Online (idle %s), %s", gaim_remote_seconds_to_string(binfo->idle), imclient);
+			g_free (imclient);
+		}
+	        return (void *)value;
+
+	} else {
+		value = e_contact_get_const((EContact*)eab_model_contact_at (priv->model, row), col);
 	return (void *)(value ? value : "");
+	}
 }
 
 /* This function sets the value at a particular point in our ETableModel. */
@@ -112,7 +151,7 @@
 	if (eab_model_editable (priv->model)) {
 		EContact *contact;
 
-		if (col >= COLS || row >= eab_model_contact_count (priv->model))
+		if (col >= COLS || row >= eab_model_contact_count (priv->model) || (col == PRESENCE_INFO_COL) || (col == PRESENCE_INFO_COL+1))
 			return;
 
 		contact = eab_model_get_contact (priv->model, row);
@@ -190,24 +229,34 @@
 static void
 addressbook_free_value (ETableModel *etc, int col, void *value)
 {
+	if (col != PRESENCE_INFO_COL)
 	g_free(value);
 }
 
 static void *
 addressbook_initialize_value (ETableModel *etc, int col)
 {
+	if (col == PRESENCE_INFO_COL)
+		return GINT_TO_POINTER(3);
+	else
 	return g_strdup("");
 }
 
 static gboolean
 addressbook_value_is_empty (ETableModel *etc, int col, const void *value)
 {
+	if (col == PRESENCE_INFO_COL)
+		return !value;
+	else
 	return !(value && *(char *)value);
 }
 
 static char *
 addressbook_value_to_string (ETableModel *etc, int col, const void *value)
 {
+	if (col == PRESENCE_INFO_COL)
+		return g_strdup_printf ("%d", GPOINTER_TO_INT(value));
+	else
 	return g_strdup(value);
 }
 
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/widgets/e-addressbook-view.c evolution/addressbook/gui/widgets/e-addressbook-view.c
--- pristine/evolution/addressbook/gui/widgets/e-addressbook-view.c	2004-09-06 00:52:56.000000000 -0400
+++ evolution/addressbook/gui/widgets/e-addressbook-view.c	2004-09-06 05:05:09.000000000 -0400
@@ -29,6 +29,7 @@
 #include <gtk/gtkscrolledwindow.h>
 #include <gal/e-table/e-table-scrolled.h>
 #include <gal/e-table/e-table-model.h>
+#include <gal/e-table/e-cell-toggle.h>
 #include <gal/widgets/e-popup-menu.h>
 #include <gal/widgets/e-gui-utils.h>
 #include <gal/menus/gal-view-factory-etable.h>
@@ -68,6 +69,7 @@
 #include "eab-contact-merging.h"
 
 #include "widgets/misc/e-error.h"
+#include "widgets/misc/e-imsend.h"
 
 #include "e-contact-editor.h"
 #include <gdk/gdkkeysyms.h>
@@ -77,8 +79,13 @@
 #include <libxml/tree.h>
 #include <libxml/parser.h>
 
+#include "e-util/e-impresence.h"
+#include "art/empty.xpm"
+
 #define SHOW_ALL_SEARCH "(contains \"x-evolution-any-field\" \"\")"
 
+#define PRESENCE_INFO_COL 50
+
 #define d(x)
 
 static void eab_view_init		(EABView		 *card);
@@ -1209,6 +1216,26 @@
 }
 
 static gint
+table_click(ETableScrolled *table, gint row, gint col, GdkEvent *event, EABView *view)
+{
+	if (E_IS_ADDRESSBOOK_TABLE_ADAPTER(view->object) && (col == PRESENCE_INFO_COL)) {
+		EABModel *model = view->model;
+		EContact *contact = eab_model_get_contact (model, row);
+
+        	if (!contact)
+		        return FALSE;
+		GList *blist = e_impresence_get_all_buddy_info (e_contact_get_const (contact, E_CONTACT_UID));
+		if (!blist)
+		        return FALSE;
+	
+		e_imsend_dialog (&(event->button), e_contact_get_const(contact, E_CONTACT_FULL_NAME), blist, TRUE);
+		return TRUE;
+		
+	}
+	return FALSE;
+}
+
+static gint
 table_right_click(ETableScrolled *table, gint row, gint col, GdkEvent *event, EABView *view)
 {
 	do_popup_menu(view, event);
@@ -1396,6 +1423,26 @@
 	e_reflow_model_changed (E_REFLOW_MODEL (adapter));
 }
 
+static ETableExtras *
+create_table_view_extras (void)
+{
+        GdkPixbuf *images [4];
+        ETableExtras *extras;
+
+        extras = e_table_extras_new ();
+
+	images[0] = gdk_pixbuf_new_from_file(EVOLUTION_IMAGESDIR "/buddy-offline.png", NULL);
+	images[1] = gdk_pixbuf_new_from_file(EVOLUTION_IMAGESDIR "/buddy-online.png", NULL);
+	images[2] = gdk_pixbuf_new_from_file(EVOLUTION_IMAGESDIR "/buddy-away.png", NULL);
+	images[3] = gdk_pixbuf_new_from_xpm_data ((const char **)empty_xpm);
+        
+	e_table_extras_add_pixbuf (extras, "presence",  images[0]);
+
+        e_table_extras_add_cell (extras, "render_presence", e_cell_toggle_new (0, 4, images));
+
+	return extras;
+}
+
 static void
 create_table_view (EABView *view)
 {
@@ -1407,7 +1454,9 @@
 	/* Here we create the table.  We give it the three pieces of
 	   the table we've created, the header, the model, and the
 	   initial layout.  It does the rest.  */
-	table = e_table_scrolled_new_from_spec_file (adapter, NULL, EVOLUTION_ETSPECDIR "/e-addressbook-view.etspec", NULL);
+
+	view->extras = create_table_view_extras();	
+	table = e_table_scrolled_new_from_spec_file (adapter, view->extras, EVOLUTION_ETSPECDIR "/e-addressbook-view.etspec", NULL);
 
 	view->object = G_OBJECT(adapter);
 	view->widget = table;
@@ -1416,6 +1465,8 @@
 			 G_CALLBACK(table_double_click), view);
 	g_signal_connect(e_table_scrolled_get_table(E_TABLE_SCROLLED(table)), "right_click",
 			 G_CALLBACK(table_right_click), view);
+	g_signal_connect(e_table_scrolled_get_table(E_TABLE_SCROLLED(table)), "click",
+			 G_CALLBACK(table_click), view);
 	g_signal_connect(e_table_scrolled_get_table(E_TABLE_SCROLLED(table)), "white_space_event",
 			 G_CALLBACK(table_white_space_event), view);
 	g_signal_connect(e_table_scrolled_get_table(E_TABLE_SCROLLED(table)), "selection_change",
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/widgets/e-addressbook-view.etspec evolution/addressbook/gui/widgets/e-addressbook-view.etspec
--- pristine/evolution/addressbook/gui/widgets/e-addressbook-view.etspec	2004-09-06 00:52:56.000000000 -0400
+++ evolution/addressbook/gui/widgets/e-addressbook-view.etspec	2004-09-05 23:37:00.000000000 -0400
@@ -44,6 +44,7 @@
 
   <ETableColumn model_col="48" _title="Spouse"            expansion="1.0" minimum_width="75" resizable="true" cell="string" compare="string"/>
   <ETableColumn model_col="49" _title="Note"              expansion="1.0" minimum_width="75" resizable="true" cell="string" compare="string"/>
+  <ETableColumn model_col="50" _title="IM Presence"  pixbuf="presence" expansion="0.0" minimum_width="22" resizable="false" cell="render_presence" compare="integer" sortable="false"/>
 
   <ETableState>
     <column source="0"/>
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/widgets/e-addressbook-view.h evolution/addressbook/gui/widgets/e-addressbook-view.h
--- pristine/evolution/addressbook/gui/widgets/e-addressbook-view.h	2004-09-06 00:52:56.000000000 -0400
+++ evolution/addressbook/gui/widgets/e-addressbook-view.h	2004-09-05 23:37:00.000000000 -0400
@@ -29,6 +29,7 @@
 #include "widgets/menus/gal-view-menus.h"
 #include "widgets/misc/e-search-bar.h"
 #include "widgets/misc/e-filter-bar.h"
+#include <gal/e-table/e-table-extras.h>
 
 G_BEGIN_DECLS
 
@@ -85,6 +86,9 @@
 	GtkWidget *contact_display;
 	GtkWidget *paned;
 
+	/* table extras -- to hold presence icon */
+	ETableExtras *extras;
+
 	/* Menus handler and the view instance */
 	GalViewInstance *view_instance;
 	GalViewMenus *view_menus;
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/widgets/e-minicard.c evolution/addressbook/gui/widgets/e-minicard.c
--- pristine/evolution/addressbook/gui/widgets/e-minicard.c	2004-09-06 00:52:56.000000000 -0400
+++ evolution/addressbook/gui/widgets/e-minicard.c	2004-09-06 05:05:09.000000000 -0400
@@ -41,6 +41,8 @@
 #include "e-contact-editor.h"
 #include <e-util/e-icon-factory.h>
 #include "util/e-destination.h"
+#include "widgets/misc/e-imsend.h"
+#include "e-util/e-impresence.h"
 
 static void e_minicard_init		(EMinicard		 *card);
 static void e_minicard_class_init	(EMinicardClass	 *klass);
@@ -452,6 +454,19 @@
 }
 
 static void
+minicard_icon_clicked_cb (EText *text, GdkEventButton *ev, gint object_num, gint textofs, EMinicard *minicard)
+{
+	g_return_if_fail (minicard != NULL);
+
+	EContact *contact = minicard->contact;
+        const GList *blist = e_impresence_get_all_buddy_info (e_contact_get_const (contact, E_CONTACT_UID));
+        if (!blist)
+                return;
+
+        e_imsend_dialog (ev, e_contact_get_const (contact, E_CONTACT_FULL_NAME) , blist, TRUE);
+}
+
+static void
 e_minicard_realize (GnomeCanvasItem *item)
 {
 	EMinicard *e_minicard;
@@ -499,6 +514,8 @@
 
 	e_canvas_item_move_absolute(e_minicard->header_text, 6, 6);
 
+	g_signal_connect (E_TEXT(e_minicard->header_text), "icon_clicked", G_CALLBACK (minicard_icon_clicked_cb), e_minicard);
+
 	e_minicard->list_icon = 
 		gnome_canvas_item_new ( group,
 					gnome_canvas_pixbuf_get_type(),
@@ -605,6 +622,28 @@
 			
 			e_canvas_item_grab_focus(item, TRUE);
 
+			/* FIXME praveen pass the event to header text too 
+			 * If i am completely wrong here somebody please point it out
+			 * 
+			 * This is needed because button events on minicard are
+			 * not filtered down to e-text item always. Why is it so??
+			 * Anyway to overcome this i thought it might be better to propagate
+			 * the event to e-text explicitly.
+			 * Unfortunately the event propagation is random, sometimes events
+			 * from minicard are propagated and sometimes not (i don't know why)
+			 * but when they are sent the below code goes into a loop and
+			 * hangs. So how do you accomplish this ?? */
+			/* 
+			GdkEvent *nev = gdk_event_copy(event);
+			g_object_unref (nev->any.window);
+			GdkWindow *cwindow = GTK_WIDGET(GNOME_CANVAS_ITEM(e_minicard)->canvas)->window;
+			g_object_ref (cwindow);
+			nev->any.window = cwindow;
+		
+			gtk_main_do_event (nev);
+			gdk_event_free (nev);
+			*/
+
 			if (gnome_canvas_item_grab (GNOME_CANVAS_ITEM (e_minicard),
 						    mask, NULL, event->button.time)) {
 				return FALSE;
@@ -743,6 +782,11 @@
 				       "width", (double) e_minicard->width - 4.0,
 				       NULL );
 	}
+	
+	if (e_minicard->presence_text)
+		gnome_canvas_item_set( e_minicard->presence_text,
+				       "width", (double) e_minicard->width - 4.0,
+				       NULL );
 }
 
 static void
@@ -824,7 +868,24 @@
 		char *file_as;
 		int left_width = -1;
 
+		const GRBuddyInfo *binfo = e_impresence_get_buddy_info (e_contact_get_const (e_minicard->contact, E_CONTACT_UID));
+		
 		if (e_minicard->header_text) {
+			EText *text = E_TEXT (e_minicard->header_text);
+			e_text_clear_icons (text);
+			if (binfo) {
+				text->manage_icons = FALSE;
+				GdkPixbuf *icon;
+
+				if (binfo->present == 0)
+					icon = gdk_pixbuf_new_from_file(EVOLUTION_IMAGESDIR "/buddy-offline.png", NULL);
+				else if ((binfo->present == 1) && (binfo->idle < 1))
+					icon = gdk_pixbuf_new_from_file(EVOLUTION_IMAGESDIR "/buddy-online.png", NULL);
+				else
+					icon = gdk_pixbuf_new_from_file(EVOLUTION_IMAGESDIR "/buddy-away.png", NULL);
+				e_text_add_icon (text, icon, 0, 0);
+			}
+
 			file_as = e_contact_get (e_minicard->contact, E_CONTACT_FILE_AS);
 			gnome_canvas_item_set (e_minicard->header_text,
 					       "text", file_as ? file_as : "",
@@ -882,6 +943,57 @@
 
 		g_list_foreach(list, (GFunc) e_minicard_field_destroy, NULL);
 		g_list_free(list);
+		
+		if (binfo) {
+			if (e_minicard->presence_text)
+				gtk_object_destroy (GTK_OBJECT(e_minicard->presence_text));
+
+			GnomeCanvasGroup *group;
+			char *name;
+			char *imclient;
+			char *ptext;
+			
+			if (binfo->protocol_id)
+				imclient = g_strdup (gaim_remote_get_protocol (binfo->protocol_id));
+			else
+				imclient = g_strdup ("");
+			
+			if (binfo->present == 0)
+				ptext = g_strdup_printf ("Offline, %s", imclient);
+			else if ((binfo->present == 1) && (binfo->idle < 1))
+				ptext = g_strdup_printf ("Online, %s", imclient);
+			else
+				ptext = g_strdup_printf ("Online (idle %s), %s", gaim_remote_seconds_to_string(binfo->idle), imclient);
+				
+			g_free (imclient);
+
+			group = GNOME_CANVAS_GROUP( e_minicard );
+		
+			name = g_strdup_printf("%s:", "Presence status");
+
+			e_minicard->presence_text = e_minicard_label_new(group);
+
+			left_width = get_left_width (e_minicard);
+			gnome_canvas_item_set( e_minicard->presence_text,
+					       "width", e_minicard->width - 4.0,
+			       		       "fieldname", name,
+					       "field", ptext,
+			       		       "max_field_name_length", (double)left_width,
+			                       "editable", FALSE /* e_minicard->editable */,
+			       		       NULL );
+
+		#if notyet
+			g_object_set(G_OBJECT(e_minicard->presence_text),
+				     "allow_newlines", FALSE,
+				     NULL);
+		#endif
+			g_object_set_data(G_OBJECT (e_minicard->presence_text),
+					  	    "EMinicard:field",
+					  	    GINT_TO_POINTER(51));
+			e_canvas_item_move_absolute(e_minicard->presence_text, 2, e_minicard->height);
+			g_free(name);
+			g_free (ptext);
+		}
 	}
 }
 
@@ -915,6 +1027,16 @@
 			e_canvas_item_move_absolute(item, 2, e_minicard->height);
 			e_minicard->height += text_height;
 		}
+
+		/* Add height for presence status */
+		if (e_minicard->presence_text) {	
+			g_object_get (e_minicard->presence_text,
+				      "height", &text_height,
+			      	      NULL);
+			e_canvas_item_move_absolute(e_minicard->presence_text, 2, e_minicard->height);
+			e_minicard->height += text_height;
+		}
+		
 		e_minicard->height += 2;
 		
 		gnome_canvas_item_set( e_minicard->rect,
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/widgets/e-minicard.h evolution/addressbook/gui/widgets/e-minicard.h
--- pristine/evolution/addressbook/gui/widgets/e-minicard.h	2004-09-06 00:52:56.000000000 -0400
+++ evolution/addressbook/gui/widgets/e-minicard.h	2004-09-05 23:37:00.000000000 -0400
@@ -67,6 +67,7 @@
 	GnomeCanvasItem *rect;
 	GnomeCanvasItem *header_rect;
 	GnomeCanvasItem *header_text;
+	GnomeCanvasItem *presence_text;
 	GnomeCanvasItem *list_icon;
 
 	GdkPixbuf *list_icon_pixbuf;
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/addressbook/gui/widgets/eab-contact-display.c evolution/addressbook/gui/widgets/eab-contact-display.c
--- pristine/evolution/addressbook/gui/widgets/eab-contact-display.c	2004-09-06 00:52:56.000000000 -0400
+++ evolution/addressbook/gui/widgets/eab-contact-display.c	2004-09-06 21:39:51.000000000 -0400
@@ -36,12 +36,17 @@
 #include <gtkhtml/gtkhtml.h>
 #include <gtkhtml/gtkhtml-stream.h>
 
+#include "e-util/e-impresence.h"
+
 /*#define HANDLE_MAILTO_INTERNALLY 1*/
 
 #define PARENT_TYPE (GTK_TYPE_HTML)
 
 struct _EABContactDisplayPrivate {
 	EContact *contact;
+	
+        char *pixbuffer;
+        int pixsize;
 };
 
 
@@ -66,15 +71,14 @@
 		  EABContactDisplay *display)
 {
 	if (!strcmp (url, "internal-contact-photo:")) {
-		EContactPhoto *photo;
-
-		photo = e_contact_get (display->priv->contact, E_CONTACT_PHOTO);
-		if (!photo)
-			photo = e_contact_get (display->priv->contact, E_CONTACT_LOGO);
-
-		gtk_html_stream_write (handle, photo->data, photo->length);
+                if (!display->priv->pixbuffer || (display->priv->pixsize < 1))
+                        return;
 
+                gtk_html_stream_write (handle, display->priv->pixbuffer, display->priv->pixsize);
 		gtk_html_end (html, handle, GTK_HTML_STREAM_OK);
+                
+		g_free(display->priv->pixbuffer);
+                display->priv->pixsize = 0;
 	}
 	else if (!strncmp (url, "evo-icon:", strlen ("evo-icon:"))) {
 		gchar *data;
@@ -110,6 +114,17 @@
 		return;
 	}
 #endif
+	if (!strncasecmp (url, "aim:", 4)) {
+                char *aim_url;
+                if (g_strrstr(url, "goim") || g_strrstr(url, "gochat") || g_strrstr(url, "addbuddy"))
+                        aim_url = g_strdup(url);
+                else
+                        aim_url = g_strdup_printf("aim:goim?screenname=%s", url+4);
+
+                gaim_remote_handle_aim_uri(aim_url);
+                g_free(aim_url);
+		return;
+        }
 
 	gnome_url_show (url, &err);
 		
@@ -385,6 +400,12 @@
 eab_contact_display_render_normal (EABContactDisplay *display, EContact *contact)
 {
 	GtkHTMLStream *html_stream;
+	char *buffer;
+	int buffersize;
+        const GRBuddyInfo *binfo;
+	GdkPixbufLoader *loader;
+	EContactPhoto *photo, *logo;
+	GdkPixbuf *pix = NULL, *saturated = NULL;
 
 	if (display->priv->contact)
 		g_object_unref (display->priv->contact);
@@ -398,17 +419,67 @@
 
 	if (contact) {
 		char *str, *html;
-		EContactPhoto *photo;
 
 		gtk_html_stream_printf (html_stream, "<table cellspacing=\"20\" border=\"0\"><td valign=\"top\">");
+		loader = gdk_pixbuf_loader_new ();
+		
 		photo = e_contact_get (contact, E_CONTACT_PHOTO);
-		if (!photo)
-			photo = e_contact_get (contact, E_CONTACT_LOGO);
-		if (photo) {
-			gtk_html_stream_printf (html_stream, "<img border=\"1\" src=\"internal-contact-photo:\">");
-			e_contact_photo_free (photo);
+
+	        if (photo)
+	        	gdk_pixbuf_loader_write (loader, photo->data, photo->length, NULL);
+	        else {
+			binfo = e_impresence_get_buddy_info (e_contact_get_const(contact, E_CONTACT_UID));
+			if ((binfo) && (binfo->ilen > 1))
+				gdk_pixbuf_loader_write (loader, binfo->icon, binfo->ilen, NULL);
+			else {
+				logo = e_contact_get (contact, E_CONTACT_LOGO);
+				if (logo)
+					gdk_pixbuf_loader_write (loader, logo->data, logo->length, NULL);
+			}
 		}
 		
+	        pix = gdk_pixbuf_loader_get_pixbuf (loader);
+	        
+		/* Note if no picture is found and the buddy is online.offline
+		 * this will automatically put a default 
+		 * picture -- right now it is network.png and display that 
+		 * Remove this if not needed 
+		 * If added , either
+		 * 	1. Create icon, put it in "art" folder and modify Makefile.am
+		 * 	2. or modify pixbuf loading code to load the proper icon */
+
+		if (pix)
+	        	gdk_pixbuf_ref (pix);
+		else if (binfo)
+			pix = gdk_pixbuf_new_from_file(EVOLUTION_IMAGESDIR "/buddy-none.png", NULL);
+		gdk_pixbuf_loader_close (loader, NULL);
+                g_object_unref (loader);
+
+		if (pix && GDK_IS_PIXBUF(pix)) {
+			if (binfo && (binfo->present == 0)) { 
+        	                saturated = gdk_pixbuf_copy(pix);
+                	        gdk_pixbuf_saturate_and_pixelate (pix, saturated, 0.5, TRUE);
+                                gdk_pixbuf_unref(pix);
+                               	pix = saturated;
+	                }
+			
+			gdk_pixbuf_save_to_buffer(pix, &display->priv->pixbuffer, &display->priv->pixsize, "png", NULL, NULL);
+	        	if (display->priv->pixbuffer) {
+				if (binfo)
+					gtk_html_stream_printf (html_stream, "<a href=\"aim:%s\"><img border=\"0\" src=\"internal-contact-photo:\"></a>", binfo->name);
+				else	
+					gtk_html_stream_printf (html_stream, "<img border=\"0\" src=\"internal-contact-photo:\">");
+			}
+			
+			gdk_pixbuf_unref (pix);
+                }
+		
+/*	This segfaults for me  FIXME - praveen
+		if (photo)
+			e_contact_photo_free (photo);
+		if (logo)
+			e_contact_photo_free (logo); */
+		
 		gtk_html_stream_printf (html_stream, "</td><td valign=\"top\">\n");
 
 		str = e_contact_get_const (contact, E_CONTACT_FILE_AS);
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/art/Makefile.am evolution/art/Makefile.am
--- pristine/evolution/art/Makefile.am	2004-09-06 00:52:54.000000000 -0400
+++ evolution/art/Makefile.am	2004-09-06 00:40:14.000000000 -0400
@@ -5,7 +5,11 @@
 	monkey-16.png			\
 	offline.png			\
 	online.png			\
-	world_map-960.png
+	world_map-960.png		\
+	buddy-online.png		\
+	buddy-offline.png		\
+	buddy-away.png			\
+	buddy-none.png
 
 install-data-local:
 	$(mkinstalldirs) $(DESTDIR)$(datadir)/pixmaps
--- pristine/evolution/configure.in	2004-09-06 00:52:55.000000000 -0400
+++ evolution/configure.in	2004-09-06 04:29:17.000000000 -0400
@@ -477,6 +477,15 @@
 AC_SUBST(GTKHTML_DATADIR)
 
 
+dnl **************
+dnl Gaim check
+dnl **************
+
+PKG_CHECK_MODULES(GAIM, gaim)
+AC_SUBST(GAIM_CFLAGS)
+AC_SUBST(GAIM_LIBS)
+
+
 dnl ******************************
 dnl Pilot checking
 dnl ******************************
@@ -1100,7 +1109,7 @@
 AC_SUBST(E_NAME_CFLAGS)
 AC_SUBST(E_NAME_LIBS)
 
-EVO_SET_COMPILE_FLAGS(E_UTIL, gthread-2.0 gconf-2.0 libxml-2.0 libbonoboui-2.0 >= $BONOBOUI_REQUIRED libglade-2.0 gal-2.2 >= $GAL_REQUIRED libgnomeui-2.0 libgnome-2.0 libgnomecanvas-2.0 libedataserver-1.0 >= $EDS_REQUIRED $mozilla_nspr, $THREADS_CFLAGS $MANUAL_NSPR_CFLAGS, $THREADS_LIBS $MANUAL_NSPR_LIBS)
+EVO_SET_COMPILE_FLAGS(E_UTIL, gthread-2.0 gconf-2.0 libxml-2.0 libbonoboui-2.0 >= $BONOBOUI_REQUIRED libglade-2.0 gal-2.2 >= $GAL_REQUIRED libgnomeui-2.0 libgnome-2.0 libgnomecanvas-2.0 libedataserver-1.0 >= $EDS_REQUIRED gaim $mozilla_nspr, $THREADS_CFLAGS $MANUAL_NSPR_CFLAGS, $THREADS_LIBS $MANUAL_NSPR_LIBS)
 AC_SUBST(E_UTIL_CFLAGS)
 AC_SUBST(E_UTIL_LIBS)
 
@@ -1134,7 +1143,7 @@
 
 dnl --- evolution-addressbook flags
 
-EVOLUTION_ADDRESSBOOK_DEPS="gconf-2.0 libbonoboui-2.0 >= $BONOBOUI_REQUIRED libglade-2.0 gal-2.2 >= $GAL_REQUIRED libgnomeui-2.0 libgnome-2.0 libgnomecanvas-2.0 gnome-vfs-2.0 libgnomeprintui-2.2 libgtkhtml-3.1 >= $GTKHTML_REQUIRED libebook-1.0 >= $EDS_REQUIRED"
+EVOLUTION_ADDRESSBOOK_DEPS="gconf-2.0 libbonoboui-2.0 >= $BONOBOUI_REQUIRED libglade-2.0 gal-2.2 >= $GAL_REQUIRED libgnomeui-2.0 libgnome-2.0 libgnomecanvas-2.0 gnome-vfs-2.0 libgnomeprintui-2.2 libgtkhtml-3.1 >= $GTKHTML_REQUIRED libebook-1.0 >= $EDS_REQUIRED gaim"
 
 EVO_SET_COMPILE_FLAGS(EVOLUTION_ADDRESSBOOK, $EVOLUTION_ADDRESSBOOK_DEPS)
 AC_SUBST(EVOLUTION_ADDRESSBOOK_CFLAGS)
@@ -1151,7 +1160,7 @@
 AC_SUBST(LIBSOUP_CFLAGS)
 AC_SUBST(LIBSOUP_LIBS)
 
-EVO_SET_COMPILE_FLAGS(EVOLUTION_CALENDAR, libgnome-2.0 libgnomeui-2.0 libbonoboui-2.0 gal-2.2 >= $GAL_REQUIRED libglade-2.0 gnome-vfs-2.0 libgnomeprint-2.2 libgnomeprintui-2.2 libgtkhtml-3.1 >= $GTKHTML_REQUIRED libebook-1.0 >= $EDS_REQUIRED libecal-1.0 >= $EDS_REQUIRED)
+EVO_SET_COMPILE_FLAGS(EVOLUTION_CALENDAR, libgnome-2.0 libgnomeui-2.0 libbonoboui-2.0 gal-2.2 >= $GAL_REQUIRED libglade-2.0 gnome-vfs-2.0 libgnomeprint-2.2 libgnomeprintui-2.2 libgtkhtml-3.1 >= $GTKHTML_REQUIRED libebook-1.0 >= $EDS_REQUIRED libecal-1.0 >= $EDS_REQUIRED gaim)
 AC_SUBST(EVOLUTION_CALENDAR_CFLAGS)
 AC_SUBST(EVOLUTION_CALENDAR_LIBS)
 
@@ -1163,7 +1172,7 @@
 
 dnl --- evolution-mail flags
 
-EVO_SET_COMPILE_FLAGS(EVOLUTION_MAIL, libgnome-2.0 libgnomeui-2.0 libbonoboui-2.0 >= $BONOBOUI_REQUIRED gal-2.2 >= $GAL_REQUIRED libglade-2.0 gnome-vfs-module-2.0 libgnomeprint-2.2 libgnomeprintui-2.2 libgtkhtml-3.1 >= $GTKHTML_REQUIRED libxml-2.0 bonobo-activation-2.0 gthread-2.0 gconf-2.0 $mozilla_nss libebook-1.0 >= $EDS_REQUIRED)
+EVO_SET_COMPILE_FLAGS(EVOLUTION_MAIL, libgnome-2.0 libgnomeui-2.0 libbonoboui-2.0 >= $BONOBOUI_REQUIRED gal-2.2 >= $GAL_REQUIRED libglade-2.0 gnome-vfs-module-2.0 libgnomeprint-2.2 libgnomeprintui-2.2 libgtkhtml-3.1 >= $GTKHTML_REQUIRED libxml-2.0 bonobo-activation-2.0 gthread-2.0 gconf-2.0 $mozilla_nss libebook-1.0 >= $EDS_REQUIRED gaim)
 AC_SUBST(EVOLUTION_MAIL_CFLAGS)
 AC_SUBST(EVOLUTION_MAIL_LIBS)
 
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/e-util/Makefile.am evolution/e-util/Makefile.am
--- pristine/evolution/e-util/Makefile.am	2004-09-06 00:52:55.000000000 -0400
+++ evolution/e-util/Makefile.am	2004-09-05 23:10:50.000000000 -0400
@@ -46,7 +46,8 @@
 	e-trie.h				\
 	e-uid.h					\
 	e-url.h					\
-	md5-utils.h
+	md5-utils.h				\
+	e-impresence.h
 
 libeutil_la_SOURCES =				\
 	$(MARSHAL_GENERATED)			\
@@ -86,12 +87,13 @@
 	e-url.c					\
 	eggtrayicon.c				\
 	eggtrayicon.h				\
-	md5-utils.c
+	md5-utils.c				\
+	e-impresence.c
 
 MARSHAL_GENERATED = e-util-marshal.c e-util-marshal.h
 @EVO_MARSHAL_RULE@
 
-libeutil_la_LIBADD = $(E_UTIL_LIBS)
+libeutil_la_LIBADD = $(E_UTIL_LIBS) -levogaimremote
 
 econdinclude_HEADERS =		\
 	e-pilot-map.h		\
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/e-util/e-impresence.c evolution/e-util/e-impresence.c
--- pristine/evolution/e-util/e-impresence.c	1969-12-31 19:00:00.000000000 -0500
+++ evolution/e-util/e-impresence.c	2004-09-06 21:37:45.000000000 -0400
@@ -0,0 +1,438 @@
+/*
+ *  Authors:  Praveen Kumar <praveen9 gmail com>
+ *
+ *  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., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <math.h>
+#include "e-impresence.h"
+#include "gal/util/e-marshal.h"
+#include <string.h>
+
+static EIMPresence *imp;
+static GObjectClass *parent_class;
+
+enum {
+        NEW_PRESENCE_SIGNAL,
+        LAST_SIGNAL
+};
+
+typedef struct {
+	GList *cl;
+	EIMPresenceCache cache;
+} ContactlistQuery;
+
+static guint e_impresence_signals[LAST_SIGNAL] = { 0 };
+
+void e_impresence_cb (EContact *contact, GList *buddylist, gpointer data);
+void destroy_key_fn (gpointer data);
+void destroy_blist_fn (gpointer data);
+void destroy_photo_fn (gpointer data);
+void e_impresence_dump_response (GList *blist);
+gboolean e_impresence_refresh_cache (gpointer data);
+void _e_impresence_query_list (GList *contactlist, EIMPresenceCache cache);
+
+gboolean requery_list (gpointer data);
+
+static gboolean 
+delete_hash_table (gpointer key, gpointer value, gpointer user_data)
+{
+	return TRUE;
+}
+
+static void
+e_impresence_dispose (GObject *obj)
+{
+	g_return_if_fail (!E_IS_IMPRESENCE(obj));
+        EIMPresence *imp = E_IMPRESENCE (obj);
+
+        if (imp->uidmap) {
+		g_hash_table_foreach_remove (imp->uidmap, delete_hash_table, NULL);
+		g_hash_table_destroy (imp->uidmap);
+		imp->uidmap = NULL;
+        }
+
+        if (imp->photomap) {
+		g_hash_table_foreach_remove (imp->photomap, delete_hash_table, NULL);
+		g_hash_table_destroy (imp->photomap);
+		imp->photomap = NULL;
+        }
+
+        if (G_OBJECT_CLASS (parent_class)->dispose)
+                (* G_OBJECT_CLASS (parent_class)->dispose) (obj);
+}
+
+static void
+e_impresence_class_init (EIMPresenceClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        parent_class = g_type_class_ref (G_TYPE_OBJECT);
+
+	e_impresence_signals[NEW_PRESENCE_SIGNAL] = 
+                g_signal_new ("new_presence_info",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (EIMPresenceClass, new_presence_info),
+                              NULL, NULL, 
+                              e_marshal_NONE__POINTER_POINTER,
+                              G_TYPE_NONE, 2,
+                              G_TYPE_POINTER, G_TYPE_POINTER);
+
+        object_class->dispose = e_impresence_dispose;
+}
+
+static void
+e_impresence_init (GObject *obj)
+{
+	EIMPresence *empr = E_IMPRESENCE (obj);
+	
+	empr->run_cache_thread = FALSE;
+	empr->max_cache = 100;
+	empr->max_cache_count = ceil (0.1*empr->max_cache);
+	empr->uidmap = g_hash_table_new_full (g_str_hash, g_str_equal, destroy_key_fn, destroy_blist_fn);
+	empr->photomap = g_hash_table_new_full (g_str_hash, g_str_equal, destroy_key_fn, destroy_photo_fn);
+	/* FIXME -- praveen 
+	 * Set timeout here for refreshing cache --
+	 * The default right now is one minute. */
+	g_timeout_add (1000*60, e_impresence_refresh_cache , NULL);
+}
+
+void
+build_contact_list (gpointer key, gpointer value, gpointer data)
+{
+	EBook *book = data;
+	
+	const char *cuid = key;
+
+	EContact *contact;
+	if (e_book_get_contact (book, cuid, &contact, NULL))
+		imp->contactlist = g_list_append ( imp->contactlist, contact);
+
+}
+
+gboolean
+e_impresence_refresh_cache (gpointer data)
+{
+	if (!imp)
+		return FALSE;
+	
+	EBook *book = e_book_new_default_addressbook (NULL);
+				        
+	if (!book || !e_book_open (book, TRUE, NULL)) {
+		g_warning ("e_impresence_refresh_cache - couldn't load addressbook");
+		return TRUE;
+	}
+	
+	imp->contactlist = NULL;
+
+	g_hash_table_foreach (imp->uidmap, build_contact_list, book);
+
+	if (imp->contactlist)
+		_e_impresence_query_list (imp->contactlist, EIMPRESENCE_RECACHE);
+
+	return TRUE;
+}
+
+void
+e_impresence_create_cache ()
+{
+	if (!imp)
+		return;
+	
+        EBookQuery *query;
+        GList *contacts;
+	int i;
+	gboolean is_list;
+
+	EBook *book = e_book_new_default_addressbook (NULL);
+				        
+	if (!book || !e_book_open (book, TRUE, NULL)) {
+		g_warning ("e_impresence_refresh_cache - couldn't load addressbook");
+		return;
+	}
+	
+	query = e_book_query_any_field_contains ("");
+	e_book_get_contacts (book, query, &contacts, NULL);
+	e_book_query_unref (query);
+
+	if (!contacts)
+		return;
+
+	GList *l = g_list_last (contacts);
+
+	for (i = g_list_length(contacts)-imp->max_cache ; l && i > 0; i--, l = g_list_last(contacts)) {
+		contacts = g_list_remove_link (contacts, l);
+		g_list_free_1 (l);
+		g_object_unref (G_OBJECT(l->data));
+	}
+
+	_e_impresence_query_list (contacts, EIMPRESENCE_RECACHE);
+
+	return;
+}
+
+GType
+e_impresence_get_type (void)
+{
+        static GType imp_type = 0;
+
+        if (!imp_type) {
+                GTypeInfo imp_info = {
+                        sizeof (EIMPresenceClass),
+                        NULL, /* base_class_init */
+                        NULL, /* base_class_finalize */
+                        (GClassInitFunc)  e_impresence_class_init,
+                        NULL, /* class_finalize */
+                        NULL, /* class_data */
+                        sizeof (EIMPresence),
+                        0,    /* n_preallocs */
+                        (GInstanceInitFunc) e_impresence_init
+                };
+
+                imp_type = g_type_register_static (G_TYPE_OBJECT, "EIMPresence", &imp_info, 0);
+        }
+
+        return imp_type;
+}
+
+EIMPresence *
+e_impresence_get_global (void)
+{
+        if (!imp)
+                imp = g_object_new (e_impresence_get_type(), 0);
+
+        return imp;
+}
+
+gboolean
+e_impresence_query (EContact *contact, EIMPresenceCache cache)
+{
+	g_return_val_if_fail (contact != NULL, FALSE);
+
+	if (cache == EIMPRESENCE_RECACHE) {
+		gaim_remote_get_buddy_info (contact, e_impresence_cb, NULL);
+		return FALSE;
+	}
+	
+	/* Search the cache */
+	
+	char *cuid = e_contact_get_const (contact, E_CONTACT_UID);
+
+	GList *blist = g_hash_table_lookup(imp->uidmap, cuid);
+
+	if (blist) {
+                g_signal_emit(imp, e_impresence_signals[NEW_PRESENCE_SIGNAL], 0, contact, blist);
+	        return TRUE;
+	}
+			
+	if (cache == EIMPRESENCE_CACHE) {
+		gaim_remote_get_buddy_info (contact, e_impresence_cb, NULL);
+	}
+
+	return FALSE;
+}
+
+gboolean requery_list (gpointer data)
+{
+	if (data != NULL) {
+		ContactlistQuery *clquery = (ContactlistQuery *)data;
+		_e_impresence_query_list (clquery->cl, clquery->cache);
+		g_free (clquery);
+	}
+	return FALSE;
+}
+
+void
+e_impresence_query_list (GList *contactlist, EIMPresenceCache cache)
+{
+	GList *l = g_list_first (contactlist);
+	
+	/* Increase ref of contact so that contact does not get destroyed while we are sending out queries */
+	while (l) {
+		if (E_IS_CONTACT(l->data))
+			g_object_ref (E_CONTACT(l->data));
+		l = l->next;
+	}
+
+	_e_impresence_query_list (contactlist, cache);
+}
+
+void
+_e_impresence_query_list (GList *contactlist, EIMPresenceCache cache)
+{
+	g_return_if_fail (contactlist != NULL);
+
+	GList *l = g_list_first (contactlist);
+	EContact *contact;
+
+	int i = 0;
+
+	/* Send out queries for first 10 contacts and schedule another query session for later*/
+	for (i = 0; l && i < 10; i++, l = contactlist) {
+		contact = l->data;
+		if (contact && E_IS_CONTACT(contact)) {
+			e_impresence_query (contact, cache);
+			g_object_unref (contact); /* Release our hold on the contact */
+		}
+		else	
+			g_warning ("The contact list does not contain valid contacts");
+		contactlist = g_list_remove_link (contactlist, l);
+		g_list_free_1(l);
+	}
+
+	if (contactlist) {
+		ContactlistQuery *clquery = g_new0(ContactlistQuery, 1);
+		clquery->cl = contactlist;
+		clquery->cache = cache;
+		g_timeout_add (100, requery_list, clquery);
+	}
+}
+
+const GList *
+e_impresence_get_all_buddy_info (const char *cuid)
+{
+	return g_hash_table_lookup(imp->uidmap, cuid);
+}
+
+const GRBuddyInfo *
+e_impresence_get_buddy_info (const char *cuid)
+{
+	GList *blist = g_hash_table_lookup(imp->uidmap, cuid);
+	GRBuddyInfo *b, *boffline = NULL;
+
+	GList *l = g_list_first(blist);
+	
+	while (l) {
+		b = l->data;
+		if (b->present == 1)
+			return b;
+		else if (!boffline  && (b->present == 0))
+			boffline = b;
+		l = l->next;
+	}
+
+	if (boffline)
+		return boffline;
+	else
+		return NULL;
+}
+
+const EIMPresencePhoto *
+e_impresence_get_photo (const char *cuid)
+{
+	return g_hash_table_lookup (imp->photomap, cuid);
+}
+
+static gboolean 
+shrink_hash_table (gpointer key, gpointer value, gpointer user_data)
+{
+	if (!imp)
+		return FALSE;
+
+	if (imp->max_cache_count-- > 0)
+		return TRUE;
+	else
+		return FALSE;
+}
+
+void
+e_impresence_cb (EContact *contact, GList *buddylist, gpointer data)
+{
+	/* if we get NULL as buddylist from gaim, still add to cache */
+
+	/* Check the contact photo and replace the one in GRBuddyInfo */
+	EIMPresencePhoto *eimphoto;
+	EContactPhoto *photo = e_contact_get (contact, E_CONTACT_PHOTO);
+	if (photo) {
+		eimphoto = g_new0(EIMPresencePhoto, 1);
+		eimphoto->data = g_new (char, photo->length);
+		memcpy(eimphoto->data, photo->data, photo->length);
+		eimphoto->length = photo->length;
+		g_hash_table_replace (imp->photomap, g_strdup(e_contact_get_const(contact, E_CONTACT_UID)), eimphoto);
+		e_contact_photo_free (photo);
+	} else	
+		g_hash_table_replace (imp->photomap, g_strdup(e_contact_get_const(contact, E_CONTACT_UID)), NULL);
+
+	g_hash_table_replace (imp->uidmap, g_strdup(e_contact_get_const(contact, E_CONTACT_UID)), buddylist);
+	
+	if (g_hash_table_size(imp->uidmap) > imp->max_cache) {
+		g_hash_table_foreach_remove (imp->uidmap, shrink_hash_table, NULL);
+		imp->max_cache_count = ceil (0.1 * imp->max_cache);
+	}
+	
+	if (g_hash_table_size(imp->photomap) > imp->max_cache) {
+		g_hash_table_foreach_remove (imp->photomap, shrink_hash_table, NULL);
+		imp->max_cache_count = ceil (0.1 * imp->max_cache);
+	}
+
+	/* e_impresence_dump_response (buddylist); */
+	g_signal_emit(imp, e_impresence_signals[NEW_PRESENCE_SIGNAL], 0, contact, buddylist);
+
+}
+
+void
+e_impresence_dump_response (GList *blist)
+{
+	if (!blist) {
+		g_message ("e-impresence -- got a null buddy list as response\n"); 
+		return;
+	}
+
+	GList *l = g_list_first(blist);
+	
+	while (l) {
+		GRBuddyInfo *binfo = l->data;
+		
+		g_message ("e-impresence -- caching presence for %s status %d \n", binfo->name, binfo->present);
+		l = l->next;
+	}
+	return;
+}
+
+void
+destroy_key_fn (gpointer data)
+{
+	if (data)
+		g_free(data);
+}
+
+void
+destroy_blist_fn (gpointer data)
+{
+	if (!data)
+		return;
+
+	GList *buddylist = data;
+	
+	for (;buddylist; buddylist = g_list_next(buddylist))
+		gaim_remote_free_buddy_info(buddylist->data);
+	
+	g_list_free(buddylist);
+}
+
+void
+destroy_photo_fn (gpointer data)
+{
+	if (!data)
+		return;
+
+	EIMPresencePhoto *eimphoto = data;
+
+	if (eimphoto->data)
+		g_free (eimphoto->data);
+	g_free (eimphoto);
+}
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/e-util/e-impresence.h evolution/e-util/e-impresence.h
--- pristine/evolution/e-util/e-impresence.h	1969-12-31 19:00:00.000000000 -0500
+++ evolution/e-util/e-impresence.h	2004-09-06 21:37:45.000000000 -0400
@@ -0,0 +1,89 @@
+/*
+ *  Authors:  Praveen Kumar <praveen9 gmail com>
+ *
+ *  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., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <glib.h>
+#include <gaim-remote/evogaimremote.h>
+
+G_BEGIN_DECLS
+
+/* This is a cache of presence status of all the contacts in the addressbook 
+   It will be a global cache used by mail component, composer component, addressbook component  
+   to query the presence status based on contact uid's */
+
+#define E_TYPE_IMPRESENCE           (e_impresence_get_type ())
+#define E_IMPRESENCE(o)             (G_TYPE_CHECK_INSTANCE_CAST ((o), E_TYPE_IMPRESENCE, EIMPresence))
+#define E_IMPRESENCE_CLASS(k)       (G_TYPE_CHECK_CLASS_CAST ((k), E_TYPE_IMPRESENCE, EIMPresenceClass))
+#define E_IS_IMPRESENCE(o)          (G_TYPE_CHECK_INSTANCE_TYPE ((o), E_TYPE_IMPRESENCE))
+#define E_IS_IMPRESENCE_CLASS(k)    (G_TYPE_CHECK_CLASS_TYPE ((k), E_TYPE_IMPRESENCE))
+#define E_IMPRESENCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_IMPRESENCE, EIMPresenceClass))
+	
+typedef struct _EIMPresence EIMPresence;
+typedef struct _EIMPresenceClass EIMPresenceClass;
+
+typedef enum {
+	EIMPRESENCE_RECACHE,		/* Don't get from cache at all -- requery and recache */
+	EIMPRESENCE_CACHE,		/* Cache and if not found send a query */
+	EIMPRESENCE_CACHE_ONLY		/* Cache only, if not found don't send a query */
+} EIMPresenceCache;
+
+typedef struct {
+	gchar *data;
+	int length;
+} EIMPresencePhoto;
+
+struct _EIMPresence {
+	GObject parent;
+		
+	gboolean run_cache_thread;	/* Should we run the cache thread at start */
+	gint  max_cache;		/* Max cache items */	
+	gint  max_cache_count;		/* Max cache items */	
+
+	GHashTable *uidmap;		/* Cache presence status for contact uid's */
+	GHashTable *photomap;		/* Cache for contact logos */
+	GList *contactlist;		/* Temorary list for recache queries */
+};
+
+struct _EIMPresenceClass {
+	GObjectClass parent_class;
+	void (*new_presence_info) (EIMPresence *imp, EContact *contact, GList *buddylist);
+};
+
+GType e_impresence_get_type (void);
+
+EIMPresence *e_impresence_get_global (void);
+
+/* This will initialise the global cache */
+void e_impresence_create_cache(void);
+
+/* This will start a new request if not found in cache */
+gboolean e_impresence_query (EContact *contact, EIMPresenceCache cache);
+
+/* This will start a new request if not found in cache */
+void e_impresence_query_list (GList *contactlist, EIMPresenceCache cache);
+
+/* This will return only the first online/offline buddy or NULL otherwise */
+const GRBuddyInfo *e_impresence_get_buddy_info (const char *cuid);
+
+/* This will return the buddy list right from cache */
+const GList *e_impresence_get_all_buddy_info (const char *cuid);
+
+/* Thsi will return the corresponding photo */
+const EIMPresencePhoto *e_impresence_get_photo (const char *cuid);
+
+G_END_DECLS
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/mail/Makefile.am evolution/mail/Makefile.am
--- pristine/evolution/mail/Makefile.am	2004-09-06 00:52:54.000000000 -0400
+++ evolution/mail/Makefile.am	2004-09-05 23:20:59.000000000 -0400
@@ -16,6 +16,7 @@
 	-I$(top_srcdir)/smime/gui			\
 	$(EVOLUTION_MAIL_CFLAGS)			\
 	$(CERT_UI_CFLAGS)				\
+	$(E_UTIL_CFLAGS)				\
 	-DEVOLUTION_DATADIR=\""$(datadir)"\"		\
 	-DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\"	\
 	-DEVOLUTION_GLADEDIR=\""$(gladedir)"\"		\
@@ -100,6 +101,8 @@
 	em-format-html-display.h		\
 	em-format-html-print.c			\
 	em-format-html-print.h			\
+	em-impresence.c				\
+	em-impresence.h				\
 	em-stripsig-filter.c			\
 	em-stripsig-filter.h			\
 	em-format-quote.c			\
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/mail/em-folder-view.c evolution/mail/em-folder-view.c
--- pristine/evolution/mail/em-folder-view.c	2004-09-06 00:52:54.000000000 -0400
+++ evolution/mail/em-folder-view.c	2004-09-06 00:22:04.000000000 -0400
@@ -77,6 +77,7 @@
 #include "em-utils.h"
 #include "em-composer-utils.h"
 #include "em-marshal.h"
+#include "em-impresence.h"
 
 #include <gtkhtml/gtkhtml.h>
 #include <gtkhtml/htmlobject.h>
@@ -134,6 +135,8 @@
 
 	GalViewInstance *view_instance;
 	GalViewMenus *view_menus;
+
+	EMIMPresence *empr;
 };
 
 static GtkVBoxClass *emfv_parent;
@@ -156,7 +159,7 @@
 	EMFolderView *emfv = (EMFolderView *)o;
 	struct _EMFolderViewPrivate *p;
 	extern CamelSession *session;
-	
+
 	gtk_box_set_homogeneous (GTK_BOX (emfv), FALSE);
 
 	p = emfv->priv = g_malloc0(sizeof(struct _EMFolderViewPrivate));
@@ -192,6 +195,9 @@
 	g_signal_connect(p->invisible, "selection_clear_event", G_CALLBACK(emfv_selection_clear_event), emfv);
 	gtk_selection_add_target(p->invisible, GDK_SELECTION_PRIMARY, GDK_SELECTION_TYPE_STRING, 0);
 	gtk_selection_add_target(p->invisible, GDK_SELECTION_CLIPBOARD, GDK_SELECTION_TYPE_STRING, 1);
+	
+	emfv->priv->empr = em_impresence_new (emfv);
+	g_object_set_data ((GObject *) emfv->preview, "impresence", emfv->priv->empr);	
 
 	emfv->async = mail_async_event_new();
 
@@ -326,7 +332,7 @@
 GtkWidget *em_folder_view_new(void)
 {
 	EMFolderView *emfv = g_object_new(em_folder_view_get_type(), 0);
-
+	
 	return (GtkWidget *)emfv;
 }
 
@@ -1942,7 +1948,7 @@
 emfv_list_done_message_selected(CamelFolder *folder, const char *uid, CamelMimeMessage *msg, void *data)
 {
 	EMFolderView *emfv = data;
-	
+
 	if (emfv->preview == NULL) {
 		emfv->priv->nomarkseen = FALSE;
 		g_object_unref (emfv);
@@ -2080,6 +2086,15 @@
 		/* ignore */
 	} else if (!strncasecmp (uri, "cid:", 4)) {
 		/* ignore */
+	} else if (!strncasecmp (uri, "aim:", 4)) {
+		char *aim_uri;
+		if (g_strrstr(uri, "goim") || g_strrstr(uri, "gochat") || g_strrstr(uri, "addbuddy"))
+			aim_uri = g_strdup(uri);
+		else
+			aim_uri = g_strdup_printf("aim:goim?screenname=%s", uri+4);
+			
+		gaim_remote_handle_aim_uri(aim_uri);	
+		g_free(aim_uri);
 	} else {
 		GError *err = NULL;
 		
@@ -2381,6 +2396,8 @@
 			g_free(addr);
 			camel_url_free(curl);
 			camel_object_unref(cia);
+		} else if (strncmp (url, "aim:", 4) == 0) {
+			nice_url = g_strdup_printf (_("Click to start chat with %s"), url + 4);
 		} else
 			nice_url = g_strdup_printf (_("Click to open %s"), url);
 	}
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/mail/em-format-html.c evolution/mail/em-format-html.c
--- pristine/evolution/mail/em-format-html.c	2004-09-06 00:52:54.000000000 -0400
+++ evolution/mail/em-format-html.c	2004-09-06 21:31:38.000000000 -0400
@@ -61,17 +61,26 @@
 #include <camel/camel-data-cache.h>
 #include <camel/camel-file-utils.h>
 
+#include <libebook/e-book.h>
+#include <libebook/e-contact.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
 #include <e-util/e-msgport.h>
 
 #include "mail-component.h"
 #include "mail-config.h"
 #include "mail-mt.h"
 
+#include "em-folder-view.h"
+#include "message-list.h"
+
 #include "em-format-html.h"
 #include "em-html-stream.h"
 #include "em-utils.h"
+#include "em-impresence.h"
 
-#define d(x) 
+#define d(x)
 
 #define EFH_TABLE_OPEN "<table>"
 
@@ -87,6 +96,9 @@
 	guint format_timeout_id;
 	struct _format_msg *format_timeout_msg;
 
+	char *pixbuffer;
+	int pixsize;
+
 	/* Table that re-maps text parts into a mutlipart/mixed, EMFormatHTMLCache * */
 	GHashTable *text_inline_parts;
 
@@ -101,6 +113,8 @@
 static void efh_format_clone(EMFormat *emf, CamelFolder *folder, const char *uid, CamelMimeMessage *msg, EMFormat *emfsource);
 static void efh_format_error(EMFormat *emf, CamelStream *stream, const char *txt);
 static void efh_format_message(EMFormat *, CamelStream *, CamelMedium *);
+static void efh_format_presence(EMFormat *, CamelStream *stream, CamelMedium *part);
+
 static void efh_format_source(EMFormat *, CamelStream *, CamelMimePart *);
 static void efh_format_attachment(EMFormat *, CamelStream *, CamelMimePart *, const char *, const EMFormatHandler *);
 static void efh_format_secure(EMFormat *emf, CamelStream *stream, CamelMimePart *part, CamelCipherValidity *valid);
@@ -538,6 +552,13 @@
 	} else if (g_ascii_strncasecmp(url, "http:", 5) == 0 || g_ascii_strncasecmp(url, "https:", 6) == 0) {
 		d(printf(" adding job, get %s\n", url));
 		job = em_format_html_job_new(efh, emfh_gethttp, g_strdup(url));
+	} else if (!g_strcasecmp(url, "internal-contact-photo:")) {
+		if (!efh->priv->pixbuffer || (efh->priv->pixsize < 1))
+			return;
+		gtk_html_stream_write (handle, efh->priv->pixbuffer, efh->priv->pixsize);
+		gtk_html_end (html, handle, GTK_HTML_STREAM_OK);
+		g_free(efh->priv->pixbuffer);
+		efh->priv->pixsize = 0;
 	} else {
 		d(printf("HTML Includes reference to unknown uri '%s'\n", url));
 		gtk_html_stream_close(handle, GTK_HTML_STREAM_ERROR);
@@ -1700,10 +1721,92 @@
 			}
 			g_free(classid);
 		}
+		efh_format_presence (emf, stream, part);
+		
 		camel_stream_printf (stream, "</tr></table>\n</font>\n");
 	}
 }
 
+static void efh_format_presence(EMFormat *emf, CamelStream *stream, CamelMedium *part)
+{
+	EMFormatHTML *efh = (EMFormatHTML *) emf;
+
+	const char *msgid = emf->uid;
+	
+	EMIMPresence *empr = g_object_get_data((GObject*) emf, "impresence");
+
+	if (!empr)
+		return;
+
+	const char *cuid = em_impresence_get_contact_uid(empr, msgid);
+
+	if (!msgid || !cuid || !strlen(cuid))
+		return;
+
+	GdkPixbuf *pix;
+	
+	/* Find the contact uid for the message */
+	const GRBuddyInfo *binfo = e_impresence_get_buddy_info (cuid);
+	
+	if (!binfo)
+		return;
+	
+	if ((binfo->present != 0) && (binfo->present != 1))
+		return;
+	
+	EIMPresencePhoto *photo = e_impresence_get_photo (cuid);
+	
+	/* If no photo exists either in addressbook or presence info display stock network icon */
+	if (!photo && (binfo->ilen < 1))
+		pix = gdk_pixbuf_new_from_file(EVOLUTION_IMAGES "/buddy-none.png", NULL);
+	else {
+		GdkPixbufLoader *loader = gdk_pixbuf_loader_new ();
+		
+		if (photo && photo->data) {
+	                gdk_pixbuf_loader_write (loader, photo->data, photo->length, NULL);
+			g_free (photo->data);
+			g_free (photo);
+		} else
+			gdk_pixbuf_loader_write (loader, binfo->icon, binfo->ilen, NULL);
+
+                pix = gdk_pixbuf_loader_get_pixbuf (loader);
+                if (pix)
+                        gdk_pixbuf_ref (pix);
+                gdk_pixbuf_loader_close (loader, NULL);
+                g_object_unref (loader);
+	}
+
+	/* Saturate 50% if contact is offline */
+	if ((binfo->present == 0) && pix) {
+		GdkPixbuf *saturated = gdk_pixbuf_copy(pix);
+		gdk_pixbuf_saturate_and_pixelate (pix, saturated, 0.5, TRUE);
+		gdk_pixbuf_unref(pix);
+		pix = saturated;
+	}
+
+	/* Scale the pixbuf to some fixed size */
+	if (pix) {
+		GdkPixbuf *scaled = gdk_pixbuf_scale_simple (pix, 96, 96, GDK_INTERP_BILINEAR);
+		gdk_pixbuf_unref (pix);
+		pix = scaled;
+	}
+
+	char *buffer;
+	int buffersize;
+
+	gdk_pixbuf_save_to_buffer(pix, &buffer, &buffersize, "png", NULL, NULL);
+	efh->priv->pixbuffer = buffer;
+	efh->priv->pixsize = buffersize;
+	
+	if (pix)
+		gdk_pixbuf_unref (pix);
+
+	char *message = g_strdup_printf("%s (%s)", (binfo->present == 0) ? "Offline" : "Online", gaim_remote_get_protocol(binfo->protocol_id));
+
+	camel_stream_printf(stream, "<td><table cellpadding=\"0\">\n<tr><td align=\"center\" valign=\"center\"><a href=\"aim:%s\"><img width=%d height=%d src=\"internal-contact-photo:\"></a></td></tr>", binfo->name, 96, 96);
+	camel_stream_printf(stream, "<tr><td align=\"center\" valign=\"center\">%s</td></tr>\n</table></td>", message);
+}
+
 static void efh_format_message(EMFormat *emf, CamelStream *stream, CamelMedium *part)
 {
 	/* TODO: make this validity stuff a method */
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/mail/em-impresence.c evolution/mail/em-impresence.c
--- pristine/evolution/mail/em-impresence.c	1969-12-31 19:00:00.000000000 -0500
+++ evolution/mail/em-impresence.c	2004-09-06 21:31:39.000000000 -0400
@@ -0,0 +1,233 @@
+/*
+ *  Authors:  Praveen Kumar <praveen9 gmail com>
+ *
+ *  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., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <camel/camel-exception.h>
+#include <camel/camel-file-utils.h>
+#include <camel/camel-folder.h>
+#include <camel/camel-folder-thread.h>
+#include <camel/camel-vee-folder.h>
+#include <camel/camel-mime-message.h>
+#include <camel/camel-internet-address.h>
+#include <libebook/e-book.h>
+#include <libebook/e-book-query.h>
+
+#include "addressbook/util/eab-book-util.h"
+
+#include "em-folder-view.h"
+#include "em-format.h"
+#include "message-list.h"
+
+#include "em-impresence.h"
+#include <string.h>
+
+typedef struct {
+	EMIMPresence *empr;
+	char *msgid;
+	guint query_tag;
+	char *name;
+	char *email;
+} EMIMQuery;
+
+typedef struct {
+	char *msgid;
+	char *cuid;
+} EMIMNode;
+
+void em_impresence_new_info (EIMPresence *eimp, EContact *contact, GList *buddylist, EMIMPresence *empr);
+void em_presence_clear_map (MessageList *ml, EMIMPresence *empr);
+static void query_cb (EBook *book, EBookStatus status, GList *contacts, gpointer closure);
+void free_imquery (EMIMQuery *emimq);
+void free_imnode (gpointer data, gpointer user_data);
+void add_to_cache (EMIMPresence *empr, EMIMNode *node);
+
+EMIMPresence *
+em_impresence_new (EMFolderView *emfv)
+{
+	EMIMPresence *empr = g_new(EMIMPresence, 1);
+
+	empr->emfv = emfv;
+	empr->uidmap = NULL;
+	
+	g_signal_connect(empr->emfv->list, "message_list_built", G_CALLBACK(em_presence_clear_map), empr);
+	g_signal_connect(empr->emfv->list, "message_selected", G_CALLBACK(em_impresence_query), empr);
+	g_signal_connect(e_impresence_get_global(), "new_presence_info", G_CALLBACK(em_impresence_new_info), empr);	
+
+        return empr;
+}
+
+void em_impresence_new_info (EIMPresence *eimp, EContact *contact, GList *buddylist, EMIMPresence *empr)
+{
+	g_return_if_fail (contact != NULL);
+	char *mcuid = em_impresence_get_contact_uid (empr, empr->emfv->displayed_uid);
+	char *cuid = e_contact_get_const (contact, E_CONTACT_UID);
+
+	if (!cuid || !mcuid || !strlen (mcuid))
+		return;
+	if (!strcmp (cuid, mcuid))
+	        em_format_redraw((EMFormat *)empr->emfv->preview);
+}
+
+void em_presence_clear_map (MessageList *ml, EMIMPresence *empr)
+{
+	if (!empr)
+		return;
+	if (!empr->uidmap)
+		return;
+
+	g_list_foreach (empr->uidmap, free_imnode, NULL);
+        g_list_free (empr->uidmap);
+        empr->uidmap = NULL;
+}
+
+void em_impresence_query (MessageList *ml, const char *uid, EMIMPresence *empr)
+{
+	g_return_if_fail (empr != NULL);
+
+	if (!uid || !empr->emfv || !empr->emfv->folder)
+		return;
+
+	/* Check in cache */
+	if (em_impresence_get_contact_uid (empr, uid))
+		return;
+
+	EBook *book = e_book_new_default_addressbook(NULL);
+	if (!e_book_open (book, TRUE, NULL))
+		return;
+	
+	CamelMimeMessage *msg = camel_folder_get_message (empr->emfv->folder, uid, NULL);
+	
+	if (msg == NULL) 
+		return;
+
+	EMIMQuery *emimq = g_new0(EMIMQuery, 1);
+	emimq->empr = empr;
+	emimq->msgid = g_strdup(uid);
+
+	camel_internet_address_get(camel_mime_message_get_from(msg), 0 , (const char **) &(emimq->name), (const char **) &(emimq->email));
+
+	/* Start query */
+        emimq->query_tag = eab_name_and_email_query (book, emimq->name, emimq->email, query_cb, emimq);
+}
+
+static void
+query_cb (EBook *book, EBookStatus status, GList *contacts, gpointer closure)
+{
+        EMIMQuery *emimq = (EMIMQuery *)closure;
+
+	g_return_if_fail (emimq != NULL);
+	g_return_if_fail (emimq->empr != NULL);
+
+        if (status != E_BOOK_ERROR_OK)
+                return;
+
+        emimq->query_tag = 0;
+        
+	EMIMNode *node = g_new0(EMIMNode, 1);
+	if (contacts == NULL) {
+		/* Add to cache to signify that we have searched in the addressbook 
+		 * and found nothing */
+		node->cuid = g_strdup ("");
+		node->msgid = g_strdup (emimq->msgid);
+
+		add_to_cache(emimq->empr, node);
+		free_imquery(emimq);
+        } else {
+		/* Found some matches -- send a gaim query */
+		/* Without duplication the contact gets deleted after some time wtf???? */
+		EContact *contact = E_CONTACT(contacts->data);
+	
+		node->cuid = g_strdup (e_contact_get_const(contact, E_CONTACT_UID));
+		node->msgid = g_strdup (emimq->msgid);
+	
+		/* Add message id -- contact id to cache */
+		add_to_cache(emimq->empr, node);
+		
+		/* Send the query */
+		e_impresence_query (contact, EIMPRESENCE_CACHE);
+		free_imquery(emimq);
+        }
+}
+
+void
+free_imquery (EMIMQuery *emimq)
+{
+	g_return_if_fail(emimq != NULL);
+
+	if (emimq->msgid)
+		g_free(	emimq->msgid);
+	if (emimq->name)
+		g_free(	emimq->name);
+	if (emimq->email)
+		g_free(	emimq->email);
+
+	g_free (emimq);
+}
+
+void 
+add_to_cache (EMIMPresence *empr, EMIMNode *node)
+{
+	g_return_if_fail (empr != NULL);
+	g_return_if_fail (node != NULL);
+
+	GList *l;
+	int i;
+
+	empr->uidmap = g_list_append (empr->uidmap, node);
+
+	if (g_list_length(empr->uidmap) > empr->max_cache) {
+		for (i = 0; i < 0.9*empr->max_cache; i++) {
+			l = empr->uidmap;
+			free_imnode (l->data, NULL);
+			empr->uidmap = g_list_remove_link(empr->uidmap, empr->uidmap);
+			g_list_free_1(l); 
+		}
+	}
+}
+
+const char *
+em_impresence_get_contact_uid (EMIMPresence *empr, const char *msgid)
+{
+	if (empr == NULL)
+		return NULL;
+
+	GList *l = g_list_first(empr->uidmap);
+
+	while (l) {
+		EMIMNode *node = (EMIMNode *)l->data;
+		if (!g_strcasecmp(node->msgid, msgid))
+			return node->cuid;
+		l = l->next;
+	}
+	return NULL;
+}
+
+void
+free_imnode (gpointer data, gpointer user_data)
+{
+	EMIMNode *node = (EMIMNode *)data;
+	
+	g_return_if_fail(node != NULL);
+	
+	if (node->msgid)
+		g_free(node->msgid);
+	if (node->cuid)
+		g_free(node->cuid);
+	
+	g_free(node);
+}
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/mail/em-impresence.h evolution/mail/em-impresence.h
--- pristine/evolution/mail/em-impresence.h	1969-12-31 19:00:00.000000000 -0500
+++ evolution/mail/em-impresence.h	2004-09-06 01:46:37.000000000 -0400
@@ -0,0 +1,42 @@
+/*
+ *  Authors:  Praveen Kumar <praveen9 gmail com>
+ *
+ *  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., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <glib.h>
+#include "e-util/e-impresence.h"
+
+/* This is a cache of presence status of all the contacts in the addressbook 
+   It will be a global cache used by mail component, composer component, addressbook component  
+   to query the presence status based on contact uid's */
+
+typedef struct _EMIMPresence EMIMPresence;
+
+struct _EMIMPresence {
+	gboolean run_cache_thread;	/* Should we run the cache thread at start */
+	gint  max_cache;		/* Max cache items */
+	
+	GList *uidmap;			/* Cache presence status for contact uid's */
+	
+	EMFolderView *emfv;
+};
+
+EMIMPresence *em_impresence_new(EMFolderView *emfv);
+
+void em_impresence_query (MessageList *ml, const char *uid, EMIMPresence *empr);
+
+const char *em_impresence_get_contact_uid (EMIMPresence *empr, const char *msgid);
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/widgets/misc/Makefile.am evolution/widgets/misc/Makefile.am
--- pristine/evolution/widgets/misc/Makefile.am	2004-09-06 00:52:57.000000000 -0400
+++ evolution/widgets/misc/Makefile.am	2004-09-05 23:23:00.000000000 -0400
@@ -4,7 +4,9 @@
 	-DMAP_DIR=\""$(imagesdir)"\"					\
 	-DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\"			\
 	-DG_LOG_DOMAIN=__FILE__						\
-	$(SOURCE_SEL_CFLAGS)
+	$(SOURCE_SEL_CFLAGS)						\
+	$(GAIM_CFLAGS)
+
 
 privlib_LTLIBRARIES =	\
 	libemiscwidgets.la
@@ -46,7 +48,8 @@
 	e-task-bar.h				\
 	e-task-widget.h				\
 	e-title-bar.h				\
-	e-url-entry.h
+	e-url-entry.h				\
+	e-imsend.h
 
 libemiscwidgets_la_SOURCES =			\
 	$(MARSHAL_GENERATED)			\
@@ -77,7 +80,8 @@
 	e-task-bar.c				\
 	e-task-widget.c				\
 	e-title-bar.c				\
-	e-url-entry.c
+	e-url-entry.c				\
+	e-imsend.c
 
 MARSHAL_GENERATED = e-util-marshal.c e-util-marshal.h
 @EVO_MARSHAL_RULE@
@@ -90,7 +94,7 @@
 
 libemiscwidgets_la_LIBADD = $(top_builddir)/e-util/libeutil.la		\
 	$(top_builddir)/a11y/widgets/libevolution-widgets-a11y.la	\
-	$(SOURCE_SEL_LIBS)
+	$(SOURCE_SEL_LIBS) -levogaimremote
 
 noinst_LTLIBRARIES = libefilterbar.la
 
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/widgets/misc/e-imsend.c evolution/widgets/misc/e-imsend.c
--- pristine/evolution/widgets/misc/e-imsend.c	1969-12-31 19:00:00.000000000 -0500
+++ evolution/widgets/misc/e-imsend.c	2004-09-06 21:34:12.000000000 -0400
@@ -0,0 +1,279 @@
+/*
+ *  Authors:  Praveen Kumar <praveen9 gmail com>
+ *
+ *  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., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#include <evogaimremote.h>
+#include <util.h>
+
+#include <gtk/gtk.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtkbutton.h>
+#include <gtk/gtkwindow.h>
+#include <gtk/gtkdialog.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkhbox.h>
+#include <gtk/gtkvbox.h>
+#include <gtk/gtkimage.h>
+
+#include "e-imsend.h"
+
+typedef struct {
+	GtkWidget	*childbox;
+	GtkWidget	*presence_icon;
+	GtkWidget	*statusbox;
+	GtkWidget 	*screen_name;
+	GtkWidget 	*presence_status;
+	GtkWidget 	*sendimbutton;
+	
+	char 		*screenname;
+} IMSendDialogChild;
+
+typedef struct {
+	GtkWidget 	*imsend_dialog;
+	GtkWidget 	*user_hbox;
+	GtkWidget 	*user_name;
+	GtkWidget 	*closebutton;
+
+	GList *children;
+
+	int x, y;
+
+} IMSendDialog;
+
+IMSendDialog *imdlg = NULL;
+
+void send_im_message (GtkWidget *sendimbutton, IMSendDialog *imdlg);
+void quit_imdialog (GtkWidget *closebutton, IMSendDialog *imdlg);
+gboolean quit_imdialog_cb (GtkWidget *closebutton, GdkEventCrossing *ev, IMSendDialog *imdlg);
+IMSendDialogChild *create_child (const GRBuddyInfo *binfo);
+void sendim_cb (GtkWidget *sendimbutton, IMSendDialogChild *child);
+
+void
+quit_imdialog (GtkWidget *closebutton, IMSendDialog *imdlg)
+{
+	gtk_dialog_response (GTK_DIALOG(imdlg->imsend_dialog), 0);
+}
+
+gboolean
+quit_imdialog_cb (GtkWidget *widget, GdkEventCrossing *ev, IMSendDialog *imdlg)
+{
+	if ((ev->detail == GDK_NOTIFY_NONLINEAR) && (ev->mode == GDK_CROSSING_NORMAL))  
+		gtk_dialog_response (GTK_DIALOG(imdlg->imsend_dialog), 0);
+	return FALSE;
+}
+
+void
+sendim_cb (GtkWidget *sendimbutton, IMSendDialogChild *child)
+{
+	char *sendim_uri = g_strconcat("aim:goim?screenname=", child->screenname, NULL);
+	gaim_remote_handle_aim_uri (sendim_uri);
+	g_free (sendim_uri);
+	gtk_dialog_response (GTK_DIALOG(imdlg->imsend_dialog), 0);
+}
+
+void
+e_imsend_dialog (GdkEventButton *ev, const char *name, const GList *blist, gboolean show_all)
+{
+	int present = 0;
+
+	/* Find first online/offline screenname 
+	 * Verify that the contact is either online/offline */
+        GRBuddyInfo *b = NULL, *binfo = NULL;
+
+	GList *l = g_list_first(blist);
+
+	
+	while (l) {
+		b = l->data;
+		if (b->present == 1) {
+			binfo = b;
+			break;
+		}
+		else if (!binfo  && (b->present == 0))
+			binfo = b;
+		l = l->next;
+	}
+	
+	if (!binfo) {
+		g_warning ("Cannot show imsend popup for contact %s - could not detect offline/online presence", name);
+		return; 
+	}
+
+	imdlg = g_new0 (IMSendDialog, 1);
+
+	/* Create the dialog box and the VBox */
+	imdlg->imsend_dialog = gtk_dialog_new_with_buttons ("Send instant message",
+							NULL,
+							GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
+							NULL);
+	GtkWindow *window = GTK_WINDOW (imdlg->imsend_dialog);
+
+	gtk_window_set_decorated (window, FALSE);
+	gtk_window_set_skip_taskbar_hint (window, TRUE);
+	gtk_window_set_skip_pager_hint (window, TRUE);
+	
+	imdlg->user_hbox = gtk_hbox_new (FALSE, 10);
+
+	/* Create the user name label and close button */
+	imdlg->user_name = gtk_label_new ("");
+	
+	char *user_status_str = g_strdup_printf(("<b>%s</b>"), name);
+	gtk_label_set_markup (GTK_LABEL(imdlg->user_name), user_status_str);
+	g_free (user_status_str);
+
+	imdlg->closebutton = gtk_button_new ();
+	gtk_button_set_label (GTK_BUTTON (imdlg->closebutton), "");
+	  
+	if (GTK_BIN (imdlg->closebutton)->child)
+		      gtk_container_remove (GTK_CONTAINER (imdlg->closebutton), GTK_BIN (imdlg->closebutton)->child);
+	  
+	GtkWidget *cb_icon = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_BUTTON);
+	gtk_container_add (GTK_CONTAINER (imdlg->closebutton), cb_icon);
+			
+	/* Add them to box */
+	gtk_box_pack_start (GTK_BOX (imdlg->user_hbox), imdlg->user_name, TRUE, TRUE, 5);
+	gtk_box_pack_start (GTK_BOX (imdlg->user_hbox), imdlg->closebutton, FALSE, FALSE, 5);
+
+	/* Add this to dialog vbox */
+	gtk_box_pack_start (GTK_BOX (GTK_DIALOG(imdlg->imsend_dialog)->vbox), imdlg->user_hbox, TRUE, TRUE, 5);
+
+	/* Now create the children */
+	IMSendDialogChild *child;
+
+	l = g_list_first (blist);
+	while (l) {
+		binfo = l->data;
+		
+		if (!binfo)
+			continue;
+		
+		child = create_child (binfo);
+
+		if (!child)
+			continue;
+		
+		/* Connect the signal for sending message */
+		g_signal_connect (child->sendimbutton, "clicked", G_CALLBACK(sendim_cb), child);
+		
+		gtk_box_pack_start (GTK_BOX (GTK_DIALOG(imdlg->imsend_dialog)->vbox), gtk_hseparator_new(), TRUE, TRUE, 5);
+		gtk_box_pack_start (GTK_BOX (GTK_DIALOG(imdlg->imsend_dialog)->vbox), child->childbox, TRUE, TRUE, 5);
+		imdlg->children = g_list_append (imdlg->children, child);
+
+		l = l->next;
+
+		if (!show_all)
+			break;
+	}
+
+	g_signal_connect (imdlg->closebutton, "clicked", G_CALLBACK(quit_imdialog), imdlg);
+	g_signal_connect (imdlg->imsend_dialog, "leave-notify-event", G_CALLBACK(quit_imdialog_cb), imdlg);
+
+	gtk_widget_show_all (imdlg->imsend_dialog);
+
+	imdlg->x = ev->x_root; imdlg->y = ev->y_root;
+	gdk_window_move (GTK_WIDGET(imdlg->imsend_dialog)->window, ev->x_root, ev->y_root);
+	
+	int response = gtk_dialog_run (GTK_DIALOG(imdlg->imsend_dialog));
+
+	gtk_widget_destroy (imdlg->imsend_dialog);
+
+	/* Destroy IMSendDialogChild */
+	l = g_list_first (imdlg->children);
+	while (l) {
+		child = l->data;
+		if (child->screenname)
+			g_free (child->screenname);
+		g_free (child);
+		l = l->next;
+	}
+
+	g_list_free (imdlg->children);
+	g_free (imdlg);
+	imdlg = NULL;
+}
+
+IMSendDialogChild *
+create_child   (const GRBuddyInfo *binfo) 
+{
+	GdkPixbuf *bicon = NULL;
+	
+	IMSendDialogChild *child = g_new0 (IMSendDialogChild, 1);
+
+	child->screenname = g_strdup (binfo->name);
+
+	child->childbox = gtk_hbox_new (FALSE, 5);
+
+	if (binfo->ilen > 1) {
+		GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
+		gdk_pixbuf_loader_write (loader, binfo->icon, binfo->ilen, NULL);
+		bicon = gdk_pixbuf_loader_get_pixbuf (loader);
+		if (bicon)
+			bicon = gdk_pixbuf_scale_simple(bicon, 32, 32, GDK_INTERP_BILINEAR);
+                gdk_pixbuf_loader_close (loader, NULL);
+                g_object_unref (loader);
+	}
+
+	if (bicon) {
+		child->presence_icon = gtk_image_new_from_pixbuf (bicon);
+		gdk_pixbuf_unref (bicon);
+	} else
+		child->presence_icon = gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_LARGE_TOOLBAR);
+	
+	child->statusbox = gtk_vbox_new (TRUE, 5);
+
+	child->screen_name = gtk_label_new ("");
+	gtk_misc_set_alignment (GTK_MISC (child->screen_name), 0.0, 0.5);
+	child->presence_status = gtk_label_new ("");
+	gtk_misc_set_alignment (GTK_MISC (child->presence_status), 0.0, 0.5);
+
+	char *screen_name_str = g_strdup_printf (("Screen Name: <b>%s</b> "), binfo->name);
+        gtk_label_set_markup (GTK_LABEL(child->screen_name), screen_name_str);
+	g_free (screen_name_str);
+	
+        char *imclient, *presence_status_str;
+	
+	if (binfo->protocol_id)
+		imclient = g_strdup (gaim_remote_get_protocol (binfo->protocol_id));
+	else
+		imclient = g_strdup ("");
+	
+	if (binfo->present == 0)
+		presence_status_str = g_strdup_printf ("<b>Offline</b>, %s", imclient);
+	else if ((binfo->present == 1) && (binfo->idle < 1))
+		presence_status_str = g_strdup_printf ("<b>Online</b>, %s", imclient);
+	else
+		presence_status_str = g_strdup_printf ("<b>Online</b> (idle %s), %s", gaim_remote_seconds_to_string(binfo->idle), imclient);
+	
+	g_free (imclient);
+	
+	gtk_label_set_markup (GTK_LABEL(child->presence_status), presence_status_str);
+	g_free (presence_status_str);
+
+	gtk_box_pack_start (GTK_BOX (child->statusbox), child->screen_name, TRUE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (child->statusbox), child->presence_status, TRUE, TRUE, 0);
+
+	child->sendimbutton = gtk_button_new_with_label ("Send instant message");
+
+	/* Now pack all of the above onto the hbox */
+	gtk_box_pack_start (GTK_BOX (child->childbox), child->presence_icon, TRUE, TRUE, 5);
+	gtk_box_pack_start (GTK_BOX (child->childbox), child->statusbox, TRUE, TRUE, 5);
+	gtk_box_pack_start (GTK_BOX (child->childbox), child->sendimbutton, FALSE, FALSE, 5);
+
+	return child;	
+}
diff -U 3 -H -B -d -i -r -N -- pristine/evolution/widgets/misc/e-imsend.h evolution/widgets/misc/e-imsend.h
--- pristine/evolution/widgets/misc/e-imsend.h	1969-12-31 19:00:00.000000000 -0500
+++ evolution/widgets/misc/e-imsend.h	2004-09-06 01:47:43.000000000 -0400
@@ -0,0 +1,21 @@
+/*
+ *  Authors:  Praveen Kumar <praveen9 gmail com>
+ *
+ *  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., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+void e_imsend_dialog (GdkEventButton *ev, const char *name, const GList *blist, gboolean show_all);
diff -U 3 -H -B -d -i -r -N -- pristine/gaim-0.82.1/plugins/gaim-remote/Makefile.am gaim-0.82.1/plugins/gaim-remote/Makefile.am
--- pristine/gaim-0.82.1/plugins/gaim-remote/Makefile.am	2004-09-06 02:40:35.000000000 -0400
+++ gaim-0.82.1/plugins/gaim-remote/Makefile.am	2004-09-06 02:51:09.000000000 -0400
@@ -1,16 +1,20 @@
-grincludedir = $(includedir)/gaim
+grincludedir = $(includedir)/gaim/gaim-remote
 
 plugindir = $(libdir)/gaim
 
-lib_LTLIBRARIES = libgaim-remote.la
+lib_LTLIBRARIES = libgaim-remote.la libevogaimremote.la
 
 grinclude_HEADERS = \
 	remote-socket.h \
-	remote.h
+	remote.h \
+	evogaimremote.h
 
 libgaim_remote_la_SOURCES = remote-socket.c
 libgaim_remote_la_LIBADD = $(GLIB_LIBS)
 
+libevogaimremote_la_SOURCES = evogaimremote.c
+libevogaimremote_la_LIBADD = $(GLIB_LIBS) $(EVOLUTION_ADDRESSBOOK_LIBS) libgaim-remote.la
+
 gaim_remote_la_LDFLAGS = -module -avoid-version
 
 if PLUGINS
@@ -18,7 +22,7 @@
 plugin_LTLIBRARIES = gaim-remote.la
 
 gaim_remote_la_SOURCES = remote.c
-gaim_remote_la_LIBADD = libgaim-remote.la $(GLIB_LIBS)
+gaim_remote_la_LIBADD = libgaim-remote.la -lgthread-2.0 $(GLIB_LIBS)
 
 endif
 
@@ -28,5 +32,6 @@
 	-I$(top_srcdir) \
 	-I$(top_srcdir)/plugins \
 	-I$(top_srcdir)/src \
+	$(EVOLUTION_ADDRESSBOOK_CFLAGS) \
 	$(GTK_CFLAGS) \
 	$(DEBUG_CFLAGS)
diff -U 3 -H -B -d -i -r -N -- pristine/gaim-0.82.1/plugins/gaim-remote/evogaimremote.c gaim-0.82.1/plugins/gaim-remote/evogaimremote.c
--- pristine/gaim-0.82.1/plugins/gaim-remote/evogaimremote.c	1969-12-31 19:00:00.000000000 -0500
+++ gaim-0.82.1/plugins/gaim-remote/evogaimremote.c	2004-09-07 01:35:52.000000000 -0400
@@ -0,0 +1,348 @@
+/*
+ * Remote control plugin for Gaim
+ *
+ * Copyright (C) 2004, Praveen Kumar <praveen9 gmail com>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <glib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "evogaimremote.h"
+
+#define	MAX_IM_SLOTS	6
+
+typedef struct {
+	EContact *contact;
+	GRCallbackFunc callback;
+	gpointer data;
+	gint waitfor;
+	GList *buddylist;
+} GRQueryInfo;
+
+typedef struct {
+	GRQueryInfo *qinfo;
+	GIOChannel *channel;
+	guint inpa;
+} GRQuery;
+
+static struct {
+	EContactField  field;
+	gchar         *protocol_id;
+	gchar         *pretty_name;
+} 
+
+im_service [] =
+{
+	{ E_CONTACT_IM_AIM,	  "prpl-aim",		"AIM"       },
+	{ E_CONTACT_IM_JABBER,    "prpl-jabber",	"Jabber"    },
+	{ E_CONTACT_IM_YAHOO,     "prpl-yahoo",		"Yahoo"     },
+	{ E_CONTACT_IM_MSN,       "prpl-msn",		"MSN"       },
+	{ E_CONTACT_IM_ICQ,       "prpl-icq",		"ICQ"       },
+	{ E_CONTACT_IM_GROUPWISE, "prpl-novell",	"GroupWise" }
+};
+
+gboolean gaim_remote_read_response (GIOChannel *source, GIOCondition cond, gpointer data);
+void _gaim_remote_get_buddy_info_name (const char *screenname, const char *protocol_id, GRQueryInfo *qinfo);
+
+void gaim_remote_free_buddy_info (GRBuddyInfo *buddy)
+{
+	if (!buddy)
+		return;
+
+	if (buddy->name)
+		g_free(buddy->name);
+	if (buddy->alias)
+		g_free(buddy->alias);
+	if (buddy->server_alias)
+		g_free(buddy->server_alias);
+	if (buddy->icon)
+		g_free(buddy->icon);
+	if (buddy->protocol_id)
+		g_free(buddy->protocol_id);
+	
+	g_free(buddy);
+
+	return;
+}
+
+void gaim_remote_get_buddy_info (EContact *contact, GRCallbackFunc cbfunc, gpointer data)
+{
+	g_return_if_fail (contact != NULL);
+	g_return_if_fail (cbfunc != NULL);
+
+	GList *im_attr_list;
+	GList *l;
+    
+	gint   record_n;
+	gint   i;
+	gboolean found = FALSE;
+
+	for (record_n = 1, i = 0; i < G_N_ELEMENTS (im_service); i++) {
+		im_attr_list = e_contact_get_attributes (contact, im_service [i].field);
+		
+		if (g_list_length(im_attr_list) < 1)
+			continue;
+		
+		GRQueryInfo *qinfo = g_new(GRQueryInfo, 1);
+		g_object_ref(contact);
+		qinfo->contact = contact;
+		qinfo->callback = cbfunc;
+		qinfo->data = data;
+		qinfo->buddylist = NULL;
+		qinfo->waitfor = g_list_length(im_attr_list);
+
+		for (l = im_attr_list; l ; l = g_list_next (l)) {
+			EVCardAttribute *attr = l->data;
+			gchar           *im_name = e_vcard_attribute_get_value (attr);
+			
+			found = TRUE;
+			_gaim_remote_get_buddy_info_name(im_name, im_service[i].protocol_id, qinfo); 
+			record_n++;
+		}
+	}
+
+	/* If we could not find even one screen name from contact return an empty buddylist */
+	if (!found) {
+		g_object_ref (contact);
+		(* cbfunc)(contact, NULL, data);
+		g_object_unref (contact);
+	}
+		
+	return;
+}
+
+void gaim_remote_get_buddy_info_name (const char *screenname, const char *protocol, GRCallbackFunc cbfunc, gpointer data)
+{
+	g_return_if_fail (screenname != NULL);
+	g_return_if_fail (cbfunc != NULL);
+
+	GRQueryInfo *qinfo = g_new(GRQueryInfo, 1);
+	qinfo->contact = NULL;
+	qinfo->callback = cbfunc;
+	qinfo->data = data;
+	qinfo->waitfor = 1;
+
+	_gaim_remote_get_buddy_info_name (screenname, protocol, qinfo);
+}
+
+void _gaim_remote_get_buddy_info_name (const char *screenname, const char *protocol, GRQueryInfo *qinfo)
+{
+	GRQuery *query = g_new0(GRQuery, 1);
+	query->qinfo = qinfo;
+
+	/* Find the status of the user */
+	int fd = 0;
+	GaimRemotePacket *p = NULL;
+	
+	fd = gaim_remote_session_connect (0);
+	if (fd<0) {
+		/* Call the callback function */
+		(* qinfo->callback)(qinfo->contact, NULL, qinfo->data);
+
+		/* We are done with the query info struct delete it */
+		g_free(qinfo);
+		return;
+	}
+	p = gaim_remote_packet_new (CUI_TYPE_REMOTE, CUI_REMOTE_STATE);
+	gaim_remote_packet_append_string (p, screenname);
+	if (protocol)
+		gaim_remote_packet_append_string (p, protocol);
+	gaim_remote_session_send_packet (fd, p);
+
+	query->channel = g_io_channel_unix_new(fd);
+	query->inpa = g_io_add_watch (query->channel, G_IO_IN | G_IO_HUP | G_IO_ERR, gaim_remote_read_response, query);
+	g_io_channel_unref (query->channel);
+
+	gaim_remote_packet_free(p);
+}
+
+const char* gaim_remote_get_protocol (char *protocol_id)
+{
+	int i;
+	
+	if (!protocol_id)
+		return NULL;
+        
+	for (i = 0; i < G_N_ELEMENTS (im_service); i++) {
+		if (!strcmp(protocol_id, im_service [i].protocol_id))
+		{
+			return(im_service [i].pretty_name); 
+			break;
+		}
+	}
+	
+	return NULL;
+}
+
+gboolean gaim_remote_read_response (GIOChannel *source, GIOCondition cond, gpointer data)
+{
+	int fd = g_io_channel_unix_get_fd(source);
+
+	GError *error = NULL;
+	GRBuddyInfo *buddyinfo = NULL;
+	int i = 0;
+ 
+	GRQuery *query = (GRQuery *)data;
+	GRQueryInfo *qinfo = query->qinfo;
+	
+	if (!(cond & G_IO_IN))
+		goto shutdown_query;
+
+	GaimRemotePacket *response = gaim_remote_session_read_packet (fd);
+
+	if ((!response) || (response->numpackets != 9))
+		goto continue_loop;
+
+	/* Create GRBuddy info from response */
+	GaimRemotePacketData *packet;
+	GList *plist = g_list_first(response->packets);
+	buddyinfo = g_new(GRBuddyInfo, 1);
+
+	for (i = 0;  plist && (i < 9) ; i++, plist = g_list_next(plist)) {
+		packet = (GaimRemotePacketData *)plist->data;
+		switch(i) {
+			case 0:
+				buddyinfo->name = g_strndup (packet->data, packet->length);
+				break;
+			case 1:
+				buddyinfo->alias = g_strndup (packet->data, packet->length);
+				break;
+			case 2:
+				buddyinfo->server_alias = g_strndup (packet->data, packet->length);
+				break;
+			case 3:
+				buddyinfo->present = strtol (packet->data, NULL, 10);
+				break;
+			case 4:
+				buddyinfo->evil = strtol (packet->data, NULL, 10);
+				break;
+			case 5:
+				buddyinfo->signon = strtol (packet->data, NULL, 10);
+				break;
+			case 6:
+				buddyinfo->idle = strtol (packet->data, NULL, 10);
+				break;
+			case 7:
+				buddyinfo->icon = g_malloc (packet->length);
+				memcpy (buddyinfo->icon, packet->data, packet->length);
+				buddyinfo->ilen = packet->length;
+				break;
+			case 8:
+				buddyinfo->protocol_id = g_strndup (packet->data, packet->length);
+				break;
+			default:
+				break;
+			}	
+	}
+
+    continue_loop:
+	gaim_remote_packet_free (response);
+
+	if (!qinfo) {
+		if (buddyinfo)
+			gaim_remote_free_buddy_info (buddyinfo);
+		goto shutdown_query;
+	}
+	
+	/* Update waitfor */
+	qinfo->waitfor--;
+
+	if ((buddyinfo) && (j == 9) && (buddyinfo->present != 2)) { /* buddyinfo is valid and we know the buddy*/
+			qinfo->buddylist = g_list_append (qinfo->buddylist, buddyinfo);
+	} else if (buddyinfo)
+        	gaim_remote_free_buddy_info (buddyinfo);
+
+	if (qinfo->waitfor == 0) {
+		/* Call the callback function */
+		(* qinfo->callback)(qinfo->contact, qinfo->buddylist, qinfo->data);
+
+		g_object_unref(qinfo->contact);
+
+		/* We are done with the query info struct delete it */
+		g_free(qinfo);
+	}
+	
+    shutdown_query:
+	
+	/* Shutdown the channel */
+	g_io_channel_shutdown(query->channel, TRUE, &error);
+	if (error) {
+		g_error_free(error);
+		error = NULL;
+	}
+	g_source_remove(query->inpa);
+	g_free(query);
+
+	return FALSE;
+}
+
+void 
+gaim_remote_handle_aim_uri (char *uri)
+{
+        int fd = 0;
+        GaimRemotePacket *p = NULL;
+        fd = gaim_remote_session_connect(0);
+        if (fd<0) {
+                return;
+        }
+        p = gaim_remote_packet_new (CUI_TYPE_REMOTE, CUI_REMOTE_URI);
+        gaim_remote_packet_append_string (p, uri);
+        gaim_remote_session_send_packet (fd, p);
+        close (fd);
+        gaim_remote_packet_free (p);
+        return;
+}
+
+/* Copied from Gaim -- thanks to Gaim devs. */
+char *
+gaim_remote_seconds_to_string(guint secs)
+{
+        GString *gstr;
+        const char *prefix = "";
+        guint days, hrs, mins;
+
+	secs = time(NULL) - secs;
+        gstr = g_string_new("");
+
+        if (secs < 60)
+        {
+                g_string_append_printf (gstr, "%d %s", secs, (secs > 1) ? "seconds" : "second");
+                return g_string_free (gstr, FALSE);
+        }
+
+        days = secs / (60 * 60 * 24);
+        secs = secs % (60 * 60 * 24);
+        hrs  = secs / (60 * 60);
+        secs = secs % (60 * 60);
+        mins = secs / 60;
+	secs = secs % 60;
+        if (days > 0) {
+               g_string_append_printf (gstr, "%d %s", days, (days > 1) ? "days" : "day");
+               prefix = ", ";
+        }
+
+        if (hrs > 0) {
+                g_string_append_printf (gstr, "%s%d %s", prefix, hrs, (hrs > 0) ? "hours" : "hour");
+                prefix = ", ";
+        }
+        if (mins > 0) {
+		g_string_append_printf (gstr, "%s%d %s", prefix, mins, (mins > 0) ? "minutes" : "minute");
+        }
+	return g_string_free (gstr, FALSE);
+}
diff -U 3 -H -B -d -i -r -N -- pristine/gaim-0.82.1/plugins/gaim-remote/evogaimremote.h gaim-0.82.1/plugins/gaim-remote/evogaimremote.h
--- pristine/gaim-0.82.1/plugins/gaim-remote/evogaimremote.h	1969-12-31 19:00:00.000000000 -0500
+++ gaim-0.82.1/plugins/gaim-remote/evogaimremote.h	2004-09-06 03:00:07.000000000 -0400
@@ -0,0 +1,61 @@
+/*
+ * Remote control plugin for Gaim
+ *
+ * Copyright (C) 2004, Praveen Kumar <praveen9 gmail com>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <gaim-remote/remote.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <libebook/e-contact.h>
+#include <libebook/e-book.h>
+
+/* This is a part of the gaim buddy structure returned in case of buddy related queries */
+
+typedef struct {
+	char *name;		/* The screen name of the buddy  */
+	char *alias;		/* The user-set alias of the buddy */
+	char *server_alias;	/* The server specified alias of the buddy */
+	int present;		/* = 0, buddy is offline; = 1, buddy is online; 
+				   = 2, buddy has recently signed on */
+	int evil;		/* the warning level */
+	long int signon;	/* the signon time */
+	int idle;		/* the idle time for buddy */
+	gpointer icon;		/* The icon data */
+	int ilen;		/* the icon data length  */
+	char *protocol_id;	/* the protocol -- this is the protocol_id
+				   in the GaimAccount struct */
+} GRBuddyInfo;
+
+typedef void (* GRCallbackFunc) (EContact *, GList *buddylist, gpointer data);
+
+/* This will find out all the IM accounts and return the buddy info for the first online 
+ * account/first offline account or NULL otherwise */ 		 
+void gaim_remote_get_buddy_info (EContact *contact, GRCallbackFunc cbfunc, gpointer data);
+
+/* Return the buddy info corresponding to screen name */
+void gaim_remote_get_buddy_info_name (const char *screenname, const char *protocol_id, GRCallbackFunc cbfunc, gpointer data);
+
+void gaim_remote_handle_aim_uri (char *uri);
+
+void gaim_remote_free_buddy_info (GRBuddyInfo *buddy);
+
+/* This will convert protocol id to string like from prpl-yahoo to "Yahoo" */
+const char *gaim_remote_get_protocol (char *protocol_id);
+
+char *gaim_remote_seconds_to_string (guint seconds);
diff -U 3 -H -B -d -i -r -N -- pristine/gaim-0.82.1/plugins/gaim-remote/remote-socket.c gaim-0.82.1/plugins/gaim-remote/remote-socket.c
--- pristine/gaim-0.82.1/plugins/gaim-remote/remote-socket.c	2004-09-06 02:40:35.000000000 -0400
+++ gaim-0.82.1/plugins/gaim-remote/remote-socket.c	2004-09-06 02:55:25.000000000 -0400
@@ -37,8 +37,18 @@
 void
 gaim_remote_session_send_packet(int fd, GaimRemotePacket *p)
 {
+	GaimRemotePacketData *packet;
+	GList *plist;
+
 	int len = sizeof(p->type) + sizeof(p->subtype) +
-	          sizeof(p->length) + p->length;
+	          sizeof(p->numpackets);
+
+	for (plist = p->packets; plist != NULL; plist = plist->next)
+	{
+		packet = (GaimRemotePacketData *)plist->data;
+		len += (sizeof(packet->length) + packet->length);
+	}
+	
 	char *pack = g_malloc(len);
 	char *a = pack;
 
@@ -46,59 +56,84 @@
 	a = a + sizeof(p->type);
 	memcpy (a, &(p->subtype), sizeof(p->subtype));
 	a = a + sizeof(p->subtype);
-	memcpy (a, &(p->length), sizeof(p->length));
-	a = a + sizeof(p->length);
-	memcpy (a, p->data, p->length);
-	write(fd, pack, len);
+	memcpy (a, &(p->numpackets), sizeof(p->numpackets));
+	a = a + sizeof(p->numpackets);
+	
+	for (plist = p->packets; plist != NULL; plist = plist->next)
+	{
+		packet = (GaimRemotePacketData *)plist->data;
+		memcpy (a, &(packet->length), sizeof(packet->length));
+		a = a + sizeof(packet->length);
+		memcpy (a, packet->data, packet->length);
+		a = a + packet->length;
+	}
+
+	if (write(fd, pack, len) == -1)
+		g_warning("There was an error sending the packet\n");
 	g_free(pack);
 }
 
 void
 gaim_remote_packet_append_string(GaimRemotePacket *p, char *str)
 {
-	int len = p->length + strlen(str);
+	GaimRemotePacketData *packet = g_new0(GaimRemotePacketData, 1);
+	
+	int len = strlen(str);
 	char *k = g_malloc(len);
 
-	memcpy(k, p->data, p->length);
-	memcpy(k + p->length, str, strlen(str));
+	memcpy(k, str, strlen(str));
 
-	if (p->data)
-		g_free(p->data);
+	packet->data = k;
+	packet->length = len;
 
-	p->data = k;
-	p->length = len;
+	p->packets = g_list_append(p->packets, packet);
+	p->numpackets ++;
+}
+
+void 
+gaim_remote_packet_append_int(GaimRemotePacket *p, long int ival)
+{
+	GaimRemotePacketData *packet = g_new0(GaimRemotePacketData, 1);
+	
+	char *str = g_strdup_printf("%ld", ival);
+	
+	packet->data = str;
+	packet->length = strlen(str);
+	
+	p->packets = g_list_append(p->packets, packet);
+	p->numpackets ++;
 }
 
 void
 gaim_remote_packet_append_char(GaimRemotePacket *p, char c)
 {
-	int len = p->length + sizeof(char);
+	GaimRemotePacketData *packet = g_new0(GaimRemotePacketData, 1);
+	
+	int len = sizeof(char);
 	char *k = g_malloc(len);
 
-	memcpy(k, p->data, p->length);
-	k[p->length] = c;
-
-	if (p->data)
-		g_free(p->data);
+	k[0] = c;
+	packet->data = k;
+	packet->length = len;
 
-	p->data = k;
-	p->length = len;
+	p->packets = g_list_append(p->packets, packet);
+	p->numpackets ++;
 }
 
 void
 gaim_remote_packet_append_raw(GaimRemotePacket *p, char *str, int len)
 {
-	int lent = p->length + len;
-	char *k = g_malloc(lent);
+	GaimRemotePacketData *packet = g_new0(GaimRemotePacketData, 1);
+	
+	char *k = g_malloc(len);
 
-	memcpy(k, p->data, p->length);
-	memcpy(k + p->length, str, len);
+	memcpy(k, str, len);
 
-	if (p->data)
-		g_free(p->data);
+	packet->data = k;
+	packet->length = len;
 
-	p->data = k;
-	p->length = lent;
+	p->packets = g_list_append(p->packets, packet);
+	p->numpackets ++;
 }
 
 GaimRemotePacket *
@@ -107,22 +142,34 @@
 	GaimRemotePacket *p = g_new0(GaimRemotePacket, 1);
 	p->type = type;
 	p->subtype = subtype;
-	p->length = 0;
-	p->data = NULL;
+	p->numpackets = 0;
+	p->packets = NULL;
 	return p;
 }
 
 void
 gaim_remote_packet_free(GaimRemotePacket *p)
 {
-	if (p->data)
-		g_free(p->data);
+	GList *plist;
+	GaimRemotePacketData *packet;
+
+	if (!p)
+		return;
+	for (plist = p->packets; plist != NULL; plist = plist->next)
+	{
+		packet = (GaimRemotePacketData *)plist->data;
+		if (packet->data)
+			g_free(packet->data);
+	}
+	
+	g_list_free(p->packets);
 	g_free(p);
 }
 
 GaimRemotePacket *
 gaim_remote_session_read_packet(int fd)
 {
+	int i;
 	GaimRemotePacket *p = g_new0(GaimRemotePacket, 1);
 	char *data = NULL;
 
@@ -136,22 +183,44 @@
 		return NULL;
 	}
 
-	if (!(read(fd, &p->length, sizeof(p->length)))) {
+	if (!(read(fd, &p->numpackets, sizeof(p->numpackets)))) {
 		g_free(p);
 		return NULL;
 	}
 	
-	if (p->length) {
-		data = g_malloc(p->length);
-
-		if (!(read(fd, data, p->length))) {
-			g_free(p);
-			return NULL;
+	if (p->numpackets > 0) {
+		for (i = 0; i < p->numpackets; i++)
+		{
+			GaimRemotePacketData *packet = g_new0(GaimRemotePacketData, 1);
+	
+			if (!(read(fd, &packet->length, sizeof(packet->length)))) {
+				g_free(packet);
+				gaim_remote_packet_free(p);
+				return NULL;
+			}
+	
+			if (packet->length != 0)
+			{	
+				data = g_malloc(packet->length);
+	
+				if (!(read(fd, data, packet->length))) {
+					g_free(packet);
+					gaim_remote_packet_free(p);
+					return NULL;
+				}
+				packet->data = g_malloc(packet->length + 1);
+				memcpy(packet->data, data, packet->length);
+				*(packet->data + packet->length) = '\0';
+				g_free(data);
+			}
+			else {
+				packet->data = NULL;
+			}
+			
+			p->packets = g_list_append(p->packets, packet);
 		}
 	}
 
-	p->data = data;
-
 	return p;
 }
 
diff -U 3 -H -B -d -i -r -N -- pristine/gaim-0.82.1/plugins/gaim-remote/remote-socket.h gaim-0.82.1/plugins/gaim-remote/remote-socket.h
--- pristine/gaim-0.82.1/plugins/gaim-remote/remote-socket.h	2004-09-06 02:40:35.000000000 -0400
+++ gaim-0.82.1/plugins/gaim-remote/remote-socket.h	2004-09-06 02:55:32.000000000 -0400
@@ -25,11 +25,16 @@
 
 typedef struct
 {
-	unsigned char type;
-	unsigned char subtype;
 	unsigned long length;
 	char *data;
+} GaimRemotePacketData;
 
+typedef struct
+{
+	unsigned char type;
+	unsigned char subtype;
+	unsigned long numpackets;
+	GList *packets;
 } GaimRemotePacket;
 
 void gaim_remote_session_send_packet(int fd, GaimRemotePacket *packet);
@@ -41,6 +46,7 @@
 void gaim_remote_packet_free(GaimRemotePacket *p);
 void gaim_remote_packet_append_string(GaimRemotePacket *p, char *str);
 void gaim_remote_packet_append_char(GaimRemotePacket *p, char c);
+void gaim_remote_packet_append_int(GaimRemotePacket *p, long int ival);
 void gaim_remote_packet_append_raw(GaimRemotePacket *p, char *str, int len);
 
 #endif /* _GAIM_SOCKET_H_ */
diff -U 3 -H -B -d -i -r -N -- pristine/gaim-0.82.1/plugins/gaim-remote/remote.c gaim-0.82.1/plugins/gaim-remote/remote.c
--- pristine/gaim-0.82.1/plugins/gaim-remote/remote.c	2004-09-06 02:40:35.000000000 -0400
+++ gaim-0.82.1/plugins/gaim-remote/remote.c	2004-09-07 01:41:21.000000000 -0400
@@ -37,6 +37,13 @@
 #include "prpl.h"
 #include "notify.h"
 
+
+/* Added fo libgremote */
+#include "account.h"
+#include "connection.h"
+#include "blist.h"
+#include "util.h"
+
 /* XXX */
 #include "away.h"
 #include "gtkconv.h"
@@ -48,17 +55,15 @@
 
 #define REMOTE_PLUGIN_ID "gtk-remote"
 
-struct UI {
-	GIOChannel *channel;
-	guint inpa;
-};
-
 #ifndef _WIN32
 static gint UI_fd = -1;
 static guint watcher = 0;
 #endif
 static int gaim_session = 0;
-static GSList *uis = NULL;
+
+gpointer start_remote_thread(gpointer data);
+gboolean wait_for_write (int fd);
+void send_packet (int fd, GaimRemotePacket *p);
 
 /* AIM URI's ARE FUN :-D */
 static const char *
@@ -300,7 +305,7 @@
 
 #ifndef _WIN32
 static void
-meta_handler(struct UI *ui, guchar subtype, gchar *data)
+meta_handler(int fd, guchar subtype, gchar *data)
 {
 	GaimRemotePacket *p;
 	GError *error = NULL;
@@ -308,25 +313,13 @@
 	case CUI_META_LIST:
 		break;
 	case CUI_META_QUIT:
-		while (uis) {
-			ui = uis->data;
-			uis = g_slist_remove(uis, ui);
-			g_io_channel_shutdown(ui->channel, TRUE, &error);
-			g_source_remove(ui->inpa);
-			g_free(ui);
-		}
 		g_timeout_add(0, gaim_core_quit_cb, NULL);
 		break;
 	case CUI_META_DETACH:
-		uis = g_slist_remove(uis, ui);
-		g_io_channel_shutdown(ui->channel, TRUE, &error);
-		g_source_remove(ui->inpa);
-		g_free(ui);
 		break;
 	case CUI_META_PING:
 		p = gaim_remote_packet_new(CUI_TYPE_META, CUI_META_ACK);
-		gaim_remote_session_send_packet(g_io_channel_unix_get_fd(ui->channel),
-										p);
+		send_packet (fd,p);
 		gaim_remote_packet_free(p);
 		break;
 	default:
@@ -340,7 +333,7 @@
 }
 
 static void
-plugin_handler(struct UI *ui, guchar subtype, gpointer data)
+plugin_handler(int fd, guchar subtype, gpointer data)
 {
 #ifdef GAIM_PLUGINS
 	guint id;
@@ -369,8 +362,23 @@
 #endif
 }
 
+gboolean
+handle_away (gpointer data)
+{
+	GSList* l;
+        const char* default_away_name = gaim_prefs_get_string("/core/away/default_message");
+
+        for(l = away_messages; l; l = l->next) {
+            if(!strcmp(default_away_name, ((struct away_message *)l->data)->name)) {
+                do_away_message(NULL, l->data);
+                break;
+            }
+        }
+	return FALSE;
+}
+
 static void
-user_handler(struct UI *ui, guchar subtype, gchar *data)
+user_handler(int fd, guchar subtype, gchar *data)
 {
 	guint id;
 	GaimAccount *account;
@@ -396,17 +404,7 @@
 		/* don't need to do anything here because the UI will get updates from other handlers */
 		break;
        case CUI_USER_AWAY:
-                {
-                    GSList* l;
-                    const char* default_away_name = gaim_prefs_get_string("/core/away/default_message");
-
-                    for(l = away_messages; l; l = l->next) {
-                        if(!strcmp(default_away_name, ((struct away_message *)l->data)->name)) {
-                            do_away_message(NULL, l->data);
-                            break;
-                        }
-                    }
-                }
+		g_timeout_add (1, handle_away, NULL);
                 break;
        case CUI_USER_BACK:
                 do_im_back(NULL, NULL);
@@ -419,7 +417,7 @@
 }
 
 static void
-message_handler(struct UI *ui, guchar subtype, gchar *data)
+message_handler(int fd, guchar subtype, gchar *data)
 {
 	switch (subtype) {
 	case CUI_MESSAGE_LIST:
@@ -467,33 +465,32 @@
 	}
 }
 
-static gint
-gaim_recv(GIOChannel *source, gchar *buf, gint len)
+gboolean 
+handle_uri (gpointer data)
 {
-	gint total = 0;
-	guint cur;
-
-	GError *error = NULL;
-
-	while (total < len) {
-		if (g_io_channel_read_chars(source, buf + total, len - total, (gsize *) &cur, &error) != G_IO_STATUS_NORMAL) {
-			if (error)
-				g_error_free(error);
-			return -1;
-		}
-		if (cur == 0)
-			return total;
-		total += cur;
-	}
-
-	return total;
+	const char *resp;
+	resp = gaim_remote_handle_uri((char *)data);
+	return FALSE;
 }
 
 static void
-remote_handler(struct UI *ui, guchar subtype, gchar *data, int len)
+remote_handler(int fd, guchar subtype, GaimRemotePacket *p_in)
 {
-	const char *resp;
 	char *send;
+	char *protocol_id = NULL;
+	char *protocol_req = NULL;
+	
+	GList *connections = NULL;
+	GaimAccount *account = NULL;
+	GaimConnection *connection = NULL;
+	GaimRemotePacket *p = NULL;
+	GaimBuddy *buddy = NULL;
+	
+	GaimRemotePacketData *pd = g_list_first(p_in->packets)->data;
+	gchar *data = pd->data;
+	int len = pd->length;
+
+
 	switch (subtype) {
 	case CUI_REMOTE_CONNECTIONS:
 		break;
@@ -501,10 +498,72 @@
 		send = g_malloc(len + 1);
 		memcpy(send, data, len);
 		send[len] = 0;
-		resp = gaim_remote_handle_uri(send);
+		printf ("The uri is %s\n", send);
+		g_timeout_add (1, handle_uri, send);
 		g_free(send);
 		/* report error */
 		break;
+	case CUI_REMOTE_STATE:
+		/* Check if we have also got a request for checking protocol id */
+		if (g_list_length (p_in->packets) == 2) {
+			pd = g_list_last(p_in->packets)->data;
+			protocol_req = pd->data;
+		}
+
+		for (connections = gaim_connections_get_all(); connections != NULL; connections = connections->next) {
+			connection = (GaimConnection *)connections->data;
+			account = gaim_connection_get_account(connection);
+			buddy = gaim_find_buddy(account, data);
+			if (buddy != NULL) {
+				protocol_id = g_strdup (gaim_account_get_protocol_id(account));
+				if (!strcmp(protocol_id, "prpl-oscar")) {
+					g_free (protocol_id);
+					GaimPluginProtocolInfo *prpl_info;
+					prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(connection->prpl);
+					
+					if (!strcmp("aim", prpl_info->list_icon(account, buddy)))
+						protocol_id = g_strdup ("prpl-aim");
+					else
+						protocol_id = g_strdup ("prpl-icq");
+				}
+					
+				if (!protocol_req || (protocol_req && !strcmp(protocol_id, protocol_req)))
+					break;
+				else
+					buddy = NULL;
+			}
+		}
+		
+		p = gaim_remote_packet_new(CUI_TYPE_REMOTE, CUI_REMOTE_STATE);
+		gaim_remote_packet_append_string(p, data);
+		if (buddy) {
+			if (buddy->alias)
+				gaim_remote_packet_append_string(p, buddy->alias);
+			else
+				gaim_remote_packet_append_string(p, "");
+			if (buddy->server_alias)
+				gaim_remote_packet_append_string(p, buddy->server_alias);
+			else
+				gaim_remote_packet_append_string(p, "");
+			gaim_remote_packet_append_int(p, buddy->present);
+			gaim_remote_packet_append_int(p, buddy->evil);
+			gaim_remote_packet_append_int(p, buddy->signon);
+			gaim_remote_packet_append_int(p, buddy->idle);
+			if (buddy->icon && buddy->icon->data)
+				gaim_remote_packet_append_raw(p, buddy->icon->data, buddy->icon->len);
+			else
+				gaim_remote_packet_append_string(p, "");
+			if (protocol_id)
+				gaim_remote_packet_append_string(p, protocol_id);
+			else
+				gaim_remote_packet_append_string(p, "");
+		}
+		send_packet(fd,p);
+
+		gaim_remote_packet_free(p);
+		if (protocol_id)
+			g_free (protocol_id);
+		break;
 	default:
 		gaim_debug(GAIM_DEBUG_WARNING, "cui",
 				   "Unhandled remote subtype %d\n", subtype);
@@ -513,84 +572,23 @@
 }
 
 static gboolean
-UI_readable(GIOChannel *source, GIOCondition cond, gpointer data)
+UI_readable(int fd, GaimRemotePacket *p)
 {
-	struct UI *ui = data;
-
-	gchar type;
-	gchar subtype;
-	gint len;
-
-	GError *error = NULL;
-
-	gchar *in;
-
-	/* no byte order worries! this'll change if we go to TCP */
-	if (gaim_recv(source, &type, sizeof(type)) != sizeof(type)) {
-		gaim_debug(GAIM_DEBUG_ERROR, "cui", "UI has abandoned us!\n");
-		uis = g_slist_remove(uis, ui);
-		g_io_channel_shutdown(ui->channel, TRUE, &error);
-		if(error) {
-			g_error_free(error);
-			error = NULL;
-		}
-		g_source_remove(ui->inpa);
-		g_free(ui);
-		return FALSE;
-	}
-
-	if (gaim_recv(source, &subtype, sizeof(subtype)) != sizeof(subtype)) {
-		gaim_debug(GAIM_DEBUG_ERROR, "cui", "UI has abandoned us!\n");
-		uis = g_slist_remove(uis, ui);
-		g_io_channel_shutdown(ui->channel, TRUE, &error);
-		if(error) {
-			g_error_free(error);
-			error = NULL;
-		}
-		g_source_remove(ui->inpa);
-		g_free(ui);
-		return FALSE;
-	}
-
-	if (gaim_recv(source, (gchar *)&len, sizeof(len)) != sizeof(len)) {
-		gaim_debug(GAIM_DEBUG_ERROR, "cui", "UI has abandoned us!\n");
-		uis = g_slist_remove(uis, ui);
-		g_io_channel_shutdown(ui->channel, TRUE, &error);
-		if(error) {
-			g_error_free(error);
-			error = NULL;
-		}
-		g_source_remove(ui->inpa);
-		g_free(ui);
-		return FALSE;
-	}
-
-	if (len) {
-		in = g_new0(gchar, len);
-		if (gaim_recv(source, in, len) != len) {
-			gaim_debug(GAIM_DEBUG_ERROR, "cui", "UI has abandoned us!\n");
-			uis = g_slist_remove(uis, ui);
-			g_io_channel_shutdown(ui->channel, TRUE, &error);
-			if(error) {
-				g_error_free(error);
-				error = NULL;
-			}
-			g_source_remove(ui->inpa);
-			g_free(ui);
-			return FALSE;
-		}
-	} else
-		in = NULL;
+	gchar type = p->type;
+	gchar subtype = p->subtype;
 
+	GaimRemotePacketData *pd = g_list_first(p->packets)->data;
+	gchar *in = pd->data;
+	
 	switch (type) {
 		case CUI_TYPE_META:
-			meta_handler(ui, subtype, in);
+			meta_handler(fd, subtype, in);
 			break;
 		case CUI_TYPE_PLUGIN:
-			plugin_handler(ui, subtype, in);
+			plugin_handler(fd, subtype, in);
 			break;
 		case CUI_TYPE_USER:
-			user_handler(ui, subtype, in);
+			user_handler(fd, subtype, in);
 			break;
 			/*
 		case CUI_TYPE_CONN:
@@ -601,7 +599,7 @@
 			break;
 			*/
 		case CUI_TYPE_MESSAGE:
-			message_handler(ui, subtype, in);
+			message_handler(fd, subtype, in);
 			break;
 			/*
 		case CUI_TYPE_CHAT:
@@ -609,7 +607,7 @@
 			break;
 			*/   
 	        case CUI_TYPE_REMOTE:
-			remote_handler(ui, subtype, in, len);
+			remote_handler(fd, subtype, p);
 			break; 
         default:
 			gaim_debug(GAIM_DEBUG_WARNING, "cui",
@@ -617,8 +615,7 @@
 			break;
 	}
 
-	if (in)
-		g_free(in);
+	gaim_remote_packet_free(p);
 	return TRUE;
 }
 
@@ -629,20 +626,61 @@
 	guint len = sizeof(saddr);
 	gint fd;
 
-	struct UI *ui;
-
 	if ((fd = accept(UI_fd, (struct sockaddr *)&saddr, &len)) == -1)
 		return FALSE;
 
-	ui = g_new0(struct UI, 1);
-	uis = g_slist_append(uis, ui);
+	if (g_thread_create(start_remote_thread, GINT_TO_POINTER(fd), FALSE, NULL) != NULL)
+		return TRUE;
+	else
+		return FALSE;
+}
 
-	ui->channel = g_io_channel_unix_new(fd);
-	ui->inpa = g_io_add_watch(ui->channel, G_IO_IN | G_IO_HUP | G_IO_ERR, UI_readable, ui);
-	g_io_channel_unref(ui->channel);
+void send_packet (int fd, GaimRemotePacket *p) 
+{
+        fd_set wfds;
+        struct timeval tv;
+        int retval;
 
-	gaim_debug(GAIM_DEBUG_MISC, "cui", "Got one\n");
-	return TRUE;
+        FD_ZERO(&wfds);
+        FD_SET(fd, &wfds);
+        
+	/* Wait up to five seconds. */
+        tv.tv_sec = 300;
+        tv.tv_usec = 0;
+
+        retval = select(fd+1, NULL, &wfds, NULL, &tv);
+
+        if (retval == 1)
+		gaim_remote_session_send_packet (fd, p);
+}
+
+gpointer
+start_remote_thread(gpointer data)
+{
+	int fd = GPOINTER_TO_INT(data);
+
+	fd_set rfds;
+	struct timeval tv;
+	int retval;
+	
+	GaimRemotePacket *p;
+
+	FD_ZERO(&rfds);
+	FD_SET(fd, &rfds);
+	
+	/* Wait up to five seconds. */
+	tv.tv_sec = 300;
+	tv.tv_usec = 0;
+
+	retval = select(fd+1, &rfds, NULL, NULL, &tv);
+
+	if (retval == 1) {
+		p = gaim_remote_session_read_packet(fd);
+		UI_readable(fd, p);
+	}
+	close(fd);
+
+	return NULL;	
 }
 
 static gint
@@ -693,6 +731,13 @@
 		return FALSE;
 	}
 
+	g_thread_init(NULL);
+	
+	/* Block SIGPIPE */
+	sigset_t sig_to_block;
+	sigaddset(&sig_to_block, SIGPIPE);
+	pthread_sigmask(SIG_BLOCK, &sig_to_block, NULL); 
+
 	channel = g_io_channel_unix_new(UI_fd);
 	watcher = g_io_add_watch(channel, G_IO_IN, socket_readable, NULL);
 	g_io_channel_unref(channel);
diff -U 3 -H -B -d -i -r -N -- pristine/gal/gal/e-text/e-completion-view.c gal/gal/e-text/e-completion-view.c
--- pristine/gal/gal/e-text/e-completion-view.c	2004-09-06 00:52:48.000000000 -0400
+++ gal/gal/e-text/e-completion-view.c	2004-09-05 21:42:08.000000000 -0400
@@ -161,8 +161,8 @@
 		requisition->width += child_requisition.width;
 		requisition->height += child_requisition.height;
 	}
-
-	requisition->height = MAX (100, requisition->height);
+	
+	requisition->height = MAX (200, requisition->height);
 }
 
 static void
@@ -676,7 +676,7 @@
 }
 
 void
-e_completion_view_construct (ECompletionView *cv, ECompletion *completion)
+e_completion_view_construct (ECompletionView *cv, ECompletion *completion, GtkWidget *table)
 {
 	g_return_if_fail (cv != NULL);
 	g_return_if_fail (E_IS_COMPLETION_VIEW (cv));
@@ -704,22 +704,27 @@
 						  G_CALLBACK (end_completion_cb),
 						  cv);
 
-	cv->model = e_table_simple_new (table_col_count,
-					table_row_count,
-					NULL,
-
-					table_value_at,
-					NULL,
-					table_is_cell_editable,
-
-					NULL, NULL,
+	if (table && E_IS_TABLE_SCROLLED (table)) {
+		cv->table = table;
+		cv->model = E_TABLE_SCROLLED(table)->table->model;
+	} else {
+		cv->model = e_table_simple_new (table_col_count,
+						table_row_count,
+						NULL,
+	
+						table_value_at,
+						NULL,
+						table_is_cell_editable,
 
-					NULL, NULL, NULL, NULL,
-					table_value_to_string,
-					cv);
+						NULL, NULL,
+	
+						NULL, NULL, NULL, NULL,
+						table_value_to_string,
+						cv);
 
-	cv->table = e_table_scrolled_new (cv->model, NULL, simple_spec, NULL);
-	g_object_unref (cv->model);
+		cv->table = e_table_scrolled_new (cv->model, NULL, simple_spec, NULL);
+		g_object_unref (cv->model);
+	}
 
 	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (cv->table), GTK_SHADOW_NONE);
 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (cv->table), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
@@ -736,7 +741,7 @@
 }
 
 GtkWidget *
-e_completion_view_new (ECompletion *completion)
+e_completion_view_new (ECompletion *completion, GtkWidget *table)
 {
 	gpointer p;
 
@@ -745,7 +750,7 @@
 
 	p = g_object_new (E_COMPLETION_VIEW_TYPE, NULL);
 
-	e_completion_view_construct (E_COMPLETION_VIEW (p), completion);
+	e_completion_view_construct (E_COMPLETION_VIEW (p), completion, table);
 
 	return GTK_WIDGET (p);
 }
diff -U 3 -H -B -d -i -r -N -- pristine/gal/gal/e-text/e-completion-view.h gal/gal/e-text/e-completion-view.h
--- pristine/gal/gal/e-text/e-completion-view.h	2004-09-06 00:52:48.000000000 -0400
+++ gal/gal/e-text/e-completion-view.h	2004-09-05 21:38:28.000000000 -0400
@@ -81,8 +81,8 @@
 
 GtkType    e_completion_view_get_type     (void);
 
-void       e_completion_view_construct    (ECompletionView *cv, ECompletion *completion);
-GtkWidget *e_completion_view_new          (ECompletion *completion);
+void       e_completion_view_construct    (ECompletionView *cv, ECompletion *completion, GtkWidget *table);
+GtkWidget *e_completion_view_new          (ECompletion *completion, GtkWidget *table);
 
 void       e_completion_view_connect_keys (ECompletionView *cv, GtkWidget *w);
 
@@ -92,6 +92,8 @@
 void       e_completion_view_set_width    (ECompletionView *cv, gint width);
 void       e_completion_view_set_editable (ECompletionView *cv, gboolean);
 
+void	   e_completion_view_set_table (GtkWidget *table);
+
 G_END_DECLS
 
 
diff -U 3 -H -B -d -i -r -N -- pristine/gal/gal/e-text/e-completion.c gal/gal/e-text/e-completion.c
--- pristine/gal/gal/e-text/e-completion.c	2004-09-06 00:52:48.000000000 -0400
+++ gal/gal/e-text/e-completion.c	2004-09-05 21:38:28.000000000 -0400
@@ -341,3 +341,8 @@
 	comp->priv->done_search = TRUE;
 }
 
+ECompletionMatch  *
+e_completion_match_at  (ECompletion *comp, gint index)
+{
+	return (ECompletionMatch *) g_ptr_array_index (comp->priv->matches, index);
+}
diff -U 3 -H -B -d -i -r -N -- pristine/gal/gal/e-text/e-completion.h gal/gal/e-text/e-completion.h
--- pristine/gal/gal/e-text/e-completion.h	2004-09-06 00:52:48.000000000 -0400
+++ gal/gal/e-text/e-completion.h	2004-09-05 21:38:28.000000000 -0400
@@ -73,6 +73,7 @@
 gint         e_completion_search_text_pos (ECompletion *comp);
 gint         e_completion_match_count     (ECompletion *comp);
 void         e_completion_foreach_match   (ECompletion *comp, ECompletionMatchFn fn, gpointer user_data);
+ECompletionMatch  *e_completion_match_at  (ECompletion *comp, gint index);
 
 ECompletion *e_completion_new (void);
 
diff -U 3 -H -B -d -i -r -N -- pristine/gal/gal/e-text/e-entry.c gal/gal/e-text/e-entry.c
--- pristine/gal/gal/e-text/e-entry.c	2004-09-06 00:52:48.000000000 -0400
+++ gal/gal/e-text/e-entry.c	2004-09-06 23:58:05.000000000 -0400
@@ -56,6 +56,7 @@
 	E_ENTRY_ACTIVATE,
 	E_ENTRY_POPULATE_POPUP,
 	E_ENTRY_COMPLETION_POPUP,
+	E_ENTRY_ICON_CLICKED,
 	E_ENTRY_LAST_SIGNAL
 };
 
@@ -99,6 +100,8 @@
 	guint changed_proxy_tag;
 	guint activate_proxy_tag;
 	guint populate_popup_proxy_tag;
+	guint icon_clicked_proxy_tag;
+
 	/* Data related to completions */
 	ECompletion *completion;
 	EEntryCompletionHandler handler;
@@ -296,6 +299,12 @@
 }
 
 static void
+proxy_icon_clicked (EText *text, GdkEventButton *ev, gint object_num, int textofs, EEntry *entry)
+{
+	g_signal_emit (entry, e_entry_signals [E_ENTRY_ICON_CLICKED], 0, ev, object_num, textofs);
+}
+
+static void
 e_entry_init (GtkObject *object)
 {
 	EEntry *entry = E_ENTRY (object);
@@ -340,7 +349,7 @@
 		"im_context", E_CANVAS (entry->canvas)->im_context,
 		"handle_popup", TRUE,
 		NULL));
-
+	
 	g_signal_connect (entry->item,
 			  "keypress",
 			  G_CALLBACK (e_entry_text_keypress),
@@ -371,6 +380,10 @@
 								  G_CALLBACK (proxy_populate_popup),
 								  entry);
 
+	entry->priv->icon_clicked_proxy_tag = g_signal_connect (entry->item,
+								"icon_clicked",
+								G_CALLBACK (proxy_icon_clicked),
+								entry);
 	entry->priv->completion_delay = 1;
 }
 
@@ -623,6 +636,14 @@
 }
 
 static void
+added_cb (ECompletionView *view, gpointer user_data)
+{
+	EEntry *entry = E_ENTRY (user_data);
+
+	e_entry_show_popup (entry, TRUE);
+}
+
+static void
 full_cb (ECompletionView *view, gpointer user_data)
 {
 	EEntry *entry = E_ENTRY (user_data);
@@ -696,7 +717,7 @@
 	g_return_if_fail (entry != NULL && E_IS_ENTRY (entry));
 	g_return_if_fail (completion != NULL && E_IS_COMPLETION (completion));
 
-	e_entry_enable_completion_full (entry, completion, -1, NULL);
+	e_entry_enable_completion_full (entry, completion, -1, NULL, NULL);
 }
 
 static void
@@ -769,7 +790,7 @@
 }
 
 void
-e_entry_enable_completion_full (EEntry *entry, ECompletion *completion, gint delay, EEntryCompletionHandler handler)
+e_entry_enable_completion_full (EEntry *entry, ECompletion *completion, gint delay, EEntryCompletionHandler handler, GtkWidget *table)
 {
 	g_return_if_fail (entry != NULL && E_IS_ENTRY (entry));
 	g_return_if_fail (completion != NULL && E_IS_COMPLETION (completion));
@@ -784,7 +805,7 @@
 	entry->priv->completion_delay = delay;
 	entry->priv->handler = handler;
 
-	entry->priv->completion_view = e_completion_view_new (completion);
+	entry->priv->completion_view = e_completion_view_new (completion, table);
 	/* Make the up and down keys enable and disable completions. */
 	e_completion_view_set_complete_key (E_COMPLETION_VIEW (entry->priv->completion_view), GDK_Down);
 	e_completion_view_set_uncomplete_key (E_COMPLETION_VIEW (entry->priv->completion_view), GDK_Up);
@@ -799,6 +820,11 @@
 							    G_CALLBACK (nonempty_cb),
 							    entry);
 
+	entry->priv->added_signal_id = g_signal_connect (entry->priv->completion_view,
+							 "added",
+							 G_CALLBACK (added_cb),
+							 entry);
+
 	entry->priv->full_signal_id = g_signal_connect (entry->priv->completion_view,
 							"full",
 							G_CALLBACK (full_cb),
@@ -1227,6 +1253,16 @@
 			      gtk_marshal_NONE__INT,
 			      G_TYPE_NONE, 1, G_TYPE_INT);
 
+	e_entry_signals[E_ENTRY_ICON_CLICKED] =
+		g_signal_new ("icon_clicked",
+			      G_OBJECT_CLASS_TYPE (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (EEntryClass, icon_clicked),
+			      NULL, NULL,
+			      e_marshal_NONE__POINTER_INT_INT,
+			      G_TYPE_NONE, 3, G_TYPE_POINTER, G_TYPE_INT, G_TYPE_INT);
+
+
 	g_object_class_install_property (object_class, PROP_MODEL,
 					 g_param_spec_object ("model",
 							      _( "Model" ),
diff -U 3 -H -B -d -i -r -N -- pristine/gal/gal/e-text/e-entry.h gal/gal/e-text/e-entry.h
--- pristine/gal/gal/e-text/e-entry.h	2004-09-06 00:52:48.000000000 -0400
+++ gal/gal/e-text/e-entry.h	2004-09-05 21:38:28.000000000 -0400
@@ -61,6 +61,7 @@
 	void (* activate) (EEntry *entry);
 	void (* populate_popup)   (EEntry *entry, GdkEventButton *ev, gint pos, GtkMenu *menu);
 	void (* completion_popup) (EEntry *entry, gint visible);
+	void (* icon_clicked)     (EEntry *entry, GdkEventButton *ev, gint object_num, gint textofs);
 };
 
 GtkType      e_entry_get_type          (void);
@@ -79,7 +80,7 @@
 
 void         e_entry_enable_completion      (EEntry *entry, ECompletion *completion);
 void         e_entry_enable_completion_full (EEntry *entry, ECompletion *completion, gint autocomplete_delay,
-					     EEntryCompletionHandler handler);
+					     EEntryCompletionHandler handler, GtkWidget *table);
 gboolean     e_entry_completion_popup_is_visible (EEntry *entry);
 
 G_END_DECLS
diff -U 3 -H -B -d -i -r -N -- pristine/gal/gal/e-text/e-text-model.c gal/gal/e-text/e-text-model.c
--- pristine/gal/gal/e-text/e-text-model.c	2004-09-06 00:52:48.000000000 -0400
+++ gal/gal/e-text/e-text-model.c	2004-09-05 21:38:28.000000000 -0400
@@ -145,6 +145,7 @@
 	klass->objectify        = NULL;
 	klass->obj_count        = NULL;
 	klass->get_nth_obj      = NULL;
+	klass->get_nth_obj_icon = NULL;
 	
 	object_class->dispose = e_text_model_dispose;
 }
@@ -498,6 +499,21 @@
 	return NULL;
 }
 
+GdkPixbuf *
+e_text_model_get_nth_object_icon (ETextModel *model, gint n)
+{
+	g_return_val_if_fail (model != NULL, NULL);
+	g_return_val_if_fail (E_IS_TEXT_MODEL (model), NULL);
+
+	if (n < 0 || n >= e_text_model_object_count (model))
+		return NULL;
+
+	if (E_TEXT_MODEL_GET_CLASS (model)->get_nth_obj)
+		return E_TEXT_MODEL_GET_CLASS (model)->get_nth_obj_icon (model, n);
+
+	return NULL;
+}
+
 gchar *
 e_text_model_strdup_nth_object (ETextModel *model, gint n)
 {
diff -U 3 -H -B -d -i -r -N -- pristine/gal/gal/e-text/e-text-model.h gal/gal/e-text/e-text-model.h
--- pristine/gal/gal/e-text/e-text-model.h	2004-09-06 00:52:48.000000000 -0400
+++ gal/gal/e-text/e-text-model.h	2004-09-05 21:38:28.000000000 -0400
@@ -26,6 +26,7 @@
 
 #include <glib.h>
 #include <gtk/gtkobject.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
 
 G_BEGIN_DECLS
 
@@ -45,7 +46,7 @@
 
 struct _ETextModel {
 	GObject item;
-
+	
 	struct _ETextModelPrivate *priv;
 };
 
@@ -73,6 +74,9 @@
 	gint         (* obj_count)          (ETextModel *model);
 	const gchar *(* get_nth_obj)        (ETextModel *model, gint n, gint *len);
 	gint         (* obj_at_offset)      (ETextModel *model, gint offset);
+	
+	/* retrieve icon */
+	GdkPixbuf   *(* get_nth_obj_icon)   (ETextModel *model, gint n);
 };
 
 GType       e_text_model_get_type (void);
@@ -102,6 +106,7 @@
 
 gint         e_text_model_object_count          (ETextModel *model);
 const gchar *e_text_model_get_nth_object        (ETextModel *model, gint n, gint *len);
+GdkPixbuf   *e_text_model_get_nth_object_icon   (ETextModel *model, gint n);
 gchar       *e_text_model_strdup_nth_object     (ETextModel *model, gint n);
 void         e_text_model_get_nth_object_bounds (ETextModel *model, gint n, gint *start_pos, gint *end_pos);
 gint         e_text_model_get_object_at_offset  (ETextModel *model, gint offset);
diff -U 3 -H -B -d -i -r -N -- pristine/gal/gal/e-text/e-text.c gal/gal/e-text/e-text.c
--- pristine/gal/gal/e-text/e-text.c	2004-09-06 00:52:48.000000000 -0400
+++ gal/gal/e-text/e-text.c	2004-09-07 00:25:20.000000000 -0400
@@ -34,6 +34,12 @@
  * 02111-1307, USA.
  */
 
+/* Draw the icon canvas items
+ * This is how it works -- we calculate the width putting a space " " 
+ * into the layout. Then get the number of spaces we need to put into 
+ * the layout to move the text aroundto make way for icons.
+ * -- is there any better way to do this ??? because this feels like a hack */
+      	 
 #include <config.h>
 
 #include "e-text.h"
@@ -55,7 +61,8 @@
 #include <gtk/gtkimmulticontext.h>
 #include <gtk/gtkmenuitem.h>
 #include <gtk/gtkseparatormenuitem.h>
-#include <libgnomecanvas/gnome-canvas-rect-ellipse.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <libgnomecanvas/libgnomecanvas.h>
 #include <libgnome/gnome-i18n.h>
 #include "gal/util/e-util.h"
 #include "gal/widgets/e-canvas.h"
@@ -81,6 +88,7 @@
 	E_TEXT_KEYPRESS,
 	E_TEXT_POPULATE_POPUP,
 	E_TEXT_STYLE_SET,
+	E_TEXT_ICON_CLICKED,
 	E_TEXT_LAST_SIGNAL
 };
 
@@ -144,6 +152,16 @@
 
 static void reset_layout_attrs (EText *text);
 
+static void recache_icons (EText *text);
+static void find_icon_xy (EText *text, ETextIcon *texticon, PangoLayout *newlayout, int i);
+static void modify_layout (EText *text, ETextIcon *texticon, PangoLayout *newlayout, int i);
+static void update_text_and_icons (EText *text);
+static int get_offset (EText *text, int index);
+static int get_reverse_offset (EText *text, int index, gboolean *on_icon);
+int find_object_num (EText *text, int pos);
+static gint sort_list (gconstpointer a, gconstpointer b);
+
+
 #if 0
 /* GtkEditable Methods */
 static void e_text_editable_do_insert_text (GtkEditable    *editable,
@@ -204,7 +222,7 @@
 e_text_dispose (GObject *object)
 {
 	EText *text;
-
+	
 	g_return_if_fail (object != NULL);
 	g_return_if_fail (E_IS_TEXT (object));
 
@@ -284,10 +302,28 @@
 		text->im_context = NULL;
 	}
 
+	if (text->icons)
+		e_text_clear_icons (text);
+
 	if (G_OBJECT_CLASS (parent_class)->dispose)
 		(* G_OBJECT_CLASS (parent_class)->dispose) (object);
 }
 
+int find_object_num (EText *text, int pos)
+{
+	ETextIcon *texticon;
+	GList *l = g_list_first(text->icons);
+
+	while (l) {
+		texticon = l->data;
+		if (texticon->textofs == pos)
+			return texticon->objectnum;
+		l = l->next;
+	}
+	return(-1);
+}
+
+
 static void
 insert_preedit_text (EText *text)
 {
@@ -347,6 +383,28 @@
 		g_string_free (tmp_string, TRUE);
 }
 
+/* The GnomeCanvasItem list needs to be purged and a new one generated */ 
+static void 
+recache_icons (EText *text)
+{
+	int object_count = e_text_model_object_count (text->model);
+	int i, start_pos, start_index;
+	GdkPixbuf *pix;
+
+	if (text->icons)
+		e_text_clear_icons (text);
+
+	for (i = 0; i < object_count; i++) {
+		e_text_model_get_nth_object_bounds (text->model, i, &start_pos, NULL);
+
+		start_index = g_utf8_offset_to_pointer (text->text, start_pos) - text->text;
+		pix = e_text_model_get_nth_object_icon (text->model, i);
+
+		if (pix)
+			e_text_add_icon (text, pix, start_index, i);
+	}
+}
+
 static void
 reset_layout_attrs (EText *text)
 {
@@ -365,13 +423,18 @@
 		attrs = pango_attr_list_new ();
 
 		for (i = 0; i < object_count; i++) {
-			int start_pos, end_pos;
+			int start_pos, end_pos, offset;
 			PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
 
 			e_text_model_get_nth_object_bounds (text->model, i, &start_pos, &end_pos);
 
 			attr->start_index = g_utf8_offset_to_pointer (text->text, start_pos) - text->text;
 			attr->end_index = g_utf8_offset_to_pointer (text->text, end_pos) - text->text;
+			
+			offset = get_offset (text, attr->start_index);
+			
+			attr->start_index += offset;
+			attr->end_index += offset;
 
 			pango_attr_list_insert (attrs, attr);
 		}
@@ -411,6 +474,11 @@
 	text->layout = gtk_widget_create_pango_layout (GTK_WIDGET (item->canvas), text->text);
 	if (text->line_wrap)
 		pango_layout_set_width (text->layout, text->clip_width < 0 ? -1 : text->clip_width * PANGO_SCALE);
+
+	/* If there is no model associated with the text the icons will be managed by 
+	 * e_text_add_icon/e_text_delete_icon/e_text_move_icon so don't mess with it */
+	if (text->manage_icons)
+		recache_icons (text);
 	reset_layout_attrs (text);
 }
 
@@ -422,14 +490,16 @@
 	}
 	else {
 		pango_layout_set_text (text->layout, text->text, -1);
+		update_text_and_icons (text);
 		reset_layout_attrs (text);
 	}
 
 	if (!text->button_down) {
 		PangoRectangle strong_pos, weak_pos;
 		char *offs = g_utf8_offset_to_pointer (text->text, text->selection_start);
+		int offset = get_offset (text, text->selection_start);
 
-		pango_layout_get_cursor_pos (text->layout, offs - text->text, &strong_pos, &weak_pos);
+		pango_layout_get_cursor_pos (text->layout, offs - text->text + offset, &strong_pos, &weak_pos);
 
 		if (strong_pos.x != weak_pos.x ||
 		    strong_pos.y != weak_pos.y ||
@@ -586,6 +656,7 @@
 	int old_width;
 	int width = 0;
 	int height = 0;
+	int newwidth, newheight;
 
 	item = GNOME_CANVAS_ITEM (text);
 
@@ -600,6 +671,11 @@
 	text->height = height;
 	text->width = width;
 
+	PangoLayout *layout = pango_layout_copy(text->layout);
+	pango_layout_set_text (layout, " ", 1);
+	pango_layout_get_pixel_size (layout, &newwidth, &newheight);
+	text->numspaces = ceil(((double)newheight + 2 * text->iborder)/newwidth);
+
 	if (old_height != text->height || old_width != text->width)
 		e_canvas_item_request_parent_reflow(item);
 }
@@ -703,7 +779,7 @@
 					  "reposition",
 					  G_CALLBACK (e_text_text_model_reposition),
 					  text);
-
+		
 		text->text = e_text_model_get_text(text->model);
 		g_signal_emit (text, e_text_signals[E_TEXT_CHANGED], 0);
 
@@ -1220,6 +1296,26 @@
 	}
 }
 
+static void
+update_text_and_icons (EText *text)
+{
+	int i = 0;
+	
+	/* Update the positions of icons/update text */
+	/* If requested that the icons will be managed externally with
+	 * e_text_add_icon/e_text_delete_icon/e_text_move_icon , don't mess with it */
+	if (text->manage_icons)
+		recache_icons (text);
+
+	/* Modify the layout to insert icons */
+	for (i = 0; i<g_list_length(text->icons); i++)
+		modify_layout (text, g_list_nth_data(text->icons, i), text->layout, i);
+
+	/* Calculate x,y pos for icons */
+	for (i = 0; i<g_list_length(text->icons); i++)
+		find_icon_xy (text, g_list_nth_data(text->icons, i), text->layout, i); 
+}
+
 /* Realize handler for the text item */
 static void
 e_text_realize (GnomeCanvasItem *item)
@@ -1536,14 +1632,14 @@
 	gdk_draw_layout (drawable, main_gc,
 			 xpos, ypos,
 			 text->layout);
-
+ 
 	if (text->editing) {
 		if (text->selection_start != text->selection_end) {
 			PangoLayoutIter *iter;
 			GdkRegion *clip_region = gdk_region_new ();
 			GdkGC *selection_gc;
 			GdkGC *text_gc;
-			int start_index, end_index;
+			int start_index, end_index, offset = 0;
 
 			start_index = MIN (text->selection_start, text->selection_end);
 			end_index = MAX (text->selection_start, text->selection_end);
@@ -1552,6 +1648,12 @@
 			start_index = g_utf8_offset_to_pointer(text->text, start_index) - text->text;
 			end_index = g_utf8_offset_to_pointer(text->text, end_index) - text->text;
 
+			offset = get_offset (text, start_index);
+			start_index += offset;
+			
+			offset = get_offset (text, end_index);
+			end_index +=offset;	
+			
 			if (text->has_selection) {
 				selection_gc = widget->style->base_gc [GTK_STATE_SELECTED];
 				text_gc = widget->style->text_gc[GTK_STATE_SELECTED];
@@ -1625,9 +1727,11 @@
 		} else {
 			if (text->show_cursor) {
 				PangoRectangle strong_pos, weak_pos;
-				char *offs = g_utf8_offset_to_pointer (text->text, text->selection_start);
 
-				pango_layout_get_cursor_pos (text->layout, offs - text->text + text->preedit_len, &strong_pos, &weak_pos);
+				int offset = get_offset(text, text->selection_start);
+
+				char *offs = g_utf8_offset_to_pointer (text->text, text->selection_start);
+				pango_layout_get_cursor_pos (text->layout, offs - text->text + text->preedit_len + offset , &strong_pos, &weak_pos);
 				draw_pango_rectangle (drawable, main_gc, xpos, ypos, strong_pos);
 				if (strong_pos.x != weak_pos.x ||
 				    strong_pos.y != weak_pos.y ||
@@ -1638,6 +1742,44 @@
 		}
 	}
 
+	/* Now draw icons */
+	ETextIcon *texticon;
+	GList *l = g_list_first(text->icons);
+	GdkPixbuf *stated; 
+	
+	while (l) {
+		texticon  = l->data;
+		l = l->next;
+		if (!texticon->show || !texticon->pixbuf )
+			continue;
+
+		if (text->on_icon) {
+			stated = gdk_pixbuf_copy (texticon->pixbuf);
+			gdk_pixbuf_saturate_and_pixelate (texticon->pixbuf, stated,
+							  1.2, FALSE);
+	        	gdk_draw_pixbuf (drawable, NULL,
+				 	 stated,
+				 	 0, 0,
+				 	 xpos + texticon->x,
+				 	 ypos + texticon->y,
+				 	 gdk_pixbuf_get_width (stated),
+				 	 gdk_pixbuf_get_height (stated),
+				 	 GDK_RGB_DITHER_MAX,
+				 	 xpos + texticon->x, ypos + texticon->y);
+
+			gdk_pixbuf_unref (stated);
+		} else {
+			gdk_draw_pixbuf (drawable, NULL,
+					 texticon->pixbuf,
+					 0, 0,
+					 xpos + texticon->x,
+					 ypos + texticon->y,
+					 gdk_pixbuf_get_width (texticon->pixbuf),
+					 gdk_pixbuf_get_height (texticon->pixbuf),
+					 GDK_RGB_DITHER_MAX,
+					 xpos + texticon->x, ypos + texticon->y);
+		}
+	}
 
 	if (text->clip) {
 		gdk_gc_set_clip_rectangle (main_gc, NULL);
@@ -1762,7 +1904,7 @@
 }
 
 static gint
-get_position_from_xy (EText *text, gint x, gint y)
+get_position_from_xy (EText *text, gint x, gint y, gboolean *on_icon)
 {
 	int index;
 	int trailing;
@@ -1785,7 +1927,8 @@
 
 	pango_layout_xy_to_index (text->layout, x * PANGO_SCALE, y * PANGO_SCALE, &index, &trailing);
 
-	return g_utf8_pointer_to_offset (text->text, text->text + index + trailing);
+	int offset = get_reverse_offset (text, index + trailing, on_icon );
+	return g_utf8_pointer_to_offset (text->text, text->text + index + trailing - offset);
 }
 
 #define SCROLL_WAIT_TIME 30000
@@ -1847,7 +1990,7 @@
 			e_tep_event.type = GDK_MOTION_NOTIFY;
 			e_tep_event.motion.state = text->last_state;
 			e_tep_event.motion.time = 0;
-			e_tep_event.motion.position = get_position_from_xy(text, text->lastx, text->lasty);
+			e_tep_event.motion.position = get_position_from_xy(text, text->lastx, text->lasty, NULL);
 			_get_tep(text);
 			e_text_event_processor_handle_event (text->tep,
 							     &e_tep_event);
@@ -2176,6 +2319,8 @@
 		return FALSE;
 
 	e_tep_event.type = event->type;
+	
+	int pos = get_position_from_xy (text, event->button.x, event->button.y, &text->on_icon);
 	switch (event->type) {
 	case GDK_FOCUS_CHANGE:
 		if (text->editable) {
@@ -2338,6 +2483,18 @@
 			e_tep_event.type = GDK_BUTTON_RELEASE;
 		}
 #else
+		if (text->on_icon && (event->button.button == 1 )) {
+			int object_num = find_object_num (text, pos);
+			GdkEventButton *bev = (GdkEventButton *) gdk_event_copy ((GdkEvent *)(&event->button));
+			g_signal_emit  (text,
+					e_text_signals[E_TEXT_ICON_CLICKED],
+					0,
+					bev, object_num,
+					pos);
+			gdk_event_free ((GdkEvent *)bev);
+			break;
+		}
+
 		if ((!text->editing) 
 		    && text->editable 
 		    && (event->button.button == 1 ||
@@ -2351,7 +2508,7 @@
 		if (event->type == GDK_BUTTON_PRESS && event->button.button == 3) {
 			if (text->handle_popup) {
 				e_text_do_popup (text, &(event->button),
-						 get_position_from_xy (text, event->button.x, event->button.y));
+						 get_position_from_xy (text, event->button.x, event->button.y, NULL));
 				return TRUE;
 			}
 			else {
@@ -2382,7 +2539,7 @@
 			e_tep_event.button.time = button.time;
 			e_tep_event.button.state = button.state;
 			e_tep_event.button.button = button.button;
-			e_tep_event.button.position = get_position_from_xy(text, button.x, button.y);
+			e_tep_event.button.position = get_position_from_xy(text, button.x, button.y, NULL);
 			_get_tep(text);
 			return_val = e_text_event_processor_handle_event (text->tep,
 									  &e_tep_event);
@@ -2398,11 +2555,19 @@
 		}
 		break;
 	case GDK_MOTION_NOTIFY:
+                if (text->on_icon) {
+			if (!text->default_cursor_shown) {
+				gdk_window_set_cursor (GTK_WIDGET(item->canvas)->window, text->default_cursor);
+				text->default_cursor_shown = TRUE;
+			}
+			break;
+                }
+		
 		if (text->editing) {
 			GdkEventMotion motion = event->motion;
 			e_tep_event.motion.time = motion.time;
 			e_tep_event.motion.state = motion.state;
-			e_tep_event.motion.position = get_position_from_xy(text, motion.x, motion.y);
+			e_tep_event.motion.position = get_position_from_xy(text, motion.x, motion.y, NULL);
 			_get_tep(text);
 			return_val = e_text_event_processor_handle_event (text->tep,
 								       &e_tep_event);
@@ -3334,6 +3499,9 @@
 				trailing = TRUE;
 			}
 
+			int offset = get_offset (text, selection_index);
+			selection_index += offset;
+
 			pango_layout_index_to_pos (text->layout, selection_index, &pango_pos);
 
 			pango_pos.x = PANGO_PIXELS (pango_pos.x);
@@ -3345,7 +3513,10 @@
 			xpos = pango_pos.x; /* + (trailing ? 0 : pango_pos.width);*/
 
 			if (xpos + 2 < text->xofs_edit) {
-				text->xofs_edit = xpos;
+				if (offset == text->numspaces)
+					text->xofs_edit = 0;
+				else
+					text->xofs_edit = xpos;
 			}
                                                                                                 
 			clip_width = text->clip_width;
@@ -3456,6 +3627,15 @@
 			      e_marshal_NONE__POINTER_INT_OBJECT,
 			      G_TYPE_NONE, 3, G_TYPE_POINTER, G_TYPE_INT, GTK_TYPE_MENU);
 
+	e_text_signals[E_TEXT_ICON_CLICKED] =
+		g_signal_new ("icon_clicked",
+			      G_OBJECT_CLASS_TYPE (gobject_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (ETextClass, icon_clicked),
+			      NULL, NULL,
+			      e_marshal_NONE__POINTER_INT_INT,
+			      G_TYPE_NONE, 3, G_TYPE_POINTER, G_TYPE_INT, GTK_TYPE_INT);
+	
 	g_object_class_install_property (gobject_class, PROP_MODEL,
 					 g_param_spec_object ("model",
 							      _( "Model" ),
@@ -3717,6 +3897,10 @@
 	text->layout                  = NULL;
 
 	text->revert                  = NULL;
+	
+	text->icons                   = NULL;
+	text->iborder                 = 3;
+	text->manage_icons 	      = TRUE;
 
 	text->model_changed_signal_id = 
 		g_signal_connect (text->model,
@@ -3759,6 +3943,7 @@
 	text->scroll_start            = 0;
 	text->show_cursor             = TRUE;
 	text->button_down             = FALSE;
+	text->on_icon 		      = FALSE;
 
 	text->tep                     = NULL;
 	text->tep_command_id          = 0;
@@ -3869,3 +4054,225 @@
 
 	return TRUE;
 }
+
+void
+e_text_add_icon (EText *text, GdkPixbuf *icon, gint pos, gint i)
+{
+	if (!GDK_IS_PIXBUF(icon) || !text)
+		return;
+
+	ETextIcon *texticon = g_new0 (ETextIcon, 1);
+
+	/* Scale the pixbuf to a square with size of side as text height */
+	calc_height(text);
+	int isize = text->height;
+	GdkPixbuf *pix = gdk_pixbuf_scale_simple(icon, isize, isize, GDK_INTERP_BILINEAR);
+	
+	texticon->pixbuf = pix;
+	texticon->textofs = pos;
+	texticon->objectnum = i;
+	texticon->x = texticon->y = 0;
+	texticon->show = TRUE;
+	
+	gdk_pixbuf_unref (icon);
+	
+	/* FIXME -- Is sorting really necessary - i think not ??? */
+	text->icons = g_list_insert_sorted(text->icons, texticon, sort_list);
+	
+	if (!text->manage_icons) {
+		text->needs_reset_layout = 1;
+		e_canvas_item_request_reflow (GNOME_CANVAS_ITEM(text));
+		gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (text));
+	}
+}
+
+
+static gint
+sort_list (gconstpointer a, gconstpointer b)
+{
+	ETextIcon *icon1 = (ETextIcon *)a;
+	ETextIcon *icon2 = (ETextIcon *)b;
+	
+	if (icon1->textofs < icon2->textofs)
+		return(-1); 
+	else if (icon1->textofs == icon2->textofs)
+		return(0);
+	else
+		return(1);
+}
+
+static void
+find_icon_xy (EText *text, ETextIcon *texticon, PangoLayout *newlayout, int i)
+{
+	g_return_if_fail(texticon != NULL);
+	g_return_if_fail(text != NULL);
+        
+	PangoRectangle *prect= g_new(PangoRectangle, 1);
+	pango_layout_index_to_pos (newlayout, texticon->textofs + i * text->numspaces, prect);
+
+	texticon->x = PANGO_PIXELS(prect->x) + text->iborder;
+	texticon->y = PANGO_PIXELS(prect->y);
+}
+
+static void
+modify_layout (EText *text, ETextIcon *texticon, PangoLayout *newlayout, int i)
+{
+	g_return_if_fail(texticon != NULL);
+	g_return_if_fail(text != NULL);
+
+	int j;
+	GString *str = g_string_new(pango_layout_get_text(newlayout));
+
+	for (j=0; j<text->numspaces; j++)
+	        g_string_insert_c(str, texticon->textofs + i*text->numspaces + j, ' ');
+
+	pango_layout_set_text(newlayout, str->str, strlen(str->str));
+}
+
+static int
+get_offset (EText *text, int index)
+{
+	int offset = 0;
+	ETextIcon *texticon;
+	GList *l = g_list_first(text->icons);
+	
+	while (l) {
+		texticon = l->data;
+		if (texticon->textofs <= index)
+			offset += text->numspaces;
+		l = l->next;
+	}
+	return(offset);
+}
+
+static int
+get_reverse_offset (EText *text, int index, gboolean *on_icon)
+{
+	int offset = 0;
+	ETextIcon *texticon;
+	int index1 = index;
+	GList *l = g_list_first(text->icons);
+	
+	if (on_icon)
+		*on_icon = FALSE;
+
+	while (l) {
+		texticon = l->data;
+		if (texticon->textofs <= index1) {
+			offset += text->numspaces;
+			index1 = index1 - text->numspaces;
+                        
+			if ((index1 < 0) || (index1 < texticon->textofs)) {
+				offset = offset + index1 - texticon->textofs;
+				if (on_icon)
+					*on_icon = TRUE;
+                                return (offset);
+			}
+		}
+		l = l->next;
+	}
+	
+	return (offset);
+}
+
+void
+e_text_delete_icon (EText *text, gint pos)
+{
+	if (!text)
+		return;
+
+	ETextIcon *texticon;
+	GList *l = g_list_first (text->icons);
+
+	while (l) {
+		texticon = l->data;
+		if (texticon->textofs == pos) {
+			texticon->show = FALSE;
+			text->icons = g_list_remove_link (text->icons, l);
+			g_free (texticon);
+			g_list_free (l);
+			if (!text->manage_icons) {
+				text->needs_reset_layout = 1;
+				e_canvas_item_request_reflow (GNOME_CANVAS_ITEM(text));
+				gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (text));
+			}
+			return;
+		}
+		l = l->next;
+	}
+}
+
+void
+e_text_move_icon (EText *text, gint oldpos, gint newpos)
+{
+	if (!text)
+		return;
+
+	ETextIcon *texticon;
+	GList *l = g_list_first (text->icons);
+
+	while (l) {
+		texticon = l->data;
+		if (texticon->textofs == oldpos) {
+		        texticon->textofs = newpos;
+			if (!text->manage_icons) {
+				text->needs_reset_layout = 1;
+				e_canvas_item_request_reflow (GNOME_CANVAS_ITEM(text));
+				gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (text));
+			}
+			return;
+		}
+		l = l->next;
+	}
+}
+
+void
+e_text_set_show_icon  (EText *text, gint pos, gboolean show)
+{
+	if (!text)
+		return;
+
+	ETextIcon *texticon;
+	GList *l = g_list_first (text->icons);
+
+	while (l) {
+		texticon = l->data;
+		if ((texticon->textofs == pos) && (texticon->show != show)) {
+		        texticon->show = show;
+			if (!text->manage_icons) {
+				text->needs_reset_layout = 1;
+				e_canvas_item_request_reflow (GNOME_CANVAS_ITEM(text));
+				gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (text));
+			}
+			return;
+		}
+		l = l->next;
+	}
+}
+
+
+void
+e_text_clear_icons (EText *text)
+{
+	GList *ilist = g_list_first (text->icons);
+	ETextIcon *texticon;
+
+	while (ilist) {
+		texticon = ilist->data;
+		if (!texticon)
+			continue;
+		
+		texticon->show = FALSE;
+		gdk_pixbuf_unref(texticon->pixbuf);
+		g_free(texticon);
+		ilist = g_list_next(ilist);
+	}
+	g_list_free(text->icons);
+	text->icons = NULL;
+	
+	if (!text->manage_icons) {
+		text->needs_reset_layout = 1;
+		e_canvas_item_request_reflow (GNOME_CANVAS_ITEM(text));
+		gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (text));
+	}
+}
diff -U 3 -H -B -d -i -r -N -- pristine/gal/gal/e-text/e-text.h gal/gal/e-text/e-text.h
--- pristine/gal/gal/e-text/e-text.h	2004-09-06 00:52:48.000000000 -0400
+++ gal/gal/e-text/e-text.h	2004-09-07 00:30:41.000000000 -0400
@@ -100,6 +100,16 @@
 typedef struct _EText EText;
 typedef struct _ETextClass ETextClass;
 
+typedef struct {
+	int textofs;		/* The text offset from beginning of the text 
+				   where the icon should be displayed */
+	int objectnum;		/* The text model object number to which this corresponds */
+	gboolean show;
+	GdkPixbuf *pixbuf;
+	double x;		/* X position of icon */
+	double y;		/* Y position of icon */
+} ETextIcon;
+
 struct _EText {
 	GnomeCanvasItem item;
 	
@@ -107,6 +117,15 @@
 	gint model_changed_signal_id;
 	gint model_repos_signal_id;
 
+	GList *icons;              	/* The icon items which are to be displayed  --
+					   key is the text position at which to display icon */
+	int iborder;              	/* icon border width */
+	
+	int numspaces;			/* Number of spaces to insert to move text */
+	gboolean manage_icons;		/* Whether icons are to be managed automatically using 
+					   ETextModel or using explicit functions like
+					   e_text_add_icon/e_text_delete_icon */
+
 	const gchar *text;              /* Text to display --- from the ETextModel */
 	gint preedit_len;      		/* preedit length to display */
 	PangoLayout *layout;
@@ -158,6 +177,7 @@
 
 	gint show_cursor;               /* Is cursor currently shown */
 	gboolean button_down;           /* Is mouse button 1 down */
+	gboolean on_icon;		/* Is the mouse over an icon */
 
 	ETextEventProcessor *tep;       /* Text Event Processor */
 	gint tep_command_id;
@@ -225,6 +245,7 @@
 	void (* keypress)        (EText *text, guint keyval, guint state);
 	void (* populate_popup)  (EText *text, GdkEventButton *ev, gint pos, GtkMenu *menu);
 	void (* style_set)       (EText *text, GtkStyle *previous_style);
+	void (* icon_clicked)    (EText *text, GdkEventButton *ev, gint object_num, gint textofs);
 };
 
 
@@ -239,6 +260,13 @@
 void     e_text_paste_clipboard  (EText *text);
 void     e_text_select_all       (EText *text);
 
+/* Functions to add and delete icons someplace in the text */
+void     e_text_add_icon        (EText *text, GdkPixbuf *icon, gint pos, gint objnum);
+void     e_text_move_icon       (EText *text, gint oldpos, gint newpos);
+void     e_text_delete_icon     (EText *text, gint pos);
+void	 e_text_set_show_icon	(EText *text, gint pos, gboolean show);
+void     e_text_clear_icons     (EText *text);
+
 G_END_DECLS
 
 #endif
diff -U 3 -H -B -d -i -r -N -- pristine/gal/tests/test-completion.c gal/tests/test-completion.c
--- pristine/gal/tests/test-completion.c	2004-09-06 00:52:48.000000000 -0400
+++ gal/tests/test-completion.c	2004-09-06 03:31:40.000000000 -0400
@@ -195,7 +195,7 @@
 
 	win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
 	entry = e_entry_new ();
-	e_entry_enable_completion_full (E_ENTRY (entry), complete, 0, NULL);
+	e_entry_enable_completion_full (E_ENTRY (entry), complete, 0, NULL, NULL);
 	e_entry_set_editable (E_ENTRY (entry), TRUE);
 
 	g_signal_connect (entry,

Attachment: makesources
Description: Binary data



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]