empathy r1041 - trunk/libempathy-gtk



Author: xclaesse
Date: Fri Apr 25 07:04:45 2008
New Revision: 1041
URL: http://svn.gnome.org/viewvc/empathy?rev=1041&view=rev

Log:
Reorder functions to not have to declare them on top.


Modified:
   trunk/libempathy-gtk/empathy-contact-list-view.c

Modified: trunk/libempathy-gtk/empathy-contact-list-view.c
==============================================================================
--- trunk/libempathy-gtk/empathy-contact-list-view.c	(original)
+++ trunk/libempathy-gtk/empathy-contact-list-view.c	Fri Apr 25 07:04:45 2008
@@ -32,12 +32,9 @@
 #include <glade/glade.h>
 
 #include <libmissioncontrol/mc-account.h>
-#include <libmissioncontrol/mission-control.h>
 
 #include <libempathy/empathy-contact-factory.h>
 #include <libempathy/empathy-contact-list.h>
-#include <libempathy/empathy-log-manager.h>
-#include <libempathy/empathy-tp-group.h>
 #include <libempathy/empathy-contact-groups.h>
 #include <libempathy/empathy-debug.h>
 #include <libempathy/empathy-utils.h>
@@ -50,8 +47,6 @@
 #include "empathy-cell-renderer-text.h"
 #include "empathy-cell-renderer-activatable.h"
 #include "empathy-ui-utils.h"
-#include "empathy-contact-dialogs.h"
-#include "empathy-log-window.h"
 #include "empathy-gtk-enum-types.h"
 #include "empathy-gtk-marshal.h"
 
@@ -84,98 +79,6 @@
 	gboolean               remove;
 } ShowActiveData;
 
-static void        empathy_contact_list_view_class_init         (EmpathyContactListViewClass *klass);
-static void        empathy_contact_list_view_init               (EmpathyContactListView      *list);
-static void        contact_list_view_finalize                  (GObject                    *object);
-static void        contact_list_view_get_property              (GObject                    *object,
-								guint                       param_id,
-								GValue                     *value,
-								GParamSpec                 *pspec);
-static void        contact_list_view_set_property              (GObject                    *object,
-								guint                       param_id,
-								const GValue               *value,
-								GParamSpec                 *pspec);
-static void        contact_list_view_setup                     (EmpathyContactListView      *view);
-static void        contact_list_view_row_has_child_toggled_cb  (GtkTreeModel               *model,
-								GtkTreePath                *path,
-								GtkTreeIter                *iter,
-								EmpathyContactListView      *view);
-static void        contact_list_view_drag_data_received        (GtkWidget                  *widget,
-								GdkDragContext             *context,
-								gint                        x,
-								gint                        y,
-								GtkSelectionData           *selection,
-								guint                       info,
-								guint                       time);
-static gboolean    contact_list_view_drag_motion               (GtkWidget                  *widget,
-								GdkDragContext             *context,
-								gint                        x,
-								gint                        y,
-								guint                       time);
-static gboolean    contact_list_view_drag_motion_cb            (DragMotionData             *data);
-static void        contact_list_view_drag_begin                (GtkWidget                  *widget,
-								GdkDragContext             *context);
-static void        contact_list_view_drag_data_get             (GtkWidget                  *widget,
-								GdkDragContext             *context,
-								GtkSelectionData           *selection,
-								guint                       info,
-								guint                       time);
-static void        contact_list_view_drag_end                  (GtkWidget                  *widget,
-								GdkDragContext             *context);
-static gboolean    contact_list_view_drag_drop                 (GtkWidget                  *widget,
-								GdkDragContext             *drag_context,
-								gint                        x,
-								gint                        y,
-								guint                       time);
-static void        contact_list_view_cell_set_background       (EmpathyContactListView      *view,
-								GtkCellRenderer            *cell,
-								gboolean                    is_group,
-								gboolean                    is_active);
-static void        contact_list_view_pixbuf_cell_data_func     (GtkTreeViewColumn          *tree_column,
-								GtkCellRenderer            *cell,
-								GtkTreeModel               *model,
-								GtkTreeIter                *iter,
-								EmpathyContactListView     *view);
-static void        contact_list_view_voip_cell_data_func       (GtkTreeViewColumn          *tree_column,
-								GtkCellRenderer            *cell,
-								GtkTreeModel               *model,
-								GtkTreeIter                *iter,
-								EmpathyContactListView     *view);
-static void        contact_list_view_avatar_cell_data_func     (GtkTreeViewColumn          *tree_column,
-								GtkCellRenderer            *cell,
-								GtkTreeModel               *model,
-								GtkTreeIter                *iter,
-								EmpathyContactListView      *view);
-static void        contact_list_view_text_cell_data_func       (GtkTreeViewColumn          *tree_column,
-								GtkCellRenderer            *cell,
-								GtkTreeModel               *model,
-								GtkTreeIter                *iter,
-								EmpathyContactListView      *view);
-static void        contact_list_view_expander_cell_data_func   (GtkTreeViewColumn          *column,
-								GtkCellRenderer            *cell,
-								GtkTreeModel               *model,
-								GtkTreeIter                *iter,
-								EmpathyContactListView      *view);
-static gboolean    contact_list_view_button_press_event_cb     (EmpathyContactListView      *view,
-								GdkEventButton             *event,
-								gpointer                    user_data);
-static void        contact_list_view_row_activated_cb          (EmpathyContactListView      *view,
-								GtkTreePath                *path,
-								GtkTreeViewColumn          *col,
-								gpointer                    user_data);
-static void        contact_list_view_voip_activated_cb         (EmpathyCellRendererActivatable *cell,
-								const gchar                *path_string,
-								EmpathyContactListView     *view);
-static void        contact_list_view_row_expand_or_collapse_cb (EmpathyContactListView      *view,
-								GtkTreeIter                *iter,
-								GtkTreePath                *path,
-								gpointer                    user_data);
-static void        contact_list_view_voip_activated            (EmpathyContactListView      *view,
-								EmpathyContact              *contact);
-static gboolean	   contact_list_view_remove_dialog_show 	(GtkWindow 		    *parent,
-								const gchar		    *window_title, 
-								const gchar 		    *text);
-
 enum {
 	PROP_0,
 	PROP_FEATURES
@@ -211,857 +114,1004 @@
 G_DEFINE_TYPE (EmpathyContactListView, empathy_contact_list_view, GTK_TYPE_TREE_VIEW);
 
 static void
-empathy_contact_list_view_class_init (EmpathyContactListViewClass *klass)
+contact_list_view_drag_data_received (GtkWidget         *widget,
+				      GdkDragContext    *context,
+				      gint               x,
+				      gint               y,
+				      GtkSelectionData  *selection,
+				      guint              info,
+				      guint              time)
 {
-	GObjectClass   *object_class = G_OBJECT_CLASS (klass);
-	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
-
-	object_class->finalize = contact_list_view_finalize;
-	object_class->get_property = contact_list_view_get_property;
-	object_class->set_property = contact_list_view_set_property;
-
-	widget_class->drag_data_received = contact_list_view_drag_data_received;
-	widget_class->drag_drop          = contact_list_view_drag_drop;
-	widget_class->drag_begin         = contact_list_view_drag_begin;
-	widget_class->drag_data_get      = contact_list_view_drag_data_get;
-	widget_class->drag_end           = contact_list_view_drag_end;
-	/* FIXME: noticed but when you drag the row over the treeview
-	 * fast, it seems to stop redrawing itself, if we don't
-	 * connect this signal, all is fine.
-	 */
-	widget_class->drag_motion        = contact_list_view_drag_motion;
+	EmpathyContactListViewPriv *priv;
+	EmpathyContactList         *list;
+	EmpathyContactFactory      *factory;
+	McAccount                  *account;
+	GtkTreeModel               *model;
+	GtkTreePath                *path;
+	GtkTreeViewDropPosition     position;
+	EmpathyContact             *contact = NULL;
+	const gchar                *id;
+	gchar                     **strv;
+	gchar                      *new_group = NULL;
+	gchar                      *old_group = NULL;
+	gboolean                    is_row;
 
-	signals[DRAG_CONTACT_RECEIVED] =
-		g_signal_new ("drag-contact-received",
-			      G_OBJECT_CLASS_TYPE (klass),
-			      G_SIGNAL_RUN_LAST,
-			      0,
-			      NULL, NULL,
-			      _empathy_gtk_marshal_VOID__OBJECT_STRING_STRING,
-			      G_TYPE_NONE,
-			      3, EMPATHY_TYPE_CONTACT, G_TYPE_STRING, G_TYPE_STRING);
+	priv = GET_PRIV (widget);
 
-	g_object_class_install_property (object_class,
-					 PROP_FEATURES,
-					 g_param_spec_flags ("features",
-							     "Features of the view",
-							     "Falgs for all enabled features",
-							      EMPATHY_TYPE_CONTACT_LIST_FEATURES,
-							      0,
-							      G_PARAM_READWRITE));
+	id = (const gchar*) selection->data;
+	empathy_debug (DEBUG_DOMAIN, "Received %s%s drag & drop contact from roster with id:'%s'",
+		      context->action == GDK_ACTION_MOVE ? "move" : "",
+		      context->action == GDK_ACTION_COPY ? "copy" : "",
+		      id);
 
-	g_type_class_add_private (object_class, sizeof (EmpathyContactListViewPriv));
-}
+	strv = g_strsplit (id, "/", 2);
+	factory = empathy_contact_factory_new ();
+	account = mc_account_lookup (strv[0]);
+	if (account) {
+		contact = empathy_contact_factory_get_from_id (factory,
+							       account,
+							       strv[1]);
+		g_object_unref (account);
+	}
+	g_object_unref (factory);
+	g_strfreev (strv);
 
-static void
-empathy_contact_list_view_init (EmpathyContactListView *view)
-{
-	/* Get saved group states. */
-	empathy_contact_groups_get_all ();
+	if (!contact) {
+		empathy_debug (DEBUG_DOMAIN, "No contact found associated with drag & drop");
+		return;
+	}
 
-	gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (view), 
-					      empathy_contact_list_store_row_separator_func,
-					      NULL, NULL);
+	empathy_contact_run_until_ready (contact,
+					 EMPATHY_CONTACT_READY_HANDLE,
+					 NULL);
 
-	/* Connect to tree view signals rather than override. */
-	g_signal_connect (view,
-			  "button-press-event",
-			  G_CALLBACK (contact_list_view_button_press_event_cb),
-			  NULL);
-	g_signal_connect (view,
-			  "row-activated",
-			  G_CALLBACK (contact_list_view_row_activated_cb),
-			  NULL);
-	g_signal_connect (view,
-			  "row-expanded",
-			  G_CALLBACK (contact_list_view_row_expand_or_collapse_cb),
-			  GINT_TO_POINTER (TRUE));
-	g_signal_connect (view,
-			  "row-collapsed",
-			  G_CALLBACK (contact_list_view_row_expand_or_collapse_cb),
-			  GINT_TO_POINTER (FALSE));
-}
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
 
