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/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 ++ 6 files changed, 441 insertions(+), 26 deletions(-) (limited to 'addressbook/backend/ebook') 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); -- cgit v1.2.3