1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2007 Collabora Ltd.
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.
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.
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.
20 * Authors: Xavier Claessens <xclaesse@gmail.com>
29 #include <glade/glade.h>
30 #include <glib/gi18n.h>
32 #include <libmissioncontrol/mc-account.h>
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>
39 #include "empathy-contact-widget.h"
40 #include "empathy-account-chooser.h"
41 #include "empathy-ui-utils.h"
43 /* Delay before updating the widget when the id entry changed (ms) */
44 #define ID_CHANGED_TIMEOUT 500
47 EmpathyContactFactory *factory;
48 EmpathyContactManager *manager;
49 EmpathyContact *contact;
51 EmpathyContactWidgetType type;
52 GtkCellRenderer *renderer;
53 guint widget_id_timeout;
55 GtkWidget *vbox_contact_widget;
58 GtkWidget *vbox_contact;
59 GtkWidget *widget_avatar;
60 GtkWidget *widget_account;
62 GtkWidget *widget_alias;
63 GtkWidget *label_alias;
64 GtkWidget *entry_alias;
65 GtkWidget *hbox_presence;
66 GtkWidget *image_state;
67 GtkWidget *label_status;
68 GtkWidget *table_contact;
69 GtkWidget *hbox_contact;
72 GtkWidget *vbox_groups;
73 GtkWidget *entry_group;
74 GtkWidget *button_group;
75 GtkWidget *treeview_groups;
78 GtkWidget *vbox_details;
79 GtkWidget *table_details;
80 GtkWidget *hbox_details_requested;
83 GtkWidget *vbox_client;
84 GtkWidget *table_client;
85 GtkWidget *hbow_client_requested;
86 } EmpathyContactWidget;
89 EmpathyContactWidget *information;
92 GtkTreeIter found_iter;
95 static void contact_widget_destroy_cb (GtkWidget *widget,
96 EmpathyContactWidget *information);
97 static void contact_widget_remove_contact (EmpathyContactWidget *information);
98 static void contact_widget_set_contact (EmpathyContactWidget *information,
99 EmpathyContact *contact);
100 static void contact_widget_contact_setup (EmpathyContactWidget *information);
101 static void contact_widget_contact_update (EmpathyContactWidget *information);
102 static gboolean contact_widget_update_contact (EmpathyContactWidget *information);
103 static void contact_widget_account_changed_cb (GtkComboBox *widget,
104 EmpathyContactWidget *information);
105 static gboolean contact_widget_id_focus_out_cb (GtkWidget *widget,
106 GdkEventFocus *event,
107 EmpathyContactWidget *information);
108 static gboolean contact_widget_entry_alias_focus_event_cb (GtkEditable *editable,
109 GdkEventFocus *event,
110 EmpathyContactWidget *information);
111 static void contact_widget_name_notify_cb (EmpathyContactWidget *information);
112 static void contact_widget_presence_notify_cb (EmpathyContactWidget *information);
113 static void contact_widget_avatar_notify_cb (EmpathyContactWidget *information);
114 static void contact_widget_groups_setup (EmpathyContactWidget *information);
115 static void contact_widget_groups_update (EmpathyContactWidget *information);
116 static void contact_widget_model_setup (EmpathyContactWidget *information);
117 static void contact_widget_model_populate_columns (EmpathyContactWidget *information);
118 static void contact_widget_groups_populate_data (EmpathyContactWidget *information);
119 static void contact_widget_groups_notify_cb (EmpathyContactWidget *information);
120 static gboolean contact_widget_model_find_name (EmpathyContactWidget *information,
123 static gboolean contact_widget_model_find_name_foreach (GtkTreeModel *model,
127 static void contact_widget_cell_toggled (GtkCellRendererToggle *cell,
129 EmpathyContactWidget *information);
130 static void contact_widget_entry_group_changed_cb (GtkEditable *editable,
131 EmpathyContactWidget *information);
132 static void contact_widget_entry_group_activate_cb (GtkEntry *entry,
133 EmpathyContactWidget *information);
134 static void contact_widget_button_group_clicked_cb (GtkButton *button,
135 EmpathyContactWidget *information);
136 static void contact_widget_details_setup (EmpathyContactWidget *information);
137 static void contact_widget_details_update (EmpathyContactWidget *information);
138 static void contact_widget_client_setup (EmpathyContactWidget *information);
139 static void contact_widget_client_update (EmpathyContactWidget *information);
149 empathy_contact_widget_new (EmpathyContact *contact,
150 EmpathyContactWidgetType type)
152 EmpathyContactWidget *information;
155 information = g_slice_new0 (EmpathyContactWidget);
156 information->type = type;
157 information->factory = empathy_contact_factory_new ();
159 information->is_user = empathy_contact_is_user (contact);
161 information->is_user = FALSE;
164 glade = empathy_glade_get_file ("empathy-contact-widget.glade",
165 "vbox_contact_widget",
167 "vbox_contact_widget", &information->vbox_contact_widget,
168 "vbox_contact", &information->vbox_contact,
169 "hbox_presence", &information->hbox_presence,
170 "label_alias", &information->label_alias,
171 "image_state", &information->image_state,
172 "label_status", &information->label_status,
173 "table_contact", &information->table_contact,
174 "hbox_contact", &information->hbox_contact,
175 "vbox_groups", &information->vbox_groups,
176 "entry_group", &information->entry_group,
177 "button_group", &information->button_group,
178 "treeview_groups", &information->treeview_groups,
179 "vbox_details", &information->vbox_details,
180 "table_details", &information->table_details,
181 "hbox_details_requested", &information->hbox_details_requested,
182 "vbox_client", &information->vbox_client,
183 "table_client", &information->table_client,
184 "hbox_client_requested", &information->hbow_client_requested,
187 empathy_glade_connect (glade,
189 "vbox_contact_widget", "destroy", contact_widget_destroy_cb,
190 "entry_group", "changed", contact_widget_entry_group_changed_cb,
191 "entry_group", "activate", contact_widget_entry_group_activate_cb,
192 "button_group", "clicked", contact_widget_button_group_clicked_cb,
195 g_object_unref (glade);
197 g_object_set_data (G_OBJECT (information->vbox_contact_widget),
198 "EmpathyContactWidget",
202 contact_widget_contact_setup (information);
203 contact_widget_groups_setup (information);
204 contact_widget_details_setup (information);
205 contact_widget_client_setup (information);
207 contact_widget_set_contact (information, contact);
209 gtk_widget_show (information->vbox_contact_widget);
211 return information->vbox_contact_widget;
215 empathy_contact_widget_get_contact (GtkWidget *widget)
217 EmpathyContactWidget *information;
219 g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
221 information = g_object_get_data (G_OBJECT (widget), "EmpathyContactWidget");
226 return information->contact;
230 contact_widget_destroy_cb (GtkWidget *widget,
231 EmpathyContactWidget *information)
233 contact_widget_remove_contact (information);
235 if (information->widget_id_timeout != 0) {
236 g_source_remove (information->widget_id_timeout);
238 if (information->factory) {
239 g_object_unref (information->factory);
241 if (information->manager) {
242 g_object_unref (information->manager);
245 g_slice_free (EmpathyContactWidget, information);
249 contact_widget_remove_contact (EmpathyContactWidget *information)
251 if (information->contact) {
252 g_signal_handlers_disconnect_by_func (information->contact,
253 contact_widget_name_notify_cb,
255 g_signal_handlers_disconnect_by_func (information->contact,
256 contact_widget_presence_notify_cb,
258 g_signal_handlers_disconnect_by_func (information->contact,
259 contact_widget_avatar_notify_cb,
261 g_signal_handlers_disconnect_by_func (information->contact,
262 contact_widget_groups_notify_cb,
265 g_object_unref (information->contact);
266 information->contact = NULL;
271 contact_widget_set_contact (EmpathyContactWidget *information,
272 EmpathyContact *contact)
274 contact_widget_remove_contact (information);
276 information->contact = g_object_ref (contact);
279 /* Update information for widgets */
280 contact_widget_contact_update (information);
281 contact_widget_groups_update (information);
282 contact_widget_details_update (information);
283 contact_widget_client_update (information);
287 contact_widget_can_add_contact_to_account (McAccount *account,
293 const gchar *protocol_name;
295 mc = empathy_mission_control_new ();
296 tp_conn = mission_control_get_connection (mc, account, NULL);
298 if (tp_conn == NULL) {
299 /* Account is disconnected */
302 g_object_unref (tp_conn);
304 profile = mc_account_get_profile (account);
305 protocol_name = mc_profile_get_protocol_name (profile);
306 if (strcmp (protocol_name, "local-xmpp") == 0) {
307 /* We can't add accounts to a XMPP LL connection
308 * FIXME: We should inspect the flags of the contact list group interface
310 g_object_unref (profile);
314 g_object_unref (profile);
319 contact_widget_id_activate_timeout (EmpathyContactWidget *self)
321 contact_widget_update_contact (self);
326 contact_widget_id_changed_cb (GtkEntry *entry,
327 EmpathyContactWidget *self)
329 if (self->widget_id_timeout != 0) {
330 g_source_remove (self->widget_id_timeout);
333 self->widget_id_timeout =
334 g_timeout_add (ID_CHANGED_TIMEOUT,
335 (GSourceFunc) contact_widget_id_activate_timeout,
340 contact_widget_contact_setup (EmpathyContactWidget *information)
342 /* FIXME: Use EmpathyAvatarImage if (editable && is_user) */
343 information->widget_avatar = gtk_image_new ();
344 gtk_box_pack_end (GTK_BOX (information->hbox_contact),
345 information->widget_avatar,
349 /* Setup account label/chooser */
350 if (information->type == CONTACT_WIDGET_TYPE_ADD) {
351 information->widget_account = empathy_account_chooser_new ();
352 empathy_account_chooser_set_filter (
353 EMPATHY_ACCOUNT_CHOOSER (information->widget_account),
354 contact_widget_can_add_contact_to_account,
357 g_signal_connect (information->widget_account, "changed",
358 G_CALLBACK (contact_widget_account_changed_cb),
361 information->widget_account = gtk_label_new (NULL);
362 gtk_label_set_selectable (GTK_LABEL (information->widget_account), TRUE);
363 gtk_misc_set_alignment (GTK_MISC (information->widget_account), 0, 0.5);
365 gtk_table_attach_defaults (GTK_TABLE (information->table_contact),
366 information->widget_account,
368 gtk_widget_show (information->widget_account);
370 /* Setup id label/entry */
371 if (information->type == CONTACT_WIDGET_TYPE_ADD) {
372 information->widget_id = gtk_entry_new ();
373 g_signal_connect (information->widget_id, "focus-out-event",
374 G_CALLBACK (contact_widget_id_focus_out_cb),
376 g_signal_connect (information->widget_id, "changed",
377 G_CALLBACK (contact_widget_id_changed_cb),
380 information->widget_id = gtk_label_new (NULL);
381 gtk_label_set_selectable (GTK_LABEL (information->widget_id), TRUE);
382 gtk_misc_set_alignment (GTK_MISC (information->widget_id), 0, 0.5);
384 gtk_table_attach_defaults (GTK_TABLE (information->table_contact),
385 information->widget_id,
387 gtk_widget_show (information->widget_id);
389 /* Setup alias label/entry */
390 if (information->type > CONTACT_WIDGET_TYPE_SHOW) {
391 information->widget_alias = gtk_entry_new ();
392 g_signal_connect (information->widget_alias, "focus-out-event",
393 G_CALLBACK (contact_widget_entry_alias_focus_event_cb),
396 information->widget_alias = gtk_label_new (NULL);
397 gtk_label_set_selectable (GTK_LABEL (information->widget_alias), TRUE);
398 gtk_misc_set_alignment (GTK_MISC (information->widget_alias), 0, 0.5);
400 gtk_table_attach_defaults (GTK_TABLE (information->table_contact),
401 information->widget_alias,
403 gtk_widget_show (information->widget_alias);
407 contact_widget_contact_update (EmpathyContactWidget *information)
409 McAccount *account = NULL;
410 const gchar *id = NULL;
412 /* Connect and get info from new contact */
413 if (information->contact) {
414 g_signal_connect_swapped (information->contact, "notify::name",
415 G_CALLBACK (contact_widget_name_notify_cb),
417 g_signal_connect_swapped (information->contact, "notify::presence",
418 G_CALLBACK (contact_widget_presence_notify_cb),
420 g_signal_connect_swapped (information->contact, "notify::avatar",
421 G_CALLBACK (contact_widget_avatar_notify_cb),
424 account = empathy_contact_get_account (information->contact);
425 id = empathy_contact_get_id (information->contact);
428 /* Update account widget */
429 if (information->type == CONTACT_WIDGET_TYPE_ADD) {
431 g_signal_handlers_block_by_func (information->widget_account,
432 contact_widget_account_changed_cb,
434 empathy_account_chooser_set_account (EMPATHY_ACCOUNT_CHOOSER (information->widget_account),
436 g_signal_handlers_unblock_by_func (information->widget_account,
437 contact_widget_account_changed_cb,
440 if (!G_STR_EMPTY (id)) {
441 gtk_entry_set_text (GTK_ENTRY (information->widget_id), id);
447 name = mc_account_get_display_name (account);
448 gtk_label_set_label (GTK_LABEL (information->widget_account), name);
450 gtk_label_set_label (GTK_LABEL (information->widget_id), id);
453 /* Update other widgets */
454 if (information->contact) {
455 contact_widget_name_notify_cb (information);
456 contact_widget_presence_notify_cb (information);
457 contact_widget_avatar_notify_cb (information);
459 gtk_widget_show (information->label_alias);
460 gtk_widget_show (information->widget_alias);
461 gtk_widget_show (information->hbox_presence);
463 gtk_widget_hide (information->label_alias);
464 gtk_widget_hide (information->widget_alias);
465 gtk_widget_hide (information->hbox_presence);
466 gtk_widget_hide (information->widget_avatar);
471 contact_widget_update_contact (EmpathyContactWidget *information)
476 account = empathy_account_chooser_get_account (EMPATHY_ACCOUNT_CHOOSER (information->widget_account));
477 id = gtk_entry_get_text (GTK_ENTRY (information->widget_id));
479 if (account && !G_STR_EMPTY (id)) {
480 EmpathyContact *contact;
482 contact = empathy_contact_factory_get_from_id (information->factory,
484 contact_widget_set_contact (information, contact);
487 g_object_unref (contact);
495 contact_widget_account_changed_cb (GtkComboBox *widget,
496 EmpathyContactWidget *information)
498 contact_widget_update_contact (information);
502 contact_widget_id_focus_out_cb (GtkWidget *widget,
503 GdkEventFocus *event,
504 EmpathyContactWidget *information)
506 contact_widget_update_contact (information);
511 contact_widget_entry_alias_focus_event_cb (GtkEditable *editable,
512 GdkEventFocus *event,
513 EmpathyContactWidget *information)
515 if (information->contact) {
518 name = gtk_entry_get_text (GTK_ENTRY (editable));
519 empathy_contact_factory_set_name (information->factory,
520 information->contact,
528 contact_widget_name_notify_cb (EmpathyContactWidget *information)
530 if (GTK_IS_ENTRY (information->widget_alias)) {
531 gtk_entry_set_text (GTK_ENTRY (information->widget_alias),
532 empathy_contact_get_name (information->contact));
534 gtk_label_set_label (GTK_LABEL (information->widget_alias),
535 empathy_contact_get_name (information->contact));
540 contact_widget_presence_notify_cb (EmpathyContactWidget *information)
542 gtk_label_set_text (GTK_LABEL (information->label_status),
543 empathy_contact_get_status (information->contact));
544 gtk_image_set_from_icon_name (GTK_IMAGE (information->image_state),
545 empathy_icon_name_for_contact (information->contact),
546 GTK_ICON_SIZE_BUTTON);
551 contact_widget_avatar_notify_cb (EmpathyContactWidget *information)
553 GdkPixbuf *avatar_pixbuf;
555 avatar_pixbuf = empathy_pixbuf_avatar_from_contact_scaled (information->contact,
559 gtk_image_set_from_pixbuf (GTK_IMAGE (information->widget_avatar),
561 gtk_widget_show (information->widget_avatar);
562 g_object_unref (avatar_pixbuf);
564 gtk_widget_hide (information->widget_avatar);
569 contact_widget_groups_setup (EmpathyContactWidget *information)
571 if (information->type > CONTACT_WIDGET_TYPE_SHOW) {
572 information->manager = empathy_contact_manager_new ();
573 contact_widget_model_setup (information);
578 contact_widget_groups_update (EmpathyContactWidget *information)
580 if (information->type > CONTACT_WIDGET_TYPE_SHOW &&
581 information->contact) {
582 g_signal_connect_swapped (information->contact, "notify::groups",
583 G_CALLBACK (contact_widget_groups_notify_cb),
585 contact_widget_groups_populate_data (information);
587 gtk_widget_show (information->vbox_groups);
589 gtk_widget_hide (information->vbox_groups);
594 contact_widget_model_setup (EmpathyContactWidget *information)
598 GtkTreeSelection *selection;
600 view = GTK_TREE_VIEW (information->treeview_groups);
602 store = gtk_list_store_new (COL_COUNT,
603 G_TYPE_STRING, /* name */
604 G_TYPE_BOOLEAN, /* enabled */
605 G_TYPE_BOOLEAN); /* editable */
607 gtk_tree_view_set_model (view, GTK_TREE_MODEL (store));
609 selection = gtk_tree_view_get_selection (view);
610 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
612 contact_widget_model_populate_columns (information);
614 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
615 COL_NAME, GTK_SORT_ASCENDING);
617 g_object_unref (store);
621 contact_widget_model_populate_columns (EmpathyContactWidget *information)
625 GtkTreeViewColumn *column;
626 GtkCellRenderer *renderer;
629 view = GTK_TREE_VIEW (information->treeview_groups);
630 model = gtk_tree_view_get_model (view);
632 renderer = gtk_cell_renderer_toggle_new ();
633 g_signal_connect (renderer, "toggled",
634 G_CALLBACK (contact_widget_cell_toggled),
637 column = gtk_tree_view_column_new_with_attributes (_("Select"), renderer,
638 "active", COL_ENABLED,
641 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
642 gtk_tree_view_column_set_fixed_width (column, 50);
643 gtk_tree_view_append_column (view, column);
645 renderer = gtk_cell_renderer_text_new ();
646 col_offset = gtk_tree_view_insert_column_with_attributes (view,
650 /* "editable", COL_EDITABLE, */
653 g_object_set_data (G_OBJECT (renderer),
654 "column", GINT_TO_POINTER (COL_NAME));
656 column = gtk_tree_view_get_column (view, col_offset - 1);
657 gtk_tree_view_column_set_sort_column_id (column, COL_NAME);
658 gtk_tree_view_column_set_resizable (column,FALSE);
659 gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE);
661 if (information->renderer) {
662 g_object_unref (information->renderer);
665 information->renderer = g_object_ref (renderer);
669 contact_widget_groups_populate_data (EmpathyContactWidget *information)
674 GList *my_groups, *l;
677 view = GTK_TREE_VIEW (information->treeview_groups);
678 store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
679 gtk_list_store_clear (store);
681 all_groups = empathy_contact_list_get_all_groups (EMPATHY_CONTACT_LIST (information->manager));
682 my_groups = empathy_contact_list_get_groups (EMPATHY_CONTACT_LIST (information->manager),
683 information->contact);
685 for (l = all_groups; l; l = l->next) {
686 const gchar *group_str;
691 enabled = g_list_find_custom (my_groups,
693 (GCompareFunc) strcmp) != NULL;
695 gtk_list_store_append (store, &iter);
696 gtk_list_store_set (store, &iter,
699 COL_ENABLED, enabled,
703 g_list_foreach (all_groups, (GFunc) g_free, NULL);
704 g_list_foreach (my_groups, (GFunc) g_free, NULL);
705 g_list_free (all_groups);
706 g_list_free (my_groups);
710 contact_widget_groups_notify_cb (EmpathyContactWidget *information)
712 /* FIXME: not implemented */
716 contact_widget_model_find_name (EmpathyContactWidget *information,
724 if (G_STR_EMPTY (name)) {
728 data.information = information;
732 view = GTK_TREE_VIEW (information->treeview_groups);
733 model = gtk_tree_view_get_model (view);
735 gtk_tree_model_foreach (model,
736 (GtkTreeModelForeachFunc) contact_widget_model_find_name_foreach,
739 if (data.found == TRUE) {
740 *iter = data.found_iter;
748 contact_widget_model_find_name_foreach (GtkTreeModel *model,
755 gtk_tree_model_get (model, iter,
763 if (data->name && strcmp (data->name, name) == 0) {
765 data->found_iter = *iter;
778 contact_widget_cell_toggled (GtkCellRendererToggle *cell,
780 EmpathyContactWidget *information)
790 view = GTK_TREE_VIEW (information->treeview_groups);
791 model = gtk_tree_view_get_model (view);
792 store = GTK_LIST_STORE (model);
794 path = gtk_tree_path_new_from_string (path_string);
796 gtk_tree_model_get_iter (model, &iter, path);
797 gtk_tree_model_get (model, &iter,
798 COL_ENABLED, &enabled,
802 gtk_list_store_set (store, &iter, COL_ENABLED, !enabled, -1);
803 gtk_tree_path_free (path);
807 empathy_contact_list_remove_from_group (EMPATHY_CONTACT_LIST (information->manager),
808 information->contact,
811 empathy_contact_list_add_to_group (EMPATHY_CONTACT_LIST (information->manager),
812 information->contact,
821 contact_widget_entry_group_changed_cb (GtkEditable *editable,
822 EmpathyContactWidget *information)
827 group = gtk_entry_get_text (GTK_ENTRY (information->entry_group));
829 if (contact_widget_model_find_name (information, group, &iter)) {
830 gtk_widget_set_sensitive (GTK_WIDGET (information->button_group), FALSE);
833 gtk_widget_set_sensitive (GTK_WIDGET (information->button_group),
834 !G_STR_EMPTY (group));
839 contact_widget_entry_group_activate_cb (GtkEntry *entry,
840 EmpathyContactWidget *information)
842 gtk_widget_activate (GTK_WIDGET (information->button_group));
846 contact_widget_button_group_clicked_cb (GtkButton *button,
847 EmpathyContactWidget *information)
854 view = GTK_TREE_VIEW (information->treeview_groups);
855 store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
857 group = gtk_entry_get_text (GTK_ENTRY (information->entry_group));
859 gtk_list_store_append (store, &iter);
860 gtk_list_store_set (store, &iter,
865 empathy_contact_list_add_to_group (EMPATHY_CONTACT_LIST (information->manager),
866 information->contact,
871 contact_widget_details_setup (EmpathyContactWidget *information)
873 /* FIXME: Needs new telepathy spec */
874 gtk_widget_hide (information->vbox_details);
878 contact_widget_details_update (EmpathyContactWidget *information)
880 /* FIXME: Needs new telepathy spec */
884 contact_widget_client_setup (EmpathyContactWidget *information)
886 /* FIXME: Needs new telepathy spec */
887 gtk_widget_hide (information->vbox_client);
891 contact_widget_client_update (EmpathyContactWidget *information)
893 /* FIXME: Needs new telepathy spec */