-static void
-contact_list_view_finalize (GObject *object)
-{
-	EmpathyContactListViewPriv *priv;
+	/* Get source group information. */
+	if (priv->drag_row) {
+		path = gtk_tree_row_reference_get_path (priv->drag_row);
+		if (path) {
+			old_group = empathy_contact_list_store_get_parent_group (model, path, NULL);
+			gtk_tree_path_free (path);
+		}
+	}
 
-	priv = GET_PRIV (object);
+	/* Get destination group information. */
+	is_row = gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
+						    x,
+						    y,
+						    &path,
+						    &position);
 
-	if (priv->store) {
-		g_object_unref (priv->store);
+	if (is_row) {
+		new_group = empathy_contact_list_store_get_parent_group (model, path, NULL);
+		gtk_tree_path_free (path);
 	}
 
-	G_OBJECT_CLASS (empathy_contact_list_view_parent_class)->finalize (object);
-}
+	empathy_debug (DEBUG_DOMAIN,
+		      "contact %s (%d) dragged from '%s' to '%s'",
+		      empathy_contact_get_id (contact),
+		      empathy_contact_get_handle (contact),
+		      old_group, new_group);
 
-static void
-contact_list_view_get_property (GObject    *object,
-				guint       param_id,
-				GValue     *value,
-				GParamSpec *pspec)
-{
-	EmpathyContactListViewPriv *priv;
+	list = empathy_contact_list_store_get_list_iface (priv->store);
+	if (new_group) {
+		empathy_contact_list_add_to_group (list, contact, new_group);
+	}
+	if (old_group && context->action == GDK_ACTION_MOVE) {	
+		empathy_contact_list_remove_from_group (list, contact, old_group);
+	}
 
-	priv = GET_PRIV (object);
+	g_free (old_group);
+	g_free (new_group);
 
-	switch (param_id) {
-	case PROP_FEATURES:
-		g_value_set_flags (value, priv->features);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
-		break;
-	};
+	gtk_drag_finish (context, TRUE, FALSE, GDK_CURRENT_TIME);
 }
 
-static void
-contact_list_view_set_property (GObject      *object,
-				guint         param_id,
-				const GValue *value,
-				GParamSpec   *pspec)
+static gboolean
+contact_list_view_drag_motion_cb (DragMotionData *data)
 {
-	EmpathyContactListView     *view = EMPATHY_CONTACT_LIST_VIEW (object);
-	EmpathyContactListViewPriv *priv;
+	gtk_tree_view_expand_row (GTK_TREE_VIEW (data->view),
+				  data->path,
+				  FALSE);
 
-	priv = GET_PRIV (object);
+	data->timeout_id = 0;
 
-	switch (param_id) {
-	case PROP_FEATURES:
-		empathy_contact_list_view_set_features (view, g_value_get_flags (value));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
-		break;
-	};
+	return FALSE;
 }
 
-EmpathyContactListView *
-empathy_contact_list_view_new (EmpathyContactListStore    *store,
-			       EmpathyContactListFeatures  features)
+static gboolean
+contact_list_view_drag_motion (GtkWidget      *widget,
+			       GdkDragContext *context,
+			       gint            x,
+			       gint            y,
+			       guint           time)
 {
-	EmpathyContactListView     *view;
-	EmpathyContactListViewPriv *priv;
-
-	g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store), NULL);
-	
-	view = g_object_new (EMPATHY_TYPE_CONTACT_LIST_VIEW,
-			    "features", features,
-			    NULL);
+	static DragMotionData *dm = NULL;
+	GtkTreePath           *path;
+	gboolean               is_row;
+	gboolean               is_different = FALSE;
+	gboolean               cleanup = TRUE;
 
-	priv = GET_PRIV (view);
-	priv->store = g_object_ref (store);
-	contact_list_view_setup (EMPATHY_CONTACT_LIST_VIEW (view));
+	is_row = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
+						x,
+						y,
+						&path,
+						NULL,
+						NULL,
+						NULL);
 
-	return view;
-}
+	cleanup &= (!dm);
 
-void
-empathy_contact_list_view_set_features (EmpathyContactListView     *view,
-					EmpathyContactListFeatures  features)
-{
-	EmpathyContactListViewPriv *priv = GET_PRIV (view);
+	if (is_row) {
+		cleanup &= (dm && gtk_tree_path_compare (dm->path, path) != 0);
+		is_different = (!dm || (dm && gtk_tree_path_compare (dm->path, path) != 0));
+	} else {
+		cleanup &= FALSE;
+	}
 
-	g_return_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view));
+	if (!is_different && !cleanup) {
+		return TRUE;
+	}
 
-	priv->features = features;
+	if (dm) {
+		gtk_tree_path_free (dm->path);
+		if (dm->timeout_id) {
+			g_source_remove (dm->timeout_id);
+		}
 
-	/* Update DnD source/dest */
-	if (features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_DRAG) {
-		gtk_drag_source_set (GTK_WIDGET (view),
-				     GDK_BUTTON1_MASK,
-				     drag_types_source,
-				     G_N_ELEMENTS (drag_types_source),
-				     GDK_ACTION_MOVE | GDK_ACTION_COPY);
-	} else {
-		gtk_drag_source_unset (GTK_WIDGET (view));
+		g_free (dm);
 
+		dm = NULL;
 	}
 
-	if (features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_DROP) {
-		gtk_drag_dest_set (GTK_WIDGET (view),
-				   GTK_DEST_DEFAULT_ALL,
-				   drag_types_dest,
-				   G_N_ELEMENTS (drag_types_dest),
-				   GDK_ACTION_MOVE | GDK_ACTION_COPY);
-	} else {
-		/* FIXME: URI could still be  droped depending on FT feature */
-		gtk_drag_dest_unset (GTK_WIDGET (view));
-	}
-
-	g_object_notify (G_OBJECT (view), "features");
-}
+	if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), path)) {
+		dm = g_new0 (DragMotionData, 1);
 
-EmpathyContactListFeatures
-empathy_contact_list_view_get_features (EmpathyContactListView  *view)
-{
-	EmpathyContactListViewPriv *priv = GET_PRIV (view);
+		dm->view = EMPATHY_CONTACT_LIST_VIEW (widget);
+		dm->path = gtk_tree_path_copy (path);
 
-	g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), FALSE);
+		dm->timeout_id = g_timeout_add_seconds (1,
+			(GSourceFunc) contact_list_view_drag_motion_cb,
+			dm);
+	}
 
