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