/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* An auto-completer for addresses from the address book.
*
* Author:
* Jon Trowbridge <trow@ximian.com>
*
* Copyright (C) 2001 Ximian, Inc.
*/
/*
* 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 "e-address-completion.h"
static void e_address_completion_class_init (EAddressCompletionClass *klass);
static void e_address_completion_init (EAddressCompletion *addr_comp);
static void e_address_completion_destroy (GtkObject *object);
static GtkObjectClass *parent_class;
typedef struct _BookQuery BookQuery;
struct _BookQuery {
EAddressCompletion *completion;
guint seq_no;
gchar *text;
EBookView *book_view;
guint card_added_id;
guint sequence_complete_id;
};
static BookQuery *book_query_new (EAddressCompletion *, const gchar *query_text);
static void book_query_attach_book_view (BookQuery *, EBookView *);
static void book_query_free (BookQuery *);
static gboolean book_query_has_expired (BookQuery *);
static double book_query_score_e_card (BookQuery *, ECard *);
static void book_query_book_view_cb (EBook *, EBookStatus, EBookView *, gpointer book_query);
static void book_query_card_added_cb (EBookView *, const GList *cards, gpointer book_query);
static void book_query_sequence_complete_cb (EBookView *, gpointer book_query);
GtkType
e_address_completion_get_type (void)
{
static GtkType address_completion_type = 0;
if (!address_completion_type) {
GtkTypeInfo address_completion_info = {
"EAddressCompletion",
sizeof (EAddressCompletion),
sizeof (EAddressCompletionClass),
(GtkClassInitFunc) e_address_completion_class_init,
(GtkObjectInitFunc) e_address_completion_init,
NULL, NULL,
(GtkClassInitFunc) NULL
};
address_completion_type = gtk_type_unique (e_completion_get_type (),
&address_completion_info);
}
return address_completion_type;
}
static void
e_address_completion_class_init (EAddressCompletionClass *klass)
{
GtkObjectClass *object_class = (GtkObjectClass *) klass;
parent_class = GTK_OBJECT_CLASS (gtk_type_class (e_completion_get_type ()));
object_class->destroy = e_address_completion_destroy;
}
static void
e_address_completion_init (EAddressCompletion *addr_comp)
{
}
static void
e_address_completion_destroy (GtkObject *object)
{
EAddressCompletion *addr_comp = E_ADDRESS_COMPLETION (object);
gtk_object_unref (GTK_OBJECT (addr_comp->book));
}
static BookQuery *
book_query_new (EAddressCompletion *comp, const gchar *text)
{
BookQuery *q = g_new0 (BookQuery, 1);
q->completion = comp;
gtk_object_ref (GTK_OBJECT (comp));
q->seq_no = comp->seq_no;
q->text = g_strdup (text);
return q;
}
static void
book_query_attach_book_view (BookQuery *q, EBookView *book_view)
{
g_return_if_fail (q);
g_assert (q->book_view == NULL);
q->book_view = book_view;
gtk_object_ref (GTK_OBJECT (book_view));
q->card_added_id = gtk_signal_connect (GTK_OBJECT (book_view),
"card_added",
GTK_SIGNAL_FUNC (book_query_card_added_cb),
q);
q->sequence_complete_id = gtk_signal_connect (GTK_OBJECT (book_view),
"sequence_complete",
GTK_SIGNAL_FUNC (book_query_sequence_complete_cb),
q);
}
static void
book_query_free (BookQuery *q)
{
if (q) {
if (q->book_view) {
gtk_signal_disconnect (GTK_OBJECT (q->book_view), q->card_added_id);
gtk_signal_disconnect (GTK_OBJECT (q->book_view), q->sequence_complete_id);
gtk_object_unref (GTK_OBJECT (q->book_view));
}
gtk_object_unref (GTK_OBJECT (q->completion));
g_free (q->text);
g_free (q);
}
}
static gboolean
book_query_has_expired (BookQuery *q)
{
g_return_val_if_fail (q != NULL, FALSE);
return q->seq_no != q->completion->seq_no;
}
static double
book_query_score_e_card (BookQuery *q, ECard *card)
{
gint len;
g_return_val_if_fail (q != NULL, -1);
g_return_val_if_fail (card != NULL && E_IS_CARD (card), -1);
len = strlen (q->text);
if (card->name->given && !g_strncasecmp (card->name->given, q->text, len))
return len;
if (card->name->additional && !g_strncasecmp (card->name->additional, q->text, len))
return len;
if (card->name->family && !g_strncasecmp (card->name->family, q->text, len))
return len;
return 0.5; /* Not good, but we'll leave them in anyway for now... */
}
static gchar*
book_query_card_text (ECard *card)
{
if (card->name) {
/* Sort of a lame hack. */
return g_strdup_printf ("%s %s%s%s",
card->name->given ? card->name->given : "",
card->name->additional ? card->name->additional : "",
card->name->additional ? " " : "",
card->name->family ? card->name->family : "");
} else if (card->fname) {
return g_strdup (card->fname);
} else if (card->file_as) {
return g_strdup (card->file_as);
}
return NULL;
}
static void
book_query_book_view_cb (EBook *book, EBookStatus status, EBookView *book_view, gpointer user_data)
{
BookQuery *q = (BookQuery *) user_data;
if (book_query_has_expired (q)) {
book_query_free (q);
return;
}
book_query_attach_book_view (q, book_view);
}
static void
book_query_card_added_cb (EBookView *book_view, const GList *cards, gpointer user_data)
{
BookQuery *q = (BookQuery *) user_data;
if (book_query_has_expired (q)) {
book_query_free (q);
return;
}
while (cards) {
ECard *card = (ECard *) cards->data;
double score;
if (card && (score = book_query_score_e_card (q, card)) >= 0) {
gchar *str = book_query_card_text (card);
if (str) {
gtk_object_ref (GTK_OBJECT (card));
e_completion_found_match_full (E_COMPLETION (q->completion), str, score,
card, (GtkDestroyNotify)gtk_object_unref);
}
}
cards = g_list_next (cards);
}
}
static void
book_query_sequence_complete_cb (EBookView *book_view, gpointer user_data)
{
BookQuery *q = (BookQuery *) user_data;
if (! book_query_has_expired (q)) {
e_completion_end_search (E_COMPLETION (q->completion));
}
book_query_free (q);
}
static void
e_address_completion_begin (ECompletion *comp, const gchar *text, gint pos, gint limit, gpointer user_data)
{
EAddressCompletion *addr_comp = E_ADDRESS_COMPLETION (comp);
BookQuery *q;
gchar *query;
++addr_comp->seq_no; /* Paranoia, in case completion_begin were to be called twice in a row
without an intervening completion_end. (Of course, this shouldn't
happen...) */
q = book_query_new (addr_comp, text);
query = g_strdup_printf ("(contains \"x-evolution-any-field\" \"%s\")", text);
e_book_get_book_view (addr_comp->book, query, book_query_book_view_cb, q);
g_free (query);
}
static void
e_address_completion_end (ECompletion *comp, gboolean finished, gpointer user_data)
{
EAddressCompletion *addr_comp = E_ADDRESS_COMPLETION (comp);
++addr_comp->seq_no;
}
void
e_address_completion_construct (EAddressCompletion *addr_comp, EBook *book)
{
g_return_if_fail (addr_comp != NULL);
g_return_if_fail (E_IS_ADDRESS_COMPLETION (addr_comp));
g_return_if_fail (book != NULL);
g_return_if_fail (E_IS_BOOK (book));
/* No switching books mid-stream. */
g_return_if_fail (addr_comp->book == NULL);
e_completion_construct (E_COMPLETION (addr_comp),
e_address_completion_begin,
e_address_completion_end,
NULL);
addr_comp->book = book;
gtk_object_ref (GTK_OBJECT (book));
}
ECompletion *
e_address_completion_new (EBook *book)
{
gpointer ptr;
g_return_val_if_fail (book != NULL, NULL);
g_return_val_if_fail (E_IS_BOOK (book), NULL);
ptr = gtk_type_new (e_address_completion_get_type ());
e_address_completion_construct (E_ADDRESS_COMPLETION (ptr), book);
return E_COMPLETION (ptr);
}