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
This commit is contained in:
Jon Trowbridge
2001-03-16 08:16:29 +00:00
committed by Jon Trowbridge
parent 403205b15e
commit 57de6972c8
18 changed files with 1027 additions and 223 deletions

View File

@ -1,3 +1,38 @@
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 Dan Winship <danw@ximian.com>
* gui/widgets/e-minicard-label.c (e_minicard_label_set_arg):

View File

@ -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 \

View File

@ -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;
}

View File

@ -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__ */

View File

@ -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));

View File

@ -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:
*/

View File

@ -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);

View File

@ -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.
*/

View File

@ -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);

View File

@ -475,6 +475,7 @@ pas_backend_file_search (PASBackendFile *bf,
{
int db_error = 0;
GList *cards = NULL;
gint card_count = 0;
DB *db = bf->priv->file_db;
DBT id_dbt, vcard_dbt;
int i;
@ -518,6 +519,18 @@ pas_backend_file_search (PASBackendFile *bf,
/* check if the vcard matches the search sexp */
if (vcard_matches_search (view, vcard_string)) {
cards = g_list_append (cards, g_strdup (vcard_string));
++card_count;
#if 0
/* If we've accumulated a number of matches, pass them off to the client. */
if (card_count > 50) {
pas_book_view_notify_add (view->book_view, cards);
/* Clean up the handed-off data. */
g_list_foreach (cards, (GFunc)g_free, NULL);
g_list_free (cards);
cards = NULL;
card_count = 0;
}
#endif
}
}
@ -528,7 +541,9 @@ pas_backend_file_search (PASBackendFile *bf,
g_warning ("pas_backend_file_search: error building list\n");
}
pas_book_view_notify_add (view->book_view, cards);
// if (card_count)
pas_book_view_notify_add (view->book_view, cards);
pas_book_view_notify_complete (view->book_view);
/*

View File

@ -154,7 +154,7 @@ pas_book_view_notify_status_message (PASBookView *book_view,
book_view->priv->listener, message, &ev);
if (ev._major != CORBA_NO_EXCEPTION) {
g_warning ("pas_book_view_notify_complete: Exception signaling BookViewListener!\n");
g_warning ("pas_book_view_notify_status_message: Exception signaling BookViewListener!\n");
}
CORBA_exception_free (&ev);
@ -248,7 +248,6 @@ pas_book_view_destroy (GtkObject *object)
GNOME_Evolution_Addressbook_BookViewListener_unref (book_view->priv->listener, &ev);
if (ev._major != CORBA_NO_EXCEPTION) {
g_warning("Unable to unref listener object in pas-book-view.c\n");
CORBA_exception_free (&ev);
return;
@ -256,7 +255,6 @@ pas_book_view_destroy (GtkObject *object)
CORBA_Object_release (book_view->priv->listener, &ev);
if (ev._major != CORBA_NO_EXCEPTION) {
g_warning("Unable to release listener object in pas-book-view.c\n");
CORBA_exception_free (&ev);
return;

View File

@ -29,6 +29,7 @@
#include <ctype.h>
#include <gnome.h>
#include <addressbook/backend/ebook/e-book.h>
#include <addressbook/backend/ebook/e-book-util.h>
#include <addressbook/backend/ebook/e-card.h>
#include "e-contact-editor.h"
#include "e-contact-quick-add.h"

View File

@ -70,6 +70,8 @@ main (int argc, char **argv)
unicode_init();
g_log_set_always_fatal (G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING);
bonobo_main ();
return 0;

View File

@ -26,6 +26,7 @@
*/
#include <config.h>
#include <ctype.h>
#include <bonobo/bonobo-control.h>
#include <bonobo/bonobo-property-bag.h>
#include <bonobo/bonobo-generic-factory.h>
@ -38,9 +39,14 @@ static void e_address_widget_destroy (GtkObject *obj);
static gint e_address_widget_button_press_handler (GtkWidget *w, GdkEventButton *ev);
static void e_address_widget_popup (EAddressWidget *, GdkEventButton *ev);
static void e_address_widget_schedule_query (EAddressWidget *);
static GtkObjectClass *parent_class;
static EBook *common_book = NULL; /* sort of lame */
static gboolean doing_queries = FALSE;
static void
e_address_widget_class_init (EAddressWidgetClass *klass)
{
@ -67,8 +73,12 @@ e_address_widget_destroy (GtkObject *obj)
g_free (addr->name);
g_free (addr->email);
if (addr->card)
gtk_object_unref (GTK_OBJECT (addr->card));
if (addr->query_tag)
e_book_simple_query_cancel (common_book, addr->query_tag);
if (addr->query_idle_tag)
gtk_idle_remove (addr->query_idle_tag);
}
static gint
@ -123,10 +133,21 @@ e_address_widget_refresh (EAddressWidget *addr)
g_return_if_fail (addr && E_IS_ADDRESS_WIDGET (addr));
have_name = addr->name && *addr->name;
have_email = addr->email && *addr->email;
have_email = addr->email && *addr->email && (addr->card == NULL || !addr->known_email);
gtk_label_set_text (GTK_LABEL (addr->name_widget), have_name ? addr->name : "");
gtk_widget_visible (addr->name_widget, have_name);
if (addr->card) {
gint i, N = strlen (addr->name);
gchar *pattern = g_malloc (N+1);
for (i=0; i<N; ++i)
pattern[i] = '_';
pattern[i] = '\0';
gtk_label_set_pattern (GTK_LABEL (addr->name_widget), pattern);
g_free (pattern);
} else {
gtk_label_set_pattern (GTK_LABEL (addr->name_widget), "");
}
if (have_email) {
str = g_strdup_printf (have_name ? "<%s>" : "%s", addr->email);
@ -140,7 +161,8 @@ e_address_widget_refresh (EAddressWidget *addr)
gtk_widget_visible (addr->spacer, have_name && have_email);
/* Launch a query to find the appropriate card, if necessary. */
addr->querying = TRUE;
if (addr->card == NULL)
e_address_widget_schedule_query (addr);
}
void
@ -207,6 +229,92 @@ e_address_widget_new (void)
return GTK_WIDGET (addr);
}
/*
*
* Cardification
*
*/
static void
e_address_widget_cardify (EAddressWidget *addr, ECard *card, gboolean known_email)
{
if (addr->card != card || addr->known_email != known_email) {
if (addr->card != card) {
if (addr->card)
gtk_object_unref (GTK_OBJECT (addr->card));
addr->card = card;
gtk_object_ref (GTK_OBJECT (addr->card));
}
addr->known_email = known_email;
if (!(addr->name && *addr->name)) {
gchar *s = e_card_name_to_string (card->name);
e_address_widget_set_name (addr, s);
g_free (s);
}
e_address_widget_refresh (addr);
}
}
static void
query_results_cb (EBook *book, EBookSimpleQueryStatus status, const GList *cards, gpointer user_data)
{
EAddressWidget *addr = user_data;
if (g_list_length ((GList *) cards) == 1) {
ECard *card = E_CARD (cards->data);
e_address_widget_cardify (addr, card, TRUE);
}
addr->query_tag = 0;
}
static void
e_address_widget_do_query (EAddressWidget *addr)
{
e_book_name_and_email_query (common_book, addr->name, addr->email, query_results_cb, addr);
}
static void
book_ready_cb (EBook *book, EBookStatus status, gpointer user_data)
{
EAddressWidget *addr = E_ADDRESS_WIDGET (user_data);
if (common_book == NULL) {
common_book = book;
gtk_object_ref (GTK_OBJECT (common_book));
} else
gtk_object_unref (GTK_OBJECT (book));
e_address_widget_do_query (addr);
}
static gint
query_idle_fn (gpointer ptr)
{
EAddressWidget *addr = E_ADDRESS_WIDGET (ptr);
if (common_book) {
e_address_widget_do_query (addr);
} else {
e_book_load_local_address_book (e_book_new (), book_ready_cb, addr);
}
addr->query_idle_tag = 0;
return FALSE;
}
static void
e_address_widget_schedule_query (EAddressWidget *addr)
{
if (addr->query_idle_tag || !doing_queries)
return;
addr->query_idle_tag = gtk_idle_add (query_idle_fn, addr);
}
/*
*
* Popup Menu
@ -214,13 +322,84 @@ e_address_widget_new (void)
*/
#define ARBITRARY_UIINFO_LIMIT 64
static gint
popup_add_name_and_address (EAddressWidget *addr, GnomeUIInfo *uiinfo, gint i)
{
gboolean flag = FALSE;
if (addr->name && *addr->name) {
uiinfo[i].type = GNOME_APP_UI_ITEM;
uiinfo[i].label = addr->name;
++i;
flag = TRUE;
}
if (addr->email && *addr->email) {
uiinfo[i].type = GNOME_APP_UI_ITEM;
uiinfo[i].label = addr->email;
++i;
flag = TRUE;
}
if (flag) {
uiinfo[i].type = GNOME_APP_UI_SEPARATOR;
++i;
}
return i;
}
static void
flip_queries_flag_cb (GtkWidget *w, gpointer user_data)
{
doing_queries = !doing_queries;
}
static gint
popup_add_query_change (EAddressWidget *addr, GnomeUIInfo *uiinfo, gint i)
{
uiinfo[i].type = GNOME_APP_UI_SEPARATOR;
++i;
uiinfo[i].type = GNOME_APP_UI_ITEM;
uiinfo[i].label = doing_queries ? _("Disable Queries") : _("Enable Queries (Dangerous!)");
uiinfo[i].moreinfo = flip_queries_flag_cb;
++i;
return i;
}
static GtkWidget *
popup_menu_card (EAddressWidget *addr)
{
GnomeUIInfo uiinfo[ARBITRARY_UIINFO_LIMIT];
GtkWidget *pop;
gint i=0;
ECard *card = E_CARD (addr->card);
g_return_val_if_fail (card != NULL, NULL);
return NULL;
memset (uiinfo, 0, sizeof (uiinfo));
i = popup_add_name_and_address (addr, uiinfo, i);
uiinfo[i].type = GNOME_APP_UI_ITEM;
uiinfo[i].label = _("Edit Contact Info");
++i;
i = popup_add_query_change (addr, uiinfo, i);
uiinfo[i].type = GNOME_APP_UI_ENDOFINFO;
pop = gnome_popup_menu_new (uiinfo);
return pop;
}
static void
post_quick_add_cb (ECard *card, gpointer user_data)
{
e_address_widget_cardify (E_ADDRESS_WIDGET (user_data), card, TRUE);
}
static void
@ -228,7 +407,7 @@ add_contacts_cb (GtkWidget *w, gpointer user_data)
{
EAddressWidget *addr = E_ADDRESS_WIDGET (user_data);
e_contact_quick_add (addr->name, addr->email, NULL, NULL);
e_contact_quick_add (addr->name, addr->email, post_quick_add_cb, addr);
}
static GtkWidget *
@ -240,30 +419,17 @@ popup_menu_nocard (EAddressWidget *addr)
memset (uiinfo, 0, sizeof (uiinfo));
if (addr->name) {
uiinfo[i].type = GNOME_APP_UI_ITEM;
uiinfo[i].label = addr->name;
++i;
}
if (addr->email) {
uiinfo[i].type = GNOME_APP_UI_ITEM;
uiinfo[i].label = addr->email;
++i;
}
uiinfo[i].type = GNOME_APP_UI_SEPARATOR;
++i;
i = popup_add_name_and_address (addr, uiinfo, i);
uiinfo[i].type = GNOME_APP_UI_ITEM;
uiinfo[i].label = N_("Add to Contacts");
uiinfo[i].label = _("Add to Contacts");
uiinfo[i].moreinfo = add_contacts_cb;
++i;
i = popup_add_query_change (addr, uiinfo, i);
uiinfo[i].type = GNOME_APP_UI_ENDOFINFO;
pop = gnome_popup_menu_new (uiinfo);
return pop;
}
@ -409,3 +575,4 @@ e_address_widget_factory_init (void)
if (factory == NULL)
g_error ("I could not register an AddressWidget factory.");
}