-	return priv->features;
+	return TRUE;
 }
 
-EmpathyContact *
-empathy_contact_list_view_get_selected (EmpathyContactListView *view)
+static void
+contact_list_view_drag_begin (GtkWidget      *widget,
+			      GdkDragContext *context)
 {
 	EmpathyContactListViewPriv *priv;
 	GtkTreeSelection          *selection;
-	GtkTreeIter                iter;
 	GtkTreeModel              *model;
-	EmpathyContact             *contact;
+	GtkTreePath               *path;
+	GtkTreeIter                iter;
 
-	g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), NULL);
+	priv = GET_PRIV (widget);
 
-	priv = GET_PRIV (view);
+	GTK_WIDGET_CLASS (empathy_contact_list_view_parent_class)->drag_begin (widget,
+									      context);
 
-	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
 	if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
-		return NULL;
+		return;
 	}
 
-	gtk_tree_model_get (model, &iter,
-			    EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
-			    -1);
-
-	return contact;
+	path = gtk_tree_model_get_path (model, &iter);
+	priv->drag_row = gtk_tree_row_reference_new (model, path);
+	gtk_tree_path_free (path);
 }
 
-gchar *
-empathy_contact_list_view_get_selected_group (EmpathyContactListView *view)
+static void
+contact_list_view_drag_data_get (GtkWidget        *widget,
+				 GdkDragContext   *context,
+				 GtkSelectionData *selection,
+				 guint             info,
+				 guint             time)
 {
 	EmpathyContactListViewPriv *priv;
-	GtkTreeSelection          *selection;
-	GtkTreeIter                iter;
-	GtkTreeModel              *model;
-	gboolean                   is_group;
-	gchar                     *name;
+	GtkTreePath                *src_path;
+	GtkTreeIter                 iter;
+	GtkTreeModel               *model;
+	EmpathyContact             *contact;
+	McAccount                  *account;
+	const gchar                *contact_id;
+	const gchar                *account_id;
+	gchar                      *str;
 
-	g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), NULL);
+	priv = GET_PRIV (widget);
 
-	priv = GET_PRIV (view);
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+	if (!priv->drag_row) {
+		return;
+	}
 
-	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
-	if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
-		return NULL;
+	src_path = gtk_tree_row_reference_get_path (priv->drag_row);
+	if (!src_path) {
+		return;
 	}
 
-	gtk_tree_model_get (model, &iter,
-			    EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
-			    EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
-			    -1);
+	if (!gtk_tree_model_get_iter (model, &iter, src_path)) {
+		gtk_tree_path_free (src_path);
+		return;
+	}
 
-	if (!is_group) {
-		g_free (name);
-		return NULL;
+	gtk_tree_path_free (src_path);
+
+	contact = empathy_contact_list_view_get_selected (EMPATHY_CONTACT_LIST_VIEW (widget));
+	if (!contact) {
+		return;
 	}
 
-	return name;
+	account = empathy_contact_get_account (contact);
+	account_id = mc_account_get_unique_name (account);
+	contact_id = empathy_contact_get_id (contact);
+	g_object_unref (contact);
+	str = g_strconcat (account_id, "/", contact_id, NULL);
+
+	switch (info) {
+	case DND_DRAG_TYPE_CONTACT_ID:
+		gtk_selection_data_set (selection, drag_atoms_source[info], 8,
+					(guchar*)str, strlen (str) + 1);
+		break;
+	}
+
+	g_free (str);
 }
 
 static void
-contact_list_view_setup (EmpathyContactListView *view)
+contact_list_view_drag_end (GtkWidget      *widget,
+			    GdkDragContext *context)
 {
 	EmpathyContactListViewPriv *priv;
-	GtkCellRenderer           *cell;
-	GtkTreeViewColumn         *col;
-	gint                       i;
 
-	priv = GET_PRIV (view);
+	priv = GET_PRIV (widget);
 
-	gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (view),
-					     empathy_contact_list_store_search_equal_func,
-					     NULL, NULL);
+	GTK_WIDGET_CLASS (empathy_contact_list_view_parent_class)->drag_end (widget,
+									    context);
 
-	g_signal_connect (priv->store, "row-has-child-toggled",
-			  G_CALLBACK (contact_list_view_row_has_child_toggled_cb),
-			  view);
-	gtk_tree_view_set_model (GTK_TREE_VIEW (view),
-				 GTK_TREE_MODEL (priv->store));
+	if (priv->drag_row) {
+		gtk_tree_row_reference_free (priv->drag_row);
+		priv->drag_row = NULL;
+	}
+}
 
-	/* Setup view */
-	g_object_set (view,
-		      "headers-visible", FALSE,
-		      "reorderable", TRUE,
-		      "show-expanders", FALSE,
-		      NULL);
+static gboolean
+contact_list_view_drag_drop (GtkWidget      *widget,
+			     GdkDragContext *drag_context,
+			     gint            x,
+			     gint            y,
+			     guint           time)
+{
+	return FALSE;
+}
 
-	col = gtk_tree_view_column_new ();
+static gboolean
+contact_list_view_button_press_event_cb (EmpathyContactListView *view,
+					 GdkEventButton        *event,
+					 gpointer               user_data)
+{
+	EmpathyContactListViewPriv *priv;
+	EmpathyContact             *contact;
+	GtkTreePath               *path;
+	GtkTreeSelection          *selection;
+	GtkTreeModel              *model;
+	GtkTreeIter                iter;
+	gboolean                   row_exists;
+	GtkWidget                 *menu;
 
-	/* State */
-	cell = gtk_cell_renderer_pixbuf_new ();
-	gtk_tree_view_column_pack_start (col, cell, FALSE);
-	gtk_tree_view_column_set_cell_data_func (
-		col, cell,
-		(GtkTreeCellDataFunc) contact_list_view_pixbuf_cell_data_func,
-		view, NULL);
+	priv = GET_PRIV (view);
 
-	g_object_set (cell,
-		      "xpad", 5,
-		      "ypad", 1,
-		      "visible", FALSE,
-		      NULL);
+	if (event->button != 3) {
+		return FALSE;
+	}
 
-	/* Name */
-	cell = empathy_cell_renderer_text_new ();
-	gtk_tree_view_column_pack_start (col, cell, TRUE);
-	gtk_tree_view_column_set_cell_data_func (
-		col, cell,
-		(GtkTreeCellDataFunc) contact_list_view_text_cell_data_func,
-		view, NULL);
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
 
-	gtk_tree_view_column_add_attribute (col, cell,
-					    "name", EMPATHY_CONTACT_LIST_STORE_COL_NAME);
-	gtk_tree_view_column_add_attribute (col, cell,
-					    "status", EMPATHY_CONTACT_LIST_STORE_COL_STATUS);
-	gtk_tree_view_column_add_attribute (col, cell,
-					    "is_group", EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP);
+	gtk_widget_grab_focus (GTK_WIDGET (view));
 
-	/* Voip Capability Icon */
-	cell = empathy_cell_renderer_activatable_new ();
-	gtk_tree_view_column_pack_start (col, cell, FALSE);
-	gtk_tree_view_column_set_cell_data_func (
-		col, cell,
-		(GtkTreeCellDataFunc) contact_list_view_voip_cell_data_func,
-		view, NULL);
+	row_exists = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (view),
+						    event->x, event->y,
+						    &path,
+						    NULL, NULL, NULL);
+	if (!row_exists) {
+		return FALSE;
+	}
 
-	g_object_set (cell,
-		      "visible", FALSE,
-		      NULL);
+	gtk_tree_selection_unselect_all (selection);
+	gtk_tree_selection_select_path (selection, path);
 
-	g_signal_connect (cell, "path-activated",
-			  G_CALLBACK (contact_list_view_voip_activated_cb),
-			  view);
+	gtk_tree_model_get_iter (model, &iter, path);
+	gtk_tree_path_free (path);
 
