1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /* e-name-selector-entry.c - Single-line text entry widget for EDestinations.
5 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of version 2 of the GNU Lesser General Public
9 * License as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
21 * Authors: Hans Petter Jansson <hpj@novell.com>
26 #include <glib/gi18n-lib.h>
28 #include <camel/camel.h>
29 #include <libebackend/libebackend.h>
31 #include <libedataserverui/e-client-utils.h>
33 #include "e-name-selector-entry.h"
35 #define E_NAME_SELECTOR_ENTRY_GET_PRIVATE(obj) \
36 (G_TYPE_INSTANCE_GET_PRIVATE \
37 ((obj), E_TYPE_NAME_SELECTOR_ENTRY, ENameSelectorEntryPrivate))
39 struct _ENameSelectorEntryPrivate {
41 ESourceRegistry *registry;
42 gint minimum_query_length;
43 gboolean show_address;
45 PangoAttrList *attr_list;
46 EContactStore *contact_store;
47 ETreeModelGenerator *email_generator;
48 EDestinationStore *destination_store;
49 GtkEntryCompletion *entry_completion;
51 guint type_ahead_complete_cb_id;
52 guint update_completions_cb_id;
54 EDestination *popup_destination;
56 gpointer (*contact_editor_func) (EBookClient *,
60 gpointer (*contact_list_editor_func)
66 gboolean is_completing;
67 GSList *user_query_fields;
69 /* For asynchronous operations. */
76 PROP_MINIMUM_QUERY_LENGTH,
85 static guint signals[LAST_SIGNAL] = { 0 };
88 G_DEFINE_TYPE_WITH_CODE (
90 e_name_selector_entry,
92 G_IMPLEMENT_INTERFACE (
93 E_TYPE_EXTENSIBLE, NULL))
95 /* 1/3 of the second to wait until invoking autocomplete lookup */
96 #define AUTOCOMPLETE_TIMEOUT 333
98 #define re_set_timeout(id,func,ptr) \
100 g_source_remove (id); \
101 id = g_timeout_add (AUTOCOMPLETE_TIMEOUT, \
102 (GSourceFunc) func, ptr);
104 static void destination_row_inserted (ENameSelectorEntry *name_selector_entry, GtkTreePath *path, GtkTreeIter *iter);
105 static void destination_row_changed (ENameSelectorEntry *name_selector_entry, GtkTreePath *path, GtkTreeIter *iter);
106 static void destination_row_deleted (ENameSelectorEntry *name_selector_entry, GtkTreePath *path);
108 static void user_insert_text (ENameSelectorEntry *name_selector_entry, gchar *new_text, gint new_text_length, gint *position, gpointer user_data);
109 static void user_delete_text (ENameSelectorEntry *name_selector_entry, gint start_pos, gint end_pos, gpointer user_data);
111 static void setup_default_contact_store (ENameSelectorEntry *name_selector_entry);
112 static void deep_free_list (GList *list);
115 name_selector_entry_set_property (GObject *object,
120 switch (property_id) {
122 e_name_selector_entry_set_registry (
123 E_NAME_SELECTOR_ENTRY (object),
124 g_value_get_object (value));
127 case PROP_MINIMUM_QUERY_LENGTH:
128 e_name_selector_entry_set_minimum_query_length (
129 E_NAME_SELECTOR_ENTRY (object),
130 g_value_get_int (value));
133 case PROP_SHOW_ADDRESS:
134 e_name_selector_entry_set_show_address (
135 E_NAME_SELECTOR_ENTRY (object),
136 g_value_get_boolean (value));
140 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
144 name_selector_entry_get_property (GObject *object,
149 switch (property_id) {
153 e_name_selector_entry_get_registry (
154 E_NAME_SELECTOR_ENTRY (object)));
157 case PROP_MINIMUM_QUERY_LENGTH:
160 e_name_selector_entry_get_minimum_query_length (
161 E_NAME_SELECTOR_ENTRY (object)));
164 case PROP_SHOW_ADDRESS:
165 g_value_set_boolean (
167 e_name_selector_entry_get_show_address (
168 E_NAME_SELECTOR_ENTRY (object)));
172 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
176 name_selector_entry_dispose (GObject *object)
178 ENameSelectorEntryPrivate *priv;
180 priv = E_NAME_SELECTOR_ENTRY_GET_PRIVATE (object);
182 if (priv->registry != NULL) {
183 g_object_unref (priv->registry);
184 priv->registry = NULL;
187 if (priv->attr_list != NULL) {
188 pango_attr_list_unref (priv->attr_list);
189 priv->attr_list = NULL;
192 if (priv->entry_completion) {
193 g_object_unref (priv->entry_completion);
194 priv->entry_completion = NULL;
197 if (priv->destination_store) {
198 g_object_unref (priv->destination_store);
199 priv->destination_store = NULL;
202 if (priv->email_generator) {
203 g_object_unref (priv->email_generator);
204 priv->email_generator = NULL;
207 if (priv->contact_store) {
208 g_object_unref (priv->contact_store);
209 priv->contact_store = NULL;
212 g_slist_foreach (priv->user_query_fields, (GFunc) g_free, NULL);
213 g_slist_free (priv->user_query_fields);
214 priv->user_query_fields = NULL;
216 /* Cancel any stuck book loading operations. */
217 while (!g_queue_is_empty (&priv->cancellables)) {
218 GCancellable *cancellable;
220 cancellable = g_queue_pop_head (&priv->cancellables);
221 g_cancellable_cancel (cancellable);
222 g_object_unref (cancellable);
225 /* Chain up to parent's dispose() method. */
226 G_OBJECT_CLASS (e_name_selector_entry_parent_class)->dispose (object);
230 name_selector_entry_constructed (GObject *object)
232 /* Chain up to parent's constructed() method. */
233 G_OBJECT_CLASS (e_name_selector_entry_parent_class)->
234 constructed (object);
236 e_extensible_load_extensions (E_EXTENSIBLE (object));
240 name_selector_entry_realize (GtkWidget *widget)
242 ENameSelectorEntryPrivate *priv;
244 priv = E_NAME_SELECTOR_ENTRY_GET_PRIVATE (widget);
246 /* Chain up to parent's realize() method. */
247 GTK_WIDGET_CLASS (e_name_selector_entry_parent_class)->realize (widget);
249 if (priv->contact_store == NULL)
250 setup_default_contact_store (E_NAME_SELECTOR_ENTRY (widget));
254 name_selector_entry_drag_data_received (GtkWidget *widget,
255 GdkDragContext *context,
258 GtkSelectionData *selection_data,
262 CamelInternetAddress *address;
263 gint n_addresses = 0;
266 address = camel_internet_address_new ();
267 text = (gchar *) gtk_selection_data_get_text (selection_data);
269 /* See if Camel can parse a valid email address from the text. */
270 if (text != NULL && *text != '\0') {
271 camel_url_decode (text);
272 if (g_ascii_strncasecmp (text, "mailto:", 7) == 0)
273 n_addresses = camel_address_decode (
274 CAMEL_ADDRESS (address), text + 7);
276 n_addresses = camel_address_decode (
277 CAMEL_ADDRESS (address), text);
280 if (n_addresses > 0) {
281 GtkEditable *editable;
282 GdkDragAction action;
286 editable = GTK_EDITABLE (widget);
287 gtk_editable_set_position (editable, -1);
288 position = gtk_editable_get_position (editable);
292 text = camel_address_format (CAMEL_ADDRESS (address));
293 gtk_editable_insert_text (editable, text, -1, &position);
295 action = gdk_drag_context_get_selected_action (context);
296 delete = (action == GDK_ACTION_MOVE);
297 gtk_drag_finish (context, TRUE, delete, time);
300 g_object_unref (address);
303 if (n_addresses <= 0)
304 /* Chain up to parent's drag_data_received() method. */
305 GTK_WIDGET_CLASS (e_name_selector_entry_parent_class)->
307 widget, context, x, y,
308 selection_data, info, time);
312 e_name_selector_entry_class_init (ENameSelectorEntryClass *class)
314 GObjectClass *object_class;
315 GtkWidgetClass *widget_class;
317 g_type_class_add_private (class, sizeof (ENameSelectorEntryPrivate));
319 object_class = G_OBJECT_CLASS (class);
320 object_class->set_property = name_selector_entry_set_property;
321 object_class->get_property = name_selector_entry_get_property;
322 object_class->dispose = name_selector_entry_dispose;
323 object_class->constructed = name_selector_entry_constructed;
325 widget_class = GTK_WIDGET_CLASS (class);
326 widget_class->realize = name_selector_entry_realize;
327 widget_class->drag_data_received = name_selector_entry_drag_data_received;
329 g_object_class_install_property (
332 g_param_spec_object (
335 "Data source registry",
336 E_TYPE_SOURCE_REGISTRY,
339 G_PARAM_STATIC_STRINGS));
341 g_object_class_install_property (
343 PROP_MINIMUM_QUERY_LENGTH,
345 "minimum-query-length",
346 "Minimum Query Length",
351 G_PARAM_STATIC_STRINGS));
353 g_object_class_install_property (
356 g_param_spec_boolean (
362 G_PARAM_STATIC_STRINGS));
364 signals[UPDATED] = g_signal_new (
366 E_TYPE_NAME_SELECTOR_ENTRY,
368 G_STRUCT_OFFSET (ENameSelectorEntryClass, updated),
370 g_cclosure_marshal_VOID__POINTER,
371 G_TYPE_NONE, 1, G_TYPE_POINTER);
374 /* Remove unquoted commas and control characters from string */
376 sanitize_string (const gchar *string)
379 gboolean quoted = FALSE;
382 gstring = g_string_new ("");
385 return g_string_free (gstring, FALSE);
387 for (p = string; *p; p = g_utf8_next_char (p)) {
388 gunichar c = g_utf8_get_char (p);
392 else if (c == ',' && !quoted)
394 else if (c == '\t' || c == '\n')
397 g_string_append_unichar (gstring, c);
400 return g_string_free (gstring, FALSE);
403 /* Called for each list store entry whenever the user types (but not on cut/paste) */
405 completion_match_cb (GtkEntryCompletion *completion,
410 ENS_DEBUG (g_print ("completion_match_cb, key=%s\n", key));
415 /* Gets context of n_unichars total (n_unicars / 2, before and after position)
416 * and places them in array. If any positions would be outside the string, the
417 * corresponding unichars are set to zero. */
419 get_utf8_string_context (const gchar *string,
429 /* n_unichars must be even */
430 g_assert (n_unichars % 2 == 0);
432 len = g_utf8_strlen (string, -1);
433 gap = n_unichars / 2;
435 for (i = 0; i < n_unichars; i++) {
436 gint char_pos = position - gap + i;
438 if (char_pos < 0 || char_pos >= len) {
444 p = g_utf8_next_char (p);
446 p = g_utf8_offset_to_pointer (string, char_pos);
448 unichars[i] = g_utf8_get_char (p);
453 get_range_at_position (const gchar *string,
459 gboolean quoted = FALSE;
460 gint local_start_pos = 0;
461 gint local_end_pos = 0;
464 if (!string || !*string)
467 for (p = string, i = 0; *p; p = g_utf8_next_char (p), i++) {
468 gunichar c = g_utf8_get_char (p);
472 } else if (c == ',' && !quoted) {
474 /* Start right after comma */
475 local_start_pos = i + 1;
477 /* Stop right before comma */
481 } else if (c == ' ' && local_start_pos == i) {
482 /* Adjust start to skip space after first comma */
487 /* If we didn't hit a comma, we must've hit NULL, and ours was the last element. */
492 *start_pos = local_start_pos;
494 *end_pos = local_end_pos;
500 is_quoted_at (const gchar *string,
504 gboolean quoted = FALSE;
507 for (p = string, i = 0; *p && i < pos; p = g_utf8_next_char (p), i++) {
508 gunichar c = g_utf8_get_char (p);
514 return quoted ? TRUE : FALSE;
518 get_index_at_position (const gchar *string,
522 gboolean quoted = FALSE;
526 for (p = string, i = 0; *p && i < pos; p = g_utf8_next_char (p), i++) {
527 gunichar c = g_utf8_get_char (p);
531 else if (c == ',' && !quoted)
539 get_range_by_index (const gchar *string,
545 gboolean quoted = FALSE;
549 for (p = string, i = 0; *p && n < index; p = g_utf8_next_char (p), i++) {
550 gunichar c = g_utf8_get_char (p);
554 if (c == ',' && !quoted)
561 return get_range_at_position (string, i, start_pos, end_pos);
565 get_address_at_position (const gchar *string,
570 const gchar *start_p;
573 if (!get_range_at_position (string, pos, &start_pos, &end_pos))
576 start_p = g_utf8_offset_to_pointer (string, start_pos);
577 end_p = g_utf8_offset_to_pointer (string, end_pos);
579 return g_strndup (start_p, end_p - start_p);
582 /* Finds the destination in model */
583 static EDestination *
584 find_destination_by_index (ENameSelectorEntry *name_selector_entry,
590 path = gtk_tree_path_new_from_indices (index, -1);
591 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (name_selector_entry->priv->destination_store),
593 /* If we have zero destinations, getting a NULL destination at index 0
596 g_warning ("ENameSelectorEntry is out of sync with model!");
597 gtk_tree_path_free (path);
600 gtk_tree_path_free (path);
602 return e_destination_store_get_destination (name_selector_entry->priv->destination_store, &iter);
605 /* Finds the destination in model */
606 static EDestination *
607 find_destination_at_position (ENameSelectorEntry *name_selector_entry,
613 text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
614 index = get_index_at_position (text, pos);
616 return find_destination_by_index (name_selector_entry, index);
619 /* Builds destination from our text */
620 static EDestination *
621 build_destination_at_position (const gchar *string,
624 EDestination *destination;
627 address = get_address_at_position (string, pos);
631 destination = e_destination_new ();
632 e_destination_set_raw (destination, address);
639 name_style_query (const gchar *field,
644 GString *out = g_string_new ("");
648 spaced_str = sanitize_string (value);
649 g_strstrip (spaced_str);
651 strv = g_strsplit (spaced_str, " ", 0);
653 if (strv[0] && strv[1]) {
654 g_string_append (out, "(or ");
655 comma_str = g_strjoinv (", ", strv);
660 g_string_append (out, " (beginswith ");
661 e_sexp_encode_string (out, field);
662 e_sexp_encode_string (out, spaced_str);
663 g_string_append (out, ")");
666 g_string_append (out, " (beginswith ");
668 e_sexp_encode_string (out, field);
669 g_strstrip (comma_str);
670 e_sexp_encode_string (out, comma_str);
671 g_string_append (out, "))");
674 query = g_string_free (out, FALSE);
684 escape_sexp_string (const gchar *string)
687 gchar *encoded_string;
689 gstring = g_string_new ("");
690 e_sexp_encode_string (gstring, string);
692 encoded_string = gstring->str;
693 g_string_free (gstring, FALSE);
695 return encoded_string;
699 * ens_util_populate_user_query_fields:
701 * Populates list of user query fields to string usable in query string.
702 * Returned pointer is either newly allocated string, supposed to be freed with g_free,
703 * or NULL if no fields defined.
708 ens_util_populate_user_query_fields (GSList *user_query_fields,
709 const gchar *cue_str,
710 const gchar *encoded_cue_str)
712 GString *user_fields;
715 g_return_val_if_fail (cue_str != NULL, NULL);
716 g_return_val_if_fail (encoded_cue_str != NULL, NULL);
718 user_fields = g_string_new ("");
720 for (s = user_query_fields; s; s = s->next) {
721 const gchar *field = s->data;
723 if (!field || !*field)
727 g_string_append_printf (user_fields, " (beginswith \"%s\" %s) ", field + 1, encoded_cue_str);
728 } else if (*field == '@') {
729 g_string_append_printf (user_fields, " (is \"%s\" %s) ", field + 1, encoded_cue_str);
731 gchar *tmp = name_style_query (field, cue_str);
733 g_string_append (user_fields, " ");
734 g_string_append (user_fields, tmp);
735 g_string_append (user_fields, " ");
740 return g_string_free (user_fields, !user_fields->str || !*user_fields->str);
744 set_completion_query (ENameSelectorEntry *name_selector_entry,
745 const gchar *cue_str)
747 ENameSelectorEntryPrivate *priv;
748 EBookQuery *book_query;
750 gchar *encoded_cue_str;
751 gchar *full_name_query_str;
752 gchar *file_as_query_str;
753 gchar *user_fields_str;
755 priv = E_NAME_SELECTOR_ENTRY_GET_PRIVATE (name_selector_entry);
757 if (!name_selector_entry->priv->contact_store)
761 /* Clear the store */
762 e_contact_store_set_query (name_selector_entry->priv->contact_store, NULL);
766 encoded_cue_str = escape_sexp_string (cue_str);
767 full_name_query_str = name_style_query ("full_name", cue_str);
768 file_as_query_str = name_style_query ("file_as", cue_str);
769 user_fields_str = ens_util_populate_user_query_fields (priv->user_query_fields, cue_str, encoded_cue_str);
771 query_str = g_strdup_printf (
773 " (beginswith \"nickname\" %s) "
774 " (beginswith \"email\" %s) "
779 encoded_cue_str, encoded_cue_str,
780 full_name_query_str, file_as_query_str,
781 user_fields_str ? user_fields_str : "");
783 g_free (user_fields_str);
784 g_free (file_as_query_str);
785 g_free (full_name_query_str);
786 g_free (encoded_cue_str);
788 ENS_DEBUG (g_print ("%s\n", query_str));
790 book_query = e_book_query_from_string (query_str);
791 e_contact_store_set_query (name_selector_entry->priv->contact_store, book_query);
792 e_book_query_unref (book_query);
798 get_entry_substring (ENameSelectorEntry *name_selector_entry,
802 const gchar *entry_text;
805 entry_text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
807 p0 = g_utf8_offset_to_pointer (entry_text, range_start);
808 p1 = g_utf8_offset_to_pointer (entry_text, range_end);
810 return g_strndup (p0, p1 - p0);
814 utf8_casefold_collate_len (const gchar *str1,
818 gchar *s1 = g_utf8_casefold (str1, len);
819 gchar *s2 = g_utf8_casefold (str2, len);
822 rv = g_utf8_collate (s1, s2);
831 build_textrep_for_contact (EContact *contact,
832 EContactField cue_field)
839 case E_CONTACT_FULL_NAME:
840 case E_CONTACT_NICKNAME:
841 case E_CONTACT_FILE_AS:
842 name = e_contact_get (contact, cue_field);
843 email = e_contact_get (contact, E_CONTACT_EMAIL_1);
846 case E_CONTACT_EMAIL_1:
847 case E_CONTACT_EMAIL_2:
848 case E_CONTACT_EMAIL_3:
849 case E_CONTACT_EMAIL_4:
851 email = e_contact_get (contact, cue_field);
855 g_assert_not_reached ();
860 g_assert (strlen (email) > 0);
863 textrep = g_strdup_printf ("%s <%s>", name, email);
865 textrep = g_strdup_printf ("%s", email);
873 contact_match_cue (ENameSelectorEntry *name_selector_entry,
875 const gchar *cue_str,
876 EContactField *matched_field,
877 gint *matched_field_rank)
879 EContactField fields[] = { E_CONTACT_FULL_NAME, E_CONTACT_NICKNAME, E_CONTACT_FILE_AS,
880 E_CONTACT_EMAIL_1, E_CONTACT_EMAIL_2, E_CONTACT_EMAIL_3,
883 gboolean result = FALSE;
890 if (g_utf8_strlen (cue_str, -1) < name_selector_entry->priv->minimum_query_length)
893 cue_len = strlen (cue_str);
895 /* Make sure contact has an email address */
896 email = e_contact_get (contact, E_CONTACT_EMAIL_1);
897 if (!email || !*email) {
903 for (i = 0; i < G_N_ELEMENTS (fields); i++) {
907 /* Don't match e-mail addresses in contact lists */
908 if (e_contact_get (contact, E_CONTACT_IS_LIST) &&
909 fields[i] >= E_CONTACT_FIRST_EMAIL_ID &&
910 fields[i] <= E_CONTACT_LAST_EMAIL_ID)
913 value = e_contact_get (contact, fields[i]);
917 value_sane = sanitize_string (value);
920 ENS_DEBUG (g_print ("Comparing '%s' to '%s'\n", value, cue_str));
922 if (!utf8_casefold_collate_len (value_sane, cue_str, cue_len)) {
924 *matched_field = fields [i];
925 if (matched_field_rank)
926 *matched_field_rank = i;
939 find_existing_completion (ENameSelectorEntry *name_selector_entry,
940 const gchar *cue_str,
943 EContactField *matched_field,
944 EBookClient **book_client)
947 EContact *best_contact = NULL;
948 gint best_field_rank = G_MAXINT;
949 EContactField best_field = 0;
950 EBookClient *best_book_client = NULL;
954 if (!name_selector_entry->priv->contact_store)
957 ENS_DEBUG (g_print ("Completing '%s'\n", cue_str));
959 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (name_selector_entry->priv->contact_store), &iter))
963 EContact *current_contact;
964 gint current_field_rank;
965 EContactField current_field;
968 current_contact = e_contact_store_get_contact (name_selector_entry->priv->contact_store, &iter);
969 if (!current_contact)
972 matches = contact_match_cue (name_selector_entry, current_contact, cue_str, ¤t_field, ¤t_field_rank);
973 if (matches && current_field_rank < best_field_rank) {
974 best_contact = current_contact;
975 best_field_rank = current_field_rank;
976 best_field = current_field;
977 best_book_client = e_contact_store_get_client (name_selector_entry->priv->contact_store, &iter);
979 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (name_selector_entry->priv->contact_store), &iter));
985 *contact = best_contact;
987 *text = build_textrep_for_contact (best_contact, best_field);
989 *matched_field = best_field;
991 *book_client = best_book_client;
997 generate_attribute_list (ENameSelectorEntry *name_selector_entry)
1000 PangoAttrList *attr_list;
1004 text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1005 layout = gtk_entry_get_layout (GTK_ENTRY (name_selector_entry));
1007 /* Set up the attribute list */
1009 attr_list = pango_attr_list_new ();
1011 if (name_selector_entry->priv->attr_list)
1012 pango_attr_list_unref (name_selector_entry->priv->attr_list);
1014 name_selector_entry->priv->attr_list = attr_list;
1016 /* Parse the entry's text and apply attributes to real contacts */
1018 for (i = 0; ; i++) {
1019 EDestination *destination;
1020 PangoAttribute *attr;
1024 if (!get_range_by_index (text, i, &start_pos, &end_pos))
1027 destination = find_destination_at_position (name_selector_entry, start_pos);
1029 /* Destination will be NULL if we have no entries */
1030 if (!destination || !e_destination_get_contact (destination))
1033 attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
1034 attr->start_index = g_utf8_offset_to_pointer (text, start_pos) - text;
1035 attr->end_index = g_utf8_offset_to_pointer (text, end_pos) - text;
1036 pango_attr_list_insert (attr_list, attr);
1039 pango_layout_set_attributes (layout, attr_list);
1043 draw_event (ENameSelectorEntry *name_selector_entry)
1045 PangoLayout *layout;
1047 layout = gtk_entry_get_layout (GTK_ENTRY (name_selector_entry));
1048 pango_layout_set_attributes (layout, name_selector_entry->priv->attr_list);
1054 type_ahead_complete (ENameSelectorEntry *name_selector_entry)
1057 EBookClient *book_client = NULL;
1058 EContactField matched_field;
1059 EDestination *destination;
1061 gint range_start = 0;
1070 ENameSelectorEntryPrivate *priv;
1072 priv = E_NAME_SELECTOR_ENTRY_GET_PRIVATE (name_selector_entry);
1074 cursor_pos = gtk_editable_get_position (GTK_EDITABLE (name_selector_entry));
1078 text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1079 get_range_at_position (text, cursor_pos, &range_start, &range_end);
1080 range_len = range_end - range_start;
1081 if (range_len < priv->minimum_query_length)
1084 destination = find_destination_at_position (name_selector_entry, cursor_pos);
1086 cue_str = get_entry_substring (name_selector_entry, range_start, range_end);
1087 if (!find_existing_completion (name_selector_entry, cue_str, &contact,
1088 &textrep, &matched_field, &book_client)) {
1093 temp_str = sanitize_string (textrep);
1097 textrep_len = g_utf8_strlen (textrep, -1);
1100 g_signal_handlers_block_by_func (
1101 name_selector_entry,
1102 user_insert_text, name_selector_entry);
1103 g_signal_handlers_block_by_func (
1104 name_selector_entry,
1105 user_delete_text, name_selector_entry);
1106 g_signal_handlers_block_by_func (
1107 name_selector_entry->priv->destination_store,
1108 destination_row_changed, name_selector_entry);
1110 if (textrep_len > range_len) {
1113 /* keep character's case as user types */
1114 for (i = 0; textrep[i] && cue_str[i]; i++)
1115 textrep[i] = cue_str[i];
1117 gtk_editable_delete_text (
1118 GTK_EDITABLE (name_selector_entry),
1119 range_start, range_end);
1120 gtk_editable_insert_text (
1121 GTK_EDITABLE (name_selector_entry),
1123 gtk_editable_select_region (
1124 GTK_EDITABLE (name_selector_entry),
1125 range_end, range_start + textrep_len);
1126 priv->is_completing = TRUE;
1130 if (contact && destination) {
1133 if (matched_field >= E_CONTACT_FIRST_EMAIL_ID && matched_field <= E_CONTACT_LAST_EMAIL_ID)
1134 email_n = matched_field - E_CONTACT_FIRST_EMAIL_ID;
1136 e_destination_set_contact (destination, contact, email_n);
1138 e_destination_set_client (destination, book_client);
1139 generate_attribute_list (name_selector_entry);
1142 g_signal_handlers_unblock_by_func (
1143 name_selector_entry->priv->destination_store,
1144 destination_row_changed, name_selector_entry);
1145 g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1146 g_signal_handlers_unblock_by_func (name_selector_entry, user_insert_text, name_selector_entry);
1152 clear_completion_model (ENameSelectorEntry *name_selector_entry)
1154 ENameSelectorEntryPrivate *priv;
1156 priv = E_NAME_SELECTOR_ENTRY_GET_PRIVATE (name_selector_entry);
1158 if (!name_selector_entry->priv->contact_store)
1161 e_contact_store_set_query (name_selector_entry->priv->contact_store, NULL);
1162 priv->is_completing = FALSE;
1166 update_completion_model (ENameSelectorEntry *name_selector_entry)
1170 gint range_start = 0;
1173 text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1174 cursor_pos = gtk_editable_get_position (GTK_EDITABLE (name_selector_entry));
1176 if (cursor_pos >= 0)
1177 get_range_at_position (text, cursor_pos, &range_start, &range_end);
1179 if (range_end - range_start >= name_selector_entry->priv->minimum_query_length && cursor_pos == range_end) {
1182 cue_str = get_entry_substring (name_selector_entry, range_start, range_end);
1183 set_completion_query (name_selector_entry, cue_str);
1186 /* N/A; Clear completion model */
1187 clear_completion_model (name_selector_entry);
1192 type_ahead_complete_on_timeout_cb (ENameSelectorEntry *name_selector_entry)
1194 type_ahead_complete (name_selector_entry);
1195 name_selector_entry->priv->type_ahead_complete_cb_id = 0;
1200 update_completions_on_timeout_cb (ENameSelectorEntry *name_selector_entry)
1202 update_completion_model (name_selector_entry);
1203 name_selector_entry->priv->update_completions_cb_id = 0;
1208 insert_destination_at_position (ENameSelectorEntry *name_selector_entry,
1211 EDestination *destination;
1215 text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1216 index = get_index_at_position (text, pos);
1218 destination = build_destination_at_position (text, pos);
1219 g_assert (destination);
1221 g_signal_handlers_block_by_func (
1222 name_selector_entry->priv->destination_store,
1223 destination_row_inserted, name_selector_entry);
1224 e_destination_store_insert_destination (
1225 name_selector_entry->priv->destination_store,
1226 index, destination);
1227 g_signal_handlers_unblock_by_func (
1228 name_selector_entry->priv->destination_store,
1229 destination_row_inserted, name_selector_entry);
1230 g_object_unref (destination);
1234 modify_destination_at_position (ENameSelectorEntry *name_selector_entry,
1237 EDestination *destination;
1240 gboolean rebuild_attributes = FALSE;
1242 destination = find_destination_at_position (name_selector_entry, pos);
1246 text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1247 raw_address = get_address_at_position (text, pos);
1248 g_assert (raw_address);
1250 if (e_destination_get_contact (destination))
1251 rebuild_attributes = TRUE;
1253 g_signal_handlers_block_by_func (
1254 name_selector_entry->priv->destination_store,
1255 destination_row_changed, name_selector_entry);
1256 e_destination_set_raw (destination, raw_address);
1257 g_signal_handlers_unblock_by_func (
1258 name_selector_entry->priv->destination_store,
1259 destination_row_changed, name_selector_entry);
1261 g_free (raw_address);
1263 if (rebuild_attributes)
1264 generate_attribute_list (name_selector_entry);
1268 get_destination_textrep (ENameSelectorEntry *name_selector_entry,
1269 EDestination *destination)
1271 gboolean show_email = e_name_selector_entry_get_show_address (name_selector_entry);
1274 g_return_val_if_fail (destination != NULL, NULL);
1276 contact = e_destination_get_contact (destination);
1279 if (contact && !e_contact_get (contact, E_CONTACT_IS_LIST)) {
1282 email_list = e_contact_get (contact, E_CONTACT_EMAIL);
1283 show_email = g_list_length (email_list) > 1;
1284 deep_free_list (email_list);
1288 /* do not show emails for contact lists even user forces it */
1289 if (show_email && contact && e_contact_get (contact, E_CONTACT_IS_LIST))
1292 return sanitize_string (e_destination_get_textrep (destination, show_email));
1296 sync_destination_at_position (ENameSelectorEntry *name_selector_entry,
1300 EDestination *destination;
1304 gint range_start, range_end;
1306 /* Get the destination we're looking at. Note that the entry may be empty, and so
1307 * there may not be one. */
1308 destination = find_destination_at_position (name_selector_entry, range_pos);
1312 text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1313 if (!get_range_at_position (text, range_pos, &range_start, &range_end)) {
1314 g_warning ("ENameSelectorEntry is out of sync with model!");
1318 address = get_destination_textrep (name_selector_entry, destination);
1319 address_len = g_utf8_strlen (address, -1);
1322 /* Update cursor placement */
1323 if (*cursor_pos >= range_end)
1324 *cursor_pos += address_len - (range_end - range_start);
1325 else if (*cursor_pos > range_start)
1326 *cursor_pos = range_start + address_len;
1329 g_signal_handlers_block_by_func (name_selector_entry, user_insert_text, name_selector_entry);
1330 g_signal_handlers_block_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1332 gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry), range_start, range_end);
1333 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), address, -1, &range_start);
1335 g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1336 g_signal_handlers_unblock_by_func (name_selector_entry, user_insert_text, name_selector_entry);
1338 generate_attribute_list (name_selector_entry);
1343 remove_destination_by_index (ENameSelectorEntry *name_selector_entry,
1346 EDestination *destination;
1348 destination = find_destination_by_index (name_selector_entry, index);
1350 g_signal_handlers_block_by_func (
1351 name_selector_entry->priv->destination_store,
1352 destination_row_deleted, name_selector_entry);
1353 e_destination_store_remove_destination (
1354 name_selector_entry->priv->destination_store,
1356 g_signal_handlers_unblock_by_func (
1357 name_selector_entry->priv->destination_store,
1358 destination_row_deleted, name_selector_entry);
1363 post_insert_update (ENameSelectorEntry *name_selector_entry,
1369 text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1370 length = g_utf8_strlen (text, -1);
1371 text = g_utf8_next_char (text);
1373 if (*text == '\0') {
1374 /* First and only character, create initial destination. */
1375 insert_destination_at_position (name_selector_entry, 0);
1377 /* Modified an existing destination. */
1378 modify_destination_at_position (name_selector_entry, position);
1381 /* If editing within the string, regenerate attributes. */
1382 if (position < length)
1383 generate_attribute_list (name_selector_entry);
1386 /* Returns the number of characters inserted */
1388 insert_unichar (ENameSelectorEntry *name_selector_entry,
1393 gunichar str_context[4];
1397 text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1398 get_utf8_string_context (text, *pos, str_context, 4);
1400 /* Space is not allowed:
1401 * - Before or after another space.
1402 * - At start of string. */
1404 if (c == ' ' && (str_context[1] == ' ' || str_context[1] == '\0' || str_context[2] == ' '))
1407 /* Comma is not allowed:
1408 * - After another comma.
1409 * - At start of string. */
1411 if (c == ',' && !is_quoted_at (text, *pos)) {
1414 gboolean at_start = FALSE;
1415 gboolean at_end = FALSE;
1417 if (str_context[1] == ',' || str_context[1] == '\0')
1420 /* We do this so we can avoid disturbing destinations with completed contacts
1421 * either before or after the destination being inserted. */
1422 get_range_at_position (text, *pos, &start_pos, &end_pos);
1423 if (*pos <= start_pos)
1425 if (*pos >= end_pos)
1428 /* Must insert comma first, so modify_destination_at_position can do its job
1429 * correctly, splitting up the contact if necessary. */
1430 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), ", ", -1, pos);
1433 g_assert (*pos >= 2);
1435 /* If we inserted the comma at the end of, or in the middle of, an existing
1436 * address, add a new destination for what appears after comma. Else, we
1437 * have to add a destination for what appears before comma (a blank one). */
1439 /* End: Add last, sync first */
1440 insert_destination_at_position (name_selector_entry, *pos);
1441 sync_destination_at_position (name_selector_entry, *pos - 2, pos);
1442 /* Sync generates the attributes list */
1443 } else if (at_start) {
1444 /* Start: Add first */
1445 insert_destination_at_position (name_selector_entry, *pos - 2);
1446 generate_attribute_list (name_selector_entry);
1449 insert_destination_at_position (name_selector_entry, *pos);
1450 modify_destination_at_position (name_selector_entry, *pos - 2);
1451 generate_attribute_list (name_selector_entry);
1457 /* Generic case. Allowed spaces also end up here. */
1459 len = g_unichar_to_utf8 (c, buf);
1462 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), buf, -1, pos);
1464 post_insert_update (name_selector_entry, *pos);
1470 user_insert_text (ENameSelectorEntry *name_selector_entry,
1472 gint new_text_length,
1476 gint chars_inserted = 0;
1477 gboolean fast_insert;
1479 g_signal_handlers_block_by_func (name_selector_entry, user_insert_text, name_selector_entry);
1480 g_signal_handlers_block_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1483 (g_utf8_strchr (new_text, new_text_length, ' ') == NULL) &&
1484 (g_utf8_strchr (new_text, new_text_length, ',') == NULL);
1486 /* If the text to insert does not contain spaces or commas,
1487 * insert all of it at once. This avoids confusing on-going
1488 * input method behavior. */
1490 gint old_position = *position;
1492 gtk_editable_insert_text (
1493 GTK_EDITABLE (name_selector_entry),
1494 new_text, new_text_length, position);
1496 chars_inserted = *position - old_position;
1497 if (chars_inserted > 0)
1498 post_insert_update (name_selector_entry, *position);
1500 /* Otherwise, apply some rules as to where spaces and commas
1501 * can be inserted, and insert a trailing space after comma. */
1505 for (cp = new_text; *cp; cp = g_utf8_next_char (cp)) {
1506 gunichar uc = g_utf8_get_char (cp);
1507 insert_unichar (name_selector_entry, position, uc);
1512 if (chars_inserted >= 1) {
1513 /* If the user inserted one character, kick off completion */
1514 re_set_timeout (name_selector_entry->priv->update_completions_cb_id, update_completions_on_timeout_cb, name_selector_entry);
1515 re_set_timeout (name_selector_entry->priv->type_ahead_complete_cb_id, type_ahead_complete_on_timeout_cb, name_selector_entry);
1518 g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1519 g_signal_handlers_unblock_by_func (name_selector_entry, user_insert_text, name_selector_entry);
1521 g_signal_stop_emission_by_name (name_selector_entry, "insert_text");
1525 user_delete_text (ENameSelectorEntry *name_selector_entry,
1531 gint index_start, index_end;
1532 gint selection_start, selection_end;
1533 gunichar str_context[2], str_b_context[2];
1536 gboolean del_space = FALSE, del_comma = FALSE;
1538 if (start_pos == end_pos)
1541 text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1542 len = g_utf8_strlen (text, -1);
1547 gtk_editable_get_selection_bounds (
1548 GTK_EDITABLE (name_selector_entry),
1549 &selection_start, &selection_end);
1551 get_utf8_string_context (text, start_pos, str_context, 2);
1552 get_utf8_string_context (text, end_pos, str_b_context, 2);
1554 g_signal_handlers_block_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1556 if (end_pos - start_pos == 1) {
1557 /* Might be backspace; update completion model so dropdown is accurate */
1558 re_set_timeout (name_selector_entry->priv->update_completions_cb_id, update_completions_on_timeout_cb, name_selector_entry);
1561 index_start = get_index_at_position (text, start_pos);
1562 index_end = get_index_at_position (text, end_pos);
1564 g_signal_stop_emission_by_name (name_selector_entry, "delete_text");
1566 /* If the deletion touches more than one destination, the first one is changed
1567 * and the rest are removed. If the last destination wasn't completely deleted,
1568 * it becomes part of the first one, since the separator between them was
1571 * Here, we let the model know about removals. */
1572 for (i = index_end; i > index_start; i--) {
1573 EDestination *destination = find_destination_by_index (name_selector_entry, i);
1574 gint range_start, range_end;
1576 const gchar *email = NULL;
1577 gboolean sel = FALSE;
1580 email = e_destination_get_textrep (destination, TRUE);
1582 if (!email || !*email)
1585 if (!get_range_by_index (text, i, &range_start, &range_end)) {
1586 g_warning ("ENameSelectorEntry is out of sync with model!");
1590 if ((selection_start < range_start && selection_end > range_start) ||
1591 (selection_end > range_start && selection_end < range_end))
1595 g_signal_handlers_block_by_func (name_selector_entry, user_insert_text, name_selector_entry);
1596 g_signal_handlers_block_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1598 gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry), range_start, range_end);
1600 ttext = sanitize_string (email);
1601 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), ttext, -1, &range_start);
1604 g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1605 g_signal_handlers_unblock_by_func (name_selector_entry, user_insert_text, name_selector_entry);
1609 remove_destination_by_index (name_selector_entry, i);
1612 /* Do the actual deletion */
1614 if (end_pos == start_pos +1 && index_end == index_start) {
1615 /* We could be just deleting the empty text */
1618 /* Get the actual deleted text */
1619 c = gtk_editable_get_chars (GTK_EDITABLE (name_selector_entry), start_pos, start_pos + 1);
1622 /* If we are at the beginning or removing junk space, let us ignore it */
1626 } else if (end_pos == start_pos +1 && index_end == index_start + 1) {
1627 /* We could be just deleting the empty text */
1630 /* Get the actual deleted text */
1631 c = gtk_editable_get_chars (GTK_EDITABLE (name_selector_entry), start_pos, start_pos + 1);
1633 if ( c[0] == ',' && !is_quoted_at (text, start_pos)) {
1634 /* If we are at the beginning or removing junk space, let us ignore it */
1641 gint range_start=-1, range_end;
1642 EDestination *dest = find_destination_by_index (name_selector_entry, index_end);
1643 /* If we have deleted the last comma, let us autocomplete normally
1646 if (dest && len - end_pos != 0) {
1648 EDestination *destination1 = find_destination_by_index (name_selector_entry, index_start);
1650 const gchar *email = NULL;
1653 email = e_destination_get_textrep (destination1, TRUE);
1655 if (email && *email) {
1657 if (!get_range_by_index (text, i, &range_start, &range_end)) {
1658 g_warning ("ENameSelectorEntry is out of sync with model!");
1662 g_signal_handlers_block_by_func (name_selector_entry, user_insert_text, name_selector_entry);
1663 g_signal_handlers_block_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1665 gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry), range_start, range_end);
1667 ttext = sanitize_string (email);
1668 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), ttext, -1, &range_start);
1671 g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1672 g_signal_handlers_unblock_by_func (name_selector_entry, user_insert_text, name_selector_entry);
1675 if (range_start != -1) {
1676 start_pos = range_start;
1677 end_pos = start_pos + 1;
1678 gtk_editable_set_position (GTK_EDITABLE (name_selector_entry),start_pos);
1682 gtk_editable_delete_text (
1683 GTK_EDITABLE (name_selector_entry),
1684 start_pos, end_pos);
1686 /*If the user is deleting a '"' new destinations have to be created for ',' between the quoted text
1687 Like "fd,ty,uy" is a one entity, but if you remove the quotes it has to be broken doan into 3 seperate
1691 if (str_b_context[1] == '"') {
1695 for (p = text + (end_pos - 1), j = end_pos - 1; *p && *p != '"' ; p = g_utf8_next_char (p), j++) {
1696 gunichar c = g_utf8_get_char (p);
1698 insert_destination_at_position (name_selector_entry, j + 1);
1704 /* Let model know about changes */
1705 text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1706 if (!*text || strlen (text) <= 0) {
1707 /* If the entry was completely cleared, remove the initial destination too */
1708 remove_destination_by_index (name_selector_entry, 0);
1709 generate_attribute_list (name_selector_entry);
1710 } else if (!del_space) {
1711 modify_destination_at_position (name_selector_entry, start_pos);
1714 /* If editing within the string, we need to regenerate attributes */
1716 generate_attribute_list (name_selector_entry);
1718 /* Prevent type-ahead completion */
1719 if (name_selector_entry->priv->type_ahead_complete_cb_id) {
1720 g_source_remove (name_selector_entry->priv->type_ahead_complete_cb_id);
1721 name_selector_entry->priv->type_ahead_complete_cb_id = 0;
1724 g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1728 completion_match_selected (ENameSelectorEntry *name_selector_entry,
1729 ETreeModelGenerator *email_generator_model,
1730 GtkTreeIter *generator_iter)
1733 EBookClient *book_client;
1734 EDestination *destination;
1736 GtkTreeIter contact_iter;
1739 if (!name_selector_entry->priv->contact_store)
1742 g_return_val_if_fail (name_selector_entry->priv->email_generator == email_generator_model, FALSE);
1744 e_tree_model_generator_convert_iter_to_child_iter (
1745 email_generator_model,
1746 &contact_iter, &email_n,
1749 contact = e_contact_store_get_contact (name_selector_entry->priv->contact_store, &contact_iter);
1750 book_client = e_contact_store_get_client (name_selector_entry->priv->contact_store, &contact_iter);
1751 cursor_pos = gtk_editable_get_position (GTK_EDITABLE (name_selector_entry));
1753 /* Set the contact in the model's destination */
1755 destination = find_destination_at_position (name_selector_entry, cursor_pos);
1756 e_destination_set_contact (destination, contact, email_n);
1758 e_destination_set_client (destination, book_client);
1759 sync_destination_at_position (name_selector_entry, cursor_pos, &cursor_pos);
1761 g_signal_handlers_block_by_func (name_selector_entry, user_insert_text, name_selector_entry);
1762 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), ", ", -1, &cursor_pos);
1763 g_signal_handlers_unblock_by_func (name_selector_entry, user_insert_text, name_selector_entry);
1765 /*Add destination at end for next entry*/
1766 insert_destination_at_position (name_selector_entry, cursor_pos);
1767 /* Place cursor at end of address */
1769 gtk_editable_set_position (GTK_EDITABLE (name_selector_entry), cursor_pos);
1770 g_signal_emit (name_selector_entry, signals[UPDATED], 0, destination, NULL);
1775 entry_activate (ENameSelectorEntry *name_selector_entry)
1778 gint range_start, range_end;
1779 ENameSelectorEntryPrivate *priv;
1780 EDestination *destination;
1785 cursor_pos = gtk_editable_get_position (GTK_EDITABLE (name_selector_entry));
1789 priv = E_NAME_SELECTOR_ENTRY_GET_PRIVATE (name_selector_entry);
1791 text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1792 if (!get_range_at_position (text, cursor_pos, &range_start, &range_end))
1795 range_len = range_end - range_start;
1796 if (range_len < priv->minimum_query_length)
1799 destination = find_destination_at_position (name_selector_entry, cursor_pos);
1803 cue_str = get_entry_substring (name_selector_entry, range_start, range_end);
1805 if (!find_existing_completion (name_selector_entry, cue_str, &contact,
1806 &textrep, &matched_field)) {
1812 sync_destination_at_position (name_selector_entry, cursor_pos, &cursor_pos);
1814 /* Place cursor at end of address */
1815 text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1816 get_range_at_position (text, cursor_pos, &range_start, &range_end);
1818 if (priv->is_completing) {
1819 gchar *str_context = NULL;
1821 str_context = gtk_editable_get_chars (GTK_EDITABLE (name_selector_entry), range_end, range_end + 1);
1823 if (str_context[0] != ',') {
1825 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), ", ", -1, &range_end);
1828 gint newpos = strlen (text);
1830 /* Doing this we can make sure that It wont ask for completion again. */
1831 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), ", ", -1, &newpos);
1832 g_signal_handlers_block_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1833 gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry), newpos - 2, newpos);
1834 g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1836 /* Move it close to next destination*/
1837 range_end = range_end + 2;
1840 g_free (str_context);
1843 gtk_editable_set_position (GTK_EDITABLE (name_selector_entry), range_end);
1844 g_signal_emit (name_selector_entry, signals[UPDATED], 0, destination, NULL);
1846 if (priv->is_completing)
1847 clear_completion_model (name_selector_entry);
1851 update_text (ENameSelectorEntry *name_selector_entry,
1854 gint start = 0, end = 0;
1855 gboolean has_selection;
1857 has_selection = gtk_editable_get_selection_bounds (GTK_EDITABLE (name_selector_entry), &start, &end);
1859 gtk_entry_set_text (GTK_ENTRY (name_selector_entry), text);
1862 gtk_editable_select_region (GTK_EDITABLE (name_selector_entry), start, end);
1866 sanitize_entry (ENameSelectorEntry *name_selector_entry)
1869 GList *l, *known, *del = NULL;
1870 GString *str = g_string_new ("");
1872 g_signal_handlers_block_matched (name_selector_entry, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, name_selector_entry);
1873 g_signal_handlers_block_matched (name_selector_entry->priv->destination_store, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, name_selector_entry);
1875 known = e_destination_store_list_destinations (name_selector_entry->priv->destination_store);
1876 for (l = known, n = 0; l != NULL; l = l->next, n++) {
1877 EDestination *dest = l->data;
1879 if (!dest || !e_destination_get_address (dest))
1880 del = g_list_prepend (del, GINT_TO_POINTER (n));
1884 text = get_destination_textrep (name_selector_entry, dest);
1886 if (str->str && str->str[0])
1887 g_string_append (str, ", ");
1889 g_string_append (str, text);
1894 g_list_free (known);
1896 for (l = del; l != NULL; l = l->next) {
1897 e_destination_store_remove_destination_nth (name_selector_entry->priv->destination_store, GPOINTER_TO_INT (l->data));
1901 update_text (name_selector_entry, str->str);
1903 g_string_free (str, TRUE);
1905 g_signal_handlers_unblock_matched (name_selector_entry->priv->destination_store, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, name_selector_entry);
1906 g_signal_handlers_unblock_matched (name_selector_entry, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, name_selector_entry);
1908 generate_attribute_list (name_selector_entry);
1912 user_focus_in (ENameSelectorEntry *name_selector_entry,
1913 GdkEventFocus *event_focus)
1917 GString *str = g_string_new ("");
1918 EDestination *dest_dummy = e_destination_new ();
1920 g_signal_handlers_block_matched (name_selector_entry, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, name_selector_entry);
1921 g_signal_handlers_block_matched (name_selector_entry->priv->destination_store, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, name_selector_entry);
1923 known = e_destination_store_list_destinations (name_selector_entry->priv->destination_store);
1924 for (l = known, n = 0; l != NULL; l = l->next, n++) {
1925 EDestination *dest = l->data;
1930 text = get_destination_textrep (name_selector_entry, dest);
1932 if (str->str && str->str[0])
1933 g_string_append (str, ", ");
1935 g_string_append (str, text);
1940 g_list_free (known);
1942 /* Add a blank destination */
1943 e_destination_store_append_destination (name_selector_entry->priv->destination_store, dest_dummy);
1944 if (str->str && str->str[0])
1945 g_string_append (str, ", ");
1947 gtk_entry_set_text (GTK_ENTRY (name_selector_entry), str->str);
1949 g_string_free (str, TRUE);
1951 g_signal_handlers_unblock_matched (name_selector_entry->priv->destination_store, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, name_selector_entry);
1952 g_signal_handlers_unblock_matched (name_selector_entry, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, name_selector_entry);
1954 generate_attribute_list (name_selector_entry);
1960 user_focus_out (ENameSelectorEntry *name_selector_entry,
1961 GdkEventFocus *event_focus)
1963 if (!event_focus->in) {
1964 entry_activate (name_selector_entry);
1967 if (name_selector_entry->priv->type_ahead_complete_cb_id) {
1968 g_source_remove (name_selector_entry->priv->type_ahead_complete_cb_id);
1969 name_selector_entry->priv->type_ahead_complete_cb_id = 0;
1972 if (name_selector_entry->priv->update_completions_cb_id) {
1973 g_source_remove (name_selector_entry->priv->update_completions_cb_id);
1974 name_selector_entry->priv->update_completions_cb_id = 0;
1977 clear_completion_model (name_selector_entry);
1979 if (!event_focus->in) {
1980 sanitize_entry (name_selector_entry);
1987 deep_free_list (GList *list)
1991 for (l = list; l; l = g_list_next (l))
1997 /* Given a widget, determines the height that text will normally be drawn. */
1999 entry_height (GtkWidget *widget)
2001 PangoLayout *layout;
2004 g_return_val_if_fail (widget != NULL, 0);
2006 layout = gtk_widget_create_pango_layout (widget, NULL);
2008 pango_layout_get_pixel_size (layout, NULL, &bound);
2014 contact_layout_pixbuffer (GtkCellLayout *cell_layout,
2015 GtkCellRenderer *cell,
2016 GtkTreeModel *model,
2018 ENameSelectorEntry *name_selector_entry)
2021 GtkTreeIter generator_iter;
2022 GtkTreeIter contact_store_iter;
2024 EContactPhoto *photo;
2025 GdkPixbuf *pixbuf = NULL;
2027 if (!name_selector_entry->priv->contact_store)
2030 gtk_tree_model_filter_convert_iter_to_child_iter (
2031 GTK_TREE_MODEL_FILTER (model),
2032 &generator_iter, iter);
2033 e_tree_model_generator_convert_iter_to_child_iter (
2034 name_selector_entry->priv->email_generator,
2035 &contact_store_iter, &email_n,
2038 contact = e_contact_store_get_contact (name_selector_entry->priv->contact_store, &contact_store_iter);
2040 g_object_set (cell, "pixbuf", pixbuf, NULL);
2044 photo = e_contact_get (contact, E_CONTACT_PHOTO);
2045 if (photo && photo->type == E_CONTACT_PHOTO_TYPE_INLINED) {
2046 guint max_height = entry_height (GTK_WIDGET (name_selector_entry));
2047 GdkPixbufLoader *loader;
2049 loader = gdk_pixbuf_loader_new ();
2050 if (gdk_pixbuf_loader_write (loader, (guchar *) photo->data.inlined.data, photo->data.inlined.length, NULL) &&
2051 gdk_pixbuf_loader_close (loader, NULL)) {
2052 pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
2054 g_object_ref (pixbuf);
2056 g_object_unref (loader);
2060 gdouble scale = 1.0;
2062 w = gdk_pixbuf_get_width (pixbuf);
2063 h = gdk_pixbuf_get_height (pixbuf);
2066 scale = max_height / (double) h;
2068 scale = max_height / (double) w;
2073 tmp = gdk_pixbuf_scale_simple (pixbuf, w * scale, h * scale, GDK_INTERP_BILINEAR);
2074 g_object_unref (pixbuf);
2081 e_contact_photo_free (photo);
2083 g_object_set (cell, "pixbuf", pixbuf, NULL);
2086 g_object_unref (pixbuf);
2090 contact_layout_formatter (GtkCellLayout *cell_layout,
2091 GtkCellRenderer *cell,
2092 GtkTreeModel *model,
2094 ENameSelectorEntry *name_selector_entry)
2097 GtkTreeIter generator_iter;
2098 GtkTreeIter contact_store_iter;
2105 if (!name_selector_entry->priv->contact_store)
2108 gtk_tree_model_filter_convert_iter_to_child_iter (
2109 GTK_TREE_MODEL_FILTER (model),
2110 &generator_iter, iter);
2111 e_tree_model_generator_convert_iter_to_child_iter (
2112 name_selector_entry->priv->email_generator,
2113 &contact_store_iter, &email_n,
2116 contact = e_contact_store_get_contact (name_selector_entry->priv->contact_store, &contact_store_iter);
2117 email_list = e_contact_get (contact, E_CONTACT_EMAIL);
2118 email_str = g_list_nth_data (email_list, email_n);
2119 file_as_str = e_contact_get (contact, E_CONTACT_FILE_AS);
2121 if (e_contact_get (contact, E_CONTACT_IS_LIST)) {
2122 string = g_strdup_printf ("%s", file_as_str ? file_as_str : "?");
2124 string = g_strdup_printf (
2125 "%s%s<%s>", file_as_str ? file_as_str : "",
2126 file_as_str ? " " : "",
2127 email_str ? email_str : "");
2130 g_free (file_as_str);
2131 deep_free_list (email_list);
2133 g_object_set (cell, "text", string, NULL);
2138 generate_contact_rows (EContactStore *contact_store,
2140 ENameSelectorEntry *name_selector_entry)
2143 const gchar *contact_uid;
2147 contact = e_contact_store_get_contact (contact_store, iter);
2148 g_assert (contact != NULL);
2150 contact_uid = e_contact_get_const (contact, E_CONTACT_UID);
2152 return 0; /* Can happen with broken databases */
2154 if (e_contact_get (contact, E_CONTACT_IS_LIST))
2157 email_list = e_contact_get (contact, E_CONTACT_EMAIL);
2158 n_rows = g_list_length (email_list);
2159 deep_free_list (email_list);
2165 ensure_type_ahead_complete_on_timeout (ENameSelectorEntry *name_selector_entry)
2168 name_selector_entry->priv->type_ahead_complete_cb_id,
2169 type_ahead_complete_on_timeout_cb, name_selector_entry);
2173 setup_contact_store (ENameSelectorEntry *name_selector_entry)
2175 if (name_selector_entry->priv->email_generator) {
2176 g_object_unref (name_selector_entry->priv->email_generator);
2177 name_selector_entry->priv->email_generator = NULL;
2180 if (name_selector_entry->priv->contact_store) {
2181 name_selector_entry->priv->email_generator =
2182 e_tree_model_generator_new (
2184 name_selector_entry->priv->contact_store));
2186 e_tree_model_generator_set_generate_func (
2187 name_selector_entry->priv->email_generator,
2188 (ETreeModelGeneratorGenerateFunc) generate_contact_rows,
2189 name_selector_entry, NULL);
2191 /* Assign the store to the entry completion */
2193 gtk_entry_completion_set_model (
2194 name_selector_entry->priv->entry_completion,
2196 name_selector_entry->priv->email_generator));
2198 /* Set up callback for incoming matches */
2199 g_signal_connect_swapped (
2200 name_selector_entry->priv->contact_store, "row-inserted",
2201 G_CALLBACK (ensure_type_ahead_complete_on_timeout), name_selector_entry);
2203 /* Remove the store from the entry completion */
2205 gtk_entry_completion_set_model (name_selector_entry->priv->entry_completion, NULL);
2210 book_loaded_cb (GObject *source_object,
2211 GAsyncResult *result,
2214 EContactStore *contact_store = user_data;
2215 ESource *source = E_SOURCE (source_object);
2216 EBookClient *book_client;
2217 EClient *client = NULL;
2218 GError *error = NULL;
2220 e_client_utils_open_new_finish (source, result, &client, &error);
2222 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
2223 g_warn_if_fail (client == NULL);
2224 g_error_free (error);
2228 if (error != NULL) {
2229 g_warning ("%s", error->message);
2230 g_warn_if_fail (client == NULL);
2231 g_error_free (error);
2235 book_client = E_BOOK_CLIENT (client);
2237 g_return_if_fail (E_IS_BOOK_CLIENT (book_client));
2238 e_contact_store_add_client (contact_store, book_client);
2239 g_object_unref (book_client);
2242 g_object_unref (contact_store);
2246 setup_default_contact_store (ENameSelectorEntry *name_selector_entry)
2248 ESourceRegistry *registry;
2249 EContactStore *contact_store;
2251 const gchar *extension_name;
2253 g_return_if_fail (name_selector_entry->priv->contact_store == NULL);
2255 /* Create a book for each completion source, and assign them to the contact store */
2257 contact_store = e_contact_store_new ();
2258 name_selector_entry->priv->contact_store = contact_store;
2260 extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
2261 registry = e_name_selector_entry_get_registry (name_selector_entry);
2263 /* An ESourceRegistry should have been set by now. */
2264 g_return_if_fail (registry != NULL);
2266 list = e_source_registry_list_sources (registry, extension_name);
2268 for (iter = list; iter != NULL; iter = g_list_next (iter)) {
2269 ESource *source = E_SOURCE (iter->data);
2270 ESourceAutocomplete *extension;
2271 GCancellable *cancellable;
2272 const gchar *extension_name;
2274 extension_name = E_SOURCE_EXTENSION_AUTOCOMPLETE;
2275 extension = e_source_get_extension (source, extension_name);
2277 /* Skip disabled address books. */
2278 if (!e_source_registry_check_enabled (registry, source))
2281 /* Skip non-completion address books. */
2282 if (!e_source_autocomplete_get_include_me (extension))
2285 cancellable = g_cancellable_new ();
2288 &name_selector_entry->priv->cancellables,
2291 e_client_utils_open_new (
2292 source, E_CLIENT_SOURCE_TYPE_CONTACTS, TRUE, cancellable,
2293 book_loaded_cb, g_object_ref (contact_store));
2296 g_list_free_full (list, (GDestroyNotify) g_object_unref);
2298 setup_contact_store (name_selector_entry);
2302 destination_row_changed (ENameSelectorEntry *name_selector_entry,
2306 EDestination *destination;
2307 const gchar *entry_text;
2309 gint range_start, range_end;
2312 n = gtk_tree_path_get_indices (path)[0];
2313 destination = e_destination_store_get_destination (name_selector_entry->priv->destination_store, iter);
2320 entry_text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
2321 if (!get_range_by_index (entry_text, n, &range_start, &range_end)) {
2322 g_warning ("ENameSelectorEntry is out of sync with model!");
2326 g_signal_handlers_block_by_func (name_selector_entry, user_insert_text, name_selector_entry);
2327 g_signal_handlers_block_by_func (name_selector_entry, user_delete_text, name_selector_entry);
2329 gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry), range_start, range_end);
2331 text = get_destination_textrep (name_selector_entry, destination);
2332 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), text, -1, &range_start);
2335 g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
2336 g_signal_handlers_unblock_by_func (name_selector_entry, user_insert_text, name_selector_entry);
2338 clear_completion_model (name_selector_entry);
2339 generate_attribute_list (name_selector_entry);
2343 destination_row_inserted (ENameSelectorEntry *name_selector_entry,
2347 EDestination *destination;
2348 const gchar *entry_text;
2350 gboolean comma_before = FALSE;
2351 gboolean comma_after = FALSE;
2352 gint range_start, range_end;
2356 n = gtk_tree_path_get_indices (path)[0];
2357 destination = e_destination_store_get_destination (name_selector_entry->priv->destination_store, iter);
2360 g_assert (destination != NULL);
2362 entry_text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
2364 if (get_range_by_index (entry_text, n, &range_start, &range_end) && range_start != range_end) {
2365 /* Another destination comes after us */
2366 insert_pos = range_start;
2368 } else if (n > 0 && get_range_by_index (entry_text, n - 1, &range_start, &range_end)) {
2369 /* Another destination comes before us */
2370 insert_pos = range_end;
2371 comma_before = TRUE;
2372 } else if (n == 0) {
2373 /* We're the sole destination */
2376 g_warning ("ENameSelectorEntry is out of sync with model!");
2380 g_signal_handlers_block_by_func (name_selector_entry, user_insert_text, name_selector_entry);
2383 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), ", ", -1, &insert_pos);
2385 text = get_destination_textrep (name_selector_entry, destination);
2386 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), text, -1, &insert_pos);
2390 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), ", ", -1, &insert_pos);
2392 g_signal_handlers_unblock_by_func (name_selector_entry, user_insert_text, name_selector_entry);
2394 clear_completion_model (name_selector_entry);
2395 generate_attribute_list (name_selector_entry);
2399 destination_row_deleted (ENameSelectorEntry *name_selector_entry,
2403 gboolean deleted_comma = FALSE;
2404 gint range_start, range_end;
2408 n = gtk_tree_path_get_indices (path)[0];
2411 text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
2413 if (!get_range_by_index (text, n, &range_start, &range_end)) {
2414 g_warning ("ENameSelectorEntry is out of sync with model!");
2418 /* Expand range for deletion forwards */
2419 for (p0 = g_utf8_offset_to_pointer (text, range_end); *p0;
2420 p0 = g_utf8_next_char (p0), range_end++) {
2421 gunichar c = g_utf8_get_char (p0);
2423 /* Gobble spaces directly after comma */
2424 if (c != ' ' && deleted_comma) {
2430 deleted_comma = TRUE;
2435 /* Expand range for deletion backwards */
2436 for (p0 = g_utf8_offset_to_pointer (text, range_start); range_start > 0;
2437 p0 = g_utf8_prev_char (p0), range_start--) {
2438 gunichar c = g_utf8_get_char (p0);
2441 if (!deleted_comma) {
2442 deleted_comma = TRUE;
2448 /* Leave a space in front; we deleted the comma and spaces before the
2449 * following destination */
2450 p0 = g_utf8_next_char (p0);
2451 c = g_utf8_get_char (p0);
2459 g_signal_handlers_block_by_func (name_selector_entry, user_delete_text, name_selector_entry);
2460 gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry), range_start, range_end);
2461 g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
2463 clear_completion_model (name_selector_entry);
2464 generate_attribute_list (name_selector_entry);
2468 setup_destination_store (ENameSelectorEntry *name_selector_entry)
2472 g_signal_connect_swapped (
2473 name_selector_entry->priv->destination_store, "row-changed",
2474 G_CALLBACK (destination_row_changed), name_selector_entry);
2475 g_signal_connect_swapped (
2476 name_selector_entry->priv->destination_store, "row-deleted",
2477 G_CALLBACK (destination_row_deleted), name_selector_entry);
2478 g_signal_connect_swapped (
2479 name_selector_entry->priv->destination_store, "row-inserted",
2480 G_CALLBACK (destination_row_inserted), name_selector_entry);
2482 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (name_selector_entry->priv->destination_store), &iter))
2488 path = gtk_tree_model_get_path (GTK_TREE_MODEL (name_selector_entry->priv->destination_store), &iter);
2491 destination_row_inserted (name_selector_entry, path, &iter);
2492 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (name_selector_entry->priv->destination_store), &iter));
2496 prepare_popup_destination (ENameSelectorEntry *name_selector_entry,
2497 GdkEventButton *event_button)
2499 EDestination *destination;
2500 PangoLayout *layout;
2501 gint layout_offset_x;
2502 gint layout_offset_y;
2506 if (event_button->type != GDK_BUTTON_PRESS)
2509 if (event_button->button != 3)
2512 if (name_selector_entry->priv->popup_destination) {
2513 g_object_unref (name_selector_entry->priv->popup_destination);
2514 name_selector_entry->priv->popup_destination = NULL;
2517 gtk_entry_get_layout_offsets (
2518 GTK_ENTRY (name_selector_entry),
2519 &layout_offset_x, &layout_offset_y);
2520 x = (event_button->x + 0.5) - layout_offset_x;
2521 y = (event_button->y + 0.5) - layout_offset_y;
2526 layout = gtk_entry_get_layout (GTK_ENTRY (name_selector_entry));
2527 if (!pango_layout_xy_to_index (layout, x * PANGO_SCALE, y * PANGO_SCALE, &index, NULL))
2530 index = gtk_entry_layout_index_to_text_index (GTK_ENTRY (name_selector_entry), index);
2531 destination = find_destination_at_position (name_selector_entry, index);
2532 /* FIXME: Add this to a private variable, in ENameSelectorEntry Class*/
2533 g_object_set_data ((GObject *) name_selector_entry, "index", GINT_TO_POINTER (index));
2535 if (!destination || !e_destination_get_contact (destination))
2538 /* TODO: Unref destination when we finalize */
2539 name_selector_entry->priv->popup_destination = g_object_ref (destination);
2543 static EBookClient *
2544 find_client_by_contact (GSList *clients,
2545 const gchar *contact_uid,
2546 const gchar *source_uid)
2550 if (source_uid && *source_uid) {
2551 /* this is much quicket than asking each client for an existence */
2552 for (l = clients; l; l = g_slist_next (l)) {
2553 EBookClient *client = l->data;
2554 ESource *source = e_client_get_source (E_CLIENT (client));
2559 if (g_strcmp0 (source_uid, e_source_get_uid (source)) == 0)
2564 for (l = clients; l; l = g_slist_next (l)) {
2565 EBookClient *client = l->data;
2566 EContact *contact = NULL;
2569 result = e_book_client_get_contact_sync (client, contact_uid, &contact, NULL, NULL);
2571 g_object_unref (contact);
2581 editor_closed_cb (GtkWidget *editor,
2586 EDestination *destination;
2588 EBookClient *book_client;
2590 ENameSelectorEntry *name_selector_entry = E_NAME_SELECTOR_ENTRY (data);
2592 destination = name_selector_entry->priv->popup_destination;
2593 contact = e_destination_get_contact (destination);
2595 g_object_unref (name_selector_entry);
2599 contact_uid = e_contact_get (contact, E_CONTACT_UID);
2601 g_object_unref (contact);
2602 g_object_unref (name_selector_entry);
2606 if (name_selector_entry->priv->contact_store) {
2607 clients = e_contact_store_get_clients (name_selector_entry->priv->contact_store);
2608 book_client = find_client_by_contact (clients, contact_uid, e_destination_get_source_uid (destination));
2609 g_slist_free (clients);
2617 g_warn_if_fail (e_book_client_get_contact_sync (book_client, contact_uid, &contact, NULL, NULL));
2618 email_num = e_destination_get_email_num (destination);
2619 e_destination_set_contact (destination, contact, email_num);
2620 e_destination_set_client (destination, book_client);
2625 g_free (contact_uid);
2627 g_object_unref (contact);
2628 g_object_unref (name_selector_entry);
2631 /* To parse something like...
2632 * =?UTF-8?Q?=E0=A4=95=E0=A4=95=E0=A4=AC=E0=A5=82=E0=A5=8B=E0=A5=87?=\t\n=?UTF-8?Q?=E0=A4=B0?=\t\n<aa@aa.ccom>
2633 * and return the decoded representation of name & email parts.
2636 eab_parse_qp_email (const gchar *string,
2640 struct _camel_header_address *address;
2641 gboolean res = FALSE;
2643 address = camel_header_address_decode (string, "UTF-8");
2648 /* report success only when we have filled both name and email address */
2649 if (address->type == CAMEL_HEADER_ADDRESS_NAME && address->name && *address->name && address->v.addr && *address->v.addr) {
2650 *name = g_strdup (address->name);
2651 *email = g_strdup (address->v.addr);
2655 camel_header_address_unref (address);
2661 popup_activate_inline_expand (ENameSelectorEntry *name_selector_entry,
2662 GtkWidget *menu_item)
2665 GString *sanitized_text = g_string_new ("");
2666 EDestination *destination = name_selector_entry->priv->popup_destination;
2667 gint position, start, end;
2670 position = GPOINTER_TO_INT (g_object_get_data ((GObject *) name_selector_entry, "index"));
2672 for (dests = e_destination_list_get_dests (destination); dests; dests = dests->next) {
2673 const EDestination *dest = dests->data;
2675 gchar *name = NULL, *email = NULL, *tofree = NULL;
2680 text = e_destination_get_textrep (dest, TRUE);
2682 if (!text || !*text)
2685 if (eab_parse_qp_email (text, &name, &email)) {
2686 tofree = g_strdup_printf ("%s <%s>", name, email);
2692 sanitized = sanitize_string (text);
2698 if (*sanitized_text->str)
2699 g_string_append (sanitized_text, ", ");
2701 g_string_append (sanitized_text, sanitized);
2707 text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
2708 get_range_at_position (text, position, &start, &end);
2709 gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry), start, end);
2710 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), sanitized_text->str, -1, &start);
2711 g_string_free (sanitized_text, TRUE);
2713 clear_completion_model (name_selector_entry);
2714 generate_attribute_list (name_selector_entry);
2718 popup_activate_contact (ENameSelectorEntry *name_selector_entry,
2719 GtkWidget *menu_item)
2721 EBookClient *book_client;
2723 EDestination *destination;
2727 destination = name_selector_entry->priv->popup_destination;
2731 contact = e_destination_get_contact (destination);
2735 contact_uid = e_contact_get (contact, E_CONTACT_UID);
2739 if (name_selector_entry->priv->contact_store) {
2740 clients = e_contact_store_get_clients (name_selector_entry->priv->contact_store);
2741 book_client = find_client_by_contact (clients, contact_uid, e_destination_get_source_uid (destination));
2742 g_slist_free (clients);
2743 g_free (contact_uid);
2751 if (e_destination_is_evolution_list (destination)) {
2752 GtkWidget *contact_list_editor;
2754 if (!name_selector_entry->priv->contact_list_editor_func)
2757 contact_list_editor = (*name_selector_entry->priv->contact_list_editor_func) (book_client, contact, FALSE, TRUE);
2758 g_object_ref (name_selector_entry);
2760 contact_list_editor, "editor_closed",
2761 G_CALLBACK (editor_closed_cb), name_selector_entry);
2763 GtkWidget *contact_editor;
2765 if (!name_selector_entry->priv->contact_editor_func)
2768 contact_editor = (*name_selector_entry->priv->contact_editor_func) (book_client, contact, FALSE, TRUE);
2769 g_object_ref (name_selector_entry);
2771 contact_editor, "editor_closed",
2772 G_CALLBACK (editor_closed_cb), name_selector_entry);
2777 popup_activate_email (ENameSelectorEntry *name_selector_entry,
2778 GtkWidget *menu_item)
2780 EDestination *destination;
2784 destination = name_selector_entry->priv->popup_destination;
2788 contact = e_destination_get_contact (destination);
2792 email_num = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu_item), "order"));
2793 e_destination_set_contact (destination, contact, email_num);
2797 popup_activate_list (EDestination *destination,
2800 gboolean status = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item));
2802 e_destination_set_ignored (destination, !status);
2806 popup_activate_cut (ENameSelectorEntry *name_selector_entry,
2807 GtkWidget *menu_item)
2809 EDestination *destination;
2810 const gchar *contact_email;
2811 gchar *pemail = NULL;
2812 GtkClipboard *clipboard;
2814 destination = name_selector_entry->priv->popup_destination;
2815 contact_email =e_destination_get_textrep (destination, TRUE);
2817 g_signal_handlers_block_by_func (name_selector_entry, user_insert_text, name_selector_entry);
2818 g_signal_handlers_block_by_func (name_selector_entry, user_delete_text, name_selector_entry);
2820 clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
2821 pemail = g_strconcat (contact_email, ",", NULL);
2822 gtk_clipboard_set_text (clipboard, pemail, strlen (pemail));
2824 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
2825 gtk_clipboard_set_text (clipboard, pemail, strlen (pemail));
2827 gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry), 0, 0);
2828 e_destination_store_remove_destination (name_selector_entry->priv->destination_store, destination);
2831 g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
2832 g_signal_handlers_unblock_by_func (name_selector_entry, user_insert_text, name_selector_entry);
2836 popup_activate_copy (ENameSelectorEntry *name_selector_entry,
2837 GtkWidget *menu_item)
2839 EDestination *destination;
2840 const gchar *contact_email;
2842 GtkClipboard *clipboard;
2844 destination = name_selector_entry->priv->popup_destination;
2845 contact_email = e_destination_get_textrep (destination, TRUE);
2847 g_signal_handlers_block_by_func (name_selector_entry, user_insert_text, name_selector_entry);
2848 g_signal_handlers_block_by_func (name_selector_entry, user_delete_text, name_selector_entry);
2850 clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
2851 pemail = g_strconcat (contact_email, ",", NULL);
2852 gtk_clipboard_set_text (clipboard, pemail, strlen (pemail));
2854 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
2855 gtk_clipboard_set_text (clipboard, pemail, strlen (pemail));
2857 g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
2858 g_signal_handlers_unblock_by_func (name_selector_entry, user_insert_text, name_selector_entry);
2862 destination_set_list (GtkWidget *item,
2863 EDestination *destination)
2866 gboolean status = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item));
2868 contact = e_destination_get_contact (destination);
2872 e_destination_set_ignored (destination, !status);
2876 destination_set_email (GtkWidget *item,
2877 EDestination *destination)
2882 if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item)))
2884 contact = e_destination_get_contact (destination);
2888 email_num = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "order"));
2889 e_destination_set_contact (destination, contact, email_num);
2893 populate_popup (ENameSelectorEntry *name_selector_entry,
2896 EDestination *destination;
2898 GtkWidget *menu_item;
2899 GList *email_list = NULL;
2905 gint email_num, len;
2906 GSList *group = NULL;
2908 gboolean show_menu = FALSE;
2910 destination = name_selector_entry->priv->popup_destination;
2914 contact = e_destination_get_contact (destination);
2918 /* Prepend the menu items, backwards */
2922 menu_item = gtk_separator_menu_item_new ();
2923 gtk_widget_show (menu_item);
2924 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
2925 email_num = e_destination_get_email_num (destination);
2928 is_list = e_contact_get (contact, E_CONTACT_IS_LIST) ? TRUE : FALSE;
2930 const GList *dests = e_destination_list_get_dests (destination);
2932 gint length = g_list_length ((GList *) dests);
2934 for (iter = (GList *) dests; iter; iter = iter->next) {
2935 EDestination *dest = (EDestination *) iter->data;
2936 const gchar *email = e_destination_get_email (dest);
2938 if (!email || *email == '\0')
2942 menu_item = gtk_check_menu_item_new_with_label (email);
2944 menu_item, "toggled",
2945 G_CALLBACK (destination_set_list), dest);
2947 menu_item = gtk_menu_item_new_with_label (email);
2950 gtk_widget_show (menu_item);
2951 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
2955 gtk_check_menu_item_set_active (
2956 GTK_CHECK_MENU_ITEM (menu_item),
2957 !e_destination_is_ignored (dest));
2958 g_signal_connect_swapped (
2959 menu_item, "activate",
2960 G_CALLBACK (popup_activate_list), dest);
2965 email_list = e_contact_get (contact, E_CONTACT_EMAIL);
2966 len = g_list_length (email_list);
2968 for (l = email_list, i = 0; l; l = g_list_next (l), i++) {
2969 gchar *email = l->data;
2971 if (!email || *email == '\0')
2975 menu_item = gtk_radio_menu_item_new_with_label (group, email);
2976 group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (menu_item));
2977 g_signal_connect (menu_item, "toggled", G_CALLBACK (destination_set_email), destination);
2979 menu_item = gtk_menu_item_new_with_label (email);
2982 gtk_widget_show (menu_item);
2983 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
2985 g_object_set_data (G_OBJECT (menu_item), "order", GINT_TO_POINTER (i));
2987 if (i == email_num && len > 1) {
2988 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item), TRUE);
2989 g_signal_connect_swapped (
2990 menu_item, "activate",
2991 G_CALLBACK (popup_activate_email),
2992 name_selector_entry);
3000 menu_item = gtk_separator_menu_item_new ();
3001 gtk_widget_show (menu_item);
3002 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
3005 /* Expand a list inline */
3007 /* To Translators: This would be similiar to "Expand MyList Inline" where MyList is a Contact List*/
3008 edit_label = g_strdup_printf (_("E_xpand %s Inline"), (gchar *) e_contact_get_const (contact, E_CONTACT_FILE_AS));
3009 menu_item = gtk_menu_item_new_with_mnemonic (edit_label);
3010 g_free (edit_label);
3011 gtk_widget_show (menu_item);
3012 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
3013 g_signal_connect_swapped (
3014 menu_item, "activate", G_CALLBACK (popup_activate_inline_expand),
3015 name_selector_entry);
3018 menu_item = gtk_separator_menu_item_new ();
3019 gtk_widget_show (menu_item);
3020 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
3023 /* Copy Contact Item */
3024 copy_label = g_strdup_printf (_("Cop_y %s"), (gchar *) e_contact_get_const (contact, E_CONTACT_FILE_AS));
3025 menu_item = gtk_menu_item_new_with_mnemonic (copy_label);
3026 g_free (copy_label);
3027 gtk_widget_show (menu_item);
3028 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
3030 g_signal_connect_swapped (
3031 menu_item, "activate", G_CALLBACK (popup_activate_copy),
3032 name_selector_entry);
3034 /* Cut Contact Item */
3035 cut_label = g_strdup_printf (_("C_ut %s"), (gchar *) e_contact_get_const (contact, E_CONTACT_FILE_AS));
3036 menu_item = gtk_menu_item_new_with_mnemonic (cut_label);
3038 gtk_widget_show (menu_item);
3039 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
3041 g_signal_connect_swapped (
3042 menu_item, "activate", G_CALLBACK (popup_activate_cut),
3043 name_selector_entry);
3046 menu_item = gtk_separator_menu_item_new ();
3047 gtk_widget_show (menu_item);
3048 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
3051 /* Edit Contact item */
3053 edit_label = g_strdup_printf (_("_Edit %s"), (gchar *) e_contact_get_const (contact, E_CONTACT_FILE_AS));
3054 menu_item = gtk_menu_item_new_with_mnemonic (edit_label);
3055 g_free (edit_label);
3056 gtk_widget_show (menu_item);
3057 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
3059 g_signal_connect_swapped (
3060 menu_item, "activate", G_CALLBACK (popup_activate_contact),
3061 name_selector_entry);
3063 deep_free_list (email_list);
3067 copy_or_cut_clipboard (ENameSelectorEntry *name_selector_entry,
3070 GtkClipboard *clipboard;
3071 GtkEditable *editable;
3072 const gchar *text, *cp;
3074 GHashTableIter iter;
3075 gpointer key, value;
3077 gint ii, start, end;
3080 editable = GTK_EDITABLE (name_selector_entry);
3081 text = gtk_entry_get_text (GTK_ENTRY (editable));
3083 if (!gtk_editable_get_selection_bounds (editable, &start, &end))
3086 g_return_if_fail (end > start);
3088 hash = g_hash_table_new (g_direct_hash, g_direct_equal);
3091 cp = g_utf8_offset_to_pointer (text, end);
3092 uc = g_utf8_get_char (cp);
3094 /* Exclude trailing whitespace and commas. */
3095 while (ii >= start && (uc == ',' || g_unichar_isspace (uc))) {
3096 cp = g_utf8_prev_char (cp);
3097 uc = g_utf8_get_char (cp);
3101 /* Determine the index of each remaining character. */
3102 while (ii >= start) {
3103 gint index = get_index_at_position (text, ii--);
3104 g_hash_table_insert (hash, GINT_TO_POINTER (index), NULL);
3107 addresses = g_string_new ("");
3109 g_hash_table_iter_init (&iter, hash);
3110 while (g_hash_table_iter_next (&iter, &key, &value)) {
3111 gint index = GPOINTER_TO_INT (key);
3115 if (!get_range_by_index (text, index, &rstart, &rend))
3118 if (rstart < start) {
3119 if (addresses->str && *addresses->str)
3120 g_string_append (addresses, ", ");
3122 g_string_append_len (addresses, text + start, rend - start);
3123 } else if (rend > end) {
3124 if (addresses->str && *addresses->str)
3125 g_string_append (addresses, ", ");
3127 g_string_append_len (addresses, text + rstart, end - rstart);
3129 /* the contact is whole selected */
3130 dest = find_destination_by_index (name_selector_entry, index);
3131 if (dest && e_destination_get_textrep (dest, TRUE)) {
3132 if (addresses->str && *addresses->str)
3133 g_string_append (addresses, ", ");
3135 g_string_append (addresses, e_destination_get_textrep (dest, TRUE));
3137 /* store the 'dest' as a value for the index */
3138 g_hash_table_insert (hash, GINT_TO_POINTER (index), dest);
3140 g_string_append_len (addresses, text + rstart, rend - rstart);
3145 gtk_editable_delete_text (editable, start, end);
3147 g_hash_table_unref (hash);
3149 clipboard = gtk_widget_get_clipboard (
3150 GTK_WIDGET (name_selector_entry), GDK_SELECTION_CLIPBOARD);
3151 gtk_clipboard_set_text (clipboard, addresses->str, -1);
3153 g_string_free (addresses, TRUE);
3157 copy_clipboard (GtkEntry *entry,
3158 ENameSelectorEntry *name_selector_entry)
3160 copy_or_cut_clipboard (name_selector_entry, FALSE);
3161 g_signal_stop_emission_by_name (entry, "copy-clipboard");
3165 cut_clipboard (GtkEntry *entry,
3166 ENameSelectorEntry *name_selector_entry)
3168 copy_or_cut_clipboard (name_selector_entry, TRUE);
3169 g_signal_stop_emission_by_name (entry, "cut-clipboard");
3173 e_name_selector_entry_init (ENameSelectorEntry *name_selector_entry)
3175 GtkCellRenderer *renderer;
3177 name_selector_entry->priv =
3178 E_NAME_SELECTOR_ENTRY_GET_PRIVATE (name_selector_entry);
3180 g_queue_init (&name_selector_entry->priv->cancellables);
3182 name_selector_entry->priv->minimum_query_length = 3;
3183 name_selector_entry->priv->show_address = FALSE;
3188 name_selector_entry, "insert-text",
3189 G_CALLBACK (user_insert_text), name_selector_entry);
3191 name_selector_entry, "delete-text",
3192 G_CALLBACK (user_delete_text), name_selector_entry);
3194 name_selector_entry, "focus-out-event",
3195 G_CALLBACK (user_focus_out), name_selector_entry);
3196 g_signal_connect_after (
3197 name_selector_entry, "focus-in-event",
3198 G_CALLBACK (user_focus_in), name_selector_entry);
3203 name_selector_entry, "draw",
3204 G_CALLBACK (draw_event), name_selector_entry);
3206 /* Activation: Complete current entry if possible */
3209 name_selector_entry, "activate",
3210 G_CALLBACK (entry_activate), name_selector_entry);
3215 name_selector_entry, "button-press-event",
3216 G_CALLBACK (prepare_popup_destination), name_selector_entry);
3218 name_selector_entry, "populate-popup",
3219 G_CALLBACK (populate_popup), name_selector_entry);
3221 /* Clipboard signals */
3223 name_selector_entry, "copy-clipboard",
3224 G_CALLBACK (copy_clipboard), name_selector_entry);
3226 name_selector_entry, "cut-clipboard",
3227 G_CALLBACK (cut_clipboard), name_selector_entry);
3231 name_selector_entry->priv->email_generator = NULL;
3233 name_selector_entry->priv->entry_completion = gtk_entry_completion_new ();
3234 gtk_entry_completion_set_match_func (
3235 name_selector_entry->priv->entry_completion,
3236 (GtkEntryCompletionMatchFunc) completion_match_cb, NULL, NULL);
3237 g_signal_connect_swapped (
3238 name_selector_entry->priv->entry_completion, "match-selected",
3239 G_CALLBACK (completion_match_selected), name_selector_entry);
3241 gtk_entry_set_completion (
3242 GTK_ENTRY (name_selector_entry),
3243 name_selector_entry->priv->entry_completion);
3245 renderer = gtk_cell_renderer_pixbuf_new ();
3246 gtk_cell_layout_pack_start (
3247 GTK_CELL_LAYOUT (name_selector_entry->priv->entry_completion),
3249 gtk_cell_layout_set_cell_data_func (
3250 GTK_CELL_LAYOUT (name_selector_entry->priv->entry_completion),
3251 GTK_CELL_RENDERER (renderer),
3252 (GtkCellLayoutDataFunc) contact_layout_pixbuffer,
3253 name_selector_entry, NULL);
3255 /* Completion list name renderer */
3256 renderer = gtk_cell_renderer_text_new ();
3257 gtk_cell_layout_pack_start (
3258 GTK_CELL_LAYOUT (name_selector_entry->priv->entry_completion),
3260 gtk_cell_layout_set_cell_data_func (
3261 GTK_CELL_LAYOUT (name_selector_entry->priv->entry_completion),
3262 GTK_CELL_RENDERER (renderer),
3263 (GtkCellLayoutDataFunc) contact_layout_formatter,
3264 name_selector_entry, NULL);
3266 /* Destination store */
3268 name_selector_entry->priv->destination_store = e_destination_store_new ();
3269 setup_destination_store (name_selector_entry);
3270 name_selector_entry->priv->is_completing = FALSE;
3274 * e_name_selector_entry_new:
3276 * Creates a new #ENameSelectorEntry.
3278 * Returns: A new #ENameSelectorEntry.
3280 ENameSelectorEntry *
3281 e_name_selector_entry_new (ESourceRegistry *registry)
3283 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
3285 return g_object_new (
3286 E_TYPE_NAME_SELECTOR_ENTRY,
3287 "registry", registry, NULL);
3291 * e_name_selector_entry_get_registry:
3292 * @name_selector_entry: an #ENameSelectorEntry
3294 * Returns the #ESourceRegistry used to query address books.
3296 * Returns: the #ESourceRegistry, or %NULL
3301 e_name_selector_entry_get_registry (ENameSelectorEntry *name_selector_entry)
3303 g_return_val_if_fail (
3304 E_IS_NAME_SELECTOR_ENTRY (name_selector_entry), NULL);
3306 return name_selector_entry->priv->registry;
3310 * e_name_selector_entry_set_registry:
3311 * @name_selector_entry: an #ENameSelectorEntry
3312 * @registry: an #ESourceRegistry
3314 * Sets the #ESourceRegistry used to query address books.
3316 * This function is intended for cases where @name_selector_entry is
3317 * instantiated by a #GtkBuilder and has to be given an #EsourceRegistry
3318 * after it is fully constructed.
3323 e_name_selector_entry_set_registry (ENameSelectorEntry *name_selector_entry,
3324 ESourceRegistry *registry)
3326 g_return_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry));
3328 if (name_selector_entry->priv->registry == registry)
3331 if (registry != NULL) {
3332 g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
3333 g_object_ref (registry);
3336 if (name_selector_entry->priv->registry != NULL)
3337 g_object_unref (name_selector_entry->priv->registry);
3339 name_selector_entry->priv->registry = registry;
3341 g_object_notify (G_OBJECT (name_selector_entry), "registry");
3345 * e_name_selector_entry_get_minimum_query_length:
3346 * @name_selector_entry: an #ENameSelectorEntry
3348 * Returns: Minimum length of query before completion starts
3353 e_name_selector_entry_get_minimum_query_length (ENameSelectorEntry *name_selector_entry)
3355 g_return_val_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry), -1);
3357 return name_selector_entry->priv->minimum_query_length;
3361 * e_name_selector_entry_set_minimum_query_length:
3362 * @name_selector_entry: an #ENameSelectorEntry
3363 * @length: minimum query length
3365 * Sets minimum length of query before completion starts.
3370 e_name_selector_entry_set_minimum_query_length (ENameSelectorEntry *name_selector_entry,
3373 g_return_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry));
3374 g_return_if_fail (length > 0);
3376 if (name_selector_entry->priv->minimum_query_length == length)
3379 name_selector_entry->priv->minimum_query_length = length;
3381 g_object_notify (G_OBJECT (name_selector_entry), "minimum-query-length");
3385 * e_name_selector_entry_get_show_address:
3386 * @name_selector_entry: an #ENameSelectorEntry
3388 * Returns: Whether always show email address for an auto-completed contact.
3393 e_name_selector_entry_get_show_address (ENameSelectorEntry *name_selector_entry)
3395 g_return_val_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry), FALSE);
3397 return name_selector_entry->priv->show_address;
3401 * e_name_selector_entry_set_show_address:
3402 * @name_selector_entry: an #ENameSelectorEntry
3403 * @show: new value to set
3405 * Sets whether always show email address for an auto-completed contact.
3410 e_name_selector_entry_set_show_address (ENameSelectorEntry *name_selector_entry,
3413 g_return_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry));
3415 if ((name_selector_entry->priv->show_address ? 1 : 0) == (show ? 1 : 0))
3418 name_selector_entry->priv->show_address = show;
3420 sanitize_entry (name_selector_entry);
3422 g_object_notify (G_OBJECT (name_selector_entry), "show-address");
3426 * e_name_selector_entry_peek_contact_store:
3427 * @name_selector_entry: an #ENameSelectorEntry
3429 * Gets the #EContactStore being used by @name_selector_entry.
3431 * Returns: An #EContactStore.
3434 e_name_selector_entry_peek_contact_store (ENameSelectorEntry *name_selector_entry)
3436 g_return_val_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry), NULL);
3438 return name_selector_entry->priv->contact_store;
3442 * e_name_selector_entry_set_contact_store:
3443 * @name_selector_entry: an #ENameSelectorEntry
3444 * @contact_store: an #EContactStore to use
3446 * Sets the #EContactStore being used by @name_selector_entry to @contact_store.
3449 e_name_selector_entry_set_contact_store (ENameSelectorEntry *name_selector_entry,
3450 EContactStore *contact_store)
3452 g_return_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry));
3453 g_return_if_fail (contact_store == NULL || E_IS_CONTACT_STORE (contact_store));
3455 if (contact_store == name_selector_entry->priv->contact_store)
3458 if (name_selector_entry->priv->contact_store)
3459 g_object_unref (name_selector_entry->priv->contact_store);
3460 name_selector_entry->priv->contact_store = contact_store;
3461 if (name_selector_entry->priv->contact_store)
3462 g_object_ref (name_selector_entry->priv->contact_store);
3464 setup_contact_store (name_selector_entry);
3468 * e_name_selector_entry_peek_destination_store:
3469 * @name_selector_entry: an #ENameSelectorEntry
3471 * Gets the #EDestinationStore being used to store @name_selector_entry's destinations.
3473 * Returns: An #EDestinationStore.
3476 e_name_selector_entry_peek_destination_store (ENameSelectorEntry *name_selector_entry)
3478 g_return_val_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry), NULL);
3480 return name_selector_entry->priv->destination_store;
3484 * e_name_selector_entry_set_destination_store:
3485 * @name_selector_entry: an #ENameSelectorEntry
3486 * @destination_store: an #EDestinationStore to use
3488 * Sets @destination_store as the #EDestinationStore to be used to store
3489 * destinations for @name_selector_entry.
3492 e_name_selector_entry_set_destination_store (ENameSelectorEntry *name_selector_entry,
3493 EDestinationStore *destination_store)
3495 g_return_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry));
3496 g_return_if_fail (E_IS_DESTINATION_STORE (destination_store));
3498 if (destination_store == name_selector_entry->priv->destination_store)
3501 g_object_unref (name_selector_entry->priv->destination_store);
3502 name_selector_entry->priv->destination_store = g_object_ref (destination_store);
3504 setup_destination_store (name_selector_entry);
3508 * e_name_selector_entry_get_popup_destination:
3513 e_name_selector_entry_get_popup_destination (ENameSelectorEntry *name_selector_entry)
3515 g_return_val_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry), NULL);
3517 return name_selector_entry->priv->popup_destination;
3521 * e_name_selector_entry_set_contact_editor_func:
3526 e_name_selector_entry_set_contact_editor_func (ENameSelectorEntry *name_selector_entry,
3529 name_selector_entry->priv->contact_editor_func = func;
3533 * e_name_selector_entry_set_contact_list_editor_func:
3538 e_name_selector_entry_set_contact_list_editor_func (ENameSelectorEntry *name_selector_entry,
3541 name_selector_entry->priv->contact_list_editor_func = func;