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