From 07fbf1a035bfa00d99c6d00dfd04cba7c1c093c3 Mon Sep 17 00:00:00 2001 From: Jon Trowbridge Date: Thu, 9 Aug 2001 18:07:35 +0000 Subject: Hook up some magic to (basically) cardify an entry on focus-out. (What we 2001-08-09 Jon Trowbridge * gui/component/select-names/e-select-names-manager.c (e_select_names_manager_create_entry): Hook up some magic to (basically) cardify an entry on focus-out. (What we do is actually more complicated than that.) * gui/component/select-names/e-select-names-bonobo.c (entry_set_property_fn): After we set an entry's text, try to cardify it. We need to do this so that (for example) reply e-mails get properly cardified. * gui/component/select-names/e-select-names-model.c (e_select_names_model_duplicate): Use e_select_names_model_append, rather than manipulating lists directly. (e_select_names_model_insert): Connect "changed" signal proxy for added EDestination. (e_select_names_model_append): Ditto. (e_select_names_model_replace): Ditto, and disconnect signals for replaced EDestination. (e_select_names_model_delete): Ditto on the disconnection. (e_select_names_model_delete_all): Ditto. (e_select_names_model_cardify): Added. Try to cardify a specified EDestination. (e_select_names_model_cancel_cardify): Added. Cancel the pending cardification of a single EDestination. (e_select_names_model_cardify_all): Added. Cardify all of the EDestinations in the model. (e_select_names_model_cancel_cardify_all): Added. Cancel's any and all pending cardifications. * backend/ebook/e-destination.c (e_destination_class_init): Added "changed" and "cardified" signals. (e_destination_freeze): Added (static). (e_destination_thaw): Added (static). (e_destination_clear_card): Reset allow_cardify and cannot_cardify, cancel any pending cardifications, and emit the "changed" signal. (e_destination_clear_strings): Emit the "changed" signal. (e_destination_clear): Do freeze/thaw to prevent multiple signal emissions. (e_destination_set_card): Check that the card we are setting is not equal to the current card, and emit the "changed" signal if we are actually changing. (e_destination_set_card_uri): Emit "changed" signal, if necessary. (e_destination_set_name): Emit "changed" signal, if necessary. (e_destination_set_email): Emit "changed" signal, if necessary. (e_destination_set_html_mail_pref): Emit "changed" signal, if necessary. (use_card_cb): If we've just loaded/set the ECard, emit the "changed" signal. (e_destination_set_raw): Emit "changed" signal, if necessary. (e_destination_allow_cardification): Added. (e_destination_set_allow_cardification): Added. (e_destination_cardify): Added. Tries to automatically convert a string-based EDestination to one based on an ECard. (e_destination_cardify_delayed): Added. Cardifies in a timeout. (e_destination_cancel_cardify): Added. Cancels any pending cardifications. (e_destination_xml_decode): Added freeze/thaw. * backend/ebook/e-book-util.c (e_book_nickname_query): Added. A canned simple query for nicknames. * backend/ebook/e-card.c (e_card_email_find_number): Added. Given a card and an string containing an email address, return the index number of the address inside of the card, or -1 if the address is not found. svn path=/trunk/; revision=11837 --- addressbook/ChangeLog | 69 ++++ addressbook/backend/ebook/e-book-util.c | 50 +++ addressbook/backend/ebook/e-book-util.h | 5 + addressbook/backend/ebook/e-card.c | 23 ++ addressbook/backend/ebook/e-card.h | 2 + addressbook/backend/ebook/e-destination.c | 373 +++++++++++++++++++-- addressbook/backend/ebook/e-destination.h | 14 + .../component/select-names/e-select-names-bonobo.c | 11 +- .../select-names/e-select-names-manager.c | 45 +++ .../component/select-names/e-select-names-model.c | 113 ++++++- .../component/select-names/e-select-names-model.h | 6 + .../select-names/e-select-names-text-model.c | 2 +- 12 files changed, 681 insertions(+), 32 deletions(-) (limited to 'addressbook') diff --git a/addressbook/ChangeLog b/addressbook/ChangeLog index f2509fd5cd..3a872f2ad1 100644 --- a/addressbook/ChangeLog +++ b/addressbook/ChangeLog @@ -1,3 +1,72 @@ +2001-08-09 Jon Trowbridge + + * gui/component/select-names/e-select-names-manager.c + (e_select_names_manager_create_entry): Hook up some magic to + (basically) cardify an entry on focus-out. (What we do is actually + more complicated than that.) + + * gui/component/select-names/e-select-names-bonobo.c + (entry_set_property_fn): After we set an entry's text, try to + cardify it. We need to do this so that (for example) reply + e-mails get properly cardified. + + * gui/component/select-names/e-select-names-model.c + (e_select_names_model_duplicate): Use e_select_names_model_append, + rather than manipulating lists directly. + (e_select_names_model_insert): Connect "changed" signal proxy for + added EDestination. + (e_select_names_model_append): Ditto. + (e_select_names_model_replace): Ditto, and disconnect signals for + replaced EDestination. + (e_select_names_model_delete): Ditto on the disconnection. + (e_select_names_model_delete_all): Ditto. + (e_select_names_model_cardify): Added. Try to cardify a specified + EDestination. + (e_select_names_model_cancel_cardify): Added. Cancel the pending + cardification of a single EDestination. + (e_select_names_model_cardify_all): Added. Cardify all of the + EDestinations in the model. + (e_select_names_model_cancel_cardify_all): Added. Cancel's any + and all pending cardifications. + + * backend/ebook/e-destination.c (e_destination_class_init): Added + "changed" and "cardified" signals. + (e_destination_freeze): Added (static). + (e_destination_thaw): Added (static). + (e_destination_clear_card): Reset allow_cardify and + cannot_cardify, cancel any pending cardifications, and emit the + "changed" signal. + (e_destination_clear_strings): Emit the "changed" signal. + (e_destination_clear): Do freeze/thaw to prevent multiple signal + emissions. + (e_destination_set_card): Check that the card we are setting is + not equal to the current card, and emit the "changed" signal if we + are actually changing. + (e_destination_set_card_uri): Emit "changed" signal, if necessary. + (e_destination_set_name): Emit "changed" signal, if necessary. + (e_destination_set_email): Emit "changed" signal, if necessary. + (e_destination_set_html_mail_pref): Emit "changed" signal, if + necessary. + (use_card_cb): If we've just loaded/set the ECard, emit the + "changed" signal. + (e_destination_set_raw): Emit "changed" signal, if necessary. + (e_destination_allow_cardification): Added. + (e_destination_set_allow_cardification): Added. + (e_destination_cardify): Added. Tries to automatically convert + a string-based EDestination to one based on an ECard. + (e_destination_cardify_delayed): Added. Cardifies in a timeout. + (e_destination_cancel_cardify): Added. Cancels any pending + cardifications. + (e_destination_xml_decode): Added freeze/thaw. + + * backend/ebook/e-book-util.c (e_book_nickname_query): Added. A + canned simple query for nicknames. + + * backend/ebook/e-card.c (e_card_email_find_number): Added. Given + a card and an string containing an email address, return the index + number of the address inside of the card, or -1 if the address is + not found. + 2001-08-09 Chris Toshok [ Fixes ximian bugs #5080, #6021, #6704, #6705 ] diff --git a/addressbook/backend/ebook/e-book-util.c b/addressbook/backend/ebook/e-book-util.c index d10c5612f1..c65c70dfde 100644 --- a/addressbook/backend/ebook/e-book-util.c +++ b/addressbook/backend/ebook/e-book-util.c @@ -507,6 +507,56 @@ e_book_name_and_email_query (EBook *book, return tag; } +/* + * Simple nickname query + */ + +typedef struct _NicknameQueryInfo NicknameQueryInfo; +struct _NicknameQueryInfo { + gchar *nickname; + EBookSimpleQueryCallback cb; + gpointer closure; +}; + +static void +nickname_cb (EBook *book, EBookSimpleQueryStatus status, const GList *cards, gpointer closure) +{ + NicknameQueryInfo *info = closure; + + if (info->cb) + info->cb (book, status, cards, info->closure); + + g_free (info->nickname); + g_free (info); +} + +guint +e_book_nickname_query (EBook *book, + const char *nickname, + EBookSimpleQueryCallback cb, + gpointer closure) +{ + NicknameQueryInfo *info; + gchar *query; + guint retval; + + g_return_val_if_fail (E_IS_BOOK (book), 0); + g_return_val_if_fail (nickname && *nickname, 0); + + info = g_new0 (NicknameQueryInfo, 1); + info->nickname = g_strdup (nickname); + info->cb = cb; + info->closure = closure; + + query = g_strdup_printf ("(is \"nickname\" \"%s\")", info->nickname); + + retval = e_book_simple_query (book, query, nickname_cb, info); + + g_free (query); + + return retval; +} + /* * Convenience routine to check for addresses in the local address book. */ diff --git a/addressbook/backend/ebook/e-book-util.h b/addressbook/backend/ebook/e-book-util.h index 0988887324..69965c8363 100644 --- a/addressbook/backend/ebook/e-book-util.h +++ b/addressbook/backend/ebook/e-book-util.h @@ -61,6 +61,11 @@ guint e_book_name_and_email_query (EBook *book, EBookSimpleQueryCallback cb, gpointer closure); +guint e_book_nickname_query (EBook *book, + const char *nickname, + EBookSimpleQueryCallback cb, + gpointer closure); + /* Returns the ECard associated to email in the callback, or NULL if no match is found in the local address book. */ void e_book_query_address_locally (const gchar *email, diff --git a/addressbook/backend/ebook/e-card.c b/addressbook/backend/ebook/e-card.c index 3833211637..4b85c33868 100644 --- a/addressbook/backend/ebook/e-card.c +++ b/addressbook/backend/ebook/e-card.c @@ -1813,6 +1813,29 @@ e_card_email_match_string (const ECard *card, const gchar *str) return FALSE; } +gint +e_card_email_find_number (const ECard *card, const gchar *email) +{ + EIterator *iter; + gint count = 0; + + g_return_val_if_fail (E_IS_CARD (card), -1); + g_return_val_if_fail (email != NULL, -1); + + iter = e_list_get_iterator (card->email); + for (e_iterator_reset (iter); e_iterator_is_valid (iter); e_iterator_next (iter)) { + if (!strcmp (e_iterator_get (iter), email)) + goto finished; + ++count; + } + count = -1; + + finished: + gtk_object_unref (GTK_OBJECT (iter)); + + return count; +} + /* * ECard lifecycle management and vCard loading/saving. */ diff --git a/addressbook/backend/ebook/e-card.h b/addressbook/backend/ebook/e-card.h index 3aaeddb813..83e074ff23 100644 --- a/addressbook/backend/ebook/e-card.h +++ b/addressbook/backend/ebook/e-card.h @@ -174,6 +174,8 @@ void e_card_arbitrary_free (ECardArbitrary /* ECard email manipulation */ gboolean e_card_email_match_string (const ECard *card, const gchar *str); +gint e_card_email_find_number (const ECard *card, + const gchar *email); /* Specialized functionality */ GList *e_card_load_cards_from_file (const char *filename); diff --git a/addressbook/backend/ebook/e-destination.c b/addressbook/backend/ebook/e-destination.c index bed6fc1b7d..4e1e27519d 100644 --- a/addressbook/backend/ebook/e-destination.c +++ b/addressbook/backend/ebook/e-destination.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include "e-book.h" @@ -41,6 +42,14 @@ #include #include +enum { + CHANGED, + CARDIFIED, + LAST_SIGNAL +}; + +guint e_destination_signals[LAST_SIGNAL] = { 0 }; + struct _EDestinationPrivate { gchar *raw; @@ -57,6 +66,15 @@ struct _EDestinationPrivate { gboolean wants_html_mail; GList *list_dests; + + gboolean has_been_cardified; + gboolean allow_cardify; + gboolean cannot_cardify; + guint pending_cardification; + EBook *cardify_book; + + gint freeze_count; + gboolean pending_change; }; static void e_destination_clear_card (EDestination *); @@ -70,6 +88,10 @@ e_destination_destroy (GtkObject *obj) EDestination *dest = E_DESTINATION (obj); e_destination_clear (dest); + + if (dest->priv->cardify_book) + gtk_object_unref (GTK_OBJECT (dest->priv->cardify_book)); + g_free (dest->priv); if (parent_class->destroy) @@ -84,12 +106,34 @@ e_destination_class_init (EDestinationClass *klass) parent_class = GTK_OBJECT_CLASS (gtk_type_class (GTK_TYPE_OBJECT)); object_class->destroy = e_destination_destroy; + + e_destination_signals[CHANGED] = + gtk_signal_new ("changed", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (EDestinationClass, changed), + gtk_marshal_NONE__NONE, + GTK_TYPE_NONE, 0); + + e_destination_signals[CARDIFIED] = + gtk_signal_new ("cardified", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (EDestinationClass, cardified), + gtk_marshal_NONE__NONE, + GTK_TYPE_NONE, 0); + + gtk_object_class_add_signals (object_class, e_destination_signals, LAST_SIGNAL); } static void e_destination_init (EDestination *dest) { dest->priv = g_new0 (struct _EDestinationPrivate, 1); + + dest->priv->allow_cardify = TRUE; + dest->priv->cannot_cardify = FALSE; + dest->priv->pending_cardification = 0; } GtkType @@ -120,6 +164,38 @@ e_destination_new (void) return E_DESTINATION (gtk_type_new (E_TYPE_DESTINATION)); } +static void +e_destination_freeze (EDestination *dest) +{ + g_return_if_fail (E_IS_DESTINATION (dest)); + g_return_if_fail (dest->priv->freeze_count >= 0); + ++dest->priv->freeze_count; +} + +static void +e_destination_thaw (EDestination *dest) +{ + g_return_if_fail (E_IS_DESTINATION (dest)); + g_return_if_fail (dest->priv->freeze_count > 0); + --dest->priv->freeze_count; + if (dest->priv->freeze_count == 0 && dest->priv->pending_change) + e_destination_changed (dest); +} + +void +e_destination_changed (EDestination *dest) +{ + if (dest->priv->freeze_count == 0) { + gtk_signal_emit (GTK_OBJECT (dest), e_destination_signals[CHANGED]); + dest->priv->pending_change = FALSE; + + dest->priv->cannot_cardify = FALSE; + + } else { + dest->priv->pending_change = TRUE; + } +} + EDestination * e_destination_copy (const EDestination *dest) { @@ -166,6 +242,13 @@ e_destination_clear_card (EDestination *dest) g_list_foreach (dest->priv->list_dests, (GFunc) gtk_object_unref, NULL); g_list_free (dest->priv->list_dests); dest->priv->list_dests = NULL; + + dest->priv->allow_cardify = TRUE; + dest->priv->cannot_cardify = FALSE; + + e_destination_cancel_cardify (dest); + + e_destination_changed (dest); } static void @@ -182,6 +265,8 @@ e_destination_clear_strings (EDestination *dest) g_free (dest->priv->addr); dest->priv->addr = NULL; + + e_destination_changed (dest); } void @@ -189,8 +274,12 @@ e_destination_clear (EDestination *dest) { g_return_if_fail (dest && E_IS_DESTINATION (dest)); + e_destination_freeze (dest); + e_destination_clear_card (dest); e_destination_clear_strings (dest); + + e_destination_thaw (dest); } gboolean @@ -215,12 +304,17 @@ e_destination_set_card (EDestination *dest, ECard *card, gint email_num) g_return_if_fail (dest && E_IS_DESTINATION (dest)); g_return_if_fail (card && E_IS_CARD (card)); - e_destination_clear (dest); + if (dest->priv->card != card || dest->priv->card_email_num != email_num) { + + e_destination_clear (dest); - dest->priv->card = card; - gtk_object_ref (GTK_OBJECT (dest->priv->card)); + dest->priv->card = card; + gtk_object_ref (GTK_OBJECT (dest->priv->card)); - dest->priv->card_email_num = email_num; + dest->priv->card_email_num = email_num; + + e_destination_changed (dest); + } } void @@ -228,16 +322,23 @@ e_destination_set_card_uri (EDestination *dest, const gchar *uri, gint email_num { g_return_if_fail (dest && E_IS_DESTINATION (dest)); g_return_if_fail (uri != NULL); + + if (dest->priv->card_uri == NULL + || strcmp (dest->priv->card_uri, uri) + || dest->priv->card_email_num != email_num) { - g_free (dest->priv->card_uri); - dest->priv->card_uri = g_strdup (uri); - dest->priv->card_email_num = email_num; + g_free (dest->priv->card_uri); + dest->priv->card_uri = g_strdup (uri); + dest->priv->card_email_num = email_num; + + /* If we already have a card, remove it unless it's uri matches the one + we just set. */ + if (dest->priv->card && strcmp (uri, e_card_get_uri (dest->priv->card))) { + gtk_object_unref (GTK_OBJECT (dest->priv->card)); + dest->priv->card = NULL; + } - /* If we already have a card, remove it unless it's uri matches the one - we just set. */ - if (dest->priv->card && strcmp (uri, e_card_get_uri (dest->priv->card))) { - gtk_object_unref (GTK_OBJECT (dest->priv->card)); - dest->priv->card = NULL; + e_destination_changed (dest); } } @@ -247,12 +348,17 @@ e_destination_set_name (EDestination *dest, const gchar *name) g_return_if_fail (dest && E_IS_DESTINATION (dest)); g_return_if_fail (name != NULL); - g_free (dest->priv->name); - dest->priv->name = g_strdup (name); + if (dest->priv->name == NULL || strcmp (dest->priv->name, name)) { + + g_free (dest->priv->name); + dest->priv->name = g_strdup (name); + + if (dest->priv->addr != NULL) { + g_free (dest->priv->addr); + dest->priv->addr = NULL; + } - if (dest->priv->addr != NULL) { - g_free (dest->priv->addr); - dest->priv->addr = NULL; + e_destination_changed (dest); } } @@ -262,12 +368,17 @@ e_destination_set_email (EDestination *dest, const gchar *email) g_return_if_fail (dest && E_IS_DESTINATION (dest)); g_return_if_fail (email != NULL); - g_free (dest->priv->email); - dest->priv->email = g_strdup (email); + if (dest->priv->email == NULL || strcmp (dest->priv->email, email)) { + + g_free (dest->priv->email); + dest->priv->email = g_strdup (email); - if (dest->priv->addr != NULL) { - g_free (dest->priv->addr); - dest->priv->addr = NULL; + if (dest->priv->addr != NULL) { + g_free (dest->priv->addr); + dest->priv->addr = NULL; + } + + e_destination_changed (dest); } } @@ -275,9 +386,12 @@ void e_destination_set_html_mail_pref (EDestination *dest, gboolean x) { g_return_if_fail (dest && E_IS_DESTINATION (dest)); - + dest->priv->html_mail_override = TRUE; - dest->priv->wants_html_mail = x; + if (dest->priv->wants_html_mail != x) { + dest->priv->wants_html_mail = x; + e_destination_changed (dest); + } } gboolean @@ -311,6 +425,7 @@ use_card_cb (ECard *card, gpointer closure) uc->dest->priv->card = card; gtk_object_ref (GTK_OBJECT (uc->dest->priv->card)); + e_destination_changed (uc->dest); } @@ -520,10 +635,16 @@ e_destination_set_raw (EDestination *dest, const gchar *raw) g_return_if_fail (E_IS_DESTINATION (dest)); g_return_if_fail (raw != NULL); - e_destination_clear (dest); + if (dest->priv->raw == NULL || strcmp (dest->priv->raw, raw)) { - dest->priv->raw = g_strdup (raw); + e_destination_freeze (dest); + e_destination_clear (dest); + dest->priv->raw = g_strdup (raw); + e_destination_changed (dest); + + e_destination_thaw (dest); + } } const gchar * @@ -582,6 +703,202 @@ e_destination_get_html_mail_pref (const EDestination *dest) return dest->priv->card->wants_html; } +gboolean +e_destination_allow_cardification (const EDestination *dest) +{ + g_return_val_if_fail (E_IS_DESTINATION (dest), FALSE); + + return dest->priv->allow_cardify; +} + +void +e_destination_set_allow_cardification (EDestination *dest, gboolean x) +{ + g_return_if_fail (E_IS_DESTINATION (dest)); + + dest->priv->allow_cardify = x; +} + +static void +set_cardify_book (EDestination *dest, EBook *book) +{ + if (dest->priv->cardify_book && dest->priv->cardify_book != book) { + gtk_object_unref (GTK_OBJECT (dest->priv->cardify_book)); + } + + dest->priv->cardify_book = book; + + if (book) + gtk_object_ref (GTK_OBJECT (book)); +} + +static void +name_and_email_simple_query_cb (EBook *book, EBookSimpleQueryStatus status, const GList *cards, gpointer closure) +{ + EDestination *dest = E_DESTINATION (closure); + + if (status == E_BOOK_SIMPLE_QUERY_STATUS_SUCCESS && g_list_length ((GList *) cards) == 1) { + ECard *card = E_CARD (cards->data); + gint email_num = e_card_email_find_number (card, e_destination_get_email (dest)); + + if (email_num >= 0) { + dest->priv->has_been_cardified = TRUE; + e_destination_set_card (dest, E_CARD (cards->data), email_num); + gtk_signal_emit (GTK_OBJECT (dest), e_destination_signals[CARDIFIED]); + } + } + + if (!dest->priv->has_been_cardified) { + dest->priv->cannot_cardify = TRUE; + } + + gtk_object_unref (GTK_OBJECT (dest)); +} + + +static void +nickname_simple_query_cb (EBook *book, EBookSimpleQueryStatus status, const GList *cards, gpointer closure) +{ + EDestination *dest = E_DESTINATION (closure); + + if (status == E_BOOK_SIMPLE_QUERY_STATUS_SUCCESS && g_list_length ((GList *) cards) == 1) { + + dest->priv->has_been_cardified = TRUE; + e_destination_set_card (dest, E_CARD (cards->data), 0); /* Uses primary e-mail by default. */ + gtk_signal_emit (GTK_OBJECT (dest), e_destination_signals[CARDIFIED]); + gtk_object_unref (GTK_OBJECT (dest)); + + } else { + + e_book_name_and_email_query (book, + e_destination_get_name (dest), + e_destination_get_email (dest), + name_and_email_simple_query_cb, + dest); + } +} + +static void +launch_cardify_query (EDestination *dest) +{ + if (strchr (e_destination_get_textrep (dest), '@') == NULL) { + + /* If it doesn't look like an e-mail address, see if it is a nickname. */ + e_book_nickname_query (dest->priv->cardify_book, + e_destination_get_textrep (dest), + nickname_simple_query_cb, + dest); + + } else { + + e_book_name_and_email_query (dest->priv->cardify_book, + e_destination_get_name (dest), + e_destination_get_email (dest), + name_and_email_simple_query_cb, + dest); + } +} + +static void +use_local_book_cb (EBook *book, gpointer closure) +{ + EDestination *dest = E_DESTINATION (closure); + if (dest->priv->cardify_book == NULL) { + dest->priv->cardify_book = book; + gtk_object_ref (GTK_OBJECT (book)); + } + + launch_cardify_query (dest); +} + + +void +e_destination_cardify (EDestination *dest, EBook *book) +{ + g_return_if_fail (E_IS_DESTINATION (dest)); + g_return_if_fail (book == NULL || E_IS_BOOK (book)); + + if (e_destination_is_evolution_list (dest)) + return; + + if (e_destination_contains_card (dest)) + return; + + if (!dest->priv->allow_cardify) + return; + + if (dest->priv->cannot_cardify) + return; + + e_destination_cancel_cardify (dest); + + set_cardify_book (dest, book); + + /* Handle the case of an EDestination containing a card URL */ + if (e_destination_contains_card (dest)) { + e_destination_use_card (dest, NULL, NULL); + return; + } + + /* If we have a book ready, proceed. We hold a reference to ourselves + until our query is complete. */ + gtk_object_ref (GTK_OBJECT (dest)); + if (dest->priv->cardify_book != NULL) { + launch_cardify_query (dest); + } else { + e_book_use_local_address_book (use_local_book_cb, dest); + } +} + +static gint +do_cardify_delayed (gpointer ptr) +{ + EDestination *dest = E_DESTINATION (ptr); + e_destination_cardify (dest, dest->priv->cardify_book); + return FALSE; +} + +void +e_destination_cardify_delayed (EDestination *dest, EBook *book, gint delay) +{ + g_return_if_fail (E_IS_DESTINATION (dest)); + g_return_if_fail (book == NULL || E_IS_BOOK (book)); + + if (delay < 0) + delay = 500; + + e_destination_cancel_cardify (dest); + + set_cardify_book (dest, book); + + dest->priv->pending_cardification = gtk_timeout_add (delay, do_cardify_delayed, dest); +} + +void +e_destination_cancel_cardify (EDestination *dest) +{ + g_return_if_fail (E_IS_DESTINATION (dest)); + + if (dest->priv->pending_cardification) { + gtk_timeout_remove (dest->priv->pending_cardification); + dest->priv->pending_cardification = 0; + } +} + +#if 0 +void +e_destination_uncardify (EDestination *dest) +{ + g_return_if_fail (E_IS_DESTINATION (dest)); + + g_assert_not_reached (); +} +#endif + +/* + * Destination import/export + */ + gchar * e_destination_get_address_textv (EDestination **destv) { @@ -754,6 +1071,8 @@ e_destination_xml_decode (EDestination *dest, xmlNodePtr node) node = node->next; } + + e_destination_freeze (dest); e_destination_clear (dest); @@ -765,6 +1084,8 @@ e_destination_xml_decode (EDestination *dest, xmlNodePtr node) e_destination_set_card_uri (dest, card_uri, email_num); if (list_dests) dest->priv->list_dests = list_dests; + + e_destination_thaw (dest); return TRUE; } diff --git a/addressbook/backend/ebook/e-destination.h b/addressbook/backend/ebook/e-destination.h index 96a3989d9a..92d4d803f2 100644 --- a/addressbook/backend/ebook/e-destination.h +++ b/addressbook/backend/ebook/e-destination.h @@ -54,12 +54,16 @@ struct _EDestination { struct _EDestinationClass { GtkObjectClass parent_class; + + void (*changed) (EDestination *dest); + void (*cardified) (EDestination *dest); }; GtkType e_destination_get_type (void); EDestination *e_destination_new (void); +void e_destination_changed (EDestination *); EDestination *e_destination_copy (const EDestination *); void e_destination_clear (EDestination *); @@ -94,6 +98,16 @@ gboolean e_destination_is_evolution_list (const EDestination *); /* If true, they want HTML mail. */ gboolean e_destination_get_html_mail_pref (const EDestination *); +gboolean e_destination_allow_cardification (const EDestination *); +void e_destination_set_allow_cardification (EDestination *, gboolean); +void e_destination_cardify (EDestination *, EBook *); +void e_destination_cardify_delayed (EDestination *, EBook *, gint delay); /* delay < 0: "default" */ +void e_destination_cancel_cardify (EDestination *); + +#if 0 +void e_destination_uncardify (EDestination *); +#endif + gchar *e_destination_get_address_textv (EDestination **); xmlNodePtr e_destination_xml_encode (const EDestination *dest); diff --git a/addressbook/gui/component/select-names/e-select-names-bonobo.c b/addressbook/gui/component/select-names/e-select-names-bonobo.c index 25adf13a65..90b3e477fb 100644 --- a/addressbook/gui/component/select-names/e-select-names-bonobo.c +++ b/addressbook/gui/component/select-names/e-select-names-bonobo.c @@ -132,8 +132,15 @@ entry_set_property_fn (BonoboPropertyBag *bag, switch (arg_id) { case ENTRY_PROPERTY_ID_TEXT: - e_entry_set_text (E_ENTRY (w), BONOBO_ARG_GET_STRING (arg)); - break; + { + ESelectNamesModel *model; + model = E_SELECT_NAMES_MODEL (gtk_object_get_data (GTK_OBJECT (w), "select_names_model")); + g_assert (model != NULL); + + e_entry_set_text (E_ENTRY (w), BONOBO_ARG_GET_STRING (arg)); + e_select_names_model_cardify_all (model, NULL, 0); + break; + } case ENTRY_PROPERTY_ID_DESTINATIONS: { diff --git a/addressbook/gui/component/select-names/e-select-names-manager.c b/addressbook/gui/component/select-names/e-select-names-manager.c index 4481438327..0bec7da416 100644 --- a/addressbook/gui/component/select-names/e-select-names-manager.c +++ b/addressbook/gui/component/select-names/e-select-names-manager.c @@ -368,6 +368,38 @@ popup_cb (EEntry *entry, GdkEventButton *ev, gint pos, ESelectNamesModel *model) e_select_names_popup (model, ev, pos); } +static gint +focus_in_cb (GtkWidget *w, GdkEventFocus *ev, gpointer user_data) +{ + EEntry *entry = E_ENTRY (user_data); + ESelectNamesModel *model = E_SELECT_NAMES_MODEL (gtk_object_get_data (GTK_OBJECT (entry), "select_names_model")); + + e_select_names_model_cancel_cardify_all (model); + + return FALSE; +} + +static gint +focus_out_cb (GtkWidget *w, GdkEventFocus *ev, gpointer user_data) +{ + EEntry *entry = E_ENTRY (user_data); + ESelectNamesModel *model = E_SELECT_NAMES_MODEL (gtk_object_get_data (GTK_OBJECT (entry), "select_names_model")); + + if (!e_entry_completion_popup_is_visible (entry)) + e_select_names_model_cardify_all (model, NULL, 0); + + return FALSE; +} + +static void +completion_popup_cb (EEntry *entry, gint visible, gpointer user_data) +{ + ESelectNamesModel *model = E_SELECT_NAMES_MODEL (gtk_object_get_data (GTK_OBJECT (entry), "select_names_model")); + + if (!visible && !GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (entry->canvas))) + e_select_names_model_cardify_all (model, NULL, 0); +} + GtkWidget * e_select_names_manager_create_entry (ESelectNamesManager *manager, const char *id) { @@ -389,6 +421,19 @@ e_select_names_manager_create_entry (ESelectNamesManager *manager, const char *i GTK_SIGNAL_FUNC (popup_cb), section->model); + gtk_signal_connect (GTK_OBJECT (eentry->canvas), + "focus_in_event", + GTK_SIGNAL_FUNC (focus_in_cb), + eentry); + gtk_signal_connect (GTK_OBJECT (eentry->canvas), + "focus_out_event", + GTK_SIGNAL_FUNC (focus_out_cb), + eentry); + gtk_signal_connect (GTK_OBJECT (eentry), + "completion_popup", + GTK_SIGNAL_FUNC (completion_popup_cb), + NULL); + entry = g_new (ESelectNamesManagerEntry, 1); entry->entry = eentry; entry->id = (char *)id; diff --git a/addressbook/gui/component/select-names/e-select-names-model.c b/addressbook/gui/component/select-names/e-select-names-model.c index dc383994d3..9dda927640 100644 --- a/addressbook/gui/component/select-names/e-select-names-model.c +++ b/addressbook/gui/component/select-names/e-select-names-model.c @@ -219,6 +219,12 @@ e_select_names_model_changed (ESelectNamesModel *model) gtk_signal_emit (GTK_OBJECT(model), e_select_names_model_signals[E_SELECT_NAMES_MODEL_CHANGED]); } +static void +destination_changed_proxy (EDestination *dest, gpointer closure) +{ + e_select_names_model_changed (E_SELECT_NAMES_MODEL (closure)); +} + /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **/ ESelectNamesModel * @@ -240,7 +246,7 @@ e_select_names_model_duplicate (ESelectNamesModel *old) for (iter = old->priv->data; iter != NULL; iter = g_list_next (iter)) { EDestination *dup = e_destination_copy (E_DESTINATION (iter->data)); - model->priv->data = g_list_append (model->priv->data, dup); + e_select_names_model_append (model, dup); } model->priv->limit = old->priv->limit; @@ -444,6 +450,21 @@ e_select_names_model_get_string (ESelectNamesModel *model, gint index) return dest ? e_destination_get_textrep (dest) : ""; } +static void +connect_destination (ESelectNamesModel *model, EDestination *dest) +{ + gtk_signal_connect (GTK_OBJECT (dest), + "changed", + destination_changed_proxy, + model); +} + +static void +disconnect_destination (ESelectNamesModel *model, EDestination *dest) +{ + gtk_signal_disconnect_by_func (GTK_OBJECT (dest), destination_changed_proxy, model); +} + void e_select_names_model_insert (ESelectNamesModel *model, gint index, EDestination *dest) { @@ -458,6 +479,8 @@ e_select_names_model_insert (ESelectNamesModel *model, gint index, EDestination return; } + connect_destination (model, dest); + model->priv->data = g_list_insert (model->priv->data, dest, index); gtk_object_ref (GTK_OBJECT (dest)); @@ -478,6 +501,8 @@ e_select_names_model_append (ESelectNamesModel *model, EDestination *dest) return; } + connect_destination (model, dest); + model->priv->data = g_list_append (model->priv->data, dest); gtk_object_ref (GTK_OBJECT (dest)); @@ -503,6 +528,8 @@ e_select_names_model_replace (ESelectNamesModel *model, gint index, EDestination if (model->priv->data == NULL) { + connect_destination (model, dest); + model->priv->data = g_list_append (model->priv->data, dest); gtk_object_ref (GTK_OBJECT (dest)); gtk_object_sink (GTK_OBJECT (dest)); @@ -513,6 +540,9 @@ e_select_names_model_replace (ESelectNamesModel *model, gint index, EDestination if (node->data != dest) { + disconnect_destination (model, E_DESTINATION (node->data)); + connect_destination (model, dest); + old_str = e_destination_get_textrep (E_DESTINATION (node->data)); old_strlen = old_str ? strlen (old_str) : 0; @@ -540,6 +570,7 @@ e_select_names_model_delete (ESelectNamesModel *model, gint index) g_return_if_fail (0 <= index && index < g_list_length (model->priv->data)); node = g_list_nth (model->priv->data, index); + disconnect_destination (model, E_DESTINATION (node->data)); gtk_object_unref (GTK_OBJECT (node->data)); model->priv->data = g_list_remove_link (model->priv->data, node); @@ -565,8 +596,10 @@ e_select_names_model_clean (ESelectNamesModel *model) dest = iter->data ? E_DESTINATION (iter->data) : NULL; if (dest == NULL || e_destination_is_empty (dest)) { - if (dest) + if (dest) { + disconnect_destination (model, dest); gtk_object_unref (GTK_OBJECT (dest)); + } model->priv->data = g_list_remove_link (model->priv->data, iter); g_list_free_1 (iter); changed = TRUE; @@ -579,12 +612,19 @@ e_select_names_model_clean (ESelectNamesModel *model) e_select_names_model_changed (model); } +static void +delete_all_iter (gpointer data, gpointer closure) +{ + disconnect_destination (E_SELECT_NAMES_MODEL (closure), E_DESTINATION (data)); + gtk_object_unref (GTK_OBJECT (data)); +} + void e_select_names_model_delete_all (ESelectNamesModel *model) { g_return_if_fail (model != NULL && E_IS_SELECT_NAMES_MODEL (model)); - g_list_foreach (model->priv->data, (GFunc) gtk_object_unref, NULL); + g_list_foreach (model->priv->data, delete_all_iter, model); g_list_free (model->priv->data); model->priv->data = NULL; @@ -692,3 +732,70 @@ e_select_names_model_text_pos (ESelectNamesModel *model, gint pos, gint *index, if (length) *length = len; } + +void +e_select_names_model_cardify (ESelectNamesModel *model, EBook *book, gint index, gint delay) +{ + EDestination *dest; + + g_return_if_fail (E_IS_SELECT_NAMES_MODEL (model)); + g_return_if_fail (book == NULL || E_IS_BOOK (book)); + g_return_if_fail (0 <= index && index < g_list_length (model->priv->data)); + + dest = E_DESTINATION (g_list_nth_data (model->priv->data, index)); + + if (!e_destination_is_empty (dest)) { + + if (delay > 0) + e_destination_cardify_delayed (dest, book, delay); + else + e_destination_cardify (dest, book); + } +} + +void +e_select_names_model_cancel_cardify (ESelectNamesModel *model, gint index) +{ + EDestination *dest; + + g_return_if_fail (E_IS_SELECT_NAMES_MODEL (model)); + g_return_if_fail (0 <= index && index < g_list_length (model->priv->data)); + + dest = E_DESTINATION (g_list_nth_data (model->priv->data, index)); + + e_destination_cancel_cardify (dest); +} + +void +e_select_names_model_cardify_all (ESelectNamesModel *model, EBook *book, gint delay) +{ + GList *iter; + + g_return_if_fail (E_IS_SELECT_NAMES_MODEL (model)); + g_return_if_fail (book == NULL || E_IS_BOOK (book)); + + for (iter = model->priv->data; iter != NULL; iter = g_list_next (iter)) { + EDestination *dest = E_DESTINATION (iter->data); + if (!e_destination_is_empty (dest)) { + + if (delay > 0) + e_destination_cardify_delayed (dest, book, delay); + else + e_destination_cardify (dest, book); + } + } +} + +void +e_select_names_model_cancel_cardify_all (ESelectNamesModel *model) +{ + GList *iter; + + g_return_if_fail (E_IS_SELECT_NAMES_MODEL (model)); + + for (iter = model->priv->data; iter != NULL; iter = g_list_next (iter)) { + EDestination *dest = E_DESTINATION (iter->data); + e_destination_cancel_cardify (dest); + } +} + diff --git a/addressbook/gui/component/select-names/e-select-names-model.h b/addressbook/gui/component/select-names/e-select-names-model.h index b11e9503ec..9f89e91093 100644 --- a/addressbook/gui/component/select-names/e-select-names-model.h +++ b/addressbook/gui/component/select-names/e-select-names-model.h @@ -73,4 +73,10 @@ void e_select_names_model_clean (ESelectNamesModel *model); void e_select_names_model_name_pos (ESelectNamesModel *model, gint index, gint *pos, gint *length); void e_select_names_model_text_pos (ESelectNamesModel *model, gint pos, gint *index, gint *start_pos, gint *length); +void e_select_names_model_cardify (ESelectNamesModel *model, EBook *book, gint index, gint delay); +void e_select_names_model_cancel_cardify (ESelectNamesModel *model, gint index); +void e_select_names_model_cardify_all (ESelectNamesModel *model, EBook *book, gint delay); +void e_select_names_model_cancel_cardify_all (ESelectNamesModel *model); + + #endif /* ! __E_SELECT_NAMES_MODEL_H__ */ diff --git a/addressbook/gui/component/select-names/e-select-names-text-model.c b/addressbook/gui/component/select-names/e-select-names-text-model.c index 11b3b67323..2f10516dd1 100644 --- a/addressbook/gui/component/select-names/e-select-names-text-model.c +++ b/addressbook/gui/component/select-names/e-select-names-text-model.c @@ -423,7 +423,7 @@ e_select_names_text_model_insert_length (ETextModel *model, gint pos, const gcha EDestination *dest = e_destination_new (); e_destination_set_raw (dest, new_str); - + e_select_names_model_replace (source, index, dest); if (this_length > 0) { -- cgit v1.2.3