View File

@ -30,6 +30,8 @@
#include <gtk/gtk.h>
#include <libgnome/gnome-defs.h>
#include <addressbook/backend/ebook/e-book.h>
#include <addressbook/backend/ebook/e-book-util.h>
#include <addressbook/backend/ebook/e-card.h>
BEGIN_GNOME_DECLS
@ -53,8 +55,11 @@ struct _EAddressWidget {
GtkWidget *email_widget;
GtkWidget *spacer;
gboolean querying;
guint query_idle_tag;
guint query_tag;
ECard *card;
gboolean known_email;
};
struct _EAddressWidgetClass {
@ -71,7 +76,7 @@ void e_address_widget_construct (EAddressWidget *);
GtkWidget *e_address_widget_new (void);
void e_address_widget_factory_init (void);
void e_address_widget_factory_init (void);
@ -79,3 +84,17 @@ END_GNOME_DECLS
#endif /* __E_ADDRESS_WIDGET_H__ */

View File

@ -29,6 +29,7 @@
#include <ctype.h>
#include <gnome.h>
#include <addressbook/backend/ebook/e-book.h>
#include <addressbook/backend/ebook/e-book-util.h>
#include <addressbook/backend/ebook/e-card.h>
#include "e-contact-editor.h"
#include "e-contact-quick-add.h"

View File

@ -1,3 +1,8 @@
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.
2001-02-27 Federico Mena Quintero <federico@ximian.com>
* wombat.c (init_corba): Use VERSION and the Wombat description

View File

@ -183,9 +183,13 @@ init_bonobo (int *argc, char **argv)
int
main (int argc, char **argv)
{
gboolean did_pas=FALSE, did_pcs=FALSE, did_config=FALSE;
bindtextdomain (PACKAGE, EVOLUTION_LOCALEDIR);
textdomain (PACKAGE);
g_message ("Starting wombat");
init_bonobo (&argc, argv);
setup_vfs (argc, argv);
@ -193,10 +197,20 @@ main (int argc, char **argv)
G_LOG_LEVEL_CRITICAL |
G_LOG_LEVEL_WARNING);*/
if (!(setup_pas (argc, argv)
&& setup_pcs (argc, argv)
&& setup_config (argc, argv))) {
g_message ("main(): could not initialize all of the Wombat services; terminating");
if (!( (did_pas = setup_pas (argc, argv))
&& (did_pcs = setup_pcs (argc, argv))
&& (did_config = setup_config (argc, argv)))) {
const gchar *failed = NULL;
if (!did_pas)
failed = "PAS";
else if (!did_pcs)
failed = "PCS";
else if (!did_config)
failed = "Config";
g_message ("main(): could not initialize Wombat service \"%s\"; terminating", failed);
if (pas_book_factory) {
bonobo_object_unref (BONOBO_OBJECT (pas_book_factory));