-	/* Avatar */
-	cell = gtk_cell_renderer_pixbuf_new ();
-	gtk_tree_view_column_pack_start (col, cell, FALSE);
-	gtk_tree_view_column_set_cell_data_func (
-		col, cell,
-		(GtkTreeCellDataFunc) contact_list_view_avatar_cell_data_func,
-		view, NULL);
+	gtk_tree_model_get (model, &iter,
+			    EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
+			    -1);
 
-	g_object_set (cell,
-		      "xpad", 0,
-		      "ypad", 0,
-		      "visible", FALSE,
-		      "width", 32,
-		      "height", 32,
-		      NULL);
+	if (contact) {
+		menu = empathy_contact_list_view_get_contact_menu (view, contact);
+		g_object_unref (contact);
+	} else {
+		menu = empathy_contact_list_view_get_group_menu (view);
+	}
 
-	/* Expander */
-	cell = empathy_cell_renderer_expander_new ();
-	gtk_tree_view_column_pack_end (col, cell, FALSE);
-	gtk_tree_view_column_set_cell_data_func (
-		col, cell,
-		(GtkTreeCellDataFunc) contact_list_view_expander_cell_data_func,
-		view, NULL);
+	if (!menu) {
+		return FALSE;
+	}
 
-	/* Actually add the column now we have added all cell renderers */
-	gtk_tree_view_append_column (GTK_TREE_VIEW (view), col);
+	gtk_widget_show (menu);
 
-	/* Drag & Drop. */
-	for (i = 0; i < G_N_ELEMENTS (drag_types_dest); ++i) {
-		drag_atoms_dest[i] = gdk_atom_intern (drag_types_dest[i].target,
-						      FALSE);
-	}
+	gtk_menu_popup (GTK_MENU (menu),
+			NULL, NULL, NULL, NULL,
+			event->button, event->time);
 
-	for (i = 0; i < G_N_ELEMENTS (drag_types_source); ++i) {
-		drag_atoms_source[i] = gdk_atom_intern (drag_types_source[i].target,
-							FALSE);
-	}
+	return TRUE;
 }
 
 static void
-contact_list_view_row_has_child_toggled_cb (GtkTreeModel          *model,
-					    GtkTreePath           *path,
-					    GtkTreeIter           *iter,
-					    EmpathyContactListView *view)
+contact_list_view_row_activated_cb (EmpathyContactListView *view,
+				    GtkTreePath            *path,
+				    GtkTreeViewColumn      *col,
+				    gpointer                user_data)
 {
 	EmpathyContactListViewPriv *priv = GET_PRIV (view);
-	gboolean  is_group = FALSE;
-	gchar    *name = NULL;
-
-	gtk_tree_model_get (model, iter,
-			    EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
-			    EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
-			    -1);
+	EmpathyContact             *contact;
+	GtkTreeModel               *model;
+	GtkTreeIter                 iter;
 
-	if (!is_group || G_STR_EMPTY (name)) {
-		g_free (name);
+	if (!(priv->features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_CHAT)) {
 		return;
 	}
 
-	if (!(priv->features & EMPATHY_CONTACT_LIST_FEATURE_GROUPS_SAVE) ||
-	    empathy_contact_group_get_expanded (name)) {
-		g_signal_handlers_block_by_func (view,
-						 contact_list_view_row_expand_or_collapse_cb,
-						 GINT_TO_POINTER (TRUE));
-		gtk_tree_view_expand_row (GTK_TREE_VIEW (view), path, TRUE);
-		g_signal_handlers_unblock_by_func (view,
-						   contact_list_view_row_expand_or_collapse_cb,
-						   GINT_TO_POINTER (TRUE));
-	} else {
-		g_signal_handlers_block_by_func (view,
-						 contact_list_view_row_expand_or_collapse_cb,
-						 GINT_TO_POINTER (FALSE));
-		gtk_tree_view_collapse_row (GTK_TREE_VIEW (view), path);
-		g_signal_handlers_unblock_by_func (view,
-						   contact_list_view_row_expand_or_collapse_cb,
-						   GINT_TO_POINTER (FALSE));
-	}
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
 
-	g_free (name);
+	gtk_tree_model_get_iter (model, &iter, path);
+	gtk_tree_model_get (model, &iter,
+			    EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
+			    -1);
+
+	if (contact) {
+		empathy_chat_with_contact (contact);
+		g_object_unref (contact);
+	}
 }
 
 static void
-contact_list_view_drag_data_received (GtkWidget         *widget,
-				      GdkDragContext    *context,
-				      gint               x,
-				      gint               y,
-				      GtkSelectionData  *selection,
-				      guint              info,
-				      guint              time)
+contact_list_view_voip_activated_cb (EmpathyCellRendererActivatable *cell,
+				     const gchar                    *path_string,
+				     EmpathyContactListView         *view)
 {
-	EmpathyContactListViewPriv *priv;
-	EmpathyContactList         *list;
-	EmpathyContactFactory      *factory;
-	McAccount                  *account;
+	EmpathyContactListViewPriv *priv = GET_PRIV (view);
 	GtkTreeModel               *model;
-	GtkTreePath                *path;
-	GtkTreeViewDropPosition     position;
-	EmpathyContact             *contact = NULL;
-	const gchar                *id;
-	gchar                     **strv;
-	gchar                      *new_group = NULL;
-	gchar                      *old_group = NULL;
-	gboolean                    is_row;
-
-	priv = GET_PRIV (widget);
-
-	id = (const gchar*) selection->data;
-	empathy_debug (DEBUG_DOMAIN, "Received %s%s drag & drop contact from roster with id:'%s'",
-		      context->action == GDK_ACTION_MOVE ? "move" : "",
-		      context->action == GDK_ACTION_COPY ? "copy" : "",
-		      id);
-
-	strv = g_strsplit (id, "/", 2);
-	factory = empathy_contact_factory_new ();
-	account = mc_account_lookup (strv[0]);
-	if (account) {
-		contact = empathy_contact_factory_get_from_id (factory,
-							       account,
-							       strv[1]);
-		g_object_unref (account);
-	}
-	g_object_unref (factory);
-	g_strfreev (strv);
+	GtkTreeIter                 iter;
+	EmpathyContact             *contact;
 
-	if (!contact) {
-		empathy_debug (DEBUG_DOMAIN, "No contact found associated with drag & drop");
+	if (!(priv->features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_CALL)) {
 		return;
 	}
 
-	empathy_contact_run_until_ready (contact,
-					 EMPATHY_CONTACT_READY_HANDLE,
-					 NULL);
-
-	model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
-
-	/* Get source group information. */
-	if (priv->drag_row) {
-		path = gtk_tree_row_reference_get_path (priv->drag_row);
-		if (path) {
-			old_group = empathy_contact_list_store_get_parent_group (model, path, NULL);
-			gtk_tree_path_free (path);
-		}
-	}
-
-	/* Get destination group information. */
-	is_row = gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
-						    x,
-						    y,
-						    &path,
-						    &position);
-
-	if (is_row) {
-		new_group = empathy_contact_list_store_get_parent_group (model, path, NULL);
-		gtk_tree_path_free (path);
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+	if (!gtk_tree_model_get_iter_from_string (model, &iter, path_string)) {
+		return;
 	}
 
-	empathy_debug (DEBUG_DOMAIN,
-		      "contact %s (%d) dragged from '%s' to '%s'",
-		      empathy_contact_get_id (contact),
-		      empathy_contact_get_handle (contact),
-		      old_group, new_group);
+	gtk_tree_model_get (model, &iter,
+			    EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
+			    -1);
 
-	list = empathy_contact_list_store_get_list_iface (priv->store);
-	if (new_group) {
-		empathy_contact_list_add_to_group (list, contact, new_group);
-	}
-	if (old_group && context->action == GDK_ACTION_MOVE) {	
-		empathy_contact_list_remove_from_group (list, contact, old_group);
+	if (contact) {
+		empathy_call_with_contact (contact);
+		g_object_unref (contact);
 	}
-
-	g_free (old_group);
-	g_free (new_group);
-
-	gtk_drag_finish (context, TRUE, FALSE, GDK_CURRENT_TIME);
 }
 
-static gboolean
-contact_list_view_drag_motion (GtkWidget      *widget,
-			       GdkDragContext *context,
-			       gint            x,
-			       gint            y,
-			       guint           time)
+static void
+contact_list_view_cell_set_background (EmpathyContactListView *view,
+				       GtkCellRenderer       *cell,
+				       gboolean               is_group,
+				       gboolean               is_active)
 {
-	static DragMotionData *dm = NULL;
-	GtkTreePath           *path;
-	gboolean               is_row;
-	gboolean               is_different = FALSE;
-	gboolean               cleanup = TRUE;
+	GdkColor  color;
+	GtkStyle *style;
 
-	is_row = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
-						x,
-						y,
-						&path,
-						NULL,
-						NULL,
-						NULL);
+	style = gtk_widget_get_style (GTK_WIDGET (view));
 
-	cleanup &= (!dm);
+	if (!is_group && is_active) {
+		color = style->bg[GTK_STATE_SELECTED];
 
-	if (is_row) {
-		cleanup &= (dm && gtk_tree_path_compare (dm->path, path) != 0);
-		is_different = (!dm || (dm && gtk_tree_path_compare (dm->path, path) != 0));
-	} else {
-		cleanup &= FALSE;
-	}
+		/* Here we take the current theme colour and add it to
+		 * the colour for white and average the two. This
+		 * gives a colour which is inline with the theme but
+		 * slightly whiter.
+		 */
+		color.red = (color.red + (style->white).red) / 2;
+		color.green = (color.green + (style->white).green) / 2;
+		color.blue = (color.blue + (style->white).blue) / 2;
 
-	if (!is_different && !cleanup) {
-		return TRUE;
+		g_object_set (cell,
+			      "cell-background-gdk", &color,
+			      NULL);
+	} else {
+		g_object_set (cell,
+			      "cell-background-gdk", NULL,
+			      NULL);
 	}
+}
 
-	if (dm) {
-		gtk_tree_path_free (dm->path);
-		if (dm->timeout_id) {
-			g_source_remove (dm->timeout_id);
-		}
+static void
+contact_list_view_pixbuf_cell_data_func (GtkTreeViewColumn     *tree_column,
+					 GtkCellRenderer       *cell,
+					 GtkTreeModel          *model,
+					 GtkTreeIter           *iter,
+					 EmpathyContactListView *view)
+{
+	gchar    *icon_name;
+	gboolean  is_group;
+	gboolean  is_active;
 
-		g_free (dm);
+	gtk_tree_model_get (model, iter,
+			    EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
+			    EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
+			    EMPATHY_CONTACT_LIST_STORE_COL_ICON_STATUS, &icon_name,
+			    -1);
 
-		dm = NULL;
-	}
+	g_object_set (cell,
+		      "visible", !is_group,
+		      "icon-name", icon_name,
+		      NULL);
 
-	if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), path)) {
-		dm = g_new0 (DragMotionData, 1);
+	g_free (icon_name);
 
-		dm->view = EMPATHY_CONTACT_LIST_VIEW (widget);
-		dm->path = gtk_tree_path_copy (path);
-
-		dm->timeout_id = g_timeout_add_seconds (1,
-			(GSourceFunc) contact_list_view_drag_motion_cb,
-			dm);
-	}
-
-	return TRUE;
-}
-
-static gboolean
-contact_list_view_drag_motion_cb (DragMotionData *data)
-{
-	gtk_tree_view_expand_row (GTK_TREE_VIEW (data->view),
-				  data->path,
-				  FALSE);
-
-	data->timeout_id = 0;
-
-	return FALSE;
-}
+	contact_list_view_cell_set_background (view, cell, is_group, is_active);
+}
 
 static void
