]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-contact-list-view.c
6e5ab85366c3067c7ee594c1f0e0c6dd4c10cf67
[empathy.git] / libempathy-gtk / empathy-contact-list-view.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2005-2007 Imendio AB
4  * Copyright (C) 2007 Collabora Ltd.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public
17  * License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  *
21  * Authors: Mikael Hallendal <micke@imendio.com>
22  *          Martyn Russell <martyn@imendio.com>
23  *          Xavier Claessens <xclaesse@gmail.com>
24  */
25
26 #include "config.h"
27
28 #include <string.h>
29
30 #include <glib/gi18n.h>
31 #include <gtk/gtk.h>
32 #include <glade/glade.h>
33
34 #include <libtelepathy/tp-helpers.h>
35
36 #include <libmissioncontrol/mc-account.h>
37 #include <libmissioncontrol/mission-control.h>
38
39 #include <libempathy/empathy-contact-factory.h>
40 #include <libempathy/empathy-contact-list.h>
41 #include <libempathy/empathy-log-manager.h>
42 #include <libempathy/empathy-tp-group.h>
43 #include <libempathy/empathy-debug.h>
44 #include <libempathy/empathy-utils.h>
45 #include <libempathy/empathy-marshal.h>
46
47 #include "empathy-contact-list-view.h"
48 #include "empathy-contact-list-store.h"
49 #include "empathy-images.h"
50 #include "empathy-contact-groups.h"
51 #include "empathy-cell-renderer-expander.h"
52 #include "empathy-cell-renderer-text.h"
53 #include "empathy-cell-renderer-activatable.h"
54 #include "empathy-ui-utils.h"
55 #include "empathy-contact-dialogs.h"
56 //#include "empathy-chat-invite.h"
57 //#include "empathy-ft-window.h"
58 #include "empathy-log-window.h"
59
60 #define DEBUG_DOMAIN "ContactListView"
61
62 /* Flashing delay for icons (milliseconds). */
63 #define FLASH_TIMEOUT 500
64
65 /* Active users are those which have recently changed state
66  * (e.g. online, offline or from normal to a busy state).
67  */
68
69 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EMPATHY_TYPE_CONTACT_LIST_VIEW, EmpathyContactListViewPriv))
70
71 struct _EmpathyContactListViewPriv {
72         EmpathyContactListStore *store;
73         GtkUIManager            *ui;
74         GtkTreeRowReference     *drag_row;
75         gboolean                 interactive;
76 };
77
78 typedef struct {
79         EmpathyContactListView *view;
80         GtkTreePath           *path;
81         guint                  timeout_id;
82 } DragMotionData;
83
84 typedef struct {
85         EmpathyContactListView *view;
86         EmpathyContact         *contact;
87         gboolean               remove;
88 } ShowActiveData;
89
90 static void        empathy_contact_list_view_class_init         (EmpathyContactListViewClass *klass);
91 static void        empathy_contact_list_view_init               (EmpathyContactListView      *list);
92 static void        contact_list_view_finalize                  (GObject                    *object);
93 static void        contact_list_view_get_property              (GObject                    *object,
94                                                                 guint                       param_id,
95                                                                 GValue                     *value,
96                                                                 GParamSpec                 *pspec);
97 static void        contact_list_view_set_property              (GObject                    *object,
98                                                                 guint                       param_id,
99                                                                 const GValue               *value,
100                                                                 GParamSpec                 *pspec);
101 static void        contact_list_view_setup                     (EmpathyContactListView      *view);
102 static void        contact_list_view_row_has_child_toggled_cb  (GtkTreeModel               *model,
103                                                                 GtkTreePath                *path,
104                                                                 GtkTreeIter                *iter,
105                                                                 EmpathyContactListView      *view);
106 static void        contact_list_view_drag_data_received        (GtkWidget                  *widget,
107                                                                 GdkDragContext             *context,
108                                                                 gint                        x,
109                                                                 gint                        y,
110                                                                 GtkSelectionData           *selection,
111                                                                 guint                       info,
112                                                                 guint                       time);
113 static gboolean    contact_list_view_drag_motion               (GtkWidget                  *widget,
114                                                                 GdkDragContext             *context,
115                                                                 gint                        x,
116                                                                 gint                        y,
117                                                                 guint                       time);
118 static gboolean    contact_list_view_drag_motion_cb            (DragMotionData             *data);
119 static void        contact_list_view_drag_begin                (GtkWidget                  *widget,
120                                                                 GdkDragContext             *context);
121 static void        contact_list_view_drag_data_get             (GtkWidget                  *widget,
122                                                                 GdkDragContext             *context,
123                                                                 GtkSelectionData           *selection,
124                                                                 guint                       info,
125                                                                 guint                       time);
126 static void        contact_list_view_drag_end                  (GtkWidget                  *widget,
127                                                                 GdkDragContext             *context);
128 static gboolean    contact_list_view_drag_drop                 (GtkWidget                  *widget,
129                                                                 GdkDragContext             *drag_context,
130                                                                 gint                        x,
131                                                                 gint                        y,
132                                                                 guint                       time);
133 static void        contact_list_view_cell_set_background       (EmpathyContactListView      *view,
134                                                                 GtkCellRenderer            *cell,
135                                                                 gboolean                    is_group,
136                                                                 gboolean                    is_active);
137 static void        contact_list_view_pixbuf_cell_data_func     (GtkTreeViewColumn          *tree_column,
138                                                                 GtkCellRenderer            *cell,
139                                                                 GtkTreeModel               *model,
140                                                                 GtkTreeIter                *iter,
141                                                                 EmpathyContactListView     *view);
142 #ifdef HAVE_VOIP
143 static void        contact_list_view_voip_cell_data_func       (GtkTreeViewColumn          *tree_column,
144                                                                 GtkCellRenderer            *cell,
145                                                                 GtkTreeModel               *model,
146                                                                 GtkTreeIter                *iter,
147                                                                 EmpathyContactListView     *view);
148 #endif
149 static void        contact_list_view_avatar_cell_data_func     (GtkTreeViewColumn          *tree_column,
150                                                                 GtkCellRenderer            *cell,
151                                                                 GtkTreeModel               *model,
152                                                                 GtkTreeIter                *iter,
153                                                                 EmpathyContactListView      *view);
154 static void        contact_list_view_text_cell_data_func       (GtkTreeViewColumn          *tree_column,
155                                                                 GtkCellRenderer            *cell,
156                                                                 GtkTreeModel               *model,
157                                                                 GtkTreeIter                *iter,
158                                                                 EmpathyContactListView      *view);
159 static void        contact_list_view_expander_cell_data_func   (GtkTreeViewColumn          *column,
160                                                                 GtkCellRenderer            *cell,
161                                                                 GtkTreeModel               *model,
162                                                                 GtkTreeIter                *iter,
163                                                                 EmpathyContactListView      *view);
164 static GtkWidget * contact_list_view_get_contact_menu          (EmpathyContactListView      *view,
165                                                                 gboolean                    can_send_file,
166                                                                 gboolean                    can_show_log,
167                                                                 gboolean                    can_voip);
168 static gboolean    contact_list_view_button_press_event_cb     (EmpathyContactListView      *view,
169                                                                 GdkEventButton             *event,
170                                                                 gpointer                    user_data);
171 static void        contact_list_view_row_activated_cb          (EmpathyContactListView      *view,
172                                                                 GtkTreePath                *path,
173                                                                 GtkTreeViewColumn          *col,
174                                                                 gpointer                    user_data);
175 #ifdef HAVE_VOIP
176 static void        contact_list_view_voip_activated_cb         (EmpathyCellRendererActivatable *cell,
177                                                                 const gchar                *path_string,
178                                                                 EmpathyContactListView     *view);
179 #endif
180 static void        contact_list_view_row_expand_or_collapse_cb (EmpathyContactListView      *view,
181                                                                 GtkTreeIter                *iter,
182                                                                 GtkTreePath                *path,
183                                                                 gpointer                    user_data);
184 static void        contact_list_view_action_cb                 (GtkAction                  *action,
185                                                                 EmpathyContactListView      *view);
186 static void        contact_list_view_voip_activated            (EmpathyContactListView      *view,
187                                                                 EmpathyContact              *contact);
188
189 enum {
190         PROP_0,
191         PROP_INTERACTIVE
192 };
193
194 static const GtkActionEntry entries[] = {
195         { "ContactMenu", NULL,
196           N_("_Contact"), NULL, NULL,
197           NULL
198         },
199         { "GroupMenu", NULL,
200           N_("_Group"),NULL, NULL,
201           NULL
202         },
203         { "Chat", EMPATHY_IMAGE_MESSAGE,
204           N_("_Chat"), NULL, N_("Chat with contact"),
205           G_CALLBACK (contact_list_view_action_cb)
206         },
207         { "Information", EMPATHY_IMAGE_CONTACT_INFORMATION,
208           N_("Infor_mation"), "<control>I", N_("View contact information"),
209           G_CALLBACK (contact_list_view_action_cb)
210         },
211         { "Rename", NULL,
212           N_("Re_name"), NULL, N_("Rename"),
213           G_CALLBACK (contact_list_view_action_cb)
214         },
215         { "Edit", GTK_STOCK_EDIT,
216           N_("_Edit"), NULL, N_("Edit the groups and name for this contact"),
217           G_CALLBACK (contact_list_view_action_cb)
218         },
219         { "Remove", GTK_STOCK_REMOVE,
220           N_("_Remove"), NULL, N_("Remove contact"),
221           G_CALLBACK (contact_list_view_action_cb)
222         },
223         { "Invite", EMPATHY_IMAGE_GROUP_MESSAGE,
224           N_("_Invite to Chat Room"), NULL, N_("Invite to a currently open chat room"),
225           G_CALLBACK (contact_list_view_action_cb)
226         },
227         { "SendFile", NULL,
228           N_("_Send File..."), NULL, N_("Send a file"),
229           G_CALLBACK (contact_list_view_action_cb)
230         },
231         { "Log", EMPATHY_IMAGE_LOG,
232           N_("_View Previous Conversations"), NULL, N_("View previous conversations with this contact"),
233           G_CALLBACK (contact_list_view_action_cb)
234         },
235 #ifdef HAVE_VOIP
236         { "Call", EMPATHY_IMAGE_VOIP,
237           N_("_Call"), NULL, N_("Start a voice or video conversation with this contact"),
238           G_CALLBACK (contact_list_view_action_cb)
239         },
240 #endif
241 };
242
243 static guint n_entries = G_N_ELEMENTS (entries);
244
245 static const gchar *ui_info =
246         "<ui>"
247         "  <popup name='Contact'>"
248         "    <menuitem action='Chat'/>"
249 #ifdef HAVE_VOIP
250         "    <menuitem action='Call'/>"
251 #endif
252         "    <menuitem action='Log'/>"
253         "    <menuitem action='SendFile'/>"
254         "    <separator/>"
255         "    <menuitem action='Invite'/>"
256         "    <separator/>"
257         "    <menuitem action='Edit'/>"
258         "    <menuitem action='Remove'/>"
259         "    <separator/>"
260         "    <menuitem action='Information'/>"
261         "  </popup>"
262         "  <popup name='Group'>"
263         "    <menuitem action='Rename'/>"
264         "    <menuitem action='Remove'/>"
265         "  </popup>"
266         "</ui>";
267
268 enum DndDragType {
269         DND_DRAG_TYPE_CONTACT_ID,
270         DND_DRAG_TYPE_URL,
271         DND_DRAG_TYPE_STRING,
272 };
273
274 static const GtkTargetEntry drag_types_dest[] = {
275         { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
276         { "text/uri-list",   0, DND_DRAG_TYPE_URL },
277         { "text/plain",      0, DND_DRAG_TYPE_STRING },
278         { "STRING",          0, DND_DRAG_TYPE_STRING },
279 };
280
281 static const GtkTargetEntry drag_types_source[] = {
282         { "text/contact-id", 0, DND_DRAG_TYPE_CONTACT_ID },
283 };
284
285 static GdkAtom drag_atoms_dest[G_N_ELEMENTS (drag_types_dest)];
286 static GdkAtom drag_atoms_source[G_N_ELEMENTS (drag_types_source)];
287
288 enum {
289         DRAG_CONTACT_RECEIVED,
290         LAST_SIGNAL
291 };
292
293 static guint signals[LAST_SIGNAL];
294
295 G_DEFINE_TYPE (EmpathyContactListView, empathy_contact_list_view, GTK_TYPE_TREE_VIEW);
296
297 static void
298 empathy_contact_list_view_class_init (EmpathyContactListViewClass *klass)
299 {
300         GObjectClass   *object_class = G_OBJECT_CLASS (klass);
301         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
302
303         object_class->finalize = contact_list_view_finalize;
304         object_class->get_property = contact_list_view_get_property;
305         object_class->set_property = contact_list_view_set_property;
306
307         widget_class->drag_data_received = contact_list_view_drag_data_received;
308         widget_class->drag_drop          = contact_list_view_drag_drop;
309         widget_class->drag_begin         = contact_list_view_drag_begin;
310         widget_class->drag_data_get      = contact_list_view_drag_data_get;
311         widget_class->drag_end           = contact_list_view_drag_end;
312         /* FIXME: noticed but when you drag the row over the treeview
313          * fast, it seems to stop redrawing itself, if we don't
314          * connect this signal, all is fine.
315          */
316         widget_class->drag_motion        = contact_list_view_drag_motion;
317
318         signals[DRAG_CONTACT_RECEIVED] =
319                 g_signal_new ("drag-contact-received",
320                               G_OBJECT_CLASS_TYPE (klass),
321                               G_SIGNAL_RUN_LAST,
322                               0,
323                               NULL, NULL,
324                               empathy_marshal_VOID__OBJECT_STRING_STRING,
325                               G_TYPE_NONE,
326                               3, EMPATHY_TYPE_CONTACT, G_TYPE_STRING, G_TYPE_STRING);
327
328         g_object_class_install_property (object_class,
329                                          PROP_INTERACTIVE,
330                                          g_param_spec_boolean ("interactive",
331                                                                "View is interactive",
332                                                                "Is the view interactive",
333                                                                FALSE,
334                                                                G_PARAM_READWRITE));
335
336         g_type_class_add_private (object_class, sizeof (EmpathyContactListViewPriv));
337 }
338
339 static void
340 empathy_contact_list_view_init (EmpathyContactListView *view)
341 {
342         EmpathyContactListViewPriv *priv;
343         GtkActionGroup            *action_group;
344         GError                    *error = NULL;
345
346         priv = GET_PRIV (view);
347
348         /* Get saved group states. */
349         empathy_contact_groups_get_all ();
350
351         /* Set up UI Manager */
352         priv->ui = gtk_ui_manager_new ();
353
354         action_group = gtk_action_group_new ("Actions");
355         gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
356         gtk_action_group_add_actions (action_group, entries, n_entries, view);
357         gtk_ui_manager_insert_action_group (priv->ui, action_group, 0);
358
359         if (!gtk_ui_manager_add_ui_from_string (priv->ui, ui_info, -1, &error)) {
360                 g_warning ("Could not build contact menus from string:'%s'", error->message);
361                 g_error_free (error);
362         }
363
364         g_object_unref (action_group);
365
366         gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (view), 
367                                               empathy_contact_list_store_row_separator_func,
368                                               NULL, NULL);
369
370         /* Connect to tree view signals rather than override. */
371         g_signal_connect (view,
372                           "button-press-event",
373                           G_CALLBACK (contact_list_view_button_press_event_cb),
374                           NULL);
375         g_signal_connect (view,
376                           "row-activated",
377                           G_CALLBACK (contact_list_view_row_activated_cb),
378                           NULL);
379         g_signal_connect (view,
380                           "row-expanded",
381                           G_CALLBACK (contact_list_view_row_expand_or_collapse_cb),
382                           GINT_TO_POINTER (TRUE));
383         g_signal_connect (view,
384                           "row-collapsed",
385                           G_CALLBACK (contact_list_view_row_expand_or_collapse_cb),
386                           GINT_TO_POINTER (FALSE));
387 }
388
389 static void
390 contact_list_view_finalize (GObject *object)
391 {
392         EmpathyContactListViewPriv *priv;
393
394         priv = GET_PRIV (object);
395
396         if (priv->ui) {
397                 g_object_unref (priv->ui);
398         }
399         if (priv->store) {
400                 g_object_unref (priv->store);
401         }
402
403         G_OBJECT_CLASS (empathy_contact_list_view_parent_class)->finalize (object);
404 }
405
406 static void
407 contact_list_view_get_property (GObject    *object,
408                                 guint       param_id,
409                                 GValue     *value,
410                                 GParamSpec *pspec)
411 {
412         EmpathyContactListViewPriv *priv;
413
414         priv = GET_PRIV (object);
415
416         switch (param_id) {
417         case PROP_INTERACTIVE:
418                 g_value_set_boolean (value, priv->interactive);
419                 break;
420         default:
421                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
422                 break;
423         };
424 }
425
426 static void
427 contact_list_view_set_property (GObject      *object,
428                                 guint         param_id,
429                                 const GValue *value,
430                                 GParamSpec   *pspec)
431 {
432         EmpathyContactListView     *view = EMPATHY_CONTACT_LIST_VIEW (object);
433         EmpathyContactListViewPriv *priv;
434
435         priv = GET_PRIV (object);
436
437         switch (param_id) {
438         case PROP_INTERACTIVE:
439                 empathy_contact_list_view_set_interactive (view, g_value_get_boolean (value));
440                 break;
441         default:
442                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
443                 break;
444         };
445 }
446
447 EmpathyContactListView *
448 empathy_contact_list_view_new (EmpathyContactListStore *store)
449 {
450         EmpathyContactListViewPriv *priv;
451         EmpathyContactListView     *view;
452         
453         view = g_object_new (EMPATHY_TYPE_CONTACT_LIST_VIEW, NULL);
454         priv = GET_PRIV (view);
455
456         priv->store = g_object_ref (store);
457         contact_list_view_setup (view);
458
459         return view;
460 }
461
462 void
463 empathy_contact_list_view_set_interactive (EmpathyContactListView  *view,
464                                            gboolean                 interactive)
465 {
466         EmpathyContactListViewPriv *priv = GET_PRIV (view);
467
468         g_return_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view));
469
470         priv->interactive = interactive;
471         g_object_notify (G_OBJECT (view), "interactive");
472 }
473
474 gboolean
475 empathy_contact_list_view_get_interactive (EmpathyContactListView  *view)
476 {
477         EmpathyContactListViewPriv *priv = GET_PRIV (view);
478
479         g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), FALSE);
480
481         return priv->interactive;
482 }
483
484 EmpathyContact *
485 empathy_contact_list_view_get_selected (EmpathyContactListView *view)
486 {
487         EmpathyContactListViewPriv *priv;
488         GtkTreeSelection          *selection;
489         GtkTreeIter                iter;
490         GtkTreeModel              *model;
491         EmpathyContact             *contact;
492
493         g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), NULL);
494
495         priv = GET_PRIV (view);
496
497         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
498         if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
499                 return NULL;
500         }
501
502         gtk_tree_model_get (model, &iter,
503                             EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
504                             -1);
505
506         return contact;
507 }
508
509 gchar *
510 empathy_contact_list_view_get_selected_group (EmpathyContactListView *view)
511 {
512         EmpathyContactListViewPriv *priv;
513         GtkTreeSelection          *selection;
514         GtkTreeIter                iter;
515         GtkTreeModel              *model;
516         gboolean                   is_group;
517         gchar                     *name;
518
519         g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), NULL);
520
521         priv = GET_PRIV (view);
522
523         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
524         if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
525                 return NULL;
526         }
527
528         gtk_tree_model_get (model, &iter,
529                             EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
530                             EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
531                             -1);
532
533         if (!is_group) {
534                 g_free (name);
535                 return NULL;
536         }
537
538         return name;
539 }
540
541 GtkWidget *
542 empathy_contact_list_view_get_group_menu (EmpathyContactListView *view)
543 {
544         EmpathyContactListViewPriv *priv;
545         GtkWidget                 *widget;
546
547         g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), NULL);
548
549         priv = GET_PRIV (view);
550
551         widget = gtk_ui_manager_get_widget (priv->ui, "/Group");
552
553         return widget;
554 }
555
556 GtkWidget *
557 empathy_contact_list_view_get_contact_menu (EmpathyContactListView *view,
558                                             EmpathyContact         *contact)
559 {
560         EmpathyLogManager *log_manager;
561         gboolean           can_show_log;
562         gboolean           can_send_file;
563         gboolean           can_voip;
564
565         g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), NULL);
566         g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
567
568         log_manager = empathy_log_manager_new ();
569         can_show_log = empathy_log_manager_exists (log_manager,
570                                                    empathy_contact_get_account (contact),
571                                                    empathy_contact_get_id (contact),
572                                                    FALSE);
573         g_object_unref (log_manager);
574         can_send_file = FALSE;
575         can_voip = empathy_contact_can_voip (contact);
576
577         return contact_list_view_get_contact_menu (view,
578                                                    can_send_file,
579                                                    can_show_log,
580                                                    can_voip);
581 }
582
583 static void
584 contact_list_view_setup (EmpathyContactListView *view)
585 {
586         EmpathyContactListViewPriv *priv;
587         GtkCellRenderer           *cell;
588         GtkTreeViewColumn         *col;
589         gint                       i;
590
591         priv = GET_PRIV (view);
592
593         gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (view),
594                                              empathy_contact_list_store_search_equal_func,
595                                              NULL, NULL);
596
597         g_signal_connect (priv->store, "row-has-child-toggled",
598                           G_CALLBACK (contact_list_view_row_has_child_toggled_cb),
599                           view);
600         gtk_tree_view_set_model (GTK_TREE_VIEW (view),
601                                  GTK_TREE_MODEL (priv->store));
602
603         /* Setup view */
604         g_object_set (view,
605                       "headers-visible", FALSE,
606                       "reorderable", TRUE,
607                       "show-expanders", FALSE,
608                       NULL);
609
610         col = gtk_tree_view_column_new ();
611
612         /* State */
613         cell = gtk_cell_renderer_pixbuf_new ();
614         gtk_tree_view_column_pack_start (col, cell, FALSE);
615         gtk_tree_view_column_set_cell_data_func (
616                 col, cell,
617                 (GtkTreeCellDataFunc) contact_list_view_pixbuf_cell_data_func,
618                 view, NULL);
619
620         g_object_set (cell,
621                       "xpad", 5,
622                       "ypad", 1,
623                       "visible", FALSE,
624                       NULL);
625
626         /* Name */
627         cell = empathy_cell_renderer_text_new ();
628         gtk_tree_view_column_pack_start (col, cell, TRUE);
629         gtk_tree_view_column_set_cell_data_func (
630                 col, cell,
631                 (GtkTreeCellDataFunc) contact_list_view_text_cell_data_func,
632                 view, NULL);
633
634         gtk_tree_view_column_add_attribute (col, cell,
635                                             "name", EMPATHY_CONTACT_LIST_STORE_COL_NAME);
636         gtk_tree_view_column_add_attribute (col, cell,
637                                             "status", EMPATHY_CONTACT_LIST_STORE_COL_STATUS);
638         gtk_tree_view_column_add_attribute (col, cell,
639                                             "is_group", EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP);
640
641 #ifdef HAVE_VOIP
642         /* Voip Capability Icon */
643         cell = empathy_cell_renderer_activatable_new ();
644         gtk_tree_view_column_pack_start (col, cell, FALSE);
645         gtk_tree_view_column_set_cell_data_func (
646                 col, cell,
647                 (GtkTreeCellDataFunc) contact_list_view_voip_cell_data_func,
648                 view, NULL);
649
650         g_object_set (cell,
651                       "visible", FALSE,
652                       NULL);
653
654         g_signal_connect (cell, "path-activated",
655                           G_CALLBACK (contact_list_view_voip_activated_cb),
656                           view);
657 #endif
658
659         /* Avatar */
660         cell = gtk_cell_renderer_pixbuf_new ();
661         gtk_tree_view_column_pack_start (col, cell, FALSE);
662         gtk_tree_view_column_set_cell_data_func (
663                 col, cell,
664                 (GtkTreeCellDataFunc) contact_list_view_avatar_cell_data_func,
665                 view, NULL);
666
667         g_object_set (cell,
668                       "xpad", 0,
669                       "ypad", 0,
670                       "visible", FALSE,
671                       "width", 32,
672                       "height", 32,
673                       NULL);
674
675         /* Expander */
676         cell = empathy_cell_renderer_expander_new ();
677         gtk_tree_view_column_pack_end (col, cell, FALSE);
678         gtk_tree_view_column_set_cell_data_func (
679                 col, cell,
680                 (GtkTreeCellDataFunc) contact_list_view_expander_cell_data_func,
681                 view, NULL);
682
683         /* Actually add the column now we have added all cell renderers */
684         gtk_tree_view_append_column (GTK_TREE_VIEW (view), col);
685
686         /* Drag & Drop. */
687         for (i = 0; i < G_N_ELEMENTS (drag_types_dest); ++i) {
688                 drag_atoms_dest[i] = gdk_atom_intern (drag_types_dest[i].target,
689                                                       FALSE);
690         }
691
692         for (i = 0; i < G_N_ELEMENTS (drag_types_source); ++i) {
693                 drag_atoms_source[i] = gdk_atom_intern (drag_types_source[i].target,
694                                                         FALSE);
695         }
696
697         /* Note: We support the COPY action too, but need to make the
698          * MOVE action the default.
699          */
700         gtk_drag_source_set (GTK_WIDGET (view),
701                              GDK_BUTTON1_MASK,
702                              drag_types_source,
703                              G_N_ELEMENTS (drag_types_source),
704                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
705
706         gtk_drag_dest_set (GTK_WIDGET (view),
707                            GTK_DEST_DEFAULT_ALL,
708                            drag_types_dest,
709                            G_N_ELEMENTS (drag_types_dest),
710                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
711 }
712
713 static void
714 contact_list_view_row_has_child_toggled_cb (GtkTreeModel          *model,
715                                             GtkTreePath           *path,
716                                             GtkTreeIter           *iter,
717                                             EmpathyContactListView *view)
718 {
719         gboolean  is_group = FALSE;
720         gchar    *name = NULL;
721
722         gtk_tree_model_get (model, iter,
723                             EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
724                             EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
725                             -1);
726
727         if (!is_group || G_STR_EMPTY (name)) {
728                 g_free (name);
729                 return;
730         }
731
732         if (empathy_contact_group_get_expanded (name)) {
733                 g_signal_handlers_block_by_func (view,
734                                                  contact_list_view_row_expand_or_collapse_cb,
735                                                  GINT_TO_POINTER (TRUE));
736                 gtk_tree_view_expand_row (GTK_TREE_VIEW (view), path, TRUE);
737                 g_signal_handlers_unblock_by_func (view,
738                                                    contact_list_view_row_expand_or_collapse_cb,
739                                                    GINT_TO_POINTER (TRUE));
740         } else {
741                 g_signal_handlers_block_by_func (view,
742                                                  contact_list_view_row_expand_or_collapse_cb,
743                                                  GINT_TO_POINTER (FALSE));
744                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (view), path);
745                 g_signal_handlers_unblock_by_func (view,
746                                                    contact_list_view_row_expand_or_collapse_cb,
747                                                    GINT_TO_POINTER (FALSE));
748         }
749
750         g_free (name);
751 }
752
753 static void
754 contact_list_view_drag_data_received (GtkWidget         *widget,
755                                       GdkDragContext    *context,
756                                       gint               x,
757                                       gint               y,
758                                       GtkSelectionData  *selection,
759                                       guint              info,
760                                       guint              time)
761 {
762         EmpathyContactListViewPriv *priv;
763         EmpathyContactList         *list;
764         EmpathyContactFactory      *factory;
765         McAccount                  *account;
766         GtkTreeModel               *model;
767         GtkTreePath                *path;
768         GtkTreeViewDropPosition     position;
769         EmpathyContact             *contact = NULL;
770         const gchar                *id;
771         gchar                     **strv;
772         gchar                      *new_group = NULL;
773         gchar                      *old_group = NULL;
774         gboolean                    is_row;
775
776         priv = GET_PRIV (widget);
777
778         id = (const gchar*) selection->data;
779         empathy_debug (DEBUG_DOMAIN, "Received %s%s drag & drop contact from roster with id:'%s'",
780                       context->action == GDK_ACTION_MOVE ? "move" : "",
781                       context->action == GDK_ACTION_COPY ? "copy" : "",
782                       id);
783
784         strv = g_strsplit (id, "/", 2);
785         factory = empathy_contact_factory_new ();
786         account = mc_account_lookup (strv[0]);
787         if (account) {
788                 contact = empathy_contact_factory_get_from_id (factory,
789                                                                account,
790                                                                strv[1]);
791                 g_object_unref (account);
792         }
793         g_object_unref (factory);
794         g_strfreev (strv);
795
796         if (!contact) {
797                 empathy_debug (DEBUG_DOMAIN, "No contact found associated with drag & drop");
798                 return;
799         }
800
801         model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
802
803         /* Get source group information. */
804         if (priv->drag_row) {
805                 path = gtk_tree_row_reference_get_path (priv->drag_row);
806                 if (path) {
807                         old_group = empathy_contact_list_store_get_parent_group (model, path, NULL);
808                         gtk_tree_path_free (path);
809                 }
810         }
811
812         /* Get destination group information. */
813         is_row = gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
814                                                     x,
815                                                     y,
816                                                     &path,
817                                                     &position);
818
819         if (is_row) {
820                 new_group = empathy_contact_list_store_get_parent_group (model, path, NULL);
821                 gtk_tree_path_free (path);
822         }
823
824         empathy_debug (DEBUG_DOMAIN,
825                       "contact %s (%d) dragged from '%s' to '%s'",
826                       empathy_contact_get_id (contact),
827                       empathy_contact_get_handle (contact),
828                       old_group, new_group);
829
830         list = empathy_contact_list_store_get_list_iface (priv->store);
831         if (new_group) {
832                 empathy_contact_list_add_to_group (list, contact, new_group);
833         }
834         if (old_group && context->action == GDK_ACTION_MOVE) {  
835                 empathy_contact_list_remove_from_group (list, contact, old_group);
836         }
837
838         g_free (old_group);
839         g_free (new_group);
840
841         gtk_drag_finish (context, TRUE, FALSE, GDK_CURRENT_TIME);
842 }
843
844 static gboolean
845 contact_list_view_drag_motion (GtkWidget      *widget,
846                                GdkDragContext *context,
847                                gint            x,
848                                gint            y,
849                                guint           time)
850 {
851         static DragMotionData *dm = NULL;
852         GtkTreePath           *path;
853         gboolean               is_row;
854         gboolean               is_different = FALSE;
855         gboolean               cleanup = TRUE;
856
857         is_row = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
858                                                 x,
859                                                 y,
860                                                 &path,
861                                                 NULL,
862                                                 NULL,
863                                                 NULL);
864
865         cleanup &= (!dm);
866
867         if (is_row) {
868                 cleanup &= (dm && gtk_tree_path_compare (dm->path, path) != 0);
869                 is_different = (!dm || (dm && gtk_tree_path_compare (dm->path, path) != 0));
870         } else {
871                 cleanup &= FALSE;
872         }
873
874         if (!is_different && !cleanup) {
875                 return TRUE;
876         }
877
878         if (dm) {
879                 gtk_tree_path_free (dm->path);
880                 if (dm->timeout_id) {
881                         g_source_remove (dm->timeout_id);
882                 }
883
884                 g_free (dm);
885
886                 dm = NULL;
887         }
888
889         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), path)) {
890                 dm = g_new0 (DragMotionData, 1);
891
892                 dm->view = EMPATHY_CONTACT_LIST_VIEW (widget);
893                 dm->path = gtk_tree_path_copy (path);
894
895                 dm->timeout_id = g_timeout_add_seconds (1,
896                         (GSourceFunc) contact_list_view_drag_motion_cb,
897                         dm);
898         }
899
900         return TRUE;
901 }
902
903 static gboolean
904 contact_list_view_drag_motion_cb (DragMotionData *data)
905 {
906         gtk_tree_view_expand_row (GTK_TREE_VIEW (data->view),
907                                   data->path,
908                                   FALSE);
909
910         data->timeout_id = 0;
911
912         return FALSE;
913 }
914
915 static void
916 contact_list_view_drag_begin (GtkWidget      *widget,
917                               GdkDragContext *context)
918 {
919         EmpathyContactListViewPriv *priv;
920         GtkTreeSelection          *selection;
921         GtkTreeModel              *model;
922         GtkTreePath               *path;
923         GtkTreeIter                iter;
924
925         priv = GET_PRIV (widget);
926
927         GTK_WIDGET_CLASS (empathy_contact_list_view_parent_class)->drag_begin (widget,
928                                                                               context);
929
930         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
931         if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
932                 return;
933         }
934
935         path = gtk_tree_model_get_path (model, &iter);
936         priv->drag_row = gtk_tree_row_reference_new (model, path);
937         gtk_tree_path_free (path);
938 }
939
940 static void
941 contact_list_view_drag_data_get (GtkWidget        *widget,
942                                  GdkDragContext   *context,
943                                  GtkSelectionData *selection,
944                                  guint             info,
945                                  guint             time)
946 {
947         EmpathyContactListViewPriv *priv;
948         GtkTreePath                *src_path;
949         GtkTreeIter                 iter;
950         GtkTreeModel               *model;
951         EmpathyContact             *contact;
952         McAccount                  *account;
953         const gchar                *contact_id;
954         const gchar                *account_id;
955         gchar                      *str;
956         
957
958         priv = GET_PRIV (widget);
959
960         model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
961         if (!priv->drag_row) {
962                 return;
963         }
964
965         src_path = gtk_tree_row_reference_get_path (priv->drag_row);
966         if (!src_path) {
967                 return;
968         }
969
970         if (!gtk_tree_model_get_iter (model, &iter, src_path)) {
971                 gtk_tree_path_free (src_path);
972                 return;
973         }
974
975         gtk_tree_path_free (src_path);
976
977         contact = empathy_contact_list_view_get_selected (EMPATHY_CONTACT_LIST_VIEW (widget));
978         if (!contact) {
979                 return;
980         }
981
982         account = empathy_contact_get_account (contact);
983         account_id = mc_account_get_unique_name (account);
984         contact_id = empathy_contact_get_id (contact);
985         g_object_unref (contact);
986         str = g_strconcat (account_id, "/", contact_id, NULL);
987
988         switch (info) {
989         case DND_DRAG_TYPE_CONTACT_ID:
990                 gtk_selection_data_set (selection, drag_atoms_source[info], 8,
991                                         (guchar*)str, strlen (str) + 1);
992                 break;
993         }
994
995         g_free (str);
996 }
997
998 static void
999 contact_list_view_drag_end (GtkWidget      *widget,
1000                             GdkDragContext *context)
1001 {
1002         EmpathyContactListViewPriv *priv;
1003
1004         priv = GET_PRIV (widget);
1005
1006         GTK_WIDGET_CLASS (empathy_contact_list_view_parent_class)->drag_end (widget,
1007                                                                             context);
1008
1009         if (priv->drag_row) {
1010                 gtk_tree_row_reference_free (priv->drag_row);
1011                 priv->drag_row = NULL;
1012         }
1013 }
1014
1015 static gboolean
1016 contact_list_view_drag_drop (GtkWidget      *widget,
1017                              GdkDragContext *drag_context,
1018                              gint            x,
1019                              gint            y,
1020                              guint           time)
1021 {
1022         return FALSE;
1023 }
1024
1025 static void
1026 contact_list_view_cell_set_background (EmpathyContactListView *view,
1027                                        GtkCellRenderer       *cell,
1028                                        gboolean               is_group,
1029                                        gboolean               is_active)
1030 {
1031         GdkColor  color;
1032         GtkStyle *style;
1033
1034         style = gtk_widget_get_style (GTK_WIDGET (view));
1035
1036         if (!is_group && is_active) {
1037                 color = style->bg[GTK_STATE_SELECTED];
1038
1039                 /* Here we take the current theme colour and add it to
1040                  * the colour for white and average the two. This
1041                  * gives a colour which is inline with the theme but
1042                  * slightly whiter.
1043                  */
1044                 color.red = (color.red + (style->white).red) / 2;
1045                 color.green = (color.green + (style->white).green) / 2;
1046                 color.blue = (color.blue + (style->white).blue) / 2;
1047
1048                 g_object_set (cell,
1049                               "cell-background-gdk", &color,
1050                               NULL);
1051         } else {
1052                 g_object_set (cell,
1053                               "cell-background-gdk", NULL,
1054                               NULL);
1055         }
1056 }
1057
1058 static void
1059 contact_list_view_pixbuf_cell_data_func (GtkTreeViewColumn     *tree_column,
1060                                          GtkCellRenderer       *cell,
1061                                          GtkTreeModel          *model,
1062                                          GtkTreeIter           *iter,
1063                                          EmpathyContactListView *view)
1064 {
1065         gchar    *icon_name;
1066         gboolean  is_group;
1067         gboolean  is_active;
1068
1069         gtk_tree_model_get (model, iter,
1070                             EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
1071                             EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
1072                             EMPATHY_CONTACT_LIST_STORE_COL_ICON_STATUS, &icon_name,
1073                             -1);
1074
1075         g_object_set (cell,
1076                       "visible", !is_group,
1077                       "icon-name", icon_name,
1078                       NULL);
1079
1080         g_free (icon_name);
1081
1082         contact_list_view_cell_set_background (view, cell, is_group, is_active);
1083 }
1084
1085 #ifdef HAVE_VOIP
1086 static void
1087 contact_list_view_voip_cell_data_func (GtkTreeViewColumn      *tree_column,
1088                                        GtkCellRenderer        *cell,
1089                                        GtkTreeModel           *model,
1090                                        GtkTreeIter            *iter,
1091                                        EmpathyContactListView *view)
1092 {
1093         gboolean is_group;
1094         gboolean is_active;
1095         gboolean can_voip;
1096
1097         gtk_tree_model_get (model, iter,
1098                             EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
1099                             EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
1100                             EMPATHY_CONTACT_LIST_STORE_COL_CAN_VOIP, &can_voip,
1101                             -1);
1102
1103         g_object_set (cell,
1104                       "visible", !is_group && can_voip,
1105                       "icon-name", EMPATHY_IMAGE_VOIP,
1106                       NULL);
1107
1108         contact_list_view_cell_set_background (view, cell, is_group, is_active);
1109 }
1110 #endif
1111
1112 static void
1113 contact_list_view_avatar_cell_data_func (GtkTreeViewColumn     *tree_column,
1114                                          GtkCellRenderer       *cell,
1115                                          GtkTreeModel          *model,
1116                                          GtkTreeIter           *iter,
1117                                          EmpathyContactListView *view)
1118 {
1119         GdkPixbuf *pixbuf;
1120         gboolean   show_avatar;
1121         gboolean   is_group;
1122         gboolean   is_active;
1123
1124         gtk_tree_model_get (model, iter,
1125                             EMPATHY_CONTACT_LIST_STORE_COL_PIXBUF_AVATAR, &pixbuf,
1126                             EMPATHY_CONTACT_LIST_STORE_COL_PIXBUF_AVATAR_VISIBLE, &show_avatar,
1127                             EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
1128                             EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
1129                             -1);
1130
1131         g_object_set (cell,
1132                       "visible", !is_group && show_avatar,
1133                       "pixbuf", pixbuf,
1134                       NULL);
1135
1136         if (pixbuf) {
1137                 g_object_unref (pixbuf);
1138         }
1139
1140         contact_list_view_cell_set_background (view, cell, is_group, is_active);
1141 }
1142
1143 static void
1144 contact_list_view_text_cell_data_func (GtkTreeViewColumn     *tree_column,
1145                                        GtkCellRenderer       *cell,
1146                                        GtkTreeModel          *model,
1147                                        GtkTreeIter           *iter,
1148                                        EmpathyContactListView *view)
1149 {
1150         gboolean is_group;
1151         gboolean is_active;
1152         gboolean show_status;
1153
1154         gtk_tree_model_get (model, iter,
1155                             EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
1156                             EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
1157                             EMPATHY_CONTACT_LIST_STORE_COL_STATUS_VISIBLE, &show_status,
1158                             -1);
1159
1160         g_object_set (cell,
1161                       "show-status", show_status,
1162                       NULL);
1163
1164         contact_list_view_cell_set_background (view, cell, is_group, is_active);
1165 }
1166
1167 static void
1168 contact_list_view_expander_cell_data_func (GtkTreeViewColumn     *column,
1169                                            GtkCellRenderer       *cell,
1170                                            GtkTreeModel          *model,
1171                                            GtkTreeIter           *iter,
1172                                            EmpathyContactListView *view)
1173 {
1174         gboolean is_group;
1175         gboolean is_active;
1176
1177         gtk_tree_model_get (model, iter,
1178                             EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
1179                             EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
1180                             -1);
1181
1182         if (gtk_tree_model_iter_has_child (model, iter)) {
1183                 GtkTreePath *path;
1184                 gboolean     row_expanded;
1185
1186                 path = gtk_tree_model_get_path (model, iter);
1187                 row_expanded = gtk_tree_view_row_expanded (GTK_TREE_VIEW (column->tree_view), path);
1188                 gtk_tree_path_free (path);
1189
1190                 g_object_set (cell,
1191                               "visible", TRUE,
1192                               "expander-style", row_expanded ? GTK_EXPANDER_EXPANDED : GTK_EXPANDER_COLLAPSED,
1193                               NULL);
1194         } else {
1195                 g_object_set (cell, "visible", FALSE, NULL);
1196         }
1197
1198         contact_list_view_cell_set_background (view, cell, is_group, is_active);
1199 }
1200
1201 static GtkWidget *
1202 contact_list_view_get_contact_menu (EmpathyContactListView *view,
1203                                     gboolean               can_send_file,
1204                                     gboolean               can_show_log,
1205                                     gboolean               can_voip)
1206 {
1207         EmpathyContactListViewPriv *priv;
1208         GtkAction                 *action;
1209         GtkWidget                 *widget;
1210
1211         priv = GET_PRIV (view);
1212
1213         /* Sort out sensitive items */
1214         action = gtk_ui_manager_get_action (priv->ui, "/Contact/Log");
1215         gtk_action_set_sensitive (action, can_show_log);
1216
1217 #ifdef HAVE_VOIP
1218         action = gtk_ui_manager_get_action (priv->ui, "/Contact/Call");
1219         gtk_action_set_sensitive (action, can_voip);
1220 #endif
1221
1222         action = gtk_ui_manager_get_action (priv->ui, "/Contact/SendFile");
1223         gtk_action_set_visible (action, can_send_file);
1224
1225         widget = gtk_ui_manager_get_widget (priv->ui, "/Contact");
1226
1227         return widget;
1228 }
1229
1230 static gboolean
1231 contact_list_view_button_press_event_cb (EmpathyContactListView *view,
1232                                          GdkEventButton        *event,
1233                                          gpointer               user_data)
1234 {
1235         EmpathyContactListViewPriv *priv;
1236         EmpathyContact             *contact;
1237         GtkTreePath               *path;
1238         GtkTreeSelection          *selection;
1239         GtkTreeModel              *model;
1240         GtkTreeIter                iter;
1241         gboolean                   row_exists;
1242         GtkWidget                 *menu;
1243
1244         priv = GET_PRIV (view);
1245
1246         if (!priv->interactive || event->button != 3) {
1247                 return FALSE;
1248         }
1249
1250         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
1251         model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
1252
1253         gtk_widget_grab_focus (GTK_WIDGET (view));
1254
1255         row_exists = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (view),
1256                                                     event->x, event->y,
1257                                                     &path,
1258                                                     NULL, NULL, NULL);
1259         if (!row_exists) {
1260                 return FALSE;
1261         }
1262
1263         gtk_tree_selection_unselect_all (selection);
1264         gtk_tree_selection_select_path (selection, path);
1265
1266         gtk_tree_model_get_iter (model, &iter, path);
1267         gtk_tree_path_free (path);
1268
1269         gtk_tree_model_get (model, &iter,
1270                             EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
1271                             -1);
1272
1273         if (contact) {
1274                 menu = empathy_contact_list_view_get_contact_menu (view, contact);
1275                 g_object_unref (contact);
1276         } else {
1277                 menu = empathy_contact_list_view_get_group_menu (view);
1278         }
1279
1280         if (!menu) {
1281                 return FALSE;
1282         }
1283
1284         gtk_widget_show (menu);
1285
1286         gtk_menu_popup (GTK_MENU (menu),
1287                         NULL, NULL, NULL, NULL,
1288                         event->button, event->time);
1289
1290         return TRUE;
1291 }
1292
1293 static void
1294 contact_list_view_row_activated_cb (EmpathyContactListView *view,
1295                                     GtkTreePath            *path,
1296                                     GtkTreeViewColumn      *col,
1297                                     gpointer                user_data)
1298 {
1299         EmpathyContactListViewPriv *priv = GET_PRIV (view);
1300         EmpathyContact             *contact;
1301         GtkTreeModel               *model;
1302         GtkTreeIter                 iter;
1303
1304         if (!priv->interactive) {
1305                 return;
1306         }
1307
1308         model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
1309
1310         gtk_tree_model_get_iter (model, &iter, path);
1311         gtk_tree_model_get (model, &iter,
1312                             EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
1313                             -1);
1314
1315         if (contact) {
1316                 empathy_chat_with_contact (contact);
1317                 g_object_unref (contact);
1318         }
1319 }
1320
1321 #ifdef HAVE_VOIP
1322 static void
1323 contact_list_view_voip_activated_cb (EmpathyCellRendererActivatable *cell,
1324                                      const gchar                    *path_string,
1325                                      EmpathyContactListView         *view)
1326 {
1327         EmpathyContactListViewPriv *priv = GET_PRIV (view);
1328         GtkTreeModel               *model;
1329         GtkTreeIter                 iter;
1330         EmpathyContact             *contact;
1331
1332         if (!priv->interactive) {
1333                 return;
1334         }
1335
1336         model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
1337         if (!gtk_tree_model_get_iter_from_string (model, &iter, path_string)) {
1338                 return;
1339         }
1340
1341         gtk_tree_model_get (model, &iter,
1342                             EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
1343                             -1);
1344
1345         if (contact) {
1346                 contact_list_view_voip_activated (view, contact);
1347                 g_object_unref (contact);
1348         }
1349 }
1350 #endif
1351
1352
1353 static void
1354 contact_list_view_row_expand_or_collapse_cb (EmpathyContactListView *view,
1355                                              GtkTreeIter           *iter,
1356                                              GtkTreePath           *path,
1357                                              gpointer               user_data)
1358 {
1359         GtkTreeModel *model;
1360         gchar        *name;
1361         gboolean      expanded;
1362
1363         model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
1364
1365         gtk_tree_model_get (model, iter,
1366                             EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
1367                             -1);
1368
1369         expanded = GPOINTER_TO_INT (user_data);
1370         empathy_contact_group_set_expanded (name, expanded);
1371
1372         g_free (name);
1373 }
1374
1375 static void
1376 contact_list_view_action_cb (GtkAction             *action,
1377                              EmpathyContactListView *view)
1378 {
1379         EmpathyContactListViewPriv *priv;
1380         EmpathyContact             *contact;
1381         const gchar               *name;
1382         gchar                     *group;
1383         GtkWindow                 *parent;
1384
1385         priv = GET_PRIV (view);
1386
1387         name = gtk_action_get_name (action);
1388         if (!name) {
1389                 return;
1390         }
1391
1392         empathy_debug (DEBUG_DOMAIN, "Action:'%s' activated", name);
1393
1394         contact = empathy_contact_list_view_get_selected (view);
1395         group = empathy_contact_list_view_get_selected_group (view);
1396         parent = empathy_get_toplevel_window (GTK_WIDGET (view));
1397
1398         if (contact && strcmp (name, "Chat") == 0) {
1399                 empathy_chat_with_contact (contact);
1400         }
1401         else if (contact && strcmp (name, "Call") == 0) {
1402                 contact_list_view_voip_activated (view, contact);
1403         }
1404         else if (contact && strcmp (name, "Information") == 0) {
1405                 empathy_contact_information_dialog_show (contact, parent, FALSE, FALSE);
1406         }
1407         else if (contact && strcmp (name, "Edit") == 0) {
1408                 empathy_contact_information_dialog_show (contact, parent, TRUE, FALSE);
1409         }
1410         else if (contact && strcmp (name, "Remove") == 0) {
1411                 /* FIXME: Ask for confirmation */
1412                 EmpathyContactList *list;
1413
1414                 list = empathy_contact_list_store_get_list_iface (priv->store);
1415                 empathy_contact_list_remove (list, contact,
1416                                              _("Sorry, I don't want you in my contact list anymore."));
1417         }
1418         else if (contact && strcmp (name, "Invite") == 0) {
1419         }
1420         else if (contact && strcmp (name, "SendFile") == 0) {
1421         }
1422         else if (contact && strcmp (name, "Log") == 0) {
1423                 empathy_log_window_show (empathy_contact_get_account (contact),
1424                                         empathy_contact_get_id (contact),
1425                                         FALSE,
1426                                         parent);
1427         }
1428         else if (group && strcmp (name, "Rename") == 0) {
1429         }
1430         else if (group && strcmp (name, "Remove") == 0) {
1431                 EmpathyContactList *list;
1432
1433                 list = empathy_contact_list_store_get_list_iface (priv->store);
1434                 empathy_contact_list_remove_group (list, group);
1435         }
1436
1437         g_free (group);
1438         if (contact) {
1439                 g_object_unref (contact);
1440         }
1441 }
1442
1443 static void
1444 contact_list_view_voip_activated (EmpathyContactListView *view,
1445                                   EmpathyContact         *contact)
1446 {
1447         empathy_call_contact (contact);
1448 }
1449