]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-contact-widget.c
New dialog: EmpathySubscriptionDialog. It uses EmpathyContactWidget to
[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 <libempathy/empathy-contact-manager.h>
33
34 #include "empathy-contact-widget.h"
35 #include "gossip-ui-utils.h"
36
37 typedef struct {
38         GossipContact   *contact;
39         gboolean         is_user;
40         gboolean         changes_made;
41         GtkCellRenderer *renderer;
42
43         GtkWidget       *vbox_contact_widget;
44
45         GtkWidget       *vbox_contact;
46         GtkWidget       *label_id;
47         GtkWidget       *entry_alias;
48         GtkWidget       *image_state;
49         GtkWidget       *label_status;
50         GtkWidget       *table_contact;
51         GtkWidget       *hbox_contact;
52         GtkWidget       *image_avatar;
53
54         GtkWidget       *vbox_groups;
55         GtkWidget       *entry_group;
56         GtkWidget       *button_group;
57         GtkWidget       *treeview_groups;
58
59         GtkWidget       *vbox_details;
60         GtkWidget       *table_details;
61         GtkWidget       *hbox_details_requested;
62
63         GtkWidget       *vbox_client;
64         GtkWidget       *table_client;
65         GtkWidget       *hbow_client_requested;
66 } EmpathyContactWidget;
67
68 typedef struct {
69         EmpathyContactWidget *information;
70         const gchar          *name;
71         gboolean              found;
72         GtkTreeIter           found_iter;
73 } FindName;
74
75 typedef struct {
76         EmpathyContactWidget *information;
77         GList                *list;
78 } FindSelected;
79
80 static void     contact_widget_destroy_cb                  (GtkWidget             *widget,
81                                                             EmpathyContactWidget  *information);
82 static void     contact_widget_contact_setup               (EmpathyContactWidget  *information);
83 static void     contact_widget_name_notify_cb              (EmpathyContactWidget  *information);
84 static void     contact_widget_presence_notify_cb          (EmpathyContactWidget  *information);
85 static void     contact_widget_avatar_notify_cb            (EmpathyContactWidget  *information);
86 static void     contact_widget_groups_setup                (EmpathyContactWidget  *information);
87 static void     contact_widget_model_setup                 (EmpathyContactWidget  *information);
88 static void     contact_widget_model_populate_columns      (EmpathyContactWidget  *information);
89 static void     contact_widget_groups_populate_data        (EmpathyContactWidget  *information);
90 static void     contact_widget_groups_notify_cb            (EmpathyContactWidget  *information);
91 static gboolean contact_widget_model_find_name             (EmpathyContactWidget  *information,
92                                                             const gchar           *name,
93                                                             GtkTreeIter           *iter);
94 static gboolean contact_widget_model_find_name_foreach     (GtkTreeModel          *model,
95                                                             GtkTreePath           *path,
96                                                             GtkTreeIter           *iter,
97                                                             FindName              *data);
98 static GList *  contact_widget_model_find_selected         (EmpathyContactWidget  *information);
99 static gboolean contact_widget_model_find_selected_foreach (GtkTreeModel          *model,
100                                                             GtkTreePath           *path,
101                                                             GtkTreeIter           *iter,
102                                                             FindSelected          *data);
103 static void     contact_widget_cell_toggled                (GtkCellRendererToggle *cell,
104                                                             gchar                 *path_string,
105                                                             EmpathyContactWidget  *information);
106 static void     contact_widget_entry_alias_changed_cb      (GtkEditable           *editable,
107                                                             EmpathyContactWidget  *information);
108 static void     contact_widget_entry_group_changed_cb      (GtkEditable           *editable,
109                                                             EmpathyContactWidget  *information);
110 static void     contact_widget_entry_group_activate_cb     (GtkEntry              *entry,
111                                                             EmpathyContactWidget  *information);
112 static void     contact_widget_button_group_clicked_cb     (GtkButton             *button,
113                                                             EmpathyContactWidget  *information);
114 static void     contact_widget_details_setup               (EmpathyContactWidget  *information);
115 static void     contact_widget_client_setup                (EmpathyContactWidget  *information);
116
117 enum {
118         COL_NAME,
119         COL_ENABLED,
120         COL_EDITABLE,
121         COL_COUNT
122 };
123
124 GtkWidget *
125 empathy_contact_widget_new (GossipContact *contact)
126 {
127         EmpathyContactWidget *information;
128         GladeXML             *glade;
129         GossipContact        *user_contact;
130
131         g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
132
133         information = g_slice_new0 (EmpathyContactWidget);
134         information->contact = g_object_ref (contact);
135         user_contact = gossip_contact_get_user (contact);
136         information->is_user = gossip_contact_equal (contact, user_contact);
137
138         glade = gossip_glade_get_file ("empathy-contact-widget.glade",
139                                        "vbox_contact_widget",
140                                        NULL,
141                                        "vbox_contact_widget", &information->vbox_contact_widget,
142                                        "vbox_contact", &information->vbox_contact,
143                                        "label_id", &information->label_id,
144                                        "entry_alias", &information->entry_alias,
145                                        "image_state", &information->image_state,
146                                        "label_status", &information->label_status,
147                                        "table_contact", &information->table_contact,
148                                        "hbox_contact", &information->hbox_contact,
149                                        "vbox_groups", &information->vbox_groups,
150                                        "entry_group", &information->entry_group,
151                                        "button_group", &information->button_group,
152                                        "treeview_groups", &information->treeview_groups,
153                                        "vbox_details", &information->vbox_details,
154                                        "table_details", &information->table_details,
155                                        "hbox_details_requested", &information->hbox_details_requested,
156                                        "vbox_client", &information->vbox_client,
157                                        "table_client", &information->table_client,
158                                        "hbox_client_requested", &information->hbow_client_requested,
159                                        NULL);
160
161         gossip_glade_connect (glade,
162                               information,
163                               "vbox_contact_widget", "destroy", contact_widget_destroy_cb,
164                               "entry_alias", "changed", contact_widget_entry_alias_changed_cb,
165                               "entry_group", "changed", contact_widget_entry_group_changed_cb,
166                               "entry_group", "activate", contact_widget_entry_group_activate_cb,
167                               "button_group", "clicked", contact_widget_button_group_clicked_cb,
168                               NULL);
169
170         g_object_unref (glade);
171
172         g_object_set_data (G_OBJECT (information->vbox_contact_widget),
173                            "EmpathyContactWidget",
174                            information);
175
176         contact_widget_contact_setup (information);
177         contact_widget_groups_setup (information);
178         contact_widget_details_setup (information);
179         contact_widget_client_setup (information);
180
181         gtk_widget_show (information->vbox_contact_widget);
182
183         return information->vbox_contact_widget;
184 }
185
186 void
187 empathy_contact_widget_save (GtkWidget *widget)
188 {
189         EmpathyContactWidget *information;
190         const gchar          *name;
191         GList                *groups;
192
193         g_return_if_fail (GTK_IS_WIDGET (widget));
194
195         information = g_object_get_data (G_OBJECT (widget), "EmpathyContactWidget");
196         if (!information || !information->changes_made) {
197                 return;
198         }
199
200         name = gtk_entry_get_text (GTK_ENTRY (information->entry_alias));
201         groups = contact_widget_model_find_selected (information);
202
203         gossip_contact_set_name (information->contact, name);
204         gossip_contact_set_groups (information->contact, groups);
205
206         g_list_foreach (groups, (GFunc) g_free, NULL);
207         g_list_free (groups);
208 }
209
210 static void
211 contact_widget_destroy_cb (GtkWidget            *widget,
212                            EmpathyContactWidget *information)
213 {
214         g_signal_handlers_disconnect_by_func (information->contact,
215                                               contact_widget_name_notify_cb,
216                                               information);
217         g_signal_handlers_disconnect_by_func (information->contact,
218                                               contact_widget_presence_notify_cb,
219                                               information);
220         g_signal_handlers_disconnect_by_func (information->contact,
221                                               contact_widget_avatar_notify_cb,
222                                               information);
223         g_signal_handlers_disconnect_by_func (information->contact,
224                                               contact_widget_groups_notify_cb,
225                                               information);
226
227         g_object_unref (information->contact);
228         g_slice_free (EmpathyContactWidget, information);
229 }
230
231 static void
232 contact_widget_contact_setup (EmpathyContactWidget *information)
233 {
234         g_signal_connect_swapped (information->contact, "notify::name",
235                                   G_CALLBACK (contact_widget_name_notify_cb),
236                                   information);
237         g_signal_connect_swapped (information->contact, "notify::presence",
238                                   G_CALLBACK (contact_widget_presence_notify_cb),
239                                   information);
240         g_signal_connect_swapped (information->contact, "notify::avatar",
241                                   G_CALLBACK (contact_widget_avatar_notify_cb),
242                                   information);
243
244         information->image_avatar = gtk_image_new ();
245         gtk_box_pack_end (GTK_BOX (information->hbox_contact),
246                           information->image_avatar,
247                           FALSE, FALSE,
248                           6);
249
250         gtk_label_set_text (GTK_LABEL (information->label_id),
251                             gossip_contact_get_id (information->contact));
252         contact_widget_name_notify_cb (information);
253         contact_widget_presence_notify_cb (information);
254         contact_widget_avatar_notify_cb (information);
255 }
256
257 static void
258 contact_widget_name_notify_cb (EmpathyContactWidget *information)
259 {
260         gtk_entry_set_text (GTK_ENTRY (information->entry_alias),
261                             gossip_contact_get_name (information->contact));
262 }
263
264 static void
265 contact_widget_presence_notify_cb (EmpathyContactWidget *information)
266 {
267         gtk_label_set_text (GTK_LABEL (information->label_status),
268                             gossip_contact_get_status (information->contact));
269         gtk_image_set_from_icon_name (GTK_IMAGE (information->image_state),
270                                       gossip_icon_name_for_contact (information->contact),
271                                       GTK_ICON_SIZE_BUTTON);
272
273 }
274
275 static void
276 contact_widget_avatar_notify_cb (EmpathyContactWidget *information)
277 {
278         GdkPixbuf *avatar_pixbuf;
279
280         avatar_pixbuf = gossip_pixbuf_avatar_from_contact_scaled (information->contact,
281                                                                   48, 48);
282
283         if (avatar_pixbuf) {
284                 gtk_image_set_from_pixbuf (GTK_IMAGE (information->image_avatar),
285                                            avatar_pixbuf);
286                 gtk_widget_show  (information->image_avatar);
287                 g_object_unref (avatar_pixbuf);
288         } else {
289                 gtk_widget_hide  (information->image_avatar);
290         }
291 }
292
293 static void
294 contact_widget_groups_setup (EmpathyContactWidget *information)
295 {
296         if (!information->is_user) {
297                 contact_widget_model_setup (information);
298
299                 g_signal_connect_swapped (information->contact, "notify::groups",
300                                           G_CALLBACK (contact_widget_groups_notify_cb),
301                                           information);
302                 contact_widget_groups_populate_data (information);
303
304                 gtk_widget_show (information->vbox_groups);
305         }
306 }
307
308 static void
309 contact_widget_model_setup (EmpathyContactWidget *information)
310 {
311         GtkTreeView      *view;
312         GtkListStore     *store;
313         GtkTreeSelection *selection;
314
315         view = GTK_TREE_VIEW (information->treeview_groups);
316
317         store = gtk_list_store_new (COL_COUNT,
318                                     G_TYPE_STRING,   /* name */
319                                     G_TYPE_BOOLEAN,  /* enabled */
320                                     G_TYPE_BOOLEAN); /* editable */
321
322         gtk_tree_view_set_model (view, GTK_TREE_MODEL (store));
323
324         selection = gtk_tree_view_get_selection (view);
325         gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
326
327         contact_widget_model_populate_columns (information);
328
329         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
330                                               COL_NAME, GTK_SORT_ASCENDING);
331
332         g_object_unref (store);
333 }
334
335 static void
336 contact_widget_model_populate_columns (EmpathyContactWidget *information)
337 {
338         GtkTreeView       *view;
339         GtkTreeModel      *model;
340         GtkTreeViewColumn *column;
341         GtkCellRenderer   *renderer;
342         guint              col_offset;
343
344         view = GTK_TREE_VIEW (information->treeview_groups);
345         model = gtk_tree_view_get_model (view);
346
347         renderer = gtk_cell_renderer_toggle_new ();
348         g_signal_connect (renderer, "toggled",
349                           G_CALLBACK (contact_widget_cell_toggled),
350                           information);
351
352         column = gtk_tree_view_column_new_with_attributes (_("Select"), renderer,
353                                                            "active", COL_ENABLED,
354                                                            NULL);
355
356         gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
357         gtk_tree_view_column_set_fixed_width (column, 50);
358         gtk_tree_view_append_column (view, column);
359
360         renderer = gtk_cell_renderer_text_new ();
361         col_offset = gtk_tree_view_insert_column_with_attributes (view,
362                                                                   -1, _("Group"),
363                                                                   renderer,
364                                                                   "text", COL_NAME,
365                                                                   /* "editable", COL_EDITABLE, */
366                                                                   NULL);
367
368         g_object_set_data (G_OBJECT (renderer),
369                            "column", GINT_TO_POINTER (COL_NAME));
370
371         column = gtk_tree_view_get_column (view, col_offset - 1);
372         gtk_tree_view_column_set_sort_column_id (column, COL_NAME);
373         gtk_tree_view_column_set_resizable (column,FALSE);
374         gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
375
376         if (information->renderer) {
377                 g_object_unref (information->renderer);
378         }
379
380         information->renderer = g_object_ref (renderer);
381 }
382
383 static void
384 contact_widget_groups_populate_data (EmpathyContactWidget *information)
385 {
386         EmpathyContactManager *manager;
387         GtkTreeView           *view;
388         GtkListStore          *store;
389         GtkTreeIter            iter;
390         GList                 *groups, *l;
391         GList                 *my_groups = NULL;
392         GList                 *all_groups;
393
394         view = GTK_TREE_VIEW (information->treeview_groups);
395         store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
396
397         manager = empathy_contact_manager_new ();
398         all_groups = empathy_contact_manager_get_groups (manager);
399         groups = gossip_contact_get_groups (information->contact);
400         g_object_unref (manager);
401
402         for (l = groups; l; l = l->next) {
403                 const gchar *group_str;
404
405                 group_str = l->data;
406                 if (strcmp (group_str, _("Unsorted")) == 0) {
407                         continue;
408                 }
409
410                 my_groups = g_list_append (my_groups, g_strdup (group_str));
411         }
412
413         for (l = all_groups; l; l = l->next) {
414                 const gchar *group_str;
415                 gboolean     enabled;
416
417                 group_str = l->data;
418                 if (strcmp (group_str, _("Unsorted")) == 0) {
419                         continue;
420                 }
421
422                 enabled = g_list_find_custom (my_groups,
423                                               group_str,
424                                               (GCompareFunc) strcmp) != NULL;
425
426                 gtk_list_store_append (store, &iter);
427                 gtk_list_store_set (store, &iter,
428                                     COL_NAME, group_str,
429                                     COL_EDITABLE, TRUE,
430                                     COL_ENABLED, enabled,
431                                     -1);
432         }
433
434         g_list_foreach (my_groups, (GFunc) g_free, NULL);
435         g_list_free (my_groups);
436
437         g_list_free (all_groups);
438 }
439
440 static void
441 contact_widget_groups_notify_cb (EmpathyContactWidget *information)
442 {
443         /* FIXME: not implemented */
444 }
445
446 static gboolean
447 contact_widget_model_find_name (EmpathyContactWidget *information,
448                                 const gchar          *name,
449                                 GtkTreeIter          *iter)
450 {
451         GtkTreeView  *view;
452         GtkTreeModel *model;
453         FindName      data;
454
455         if (G_STR_EMPTY (name)) {
456                 return FALSE;
457         }
458
459         data.information = information;
460         data.name = name;
461         data.found = FALSE;
462
463         view = GTK_TREE_VIEW (information->treeview_groups);
464         model = gtk_tree_view_get_model (view);
465
466         gtk_tree_model_foreach (model,
467                                 (GtkTreeModelForeachFunc) contact_widget_model_find_name_foreach,
468                                 &data);
469
470         if (data.found == TRUE) {
471                 *iter = data.found_iter;
472                 return TRUE;
473         }
474
475         return FALSE;
476 }
477
478 static gboolean
479 contact_widget_model_find_name_foreach (GtkTreeModel *model,
480                                         GtkTreePath  *path,
481                                         GtkTreeIter  *iter,
482                                         FindName     *data)
483 {
484         gchar *name;
485
486         gtk_tree_model_get (model, iter,
487                             COL_NAME, &name,
488                             -1);
489
490         if (!name) {
491                 return FALSE;
492         }
493
494         if (data->name && strcmp (data->name, name) == 0) {
495                 data->found = TRUE;
496                 data->found_iter = *iter;
497
498                 g_free (name);
499
500                 return TRUE;
501         }
502
503         g_free (name);
504
505         return FALSE;
506 }
507
508 static GList *
509 contact_widget_model_find_selected (EmpathyContactWidget *information)
510 {
511         GtkTreeView  *view;
512         GtkTreeModel *model;
513         FindSelected  data;
514
515         data.information = information;
516         data.list = NULL;
517
518         view = GTK_TREE_VIEW (information->treeview_groups);
519         model = gtk_tree_view_get_model (view);
520
521         gtk_tree_model_foreach (model,
522                                 (GtkTreeModelForeachFunc) contact_widget_model_find_selected_foreach,
523                                 &data);
524
525         return data.list;
526 }
527
528 static gboolean
529 contact_widget_model_find_selected_foreach (GtkTreeModel *model,
530                                             GtkTreePath  *path,
531                                             GtkTreeIter  *iter,
532                                             FindSelected *data)
533 {
534         gchar    *name;
535         gboolean  selected;
536
537         gtk_tree_model_get (model, iter,
538                             COL_NAME, &name,
539                             COL_ENABLED, &selected,
540                             -1);
541
542         if (!name) {
543                 return FALSE;
544         }
545
546         if (selected) {
547                 data->list = g_list_append (data->list, name);
548                 return FALSE;
549         }
550
551         g_free (name);
552
553         return FALSE;
554 }
555
556 static void
557 contact_widget_cell_toggled (GtkCellRendererToggle *cell,
558                              gchar                 *path_string,
559                              EmpathyContactWidget  *information)
560 {
561         GtkTreeView  *view;
562         GtkTreeModel *model;
563         GtkListStore *store;
564         GtkTreePath  *path;
565         GtkTreeIter   iter;
566         gboolean      enabled;
567
568         view = GTK_TREE_VIEW (information->treeview_groups);
569         model = gtk_tree_view_get_model (view);
570         store = GTK_LIST_STORE (model);
571
572         path = gtk_tree_path_new_from_string (path_string);
573
574         gtk_tree_model_get_iter (model, &iter, path);
575         gtk_tree_model_get (model, &iter, COL_ENABLED, &enabled, -1);
576
577         enabled ^= 1;
578
579         gtk_list_store_set (store, &iter, COL_ENABLED, enabled, -1);
580         gtk_tree_path_free (path);
581
582         information->changes_made = TRUE;
583 }
584
585 static void
586 contact_widget_entry_alias_changed_cb (GtkEditable           *editable,
587                                        EmpathyContactWidget  *information)
588 {
589         information->changes_made = TRUE;
590 }
591
592 static void
593 contact_widget_entry_group_changed_cb (GtkEditable           *editable,
594                                        EmpathyContactWidget  *information)
595 {
596         GtkTreeIter  iter;
597         const gchar *group;
598
599         group = gtk_entry_get_text (GTK_ENTRY (information->entry_group));
600
601         if (contact_widget_model_find_name (information, group, &iter)) {
602                 gtk_widget_set_sensitive (GTK_WIDGET (information->button_group), FALSE);
603
604         } else {
605                 gtk_widget_set_sensitive (GTK_WIDGET (information->button_group),
606                                           !G_STR_EMPTY (group));
607         }
608 }
609
610 static void
611 contact_widget_entry_group_activate_cb (GtkEntry              *entry,
612                                         EmpathyContactWidget  *information)
613 {
614         gtk_widget_activate (GTK_WIDGET (information->button_group));
615 }
616
617 static void
618 contact_widget_button_group_clicked_cb (GtkButton             *button,
619                                         EmpathyContactWidget  *information)
620 {
621         GtkTreeView  *view;
622         GtkListStore *store;
623         GtkTreeIter   iter;
624         const gchar  *group;
625
626         view = GTK_TREE_VIEW (information->treeview_groups);
627         store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
628
629         group = gtk_entry_get_text (GTK_ENTRY (information->entry_group));
630
631         gtk_list_store_append (store, &iter);
632         gtk_list_store_set (store, &iter,
633                             COL_NAME, group,
634                             COL_ENABLED, TRUE,
635                             -1);
636
637         information->changes_made = TRUE;
638 }
639
640 static void
641 contact_widget_details_setup (EmpathyContactWidget *information)
642 {
643         /* FIXME: Needs new telepathy spec */
644 }
645
646 static void
647 contact_widget_client_setup (EmpathyContactWidget *information)
648 {
649         /* FIXME: Needs new telepathy spec */
650 }
651