-contact_list_view_drag_begin (GtkWidget      *widget,
-			      GdkDragContext *context)
+contact_list_view_voip_cell_data_func (GtkTreeViewColumn      *tree_column,
+				       GtkCellRenderer        *cell,
+				       GtkTreeModel           *model,
+				       GtkTreeIter            *iter,
+				       EmpathyContactListView *view)
 {
-	EmpathyContactListViewPriv *priv;
-	GtkTreeSelection          *selection;
-	GtkTreeModel              *model;
-	GtkTreePath               *path;
-	GtkTreeIter                iter;
-
-	priv = GET_PRIV (widget);
+	gboolean is_group;
+	gboolean is_active;
+	gboolean can_voip;
 
-	GTK_WIDGET_CLASS (empathy_contact_list_view_parent_class)->drag_begin (widget,
-									      context);
+	gtk_tree_model_get (model, iter,
+			    EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
+			    EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
+			    EMPATHY_CONTACT_LIST_STORE_COL_CAN_VOIP, &can_voip,
+			    -1);
 
-	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
-	if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
-		return;
-	}
+	g_object_set (cell,
+		      "visible", !is_group && can_voip,
+		      "icon-name", EMPATHY_IMAGE_VOIP,
+		      NULL);
 
-	path = gtk_tree_model_get_path (model, &iter);
-	priv->drag_row = gtk_tree_row_reference_new (model, path);
-	gtk_tree_path_free (path);
+	contact_list_view_cell_set_background (view, cell, is_group, is_active);
 }
 
 static void
