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