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