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