-contact_list_view_drag_data_get (GtkWidget        *widget,
-				 GdkDragContext   *context,
-				 GtkSelectionData *selection,
-				 guint             info,
-				 guint             time)
+contact_list_view_avatar_cell_data_func (GtkTreeViewColumn     *tree_column,
+					 GtkCellRenderer       *cell,
+					 GtkTreeModel          *model,
+					 GtkTreeIter           *iter,
+					 EmpathyContactListView *view)
 {
-	EmpathyContactListViewPriv *priv;
-	GtkTreePath                *src_path;
-	GtkTreeIter                 iter;
-	GtkTreeModel               *model;
-	EmpathyContact             *contact;
-	McAccount                  *account;
-	const gchar                *contact_id;
-	const gchar                *account_id;
-	gchar                      *str;
-
-	priv = GET_PRIV (widget);
-
-	model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
-	if (!priv->drag_row) {
-		return;
-	}
-
-	src_path = gtk_tree_row_reference_get_path (priv->drag_row);
-	if (!src_path) {
-		return;
-	}
-
-	if (!gtk_tree_model_get_iter (model, &iter, src_path)) {
-		gtk_tree_path_free (src_path);
-		return;
-	}
-
-	gtk_tree_path_free (src_path);
+	GdkPixbuf *pixbuf;
+	gboolean   show_avatar;
+	gboolean   is_group;
+	gboolean   is_active;
 
-	contact = empathy_contact_list_view_get_selected (EMPATHY_CONTACT_LIST_VIEW (widget));
-	if (!contact) {
-		return;
-	}
+	gtk_tree_model_get (model, iter,
+			    EMPATHY_CONTACT_LIST_STORE_COL_PIXBUF_AVATAR, &pixbuf,
+			    EMPATHY_CONTACT_LIST_STORE_COL_PIXBUF_AVATAR_VISIBLE, &show_avatar,
+			    EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
+			    EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
+			    -1);
 
-	account = empathy_contact_get_account (contact);
-	account_id = mc_account_get_unique_name (account);
-	contact_id = empathy_contact_get_id (contact);
-	g_object_unref (contact);
-	str = g_strconcat (account_id, "/", contact_id, NULL);
+	g_object_set (cell,
+		      "visible", !is_group && show_avatar,
+		      "pixbuf", pixbuf,
+		      NULL);
 
-	switch (info) {
-	case DND_DRAG_TYPE_CONTACT_ID:
-		gtk_selection_data_set (selection, drag_atoms_source[info], 8,
-					(guchar*)str, strlen (str) + 1);
-		break;
+	if (pixbuf) {
+		g_object_unref (pixbuf);
 	}
 
-	g_free (str);
+	contact_list_view_cell_set_background (view, cell, is_group, is_active);
 }
 
 static void
-contact_list_view_drag_end (GtkWidget      *widget,
-			    GdkDragContext *context)
+contact_list_view_text_cell_data_func (GtkTreeViewColumn     *tree_column,
+				       GtkCellRenderer       *cell,
+				       GtkTreeModel          *model,
+				       GtkTreeIter           *iter,
+				       EmpathyContactListView *view)
 {
-	EmpathyContactListViewPriv *priv;
-
-	priv = GET_PRIV (widget);
+	gboolean is_group;
+	gboolean is_active;
+	gboolean show_status;
 
-	GTK_WIDGET_CLASS (empathy_contact_list_view_parent_class)->drag_end (widget,
-									    context);
+	gtk_tree_model_get (model, iter,
+			    EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
+			    EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
+			    EMPATHY_CONTACT_LIST_STORE_COL_STATUS_VISIBLE, &show_status,
+			    -1);
 
-	if (priv->drag_row) {
-		gtk_tree_row_reference_free (priv->drag_row);
-		priv->drag_row = NULL;
-	}
-}
+	g_object_set (cell,
+		      "show-status", show_status,
+		      NULL);
 
-static gboolean
-contact_list_view_drag_drop (GtkWidget      *widget,
-			     GdkDragContext *drag_context,
-			     gint            x,
-			     gint            y,
-			     guint           time)
-{
-	return FALSE;
+	contact_list_view_cell_set_background (view, cell, is_group, is_active);
 }
 
 static void
-contact_list_view_cell_set_background (EmpathyContactListView *view,
-				       GtkCellRenderer       *cell,
-				       gboolean               is_group,
-				       gboolean               is_active)
+contact_list_view_expander_cell_data_func (GtkTreeViewColumn     *column,
+					   GtkCellRenderer       *cell,
+					   GtkTreeModel          *model,
+					   GtkTreeIter           *iter,
+					   EmpathyContactListView *view)
 {
-	GdkColor  color;
-	GtkStyle *style;
+	gboolean is_group;
+	gboolean is_active;
 
-	style = gtk_widget_get_style (GTK_WIDGET (view));
+	gtk_tree_model_get (model, iter,
+			    EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
+			    EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
+			    -1);
 
-	if (!is_group && is_active) {
-		color = style->bg[GTK_STATE_SELECTED];
+	if (gtk_tree_model_iter_has_child (model, iter)) {
+		GtkTreePath *path;
+		gboolean     row_expanded;
 
-		/* Here we take the current theme colour and add it to
-		 * the colour for white and average the two. This
-		 * gives a colour which is inline with the theme but
-		 * slightly whiter.
-		 */
-		color.red = (color.red + (style->white).red) / 2;
-		color.green = (color.green + (style->white).green) / 2;
-		color.blue = (color.blue + (style->white).blue) / 2;
+		path = gtk_tree_model_get_path (model, iter);
+		row_expanded = gtk_tree_view_row_expanded (GTK_TREE_VIEW (column->tree_view), path);
+		gtk_tree_path_free (path);
 
 		g_object_set (cell,
-			      "cell-background-gdk", &color,
+			      "visible", TRUE,
+			      "expander-style", row_expanded ? GTK_EXPANDER_EXPANDED : GTK_EXPANDER_COLLAPSED,
 			      NULL);
 	} else {
-		g_object_set (cell,
-			      "cell-background-gdk", NULL,
-			      NULL);
+		g_object_set (cell, "visible", FALSE, NULL);
 	}
+
+	contact_list_view_cell_set_background (view, cell, is_group, is_active);
 }
 
 static void
-contact_list_view_pixbuf_cell_data_func (GtkTreeViewColumn     *tree_column,
-					 GtkCellRenderer       *cell,
-					 GtkTreeModel          *model,
-					 GtkTreeIter           *iter,
-					 EmpathyContactListView *view)
+contact_list_view_row_expand_or_collapse_cb (EmpathyContactListView *view,
+					     GtkTreeIter           *iter,
+					     GtkTreePath           *path,
+					     gpointer               user_data)
 {
-	gchar    *icon_name;
-	gboolean  is_group;
-	gboolean  is_active;
+	EmpathyContactListViewPriv *priv = GET_PRIV (view);
+	GtkTreeModel               *model;
+	gchar                      *name;
+	gboolean                    expanded;
+
+	if (!(priv->features & EMPATHY_CONTACT_LIST_FEATURE_GROUPS_SAVE)) {
+		return;
+	}
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
 
 	gtk_tree_model_get (model, iter,
-			    EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
-			    EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
-			    EMPATHY_CONTACT_LIST_STORE_COL_ICON_STATUS, &icon_name,
+			    EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
 			    -1);
 
-	g_object_set (cell,
-		      "visible", !is_group,
-		      "icon-name", icon_name,
-		      NULL);
-
-	g_free (icon_name);
+	expanded = GPOINTER_TO_INT (user_data);
+	empathy_contact_group_set_expanded (name, expanded);
 
-	contact_list_view_cell_set_background (view, cell, is_group, is_active);
+	g_free (name);
 }
 
 static void
-contact_list_view_voip_cell_data_func (GtkTreeViewColumn      *tree_column,
-				       GtkCellRenderer        *cell,
-				       GtkTreeModel           *model,
-				       GtkTreeIter            *iter,
-				       EmpathyContactListView *view)
+contact_list_view_row_has_child_toggled_cb (GtkTreeModel          *model,
+					    GtkTreePath           *path,
+					    GtkTreeIter           *iter,
+					    EmpathyContactListView *view)
 {
-	gboolean is_group;
-	gboolean is_active;
-	gboolean can_voip;
+	EmpathyContactListViewPriv *priv = GET_PRIV (view);
+	gboolean  is_group = FALSE;
+	gchar    *name = NULL;
 
 	gtk_tree_model_get (model, iter,
 			    EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
-			    EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
-			    EMPATHY_CONTACT_LIST_STORE_COL_CAN_VOIP, &can_voip,
+			    EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
 			    -1);
 
+	if (!is_group || G_STR_EMPTY (name)) {
+		g_free (name);
+		return;
+	}
+
+	if (!(priv->features & EMPATHY_CONTACT_LIST_FEATURE_GROUPS_SAVE) ||
+	    empathy_contact_group_get_expanded (name)) {
+		g_signal_handlers_block_by_func (view,
+						 contact_list_view_row_expand_or_collapse_cb,
+						 GINT_TO_POINTER (TRUE));
+		gtk_tree_view_expand_row (GTK_TREE_VIEW (view), path, TRUE);
+		g_signal_handlers_unblock_by_func (view,
+						   contact_list_view_row_expand_or_collapse_cb,
+						   GINT_TO_POINTER (TRUE));
+	} else {
+		g_signal_handlers_block_by_func (view,
+						 contact_list_view_row_expand_or_collapse_cb,
+						 GINT_TO_POINTER (FALSE));
+		gtk_tree_view_collapse_row (GTK_TREE_VIEW (view), path);
+		g_signal_handlers_unblock_by_func (view,
+						   contact_list_view_row_expand_or_collapse_cb,
+						   GINT_TO_POINTER (FALSE));
+	}
+
+	g_free (name);
+}
+
+static void
+contact_list_view_setup (EmpathyContactListView *view)
+{
+	EmpathyContactListViewPriv *priv;
+	GtkCellRenderer           *cell;
+	GtkTreeViewColumn         *col;
+	gint                       i;
+
+	priv = GET_PRIV (view);
+
+	gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (view),
+					     empathy_contact_list_store_search_equal_func,
+					     NULL, NULL);
+
+	g_signal_connect (priv->store, "row-has-child-toggled",
+			  G_CALLBACK (contact_list_view_row_has_child_toggled_cb),
+			  view);
+	gtk_tree_view_set_model (GTK_TREE_VIEW (view),
+				 GTK_TREE_MODEL (priv->store));
+
+	/* Setup view */
+	g_object_set (view,
+		      "headers-visible", FALSE,
+		      "reorderable", TRUE,
+		      "show-expanders", FALSE,
+		      NULL);
+
+	col = gtk_tree_view_column_new ();
+
+	/* State */
+	cell = gtk_cell_renderer_pixbuf_new ();
+	gtk_tree_view_column_pack_start (col, cell, FALSE);
+	gtk_tree_view_column_set_cell_data_func (
+		col, cell,
+		(GtkTreeCellDataFunc) contact_list_view_pixbuf_cell_data_func,
+		view, NULL);
+
 	g_object_set (cell,
-		      "visible", !is_group && can_voip,
-		      "icon-name", EMPATHY_IMAGE_VOIP,
+		      "xpad", 5,
+		      "ypad", 1,
+		      "visible", FALSE,
 		      NULL);
 
-	contact_list_view_cell_set_background (view, cell, is_group, is_active);
+	/* Name */
+	cell = empathy_cell_renderer_text_new ();
+	gtk_tree_view_column_pack_start (col, cell, TRUE);
+	gtk_tree_view_column_set_cell_data_func (
+		col, cell,
+		(GtkTreeCellDataFunc) contact_list_view_text_cell_data_func,
+		view, NULL);
+
+	gtk_tree_view_column_add_attribute (col, cell,
+					    "name", EMPATHY_CONTACT_LIST_STORE_COL_NAME);
+	gtk_tree_view_column_add_attribute (col, cell,
+					    "status", EMPATHY_CONTACT_LIST_STORE_COL_STATUS);
+	gtk_tree_view_column_add_attribute (col, cell,
+					    "is_group", EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP);
+
+	/* Voip Capability Icon */
+	cell = empathy_cell_renderer_activatable_new ();
+	gtk_tree_view_column_pack_start (col, cell, FALSE);
+	gtk_tree_view_column_set_cell_data_func (
+		col, cell,
+		(GtkTreeCellDataFunc) contact_list_view_voip_cell_data_func,
+		view, NULL);
+
+	g_object_set (cell,
+		      "visible", FALSE,
+		      NULL);
+
+	g_signal_connect (cell, "path-activated",
+			  G_CALLBACK (contact_list_view_voip_activated_cb),
+			  view);
+
+	/* Avatar */
+	cell = gtk_cell_renderer_pixbuf_new ();
+	gtk_tree_view_column_pack_start (col, cell, FALSE);
+	gtk_tree_view_column_set_cell_data_func (
+		col, cell,
+		(GtkTreeCellDataFunc) contact_list_view_avatar_cell_data_func,
+		view, NULL);
+
+	g_object_set (cell,
+		      "xpad", 0,
+		      "ypad", 0,
+		      "visible", FALSE,
+		      "width", 32,
+		      "height", 32,
+		      NULL);
+
+	/* Expander */
+	cell = empathy_cell_renderer_expander_new ();
+	gtk_tree_view_column_pack_end (col, cell, FALSE);
+	gtk_tree_view_column_set_cell_data_func (
+		col, cell,
+		(GtkTreeCellDataFunc) contact_list_view_expander_cell_data_func,
+		view, NULL);
+
+	/* Actually add the column now we have added all cell renderers */
+	gtk_tree_view_append_column (GTK_TREE_VIEW (view), col);
+
+	/* Drag & Drop. */
+	for (i = 0; i < G_N_ELEMENTS (drag_types_dest); ++i) {
+		drag_atoms_dest[i] = gdk_atom_intern (drag_types_dest[i].target,
+						      FALSE);
+	}
+
+	for (i = 0; i < G_N_ELEMENTS (drag_types_source); ++i) {
+		drag_atoms_source[i] = gdk_atom_intern (drag_types_source[i].target,
+							FALSE);
+	}
+}
+
+static void
+contact_list_view_finalize (GObject *object)
+{
+	EmpathyContactListViewPriv *priv;
+
+	priv = GET_PRIV (object);
+
+	if (priv->store) {
+		g_object_unref (priv->store);
+	}
+
+	G_OBJECT_CLASS (empathy_contact_list_view_parent_class)->finalize (object);
+}
+
+static void
+contact_list_view_get_property (GObject    *object,
+				guint       param_id,
+				GValue     *value,
+				GParamSpec *pspec)
+{
+	EmpathyContactListViewPriv *priv;
+
+	priv = GET_PRIV (object);
+
+	switch (param_id) {
+	case PROP_FEATURES:
+		g_value_set_flags (value, priv->features);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	};
+}
+
+static void
+contact_list_view_set_property (GObject      *object,
+				guint         param_id,
+				const GValue *value,
+				GParamSpec   *pspec)
+{
+	EmpathyContactListView     *view = EMPATHY_CONTACT_LIST_VIEW (object);
+	EmpathyContactListViewPriv *priv;
+
+	priv = GET_PRIV (object);
+
+	switch (param_id) {
+	case PROP_FEATURES:
+		empathy_contact_list_view_set_features (view, g_value_get_flags (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	};
+}
+
+static void
+empathy_contact_list_view_class_init (EmpathyContactListViewClass *klass)
+{
+	GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+	object_class->finalize = contact_list_view_finalize;
+	object_class->get_property = contact_list_view_get_property;
+	object_class->set_property = contact_list_view_set_property;
+
+	widget_class->drag_data_received = contact_list_view_drag_data_received;
+	widget_class->drag_drop          = contact_list_view_drag_drop;
+	widget_class->drag_begin         = contact_list_view_drag_begin;
+	widget_class->drag_data_get      = contact_list_view_drag_data_get;
+	widget_class->drag_end           = contact_list_view_drag_end;
+	/* FIXME: noticed but when you drag the row over the treeview
+	 * fast, it seems to stop redrawing itself, if we don't
+	 * connect this signal, all is fine.
+	 */
+	widget_class->drag_motion        = contact_list_view_drag_motion;
+
+	signals[DRAG_CONTACT_RECEIVED] =
+		g_signal_new ("drag-contact-received",
+			      G_OBJECT_CLASS_TYPE (klass),
+			      G_SIGNAL_RUN_LAST,
+			      0,
+			      NULL, NULL,
+			      _empathy_gtk_marshal_VOID__OBJECT_STRING_STRING,
+			      G_TYPE_NONE,
+			      3, EMPATHY_TYPE_CONTACT, G_TYPE_STRING, G_TYPE_STRING);
+
+	g_object_class_install_property (object_class,
+					 PROP_FEATURES,
+					 g_param_spec_flags ("features",
+							     "Features of the view",
+							     "Falgs for all enabled features",
+							      EMPATHY_TYPE_CONTACT_LIST_FEATURES,
+							      0,
+							      G_PARAM_READWRITE));
+
+	g_type_class_add_private (object_class, sizeof (EmpathyContactListViewPriv));
+}
+
+static void
+empathy_contact_list_view_init (EmpathyContactListView *view)
+{
+	/* Get saved group states. */
+	empathy_contact_groups_get_all ();
+
+	gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (view), 
+					      empathy_contact_list_store_row_separator_func,
+					      NULL, NULL);
+
+	/* Connect to tree view signals rather than override. */
+	g_signal_connect (view,
+			  "button-press-event",
+			  G_CALLBACK (contact_list_view_button_press_event_cb),
+			  NULL);
+	g_signal_connect (view,
+			  "row-activated",
+			  G_CALLBACK (contact_list_view_row_activated_cb),
+			  NULL);
+	g_signal_connect (view,
+			  "row-expanded",
+			  G_CALLBACK (contact_list_view_row_expand_or_collapse_cb),
+			  GINT_TO_POINTER (TRUE));
+	g_signal_connect (view,
+			  "row-collapsed",
+			  G_CALLBACK (contact_list_view_row_expand_or_collapse_cb),
+			  GINT_TO_POINTER (FALSE));
+}
+
+EmpathyContactListView *
+empathy_contact_list_view_new (EmpathyContactListStore    *store,
+			       EmpathyContactListFeatures  features)
+{
+	EmpathyContactListView     *view;
+	EmpathyContactListViewPriv *priv;
+
+	g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store), NULL);
+	
+	view = g_object_new (EMPATHY_TYPE_CONTACT_LIST_VIEW,
+			    "features", features,
+			    NULL);
+
+	priv = GET_PRIV (view);
+	priv->store = g_object_ref (store);
+	contact_list_view_setup (EMPATHY_CONTACT_LIST_VIEW (view));
+
+	return view;
+}
+
+void
+empathy_contact_list_view_set_features (EmpathyContactListView     *view,
+					EmpathyContactListFeatures  features)
+{
+	EmpathyContactListViewPriv *priv = GET_PRIV (view);
+
+	g_return_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view));
+
+	priv->features = features;
+
+	/* Update DnD source/dest */
+	if (features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_DRAG) {
+		gtk_drag_source_set (GTK_WIDGET (view),
+				     GDK_BUTTON1_MASK,
+				     drag_types_source,
+				     G_N_ELEMENTS (drag_types_source),
+				     GDK_ACTION_MOVE | GDK_ACTION_COPY);
+	} else {
+		gtk_drag_source_unset (GTK_WIDGET (view));
+
+	}
+
+	if (features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_DROP) {
+		gtk_drag_dest_set (GTK_WIDGET (view),
+				   GTK_DEST_DEFAULT_ALL,
+				   drag_types_dest,
+				   G_N_ELEMENTS (drag_types_dest),
+				   GDK_ACTION_MOVE | GDK_ACTION_COPY);
+	} else {
+		/* FIXME: URI could still be  droped depending on FT feature */
+		gtk_drag_dest_unset (GTK_WIDGET (view));
+	}
+
+	g_object_notify (G_OBJECT (view), "features");
+}
+
+EmpathyContactListFeatures
+empathy_contact_list_view_get_features (EmpathyContactListView  *view)
+{
+	EmpathyContactListViewPriv *priv = GET_PRIV (view);
+
+	g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), FALSE);
+
+	return priv->features;
 }
 
