]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-contact-widget.c
Completely reworked ContactList API. Fixes bug #471611, bug #467280, bug #459540...
[empathy.git] / libempathy-gtk / empathy-contact-widget.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2007 Collabora Ltd.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Authors: Xavier Claessens <xclaesse@gmail.com>
21  */
22
23 #include <config.h>
24
25 #include <string.h>
26 #include <stdlib.h>
27
28 #include <gtk/gtk.h>
29 #include <glade/glade.h>
30 #include <glib/gi18n.h>
31
32 #include <libmissioncontrol/mc-account.h>
33
34 #include <libempathy/empathy-contact-factory.h>
35 #include <libempathy/empathy-contact-manager.h>
36 #include <libempathy/empathy-contact-list.h>
37 #include <libempathy/empathy-utils.h>
38
39 #include "empathy-contact-widget.h"
40 #include "empathy-account-chooser.h"
41 #include "empathy-ui-utils.h"
42
43 /* Delay before updating the widget when the id entry changed (ms) */
44 #define ID_CHANGED_TIMEOUT 500
45
46 typedef struct {
47         EmpathyContactFactory    *factory;
48         EmpathyContactManager    *manager;
49         EmpathyContact           *contact;
50         gboolean                  is_user;
51         EmpathyContactWidgetType  type;
52         GtkCellRenderer          *renderer;
53         guint                     widget_id_timeout;
54
55         GtkWidget                *vbox_contact_widget;
56
57         /* Contact */
58         GtkWidget                *vbox_contact;
59         GtkWidget                *widget_avatar;
60         GtkWidget                *widget_account;
61         GtkWidget                *widget_id;
62         GtkWidget                *widget_alias;
63         GtkWidget                *label_alias;
64         GtkWidget                *entry_alias;
65         GtkWidget                *hbox_presence;
66         GtkWidget                *image_state;
67         GtkWidget                *label_status;
68         GtkWidget                *table_contact;
69         GtkWidget                *hbox_contact;
70
71         /* Groups */
72         GtkWidget                *vbox_groups;
73         GtkWidget                *entry_group;
74         GtkWidget                *button_group;
75         GtkWidget                *treeview_groups;
76
77         /* Details */
78         GtkWidget                *vbox_details;
79         GtkWidget                *table_details;
80         GtkWidget                *hbox_details_requested;
81
82         /* Client */
83         GtkWidget                *vbox_client;
84         GtkWidget                *table_client;
85         GtkWidget                *hbow_client_requested;
86 } EmpathyContactWidget;
87
88 typedef struct {
89         EmpathyContactWidget *information;
90         const gchar          *name;
91         gboolean              found;
92         GtkTreeIter           found_iter;
93 } FindName;
94
95 static void     contact_widget_destroy_cb                 (GtkWidget             *widget,
96                                                            EmpathyContactWidget  *information);
97 static void     contact_widget_remove_contact             (EmpathyContactWidget  *information);
98 static void     contact_widget_set_contact                (EmpathyContactWidget  *information,
99                                                            EmpathyContact         *contact);
100 static void     contact_widget_contact_setup              (EmpathyContactWidget  *information);
101 static void     contact_widget_contact_update             (EmpathyContactWidget  *information);
102 static gboolean contact_widget_update_contact             (EmpathyContactWidget  *information);
103 static void     contact_widget_account_changed_cb         (GtkComboBox           *widget,
104                                                            EmpathyContactWidget  *information);
105 static gboolean contact_widget_id_focus_out_cb            (GtkWidget             *widget,
106                                                            GdkEventFocus         *event,
107                                                            EmpathyContactWidget  *information);
108 static gboolean contact_widget_entry_alias_focus_event_cb (GtkEditable           *editable,
109                                                            GdkEventFocus         *event,
110                                                            EmpathyContactWidget  *information);
111 static void     contact_widget_name_notify_cb             (EmpathyContactWidget  *information);
112 static void     contact_widget_presence_notify_cb         (EmpathyContactWidget  *information);
113 static void     contact_widget_avatar_notify_cb           (EmpathyContactWidget  *information);
114 static void     contact_widget_groups_setup               (EmpathyContactWidget  *information);
115 static void     contact_widget_groups_update              (EmpathyContactWidget  *information);
116 static void     contact_widget_model_setup                (EmpathyContactWidget  *information);
117 static void     contact_widget_model_populate_columns     (EmpathyContactWidget  *information);
118 static void     contact_widget_groups_populate_data       (EmpathyContactWidget  *information);
119 static void     contact_widget_groups_notify_cb           (EmpathyContactWidget  *information);
120 static gboolean contact_widget_model_find_name            (EmpathyContactWidget  *information,
121                                                            const gchar           *name,
122                                                            GtkTreeIter           *iter);
123 static gboolean contact_widget_model_find_name_foreach    (GtkTreeModel          *model,
124                                                            GtkTreePath           *path,
125                                                            GtkTreeIter           *iter,
126                                                            FindName              *data);
127 static void     contact_widget_cell_toggled               (GtkCellRendererToggle *cell,
128                                                            gchar                 *path_string,
129                                                            EmpathyContactWidget  *information);
130 static void     contact_widget_entry_group_changed_cb     (GtkEditable           *editable,
131                                                            EmpathyContactWidget  *information);
132 static void     contact_widget_entry_group_activate_cb    (GtkEntry              *entry,
133                                                            EmpathyContactWidget  *information);
134 static void     contact_widget_button_group_clicked_cb    (GtkButton             *button,
135                                                            EmpathyContactWidget  *information);
136 static void     contact_widget_details_setup              (EmpathyContactWidget  *information);
137 static void     contact_widget_details_update             (EmpathyContactWidget  *information);
138 static void     contact_widget_client_setup               (EmpathyContactWidget  *information);
139 static void     contact_widget_client_update              (EmpathyContactWidget  *information);
140
141 enum {
142         COL_NAME,
143         COL_ENABLED,
144         COL_EDITABLE,
145         COL_COUNT
146 };
147
148 GtkWidget *
149 empathy_contact_widget_new (EmpathyContact           *contact,
150                             EmpathyContactWidgetType  type)
151 {
152         EmpathyContactWidget *information;
153         GladeXML             *glade;
154
155         information = g_slice_new0 (EmpathyContactWidget);
156         information->type = type;
157         information->factory = empathy_contact_factory_new ();
158         if (contact) {
159                 information->is_user = empathy_contact_is_user (contact);
160         } else {
161                 information->is_user = FALSE;
162         }
163
164         glade = empathy_glade_get_file ("empathy-contact-widget.glade",
165                                        "vbox_contact_widget",
166                                        NULL,
167                                        "vbox_contact_widget", &information->vbox_contact_widget,
168                                        "vbox_contact", &information->vbox_contact,
169                                        "hbox_presence", &information->hbox_presence,
170                                        "label_alias", &information->label_alias,
171                                        "image_state", &information->image_state,
172                                        "label_status", &information->label_status,
173                                        "table_contact", &information->table_contact,
174                                        "hbox_contact", &information->hbox_contact,
175                                        "vbox_groups", &information->vbox_groups,
176                                        "entry_group", &information->entry_group,
177                                        "button_group", &information->button_group,
178                                        "treeview_groups", &information->treeview_groups,
179                                        "vbox_details", &information->vbox_details,
180                                        "table_details", &information->table_details,
181                                        "hbox_details_requested", &information->hbox_details_requested,
182                                        "vbox_client", &information->vbox_client,
183                                        "table_client", &information->table_client,
184                                        "hbox_client_requested", &information->hbow_client_requested,
185                                        NULL);
186
187         empathy_glade_connect (glade,
188                               information,
189                               "vbox_contact_widget", "destroy", contact_widget_destroy_cb,
190                               "entry_group", "changed", contact_widget_entry_group_changed_cb,
191                               "entry_group", "activate", contact_widget_entry_group_activate_cb,
192                               "button_group", "clicked", contact_widget_button_group_clicked_cb,
193                               NULL);
194
195         g_object_unref (glade);
196
197         g_object_set_data (G_OBJECT (information->vbox_contact_widget),
198                            "EmpathyContactWidget",
199                            information);
200
201         /* Create widgets */
202         contact_widget_contact_setup (information);
203         contact_widget_groups_setup (information);
204         contact_widget_details_setup (information);
205         contact_widget_client_setup (information);
206
207         contact_widget_set_contact (information, contact);
208
209         gtk_widget_show (information->vbox_contact_widget);
210
211         return information->vbox_contact_widget;
212 }
213
214 EmpathyContact *
215 empathy_contact_widget_get_contact (GtkWidget *widget)
216 {
217         EmpathyContactWidget *information;
218
219         g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
220
221         information = g_object_get_data (G_OBJECT (widget), "EmpathyContactWidget");
222         if (!information) {
223                 return NULL;
224         }
225
226         return information->contact;
227 }
228         
229 static void
230 contact_widget_destroy_cb (GtkWidget            *widget,
231                            EmpathyContactWidget *information)
232 {
233         contact_widget_remove_contact (information);
234
235         if (information->widget_id_timeout != 0) {
236                 g_source_remove (information->widget_id_timeout);
237         }
238         if (information->factory) {
239                 g_object_unref (information->factory);
240         }               
241         if (information->manager) {
242                 g_object_unref (information->manager);
243         }               
244
245         g_slice_free (EmpathyContactWidget, information);
246 }
247
248 static void
249 contact_widget_remove_contact (EmpathyContactWidget *information)
250 {
251         if (information->contact) {
252                 g_signal_handlers_disconnect_by_func (information->contact,
253                                                       contact_widget_name_notify_cb,
254                                                       information);
255                 g_signal_handlers_disconnect_by_func (information->contact,
256                                                       contact_widget_presence_notify_cb,
257                                                       information);
258                 g_signal_handlers_disconnect_by_func (information->contact,
259                                                       contact_widget_avatar_notify_cb,
260                                                       information);
261                 g_signal_handlers_disconnect_by_func (information->contact,
262                                                       contact_widget_groups_notify_cb,
263                                                       information);
264
265                 g_object_unref (information->contact);
266                 information->contact = NULL;
267         }
268 }
269
270 static void
271 contact_widget_set_contact (EmpathyContactWidget *information,
272                             EmpathyContact        *contact)
273 {
274         contact_widget_remove_contact (information);
275         if (contact) {
276                 information->contact = g_object_ref (contact);
277         }
278
279         /* Update information for widgets */
280         contact_widget_contact_update (information);
281         contact_widget_groups_update (information);
282         contact_widget_details_update (information);
283         contact_widget_client_update (information);
284 }
285
286 static gboolean
287 contact_widget_can_add_contact_to_account (McAccount *account,
288                                            gpointer   user_data)
289 {
290         MissionControl *mc;
291         TpConn         *tp_conn;
292         McProfile      *profile;
293         const gchar    *protocol_name;
294
295         mc = empathy_mission_control_new ();
296         tp_conn = mission_control_get_connection (mc, account, NULL);
297         g_object_unref (mc);
298         if (tp_conn == NULL) {
299                 /* Account is disconnected */
300                 return FALSE;
301         }
302         g_object_unref (tp_conn);
303
304         profile = mc_account_get_profile (account);
305         protocol_name = mc_profile_get_protocol_name (profile);
306         if (strcmp (protocol_name, "local-xmpp") == 0) {
307                 /* We can't add accounts to a XMPP LL connection
308                  * FIXME: We should inspect the flags of the contact list group interface
309                  */
310                 g_object_unref (profile);
311                 return FALSE;
312         }
313
314         g_object_unref (profile);
315         return TRUE;
316 }
317
318 static gboolean
319 contact_widget_id_activate_timeout (EmpathyContactWidget *self)
320 {
321         contact_widget_update_contact (self);
322         return FALSE;
323 }
324
325 static void
326 contact_widget_id_changed_cb (GtkEntry             *entry,
327                               EmpathyContactWidget *self)
328 {
329         if (self->widget_id_timeout != 0) {             
330                 g_source_remove (self->widget_id_timeout);
331         }
332
333         self->widget_id_timeout =
334                 g_timeout_add (ID_CHANGED_TIMEOUT,
335                                (GSourceFunc) contact_widget_id_activate_timeout,
336                                self);
337 }
338
339 static void
340 contact_widget_contact_setup (EmpathyContactWidget *information)
341 {
342         /* FIXME: Use EmpathyAvatarImage if (editable && is_user)  */
343         information->widget_avatar = gtk_image_new ();
344         gtk_box_pack_end (GTK_BOX (information->hbox_contact),
345                           information->widget_avatar,
346                           FALSE, FALSE,
347                           6);
348
349         /* Setup account label/chooser */
350         if (information->type == CONTACT_WIDGET_TYPE_ADD) {
351                 information->widget_account = empathy_account_chooser_new ();
352                 empathy_account_chooser_set_filter (
353                         EMPATHY_ACCOUNT_CHOOSER (information->widget_account),
354                         contact_widget_can_add_contact_to_account,
355                         NULL);
356
357                 g_signal_connect (information->widget_account, "changed",
358                                   G_CALLBACK (contact_widget_account_changed_cb),
359                                   information);
360         } else {
361                 information->widget_account = gtk_label_new (NULL);
362                 gtk_label_set_selectable (GTK_LABEL (information->widget_account), TRUE);
363                 gtk_misc_set_alignment (GTK_MISC (information->widget_account), 0, 0.5);
364         }
365         gtk_table_attach_defaults (GTK_TABLE (information->table_contact),
366                                    information->widget_account,
367                                    1, 2, 0, 1);
368         gtk_widget_show (information->widget_account);
369
370         /* Setup id label/entry */
371         if (information->type == CONTACT_WIDGET_TYPE_ADD) {
372                 information->widget_id = gtk_entry_new ();
373                 g_signal_connect (information->widget_id, "focus-out-event",
374                                   G_CALLBACK (contact_widget_id_focus_out_cb),
375                                   information);
376                 g_signal_connect (information->widget_id, "changed",
377                                   G_CALLBACK (contact_widget_id_changed_cb),
378                                   information);
379         } else {
380                 information->widget_id = gtk_label_new (NULL);
381                 gtk_label_set_selectable (GTK_LABEL (information->widget_id), TRUE);
382                 gtk_misc_set_alignment (GTK_MISC (information->widget_id), 0, 0.5);
383         }
384         gtk_table_attach_defaults (GTK_TABLE (information->table_contact),
385                                    information->widget_id,
386                                    1, 2, 1, 2);
387         gtk_widget_show (information->widget_id);
388
389         /* Setup alias label/entry */
390         if (information->type > CONTACT_WIDGET_TYPE_SHOW) {
391                 information->widget_alias = gtk_entry_new ();
392                 g_signal_connect (information->widget_alias, "focus-out-event",
393                                   G_CALLBACK (contact_widget_entry_alias_focus_event_cb),
394                                   information);
395         } else {
396                 information->widget_alias = gtk_label_new (NULL);
397                 gtk_label_set_selectable (GTK_LABEL (information->widget_alias), TRUE);
398                 gtk_misc_set_alignment (GTK_MISC (information->widget_alias), 0, 0.5);
399         }
400         gtk_table_attach_defaults (GTK_TABLE (information->table_contact),
401                                    information->widget_alias,
402                                    1, 2, 2, 3);
403         gtk_widget_show (information->widget_alias);
404 }
405
406 static void
407 contact_widget_contact_update (EmpathyContactWidget *information)
408 {
409         McAccount   *account = NULL;
410         const gchar *id = NULL;
411
412         /* Connect and get info from new contact */
413         if (information->contact) {
414                 g_signal_connect_swapped (information->contact, "notify::name",
415                                           G_CALLBACK (contact_widget_name_notify_cb),
416                                           information);
417                 g_signal_connect_swapped (information->contact, "notify::presence",
418                                           G_CALLBACK (contact_widget_presence_notify_cb),
419                                           information);
420                 g_signal_connect_swapped (information->contact, "notify::avatar",
421                                           G_CALLBACK (contact_widget_avatar_notify_cb),
422                                           information);
423
424                 account = empathy_contact_get_account (information->contact);
425                 id = empathy_contact_get_id (information->contact);
426         }
427
428         /* Update account widget */
429         if (information->type == CONTACT_WIDGET_TYPE_ADD) {
430                 if (account) {
431                         g_signal_handlers_block_by_func (information->widget_account,
432                                                          contact_widget_account_changed_cb,
433                                                          information);
434                         empathy_account_chooser_set_account (EMPATHY_ACCOUNT_CHOOSER (information->widget_account),
435                                                             account);
436                         g_signal_handlers_unblock_by_func (information->widget_account,
437                                                            contact_widget_account_changed_cb,
438                                                            information);
439                 }
440                 if (!G_STR_EMPTY (id)) {
441                         gtk_entry_set_text (GTK_ENTRY (information->widget_id), id);
442                 }
443         } else {
444                 if (account) {
445                         const gchar *name;
446
447                         name = mc_account_get_display_name (account);
448                         gtk_label_set_label (GTK_LABEL (information->widget_account), name);
449                 }
450                 gtk_label_set_label (GTK_LABEL (information->widget_id), id);
451         }
452
453         /* Update other widgets */
454         if (information->contact) {
455                 contact_widget_name_notify_cb (information);
456                 contact_widget_presence_notify_cb (information);
457                 contact_widget_avatar_notify_cb (information);
458
459                 gtk_widget_show (information->label_alias);
460                 gtk_widget_show (information->widget_alias);
461                 gtk_widget_show (information->hbox_presence);
462         } else {
463                 gtk_widget_hide (information->label_alias);
464                 gtk_widget_hide (information->widget_alias);
465                 gtk_widget_hide (information->hbox_presence);
466                 gtk_widget_hide (information->widget_avatar);
467         }
468 }
469
470 static gboolean
471 contact_widget_update_contact (EmpathyContactWidget *information)
472 {
473         McAccount   *account;
474         const gchar *id;
475
476         account = empathy_account_chooser_get_account (EMPATHY_ACCOUNT_CHOOSER (information->widget_account));
477         id = gtk_entry_get_text (GTK_ENTRY (information->widget_id));
478
479         if (account && !G_STR_EMPTY (id)) {
480                 EmpathyContact *contact;
481
482                 contact = empathy_contact_factory_get_from_id (information->factory,
483                                                                account, id);
484                 contact_widget_set_contact (information, contact);
485
486                 if (contact) {
487                         g_object_unref (contact);
488                 }
489         }
490
491         return FALSE;
492 }
493
494 static void
495 contact_widget_account_changed_cb (GtkComboBox          *widget,
496                                    EmpathyContactWidget *information)
497 {
498         contact_widget_update_contact (information);
499 }
500
501 static gboolean
502 contact_widget_id_focus_out_cb (GtkWidget            *widget,
503                                 GdkEventFocus        *event,
504                                 EmpathyContactWidget *information)
505 {
506         contact_widget_update_contact (information);
507         return FALSE;
508 }
509
510 static gboolean
511 contact_widget_entry_alias_focus_event_cb (GtkEditable          *editable,
512                                            GdkEventFocus        *event,
513                                            EmpathyContactWidget *information)
514 {
515         if (information->contact) {
516                 const gchar *name;
517
518                 name = gtk_entry_get_text (GTK_ENTRY (editable));
519                 empathy_contact_factory_set_name (information->factory,
520                                                   information->contact,
521                                                   name);
522         }
523
524         return FALSE;
525 }
526
527 static void
528 contact_widget_name_notify_cb (EmpathyContactWidget *information)
529 {
530         if (GTK_IS_ENTRY (information->widget_alias)) {
531                 gtk_entry_set_text (GTK_ENTRY (information->widget_alias),
532                                     empathy_contact_get_name (information->contact));
533         } else {
534                 gtk_label_set_label (GTK_LABEL (information->widget_alias),
535                                      empathy_contact_get_name (information->contact));
536         }
537 }
538
539 static void
540 contact_widget_presence_notify_cb (EmpathyContactWidget *information)
541 {
542         gtk_label_set_text (GTK_LABEL (information->label_status),
543                             empathy_contact_get_status (information->contact));
544         gtk_image_set_from_icon_name (GTK_IMAGE (information->image_state),
545                                       empathy_icon_name_for_contact (information->contact),
546                                       GTK_ICON_SIZE_BUTTON);
547
548 }
549
550 static void
551 contact_widget_avatar_notify_cb (EmpathyContactWidget *information)
552 {
553         GdkPixbuf *avatar_pixbuf;
554
555         avatar_pixbuf = empathy_pixbuf_avatar_from_contact_scaled (information->contact,
556                                                                   48, 48);
557
558         if (avatar_pixbuf) {
559                 gtk_image_set_from_pixbuf (GTK_IMAGE (information->widget_avatar),
560                                            avatar_pixbuf);
561                 gtk_widget_show  (information->widget_avatar);
562                 g_object_unref (avatar_pixbuf);
563         } else {
564                 gtk_widget_hide  (information->widget_avatar);
565         }
566 }
567
568 static void
569 contact_widget_groups_setup (EmpathyContactWidget *information)
570 {
571         if (information->type > CONTACT_WIDGET_TYPE_SHOW) {
572                 information->manager = empathy_contact_manager_new ();
573                 contact_widget_model_setup (information);
574         }
575 }
576
577 static void
578 contact_widget_groups_update (EmpathyContactWidget *information)
579 {
580         if (information->type > CONTACT_WIDGET_TYPE_SHOW &&
581             information->contact) {
582                 g_signal_connect_swapped (information->contact, "notify::groups",
583                                           G_CALLBACK (contact_widget_groups_notify_cb),
584                                           information);
585                 contact_widget_groups_populate_data (information);
586
587                 gtk_widget_show (information->vbox_groups);
588         } else {
589                 gtk_widget_hide (information->vbox_groups);
590         }
591 }
592
593 static void
594 contact_widget_model_setup (EmpathyContactWidget *information)
595 {
596         GtkTreeView      *view;
597         GtkListStore     *store;
598         GtkTreeSelection *selection;
599
600         view = GTK_TREE_VIEW (information->treeview_groups);
601
602         store = gtk_list_store_new (COL_COUNT,
603                                     G_TYPE_STRING,   /* name */
604                                     G_TYPE_BOOLEAN,  /* enabled */
605                                     G_TYPE_BOOLEAN); /* editable */
606
607         gtk_tree_view_set_model (view, GTK_TREE_MODEL (store));
608
609         selection = gtk_tree_view_get_selection (view);
610         gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
611
612         contact_widget_model_populate_columns (information);
613
614         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
615                                               COL_NAME, GTK_SORT_ASCENDING);
616
617         g_object_unref (store);
618 }
619
620 static void
621 contact_widget_model_populate_columns (EmpathyContactWidget *information)
622 {
623         GtkTreeView       *view;
624         GtkTreeModel      *model;
625         GtkTreeViewColumn *column;
626         GtkCellRenderer   *renderer;
627         guint              col_offset;
628
629         view = GTK_TREE_VIEW (information->treeview_groups);
630         model = gtk_tree_view_get_model (view);
631
632         renderer = gtk_cell_renderer_toggle_new ();
633         g_signal_connect (renderer, "toggled",
634                           G_CALLBACK (contact_widget_cell_toggled),
635                           information);
636
637         column = gtk_tree_view_column_new_with_attributes (_("Select"), renderer,
638                                                            "active", COL_ENABLED,
639                                                            NULL);
640
641         gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
642         gtk_tree_view_column_set_fixed_width (column, 50);
643         gtk_tree_view_append_column (view, column);
644
645         renderer = gtk_cell_renderer_text_new ();
646         col_offset = gtk_tree_view_insert_column_with_attributes (view,
647                                                                   -1, _("Group"),
648                                                                   renderer,
649                                                                   "text", COL_NAME,
650                                                                   /* "editable", COL_EDITABLE, */
651                                                                   NULL);
652
653         g_object_set_data (G_OBJECT (renderer),
654                            "column", GINT_TO_POINTER (COL_NAME));
655
656         column = gtk_tree_view_get_column (view, col_offset - 1);
657         gtk_tree_view_column_set_sort_column_id (column, COL_NAME);
658         gtk_tree_view_column_set_resizable (column,FALSE);
659         gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
660
661         if (information->renderer) {
662                 g_object_unref (information->renderer);
663         }
664
665         information->renderer = g_object_ref (renderer);
666 }
667
668 static void
669 contact_widget_groups_populate_data (EmpathyContactWidget *information)
670 {
671         GtkTreeView  *view;
672         GtkListStore *store;
673         GtkTreeIter   iter;
674         GList        *my_groups, *l;
675         GList        *all_groups;
676
677         view = GTK_TREE_VIEW (information->treeview_groups);
678         store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
679         gtk_list_store_clear (store);
680
681         all_groups = empathy_contact_list_get_all_groups (EMPATHY_CONTACT_LIST (information->manager));
682         my_groups = empathy_contact_list_get_groups (EMPATHY_CONTACT_LIST (information->manager),
683                                                      information->contact);
684
685         for (l = all_groups; l; l = l->next) {
686                 const gchar *group_str;
687                 gboolean     enabled;
688
689                 group_str = l->data;
690
691                 enabled = g_list_find_custom (my_groups,
692                                               group_str,
693                                               (GCompareFunc) strcmp) != NULL;
694
695                 gtk_list_store_append (store, &iter);
696                 gtk_list_store_set (store, &iter,
697                                     COL_NAME, group_str,
698                                     COL_EDITABLE, TRUE,
699                                     COL_ENABLED, enabled,
700                                     -1);
701         }
702
703         g_list_foreach (all_groups, (GFunc) g_free, NULL);
704         g_list_foreach (my_groups, (GFunc) g_free, NULL);
705         g_list_free (all_groups);
706         g_list_free (my_groups);
707 }
708
709 static void
710 contact_widget_groups_notify_cb (EmpathyContactWidget *information)
711 {
712         /* FIXME: not implemented */
713 }
714
715 static gboolean
716 contact_widget_model_find_name (EmpathyContactWidget *information,
717                                 const gchar          *name,
718                                 GtkTreeIter          *iter)
719 {
720         GtkTreeView  *view;
721         GtkTreeModel *model;
722         FindName      data;
723
724         if (G_STR_EMPTY (name)) {
725                 return FALSE;
726         }
727
728         data.information = information;
729         data.name = name;
730         data.found = FALSE;
731
732         view = GTK_TREE_VIEW (information->treeview_groups);
733         model = gtk_tree_view_get_model (view);
734
735         gtk_tree_model_foreach (model,
736                                 (GtkTreeModelForeachFunc) contact_widget_model_find_name_foreach,
737                                 &data);
738
739         if (data.found == TRUE) {
740                 *iter = data.found_iter;
741                 return TRUE;
742         }
743
744         return FALSE;
745 }
746
747 static gboolean
748 contact_widget_model_find_name_foreach (GtkTreeModel *model,
749                                         GtkTreePath  *path,
750                                         GtkTreeIter  *iter,
751                                         FindName     *data)
752 {
753         gchar *name;
754
755         gtk_tree_model_get (model, iter,
756                             COL_NAME, &name,
757                             -1);
758
759         if (!name) {
760                 return FALSE;
761         }
762
763         if (data->name && strcmp (data->name, name) == 0) {
764                 data->found = TRUE;
765                 data->found_iter = *iter;
766
767                 g_free (name);
768
769                 return TRUE;
770         }
771
772         g_free (name);
773
774         return FALSE;
775 }
776
777 static void
778 contact_widget_cell_toggled (GtkCellRendererToggle *cell,
779                              gchar                 *path_string,
780                              EmpathyContactWidget  *information)
781 {
782         GtkTreeView  *view;
783         GtkTreeModel *model;
784         GtkListStore *store;
785         GtkTreePath  *path;
786         GtkTreeIter   iter;
787         gboolean      enabled;
788         gchar        *group;
789
790         view = GTK_TREE_VIEW (information->treeview_groups);
791         model = gtk_tree_view_get_model (view);
792         store = GTK_LIST_STORE (model);
793
794         path = gtk_tree_path_new_from_string (path_string);
795
796         gtk_tree_model_get_iter (model, &iter, path);
797         gtk_tree_model_get (model, &iter,
798                             COL_ENABLED, &enabled,
799                             COL_NAME, &group,
800                             -1);
801
802         gtk_list_store_set (store, &iter, COL_ENABLED, !enabled, -1);
803         gtk_tree_path_free (path);
804
805         if (group) {
806                 if (enabled) {
807                         empathy_contact_list_remove_from_group (EMPATHY_CONTACT_LIST (information->manager),
808                                                                 information->contact,
809                                                                 group);
810                 } else {
811                         empathy_contact_list_add_to_group (EMPATHY_CONTACT_LIST (information->manager),
812                                                            information->contact,
813                                                            group);
814                 }
815
816                 g_free (group);
817         }
818 }
819
820 static void
821 contact_widget_entry_group_changed_cb (GtkEditable           *editable,
822                                        EmpathyContactWidget  *information)
823 {
824         GtkTreeIter  iter;
825         const gchar *group;
826
827         group = gtk_entry_get_text (GTK_ENTRY (information->entry_group));
828
829         if (contact_widget_model_find_name (information, group, &iter)) {
830                 gtk_widget_set_sensitive (GTK_WIDGET (information->button_group), FALSE);
831
832         } else {
833                 gtk_widget_set_sensitive (GTK_WIDGET (information->button_group),
834                                           !G_STR_EMPTY (group));
835         }
836 }
837
838 static void
839 contact_widget_entry_group_activate_cb (GtkEntry              *entry,
840                                         EmpathyContactWidget  *information)
841 {
842         gtk_widget_activate (GTK_WIDGET (information->button_group));
843 }
844
845 static void
846 contact_widget_button_group_clicked_cb (GtkButton             *button,
847                                         EmpathyContactWidget  *information)
848 {
849         GtkTreeView  *view;
850         GtkListStore *store;
851         GtkTreeIter   iter;
852         const gchar  *group;
853
854         view = GTK_TREE_VIEW (information->treeview_groups);
855         store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
856
857         group = gtk_entry_get_text (GTK_ENTRY (information->entry_group));
858
859         gtk_list_store_append (store, &iter);
860         gtk_list_store_set (store, &iter,
861                             COL_NAME, group,
862                             COL_ENABLED, TRUE,
863                             -1);
864
865         empathy_contact_list_add_to_group (EMPATHY_CONTACT_LIST (information->manager),
866                                            information->contact,
867                                            group);
868 }
869
870 static void
871 contact_widget_details_setup (EmpathyContactWidget *information)
872 {
873         /* FIXME: Needs new telepathy spec */
874         gtk_widget_hide (information->vbox_details);
875 }
876
877 static void
878 contact_widget_details_update (EmpathyContactWidget *information)
879 {
880         /* FIXME: Needs new telepathy spec */
881 }
882
883 static void
884 contact_widget_client_setup (EmpathyContactWidget *information)
885 {
886         /* FIXME: Needs new telepathy spec */
887         gtk_widget_hide (information->vbox_client);
888 }
889
890 static void
891 contact_widget_client_update (EmpathyContactWidget *information)
892 {
893         /* FIXME: Needs new telepathy spec */
894 }
895