diff options
author | Jon Trowbridge <trow@ximian.com> | 2001-03-16 16:16:29 +0800 |
---|---|---|
committer | Jon Trowbridge <trow@src.gnome.org> | 2001-03-16 16:16:29 +0800 |
commit | 57de6972c845dbae49717a4520aeed75f88f0078 (patch) | |
tree | f58e45cdbb09793d8a09506af00c70b1ca63a389 /addressbook/backend/ebook | |
parent | 403205b15e9f14472711ee115cae17031eb4ce7b (diff) | |
download | gsoc2013-evolution-57de6972c845dbae49717a4520aeed75f88f0078.tar.gz gsoc2013-evolution-57de6972c845dbae49717a4520aeed75f88f0078.tar.zst gsoc2013-evolution-57de6972c845dbae49717a4520aeed75f88f0078.zip |
Added addressbook querying and "cardification" functions, which are turned
2001-03-15 Jon Trowbridge <trow@ximian.com>
* gui/component/e-address-widget.c: Added addressbook querying and
"cardification" functions, which are turned off by default for now
because of addressbook bugs. Added a popup menu option to turn
queries on, so that others can enjoy the thrill of massive flaming
death.
* gui/component/addressbook-factory.c (main): Made warnings always
be fatal.
* backend/pas/pas-book-view.c: Added some debugging spew.
* backend/pas/pas-backend-file.c (pas_backend_file_search): Added
a little experimental code to try to make file searches scale
better. #if 0/#endif-ed out for now.
* contact-editor/e-contact-quick-add.c: #included e-book-util.h.
* backend/ebook/e-card.c (e_card_name_match_string): Added.
Looser name-matching function.
(e_card_email_match_string): Added. Loose e-mail matching.
* backend/ebook/e-book-view-listener.c
(e_book_view_listener_check_queue): Added code to cause us to
abort rather than get trapped in a 100%-CPU-consuming loop in
certain situations. Now we just need to figure out how to avoid
these situations altogether.
* backend/ebook/e-book-util.c: Added. Now contains the simple
query stuff and the open local addressbook functions.
* backend/ebook/e-book.c: Moved simple query stuff and open local
addressbook functions into e-book-util.c.
2001-03-15 Jon Trowbridge <trow@ximian.com>
* wombat.c (main): If we can't initialize a service on startup,
tell us which one before terminating.
svn path=/trunk/; revision=8754
Diffstat (limited to 'addressbook/backend/ebook')
-rw-r--r-- | addressbook/backend/ebook/Makefile.am | 2 | ||||
-rw-r--r-- | addressbook/backend/ebook/e-book-util.c | 444 | ||||
-rw-r--r-- | addressbook/backend/ebook/e-book-util.h | 64 | ||||
-rw-r--r-- | addressbook/backend/ebook/e-book-view-listener.c | 19 | ||||
-rw-r--r-- | addressbook/backend/ebook/e-book.c | 174 | ||||
-rw-r--r-- | addressbook/backend/ebook/e-book.h | 16 | ||||
-rw-r--r-- | addressbook/backend/ebook/e-card.c | 202 | ||||
-rw-r--r-- | addressbook/backend/ebook/e-card.h | 6 |
8 files changed, 737 insertions, 190 deletions
diff --git a/addressbook/backend/ebook/Makefile.am b/addressbook/backend/ebook/Makefile.am index 7abee848e9..a46d94ac87 100644 --- a/addressbook/backend/ebook/Makefile.am +++ b/addressbook/backend/ebook/Makefile.am @@ -43,6 +43,7 @@ libebook_la_SOURCES = \ e-book-view-listener.c \ e-book-view.c \ e-book.c \ + e-book-util.c \ e-card-cursor.c \ e-card-simple.c \ e-card.c \ @@ -56,6 +57,7 @@ libebookinclude_HEADERS = \ e-book-view-listener.h \ e-book-view.h \ e-book.h \ + e-book-util.h \ e-card-cursor.h \ e-card-pairs.h \ e-card-simple.h \ diff --git a/addressbook/backend/ebook/e-book-util.c b/addressbook/backend/ebook/e-book-util.c new file mode 100644 index 0000000000..1d1106e8f2 --- /dev/null +++ b/addressbook/backend/ebook/e-book-util.c @@ -0,0 +1,444 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * e-book-util.c + * + * Copyright (C) 2001 Ximian, Inc. + * + * Developed by Jon Trowbridge <trow@ximian.com> + */ + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA. + */ + +#include <config.h> +#include <gtk/gtk.h> +#include <libgnome/gnome-defs.h> +#include <libgnome/gnome-util.h> +#include "e-book-util.h" + +gboolean +e_book_load_local_address_book (EBook *book, EBookCallback open_response, gpointer closure) +{ + gchar *filename; + gchar *uri; + gboolean rv; + + g_return_val_if_fail (book != NULL, FALSE); + g_return_val_if_fail (E_IS_BOOK (book), FALSE); + g_return_val_if_fail (open_response != NULL, FALSE); + + filename = gnome_util_prepend_user_home ("evolution/local/Contacts/addressbook.db"); + uri = g_strdup_printf ("file://%s", filename); + + rv = e_book_load_uri (book, uri, open_response, closure); + + g_free (filename); + g_free (uri); + + return rv; +} + +/* + * + * Simple Query Stuff + * + */ + +typedef struct _SimpleQueryInfo SimpleQueryInfo; +struct _SimpleQueryInfo { + guint tag; + EBook *book; + gchar *query; + EBookSimpleQueryCallback cb; + gpointer closure; + EBookView *view; + guint add_tag; + guint seq_complete_tag; + GList *cards; +}; + +static void +book_add_simple_query (EBook *book, SimpleQueryInfo *info) +{ + GList *pending = gtk_object_get_data (GTK_OBJECT (book), "sq_pending"); + pending = g_list_prepend (pending, info); + gtk_object_set_data (GTK_OBJECT (book), "sq_pending", pending); +} + +static SimpleQueryInfo * +book_lookup_simple_query (EBook *book, guint tag) +{ + GList *pending = gtk_object_get_data (GTK_OBJECT (book), "sq_pending"); + while (pending) { + SimpleQueryInfo *sq = pending->data; + if (sq->tag == tag) + return sq; + pending = g_list_next (pending); + } + return NULL; +} + +static void +book_remove_simple_query (EBook *book, SimpleQueryInfo *info) +{ + GList *pending = gtk_object_get_data (GTK_OBJECT (book), "sq_pending"); + GList *i; + + for (i=pending; i != NULL; i = g_list_next (i)) { + if (i->data == info) { + pending = g_list_remove_link (pending, i); + g_list_free_1 (i); + break; + } + } + gtk_object_set_data (GTK_OBJECT (book), "sq_pending", pending); +} + +static guint +book_issue_tag (EBook *book) +{ + gpointer ptr = gtk_object_get_data (GTK_OBJECT (book), "sq_tag"); + guint tag = GPOINTER_TO_UINT (ptr); + if (tag == 0) + tag = 1; + gtk_object_set_data (GTK_OBJECT (book), "sq_tag", GUINT_TO_POINTER (tag+1)); + return tag; +} + +#ifdef USE_WORKAROUND +static GList *WORKAROUND_sq_queue = NULL; +static gboolean WORKAROUND_running_query = FALSE; +#endif + +static SimpleQueryInfo * +simple_query_new (EBook *book, const char *query, EBookSimpleQueryCallback cb, gpointer closure) +{ + SimpleQueryInfo *sq = g_new0 (SimpleQueryInfo, 1); + + sq->tag = book_issue_tag (book); + sq->book = book; + gtk_object_ref (GTK_OBJECT (book)); + sq->query = g_strdup_printf (query); + sq->cb = cb; + sq->closure = closure; + + /* Automatically add ourselves to the EBook's pending list. */ + book_add_simple_query (book, sq); + +#ifdef USE_WORKAROUND + /* Add ourselves to the queue. */ + WORKAROUND_sq_queue = g_list_append (WORKAROUND_sq_queue, sq); +#endif + + return sq; +} + +static void +simple_query_free (SimpleQueryInfo *sq) +{ + GList *i; + + /* Remove ourselves from the EBook's pending list. */ + book_remove_simple_query (sq->book, sq); + +#ifdef USE_WORKAROUND + /* If we are still in the queue, remove ourselves. */ + for (i = WORKAROUND_sq_queue; i != NULL; i = g_list_next (i)) { + if (i->data == sq) { + WORKAROUND_sq_queue = g_list_remove_link (WORKAROUND_sq_queue, i); + g_list_free_1 (i); + break; + } + } +#endif + + g_free (sq->query); + + if (sq->add_tag) + gtk_signal_disconnect (GTK_OBJECT (sq->view), sq->add_tag); + if (sq->seq_complete_tag) + gtk_signal_disconnect (GTK_OBJECT (sq->view), sq->seq_complete_tag); + +#ifdef USE_WORKAROUND + if (sq->view) + WORKAROUND_running_query = FALSE; +#endif + + if (sq->view) + gtk_object_unref (GTK_OBJECT (sq->view)); + + if (sq->book) + gtk_object_unref (GTK_OBJECT (sq->book)); + + g_list_foreach (sq->cards, (GFunc) gtk_object_unref, NULL); + g_list_free (sq->cards); + + g_free (sq); +} + +static void +simple_query_card_added_cb (EBookView *view, const GList *cards, gpointer closure) +{ + SimpleQueryInfo *sq = closure; + + sq->cards = g_list_concat (sq->cards, g_list_copy ((GList *) cards)); + g_list_foreach ((GList *) cards, (GFunc) gtk_object_ref, NULL); +} + +static void +simple_query_sequence_complete_cb (EBookView *view, gpointer closure) +{ + SimpleQueryInfo *sq = closure; + + sq->cb (sq->book, E_BOOK_SIMPLE_QUERY_STATUS_SUCCESS, sq->cards, sq->closure); + simple_query_free (sq); +} + +static void +simple_query_book_view_cb (EBook *book, EBookStatus status, EBookView *book_view, gpointer closure) +{ + SimpleQueryInfo *sq = closure; + + if (status != E_BOOK_STATUS_SUCCESS) { + sq->cb (sq->book, E_BOOK_SIMPLE_QUERY_STATUS_OTHER_ERROR, NULL, sq->closure); + simple_query_free (sq); + return; + } + + sq->view = book_view; + gtk_object_ref (GTK_OBJECT (book_view)); + + sq->add_tag = gtk_signal_connect (GTK_OBJECT (sq->view), + "card_added", + GTK_SIGNAL_FUNC (simple_query_card_added_cb), + sq); + sq->seq_complete_tag = gtk_signal_connect (GTK_OBJECT (sq->view), + "sequence_complete", + GTK_SIGNAL_FUNC (simple_query_sequence_complete_cb), + sq); +} + +#ifdef USE_WORKAROUND +static gint +WORKAROUND_try_queue (gpointer foo) +{ + if (WORKAROUND_sq_queue) { + SimpleQueryInfo *sq; + GList *i; + + if (WORKAROUND_running_query) + return TRUE; + + WORKAROUND_running_query = TRUE; + sq = WORKAROUND_sq_queue->data; + + i = WORKAROUND_sq_queue; + WORKAROUND_sq_queue = g_list_remove_link (WORKAROUND_sq_queue, WORKAROUND_sq_queue); + g_list_free_1 (i); + + e_book_get_book_view (sq->book, sq->query, simple_query_book_view_cb, sq); + } + + return FALSE; +} +#endif + +guint +e_book_simple_query (EBook *book, const char *query, EBookSimpleQueryCallback cb, gpointer closure) +{ + SimpleQueryInfo *sq; + + g_return_val_if_fail (book && E_IS_BOOK (book), 0); + g_return_val_if_fail (query, 0); + g_return_val_if_fail (cb, 0); + + sq = simple_query_new (book, query, cb, closure); +#ifdef USE_WORKAROUND + gtk_timeout_add (50, WORKAROUND_try_queue, NULL); +#else + e_book_get_book_view (book, query, simple_query_book_view_cb, sq); +#endif + + return sq->tag; +} + +void +e_book_simple_query_cancel (EBook *book, guint tag) +{ + SimpleQueryInfo *sq; + + g_return_if_fail (book && E_IS_BOOK (book)); + + sq = book_lookup_simple_query (book, tag); + + if (sq) { + sq->cb (sq->book, E_BOOK_SIMPLE_QUERY_STATUS_CANCELLED, NULL, sq->closure); + simple_query_free (sq); + } else { + g_warning ("Simple query tag %d is unknown", tag); + } +} + +/* + * + * Specialized Queries + * + */ + +typedef struct _NameEmailQueryInfo NameEmailQueryInfo; +struct _NameEmailQueryInfo { + gchar *name; + gchar *email; + EBookSimpleQueryCallback cb; + gpointer closure; +}; + +static void +name_email_query_info_free (NameEmailQueryInfo *info) +{ + if (info) { + g_free (info->name); + g_free (info->email); + g_free (info); + } +} + +static void +name_and_email_cb (EBook *book, EBookSimpleQueryStatus status, const GList *cards, gpointer closure) +{ + NameEmailQueryInfo *info = closure; + GList *filtered_cards = NULL; + + while (cards) { + ECard *card = E_CARD (cards->data); + if ((info->name == NULL || e_card_name_match_string (card->name, info->name)) + && (info->email == NULL || e_card_email_match_string (card, info->email))) + filtered_cards = g_list_append (filtered_cards, card); + cards = g_list_next (cards); + } + + info->cb (book, status, filtered_cards, info->closure); + + g_list_free (filtered_cards); + + name_email_query_info_free (info); +} + +guint +e_book_name_and_email_query (EBook *book, + const gchar *name, + const gchar *email, + EBookSimpleQueryCallback cb, + gpointer closure) +{ + NameEmailQueryInfo *info; + gchar *email_query=NULL, *name_query=NULL, *query; + guint tag; + + g_return_val_if_fail (book && E_IS_BOOK (book), 0); + g_return_val_if_fail (cb != NULL, 0); + + if (name && !*name) + name = NULL; + if (email && !*email) + email = NULL; + + if (name == NULL && email == NULL) + return 0; + + /* Build our e-mail query. + * We only query against the username part of the address, to avoid not matching + * fred@foo.com and fred@mail.foo.com. While their may be namespace collisions + * in the usernames of everyone out there, it shouldn't be that bad. (Famous last words.) + */ + if (email) { + const gchar *t=email; + while (*t && *t != '@') + ++t; + if (*t == '@') { + email_query = g_strdup_printf ("(beginswith \"email\" \"%.*s@\")", t-email, email); + + } else { + email_query = g_strdup_printf ("(beginswith \"email\" \"%s\")", email); + } + } + + /* Build our name query. + * We only do name-query stuff if we don't have an e-mail address. Our basic assumption + * is that the username part of the email is good enough to keep the amount of stuff returned + * in the query relatively small. + */ + if (name && !email) { + gchar *name_cpy = g_strdup (name), *qjoined; + gchar **namev; + gint i, count=0; + + g_strstrip (name_cpy); + namev = g_strsplit (" ", name_cpy, 0); + for (i=0; namev[i]; ++i) { + if (*namev[i]) { + namev[i] = g_strdup_printf ("(contains \"file_as\" \"%s\")", namev[i]); + ++count; + } + } + + qjoined = g_strjoinv (" ", namev); + if (count > 1) { + name_query = g_strdup_printf ("(or %s)", qjoined); + } else { + name_query = qjoined; + qjoined = NULL; + } + + + g_free (name_cpy); + for (i=0; namev[i]; ++i) + if (*namev[i]) + g_free (namev[i]); + g_free (namev); + g_free (qjoined); + } + + /* Assemble our e-mail & name queries */ + if (email_query && name_query) { + query = g_strdup_printf ("(and %s %s)", email_query, name_query); + } else if (email_query) { + query = email_query; + email_query = NULL; + } else if (name_query) { + query = name_query; + name_query = NULL; + } else + return 0; + + g_message ("query: %s", query); + + info = g_new0 (NameEmailQueryInfo, 1); + info->name = g_strdup (name); + info->email = g_strdup (email); + info->cb = cb; + info->closure = closure; + + tag = e_book_simple_query (book, query, name_and_email_cb, info); + + g_free (email_query); + g_free (name_query); + g_free (query); + + return tag; +} diff --git a/addressbook/backend/ebook/e-book-util.h b/addressbook/backend/ebook/e-book-util.h new file mode 100644 index 0000000000..cf0103bfea --- /dev/null +++ b/addressbook/backend/ebook/e-book-util.h @@ -0,0 +1,64 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * e-book-util.h + * + * Copyright (C) 2001 Ximian, Inc. + * + * Developed by Jon Trowbridge <trow@ximian.com> + */ + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA. + */ + +#ifndef __E_BOOK_UTIL_H__ +#define __E_BOOK_UTIL_H__ + +#include <libgnome/gnome-defs.h> +#include "e-book.h" + +BEGIN_GNOME_DECLS + +/* Callbacks for asynchronous functions. */ +typedef void (*EBookSimpleQueryCallback) (EBook *book, EBookSimpleQueryStatus status, const GList *cards, gpointer closure); + +gboolean e_book_load_local_address_book (EBook *book, + EBookCallback open_response, + gpointer closure); + +/* Simple Query Interface. */ + +guint e_book_simple_query (EBook *book, + const char *query, + EBookSimpleQueryCallback cb, + gpointer closure); +void e_book_simple_query_cancel (EBook *book, + guint tag); + +/* Specialized Name/Email Queries */ + +guint e_book_name_and_email_query (EBook *book, + const char *name, + const char *email, + EBookSimpleQueryCallback cb, + gpointer closure); + +END_GNOME_DECLS + + +#endif /* __E_BOOK_UTIL_H__ */ + diff --git a/addressbook/backend/ebook/e-book-view-listener.c b/addressbook/backend/ebook/e-book-view-listener.c index 4ad674ccd8..8433d1cc29 100644 --- a/addressbook/backend/ebook/e-book-view-listener.c +++ b/addressbook/backend/ebook/e-book-view-listener.c @@ -33,12 +33,31 @@ struct _EBookViewListenerPrivate { static gboolean e_book_view_listener_check_queue (EBookViewListener *listener) { + static gint thrash = 0; + gint queue_len; + + queue_len = g_list_length (listener->priv->response_queue); + bonobo_object_ref (BONOBO_OBJECT (listener)); if (listener->priv->response_queue != NULL) { gtk_signal_emit (GTK_OBJECT (listener), e_book_view_listener_signals [RESPONSES_QUEUED]); } + /* This means we didn't make any progress in dealing with what is on our + response queue. */ + if (queue_len == g_list_length (listener->priv->response_queue)) + ++thrash; + else + thrash = 0; + + if (thrash > 20) { + g_error ("e_book_view_listener_check_queue thrashing!"); + thrash = 0; + listener->priv->idle_id = 0; + return FALSE; + } + if (listener->priv->response_queue == NULL) { listener->priv->idle_id = 0; bonobo_object_unref (BONOBO_OBJECT (listener)); diff --git a/addressbook/backend/ebook/e-book.c b/addressbook/backend/ebook/e-book.c index e0b62c48d1..2a02b78fbf 100644 --- a/addressbook/backend/ebook/e-book.c +++ b/addressbook/backend/ebook/e-book.c @@ -12,7 +12,6 @@ #include <gtk/gtksignal.h> #include <gtk/gtkmarshal.h> #include <libgnome/gnome-defs.h> -#include <libgnome/gnome-util.h> #include <liboaf/liboaf.h> #include "addressbook.h" @@ -517,28 +516,6 @@ e_book_unload_uri (EBook *book) book->priv->load_state = URINotLoaded; } -gboolean -e_book_load_local_address_book (EBook *book, EBookCallback open_response, gpointer closure) -{ - gchar *filename; - gchar *uri; - gboolean rv; - - g_return_val_if_fail (book != NULL, FALSE); - g_return_val_if_fail (E_IS_BOOK (book), FALSE); - g_return_val_if_fail (open_response != NULL, FALSE); - - filename = gnome_util_prepend_user_home ("evolution/local/Contacts/addressbook.db"); - uri = g_strdup_printf ("file://%s", filename); - - rv = e_book_load_uri (book, uri, open_response, closure); - - g_free (filename); - g_free (uri); - - return rv; -} - char * e_book_get_static_capabilities (EBook *book) { @@ -1119,157 +1096,6 @@ e_book_get_changes (EBook *book, return TRUE; } -/* - * - * Simple Query Stuff - * - */ - -typedef struct _SimpleQueryInfo SimpleQueryInfo; -struct _SimpleQueryInfo { - guint tag; - EBook *book; - gchar *query; - EBookSimpleQueryCallback cb; - gpointer closure; - EBookView *view; - guint add_tag; - guint seq_complete_tag; - GList *cards; -}; - -static SimpleQueryInfo * -simple_query_new (EBook *book, char *query, EBookSimpleQueryCallback cb, gpointer closure) -{ - SimpleQueryInfo *sq = g_new0 (SimpleQueryInfo, 1); - - sq->tag = ++book->priv->sq_tag; - sq->book = book; - gtk_object_ref (GTK_OBJECT (book)); - sq->query = g_strdup_printf (query); - sq->cb = cb; - sq->closure = closure; - - /* Automatically add ourselves to the EBook's pending list. */ - book->priv->sq_pending = g_list_prepend (book->priv->sq_pending, sq); - - return sq; -} - -static void -simple_query_free (SimpleQueryInfo *sq) -{ - GList *i; - gboolean found = FALSE; - - /* Find & remove ourselves from the EBook's pending list. */ - for (i = sq->book->priv->sq_pending; i != NULL; i = g_list_next (i)) { - if (i->data == sq) { - sq->book->priv->sq_pending = g_list_remove_link (sq->book->priv->sq_pending, i); - g_list_free_1 (i); - i = NULL; - found = TRUE; - } else - i = g_list_next (i); - } - - g_assert (found); - - g_free (sq->query); - - if (sq->add_tag) - gtk_signal_disconnect (GTK_OBJECT (sq->view), sq->add_tag); - if (sq->seq_complete_tag) - gtk_signal_disconnect (GTK_OBJECT (sq->view), sq->seq_complete_tag); - - if (sq->view) - gtk_object_unref (GTK_OBJECT (sq->view)); - - if (sq->book) - gtk_object_unref (GTK_OBJECT (sq->book)); - - g_list_foreach (sq->cards, (GFunc) gtk_object_unref, NULL); - g_list_free (sq->cards); - - g_free (sq); -} - -static void -simple_query_card_added_cb (EBookView *view, const GList *cards, gpointer closure) -{ - SimpleQueryInfo *sq = closure; - - sq->cards = g_list_concat (sq->cards, g_list_copy ((GList *) cards)); - g_list_foreach ((GList *) cards, (GFunc) gtk_object_ref, NULL); -} - -static void -simple_query_sequence_complete_cb (EBookView *view, gpointer closure) -{ - SimpleQueryInfo *sq = closure; - - sq->cb (sq->book, E_BOOK_SIMPLE_QUERY_STATUS_SUCCESS, sq->cards, sq->closure); - simple_query_free (sq); -} - -static void -simple_query_book_view_cb (EBook *book, EBookStatus status, EBookView *book_view, gpointer closure) -{ - SimpleQueryInfo *sq = closure; - - if (status != E_BOOK_STATUS_SUCCESS) { - sq->cb (sq->book, E_BOOK_SIMPLE_QUERY_STATUS_OTHER_ERROR, NULL, sq->closure); - simple_query_free (sq); - return; - } - - sq->view = book_view; - gtk_object_ref (GTK_OBJECT (book_view)); - - sq->add_tag = gtk_signal_connect (GTK_OBJECT (sq->view), - "card_added", - GTK_SIGNAL_FUNC (simple_query_card_added_cb), - sq); - sq->seq_complete_tag = gtk_signal_connect (GTK_OBJECT (sq->view), - "sequence_complete", - GTK_SIGNAL_FUNC (simple_query_sequence_complete_cb), - sq); -} - -guint -e_book_simple_query (EBook *book, char *query, EBookSimpleQueryCallback cb, gpointer closure) -{ - SimpleQueryInfo *sq; - - g_return_val_if_fail (book && E_IS_BOOK (book), 0); - g_return_val_if_fail (query, 0); - g_return_val_if_fail (cb, 0); - - sq = simple_query_new (book, query, cb, closure); - e_book_get_book_view (book, query, simple_query_book_view_cb, sq); - - return sq->tag; -} - -void -e_book_simple_query_cancel (EBook *book, guint tag) -{ - GList *i; - - g_return_if_fail (book && E_IS_BOOK (book)); - - for (i=book->priv->sq_pending; i != NULL; i=g_list_next (i)) { - SimpleQueryInfo *sq = i->data; - - if (sq->tag == tag) { - sq->cb (sq->book, E_BOOK_SIMPLE_QUERY_STATUS_CANCELLED, NULL, sq->closure); - simple_query_free (sq); - return; - } - } - g_warning ("Simple query tag %d is unknown", tag); -} - /** * e_book_get_name: */ diff --git a/addressbook/backend/ebook/e-book.h b/addressbook/backend/ebook/e-book.h index 2521edb24f..0713bc6bdb 100644 --- a/addressbook/backend/ebook/e-book.h +++ b/addressbook/backend/ebook/e-book.h @@ -50,9 +50,6 @@ typedef void (*EBookCursorCallback) (EBook *book, EBookStatus status, ECardCurso typedef void (*EBookBookViewCallback) (EBook *book, EBookStatus status, EBookView *book_view, gpointer closure); typedef void (*EBookFieldsCallback) (EBook *book, EBookStatus status, EList *fields, gpointer closure); -typedef void (*EBookSimpleQueryCallback) (EBook *book, EBookSimpleQueryStatus status, const GList *cards, gpointer closure); - - /* Creating a new addressbook. */ EBook *e_book_new (void); @@ -62,10 +59,6 @@ gboolean e_book_load_uri (EBook *book, gpointer closure); void e_book_unload_uri (EBook *book); -gboolean e_book_load_local_address_book (EBook *book, - EBookCallback open_response, - gpointer closure); - char *e_book_get_static_capabilities (EBook *book); gboolean e_book_get_supported_fields (EBook *book, @@ -133,15 +126,6 @@ gboolean e_book_get_changes (EBook *book, EBookBookViewCallback cb, gpointer closure); -/* Simple Query Interface. */ - -guint e_book_simple_query (EBook *book, - char *query, - EBookSimpleQueryCallback cb, - gpointer closure); -void e_book_simple_query_cancel (EBook *book, - guint tag); - /* Getting the name of the repository. */ char *e_book_get_name (EBook *book); diff --git a/addressbook/backend/ebook/e-card.c b/addressbook/backend/ebook/e-card.c index 12877d3646..8c1f7b1905 100644 --- a/addressbook/backend/ebook/e-card.c +++ b/addressbook/backend/ebook/e-card.c @@ -9,6 +9,7 @@ */ #include <config.h> +#include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -1348,6 +1349,154 @@ e_card_name_from_string(const char *full_name) return name; } + +/* This *so* doesn't belong here... at least not implemented in a + sucky way like this. But by getting it in here now, I can fix it + up w/o adding a new feature when we are in feature freeze. :-) */ + +/* This is very Anglocentric. Maybe it should be by locale? */ +static gchar *name_synonyms[][2] = { + { "Jon", "John" }, /* Ah, the hacker's perogative */ + { "Jon", "Jonathan" }, + { "Daniel", "Dan" }, + { "Joseph", "Joe" }, + { "Robert", "Rob" }, + { "Robert", "Bob" }, + { "Richard", "Rich" }, + { "Richard", "Dick" }, + { "William", "Will" }, + { "William", "Bill" }, + { "Anthony", "Tony" }, + { "Steven", "Steve" }, + { "Michael", "Mike" }, + { "Douglas", "Doug" }, + { "Sidney", "Sid" }, + { "Eric", "Erik" }, + { "Chris", "Christopher" }, + { "Chris", "Christine" }, + { "Chris", "Christy" }, + { "Elizabeth", "Liz" }, + { "Jeff", "Geoff" }, + { "Jeff", "Jeffrey" }, + { "Jeff", "Geoffrey" }, + { "Jim", "James" }, + { "Abigal", "Abby" }, + { "Amanda", "Amy" }, + { "Amanda", "Manda" }, + { "Di", "Diana" }, + { "Di", "Diane" }, + { "Maxine", "Max" }, + { "Rebecca", "Becca" }, + { "Rebecca", "Becky" }, + { "Jennifer", "Jen" }, + { "Jennifer", "Jenny" }, + /* We could go on and on... */ + { NULL, NULL } +}; + +static gboolean +name_fragment_match (const gchar *a, const gchar *b) +{ + gint i; + gboolean nickname_match = FALSE; + + if (!g_strcasecmp (a, b)) + return TRUE; + + /* Check for nicknames. Yes, the linear search blows. */ + for (i=0; name_synonyms[i][0]; ++i) { + if (!g_strcasecmp (name_synonyms[i][1], a)) { + a = name_synonyms[i][0]; + nickname_match = TRUE; + break; + } + } + + for (i=0; name_synonyms[i][0]; ++i) { + if (!g_strcasecmp (name_synonyms[i][1], b)) { + b = name_synonyms[i][0]; + nickname_match = TRUE; + break; + } + } + + return nickname_match && !g_strcasecmp (a, b); +} + +gboolean +e_card_name_match_string (const ECardName *name, const gchar *str) +{ + gchar *cpy, *name_str; + gchar **strv, **namev; + gint i, j, match_count; + gboolean matched = FALSE; + + g_return_val_if_fail (name != NULL, FALSE); + g_return_val_if_fail (str != NULL, FALSE); + + cpy = g_strdup (str); + strv = g_strsplit (cpy, " ", 0); + for (i=0; strv[i]; ++i) + g_strstrip (strv[i]); + + name_str = e_card_name_to_string (name); + namev = g_strsplit (name_str, " ", 0); + for (i=0; namev[i]; ++i) + g_strstrip (namev[i]); + + match_count = 0; + i = j = 0; + while (strv[i] && namev[j]) { + gint k1, k2; + + for (k1=0; strv[i+k1]; ++k1) { + if (name_fragment_match (strv[i+k1], namev[j])) + break; + } + + for (k2=0; namev[j+k2]; ++k2) { + if (name_fragment_match (strv[i], namev[j+k2])) + break; + } + + if (strv[i+k1] == NULL && namev[j+k2] == NULL) { + matched = FALSE; + goto cleanup_and_return; + } + + ++match_count; + + if (k1 < k2) { + i += k1+1; + ++j; + } else if (k2 < k1) { + ++i; + j += k2+1; + } else if (k1 == k2) { + i += k1+1; + j += k2+1; + } + } + + /* This rule could be made more precise. + As it is, it will say that "Joe Smith" will match the name + "Joe Allen Smith" (which is good), but "de Icaza" will match + either "Miguel de Icaza" as well as Miguel's shiftless + brother "Roger de Icaza". In this sort of a case, the match + threshold should go up to 3. */ + if (match_count >= 2) + matched = TRUE; + + + cleanup_and_return: + g_free (strv); + g_free (cpy); + g_free (namev); + g_free (name_str); + + return matched; +} + ECardArbitrary * e_card_arbitrary_new(void) { @@ -1382,6 +1531,59 @@ e_card_arbitrary_free(ECardArbitrary *arbitrary) g_free(arbitrary); } +/* EMail matching */ +static gboolean +e_card_email_match_single_string (const gchar *a, const gchar *b) +{ + const gchar *xa = NULL, *xb = NULL; + gboolean match = TRUE; + + for (xa=a; *xa && *xa != '@'; ++xa); + for (xb=b; *xb && *xb != '@'; ++xb); + + if (xa-a != xb-b || *xa != *xb || g_strncasecmp (a, b, xa-a)) + return FALSE; + + if (*xa == '\0') + return TRUE; + + /* Find the end of the string, then walk through backwards comparing. + This is so that we'll match joe@foobar.com and joe@mail.foobar.com. + */ + while (*xa) + ++xa; + while (*xb) + ++xb; + + while (match && *xa != '@' && *xb != '@') { + match = (*xa == *xb); + --xa; + --xb; + } + + match = match && ((*xa == *xb) || (*xa == '.') || (*xb == '.')); + + return match; +} + +gboolean +e_card_email_match_string (const ECard *card, const gchar *str) +{ + EIterator *iter; + + g_return_val_if_fail (card && E_IS_CARD (card), FALSE); + g_return_val_if_fail (str != NULL, FALSE); + + iter = e_list_get_iterator (card->email); + for (e_iterator_reset (iter); e_iterator_is_valid (iter); e_iterator_next (iter)) { + if (e_card_email_match_single_string (e_iterator_get (iter), str)) + return TRUE; + } + gtk_object_unref (GTK_OBJECT (iter)); + + return FALSE; +} + /* * ECard lifecycle management and vCard loading/saving. */ diff --git a/addressbook/backend/ebook/e-card.h b/addressbook/backend/ebook/e-card.h index 81473a28ed..eba8c07140 100644 --- a/addressbook/backend/ebook/e-card.h +++ b/addressbook/backend/ebook/e-card.h @@ -140,12 +140,18 @@ ECardName *e_card_name_copy (const ECardName void e_card_name_free (ECardName *name); char *e_card_name_to_string (const ECardName *name); ECardName *e_card_name_from_string (const char *full_name); +gboolean e_card_name_match_string (const ECardName *name, + const gchar *str); /* ECardArbitrary manipulation */ ECardArbitrary *e_card_arbitrary_new (void); ECardArbitrary *e_card_arbitrary_copy (const ECardArbitrary *arbitrary); void e_card_arbitrary_free (ECardArbitrary *arbitrary); +/* ECard email manipulation */ +gboolean e_card_email_match_string (const ECard *card, + const gchar *str); + /* Specialized functionality */ GList *e_card_load_cards_from_file (const char *filename); |