-static void
-contact_list_view_avatar_cell_data_func (GtkTreeViewColumn     *tree_column,
-					 GtkCellRenderer       *cell,
-					 GtkTreeModel          *model,
-					 GtkTreeIter           *iter,
-					 EmpathyContactListView *view)
+EmpathyContact *
+empathy_contact_list_view_get_selected (EmpathyContactListView *view)
 {
-	GdkPixbuf *pixbuf;
-	gboolean   show_avatar;
-	gboolean   is_group;
-	gboolean   is_active;
+	EmpathyContactListViewPriv *priv;
+	GtkTreeSelection          *selection;
+	GtkTreeIter                iter;
+	GtkTreeModel              *model;
+	EmpathyContact             *contact;
 
-	gtk_tree_model_get (model, iter,
-			    EMPATHY_CONTACT_LIST_STORE_COL_PIXBUF_AVATAR, &pixbuf,
-			    EMPATHY_CONTACT_LIST_STORE_COL_PIXBUF_AVATAR_VISIBLE, &show_avatar,
-			    EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
-			    EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
-			    -1);
+	g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), NULL);
 
-	g_object_set (cell,
-		      "visible", !is_group && show_avatar,
-		      "pixbuf", pixbuf,
-		      NULL);
+	priv = GET_PRIV (view);
 
