Fix FSF address (Tobias Mueller, #470445)
[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) 2004 Novell, Inc.
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 <gtk/gtkentry.h>
27 #include <gtk/gtkentrycompletion.h>
28 #include <gtk/gtkcelllayout.h>
29 #include <gtk/gtkcellrenderertext.h>
30 #include <gtk/gtkmenuitem.h>
31 #include <gtk/gtkradiomenuitem.h>
32 #include <gtk/gtkseparatormenuitem.h>
33 #include <glib/gi18n-lib.h>
34 #include <gtk/gtkclipboard.h>
35
36 #include <libebook/e-book.h>
37 #include <libebook/e-contact.h>
38 #include <libebook/e-destination.h>
39 #include <libedataserverui/e-book-auth-util.h>
40 #include "libedataserver/e-sexp.h"
41
42 #include "e-name-selector-entry.h"
43
44 enum {
45         UPDATED,
46         LAST_SIGNAL
47 };
48 static guint signals[LAST_SIGNAL] = { 0 };
49 static guint COMPLETION_CUE_MIN_LEN = 0;
50 #define ENS_DEBUG(x)
51
52 G_DEFINE_TYPE (ENameSelectorEntry, e_name_selector_entry, GTK_TYPE_ENTRY);
53
54 typedef struct _ENameSelectorEntryPrivate       ENameSelectorEntryPrivate;
55 struct _ENameSelectorEntryPrivate
56 {
57         gboolean is_completing;
58 };
59
60 #define E_NAME_SELECTOR_ENTRY_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), E_TYPE_NAME_SELECTOR_ENTRY, ENameSelectorEntryPrivate))
61
62 static void e_name_selector_entry_class_init (ENameSelectorEntryClass *name_selector_entry_class);
63 static void e_name_selector_entry_init       (ENameSelectorEntry *name_selector_entry);
64 static void e_name_selector_entry_dispose    (GObject *object);
65 static void e_name_selector_entry_finalize   (GObject *object);
66
67 static void destination_row_inserted (ENameSelectorEntry *name_selector_entry, GtkTreePath *path, GtkTreeIter *iter);
68 static void destination_row_changed  (ENameSelectorEntry *name_selector_entry, GtkTreePath *path, GtkTreeIter *iter);
69 static void destination_row_deleted  (ENameSelectorEntry *name_selector_entry, GtkTreePath *path);
70
71 static void user_insert_text (ENameSelectorEntry *name_selector_entry, gchar *new_text, gint new_text_length, gint *position, gpointer user_data);
72 static void user_delete_text (ENameSelectorEntry *name_selector_entry, gint start_pos, gint end_pos, gpointer user_data);
73
74 static void setup_default_contact_store (ENameSelectorEntry *name_selector_entry);
75
76 static void
77 e_name_selector_entry_get_property (GObject *object, guint prop_id,
78                                     GValue *value, GParamSpec *pspec)
79 {
80 }
81
82 static void
83 e_name_selector_entry_set_property (GObject *object, guint prop_id,
84                                     const GValue *value, GParamSpec *pspec)
85 {
86 }
87
88 static void
89 e_name_selector_entry_realize (GtkWidget *widget)
90 {
91         ENameSelectorEntry *name_selector_entry = E_NAME_SELECTOR_ENTRY (widget);
92
93         GTK_WIDGET_CLASS (e_name_selector_entry_parent_class)->realize (widget);
94
95         if (!name_selector_entry->contact_store) {
96                 setup_default_contact_store (name_selector_entry);
97         }
98 }
99
100 /* Partial, repeatable destruction. Release references. */
101 static void
102 e_name_selector_entry_dispose (GObject *object)
103 {
104         ENameSelectorEntry *name_selector_entry = E_NAME_SELECTOR_ENTRY (object);
105
106         if (name_selector_entry->entry_completion) {
107                 g_object_unref (name_selector_entry->entry_completion);
108                 name_selector_entry->entry_completion = NULL;
109         }
110
111         if (name_selector_entry->destination_store) {
112                 g_object_unref (name_selector_entry->destination_store);
113                 name_selector_entry->destination_store = NULL;
114         }
115
116         if (G_OBJECT_CLASS (e_name_selector_entry_parent_class)->dispose)
117                 G_OBJECT_CLASS (e_name_selector_entry_parent_class)->dispose (object);
118 }
119
120 /* Final, one-time destruction. Free all. */
121 static void
122 e_name_selector_entry_finalize (GObject *object)
123 {
124         if (G_OBJECT_CLASS (e_name_selector_entry_parent_class)->finalize)
125                 G_OBJECT_CLASS (e_name_selector_entry_parent_class)->finalize (object);
126 }
127
128 static void
129 e_name_selector_entry_updated (ENameSelectorEntry *entry, char *email)
130 {
131         g_return_if_fail (E_IS_NAME_SELECTOR_ENTRY (entry));
132 }
133
134 static void
135 e_name_selector_entry_class_init (ENameSelectorEntryClass *name_selector_entry_class)
136 {
137         GObjectClass   *object_class = G_OBJECT_CLASS (name_selector_entry_class);
138         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (name_selector_entry_class);
139
140         object_class->get_property = e_name_selector_entry_get_property;
141         object_class->set_property = e_name_selector_entry_set_property;
142         object_class->dispose      = e_name_selector_entry_dispose;
143         object_class->finalize     = e_name_selector_entry_finalize;
144         name_selector_entry_class->updated         = e_name_selector_entry_updated;
145
146         widget_class->realize      = e_name_selector_entry_realize;
147
148         /* Install properties */
149
150         /* Install signals */
151         
152         signals[UPDATED] = g_signal_new ("updated",
153                                          E_TYPE_NAME_SELECTOR_ENTRY,
154                                          G_SIGNAL_RUN_FIRST,
155                                          G_STRUCT_OFFSET (ENameSelectorEntryClass, updated),
156                                          NULL,
157                                          NULL,
158                                          g_cclosure_marshal_VOID__POINTER,
159                                          G_TYPE_NONE, 1, G_TYPE_POINTER);
160
161         g_type_class_add_private (object_class, sizeof(ENameSelectorEntryPrivate));
162 }
163
164 /* Remove unquoted commas from string */
165 static gchar *
166 sanitize_string (const gchar *string)
167 {
168         GString     *gstring;
169         gboolean     quoted = FALSE;
170         const gchar *p;
171
172         gstring = g_string_new ("");
173
174         if (!string)
175                 return g_string_free (gstring, FALSE);
176
177         for (p = string; *p; p = g_utf8_next_char (p)) {
178                 gunichar c = g_utf8_get_char (p);
179
180                 if (c == '"')
181                         quoted = ~quoted;
182                 else if (c == ',' && !quoted)
183                         continue;
184
185                 g_string_append_unichar (gstring, c);
186         }
187
188         return g_string_free (gstring, FALSE);
189 }
190
191 /* Called for each list store entry whenever the user types (but not on cut/paste) */
192 static gboolean
193 completion_match_cb (GtkEntryCompletion *completion, const gchar *key,
194                      GtkTreeIter *iter, gpointer user_data)
195 {
196         ENS_DEBUG (g_print ("completion_match_cb, key=%s\n", key));
197
198         return TRUE;
199 }
200
201 /* Gets context of n_unichars total (n_unicars / 2, before and after position)
202  * and places them in array. If any positions would be outside the string, the
203  * corresponding unichars are set to zero. */
204 static void
205 get_utf8_string_context (const gchar *string, gint position, gunichar *unichars, gint n_unichars)
206 {
207         gchar *p = NULL;
208         gint   len;
209         gint   gap;
210         gint   i;
211
212         /* n_unichars must be even */
213         g_assert (n_unichars % 2 == 0);
214
215         len = g_utf8_strlen (string, -1);
216         gap = n_unichars / 2;
217
218         for (i = 0; i < n_unichars; i++) {
219                 gint char_pos = position - gap + i;
220
221                 if (char_pos < 0 || char_pos >= len) {
222                         unichars [i] = '\0';
223                         continue;
224                 }
225
226                 if (p)
227                         p = g_utf8_next_char (p);
228                 else
229                         p = g_utf8_offset_to_pointer (string, char_pos);
230
231                 unichars [i] = g_utf8_get_char (p);
232         }
233 }
234
235 static gboolean
236 get_range_at_position (const gchar *string, gint pos, gint *start_pos, gint *end_pos)
237 {
238         const gchar *p;
239         gboolean     quoted          = FALSE;
240         gint         local_start_pos = 0;
241         gint         local_end_pos   = 0;
242         gint         i;
243
244         if (!string || !*string)
245                 return FALSE;
246
247         for (p = string, i = 0; *p; p = g_utf8_next_char (p), i++) {
248                 gunichar c = g_utf8_get_char (p);
249
250                 if (c == '"') {
251                         quoted = ~quoted;
252                 } else if (c == ',' && !quoted) {
253                         if (i < pos) {
254                                 /* Start right after comma */
255                                 local_start_pos = i + 1;
256                         } else {
257                                 /* Stop right before comma */
258                                 local_end_pos = i;
259                                 break;
260                         }
261                 } else if (c == ' ' && local_start_pos == i) {
262                         /* Adjust start to skip space after first comma */
263                         local_start_pos++;
264                 }
265         }
266
267         /* If we didn't hit a comma, we must've hit NULL, and ours was the last element. */
268         if (!local_end_pos)
269                 local_end_pos = i;
270
271         if (start_pos)
272                 *start_pos = local_start_pos;
273         if (end_pos)
274                 *end_pos   = local_end_pos;
275
276         return TRUE;
277 }
278
279 static gboolean
280 is_quoted_at (const gchar *string, gint pos)
281 {
282         const gchar *p;
283         gboolean     quoted = FALSE;
284         gint         i;
285
286         for (p = string, i = 0; *p && i < pos; p = g_utf8_next_char (p), i++) {
287                 gunichar c = g_utf8_get_char (p);
288
289                 if (c == '"')
290                         quoted = ~quoted;
291         }
292
293         return quoted ? TRUE : FALSE;
294 }
295
296 static gint
297 get_index_at_position (const gchar *string, gint pos)
298 {
299         const gchar *p;
300         gboolean     quoted = FALSE;
301         gint         n      = 0;
302         gint         i;
303
304         for (p = string, i = 0; *p && i < pos; p = g_utf8_next_char (p), i++) {
305                 gunichar c = g_utf8_get_char (p);
306
307                 if (c == '"')
308                         quoted = ~quoted;
309                 else if (c == ',' && !quoted)
310                         n++;
311         }
312
313         return n;
314 }
315
316 static gboolean
317 get_range_by_index (const gchar *string, gint index, gint *start_pos, gint *end_pos)
318 {
319         const gchar *p;
320         gboolean     quoted = FALSE;
321         gint         i;
322         gint         n = 0;
323
324         for (p = string, i = 0; *p && n < index; p = g_utf8_next_char (p), i++) {
325                 gunichar c = g_utf8_get_char (p);
326
327                 if (c == '"')
328                         quoted = ~quoted;
329                 if (c == ',' && !quoted)
330                         n++;
331         }
332
333         if (n < index)
334                 return FALSE;
335
336         return get_range_at_position (string, i, start_pos, end_pos);
337 }
338
339 static gchar *
340 get_address_at_position (const gchar *string, gint pos)
341 {
342         gint         start_pos;
343         gint         end_pos;
344         const gchar *start_p;
345         const gchar *end_p;
346
347         if (!get_range_at_position (string, pos, &start_pos, &end_pos))
348                 return NULL;
349
350         start_p = g_utf8_offset_to_pointer (string, start_pos);
351         end_p   = g_utf8_offset_to_pointer (string, end_pos);
352
353         return g_strndup (start_p, end_p - start_p);
354 }
355
356 /* Finds the destination in model */
357 static EDestination *
358 find_destination_by_index (ENameSelectorEntry *name_selector_entry, gint index)
359 {
360         GtkTreePath  *path;
361         GtkTreeIter   iter;
362
363         path = gtk_tree_path_new_from_indices (index, -1);
364         if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (name_selector_entry->destination_store),
365                                       &iter, path)) {
366                 /* If we have zero destinations, getting a NULL destination at index 0
367                  * is valid. */
368                 if (index > 0)
369                         g_warning ("ENameSelectorEntry is out of sync with model!");
370                 gtk_tree_path_free (path);
371                 return NULL;
372         }
373         gtk_tree_path_free (path);
374
375         return e_destination_store_get_destination (name_selector_entry->destination_store, &iter);
376 }
377
378 /* Finds the destination in model */
379 static EDestination *
380 find_destination_at_position (ENameSelectorEntry *name_selector_entry, gint pos)
381 {
382         const gchar  *text;
383         gint          index;
384
385         text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
386         index = get_index_at_position (text, pos);
387
388         return find_destination_by_index (name_selector_entry, index);
389 }
390
391 /* Builds destination from our text */
392 static EDestination *
393 build_destination_at_position (const gchar *string, gint pos)
394 {
395         EDestination *destination;
396         gchar        *address;
397
398         address = get_address_at_position (string, pos);
399         if (!address)
400                 return NULL;
401
402         destination = e_destination_new ();
403         e_destination_set_raw (destination, address);
404
405         g_free (address);
406         return destination;
407 }
408
409 static gchar *
410 name_style_query (const gchar *field, const gchar *value)
411 {
412         gchar   *spaced_str;
413         gchar   *comma_str;
414         GString *out = g_string_new ("");
415         gchar  **strv;
416         gchar   *query;
417
418         spaced_str = sanitize_string (value);
419         g_strstrip (spaced_str);
420
421         strv = g_strsplit (spaced_str, " ", 0);
422
423         if (strv [0] && strv [1]) {
424                 g_string_append (out, "(or ");
425                 comma_str = g_strjoinv (", ", strv);
426         } else {
427                 comma_str = NULL;
428         }
429
430         g_string_append (out, " (beginswith ");
431         e_sexp_encode_string (out, field);
432         e_sexp_encode_string (out, spaced_str);
433         g_string_append (out, ")");
434
435         if (comma_str) {
436                 g_string_append (out, " (beginswith ");
437
438                 e_sexp_encode_string (out, field);
439                 g_strstrip (comma_str);
440                 e_sexp_encode_string (out, comma_str);
441                 g_string_append (out, "))");
442         }
443
444         query = g_string_free (out, FALSE);
445
446         g_free (spaced_str);
447         g_free (comma_str);
448         g_strfreev (strv);
449
450         return query;
451 }
452
453 static gchar *
454 escape_sexp_string (const gchar *string)
455 {
456         GString *gstring;
457         gchar   *encoded_string;
458
459         gstring = g_string_new ("");
460         e_sexp_encode_string (gstring, string);
461
462         encoded_string = gstring->str;
463         g_string_free (gstring, FALSE);
464
465         return encoded_string;
466 }
467
468 static void
469 set_completion_query (ENameSelectorEntry *name_selector_entry, const gchar *cue_str)
470 {
471         EBookQuery *book_query;
472         gchar      *query_str;
473         gchar      *encoded_cue_str;
474         gchar      *full_name_query_str;
475         gchar      *file_as_query_str;
476
477         if (!name_selector_entry->contact_store)
478                 return;
479
480         if (!cue_str) {
481                 /* Clear the store */
482                 e_contact_store_set_query (name_selector_entry->contact_store, NULL);
483                 return;
484         }
485
486         encoded_cue_str     = escape_sexp_string (cue_str);
487         full_name_query_str = name_style_query ("full_name", cue_str);
488         file_as_query_str   = name_style_query ("file_as",   cue_str);
489
490         query_str = g_strdup_printf ("(or "
491                                      " (beginswith \"nickname\"  %s) "
492                                      " (beginswith \"email\"     %s) "
493                                      " %s "
494                                      " %s "
495                                      ")",
496                                      encoded_cue_str, encoded_cue_str,
497                                      full_name_query_str, file_as_query_str);
498
499         g_free (file_as_query_str);
500         g_free (full_name_query_str);
501         g_free (encoded_cue_str);
502
503         ENS_DEBUG (g_print ("%s\n", query_str));
504
505         book_query = e_book_query_from_string (query_str);
506         e_contact_store_set_query (name_selector_entry->contact_store, book_query);
507         e_book_query_unref (book_query);
508
509         g_free (query_str);
510 }
511
512 static gchar *
513 get_entry_substring (ENameSelectorEntry *name_selector_entry, gint range_start, gint range_end)
514 {
515         const gchar *entry_text;
516         gchar       *p0, *p1;
517
518         entry_text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
519
520         p0 = g_utf8_offset_to_pointer (entry_text, range_start);
521         p1 = g_utf8_offset_to_pointer (entry_text, range_end);
522
523         return g_strndup (p0, p1 - p0);
524 }
525
526 static gint
527 utf8_casefold_collate_len (const gchar *str1, const gchar *str2, gint len)
528 {
529         gchar *s1 = g_utf8_casefold(str1, len);
530         gchar *s2 = g_utf8_casefold(str2, len);
531         int rv;
532
533         rv = g_utf8_collate (s1, s2);
534
535         g_free (s1);
536         g_free (s2);
537
538         return rv;
539 }
540
541 static gchar *
542 build_textrep_for_contact (EContact *contact, EContactField cue_field)
543 {
544         gchar *name  = NULL;
545         gchar *email = NULL;
546         gchar *textrep;
547
548         switch (cue_field) {
549                 case E_CONTACT_FULL_NAME:
550                 case E_CONTACT_NICKNAME:
551                 case E_CONTACT_FILE_AS:
552                         name  = e_contact_get (contact, cue_field);
553                         email = e_contact_get (contact, E_CONTACT_EMAIL_1);
554                         break;
555
556                 case E_CONTACT_EMAIL_1:
557                 case E_CONTACT_EMAIL_2:
558                 case E_CONTACT_EMAIL_3:
559                 case E_CONTACT_EMAIL_4:
560                         name = NULL;
561                         email = e_contact_get (contact, cue_field);
562                         break;
563
564                 default:
565                         g_assert_not_reached ();
566                         break;
567         }
568
569         g_assert (email);
570         g_assert (strlen (email) > 0);
571
572         if (name)
573                 textrep = g_strdup_printf ("%s <%s>", name, email);
574         else
575                 textrep = g_strdup_printf ("%s", email);
576
577         g_free (name);
578         g_free (email);
579         return textrep;
580 }
581
582 static gboolean
583 contact_match_cue (EContact *contact, const gchar *cue_str,
584                    EContactField *matched_field, gint *matched_field_rank)
585 {
586         EContactField  fields [] = { E_CONTACT_FULL_NAME, E_CONTACT_NICKNAME, E_CONTACT_FILE_AS,
587                                      E_CONTACT_EMAIL_1, E_CONTACT_EMAIL_2, E_CONTACT_EMAIL_3,
588                                      E_CONTACT_EMAIL_4 };
589         gchar         *email;
590         gboolean       result = FALSE;
591         gint           cue_len;
592         gint           i;
593
594         g_assert (contact);
595         g_assert (cue_str);
596
597         if (g_utf8_strlen (cue_str, -1) < COMPLETION_CUE_MIN_LEN)
598                 return FALSE;
599
600         cue_len = strlen (cue_str);
601
602         /* Make sure contact has an email address */
603         email = e_contact_get (contact, E_CONTACT_EMAIL_1);
604         if (!email || !*email) {
605                 g_free (email);
606                 return FALSE;
607         }
608         g_free (email);
609
610         for (i = 0; i < G_N_ELEMENTS (fields); i++) {
611                 gchar *value;
612                 gchar *value_sane;
613
614                 /* Don't match e-mail addresses in contact lists */
615                 if (e_contact_get (contact, E_CONTACT_IS_LIST) &&
616                     fields [i] >= E_CONTACT_FIRST_EMAIL_ID &&
617                     fields [i] <= E_CONTACT_LAST_EMAIL_ID)
618                         continue;
619
620                 value = e_contact_get (contact, fields [i]);
621                 if (!value)
622                         continue;
623
624                 value_sane = sanitize_string (value);
625                 g_free (value);
626
627                 ENS_DEBUG (g_print ("Comparing '%s' to '%s'\n", value, cue_str));
628
629                 if (!utf8_casefold_collate_len (value_sane, cue_str, cue_len)) {
630                         if (matched_field)
631                                 *matched_field = fields [i];
632                         if (matched_field_rank)
633                                 *matched_field_rank = i;
634
635                         result = TRUE;
636                         g_free (value_sane);
637                         break;
638                 }
639                 g_free (value_sane);
640         }
641
642         return result;
643 }
644
645 static gboolean
646 find_existing_completion (ENameSelectorEntry *name_selector_entry, const gchar *cue_str,
647                           EContact **contact, gchar **text, EContactField *matched_field)
648 {
649         GtkTreeIter    iter;
650         EContact      *best_contact    = NULL;
651         gint           best_field_rank = G_MAXINT;
652         EContactField  best_field = 0;
653         gint           cue_len;
654
655         g_assert (cue_str);
656
657         if (!name_selector_entry->contact_store)
658                 return FALSE;
659
660         cue_len = strlen (cue_str);
661
662         ENS_DEBUG (g_print ("Completing '%s'\n", cue_str));
663
664         if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (name_selector_entry->contact_store), &iter))
665                 return FALSE;
666
667         do {
668                 EContact      *current_contact;
669                 gint           current_field_rank;
670                 EContactField  current_field;
671                 gboolean       matches;
672
673                 current_contact = e_contact_store_get_contact (name_selector_entry->contact_store, &iter);
674                 if (!current_contact)
675                         continue;
676
677                 matches = contact_match_cue (current_contact, cue_str, &current_field, &current_field_rank);
678                 if (matches && current_field_rank < best_field_rank) {
679                         best_contact    = current_contact;
680                         best_field_rank = current_field_rank;
681                         best_field      = current_field;
682                 }
683         } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (name_selector_entry->contact_store), &iter));
684
685         if (!best_contact)
686                 return FALSE;
687
688         if (contact)
689                 *contact = best_contact;
690         if (text)
691                 *text = build_textrep_for_contact (best_contact, best_field);
692         if (matched_field)
693                 *matched_field = best_field;
694
695         return TRUE;
696 }
697
698 static void
699 generate_attribute_list (ENameSelectorEntry *name_selector_entry)
700 {
701         PangoLayout    *layout;
702         PangoAttrList  *attr_list;
703         const gchar    *text;
704         gint            i;
705
706         text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
707         layout = gtk_entry_get_layout (GTK_ENTRY (name_selector_entry));
708
709         /* Set up the attribute list */
710
711         attr_list = pango_attr_list_new ();
712
713         if (name_selector_entry->attr_list)
714                 pango_attr_list_unref (name_selector_entry->attr_list);
715
716         name_selector_entry->attr_list = attr_list;
717
718         /* Parse the entry's text and apply attributes to real contacts */
719
720         for (i = 0; ; i++) {
721                 EDestination   *destination;
722                 PangoAttribute *attr;
723                 gint            start_pos;
724                 gint            end_pos;
725
726                 if (!get_range_by_index (text, i, &start_pos, &end_pos))
727                         break;
728
729                 destination = find_destination_at_position (name_selector_entry, start_pos);
730
731                 /* Destination will be NULL if we have no entries */
732                 if (!destination || !e_destination_get_contact (destination))
733                         continue;
734
735                 attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
736                 attr->start_index = g_utf8_offset_to_pointer (text, start_pos) - text;
737                 attr->end_index = g_utf8_offset_to_pointer (text, end_pos) - text;
738                 pango_attr_list_insert (attr_list, attr);
739         }
740
741         pango_layout_set_attributes (layout, attr_list);
742 }
743
744 static gboolean
745 expose_event (ENameSelectorEntry *name_selector_entry)
746 {
747         PangoLayout *layout;
748
749         layout = gtk_entry_get_layout (GTK_ENTRY (name_selector_entry));
750         pango_layout_set_attributes (layout, name_selector_entry->attr_list);
751
752         return FALSE;
753 }
754
755 static void
756 type_ahead_complete (ENameSelectorEntry *name_selector_entry)
757 {
758         EContact      *contact;
759         EContactField  matched_field;
760         EDestination  *destination;
761         gint           cursor_pos;
762         gint           range_start = 0;
763         gint           range_end   = 0;
764         gint           pos         = 0;
765         gchar         *textrep;
766         gint           textrep_len;
767         gint           range_len;
768         const gchar   *text;
769         gchar         *cue_str;
770         gchar         *temp_str;
771         ENameSelectorEntryPrivate *priv;
772
773         priv = E_NAME_SELECTOR_ENTRY_GET_PRIVATE (name_selector_entry);
774
775         cursor_pos = gtk_editable_get_position (GTK_EDITABLE (name_selector_entry));
776         if (cursor_pos < 0)
777                 return;
778
779         text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
780         get_range_at_position (text, cursor_pos, &range_start, &range_end);
781         range_len = range_end - range_start;
782         if (range_len < COMPLETION_CUE_MIN_LEN)
783                 return;
784
785         destination = find_destination_at_position (name_selector_entry, cursor_pos);
786
787         cue_str = get_entry_substring (name_selector_entry, range_start, range_end);
788         if (!find_existing_completion (name_selector_entry, cue_str, &contact,
789                                        &textrep, &matched_field)) {
790                 g_free (cue_str);
791                 return;
792         }
793         g_free (cue_str);
794
795         temp_str = sanitize_string (textrep);
796         g_free (textrep);
797         textrep = temp_str;
798
799         textrep_len = g_utf8_strlen (textrep, -1);
800         pos         = range_start;
801
802         g_signal_handlers_block_by_func (name_selector_entry, user_insert_text, name_selector_entry);
803         g_signal_handlers_block_by_func (name_selector_entry, user_delete_text, name_selector_entry);
804         g_signal_handlers_block_by_func (name_selector_entry->destination_store,
805                                          destination_row_changed, name_selector_entry);
806
807         if (textrep_len > range_len) {
808                 gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry), range_start, range_end);
809                 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), textrep, -1, &pos);
810                 gtk_editable_select_region (GTK_EDITABLE (name_selector_entry), range_end,
811                                             range_start + textrep_len);
812                 priv->is_completing = TRUE;
813         }
814
815         if (contact && destination) {
816                 gint email_n = 0;
817
818                 if (matched_field >= E_CONTACT_FIRST_EMAIL_ID && matched_field <= E_CONTACT_LAST_EMAIL_ID)
819                         email_n = matched_field - E_CONTACT_FIRST_EMAIL_ID;
820
821                 e_destination_set_contact (destination, contact, email_n);
822                 generate_attribute_list (name_selector_entry);
823         }
824
825         g_signal_handlers_unblock_by_func (name_selector_entry->destination_store,
826                                            destination_row_changed, name_selector_entry);
827         g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
828         g_signal_handlers_unblock_by_func (name_selector_entry, user_insert_text, name_selector_entry);
829
830         g_free (textrep);
831 }
832
833 static void
834 clear_completion_model (ENameSelectorEntry *name_selector_entry)
835 {
836         ENameSelectorEntryPrivate *priv;
837
838         priv = E_NAME_SELECTOR_ENTRY_GET_PRIVATE (name_selector_entry);
839
840         if (!name_selector_entry->contact_store)
841                 return;
842
843         e_contact_store_set_query (name_selector_entry->contact_store, NULL);
844         priv->is_completing = FALSE;
845 }
846
847 static void
848 update_completion_model (ENameSelectorEntry *name_selector_entry)
849 {
850         const gchar *text;
851         gint         cursor_pos;
852         gint         range_start = 0;
853         gint         range_end   = 0;
854
855         text       = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
856         cursor_pos = gtk_editable_get_position (GTK_EDITABLE (name_selector_entry));
857
858         if (cursor_pos >= 0)
859                 get_range_at_position (text, cursor_pos, &range_start, &range_end);
860
861         if (range_end - range_start >= COMPLETION_CUE_MIN_LEN && cursor_pos == range_end) {
862                 gchar *cue_str;
863
864                 cue_str = get_entry_substring (name_selector_entry, range_start, range_end);
865                 set_completion_query (name_selector_entry, cue_str);
866                 g_free (cue_str);
867         } else {
868                 /* N/A; Clear completion model */
869                 clear_completion_model (name_selector_entry);
870         }
871 }
872
873 static gboolean
874 type_ahead_complete_on_idle_cb (ENameSelectorEntry *name_selector_entry)
875 {
876         type_ahead_complete (name_selector_entry);
877         name_selector_entry->type_ahead_complete_cb_id = 0;
878         return FALSE;
879 }
880
881 static gboolean
882 update_completions_on_idle_cb (ENameSelectorEntry *name_selector_entry)
883 {
884         update_completion_model (name_selector_entry);
885         name_selector_entry->update_completions_cb_id = 0;
886         return FALSE;
887 }
888
889 static void
890 insert_destination_at_position (ENameSelectorEntry *name_selector_entry, gint pos)
891 {
892         EDestination *destination;
893         const gchar  *text;
894         gint          index;
895
896         text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
897         index = get_index_at_position (text, pos);
898
899         destination = build_destination_at_position (text, pos);
900         g_assert (destination);
901
902         g_signal_handlers_block_by_func (name_selector_entry->destination_store,
903                                          destination_row_inserted, name_selector_entry);
904         e_destination_store_insert_destination (name_selector_entry->destination_store,
905                                                 index, destination);
906         g_signal_handlers_unblock_by_func (name_selector_entry->destination_store,
907                                            destination_row_inserted, name_selector_entry);
908         g_object_unref (destination);
909 }
910
911 static void
912 modify_destination_at_position (ENameSelectorEntry *name_selector_entry, gint pos)
913 {
914         EDestination *destination;
915         const gchar  *text;
916         gchar        *raw_address;
917         gboolean      rebuild_attributes = FALSE;
918
919         destination = find_destination_at_position (name_selector_entry, pos);
920         if (!destination)
921                 return;
922         
923         text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
924         raw_address = get_address_at_position (text, pos);
925         g_assert (raw_address);
926
927         if (e_destination_get_contact (destination))
928                 rebuild_attributes = TRUE;
929
930         g_signal_handlers_block_by_func (name_selector_entry->destination_store,
931                                          destination_row_changed, name_selector_entry);
932         e_destination_set_raw (destination, raw_address);
933         g_signal_handlers_unblock_by_func (name_selector_entry->destination_store,
934                                            destination_row_changed, name_selector_entry);
935
936         g_free (raw_address);
937
938         if (rebuild_attributes)
939                 generate_attribute_list (name_selector_entry);
940 }
941
942 static void
943 sync_destination_at_position (ENameSelectorEntry *name_selector_entry, gint range_pos, gint *cursor_pos)
944 {
945         EDestination *destination;
946         const gchar  *text;
947         gchar        *address;
948         gint          address_len;
949         gint          range_start, range_end;
950
951         /* Get the destination we're looking at. Note that the entry may be empty, and so
952          * there may not be one. */
953         destination = find_destination_at_position (name_selector_entry, range_pos);
954         if (!destination)
955                 return;
956
957         text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
958         if (!get_range_at_position (text, range_pos, &range_start, &range_end)) {
959                 g_warning ("ENameSelectorEntry is out of sync with model!");
960                 return;
961         }
962
963         address = sanitize_string (e_destination_get_textrep (destination, FALSE));
964         address_len = g_utf8_strlen (address, -1);
965
966         if (cursor_pos) {
967                 /* Update cursor placement */
968                 if (*cursor_pos >= range_end)
969                         *cursor_pos += address_len - (range_end - range_start);
970                 else if (*cursor_pos > range_start)
971                         *cursor_pos = range_start + address_len;
972         }
973
974         g_signal_handlers_block_by_func (name_selector_entry, user_insert_text, name_selector_entry);
975         g_signal_handlers_block_by_func (name_selector_entry, user_delete_text, name_selector_entry);
976
977         gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry), range_start, range_end);
978         gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), address, -1, &range_start);
979
980         g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
981         g_signal_handlers_unblock_by_func (name_selector_entry, user_insert_text, name_selector_entry);
982
983         generate_attribute_list (name_selector_entry);
984         g_free (address);
985 }
986
987 static void
988 remove_destination_by_index (ENameSelectorEntry *name_selector_entry, gint index)
989 {
990         EDestination *destination;
991
992         destination = find_destination_by_index (name_selector_entry, index);
993         if (destination) {
994                 g_signal_handlers_block_by_func (name_selector_entry->destination_store,
995                                          destination_row_deleted, name_selector_entry);
996                 e_destination_store_remove_destination (name_selector_entry->destination_store,
997                                                 destination);
998                 g_signal_handlers_unblock_by_func (name_selector_entry->destination_store,
999                                            destination_row_deleted, name_selector_entry);
1000         }
1001 }
1002
1003 /* Returns the number of characters inserted */
1004 static gint
1005 insert_unichar (ENameSelectorEntry *name_selector_entry, gint *pos, gunichar c)
1006 {
1007         const gchar *text;
1008         gunichar     str_context [4];
1009         gchar        buf [7];
1010         gint         len;
1011
1012         text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1013         get_utf8_string_context (text, *pos, str_context, 4);
1014
1015         /* Space is not allowed:
1016          * - Before or after another space.
1017          * - At start of string. */
1018
1019         if (c == ' ' && (str_context [1] == ' ' || str_context [1] == '\0' || str_context [2] == ' '))
1020                 return 0;
1021
1022         /* Comma is not allowed:
1023          * - After another comma.
1024          * - At start of string. */
1025
1026         if (c == ',' && !is_quoted_at (text, *pos)) {
1027                 gint         start_pos;
1028                 gint         end_pos;
1029                 gboolean     at_start = FALSE;
1030                 gboolean     at_end   = FALSE;
1031
1032                 if (str_context [1] == ',' || str_context [1] == '\0')
1033                         return 0;
1034
1035                 /* We do this so we can avoid disturbing destinations with completed contacts
1036                  * either before or after the destination being inserted. */
1037                 get_range_at_position (text, *pos, &start_pos, &end_pos);
1038                 if (*pos <= start_pos)
1039                         at_start = TRUE;
1040                 if (*pos >= end_pos)
1041                         at_end = TRUE;
1042
1043                 /* Must insert comma first, so modify_destination_at_position can do its job
1044                  * correctly, splitting up the contact if necessary. */
1045                 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), ", ", -1, pos);
1046
1047                 /* Update model */
1048                 g_assert (*pos >= 2);
1049
1050                 /* If we inserted the comma at the end of, or in the middle of, an existing
1051                  * address, add a new destination for what appears after comma. Else, we
1052                  * have to add a destination for what appears before comma (a blank one). */
1053                 if (at_end) {
1054                         /* End: Add last, sync first */
1055                         insert_destination_at_position (name_selector_entry, *pos);
1056                         sync_destination_at_position (name_selector_entry, *pos - 2, pos);
1057                         /* Sync generates the attributes list */
1058                 } else if (at_start) {
1059                         /* Start: Add first */
1060                         insert_destination_at_position (name_selector_entry, *pos - 2);
1061                         generate_attribute_list (name_selector_entry);
1062                 } else {
1063                         /* Middle: */
1064                         insert_destination_at_position (name_selector_entry, *pos);
1065                         modify_destination_at_position (name_selector_entry, *pos - 2);
1066                         generate_attribute_list (name_selector_entry);
1067                 }
1068
1069                 return 2;
1070         }
1071
1072         /* Generic case. Allowed spaces also end up here. */
1073
1074         len = g_unichar_to_utf8 (c, buf);
1075         buf [len] = '\0';
1076
1077         gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), buf, -1, pos);
1078
1079         text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1080         len = g_utf8_strlen (text, -1);
1081         text = g_utf8_next_char (text);
1082
1083         if (!*text) {
1084                 /* First and only character so far, create initial destination */
1085                 insert_destination_at_position (name_selector_entry, 0);
1086         } else {
1087                 /* Modified existing destination */
1088                 modify_destination_at_position (name_selector_entry, *pos);
1089         }
1090
1091         /* If editing within the string, we need to regenerate attributes */
1092         if (*pos < len)
1093                 generate_attribute_list (name_selector_entry);
1094
1095         return 1;
1096 }
1097
1098 static void
1099 user_insert_text (ENameSelectorEntry *name_selector_entry, gchar *new_text,
1100                   gint new_text_length, gint *position, gpointer user_data)
1101 {
1102         gchar *p;
1103         gint   chars_inserted = 0;
1104
1105         g_signal_handlers_block_by_func (name_selector_entry, user_insert_text, name_selector_entry);
1106         g_signal_handlers_block_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1107
1108         /* Apply some rules as to where spaces and commas can be inserted, and insert
1109          * a trailing space after comma. */
1110
1111         for (p = new_text; *p; p = g_utf8_next_char (p)) {
1112                 gunichar c = g_utf8_get_char (p);
1113                 insert_unichar (name_selector_entry, position, c);
1114                 chars_inserted++;
1115         }
1116
1117         if (chars_inserted >= 1) {
1118                 /* If the user inserted one character, kick off completion */
1119                 if (!name_selector_entry->update_completions_cb_id) {
1120                         name_selector_entry->update_completions_cb_id =
1121                                 g_idle_add ((GSourceFunc) update_completions_on_idle_cb,
1122                                             name_selector_entry);
1123                 }
1124
1125                 if (!name_selector_entry->type_ahead_complete_cb_id) {
1126                         name_selector_entry->type_ahead_complete_cb_id =
1127                                 g_idle_add ((GSourceFunc) type_ahead_complete_on_idle_cb,
1128                                             name_selector_entry);
1129                 }
1130         }
1131
1132         g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1133         g_signal_handlers_unblock_by_func (name_selector_entry, user_insert_text, name_selector_entry);
1134
1135         g_signal_stop_emission_by_name (name_selector_entry, "insert_text");
1136 }
1137
1138 static void
1139 user_delete_text (ENameSelectorEntry *name_selector_entry, gint start_pos, gint end_pos,
1140                   gpointer user_data)
1141 {
1142         const gchar *text;
1143         gint         index_start, index_end;
1144         gint         selection_start, selection_end;    
1145         gunichar     str_context [2], str_b_context [2];
1146         gint         len;
1147         gint         i;
1148         gboolean     already_selected = FALSE, del_space = FALSE, del_comma = FALSE;
1149
1150         if (start_pos == end_pos) 
1151                 return;
1152
1153         text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1154         len = g_utf8_strlen (text, -1);
1155
1156         if (gtk_editable_get_selection_bounds (GTK_EDITABLE (name_selector_entry), 
1157                                                &selection_start, 
1158                                                &selection_end)) 
1159                 if ((g_utf8_get_char (g_utf8_offset_to_pointer (text, selection_end)) == 0) ||
1160                     (g_utf8_get_char (g_utf8_offset_to_pointer (text, selection_end)) == ','))
1161                         already_selected = TRUE;
1162         
1163         get_utf8_string_context (text, start_pos, str_context, 2);
1164         get_utf8_string_context (text, end_pos, str_b_context, 2);
1165
1166         g_signal_handlers_block_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1167
1168         if (end_pos - start_pos == 1) {
1169                 /* Might be backspace; update completion model so dropdown is accurate */
1170                 if (!name_selector_entry->update_completions_cb_id) {
1171                         name_selector_entry->update_completions_cb_id =
1172                                 g_idle_add ((GSourceFunc) update_completions_on_idle_cb,
1173                                             name_selector_entry);
1174                 }
1175         }
1176
1177         index_start = get_index_at_position (text, start_pos);
1178         index_end   = get_index_at_position (text, end_pos);
1179         
1180         g_signal_stop_emission_by_name (name_selector_entry, "delete_text");
1181         
1182         /* If the deletion touches more than one destination, the first one is changed
1183          * and the rest are removed. If the last destination wasn't completely deleted,
1184          * it becomes part of the first one, since the separator between them was
1185          * removed.
1186          *
1187          * Here, we let the model know about removals. */
1188         for (i = index_end; i > index_start; i--) {
1189                 EDestination *destination = find_destination_by_index (name_selector_entry, i);
1190                 int range_start, range_end;
1191                 char *ttext;
1192                 const char *email=NULL;
1193                 gboolean sel=FALSE;
1194
1195                 if (destination)
1196                         email = e_destination_get_address (destination);
1197
1198                 if (!email || !*email)
1199                         continue;
1200                 
1201                 if (!get_range_by_index (text, i, &range_start, &range_end)) {
1202                         g_warning ("ENameSelectorEntry is out of sync with model!");
1203                         return;
1204                 }
1205
1206                 if ((selection_start < range_start && selection_end > range_start) ||
1207                     (selection_end > range_start && selection_end < range_end))
1208                         sel=TRUE;
1209
1210                 if (!sel) {
1211                         g_signal_handlers_block_by_func (name_selector_entry, user_insert_text, name_selector_entry);
1212                         g_signal_handlers_block_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1213
1214                         gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry), range_start, range_end);
1215
1216                         ttext = sanitize_string (email);
1217                         gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), ttext, -1, &range_start);
1218                         g_free (ttext);
1219
1220                         g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1221                         g_signal_handlers_unblock_by_func (name_selector_entry, user_insert_text, name_selector_entry);
1222
1223                 }
1224                 
1225                 remove_destination_by_index (name_selector_entry, i);
1226         }
1227         
1228         /* Do the actual deletion */
1229         
1230         if (end_pos == start_pos +1 &&  index_end == index_start) {
1231                 /* We could be just deleting the empty text */
1232                 char *c;
1233
1234                 /* Get the actual deleted text */
1235                 c = gtk_editable_get_chars (GTK_EDITABLE (name_selector_entry), start_pos, start_pos+1);
1236
1237                 if ( c[0] == ' ') {
1238                         /* If we are at the beginning or removing junk space, let us ignore it */
1239                         del_space = TRUE;
1240                 }
1241         } else  if (end_pos == start_pos +1 &&  index_end == index_start+1) {
1242                 /* We could be just deleting the empty text */
1243                 char *c;
1244
1245                 /* Get the actual deleted text */
1246                 c = gtk_editable_get_chars (GTK_EDITABLE (name_selector_entry), start_pos, start_pos+1);
1247
1248                 if ( c[0] == ',' && !is_quoted_at (text, start_pos)) {
1249                         /* If we are at the beginning or removing junk space, let us ignore it */
1250                         del_comma = TRUE;
1251                 }
1252         }
1253
1254         if (del_comma) {
1255                 int range_start=-1, range_end;
1256                 EDestination *dest = find_destination_by_index (name_selector_entry, index_end);
1257                 /* If we have deleted the last comma, let us autocomplete normally
1258                  */
1259                 
1260                 if (dest && len - end_pos  != 0) {
1261                         
1262                         EDestination *destination1  = find_destination_by_index (name_selector_entry, index_start);
1263                         char *ttext;
1264                         const char *email=NULL;
1265
1266                         if (destination1)
1267                                 email = e_destination_get_address (destination1);
1268
1269                         if (email && *email) {
1270
1271                                 if (!get_range_by_index (text, i, &range_start, &range_end)) {
1272                                         g_warning ("ENameSelectorEntry is out of sync with model!");
1273                                         return;
1274                                 }
1275
1276                                 g_signal_handlers_block_by_func (name_selector_entry, user_insert_text, name_selector_entry);
1277                                 g_signal_handlers_block_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1278
1279                                 gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry), range_start, range_end);
1280
1281                                 ttext = sanitize_string (email);
1282                                 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), ttext, -1, &range_start);
1283                                 g_free (ttext);
1284
1285                                 g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1286                                 g_signal_handlers_unblock_by_func (name_selector_entry, user_insert_text, name_selector_entry);
1287                         }
1288                         
1289                         if (range_start != -1) {
1290                                 start_pos = range_start;
1291                                 end_pos = start_pos+1;
1292                                 gtk_editable_set_position (GTK_EDITABLE (name_selector_entry),start_pos);
1293                         }
1294                 }
1295         }
1296         gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry),
1297                                   start_pos, end_pos);
1298         
1299         /*If the user is deleting a '"' new destinations have to be created for ',' between the quoted text
1300          Like "fd,ty,uy" is a one entity, but if you remove the quotes it has to be broken doan into 3 seperate
1301          addresses.
1302         */
1303
1304         if (str_b_context [1] == '"') {
1305                 const gchar *p;
1306                 gint j;
1307                 p = text + end_pos;
1308                 for (p = text + (end_pos-1), j = end_pos - 1; *p && *p != '"' ; p = g_utf8_next_char (p), j++) {
1309                         gunichar c = g_utf8_get_char (p);
1310                         if(c == ',') {
1311                                 insert_destination_at_position (name_selector_entry, j+1);
1312                         }
1313                 }
1314                 
1315         }
1316
1317         /* Let model know about changes */
1318         text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1319         if (!*text || strlen(text) <= 0) {
1320                 /* If the entry was completely cleared, remove the initial destination too */
1321                 remove_destination_by_index (name_selector_entry, 0);
1322                 generate_attribute_list (name_selector_entry);
1323         } else  if (!del_space){
1324                 modify_destination_at_position (name_selector_entry, start_pos);
1325         }
1326
1327         /* If editing within the string, we need to regenerate attributes */
1328         if (end_pos < len)
1329                 generate_attribute_list (name_selector_entry);
1330
1331         /* Prevent type-ahead completion */
1332         if (name_selector_entry->type_ahead_complete_cb_id) {
1333                 g_source_remove (name_selector_entry->type_ahead_complete_cb_id);
1334                 name_selector_entry->type_ahead_complete_cb_id = 0;
1335         }
1336         
1337         g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1338 }
1339
1340 static gboolean
1341 completion_match_selected (ENameSelectorEntry *name_selector_entry, GtkTreeModel *model,
1342                            GtkTreeIter *iter)
1343 {
1344         EContact      *contact;
1345         EDestination  *destination;
1346         gint           cursor_pos;
1347         GtkTreeIter    generator_iter;
1348         GtkTreeIter    contact_iter;
1349         gint           email_n;
1350
1351         if (!name_selector_entry->contact_store)
1352                 return FALSE;
1353
1354         gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model),
1355                                                           &generator_iter, iter);
1356         e_tree_model_generator_convert_iter_to_child_iter (name_selector_entry->email_generator,
1357                                                            &contact_iter, &email_n,
1358                                                            &generator_iter);
1359
1360         contact = e_contact_store_get_contact (name_selector_entry->contact_store, &contact_iter);
1361         cursor_pos = gtk_editable_get_position (GTK_EDITABLE (name_selector_entry));
1362
1363         /* Set the contact in the model's destination */
1364
1365         destination = find_destination_at_position (name_selector_entry, cursor_pos);
1366         e_destination_set_contact (destination, contact, email_n);
1367         sync_destination_at_position (name_selector_entry, cursor_pos, &cursor_pos);
1368         gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), ",", -1, &cursor_pos);
1369
1370         /* Place cursor at end of address */
1371
1372         gtk_editable_set_position (GTK_EDITABLE (name_selector_entry), cursor_pos);
1373         g_signal_emit (name_selector_entry, signals[UPDATED], 0, destination, NULL);
1374         return TRUE;
1375 }
1376
1377 static void
1378 entry_activate (ENameSelectorEntry *name_selector_entry)
1379 {
1380         gint         cursor_pos;
1381         gint         range_start, range_end;
1382         ENameSelectorEntryPrivate *priv;
1383         EDestination  *destination;
1384         gint           range_len;
1385         const gchar   *text;
1386         gchar         *cue_str;
1387         
1388         cursor_pos = gtk_editable_get_position (GTK_EDITABLE (name_selector_entry));
1389         if (cursor_pos < 0)
1390                 return;
1391
1392         priv = E_NAME_SELECTOR_ENTRY_GET_PRIVATE (name_selector_entry);
1393
1394         text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1395         if (!get_range_at_position (text, cursor_pos, &range_start, &range_end))
1396                 return;
1397
1398         range_len = range_end - range_start;
1399         if (range_len < COMPLETION_CUE_MIN_LEN)
1400                 return;
1401
1402         destination = find_destination_at_position (name_selector_entry, cursor_pos);
1403         if (!destination)
1404                 return;
1405
1406         cue_str = get_entry_substring (name_selector_entry, range_start, range_end);
1407 #if 0   
1408         if (!find_existing_completion (name_selector_entry, cue_str, &contact,
1409                                        &textrep, &matched_field)) {
1410                 g_free (cue_str);
1411                 return;
1412         }
1413 #endif  
1414         g_free (cue_str);       
1415         sync_destination_at_position (name_selector_entry, cursor_pos, &cursor_pos);
1416
1417         /* Place cursor at end of address */
1418         get_range_at_position (text, cursor_pos, &range_start, &range_end);
1419
1420         if (priv->is_completing) {
1421                 char *str_context=NULL;
1422                 
1423                 str_context = gtk_editable_get_chars (GTK_EDITABLE (name_selector_entry), range_end, range_end+1);
1424                 
1425                 if (str_context[0] != ',') {
1426                         /* At the end*/
1427                         gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), ", ", -1, &range_end);
1428                 } else {
1429                         /* In the middle */
1430                         int newpos = strlen (text);
1431
1432                         /* Doing this we can make sure that It wont ask for completion again. */
1433                         gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), ", ", -1, &newpos);
1434                         g_signal_handlers_block_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1435                         gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry), newpos-2, newpos);
1436                         g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1437
1438                         /* Move it close to next destination*/
1439                         range_end = range_end+2;
1440                         
1441                 }
1442         }
1443
1444         gtk_editable_set_position (GTK_EDITABLE (name_selector_entry), range_end);
1445         g_signal_emit (name_selector_entry, signals[UPDATED], 0, destination, NULL);
1446         
1447         priv->is_completing = FALSE;
1448 }
1449
1450 static gboolean
1451 user_focus_in (ENameSelectorEntry *name_selector_entry, GdkEventFocus *event_focus)
1452 {
1453         /*
1454          * To preserve selected text, do not propagate the event any more. 
1455          */
1456
1457         return TRUE;
1458 }
1459
1460 static gboolean
1461 user_focus_out (ENameSelectorEntry *name_selector_entry, GdkEventFocus *event_focus)
1462 {
1463         ENameSelectorEntryPrivate *priv;
1464
1465         priv = E_NAME_SELECTOR_ENTRY_GET_PRIVATE (name_selector_entry);
1466
1467         if (!event_focus->in && priv->is_completing) {
1468                 entry_activate (name_selector_entry);
1469         }
1470         
1471         if (name_selector_entry->type_ahead_complete_cb_id) {
1472                 g_source_remove (name_selector_entry->type_ahead_complete_cb_id);
1473         }
1474
1475         if (name_selector_entry->update_completions_cb_id) {
1476                 g_source_remove (name_selector_entry->update_completions_cb_id);
1477 }
1478
1479         clear_completion_model (name_selector_entry);
1480
1481         return FALSE;
1482 }
1483
1484 static void
1485 deep_free_list (GList *list)
1486 {
1487         GList *l;
1488
1489         for (l = list; l; l = g_list_next (l))
1490                 g_free (l->data);
1491
1492         g_list_free (list);
1493 }
1494
1495 static void
1496 contact_layout_formatter (GtkCellLayout *cell_layout, GtkCellRenderer *cell, GtkTreeModel *model,
1497                           GtkTreeIter *iter, ENameSelectorEntry *name_selector_entry)
1498 {
1499         EContact      *contact;
1500         GtkTreeIter    generator_iter;
1501         GtkTreeIter    contact_store_iter;
1502         GList         *email_list;
1503         gchar         *string;
1504         gchar         *file_as_str;
1505         gchar         *email_str;
1506         gint           email_n;
1507
1508         if (!name_selector_entry->contact_store)
1509                 return;
1510
1511         gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model),
1512                                                           &generator_iter, iter);
1513         e_tree_model_generator_convert_iter_to_child_iter (name_selector_entry->email_generator,
1514                                                            &contact_store_iter, &email_n,
1515                                                            &generator_iter);
1516
1517         contact = e_contact_store_get_contact (name_selector_entry->contact_store, &contact_store_iter);
1518         email_list = e_contact_get (contact, E_CONTACT_EMAIL);
1519         email_str = g_list_nth_data (email_list, email_n);
1520         file_as_str = e_contact_get (contact, E_CONTACT_FILE_AS);
1521
1522         if (e_contact_get (contact, E_CONTACT_IS_LIST)) {
1523                 string = g_strdup_printf ("%s", file_as_str ? file_as_str : "?");
1524         } else {
1525                 string = g_strdup_printf ("%s%s<%s>", file_as_str ? file_as_str : "",
1526                                           file_as_str ? " " : "",
1527                                           email_str ? email_str : "");
1528         }
1529
1530         g_free (file_as_str);
1531         deep_free_list (email_list);
1532
1533         g_object_set (cell, "text", string, NULL);
1534         g_free (string);
1535 }
1536
1537 static gint
1538 generate_contact_rows (EContactStore *contact_store, GtkTreeIter *iter,
1539                        ENameSelectorEntry *name_selector_entry)
1540 {
1541         EContact    *contact;
1542         const gchar *contact_uid;
1543         GList       *email_list;
1544         gint         n_rows;
1545
1546         contact = e_contact_store_get_contact (contact_store, iter);
1547         g_assert (contact != NULL);
1548
1549         contact_uid = e_contact_get_const (contact, E_CONTACT_UID);
1550         if (!contact_uid)
1551                 return 0;  /* Can happen with broken databases */
1552
1553         if (e_contact_get (contact, E_CONTACT_IS_LIST))
1554                 return 1;
1555
1556         email_list = e_contact_get (contact, E_CONTACT_EMAIL);
1557         n_rows = g_list_length (email_list);
1558         deep_free_list (email_list);
1559
1560         return n_rows;
1561 }
1562
1563 static void
1564 ensure_type_ahead_complete_on_idle (ENameSelectorEntry *name_selector_entry)
1565 {
1566         if (!name_selector_entry->type_ahead_complete_cb_id) {
1567                 name_selector_entry->type_ahead_complete_cb_id =
1568                         g_idle_add ((GSourceFunc) type_ahead_complete_on_idle_cb,
1569                                     name_selector_entry);
1570         }
1571 }
1572
1573 static void
1574 setup_contact_store (ENameSelectorEntry *name_selector_entry)
1575 {
1576         if (name_selector_entry->email_generator) {
1577                 g_object_unref (name_selector_entry->email_generator);
1578                 name_selector_entry->email_generator = NULL;
1579         }
1580
1581         if (name_selector_entry->contact_store) {
1582                 name_selector_entry->email_generator =
1583                         e_tree_model_generator_new (GTK_TREE_MODEL (name_selector_entry->contact_store));
1584
1585                 e_tree_model_generator_set_generate_func (name_selector_entry->email_generator,
1586                                                           (ETreeModelGeneratorGenerateFunc) generate_contact_rows,
1587                                                           name_selector_entry, NULL);
1588
1589                 /* Assign the store to the entry completion */
1590
1591                 gtk_entry_completion_set_model (name_selector_entry->entry_completion,
1592                                                 GTK_TREE_MODEL (name_selector_entry->email_generator));
1593
1594                 /* Set up callback for incoming matches */
1595                 g_signal_connect_swapped (name_selector_entry->contact_store, "row-inserted",
1596                                           G_CALLBACK (ensure_type_ahead_complete_on_idle), name_selector_entry);
1597         } else {
1598                 /* Remove the store from the entry completion */
1599
1600                 gtk_entry_completion_set_model (name_selector_entry->entry_completion, NULL);
1601         }
1602 }
1603
1604 static void
1605 setup_default_contact_store (ENameSelectorEntry *name_selector_entry)
1606 {
1607         GSList *groups;
1608         GSList *l;
1609
1610         g_return_if_fail (name_selector_entry->contact_store == NULL);
1611
1612         /* Create a book for each completion source, and assign them to the contact store */
1613
1614         name_selector_entry->contact_store = e_contact_store_new ();
1615         groups = e_source_list_peek_groups (name_selector_entry->source_list);
1616
1617         for (l = groups; l; l = g_slist_next (l)) {
1618                 ESourceGroup *group   = l->data;
1619                 GSList       *sources = e_source_group_peek_sources (group);
1620                 GSList       *m;
1621
1622                 for (m = sources; m; m = g_slist_next (m)) {
1623                         ESource     *source = m->data;
1624                         EBook       *book;
1625                         const gchar *completion;
1626
1627                         /* Skip non-completion sources */
1628                         completion = e_source_get_property (source, "completion");
1629                         if (!completion || g_ascii_strcasecmp (completion, "true"))
1630                                 continue;
1631
1632                         book = e_load_book_source (source, NULL, NULL);
1633                         if (!book)
1634                                 continue;
1635
1636                         e_contact_store_add_book (name_selector_entry->contact_store, book);
1637                         g_object_unref (book);
1638                 }
1639         }
1640
1641         setup_contact_store (name_selector_entry);
1642 }
1643
1644 static void
1645 destination_row_changed (ENameSelectorEntry *name_selector_entry, GtkTreePath *path, GtkTreeIter *iter)
1646 {
1647         EDestination *destination;
1648         const gchar  *entry_text;
1649         gchar        *text;
1650         gint          range_start, range_end;
1651         gint          n;
1652
1653         n = gtk_tree_path_get_indices (path) [0];
1654         destination = e_destination_store_get_destination (name_selector_entry->destination_store, iter);
1655
1656         if (!destination)
1657                 return;
1658         
1659         g_assert (n >= 0);
1660
1661         entry_text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1662         if (!get_range_by_index (entry_text, n, &range_start, &range_end)) {
1663                 g_warning ("ENameSelectorEntry is out of sync with model!");
1664                 return;
1665         }
1666
1667         g_signal_handlers_block_by_func (name_selector_entry, user_insert_text, name_selector_entry);
1668         g_signal_handlers_block_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1669
1670         gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry), range_start, range_end);
1671
1672         text = sanitize_string (e_destination_get_textrep (destination, FALSE));
1673         gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), text, -1, &range_start);
1674         g_free (text);
1675
1676         g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1677         g_signal_handlers_unblock_by_func (name_selector_entry, user_insert_text, name_selector_entry);
1678
1679         clear_completion_model (name_selector_entry);
1680         generate_attribute_list (name_selector_entry);
1681 }
1682
1683 static void
1684 destination_row_inserted (ENameSelectorEntry *name_selector_entry, GtkTreePath *path, GtkTreeIter *iter)
1685 {
1686         EDestination *destination;
1687         const gchar  *entry_text;
1688         gchar        *text;
1689         gboolean      comma_before = FALSE;
1690         gboolean      comma_after  = FALSE;
1691         gint          range_start, range_end;
1692         gint          insert_pos;
1693         gint          n;
1694
1695         n = gtk_tree_path_get_indices (path) [0];
1696         destination = e_destination_store_get_destination (name_selector_entry->destination_store, iter);
1697
1698         g_assert (n >= 0);
1699         g_assert (destination != NULL);
1700
1701         entry_text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1702
1703         if (get_range_by_index (entry_text, n, &range_start, &range_end) && range_start != range_end) {
1704                 /* Another destination comes after us */
1705                 insert_pos = range_start;
1706                 comma_after = TRUE;
1707         } else if (n > 0 && get_range_by_index (entry_text, n - 1, &range_start, &range_end)) {
1708                 /* Another destination comes before us */
1709                 insert_pos = range_end;
1710                 comma_before = TRUE;
1711         } else if (n == 0) {
1712                 /* We're the sole destination */
1713                 insert_pos = 0;
1714         } else {
1715                 g_warning ("ENameSelectorEntry is out of sync with model!");
1716                 return;
1717         }
1718
1719         g_signal_handlers_block_by_func (name_selector_entry, user_insert_text, name_selector_entry);
1720
1721         if (comma_before)
1722                 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), ", ", -1, &insert_pos);
1723
1724         text = sanitize_string (e_destination_get_textrep (destination, FALSE));
1725         gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), text, -1, &insert_pos);
1726         g_free (text);
1727
1728         if (comma_after)
1729                 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), ", ", -1, &insert_pos);
1730
1731         g_signal_handlers_unblock_by_func (name_selector_entry, user_insert_text, name_selector_entry);
1732
1733         clear_completion_model (name_selector_entry);
1734         generate_attribute_list (name_selector_entry);
1735 }
1736
1737 static void
1738 destination_row_deleted (ENameSelectorEntry *name_selector_entry, GtkTreePath *path)
1739 {
1740         const gchar *text;
1741         gboolean     deleted_comma = FALSE;
1742         gint         range_start, range_end;
1743         gchar       *p0;
1744         gint         n;
1745
1746         n = gtk_tree_path_get_indices (path) [0];
1747         g_assert (n >= 0);
1748
1749         text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));
1750
1751         if (!get_range_by_index (text, n, &range_start, &range_end)) {
1752                 g_warning ("ENameSelectorEntry is out of sync with model!");
1753                 return;
1754         }
1755
1756         /* Expand range for deletion forwards */
1757         for (p0 = g_utf8_offset_to_pointer (text, range_end); *p0;
1758              p0 = g_utf8_next_char (p0), range_end++) {
1759                 gunichar c = g_utf8_get_char (p0);
1760
1761                 /* Gobble spaces directly after comma */
1762                 if (c != ' ' && deleted_comma) {
1763                         range_end--;
1764                         break;
1765                 }
1766
1767                 if (c == ',') {
1768                         deleted_comma = TRUE;
1769                         range_end++;
1770                 }
1771         }
1772
1773         /* Expand range for deletion backwards */
1774         for (p0 = g_utf8_offset_to_pointer (text, range_start); range_start > 0;
1775              p0 = g_utf8_prev_char (p0), range_start--) {
1776                 gunichar c = g_utf8_get_char (p0);
1777
1778                 if (c == ',') {
1779                         if (!deleted_comma) {
1780                                 deleted_comma = TRUE;
1781                                 break;
1782                         }
1783
1784                         range_start++;
1785
1786                         /* Leave a space in front; we deleted the comma and spaces before the
1787                          * following destination */
1788                         p0 = g_utf8_next_char (p0);
1789                         c = g_utf8_get_char (p0);
1790                         if (c == ' ')
1791                                 range_start++;
1792
1793                         break;
1794                 }
1795         }
1796
1797         g_signal_handlers_block_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1798         gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry), range_start, range_end);
1799         g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1800
1801         clear_completion_model (name_selector_entry);
1802         generate_attribute_list (name_selector_entry);
1803 }
1804
1805 static void
1806 setup_destination_store (ENameSelectorEntry *name_selector_entry)
1807 {
1808         GtkTreeIter  iter;
1809
1810         g_signal_connect_swapped (name_selector_entry->destination_store, "row-changed",
1811                                   G_CALLBACK (destination_row_changed), name_selector_entry);
1812         g_signal_connect_swapped (name_selector_entry->destination_store, "row-deleted",
1813                                   G_CALLBACK (destination_row_deleted), name_selector_entry);
1814         g_signal_connect_swapped (name_selector_entry->destination_store, "row-inserted",
1815                                   G_CALLBACK (destination_row_inserted), name_selector_entry);
1816
1817         if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (name_selector_entry->destination_store), &iter))
1818                 return;
1819
1820         do {
1821                 GtkTreePath *path;
1822
1823                 path = gtk_tree_model_get_path (GTK_TREE_MODEL (name_selector_entry->destination_store), &iter);
1824                 g_assert (path);
1825
1826                 destination_row_inserted (name_selector_entry, path, &iter);
1827         } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (name_selector_entry->destination_store), &iter));
1828 }
1829
1830 static gboolean
1831 prepare_popup_destination (ENameSelectorEntry *name_selector_entry, GdkEventButton *event_button)
1832 {
1833         EDestination *destination;
1834         PangoLayout  *layout;
1835         gint          layout_offset_x;
1836         gint          layout_offset_y;
1837         gint          x, y;
1838         gint          index;
1839
1840         if (event_button->type != GDK_BUTTON_PRESS)
1841                 return FALSE;
1842
1843         if (event_button->button != 3)
1844                 return FALSE;
1845
1846         if (name_selector_entry->popup_destination) {
1847                 g_object_unref (name_selector_entry->popup_destination);
1848                 name_selector_entry->popup_destination = NULL;
1849         }
1850
1851         gtk_entry_get_layout_offsets (GTK_ENTRY (name_selector_entry),
1852                                       &layout_offset_x, &layout_offset_y);
1853         x = (event_button->x + 0.5) - layout_offset_x;
1854         y = (event_button->y + 0.5) - layout_offset_y;
1855
1856         if (x < 0 || y < 0)
1857                 return FALSE;
1858
1859         layout = gtk_entry_get_layout (GTK_ENTRY (name_selector_entry));
1860         if (!pango_layout_xy_to_index (layout, x * PANGO_SCALE, y * PANGO_SCALE, &index, NULL))
1861                 return FALSE;
1862
1863         index = gtk_entry_layout_index_to_text_index (GTK_ENTRY (name_selector_entry), index);
1864         destination = find_destination_at_position (name_selector_entry, index);
1865         /* FIXME: Add this to a private variable, in ENameSelectorEntry Class*/
1866         g_object_set_data ((GObject *)name_selector_entry, "index", GINT_TO_POINTER(index));
1867
1868         if (!destination || !e_destination_get_contact (destination))
1869                 return FALSE;
1870
1871         /* TODO: Unref destination when we finalize */
1872         name_selector_entry->popup_destination = g_object_ref (destination);
1873         return FALSE;
1874 }
1875
1876 static EBook *
1877 find_book_by_contact (GList *books, const gchar *contact_uid)
1878 {
1879         GList *l;
1880
1881         for (l = books; l; l = g_list_next (l)) {
1882                 EBook    *book = l->data;
1883                 EContact *contact;
1884                 gboolean  result;
1885
1886                 result = e_book_get_contact (book, contact_uid, &contact, NULL);
1887                 if (contact)
1888                         g_object_unref (contact);
1889
1890                 if (result)
1891                         return book;
1892         }
1893
1894         return NULL;
1895 }
1896
1897 static void
1898 editor_closed_cb (GtkObject *editor, gpointer data)
1899 {
1900         EContact *contact;
1901         gchar *contact_uid;
1902         EDestination *destination;
1903         GList *books;
1904         EBook *book;
1905         gboolean result;
1906         gint email_num;
1907         ENameSelectorEntry *name_selector_entry = E_NAME_SELECTOR_ENTRY (data);
1908         
1909         destination = name_selector_entry->popup_destination;
1910         contact = e_destination_get_contact (destination);
1911         if (!contact)
1912                 return;
1913         contact_uid = e_contact_get (contact, E_CONTACT_UID);
1914         if (!contact_uid)
1915                 return;
1916
1917         if (name_selector_entry->contact_store) {
1918                 books = e_contact_store_get_books (name_selector_entry->contact_store);
1919                 book = find_book_by_contact (books, contact_uid);
1920                 g_list_free (books);
1921         } else {
1922                 book = NULL;
1923         }
1924         if (!book)
1925                 return;
1926
1927         result = e_book_get_contact(book, contact_uid, &contact, NULL);
1928         email_num = e_destination_get_email_num(destination);
1929         e_destination_set_contact (destination, contact, email_num);
1930
1931         g_free (contact_uid);
1932         g_object_unref (contact);
1933         g_object_unref (editor);
1934         g_object_unref (name_selector_entry);
1935 }
1936
1937 static void
1938 popup_activate_inline_expand (ENameSelectorEntry *name_selector_entry, GtkWidget *menu_item)
1939 {
1940         const char *email_list, *text;
1941         gchar *sanitized_text;
1942         EDestination *destination = name_selector_entry->popup_destination;
1943         int position, start, end;
1944
1945         position = GPOINTER_TO_INT(g_object_get_data ((GObject *)name_selector_entry, "index"));
1946         
1947         email_list = e_destination_get_address(destination);
1948         text = gtk_entry_get_text (GTK_ENTRY (name_selector_entry));    
1949         get_range_at_position (text, position, &start, &end);
1950
1951         g_signal_handlers_block_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1952
1953         gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry), start, end);
1954
1955         sanitized_text = sanitize_string (email_list);
1956         gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry), sanitized_text, -1, &start);
1957         g_free (sanitized_text);
1958
1959         g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
1960
1961         clear_completion_model (name_selector_entry);
1962         generate_attribute_list (name_selector_entry);
1963
1964 }
1965
1966 static void
1967 popup_activate_contact (ENameSelectorEntry *name_selector_entry, GtkWidget *menu_item)
1968 {
1969         EBook        *book;
1970         GList        *books;
1971         EDestination *destination;
1972         EContact     *contact;
1973         gchar        *contact_uid;
1974
1975         destination = name_selector_entry->popup_destination;
1976         if (!destination)
1977                 return;
1978
1979         contact = e_destination_get_contact (destination);
1980         if (!contact)
1981                 return;
1982
1983         contact_uid = e_contact_get (contact, E_CONTACT_UID);
1984         if (!contact_uid)
1985                 return;
1986         if (name_selector_entry->contact_store) {
1987                 books = e_contact_store_get_books (name_selector_entry->contact_store);
1988                 /*FIXME: read URI from contact and get the book ?*/
1989                 book = find_book_by_contact (books, contact_uid);
1990                 g_list_free (books);
1991                 g_free (contact_uid);
1992         } else {
1993                 book = NULL;
1994         }
1995
1996         if (!book)
1997                 return;
1998
1999         if (e_destination_is_evolution_list (destination)) {
2000                 GtkWidget *contact_list_editor;
2001
2002                 if (!name_selector_entry->contact_list_editor_func)
2003                         return;
2004
2005                 contact_list_editor = (*name_selector_entry->contact_list_editor_func) (book, contact, FALSE, TRUE);
2006                 g_object_ref (name_selector_entry);
2007                 g_signal_connect (contact_list_editor, "editor_closed",
2008                                   G_CALLBACK (editor_closed_cb), name_selector_entry);
2009         } else {
2010                 GtkWidget *contact_editor;
2011
2012                 if (!name_selector_entry->contact_editor_func)
2013                         return;
2014
2015                 contact_editor = (*name_selector_entry->contact_editor_func) (book, contact, FALSE, TRUE);
2016                 g_object_ref (name_selector_entry);
2017                 g_signal_connect (contact_editor, "editor_closed",
2018                                   G_CALLBACK (editor_closed_cb), name_selector_entry);
2019         }
2020 }
2021
2022 static void
2023 popup_activate_email (ENameSelectorEntry *name_selector_entry, GtkWidget *menu_item)
2024 {
2025         EDestination *destination;
2026         EContact     *contact;
2027         gint          email_num;
2028
2029         destination = name_selector_entry->popup_destination;
2030         if (!destination)
2031                 return;
2032
2033         contact = e_destination_get_contact (destination);
2034         if (!contact)
2035                 return;
2036
2037         email_num = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu_item), "order"));
2038         e_destination_set_contact (destination, contact, email_num);
2039 }
2040
2041 static void
2042 popup_activate_list (EDestination *destination, GtkWidget *item)
2043 {
2044         gboolean status = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item));
2045         
2046         e_destination_set_ignored (destination, !status);       
2047 }
2048
2049 static void
2050 popup_activate_cut (ENameSelectorEntry *name_selector_entry, GtkWidget *menu_item)
2051 {
2052         EDestination *destination;
2053         const char *contact_email;
2054         char *pemail = NULL;
2055         GtkClipboard *clipboard;
2056
2057         destination = name_selector_entry->popup_destination;
2058         contact_email =e_destination_get_address(destination);
2059
2060         g_signal_handlers_block_by_func (name_selector_entry, user_insert_text, name_selector_entry);
2061         g_signal_handlers_block_by_func (name_selector_entry, user_delete_text, name_selector_entry);
2062
2063         clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
2064         pemail = g_strconcat (contact_email, ",", NULL);
2065         gtk_clipboard_set_text (clipboard, pemail, strlen (pemail));
2066
2067         clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
2068         gtk_clipboard_set_text (clipboard, pemail, strlen (pemail));
2069
2070         gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry), 0, 0);
2071         e_destination_store_remove_destination (name_selector_entry->destination_store, destination);
2072
2073         g_free (pemail);
2074         g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
2075         g_signal_handlers_unblock_by_func (name_selector_entry, user_insert_text, name_selector_entry);
2076 }
2077
2078 static void
2079 popup_activate_copy (ENameSelectorEntry *name_selector_entry, GtkWidget *menu_item)
2080 {
2081         EDestination *destination;
2082         const char *contact_email;
2083         char *pemail;
2084         GtkClipboard *clipboard;
2085
2086         destination = name_selector_entry->popup_destination;
2087         contact_email = e_destination_get_address(destination);
2088
2089         g_signal_handlers_block_by_func (name_selector_entry, user_insert_text, name_selector_entry);
2090         g_signal_handlers_block_by_func (name_selector_entry, user_delete_text, name_selector_entry);
2091
2092         clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
2093         pemail = g_strconcat (contact_email, ",", NULL);
2094         gtk_clipboard_set_text (clipboard, pemail, strlen (pemail));
2095
2096         clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
2097         gtk_clipboard_set_text (clipboard, pemail, strlen (pemail));
2098         g_free (pemail);
2099         g_signal_handlers_unblock_by_func (name_selector_entry, user_delete_text, name_selector_entry);
2100         g_signal_handlers_unblock_by_func (name_selector_entry, user_insert_text, name_selector_entry);
2101
2102 }
2103
2104 static void
2105 destination_set_list (GtkWidget *item, EDestination *destination)
2106 {
2107         EContact *contact;
2108         gboolean status = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item));
2109
2110         contact = e_destination_get_contact (destination);
2111         if (!contact)
2112                 return;
2113         
2114         e_destination_set_ignored (destination, !status);
2115 }
2116
2117 static void
2118 destination_set_email (GtkWidget *item, EDestination *destination)
2119 {
2120         int email_num;
2121         EContact *contact;
2122
2123         if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item)))
2124                 return;
2125         contact = e_destination_get_contact (destination);
2126         if (!contact)
2127                 return;
2128
2129         email_num = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "order"));
2130         e_destination_set_contact (destination, contact, email_num);
2131 }
2132
2133 static void
2134 populate_popup (ENameSelectorEntry *name_selector_entry, GtkMenu *menu)
2135 {
2136         EDestination *destination;
2137         EContact     *contact;
2138         GtkWidget    *menu_item;
2139         GList        *email_list=NULL;
2140         GList        *l;
2141         gint          i;
2142         char         *edit_label;
2143         char         *cut_label;
2144         char         *copy_label;
2145         int           email_num, len;
2146         GSList       *group = NULL;
2147         gboolean      is_list;
2148         gboolean      show_menu = FALSE;
2149
2150         destination = name_selector_entry->popup_destination;
2151         if (!destination)
2152                 return;
2153
2154         contact = e_destination_get_contact (destination);
2155         if (!contact)
2156                 return;
2157
2158         /* Prepend the menu items, backwards */
2159
2160         /* Separator */
2161
2162         menu_item = gtk_separator_menu_item_new ();
2163         gtk_widget_show (menu_item);
2164         gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
2165         email_num = e_destination_get_email_num (destination);
2166
2167         /* Addresses */
2168         is_list = e_contact_get (contact, E_CONTACT_IS_LIST) ? TRUE : FALSE;
2169         if (is_list) {
2170                 const GList *dests = e_destination_list_get_dests (destination);
2171                 GList *iter;
2172                 int length = g_list_length ((GList *)dests);
2173
2174                 for (iter = (GList *)dests; iter; iter = iter->next) {
2175                         EDestination *dest = (EDestination *) iter->data;
2176                         const char *email = e_destination_get_email (dest);
2177                         
2178                         if (!email || *email == '\0')
2179                                 continue;       
2180
2181                         if (length > 1) {
2182                                 menu_item = gtk_check_menu_item_new_with_label (email);
2183                                 g_signal_connect (menu_item, "toggled", G_CALLBACK (destination_set_list), dest);                               
2184                         } else {
2185                                 menu_item = gtk_menu_item_new_with_label (email);
2186                         }
2187                         
2188                         gtk_widget_show (menu_item);
2189                         gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
2190                         show_menu = TRUE;
2191
2192                         if (length > 1) {
2193                                 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item), !e_destination_is_ignored(dest));
2194                                 g_signal_connect_swapped (menu_item, "activate", G_CALLBACK (popup_activate_list),
2195                                                           dest);        
2196                         }
2197                 }
2198                 
2199         } else {
2200                 email_list = e_contact_get (contact, E_CONTACT_EMAIL);
2201                 len = g_list_length (email_list);
2202
2203                 for (l = email_list, i = 0; l; l = g_list_next (l), i++) {
2204                         gchar *email = l->data;
2205
2206                         if (!email || *email == '\0')
2207                                 continue;
2208                 
2209                         if (len > 1) {
2210                                 menu_item = gtk_radio_menu_item_new_with_label (group, email);
2211                                 group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menu_item));
2212                                 g_signal_connect (menu_item, "toggled", G_CALLBACK (destination_set_email), destination);
2213                         } else {
2214                                 menu_item = gtk_menu_item_new_with_label (email);                       
2215                         }
2216                         
2217                         gtk_widget_show (menu_item);
2218                         gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
2219                         show_menu = TRUE;
2220                         g_object_set_data (G_OBJECT (menu_item), "order", GINT_TO_POINTER (i));                 
2221
2222                         if ( i == email_num && len > 1 ) {
2223                                 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item), TRUE);
2224                                 g_signal_connect_swapped (menu_item, "activate", G_CALLBACK (popup_activate_email),
2225                                                           name_selector_entry);
2226                         }
2227                 }
2228         }
2229
2230         /* Separator */
2231
2232         if (show_menu) {
2233                 menu_item = gtk_separator_menu_item_new ();
2234                 gtk_widget_show (menu_item);
2235                 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
2236         }
2237
2238         /* Expand a list inline */
2239         if (is_list) {
2240                 /* To Translators: This would be similiar to "Expand MyList Inline" where MyList is a Contact List*/
2241                 edit_label = g_strdup_printf (_("E_xpand %s Inline"), (char *)e_contact_get_const (contact, E_CONTACT_FILE_AS)); 
2242                 menu_item = gtk_menu_item_new_with_mnemonic (edit_label);
2243                 g_free (edit_label);
2244                 gtk_widget_show (menu_item);
2245                 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
2246                 g_signal_connect_swapped (menu_item, "activate", G_CALLBACK (popup_activate_inline_expand),
2247                                           name_selector_entry);
2248
2249                 /* Separator */
2250                 menu_item = gtk_separator_menu_item_new ();
2251                 gtk_widget_show (menu_item);
2252                 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
2253         }
2254
2255         /* Copy Contact Item */
2256         copy_label = g_strdup_printf (_("Cop_y %s"), (char *)e_contact_get_const (contact, E_CONTACT_FILE_AS));
2257         menu_item = gtk_menu_item_new_with_mnemonic (copy_label);
2258         g_free (copy_label);
2259         gtk_widget_show (menu_item);
2260         gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
2261
2262         g_signal_connect_swapped (menu_item, "activate", G_CALLBACK (popup_activate_copy),
2263                                   name_selector_entry);
2264
2265         /* Cut Contact Item */
2266         cut_label = g_strdup_printf (_("C_ut %s"), (char *)e_contact_get_const (contact, E_CONTACT_FILE_AS));
2267         menu_item = gtk_menu_item_new_with_mnemonic (cut_label);
2268         g_free (cut_label);     
2269         gtk_widget_show (menu_item);
2270         gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
2271         
2272         g_signal_connect_swapped (menu_item, "activate", G_CALLBACK (popup_activate_cut),
2273                                   name_selector_entry);
2274         
2275         if (show_menu) {
2276                 menu_item = gtk_separator_menu_item_new ();
2277                 gtk_widget_show (menu_item);
2278                 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
2279         }
2280
2281         /* Edit Contact item */
2282
2283         edit_label = g_strdup_printf (_("_Edit %s"), (char *)e_contact_get_const (contact, E_CONTACT_FILE_AS)); 
2284         menu_item = gtk_menu_item_new_with_mnemonic (edit_label);
2285         g_free (edit_label);
2286         gtk_widget_show (menu_item);
2287         gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
2288
2289         g_signal_connect_swapped (menu_item, "activate", G_CALLBACK (popup_activate_contact),
2290                                   name_selector_entry);
2291
2292         deep_free_list (email_list);
2293 }
2294
2295 static void
2296 e_name_selector_entry_init (ENameSelectorEntry *name_selector_entry)
2297 {
2298   GtkCellRenderer *renderer;
2299   ENameSelectorEntryPrivate *priv;
2300   GConfClient *gconf;
2301
2302   priv = E_NAME_SELECTOR_ENTRY_GET_PRIVATE (name_selector_entry);
2303
2304   /* Source list */
2305
2306   if (!e_book_get_addressbooks (&name_selector_entry->source_list, NULL)) {
2307           g_warning ("ENameSelectorEntry can't find any addressbooks!");
2308           return;
2309   }
2310
2311   /* read minimum_query_length from gconf*/
2312   gconf = gconf_client_get_default();
2313   if (COMPLETION_CUE_MIN_LEN == 0) {
2314           if ((COMPLETION_CUE_MIN_LEN = gconf_client_get_int (gconf, MINIMUM_QUERY_LENGTH, NULL)))
2315                 ;
2316           else COMPLETION_CUE_MIN_LEN = 3;
2317   }
2318   g_object_unref (G_OBJECT (gconf));
2319
2320   /* Edit signals */
2321
2322   g_signal_connect (name_selector_entry, "insert-text", G_CALLBACK (user_insert_text), name_selector_entry);
2323   g_signal_connect (name_selector_entry, "delete-text", G_CALLBACK (user_delete_text), name_selector_entry);
2324   g_signal_connect (name_selector_entry, "focus-out-event", G_CALLBACK (user_focus_out), name_selector_entry);
2325   g_signal_connect_after (name_selector_entry, "focus-in-event", G_CALLBACK (user_focus_in), name_selector_entry);
2326
2327   /* Exposition */
2328
2329   g_signal_connect (name_selector_entry, "expose-event", G_CALLBACK (expose_event), name_selector_entry);
2330
2331   /* Activation: Complete current entry if possible */
2332
2333   g_signal_connect (name_selector_entry, "activate", G_CALLBACK (entry_activate), name_selector_entry);
2334
2335   /* Pop-up menu */
2336
2337   g_signal_connect (name_selector_entry, "button-press-event", G_CALLBACK (prepare_popup_destination), name_selector_entry);
2338   g_signal_connect (name_selector_entry, "populate-popup", G_CALLBACK (populate_popup), name_selector_entry);
2339
2340   /* Completion */
2341
2342   name_selector_entry->email_generator = NULL;
2343
2344   name_selector_entry->entry_completion = gtk_entry_completion_new ();
2345   gtk_entry_completion_set_match_func (name_selector_entry->entry_completion,
2346                                        (GtkEntryCompletionMatchFunc) completion_match_cb, NULL, NULL);
2347   g_signal_connect_swapped (name_selector_entry->entry_completion, "match-selected",
2348                             G_CALLBACK (completion_match_selected), name_selector_entry);
2349
2350   gtk_entry_set_completion (GTK_ENTRY (name_selector_entry), name_selector_entry->entry_completion);
2351
2352   /* Completion list name renderer */
2353
2354   renderer = gtk_cell_renderer_text_new ();
2355   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (name_selector_entry->entry_completion),
2356                               renderer, TRUE);
2357   gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (name_selector_entry->entry_completion),
2358                                       GTK_CELL_RENDERER (renderer),
2359                                       (GtkCellLayoutDataFunc) contact_layout_formatter,
2360                                       name_selector_entry, NULL);
2361
2362   /* Destination store */
2363
2364   name_selector_entry->destination_store = e_destination_store_new ();
2365   setup_destination_store (name_selector_entry);
2366   priv->is_completing = FALSE;
2367 }
2368
2369 /**
2370  * e_name_selector_entry_new:
2371  *
2372  * Creates a new #ENameSelectorEntry.
2373  *
2374  * Return value: A new #ENameSelectorEntry.
2375  **/
2376 ENameSelectorEntry *
2377 e_name_selector_entry_new (void)
2378 {
2379           return g_object_new (e_name_selector_entry_get_type (), NULL);
2380 }
2381
2382 /**
2383  * e_name_selector_entry_peek_contact_store:
2384  * @name_selector_entry: an #ENameSelectorEntry
2385  *
2386  * Gets the #EContactStore being used by @name_selector_entry.
2387  *
2388  * Return value: An #EContactStore.
2389  **/
2390 EContactStore *
2391 e_name_selector_entry_peek_contact_store (ENameSelectorEntry *name_selector_entry)
2392 {
2393         g_return_val_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry), NULL);
2394
2395         return name_selector_entry->contact_store;
2396 }
2397
2398 /**
2399  * e_name_selector_entry_set_contact_store:
2400  * @name_selector_entry: an #ENameSelectorEntry
2401  * @contact_store: an #EContactStore to use
2402  *
2403  * Sets the #EContactStore being used by @name_selector_entry to @contact_store.
2404  **/
2405 void
2406 e_name_selector_entry_set_contact_store (ENameSelectorEntry *name_selector_entry,
2407                                          EContactStore *contact_store)
2408 {
2409         g_return_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry));
2410         g_return_if_fail (contact_store == NULL || E_IS_CONTACT_STORE (contact_store));
2411
2412         if (contact_store == name_selector_entry->contact_store)
2413                 return;
2414
2415         if (name_selector_entry->contact_store)
2416                 g_object_unref (name_selector_entry->contact_store);
2417         name_selector_entry->contact_store = contact_store;
2418         if (name_selector_entry->contact_store)
2419                 g_object_ref (name_selector_entry->contact_store);
2420
2421         setup_contact_store (name_selector_entry);
2422 }
2423
2424 /**
2425  * e_name_selector_entry_peek_destination_store:
2426  * @name_selector_entry: an #ENameSelectorEntry
2427  * 
2428  * Gets the #EDestinationStore being used to store @name_selector_entry's destinations.
2429  *
2430  * Return value: An #EDestinationStore.
2431  **/
2432 EDestinationStore *
2433 e_name_selector_entry_peek_destination_store (ENameSelectorEntry *name_selector_entry)
2434 {
2435         g_return_val_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry), NULL);
2436
2437         return name_selector_entry->destination_store;
2438 }
2439
2440 /**
2441  * e_name_selector_entry_set_destination_store:
2442  * @name_selector_entry: an #ENameSelectorEntry
2443  * @destination_store: an #EDestinationStore to use
2444  *
2445  * Sets @destination_store as the #EDestinationStore to be used to store
2446  * destinations for @name_selector_entry.
2447  **/
2448 void
2449 e_name_selector_entry_set_destination_store  (ENameSelectorEntry *name_selector_entry,
2450                                               EDestinationStore *destination_store)
2451 {
2452         g_return_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry));
2453         g_return_if_fail (E_IS_DESTINATION_STORE (destination_store));
2454
2455         if (destination_store == name_selector_entry->destination_store)
2456                 return;
2457
2458         g_object_unref (name_selector_entry->destination_store);
2459         name_selector_entry->destination_store = g_object_ref (destination_store);
2460
2461         setup_destination_store (name_selector_entry);
2462 }
2463
2464 /**
2465  * e_name_selector_entry_set_contact_editor_func:
2466  *
2467  * DO NOT USE.
2468  **/
2469 void
2470 e_name_selector_entry_set_contact_editor_func (ENameSelectorEntry *name_selector_entry, gpointer func)
2471 {
2472         name_selector_entry->contact_editor_func = func;
2473 }
2474
2475 /**
2476  * e_name_selector_entry_set_contact_list_editor_func:
2477  *
2478  * DO NOT USE.
2479  **/
2480 void
2481 e_name_selector_entry_set_contact_list_editor_func (ENameSelectorEntry *name_selector_entry, gpointer func)
2482 {
2483         name_selector_entry->contact_list_editor_func = func;
2484 }