]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-contact-list-view.c
Simplify contact_list_view_cell_set_background()
[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", EMPATHY_IMAGE_LOG,
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,
504                             EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
505                             -1);
506
507         return contact;
508 }
509
510 gchar *
511 empathy_contact_list_view_get_selected_group (EmpathyContactListView *view)
512 {
513         EmpathyContactListViewPriv *priv;
514         GtkTreeSelection          *selection;
515         GtkTreeIter                iter;
516         GtkTreeModel              *model;
517         gboolean                   is_group;
518         gchar                     *name;
519
520         g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), NULL);
521
522         priv = GET_PRIV (view);
523
524         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
525         if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
526                 return NULL;
527         }
528
529         gtk_tree_model_get (model, &iter,
530                             EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
531                             EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
532                             -1);
533
534         if (!is_group) {
535                 g_free (name);
536                 return NULL;
537         }
538
539         return name;
540 }
541
542 GtkWidget *
543 empathy_contact_list_view_get_group_menu (EmpathyContactListView *view)
544 {
545         EmpathyContactListViewPriv *priv;
546         GtkWidget                 *widget;
547
548         g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), NULL);
549
550         priv = GET_PRIV (view);
551
552         widget = gtk_ui_manager_get_widget (priv->ui, "/Group");
553
554         return widget;
555 }
556
557 GtkWidget *
558 empathy_contact_list_view_get_contact_menu (EmpathyContactListView *view,
559                                             EmpathyContact         *contact)
560 {
561         EmpathyLogManager *log_manager;
562         gboolean           can_show_log;
563         gboolean           can_send_file;
564         gboolean           can_voip;
565
566         g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_VIEW (view), NULL);
567         g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
568
569         log_manager = empathy_log_manager_new ();
570         can_show_log = empathy_log_manager_exists (log_manager,
571                                                    empathy_contact_get_account (contact),
572                                                    empathy_contact_get_id (contact),
573                                                    FALSE);
574         g_object_unref (log_manager);
575         can_send_file = FALSE;
576         can_voip = empathy_contact_can_voip (contact);
577
578         return contact_list_view_get_contact_menu (view,
579                                                    can_send_file,
580                                                    can_show_log,
581                                                    can_voip);
582 }
583
584 static void
585 contact_list_view_setup (EmpathyContactListView *view)
586 {
587         EmpathyContactListViewPriv *priv;
588         GtkCellRenderer           *cell;
589         GtkTreeViewColumn         *col;
590         gint                       i;
591
592         priv = GET_PRIV (view);
593
594         gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (view),
595                                              empathy_contact_list_store_search_equal_func,
596                                              NULL, NULL);
597
598         g_signal_connect (priv->store, "row-has-child-toggled",
599                           G_CALLBACK (contact_list_view_row_has_child_toggled_cb),
600                           view);
601         gtk_tree_view_set_model (GTK_TREE_VIEW (view),
602                                  GTK_TREE_MODEL (priv->store));
603
604         /* Setup view */
605         g_object_set (view,
606                       "headers-visible", FALSE,
607                       "reorderable", TRUE,
608                       "show-expanders", FALSE,
609                       NULL);
610
611         col = gtk_tree_view_column_new ();
612
613         /* State */
614         cell = gtk_cell_renderer_pixbuf_new ();
615         gtk_tree_view_column_pack_start (col, cell, FALSE);
616         gtk_tree_view_column_set_cell_data_func (
617                 col, cell,
618                 (GtkTreeCellDataFunc) contact_list_view_pixbuf_cell_data_func,
619                 view, NULL);
620
621         g_object_set (cell,
622                       "xpad", 5,
623                       "ypad", 1,
624                       "visible", FALSE,
625                       NULL);
626
627         /* Name */
628         cell = empathy_cell_renderer_text_new ();
629         gtk_tree_view_column_pack_start (col, cell, TRUE);
630         gtk_tree_view_column_set_cell_data_func (
631                 col, cell,
632                 (GtkTreeCellDataFunc) contact_list_view_text_cell_data_func,
633                 view, NULL);
634
635         gtk_tree_view_column_add_attribute (col, cell,
636                                             "name", EMPATHY_CONTACT_LIST_STORE_COL_NAME);
637         gtk_tree_view_column_add_attribute (col, cell,
638                                             "status", EMPATHY_CONTACT_LIST_STORE_COL_STATUS);
639         gtk_tree_view_column_add_attribute (col, cell,
640                                             "is_group", EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP);
641
642 #ifdef HAVE_VOIP
643         /* Voip Capability Icon */
644         cell = empathy_cell_renderer_activatable_new ();
645         gtk_tree_view_column_pack_start (col, cell, FALSE);
646         gtk_tree_view_column_set_cell_data_func (
647                 col, cell,
648                 (GtkTreeCellDataFunc) contact_list_view_voip_cell_data_func,
649                 view, NULL);
650
651         g_object_set (cell,
652                       "visible", FALSE,
653                       NULL);
654
655         g_signal_connect (cell, "path-activated",
656                           G_CALLBACK (contact_list_view_voip_activated_cb),
657                           view);
658 #endif
659
660         /* Avatar */
661         cell = gtk_cell_renderer_pixbuf_new ();
662         gtk_tree_view_column_pack_start (col, cell, FALSE);
663         gtk_tree_view_column_set_cell_data_func (
664                 col, cell,
665                 (GtkTreeCellDataFunc) contact_list_view_avatar_cell_data_func,
666                 view, NULL);
667
668         g_object_set (cell,
669                       "xpad", 0,
670                       "ypad", 0,
671                       "visible", FALSE,
672                       "width", 32,
673                       "height", 32,
674                       NULL);
675
676         /* Expander */
677         cell = empathy_cell_renderer_expander_new ();
678         gtk_tree_view_column_pack_end (col, cell, FALSE);
679         gtk_tree_view_column_set_cell_data_func (
680                 col, cell,
681                 (GtkTreeCellDataFunc) contact_list_view_expander_cell_data_func,
682                 view, NULL);
683
684         /* Actually add the column now we have added all cell renderers */
685         gtk_tree_view_append_column (GTK_TREE_VIEW (view), col);
686
687         /* Drag & Drop. */
688         for (i = 0; i < G_N_ELEMENTS (drag_types_dest); ++i) {
689                 drag_atoms_dest[i] = gdk_atom_intern (drag_types_dest[i].target,
690                                                       FALSE);
691         }
692
693         for (i = 0; i < G_N_ELEMENTS (drag_types_source); ++i) {
694                 drag_atoms_source[i] = gdk_atom_intern (drag_types_source[i].target,
695                                                         FALSE);
696         }
697
698         /* Note: We support the COPY action too, but need to make the
699          * MOVE action the default.
700          */
701         gtk_drag_source_set (GTK_WIDGET (view),
702                              GDK_BUTTON1_MASK,
703                              drag_types_source,
704                              G_N_ELEMENTS (drag_types_source),
705                              GDK_ACTION_MOVE | GDK_ACTION_COPY);
706
707         gtk_drag_dest_set (GTK_WIDGET (view),
708                            GTK_DEST_DEFAULT_ALL,
709                            drag_types_dest,
710                            G_N_ELEMENTS (drag_types_dest),
711                            GDK_ACTION_MOVE | GDK_ACTION_COPY);
712 }
713
714 static void
715 contact_list_view_row_has_child_toggled_cb (GtkTreeModel          *model,
716                                             GtkTreePath           *path,
717                                             GtkTreeIter           *iter,
718                                             EmpathyContactListView *view)
719 {
720         gboolean  is_group = FALSE;
721         gchar    *name = NULL;
722
723         gtk_tree_model_get (model, iter,
724                             EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
725                             EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
726                             -1);
727
728         if (!is_group || G_STR_EMPTY (name)) {
729                 g_free (name);
730                 return;
731         }
732
733         if (empathy_contact_group_get_expanded (name)) {
734                 g_signal_handlers_block_by_func (view,
735                                                  contact_list_view_row_expand_or_collapse_cb,
736                                                  GINT_TO_POINTER (TRUE));
737                 gtk_tree_view_expand_row (GTK_TREE_VIEW (view), path, TRUE);
738                 g_signal_handlers_unblock_by_func (view,
739                                                    contact_list_view_row_expand_or_collapse_cb,
740                                                    GINT_TO_POINTER (TRUE));
741         } else {
742                 g_signal_handlers_block_by_func (view,
743                                                  contact_list_view_row_expand_or_collapse_cb,
744                                                  GINT_TO_POINTER (FALSE));
745                 gtk_tree_view_collapse_row (GTK_TREE_VIEW (view), path);
746                 g_signal_handlers_unblock_by_func (view,
747                                                    contact_list_view_row_expand_or_collapse_cb,
748                                                    GINT_TO_POINTER (FALSE));
749         }
750
751         g_free (name);
752 }
753
754 static void
755 contact_list_view_drag_data_received (GtkWidget         *widget,
756                                       GdkDragContext    *context,
757                                       gint               x,
758                                       gint               y,
759                                       GtkSelectionData  *selection,
760                                       guint              info,
761                                       guint              time)
762 {
763         EmpathyContactListViewPriv *priv;
764         EmpathyContactList         *list;
765         EmpathyContactFactory      *factory;
766         McAccount                  *account;
767         GtkTreeModel               *model;
768         GtkTreePath                *path;
769         GtkTreeViewDropPosition     position;
770         EmpathyContact             *contact = NULL;
771         const gchar                *id;
772         gchar                     **strv;
773         gchar                      *new_group = NULL;
774         gchar                      *old_group = NULL;
775         gboolean                    is_row;
776
777         priv = GET_PRIV (widget);
778
779         id = (const gchar*) selection->data;
780         empathy_debug (DEBUG_DOMAIN, "Received %s%s drag & drop contact from roster with id:'%s'",
781                       context->action == GDK_ACTION_MOVE ? "move" : "",
782                       context->action == GDK_ACTION_COPY ? "copy" : "",
783                       id);
784
785         strv = g_strsplit (id, "/", 2);
786         factory = empathy_contact_factory_new ();
787         account = mc_account_lookup (strv[0]);
788         if (account) {
789                 contact = empathy_contact_factory_get_from_id (factory,
790                                                                account,
791                                                                strv[1]);
792                 g_object_unref (account);
793         }
794         g_object_unref (factory);
795         g_strfreev (strv);
796
797         if (!contact) {
798                 empathy_debug (DEBUG_DOMAIN, "No contact found associated with drag & drop");
799                 return;
800         }
801
802         model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
803
804         /* Get source group information. */
805         if (priv->drag_row) {
806                 path = gtk_tree_row_reference_get_path (priv->drag_row);
807                 if (path) {
808                         old_group = empathy_contact_list_store_get_parent_group (model, path, NULL);
809                         gtk_tree_path_free (path);
810                 }
811         }
812
813         /* Get destination group information. */
814         is_row = gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget),
815                                                     x,
816                                                     y,
817                                                     &path,
818                                                     &position);
819
820         if (is_row) {
821                 new_group = empathy_contact_list_store_get_parent_group (model, path, NULL);
822                 gtk_tree_path_free (path);
823         }
824
825         empathy_debug (DEBUG_DOMAIN,
826                       "contact %s (%d) dragged from '%s' to '%s'",
827                       empathy_contact_get_id (contact),
828                       empathy_contact_get_handle (contact),
829                       old_group, new_group);
830
831         list = empathy_contact_list_store_get_list_iface (priv->store);
832         if (new_group) {
833                 empathy_contact_list_add_to_group (list, contact, new_group);
834         }
835         if (old_group && context->action == GDK_ACTION_MOVE) {  
836                 empathy_contact_list_remove_from_group (list, contact, old_group);
837         }
838
839         g_free (old_group);
840         g_free (new_group);
841
842         gtk_drag_finish (context, TRUE, FALSE, GDK_CURRENT_TIME);
843 }
844
845 static gboolean
846 contact_list_view_drag_motion (GtkWidget      *widget,
847                                GdkDragContext *context,
848                                gint            x,
849                                gint            y,
850                                guint           time)
851 {
852         static DragMotionData *dm = NULL;
853         GtkTreePath           *path;
854         gboolean               is_row;
855         gboolean               is_different = FALSE;
856         gboolean               cleanup = TRUE;
857
858         is_row = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
859                                                 x,
860                                                 y,
861                                                 &path,
862                                                 NULL,
863                                                 NULL,
864                                                 NULL);
865
866         cleanup &= (!dm);
867
868         if (is_row) {
869                 cleanup &= (dm && gtk_tree_path_compare (dm->path, path) != 0);
870                 is_different = (!dm || (dm && gtk_tree_path_compare (dm->path, path) != 0));
871         } else {
872                 cleanup &= FALSE;
873         }
874
875         if (!is_different && !cleanup) {
876                 return TRUE;
877         }
878
879         if (dm) {
880                 gtk_tree_path_free (dm->path);
881                 if (dm->timeout_id) {
882                         g_source_remove (dm->timeout_id);
883                 }
884
885                 g_free (dm);
886
887                 dm = NULL;
888         }
889
890         if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget), path)) {
891                 dm = g_new0 (DragMotionData, 1);
892
893                 dm->view = EMPATHY_CONTACT_LIST_VIEW (widget);
894                 dm->path = gtk_tree_path_copy (path);
895
896                 dm->timeout_id = g_timeout_add_seconds (1,
897                         (GSourceFunc) contact_list_view_drag_motion_cb,
898                         dm);
899         }
900
901         return TRUE;
902 }
903
904 static gboolean
905 contact_list_view_drag_motion_cb (DragMotionData *data)
906 {
907         gtk_tree_view_expand_row (GTK_TREE_VIEW (data->view),
908                                   data->path,
909                                   FALSE);
910
911         data->timeout_id = 0;
912
913         return FALSE;
914 }
915
916 static void
917 contact_list_view_drag_begin (GtkWidget      *widget,
918                               GdkDragContext *context)
919 {
920         EmpathyContactListViewPriv *priv;
921         GtkTreeSelection          *selection;
922         GtkTreeModel              *model;
923         GtkTreePath               *path;
924         GtkTreeIter                iter;
925
926         priv = GET_PRIV (widget);
927
928         GTK_WIDGET_CLASS (empathy_contact_list_view_parent_class)->drag_begin (widget,
929                                                                               context);
930
931         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
932         if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
933                 return;
934         }
935
936         path = gtk_tree_model_get_path (model, &iter);
937         priv->drag_row = gtk_tree_row_reference_new (model, path);
938         gtk_tree_path_free (path);
939 }
940
941 static void
942 contact_list_view_drag_data_get (GtkWidget        *widget,
943                                  GdkDragContext   *context,
944                                  GtkSelectionData *selection,
945                                  guint             info,
946                                  guint             time)
947 {
948         EmpathyContactListViewPriv *priv;
949         GtkTreePath                *src_path;
950         GtkTreeIter                 iter;
951         GtkTreeModel               *model;
952         EmpathyContact             *contact;
953         McAccount                  *account;
954         const gchar                *contact_id;
955         const gchar                *account_id;
956         gchar                      *str;
957         
958
959         priv = GET_PRIV (widget);
960
961         model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
962         if (!priv->drag_row) {
963                 return;
964         }
965
966         src_path = gtk_tree_row_reference_get_path (priv->drag_row);
967         if (!src_path) {
968                 return;
969         }
970
971         if (!gtk_tree_model_get_iter (model, &iter, src_path)) {
972                 gtk_tree_path_free (src_path);
973                 return;
974         }
975
976         gtk_tree_path_free (src_path);
977
978         contact = empathy_contact_list_view_get_selected (EMPATHY_CONTACT_LIST_VIEW (widget));
979         if (!contact) {
980                 return;
981         }
982
983         account = empathy_contact_get_account (contact);
984         account_id = mc_account_get_unique_name (account);
985         contact_id = empathy_contact_get_id (contact);
986         g_object_unref (contact);
987         str = g_strconcat (account_id, "/", contact_id, NULL);
988
989         switch (info) {
990         case DND_DRAG_TYPE_CONTACT_ID:
991                 gtk_selection_data_set (selection, drag_atoms_source[info], 8,
992                                         (guchar*)str, strlen (str) + 1);
993                 break;
994         }
995
996         g_free (str);
997 }
998
999 static void
1000 contact_list_view_drag_end (GtkWidget      *widget,
1001                             GdkDragContext *context)
1002 {
1003         EmpathyContactListViewPriv *priv;
1004
1005         priv = GET_PRIV (widget);
1006
1007         GTK_WIDGET_CLASS (empathy_contact_list_view_parent_class)->drag_end (widget,
1008                                                                             context);
1009
1010         if (priv->drag_row) {
1011                 gtk_tree_row_reference_free (priv->drag_row);
1012                 priv->drag_row = NULL;
1013         }
1014 }
1015
1016 static gboolean
1017 contact_list_view_drag_drop (GtkWidget      *widget,
1018                              GdkDragContext *drag_context,
1019                              gint            x,
1020                              gint            y,
1021                              guint           time)
1022 {
1023         return FALSE;
1024 }
1025
1026 static void
1027 contact_list_view_cell_set_background (EmpathyContactListView *view,
1028                                        GtkCellRenderer       *cell,
1029                                        gboolean               is_group,
1030                                        gboolean               is_active)
1031 {
1032         GdkColor  color;
1033         GtkStyle *style;
1034
1035         style = gtk_widget_get_style (GTK_WIDGET (view));
1036
1037         if (!is_group && is_active) {
1038                 color = style->bg[GTK_STATE_SELECTED];
1039
1040                 /* Here we take the current theme colour and add it to
1041                  * the colour for white and average the two. This
1042                  * gives a colour which is inline with the theme but
1043                  * slightly whiter.
1044                  */
1045                 color.red = (color.red + (style->white).red) / 2;
1046                 color.green = (color.green + (style->white).green) / 2;
1047                 color.blue = (color.blue + (style->white).blue) / 2;
1048
1049                 g_object_set (cell,
1050                               "cell-background-gdk", &color,
1051                               NULL);
1052         } else {
1053                 g_object_set (cell,
1054                               "cell-background-gdk", NULL,
1055                               NULL);
1056         }
1057 }
1058
1059 static void
1060 contact_list_view_pixbuf_cell_data_func (GtkTreeViewColumn     *tree_column,
1061                                          GtkCellRenderer       *cell,
1062                                          GtkTreeModel          *model,
1063                                          GtkTreeIter           *iter,
1064                                          EmpathyContactListView *view)
1065 {
1066         gchar    *icon_name;
1067         gboolean  is_group;
1068         gboolean  is_active;
1069
1070         gtk_tree_model_get (model, iter,
1071                             EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
1072                             EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
1073                             EMPATHY_CONTACT_LIST_STORE_COL_ICON_STATUS, &icon_name,
1074                             -1);
1075
1076         g_object_set (cell,
1077                       "visible", !is_group,
1078                       "icon-name", icon_name,
1079                       NULL);
1080
1081         g_free (icon_name);
1082
1083         contact_list_view_cell_set_background (view, cell, is_group, is_active);
1084 }
1085
1086 #ifdef HAVE_VOIP
1087 static void
1088 contact_list_view_voip_cell_data_func (GtkTreeViewColumn      *tree_column,
1089                                        GtkCellRenderer        *cell,
1090                                        GtkTreeModel           *model,
1091                                        GtkTreeIter            *iter,
1092                                        EmpathyContactListView *view)
1093 {
1094         gboolean is_group;
1095         gboolean is_active;
1096         gboolean can_voip;
1097
1098         gtk_tree_model_get (model, iter,
1099                             EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
1100                             EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
1101                             EMPATHY_CONTACT_LIST_STORE_COL_CAN_VOIP, &can_voip,
1102                             -1);
1103
1104         g_object_set (cell,
1105                       "visible", !is_group && can_voip,
1106                       "icon-name", EMPATHY_IMAGE_VOIP,
1107                       NULL);
1108
1109         contact_list_view_cell_set_background (view, cell, is_group, is_active);
1110 }
1111 #endif
1112
1113 static void
1114 contact_list_view_avatar_cell_data_func (GtkTreeViewColumn     *tree_column,
1115                                          GtkCellRenderer       *cell,
1116                                          GtkTreeModel          *model,
1117                                          GtkTreeIter           *iter,
1118                                          EmpathyContactListView *view)
1119 {
1120         GdkPixbuf *pixbuf;
1121         gboolean   show_avatar;
1122         gboolean   is_group;
1123         gboolean   is_active;
1124
1125         gtk_tree_model_get (model, iter,
1126                             EMPATHY_CONTACT_LIST_STORE_COL_PIXBUF_AVATAR, &pixbuf,
1127                             EMPATHY_CONTACT_LIST_STORE_COL_PIXBUF_AVATAR_VISIBLE, &show_avatar,
1128                             EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
1129                             EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
1130                             -1);
1131
1132         g_object_set (cell,
1133                       "visible", !is_group && show_avatar,
1134                       "pixbuf", pixbuf,
1135                       NULL);
1136
1137         if (pixbuf) {
1138                 g_object_unref (pixbuf);
1139         }
1140
1141         contact_list_view_cell_set_background (view, cell, is_group, is_active);
1142 }
1143
1144 static void
1145 contact_list_view_text_cell_data_func (GtkTreeViewColumn     *tree_column,
1146                                        GtkCellRenderer       *cell,
1147                                        GtkTreeModel          *model,
1148                                        GtkTreeIter           *iter,
1149                                        EmpathyContactListView *view)
1150 {
1151         gboolean is_group;
1152         gboolean is_active;
1153         gboolean show_status;
1154
1155         gtk_tree_model_get (model, iter,
1156                             EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
1157                             EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
1158                             EMPATHY_CONTACT_LIST_STORE_COL_STATUS_VISIBLE, &show_status,
1159                             -1);
1160
1161         g_object_set (cell,
1162                       "show-status", show_status,
1163                       NULL);
1164
1165         contact_list_view_cell_set_background (view, cell, is_group, is_active);
1166 }
1167
1168 static void
1169 contact_list_view_expander_cell_data_func (GtkTreeViewColumn     *column,
1170                                            GtkCellRenderer       *cell,
1171                                            GtkTreeModel          *model,
1172                                            GtkTreeIter           *iter,
1173                                            EmpathyContactListView *view)
1174 {
1175         gboolean is_group;
1176         gboolean is_active;
1177
1178         gtk_tree_model_get (model, iter,
1179                             EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
1180                             EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, &is_active,
1181                             -1);
1182
1183         if (gtk_tree_model_iter_has_child (model, iter)) {
1184                 GtkTreePath *path;
1185                 gboolean     row_expanded;
1186
1187                 path = gtk_tree_model_get_path (model, iter);
1188                 row_expanded = gtk_tree_view_row_expanded (GTK_TREE_VIEW (column->tree_view), path);
1189                 gtk_tree_path_free (path);
1190
1191                 g_object_set (cell,
1192                               "visible", TRUE,
1193                               "expander-style", row_expanded ? GTK_EXPANDER_EXPANDED : GTK_EXPANDER_COLLAPSED,
1194                               NULL);
1195         } else {
1196                 g_object_set (cell, "visible", FALSE, NULL);
1197         }
1198
1199         contact_list_view_cell_set_background (view, cell, is_group, is_active);
1200 }
1201
1202 static GtkWidget *
1203 contact_list_view_get_contact_menu (EmpathyContactListView *view,
1204                                     gboolean               can_send_file,
1205                                     gboolean               can_show_log,
1206                                     gboolean               can_voip)
1207 {
1208         EmpathyContactListViewPriv *priv;
1209         GtkAction                 *action;
1210         GtkWidget                 *widget;
1211
1212         priv = GET_PRIV (view);
1213
1214         /* Sort out sensitive items */
1215         action = gtk_ui_manager_get_action (priv->ui, "/Contact/Log");
1216         gtk_action_set_sensitive (action, can_show_log);
1217
1218 #ifdef HAVE_VOIP
1219         action = gtk_ui_manager_get_action (priv->ui, "/Contact/Call");
1220         gtk_action_set_sensitive (action, can_voip);
1221 #endif
1222
1223         action = gtk_ui_manager_get_action (priv->ui, "/Contact/SendFile");
1224         gtk_action_set_visible (action, can_send_file);
1225
1226         widget = gtk_ui_manager_get_widget (priv->ui, "/Contact");
1227
1228         return widget;
1229 }
1230
1231 static gboolean
1232 contact_list_view_button_press_event_cb (EmpathyContactListView *view,
1233                                          GdkEventButton        *event,
1234                                          gpointer               user_data)
1235 {
1236         EmpathyContactListViewPriv *priv;
1237         EmpathyContact             *contact;
1238         GtkTreePath               *path;
1239         GtkTreeSelection          *selection;
1240         GtkTreeModel              *model;
1241         GtkTreeIter                iter;
1242         gboolean                   row_exists;
1243         GtkWidget                 *menu;
1244
1245         priv = GET_PRIV (view);
1246
1247         if (!priv->interactive || event->button != 3) {
1248                 return FALSE;
1249         }
1250
1251         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
1252         model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
1253
1254         gtk_widget_grab_focus (GTK_WIDGET (view));
1255
1256         row_exists = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (view),
1257                                                     event->x, event->y,
1258                                                     &path,
1259                                                     NULL, NULL, NULL);
1260         if (!row_exists) {
1261                 return FALSE;
1262         }
1263
1264         gtk_tree_selection_unselect_all (selection);
1265         gtk_tree_selection_select_path (selection, path);
1266
1267         gtk_tree_model_get_iter (model, &iter, path);
1268         gtk_tree_path_free (path);
1269
1270         gtk_tree_model_get (model, &iter,
1271                             EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
1272                             -1);
1273
1274         if (contact) {
1275                 menu = empathy_contact_list_view_get_contact_menu (view, contact);
1276                 g_object_unref (contact);
1277         } else {
1278                 menu = empathy_contact_list_view_get_group_menu (view);
1279         }
1280
1281         if (!menu) {
1282                 return FALSE;
1283         }
1284
1285         gtk_widget_show (menu);
1286
1287         gtk_menu_popup (GTK_MENU (menu),
1288                         NULL, NULL, NULL, NULL,
1289                         event->button, event->time);
1290
1291         return TRUE;
1292 }
1293
1294 static void
1295 contact_list_view_row_activated_cb (EmpathyContactListView *view,
1296                                     GtkTreePath            *path,
1297                                     GtkTreeViewColumn      *col,
1298                                     gpointer                user_data)
1299 {
1300         EmpathyContactListViewPriv *priv = GET_PRIV (view);
1301         EmpathyContact             *contact;
1302         GtkTreeModel               *model;
1303         GtkTreeIter                 iter;
1304
1305         if (!priv->interactive) {
1306                 return;
1307         }
1308
1309         model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
1310
1311         gtk_tree_model_get_iter (model, &iter, path);
1312         gtk_tree_model_get (model, &iter,
1313                             EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
1314                             -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,
1343                             EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
1344                             -1);
1345
1346         if (contact) {
1347                 contact_list_view_voip_activated (view, contact);
1348                 g_object_unref (contact);
1349         }
1350 }
1351 #endif
1352
1353
1354 static void
1355 contact_list_view_row_expand_or_collapse_cb (EmpathyContactListView *view,
1356                                              GtkTreeIter           *iter,
1357                                              GtkTreePath           *path,
1358                                              gpointer               user_data)
1359 {
1360         GtkTreeModel *model;
1361         gchar        *name;
1362         gboolean      expanded;
1363
1364         model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
1365
1366         gtk_tree_model_get (model, iter,
1367                             EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
1368                             -1);
1369
1370         expanded = GPOINTER_TO_INT (user_data);
1371         empathy_contact_group_set_expanded (name, expanded);
1372
1373         g_free (name);
1374 }
1375
1376 static void
1377 contact_list_view_action_cb (GtkAction             *action,
1378                              EmpathyContactListView *view)
1379 {
1380         EmpathyContactListViewPriv *priv;
1381         EmpathyContact             *contact;
1382         const gchar               *name;
1383         gchar                     *group;
1384         GtkWindow                 *parent;
1385
1386         priv = GET_PRIV (view);
1387
1388         name = gtk_action_get_name (action);
1389         if (!name) {
1390                 return;
1391         }
1392
1393         empathy_debug (DEBUG_DOMAIN, "Action:'%s' activated", name);
1394
1395         contact = empathy_contact_list_view_get_selected (view);
1396         group = empathy_contact_list_view_get_selected_group (view);
1397         parent = empathy_get_toplevel_window (GTK_WIDGET (view));
1398
1399         if (contact && strcmp (name, "Chat") == 0) {
1400                 contact_list_view_action_activated (view, contact);
1401         }
1402         else if (contact && strcmp (name, "Call") == 0) {
1403                 contact_list_view_voip_activated (view, contact);
1404         }
1405         else if (contact && strcmp (name, "Information") == 0) {
1406                 empathy_contact_information_dialog_show (contact, parent, FALSE, FALSE);
1407         }
1408         else if (contact && strcmp (name, "Edit") == 0) {
1409                 empathy_contact_information_dialog_show (contact, parent, TRUE, FALSE);
1410         }
1411         else if (contact && strcmp (name, "Remove") == 0) {
1412                 /* FIXME: Ask for confirmation */
1413                 EmpathyContactList *list;
1414
1415                 list = empathy_contact_list_store_get_list_iface (priv->store);
1416                 empathy_contact_list_remove (list, contact,
1417                                              _("Sorry, I don't want you in my contact list anymore."));
1418         }
1419         else if (contact && strcmp (name, "Invite") == 0) {
1420         }
1421         else if (contact && strcmp (name, "SendFile") == 0) {
1422         }
1423         else if (contact && strcmp (name, "Log") == 0) {
1424                 empathy_log_window_show (empathy_contact_get_account (contact),
1425                                         empathy_contact_get_id (contact),
1426                                         FALSE,
1427                                         parent);
1428         }
1429         else if (group && strcmp (name, "Rename") == 0) {
1430         }
1431
1432         g_free (group);
1433         if (contact) {
1434                 g_object_unref (contact);
1435         }
1436 }
1437
1438 static void
1439 contact_list_view_action_activated (EmpathyContactListView *view,
1440                                     EmpathyContact         *contact)
1441 {
1442         MissionControl *mc;
1443
1444         mc = empathy_mission_control_new ();
1445         mission_control_request_channel (mc,
1446                                          empathy_contact_get_account (contact),
1447                                          TP_IFACE_CHANNEL_TYPE_TEXT,
1448                                          empathy_contact_get_handle (contact),
1449                                          TP_HANDLE_TYPE_CONTACT,
1450                                          NULL, NULL);
1451         g_object_unref (mc);
1452 }
1453
1454 static void
1455 contact_list_view_voip_activated (EmpathyContactListView *view,
1456                                   EmpathyContact         *contact)
1457 {
1458         empathy_call_contact (contact);
1459 }
1460