-	if (pixbuf) {
-		g_object_unref (pixbuf);
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+	if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
+		return NULL;
 	}
 
-	contact_list_view_cell_set_background (view, cell, is_group, is_active);
+	gtk_tree_model_get (model, &iter,
+			    EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
+			    -1);
+
+	return contact;
 }
 
-static void
-contact_list_view_text_cell_data_func (GtkTreeViewColumn     *tree_column,
-				       GtkCellRenderer       *cell,
-				       GtkTreeModel          *model,
-				       GtkTreeIter           *iter,
-				       EmpathyContactListView *view)
+gchar *
+empathy_contact_list_view_get_selected_group (EmpathyContactListView *view)
 {
-	gboolean is_group;
-	gboolean is_active;
-	gboolean show_status;
-
-	gtk_tree_model_get (model, iter,
-			    EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
-			    EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
-			    EMPATHY_CONTACT_LIST_STORE_COL_STATUS_VISIBLE, &show_status,
-			    -1);
+	EmpathyContactListViewPriv *priv;
+	GtkTreeSelection          *selection;
+	GtkTreeIter                iter;
+	GtkTreeModel              *model;
+	gboolean                   is_group;
+	gchar                     *name;
 
-	g_object_set (cell,
-		      "show-status", show_status,
-		      NULL);
+	g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), NULL);
 
-	contact_list_view_cell_set_background (view, cell, is_group, is_active);
-}
+	priv = GET_PRIV (view);
 
-static void
-contact_list_view_expander_cell_data_func (GtkTreeViewColumn     *column,
-					   GtkCellRenderer       *cell,
-					   GtkTreeModel          *model,
-					   GtkTreeIter           *iter,
-					   EmpathyContactListView *view)
-{
-	gboolean is_group;
-	gboolean is_active;
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+	if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
+		return NULL;
+	}
 
-	gtk_tree_model_get (model, iter,
+	gtk_tree_model_get (model, &iter,
 			    EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
-			    EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
+			    EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
 			    -1);
 
-	if (gtk_tree_model_iter_has_child (model, iter)) {
-		GtkTreePath *path;
-		gboolean     row_expanded;
-
-		path = gtk_tree_model_get_path (model, iter);
-		row_expanded = gtk_tree_view_row_expanded (GTK_TREE_VIEW (column->tree_view), path);
-		gtk_tree_path_free (path);
-
-		g_object_set (cell,
-			      "visible", TRUE,
-			      "expander-style", row_expanded ? GTK_EXPANDER_EXPANDED : GTK_EXPANDER_COLLAPSED,
-			      NULL);
-	} else {
-		g_object_set (cell, "visible", FALSE, NULL);
+	if (!is_group) {
+		g_free (name);
+		return NULL;
 	}
 
-	contact_list_view_cell_set_background (view, cell, is_group, is_active);
+	return name;
 }
 
 static gboolean
@@ -1284,157 +1334,3 @@
 	return menu;
 }
 
-static gboolean
-contact_list_view_button_press_event_cb (EmpathyContactListView *view,
-					 GdkEventButton        *event,
-					 gpointer               user_data)
-{
-	EmpathyContactListViewPriv *priv;
-	EmpathyContact             *contact;
-	GtkTreePath               *path;
-	GtkTreeSelection          *selection;
-	GtkTreeModel              *model;
-	GtkTreeIter                iter;
-	gboolean                   row_exists;
-	GtkWidget                 *menu;
-
-	priv = GET_PRIV (view);
-
-	if (event->button != 3) {
-		return FALSE;
-	}
-
-	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
-	model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
-
-	gtk_widget_grab_focus (GTK_WIDGET (view));
-
-	row_exists = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (view),
-						    event->x, event->y,
-						    &path,
-						    NULL, NULL, NULL);
-	if (!row_exists) {
-		return FALSE;
-	}
-
-	gtk_tree_selection_unselect_all (selection);
-	gtk_tree_selection_select_path (selection, path);
-
-	gtk_tree_model_get_iter (model, &iter, path);
-	gtk_tree_path_free (path);
-
-	gtk_tree_model_get (model, &iter,
-			    EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
-			    -1);
-
-	if (contact) {
-		menu = empathy_contact_list_view_get_contact_menu (view, contact);
-		g_object_unref (contact);
-	} else {
-		menu = empathy_contact_list_view_get_group_menu (view);
-	}
-
-	if (!menu) {
-		return FALSE;
-	}
-
-	gtk_widget_show (menu);
-
-	gtk_menu_popup (GTK_MENU (menu),
-			NULL, NULL, NULL, NULL,
-			event->button, event->time);
-
-	return TRUE;
-}
-
-static void
-contact_list_view_row_activated_cb (EmpathyContactListView *view,
-				    GtkTreePath            *path,
-				    GtkTreeViewColumn      *col,
-				    gpointer                user_data)
-{
-	EmpathyContactListViewPriv *priv = GET_PRIV (view);
-	EmpathyContact             *contact;
-	GtkTreeModel               *model;
-	GtkTreeIter                 iter;
-
-	if (!(priv->features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_CHAT)) {
-		return;
-	}
-
-	model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
-
-	gtk_tree_model_get_iter (model, &iter, path);
-	gtk_tree_model_get (model, &iter,
-			    EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
-			    -1);
-
-	if (contact) {
-		empathy_chat_with_contact (contact);
-		g_object_unref (contact);
-	}
-}
-
-static void
-contact_list_view_voip_activated_cb (EmpathyCellRendererActivatable *cell,
-				     const gchar                    *path_string,
-				     EmpathyContactListView         *view)
-{
-	EmpathyContactListViewPriv *priv = GET_PRIV (view);
-	GtkTreeModel               *model;
-	GtkTreeIter                 iter;
-	EmpathyContact             *contact;
-
-	if (!(priv->features & EMPATHY_CONTACT_LIST_FEATURE_CONTACT_CALL)) {
-		return;
-	}
-
-	model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
-	if (!gtk_tree_model_get_iter_from_string (model, &iter, path_string)) {
-		return;
-	}
-
-	gtk_tree_model_get (model, &iter,
-			    EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
-			    -1);
-
-	if (contact) {
-		contact_list_view_voip_activated (view, contact);
-		g_object_unref (contact);
-	}
-}
-
-static void
-contact_list_view_row_expand_or_collapse_cb (EmpathyContactListView *view,
-					     GtkTreeIter           *iter,
-					     GtkTreePath           *path,
-					     gpointer               user_data)
-{
-	EmpathyContactListViewPriv *priv = GET_PRIV (view);
-	GtkTreeModel               *model;
-	gchar                      *name;
-	gboolean                    expanded;
-
-	if (!(priv->features & EMPATHY_CONTACT_LIST_FEATURE_GROUPS_SAVE)) {
-		return;
-	}
-
-	model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
-
-	gtk_tree_model_get (model, iter,
-			    EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
-			    -1);
-
-	expanded = GPOINTER_TO_INT (user_data);
-	empathy_contact_group_set_expanded (name, expanded);
-
-	g_free (name);
-}
-
-static void
-contact_list_view_voip_activated (EmpathyContactListView *view,
-				  EmpathyContact         *contact)
-{
-	empathy_call_with_contact (contact);
-}
-



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