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