]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-contact-widget.c
empathy-contact-widget: use gtk_image_menu_item_new_from_stock (Guillaume Desmottes)
[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 #include <telepathy-glib/util.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 (seconds) */
46 #define ID_CHANGED_TIMEOUT 1
47
48 typedef struct
49 {
50         EmpathyContactFactory *factory;
51         EmpathyContactManager *manager;
52         EmpathyContact *contact;
53         EmpathyContactWidgetFlags flags;
54         GtkCellRenderer *renderer;
55         guint widget_id_timeout;
56
57         GtkWidget *vbox_contact_widget;
58
59         /* Contact */
60         GtkWidget *vbox_contact;
61         GtkWidget *widget_avatar;
62         GtkWidget *widget_account;
63         GtkWidget *widget_id;
64         GtkWidget *widget_alias;
65         GtkWidget *label_alias;
66         GtkWidget *entry_alias;
67         GtkWidget *hbox_presence;
68         GtkWidget *image_state;
69         GtkWidget *label_status;
70         GtkWidget *table_contact;
71         GtkWidget *vbox_avatar;
72
73         /* Groups */
74         GtkWidget *vbox_groups;
75         GtkWidget *entry_group;
76         GtkWidget *button_group;
77         GtkWidget *treeview_groups;
78
79         /* Details */
80         GtkWidget *vbox_details;
81         GtkWidget *table_details;
82         GtkWidget *hbox_details_requested;
83
84         /* Client */
85         GtkWidget *vbox_client;
86         GtkWidget *table_client;
87         GtkWidget *hbow_client_requested;
88 } EmpathyContactWidget;
89
90 typedef struct
91 {
92         EmpathyContactWidget *information;
93         const gchar *name;
94         gboolean found;
95         GtkTreeIter found_iter;
96 } FindName;
97
98 static void contact_widget_destroy_cb (GtkWidget *widget,
99     EmpathyContactWidget *information);
100 static void contact_widget_remove_contact (EmpathyContactWidget *information);
101 static void contact_widget_set_contact (EmpathyContactWidget *information,
102     EmpathyContact *contact);
103 static void contact_widget_contact_setup (EmpathyContactWidget *information);
104 static void contact_widget_contact_update (EmpathyContactWidget *information);
105 static void contact_widget_change_contact (EmpathyContactWidget *information);
106 static void contact_widget_avatar_changed_cb (EmpathyAvatarChooser *chooser,
107     EmpathyContactWidget *information);
108 static void contact_widget_account_changed_cb (GtkComboBox *widget,
109     EmpathyContactWidget *information);
110 static gboolean contact_widget_id_focus_out_cb (GtkWidget *widget,
111     GdkEventFocus *event, EmpathyContactWidget *information);
112 static gboolean contact_widget_entry_alias_focus_event_cb (
113     GtkEditable *editable, GdkEventFocus *event,
114     EmpathyContactWidget *information);
115 static void contact_widget_name_notify_cb (EmpathyContactWidget *information);
116 static void contact_widget_presence_notify_cb (
117     EmpathyContactWidget *information);
118 static void contact_widget_avatar_notify_cb (
119     EmpathyContactWidget *information);
120 static void contact_widget_groups_setup (
121     EmpathyContactWidget *information);
122 static void contact_widget_groups_update (EmpathyContactWidget *information);
123 static void contact_widget_model_setup (EmpathyContactWidget *information);
124 static void contact_widget_model_populate_columns (
125     EmpathyContactWidget *information);
126 static void contact_widget_groups_populate_data (
127     EmpathyContactWidget *information);
128 static void contact_widget_groups_notify_cb (
129     EmpathyContactWidget *information);
130 static gboolean contact_widget_model_find_name (
131     EmpathyContactWidget *information,const gchar *name, GtkTreeIter *iter);
132 static gboolean contact_widget_model_find_name_foreach (GtkTreeModel *model,
133     GtkTreePath *path, GtkTreeIter *iter, FindName *data);
134 static void contact_widget_cell_toggled (GtkCellRendererToggle *cell,
135     gchar *path_string, EmpathyContactWidget *information);
136 static void contact_widget_entry_group_changed_cb (GtkEditable *editable,
137     EmpathyContactWidget *information);
138 static void contact_widget_entry_group_activate_cb (GtkEntry *entry,
139     EmpathyContactWidget *information);
140 static void contact_widget_button_group_clicked_cb (GtkButton *button,
141     EmpathyContactWidget *information);
142 static void contact_widget_details_setup (EmpathyContactWidget *information);
143 static void contact_widget_details_update (EmpathyContactWidget *information);
144 static void contact_widget_client_setup (EmpathyContactWidget *information);
145 static void contact_widget_client_update (EmpathyContactWidget *information);
146
147 enum
148 {
149         COL_NAME,
150         COL_ENABLED,
151         COL_EDITABLE,
152         COL_COUNT
153 };
154
155 GtkWidget *
156 empathy_contact_widget_new (EmpathyContact *contact,
157                             EmpathyContactWidgetFlags flags)
158 {
159         EmpathyContactWidget *information;
160         GladeXML *glade;
161         gchar *filename;
162
163         information = g_slice_new0 (EmpathyContactWidget);
164         information->flags = flags;
165         information->factory = empathy_contact_factory_new ();
166
167         filename = empathy_file_lookup ("empathy-contact-widget.glade",
168       "libempathy-gtk");
169         glade = empathy_glade_get_file (filename,
170       "vbox_contact_widget",
171        NULL,
172        "vbox_contact_widget", &information->vbox_contact_widget,
173        "vbox_contact", &information->vbox_contact,
174        "hbox_presence", &information->hbox_presence,
175        "label_alias", &information->label_alias,
176        "image_state", &information->image_state,
177        "label_status", &information->label_status,
178        "table_contact", &information->table_contact,
179        "vbox_avatar", &information->vbox_avatar,
180        "vbox_groups", &information->vbox_groups,
181        "entry_group", &information->entry_group,
182        "button_group", &information->button_group,
183        "treeview_groups", &information->treeview_groups,
184        "vbox_details", &information->vbox_details,
185        "table_details", &information->table_details,
186        "hbox_details_requested", &information->hbox_details_requested,
187        "vbox_client", &information->vbox_client,
188        "table_client", &information->table_client,
189        "hbox_client_requested", &information->hbow_client_requested,
190        NULL);
191         g_free (filename);
192
193         empathy_glade_connect (glade,
194       information,
195       "vbox_contact_widget", "destroy", contact_widget_destroy_cb,
196       "entry_group", "changed", contact_widget_entry_group_changed_cb,
197       "entry_group", "activate", contact_widget_entry_group_activate_cb,
198       "button_group", "clicked", contact_widget_button_group_clicked_cb,
199       NULL);
200
201         g_object_unref (glade);
202
203         g_object_set_data (G_OBJECT (information->vbox_contact_widget),
204       "EmpathyContactWidget",
205                         information);
206
207         /* Create widgets */
208         contact_widget_contact_setup (information);
209         contact_widget_groups_setup (information);
210         contact_widget_details_setup (information);
211         contact_widget_client_setup (information);
212
213         contact_widget_set_contact (information, contact);
214
215         gtk_widget_show (information->vbox_contact_widget);
216
217         return information->vbox_contact_widget;
218 }
219
220 EmpathyContact *
221 empathy_contact_widget_get_contact (GtkWidget *widget)
222 {
223         EmpathyContactWidget *information;
224
225         g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
226
227         information = g_object_get_data (G_OBJECT (widget), "EmpathyContactWidget");
228         if (!information)
229     {
230       return NULL;
231     }
232
233         return information->contact;
234 }
235
236 void
237 empathy_contact_widget_set_contact (GtkWidget *widget,
238                                     EmpathyContact *contact)
239 {
240         EmpathyContactWidget *information;
241
242         g_return_if_fail (GTK_IS_WIDGET (widget));
243         g_return_if_fail (EMPATHY_IS_CONTACT (contact));
244
245         information = g_object_get_data (G_OBJECT (widget), "EmpathyContactWidget");
246         if (!information) {
247                 return;
248         }
249
250         contact_widget_set_contact (information, contact);
251 }
252
253 void
254 empathy_contact_widget_set_account_filter (
255     GtkWidget *widget,
256     EmpathyAccountChooserFilterFunc filter,
257     gpointer user_data)
258 {
259         EmpathyContactWidget *information;
260         EmpathyAccountChooser *chooser;
261
262         g_return_if_fail (GTK_IS_WIDGET (widget));
263
264         information = g_object_get_data (G_OBJECT (widget), "EmpathyContactWidget");
265         if (!information)
266                 return;
267
268         chooser = EMPATHY_ACCOUNT_CHOOSER (information->widget_account);
269         if (chooser)
270     {
271       empathy_account_chooser_set_filter (chooser, filter, user_data);
272     }
273 }
274         
275 static void
276 contact_widget_destroy_cb (GtkWidget *widget,
277                            EmpathyContactWidget *information)
278 {
279         contact_widget_remove_contact (information);
280
281         if (information->widget_id_timeout != 0)
282     {
283       g_source_remove (information->widget_id_timeout);
284     }
285         if (information->factory)
286     {
287       g_object_unref (information->factory);
288     }           
289         if (information->manager)
290     {
291       g_object_unref (information->manager);
292     }           
293
294         g_slice_free (EmpathyContactWidget, information);
295 }
296
297 static void
298 contact_widget_remove_contact (EmpathyContactWidget *information)
299 {
300         if (information->contact)
301     {
302       g_signal_handlers_disconnect_by_func (information->contact,
303           contact_widget_name_notify_cb, information);
304       g_signal_handlers_disconnect_by_func (information->contact,
305           contact_widget_presence_notify_cb, information);
306       g_signal_handlers_disconnect_by_func (information->contact,
307           contact_widget_avatar_notify_cb, information);
308       g_signal_handlers_disconnect_by_func (information->contact,
309           contact_widget_groups_notify_cb, information);
310
311       g_object_unref (information->contact);
312       information->contact = NULL;
313     }
314 }
315
316 static void
317 contact_widget_set_contact (EmpathyContactWidget *information,
318                                         EmpathyContact *contact)
319 {
320         if (contact == information->contact)
321                 return;
322
323         contact_widget_remove_contact (information);
324         if (contact)
325     {
326       information->contact = g_object_ref (contact);
327     }
328
329         /* Update information for widgets */
330         contact_widget_contact_update (information);
331         contact_widget_groups_update (information);
332         contact_widget_details_update (information);
333         contact_widget_client_update (information);
334 }
335
336 static gboolean
337 contact_widget_id_activate_timeout (EmpathyContactWidget *self)
338 {
339         contact_widget_change_contact (self);
340         return FALSE;
341 }
342
343 static void
344 contact_widget_id_changed_cb (GtkEntry *entry,
345                               EmpathyContactWidget *self)
346 {
347         if (self->widget_id_timeout != 0)
348     {           
349       g_source_remove (self->widget_id_timeout);
350     }
351
352         self->widget_id_timeout =
353                 g_timeout_add_seconds (ID_CHANGED_TIMEOUT,
354         (GSourceFunc) contact_widget_id_activate_timeout, self);
355 }
356
357 static void
358 save_avatar_menu_activate_cb (GtkWidget *widget,
359                               EmpathyContactWidget *information)
360 {
361   GtkWidget *dialog;
362   EmpathyAvatar *avatar;
363   gchar *ext = NULL, *filename;
364
365   dialog = gtk_file_chooser_dialog_new (_("Save Avatar"),
366       NULL,
367       GTK_FILE_CHOOSER_ACTION_SAVE,
368       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
369       GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
370       NULL);
371
372   gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
373       TRUE);
374
375   /* look for the avatar extension */
376   avatar = empathy_contact_get_avatar (information->contact);
377   if (avatar->format != NULL)
378     {
379       gchar **splitted;
380
381       splitted = g_strsplit (avatar->format, "/", 2);
382       if (splitted[0] != NULL && splitted[1] != NULL)
383         {
384           ext = g_strdup (splitted[1]);
385         }
386
387       g_strfreev (splitted);
388     }
389   else
390     {
391       /* Avatar was loaded from the cache so was converted to PNG */
392       ext = g_strdup ("png");
393     }
394
395   if (ext != NULL)
396     {
397       gchar *id;
398
399       id = tp_escape_as_identifier (empathy_contact_get_id (
400             information->contact));
401
402       filename = g_strdup_printf ("%s.%s", id, ext);
403       gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), filename);
404
405       g_free (id);
406       g_free (ext);
407       g_free (filename);
408     }
409
410   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
411     {
412       GError *error = NULL;
413
414       filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
415
416       if (!empathy_avatar_save_to_file (avatar, filename, &error))
417         {
418           /* Save error */
419           GtkWidget *dialog;
420
421           dialog = gtk_message_dialog_new (NULL, 0,
422               GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, 
423               _("Unable to save avatar"));
424
425           gtk_message_dialog_format_secondary_text (
426               GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
427
428           g_signal_connect (dialog, "response",
429               G_CALLBACK (gtk_widget_destroy), NULL);
430
431           gtk_window_present (GTK_WINDOW (dialog));
432
433           g_clear_error (&error);
434         }
435
436       g_free (filename);
437     }
438
439   gtk_widget_destroy (dialog);
440 }
441
442 static void
443 popup_avatar_menu (EmpathyContactWidget *information,
444                    GtkWidget *parent,
445                    GdkEventButton *event)
446 {
447   GtkWidget *menu, *item;
448   gint button, event_time;
449
450   menu = gtk_menu_new ();
451
452   /* Add "Save as..." entry */
453   item = gtk_image_menu_item_new_from_stock (GTK_STOCK_SAVE_AS, NULL);
454   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
455   gtk_widget_show (item);
456
457   g_signal_connect (item, "activate",
458       G_CALLBACK (save_avatar_menu_activate_cb), information);
459
460   if (event)
461     {
462       button = event->button;
463       event_time = event->time;
464     }
465   else
466     {
467       button = 0;
468       event_time = gtk_get_current_event_time ();
469     }
470
471   gtk_menu_attach_to_widget (GTK_MENU (menu), parent, NULL);
472   gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 
473       button, event_time);
474 }
475
476 static gboolean
477 widget_avatar_popup_menu_cb (GtkWidget *widget,
478                              EmpathyContactWidget *information)
479 {
480   popup_avatar_menu (information, widget, NULL);
481
482   return TRUE;
483 }
484
485 static gboolean
486 widget_avatar_button_press_event_cb (GtkWidget *widget,
487                                      GdkEventButton *event,
488                                      EmpathyContactWidget *information)
489 {
490   /* Ignore double-clicks and triple-clicks */
491   if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
492     {
493       popup_avatar_menu (information, widget, event);
494       return TRUE;
495     }
496
497   return FALSE;
498 }
499
500 static void
501 contact_widget_contact_setup (EmpathyContactWidget *information)
502 {
503         if (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_AVATAR) {
504                 information->widget_avatar = empathy_avatar_chooser_new ();
505                 g_signal_connect (information->widget_avatar, "changed",
506                                   G_CALLBACK (contact_widget_avatar_changed_cb),
507                                   information);
508         } else {
509                 information->widget_avatar = empathy_avatar_image_new ();
510
511     g_signal_connect (information->widget_avatar, "popup-menu",
512         G_CALLBACK (widget_avatar_popup_menu_cb), information);
513     g_signal_connect (information->widget_avatar, "button-press-event",
514         G_CALLBACK (widget_avatar_button_press_event_cb), information);
515         }
516   
517         gtk_box_pack_start (GTK_BOX (information->vbox_avatar),
518                             information->widget_avatar,
519                             FALSE, FALSE,
520                             6);
521         gtk_widget_show (information->widget_avatar);
522
523         /* Setup account label/chooser */
524         if (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_ACCOUNT) {
525                 information->widget_account = empathy_account_chooser_new ();
526
527                 g_signal_connect (information->widget_account, "changed",
528                                   G_CALLBACK (contact_widget_account_changed_cb),
529                                   information);
530         } else {
531                 information->widget_account = gtk_label_new (NULL);
532                 gtk_label_set_selectable (GTK_LABEL (information->widget_account), TRUE);
533                 gtk_misc_set_alignment (GTK_MISC (information->widget_account), 0, 0.5);
534         }
535         gtk_table_attach_defaults (GTK_TABLE (information->table_contact),
536                                    information->widget_account,
537                                    1, 2, 0, 1);
538         gtk_widget_show (information->widget_account);
539
540         /* Setup id label/entry */
541         if (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_ID) {
542                 information->widget_id = gtk_entry_new ();
543                 g_signal_connect (information->widget_id, "focus-out-event",
544                                   G_CALLBACK (contact_widget_id_focus_out_cb),
545                                   information);
546                 g_signal_connect (information->widget_id, "changed",
547                                   G_CALLBACK (contact_widget_id_changed_cb),
548                                   information);
549         } else {
550                 information->widget_id = gtk_label_new (NULL);
551                 gtk_label_set_selectable (GTK_LABEL (information->widget_id), TRUE);
552                 gtk_misc_set_alignment (GTK_MISC (information->widget_id), 0, 0.5);
553         }
554         gtk_table_attach_defaults (GTK_TABLE (information->table_contact),
555                                    information->widget_id,
556                                    1, 2, 1, 2);
557         gtk_widget_show (information->widget_id);
558
559         /* Setup alias label/entry */
560         if (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_ALIAS) {
561                 information->widget_alias = gtk_entry_new ();
562                 g_signal_connect (information->widget_alias, "focus-out-event",
563                                   G_CALLBACK (contact_widget_entry_alias_focus_event_cb),
564                                   information);
565                 /* Make return activate the window default (the Close button) */
566                 gtk_entry_set_activates_default (GTK_ENTRY (information->widget_alias), TRUE);
567         } else {
568                 information->widget_alias = gtk_label_new (NULL);
569                 gtk_label_set_selectable (GTK_LABEL (information->widget_alias), TRUE);
570                 gtk_misc_set_alignment (GTK_MISC (information->widget_alias), 0, 0.5);
571         }
572         gtk_table_attach_defaults (GTK_TABLE (information->table_contact),
573                                    information->widget_alias,
574                                    1, 2, 2, 3);
575         gtk_widget_show (information->widget_alias);
576 }
577
578 static void
579 contact_widget_contact_update (EmpathyContactWidget *information)
580 {
581         McAccount *account = NULL;
582         const gchar *id = NULL;
583
584         /* Connect and get info from new contact */
585         if (information->contact)
586     {
587       g_signal_connect_swapped (information->contact, "notify::name",
588           G_CALLBACK (contact_widget_name_notify_cb), information);
589       g_signal_connect_swapped (information->contact, "notify::presence",
590           G_CALLBACK (contact_widget_presence_notify_cb), information);
591       g_signal_connect_swapped (information->contact,
592           "notify::presence-message",
593           G_CALLBACK (contact_widget_presence_notify_cb), information);
594       g_signal_connect_swapped (information->contact, "notify::avatar",
595           G_CALLBACK (contact_widget_avatar_notify_cb), information);
596
597       account = empathy_contact_get_account (information->contact);
598       id = empathy_contact_get_id (information->contact);
599     }
600
601         /* Update account widget */
602         if (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_ACCOUNT)
603     {
604       if (account)
605         {
606           g_signal_handlers_block_by_func (information->widget_account,
607                    contact_widget_account_changed_cb,
608                    information);
609           empathy_account_chooser_set_account (
610               EMPATHY_ACCOUNT_CHOOSER (information->widget_account), account);
611           g_signal_handlers_unblock_by_func (information->widget_account,
612               contact_widget_account_changed_cb, information);
613         }
614     }
615   else
616     {
617       if (account)
618         {
619           const gchar *name;
620
621           name = mc_account_get_display_name (account);
622           gtk_label_set_label (GTK_LABEL (information->widget_account), name);
623         }
624           }
625
626         /* Update id widget */
627         if (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_ID)
628     {
629                   gtk_entry_set_text (GTK_ENTRY (information->widget_id), id ? id : "");
630           }
631   else
632     {
633       gtk_label_set_label (GTK_LABEL (information->widget_id), id ? id : "");
634     }
635
636         /* Update other widgets */
637         if (information->contact)
638     {
639       contact_widget_name_notify_cb (information);
640       contact_widget_presence_notify_cb (information);
641       contact_widget_avatar_notify_cb (information);
642
643       gtk_widget_show (information->label_alias);
644       gtk_widget_show (information->widget_alias);
645       gtk_widget_show (information->hbox_presence);
646       gtk_widget_show (information->widget_avatar);
647     }
648   else
649     {
650       gtk_widget_hide (information->label_alias);
651       gtk_widget_hide (information->widget_alias);
652       gtk_widget_hide (information->hbox_presence);
653       gtk_widget_hide (information->widget_avatar);
654     }
655 }
656
657 static void
658 contact_widget_change_contact (EmpathyContactWidget *information)
659 {
660         EmpathyContact *contact;
661         McAccount *account;
662
663         account = empathy_account_chooser_get_account (
664       EMPATHY_ACCOUNT_CHOOSER (information->widget_account));
665         if (!account)
666                 return;
667
668         if (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_ID)
669     {
670       const gchar *id;
671
672       id = gtk_entry_get_text (GTK_ENTRY (information->widget_id));
673       if (G_STR_EMPTY (id))
674         return;
675
676       contact = empathy_contact_factory_get_from_id (information->factory,
677           account, id);
678     }
679   else
680     {
681                 contact = empathy_contact_factory_get_user (information->factory,
682         account);
683         }
684
685         if (contact)
686     {
687       empathy_contact_run_until_ready (contact,
688           EMPATHY_CONTACT_READY_HANDLE |
689           EMPATHY_CONTACT_READY_ID,
690           NULL);
691       contact_widget_set_contact (information, contact);
692       g_object_unref (contact);
693     }
694 }
695
696 static void
697 contact_widget_avatar_changed_cb (EmpathyAvatarChooser *chooser,
698                                               EmpathyContactWidget *information)
699 {
700         if (information->contact && empathy_contact_is_user (information->contact))
701     {
702       McAccount *account;
703       const gchar *data;
704       gsize size;
705       const gchar *mime_type;
706
707       account = empathy_contact_get_account (information->contact);
708       empathy_avatar_chooser_get_image_data (
709           EMPATHY_AVATAR_CHOOSER (information->widget_avatar),
710           &data, &size, &mime_type);
711       empathy_contact_factory_set_avatar (information->factory, account,
712           data, size, mime_type);
713     }
714 }
715
716 static void
717 contact_widget_account_changed_cb (GtkComboBox *widget,
718                                    EmpathyContactWidget *information)
719 {
720         contact_widget_change_contact (information);
721 }
722
723 static gboolean
724 contact_widget_id_focus_out_cb (GtkWidget *widget,
725                                                         GdkEventFocus *event,
726                                 EmpathyContactWidget *information)
727 {
728         contact_widget_change_contact (information);
729         return FALSE;
730 }
731
732 static gboolean
733 contact_widget_entry_alias_focus_event_cb (GtkEditable *editable,
734                                            GdkEventFocus *event,
735                                            EmpathyContactWidget *information)
736 {
737         if (information->contact)
738     {
739       const gchar *alias;
740
741       alias = gtk_entry_get_text (GTK_ENTRY (editable));
742       empathy_contact_factory_set_alias (information->factory,
743           information->contact, alias);
744     }
745
746         return FALSE;
747 }
748
749 static void
750 contact_widget_name_notify_cb (EmpathyContactWidget *information)
751 {
752         if (GTK_IS_ENTRY (information->widget_alias))
753     {
754       gtk_entry_set_text (GTK_ENTRY (information->widget_alias),
755           empathy_contact_get_name (information->contact));
756     }
757   else
758     {
759       gtk_label_set_label (GTK_LABEL (information->widget_alias),
760           empathy_contact_get_name (information->contact));
761     }
762 }
763
764 static void
765 contact_widget_presence_notify_cb (EmpathyContactWidget *information)
766 {
767         gtk_label_set_text (GTK_LABEL (information->label_status),
768       empathy_contact_get_status (information->contact));
769         gtk_image_set_from_icon_name (GTK_IMAGE (information->image_state),
770                         empathy_icon_name_for_contact (information->contact),
771       GTK_ICON_SIZE_BUTTON);
772 }
773
774 static void
775 contact_widget_avatar_notify_cb (EmpathyContactWidget *information)
776 {
777         EmpathyAvatar *avatar = NULL;
778
779         if (information->contact) {
780                 avatar = empathy_contact_get_avatar (information->contact);
781         }
782         if (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_AVATAR) {
783                 g_signal_handlers_block_by_func (information->widget_avatar,
784                                                  contact_widget_avatar_changed_cb,
785                                                  information);
786                 empathy_avatar_chooser_set (
787         EMPATHY_AVATAR_CHOOSER (information->widget_avatar), avatar);
788                 g_signal_handlers_unblock_by_func (information->widget_avatar,
789         contact_widget_avatar_changed_cb, information);
790         }
791   else
792     {
793       empathy_avatar_image_set (
794           EMPATHY_AVATAR_IMAGE (information->widget_avatar), avatar);
795     }
796 }
797
798 static void
799 contact_widget_groups_setup (EmpathyContactWidget *information)
800 {
801         if (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_GROUPS)
802     {
803       information->manager = empathy_contact_manager_new ();
804       contact_widget_model_setup (information);
805     }
806 }
807
808 static void
809 contact_widget_groups_update (EmpathyContactWidget *information)
810 {
811         if (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_GROUPS &&
812       information->contact)
813     {
814       g_signal_connect_swapped (information->contact, "notify::groups",
815           G_CALLBACK (contact_widget_groups_notify_cb), information);
816       contact_widget_groups_populate_data (information);
817
818       gtk_widget_show (information->vbox_groups);
819     }
820   else
821     {
822       gtk_widget_hide (information->vbox_groups);
823     }
824 }
825
826 static void
827 contact_widget_model_setup (EmpathyContactWidget *information)
828 {
829         GtkTreeView *view;
830         GtkListStore *store;
831         GtkTreeSelection *selection;
832
833         view = GTK_TREE_VIEW (information->treeview_groups);
834
835         store = gtk_list_store_new (COL_COUNT,
836       G_TYPE_STRING,   /* name */
837                         G_TYPE_BOOLEAN,  /* enabled */
838                         G_TYPE_BOOLEAN); /* editable */
839
840         gtk_tree_view_set_model (view, GTK_TREE_MODEL (store));
841
842         selection = gtk_tree_view_get_selection (view);
843         gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
844
845         contact_widget_model_populate_columns (information);
846
847         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
848       COL_NAME, GTK_SORT_ASCENDING);
849
850         g_object_unref (store);
851 }
852
853 static void
854 contact_widget_model_populate_columns (EmpathyContactWidget *information)
855 {
856         GtkTreeView *view;
857         GtkTreeModel *model;
858         GtkTreeViewColumn *column;
859         GtkCellRenderer  *renderer;
860         guint col_offset;
861
862         view = GTK_TREE_VIEW (information->treeview_groups);
863         model = gtk_tree_view_get_model (view);
864
865         renderer = gtk_cell_renderer_toggle_new ();
866         g_signal_connect (renderer, "toggled",
867       G_CALLBACK (contact_widget_cell_toggled), information);
868
869         column = gtk_tree_view_column_new_with_attributes (_("Select"), renderer,
870       "active", COL_ENABLED, NULL);
871
872         gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
873         gtk_tree_view_column_set_fixed_width (column, 50);
874         gtk_tree_view_append_column (view, column);
875
876         renderer = gtk_cell_renderer_text_new ();
877         col_offset = gtk_tree_view_insert_column_with_attributes (view,
878       -1, _("Group"),
879                         renderer,
880                         "text", COL_NAME,
881                         /* "editable", COL_EDITABLE, */
882                         NULL);
883
884         g_object_set_data (G_OBJECT (renderer),
885       "column", GINT_TO_POINTER (COL_NAME));
886
887         column = gtk_tree_view_get_column (view, col_offset - 1);
888         gtk_tree_view_column_set_sort_column_id (column, COL_NAME);
889         gtk_tree_view_column_set_resizable (column,FALSE);
890         gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
891
892         if (information->renderer)
893     {
894       g_object_unref (information->renderer);
895     }
896
897         information->renderer = g_object_ref (renderer);
898 }
899
900 static void
901 contact_widget_groups_populate_data (EmpathyContactWidget *information)
902 {
903         GtkTreeView *view;
904         GtkListStore *store;
905         GtkTreeIter iter;
906         GList *my_groups, *l;
907         GList *all_groups;
908
909         view = GTK_TREE_VIEW (information->treeview_groups);
910         store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
911         gtk_list_store_clear (store);
912
913         all_groups = empathy_contact_list_get_all_groups (
914       EMPATHY_CONTACT_LIST (information->manager));
915         my_groups = empathy_contact_list_get_groups (
916       EMPATHY_CONTACT_LIST (information->manager),
917                         information->contact);
918
919         for (l = all_groups; l; l = l->next)
920     {
921       const gchar *group_str;
922       gboolean enabled;
923
924       group_str = l->data;
925
926       enabled = g_list_find_custom (my_groups,
927           group_str, (GCompareFunc) strcmp) != NULL;
928
929       gtk_list_store_append (store, &iter);
930       gtk_list_store_set (store, &iter,
931           COL_NAME, group_str,
932           COL_EDITABLE, TRUE,
933           COL_ENABLED, enabled,
934           -1);
935     }
936
937         g_list_foreach (all_groups, (GFunc) g_free, NULL);
938         g_list_foreach (my_groups, (GFunc) g_free, NULL);
939         g_list_free (all_groups);
940         g_list_free (my_groups);
941 }
942
943 static void
944 contact_widget_groups_notify_cb (EmpathyContactWidget *information)
945 {
946         /* FIXME: not implemented */
947 }
948
949 static gboolean
950 contact_widget_model_find_name (EmpathyContactWidget *information,
951                                                         const gchar *name,
952                                                         GtkTreeIter *iter)
953 {
954         GtkTreeView *view;
955         GtkTreeModel *model;
956         FindName data;
957
958         if (G_STR_EMPTY (name))
959                 return FALSE;
960
961         data.information = information;
962         data.name = name;
963         data.found = FALSE;
964
965         view = GTK_TREE_VIEW (information->treeview_groups);
966         model = gtk_tree_view_get_model (view);
967
968         gtk_tree_model_foreach (model,
969       (GtkTreeModelForeachFunc) contact_widget_model_find_name_foreach,
970                         &data);
971
972         if (data.found == TRUE)
973     {
974       *iter = data.found_iter;
975       return TRUE;
976     }
977
978         return FALSE;
979 }
980
981 static gboolean
982 contact_widget_model_find_name_foreach (GtkTreeModel *model,
983                                         GtkTreePath *path,
984                                         GtkTreeIter *iter,
985                                         FindName *data)
986 {
987         gchar *name;
988
989         gtk_tree_model_get (model, iter,
990       COL_NAME, &name,
991       -1);
992
993         if (!name)
994                 return FALSE;
995
996         if (data->name && strcmp (data->name, name) == 0)
997     {
998       data->found = TRUE;
999       data->found_iter = *iter;
1000
1001       g_free (name);
1002
1003       return TRUE;
1004     }
1005
1006         g_free (name);
1007
1008         return FALSE;
1009 }
1010
1011 static void
1012 contact_widget_cell_toggled (GtkCellRendererToggle *cell,
1013                              gchar *path_string,
1014                              EmpathyContactWidget *information)
1015 {
1016         GtkTreeView *view;
1017         GtkTreeModel *model;
1018         GtkListStore *store;
1019         GtkTreePath *path;
1020         GtkTreeIter iter;
1021         gboolean enabled;
1022         gchar *group;
1023
1024         view = GTK_TREE_VIEW (information->treeview_groups);
1025         model = gtk_tree_view_get_model (view);
1026         store = GTK_LIST_STORE (model);
1027
1028         path = gtk_tree_path_new_from_string (path_string);
1029
1030         gtk_tree_model_get_iter (model, &iter, path);
1031         gtk_tree_model_get (model, &iter,
1032       COL_ENABLED, &enabled,
1033                         COL_NAME, &group,
1034                         -1);
1035
1036         gtk_list_store_set (store, &iter, COL_ENABLED, !enabled, -1);
1037         gtk_tree_path_free (path);
1038
1039         if (group)
1040     {
1041                 if (enabled)
1042       {
1043         empathy_contact_list_remove_from_group (
1044             EMPATHY_CONTACT_LIST (information->manager), information->contact,
1045             group);
1046                 }
1047     else
1048       {
1049                         empathy_contact_list_add_to_group (
1050           EMPATHY_CONTACT_LIST (information->manager), information->contact,
1051           group);
1052                 }
1053
1054                 g_free (group);
1055         }
1056 }
1057
1058 static void
1059 contact_widget_entry_group_changed_cb (GtkEditable *editable,
1060                                        EmpathyContactWidget *information)
1061 {
1062         GtkTreeIter iter;
1063         const gchar *group;
1064
1065         group = gtk_entry_get_text (GTK_ENTRY (information->entry_group));
1066
1067         if (contact_widget_model_find_name (information, group, &iter))
1068     {
1069       gtk_widget_set_sensitive (GTK_WIDGET (information->button_group), FALSE);
1070     }
1071   else
1072     {
1073       gtk_widget_set_sensitive (GTK_WIDGET (information->button_group),
1074           !G_STR_EMPTY (group));
1075           }
1076 }
1077
1078 static void
1079 contact_widget_entry_group_activate_cb (GtkEntry *entry,
1080                                         EmpathyContactWidget  *information)
1081 {
1082         gtk_widget_activate (GTK_WIDGET (information->button_group));
1083 }
1084
1085 static void
1086 contact_widget_button_group_clicked_cb (GtkButton *button,
1087                                                                       EmpathyContactWidget *information)
1088 {
1089         GtkTreeView *view;
1090         GtkListStore *store;
1091         GtkTreeIter iter;
1092         const gchar *group;
1093
1094         view = GTK_TREE_VIEW (information->treeview_groups);
1095         store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
1096
1097         group = gtk_entry_get_text (GTK_ENTRY (information->entry_group));
1098
1099         gtk_list_store_append (store, &iter);
1100         gtk_list_store_set (store, &iter,
1101       COL_NAME, group,
1102                         COL_ENABLED, TRUE,
1103                         -1);
1104
1105         empathy_contact_list_add_to_group (
1106       EMPATHY_CONTACT_LIST (information->manager), information->contact,
1107       group);
1108 }
1109
1110 static void
1111 contact_widget_details_setup (EmpathyContactWidget *information)
1112 {
1113         /* FIXME: Needs new telepathy spec */
1114         gtk_widget_hide (information->vbox_details);
1115 }
1116
1117 static void
1118 contact_widget_details_update (EmpathyContactWidget *information)
1119 {
1120         /* FIXME: Needs new telepathy spec */
1121 }
1122
1123 static void
1124 contact_widget_client_setup (EmpathyContactWidget *information)
1125 {
1126         /* FIXME: Needs new telepathy spec */
1127         gtk_widget_hide (information->vbox_client);
1128 }
1129
1130 static void
1131 contact_widget_client_update (EmpathyContactWidget *information)
1132 {
1133         /* FIXME: Needs new telepathy spec */
1134 }