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