Assamese translation updated
[platform/upstream/evolution-data-server.git] / libedataserverui / e-name-selector-entry.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /* e-name-selector-entry.c - Single-line text entry widget for EDestinations.
4  *
5  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
6  *
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.
10  *
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.
15  *
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.
20  *
21  * Authors: Hans Petter Jansson <hpj@novell.com>
22  */
23
24 #include <config.h>
25 #include <string.h>
26 #include <glib/gi18n-lib.h>
27
28 #include <camel/camel.h>
29 #include <libebackend/libebackend.h>
30
31 #include <libedataserverui/e-client-utils.h>
32
33 #include "e-name-selector-entry.h"
34
35 #define E_NAME_SELECTOR_ENTRY_GET_PRIVATE(obj) \
36         (G_TYPE_INSTANCE_GET_PRIVATE \
37         ((obj), E_TYPE_NAME_SELECTOR_ENTRY, ENameSelectorEntryPrivate))
38
39 struct _ENameSelectorEntryPrivate {
40
41         ESourceRegistry *registry;
42         gint minimum_query_length;
43         gboolean show_address;
44
45         PangoAttrList *attr_list;
46         EContactStore *contact_store;
47         ETreeModelGenerator *email_generator;
48         EDestinationStore *destination_store;
49         GtkEntryCompletion *entry_completion;
50
51         guint type_ahead_complete_cb_id;
52         guint update_completions_cb_id;
53
54         EDestination *popup_destination;
55
56         gpointer        (*contact_editor_func)  (EBookClient *,
57                                                  EContact *,
58                                                  gboolean,
59                                                  gboolean);
60         gpointer        (*contact_list_editor_func)
61                                                 (EBookClient *,
62                                                  EContact *,
63                                                  gboolean,
64                                                  gboolean);
65
66         gboolean is_completing;
67         GSList *user_query_fields;
68
69         /* For asynchronous operations. */
70         GQueue cancellables;
71 };
72
73 enum {
74         PROP_0,
75         PROP_REGISTRY,
76         PROP_MINIMUM_QUERY_LENGTH,
77         PROP_SHOW_ADDRESS
78 };
79
80 enum {
81         UPDATED,
82         LAST_SIGNAL
83 };
84
85 static guint signals[LAST_SIGNAL] = { 0 };
86 #define ENS_DEBUG(x)
87
88 G_DEFINE_TYPE_WITH_CODE (
89         ENameSelectorEntry,
90         e_name_selector_entry,
91         GTK_TYPE_ENTRY,
92         G_IMPLEMENT_INTERFACE (
93                 E_TYPE_EXTENSIBLE, NULL))
94
95 /* 1/3 of the second to wait until invoking autocomplete lookup */
96 #define AUTOCOMPLETE_TIMEOUT 333
97
98 #define re_set_timeout(id,func,ptr)                     \
99         if (id)                                         \
100                 g_source_remove (id);                   \
101         id = g_timeout_add (AUTOCOMPLETE_TIMEOUT,       \
102                             (GSourceFunc) func, ptr);
103
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);
107
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);
110
111 static void setup_default_contact_store (ENameSelectorEntry *name_selector_entry);
112 static void deep_free_list (GList *list);
113
114 static void
115 name_selector_entry_set_property (GObject *object,
116                                   guint property_id,
117                                   const GValue *value,
118                                   GParamSpec *pspec)
119 {
120         switch (property_id) {
121                 case PROP_REGISTRY:
122                         e_name_selector_entry_set_registry (
123                                 E_NAME_SELECTOR_ENTRY (object),
124                                 g_value_get_object (value));
125                         return;
126
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));
131                         return;
132
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));
137                         return;
138         }
139
140         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
141 }
142
143 static void
144 name_selector_entry_get_property (GObject *object,
145                                   guint property_id,
146                                   GValue *value,
147                                   GParamSpec *pspec)
148 {
149         switch (property_id) {
150                 case PROP_REGISTRY:
151                         g_value_set_object (
152                                 value,
153                                 e_name_selector_entry_get_registry (
154                                 E_NAME_SELECTOR_ENTRY (object)));
155                         return;
156
157                 case PROP_MINIMUM_QUERY_LENGTH:
158                         g_value_set_int (
159                                 value,
160                                 e_name_selector_entry_get_minimum_query_length (
161                                 E_NAME_SELECTOR_ENTRY (object)));
162                         return;
163
164                 case PROP_SHOW_ADDRESS:
165                         g_value_set_boolean (
166                                 value,
167                                 e_name_selector_entry_get_show_address (
168                                 E_NAME_SELECTOR_ENTRY (object)));
169                         return;
170         }
171
172         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
173 }
174
175 static void
176 name_selector_entry_dispose (GObject *object)
177 {
178         ENameSelectorEntryPrivate *priv;
179
180         priv = E_NAME_SELECTOR_ENTRY_GET_PRIVATE (object);
181
182         if (priv->registry != NULL) {
183                 g_object_unref (priv->registry);
184                 priv->registry = NULL;
185         }
186
187         if (priv->attr_list != NULL) {
188                 pango_attr_list_unref (priv->attr_list);
189                 priv->attr_list = NULL;
190         }
191
192         if (priv->entry_completion) {
193                 g_object_unref (priv->entry_completion);
194                 priv->entry_completion = NULL;
195         }
196
197         if (priv->destination_store) {
198                 g_object_unref (priv->destination_store);
199                 priv->destination_store = NULL;
200         }
201
202         if (priv->email_generator) {
203                 g_object_unref (priv->email_generator);
204                 priv->email_generator = NULL;
205         }
206
207         if (priv->contact_store) {
208                 g_object_unref (priv->contact_store);
209                 priv->contact_store = NULL;
210         }
211
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;
215
216         /* Cancel any stuck book loading operations. */
217         while (!g_queue_is_empty (&priv->cancellables)) {
218                 GCancellable *cancellable;
219
220                 cancellable = g_queue_pop_head (&priv->cancellables);
221                 g_cancellable_cancel (cancellable);
222                 g_object_unref (cancellable);
223         }
224
225         /* Chain up to parent's dispose() method. */
226         G_OBJECT_CLASS (e_name_selector_entry_parent_class)->dispose (object);
227 }
228
229 static void
230 name_selector_entry_constructed (GObject *object)
231 {
232         /* Chain up to parent's constructed() method. */
233         G_OBJECT_CLASS (e_name_selector_entry_parent_class)->
234                 constructed (object);
235
236         e_extensible_load_extensions (E_EXTENSIBLE (object));
237 }
238
239 static void
240 name_selector_entry_realize (GtkWidget *widget)
241 {
242         ENameSelectorEntryPrivate *priv;
243
244         priv = E_NAME_SELECTOR_ENTRY_GET_PRIVATE (widget);
245
246         /* Chain up to parent's realize() method. */
247         GTK_WIDGET_CLASS (e_name_selector_entry_parent_class)->realize (widget);
248
249         if (priv->contact_store == NULL)
250                 setup_default_contact_store (E_NAME_SELECTOR_ENTRY (widget));
251 }
252
253 static void
254 name_selector_entry_drag_data_received (GtkWidget *widget,
255                                         GdkDragContext *context,
256                                         gint x,
257                                         gint y,
258                                         GtkSelectionData *selection_data,
259                                         guint info,
260                                         guint time)
261 {
262         CamelInternetAddress *address;
263         gint n_addresses = 0;
264         gchar *text;
265
266         address = camel_internet_address_new ();
267         text = (gchar *) gtk_selection_data_get_text (selection_data);
268
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);
275                 else
276                         n_addresses = camel_address_decode (
277                                 CAMEL_ADDRESS (address), text);
278         }
279
280         if (n_addresses > 0) {
281                 GtkEditable *editable;
282                 GdkDragAction action;
283                 gboolean delete;
284                 gint position;
285
286                 editable = GTK_EDITABLE (widget);
287                 gtk_editable_set_position (editable, -1);
288                 position = gtk_editable_get_position (editable);
289
290                 g_free (text);
291
292                 text = camel_address_format (CAMEL_ADDRESS (address));
293                 gtk_editable_insert_text (editable, text, -1, &position);
294
295                 action = gdk_drag_context_get_selected_action (context);
296                 delete = (action == GDK_ACTION_MOVE);
297                 gtk_drag_finish (context, TRUE, delete, time);
298         }
299
300         g_object_unref (address);
301         g_free (text);
302
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)->
306                         drag_data_received (
307                                 widget, context, x, y,
308                                 selection_data, info, time);
309 }
310
311 static void
312 e_name_selector_entry_class_init (ENameSelectorEntryClass *class)
313 {
314         GObjectClass *object_class;
315         GtkWidgetClass *widget_class;
316
317         g_type_class_add_private (class, sizeof (ENameSelectorEntryPrivate));
318
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;
324
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;
328
329         g_object_class_install_property (
330                 object_class,
331                 PROP_REGISTRY,
332                 g_param_spec_object (
333                         "registry",
334                         "Registry",
335                         "Data source registry",
336                         E_TYPE_SOURCE_REGISTRY,
337                         G_PARAM_READWRITE |
338                         G_PARAM_CONSTRUCT |
339                         G_PARAM_STATIC_STRINGS));
340
341         g_object_class_install_property (
342                 object_class,
343                 PROP_MINIMUM_QUERY_LENGTH,
344                 g_param_spec_int (
345                         "minimum-query-length",
346                         "Minimum Query Length",
347                         NULL,
348                         1, G_MAXINT,
349                         3,
350                         G_PARAM_READWRITE |
351                         G_PARAM_STATIC_STRINGS));
352
353         g_object_class_install_property (
354                 object_class,
355                 PROP_SHOW_ADDRESS,
356                 g_param_spec_boolean (
357                         "show-address",
358                         "Show Address",
359                         NULL,
360                         FALSE,
361                         G_PARAM_READWRITE |
362                         G_PARAM_STATIC_STRINGS));
363
364         signals[UPDATED] = g_signal_new (
365                 "updated",
366                 E_TYPE_NAME_SELECTOR_ENTRY,
367                 G_SIGNAL_RUN_FIRST,
368                 G_STRUCT_OFFSET (ENameSelectorEntryClass, updated),
369                 NULL, NULL,
370                 g_cclosure_marshal_VOID__POINTER,
371                 G_TYPE_NONE, 1, G_TYPE_POINTER);
372 }
373
374 /* Remove unquoted commas and control characters from string */
375 static gchar *
376 sanitize_string (const gchar *string)
377 {
378         GString     *gstring;
379         gboolean     quoted = FALSE;
380         const gchar *p;
381
382         gstring = g_string_new ("");
383
384         if (!string)
385                 return g_string_free (gstring, FALSE);
386
387         for (p = string; *p; p = g_utf8_next_char (p)) {
388                 gunichar c = g_utf8_get_char (p);
389
390                 if (c == '"')
391                         quoted = ~quoted;
392                 else if (c == ',' && !quoted)
393                         continue;
394                 else if (c == '\t' || c == '\n')
395                         continue;
396
397                 g_string_append_unichar (gstring, c);
398         }
399
400         return g_string_free (gstring, FALSE);
401 }
402
403 /* Called for each list store entry whenever the user types (but not on cut/paste) */
404 static gboolean
405 completion_match_cb (GtkEntryCompletion *completion,
406                      const gchar *key,
407                      GtkTreeIter *iter,
408                      gpointer user_data)
409 {
410         ENS_DEBUG (g_print ("completion_match_cb, key=%s\n", key));
411
412         return TRUE;
413 }
414
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. */
418 static void
419 get_utf8_string_context (const gchar *string,
420                          gint position,
421                          gunichar *unichars,
422                          gint n_unichars)
423 {
424         gchar *p = NULL;
425         gint   len;
426         gint   gap;
427         gint   i;
428
429         /* n_unichars must be even */
430         g_assert (n_unichars % 2 == 0);
431
432         len = g_utf8_strlen (string, -1);
433         gap = n_unichars / 2;
434
435         for (i = 0; i < n_unichars; i++) {
436                 gint char_pos = position - gap + i;
437
438                 if (char_pos < 0 || char_pos >= len) {
439                         unichars[i] = '\0';
440                         continue;
441                 }
442
443                 if (p)
444                         p = g_utf8_next_char (p);
445                 else
446                         p = g_utf8_offset_to_pointer (string, char_pos);
447
448                 unichars[i] = g_utf8_get_char (p);
449         }
450 }
451
452 static gboolean
453 get_range_at_position (const gchar *string,
454                        gint pos,
455                        gint *start_pos,
456                        gint *end_pos)
457 {
458         const gchar *p;
459         gboolean     quoted          = FALSE;
460         gint         local_start_pos = 0;
461         gint         local_end_pos   = 0;
462         gint         i;
463
464         if (!string || !*string)
465                 return FALSE;
466
467         for (p = string, i = 0; *p; p = g_utf8_next_char (p), i++) {
468                 gunichar c = g_utf8_get_char (p);
469
470                 if (c == '"') {
471                         quoted = ~quoted;
472                 } else if (c == ',' && !quoted) {
473                         if (i < pos) {
474                                 /* Start right after comma */
475                                 local_start_pos = i + 1;
476                         } else {
477                                 /* Stop right before comma */
478                                 local_end_pos = i;
479                                 break;
480                         }
481                 } else if (c == ' ' && local_start_pos == i) {
482                         /* Adjust start to skip space after first comma */
483                         local_start_pos++;
484                 }
485         }
486
487         /* If we didn't hit a comma, we must've hit NULL, and ours was the last element. */
488         if (!local_end_pos)
489                 local_end_pos = i;
490
491         if (start_pos)
492                 *start_pos = local_start_pos;
493         if (end_pos)
494                 *end_pos   = local_end_pos;
495
496         return TRUE;
497 }
498
499 static gboolean
500 is_quoted_at (const gchar *string,
501               gint pos)
502 {
503         const gchar *p;
504         gboolean     quoted = FALSE;
505         gint         i;
506
507         for (p = string, i = 0; *p && i < pos; p = g_utf8_next_char (p), i++) {
508                 gunichar c = g_utf8_get_char (p);
509
510                 if (c == '"')
511                         quoted = ~quoted;
512         }
513
514         return quoted ? TRUE : FALSE;
515 }
516
517 static gint
518 get_index_at_position (const gchar *string,
519                        gint pos)
520 {
521         const gchar *p;
522         gboolean     quoted = FALSE;
523         gint         n      = 0;
524         gint         i;
525
526         for (p = string, i = 0; *p && i < pos; p = g_utf8_next_char (p), i++) {
527                 gunichar c = g_utf8_get_char (p);
528
529                 if (c == '"')
530                         quoted = ~quoted;
531                 else if (c == ',' && !quoted)
532                         n++;
533         }
534
535         return n;
536 }
537
538 static gboolean
539 get_range_by_index (const gchar *string,
540                     gint index,
541                     gint *start_pos,
542                     gint *end_pos)
543 {
544         const gchar *p;
545         gboolean     quoted = FALSE;
546         gint         i;
547         gint         n = 0;
548
549         for (p = string, i = 0; *p && n < index; p = g_utf8_next_char (p), i++) {
550                 gunichar c = g_utf8_get_char (p);
551
552                 if (c == '"')
553                         quoted = ~quoted;
554                 if (c == ',' && !quoted)
555                         n++;
556         }
557
558         if (n < index)
559                 return FALSE;
560
561         return get_range_at_position (string, i, start_pos, end_pos);
562 }
563
564 static gchar *
565 get_address_at_position (const gchar *string,
566                          gint pos)
567 {
568         gint         start_pos;
569         gint         end_pos;
570         const gchar *start_p;
571         const gchar *end_p;
572
573         if (!get_range_at_position (string, pos, &start_pos, &end_pos))
574                 return NULL;
575
576         start_p = g_utf8_offset_to_pointer (string, start_pos);
577         end_p   = g_utf8_offset_to_pointer (string, end_pos);
578
579         return g_strndup (start_p, end_p - start_p);
580 }
581
582 /* Finds the destination in model */
583 static EDestination *
584 find_destination_by_index (ENameSelectorEntry *name_selector_entry,
585                            gint index)
586 {
587         GtkTreePath  *path;
588         GtkTreeIter   iter;
589
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),
592                                       &iter, path)) {
593                 /* If we have zero destinations, getting a NULL destination at index 0
594                  * is valid. */
595                 if (index > 0)
596                         g_warning ("ENameSelectorEntry is out of sync with model!");
597                 gtk_tree_path_free (path);
598                 return NULL;
599         }
600         gtk_tree_path_free (path);
601
602         return e_destination_store_get_destination (name_selector_entry->priv->destination_store, &iter);
603 }
604
605 /* Finds the destination in model */
606 static EDestination *
607 find_destination_at_position (ENameSelectorEntry *name_selector_entry,
608                               gint pos)
609 {
610         const gchar  *text;
611         gint          index;
612
613         text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
614         index = get_index_at_position (text, pos);
615
616         return find_destination_by_index (name_selector_entry, index);
617 }
618
619 /* Builds destination from our text */
620 static EDestination *
621 build_destination_at_position (const gchar *string,
622                                gint pos)
623 {
624         EDestination *destination;
625         gchar        *address;
626
627         address = get_address_at_position (string, pos);
628         if (!address)
629                 return NULL;
630
631         destination = e_destination_new ();
632         e_destination_set_raw (destination, address);
633
634         g_free (address);
635         return destination;
636 }
637
638 static gchar *
639 name_style_query (const gchar *field,
640                   const gchar *value)
641 {
642         gchar   *spaced_str;
643         gchar   *comma_str;
644         GString *out = g_string_new ("");
645         gchar  **strv;
646         gchar   *query;
647
648         spaced_str = sanitize_string (value);
649         g_strstrip (spaced_str);
650
651         strv = g_strsplit (spaced_str, " ", 0);
652
653         if (strv[0] && strv[1]) {
654                 g_string_append (out, "(or ");
655                 comma_str = g_strjoinv (", ", strv);
656         } else {
657                 comma_str = NULL;
658         }
659
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, ")");
664
665         if (comma_str) {
666                 g_string_append (out, " (beginswith ");
667
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, "))");
672         }
673
674         query = g_string_free (out, FALSE);
675
676         g_free (spaced_str);
677         g_free (comma_str);
678         g_strfreev (strv);
679
680         return query;
681 }
682
683 static gchar *
684 escape_sexp_string (const gchar *string)
685 {
686         GString *gstring;
687         gchar   *encoded_string;
688
689         gstring = g_string_new ("");
690         e_sexp_encode_string (gstring, string);
691
692         encoded_string = gstring->str;
693         g_string_free (gstring, FALSE);
694
695         return encoded_string;
696 }
697
698 /**
699  * ens_util_populate_user_query_fields:
700  *
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.
704  *
705  * Since: 2.24
706  **/
707 gchar *
708 ens_util_populate_user_query_fields (GSList *user_query_fields,
709                                      const gchar *cue_str,
710                                      const gchar *encoded_cue_str)
711 {
712         GString *user_fields;
713         GSList *s;
714
715         g_return_val_if_fail (cue_str != NULL, NULL);
716         g_return_val_if_fail (encoded_cue_str != NULL, NULL);
717
718         user_fields = g_string_new ("");
719
720         for (s = user_query_fields; s; s = s->next) {
721                 const gchar *field = s->data;
722
723                 if (!field || !*field)
724                         continue;
725
726                 if (*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);
730                 } else {
731                         gchar *tmp = name_style_query (field, cue_str);
732
733                         g_string_append (user_fields, " ");
734                         g_string_append (user_fields, tmp);
735                         g_string_append (user_fields, " ");
736                         g_free (tmp);
737                 }
738         }
739
740         return g_string_free (user_fields, !user_fields->str || !*user_fields->str);
741 }
742
743 static void
744 set_completion_query (ENameSelectorEntry *name_selector_entry,
745                       const gchar *cue_str)
746 {
747         ENameSelectorEntryPrivate *priv;
748         EBookQuery *book_query;
749         gchar      *query_str;
750         gchar      *encoded_cue_str;
751         gchar      *full_name_query_str;
752         gchar      *file_as_query_str;
753         gchar      *user_fields_str;
754
755         priv = E_NAME_SELECTOR_ENTRY_GET_PRIVATE (name_selector_entry);
756
757         if (!name_selector_entry->priv->contact_store)
758                 return;
759
760         if (!cue_str) {
761                 /* Clear the store */
762                 e_contact_store_set_query (name_selector_entry->priv->contact_store, NULL);
763                 return;
764         }
765
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);
770
771         query_str = g_strdup_printf (
772                 "(or "
773                 " (beginswith \"nickname\"  %s) "
774                 " (beginswith \"email\"     %s) "
775                 " %s "
776                 " %s "
777                 " %s "
778                 ")",
779                 encoded_cue_str, encoded_cue_str,
780                 full_name_query_str, file_as_query_str,
781                 user_fields_str ? user_fields_str : "");
782
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);
787
788         ENS_DEBUG (g_print ("%s\n", query_str));
789
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);
793
794         g_free (query_str);
795 }
796
797 static gchar *
798 get_entry_substring (ENameSelectorEntry *name_selector_entry,
799                      gint range_start,
800                      gint range_end)
801 {
802         const gchar *entry_text;
803         gchar       *p0, *p1;
804
805         entry_text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
806
807         p0 = g_utf8_offset_to_pointer (entry_text, range_start);
808         p1 = g_utf8_offset_to_pointer (entry_text, range_end);
809
810         return g_strndup (p0, p1 - p0);
811 }
812
813 static gint
814 utf8_casefold_collate_len (const gchar *str1,
815                            const gchar *str2,
816                            gint len)
817 {
818         gchar *s1 = g_utf8_casefold (str1, len);
819         gchar *s2 = g_utf8_casefold (str2, len);
820         gint rv;
821
822         rv = g_utf8_collate (s1, s2);
823
824         g_free (s1);
825         g_free (s2);
826
827         return rv;
828 }
829
830 static gchar *
831 build_textrep_for_contact (EContact *contact,
832                            EContactField cue_field)
833 {
834         gchar *name  = NULL;
835         gchar *email = NULL;
836         gchar *textrep;
837
838         switch (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);
844                         break;
845
846                 case E_CONTACT_EMAIL_1:
847                 case E_CONTACT_EMAIL_2:
848                 case E_CONTACT_EMAIL_3:
849                 case E_CONTACT_EMAIL_4:
850                         name = NULL;
851                         email = e_contact_get (contact, cue_field);
852                         break;
853
854                 default:
855                         g_assert_not_reached ();
856                         break;
857         }
858
859         g_assert (email);
860         g_assert (strlen (email) > 0);
861
862         if (name)
863                 textrep = g_strdup_printf ("%s <%s>", name, email);
864         else
865                 textrep = g_strdup_printf ("%s", email);
866
867         g_free (name);
868         g_free (email);
869         return textrep;
870 }
871
872 static gboolean
873 contact_match_cue (ENameSelectorEntry *name_selector_entry,
874                    EContact *contact,
875                    const gchar *cue_str,
876                    EContactField *matched_field,
877                    gint *matched_field_rank)
878 {
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,
881                                      E_CONTACT_EMAIL_4 };
882         gchar         *email;
883         gboolean       result = FALSE;
884         gint           cue_len;
885         gint           i;
886
887         g_assert (contact);
888         g_assert (cue_str);
889
890         if (g_utf8_strlen (cue_str, -1) < name_selector_entry->priv->minimum_query_length)
891                 return FALSE;
892
893         cue_len = strlen (cue_str);
894
895         /* Make sure contact has an email address */
896         email = e_contact_get (contact, E_CONTACT_EMAIL_1);
897         if (!email || !*email) {
898                 g_free (email);
899                 return FALSE;
900         }
901         g_free (email);
902
903         for (i = 0; i < G_N_ELEMENTS (fields); i++) {
904                 gchar *value;
905                 gchar *value_sane;
906
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)
911                         continue;
912
913                 value = e_contact_get (contact, fields[i]);
914                 if (!value)
915                         continue;
916
917                 value_sane = sanitize_string (value);
918                 g_free (value);
919
920                 ENS_DEBUG (g_print ("Comparing '%s' to '%s'\n", value, cue_str));
921
922                 if (!utf8_casefold_collate_len (value_sane, cue_str, cue_len)) {
923                         if (matched_field)
924                                 *matched_field = fields [i];
925                         if (matched_field_rank)
926                                 *matched_field_rank = i;
927
928                         result = TRUE;
929                         g_free (value_sane);
930                         break;
931                 }
932                 g_free (value_sane);
933         }
934
935         return result;
936 }
937
938 static gboolean
939 find_existing_completion (ENameSelectorEntry *name_selector_entry,
940                           const gchar *cue_str,
941                           EContact **contact,
942                           gchar **text,
943                           EContactField *matched_field,
944                           EBookClient **book_client)
945 {
946         GtkTreeIter    iter;
947         EContact      *best_contact    = NULL;
948         gint           best_field_rank = G_MAXINT;
949         EContactField  best_field = 0;
950         EBookClient   *best_book_client = NULL;
951
952         g_assert (cue_str);
953
954         if (!name_selector_entry->priv->contact_store)
955                 return FALSE;
956
957         ENS_DEBUG (g_print ("Completing '%s'\n", cue_str));
958
959         if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (name_selector_entry->priv->contact_store), &iter))
960                 return FALSE;
961
962         do {
963                 EContact      *current_contact;
964                 gint           current_field_rank;
965                 EContactField  current_field;
966                 gboolean       matches;
967
968                 current_contact = e_contact_store_get_contact (name_selector_entry->priv->contact_store, &iter);
969                 if (!current_contact)
970                         continue;
971
972                 matches = contact_match_cue (name_selector_entry, current_contact, cue_str, &current_field, &current_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);
978                 }
979         } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (name_selector_entry->priv->contact_store), &iter));
980
981         if (!best_contact)
982                 return FALSE;
983
984         if (contact)
985                 *contact = best_contact;
986         if (text)
987                 *text = build_textrep_for_contact (best_contact, best_field);
988         if (matched_field)
989                 *matched_field = best_field;
990         if (book_client)
991                 *book_client = best_book_client;
992
993         return TRUE;
994 }
995
996 static void
997 generate_attribute_list (ENameSelectorEntry *name_selector_entry)
998 {
999         PangoLayout    *layout;
1000         PangoAttrList  *attr_list;
1001         const gchar    *text;
1002         gint            i;
1003
1004         text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1005         layout = gtk_entry_get_layout (GTK_ENTRY (name_selector_entry));
1006
1007         /* Set up the attribute list */
1008
1009         attr_list = pango_attr_list_new ();
1010
1011         if (name_selector_entry->priv->attr_list)
1012                 pango_attr_list_unref (name_selector_entry->priv->attr_list);
1013
1014         name_selector_entry->priv->attr_list = attr_list;
1015
1016         /* Parse the entry's text and apply attributes to real contacts */
1017
1018         for (i = 0; ; i++) {
1019                 EDestination   *destination;
1020                 PangoAttribute *attr;
1021                 gint            start_pos;
1022                 gint            end_pos;
1023
1024                 if (!get_range_by_index (text, i, &start_pos, &end_pos))
1025                         break;
1026
1027                 destination = find_destination_at_position (name_selector_entry, start_pos);
1028
1029                 /* Destination will be NULL if we have no entries */
1030                 if (!destination || !e_destination_get_contact (destination))
1031                         continue;
1032
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);
1037         }
1038
1039         pango_layout_set_attributes (layout, attr_list);
1040 }
1041
1042 static gboolean
1043 draw_event (ENameSelectorEntry *name_selector_entry)
1044 {
1045         PangoLayout *layout;
1046
1047         layout = gtk_entry_get_layout (GTK_ENTRY (name_selector_entry));
1048         pango_layout_set_attributes (layout, name_selector_entry->priv->attr_list);
1049
1050         return FALSE;
1051 }
1052
1053 static void
1054 type_ahead_complete (ENameSelectorEntry *name_selector_entry)
1055 {
1056         EContact      *contact;
1057         EBookClient   *book_client = NULL;
1058         EContactField  matched_field;
1059         EDestination  *destination;
1060         gint           cursor_pos;
1061         gint           range_start = 0;
1062         gint           range_end   = 0;
1063         gint           pos         = 0;
1064         gchar         *textrep;
1065         gint           textrep_len;
1066         gint           range_len;
1067         const gchar   *text;
1068         gchar         *cue_str;
1069         gchar         *temp_str;
1070         ENameSelectorEntryPrivate *priv;
1071
1072         priv = E_NAME_SELECTOR_ENTRY_GET_PRIVATE (name_selector_entry);
1073
1074         cursor_pos = gtk_editable_get_position (GTK_EDITABLE (name_selector_entry));
1075         if (cursor_pos < 0)
1076                 return;
1077
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)
1082                 return;
1083
1084         destination = find_destination_at_position (name_selector_entry, cursor_pos);
1085
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)) {
1089                 g_free (cue_str);
1090                 return;
1091         }
1092
1093         temp_str = sanitize_string (textrep);
1094         g_free (textrep);
1095         textrep = temp_str;
1096
1097         textrep_len = g_utf8_strlen (textrep, -1);
1098         pos         = range_start;
1099
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);
1109
1110         if (textrep_len > range_len) {
1111                 gint i;
1112
1113                 /* keep character's case as user types */
1114                 for (i = 0; textrep[i] && cue_str[i]; i++)
1115                         textrep[i] = cue_str[i];
1116
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),
1122                         textrep, -1, &pos);
1123                 gtk_editable_select_region (
1124                         GTK_EDITABLE (name_selector_entry),
1125                         range_end, range_start + textrep_len);
1126                 priv->is_completing = TRUE;
1127         }
1128         g_free (cue_str);
1129
1130         if (contact && destination) {
1131                 gint email_n = 0;
1132
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;
1135
1136                 e_destination_set_contact (destination, contact, email_n);
1137                 if (book_client)
1138                         e_destination_set_client (destination, book_client);
1139                 generate_attribute_list (name_selector_entry);
1140         }
1141
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);
1147
1148         g_free (textrep);
1149 }
1150
1151 static void
1152 clear_completion_model (ENameSelectorEntry *name_selector_entry)
1153 {
1154         ENameSelectorEntryPrivate *priv;
1155
1156         priv = E_NAME_SELECTOR_ENTRY_GET_PRIVATE (name_selector_entry);
1157
1158         if (!name_selector_entry->priv->contact_store)
1159                 return;
1160
1161         e_contact_store_set_query (name_selector_entry->priv->contact_store, NULL);
1162         priv->is_completing = FALSE;
1163 }
1164
1165 static void
1166 update_completion_model (ENameSelectorEntry *name_selector_entry)
1167 {
1168         const gchar *text;
1169         gint         cursor_pos;
1170         gint         range_start = 0;
1171         gint         range_end   = 0;
1172
1173         text       = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1174         cursor_pos = gtk_editable_get_position (GTK_EDITABLE (name_selector_entry));
1175
1176         if (cursor_pos >= 0)
1177                 get_range_at_position (text, cursor_pos, &range_start, &range_end);
1178
1179         if (range_end - range_start >= name_selector_entry->priv->minimum_query_length && cursor_pos == range_end) {
1180                 gchar *cue_str;
1181
1182                 cue_str = get_entry_substring (name_selector_entry, range_start, range_end);
1183                 set_completion_query (name_selector_entry, cue_str);
1184                 g_free (cue_str);
1185         } else {
1186                 /* N/A; Clear completion model */
1187                 clear_completion_model (name_selector_entry);
1188         }
1189 }
1190
1191 static gboolean
1192 type_ahead_complete_on_timeout_cb (ENameSelectorEntry *name_selector_entry)
1193 {
1194         type_ahead_complete (name_selector_entry);
1195         name_selector_entry->priv->type_ahead_complete_cb_id = 0;
1196         return FALSE;
1197 }
1198
1199 static gboolean
1200 update_completions_on_timeout_cb (ENameSelectorEntry *name_selector_entry)
1201 {
1202         update_completion_model (name_selector_entry);
1203         name_selector_entry->priv->update_completions_cb_id = 0;
1204         return FALSE;
1205 }
1206
1207 static void
1208 insert_destination_at_position (ENameSelectorEntry *name_selector_entry,
1209                                 gint pos)
1210 {
1211         EDestination *destination;
1212         const gchar  *text;
1213         gint          index;
1214
1215         text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1216         index = get_index_at_position (text, pos);
1217
1218         destination = build_destination_at_position (text, pos);
1219         g_assert (destination);
1220
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);
1231 }
1232
1233 static void
1234 modify_destination_at_position (ENameSelectorEntry *name_selector_entry,
1235                                 gint pos)
1236 {
1237         EDestination *destination;
1238         const gchar  *text;
1239         gchar        *raw_address;
1240         gboolean      rebuild_attributes = FALSE;
1241
1242         destination = find_destination_at_position (name_selector_entry, pos);
1243         if (!destination)
1244                 return;
1245
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);
1249
1250         if (e_destination_get_contact (destination))
1251                 rebuild_attributes = TRUE;
1252
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);
1260
1261         g_free (raw_address);
1262
1263         if (rebuild_attributes)
1264                 generate_attribute_list (name_selector_entry);
1265 }
1266
1267 static gchar *
1268 get_destination_textrep (ENameSelectorEntry *name_selector_entry,
1269                          EDestination *destination)
1270 {
1271         gboolean show_email = e_name_selector_entry_get_show_address (name_selector_entry);
1272         EContact *contact;
1273
1274         g_return_val_if_fail (destination != NULL, NULL);
1275
1276         contact = e_destination_get_contact (destination);
1277
1278         if (!show_email) {
1279                 if (contact && !e_contact_get (contact, E_CONTACT_IS_LIST)) {
1280                         GList *email_list;
1281
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);
1285                 }
1286         }
1287
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))
1290                 show_email = FALSE;
1291
1292         return sanitize_string (e_destination_get_textrep (destination, show_email));
1293 }
1294
1295 static void
1296 sync_destination_at_position (ENameSelectorEntry *name_selector_entry,
1297                               gint range_pos,
1298                               gint *cursor_pos)
1299 {
1300         EDestination *destination;
1301         const gchar  *text;
1302         gchar        *address;
1303         gint          address_len;
1304         gint          range_start, range_end;
1305
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);
1309         if (!destination)
1310                 return;
1311
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!");
1315                 return;
1316         }
1317
1318         address = get_destination_textrep (name_selector_entry, destination);
1319         address_len = g_utf8_strlen (address, -1);
1320
1321         if (cursor_pos) {
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;
1327         }
1328
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);
1331
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);
1334
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);
1337
1338         generate_attribute_list (name_selector_entry);
1339         g_free (address);
1340 }
1341
1342 static void
1343 remove_destination_by_index (ENameSelectorEntry *name_selector_entry,
1344                              gint index)
1345 {
1346         EDestination *destination;
1347
1348         destination = find_destination_by_index (name_selector_entry, index);
1349         if (destination) {
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,
1355                                                 destination);
1356                 g_signal_handlers_unblock_by_func (
1357                         name_selector_entry->priv->destination_store,
1358                         destination_row_deleted, name_selector_entry);
1359         }
1360 }
1361
1362 static void
1363 post_insert_update (ENameSelectorEntry *name_selector_entry,
1364                     gint position)
1365 {
1366         const gchar *text;
1367         glong length;
1368
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);
1372
1373         if (*text == '\0') {
1374                 /* First and only character, create initial destination. */
1375                 insert_destination_at_position (name_selector_entry, 0);
1376         } else {
1377                 /* Modified an existing destination. */
1378                 modify_destination_at_position (name_selector_entry, position);
1379         }
1380
1381         /* If editing within the string, regenerate attributes. */
1382         if (position < length)
1383                 generate_attribute_list (name_selector_entry);
1384 }
1385
1386 /* Returns the number of characters inserted */
1387 static gint
1388 insert_unichar (ENameSelectorEntry *name_selector_entry,
1389                 gint *pos,
1390                 gunichar c)
1391 {
1392         const gchar *text;
1393         gunichar     str_context[4];
1394         gchar        buf[7];
1395         gint         len;
1396
1397         text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1398         get_utf8_string_context (text, *pos, str_context, 4);
1399
1400         /* Space is not allowed:
1401          * - Before or after another space.
1402          * - At start of string. */
1403
1404         if (c == ' ' && (str_context[1] == ' ' || str_context[1] == '\0' || str_context[2] == ' '))
1405                 return 0;
1406
1407         /* Comma is not allowed:
1408          * - After another comma.
1409          * - At start of string. */
1410
1411         if (c == ',' && !is_quoted_at (text, *pos)) {
1412                 gint         start_pos;
1413                 gint         end_pos;
1414                 gboolean     at_start = FALSE;
1415                 gboolean     at_end   = FALSE;
1416
1417                 if (str_context[1] == ',' || str_context[1] == '\0')
1418                         return 0;
1419
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)
1424                         at_start = TRUE;
1425                 if (*pos >= end_pos)
1426                         at_end = TRUE;
1427
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);
1431
1432                 /* Update model */
1433                 g_assert (*pos >= 2);
1434
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). */
1438                 if (at_end) {
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);
1447                 } else {
1448                         /* Middle: */
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);
1452                 }
1453
1454                 return 2;
1455         }
1456
1457         /* Generic case. Allowed spaces also end up here. */
1458
1459         len = g_unichar_to_utf8 (c, buf);
1460         buf[len] = '\0';
1461
1462         gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), buf, -1, pos);
1463
1464         post_insert_update (name_selector_entry, *pos);
1465
1466         return 1;
1467 }
1468
1469 static void
1470 user_insert_text (ENameSelectorEntry *name_selector_entry,
1471                   gchar *new_text,
1472                   gint new_text_length,
1473                   gint *position,
1474                   gpointer user_data)
1475 {
1476         gint chars_inserted = 0;
1477         gboolean fast_insert;
1478
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);
1481
1482         fast_insert =
1483                 (g_utf8_strchr (new_text, new_text_length, ' ') == NULL) &&
1484                 (g_utf8_strchr (new_text, new_text_length, ',') == NULL);
1485
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. */
1489         if (fast_insert) {
1490                 gint old_position = *position;
1491
1492                 gtk_editable_insert_text (
1493                         GTK_EDITABLE (name_selector_entry),
1494                         new_text, new_text_length, position);
1495
1496                 chars_inserted = *position - old_position;
1497                 if (chars_inserted > 0)
1498                         post_insert_update (name_selector_entry, *position);
1499
1500         /* Otherwise, apply some rules as to where spaces and commas
1501          * can be inserted, and insert a trailing space after comma. */
1502         } else {
1503                 const gchar *cp;
1504
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);
1508                         chars_inserted++;
1509                 }
1510         }
1511
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);
1516         }
1517
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);
1520
1521         g_signal_stop_emission_by_name (name_selector_entry, "insert_text");
1522 }
1523
1524 static void
1525 user_delete_text (ENameSelectorEntry *name_selector_entry,
1526                   gint start_pos,
1527                   gint end_pos,
1528                   gpointer user_data)
1529 {
1530         const gchar *text;
1531         gint         index_start, index_end;
1532         gint         selection_start, selection_end;
1533         gunichar     str_context[2], str_b_context[2];
1534         gint         len;
1535         gint         i;
1536         gboolean     del_space = FALSE, del_comma = FALSE;
1537
1538         if (start_pos == end_pos)
1539                 return;
1540
1541         text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1542         len = g_utf8_strlen (text, -1);
1543
1544         if (end_pos == -1)
1545                 end_pos = len;
1546
1547         gtk_editable_get_selection_bounds (
1548                 GTK_EDITABLE (name_selector_entry),
1549                 &selection_start, &selection_end);
1550
1551         get_utf8_string_context (text, start_pos, str_context, 2);
1552         get_utf8_string_context (text, end_pos, str_b_context, 2);
1553
1554         g_signal_handlers_block_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1555
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);
1559         }
1560
1561         index_start = get_index_at_position (text, start_pos);
1562         index_end   = get_index_at_position (text, end_pos);
1563
1564         g_signal_stop_emission_by_name (name_selector_entry, "delete_text");
1565
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
1569          * removed.
1570          *
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;
1575                 gchar *ttext;
1576                 const gchar *email = NULL;
1577                 gboolean sel = FALSE;
1578
1579                 if (destination)
1580                         email = e_destination_get_textrep (destination, TRUE);
1581
1582                 if (!email || !*email)
1583                         continue;
1584
1585                 if (!get_range_by_index (text, i, &range_start, &range_end)) {
1586                         g_warning ("ENameSelectorEntry is out of sync with model!");
1587                         return;
1588                 }
1589
1590                 if ((selection_start < range_start && selection_end > range_start) ||
1591                     (selection_end > range_start && selection_end < range_end))
1592                         sel = TRUE;
1593
1594                 if (!sel) {
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);
1597
1598                         gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry), range_start, range_end);
1599
1600                         ttext = sanitize_string (email);
1601                         gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), ttext, -1, &range_start);
1602                         g_free (ttext);
1603
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);
1606
1607                 }
1608
1609                 remove_destination_by_index (name_selector_entry, i);
1610         }
1611
1612         /* Do the actual deletion */
1613
1614         if (end_pos == start_pos +1 &&  index_end == index_start) {
1615                 /* We could be just deleting the empty text */
1616                 gchar *c;
1617
1618                 /* Get the actual deleted text */
1619                 c = gtk_editable_get_chars (GTK_EDITABLE (name_selector_entry), start_pos, start_pos + 1);
1620
1621                 if ( c[0] == ' ') {
1622                         /* If we are at the beginning or removing junk space, let us ignore it */
1623                         del_space = TRUE;
1624                 }
1625                 g_free (c);
1626         } else  if (end_pos == start_pos +1 &&  index_end == index_start + 1) {
1627                 /* We could be just deleting the empty text */
1628                 gchar *c;
1629
1630                 /* Get the actual deleted text */
1631                 c = gtk_editable_get_chars (GTK_EDITABLE (name_selector_entry), start_pos, start_pos + 1);
1632
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 */
1635                         del_comma = TRUE;
1636                 }
1637                 g_free (c);
1638         }
1639
1640         if (del_comma) {
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
1644                  */
1645
1646                 if (dest && len - end_pos  != 0) {
1647
1648                         EDestination *destination1  = find_destination_by_index (name_selector_entry, index_start);
1649                         gchar *ttext;
1650                         const gchar *email = NULL;
1651
1652                         if (destination1)
1653                                 email = e_destination_get_textrep (destination1, TRUE);
1654
1655                         if (email && *email) {
1656
1657                                 if (!get_range_by_index (text, i, &range_start, &range_end)) {
1658                                         g_warning ("ENameSelectorEntry is out of sync with model!");
1659                                         return;
1660                                 }
1661
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);
1664
1665                                 gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry), range_start, range_end);
1666
1667                                 ttext = sanitize_string (email);
1668                                 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), ttext, -1, &range_start);
1669                                 g_free (ttext);
1670
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);
1673                         }
1674
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);
1679                         }
1680                 }
1681         }
1682         gtk_editable_delete_text (
1683                 GTK_EDITABLE (name_selector_entry),
1684                 start_pos, end_pos);
1685
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
1688          addresses.
1689         */
1690
1691         if (str_b_context[1] == '"') {
1692                 const gchar *p;
1693                 gint j;
1694                 p = text + end_pos;
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);
1697                         if (c == ',') {
1698                                 insert_destination_at_position (name_selector_entry, j + 1);
1699                         }
1700                 }
1701
1702         }
1703
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);
1712         }
1713
1714         /* If editing within the string, we need to regenerate attributes */
1715         if (end_pos < len)
1716                 generate_attribute_list (name_selector_entry);
1717
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;
1722         }
1723
1724         g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1725 }
1726
1727 static gboolean
1728 completion_match_selected (ENameSelectorEntry *name_selector_entry,
1729                            ETreeModelGenerator *email_generator_model,
1730                            GtkTreeIter *generator_iter)
1731 {
1732         EContact      *contact;
1733         EBookClient   *book_client;
1734         EDestination  *destination;
1735         gint           cursor_pos;
1736         GtkTreeIter    contact_iter;
1737         gint           email_n;
1738
1739         if (!name_selector_entry->priv->contact_store)
1740                 return FALSE;
1741
1742         g_return_val_if_fail (name_selector_entry->priv->email_generator == email_generator_model, FALSE);
1743
1744         e_tree_model_generator_convert_iter_to_child_iter (
1745                 email_generator_model,
1746                 &contact_iter, &email_n,
1747                 generator_iter);
1748
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));
1752
1753         /* Set the contact in the model's destination */
1754
1755         destination = find_destination_at_position (name_selector_entry, cursor_pos);
1756         e_destination_set_contact (destination, contact, email_n);
1757         if (book_client)
1758                 e_destination_set_client (destination, book_client);
1759         sync_destination_at_position (name_selector_entry, cursor_pos, &cursor_pos);
1760
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);
1764
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 */
1768
1769         gtk_editable_set_position (GTK_EDITABLE (name_selector_entry), cursor_pos);
1770         g_signal_emit (name_selector_entry, signals[UPDATED], 0, destination, NULL);
1771         return TRUE;
1772 }
1773
1774 static void
1775 entry_activate (ENameSelectorEntry *name_selector_entry)
1776 {
1777         gint         cursor_pos;
1778         gint         range_start, range_end;
1779         ENameSelectorEntryPrivate *priv;
1780         EDestination  *destination;
1781         gint           range_len;
1782         const gchar   *text;
1783         gchar         *cue_str;
1784
1785         cursor_pos = gtk_editable_get_position (GTK_EDITABLE (name_selector_entry));
1786         if (cursor_pos < 0)
1787                 return;
1788
1789         priv = E_NAME_SELECTOR_ENTRY_GET_PRIVATE (name_selector_entry);
1790
1791         text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1792         if (!get_range_at_position (text, cursor_pos, &range_start, &range_end))
1793                 return;
1794
1795         range_len = range_end - range_start;
1796         if (range_len < priv->minimum_query_length)
1797                 return;
1798
1799         destination = find_destination_at_position (name_selector_entry, cursor_pos);
1800         if (!destination)
1801                 return;
1802
1803         cue_str = get_entry_substring (name_selector_entry, range_start, range_end);
1804 #if 0
1805         if (!find_existing_completion (name_selector_entry, cue_str, &contact,
1806                                        &textrep, &matched_field)) {
1807                 g_free (cue_str);
1808                 return;
1809         }
1810 #endif
1811         g_free (cue_str);
1812         sync_destination_at_position (name_selector_entry, cursor_pos, &cursor_pos);
1813
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);
1817
1818         if (priv->is_completing) {
1819                 gchar *str_context = NULL;
1820
1821                 str_context = gtk_editable_get_chars (GTK_EDITABLE (name_selector_entry), range_end, range_end + 1);
1822
1823                 if (str_context[0] != ',') {
1824                         /* At the end*/
1825                         gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), ", ", -1, &range_end);
1826                 } else {
1827                         /* In the middle */
1828                         gint newpos = strlen (text);
1829
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);
1835
1836                         /* Move it close to next destination*/
1837                         range_end = range_end + 2;
1838
1839                 }
1840                 g_free (str_context);
1841         }
1842
1843         gtk_editable_set_position (GTK_EDITABLE (name_selector_entry), range_end);
1844         g_signal_emit (name_selector_entry, signals[UPDATED], 0, destination, NULL);
1845
1846         if (priv->is_completing)
1847                 clear_completion_model (name_selector_entry);
1848 }
1849
1850 static void
1851 update_text (ENameSelectorEntry *name_selector_entry,
1852              const gchar *text)
1853 {
1854         gint start = 0, end = 0;
1855         gboolean has_selection;
1856
1857         has_selection = gtk_editable_get_selection_bounds (GTK_EDITABLE (name_selector_entry), &start, &end);
1858
1859         gtk_entry_set_text (GTK_ENTRY (name_selector_entry), text);
1860
1861         if (has_selection)
1862                 gtk_editable_select_region (GTK_EDITABLE (name_selector_entry), start, end);
1863 }
1864
1865 static void
1866 sanitize_entry (ENameSelectorEntry *name_selector_entry)
1867 {
1868         gint n;
1869         GList *l, *known, *del = NULL;
1870         GString *str = g_string_new ("");
1871
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);
1874
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;
1878
1879                 if (!dest || !e_destination_get_address (dest))
1880                         del = g_list_prepend (del, GINT_TO_POINTER (n));
1881                 else {
1882                         gchar *text;
1883
1884                         text = get_destination_textrep (name_selector_entry, dest);
1885                         if (text) {
1886                                 if (str->str && str->str[0])
1887                                         g_string_append (str, ", ");
1888
1889                                 g_string_append (str, text);
1890                         }
1891                         g_free (text);
1892                 }
1893         }
1894         g_list_free (known);
1895
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));
1898         }
1899         g_list_free (del);
1900
1901         update_text (name_selector_entry, str->str);
1902
1903         g_string_free (str, TRUE);
1904
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);
1907
1908         generate_attribute_list (name_selector_entry);
1909 }
1910
1911 static gboolean
1912 user_focus_in (ENameSelectorEntry *name_selector_entry,
1913                GdkEventFocus *event_focus)
1914 {
1915         gint n;
1916         GList *l, *known;
1917         GString *str = g_string_new ("");
1918         EDestination *dest_dummy = e_destination_new ();
1919
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);
1922
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;
1926
1927                 if (dest) {
1928                         gchar *text;
1929
1930                         text = get_destination_textrep (name_selector_entry, dest);
1931                         if (text) {
1932                                 if (str->str && str->str[0])
1933                                         g_string_append (str, ", ");
1934
1935                                 g_string_append (str, text);
1936                         }
1937                         g_free (text);
1938                 }
1939         }
1940         g_list_free (known);
1941
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, ", ");
1946
1947         gtk_entry_set_text (GTK_ENTRY (name_selector_entry), str->str);
1948
1949         g_string_free (str, TRUE);
1950
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);
1953
1954         generate_attribute_list (name_selector_entry);
1955
1956         return FALSE;
1957 }
1958
1959 static gboolean
1960 user_focus_out (ENameSelectorEntry *name_selector_entry,
1961                 GdkEventFocus *event_focus)
1962 {
1963         if (!event_focus->in) {
1964                 entry_activate (name_selector_entry);
1965         }
1966
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;
1970         }
1971
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;
1975         }
1976
1977         clear_completion_model (name_selector_entry);
1978
1979         if (!event_focus->in) {
1980                 sanitize_entry (name_selector_entry);
1981         }
1982
1983         return FALSE;
1984 }
1985
1986 static void
1987 deep_free_list (GList *list)
1988 {
1989         GList *l;
1990
1991         for (l = list; l; l = g_list_next (l))
1992                 g_free (l->data);
1993
1994         g_list_free (list);
1995 }
1996
1997 /* Given a widget, determines the height that text will normally be drawn. */
1998 static guint
1999 entry_height (GtkWidget *widget)
2000 {
2001         PangoLayout *layout;
2002         gint bound;
2003
2004         g_return_val_if_fail (widget != NULL, 0);
2005
2006         layout = gtk_widget_create_pango_layout (widget, NULL);
2007
2008         pango_layout_get_pixel_size (layout, NULL, &bound);
2009
2010         return bound;
2011 }
2012
2013 static void
2014 contact_layout_pixbuffer (GtkCellLayout *cell_layout,
2015                           GtkCellRenderer *cell,
2016                           GtkTreeModel *model,
2017                           GtkTreeIter *iter,
2018                           ENameSelectorEntry *name_selector_entry)
2019 {
2020         EContact      *contact;
2021         GtkTreeIter    generator_iter;
2022         GtkTreeIter    contact_store_iter;
2023         gint           email_n;
2024         EContactPhoto *photo;
2025         GdkPixbuf *pixbuf = NULL;
2026
2027         if (!name_selector_entry->priv->contact_store)
2028                 return;
2029
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,
2036                 &generator_iter);
2037
2038         contact = e_contact_store_get_contact (name_selector_entry->priv->contact_store, &contact_store_iter);
2039         if (!contact) {
2040                 g_object_set (cell, "pixbuf", pixbuf, NULL);
2041                 return;
2042         }
2043
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;
2048
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);
2053                         if (pixbuf)
2054                                 g_object_ref (pixbuf);
2055                 }
2056                 g_object_unref (loader);
2057
2058                 if (pixbuf) {
2059                         gint w, h;
2060                         gdouble scale = 1.0;
2061
2062                         w = gdk_pixbuf_get_width (pixbuf);
2063                         h = gdk_pixbuf_get_height (pixbuf);
2064
2065                         if (h > w)
2066                                 scale = max_height / (double) h;
2067                         else
2068                                 scale = max_height / (double) w;
2069
2070                         if (scale < 1.0) {
2071                                 GdkPixbuf *tmp;
2072
2073                                 tmp = gdk_pixbuf_scale_simple (pixbuf, w * scale, h * scale, GDK_INTERP_BILINEAR);
2074                                 g_object_unref (pixbuf);
2075                                 pixbuf = tmp;
2076                         }
2077
2078                 }
2079         }
2080
2081         e_contact_photo_free (photo);
2082
2083         g_object_set (cell, "pixbuf", pixbuf, NULL);
2084
2085         if (pixbuf)
2086                 g_object_unref (pixbuf);
2087 }
2088
2089 static void
2090 contact_layout_formatter (GtkCellLayout *cell_layout,
2091                           GtkCellRenderer *cell,
2092                           GtkTreeModel *model,
2093                           GtkTreeIter *iter,
2094                           ENameSelectorEntry *name_selector_entry)
2095 {
2096         EContact      *contact;
2097         GtkTreeIter    generator_iter;
2098         GtkTreeIter    contact_store_iter;
2099         GList         *email_list;
2100         gchar         *string;
2101         gchar         *file_as_str;
2102         gchar         *email_str;
2103         gint           email_n;
2104
2105         if (!name_selector_entry->priv->contact_store)
2106                 return;
2107
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,
2114                 &generator_iter);
2115
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);
2120
2121         if (e_contact_get (contact, E_CONTACT_IS_LIST)) {
2122                 string = g_strdup_printf ("%s", file_as_str ? file_as_str : "?");
2123         } else {
2124                 string = g_strdup_printf (
2125                         "%s%s<%s>", file_as_str ? file_as_str : "",
2126                         file_as_str ? " " : "",
2127                         email_str ? email_str : "");
2128         }
2129
2130         g_free (file_as_str);
2131         deep_free_list (email_list);
2132
2133         g_object_set (cell, "text", string, NULL);
2134         g_free (string);
2135 }
2136
2137 static gint
2138 generate_contact_rows (EContactStore *contact_store,
2139                        GtkTreeIter *iter,
2140                        ENameSelectorEntry *name_selector_entry)
2141 {
2142         EContact    *contact;
2143         const gchar *contact_uid;
2144         GList       *email_list;
2145         gint         n_rows;
2146
2147         contact = e_contact_store_get_contact (contact_store, iter);
2148         g_assert (contact != NULL);
2149
2150         contact_uid = e_contact_get_const (contact, E_CONTACT_UID);
2151         if (!contact_uid)
2152                 return 0;  /* Can happen with broken databases */
2153
2154         if (e_contact_get (contact, E_CONTACT_IS_LIST))
2155                 return 1;
2156
2157         email_list = e_contact_get (contact, E_CONTACT_EMAIL);
2158         n_rows = g_list_length (email_list);
2159         deep_free_list (email_list);
2160
2161         return n_rows;
2162 }
2163
2164 static void
2165 ensure_type_ahead_complete_on_timeout (ENameSelectorEntry *name_selector_entry)
2166 {
2167         re_set_timeout (
2168                 name_selector_entry->priv->type_ahead_complete_cb_id,
2169                 type_ahead_complete_on_timeout_cb, name_selector_entry);
2170 }
2171
2172 static void
2173 setup_contact_store (ENameSelectorEntry *name_selector_entry)
2174 {
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;
2178         }
2179
2180         if (name_selector_entry->priv->contact_store) {
2181                 name_selector_entry->priv->email_generator =
2182                         e_tree_model_generator_new (
2183                                 GTK_TREE_MODEL (
2184                                 name_selector_entry->priv->contact_store));
2185
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);
2190
2191                 /* Assign the store to the entry completion */
2192
2193                 gtk_entry_completion_set_model (
2194                         name_selector_entry->priv->entry_completion,
2195                         GTK_TREE_MODEL (
2196                         name_selector_entry->priv->email_generator));
2197
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);
2202         } else {
2203                 /* Remove the store from the entry completion */
2204
2205                 gtk_entry_completion_set_model (name_selector_entry->priv->entry_completion, NULL);
2206         }
2207 }
2208
2209 static void
2210 book_loaded_cb (GObject *source_object,
2211                 GAsyncResult *result,
2212                 gpointer user_data)
2213 {
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;
2219
2220         e_client_utils_open_new_finish (source, result, &client, &error);
2221
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);
2225                 goto exit;
2226         }
2227
2228         if (error != NULL) {
2229                 g_warning ("%s", error->message);
2230                 g_warn_if_fail (client == NULL);
2231                 g_error_free (error);
2232                 goto exit;
2233         }
2234
2235         book_client = E_BOOK_CLIENT (client);
2236
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);
2240
2241  exit:
2242         g_object_unref (contact_store);
2243 }
2244
2245 static void
2246 setup_default_contact_store (ENameSelectorEntry *name_selector_entry)
2247 {
2248         ESourceRegistry *registry;
2249         EContactStore *contact_store;
2250         GList *list, *iter;
2251         const gchar *extension_name;
2252
2253         g_return_if_fail (name_selector_entry->priv->contact_store == NULL);
2254
2255         /* Create a book for each completion source, and assign them to the contact store */
2256
2257         contact_store = e_contact_store_new ();
2258         name_selector_entry->priv->contact_store = contact_store;
2259
2260         extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
2261         registry = e_name_selector_entry_get_registry (name_selector_entry);
2262
2263         /* An ESourceRegistry should have been set by now. */
2264         g_return_if_fail (registry != NULL);
2265
2266         list = e_source_registry_list_sources (registry, extension_name);
2267
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;
2273
2274                 extension_name = E_SOURCE_EXTENSION_AUTOCOMPLETE;
2275                 extension = e_source_get_extension (source, extension_name);
2276
2277                 /* Skip disabled address books. */
2278                 if (!e_source_registry_check_enabled (registry, source))
2279                         continue;
2280
2281                 /* Skip non-completion address books. */
2282                 if (!e_source_autocomplete_get_include_me (extension))
2283                         continue;
2284
2285                 cancellable = g_cancellable_new ();
2286
2287                 g_queue_push_tail (
2288                         &name_selector_entry->priv->cancellables,
2289                         cancellable);
2290
2291                 e_client_utils_open_new (
2292                         source, E_CLIENT_SOURCE_TYPE_CONTACTS, TRUE, cancellable,
2293                         book_loaded_cb, g_object_ref (contact_store));
2294         }
2295
2296         g_list_free_full (list, (GDestroyNotify) g_object_unref);
2297
2298         setup_contact_store (name_selector_entry);
2299 }
2300
2301 static void
2302 destination_row_changed (ENameSelectorEntry *name_selector_entry,
2303                          GtkTreePath *path,
2304                          GtkTreeIter *iter)
2305 {
2306         EDestination *destination;
2307         const gchar  *entry_text;
2308         gchar        *text;
2309         gint          range_start, range_end;
2310         gint          n;
2311
2312         n = gtk_tree_path_get_indices (path)[0];
2313         destination = e_destination_store_get_destination (name_selector_entry->priv->destination_store, iter);
2314
2315         if (!destination)
2316                 return;
2317
2318         g_assert (n >= 0);
2319
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!");
2323                 return;
2324         }
2325
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);
2328
2329         gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry), range_start, range_end);
2330
2331         text = get_destination_textrep (name_selector_entry, destination);
2332         gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), text, -1, &range_start);
2333         g_free (text);
2334
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);
2337
2338         clear_completion_model (name_selector_entry);
2339         generate_attribute_list (name_selector_entry);
2340 }
2341
2342 static void
2343 destination_row_inserted (ENameSelectorEntry *name_selector_entry,
2344                           GtkTreePath *path,
2345                           GtkTreeIter *iter)
2346 {
2347         EDestination *destination;
2348         const gchar  *entry_text;
2349         gchar        *text;
2350         gboolean      comma_before = FALSE;
2351         gboolean      comma_after  = FALSE;
2352         gint          range_start, range_end;
2353         gint          insert_pos;
2354         gint          n;
2355
2356         n = gtk_tree_path_get_indices (path)[0];
2357         destination = e_destination_store_get_destination (name_selector_entry->priv->destination_store, iter);
2358
2359         g_assert (n >= 0);
2360         g_assert (destination != NULL);
2361
2362         entry_text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
2363
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;
2367                 comma_after = TRUE;
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 */
2374                 insert_pos = 0;
2375         } else {
2376                 g_warning ("ENameSelectorEntry is out of sync with model!");
2377                 return;
2378         }
2379
2380         g_signal_handlers_block_by_func (name_selector_entry, user_insert_text, name_selector_entry);
2381
2382         if (comma_before)
2383                 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), ", ", -1, &insert_pos);
2384
2385         text = get_destination_textrep (name_selector_entry, destination);
2386         gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), text, -1, &insert_pos);
2387         g_free (text);
2388
2389         if (comma_after)
2390                 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), ", ", -1, &insert_pos);
2391
2392         g_signal_handlers_unblock_by_func (name_selector_entry, user_insert_text, name_selector_entry);
2393
2394         clear_completion_model (name_selector_entry);
2395         generate_attribute_list (name_selector_entry);
2396 }
2397
2398 static void
2399 destination_row_deleted (ENameSelectorEntry *name_selector_entry,
2400                          GtkTreePath *path)
2401 {
2402         const gchar *text;
2403         gboolean     deleted_comma = FALSE;
2404         gint         range_start, range_end;
2405         gchar       *p0;
2406         gint         n;
2407
2408         n = gtk_tree_path_get_indices (path)[0];
2409         g_assert (n >= 0);
2410
2411         text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
2412
2413         if (!get_range_by_index (text, n, &range_start, &range_end)) {
2414                 g_warning ("ENameSelectorEntry is out of sync with model!");
2415                 return;
2416         }
2417
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);
2422
2423                 /* Gobble spaces directly after comma */
2424                 if (c != ' ' && deleted_comma) {
2425                         range_end--;
2426                         break;
2427                 }
2428
2429                 if (c == ',') {
2430                         deleted_comma = TRUE;
2431                         range_end++;
2432                 }
2433         }
2434
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);
2439
2440                 if (c == ',') {
2441                         if (!deleted_comma) {
2442                                 deleted_comma = TRUE;
2443                                 break;
2444                         }
2445
2446                         range_start++;
2447
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);
2452                         if (c == ' ')
2453                                 range_start++;
2454
2455                         break;
2456                 }
2457         }
2458
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);
2462
2463         clear_completion_model (name_selector_entry);
2464         generate_attribute_list (name_selector_entry);
2465 }
2466
2467 static void
2468 setup_destination_store (ENameSelectorEntry *name_selector_entry)
2469 {
2470         GtkTreeIter  iter;
2471
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);
2481
2482         if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (name_selector_entry->priv->destination_store), &iter))
2483                 return;
2484
2485         do {
2486                 GtkTreePath *path;
2487
2488                 path = gtk_tree_model_get_path (GTK_TREE_MODEL (name_selector_entry->priv->destination_store), &iter);
2489                 g_assert (path);
2490
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));
2493 }
2494
2495 static gboolean
2496 prepare_popup_destination (ENameSelectorEntry *name_selector_entry,
2497                            GdkEventButton *event_button)
2498 {
2499         EDestination *destination;
2500         PangoLayout  *layout;
2501         gint          layout_offset_x;
2502         gint          layout_offset_y;
2503         gint          x, y;
2504         gint          index;
2505
2506         if (event_button->type != GDK_BUTTON_PRESS)
2507                 return FALSE;
2508
2509         if (event_button->button != 3)
2510                 return FALSE;
2511
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;
2515         }
2516
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;
2522
2523         if (x < 0 || y < 0)
2524                 return FALSE;
2525
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))
2528                 return FALSE;
2529
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));
2534
2535         if (!destination || !e_destination_get_contact (destination))
2536                 return FALSE;
2537
2538         /* TODO: Unref destination when we finalize */
2539         name_selector_entry->priv->popup_destination = g_object_ref (destination);
2540         return FALSE;
2541 }
2542
2543 static EBookClient *
2544 find_client_by_contact (GSList *clients,
2545                         const gchar *contact_uid,
2546                         const gchar *source_uid)
2547 {
2548         GSList *l;
2549
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));
2555
2556                         if (!source)
2557                                 continue;
2558
2559                         if (g_strcmp0 (source_uid, e_source_get_uid (source)) == 0)
2560                                 return client;
2561                 }
2562         }
2563
2564         for (l = clients; l; l = g_slist_next (l)) {
2565                 EBookClient *client = l->data;
2566                 EContact *contact = NULL;
2567                 gboolean  result;
2568
2569                 result = e_book_client_get_contact_sync (client, contact_uid, &contact, NULL, NULL);
2570                 if (contact)
2571                         g_object_unref (contact);
2572
2573                 if (result)
2574                         return client;
2575         }
2576
2577         return NULL;
2578 }
2579
2580 static void
2581 editor_closed_cb (GtkWidget *editor,
2582                   gpointer data)
2583 {
2584         EContact *contact;
2585         gchar *contact_uid;
2586         EDestination *destination;
2587         GSList *clients;
2588         EBookClient *book_client;
2589         gint email_num;
2590         ENameSelectorEntry *name_selector_entry = E_NAME_SELECTOR_ENTRY (data);
2591
2592         destination = name_selector_entry->priv->popup_destination;
2593         contact = e_destination_get_contact (destination);
2594         if (!contact) {
2595                 g_object_unref (name_selector_entry);
2596                 return;
2597         }
2598
2599         contact_uid = e_contact_get (contact, E_CONTACT_UID);
2600         if (!contact_uid) {
2601                 g_object_unref (contact);
2602                 g_object_unref (name_selector_entry);
2603                 return;
2604         }
2605
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);
2610         } else {
2611                 book_client = NULL;
2612         }
2613
2614         if (book_client) {
2615                 contact = NULL;
2616
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);
2621         } else {
2622                 contact = NULL;
2623         }
2624
2625         g_free (contact_uid);
2626         if (contact)
2627                 g_object_unref (contact);
2628         g_object_unref (name_selector_entry);
2629 }
2630
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.
2634  * */
2635 static gboolean
2636 eab_parse_qp_email (const gchar *string,
2637                     gchar **name,
2638                     gchar **email)
2639 {
2640         struct _camel_header_address *address;
2641         gboolean res = FALSE;
2642
2643         address = camel_header_address_decode (string, "UTF-8");
2644
2645         if (!address)
2646                 return FALSE;
2647
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);
2652                 res = TRUE;
2653         }
2654
2655         camel_header_address_unref (address);
2656
2657         return res;
2658 }
2659
2660 static void
2661 popup_activate_inline_expand (ENameSelectorEntry *name_selector_entry,
2662                               GtkWidget *menu_item)
2663 {
2664         const gchar *text;
2665         GString *sanitized_text = g_string_new ("");
2666         EDestination *destination = name_selector_entry->priv->popup_destination;
2667         gint position, start, end;
2668         const GList *dests;
2669
2670         position = GPOINTER_TO_INT (g_object_get_data ((GObject *) name_selector_entry, "index"));
2671
2672         for (dests = e_destination_list_get_dests (destination); dests; dests = dests->next) {
2673                 const EDestination *dest = dests->data;
2674                 gchar *sanitized;
2675                 gchar *name = NULL, *email = NULL, *tofree = NULL;
2676
2677                 if (!dest)
2678                         continue;
2679
2680                 text = e_destination_get_textrep (dest, TRUE);
2681
2682                 if (!text || !*text)
2683                         continue;
2684
2685                 if (eab_parse_qp_email (text, &name, &email)) {
2686                         tofree = g_strdup_printf ("%s <%s>", name, email);
2687                         text = tofree;
2688                         g_free (name);
2689                         g_free (email);
2690                 }
2691
2692                 sanitized = sanitize_string (text);
2693                 g_free (tofree);
2694                 if (!sanitized)
2695                         continue;
2696
2697                 if (*sanitized) {
2698                         if (*sanitized_text->str)
2699                                 g_string_append (sanitized_text, ", ");
2700
2701                         g_string_append (sanitized_text, sanitized);
2702                 }
2703
2704                 g_free (sanitized);
2705         }
2706
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);
2712
2713         clear_completion_model (name_selector_entry);
2714         generate_attribute_list (name_selector_entry);
2715 }
2716
2717 static void
2718 popup_activate_contact (ENameSelectorEntry *name_selector_entry,
2719                         GtkWidget *menu_item)
2720 {
2721         EBookClient  *book_client;
2722         GSList       *clients;
2723         EDestination *destination;
2724         EContact     *contact;
2725         gchar        *contact_uid;
2726
2727         destination = name_selector_entry->priv->popup_destination;
2728         if (!destination)
2729                 return;
2730
2731         contact = e_destination_get_contact (destination);
2732         if (!contact)
2733                 return;
2734
2735         contact_uid = e_contact_get (contact, E_CONTACT_UID);
2736         if (!contact_uid)
2737                 return;
2738
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);
2744         } else {
2745                 book_client = NULL;
2746         }
2747
2748         if (!book_client)
2749                 return;
2750
2751         if (e_destination_is_evolution_list (destination)) {
2752                 GtkWidget *contact_list_editor;
2753
2754                 if (!name_selector_entry->priv->contact_list_editor_func)
2755                         return;
2756
2757                 contact_list_editor = (*name_selector_entry->priv->contact_list_editor_func) (book_client, contact, FALSE, TRUE);
2758                 g_object_ref (name_selector_entry);
2759                 g_signal_connect (
2760                         contact_list_editor, "editor_closed",
2761                         G_CALLBACK (editor_closed_cb), name_selector_entry);
2762         } else {
2763                 GtkWidget *contact_editor;
2764
2765                 if (!name_selector_entry->priv->contact_editor_func)
2766                         return;
2767
2768                 contact_editor = (*name_selector_entry->priv->contact_editor_func) (book_client, contact, FALSE, TRUE);
2769                 g_object_ref (name_selector_entry);
2770                 g_signal_connect (
2771                         contact_editor, "editor_closed",
2772                         G_CALLBACK (editor_closed_cb), name_selector_entry);
2773         }
2774 }
2775
2776 static void
2777 popup_activate_email (ENameSelectorEntry *name_selector_entry,
2778                       GtkWidget *menu_item)
2779 {
2780         EDestination *destination;
2781         EContact     *contact;
2782         gint          email_num;
2783
2784         destination = name_selector_entry->priv->popup_destination;
2785         if (!destination)
2786                 return;
2787
2788         contact = e_destination_get_contact (destination);
2789         if (!contact)
2790                 return;
2791
2792         email_num = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu_item), "order"));
2793         e_destination_set_contact (destination, contact, email_num);
2794 }
2795
2796 static void
2797 popup_activate_list (EDestination *destination,
2798                      GtkWidget *item)
2799 {
2800         gboolean status = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item));
2801
2802         e_destination_set_ignored (destination, !status);
2803 }
2804
2805 static void
2806 popup_activate_cut (ENameSelectorEntry *name_selector_entry,
2807                     GtkWidget *menu_item)
2808 {
2809         EDestination *destination;
2810         const gchar *contact_email;
2811         gchar *pemail = NULL;
2812         GtkClipboard *clipboard;
2813
2814         destination = name_selector_entry->priv->popup_destination;
2815         contact_email =e_destination_get_textrep (destination, TRUE);
2816
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);
2819
2820         clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
2821         pemail = g_strconcat (contact_email, ",", NULL);
2822         gtk_clipboard_set_text (clipboard, pemail, strlen (pemail));
2823
2824         clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
2825         gtk_clipboard_set_text (clipboard, pemail, strlen (pemail));
2826
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);
2829
2830         g_free (pemail);
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);
2833 }
2834
2835 static void
2836 popup_activate_copy (ENameSelectorEntry *name_selector_entry,
2837                      GtkWidget *menu_item)
2838 {
2839         EDestination *destination;
2840         const gchar *contact_email;
2841         gchar *pemail;
2842         GtkClipboard *clipboard;
2843
2844         destination = name_selector_entry->priv->popup_destination;
2845         contact_email = e_destination_get_textrep (destination, TRUE);
2846
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);
2849
2850         clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
2851         pemail = g_strconcat (contact_email, ",", NULL);
2852         gtk_clipboard_set_text (clipboard, pemail, strlen (pemail));
2853
2854         clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
2855         gtk_clipboard_set_text (clipboard, pemail, strlen (pemail));
2856         g_free (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);
2859 }
2860
2861 static void
2862 destination_set_list (GtkWidget *item,
2863                       EDestination *destination)
2864 {
2865         EContact *contact;
2866         gboolean status = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item));
2867
2868         contact = e_destination_get_contact (destination);
2869         if (!contact)
2870                 return;
2871
2872         e_destination_set_ignored (destination, !status);
2873 }
2874
2875 static void
2876 destination_set_email (GtkWidget *item,
2877                        EDestination *destination)
2878 {
2879         gint email_num;
2880         EContact *contact;
2881
2882         if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item)))
2883                 return;
2884         contact = e_destination_get_contact (destination);
2885         if (!contact)
2886                 return;
2887
2888         email_num = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "order"));
2889         e_destination_set_contact (destination, contact, email_num);
2890 }
2891
2892 static void
2893 populate_popup (ENameSelectorEntry *name_selector_entry,
2894                 GtkMenu *menu)
2895 {
2896         EDestination *destination;
2897         EContact     *contact;
2898         GtkWidget    *menu_item;
2899         GList        *email_list = NULL;
2900         GList        *l;
2901         gint          i;
2902         gchar        *edit_label;
2903         gchar        *cut_label;
2904         gchar         *copy_label;
2905         gint          email_num, len;
2906         GSList       *group = NULL;
2907         gboolean      is_list;
2908         gboolean      show_menu = FALSE;
2909
2910         destination = name_selector_entry->priv->popup_destination;
2911         if (!destination)
2912                 return;
2913
2914         contact = e_destination_get_contact (destination);
2915         if (!contact)
2916                 return;
2917
2918         /* Prepend the menu items, backwards */
2919
2920         /* Separator */
2921
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);
2926
2927         /* Addresses */
2928         is_list = e_contact_get (contact, E_CONTACT_IS_LIST) ? TRUE : FALSE;
2929         if (is_list) {
2930                 const GList *dests = e_destination_list_get_dests (destination);
2931                 GList *iter;
2932                 gint length = g_list_length ((GList *) dests);
2933
2934                 for (iter = (GList *) dests; iter; iter = iter->next) {
2935                         EDestination *dest = (EDestination *) iter->data;
2936                         const gchar *email = e_destination_get_email (dest);
2937
2938                         if (!email || *email == '\0')
2939                                 continue;
2940
2941                         if (length > 1) {
2942                                 menu_item = gtk_check_menu_item_new_with_label (email);
2943                                 g_signal_connect (
2944                                         menu_item, "toggled",
2945                                         G_CALLBACK (destination_set_list), dest);
2946                         } else {
2947                                 menu_item = gtk_menu_item_new_with_label (email);
2948                         }
2949
2950                         gtk_widget_show (menu_item);
2951                         gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
2952                         show_menu = TRUE;
2953
2954                         if (length > 1) {
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);
2961                         }
2962                 }
2963
2964         } else {
2965                 email_list = e_contact_get (contact, E_CONTACT_EMAIL);
2966                 len = g_list_length (email_list);
2967
2968                 for (l = email_list, i = 0; l; l = g_list_next (l), i++) {
2969                         gchar *email = l->data;
2970
2971                         if (!email || *email == '\0')
2972                                 continue;
2973
2974                         if (len > 1) {
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);
2978                         } else {
2979                                 menu_item = gtk_menu_item_new_with_label (email);
2980                         }
2981
2982                         gtk_widget_show (menu_item);
2983                         gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
2984                         show_menu = TRUE;
2985                         g_object_set_data (G_OBJECT (menu_item), "order", GINT_TO_POINTER (i));
2986
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);
2993                         }
2994                 }
2995         }
2996
2997         /* Separator */
2998
2999         if (show_menu) {
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);
3003         }
3004
3005         /* Expand a list inline */
3006         if (is_list) {
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);
3016
3017                 /* Separator */
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);
3021         }
3022
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);
3029
3030         g_signal_connect_swapped (
3031                 menu_item, "activate", G_CALLBACK (popup_activate_copy),
3032                 name_selector_entry);
3033
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);
3037         g_free (cut_label);
3038         gtk_widget_show (menu_item);
3039         gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
3040
3041         g_signal_connect_swapped (
3042                 menu_item, "activate", G_CALLBACK (popup_activate_cut),
3043                 name_selector_entry);
3044
3045         if (show_menu) {
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);
3049         }
3050
3051         /* Edit Contact item */
3052
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);
3058
3059         g_signal_connect_swapped (
3060                 menu_item, "activate", G_CALLBACK (popup_activate_contact),
3061                 name_selector_entry);
3062
3063         deep_free_list (email_list);
3064 }
3065
3066 static void
3067 copy_or_cut_clipboard (ENameSelectorEntry *name_selector_entry,
3068                        gboolean is_cut)
3069 {
3070         GtkClipboard *clipboard;
3071         GtkEditable *editable;
3072         const gchar *text, *cp;
3073         GHashTable *hash;
3074         GHashTableIter iter;
3075         gpointer key, value;
3076         GString *addresses;
3077         gint ii, start, end;
3078         gunichar uc;
3079
3080         editable = GTK_EDITABLE (name_selector_entry);
3081         text = gtk_entry_get_text (GTK_ENTRY (editable));
3082
3083         if (!gtk_editable_get_selection_bounds (editable, &start, &end))
3084                 return;
3085
3086         g_return_if_fail (end > start);
3087
3088         hash = g_hash_table_new (g_direct_hash, g_direct_equal);
3089
3090         ii = end;
3091         cp = g_utf8_offset_to_pointer (text, end);
3092         uc = g_utf8_get_char (cp);
3093
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);
3098                 ii--;
3099         }
3100
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);
3105         }
3106
3107         addresses = g_string_new ("");
3108
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);
3112                 EDestination *dest;
3113                 gint rstart, rend;
3114
3115                 if (!get_range_by_index (text, index, &rstart, &rend))
3116                         continue;
3117
3118                 if (rstart < start) {
3119                         if (addresses->str && *addresses->str)
3120                                 g_string_append (addresses, ", ");
3121
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, ", ");
3126
3127                         g_string_append_len (addresses, text + rstart, end - rstart);
3128                 } else {
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, ", ");
3134
3135                                 g_string_append (addresses, e_destination_get_textrep (dest, TRUE));
3136
3137                                 /* store the 'dest' as a value for the index */
3138                                 g_hash_table_insert (hash, GINT_TO_POINTER (index), dest);
3139                         } else
3140                                 g_string_append_len (addresses, text + rstart, rend - rstart);
3141                 }
3142         }
3143
3144         if (is_cut)
3145                 gtk_editable_delete_text (editable, start, end);
3146
3147         g_hash_table_unref (hash);
3148
3149         clipboard = gtk_widget_get_clipboard (
3150                 GTK_WIDGET (name_selector_entry), GDK_SELECTION_CLIPBOARD);
3151         gtk_clipboard_set_text (clipboard, addresses->str, -1);
3152
3153         g_string_free (addresses, TRUE);
3154 }
3155
3156 static void
3157 copy_clipboard (GtkEntry *entry,
3158                 ENameSelectorEntry *name_selector_entry)
3159 {
3160         copy_or_cut_clipboard (name_selector_entry, FALSE);
3161         g_signal_stop_emission_by_name (entry, "copy-clipboard");
3162 }
3163
3164 static void
3165 cut_clipboard (GtkEntry *entry,
3166                ENameSelectorEntry *name_selector_entry)
3167 {
3168         copy_or_cut_clipboard (name_selector_entry, TRUE);
3169         g_signal_stop_emission_by_name (entry, "cut-clipboard");
3170 }
3171
3172 static void
3173 e_name_selector_entry_init (ENameSelectorEntry *name_selector_entry)
3174 {
3175         GtkCellRenderer *renderer;
3176
3177         name_selector_entry->priv =
3178                 E_NAME_SELECTOR_ENTRY_GET_PRIVATE (name_selector_entry);
3179
3180         g_queue_init (&name_selector_entry->priv->cancellables);
3181
3182         name_selector_entry->priv->minimum_query_length = 3;
3183         name_selector_entry->priv->show_address = FALSE;
3184
3185         /* Edit signals */
3186
3187         g_signal_connect (
3188                 name_selector_entry, "insert-text",
3189                 G_CALLBACK (user_insert_text), name_selector_entry);
3190         g_signal_connect (
3191                 name_selector_entry, "delete-text",
3192                 G_CALLBACK (user_delete_text), name_selector_entry);
3193         g_signal_connect (
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);
3199
3200         /* Drawing */
3201
3202         g_signal_connect (
3203                 name_selector_entry, "draw",
3204                 G_CALLBACK (draw_event), name_selector_entry);
3205
3206         /* Activation: Complete current entry if possible */
3207
3208         g_signal_connect (
3209                 name_selector_entry, "activate",
3210                 G_CALLBACK (entry_activate), name_selector_entry);
3211
3212         /* Pop-up menu */
3213
3214         g_signal_connect (
3215                 name_selector_entry, "button-press-event",
3216                 G_CALLBACK (prepare_popup_destination), name_selector_entry);
3217         g_signal_connect (
3218                 name_selector_entry, "populate-popup",
3219                 G_CALLBACK (populate_popup), name_selector_entry);
3220
3221         /* Clipboard signals */
3222         g_signal_connect (
3223                 name_selector_entry, "copy-clipboard",
3224                 G_CALLBACK (copy_clipboard), name_selector_entry);
3225         g_signal_connect (
3226                 name_selector_entry, "cut-clipboard",
3227                 G_CALLBACK (cut_clipboard), name_selector_entry);
3228
3229         /* Completion */
3230
3231         name_selector_entry->priv->email_generator = NULL;
3232
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);
3240
3241         gtk_entry_set_completion (
3242                 GTK_ENTRY (name_selector_entry),
3243                 name_selector_entry->priv->entry_completion);
3244
3245         renderer = gtk_cell_renderer_pixbuf_new ();
3246         gtk_cell_layout_pack_start (
3247                 GTK_CELL_LAYOUT (name_selector_entry->priv->entry_completion),
3248                 renderer, FALSE);
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);
3254
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),
3259                 renderer, TRUE);
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);
3265
3266         /* Destination store */
3267
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;
3271 }
3272
3273 /**
3274  * e_name_selector_entry_new:
3275  *
3276  * Creates a new #ENameSelectorEntry.
3277  *
3278  * Returns: A new #ENameSelectorEntry.
3279  **/
3280 ENameSelectorEntry *
3281 e_name_selector_entry_new (ESourceRegistry *registry)
3282 {
3283         g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
3284
3285         return g_object_new (
3286                 E_TYPE_NAME_SELECTOR_ENTRY,
3287                 "registry", registry, NULL);
3288 }
3289
3290 /**
3291  * e_name_selector_entry_get_registry:
3292  * @name_selector_entry: an #ENameSelectorEntry
3293  *
3294  * Returns the #ESourceRegistry used to query address books.
3295  *
3296  * Returns: the #ESourceRegistry, or %NULL
3297  *
3298  * Since: 3.6
3299  **/
3300 ESourceRegistry *
3301 e_name_selector_entry_get_registry (ENameSelectorEntry *name_selector_entry)
3302 {
3303         g_return_val_if_fail (
3304                 E_IS_NAME_SELECTOR_ENTRY (name_selector_entry), NULL);
3305
3306         return name_selector_entry->priv->registry;
3307 }
3308
3309 /**
3310  * e_name_selector_entry_set_registry:
3311  * @name_selector_entry: an #ENameSelectorEntry
3312  * @registry: an #ESourceRegistry
3313  *
3314  * Sets the #ESourceRegistry used to query address books.
3315  *
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.
3319  *
3320  * Since: 3.6
3321  **/
3322 void
3323 e_name_selector_entry_set_registry (ENameSelectorEntry *name_selector_entry,
3324                                     ESourceRegistry *registry)
3325 {
3326         g_return_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry));
3327
3328         if (name_selector_entry->priv->registry == registry)
3329                 return;
3330
3331         if (registry != NULL) {
3332                 g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
3333                 g_object_ref (registry);
3334         }
3335
3336         if (name_selector_entry->priv->registry != NULL)
3337                 g_object_unref (name_selector_entry->priv->registry);
3338
3339         name_selector_entry->priv->registry = registry;
3340
3341         g_object_notify (G_OBJECT (name_selector_entry), "registry");
3342 }
3343
3344 /**
3345  * e_name_selector_entry_get_minimum_query_length:
3346  * @name_selector_entry: an #ENameSelectorEntry
3347  *
3348  * Returns: Minimum length of query before completion starts
3349  *
3350  * Since: 3.6
3351  **/
3352 gint
3353 e_name_selector_entry_get_minimum_query_length (ENameSelectorEntry *name_selector_entry)
3354 {
3355         g_return_val_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry), -1);
3356
3357         return name_selector_entry->priv->minimum_query_length;
3358 }
3359
3360 /**
3361  * e_name_selector_entry_set_minimum_query_length:
3362  * @name_selector_entry: an #ENameSelectorEntry
3363  * @length: minimum query length
3364  *
3365  * Sets minimum length of query before completion starts.
3366  *
3367  * Since: 3.6
3368  **/
3369 void
3370 e_name_selector_entry_set_minimum_query_length (ENameSelectorEntry *name_selector_entry,
3371                                                 gint length)
3372 {
3373         g_return_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry));
3374         g_return_if_fail (length > 0);
3375
3376         if (name_selector_entry->priv->minimum_query_length == length)
3377                 return;
3378
3379         name_selector_entry->priv->minimum_query_length = length;
3380
3381         g_object_notify (G_OBJECT (name_selector_entry), "minimum-query-length");
3382 }
3383
3384 /**
3385  * e_name_selector_entry_get_show_address:
3386  * @name_selector_entry: an #ENameSelectorEntry
3387  *
3388  * Returns: Whether always show email address for an auto-completed contact.
3389  *
3390  * Since: 3.6
3391  **/
3392 gboolean
3393 e_name_selector_entry_get_show_address (ENameSelectorEntry *name_selector_entry)
3394 {
3395         g_return_val_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry), FALSE);
3396
3397         return name_selector_entry->priv->show_address;
3398 }
3399
3400 /**
3401  * e_name_selector_entry_set_show_address:
3402  * @name_selector_entry: an #ENameSelectorEntry
3403  * @show: new value to set
3404  *
3405  * Sets whether always show email address for an auto-completed contact.
3406  *
3407  * Since: 3.6
3408  **/
3409 void
3410 e_name_selector_entry_set_show_address (ENameSelectorEntry *name_selector_entry,
3411                                         gboolean show)
3412 {
3413         g_return_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry));
3414
3415         if ((name_selector_entry->priv->show_address ? 1 : 0) == (show ? 1 : 0))
3416                 return;
3417
3418         name_selector_entry->priv->show_address = show;
3419
3420         sanitize_entry (name_selector_entry);
3421
3422         g_object_notify (G_OBJECT (name_selector_entry), "show-address");
3423 }
3424
3425 /**
3426  * e_name_selector_entry_peek_contact_store:
3427  * @name_selector_entry: an #ENameSelectorEntry
3428  *
3429  * Gets the #EContactStore being used by @name_selector_entry.
3430  *
3431  * Returns: An #EContactStore.
3432  **/
3433 EContactStore *
3434 e_name_selector_entry_peek_contact_store (ENameSelectorEntry *name_selector_entry)
3435 {
3436         g_return_val_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry), NULL);
3437
3438         return name_selector_entry->priv->contact_store;
3439 }
3440
3441 /**
3442  * e_name_selector_entry_set_contact_store:
3443  * @name_selector_entry: an #ENameSelectorEntry
3444  * @contact_store: an #EContactStore to use
3445  *
3446  * Sets the #EContactStore being used by @name_selector_entry to @contact_store.
3447  **/
3448 void
3449 e_name_selector_entry_set_contact_store (ENameSelectorEntry *name_selector_entry,
3450                                          EContactStore *contact_store)
3451 {
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));
3454
3455         if (contact_store == name_selector_entry->priv->contact_store)
3456                 return;
3457
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);
3463
3464         setup_contact_store (name_selector_entry);
3465 }
3466
3467 /**
3468  * e_name_selector_entry_peek_destination_store:
3469  * @name_selector_entry: an #ENameSelectorEntry
3470  *
3471  * Gets the #EDestinationStore being used to store @name_selector_entry's destinations.
3472  *
3473  * Returns: An #EDestinationStore.
3474  **/
3475 EDestinationStore *
3476 e_name_selector_entry_peek_destination_store (ENameSelectorEntry *name_selector_entry)
3477 {
3478         g_return_val_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry), NULL);
3479
3480         return name_selector_entry->priv->destination_store;
3481 }
3482
3483 /**
3484  * e_name_selector_entry_set_destination_store:
3485  * @name_selector_entry: an #ENameSelectorEntry
3486  * @destination_store: an #EDestinationStore to use
3487  *
3488  * Sets @destination_store as the #EDestinationStore to be used to store
3489  * destinations for @name_selector_entry.
3490  **/
3491 void
3492 e_name_selector_entry_set_destination_store (ENameSelectorEntry *name_selector_entry,
3493                                              EDestinationStore *destination_store)
3494 {
3495         g_return_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry));
3496         g_return_if_fail (E_IS_DESTINATION_STORE (destination_store));
3497
3498         if (destination_store == name_selector_entry->priv->destination_store)
3499                 return;
3500
3501         g_object_unref (name_selector_entry->priv->destination_store);
3502         name_selector_entry->priv->destination_store = g_object_ref (destination_store);
3503
3504         setup_destination_store (name_selector_entry);
3505 }
3506
3507 /**
3508  * e_name_selector_entry_get_popup_destination:
3509  *
3510  * Since: 2.32
3511  **/
3512 EDestination *
3513 e_name_selector_entry_get_popup_destination (ENameSelectorEntry *name_selector_entry)
3514 {
3515         g_return_val_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry), NULL);
3516
3517         return name_selector_entry->priv->popup_destination;
3518 }
3519
3520 /**
3521  * e_name_selector_entry_set_contact_editor_func:
3522  *
3523  * DO NOT USE.
3524  **/
3525 void
3526 e_name_selector_entry_set_contact_editor_func (ENameSelectorEntry *name_selector_entry,
3527                                                gpointer func)
3528 {
3529         name_selector_entry->priv->contact_editor_func = func;
3530 }
3531
3532 /**
3533  * e_name_selector_entry_set_contact_list_editor_func:
3534  *
3535  * DO NOT USE.
3536  **/
3537 void
3538 e_name_selector_entry_set_contact_list_editor_func (ENameSelectorEntry *name_selector_entry,
3539                                                     gpointer func)
3540 {
3541         name_selector_entry->priv->contact_list_editor_func = func;
3542 }