1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2007-2009 Collabora Ltd.
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.
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.
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
19 * Authors: Xavier Claessens <xclaesse@gmail.com>
28 #include <glib/gi18n-lib.h>
30 #ifdef HAVE_LIBCHAMPLAIN
31 #include <champlain/champlain.h>
32 #include <champlain-gtk/champlain-gtk.h>
35 #include <telepathy-glib/account.h>
36 #include <telepathy-glib/util.h>
37 #include <telepathy-glib/interfaces.h>
39 #include <libempathy/empathy-tp-contact-factory.h>
40 #include <libempathy/empathy-contact-manager.h>
41 #include <libempathy/empathy-contact-list.h>
42 #include <libempathy/empathy-location.h>
43 #include <libempathy/empathy-request-util.h>
44 #include <libempathy/empathy-time.h>
45 #include <libempathy/empathy-utils.h>
47 #include "empathy-contact-widget.h"
48 #include "empathy-account-chooser.h"
49 #include "empathy-avatar-chooser.h"
50 #include "empathy-avatar-image.h"
51 #include "empathy-groups-widget.h"
52 #include "empathy-ui-utils.h"
53 #include "empathy-string-parser.h"
55 #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
56 #include <libempathy/empathy-debug.h>
59 * SECTION:empathy-contact-widget
60 * @title:EmpathyContactWidget
61 * @short_description: A widget used to display and edit details about a contact
62 * @include: libempathy-empathy-contact-widget.h
64 * #EmpathyContactWidget is a widget which displays appropriate widgets
65 * with details about a contact, also allowing changing these details,
70 * EmpathyContactWidget:
71 * @parent: parent object
73 * Widget which displays appropriate widgets with details about a contact,
74 * also allowing changing these details, if desired.
77 /* Delay before updating the widget when the id entry changed (seconds) */
78 #define ID_CHANGED_TIMEOUT 1
80 #define DATA_FIELD "contact-info-field"
84 EmpathyContactManager
*manager
;
85 EmpathyContact
*contact
;
86 EmpathyContactWidgetFlags flags
;
87 guint widget_id_timeout
;
90 GtkWidget
*vbox_contact_widget
;
93 GtkWidget
*hbox_contact
;
94 GtkWidget
*widget_avatar
;
95 GtkWidget
*widget_account
;
96 GtkWidget
*image_account
;
97 GtkWidget
*label_account
;
99 GtkWidget
*widget_alias
;
100 GtkWidget
*label_alias
;
101 GtkWidget
*hbox_presence
;
102 GtkWidget
*image_state
;
103 GtkWidget
*label_status
;
104 GtkWidget
*table_contact
;
105 GtkWidget
*vbox_avatar
;
106 GtkWidget
*favourite_checkbox
;
109 GtkWidget
*vbox_location
;
110 GtkWidget
*subvbox_location
;
111 GtkWidget
*table_location
;
112 GtkWidget
*label_location
;
113 #ifdef HAVE_LIBCHAMPLAIN
114 GtkWidget
*viewport_map
;
115 GtkWidget
*map_view_embed
;
116 ChamplainView
*map_view
;
120 GtkWidget
*groups_widget
;
123 GtkWidget
*vbox_details
;
124 GtkWidget
*table_details
;
125 GtkWidget
*hbox_details_requested
;
126 GtkWidget
*spinner_details
;
127 GList
*details_to_set
;
128 GCancellable
*details_cancellable
;
129 gboolean details_changed
;
132 GtkWidget
*vbox_client
;
133 GtkWidget
*table_client
;
134 GtkWidget
*hbox_client_requested
;
135 } EmpathyContactWidget
;
139 EmpathyContactWidget
*information
;
142 GtkTreeIter found_iter
;
154 field_value_is_empty (TpContactInfoField
*field
)
158 if (field
->field_value
== NULL
)
161 /* Field is empty if all its values are empty */
162 for (i
= 0; field
->field_value
[i
] != NULL
; i
++)
164 if (!tp_str_empty (field
->field_value
[i
]))
172 set_contact_info_cb (GObject
*source
,
173 GAsyncResult
*result
,
176 GError
*error
= NULL
;
178 if (!tp_connection_set_contact_info_finish (TP_CONNECTION (source
), result
,
181 DEBUG ("SetContactInfo() failed: %s", error
->message
);
182 g_error_free (error
);
186 DEBUG ("SetContactInfo() succeeded");
190 contact_widget_save (EmpathyContactWidget
*information
)
192 TpConnection
*connection
;
195 connection
= empathy_contact_get_connection (information
->contact
);
197 /* Remove empty fields */
198 for (l
= information
->details_to_set
; l
!= NULL
; l
= next
)
200 TpContactInfoField
*field
= l
->data
;
203 if (field_value_is_empty (field
))
205 DEBUG ("Drop empty field: %s", field
->field_name
);
206 tp_contact_info_field_free (field
);
207 information
->details_to_set
=
208 g_list_delete_link (information
->details_to_set
, l
);
212 if (information
->details_to_set
!= NULL
)
214 if (information
->details_changed
)
216 tp_connection_set_contact_info_async (connection
,
217 information
->details_to_set
, set_contact_info_cb
, NULL
);
220 tp_contact_info_list_free (information
->details_to_set
);
221 information
->details_to_set
= NULL
;
226 contact_widget_details_setup (EmpathyContactWidget
*information
)
228 gtk_widget_hide (information
->vbox_details
);
230 information
->spinner_details
= gtk_spinner_new ();
231 gtk_box_pack_end (GTK_BOX (information
->hbox_details_requested
),
232 information
->spinner_details
, TRUE
, TRUE
, 0);
233 gtk_widget_show (information
->spinner_details
);
237 contact_widget_details_changed_cb (GtkEntry
*entry
,
238 EmpathyContactWidget
*self
)
240 const gchar
*strv
[] = { NULL
, NULL
};
241 TpContactInfoField
*field
;
243 self
->details_changed
= TRUE
;
245 field
= g_object_get_data ((GObject
*) entry
, DATA_FIELD
);
246 g_assert (field
!= NULL
);
248 strv
[0] = gtk_entry_get_text (entry
);
250 if (field
->field_value
!= NULL
)
251 g_strfreev (field
->field_value
);
252 field
->field_value
= g_strdupv ((GStrv
) strv
);
256 contact_widget_bday_changed_cb (GtkCalendar
*calendar
,
257 EmpathyContactWidget
*self
)
259 guint year
, month
, day
;
262 const gchar
*strv
[] = { NULL
, NULL
};
263 TpContactInfoField
*field
;
265 self
->details_changed
= TRUE
;
267 field
= g_object_get_data ((GObject
*) calendar
, DATA_FIELD
);
268 g_assert (field
!= NULL
);
270 gtk_calendar_get_date (calendar
, &year
, &month
, &day
);
271 date
= g_date_new_dmy (day
, month
+1, year
);
273 gtk_calendar_clear_marks (calendar
);
274 gtk_calendar_mark_day (calendar
, g_date_get_day (date
));
276 g_date_strftime (tmp
, sizeof (tmp
), EMPATHY_DATE_FORMAT_DISPLAY_SHORT
, date
);
278 if (field
->field_value
!= NULL
)
279 g_strfreev (field
->field_value
);
280 field
->field_value
= g_strdupv ((GStrv
) strv
);
285 static void contact_widget_details_notify_cb (EmpathyContactWidget
*information
);
287 typedef gchar
* (* FieldFormatFunc
) (GStrv
);
291 const gchar
*field_name
;
293 FieldFormatFunc format
;
297 linkify_first_value (GStrv values
)
299 return empathy_add_link_markup (values
[0]);
303 format_idle_time (GStrv values
)
305 const gchar
*value
= values
[0];
306 int duration
= strtol (value
, NULL
, 10);
311 return empathy_duration_to_string (duration
);
315 format_server (GStrv values
)
317 g_assert (values
[0] != NULL
);
319 if (values
[1] == NULL
)
320 return g_markup_escape_text (values
[0], -1);
322 return g_markup_printf_escaped ("%s (%s)", values
[0], values
[1]);
326 presence_hack (GStrv values
)
328 if (tp_str_empty (values
[0]))
331 return g_markup_escape_text (values
[0], -1);
334 static InfoFieldData info_field_datas
[] =
336 { "fn", N_("Full name:"), NULL
},
337 { "tel", N_("Phone number:"), NULL
},
338 { "email", N_("E-mail address:"), linkify_first_value
},
339 { "url", N_("Website:"), linkify_first_value
},
340 { "bday", N_("Birthday:"), NULL
},
342 /* Note to translators: this is the caption for a string of the form "5
343 * minutes ago", and refers to the time since the contact last interacted
344 * with their IM client.
346 { "x-idle-time", N_("Last seen:"), format_idle_time
},
347 { "x-irc-server", N_("Server:"), format_server
},
348 { "x-host", N_("Connected from:"), format_server
},
350 /* FIXME: once Idle implements SimplePresence using this information, we can
351 * and should bin this.
353 { "x-presence-status-message", N_("Away message:"), presence_hack
},
358 static InfoFieldData
*
359 find_info_field_data (const gchar
*field_name
)
363 for (i
= 0; info_field_datas
[i
].field_name
!= NULL
; i
++)
365 if (!tp_strdiff (info_field_datas
[i
].field_name
, field_name
))
366 return info_field_datas
+ i
;
372 contact_info_field_name_cmp (const gchar
*name1
,
377 if (!tp_strdiff (name1
, name2
))
380 /* We use the order of info_field_datas */
381 for (i
= 0; info_field_datas
[i
].field_name
!= NULL
; i
++)
383 if (!tp_strdiff (info_field_datas
[i
].field_name
, name1
))
385 if (!tp_strdiff (info_field_datas
[i
].field_name
, name2
))
389 return g_strcmp0 (name1
, name2
);
393 contact_info_field_cmp (TpContactInfoField
*field1
,
394 TpContactInfoField
*field2
)
396 return contact_info_field_name_cmp (field1
->field_name
, field2
->field_name
);
400 field_name_in_field_list (GList
*list
,
405 for (l
= list
; l
!= NULL
; l
= g_list_next (l
))
407 TpContactInfoField
*field
= l
->data
;
409 if (!tp_strdiff (field
->field_name
, name
))
416 static TpContactInfoFieldSpec
*
417 get_spec_from_list (GList
*list
,
422 for (l
= list
; l
!= NULL
; l
= g_list_next (l
))
424 TpContactInfoFieldSpec
*spec
= l
->data
;
426 if (!tp_strdiff (spec
->name
, name
))
434 contact_widget_details_update_edit (EmpathyContactWidget
*information
)
437 TpConnection
*connection
;
443 g_assert (information
->details_to_set
== NULL
);
445 information
->details_changed
= FALSE
;
447 contact
= empathy_contact_get_tp_contact (information
->contact
);
448 connection
= tp_contact_get_connection (contact
);
450 info
= tp_contact_get_contact_info (contact
);
452 specs
= tp_connection_get_contact_info_supported_fields (connection
);
454 /* Look at the fields set in our vCard */
455 for (l
= info
; l
!= NULL
; l
= l
->next
)
457 TpContactInfoField
*field
= l
->data
;
459 /* make a copy for the details_to_set list */
460 field
= tp_contact_info_field_copy (field
);
461 DEBUG ("Field %s is in our vCard", field
->field_name
);
463 information
->details_to_set
= g_list_prepend (information
->details_to_set
,
467 /* Add fields which are supported but not in the vCard */
468 for (i
= 0; info_field_datas
[i
].field_name
!= NULL
; i
++)
470 TpContactInfoFieldSpec
*spec
;
471 TpContactInfoField
*field
;
473 /* Check if the field was in the vCard */
474 if (field_name_in_field_list (information
->details_to_set
,
475 info_field_datas
[i
].field_name
))
478 /* Check if the CM supports the field */
479 spec
= get_spec_from_list (specs
, info_field_datas
[i
].field_name
);
483 /* add an empty field so user can set a value */
484 field
= tp_contact_info_field_new (spec
->name
, spec
->parameters
, NULL
);
486 information
->details_to_set
= g_list_prepend (information
->details_to_set
,
490 /* Add widgets for supported fields */
491 information
->details_to_set
= g_list_sort (information
->details_to_set
,
492 (GCompareFunc
) contact_info_field_cmp
);
494 for (l
= information
->details_to_set
; l
!= NULL
; l
= g_list_next (l
))
496 TpContactInfoField
*field
= l
->data
;
497 InfoFieldData
*field_data
;
499 TpContactInfoFieldSpec
*spec
;
501 field_data
= find_info_field_data (field
->field_name
);
502 if (field_data
== NULL
)
504 /* Empathy doesn't display this field so we can't change it.
505 * But we put it in the details_to_set list so it won't be erased
506 * when calling SetContactInfo (bgo #630427) */
507 DEBUG ("Unhandled ContactInfo field spec: %s", field
->field_name
);
511 spec
= get_spec_from_list (specs
, field
->field_name
);
512 /* We shouldn't have added the field to details_to_set if it's not
513 * supported by the CM */
514 g_assert (spec
!= NULL
);
516 if (spec
->flags
& TP_CONTACT_INFO_FIELD_FLAG_OVERWRITTEN_BY_NICKNAME
)
518 DEBUG ("Ignoring field '%s' due it to having the "
519 "Overwritten_By_Nickname flag", field
->field_name
);
524 w
= gtk_label_new (_(field_data
->title
));
525 gtk_table_attach (GTK_TABLE (information
->table_details
),
526 w
, 0, 1, n_rows
, n_rows
+ 1, GTK_FILL
, 0, 0, 0);
527 gtk_misc_set_alignment (GTK_MISC (w
), 0, 0.5);
531 if (!tp_strdiff (field
->field_name
, "bday"))
533 w
= gtk_calendar_new ();
534 if (field
->field_value
[0])
538 g_date_set_parse (&date
, field
->field_value
[0]);
539 if (g_date_valid (&date
))
541 gtk_calendar_select_day (GTK_CALENDAR (w
),
542 g_date_get_day (&date
));
543 gtk_calendar_select_month (GTK_CALENDAR (w
),
544 g_date_get_month (&date
) - 1, g_date_get_year (&date
));
545 gtk_calendar_mark_day (GTK_CALENDAR (w
),
546 g_date_get_day (&date
));
549 gtk_table_attach_defaults (GTK_TABLE (information
->table_details
),
550 w
, 1, 2, n_rows
, n_rows
+ 1);
553 g_object_set_data ((GObject
*) w
, DATA_FIELD
, field
);
555 g_signal_connect (w
, "day-selected-double-click",
556 G_CALLBACK (contact_widget_bday_changed_cb
), information
);
557 g_signal_connect (w
, "month-changed",
558 G_CALLBACK (contact_widget_bday_changed_cb
), information
);
562 w
= gtk_entry_new ();
563 gtk_entry_set_text (GTK_ENTRY (w
),
564 field
->field_value
[0] ? field
->field_value
[0] : "");
565 gtk_table_attach_defaults (GTK_TABLE (information
->table_details
),
566 w
, 1, 2, n_rows
, n_rows
+ 1);
569 g_object_set_data ((GObject
*) w
, DATA_FIELD
, field
);
571 g_signal_connect (w
, "changed",
572 G_CALLBACK (contact_widget_details_changed_cb
), information
);
585 channel_name_activated_cb (
588 EmpathyContactWidget
*information
)
590 TpAccount
*account
= empathy_contact_get_account (information
->contact
);
592 empathy_join_muc (account
, uri
, empathy_get_current_action_time ());
598 EmpathyContactWidget
*information
,
603 GString
*label_markup
= g_string_new ("");
606 w
= gtk_label_new (_("Channels:"));
607 gtk_table_attach (GTK_TABLE (information
->table_details
),
608 w
, 0, 1, row
, row
+ 1, GTK_FILL
, 0, 0, 0);
609 gtk_misc_set_alignment (GTK_MISC (w
), 0, 0.5);
612 for (i
= 0; i
< channels
->len
; i
++)
614 const gchar
*channel_name
= g_ptr_array_index (channels
, i
);
615 /* We abuse the URI of the link to hold the channel name. It seems to
616 * be okay to just use it essentially verbatim, rather than trying to
617 * ensure it's actually a valid URI. g_string_append_uri_escaped()
618 * escapes way more than we actually need to; so we're just using
619 * g_markup_escape_text directly.
621 gchar
*escaped
= g_markup_escape_text (channel_name
, -1);
624 g_string_append (label_markup
, ", ");
626 g_string_append_printf (label_markup
, "<a href='%s'>%s</a>",
627 escaped
, channel_name
);
631 w
= gtk_label_new (NULL
);
632 gtk_label_set_markup (GTK_LABEL (w
), label_markup
->str
);
633 gtk_label_set_line_wrap (GTK_LABEL (w
), TRUE
);
634 g_signal_connect (w
, "activate-link",
635 (GCallback
) channel_name_activated_cb
, information
);
636 gtk_table_attach_defaults (GTK_TABLE (information
->table_details
),
637 w
, 1, 2, row
, row
+ 1);
638 gtk_misc_set_alignment (GTK_MISC (w
), 0, 0.5);
641 g_string_free (label_markup
, TRUE
);
645 contact_widget_details_update_show (EmpathyContactWidget
*information
)
650 GPtrArray
*channels
= g_ptr_array_new ();
652 contact
= empathy_contact_get_tp_contact (information
->contact
);
653 info
= tp_contact_get_contact_info (contact
);
654 info
= g_list_sort (info
, (GCompareFunc
) contact_info_field_cmp
);
655 for (l
= info
; l
!= NULL
; l
= l
->next
)
657 TpContactInfoField
*field
= l
->data
;
658 InfoFieldData
*field_data
;
660 gchar
*markup
= NULL
;
663 if (field
->field_value
== NULL
|| field
->field_value
[0] == NULL
)
666 value
= field
->field_value
[0];
668 if (!tp_strdiff (field
->field_name
, "x-irc-channel"))
670 g_ptr_array_add (channels
, (gpointer
) field
->field_value
[0]);
674 field_data
= find_info_field_data (field
->field_name
);
675 if (field_data
== NULL
)
677 DEBUG ("Unhandled ContactInfo field: %s", field
->field_name
);
681 if (field_data
->format
!= NULL
)
683 markup
= field_data
->format (field
->field_value
);
687 DEBUG ("Invalid value for field '%s' (first element was '%s')",
688 field
->field_name
, field
->field_value
[0]);
694 w
= gtk_label_new (_(field_data
->title
));
695 gtk_table_attach (GTK_TABLE (information
->table_details
),
696 w
, 0, 1, n_rows
, n_rows
+ 1, GTK_FILL
, 0, 0, 0);
697 gtk_misc_set_alignment (GTK_MISC (w
), 0, 0.5);
701 w
= gtk_label_new (value
);
704 gtk_label_set_markup (GTK_LABEL (w
), markup
);
708 if ((information
->flags
& EMPATHY_CONTACT_WIDGET_FOR_TOOLTIP
) == 0)
709 gtk_label_set_selectable (GTK_LABEL (w
), TRUE
);
711 gtk_table_attach_defaults (GTK_TABLE (information
->table_details
),
712 w
, 1, 2, n_rows
, n_rows
+ 1);
713 gtk_misc_set_alignment (GTK_MISC (w
), 0, 0.5);
720 if (channels
->len
> 0)
722 add_channel_list (information
, channels
, n_rows
);
726 g_ptr_array_unref (channels
);
731 contact_widget_details_notify_cb (EmpathyContactWidget
*information
)
735 gtk_container_foreach (GTK_CONTAINER (information
->table_details
),
736 (GtkCallback
) gtk_widget_destroy
, NULL
);
738 if ((information
->flags
& EMPATHY_CONTACT_WIDGET_EDIT_DETAILS
) != 0)
739 n_rows
= contact_widget_details_update_edit (information
);
741 n_rows
= contact_widget_details_update_show (information
);
745 gtk_widget_show (information
->vbox_details
);
746 gtk_widget_show (information
->table_details
);
750 gtk_widget_hide (information
->vbox_details
);
753 gtk_widget_hide (information
->hbox_details_requested
);
754 gtk_spinner_stop (GTK_SPINNER (information
->spinner_details
));
758 contact_widget_details_request_cb (GObject
*object
,
762 TpContact
*contact
= TP_CONTACT (object
);
763 EmpathyContactWidget
*information
= user_data
;
764 GError
*error
= NULL
;
766 if (!tp_contact_request_contact_info_finish (contact
, res
, &error
))
768 /* If the request got cancelled it could mean the contact widget is
769 * destroyed, so we should not dereference information */
770 if (g_error_matches (error
, G_IO_ERROR
, G_IO_ERROR_CANCELLED
))
772 g_clear_error (&error
);
776 gtk_widget_hide (information
->vbox_details
);
777 g_clear_error (&error
);
781 contact_widget_details_notify_cb (information
);
784 /* If we are going to edit ContactInfo, we don't want live updates */
785 if ((information
->flags
& EMPATHY_CONTACT_WIDGET_EDIT_DETAILS
) == 0)
787 g_signal_connect_swapped (contact
, "notify::contact-info",
788 G_CALLBACK (contact_widget_details_notify_cb
), information
);
791 tp_clear_object (&information
->details_cancellable
);
795 contact_widget_details_feature_prepared_cb (GObject
*object
,
799 TpConnection
*connection
= TP_CONNECTION (object
);
800 EmpathyContactWidget
*information
= user_data
;
802 TpContactInfoFlags flags
;
804 if (!tp_proxy_prepare_finish (connection
, res
, NULL
))
806 gtk_widget_hide (information
->vbox_details
);
810 /* If we want to edit info, but connection does not support that, stop */
811 flags
= tp_connection_get_contact_info_flags (connection
);
812 if ((flags
& TP_CONTACT_INFO_FLAG_CAN_SET
) == 0 &&
813 (information
->flags
& EMPATHY_CONTACT_WIDGET_EDIT_DETAILS
) != 0)
815 gtk_widget_hide (information
->vbox_details
);
819 /* Request the contact's info */
820 gtk_widget_show (information
->vbox_details
);
821 gtk_widget_show (information
->hbox_details_requested
);
822 gtk_widget_hide (information
->table_details
);
823 gtk_spinner_start (GTK_SPINNER (information
->spinner_details
));
825 contact
= empathy_contact_get_tp_contact (information
->contact
);
826 g_assert (information
->details_cancellable
== NULL
);
827 information
->details_cancellable
= g_cancellable_new ();
828 tp_contact_request_contact_info_async (contact
,
829 information
->details_cancellable
, contact_widget_details_request_cb
,
834 contact_widget_details_update (EmpathyContactWidget
*information
)
836 TpContact
*tp_contact
= NULL
;
838 if ((information
->flags
& EMPATHY_CONTACT_WIDGET_SHOW_DETAILS
) == 0 &&
839 (information
->flags
& EMPATHY_CONTACT_WIDGET_EDIT_DETAILS
) == 0)
842 gtk_widget_hide (information
->vbox_details
);
844 if (information
->contact
!= NULL
)
845 tp_contact
= empathy_contact_get_tp_contact (information
->contact
);
847 if (tp_contact
!= NULL
)
849 GQuark features
[] = { TP_CONNECTION_FEATURE_CONTACT_INFO
, 0 };
850 TpConnection
*connection
;
852 /* First, make sure the CONTACT_INFO feature is ready on the connection */
853 connection
= tp_contact_get_connection (tp_contact
);
854 tp_proxy_prepare_async (connection
, features
,
855 contact_widget_details_feature_prepared_cb
, information
);
860 contact_widget_client_update (EmpathyContactWidget
*information
)
862 /* FIXME: Needs new telepathy spec */
866 contact_widget_client_setup (EmpathyContactWidget
*information
)
868 /* FIXME: Needs new telepathy spec */
869 gtk_widget_hide (information
->vbox_client
);
873 contact_widget_groups_update (EmpathyContactWidget
*information
)
875 if (information
->flags
& EMPATHY_CONTACT_WIDGET_EDIT_GROUPS
&&
876 information
->contact
!= NULL
)
878 FolksPersona
*persona
=
879 empathy_contact_get_persona (information
->contact
);
881 if (FOLKS_IS_GROUP_DETAILS (persona
))
883 empathy_groups_widget_set_group_details (
884 EMPATHY_GROUPS_WIDGET (information
->groups_widget
),
885 FOLKS_GROUP_DETAILS (persona
));
886 gtk_widget_show (information
->groups_widget
);
892 /* In case of failure */
893 gtk_widget_hide (information
->groups_widget
);
896 /* Converts the Location's GHashTable's key to a user readable string */
898 location_key_to_label (const gchar
*key
)
900 if (tp_strdiff (key
, EMPATHY_LOCATION_COUNTRY_CODE
) == FALSE
)
901 return _("Country ISO Code:");
902 else if (tp_strdiff (key
, EMPATHY_LOCATION_COUNTRY
) == FALSE
)
903 return _("Country:");
904 else if (tp_strdiff (key
, EMPATHY_LOCATION_REGION
) == FALSE
)
906 else if (tp_strdiff (key
, EMPATHY_LOCATION_LOCALITY
) == FALSE
)
908 else if (tp_strdiff (key
, EMPATHY_LOCATION_AREA
) == FALSE
)
910 else if (tp_strdiff (key
, EMPATHY_LOCATION_POSTAL_CODE
) == FALSE
)
911 return _("Postal Code:");
912 else if (tp_strdiff (key
, EMPATHY_LOCATION_STREET
) == FALSE
)
914 else if (tp_strdiff (key
, EMPATHY_LOCATION_BUILDING
) == FALSE
)
915 return _("Building:");
916 else if (tp_strdiff (key
, EMPATHY_LOCATION_FLOOR
) == FALSE
)
918 else if (tp_strdiff (key
, EMPATHY_LOCATION_ROOM
) == FALSE
)
920 else if (tp_strdiff (key
, EMPATHY_LOCATION_TEXT
) == FALSE
)
922 else if (tp_strdiff (key
, EMPATHY_LOCATION_DESCRIPTION
) == FALSE
)
923 return _("Description:");
924 else if (tp_strdiff (key
, EMPATHY_LOCATION_URI
) == FALSE
)
926 else if (tp_strdiff (key
, EMPATHY_LOCATION_ACCURACY_LEVEL
) == FALSE
)
927 return _("Accuracy Level:");
928 else if (tp_strdiff (key
, EMPATHY_LOCATION_ERROR
) == FALSE
)
930 else if (tp_strdiff (key
, EMPATHY_LOCATION_VERTICAL_ERROR_M
) == FALSE
)
931 return _("Vertical Error (meters):");
932 else if (tp_strdiff (key
, EMPATHY_LOCATION_HORIZONTAL_ERROR_M
) == FALSE
)
933 return _("Horizontal Error (meters):");
934 else if (tp_strdiff (key
, EMPATHY_LOCATION_SPEED
) == FALSE
)
936 else if (tp_strdiff (key
, EMPATHY_LOCATION_BEARING
) == FALSE
)
937 return _("Bearing:");
938 else if (tp_strdiff (key
, EMPATHY_LOCATION_CLIMB
) == FALSE
)
939 return _("Climb Speed:");
940 else if (tp_strdiff (key
, EMPATHY_LOCATION_TIMESTAMP
) == FALSE
)
941 return _("Last Updated on:");
942 else if (tp_strdiff (key
, EMPATHY_LOCATION_LON
) == FALSE
)
943 return _("Longitude:");
944 else if (tp_strdiff (key
, EMPATHY_LOCATION_LAT
) == FALSE
)
945 return _("Latitude:");
946 else if (tp_strdiff (key
, EMPATHY_LOCATION_ALT
) == FALSE
)
947 return _("Altitude:");
950 DEBUG ("Unexpected Location key: %s", key
);
956 contact_widget_location_update (EmpathyContactWidget
*information
)
958 GHashTable
*location
;
960 #ifdef HAVE_LIBCHAMPLAIN
961 gdouble lat
= 0.0, lon
= 0.0;
962 gboolean has_position
= TRUE
;
966 static const gchar
* ordered_geolocation_keys
[] = {
967 EMPATHY_LOCATION_TEXT
,
968 EMPATHY_LOCATION_URI
,
969 EMPATHY_LOCATION_DESCRIPTION
,
970 EMPATHY_LOCATION_BUILDING
,
971 EMPATHY_LOCATION_FLOOR
,
972 EMPATHY_LOCATION_ROOM
,
973 EMPATHY_LOCATION_STREET
,
974 EMPATHY_LOCATION_AREA
,
975 EMPATHY_LOCATION_LOCALITY
,
976 EMPATHY_LOCATION_REGION
,
977 EMPATHY_LOCATION_COUNTRY
,
982 gboolean display_map
= FALSE
;
984 if (!(information
->flags
& EMPATHY_CONTACT_WIDGET_SHOW_LOCATION
))
986 gtk_widget_hide (information
->vbox_location
);
990 location
= empathy_contact_get_location (information
->contact
);
991 if (location
== NULL
|| g_hash_table_size (location
) == 0)
993 gtk_widget_hide (information
->vbox_location
);
997 value
= g_hash_table_lookup (location
, EMPATHY_LOCATION_TIMESTAMP
);
1000 gchar
*loc
= g_strdup_printf ("<b>%s</b>", _("Location"));
1001 gtk_label_set_markup (GTK_LABEL (information
->label_location
), loc
);
1011 stamp
= g_value_get_int64 (value
);
1013 user_date
= empathy_time_to_string_relative (stamp
);
1015 tmp
= g_strdup_printf ("<b>%s</b>", _("Location"));
1016 /* translators: format is "Location, $date" */
1017 text
= g_strdup_printf (_("%s, %s"), tmp
, user_date
);
1019 gtk_label_set_markup (GTK_LABEL (information
->label_location
), text
);
1025 /* Prepare the location information table */
1026 if (information
->table_location
!= NULL
)
1028 gtk_widget_destroy (information
->table_location
);
1031 information
->table_location
= gtk_table_new (1, 2, FALSE
);
1032 gtk_box_pack_start (GTK_BOX (information
->subvbox_location
),
1033 information
->table_location
, FALSE
, FALSE
, 5);
1036 for (i
= 0; (skey
= ordered_geolocation_keys
[i
]); i
++)
1038 const gchar
* user_label
;
1040 char *svalue
= NULL
;
1042 gvalue
= g_hash_table_lookup (location
, (gpointer
) skey
);
1046 user_label
= location_key_to_label (skey
);
1048 label
= gtk_label_new (user_label
);
1049 gtk_misc_set_alignment (GTK_MISC (label
), 0, 0.5);
1050 gtk_table_attach (GTK_TABLE (information
->table_location
),
1051 label
, 0, 1, row
, row
+ 1, GTK_FILL
, GTK_FILL
, 10, 0);
1052 gtk_widget_show (label
);
1054 if (G_VALUE_TYPE (gvalue
) == G_TYPE_DOUBLE
)
1057 dvalue
= g_value_get_double (gvalue
);
1058 svalue
= g_strdup_printf ("%f", dvalue
);
1060 else if (G_VALUE_TYPE (gvalue
) == G_TYPE_STRING
)
1062 svalue
= g_value_dup_string (gvalue
);
1064 else if (G_VALUE_TYPE (gvalue
) == G_TYPE_INT64
)
1068 time_
= g_value_get_int64 (value
);
1069 svalue
= empathy_time_to_string_utc (time_
, _("%B %e, %Y at %R UTC"));
1074 label
= gtk_label_new (svalue
);
1075 gtk_table_attach_defaults (GTK_TABLE (information
->table_location
),
1076 label
, 1, 2, row
, row
+ 1);
1077 gtk_misc_set_alignment (GTK_MISC (label
), 0, 0);
1078 gtk_widget_show (label
);
1080 if (!(information
->flags
& EMPATHY_CONTACT_WIDGET_FOR_TOOLTIP
))
1081 gtk_label_set_selectable (GTK_LABEL (label
), TRUE
);
1088 #ifdef HAVE_LIBCHAMPLAIN
1090 !(information
->flags
& EMPATHY_CONTACT_WIDGET_FOR_TOOLTIP
))
1092 /* Cannot be displayed in tooltips until Clutter-Gtk can deal with such
1100 /* We can display some fields */
1101 gtk_widget_show (information
->table_location
);
1103 else if (!display_map
)
1105 /* Can't display either fields or map */
1106 gtk_widget_hide (information
->vbox_location
);
1110 #ifdef HAVE_LIBCHAMPLAIN
1113 ClutterActor
*marker
;
1114 ChamplainMarkerLayer
*layer
;
1116 information
->map_view_embed
= gtk_champlain_embed_new ();
1117 information
->map_view
= gtk_champlain_embed_get_view (
1118 GTK_CHAMPLAIN_EMBED (information
->map_view_embed
));
1120 gtk_container_add (GTK_CONTAINER (information
->viewport_map
),
1121 information
->map_view_embed
);
1122 g_object_set (G_OBJECT (information
->map_view
),
1123 "kinetic-mode", TRUE
,
1127 layer
= champlain_marker_layer_new ();
1128 champlain_view_add_layer (information
->map_view
, CHAMPLAIN_LAYER (layer
));
1130 marker
= champlain_label_new_with_text (
1131 empathy_contact_get_alias (information
->contact
), NULL
, NULL
, NULL
);
1132 champlain_location_set_location (CHAMPLAIN_LOCATION (marker
), lat
, lon
);
1133 champlain_marker_layer_add_marker (layer
, CHAMPLAIN_MARKER (marker
));
1135 champlain_view_center_on (information
->map_view
, lat
, lon
);
1136 gtk_widget_show_all (information
->viewport_map
);
1140 gtk_widget_show (information
->vbox_location
);
1144 save_avatar_menu_activate_cb (GtkWidget
*widget
,
1145 EmpathyContactWidget
*information
)
1148 EmpathyAvatar
*avatar
;
1149 gchar
*ext
= NULL
, *filename
;
1151 dialog
= gtk_file_chooser_dialog_new (_("Save Avatar"),
1153 GTK_FILE_CHOOSER_ACTION_SAVE
,
1154 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
1155 GTK_STOCK_SAVE
, GTK_RESPONSE_ACCEPT
,
1158 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog
),
1161 /* look for the avatar extension */
1162 avatar
= empathy_contact_get_avatar (information
->contact
);
1163 if (avatar
->format
!= NULL
)
1167 splitted
= g_strsplit (avatar
->format
, "/", 2);
1168 if (splitted
[0] != NULL
&& splitted
[1] != NULL
)
1169 ext
= g_strdup (splitted
[1]);
1171 g_strfreev (splitted
);
1175 /* Avatar was loaded from the cache so was converted to PNG */
1176 ext
= g_strdup ("png");
1183 id
= tp_escape_as_identifier (empathy_contact_get_id (
1184 information
->contact
));
1186 filename
= g_strdup_printf ("%s.%s", id
, ext
);
1187 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog
), filename
);
1194 if (gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_ACCEPT
)
1196 GError
*error
= NULL
;
1198 filename
= gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog
));
1200 if (!empathy_avatar_save_to_file (avatar
, filename
, &error
))
1203 GtkWidget
*error_dialog
;
1205 error_dialog
= gtk_message_dialog_new (NULL
, 0,
1206 GTK_MESSAGE_ERROR
, GTK_BUTTONS_CLOSE
,
1207 _("Unable to save avatar"));
1209 gtk_message_dialog_format_secondary_text (
1210 GTK_MESSAGE_DIALOG (error_dialog
), "%s", error
->message
);
1212 g_signal_connect (error_dialog
, "response",
1213 G_CALLBACK (gtk_widget_destroy
), NULL
);
1215 gtk_window_present (GTK_WINDOW (error_dialog
));
1217 g_clear_error (&error
);
1223 gtk_widget_destroy (dialog
);
1227 popup_avatar_menu (EmpathyContactWidget
*information
,
1229 GdkEventButton
*event
)
1231 GtkWidget
*menu
, *item
;
1232 gint button
, event_time
;
1234 if (information
->contact
== NULL
||
1235 empathy_contact_get_avatar (information
->contact
) == NULL
)
1238 menu
= empathy_context_menu_new (parent
);
1240 /* Add "Save as..." entry */
1241 item
= gtk_image_menu_item_new_from_stock (GTK_STOCK_SAVE_AS
, NULL
);
1242 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
1243 gtk_widget_show (item
);
1245 g_signal_connect (item
, "activate",
1246 G_CALLBACK (save_avatar_menu_activate_cb
), information
);
1250 button
= event
->button
;
1251 event_time
= event
->time
;
1256 event_time
= gtk_get_current_event_time ();
1259 gtk_menu_popup (GTK_MENU (menu
), NULL
, NULL
, NULL
, NULL
,
1260 button
, event_time
);
1264 widget_avatar_popup_menu_cb (GtkWidget
*widget
,
1265 EmpathyContactWidget
*information
)
1267 popup_avatar_menu (information
, widget
, NULL
);
1273 widget_avatar_button_press_event_cb (GtkWidget
*widget
,
1274 GdkEventButton
*event
,
1275 EmpathyContactWidget
*information
)
1277 /* Ignore double-clicks and triple-clicks */
1278 if (event
->button
== 3 && event
->type
== GDK_BUTTON_PRESS
)
1280 popup_avatar_menu (information
, widget
, event
);
1288 set_avatar_cb (GObject
*source
,
1292 GError
*error
= NULL
;
1294 if (!tp_account_set_avatar_finish (TP_ACCOUNT (source
), res
, &error
)) {
1295 DEBUG ("Failed to set Account.Avatar: %s", error
->message
);
1296 g_error_free (error
);
1301 set_avatar_on_account (TpAccount
*account
,
1304 const gchar
*mime_type
)
1306 DEBUG ("%s Account.Avatar on %s", size
> 0 ? "Set": "Clear",
1307 tp_proxy_get_object_path (account
));
1309 tp_account_set_avatar_async (account
, (const guchar
*) data
, size
,
1310 mime_type
, set_avatar_cb
, NULL
);
1314 contact_widget_avatar_changed_cb (EmpathyAvatarChooser
*chooser
,
1315 EmpathyContactWidget
*information
)
1319 const gchar
*mime_type
;
1322 empathy_avatar_chooser_get_image_data (
1323 EMPATHY_AVATAR_CHOOSER (information
->widget_avatar
),
1324 &data
, &size
, &mime_type
);
1326 account
= empathy_contact_get_account (information
->contact
);
1327 set_avatar_on_account (account
, data
, size
, mime_type
);
1331 set_nickname_cb (GObject
*source
,
1335 GError
*error
= NULL
;
1337 if (!tp_account_set_nickname_finish (TP_ACCOUNT (source
), res
, &error
))
1339 DEBUG ("Failed to set Account.Nickname: %s", error
->message
);
1340 g_error_free (error
);
1344 /* Update all the contact info fields having the
1345 * Overwritten_By_Nickname flag to the new alias. This avoid accidentally
1346 * reseting the alias when calling SetContactInfo(). See bgo #644298 for
1349 update_nickname_in_contact_info (EmpathyContactWidget
*self
,
1350 TpConnection
*connection
,
1351 const gchar
*nickname
)
1355 specs
= tp_connection_get_contact_info_supported_fields (connection
);
1357 for (l
= self
->details_to_set
; l
!= NULL
; l
= g_list_next (l
))
1359 TpContactInfoField
*field
= l
->data
;
1360 TpContactInfoFieldSpec
*spec
;
1362 spec
= get_spec_from_list (specs
, field
->field_name
);
1363 /* We shouldn't have added the field to details_to_set if it's not
1364 * supported by the CM */
1365 g_assert (spec
!= NULL
);
1367 if (spec
->flags
& TP_CONTACT_INFO_FIELD_FLAG_OVERWRITTEN_BY_NICKNAME
)
1369 const gchar
*strv
[] = { nickname
, NULL
};
1371 DEBUG ("Updating field '%s' to '%s' as it has the "
1372 "Overwritten_By_Nickname flag and Account.Nickname has "
1373 "been updated", field
->field_name
, nickname
);
1375 if (field
->field_value
!= NULL
)
1376 g_strfreev (field
->field_value
);
1377 field
->field_value
= g_strdupv ((GStrv
) strv
);
1381 g_list_free (specs
);
1385 contact_widget_entry_alias_focus_event_cb (GtkEditable
*editable
,
1386 GdkEventFocus
*event
,
1387 EmpathyContactWidget
*information
)
1389 if (information
->contact
)
1393 alias
= gtk_entry_get_text (GTK_ENTRY (editable
));
1395 if (empathy_contact_is_user (information
->contact
))
1397 TpAccount
* account
;
1398 const gchar
*current_nickname
;
1400 account
= empathy_contact_get_account (information
->contact
);
1401 current_nickname
= tp_account_get_nickname (account
);
1403 if (tp_strdiff (current_nickname
, alias
))
1405 DEBUG ("Set Account.Nickname to %s", alias
);
1407 tp_account_set_nickname_async (account
, alias
, set_nickname_cb
,
1410 update_nickname_in_contact_info (information
,
1411 empathy_contact_get_connection (information
->contact
), alias
);
1416 empathy_contact_set_alias (information
->contact
, alias
);
1424 update_avatar_chooser_account_cb (EmpathyAccountChooser
*account_chooser
,
1425 EmpathyAvatarChooser
*avatar_chooser
)
1429 account
= empathy_account_chooser_get_account (account_chooser
);
1430 if (account
== NULL
)
1433 empathy_avatar_chooser_set_account (avatar_chooser
, account
);
1437 contact_widget_avatar_notify_cb (EmpathyContactWidget
*information
)
1439 EmpathyAvatar
*avatar
= NULL
;
1441 if (information
->contact
)
1442 avatar
= empathy_contact_get_avatar (information
->contact
);
1444 if (information
->flags
& EMPATHY_CONTACT_WIDGET_EDIT_AVATAR
)
1446 g_signal_handlers_block_by_func (information
->widget_avatar
,
1447 contact_widget_avatar_changed_cb
,
1449 empathy_avatar_chooser_set (
1450 EMPATHY_AVATAR_CHOOSER (information
->widget_avatar
), avatar
);
1451 g_signal_handlers_unblock_by_func (information
->widget_avatar
,
1452 contact_widget_avatar_changed_cb
, information
);
1455 empathy_avatar_image_set (
1456 EMPATHY_AVATAR_IMAGE (information
->widget_avatar
), avatar
);
1460 contact_widget_name_notify_cb (EmpathyContactWidget
*information
)
1462 if (GTK_IS_ENTRY (information
->widget_alias
))
1463 gtk_entry_set_text (GTK_ENTRY (information
->widget_alias
),
1464 empathy_contact_get_alias (information
->contact
));
1466 gtk_label_set_label (GTK_LABEL (information
->widget_alias
),
1467 empathy_contact_get_alias (information
->contact
));
1471 contact_widget_presence_notify_cb (EmpathyContactWidget
*information
)
1473 const gchar
*status
;
1474 gchar
*markup_text
= NULL
;
1476 status
= empathy_contact_get_status (information
->contact
);
1478 markup_text
= empathy_add_link_markup (status
);
1479 gtk_label_set_markup (GTK_LABEL (information
->label_status
), markup_text
);
1480 g_free (markup_text
);
1482 gtk_image_set_from_icon_name (GTK_IMAGE (information
->image_state
),
1483 empathy_icon_name_for_contact (information
->contact
),
1484 GTK_ICON_SIZE_BUTTON
);
1485 gtk_widget_show (information
->image_state
);
1489 contact_widget_favourites_changed_cb (EmpathyContactManager
*manager
,
1490 EmpathyContact
*contact
,
1491 gboolean is_favourite
,
1492 EmpathyContactWidget
*information
)
1494 if (contact
!= information
->contact
)
1497 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (
1498 information
->favourite_checkbox
), is_favourite
);
1502 contact_widget_remove_contact (EmpathyContactWidget
*information
)
1504 if (information
->contact
)
1506 TpContact
*tp_contact
;
1508 contact_widget_save (information
);
1510 g_signal_handlers_disconnect_by_func (information
->contact
,
1511 contact_widget_name_notify_cb
, information
);
1512 g_signal_handlers_disconnect_by_func (information
->contact
,
1513 contact_widget_presence_notify_cb
, information
);
1514 g_signal_handlers_disconnect_by_func (information
->contact
,
1515 contact_widget_avatar_notify_cb
, information
);
1517 tp_contact
= empathy_contact_get_tp_contact (information
->contact
);
1518 if (tp_contact
!= NULL
)
1520 g_signal_handlers_disconnect_by_func (tp_contact
,
1521 contact_widget_details_notify_cb
, information
);
1524 g_object_unref (information
->contact
);
1525 information
->contact
= NULL
;
1528 if (information
->details_cancellable
!= NULL
)
1530 g_cancellable_cancel (information
->details_cancellable
);
1531 tp_clear_object (&information
->details_cancellable
);
1535 static void contact_widget_change_contact (EmpathyContactWidget
*information
);
1538 contact_widget_contact_update (EmpathyContactWidget
*information
)
1540 TpAccount
*account
= NULL
;
1541 const gchar
*id
= NULL
;
1543 /* Connect and get info from new contact */
1544 if (information
->contact
)
1546 g_signal_connect_swapped (information
->contact
, "notify::name",
1547 G_CALLBACK (contact_widget_name_notify_cb
), information
);
1548 g_signal_connect_swapped (information
->contact
, "notify::presence",
1549 G_CALLBACK (contact_widget_presence_notify_cb
), information
);
1550 g_signal_connect_swapped (information
->contact
,
1551 "notify::presence-message",
1552 G_CALLBACK (contact_widget_presence_notify_cb
), information
);
1553 g_signal_connect_swapped (information
->contact
, "notify::avatar",
1554 G_CALLBACK (contact_widget_avatar_notify_cb
), information
);
1556 account
= empathy_contact_get_account (information
->contact
);
1557 id
= empathy_contact_get_id (information
->contact
);
1560 /* Update account widget */
1561 if (information
->flags
& EMPATHY_CONTACT_WIDGET_EDIT_ACCOUNT
)
1565 g_signal_handlers_block_by_func (information
->widget_account
,
1566 contact_widget_change_contact
,
1568 empathy_account_chooser_set_account (
1569 EMPATHY_ACCOUNT_CHOOSER (information
->widget_account
), account
);
1570 g_signal_handlers_unblock_by_func (information
->widget_account
,
1571 contact_widget_change_contact
, information
);
1580 name
= tp_account_get_display_name (account
);
1581 gtk_label_set_label (GTK_LABEL (information
->label_account
), name
);
1583 name
= tp_account_get_icon_name (account
);
1584 gtk_image_set_from_icon_name (GTK_IMAGE (information
->image_account
),
1585 name
, GTK_ICON_SIZE_MENU
);
1589 /* Update id widget */
1590 if (information
->flags
& EMPATHY_CONTACT_WIDGET_EDIT_ID
)
1591 gtk_entry_set_text (GTK_ENTRY (information
->widget_id
), id
? id
: "");
1593 gtk_label_set_label (GTK_LABEL (information
->widget_id
), id
? id
: "");
1595 /* Update other widgets */
1596 if (information
->contact
)
1598 contact_widget_name_notify_cb (information
);
1599 contact_widget_presence_notify_cb (information
);
1600 contact_widget_avatar_notify_cb (information
);
1602 if (information
->flags
& EMPATHY_CONTACT_WIDGET_EDIT_FAVOURITE
)
1604 FolksPersona
*persona
= empathy_contact_get_persona (
1605 information
->contact
);
1607 if (persona
!= NULL
&& FOLKS_IS_FAVOURITE_DETAILS (persona
))
1609 gboolean is_favourite
= folks_favourite_details_get_is_favourite (
1610 FOLKS_FAVOURITE_DETAILS (persona
));
1611 contact_widget_favourites_changed_cb (information
->manager
,
1612 information
->contact
, is_favourite
, information
);
1616 gtk_widget_show (information
->label_alias
);
1617 gtk_widget_show (information
->widget_alias
);
1618 gtk_widget_show (information
->hbox_presence
);
1619 gtk_widget_show (information
->widget_avatar
);
1623 gtk_widget_hide (information
->label_alias
);
1624 gtk_widget_hide (information
->widget_alias
);
1625 gtk_widget_hide (information
->hbox_presence
);
1626 gtk_widget_hide (information
->widget_avatar
);
1631 contact_widget_set_contact (EmpathyContactWidget
*information
,
1632 EmpathyContact
*contact
)
1634 if (contact
== information
->contact
)
1637 contact_widget_remove_contact (information
);
1639 information
->contact
= g_object_ref (contact
);
1641 /* set the selected account to be the account this contact came from */
1642 if (contact
&& EMPATHY_IS_ACCOUNT_CHOOSER (information
->widget_account
)) {
1643 empathy_account_chooser_set_account (
1644 EMPATHY_ACCOUNT_CHOOSER (information
->widget_account
),
1645 empathy_contact_get_account (contact
));
1648 /* Update information for widgets */
1649 contact_widget_contact_update (information
);
1650 contact_widget_groups_update (information
);
1651 contact_widget_details_update (information
);
1652 contact_widget_client_update (information
);
1653 contact_widget_location_update (information
);
1657 contact_widget_got_contact_cb (TpConnection
*connection
,
1658 EmpathyContact
*contact
,
1659 const GError
*error
,
1661 GObject
*weak_object
)
1663 EmpathyContactWidget
*information
= user_data
;
1667 DEBUG ("Error: %s", error
->message
);
1671 contact_widget_set_contact (information
, contact
);
1675 contact_widget_change_contact (EmpathyContactWidget
*information
)
1677 TpConnection
*connection
;
1679 connection
= empathy_account_chooser_get_connection (
1680 EMPATHY_ACCOUNT_CHOOSER (information
->widget_account
));
1684 if (information
->flags
& EMPATHY_CONTACT_WIDGET_EDIT_ID
)
1688 id
= gtk_entry_get_text (GTK_ENTRY (information
->widget_id
));
1689 if (!EMP_STR_EMPTY (id
))
1691 empathy_tp_contact_factory_get_from_id (connection
, id
,
1692 contact_widget_got_contact_cb
, information
, NULL
,
1693 G_OBJECT (information
->vbox_contact_widget
));
1698 empathy_tp_contact_factory_get_from_handle (connection
,
1699 tp_connection_get_self_handle (connection
),
1700 contact_widget_got_contact_cb
, information
, NULL
,
1701 G_OBJECT (information
->vbox_contact_widget
));
1706 contact_widget_id_activate_timeout (EmpathyContactWidget
*self
)
1708 contact_widget_change_contact (self
);
1713 contact_widget_id_changed_cb (GtkEntry
*entry
,
1714 EmpathyContactWidget
*self
)
1716 if (self
->widget_id_timeout
!= 0)
1718 g_source_remove (self
->widget_id_timeout
);
1721 self
->widget_id_timeout
=
1722 g_timeout_add_seconds (ID_CHANGED_TIMEOUT
,
1723 (GSourceFunc
) contact_widget_id_activate_timeout
, self
);
1727 contact_widget_id_focus_out_cb (GtkWidget
*widget
,
1728 GdkEventFocus
*event
,
1729 EmpathyContactWidget
*information
)
1731 contact_widget_change_contact (information
);
1736 favourite_toggled_cb (GtkToggleButton
*button
,
1737 EmpathyContactWidget
*information
)
1739 FolksPersona
*persona
= empathy_contact_get_persona (information
->contact
);
1741 if (persona
!= NULL
&& FOLKS_IS_FAVOURITE_DETAILS (persona
))
1743 gboolean active
= gtk_toggle_button_get_active (button
);
1744 folks_favourite_details_set_is_favourite (
1745 FOLKS_FAVOURITE_DETAILS (persona
), active
);
1750 contact_widget_contact_setup (EmpathyContactWidget
*information
)
1752 information
->label_status
= gtk_label_new ("");
1753 gtk_label_set_line_wrap_mode (GTK_LABEL (information
->label_status
),
1754 PANGO_WRAP_WORD_CHAR
);
1755 gtk_label_set_line_wrap (GTK_LABEL (information
->label_status
),
1758 if (!(information
->flags
& EMPATHY_CONTACT_WIDGET_FOR_TOOLTIP
))
1759 gtk_label_set_selectable (GTK_LABEL (information
->label_status
), TRUE
);
1761 gtk_box_pack_start (GTK_BOX (information
->hbox_presence
),
1762 information
->label_status
, TRUE
, TRUE
, 0);
1763 gtk_widget_show (information
->label_status
);
1765 /* Setup account label/chooser */
1766 if (information
->flags
& EMPATHY_CONTACT_WIDGET_EDIT_ACCOUNT
)
1768 information
->widget_account
= empathy_account_chooser_new ();
1770 g_signal_connect_swapped (information
->widget_account
, "changed",
1771 G_CALLBACK (contact_widget_change_contact
),
1776 /* Pack the protocol icon with the account name in an hbox */
1777 information
->widget_account
= gtk_hbox_new (FALSE
, 6);
1779 information
->label_account
= gtk_label_new (NULL
);
1780 if (!(information
->flags
& EMPATHY_CONTACT_WIDGET_FOR_TOOLTIP
)) {
1781 gtk_label_set_selectable (GTK_LABEL (information
->label_account
), TRUE
);
1783 gtk_misc_set_alignment (GTK_MISC (information
->label_account
), 0, 0.5);
1784 gtk_widget_show (information
->label_account
);
1786 information
->image_account
= gtk_image_new ();
1787 gtk_widget_show (information
->image_account
);
1789 gtk_box_pack_start (GTK_BOX (information
->widget_account
),
1790 information
->image_account
, FALSE
, FALSE
, 0);
1791 gtk_box_pack_start (GTK_BOX (information
->widget_account
),
1792 information
->label_account
, FALSE
, TRUE
, 0);
1794 gtk_table_attach_defaults (GTK_TABLE (information
->table_contact
),
1795 information
->widget_account
,
1797 gtk_widget_show (information
->widget_account
);
1799 /* Set up avatar chooser/display */
1800 if (information
->flags
& EMPATHY_CONTACT_WIDGET_EDIT_AVATAR
)
1802 information
->widget_avatar
= empathy_avatar_chooser_new ();
1803 g_signal_connect (information
->widget_avatar
, "changed",
1804 G_CALLBACK (contact_widget_avatar_changed_cb
),
1806 if (information
->flags
& EMPATHY_CONTACT_WIDGET_EDIT_ACCOUNT
)
1808 g_signal_connect (information
->widget_account
, "changed",
1809 G_CALLBACK (update_avatar_chooser_account_cb
),
1810 information
->widget_avatar
);
1811 update_avatar_chooser_account_cb (
1812 EMPATHY_ACCOUNT_CHOOSER (information
->widget_account
),
1813 EMPATHY_AVATAR_CHOOSER (information
->widget_avatar
));
1818 information
->widget_avatar
= empathy_avatar_image_new ();
1820 g_signal_connect (information
->widget_avatar
, "popup-menu",
1821 G_CALLBACK (widget_avatar_popup_menu_cb
), information
);
1822 g_signal_connect (information
->widget_avatar
, "button-press-event",
1823 G_CALLBACK (widget_avatar_button_press_event_cb
), information
);
1826 gtk_box_pack_start (GTK_BOX (information
->vbox_avatar
),
1827 information
->widget_avatar
,
1830 gtk_widget_show (information
->widget_avatar
);
1832 /* Setup id label/entry */
1833 if (information
->flags
& EMPATHY_CONTACT_WIDGET_EDIT_ID
)
1835 information
->widget_id
= gtk_entry_new ();
1836 g_signal_connect (information
->widget_id
, "focus-out-event",
1837 G_CALLBACK (contact_widget_id_focus_out_cb
),
1839 g_signal_connect (information
->widget_id
, "changed",
1840 G_CALLBACK (contact_widget_id_changed_cb
),
1845 information
->widget_id
= gtk_label_new (NULL
);
1846 if (!(information
->flags
& EMPATHY_CONTACT_WIDGET_FOR_TOOLTIP
)) {
1847 gtk_label_set_selectable (GTK_LABEL (information
->widget_id
), TRUE
);
1849 gtk_misc_set_alignment (GTK_MISC (information
->widget_id
), 0, 0.5);
1851 gtk_table_attach_defaults (GTK_TABLE (information
->table_contact
),
1852 information
->widget_id
,
1854 gtk_widget_show (information
->widget_id
);
1856 /* Setup alias label/entry */
1857 if (information
->flags
& EMPATHY_CONTACT_WIDGET_EDIT_ALIAS
)
1859 information
->widget_alias
= gtk_entry_new ();
1861 if (!(information
->flags
& EMPATHY_CONTACT_WIDGET_NO_SET_ALIAS
))
1862 g_signal_connect (information
->widget_alias
, "focus-out-event",
1863 G_CALLBACK (contact_widget_entry_alias_focus_event_cb
),
1866 /* Make return activate the window default (the Close button) */
1867 gtk_entry_set_activates_default (GTK_ENTRY (information
->widget_alias
),
1872 information
->widget_alias
= gtk_label_new (NULL
);
1873 if (!(information
->flags
& EMPATHY_CONTACT_WIDGET_FOR_TOOLTIP
)) {
1874 gtk_label_set_selectable (GTK_LABEL (information
->widget_alias
), TRUE
);
1876 gtk_misc_set_alignment (GTK_MISC (information
->widget_alias
), 0, 0.5);
1878 gtk_table_attach_defaults (GTK_TABLE (information
->table_contact
),
1879 information
->widget_alias
,
1881 if (information
->flags
& EMPATHY_CONTACT_WIDGET_FOR_TOOLTIP
) {
1882 gtk_label_set_selectable (GTK_LABEL (information
->label_status
), FALSE
);
1884 gtk_widget_show (information
->widget_alias
);
1887 if (information
->flags
& EMPATHY_CONTACT_WIDGET_EDIT_FAVOURITE
)
1889 information
->favourite_checkbox
= gtk_check_button_new_with_label (
1892 g_signal_connect (information
->favourite_checkbox
, "toggled",
1893 G_CALLBACK (favourite_toggled_cb
), information
);
1895 gtk_table_attach_defaults (GTK_TABLE (information
->table_contact
),
1896 information
->favourite_checkbox
, 0, 2, 3, 4);
1898 information
->fav_sig_id
= g_signal_connect (information
->manager
,
1899 "favourites-changed",
1900 G_CALLBACK (contact_widget_favourites_changed_cb
), information
);
1902 gtk_widget_show (information
->favourite_checkbox
);
1907 contact_widget_destroy_cb (GtkWidget
*widget
,
1908 EmpathyContactWidget
*information
)
1910 contact_widget_remove_contact (information
);
1912 if (information
->widget_id_timeout
!= 0)
1914 g_source_remove (information
->widget_id_timeout
);
1917 if (information
->fav_sig_id
!= 0)
1918 g_signal_handler_disconnect (information
->manager
, information
->fav_sig_id
);
1920 g_object_unref (information
->manager
);
1922 g_slice_free (EmpathyContactWidget
, information
);
1926 * empathy_contact_widget_new:
1927 * @contact: an #EmpathyContact
1928 * @flags: #EmpathyContactWidgetFlags for the new contact widget
1930 * Creates a new #EmpathyContactWidget.
1932 * Return value: a new #EmpathyContactWidget
1935 empathy_contact_widget_new (EmpathyContact
*contact
,
1936 EmpathyContactWidgetFlags flags
)
1938 EmpathyContactWidget
*information
;
1942 g_return_val_if_fail (contact
== NULL
|| EMPATHY_IS_CONTACT (contact
), NULL
);
1944 information
= g_slice_new0 (EmpathyContactWidget
);
1945 information
->flags
= flags
;
1947 filename
= empathy_file_lookup ("empathy-contact-widget.ui",
1949 gui
= empathy_builder_get_file (filename
,
1950 "vbox_contact_widget", &information
->vbox_contact_widget
,
1951 "hbox_contact", &information
->hbox_contact
,
1952 "hbox_presence", &information
->hbox_presence
,
1953 "label_alias", &information
->label_alias
,
1954 "image_state", &information
->image_state
,
1955 "table_contact", &information
->table_contact
,
1956 "vbox_avatar", &information
->vbox_avatar
,
1957 "vbox_location", &information
->vbox_location
,
1958 "subvbox_location", &information
->subvbox_location
,
1959 "label_location", &information
->label_location
,
1960 #ifdef HAVE_LIBCHAMPLAIN
1961 "viewport_map", &information
->viewport_map
,
1963 "groups_widget", &information
->groups_widget
,
1964 "vbox_details", &information
->vbox_details
,
1965 "table_details", &information
->table_details
,
1966 "hbox_details_requested", &information
->hbox_details_requested
,
1967 "vbox_client", &information
->vbox_client
,
1968 "table_client", &information
->table_client
,
1969 "hbox_client_requested", &information
->hbox_client_requested
,
1973 empathy_builder_connect (gui
, information
,
1974 "vbox_contact_widget", "destroy", contact_widget_destroy_cb
,
1976 information
->table_location
= NULL
;
1978 g_object_set_data (G_OBJECT (information
->vbox_contact_widget
),
1979 "EmpathyContactWidget",
1982 information
->manager
= empathy_contact_manager_dup_singleton ();
1984 /* Create widgets */
1985 contact_widget_contact_setup (information
);
1986 contact_widget_details_setup (information
);
1987 contact_widget_client_setup (information
);
1989 if (contact
!= NULL
)
1990 contact_widget_set_contact (information
, contact
);
1991 else if (information
->flags
& EMPATHY_CONTACT_WIDGET_EDIT_ACCOUNT
||
1992 information
->flags
& EMPATHY_CONTACT_WIDGET_EDIT_ID
)
1993 contact_widget_change_contact (information
);
1995 return empathy_builder_unref_and_keep_widget (gui
,
1996 information
->vbox_contact_widget
);
2000 * empathy_contact_widget_get_contact:
2001 * @widget: an #EmpathyContactWidget
2003 * Get the #EmpathyContact related with the #EmpathyContactWidget @widget.
2005 * Returns: the #EmpathyContact associated with @widget
2008 empathy_contact_widget_get_contact (GtkWidget
*widget
)
2010 EmpathyContactWidget
*information
;
2012 g_return_val_if_fail (GTK_IS_WIDGET (widget
), NULL
);
2014 information
= g_object_get_data (G_OBJECT (widget
), "EmpathyContactWidget");
2018 return information
->contact
;
2022 empathy_contact_widget_get_alias (GtkWidget
*widget
)
2024 EmpathyContactWidget
*information
;
2026 g_return_val_if_fail (GTK_IS_WIDGET (widget
), NULL
);
2028 information
= g_object_get_data (G_OBJECT (widget
), "EmpathyContactWidget");
2032 return gtk_entry_get_text (GTK_ENTRY (information
->widget_alias
));
2036 * empathy_contact_widget_set_contact:
2037 * @widget: an #EmpathyContactWidget
2038 * @contact: a different #EmpathyContact
2040 * Change the #EmpathyContact related with the #EmpathyContactWidget @widget.
2043 empathy_contact_widget_set_contact (GtkWidget
*widget
,
2044 EmpathyContact
*contact
)
2046 EmpathyContactWidget
*information
;
2048 g_return_if_fail (GTK_IS_WIDGET (widget
));
2049 g_return_if_fail (EMPATHY_IS_CONTACT (contact
));
2051 information
= g_object_get_data (G_OBJECT (widget
), "EmpathyContactWidget");
2055 contact_widget_set_contact (information
, contact
);
2059 * empathy_contact_widget_set_account_filter:
2060 * @widget: an #EmpathyContactWidget
2061 * @filter: a #EmpathyAccountChooserFilterFunc
2062 * @user_data: user data to pass to @filter, or %NULL
2064 * Set a filter on the #EmpathyAccountChooser included in the
2065 * #EmpathyContactWidget.
2068 empathy_contact_widget_set_account_filter (
2070 EmpathyAccountChooserFilterFunc filter
,
2073 EmpathyContactWidget
*information
;
2074 EmpathyAccountChooser
*chooser
;
2076 g_return_if_fail (GTK_IS_WIDGET (widget
));
2078 information
= g_object_get_data (G_OBJECT (widget
), "EmpathyContactWidget");
2082 chooser
= EMPATHY_ACCOUNT_CHOOSER (information
->widget_account
);
2084 empathy_account_chooser_set_filter (chooser
, filter
, user_data
);