1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /* e-name-selector-entry.c - Single-line text entry widget for EDestinations.
5 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
19 * Authors: Hans Petter Jansson <hpj@novell.com>
24 #include <glib/gi18n-lib.h>
26 #include <camel/camel.h>
27 #include <libebackend/libebackend.h>
29 #include "e-name-selector-entry.h"
31 #define E_NAME_SELECTOR_ENTRY_GET_PRIVATE(obj) \
32 (G_TYPE_INSTANCE_GET_PRIVATE \
33 ((obj), E_TYPE_NAME_SELECTOR_ENTRY, ENameSelectorEntryPrivate))
35 struct _ENameSelectorEntryPrivate
{
36 EClientCache
*client_cache
;
37 gint minimum_query_length
;
38 gboolean show_address
;
40 PangoAttrList
*attr_list
;
41 EContactStore
*contact_store
;
42 ETreeModelGenerator
*email_generator
;
43 EDestinationStore
*destination_store
;
44 GtkEntryCompletion
*entry_completion
;
46 guint type_ahead_complete_cb_id
;
47 guint update_completions_cb_id
;
49 EDestination
*popup_destination
;
51 gpointer (*contact_editor_func
) (EBookClient
*,
55 gpointer (*contact_list_editor_func
)
61 gboolean is_completing
;
62 GSList
*user_query_fields
;
64 /* For asynchronous operations. */
67 GHashTable
*known_contacts
; /* gchar * ~> 1 */
73 PROP_MINIMUM_QUERY_LENGTH
,
82 static guint signals
[LAST_SIGNAL
] = { 0 };
85 G_DEFINE_TYPE_WITH_CODE (
87 e_name_selector_entry
,
89 G_IMPLEMENT_INTERFACE (
90 E_TYPE_EXTENSIBLE
, NULL
))
92 /* 1/3 of the second to wait until invoking autocomplete lookup */
93 #define AUTOCOMPLETE_TIMEOUT 333
95 /* 1/20 of a second to wait until show the completion results */
96 #define SHOW_RESULT_TIMEOUT 50
98 #define re_set_timeout(id,func,ptr,tout) G_STMT_START { \
100 g_source_remove (id); \
101 id = e_named_timeout_add (tout, func, ptr); \
104 static void destination_row_inserted (ENameSelectorEntry
*name_selector_entry
, GtkTreePath
*path
, GtkTreeIter
*iter
);
105 static void destination_row_changed (ENameSelectorEntry
*name_selector_entry
, GtkTreePath
*path
, GtkTreeIter
*iter
);
106 static void destination_row_deleted (ENameSelectorEntry
*name_selector_entry
, GtkTreePath
*path
);
108 static void user_insert_text (ENameSelectorEntry
*name_selector_entry
, gchar
*new_text
, gint new_text_length
, gint
*position
, gpointer user_data
);
109 static void user_delete_text (ENameSelectorEntry
*name_selector_entry
, gint start_pos
, gint end_pos
, gpointer user_data
);
111 static void setup_default_contact_store (ENameSelectorEntry
*name_selector_entry
);
112 static void deep_free_list (GList
*list
);
115 name_selector_entry_set_property (GObject
*object
,
120 switch (property_id
) {
121 case PROP_CLIENT_CACHE
:
122 e_name_selector_entry_set_client_cache (
123 E_NAME_SELECTOR_ENTRY (object
),
124 g_value_get_object (value
));
127 case PROP_MINIMUM_QUERY_LENGTH
:
128 e_name_selector_entry_set_minimum_query_length (
129 E_NAME_SELECTOR_ENTRY (object
),
130 g_value_get_int (value
));
133 case PROP_SHOW_ADDRESS
:
134 e_name_selector_entry_set_show_address (
135 E_NAME_SELECTOR_ENTRY (object
),
136 g_value_get_boolean (value
));
140 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
144 name_selector_entry_get_property (GObject
*object
,
149 switch (property_id
) {
150 case PROP_CLIENT_CACHE
:
151 g_value_take_object (
153 e_name_selector_entry_ref_client_cache (
154 E_NAME_SELECTOR_ENTRY (object
)));
157 case PROP_MINIMUM_QUERY_LENGTH
:
160 e_name_selector_entry_get_minimum_query_length (
161 E_NAME_SELECTOR_ENTRY (object
)));
164 case PROP_SHOW_ADDRESS
:
165 g_value_set_boolean (
167 e_name_selector_entry_get_show_address (
168 E_NAME_SELECTOR_ENTRY (object
)));
172 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
176 name_selector_entry_dispose (GObject
*object
)
178 ENameSelectorEntryPrivate
*priv
;
180 priv
= E_NAME_SELECTOR_ENTRY_GET_PRIVATE (object
);
182 if (priv
->client_cache
!= NULL
) {
183 g_object_unref (priv
->client_cache
);
184 priv
->client_cache
= NULL
;
187 if (priv
->attr_list
!= NULL
) {
188 pango_attr_list_unref (priv
->attr_list
);
189 priv
->attr_list
= NULL
;
192 if (priv
->entry_completion
) {
193 g_object_unref (priv
->entry_completion
);
194 priv
->entry_completion
= NULL
;
197 if (priv
->destination_store
) {
198 g_object_unref (priv
->destination_store
);
199 priv
->destination_store
= NULL
;
202 if (priv
->email_generator
) {
203 g_object_unref (priv
->email_generator
);
204 priv
->email_generator
= NULL
;
207 if (priv
->contact_store
) {
208 g_object_unref (priv
->contact_store
);
209 priv
->contact_store
= NULL
;
212 if (priv
->known_contacts
) {
213 g_hash_table_destroy (priv
->known_contacts
);
214 priv
->known_contacts
= NULL
;
217 g_slist_foreach (priv
->user_query_fields
, (GFunc
) g_free
, NULL
);
218 g_slist_free (priv
->user_query_fields
);
219 priv
->user_query_fields
= NULL
;
221 /* Cancel any stuck book loading operations. */
222 while (!g_queue_is_empty (&priv
->cancellables
)) {
223 GCancellable
*cancellable
;
225 cancellable
= g_queue_pop_head (&priv
->cancellables
);
226 g_cancellable_cancel (cancellable
);
227 g_object_unref (cancellable
);
230 /* Chain up to parent's dispose() method. */
231 G_OBJECT_CLASS (e_name_selector_entry_parent_class
)->dispose (object
);
235 name_selector_entry_constructed (GObject
*object
)
237 /* Chain up to parent's constructed() method. */
238 G_OBJECT_CLASS (e_name_selector_entry_parent_class
)->constructed (object
);
240 e_extensible_load_extensions (E_EXTENSIBLE (object
));
244 name_selector_entry_realize (GtkWidget
*widget
)
246 ENameSelectorEntryPrivate
*priv
;
248 priv
= E_NAME_SELECTOR_ENTRY_GET_PRIVATE (widget
);
250 /* Chain up to parent's realize() method. */
251 GTK_WIDGET_CLASS (e_name_selector_entry_parent_class
)->realize (widget
);
253 if (priv
->contact_store
== NULL
)
254 setup_default_contact_store (E_NAME_SELECTOR_ENTRY (widget
));
258 name_selector_entry_drag_data_received (GtkWidget
*widget
,
259 GdkDragContext
*context
,
262 GtkSelectionData
*selection_data
,
266 CamelInternetAddress
*address
;
267 gint n_addresses
= 0;
270 address
= camel_internet_address_new ();
271 text
= (gchar
*) gtk_selection_data_get_text (selection_data
);
273 /* See if Camel can parse a valid email address from the text. */
274 if (text
!= NULL
&& *text
!= '\0') {
275 camel_url_decode (text
);
276 if (g_ascii_strncasecmp (text
, "mailto:", 7) == 0)
277 n_addresses
= camel_address_decode (
278 CAMEL_ADDRESS (address
), text
+ 7);
280 n_addresses
= camel_address_decode (
281 CAMEL_ADDRESS (address
), text
);
284 if (n_addresses
> 0) {
285 GtkEditable
*editable
;
286 GdkDragAction action
;
290 editable
= GTK_EDITABLE (widget
);
291 gtk_editable_set_position (editable
, -1);
292 position
= gtk_editable_get_position (editable
);
296 text
= camel_address_format (CAMEL_ADDRESS (address
));
297 gtk_editable_insert_text (editable
, text
, -1, &position
);
299 action
= gdk_drag_context_get_selected_action (context
);
300 delete = (action
== GDK_ACTION_MOVE
);
301 gtk_drag_finish (context
, TRUE
, delete, time
);
304 g_object_unref (address
);
307 if (n_addresses
<= 0)
308 /* Chain up to parent's drag_data_received() method. */
309 GTK_WIDGET_CLASS (e_name_selector_entry_parent_class
)->
311 widget
, context
, x
, y
,
312 selection_data
, info
, time
);
316 e_name_selector_entry_class_init (ENameSelectorEntryClass
*class)
318 GObjectClass
*object_class
;
319 GtkWidgetClass
*widget_class
;
321 g_type_class_add_private (class, sizeof (ENameSelectorEntryPrivate
));
323 object_class
= G_OBJECT_CLASS (class);
324 object_class
->set_property
= name_selector_entry_set_property
;
325 object_class
->get_property
= name_selector_entry_get_property
;
326 object_class
->dispose
= name_selector_entry_dispose
;
327 object_class
->constructed
= name_selector_entry_constructed
;
329 widget_class
= GTK_WIDGET_CLASS (class);
330 widget_class
->realize
= name_selector_entry_realize
;
331 widget_class
->drag_data_received
= name_selector_entry_drag_data_received
;
334 * ENameSelectorEntry:client-cache:
336 * Cache of shared #EClient instances.
338 g_object_class_install_property (
341 g_param_spec_object (
344 "Cache of shared EClient instances",
348 G_PARAM_STATIC_STRINGS
));
350 g_object_class_install_property (
352 PROP_MINIMUM_QUERY_LENGTH
,
354 "minimum-query-length",
355 "Minimum Query Length",
360 G_PARAM_STATIC_STRINGS
));
362 g_object_class_install_property (
365 g_param_spec_boolean (
371 G_PARAM_STATIC_STRINGS
));
373 signals
[UPDATED
] = g_signal_new (
375 E_TYPE_NAME_SELECTOR_ENTRY
,
377 G_STRUCT_OFFSET (ENameSelectorEntryClass
, updated
),
379 g_cclosure_marshal_VOID__POINTER
,
380 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
384 describe_contact (EContact
*contact
)
386 GString
*description
;
388 GList
*emails
, *link
;
390 g_return_val_if_fail (E_IS_CONTACT (contact
), NULL
);
392 emails
= e_contact_get (contact
, E_CONTACT_EMAIL
);
393 /* Cannot merge one contact with multiple addresses with another contact */
394 if (!e_contact_get (contact
, E_CONTACT_IS_LIST
) && emails
&& emails
->next
) {
395 deep_free_list (emails
);
399 description
= g_string_new ("");
401 if (e_contact_get (contact
, E_CONTACT_IS_LIST
)) {
402 g_string_append (description
, "list\n");
404 g_string_append (description
, "indv\n");
407 str
= e_contact_get_const (contact
, E_CONTACT_FILE_AS
);
408 g_string_append (description
, str
? str
: "");
409 g_string_append (description
, "\n");
411 str
= e_contact_get_const (contact
, E_CONTACT_FULL_NAME
);
412 g_string_append (description
, str
? str
: "");
413 g_string_append (description
, "\n");
415 emails
= e_contact_get (contact
, E_CONTACT_EMAIL
);
416 emails
= g_list_sort (emails
, (GCompareFunc
) g_ascii_strcasecmp
);
417 for (link
= emails
; link
; link
= g_list_next (link
)) {
420 g_string_append (description
, str
? str
: "");
421 g_string_append (description
, "\n");
424 deep_free_list (emails
);
426 return g_string_free (description
, FALSE
);
430 is_duplicate_contact_and_remember (ENameSelectorEntry
*nsentry
,
435 g_return_val_if_fail (E_IS_NAME_SELECTOR_ENTRY (nsentry
), FALSE
);
436 g_return_val_if_fail (E_IS_CONTACT (contact
), FALSE
);
438 description
= describe_contact (contact
);
440 /* Might be a contact with multiple addresses */
444 if (g_hash_table_lookup (nsentry
->priv
->known_contacts
, description
)) {
445 g_free (description
);
449 g_hash_table_insert (nsentry
->priv
->known_contacts
, description
, GINT_TO_POINTER (1));
454 /* Remove unquoted commas and control characters from string */
456 sanitize_string (const gchar
*string
)
459 gboolean quoted
= FALSE
;
462 gstring
= g_string_new ("");
465 return g_string_free (gstring
, FALSE
);
467 for (p
= string
; *p
; p
= g_utf8_next_char (p
)) {
468 gunichar c
= g_utf8_get_char (p
);
472 else if (c
== ',' && !quoted
)
474 else if (c
== '\t' || c
== '\n')
477 g_string_append_unichar (gstring
, c
);
480 return g_string_free (gstring
, FALSE
);
483 /* Called for each list store entry whenever the user types (but not on cut/paste) */
485 completion_match_cb (GtkEntryCompletion
*completion
,
490 ENS_DEBUG (g_print ("completion_match_cb, key=%s\n", key
));
495 /* Gets context of n_unichars total (n_unicars / 2, before and after position)
496 * and places them in array. If any positions would be outside the string, the
497 * corresponding unichars are set to zero. */
499 get_utf8_string_context (const gchar
*string
,
509 /* n_unichars must be even */
510 g_return_if_fail (n_unichars
% 2 == 0);
512 len
= g_utf8_strlen (string
, -1);
513 gap
= n_unichars
/ 2;
515 for (i
= 0; i
< n_unichars
; i
++) {
516 gint char_pos
= position
- gap
+ i
;
518 if (char_pos
< 0 || char_pos
>= len
) {
524 p
= g_utf8_next_char (p
);
526 p
= g_utf8_offset_to_pointer (string
, char_pos
);
528 unichars
[i
] = g_utf8_get_char (p
);
533 get_range_at_position (const gchar
*string
,
539 gboolean quoted
= FALSE
;
540 gint local_start_pos
= 0;
541 gint local_end_pos
= 0;
544 if (!string
|| !*string
)
547 for (p
= string
, i
= 0; *p
; p
= g_utf8_next_char (p
), i
++) {
548 gunichar c
= g_utf8_get_char (p
);
552 } else if (c
== ',' && !quoted
) {
554 /* Start right after comma */
555 local_start_pos
= i
+ 1;
557 /* Stop right before comma */
561 } else if (c
== ' ' && local_start_pos
== i
) {
562 /* Adjust start to skip space after first comma */
567 /* If we didn't hit a comma, we must've hit NULL, and ours was the last element. */
572 *start_pos
= local_start_pos
;
574 *end_pos
= local_end_pos
;
580 is_quoted_at (const gchar
*string
,
584 gboolean quoted
= FALSE
;
587 for (p
= string
, i
= 0; *p
&& i
< pos
; p
= g_utf8_next_char (p
), i
++) {
588 gunichar c
= g_utf8_get_char (p
);
598 get_index_at_position (const gchar
*string
,
602 gboolean quoted
= FALSE
;
606 for (p
= string
, i
= 0; *p
&& i
< pos
; p
= g_utf8_next_char (p
), i
++) {
607 gunichar c
= g_utf8_get_char (p
);
611 else if (c
== ',' && !quoted
)
619 get_range_by_index (const gchar
*string
,
625 gboolean quoted
= FALSE
;
629 for (p
= string
, i
= 0; *p
&& n
< index
; p
= g_utf8_next_char (p
), i
++) {
630 gunichar c
= g_utf8_get_char (p
);
634 if (c
== ',' && !quoted
)
641 return get_range_at_position (string
, i
, start_pos
, end_pos
);
645 get_address_at_position (const gchar
*string
,
650 const gchar
*start_p
;
653 if (!get_range_at_position (string
, pos
, &start_pos
, &end_pos
))
656 start_p
= g_utf8_offset_to_pointer (string
, start_pos
);
657 end_p
= g_utf8_offset_to_pointer (string
, end_pos
);
659 return g_strndup (start_p
, end_p
- start_p
);
662 /* Finds the destination in model */
663 static EDestination
*
664 find_destination_by_index (ENameSelectorEntry
*name_selector_entry
,
670 path
= gtk_tree_path_new_from_indices (index
, -1);
671 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (name_selector_entry
->priv
->destination_store
),
673 /* If we have zero destinations, getting a NULL destination at index 0
676 g_warning ("ENameSelectorEntry is out of sync with model!");
677 gtk_tree_path_free (path
);
680 gtk_tree_path_free (path
);
682 return e_destination_store_get_destination (name_selector_entry
->priv
->destination_store
, &iter
);
685 /* Finds the destination in model */
686 static EDestination
*
687 find_destination_at_position (ENameSelectorEntry
*name_selector_entry
,
693 text
= gtk_entry_get_text (GTK_ENTRY (name_selector_entry
));
694 index
= get_index_at_position (text
, pos
);
696 return find_destination_by_index (name_selector_entry
, index
);
699 /* Builds destination from our text */
700 static EDestination
*
701 build_destination_at_position (const gchar
*string
,
704 EDestination
*destination
;
707 address
= get_address_at_position (string
, pos
);
711 destination
= e_destination_new ();
712 e_destination_set_raw (destination
, address
);
719 name_style_query (const gchar
*field
,
724 GString
*out
= g_string_new ("");
728 spaced_str
= sanitize_string (value
);
729 g_strstrip (spaced_str
);
731 strv
= g_strsplit (spaced_str
, " ", 0);
733 if (strv
[0] && strv
[1]) {
734 g_string_append (out
, "(or ");
735 comma_str
= g_strjoinv (", ", strv
);
740 g_string_append (out
, " (beginswith ");
741 e_sexp_encode_string (out
, field
);
742 e_sexp_encode_string (out
, spaced_str
);
743 g_string_append (out
, ")");
746 g_string_append (out
, " (beginswith ");
748 e_sexp_encode_string (out
, field
);
749 g_strstrip (comma_str
);
750 e_sexp_encode_string (out
, comma_str
);
751 g_string_append (out
, "))");
754 query
= g_string_free (out
, FALSE
);
764 escape_sexp_string (const gchar
*string
)
767 gchar
*encoded_string
;
769 gstring
= g_string_new ("");
770 e_sexp_encode_string (gstring
, string
);
772 encoded_string
= gstring
->str
;
773 g_string_free (gstring
, FALSE
);
775 return encoded_string
;
779 * ens_util_populate_user_query_fields:
781 * Populates list of user query fields to string usable in query string.
782 * Returned pointer is either newly allocated string, supposed to be freed with g_free,
783 * or NULL if no fields defined.
788 ens_util_populate_user_query_fields (GSList
*user_query_fields
,
789 const gchar
*cue_str
,
790 const gchar
*encoded_cue_str
)
792 GString
*user_fields
;
795 g_return_val_if_fail (cue_str
!= NULL
, NULL
);
796 g_return_val_if_fail (encoded_cue_str
!= NULL
, NULL
);
798 user_fields
= g_string_new ("");
800 for (s
= user_query_fields
; s
; s
= s
->next
) {
801 const gchar
*field
= s
->data
;
803 if (!field
|| !*field
)
807 g_string_append_printf (user_fields
, " (beginswith \"%s\" %s) ", field
+ 1, encoded_cue_str
);
808 } else if (*field
== '@') {
809 g_string_append_printf (user_fields
, " (is \"%s\" %s) ", field
+ 1, encoded_cue_str
);
811 gchar
*tmp
= name_style_query (field
, cue_str
);
813 g_string_append (user_fields
, " ");
814 g_string_append (user_fields
, tmp
);
815 g_string_append (user_fields
, " ");
820 return g_string_free (user_fields
, !user_fields
->str
|| !*user_fields
->str
);
824 set_completion_query (ENameSelectorEntry
*name_selector_entry
,
825 const gchar
*cue_str
)
827 ENameSelectorEntryPrivate
*priv
;
828 EBookQuery
*book_query
;
830 gchar
*encoded_cue_str
;
831 gchar
*full_name_query_str
;
832 gchar
*file_as_query_str
;
833 gchar
*user_fields_str
;
835 priv
= E_NAME_SELECTOR_ENTRY_GET_PRIVATE (name_selector_entry
);
837 if (!name_selector_entry
->priv
->contact_store
)
841 /* Clear the store */
842 e_contact_store_set_query (name_selector_entry
->priv
->contact_store
, NULL
);
846 encoded_cue_str
= escape_sexp_string (cue_str
);
847 full_name_query_str
= name_style_query ("full_name", cue_str
);
848 file_as_query_str
= name_style_query ("file_as", cue_str
);
849 user_fields_str
= ens_util_populate_user_query_fields (priv
->user_query_fields
, cue_str
, encoded_cue_str
);
851 query_str
= g_strdup_printf (
853 " (beginswith \"nickname\" %s) "
854 " (beginswith \"email\" %s) "
855 " (contains \"nickname\" %s) "
856 " (contains \"email\" %s) "
861 encoded_cue_str
, encoded_cue_str
,
862 encoded_cue_str
, encoded_cue_str
,
863 full_name_query_str
, file_as_query_str
,
864 user_fields_str
? user_fields_str
: "");
866 g_free (user_fields_str
);
867 g_free (file_as_query_str
);
868 g_free (full_name_query_str
);
869 g_free (encoded_cue_str
);
871 ENS_DEBUG (g_print ("%s\n", query_str
));
873 book_query
= e_book_query_from_string (query_str
);
874 e_contact_store_set_query (name_selector_entry
->priv
->contact_store
, book_query
);
875 e_book_query_unref (book_query
);
881 get_entry_substring (ENameSelectorEntry
*name_selector_entry
,
885 const gchar
*entry_text
;
888 entry_text
= gtk_entry_get_text (GTK_ENTRY (name_selector_entry
));
890 p0
= g_utf8_offset_to_pointer (entry_text
, range_start
);
891 p1
= g_utf8_offset_to_pointer (entry_text
, range_end
);
893 return g_strndup (p0
, p1
- p0
);
897 utf8_casefold_collate_len (const gchar
*str1
,
901 gchar
*s1
= g_utf8_casefold (str1
, len
);
902 gchar
*s2
= g_utf8_casefold (str2
, len
);
905 rv
= g_utf8_collate (s1
, s2
);
914 build_textrep_for_contact (EContact
*contact
,
915 EContactField cue_field
,
924 case E_CONTACT_FULL_NAME
:
925 case E_CONTACT_NICKNAME
:
926 case E_CONTACT_FILE_AS
:
927 name
= e_contact_get (contact
, cue_field
);
928 email
= e_contact_get (contact
, E_CONTACT_EMAIL_1
);
931 case E_CONTACT_EMAIL
:
933 l
= e_contact_get (contact
, cue_field
);
934 email
= strdup (g_list_nth_data (l
, email_num
));
935 g_list_free_full (l
, g_free
);
939 g_return_val_if_reached (NULL
);
943 g_return_val_if_fail (email
, NULL
);
944 g_return_val_if_fail (strlen (email
) > 0, NULL
);
947 textrep
= g_strdup_printf ("%s <%s>", name
, email
);
949 textrep
= g_strdup_printf ("%s", email
);
957 contact_match_cue (ENameSelectorEntry
*name_selector_entry
,
959 const gchar
*cue_str
,
960 EContactField
*matched_field
,
961 gint
*matched_field_rank
,
962 gint
*matched_email_num
)
964 EContactField fields
[] = { E_CONTACT_FULL_NAME
, E_CONTACT_NICKNAME
, E_CONTACT_FILE_AS
,
967 gboolean result
= FALSE
;
971 g_return_val_if_fail (contact
, FALSE
);
972 g_return_val_if_fail (cue_str
, FALSE
);
974 if (g_utf8_strlen (cue_str
, -1) < name_selector_entry
->priv
->minimum_query_length
)
977 cue_len
= strlen (cue_str
);
979 /* Make sure contact has an email address */
980 email
= e_contact_get (contact
, E_CONTACT_EMAIL_1
);
981 if (!email
|| !*email
) {
987 for (i
= 0; i
< G_N_ELEMENTS (fields
) && result
== FALSE
; i
++) {
991 GList
*emails
= NULL
, *ll
= NULL
;
993 /* Don't match e-mail addresses in contact lists */
994 if (e_contact_get (contact
, E_CONTACT_IS_LIST
) &&
995 fields
[i
] == E_CONTACT_EMAIL
)
998 if (fields
[i
] == E_CONTACT_EMAIL
) {
999 emails
= e_contact_get (contact
, fields
[i
]);
1001 value
= e_contact_get (contact
, fields
[i
]);
1004 emails
= g_list_append (emails
, value
);
1007 for (ll
= emails
, email_num
= 0; ll
; ll
= ll
->next
, email_num
++) {
1009 value_sane
= sanitize_string (value
);
1011 ENS_DEBUG (g_print ("Comparing '%s' to '%s'\n", value
, cue_str
));
1013 if (!utf8_casefold_collate_len (value_sane
, cue_str
, cue_len
)) {
1015 *matched_field
= fields
[i
];
1016 if (matched_field_rank
)
1017 *matched_field_rank
= i
;
1018 if (matched_email_num
)
1019 *matched_email_num
= email_num
;
1022 g_free (value_sane
);
1025 g_free (value_sane
);
1027 g_list_free_full (emails
, g_free
);
1034 find_existing_completion (ENameSelectorEntry
*name_selector_entry
,
1035 const gchar
*cue_str
,
1038 EContactField
*matched_field
,
1039 gint
*matched_email_num
,
1040 EBookClient
**book_client
)
1043 EContact
*best_contact
= NULL
;
1044 gint best_field_rank
= G_MAXINT
;
1045 EContactField best_field
= 0;
1046 gint best_email_num
= -1;
1047 EBookClient
*best_book_client
= NULL
;
1049 g_return_val_if_fail (cue_str
, FALSE
);
1051 if (!name_selector_entry
->priv
->contact_store
)
1054 ENS_DEBUG (g_print ("Completing '%s'\n", cue_str
));
1056 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (name_selector_entry
->priv
->contact_store
), &iter
))
1060 EContact
*current_contact
;
1061 gint current_field_rank
= best_field_rank
;
1062 gint current_email_num
= best_email_num
;
1063 EContactField current_field
= best_field
;
1066 current_contact
= e_contact_store_get_contact (name_selector_entry
->priv
->contact_store
, &iter
);
1067 if (!current_contact
)
1070 matches
= contact_match_cue (name_selector_entry
, current_contact
, cue_str
, ¤t_field
, ¤t_field_rank
, ¤t_email_num
);
1071 if (matches
&& current_field_rank
< best_field_rank
) {
1072 best_contact
= current_contact
;
1073 best_field_rank
= current_field_rank
;
1074 best_field
= current_field
;
1075 best_book_client
= e_contact_store_get_client (name_selector_entry
->priv
->contact_store
, &iter
);
1076 best_email_num
= current_email_num
;
1079 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (name_selector_entry
->priv
->contact_store
), &iter
));
1085 *contact
= best_contact
;
1087 *text
= build_textrep_for_contact (best_contact
, best_field
, best_email_num
);
1089 *matched_field
= best_field
;
1091 *book_client
= best_book_client
;
1092 if (matched_email_num
)
1093 *matched_email_num
= best_email_num
;
1098 generate_attribute_list (ENameSelectorEntry
*name_selector_entry
)
1100 PangoLayout
*layout
;
1101 PangoAttrList
*attr_list
;
1105 text
= gtk_entry_get_text (GTK_ENTRY (name_selector_entry
));
1106 layout
= gtk_entry_get_layout (GTK_ENTRY (name_selector_entry
));
1108 /* Set up the attribute list */
1110 attr_list
= pango_attr_list_new ();
1112 if (name_selector_entry
->priv
->attr_list
)
1113 pango_attr_list_unref (name_selector_entry
->priv
->attr_list
);
1115 name_selector_entry
->priv
->attr_list
= attr_list
;
1117 /* Parse the entry's text and apply attributes to real contacts */
1119 for (i
= 0; ; i
++) {
1120 EDestination
*destination
;
1121 PangoAttribute
*attr
;
1125 if (!get_range_by_index (text
, i
, &start_pos
, &end_pos
))
1128 destination
= find_destination_at_position (name_selector_entry
, start_pos
);
1130 /* Destination will be NULL if we have no entries */
1131 if (!destination
|| !e_destination_get_contact (destination
))
1134 attr
= pango_attr_underline_new (PANGO_UNDERLINE_SINGLE
);
1135 attr
->start_index
= g_utf8_offset_to_pointer (text
, start_pos
) - text
;
1136 attr
->end_index
= g_utf8_offset_to_pointer (text
, end_pos
) - text
;
1137 pango_attr_list_insert (attr_list
, attr
);
1140 pango_layout_set_attributes (layout
, attr_list
);
1144 draw_event (ENameSelectorEntry
*name_selector_entry
)
1146 PangoLayout
*layout
;
1148 layout
= gtk_entry_get_layout (GTK_ENTRY (name_selector_entry
));
1149 pango_layout_set_attributes (layout
, name_selector_entry
->priv
->attr_list
);
1155 type_ahead_complete (ENameSelectorEntry
*name_selector_entry
)
1158 EBookClient
*book_client
= NULL
;
1159 EContactField matched_field
= E_CONTACT_FIELD_LAST
;
1160 EDestination
*destination
;
1161 gint matched_email_num
= -1;
1163 gint range_start
= 0;
1172 ENameSelectorEntryPrivate
*priv
;
1174 priv
= E_NAME_SELECTOR_ENTRY_GET_PRIVATE (name_selector_entry
);
1176 cursor_pos
= gtk_editable_get_position (GTK_EDITABLE (name_selector_entry
));
1180 text
= gtk_entry_get_text (GTK_ENTRY (name_selector_entry
));
1181 get_range_at_position (text
, cursor_pos
, &range_start
, &range_end
);
1182 range_len
= range_end
- range_start
;
1183 if (range_len
< priv
->minimum_query_length
)
1186 destination
= find_destination_at_position (name_selector_entry
, cursor_pos
);
1188 cue_str
= get_entry_substring (name_selector_entry
, range_start
, range_end
);
1189 if (!find_existing_completion (name_selector_entry
, cue_str
, &contact
,
1190 &textrep
, &matched_field
, &matched_email_num
, &book_client
)) {
1195 temp_str
= sanitize_string (textrep
);
1199 textrep_len
= g_utf8_strlen (textrep
, -1);
1202 g_signal_handlers_block_by_func (
1203 name_selector_entry
,
1204 user_insert_text
, name_selector_entry
);
1205 g_signal_handlers_block_by_func (
1206 name_selector_entry
,
1207 user_delete_text
, name_selector_entry
);
1208 g_signal_handlers_block_by_func (
1209 name_selector_entry
->priv
->destination_store
,
1210 destination_row_changed
, name_selector_entry
);
1212 if (textrep_len
> range_len
) {
1215 /* keep character's case as user types */
1216 for (i
= 0; textrep
[i
] && cue_str
[i
]; i
++)
1217 textrep
[i
] = cue_str
[i
];
1219 gtk_editable_delete_text (
1220 GTK_EDITABLE (name_selector_entry
),
1221 range_start
, range_end
);
1222 gtk_editable_insert_text (
1223 GTK_EDITABLE (name_selector_entry
),
1225 gtk_editable_select_region (
1226 GTK_EDITABLE (name_selector_entry
),
1227 range_end
, range_start
+ textrep_len
);
1228 priv
->is_completing
= TRUE
;
1232 if (contact
&& destination
) {
1235 if (matched_field
== E_CONTACT_EMAIL
)
1236 email_n
= matched_email_num
;
1237 e_destination_set_contact (destination
, contact
, email_n
);
1239 e_destination_set_client (destination
, book_client
);
1240 generate_attribute_list (name_selector_entry
);
1243 g_signal_handlers_unblock_by_func (
1244 name_selector_entry
->priv
->destination_store
,
1245 destination_row_changed
, name_selector_entry
);
1246 g_signal_handlers_unblock_by_func (name_selector_entry
, user_delete_text
, name_selector_entry
);
1247 g_signal_handlers_unblock_by_func (name_selector_entry
, user_insert_text
, name_selector_entry
);
1253 clear_completion_model (ENameSelectorEntry
*name_selector_entry
)
1255 ENameSelectorEntryPrivate
*priv
;
1257 priv
= E_NAME_SELECTOR_ENTRY_GET_PRIVATE (name_selector_entry
);
1259 if (!name_selector_entry
->priv
->contact_store
)
1262 e_contact_store_set_query (name_selector_entry
->priv
->contact_store
, NULL
);
1263 g_hash_table_remove_all (name_selector_entry
->priv
->known_contacts
);
1264 priv
->is_completing
= FALSE
;
1268 update_completion_model (ENameSelectorEntry
*name_selector_entry
)
1272 gint range_start
= 0;
1275 text
= gtk_entry_get_text (GTK_ENTRY (name_selector_entry
));
1276 cursor_pos
= gtk_editable_get_position (GTK_EDITABLE (name_selector_entry
));
1278 if (cursor_pos
>= 0)
1279 get_range_at_position (text
, cursor_pos
, &range_start
, &range_end
);
1281 if (range_end
- range_start
>= name_selector_entry
->priv
->minimum_query_length
&& cursor_pos
== range_end
) {
1284 cue_str
= get_entry_substring (name_selector_entry
, range_start
, range_end
);
1285 set_completion_query (name_selector_entry
, cue_str
);
1288 g_hash_table_remove_all (name_selector_entry
->priv
->known_contacts
);
1290 /* N/A; Clear completion model */
1291 clear_completion_model (name_selector_entry
);
1296 type_ahead_complete_on_timeout_cb (gpointer user_data
)
1298 ENameSelectorEntry
*name_selector_entry
;
1300 name_selector_entry
= E_NAME_SELECTOR_ENTRY (user_data
);
1301 type_ahead_complete (name_selector_entry
);
1302 name_selector_entry
->priv
->type_ahead_complete_cb_id
= 0;
1308 update_completions_on_timeout_cb (gpointer user_data
)
1310 ENameSelectorEntry
*name_selector_entry
;
1312 name_selector_entry
= E_NAME_SELECTOR_ENTRY (user_data
);
1313 update_completion_model (name_selector_entry
);
1314 name_selector_entry
->priv
->update_completions_cb_id
= 0;
1320 insert_destination_at_position (ENameSelectorEntry
*name_selector_entry
,
1323 EDestination
*destination
;
1327 text
= gtk_entry_get_text (GTK_ENTRY (name_selector_entry
));
1328 index
= get_index_at_position (text
, pos
);
1330 destination
= build_destination_at_position (text
, pos
);
1331 g_return_if_fail (destination
);
1333 g_signal_handlers_block_by_func (
1334 name_selector_entry
->priv
->destination_store
,
1335 destination_row_inserted
, name_selector_entry
);
1336 e_destination_store_insert_destination (
1337 name_selector_entry
->priv
->destination_store
,
1338 index
, destination
);
1339 g_signal_handlers_unblock_by_func (
1340 name_selector_entry
->priv
->destination_store
,
1341 destination_row_inserted
, name_selector_entry
);
1342 g_object_unref (destination
);
1346 modify_destination_at_position (ENameSelectorEntry
*name_selector_entry
,
1349 EDestination
*destination
;
1352 gboolean rebuild_attributes
= FALSE
;
1354 destination
= find_destination_at_position (name_selector_entry
, pos
);
1358 text
= gtk_entry_get_text (GTK_ENTRY (name_selector_entry
));
1359 raw_address
= get_address_at_position (text
, pos
);
1360 g_return_if_fail (raw_address
);
1362 if (e_destination_get_contact (destination
))
1363 rebuild_attributes
= TRUE
;
1365 g_signal_handlers_block_by_func (
1366 name_selector_entry
->priv
->destination_store
,
1367 destination_row_changed
, name_selector_entry
);
1368 e_destination_set_raw (destination
, raw_address
);
1369 g_signal_handlers_unblock_by_func (
1370 name_selector_entry
->priv
->destination_store
,
1371 destination_row_changed
, name_selector_entry
);
1373 g_free (raw_address
);
1375 if (rebuild_attributes
)
1376 generate_attribute_list (name_selector_entry
);
1380 get_destination_textrep (ENameSelectorEntry
*name_selector_entry
,
1381 EDestination
*destination
)
1383 gboolean show_email
= e_name_selector_entry_get_show_address (name_selector_entry
);
1386 g_return_val_if_fail (destination
!= NULL
, NULL
);
1388 contact
= e_destination_get_contact (destination
);
1391 if (contact
&& !e_contact_get (contact
, E_CONTACT_IS_LIST
)) {
1394 email_list
= e_contact_get (contact
, E_CONTACT_EMAIL
);
1395 show_email
= g_list_length (email_list
) > 1;
1396 deep_free_list (email_list
);
1400 /* do not show emails for contact lists even user forces it */
1401 if (show_email
&& contact
&& e_contact_get (contact
, E_CONTACT_IS_LIST
))
1404 return sanitize_string (e_destination_get_textrep (destination
, show_email
));
1408 sync_destination_at_position (ENameSelectorEntry
*name_selector_entry
,
1412 EDestination
*destination
;
1416 gint range_start
, range_end
;
1418 /* Get the destination we're looking at. Note that the entry may be empty, and so
1419 * there may not be one. */
1420 destination
= find_destination_at_position (name_selector_entry
, range_pos
);
1424 text
= gtk_entry_get_text (GTK_ENTRY (name_selector_entry
));
1425 if (!get_range_at_position (text
, range_pos
, &range_start
, &range_end
)) {
1426 g_warning ("ENameSelectorEntry is out of sync with model!");
1430 address
= get_destination_textrep (name_selector_entry
, destination
);
1431 address_len
= g_utf8_strlen (address
, -1);
1434 /* Update cursor placement */
1435 if (*cursor_pos
>= range_end
)
1436 *cursor_pos
+= address_len
- (range_end
- range_start
);
1437 else if (*cursor_pos
> range_start
)
1438 *cursor_pos
= range_start
+ address_len
;
1441 g_signal_handlers_block_by_func (name_selector_entry
, user_insert_text
, name_selector_entry
);
1442 g_signal_handlers_block_by_func (name_selector_entry
, user_delete_text
, name_selector_entry
);
1444 gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry
), range_start
, range_end
);
1445 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry
), address
, -1, &range_start
);
1447 g_signal_handlers_unblock_by_func (name_selector_entry
, user_delete_text
, name_selector_entry
);
1448 g_signal_handlers_unblock_by_func (name_selector_entry
, user_insert_text
, name_selector_entry
);
1450 generate_attribute_list (name_selector_entry
);
1455 remove_destination_by_index (ENameSelectorEntry
*name_selector_entry
,
1458 EDestination
*destination
;
1460 destination
= find_destination_by_index (name_selector_entry
, index
);
1462 g_signal_handlers_block_by_func (
1463 name_selector_entry
->priv
->destination_store
,
1464 destination_row_deleted
, name_selector_entry
);
1465 e_destination_store_remove_destination (
1466 name_selector_entry
->priv
->destination_store
,
1468 g_signal_handlers_unblock_by_func (
1469 name_selector_entry
->priv
->destination_store
,
1470 destination_row_deleted
, name_selector_entry
);
1475 post_insert_update (ENameSelectorEntry
*name_selector_entry
,
1481 text
= gtk_entry_get_text (GTK_ENTRY (name_selector_entry
));
1482 length
= g_utf8_strlen (text
, -1);
1483 text
= g_utf8_next_char (text
);
1485 if (*text
== '\0') {
1486 /* First and only character, create initial destination. */
1487 insert_destination_at_position (name_selector_entry
, 0);
1489 /* Modified an existing destination. */
1490 modify_destination_at_position (name_selector_entry
, position
);
1493 /* If editing within the string, regenerate attributes. */
1494 if (position
< length
)
1495 generate_attribute_list (name_selector_entry
);
1498 /* Returns the number of characters inserted */
1500 insert_unichar (ENameSelectorEntry
*name_selector_entry
,
1505 gunichar str_context
[4];
1509 text
= gtk_entry_get_text (GTK_ENTRY (name_selector_entry
));
1510 get_utf8_string_context (text
, *pos
, str_context
, 4);
1512 /* Space is not allowed:
1513 * - Before or after another space.
1514 * - At start of string. */
1516 if (c
== ' ' && (str_context
[1] == ' ' || str_context
[1] == '\0' || str_context
[2] == ' '))
1519 /* Comma is not allowed:
1520 * - After another comma.
1521 * - At start of string. */
1523 if (c
== ',' && !is_quoted_at (text
, *pos
)) {
1526 gboolean at_start
= FALSE
;
1527 gboolean at_end
= FALSE
;
1529 if (str_context
[1] == ',' || str_context
[1] == '\0')
1532 /* We do this so we can avoid disturbing destinations with completed contacts
1533 * either before or after the destination being inserted. */
1534 get_range_at_position (text
, *pos
, &start_pos
, &end_pos
);
1535 if (*pos
<= start_pos
)
1537 if (*pos
>= end_pos
)
1540 /* Must insert comma first, so modify_destination_at_position can do its job
1541 * correctly, splitting up the contact if necessary. */
1542 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry
), ", ", -1, pos
);
1545 g_return_val_if_fail (*pos
>= 2, 0);
1547 /* If we inserted the comma at the end of, or in the middle of, an existing
1548 * address, add a new destination for what appears after comma. Else, we
1549 * have to add a destination for what appears before comma (a blank one). */
1551 /* End: Add last, sync first */
1552 insert_destination_at_position (name_selector_entry
, *pos
);
1553 sync_destination_at_position (name_selector_entry
, *pos
- 2, pos
);
1554 /* Sync generates the attributes list */
1555 } else if (at_start
) {
1556 /* Start: Add first */
1557 insert_destination_at_position (name_selector_entry
, *pos
- 2);
1558 generate_attribute_list (name_selector_entry
);
1561 insert_destination_at_position (name_selector_entry
, *pos
);
1562 modify_destination_at_position (name_selector_entry
, *pos
- 2);
1563 generate_attribute_list (name_selector_entry
);
1569 /* Generic case. Allowed spaces also end up here. */
1571 len
= g_unichar_to_utf8 (c
, buf
);
1574 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry
), buf
, -1, pos
);
1576 post_insert_update (name_selector_entry
, *pos
);
1582 user_insert_text (ENameSelectorEntry
*name_selector_entry
,
1584 gint new_text_length
,
1588 gint chars_inserted
= 0;
1589 gboolean fast_insert
;
1591 g_signal_handlers_block_by_func (name_selector_entry
, user_insert_text
, name_selector_entry
);
1592 g_signal_handlers_block_by_func (name_selector_entry
, user_delete_text
, name_selector_entry
);
1595 (g_utf8_strchr (new_text
, new_text_length
, ' ') == NULL
) &&
1596 (g_utf8_strchr (new_text
, new_text_length
, ',') == NULL
);
1598 /* If the text to insert does not contain spaces or commas,
1599 * insert all of it at once. This avoids confusing on-going
1600 * input method behavior. */
1602 gint old_position
= *position
;
1604 gtk_editable_insert_text (
1605 GTK_EDITABLE (name_selector_entry
),
1606 new_text
, new_text_length
, position
);
1608 chars_inserted
= *position
- old_position
;
1609 if (chars_inserted
> 0)
1610 post_insert_update (name_selector_entry
, *position
);
1612 /* Otherwise, apply some rules as to where spaces and commas
1613 * can be inserted, and insert a trailing space after comma. */
1617 for (cp
= new_text
; *cp
; cp
= g_utf8_next_char (cp
)) {
1618 gunichar uc
= g_utf8_get_char (cp
);
1619 insert_unichar (name_selector_entry
, position
, uc
);
1624 if (chars_inserted
>= 1) {
1625 /* If the user inserted one character, kick off completion */
1627 name_selector_entry
->priv
->update_completions_cb_id
,
1628 update_completions_on_timeout_cb
, name_selector_entry
,
1629 AUTOCOMPLETE_TIMEOUT
);
1631 name_selector_entry
->priv
->type_ahead_complete_cb_id
,
1632 type_ahead_complete_on_timeout_cb
, name_selector_entry
,
1633 AUTOCOMPLETE_TIMEOUT
);
1636 g_signal_handlers_unblock_by_func (name_selector_entry
, user_delete_text
, name_selector_entry
);
1637 g_signal_handlers_unblock_by_func (name_selector_entry
, user_insert_text
, name_selector_entry
);
1639 g_signal_stop_emission_by_name (name_selector_entry
, "insert_text");
1643 user_delete_text (ENameSelectorEntry
*name_selector_entry
,
1649 gint index_start
, index_end
;
1650 gint selection_start
, selection_end
;
1651 gunichar str_context
[2], str_b_context
[2];
1654 gboolean del_space
= FALSE
, del_comma
= FALSE
;
1656 if (start_pos
== end_pos
)
1659 text
= gtk_entry_get_text (GTK_ENTRY (name_selector_entry
));
1660 len
= g_utf8_strlen (text
, -1);
1665 gtk_editable_get_selection_bounds (
1666 GTK_EDITABLE (name_selector_entry
),
1667 &selection_start
, &selection_end
);
1669 get_utf8_string_context (text
, start_pos
, str_context
, 2);
1670 get_utf8_string_context (text
, end_pos
, str_b_context
, 2);
1672 g_signal_handlers_block_by_func (name_selector_entry
, user_delete_text
, name_selector_entry
);
1674 if (end_pos
- start_pos
== 1) {
1675 /* Might be backspace; update completion model so dropdown is accurate */
1677 name_selector_entry
->priv
->update_completions_cb_id
,
1678 update_completions_on_timeout_cb
, name_selector_entry
,
1679 AUTOCOMPLETE_TIMEOUT
);
1682 index_start
= get_index_at_position (text
, start_pos
);
1683 index_end
= get_index_at_position (text
, end_pos
);
1685 g_signal_stop_emission_by_name (name_selector_entry
, "delete_text");
1687 /* If the deletion touches more than one destination, the first one is changed
1688 * and the rest are removed. If the last destination wasn't completely deleted,
1689 * it becomes part of the first one, since the separator between them was
1692 * Here, we let the model know about removals. */
1693 for (i
= index_end
; i
> index_start
; i
--) {
1694 EDestination
*destination
= find_destination_by_index (name_selector_entry
, i
);
1695 gint range_start
, range_end
;
1697 const gchar
*email
= NULL
;
1698 gboolean sel
= FALSE
;
1701 email
= e_destination_get_textrep (destination
, TRUE
);
1703 if (!email
|| !*email
)
1706 if (!get_range_by_index (text
, i
, &range_start
, &range_end
)) {
1707 g_warning ("ENameSelectorEntry is out of sync with model!");
1711 if ((selection_start
< range_start
&& selection_end
> range_start
) ||
1712 (selection_end
> range_start
&& selection_end
< range_end
))
1716 g_signal_handlers_block_by_func (name_selector_entry
, user_insert_text
, name_selector_entry
);
1717 g_signal_handlers_block_by_func (name_selector_entry
, user_delete_text
, name_selector_entry
);
1719 gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry
), range_start
, range_end
);
1721 ttext
= sanitize_string (email
);
1722 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry
), ttext
, -1, &range_start
);
1725 g_signal_handlers_unblock_by_func (name_selector_entry
, user_delete_text
, name_selector_entry
);
1726 g_signal_handlers_unblock_by_func (name_selector_entry
, user_insert_text
, name_selector_entry
);
1730 remove_destination_by_index (name_selector_entry
, i
);
1733 /* Do the actual deletion */
1735 if (end_pos
== start_pos
+1 && index_end
== index_start
) {
1736 /* We could be just deleting the empty text */
1739 /* Get the actual deleted text */
1740 c
= gtk_editable_get_chars (GTK_EDITABLE (name_selector_entry
), start_pos
, start_pos
+ 1);
1743 /* If we are at the beginning or removing junk space, let us ignore it */
1747 } else if (end_pos
== start_pos
+1 && index_end
== index_start
+ 1) {
1748 /* We could be just deleting the empty text */
1751 /* Get the actual deleted text */
1752 c
= gtk_editable_get_chars (GTK_EDITABLE (name_selector_entry
), start_pos
, start_pos
+ 1);
1754 if ( c
[0] == ',' && !is_quoted_at (text
, start_pos
)) {
1755 /* If we are at the beginning or removing junk space, let us ignore it */
1762 gint range_start
=-1, range_end
;
1763 EDestination
*dest
= find_destination_by_index (name_selector_entry
, index_end
);
1764 /* If we have deleted the last comma, let us autocomplete normally
1767 if (dest
&& len
- end_pos
!= 0) {
1769 EDestination
*destination1
= find_destination_by_index (name_selector_entry
, index_start
);
1771 const gchar
*email
= NULL
;
1774 email
= e_destination_get_textrep (destination1
, TRUE
);
1776 if (email
&& *email
) {
1778 if (!get_range_by_index (text
, i
, &range_start
, &range_end
)) {
1779 g_warning ("ENameSelectorEntry is out of sync with model!");
1783 g_signal_handlers_block_by_func (name_selector_entry
, user_insert_text
, name_selector_entry
);
1784 g_signal_handlers_block_by_func (name_selector_entry
, user_delete_text
, name_selector_entry
);
1786 gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry
), range_start
, range_end
);
1788 ttext
= sanitize_string (email
);
1789 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry
), ttext
, -1, &range_start
);
1792 g_signal_handlers_unblock_by_func (name_selector_entry
, user_delete_text
, name_selector_entry
);
1793 g_signal_handlers_unblock_by_func (name_selector_entry
, user_insert_text
, name_selector_entry
);
1796 if (range_start
!= -1) {
1797 start_pos
= range_start
;
1798 end_pos
= start_pos
+ 1;
1799 gtk_editable_set_position (GTK_EDITABLE (name_selector_entry
),start_pos
);
1803 gtk_editable_delete_text (
1804 GTK_EDITABLE (name_selector_entry
),
1805 start_pos
, end_pos
);
1807 /*If the user is deleting a '"' new destinations have to be created for ',' between the quoted text
1808 Like "fd,ty,uy" is a one entity, but if you remove the quotes it has to be broken doan into 3 seperate
1812 if (str_b_context
[1] == '"') {
1816 for (p
= text
+ (end_pos
- 1), j
= end_pos
- 1; *p
&& *p
!= '"' ; p
= g_utf8_next_char (p
), j
++) {
1817 gunichar c
= g_utf8_get_char (p
);
1819 insert_destination_at_position (name_selector_entry
, j
+ 1);
1825 /* Let model know about changes */
1826 text
= gtk_entry_get_text (GTK_ENTRY (name_selector_entry
));
1827 if (!*text
|| strlen (text
) <= 0) {
1828 /* If the entry was completely cleared, remove the initial destination too */
1829 remove_destination_by_index (name_selector_entry
, 0);
1830 generate_attribute_list (name_selector_entry
);
1831 } else if (!del_space
) {
1832 modify_destination_at_position (name_selector_entry
, start_pos
);
1835 /* If editing within the string, we need to regenerate attributes */
1837 generate_attribute_list (name_selector_entry
);
1839 /* Prevent type-ahead completion */
1840 if (name_selector_entry
->priv
->type_ahead_complete_cb_id
) {
1841 g_source_remove (name_selector_entry
->priv
->type_ahead_complete_cb_id
);
1842 name_selector_entry
->priv
->type_ahead_complete_cb_id
= 0;
1845 g_signal_handlers_unblock_by_func (name_selector_entry
, user_delete_text
, name_selector_entry
);
1849 completion_match_selected (ENameSelectorEntry
*name_selector_entry
,
1850 ETreeModelGenerator
*email_generator_model
,
1851 GtkTreeIter
*generator_iter
)
1854 EBookClient
*book_client
;
1855 EDestination
*destination
;
1857 GtkTreeIter contact_iter
;
1860 if (!name_selector_entry
->priv
->contact_store
)
1863 g_return_val_if_fail (name_selector_entry
->priv
->email_generator
== email_generator_model
, FALSE
);
1865 e_tree_model_generator_convert_iter_to_child_iter (
1866 email_generator_model
,
1867 &contact_iter
, &email_n
,
1870 contact
= e_contact_store_get_contact (name_selector_entry
->priv
->contact_store
, &contact_iter
);
1871 book_client
= e_contact_store_get_client (name_selector_entry
->priv
->contact_store
, &contact_iter
);
1872 cursor_pos
= gtk_editable_get_position (GTK_EDITABLE (name_selector_entry
));
1874 /* Set the contact in the model's destination */
1876 destination
= find_destination_at_position (name_selector_entry
, cursor_pos
);
1877 e_destination_set_contact (destination
, contact
, email_n
);
1879 e_destination_set_client (destination
, book_client
);
1880 sync_destination_at_position (name_selector_entry
, cursor_pos
, &cursor_pos
);
1882 g_signal_handlers_block_by_func (name_selector_entry
, user_insert_text
, name_selector_entry
);
1883 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry
), ", ", -1, &cursor_pos
);
1884 g_signal_handlers_unblock_by_func (name_selector_entry
, user_insert_text
, name_selector_entry
);
1886 /*Add destination at end for next entry*/
1887 insert_destination_at_position (name_selector_entry
, cursor_pos
);
1888 /* Place cursor at end of address */
1890 gtk_editable_set_position (GTK_EDITABLE (name_selector_entry
), cursor_pos
);
1891 g_signal_emit (name_selector_entry
, signals
[UPDATED
], 0, destination
, NULL
);
1896 entry_activate (ENameSelectorEntry
*name_selector_entry
)
1899 gint range_start
, range_end
;
1900 ENameSelectorEntryPrivate
*priv
;
1901 EDestination
*destination
;
1906 cursor_pos
= gtk_editable_get_position (GTK_EDITABLE (name_selector_entry
));
1910 priv
= E_NAME_SELECTOR_ENTRY_GET_PRIVATE (name_selector_entry
);
1912 text
= gtk_entry_get_text (GTK_ENTRY (name_selector_entry
));
1913 if (!get_range_at_position (text
, cursor_pos
, &range_start
, &range_end
))
1916 range_len
= range_end
- range_start
;
1917 if (range_len
< priv
->minimum_query_length
)
1920 destination
= find_destination_at_position (name_selector_entry
, cursor_pos
);
1924 cue_str
= get_entry_substring (name_selector_entry
, range_start
, range_end
);
1926 if (!find_existing_completion (name_selector_entry
, cue_str
, &contact
,
1927 &textrep
, &matched_field
)) {
1933 sync_destination_at_position (name_selector_entry
, cursor_pos
, &cursor_pos
);
1935 /* Place cursor at end of address */
1936 text
= gtk_entry_get_text (GTK_ENTRY (name_selector_entry
));
1937 get_range_at_position (text
, cursor_pos
, &range_start
, &range_end
);
1939 if (priv
->is_completing
) {
1940 gchar
*str_context
= NULL
;
1942 str_context
= gtk_editable_get_chars (GTK_EDITABLE (name_selector_entry
), range_end
, range_end
+ 1);
1944 if (str_context
[0] != ',') {
1946 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry
), ", ", -1, &range_end
);
1949 gint newpos
= strlen (text
);
1951 /* Doing this we can make sure that It wont ask for completion again. */
1952 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry
), ", ", -1, &newpos
);
1953 g_signal_handlers_block_by_func (name_selector_entry
, user_delete_text
, name_selector_entry
);
1954 gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry
), newpos
- 2, newpos
);
1955 g_signal_handlers_unblock_by_func (name_selector_entry
, user_delete_text
, name_selector_entry
);
1957 /* Move it close to next destination*/
1958 range_end
= range_end
+ 2;
1961 g_free (str_context
);
1964 gtk_editable_set_position (GTK_EDITABLE (name_selector_entry
), range_end
);
1965 g_signal_emit (name_selector_entry
, signals
[UPDATED
], 0, destination
, NULL
);
1967 if (priv
->is_completing
)
1968 clear_completion_model (name_selector_entry
);
1972 update_text (ENameSelectorEntry
*name_selector_entry
,
1975 gint start
= 0, end
= 0;
1976 gboolean has_selection
;
1978 has_selection
= gtk_editable_get_selection_bounds (GTK_EDITABLE (name_selector_entry
), &start
, &end
);
1980 gtk_entry_set_text (GTK_ENTRY (name_selector_entry
), text
);
1983 gtk_editable_select_region (GTK_EDITABLE (name_selector_entry
), start
, end
);
1987 sanitize_entry (ENameSelectorEntry
*name_selector_entry
)
1990 GList
*l
, *known
, *del
= NULL
;
1991 GString
*str
= g_string_new ("");
1993 g_signal_handlers_block_matched (name_selector_entry
, G_SIGNAL_MATCH_DATA
, 0, 0, NULL
, NULL
, name_selector_entry
);
1994 g_signal_handlers_block_matched (name_selector_entry
->priv
->destination_store
, G_SIGNAL_MATCH_DATA
, 0, 0, NULL
, NULL
, name_selector_entry
);
1996 known
= e_destination_store_list_destinations (name_selector_entry
->priv
->destination_store
);
1997 for (l
= known
, n
= 0; l
!= NULL
; l
= l
->next
, n
++) {
1998 EDestination
*dest
= l
->data
;
2000 if (!dest
|| !e_destination_get_address (dest
))
2001 del
= g_list_prepend (del
, GINT_TO_POINTER (n
));
2005 text
= get_destination_textrep (name_selector_entry
, dest
);
2007 if (str
->str
&& str
->str
[0])
2008 g_string_append (str
, ", ");
2010 g_string_append (str
, text
);
2015 g_list_free (known
);
2017 for (l
= del
; l
!= NULL
; l
= l
->next
) {
2018 e_destination_store_remove_destination_nth (name_selector_entry
->priv
->destination_store
, GPOINTER_TO_INT (l
->data
));
2022 update_text (name_selector_entry
, str
->str
);
2024 g_string_free (str
, TRUE
);
2026 g_signal_handlers_unblock_matched (name_selector_entry
->priv
->destination_store
, G_SIGNAL_MATCH_DATA
, 0, 0, NULL
, NULL
, name_selector_entry
);
2027 g_signal_handlers_unblock_matched (name_selector_entry
, G_SIGNAL_MATCH_DATA
, 0, 0, NULL
, NULL
, name_selector_entry
);
2029 generate_attribute_list (name_selector_entry
);
2033 user_focus_in (ENameSelectorEntry
*name_selector_entry
,
2034 GdkEventFocus
*event_focus
)
2038 GString
*str
= g_string_new ("");
2039 EDestination
*dest_dummy
= e_destination_new ();
2041 g_signal_handlers_block_matched (name_selector_entry
, G_SIGNAL_MATCH_DATA
, 0, 0, NULL
, NULL
, name_selector_entry
);
2042 g_signal_handlers_block_matched (name_selector_entry
->priv
->destination_store
, G_SIGNAL_MATCH_DATA
, 0, 0, NULL
, NULL
, name_selector_entry
);
2044 known
= e_destination_store_list_destinations (name_selector_entry
->priv
->destination_store
);
2045 for (l
= known
, n
= 0; l
!= NULL
; l
= l
->next
, n
++) {
2046 EDestination
*dest
= l
->data
;
2051 text
= get_destination_textrep (name_selector_entry
, dest
);
2053 if (str
->str
&& str
->str
[0])
2054 g_string_append (str
, ", ");
2056 g_string_append (str
, text
);
2061 g_list_free (known
);
2063 /* Add a blank destination */
2064 e_destination_store_append_destination (name_selector_entry
->priv
->destination_store
, dest_dummy
);
2065 if (str
->str
&& str
->str
[0])
2066 g_string_append (str
, ", ");
2068 gtk_entry_set_text (GTK_ENTRY (name_selector_entry
), str
->str
);
2070 g_string_free (str
, TRUE
);
2072 g_signal_handlers_unblock_matched (name_selector_entry
->priv
->destination_store
, G_SIGNAL_MATCH_DATA
, 0, 0, NULL
, NULL
, name_selector_entry
);
2073 g_signal_handlers_unblock_matched (name_selector_entry
, G_SIGNAL_MATCH_DATA
, 0, 0, NULL
, NULL
, name_selector_entry
);
2075 generate_attribute_list (name_selector_entry
);
2081 user_focus_out (ENameSelectorEntry
*name_selector_entry
,
2082 GdkEventFocus
*event_focus
)
2084 if (!event_focus
->in
) {
2085 entry_activate (name_selector_entry
);
2088 if (name_selector_entry
->priv
->type_ahead_complete_cb_id
) {
2089 g_source_remove (name_selector_entry
->priv
->type_ahead_complete_cb_id
);
2090 name_selector_entry
->priv
->type_ahead_complete_cb_id
= 0;
2093 if (name_selector_entry
->priv
->update_completions_cb_id
) {
2094 g_source_remove (name_selector_entry
->priv
->update_completions_cb_id
);
2095 name_selector_entry
->priv
->update_completions_cb_id
= 0;
2098 clear_completion_model (name_selector_entry
);
2100 if (!event_focus
->in
) {
2101 sanitize_entry (name_selector_entry
);
2108 user_key_press_event_cb (ENameSelectorEntry
*name_selector_entry
,
2109 GdkEventKey
*event_key
)
2113 g_return_val_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry
), FALSE
);
2114 g_return_val_if_fail (event_key
!= NULL
, FALSE
);
2116 if ((event_key
->state
& (GDK_SHIFT_MASK
| GDK_CONTROL_MASK
| GDK_MOD1_MASK
)) == 0 &&
2117 event_key
->keyval
== GDK_KEY_comma
&&
2118 gtk_editable_get_selection_bounds (GTK_EDITABLE (name_selector_entry
), NULL
, &end
)) {
2119 entry_activate (name_selector_entry
);
2121 if (name_selector_entry
->priv
->type_ahead_complete_cb_id
) {
2122 g_source_remove (name_selector_entry
->priv
->type_ahead_complete_cb_id
);
2123 name_selector_entry
->priv
->type_ahead_complete_cb_id
= 0;
2126 if (name_selector_entry
->priv
->update_completions_cb_id
) {
2127 g_source_remove (name_selector_entry
->priv
->update_completions_cb_id
);
2128 name_selector_entry
->priv
->update_completions_cb_id
= 0;
2131 clear_completion_model (name_selector_entry
);
2133 sanitize_entry (name_selector_entry
);
2135 gtk_editable_select_region (GTK_EDITABLE (name_selector_entry
), end
, end
);
2142 deep_free_list (GList
*list
)
2146 for (l
= list
; l
; l
= g_list_next (l
))
2152 /* Given a widget, determines the height that text will normally be drawn. */
2154 entry_height (GtkWidget
*widget
)
2156 PangoLayout
*layout
;
2159 g_return_val_if_fail (widget
!= NULL
, 0);
2161 layout
= gtk_widget_create_pango_layout (widget
, NULL
);
2163 pango_layout_get_pixel_size (layout
, NULL
, &bound
);
2169 contact_layout_pixbuffer (GtkCellLayout
*cell_layout
,
2170 GtkCellRenderer
*cell
,
2171 GtkTreeModel
*model
,
2173 ENameSelectorEntry
*name_selector_entry
)
2176 GtkTreeIter generator_iter
;
2177 GtkTreeIter contact_store_iter
;
2179 EContactPhoto
*photo
;
2180 gboolean iter_is_valid
;
2181 GdkPixbuf
*pixbuf
= NULL
;
2183 if (!name_selector_entry
->priv
->contact_store
)
2186 gtk_tree_model_filter_convert_iter_to_child_iter (
2187 GTK_TREE_MODEL_FILTER (model
),
2188 &generator_iter
, iter
);
2189 iter_is_valid
= e_tree_model_generator_convert_iter_to_child_iter (
2190 name_selector_entry
->priv
->email_generator
,
2191 &contact_store_iter
, &email_n
,
2194 if (!iter_is_valid
) {
2198 contact
= e_contact_store_get_contact (name_selector_entry
->priv
->contact_store
, &contact_store_iter
);
2200 g_object_set (cell
, "pixbuf", pixbuf
, NULL
);
2204 photo
= e_contact_get (contact
, E_CONTACT_PHOTO
);
2205 if (photo
&& photo
->type
== E_CONTACT_PHOTO_TYPE_INLINED
) {
2206 guint max_height
= entry_height (GTK_WIDGET (name_selector_entry
));
2207 GdkPixbufLoader
*loader
;
2209 loader
= gdk_pixbuf_loader_new ();
2210 if (gdk_pixbuf_loader_write (loader
, (guchar
*) photo
->data
.inlined
.data
, photo
->data
.inlined
.length
, NULL
) &&
2211 gdk_pixbuf_loader_close (loader
, NULL
)) {
2212 pixbuf
= gdk_pixbuf_loader_get_pixbuf (loader
);
2214 g_object_ref (pixbuf
);
2216 g_object_unref (loader
);
2220 gdouble scale
= 1.0;
2222 w
= gdk_pixbuf_get_width (pixbuf
);
2223 h
= gdk_pixbuf_get_height (pixbuf
);
2226 scale
= max_height
/ (double) h
;
2228 scale
= max_height
/ (double) w
;
2233 tmp
= gdk_pixbuf_scale_simple (pixbuf
, w
* scale
, h
* scale
, GDK_INTERP_BILINEAR
);
2234 g_object_unref (pixbuf
);
2241 e_contact_photo_free (photo
);
2243 g_object_set (cell
, "pixbuf", pixbuf
, NULL
);
2246 g_object_unref (pixbuf
);
2250 contact_layout_formatter (GtkCellLayout
*cell_layout
,
2251 GtkCellRenderer
*cell
,
2252 GtkTreeModel
*model
,
2254 ENameSelectorEntry
*name_selector_entry
)
2257 GtkTreeIter generator_iter
;
2258 GtkTreeIter contact_store_iter
;
2264 gboolean iter_is_valid
;
2266 if (!name_selector_entry
->priv
->contact_store
)
2269 gtk_tree_model_filter_convert_iter_to_child_iter (
2270 GTK_TREE_MODEL_FILTER (model
),
2271 &generator_iter
, iter
);
2272 iter_is_valid
= e_tree_model_generator_convert_iter_to_child_iter (
2273 name_selector_entry
->priv
->email_generator
,
2274 &contact_store_iter
, &email_n
,
2277 if (!iter_is_valid
) {
2281 contact
= e_contact_store_get_contact (name_selector_entry
->priv
->contact_store
, &contact_store_iter
);
2282 email_list
= e_contact_get (contact
, E_CONTACT_EMAIL
);
2283 email_str
= g_list_nth_data (email_list
, email_n
);
2284 file_as_str
= e_contact_get (contact
, E_CONTACT_FILE_AS
);
2286 if (e_contact_get (contact
, E_CONTACT_IS_LIST
)) {
2287 string
= g_strdup_printf ("%s", file_as_str
? file_as_str
: "?");
2289 string
= g_strdup_printf (
2290 "%s%s<%s>", file_as_str
? file_as_str
: "",
2291 file_as_str
? " " : "",
2292 email_str
? email_str
: "");
2295 g_free (file_as_str
);
2296 deep_free_list (email_list
);
2298 g_object_set (cell
, "text", string
, NULL
);
2303 generate_contact_rows (EContactStore
*contact_store
,
2305 ENameSelectorEntry
*name_selector_entry
)
2308 const gchar
*contact_uid
;
2312 contact
= e_contact_store_get_contact (contact_store
, iter
);
2313 g_return_val_if_fail (contact
!= NULL
, 0);
2315 contact_uid
= e_contact_get_const (contact
, E_CONTACT_UID
);
2317 return 0; /* Can happen with broken databases */
2319 if (is_duplicate_contact_and_remember (name_selector_entry
, contact
))
2322 if (e_contact_get (contact
, E_CONTACT_IS_LIST
))
2325 email_list
= e_contact_get (contact
, E_CONTACT_EMAIL
);
2326 n_rows
= g_list_length (email_list
);
2327 deep_free_list (email_list
);
2333 ensure_type_ahead_complete_on_timeout (ENameSelectorEntry
*name_selector_entry
)
2335 /* this is called whenever a new item is added to the model,
2336 * thus, to not starve when there are many matches, do not
2337 * postpone on each add, but show results as soon as possible */
2338 if (!name_selector_entry
->priv
->type_ahead_complete_cb_id
) {
2340 name_selector_entry
->priv
->type_ahead_complete_cb_id
,
2341 type_ahead_complete_on_timeout_cb
, name_selector_entry
,
2342 SHOW_RESULT_TIMEOUT
);
2347 setup_contact_store (ENameSelectorEntry
*name_selector_entry
)
2349 if (name_selector_entry
->priv
->email_generator
) {
2350 g_object_unref (name_selector_entry
->priv
->email_generator
);
2351 name_selector_entry
->priv
->email_generator
= NULL
;
2354 if (name_selector_entry
->priv
->contact_store
) {
2355 name_selector_entry
->priv
->email_generator
=
2356 e_tree_model_generator_new (
2358 name_selector_entry
->priv
->contact_store
));
2360 e_tree_model_generator_set_generate_func (
2361 name_selector_entry
->priv
->email_generator
,
2362 (ETreeModelGeneratorGenerateFunc
) generate_contact_rows
,
2363 name_selector_entry
, NULL
);
2365 /* Assign the store to the entry completion */
2367 gtk_entry_completion_set_model (
2368 name_selector_entry
->priv
->entry_completion
,
2370 name_selector_entry
->priv
->email_generator
));
2372 /* Set up callback for incoming matches */
2373 g_signal_connect_swapped (
2374 name_selector_entry
->priv
->contact_store
, "row-inserted",
2375 G_CALLBACK (ensure_type_ahead_complete_on_timeout
), name_selector_entry
);
2377 /* Remove the store from the entry completion */
2379 gtk_entry_completion_set_model (name_selector_entry
->priv
->entry_completion
, NULL
);
2384 name_selector_entry_get_client_cb (GObject
*source_object
,
2385 GAsyncResult
*result
,
2388 EContactStore
*contact_store
= user_data
;
2389 EBookClient
*book_client
;
2391 GError
*error
= NULL
;
2393 client
= e_client_cache_get_client_finish (
2394 E_CLIENT_CACHE (source_object
), result
, &error
);
2398 ((client
!= NULL
) && (error
== NULL
)) ||
2399 ((client
== NULL
) && (error
!= NULL
)));
2401 if (g_error_matches (error
, G_IO_ERROR
, G_IO_ERROR_CANCELLED
)) {
2402 g_error_free (error
);
2406 if (error
!= NULL
) {
2407 g_warning ("%s: %s", G_STRFUNC
, error
->message
);
2408 g_error_free (error
);
2412 book_client
= E_BOOK_CLIENT (client
);
2414 g_return_if_fail (E_IS_BOOK_CLIENT (book_client
));
2415 e_contact_store_add_client (contact_store
, book_client
);
2416 g_object_unref (book_client
);
2419 g_object_unref (contact_store
);
2423 setup_default_contact_store (ENameSelectorEntry
*name_selector_entry
)
2425 EClientCache
*client_cache
;
2426 ESourceRegistry
*registry
;
2427 EContactStore
*contact_store
;
2429 const gchar
*extension_name
;
2431 g_return_if_fail (name_selector_entry
->priv
->contact_store
== NULL
);
2433 /* Create a book for each completion source, and assign them to the contact store */
2435 contact_store
= e_contact_store_new ();
2436 name_selector_entry
->priv
->contact_store
= contact_store
;
2438 extension_name
= E_SOURCE_EXTENSION_ADDRESS_BOOK
;
2439 client_cache
= e_name_selector_entry_ref_client_cache (name_selector_entry
);
2440 registry
= e_client_cache_ref_registry (client_cache
);
2442 list
= e_source_registry_list_enabled (registry
, extension_name
);
2444 for (iter
= list
; iter
!= NULL
; iter
= g_list_next (iter
)) {
2445 ESource
*source
= E_SOURCE (iter
->data
);
2446 ESourceAutocomplete
*extension
;
2447 GCancellable
*cancellable
;
2448 const gchar
*extension_name
;
2450 extension_name
= E_SOURCE_EXTENSION_AUTOCOMPLETE
;
2451 extension
= e_source_get_extension (source
, extension_name
);
2453 /* Skip non-completion address books. */
2454 if (!e_source_autocomplete_get_include_me (extension
))
2457 cancellable
= g_cancellable_new ();
2460 &name_selector_entry
->priv
->cancellables
,
2463 e_client_cache_get_client (
2464 client_cache
, source
,
2465 E_SOURCE_EXTENSION_ADDRESS_BOOK
, (guint32
) -1,
2467 name_selector_entry_get_client_cb
,
2468 g_object_ref (contact_store
));
2471 g_list_free_full (list
, (GDestroyNotify
) g_object_unref
);
2473 g_object_unref (registry
);
2474 g_object_unref (client_cache
);
2476 setup_contact_store (name_selector_entry
);
2480 destination_row_changed (ENameSelectorEntry
*name_selector_entry
,
2484 EDestination
*destination
;
2485 const gchar
*entry_text
;
2487 gint range_start
, range_end
;
2490 n
= gtk_tree_path_get_indices (path
)[0];
2491 destination
= e_destination_store_get_destination (name_selector_entry
->priv
->destination_store
, iter
);
2496 g_return_if_fail (n
>= 0);
2498 entry_text
= gtk_entry_get_text (GTK_ENTRY (name_selector_entry
));
2499 if (!get_range_by_index (entry_text
, n
, &range_start
, &range_end
)) {
2500 g_warning ("ENameSelectorEntry is out of sync with model!");
2504 g_signal_handlers_block_by_func (name_selector_entry
, user_insert_text
, name_selector_entry
);
2505 g_signal_handlers_block_by_func (name_selector_entry
, user_delete_text
, name_selector_entry
);
2507 gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry
), range_start
, range_end
);
2509 text
= get_destination_textrep (name_selector_entry
, destination
);
2510 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry
), text
, -1, &range_start
);
2513 g_signal_handlers_unblock_by_func (name_selector_entry
, user_delete_text
, name_selector_entry
);
2514 g_signal_handlers_unblock_by_func (name_selector_entry
, user_insert_text
, name_selector_entry
);
2516 clear_completion_model (name_selector_entry
);
2517 generate_attribute_list (name_selector_entry
);
2521 destination_row_inserted (ENameSelectorEntry
*name_selector_entry
,
2525 EDestination
*destination
;
2526 const gchar
*entry_text
;
2528 gboolean comma_before
= FALSE
;
2529 gboolean comma_after
= FALSE
;
2530 gint range_start
, range_end
;
2534 n
= gtk_tree_path_get_indices (path
)[0];
2535 destination
= e_destination_store_get_destination (name_selector_entry
->priv
->destination_store
, iter
);
2537 g_return_if_fail (n
>= 0);
2538 g_return_if_fail (destination
!= NULL
);
2540 entry_text
= gtk_entry_get_text (GTK_ENTRY (name_selector_entry
));
2542 if (get_range_by_index (entry_text
, n
, &range_start
, &range_end
) && range_start
!= range_end
) {
2543 /* Another destination comes after us */
2544 insert_pos
= range_start
;
2546 } else if (n
> 0 && get_range_by_index (entry_text
, n
- 1, &range_start
, &range_end
)) {
2547 /* Another destination comes before us */
2548 insert_pos
= range_end
;
2549 comma_before
= TRUE
;
2550 } else if (n
== 0) {
2551 /* We're the sole destination */
2554 g_warning ("ENameSelectorEntry is out of sync with model!");
2558 g_signal_handlers_block_by_func (name_selector_entry
, user_insert_text
, name_selector_entry
);
2561 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry
), ", ", -1, &insert_pos
);
2563 text
= get_destination_textrep (name_selector_entry
, destination
);
2564 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry
), text
, -1, &insert_pos
);
2568 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry
), ", ", -1, &insert_pos
);
2570 g_signal_handlers_unblock_by_func (name_selector_entry
, user_insert_text
, name_selector_entry
);
2572 clear_completion_model (name_selector_entry
);
2573 generate_attribute_list (name_selector_entry
);
2577 destination_row_deleted (ENameSelectorEntry
*name_selector_entry
,
2581 gboolean deleted_comma
= FALSE
;
2582 gint range_start
, range_end
;
2586 n
= gtk_tree_path_get_indices (path
)[0];
2587 g_return_if_fail (n
>= 0);
2589 text
= gtk_entry_get_text (GTK_ENTRY (name_selector_entry
));
2591 if (!get_range_by_index (text
, n
, &range_start
, &range_end
)) {
2592 g_warning ("ENameSelectorEntry is out of sync with model!");
2596 /* Expand range for deletion forwards */
2597 for (p0
= g_utf8_offset_to_pointer (text
, range_end
); *p0
;
2598 p0
= g_utf8_next_char (p0
), range_end
++) {
2599 gunichar c
= g_utf8_get_char (p0
);
2601 /* Gobble spaces directly after comma */
2602 if (c
!= ' ' && deleted_comma
) {
2608 deleted_comma
= TRUE
;
2613 /* Expand range for deletion backwards */
2614 for (p0
= g_utf8_offset_to_pointer (text
, range_start
); range_start
> 0;
2615 p0
= g_utf8_prev_char (p0
), range_start
--) {
2616 gunichar c
= g_utf8_get_char (p0
);
2619 if (!deleted_comma
) {
2620 deleted_comma
= TRUE
;
2626 /* Leave a space in front; we deleted the comma and spaces before the
2627 * following destination */
2628 p0
= g_utf8_next_char (p0
);
2629 c
= g_utf8_get_char (p0
);
2637 g_signal_handlers_block_by_func (name_selector_entry
, user_delete_text
, name_selector_entry
);
2638 gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry
), range_start
, range_end
);
2639 g_signal_handlers_unblock_by_func (name_selector_entry
, user_delete_text
, name_selector_entry
);
2641 clear_completion_model (name_selector_entry
);
2642 generate_attribute_list (name_selector_entry
);
2646 setup_destination_store (ENameSelectorEntry
*name_selector_entry
)
2650 g_signal_connect_swapped (
2651 name_selector_entry
->priv
->destination_store
, "row-changed",
2652 G_CALLBACK (destination_row_changed
), name_selector_entry
);
2653 g_signal_connect_swapped (
2654 name_selector_entry
->priv
->destination_store
, "row-deleted",
2655 G_CALLBACK (destination_row_deleted
), name_selector_entry
);
2656 g_signal_connect_swapped (
2657 name_selector_entry
->priv
->destination_store
, "row-inserted",
2658 G_CALLBACK (destination_row_inserted
), name_selector_entry
);
2660 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (name_selector_entry
->priv
->destination_store
), &iter
))
2666 path
= gtk_tree_model_get_path (GTK_TREE_MODEL (name_selector_entry
->priv
->destination_store
), &iter
);
2667 g_return_if_fail (path
);
2669 destination_row_inserted (name_selector_entry
, path
, &iter
);
2670 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (name_selector_entry
->priv
->destination_store
), &iter
));
2674 prepare_popup_destination (ENameSelectorEntry
*name_selector_entry
,
2675 GdkEventButton
*event_button
)
2677 EDestination
*destination
;
2678 PangoLayout
*layout
;
2679 gint layout_offset_x
;
2680 gint layout_offset_y
;
2684 if (event_button
->type
!= GDK_BUTTON_PRESS
)
2687 if (event_button
->button
!= 3)
2690 if (name_selector_entry
->priv
->popup_destination
) {
2691 g_object_unref (name_selector_entry
->priv
->popup_destination
);
2692 name_selector_entry
->priv
->popup_destination
= NULL
;
2695 gtk_entry_get_layout_offsets (
2696 GTK_ENTRY (name_selector_entry
),
2697 &layout_offset_x
, &layout_offset_y
);
2698 x
= (event_button
->x
+ 0.5) - layout_offset_x
;
2699 y
= (event_button
->y
+ 0.5) - layout_offset_y
;
2704 layout
= gtk_entry_get_layout (GTK_ENTRY (name_selector_entry
));
2705 if (!pango_layout_xy_to_index (layout
, x
* PANGO_SCALE
, y
* PANGO_SCALE
, &index
, NULL
))
2708 index
= gtk_entry_layout_index_to_text_index (GTK_ENTRY (name_selector_entry
), index
);
2709 destination
= find_destination_at_position (name_selector_entry
, index
);
2710 /* FIXME: Add this to a private variable, in ENameSelectorEntry Class*/
2711 g_object_set_data ((GObject
*) name_selector_entry
, "index", GINT_TO_POINTER (index
));
2713 if (!destination
|| !e_destination_get_contact (destination
))
2716 /* TODO: Unref destination when we finalize */
2717 name_selector_entry
->priv
->popup_destination
= g_object_ref (destination
);
2721 static EBookClient
*
2722 find_client_by_contact (GSList
*clients
,
2723 const gchar
*contact_uid
,
2724 const gchar
*source_uid
)
2728 if (source_uid
&& *source_uid
) {
2729 /* this is much quicket than asking each client for an existence */
2730 for (l
= clients
; l
; l
= g_slist_next (l
)) {
2731 EBookClient
*client
= l
->data
;
2732 ESource
*source
= e_client_get_source (E_CLIENT (client
));
2737 if (g_strcmp0 (source_uid
, e_source_get_uid (source
)) == 0)
2742 for (l
= clients
; l
; l
= g_slist_next (l
)) {
2743 EBookClient
*client
= l
->data
;
2744 EContact
*contact
= NULL
;
2747 result
= e_book_client_get_contact_sync (client
, contact_uid
, &contact
, NULL
, NULL
);
2749 g_object_unref (contact
);
2759 editor_closed_cb (GtkWidget
*editor
,
2764 EDestination
*destination
;
2766 EBookClient
*book_client
;
2768 ENameSelectorEntry
*name_selector_entry
= E_NAME_SELECTOR_ENTRY (data
);
2770 destination
= name_selector_entry
->priv
->popup_destination
;
2771 contact
= e_destination_get_contact (destination
);
2773 g_object_unref (name_selector_entry
);
2777 contact_uid
= e_contact_get (contact
, E_CONTACT_UID
);
2779 g_object_unref (contact
);
2780 g_object_unref (name_selector_entry
);
2784 if (name_selector_entry
->priv
->contact_store
) {
2785 clients
= e_contact_store_get_clients (name_selector_entry
->priv
->contact_store
);
2786 book_client
= find_client_by_contact (clients
, contact_uid
, e_destination_get_source_uid (destination
));
2787 g_slist_free (clients
);
2795 g_warn_if_fail (e_book_client_get_contact_sync (book_client
, contact_uid
, &contact
, NULL
, NULL
));
2796 email_num
= e_destination_get_email_num (destination
);
2797 e_destination_set_contact (destination
, contact
, email_num
);
2798 e_destination_set_client (destination
, book_client
);
2803 g_free (contact_uid
);
2805 g_object_unref (contact
);
2806 g_object_unref (name_selector_entry
);
2809 /* To parse something like...
2810 * =?UTF-8?Q?=E0=A4=95=E0=A4=95=E0=A4=AC=E0=A5=82=E0=A5=8B=E0=A5=87?=\t\n=?UTF-8?Q?=E0=A4=B0?=\t\n<aa@aa.ccom>
2811 * and return the decoded representation of name & email parts.
2814 eab_parse_qp_email (const gchar
*string
,
2818 struct _camel_header_address
*address
;
2819 gboolean res
= FALSE
;
2821 address
= camel_header_address_decode (string
, "UTF-8");
2826 /* report success only when we have filled both name and email address */
2827 if (address
->type
== CAMEL_HEADER_ADDRESS_NAME
&& address
->name
&& *address
->name
&& address
->v
.addr
&& *address
->v
.addr
) {
2828 *name
= g_strdup (address
->name
);
2829 *email
= g_strdup (address
->v
.addr
);
2833 camel_header_address_unref (address
);
2839 popup_activate_inline_expand (ENameSelectorEntry
*name_selector_entry
,
2840 GtkWidget
*menu_item
)
2843 GString
*sanitized_text
= g_string_new ("");
2844 EDestination
*destination
= name_selector_entry
->priv
->popup_destination
;
2845 gint position
, start
, end
;
2848 position
= GPOINTER_TO_INT (g_object_get_data ((GObject
*) name_selector_entry
, "index"));
2850 for (dests
= e_destination_list_get_dests (destination
); dests
; dests
= dests
->next
) {
2851 const EDestination
*dest
= dests
->data
;
2853 gchar
*name
= NULL
, *email
= NULL
, *tofree
= NULL
;
2858 text
= e_destination_get_textrep (dest
, TRUE
);
2860 if (!text
|| !*text
)
2863 if (eab_parse_qp_email (text
, &name
, &email
)) {
2864 tofree
= g_strdup_printf ("%s <%s>", name
, email
);
2870 sanitized
= sanitize_string (text
);
2876 if (*sanitized_text
->str
)
2877 g_string_append (sanitized_text
, ", ");
2879 g_string_append (sanitized_text
, sanitized
);
2885 text
= gtk_entry_get_text (GTK_ENTRY (name_selector_entry
));
2886 get_range_at_position (text
, position
, &start
, &end
);
2887 gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry
), start
, end
);
2888 gtk_editable_insert_text (GTK_EDITABLE (name_selector_entry
), sanitized_text
->str
, -1, &start
);
2889 g_string_free (sanitized_text
, TRUE
);
2891 clear_completion_model (name_selector_entry
);
2892 generate_attribute_list (name_selector_entry
);
2896 popup_activate_contact (ENameSelectorEntry
*name_selector_entry
,
2897 GtkWidget
*menu_item
)
2899 EBookClient
*book_client
;
2901 EDestination
*destination
;
2905 destination
= name_selector_entry
->priv
->popup_destination
;
2909 contact
= e_destination_get_contact (destination
);
2913 contact_uid
= e_contact_get (contact
, E_CONTACT_UID
);
2917 if (name_selector_entry
->priv
->contact_store
) {
2918 clients
= e_contact_store_get_clients (name_selector_entry
->priv
->contact_store
);
2919 book_client
= find_client_by_contact (clients
, contact_uid
, e_destination_get_source_uid (destination
));
2920 g_slist_free (clients
);
2921 g_free (contact_uid
);
2929 if (e_destination_is_evolution_list (destination
)) {
2930 GtkWidget
*contact_list_editor
;
2932 if (!name_selector_entry
->priv
->contact_list_editor_func
)
2935 contact_list_editor
= (*name_selector_entry
->priv
->contact_list_editor_func
) (book_client
, contact
, FALSE
, TRUE
);
2936 g_object_ref (name_selector_entry
);
2938 contact_list_editor
, "editor_closed",
2939 G_CALLBACK (editor_closed_cb
), name_selector_entry
);
2941 GtkWidget
*contact_editor
;
2943 if (!name_selector_entry
->priv
->contact_editor_func
)
2946 contact_editor
= (*name_selector_entry
->priv
->contact_editor_func
) (book_client
, contact
, FALSE
, TRUE
);
2947 g_object_ref (name_selector_entry
);
2949 contact_editor
, "editor_closed",
2950 G_CALLBACK (editor_closed_cb
), name_selector_entry
);
2955 popup_activate_email (ENameSelectorEntry
*name_selector_entry
,
2956 GtkWidget
*menu_item
)
2958 EDestination
*destination
;
2962 destination
= name_selector_entry
->priv
->popup_destination
;
2966 contact
= e_destination_get_contact (destination
);
2970 email_num
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu_item
), "order"));
2971 e_destination_set_contact (destination
, contact
, email_num
);
2975 popup_activate_list (EDestination
*destination
,
2978 gboolean status
= gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item
));
2980 e_destination_set_ignored (destination
, !status
);
2984 popup_activate_cut (ENameSelectorEntry
*name_selector_entry
,
2985 GtkWidget
*menu_item
)
2987 EDestination
*destination
;
2988 const gchar
*contact_email
;
2989 gchar
*pemail
= NULL
;
2990 GtkClipboard
*clipboard
;
2992 destination
= name_selector_entry
->priv
->popup_destination
;
2993 contact_email
=e_destination_get_textrep (destination
, TRUE
);
2995 g_signal_handlers_block_by_func (name_selector_entry
, user_insert_text
, name_selector_entry
);
2996 g_signal_handlers_block_by_func (name_selector_entry
, user_delete_text
, name_selector_entry
);
2998 clipboard
= gtk_clipboard_get (GDK_SELECTION_PRIMARY
);
2999 pemail
= g_strconcat (contact_email
, ",", NULL
);
3000 gtk_clipboard_set_text (clipboard
, pemail
, strlen (pemail
));
3002 clipboard
= gtk_clipboard_get (GDK_SELECTION_CLIPBOARD
);
3003 gtk_clipboard_set_text (clipboard
, pemail
, strlen (pemail
));
3005 gtk_editable_delete_text (GTK_EDITABLE (name_selector_entry
), 0, 0);
3006 e_destination_store_remove_destination (name_selector_entry
->priv
->destination_store
, destination
);
3009 g_signal_handlers_unblock_by_func (name_selector_entry
, user_delete_text
, name_selector_entry
);
3010 g_signal_handlers_unblock_by_func (name_selector_entry
, user_insert_text
, name_selector_entry
);
3014 popup_activate_copy (ENameSelectorEntry
*name_selector_entry
,
3015 GtkWidget
*menu_item
)
3017 EDestination
*destination
;
3018 const gchar
*contact_email
;
3020 GtkClipboard
*clipboard
;
3022 destination
= name_selector_entry
->priv
->popup_destination
;
3023 contact_email
= e_destination_get_textrep (destination
, TRUE
);
3025 g_signal_handlers_block_by_func (name_selector_entry
, user_insert_text
, name_selector_entry
);
3026 g_signal_handlers_block_by_func (name_selector_entry
, user_delete_text
, name_selector_entry
);
3028 clipboard
= gtk_clipboard_get (GDK_SELECTION_PRIMARY
);
3029 pemail
= g_strconcat (contact_email
, ",", NULL
);
3030 gtk_clipboard_set_text (clipboard
, pemail
, strlen (pemail
));
3032 clipboard
= gtk_clipboard_get (GDK_SELECTION_CLIPBOARD
);
3033 gtk_clipboard_set_text (clipboard
, pemail
, strlen (pemail
));
3035 g_signal_handlers_unblock_by_func (name_selector_entry
, user_delete_text
, name_selector_entry
);
3036 g_signal_handlers_unblock_by_func (name_selector_entry
, user_insert_text
, name_selector_entry
);
3040 destination_set_list (GtkWidget
*item
,
3041 EDestination
*destination
)
3044 gboolean status
= gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item
));
3046 contact
= e_destination_get_contact (destination
);
3050 e_destination_set_ignored (destination
, !status
);
3054 destination_set_email (GtkWidget
*item
,
3055 EDestination
*destination
)
3060 if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item
)))
3062 contact
= e_destination_get_contact (destination
);
3066 email_num
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item
), "order"));
3067 e_destination_set_contact (destination
, contact
, email_num
);
3071 populate_popup (ENameSelectorEntry
*name_selector_entry
,
3074 EDestination
*destination
;
3076 GtkWidget
*menu_item
;
3077 GList
*email_list
= NULL
;
3083 gint email_num
, len
;
3084 GSList
*group
= NULL
;
3086 gboolean show_menu
= FALSE
;
3088 destination
= name_selector_entry
->priv
->popup_destination
;
3092 contact
= e_destination_get_contact (destination
);
3096 /* Prepend the menu items, backwards */
3100 menu_item
= gtk_separator_menu_item_new ();
3101 gtk_widget_show (menu_item
);
3102 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu
), menu_item
);
3103 email_num
= e_destination_get_email_num (destination
);
3106 is_list
= e_contact_get (contact
, E_CONTACT_IS_LIST
) ? TRUE
: FALSE
;
3108 const GList
*dests
= e_destination_list_get_dests (destination
);
3110 gint length
= g_list_length ((GList
*) dests
);
3112 for (iter
= (GList
*) dests
; iter
; iter
= iter
->next
) {
3113 EDestination
*dest
= (EDestination
*) iter
->data
;
3114 const gchar
*email
= e_destination_get_email (dest
);
3116 if (!email
|| *email
== '\0')
3120 menu_item
= gtk_check_menu_item_new_with_label (email
);
3122 menu_item
, "toggled",
3123 G_CALLBACK (destination_set_list
), dest
);
3125 menu_item
= gtk_menu_item_new_with_label (email
);
3128 gtk_widget_show (menu_item
);
3129 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu
), menu_item
);
3133 gtk_check_menu_item_set_active (
3134 GTK_CHECK_MENU_ITEM (menu_item
),
3135 !e_destination_is_ignored (dest
));
3136 g_signal_connect_swapped (
3137 menu_item
, "activate",
3138 G_CALLBACK (popup_activate_list
), dest
);
3143 email_list
= e_contact_get (contact
, E_CONTACT_EMAIL
);
3144 len
= g_list_length (email_list
);
3146 for (l
= email_list
, i
= 0; l
; l
= g_list_next (l
), i
++) {
3147 gchar
*email
= l
->data
;
3149 if (!email
|| *email
== '\0')
3153 menu_item
= gtk_radio_menu_item_new_with_label (group
, email
);
3154 group
= gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (menu_item
));
3155 g_signal_connect (menu_item
, "toggled", G_CALLBACK (destination_set_email
), destination
);
3157 menu_item
= gtk_menu_item_new_with_label (email
);
3160 gtk_widget_show (menu_item
);
3161 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu
), menu_item
);
3163 g_object_set_data (G_OBJECT (menu_item
), "order", GINT_TO_POINTER (i
));
3165 if (i
== email_num
&& len
> 1) {
3166 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item
), TRUE
);
3167 g_signal_connect_swapped (
3168 menu_item
, "activate",
3169 G_CALLBACK (popup_activate_email
),
3170 name_selector_entry
);
3178 menu_item
= gtk_separator_menu_item_new ();
3179 gtk_widget_show (menu_item
);
3180 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu
), menu_item
);
3183 /* Expand a list inline */
3185 /* To Translators: This would be similiar to "Expand MyList Inline" where MyList is a Contact List*/
3186 edit_label
= g_strdup_printf (_("E_xpand %s Inline"), (gchar
*) e_contact_get_const (contact
, E_CONTACT_FILE_AS
));
3187 menu_item
= gtk_menu_item_new_with_mnemonic (edit_label
);
3188 g_free (edit_label
);
3189 gtk_widget_show (menu_item
);
3190 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu
), menu_item
);
3191 g_signal_connect_swapped (
3192 menu_item
, "activate", G_CALLBACK (popup_activate_inline_expand
),
3193 name_selector_entry
);
3196 menu_item
= gtk_separator_menu_item_new ();
3197 gtk_widget_show (menu_item
);
3198 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu
), menu_item
);
3201 /* Copy Contact Item */
3202 copy_label
= g_strdup_printf (_("Cop_y %s"), (gchar
*) e_contact_get_const (contact
, E_CONTACT_FILE_AS
));
3203 menu_item
= gtk_menu_item_new_with_mnemonic (copy_label
);
3204 g_free (copy_label
);
3205 gtk_widget_show (menu_item
);
3206 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu
), menu_item
);
3208 g_signal_connect_swapped (
3209 menu_item
, "activate", G_CALLBACK (popup_activate_copy
),
3210 name_selector_entry
);
3212 /* Cut Contact Item */
3213 cut_label
= g_strdup_printf (_("C_ut %s"), (gchar
*) e_contact_get_const (contact
, E_CONTACT_FILE_AS
));
3214 menu_item
= gtk_menu_item_new_with_mnemonic (cut_label
);
3216 gtk_widget_show (menu_item
);
3217 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu
), menu_item
);
3219 g_signal_connect_swapped (
3220 menu_item
, "activate", G_CALLBACK (popup_activate_cut
),
3221 name_selector_entry
);
3224 menu_item
= gtk_separator_menu_item_new ();
3225 gtk_widget_show (menu_item
);
3226 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu
), menu_item
);
3229 /* Edit Contact item */
3231 edit_label
= g_strdup_printf (_("_Edit %s"), (gchar
*) e_contact_get_const (contact
, E_CONTACT_FILE_AS
));
3232 menu_item
= gtk_menu_item_new_with_mnemonic (edit_label
);
3233 g_free (edit_label
);
3234 gtk_widget_show (menu_item
);
3235 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu
), menu_item
);
3237 g_signal_connect_swapped (
3238 menu_item
, "activate", G_CALLBACK (popup_activate_contact
),
3239 name_selector_entry
);
3241 deep_free_list (email_list
);
3245 compare_gint_ptr_cb (gconstpointer a
,
3248 return GPOINTER_TO_INT (a
) - GPOINTER_TO_INT (b
);
3252 copy_or_cut_clipboard (ENameSelectorEntry
*name_selector_entry
,
3255 GtkClipboard
*clipboard
;
3256 GtkEditable
*editable
;
3257 const gchar
*text
, *cp
;
3259 GHashTableIter iter
;
3260 gpointer key
, value
;
3261 GSList
*sorted
, *siter
;
3263 gint ii
, start
, end
, ostart
, oend
;
3266 editable
= GTK_EDITABLE (name_selector_entry
);
3267 text
= gtk_entry_get_text (GTK_ENTRY (editable
));
3269 if (!gtk_editable_get_selection_bounds (editable
, &start
, &end
))
3272 g_return_if_fail (end
> start
);
3274 hash
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
3276 /* convert from character indexes to pointer indexes */
3277 ostart
= g_utf8_offset_to_pointer (text
, start
) - text
;
3278 oend
= g_utf8_offset_to_pointer (text
, end
) - text
;
3281 cp
= g_utf8_offset_to_pointer (text
, end
);
3282 uc
= g_utf8_get_char (cp
);
3284 /* Exclude trailing whitespace and commas. */
3285 while (ii
>= start
&& (uc
== ',' || g_unichar_isspace (uc
))) {
3286 cp
= g_utf8_prev_char (cp
);
3287 uc
= g_utf8_get_char (cp
);
3291 /* Determine the index of each remaining character. */
3292 while (ii
>= start
) {
3293 gint index
= get_index_at_position (text
, ii
--);
3294 g_hash_table_insert (hash
, GINT_TO_POINTER (index
), NULL
);
3298 g_hash_table_iter_init (&iter
, hash
);
3299 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
3300 sorted
= g_slist_prepend (sorted
, key
);
3303 sorted
= g_slist_sort (sorted
, compare_gint_ptr_cb
);
3304 addresses
= g_string_new ("");
3306 for (siter
= sorted
; siter
!= NULL
; siter
= g_slist_next (siter
)) {
3307 gint index
= GPOINTER_TO_INT (siter
->data
);
3311 if (!get_range_by_index (text
, index
, &rstart
, &rend
))
3314 /* convert from character indexes to pointer indexes */
3315 rstart
= g_utf8_offset_to_pointer (text
, rstart
) - text
;
3316 rend
= g_utf8_offset_to_pointer (text
, rend
) - text
;
3318 if (rstart
< ostart
) {
3319 if (addresses
->str
&& *addresses
->str
)
3320 g_string_append (addresses
, ", ");
3322 g_string_append_len (addresses
, text
+ ostart
, MIN (oend
- ostart
, rend
- ostart
));
3323 } else if (rend
> oend
) {
3324 if (addresses
->str
&& *addresses
->str
)
3325 g_string_append (addresses
, ", ");
3327 g_string_append_len (addresses
, text
+ rstart
, oend
- rstart
);
3329 /* the contact is whole selected */
3330 dest
= find_destination_by_index (name_selector_entry
, index
);
3331 if (dest
&& e_destination_get_textrep (dest
, TRUE
)) {
3332 if (addresses
->str
&& *addresses
->str
)
3333 g_string_append (addresses
, ", ");
3335 g_string_append (addresses
, e_destination_get_textrep (dest
, TRUE
));
3337 g_string_append_len (addresses
, text
+ rstart
, rend
- rstart
);
3341 g_slist_free (sorted
);
3344 gtk_editable_delete_text (editable
, start
, end
);
3346 g_hash_table_unref (hash
);
3348 clipboard
= gtk_widget_get_clipboard (
3349 GTK_WIDGET (name_selector_entry
), GDK_SELECTION_CLIPBOARD
);
3350 gtk_clipboard_set_text (clipboard
, addresses
->str
, -1);
3352 g_string_free (addresses
, TRUE
);
3356 copy_clipboard (GtkEntry
*entry
,
3357 ENameSelectorEntry
*name_selector_entry
)
3359 copy_or_cut_clipboard (name_selector_entry
, FALSE
);
3360 g_signal_stop_emission_by_name (entry
, "copy-clipboard");
3364 cut_clipboard (GtkEntry
*entry
,
3365 ENameSelectorEntry
*name_selector_entry
)
3367 copy_or_cut_clipboard (name_selector_entry
, TRUE
);
3368 g_signal_stop_emission_by_name (entry
, "cut-clipboard");
3372 e_name_selector_entry_init (ENameSelectorEntry
*name_selector_entry
)
3374 GtkCellRenderer
*renderer
;
3376 name_selector_entry
->priv
=
3377 E_NAME_SELECTOR_ENTRY_GET_PRIVATE (name_selector_entry
);
3379 g_queue_init (&name_selector_entry
->priv
->cancellables
);
3381 name_selector_entry
->priv
->minimum_query_length
= 3;
3382 name_selector_entry
->priv
->show_address
= FALSE
;
3383 name_selector_entry
->priv
->known_contacts
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, NULL
);
3388 name_selector_entry
, "insert-text",
3389 G_CALLBACK (user_insert_text
), name_selector_entry
);
3391 name_selector_entry
, "delete-text",
3392 G_CALLBACK (user_delete_text
), name_selector_entry
);
3394 name_selector_entry
, "focus-out-event",
3395 G_CALLBACK (user_focus_out
), name_selector_entry
);
3396 g_signal_connect_after (
3397 name_selector_entry
, "focus-in-event",
3398 G_CALLBACK (user_focus_in
), name_selector_entry
);
3400 name_selector_entry
, "key-press-event",
3401 G_CALLBACK (user_key_press_event_cb
), name_selector_entry
);
3406 name_selector_entry
, "draw",
3407 G_CALLBACK (draw_event
), name_selector_entry
);
3409 /* Activation: Complete current entry if possible */
3412 name_selector_entry
, "activate",
3413 G_CALLBACK (entry_activate
), name_selector_entry
);
3418 name_selector_entry
, "button-press-event",
3419 G_CALLBACK (prepare_popup_destination
), name_selector_entry
);
3421 name_selector_entry
, "populate-popup",
3422 G_CALLBACK (populate_popup
), name_selector_entry
);
3424 /* Clipboard signals */
3426 name_selector_entry
, "copy-clipboard",
3427 G_CALLBACK (copy_clipboard
), name_selector_entry
);
3429 name_selector_entry
, "cut-clipboard",
3430 G_CALLBACK (cut_clipboard
), name_selector_entry
);
3434 name_selector_entry
->priv
->email_generator
= NULL
;
3436 name_selector_entry
->priv
->entry_completion
= gtk_entry_completion_new ();
3437 gtk_entry_completion_set_match_func (
3438 name_selector_entry
->priv
->entry_completion
,
3439 (GtkEntryCompletionMatchFunc
) completion_match_cb
, NULL
, NULL
);
3440 g_signal_connect_swapped (
3441 name_selector_entry
->priv
->entry_completion
, "match-selected",
3442 G_CALLBACK (completion_match_selected
), name_selector_entry
);
3444 gtk_entry_set_completion (
3445 GTK_ENTRY (name_selector_entry
),
3446 name_selector_entry
->priv
->entry_completion
);
3448 renderer
= gtk_cell_renderer_pixbuf_new ();
3449 gtk_cell_layout_pack_start (
3450 GTK_CELL_LAYOUT (name_selector_entry
->priv
->entry_completion
),
3452 gtk_cell_layout_set_cell_data_func (
3453 GTK_CELL_LAYOUT (name_selector_entry
->priv
->entry_completion
),
3454 GTK_CELL_RENDERER (renderer
),
3455 (GtkCellLayoutDataFunc
) contact_layout_pixbuffer
,
3456 name_selector_entry
, NULL
);
3458 /* Completion list name renderer */
3459 renderer
= gtk_cell_renderer_text_new ();
3460 gtk_cell_layout_pack_start (
3461 GTK_CELL_LAYOUT (name_selector_entry
->priv
->entry_completion
),
3463 gtk_cell_layout_set_cell_data_func (
3464 GTK_CELL_LAYOUT (name_selector_entry
->priv
->entry_completion
),
3465 GTK_CELL_RENDERER (renderer
),
3466 (GtkCellLayoutDataFunc
) contact_layout_formatter
,
3467 name_selector_entry
, NULL
);
3469 /* Destination store */
3471 name_selector_entry
->priv
->destination_store
= e_destination_store_new ();
3472 setup_destination_store (name_selector_entry
);
3473 name_selector_entry
->priv
->is_completing
= FALSE
;
3477 * e_name_selector_entry_new:
3478 * @client_cache: an #EClientCache
3480 * Creates a new #ENameSelectorEntry.
3482 * Returns: A new #ENameSelectorEntry.
3485 e_name_selector_entry_new (EClientCache
*client_cache
)
3487 g_return_val_if_fail (E_IS_CLIENT_CACHE (client_cache
), NULL
);
3489 return g_object_new (
3490 E_TYPE_NAME_SELECTOR_ENTRY
,
3491 "client-cache", client_cache
, NULL
);
3495 * e_name_selector_entry_ref_client_cache:
3496 * @name_selector_entry: an #ENameSelectorEntry
3498 * Returns the #EClientCache passed to e_name_selector_entry_new().
3500 * The returned #EClientCache is referenced for thread-safety and must be
3501 * unreferenced with g_object_unref() when finished with it.
3503 * Returns: an #EClientCache
3508 e_name_selector_entry_ref_client_cache (ENameSelectorEntry
*name_selector_entry
)
3510 g_return_val_if_fail (
3511 E_IS_NAME_SELECTOR_ENTRY (name_selector_entry
), NULL
);
3513 if (name_selector_entry
->priv
->client_cache
== NULL
)
3516 return g_object_ref (name_selector_entry
->priv
->client_cache
);
3520 * e_name_selector_entry_set_client_cache:
3521 * @name_selector_entry: an #ENameSelectorEntry
3522 * @client_cache: an #EClientCache
3524 * Sets the #EClientCache used to query address books.
3526 * This function is intended for cases where @name_selector_entry is
3527 * instantiated by a #GtkBuilder and has to be given an #EClientCache
3528 * after it is fully constructed.
3533 e_name_selector_entry_set_client_cache (ENameSelectorEntry
*name_selector_entry
,
3534 EClientCache
*client_cache
)
3536 g_return_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry
));
3538 if (client_cache
== name_selector_entry
->priv
->client_cache
)
3541 if (client_cache
!= NULL
) {
3542 g_return_if_fail (E_IS_CLIENT_CACHE (client_cache
));
3543 g_object_ref (client_cache
);
3546 if (name_selector_entry
->priv
->client_cache
!= NULL
)
3547 g_object_unref (name_selector_entry
->priv
->client_cache
);
3549 name_selector_entry
->priv
->client_cache
= client_cache
;
3551 g_object_notify (G_OBJECT (name_selector_entry
), "client-cache");
3555 * e_name_selector_entry_get_minimum_query_length:
3556 * @name_selector_entry: an #ENameSelectorEntry
3558 * Returns: Minimum length of query before completion starts
3563 e_name_selector_entry_get_minimum_query_length (ENameSelectorEntry
*name_selector_entry
)
3565 g_return_val_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry
), -1);
3567 return name_selector_entry
->priv
->minimum_query_length
;
3571 * e_name_selector_entry_set_minimum_query_length:
3572 * @name_selector_entry: an #ENameSelectorEntry
3573 * @length: minimum query length
3575 * Sets minimum length of query before completion starts.
3580 e_name_selector_entry_set_minimum_query_length (ENameSelectorEntry
*name_selector_entry
,
3583 g_return_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry
));
3584 g_return_if_fail (length
> 0);
3586 if (name_selector_entry
->priv
->minimum_query_length
== length
)
3589 name_selector_entry
->priv
->minimum_query_length
= length
;
3591 g_object_notify (G_OBJECT (name_selector_entry
), "minimum-query-length");
3595 * e_name_selector_entry_get_show_address:
3596 * @name_selector_entry: an #ENameSelectorEntry
3598 * Returns: Whether always show email address for an auto-completed contact.
3603 e_name_selector_entry_get_show_address (ENameSelectorEntry
*name_selector_entry
)
3605 g_return_val_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry
), FALSE
);
3607 return name_selector_entry
->priv
->show_address
;
3611 * e_name_selector_entry_set_show_address:
3612 * @name_selector_entry: an #ENameSelectorEntry
3613 * @show: new value to set
3615 * Sets whether always show email address for an auto-completed contact.
3620 e_name_selector_entry_set_show_address (ENameSelectorEntry
*name_selector_entry
,
3623 g_return_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry
));
3625 if ((name_selector_entry
->priv
->show_address
? 1 : 0) == (show
? 1 : 0))
3628 name_selector_entry
->priv
->show_address
= show
;
3630 sanitize_entry (name_selector_entry
);
3632 g_object_notify (G_OBJECT (name_selector_entry
), "show-address");
3636 * e_name_selector_entry_peek_contact_store:
3637 * @name_selector_entry: an #ENameSelectorEntry
3639 * Gets the #EContactStore being used by @name_selector_entry.
3641 * Returns: An #EContactStore.
3644 e_name_selector_entry_peek_contact_store (ENameSelectorEntry
*name_selector_entry
)
3646 g_return_val_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry
), NULL
);
3648 return name_selector_entry
->priv
->contact_store
;
3652 * e_name_selector_entry_set_contact_store:
3653 * @name_selector_entry: an #ENameSelectorEntry
3654 * @contact_store: an #EContactStore to use
3656 * Sets the #EContactStore being used by @name_selector_entry to @contact_store.
3659 e_name_selector_entry_set_contact_store (ENameSelectorEntry
*name_selector_entry
,
3660 EContactStore
*contact_store
)
3662 g_return_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry
));
3663 g_return_if_fail (contact_store
== NULL
|| E_IS_CONTACT_STORE (contact_store
));
3665 if (contact_store
== name_selector_entry
->priv
->contact_store
)
3668 if (name_selector_entry
->priv
->contact_store
)
3669 g_object_unref (name_selector_entry
->priv
->contact_store
);
3670 name_selector_entry
->priv
->contact_store
= contact_store
;
3671 if (name_selector_entry
->priv
->contact_store
)
3672 g_object_ref (name_selector_entry
->priv
->contact_store
);
3674 setup_contact_store (name_selector_entry
);
3678 * e_name_selector_entry_peek_destination_store:
3679 * @name_selector_entry: an #ENameSelectorEntry
3681 * Gets the #EDestinationStore being used to store @name_selector_entry's destinations.
3683 * Returns: An #EDestinationStore.
3686 e_name_selector_entry_peek_destination_store (ENameSelectorEntry
*name_selector_entry
)
3688 g_return_val_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry
), NULL
);
3690 return name_selector_entry
->priv
->destination_store
;
3694 * e_name_selector_entry_set_destination_store:
3695 * @name_selector_entry: an #ENameSelectorEntry
3696 * @destination_store: an #EDestinationStore to use
3698 * Sets @destination_store as the #EDestinationStore to be used to store
3699 * destinations for @name_selector_entry.
3702 e_name_selector_entry_set_destination_store (ENameSelectorEntry
*name_selector_entry
,
3703 EDestinationStore
*destination_store
)
3705 g_return_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry
));
3706 g_return_if_fail (E_IS_DESTINATION_STORE (destination_store
));
3708 if (destination_store
== name_selector_entry
->priv
->destination_store
)
3711 g_object_unref (name_selector_entry
->priv
->destination_store
);
3712 name_selector_entry
->priv
->destination_store
= g_object_ref (destination_store
);
3714 setup_destination_store (name_selector_entry
);
3718 * e_name_selector_entry_get_popup_destination:
3723 e_name_selector_entry_get_popup_destination (ENameSelectorEntry
*name_selector_entry
)
3725 g_return_val_if_fail (E_IS_NAME_SELECTOR_ENTRY (name_selector_entry
), NULL
);
3727 return name_selector_entry
->priv
->popup_destination
;
3731 * e_name_selector_entry_set_contact_editor_func:
3736 e_name_selector_entry_set_contact_editor_func (ENameSelectorEntry
*name_selector_entry
,
3739 name_selector_entry
->priv
->contact_editor_func
= func
;
3743 * e_name_selector_entry_set_contact_list_editor_func:
3748 e_name_selector_entry_set_contact_list_editor_func (ENameSelectorEntry
*name_selector_entry
,
3751 name_selector_entry
->priv
->contact_list_editor_func
= func
;