diff options
Diffstat (limited to 'addressbook')
18 files changed, 2301 insertions, 1100 deletions
diff --git a/addressbook/ChangeLog b/addressbook/ChangeLog index 847a9b1547..55e3ac814b 100644 --- a/addressbook/ChangeLog +++ b/addressbook/ChangeLog @@ -1,3 +1,69 @@ +2001-03-01 Jon Trowbridge <trow@ximian.com> + + * gui/component/select-names/e-select-names-table-model.c + (fill_in_info): Change for new EDestination/ESelectNamesModel API. + + * gui/component/select-names/e-select-names-manager.c + (e_select_names_manager_get_source): Added. A function for + looking up the ESelectNamesModel by id. (I didn't end up using + this function, but it might come in handy later.) + (e_select_names_manager_get_cards): #if 0/#endif out this + function. + (e_select_names_manager_create_entry): Modified to attach an + ESelectNamesCompletion to the entry we create. + (completion_handler): A post-completion handler for our EEntry, to + take the completion's extra data (an EDestination) and properly + stick it into our ESelectNamesModel. + + * gui/component/select-names/e-select-names.c + (real_add_address_cb): Changed to operate on EDestinations rather + than ECards and to use the new ESelectNamesModel API. This leads + to a rather nice code simplication. + (remove_address): Changed for new ESelectNamesModel API. + + * gui/component/select-names/e-select-names-bonobo.c + (entry_get_property_fn): Rather than just passing the entry's text + through the property bag, get the "address text" from the model. + This returns a nice, verbose string of addresses with names + expanded when the address is tied to an ECard (i.e. "Jon + Trowbridge <trow@ximian.com>"). + (impl_SelectNames_get_entry_for_section): Make the text property + read-only. + (entry_set_property_fn): ...and since it is read-only now, chop + out the setter code. + + * gui/component/select-names/e-select-names-text-model.h: + * gui/component/select-names/e-select-names-text-model.c: Again, + this code has been (pretty much) totally rewritten to convert all + text operations into changes on the ESelectNamesModel. This lets + us give the associated EEntry some (IMHO) nice semantics regarding + whitespace, etc. Includes object activation, so destinations tied + to ECards are underlined and can be double-clicked to bring up a + contact editor. + + * gui/component/select-names/e-select-names-model.h: + * gui/component/select-names/e-select-names-model.c: I've heavily + modified this object to both hide all implementation details + (which the old version exposed a bit too much for my peculiar + tastes) and to act as an EDestination container. The old code put + the text model operations here. I've moved them all to + ESelectNamesTextModel --- so the text model actions (insert, + delete, etc.) are all done through the API rather than operating on + ESelectNamesModel internals. + + * gui/component/select-names/e-select-names-completion.c: Added. A + fairly complicated object derived from ECompletion that searches + our local addressbook in various and sundry ways. + + * gui/component/select-names/e-select-names-completion.h: + + * backend/ebook/e-destination.h: + * backend/ebook/e-destination.c: Added. This object encapsulates + a place to sent an email to, which can either be just a address as + a string ("trow@ximian.com"), a fancier string ("Jon Trowbridge + <trow@ximian.com>"), or an ECard and a specific address within + that ECard. + 2001-03-01 Christopher James Lahey <clahey@ximian.com> * gui/widgets/e-minicard-view.c, gui/widgets/e-minicard-view.h, diff --git a/addressbook/backend/ebook/Makefile.am b/addressbook/backend/ebook/Makefile.am index 207a87256e..fbba0c98d0 100644 --- a/addressbook/backend/ebook/Makefile.am +++ b/addressbook/backend/ebook/Makefile.am @@ -39,19 +39,18 @@ lib_LTLIBRARIES = libebook.la libebook_la_SOURCES = \ $(CORBA_SOURCE) \ - e-address-completion.c \ e-book-listener.c \ e-book-view-listener.c \ e-book-view.c \ e-book.c \ e-card-cursor.c \ e-card-simple.c \ - e-card.c + e-card.c \ + e-destination.c libebookincludedir = $(includedir)/evolution/ebook libebookinclude_HEADERS = \ - e-address-completion.h \ e-book-listener.h \ e-book-types.h \ e-book-view-listener.h \ @@ -61,7 +60,8 @@ libebookinclude_HEADERS = \ e-card-pairs.h \ e-card-simple.h \ e-card-types.h \ - e-card.h + e-card.h \ + e-destination.h # diff --git a/addressbook/backend/ebook/e-address-completion.c b/addressbook/backend/ebook/e-address-completion.c deleted file mode 100644 index 398782e13e..0000000000 --- a/addressbook/backend/ebook/e-address-completion.c +++ /dev/null @@ -1,315 +0,0 @@ -/* -*- 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); -} diff --git a/addressbook/backend/ebook/e-address-completion.h b/addressbook/backend/ebook/e-address-completion.h deleted file mode 100644 index d0fca50cda..0000000000 --- a/addressbook/backend/ebook/e-address-completion.h +++ /dev/null @@ -1,65 +0,0 @@ -/* -*- 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 - */ - -#ifndef E_ADDRESS_COMPLETION_H -#define E_ADDRESS_COMPLETION_H - -#include "e-book.h" -#include <gal/e-text/e-completion.h> - -BEGIN_GNOME_DECLS - -#define E_ADDRESS_COMPLETION_TYPE (e_address_completion_get_type ()) -#define E_ADDRESS_COMPLETION(o) (GTK_CHECK_CAST ((o), E_ADDRESS_COMPLETION_TYPE, EAddressCompletion)) -#define E_ADDRESS_COMPLETION_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), E_ADDRESS_COMPLETION_TYPE, EAddressCompletionClass)) -#define E_IS_ADDRESS_COMPLETION(o) (GTK_CHECK_TYPE ((o), E_ADDRESS_COMPLETION_TYPE)) -#define E_IS_ADDRESS_COMPLETION_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), E_ADDRESS_COMPLETION_TYPE)) - -typedef struct _EAddressCompletion EAddressCompletion; -typedef struct _EAddressCompletionClass EAddressCompletionClass; - -struct _EAddressCompletion { - ECompletion parent; - - EBook *book; - guint seq_no; -}; - -struct _EAddressCompletionClass { - ECompletionClass parent_class; -}; - -GtkType e_address_completion_get_type (void); - -void e_address_completion_construct (EAddressCompletion *addr_comp, EBook *book); -ECompletion *e_address_completion_new (EBook *book); - - - - -#endif /* E_ADDRESS_COMPLETION_H */ - diff --git a/addressbook/backend/ebook/e-destination.c b/addressbook/backend/ebook/e-destination.c new file mode 100644 index 0000000000..92aed64715 --- /dev/null +++ b/addressbook/backend/ebook/e-destination.c @@ -0,0 +1,284 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * e-destination.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-i18n.h> +#include "e-destination.h" + +struct _EDestinationPrivate { + ECard *card; + gint card_email_num; + + gchar *string; + gchar *string_email; + gchar *string_email_verbose; +}; + +static void e_destination_clear_card (EDestination *); +static void e_destination_clear_strings (EDestination *); + +static GtkObjectClass *parent_class; + +static void +e_destination_destroy (GtkObject *obj) +{ + EDestination *dest = E_DESTINATION (obj); + + e_destination_clear_card (dest); + e_destination_clear_strings (dest); + + g_free (dest->priv); + + if (parent_class->destroy) + parent_class->destroy (obj); +} + +static void +e_destination_class_init (EDestinationClass *klass) +{ + GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass); + + parent_class = GTK_OBJECT_CLASS (gtk_type_class (GTK_TYPE_OBJECT)); + + object_class->destroy = e_destination_destroy; +} + +static void +e_destination_init (EDestination *dest) +{ + dest->priv = g_new0 (struct _EDestinationPrivate, 1); +} + +GtkType +e_destination_get_type (void) +{ + static GtkType dest_type = 0; + + if (!dest_type) { + GtkTypeInfo dest_info = { + "EDestination", + sizeof (EDestination), + sizeof (EDestinationClass), + (GtkClassInitFunc) e_destination_class_init, + (GtkObjectInitFunc) e_destination_init, + NULL, NULL, /* reserved */ + (GtkClassInitFunc) NULL + }; + + dest_type = gtk_type_unique (gtk_object_get_type (), &dest_info); + } + + return dest_type; +} + +EDestination * +e_destination_new (void) +{ + return E_DESTINATION (gtk_type_new (E_TYPE_DESTINATION)); +} + +EDestination * +e_destination_copy (EDestination *dest) +{ + EDestination *new_dest; + + g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL); + + new_dest = e_destination_new (); + + new_dest->priv->card = dest->priv->card; + if (new_dest->priv->card) + gtk_object_ref (GTK_OBJECT (new_dest->priv->card)); + + new_dest->priv->card_email_num = dest->priv->card_email_num; + + new_dest->priv->string = g_strdup (dest->priv->string); + new_dest->priv->string_email = g_strdup (dest->priv->string_email); + new_dest->priv->string_email_verbose = g_strdup (dest->priv->string_email_verbose); + + return new_dest; +} + +static void +e_destination_clear_card (EDestination *dest) +{ + if (dest->priv->card) + gtk_object_unref (GTK_OBJECT (dest->priv->card)); + dest->priv->card = NULL; + + dest->priv->card_email_num = -1; +} + +static void +e_destination_clear_strings (EDestination *dest) +{ + g_free (dest->priv->string); + g_free (dest->priv->string_email); + g_free (dest->priv->string_email_verbose); + + dest->priv->string = NULL; + dest->priv->string_email = NULL; + dest->priv->string_email_verbose = NULL; +} + +void +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)); + + if (dest->priv->card != card) { + if (dest->priv->card) + gtk_object_unref (GTK_OBJECT (dest->priv->card)); + dest->priv->card = card; + gtk_object_ref (GTK_OBJECT (card)); + } + + dest->priv->card_email_num = email_num; + + e_destination_clear_strings (dest); +} + +void +e_destination_set_string (EDestination *dest, const gchar *string) +{ + g_return_if_fail (dest && E_IS_DESTINATION (dest)); + g_return_if_fail (string != NULL); + + g_free (dest->priv->string); + dest->priv->string = g_strdup (string); +} + +ECard * +e_destination_get_card (const EDestination *dest) +{ + g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL); + + return dest->priv->card; +} + +const gchar * +e_destination_get_string (const EDestination *dest) +{ + struct _EDestinationPrivate *priv; + + g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL); + + priv = (struct _EDestinationPrivate *)dest->priv; /* cast out const */ + + if (priv->string == NULL) { + + if (priv->card) { + + priv->string = e_card_name_to_string (priv->card->name); + + } + } + + return priv->string; +} + +gint +e_destination_get_strlen (const EDestination *dest) +{ + const gchar *str; + + g_return_val_if_fail (dest && E_IS_DESTINATION (dest), 0); + + str = e_destination_get_string (dest); + return str ? strlen (str) : 0; +} + +const gchar * +e_destination_get_email (const EDestination *dest) +{ + struct _EDestinationPrivate *priv; + + g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL); + + priv = (struct _EDestinationPrivate *)dest->priv; /* cast out const */ + + if (priv->string_email == NULL) { + + if (priv->card) { /* Pull the address out of the card. */ + + EIterator *iter = e_list_get_iterator (priv->card->email); + gint n = priv->card_email_num; + + if (n >= 0) { + while (n > 0) { + e_iterator_next (iter); + --n; + } + + if (e_iterator_is_valid (iter)) { + gconstpointer ptr = e_iterator_get (iter); + priv->string_email = g_strdup ((gchar *) ptr); + } + } + + } else if (priv->string) { /* Use the string as an e-mail address */ + return priv->string; + } + + /* else we just return NULL */ + } + + return priv->string_email; +} + +const gchar * +e_destination_get_email_verbose (const EDestination *dest) +{ + struct _EDestinationPrivate *priv; + + g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL); + + priv = (struct _EDestinationPrivate *)dest->priv; /* cast out const */ + + if (priv->string_email_verbose == NULL) { + + const gchar *email = e_destination_get_email (dest); + + if (priv->card) { + + priv->string_email_verbose = g_strdup_printf ("%s <%s>", + e_card_name_to_string (priv->card->name), + email); + } else { + + return email; + } + + } + + return priv->string_email_verbose; +} + + diff --git a/addressbook/backend/ebook/e-destination.h b/addressbook/backend/ebook/e-destination.h new file mode 100644 index 0000000000..509a351f1d --- /dev/null +++ b/addressbook/backend/ebook/e-destination.h @@ -0,0 +1,73 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * e-destination.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_DESTINATION_H__ +#define __E_DESTINATION_H__ + +#include <gtk/gtk.h> +#include <addressbook/backend/ebook/e-card.h> + +#define E_TYPE_DESTINATION (e_destination_get_type ()) +#define E_DESTINATION(o) (GTK_CHECK_CAST ((o), E_TYPE_DESTINATION, EDestination)) +#define E_DESTINATION_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), E_TYPE_DESTINATION, EDestinationClass)) +#define E_IS_DESTINATION(o) (GTK_CHECK_TYPE ((o), E_TYPE_DESTINATION)) +#define E_IS_DESTINATION_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), E_TYPE_DESTINATION)) + +typedef struct _EDestination EDestination; +typedef struct _EDestinationClass EDestinationClass; + +struct _EDestinationPrivate; + +struct _EDestination { + GtkObject object; + + struct _EDestinationPrivate *priv; +}; + +struct _EDestinationClass { + GtkObjectClass parent_class; +}; + +GtkType e_destination_get_type (void); + +EDestination *e_destination_new (void); +EDestination *e_destination_copy (EDestination *); + +void e_destination_set_card (EDestination *, ECard *card, gint email_num); +void e_destination_set_string (EDestination *, const gchar *string); + +ECard *e_destination_get_card (const EDestination *); +const gchar *e_destination_get_string (const EDestination *); +gint e_destination_get_strlen (const EDestination *); /* a convenience function... */ + +const gchar *e_destination_get_email (const EDestination *); +const gchar *e_destination_get_email_verbose (const EDestination *); + + + +#endif /* __E_DESTINATION_H__ */ + diff --git a/addressbook/gui/component/select-names/Makefile.am b/addressbook/gui/component/select-names/Makefile.am index 312ff72ee7..7e9a2e00e3 100644 --- a/addressbook/gui/component/select-names/Makefile.am +++ b/addressbook/gui/component/select-names/Makefile.am @@ -51,6 +51,8 @@ libeselectnames_la_SOURCES = \ $(IDL_GENERATED) \ e-select-names-bonobo.c \ e-select-names-bonobo.h \ + e-select-names-completion.c \ + e-select-names-completion.h \ e-select-names-factory.c \ e-select-names-factory.h \ e-select-names-manager.c \ diff --git a/addressbook/gui/component/select-names/e-select-names-bonobo.c b/addressbook/gui/component/select-names/e-select-names-bonobo.c index 87be63c3db..1d34c4198f 100644 --- a/addressbook/gui/component/select-names/e-select-names-bonobo.c +++ b/addressbook/gui/component/select-names/e-select-names-bonobo.c @@ -61,15 +61,18 @@ entry_get_property_fn (BonoboPropertyBag *bag, CORBA_Environment *ev, void *user_data) { - GtkWidget *widget; - char *text; + GtkWidget *w; + const char *text; - widget = GTK_WIDGET (user_data); + w = GTK_WIDGET (user_data); switch (arg_id) { case ENTRY_PROPERTY_ID_TEXT: - gtk_object_get (GTK_OBJECT (widget), "text", &text, NULL); - BONOBO_ARG_SET_STRING (arg, text); + { + ESelectNamesModel *model = E_SELECT_NAMES_MODEL (gtk_object_get_data (GTK_OBJECT (w), "bonobo_select_names_model")); + text = e_select_names_model_get_address_text (model); + BONOBO_ARG_SET_STRING (arg, text); + } break; default: break; @@ -84,15 +87,11 @@ entry_set_property_fn (BonoboPropertyBag *bag, gpointer user_data) { GtkWidget *widget; - const char *text; widget = GTK_WIDGET (user_data); switch (arg_id) { - case ENTRY_PROPERTY_ID_TEXT: - text = BONOBO_ARG_GET_STRING (arg); - gtk_object_set (GTK_OBJECT (widget), "text", text, NULL); - break; + case ENTRY_PROPERTY_ID_ENTRY_CHANGED: gtk_object_set_data (GTK_OBJECT (widget), "entry_property_id_changed", GUINT_TO_POINTER (1)); break; @@ -173,6 +172,10 @@ impl_SelectNames_get_entry_for_section (PortableServer_Servant servant, entry_widget = e_select_names_manager_create_entry (priv->manager, section_id); gtk_widget_show (entry_widget); + + gtk_object_set_data (GTK_OBJECT (entry_widget), + "bonobo_select_names_model", + e_select_names_manager_get_source (priv->manager, section_id)); if (entry_widget == NULL) { CORBA_exception_set (ev, @@ -187,7 +190,7 @@ impl_SelectNames_get_entry_for_section (PortableServer_Servant servant, property_bag = bonobo_property_bag_new (entry_get_property_fn, entry_set_property_fn, entry_widget); bonobo_property_bag_add (property_bag, "text", ENTRY_PROPERTY_ID_TEXT, BONOBO_ARG_STRING, NULL, NULL, - BONOBO_PROPERTY_READABLE | BONOBO_PROPERTY_WRITEABLE); + BONOBO_PROPERTY_READABLE); bonobo_property_bag_add (property_bag, "entry_changed", ENTRY_PROPERTY_ID_ENTRY_CHANGED, BONOBO_ARG_BOOLEAN, NULL, NULL, BONOBO_PROPERTY_WRITEABLE); diff --git a/addressbook/gui/component/select-names/e-select-names-completion.c b/addressbook/gui/component/select-names/e-select-names-completion.c new file mode 100644 index 0000000000..211bf6becb --- /dev/null +++ b/addressbook/gui/component/select-names/e-select-names-completion.c @@ -0,0 +1,831 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * e-select-names-completion.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 <stdio.h> +#include <string.h> +#include <ctype.h> +#include <gnome.h> +#include <addressbook/backend/ebook/e-destination.h> +#include "e-select-names-completion.h" + +struct _ESelectNamesCompletionPrivate { + + ESelectNamesModel *model; + + EBook *book; + gboolean book_ready; + gboolean cancelled; + + EBookView *book_view; + guint card_added_id; + guint seq_complete_id; + + gchar *query_text; + gchar *pending_query_text; + + gboolean primary_only; +}; + +static void e_select_names_completion_class_init (ESelectNamesCompletionClass *); +static void e_select_names_completion_init (ESelectNamesCompletion *); +static void e_select_names_completion_destroy (GtkObject *object); + +static void e_select_names_completion_got_book_view_cb (EBook *book, EBookStatus status, EBookView *view, gpointer user_data); +static void e_select_names_completion_card_added_cb (EBookView *, const GList *cards, gpointer user_data); +static void e_select_names_completion_seq_complete_cb (EBookView *, gpointer user_data); + +static void e_select_names_completion_do_query (ESelectNamesCompletion *); + +static void e_select_names_completion_begin (ECompletion *, const gchar *txt, gint pos, gint limit); +static void e_select_names_completion_end (ECompletion *); +static void e_select_names_completion_cancel (ECompletion *); + +static GtkObjectClass *parent_class; + +static FILE *out; + +/* + * + * Query builders + * + */ + +typedef gchar *(*BookQuerySExp) (ESelectNamesCompletion *); +typedef gchar *(*BookQueryMatchTester) (ESelectNamesCompletion *, EDestination *, double *score); + +/* + * Nickname query + */ + +static gchar * +sexp_nickname (ESelectNamesCompletion *comp) +{ + return g_strdup_printf ("(beginswith \"nickname\" \"%s\")", comp->priv->query_text); + +} + +static gchar * +match_nickname (ESelectNamesCompletion *comp, EDestination *dest, double *score) +{ + gint len = strlen (comp->priv->query_text); + ECard *card = e_destination_get_card (dest); + + if (card->nickname + && !g_strncasecmp (comp->priv->query_text, card->nickname, len)) { + + *score = len * 10; /* nickname gives 10 points per matching character */ + + return g_strdup_printf ("(%s) %s %s", card->nickname, card->name->given, card->name->family); + + } + + return NULL; +} + +/* + * E-Mail Query + */ + +static gchar * +sexp_email (ESelectNamesCompletion *comp) +{ + return g_strdup_printf ("(beginswith \"email\" \"%s\")", comp->priv->query_text); +} + +static gchar * +match_email (ESelectNamesCompletion *comp, EDestination *dest, double *score) +{ + gint len = strlen (comp->priv->query_text); + ECard *card = e_destination_get_card (dest); + const gchar *email = e_destination_get_email (dest); + + if (email && !g_strncasecmp (comp->priv->query_text, email, len)) { + *score = len * 2; /* 2 points for each matching character */ + return g_strdup_printf ("<%s> %s %s", email, card->name->given, card->name->family); + } + + return NULL; +} + +/* + * Name Query + */ + +static gchar * +sexp_name (ESelectNamesCompletion *comp) +{ + if (comp && comp->priv->query_text && *comp->priv->query_text) { + gchar *cpy = g_strdup (comp->priv->query_text); + gchar **strv; + gchar *query; + gint i, count=0; + + strv = g_strsplit (cpy, " ", 0); + for (i=0; strv[i]; ++i) { + ++count; + g_strstrip (strv[i]); + strv[i] = g_strdup_printf ("(contains \"full_name\" \"%s\")", strv[i]); + } + + if (count == 1) { + query = strv[0]; + strv[0] = NULL; + } else { + gchar *joined = g_strjoinv (" ", strv); + query = g_strdup_printf ("(and %s)", joined); + g_free (joined); + } + + for (i=0; strv[i]; ++i) + g_free (strv[i]); + g_free (cpy); + g_free (strv); + + return query; + } + + return NULL; +} + +enum { + MATCHED_NOTHING = 0, + MATCHED_GIVEN_NAME = 1<<0, + MATCHED_ADDITIONAL_NAME = 1<<1, + MATCHED_FAMILY_NAME = 1<<2 +}; + +static gchar * +match_name (ESelectNamesCompletion *comp, EDestination *dest, double *score) +{ + ECard *card; + gchar *cpy, **strv; + gint len, i, match_len = 0; + gint match = 0, first_match = 0; + + card = e_destination_get_card (dest); + + if (card->name == NULL) + return NULL; + + cpy = g_strdup (comp->priv->query_text); + strv = g_strsplit (cpy, " ", 0); + + for (i=0; strv[i] && !(match & MATCHED_NOTHING); ++i) { + gint this_match = 0; + + g_strstrip (strv[i]); + len = strlen (strv[i]); + + if (card->name->given + && !(match & MATCHED_GIVEN_NAME) + && !g_strncasecmp (card->name->given, strv[i], len)) { + + this_match = MATCHED_GIVEN_NAME; + + } + else if (card->name->additional + && !(match & MATCHED_ADDITIONAL_NAME) + && !g_strncasecmp (card->name->additional, strv[i], len)) { + + this_match = MATCHED_ADDITIONAL_NAME; + + } else if (card->name->family + && !(match & MATCHED_FAMILY_NAME) + && !g_strncasecmp (card->name->family, strv[i], len)) { + + this_match = MATCHED_FAMILY_NAME; + } + + + if (this_match != MATCHED_NOTHING) { + match_len += len; + match |= this_match; + if (first_match == 0) + first_match = this_match; + } else { + match = first_match = 0; + break; + } + + } + + *score = match_len * 3; /* three points per match character */ + + if (card->nickname) { + /* We massively boost the score if the nickname exists and is the same as one of the "real" names. This keeps the + nickname from matching ahead of the real name for this card. */ + len = strlen (card->nickname); + if ((card->name->given && !g_strncasecmp (card->name->given, card->nickname, MIN (strlen (card->name->given), len))) + || (card->name->family && !g_strncasecmp (card->name->family, card->nickname, MIN (strlen (card->name->family), len))) + || (card->name->additional && !g_strncasecmp (card->name->additional, card->nickname, MIN (strlen (card->name->additional), len)))) + *score *= 100; + } + + if (first_match == MATCHED_GIVEN_NAME) + return g_strdup_printf ("%s %s", card->name->given, card->name->family); + else if (first_match == MATCHED_ADDITIONAL_NAME) + return g_strdup_printf ("%s, %s %s", card->name->family, card->name->given, card->name->additional); + else if (first_match == MATCHED_FAMILY_NAME) + return g_strdup_printf ("%s, %s", card->name->family, card->name->given); + + return NULL; +} + +/* + * Initials Query + */ + +static gchar * +sexp_initials (ESelectNamesCompletion *comp) +{ + return NULL; +} + +static gchar * +match_initials (ESelectNamesCompletion *comp, EDestination *dest, double *score) +{ + return NULL; +} + + +typedef struct _BookQuery BookQuery; +struct _BookQuery { + gboolean primary; + BookQuerySExp builder; + BookQueryMatchTester tester; +}; + +static BookQuery book_queries[] = { + { TRUE, sexp_nickname, match_nickname}, + { TRUE, sexp_email, match_email }, + { TRUE, sexp_name, match_name }, + { FALSE, sexp_initials, match_initials } +}; +static gint book_query_count = sizeof (book_queries) / sizeof (BookQuery); + +/* + * Build up a big compound sexp corresponding to all of our queries. + */ +static gchar * +book_query_sexp (ESelectNamesCompletion *comp) +{ + gint i, j, count = 0; + gchar **queryv, *query; + + g_return_val_if_fail (comp && E_IS_SELECT_NAMES_COMPLETION (comp), NULL); + + if (! (comp->priv->query_text && *comp->priv->query_text)) + return NULL; + + if (comp->priv->primary_only) { + for (i=0; i<book_query_count; ++i) + if (book_queries[i].primary) + ++count; + } else { + count = book_query_count; + } + + queryv = g_new0 (gchar *, count+1); + for (i=0, j=0; i<count; ++i) { + queryv[j] = book_queries[i].builder (comp); + if (queryv[j]) + ++j; + } + + if (j == 0) { + query = NULL; + } else if (j == 1) { + query = queryv[0]; + queryv[0] = NULL; + } else { + gchar *tmp = g_strjoinv (" ", queryv); + query = g_strdup_printf ("(or %s)", tmp); + g_free (tmp); + } + + for (i=0; i<count; ++i) + g_free (queryv[i]); + g_free (queryv); + + return query; +} + +/* + * Sweep across all of our query rules and find the best score/match + * string that applies to a given destination. + */ +static gchar * +book_query_score (ESelectNamesCompletion *comp, EDestination *dest, double *score) +{ + double best_score = -1; + gchar *best_string = NULL; + gint i; + + g_return_val_if_fail (score, NULL); + *score = -1; + + g_return_val_if_fail (comp && E_IS_SELECT_NAMES_COMPLETION (comp), NULL); + g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL); + + for (i=0; i<book_query_count; ++i) { + double this_score = -1; + gchar *this_string = NULL; + + if (book_queries[i].primary || !comp->priv->primary_only) { + if (book_queries[i].tester && e_destination_get_card (dest)) + this_string = book_queries[i].tester (comp, dest, &this_score); + if (this_string) { + if (this_score > best_score) { + g_free (best_string); + best_string = this_string; + best_score = this_score; + } else { + g_free (this_string); + } + } + } + } + + /* If this destination corresponds to a card w/ multiple addresses, and if the + address isn't already in the string, append it. */ + if (best_string) { + ECard *card = e_destination_get_card (dest); + if (e_list_length (card->email) > 1) { + const gchar *email = e_destination_get_email (dest); + if (email && strstr (best_string, email) == NULL) { + gchar *tmp = g_strdup_printf ("%s <%s>", best_string, email); + g_free (best_string); + best_string = tmp; + } + } + } + + *score = best_score; + return best_string; +} + +#if 0 +static gchar * +initials_query_match_cb (QueryInfo *qi, ECard *card, double *score) +{ + gint len; + gchar f='\0', m='\0', l='\0'; /* initials */ + gchar cf, cm, cl; + + len = strlen (qi->comp->priv->query_text); + + if (len == 2) { + + f = qi->comp->priv->query_text[0]; + m = '\0'; + l = qi->comp->priv->query_text[1]; + + } else if (len == 3) { + + f = qi->comp->priv->query_text[0]; + m = qi->comp->priv->query_text[1]; + l = qi->comp->priv->query_text[2]; + + } else { + return NULL; + } + + cf = card->name->given ? *card->name->given : '\0'; + cm = card->name->additional ? *card->name->additional : '\0'; + cl = card->name->family ? *card->name->family : '\0'; + + if (f && isupper ((gint) f)) + f = tolower ((gint) f); + if (m && isupper ((gint) m)) + m = tolower ((gint) m); + if (l && isupper ((gint) l)) + l = tolower ((gint) l); + + if (cf && isupper ((gint) cf)) + cf = tolower ((gint) cf); + if (cm && isupper ((gint) cm)) + cm = tolower ((gint) cm); + if (cl && isupper ((gint) cl)) + cl = tolower ((gint) cl); + + if ((f == '\0' || (f == cf)) && (m == '\0' || (m == cm)) && (l == '\0' || (l == cl))) { + if (score) + *score = 3; + if (m) + return g_strdup_printf ("%s %s %s", card->name->given, card->name->additional, card->name->family); + else + return g_strdup_printf ("%s %s", card->name->given, card->name->family); + } + + return NULL; +} + +static gboolean +start_initials_query (ESelectNamesCompletion *comp) +{ + gint len; + gchar *query; + + if (comp && comp->priv->query_text && *(comp->priv->query_text)) { + + len = strlen (comp->priv->query_text); + if (len < 2 || len > 3) + return FALSE; + + query = g_strdup_printf ("(contains \"x-evolution-any-field\" \"%c\")", *(comp->priv->query_text)); + query_info_start (comp, comp->priv->query_text, query, initials_query_match_cb); + g_free (query); + return TRUE; + } + + return FALSE; +} +#endif + + +/* + * + * ESelectNamesCompletion code + * + */ + + +GtkType +e_select_names_completion_get_type (void) +{ + static GtkType select_names_complete_type = 0; + + if (!select_names_complete_type) { + GtkTypeInfo select_names_complete_info = { + "ESelectNamesCompletion", + sizeof (ESelectNamesCompletion), + sizeof (ESelectNamesCompletionClass), + (GtkClassInitFunc) e_select_names_completion_class_init, + (GtkObjectInitFunc) e_select_names_completion_init, + NULL, NULL, /* reserved */ + (GtkClassInitFunc) NULL + }; + + select_names_complete_type = gtk_type_unique (e_completion_get_type (), &select_names_complete_info); + } + + return select_names_complete_type; +} + +static void +e_select_names_completion_class_init (ESelectNamesCompletionClass *klass) +{ + GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass); + ECompletionClass *completion_class = E_COMPLETION_CLASS (klass); + + parent_class = GTK_OBJECT_CLASS (gtk_type_class (e_completion_get_type ())); + + object_class->destroy = e_select_names_completion_destroy; + + completion_class->begin_completion = e_select_names_completion_begin; + completion_class->end_completion = e_select_names_completion_end; + completion_class->cancel_completion = e_select_names_completion_cancel; + + if (getenv ("EVO_DEBUG_SELECT_NAMES_COMPLETION")) { + out = fopen ("/tmp/evo-debug-select-names-completion", "w"); + if (out) + setvbuf (out, NULL, _IONBF, 0); + } +} + +static void +e_select_names_completion_init (ESelectNamesCompletion *comp) +{ + comp->priv = g_new0 (struct _ESelectNamesCompletionPrivate, 1); +} + +static void +e_select_names_completion_destroy (GtkObject *object) +{ + ESelectNamesCompletion *comp = E_SELECT_NAMES_COMPLETION (object); + + gtk_object_unref (GTK_OBJECT (comp->priv->model)); + gtk_object_unref (GTK_OBJECT (comp->priv->book)); + + if (GTK_OBJECT (comp->priv->book_view)) { + gtk_signal_disconnect (GTK_OBJECT (comp->priv->book_view), comp->priv->card_added_id); + gtk_signal_disconnect (GTK_OBJECT (comp->priv->book_view), comp->priv->seq_complete_id); + gtk_object_unref (GTK_OBJECT (comp->priv->book_view)); + } + + g_free (comp->priv->query_text); + g_free (comp->priv->pending_query_text); + + g_free (comp->priv); + + if (parent_class->destroy) + parent_class->destroy (object); +} + +/* + * + * EBook/EBookView Callbacks + * + */ + +static void +e_select_names_completion_got_book_view_cb (EBook *book, EBookStatus status, EBookView *view, gpointer user_data) +{ + ESelectNamesCompletion *comp = E_SELECT_NAMES_COMPLETION (user_data); + + comp->priv->book_view = view; + gtk_object_ref (GTK_OBJECT (view)); + + + comp->priv->card_added_id = gtk_signal_connect (GTK_OBJECT (view), + "card_added", + GTK_SIGNAL_FUNC (e_select_names_completion_card_added_cb), + comp); + comp->priv->seq_complete_id = gtk_signal_connect (GTK_OBJECT (view), + "sequence_complete", + GTK_SIGNAL_FUNC (e_select_names_completion_seq_complete_cb), + comp); +} + +static void +e_select_names_completion_card_added_cb (EBookView *book_view, const GList *cards, gpointer user_data) +{ + ESelectNamesCompletion *comp = E_SELECT_NAMES_COMPLETION (user_data); + + if (comp->priv->cancelled) + return; + + while (cards) { + ECard *card = E_CARD (cards->data); + + if (card->email) { + gint i; + for (i=0; i<e_list_length (card->email); ++i) { + EDestination *dest = e_destination_new (); + gchar *match_text; + double score = -1; + + e_destination_set_card (dest, card, i); + + match_text = book_query_score (comp, dest, &score); + if (match_text && score > 0) { + + e_completion_found_match_full (E_COMPLETION (comp), match_text, score, dest, + (GtkDestroyNotify) gtk_object_unref); + } else { + gtk_object_unref (GTK_OBJECT (dest)); + } + g_free (match_text); + } + } + + cards = g_list_next (cards); + } +} + +static void +e_select_names_completion_seq_complete_cb (EBookView *book_view, gpointer user_data) +{ + ESelectNamesCompletion *comp = E_SELECT_NAMES_COMPLETION (user_data); + + gtk_signal_disconnect (GTK_OBJECT (comp->priv->book_view), comp->priv->card_added_id); + gtk_signal_disconnect (GTK_OBJECT (comp->priv->book_view), comp->priv->seq_complete_id); + gtk_object_unref (GTK_OBJECT (comp->priv->book_view)); + + comp->priv->book_view = NULL; + + g_free (comp->priv->query_text); + comp->priv->query_text = NULL; + + fprintf (out, "ending search "); + if (!e_completion_searching (E_COMPLETION (comp))) + fprintf (out, "while not searching!"); + fprintf (out, "\n"); + e_completion_end_search (E_COMPLETION (comp)); /* That's all folks! */ + + /* Need to launch a new completion if another one is pending. */ + e_select_names_completion_do_query (comp); +} + +/* + * + * Completion Callbacks + * + */ + +static void +e_select_names_completion_do_query (ESelectNamesCompletion *comp) +{ + gchar *sexp; + + g_return_if_fail (comp != NULL); + g_return_if_fail (E_IS_SELECT_NAMES_COMPLETION (comp)); + + /* Wait until we are ready... */ + if (! comp->priv->book_ready) + return; + + if (comp->priv->query_text) + return; + + if (comp->priv->pending_query_text == NULL) + return; + + + comp->priv->query_text = comp->priv->pending_query_text; + comp->priv->pending_query_text = NULL; + + sexp = book_query_sexp (comp); + + if (sexp == NULL || *sexp == '\0') { + g_free (sexp); + g_free (comp->priv->query_text); + comp->priv->query_text = NULL; + return; + } + + comp->priv->cancelled = FALSE; + + fprintf (out, "\n\n**** starting query: \"%s\"\n", comp->priv->query_text); + + if (! e_book_get_book_view (comp->priv->book, sexp, e_select_names_completion_got_book_view_cb, comp)) { + g_warning ( "exception getting book view"); + return; + } + g_free (sexp); + +} + +typedef struct _SearchOverride SearchOverride; +struct _SearchOverride { + const gchar *trigger; + const gchar *text[4]; +}; +static SearchOverride override[] = { + { "easter egg", { "This is the sample", "Easter Egg text for", "Evolution.", NULL } }, + { NULL, { NULL } } }; + +static gboolean +search_override_check (SearchOverride *over, const gchar *text) +{ + if (over == NULL || text == NULL) + return FALSE; + + return !g_strcasecmp (over->trigger, text); +} + +static void +e_select_names_completion_begin (ECompletion *comp, const gchar *text, gint pos, gint limit) +{ + ESelectNamesCompletion *selcomp = E_SELECT_NAMES_COMPLETION (comp); + const gchar *str; + gint index, j; + + g_return_if_fail (comp != NULL); + g_return_if_fail (E_IS_SELECT_NAMES_COMPLETION (comp)); + g_return_if_fail (text != NULL); + + fprintf (out, "\n\n**** requesting completion\n"); + fprintf (out, "text=\"%s\" pos=%d limit=%d\n", text, pos, limit); + + e_select_names_model_text_pos (selcomp->priv->model, pos, &index, NULL, NULL); + str = index >= 0 ? e_select_names_model_get_string (selcomp->priv->model, index) : NULL; + + fprintf (out, "index=%d str=\"%s\"\n", index, str); + + if (str == NULL || *str == '\0') { + fprintf (out, "aborting empty query\n"); + e_completion_end_search (comp); + return; + } + + for (j=0; override[j].trigger; ++j) { + if (search_override_check (&(override[j]), str)) { + gint k; + + for (k=0; override[j].text[k]; ++k) + e_completion_found_match (comp, override[j].text[k]); + + fprintf (out, "aborting on override \"%s\"\n", override[j].trigger); + e_completion_end_search (comp); + return; + } + } + + + g_free (selcomp->priv->pending_query_text); + selcomp->priv->pending_query_text = g_strdup (str); + + e_select_names_completion_do_query (selcomp); +} + +static void +e_select_names_completion_end (ECompletion *comp) +{ + g_return_if_fail (comp != NULL); + g_return_if_fail (E_IS_COMPLETION (comp)); + + fprintf (out, "completion ended\n"); +} + +static void +e_select_names_completion_cancel (ECompletion *comp) +{ + g_return_if_fail (comp != NULL); + g_return_if_fail (E_IS_COMPLETION (comp)); + + E_SELECT_NAMES_COMPLETION (comp)->priv->cancelled = TRUE; + + fprintf (out, "completion cancelled\n"); +} + +static void +e_select_names_completion_book_ready (EBook *book, EBookStatus status, ESelectNamesCompletion *comp) +{ + comp->priv->book_ready = TRUE; + + g_return_if_fail (book != NULL); + g_return_if_fail (E_IS_BOOK (book)); + g_return_if_fail (comp != NULL); + g_return_if_fail (E_IS_SELECT_NAMES_COMPLETION (comp)); + + /* If query_text is non-NULL, someone tried to start a query before the book was ready. + Now that it is, get started. */ + if (comp->priv->query_text != NULL) + e_select_names_completion_do_query (comp); + + gtk_object_unref (GTK_OBJECT (comp)); /* post-async unref */ +} + + +/* + * + * Our Pseudo-Constructor + * + */ + +ECompletion * +e_select_names_completion_new (EBook *book, ESelectNamesModel *model) +{ + ESelectNamesCompletion *comp; + + g_return_val_if_fail (book == NULL || E_IS_BOOK (book), NULL); + g_return_val_if_fail (model, NULL); + g_return_val_if_fail (E_IS_SELECT_NAMES_MODEL (model), NULL); + + comp = (ESelectNamesCompletion *) gtk_type_new (e_select_names_completion_get_type ()); + + if (book == NULL) { + gchar *filename, *uri; + + comp->priv->book = e_book_new (); + gtk_object_ref (GTK_OBJECT (comp->priv->book)); + gtk_object_sink (GTK_OBJECT (comp->priv->book)); + + filename = gnome_util_prepend_user_home ("evolution/local/Contacts/addressbook.db"); + uri = g_strdup_printf ("file://%s", filename); + + comp->priv->book_ready = FALSE; + gtk_object_ref (GTK_OBJECT (comp)); /* ref ourself before our async call */ + e_book_load_uri (comp->priv->book, uri, (EBookCallback) e_select_names_completion_book_ready, comp); + + g_free (filename); + g_free (uri); + } else { + + comp->priv->book = book; + gtk_object_ref (GTK_OBJECT (comp->priv->book)); + comp->priv->book_ready = TRUE; + } + + comp->priv->model = model; + gtk_object_ref (GTK_OBJECT (model)); + + return E_COMPLETION (comp); +} + diff --git a/addressbook/gui/component/select-names/e-select-names-completion.h b/addressbook/gui/component/select-names/e-select-names-completion.h new file mode 100644 index 0000000000..1f3de5ea3d --- /dev/null +++ b/addressbook/gui/component/select-names/e-select-names-completion.h @@ -0,0 +1,65 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * e-select-names-completion.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_SELECT_NAMES_COMPLETION_H +#define E_SELECT_NAMES_COMPLETION_H + +#include <gal/e-text/e-completion.h> +#include <addressbook/backend/ebook/e-book.h> +#include "e-select-names-model.h" + +BEGIN_GNOME_DECLS + +#define E_SELECT_NAMES_COMPLETION_TYPE (e_select_names_completion_get_type ()) +#define E_SELECT_NAMES_COMPLETION(o) (GTK_CHECK_CAST ((o), E_SELECT_NAMES_COMPLETION_TYPE, ESelectNamesCompletion)) +#define E_SELECT_NAMES_COMPLETION_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), E_SELECT_NAMES_COMPLETION_TYPE, ESelectNamesCompletionClass)) +#define E_IS_SELECT_NAMES_COMPLETION(o) (GTK_CHECK_TYPE ((o), E_SELECT_NAMES_COMPLETION_TYPE)) +#define E_IS_SELECT_NAMES_COMPLETION_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), E_SELECT_NAMES_COMPLETION_TYPE)) + +typedef struct _ESelectNamesCompletion ESelectNamesCompletion; +typedef struct _ESelectNamesCompletionClass ESelectNamesCompletionClass; +struct _ESelectNamesCompletionPrivate; + +struct _ESelectNamesCompletion { + ECompletion parent; + + struct _ESelectNamesCompletionPrivate *priv; +}; + +struct _ESelectNamesCompletionClass { + ECompletionClass parent_class; + +}; + +GtkType e_select_names_completion_get_type (void); + +ECompletion *e_select_names_completion_new (EBook *, ESelectNamesModel *); + +END_GNOME_DECLS + +#endif /* E_SELECT_NAMES_COMPLETION_H */ + diff --git a/addressbook/gui/component/select-names/e-select-names-manager.c b/addressbook/gui/component/select-names/e-select-names-manager.c index 3e629af19d..e1c8d80276 100644 --- a/addressbook/gui/component/select-names/e-select-names-manager.c +++ b/addressbook/gui/component/select-names/e-select-names-manager.c @@ -16,8 +16,9 @@ #include "e-select-names-model.h" #include "e-select-names-text-model.h" #include "e-select-names.h" -#include <addressbook/backend/ebook/e-address-completion.h> +#include "e-select-names-completion.h" #include <gal/e-text/e-entry.h> +#include <addressbook/backend/ebook/e-destination.h> /* Object argument IDs */ enum { @@ -213,9 +214,10 @@ e_select_names_manager_init (ESelectNamesManager *manager) manager->entries = e_list_new(entry_copy, entry_free, manager); } -void e_select_names_manager_add_section (ESelectNamesManager *manager, - const char *id, - const char *title) +void +e_select_names_manager_add_section (ESelectNamesManager *manager, + const char *id, + const char *title) { ESelectNamesManagerSection *section; @@ -227,6 +229,25 @@ void e_select_names_manager_add_section ( section_free(section, manager); } +ESelectNamesModel * +e_select_names_manager_get_source (ESelectNamesManager *manager, const char *id) +{ + EIterator *iterator; + + g_return_val_if_fail (manager && E_IS_SELECT_NAMES_MANAGER (manager), NULL); + g_return_val_if_fail (id, NULL); + + iterator = e_list_get_iterator (manager->sections); + for (e_iterator_reset (iterator); e_iterator_is_valid (iterator); e_iterator_next (iterator)) { + const ESelectNamesManagerSection *section = e_iterator_get (iterator); + if (!strcmp (section->id, id)) { + return section->model; + } + } + + return NULL; +} + static void entry_destroyed(EEntry *entry, ESelectNamesManager *manager) { @@ -246,31 +267,30 @@ entry_destroyed(EEntry *entry, ESelectNamesManager *manager) static void completion_handler (EEntry *entry, const gchar *text, gpointer user_data) { - ESelectNamesModel *snm = E_SELECT_NAMES_MODEL (gtk_object_get_data (GTK_OBJECT (entry), "select_names_model")); - ESelectNamesModelData *data = g_new0 (ESelectNamesModelData, 1); - EIterator *iterator; + ESelectNamesModel *snm; + EDestination *dest; + gint i, pos, start_pos, len; - data->type = E_SELECT_NAMES_MODEL_DATA_TYPE_CARD; - data->card = E_CARD (user_data); - gtk_object_ref (GTK_OBJECT (data->card)); - data->string = g_strdup (text); - - iterator = e_list_get_iterator (snm->data); - e_select_names_model_replace_item (snm, iterator, data); -} + if (user_data == NULL) + return; -static void -set_completion (EBook *book, EBookStatus status, EEntry *entry) -{ - ECompletion *addr_comp; + snm = E_SELECT_NAMES_MODEL (gtk_object_get_data (GTK_OBJECT (entry), "select_names_model")); + dest = E_DESTINATION (user_data); + + /* Sometimes I really long for garbage collection. Reference + counting makes you feel 31337, but sometimes it is just a + bitch. */ + gtk_object_ref (GTK_OBJECT (dest)); - addr_comp = e_address_completion_new (book); - e_entry_enable_completion_full (entry, addr_comp, -1, completion_handler); - gtk_object_unref (GTK_OBJECT (book)); + pos = e_entry_get_position (entry); + e_select_names_model_text_pos (snm, pos, &i, NULL, NULL); + e_select_names_model_replace (snm, i, dest); + e_select_names_model_name_pos (snm, i, &start_pos, &len); + e_entry_set_position (entry, start_pos+len); } -GtkWidget *e_select_names_manager_create_entry (ESelectNamesManager *manager, - const char *id) +GtkWidget * +e_select_names_manager_create_entry (ESelectNamesManager *manager, const char *id) { ETextModel *model; EIterator *iterator; @@ -280,27 +300,22 @@ GtkWidget *e_select_names_manager_create_entry ( if (!strcmp(section->id, id)) { ESelectNamesManagerEntry *entry; EEntry *eentry; - gchar *filename = gnome_util_prepend_user_home ("evolution/local/Contacts/addressbook.db"); - gchar *uri = g_strdup_printf ("file://%s", filename); - EBook *book; - - eentry = E_ENTRY(e_entry_new()); - gtk_object_set_data (GTK_OBJECT (eentry), "select_names_model", section->model); - - book = e_book_new (); - gtk_object_ref (GTK_OBJECT (book)); - e_book_load_uri (book, uri, (EBookCallback) set_completion, eentry); - g_free (uri); - g_free (filename); + ECompletion *comp; + + eentry = E_ENTRY (e_entry_new ()); + gtk_object_set_data (GTK_OBJECT (eentry), "select_names_model", section->model); - entry = g_new(ESelectNamesManagerEntry, 1); + entry = g_new (ESelectNamesManagerEntry, 1); entry->entry = eentry; entry->id = (char *)id; - model = e_select_names_text_model_new(section->model); - e_list_append(manager->entries, entry); + model = e_select_names_text_model_new (section->model); + e_list_append (manager->entries, entry); g_free(entry); + comp = e_select_names_completion_new (NULL, section->model); /* NULL == use local addressbook */ + e_entry_enable_completion_full (eentry, comp, 50, completion_handler); + gtk_object_set(GTK_OBJECT(eentry), "model", model, "editable", TRUE, @@ -356,8 +371,9 @@ e_select_names_clicked(ESelectNames *dialog, gint button, ESelectNamesManager *m gnome_dialog_close(GNOME_DIALOG(dialog)); } -void e_select_names_manager_activate_dialog (ESelectNamesManager *manager, - const char *id) +void +e_select_names_manager_activate_dialog (ESelectNamesManager *manager, + const char *id) { EIterator *iterator; @@ -386,9 +402,11 @@ void e_select_names_manager_activate_dialog ( } } +#if 0 /* Of type ECard */ -EList *e_select_names_manager_get_cards (ESelectNamesManager *manager, - const char *id) +EList * +e_select_names_manager_get_cards (ESelectNamesManager *manager, + const char *id) { EIterator *iterator; iterator = e_list_get_iterator(manager->sections); @@ -400,3 +418,4 @@ EList *e_select_names_manager_get_cards (ESel } return NULL; } +#endif diff --git a/addressbook/gui/component/select-names/e-select-names-manager.h b/addressbook/gui/component/select-names/e-select-names-manager.h index 13cddaac7d..7b68528669 100644 --- a/addressbook/gui/component/select-names/e-select-names-manager.h +++ b/addressbook/gui/component/select-names/e-select-names-manager.h @@ -41,14 +41,18 @@ ESelectNamesManager *e_select_names_manager_new (void); void e_select_names_manager_add_section (ESelectNamesManager *manager, const char *id, const char *title); +ESelectNamesModel *e_select_names_manager_get_source (ESelectNamesManager *manager, + const char *id); GtkWidget *e_select_names_manager_create_entry (ESelectNamesManager *manager, const char *id); void e_select_names_manager_activate_dialog (ESelectNamesManager *manager, const char *id); +#if 0 /* Of type ECard */ EList *e_select_names_manager_get_cards (ESelectNamesManager *manager, const char *id); +#endif /* Standard Gtk function */ GtkType e_select_names_manager_get_type (void); diff --git a/addressbook/gui/component/select-names/e-select-names-model.c b/addressbook/gui/component/select-names/e-select-names-model.c index 735840d2b3..e663768c21 100644 --- a/addressbook/gui/component/select-names/e-select-names-model.c +++ b/addressbook/gui/component/select-names/e-select-names-model.c @@ -1,9 +1,10 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Authors: - * Chris Lahey <clahey@helixcode.com> + * Chris Lahey <clahey@ximian.com> + * Jon Trowbidge <trow@ximian.com> * - * Copyright (C) 2000 Helix Code, Inc. + * Copyright (C) 2000, 2001 Ximian, Inc. */ #include <config.h> @@ -11,13 +12,19 @@ #include <stdlib.h> #include <string.h> #include <gtk/gtk.h> +#include <gtk/gtkmarshal.h> #include "e-select-names-model.h" #include <gal/util/e-util.h> #include "addressbook/backend/ebook/e-card-simple.h" +#define SEPARATOR ", " +#define SEPLEN 2 + + enum { E_SELECT_NAMES_MODEL_CHANGED, + E_SELECT_NAMES_MODEL_RESIZED, E_SELECT_NAMES_MODEL_LAST_SIGNAL }; @@ -29,6 +36,27 @@ enum { ARG_CARD, }; +enum { + NAME_DATA_BLANK, + NAME_DATA_CARD, + NAME_DATA_STRING +}; + +enum { + NAME_FORMAT_GIVEN_FIRST, + NAME_FORMAT_FAMILY_FIRST +}; + +struct _ESelectNamesModelPrivate { + gchar *id; + gchar *title; + + GList *data; /* of EDestination */ + gchar *text; + gchar *addr_text; +}; + + static void e_select_names_model_init (ESelectNamesModel *model); static void e_select_names_model_class_init (ESelectNamesModelClass *klass); @@ -36,15 +64,6 @@ static void e_select_names_model_destroy (GtkObject *object); static void e_select_names_model_set_arg (GtkObject *object, GtkArg *arg, guint arg_id); static void e_select_names_model_get_arg (GtkObject *object, GtkArg *arg, guint arg_id); -/** - * e_select_names_model_get_type: - * @void: - * - * Registers the &ESelectNamesModel class if necessary, and returns the type ID - * associated to it. - * - * Return value: The type ID of the &ESelectNamesModel class. - **/ GtkType e_select_names_model_get_type (void) { @@ -68,34 +87,22 @@ e_select_names_model_get_type (void) return model_type; } -/** - * e_select_names_model_new: - * @VCard: a string in vCard format - * - * Returns: a new #ESelectNamesModel that wraps the @VCard. - */ -ESelectNamesModel * -e_select_names_model_new (void) +typedef void (*GtkSignal_NONE__INT_INT_INT) (GtkObject *object, gint arg1, gint arg2, gint arg3, gpointer user_data); +static void +local_gtk_marshal_NONE__INT_INT_INT (GtkObject *object, + GtkSignalFunc func, + gpointer func_data, + GtkArg *args) { - ESelectNamesModel *model = E_SELECT_NAMES_MODEL(gtk_type_new(e_select_names_model_get_type())); - return model; + GtkSignal_NONE__INT_INT_INT rfunc; + rfunc = (GtkSignal_NONE__INT_INT_INT) func; + (* rfunc) (object, + GTK_VALUE_INT(args[0]), + GTK_VALUE_INT(args[1]), + GTK_VALUE_INT(args[2]), + func_data); } -/** - * e_select_names_model_new: - * @VCard: a string in vCard format - * - * Returns: a new #ESelectNamesModel that wraps the @VCard. - */ -ESelectNamesModel * -e_select_names_model_duplicate (ESelectNamesModel *old) -{ - ESelectNamesModel *model = E_SELECT_NAMES_MODEL(gtk_type_new(e_select_names_model_get_type())); - model->data = e_list_duplicate(old->data); - model->id = g_strdup(old->id); - model->title = g_strdup(old->title); - return model; -} static void e_select_names_model_class_init (ESelectNamesModelClass *klass) @@ -112,6 +119,14 @@ e_select_names_model_class_init (ESelectNamesModelClass *klass) gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0); + e_select_names_model_signals[E_SELECT_NAMES_MODEL_RESIZED] = + gtk_signal_new ("resized", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (ESelectNamesModelClass, resized), + local_gtk_marshal_NONE__INT_INT_INT, + GTK_TYPE_NONE, 3, GTK_TYPE_INT, GTK_TYPE_INT, GTK_TYPE_INT); + gtk_object_class_add_signals (object_class, e_select_names_model_signals, E_SELECT_NAMES_MODEL_LAST_SIGNAL); gtk_object_add_arg_type ("ESelectNamesModel::card", @@ -124,18 +139,31 @@ e_select_names_model_class_init (ESelectNamesModelClass *klass) object_class->set_arg = e_select_names_model_set_arg; } -/* - * ESelectNamesModel lifecycle management and vcard loading/saving. +/** + * e_select_names_model_init: */ +static void +e_select_names_model_init (ESelectNamesModel *model) +{ + model->priv = g_new0 (struct _ESelectNamesModelPrivate, 1); +} static void e_select_names_model_destroy (GtkObject *object) { - ESelectNamesModel *model; + ESelectNamesModel *model = E_SELECT_NAMES_MODEL (object); - model = E_SELECT_NAMES_MODEL (object); + g_free (model->priv->title); + g_free (model->priv->id); + + g_list_foreach (model->priv->data, (GFunc) gtk_object_unref, NULL); + g_list_free (model->priv->data); + + g_free (model->priv->text); + g_free (model->priv->addr_text); + + g_free (model->priv); - gtk_object_unref(GTK_OBJECT(model->data)); } @@ -172,302 +200,310 @@ e_select_names_model_get_arg (GtkObject *object, GtkArg *arg, guint arg_id) } } -static void * -data_copy(const void *sec, void *data) -{ - const ESelectNamesModelData *section = sec; - ESelectNamesModelData *newsec; - - newsec = g_new(ESelectNamesModelData, 1); - newsec->type = section->type; - newsec->card = section->card; - if (newsec->card) - gtk_object_ref(GTK_OBJECT(newsec->card)); - newsec->string = g_strdup(section->string); - return newsec; -} +/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **/ -static void -data_free(void *sec, void *data) +ESelectNamesModel * +e_select_names_model_new (void) { - ESelectNamesModelData *section = sec; - if (section->card) - gtk_object_unref(GTK_OBJECT(section->card)); - g_free(section->string); - g_free(section); + ESelectNamesModel *model; + model = E_SELECT_NAMES_MODEL (gtk_type_new (e_select_names_model_get_type ())); + return model; } -/** - * e_select_names_model_init: - */ -static void -e_select_names_model_init (ESelectNamesModel *model) +ESelectNamesModel * +e_select_names_model_duplicate (ESelectNamesModel *old) { - model->data = e_list_new(data_copy, data_free, model); -} + ESelectNamesModel *model = E_SELECT_NAMES_MODEL(gtk_type_new(e_select_names_model_get_type())); + GList *iter; -static void * -copy_func(const void *data, void *user_data) -{ - GtkObject *object = (void *) data; - if (object) - gtk_object_ref(object); - return object; + model->priv->id = g_strdup (old->priv->id); + model->priv->title = g_strdup (old->priv->title); + + for (iter = old->priv->data; iter != NULL; iter = g_list_next (iter)) { + EDestination *dup = e_destination_copy (E_DESTINATION (iter->data)); + model->priv->data = g_list_append (model->priv->data, dup); + } + + return model; } -static void -free_func(void *data, void *user_data) +const gchar * +e_select_names_model_get_textification (ESelectNamesModel *model) { - GtkObject *object = data; - if (object) - gtk_object_unref(object); + g_return_val_if_fail (model != NULL, NULL); + g_return_val_if_fail (E_IS_SELECT_NAMES_MODEL (model), NULL); + + if (model->priv->text == NULL) { + + if (model->priv->data == NULL) { + + model->priv->text = g_strdup (""); + + } else { + gchar **strv = g_new0 (gchar *, g_list_length (model->priv->data)+1); + gint i = 0; + GList *iter = model->priv->data; + + while (iter) { + EDestination *dest = E_DESTINATION (iter->data); + strv[i] = (gchar *) e_destination_get_string (dest); + if (strv[i] == NULL) + strv[i] = ""; + ++i; + iter = g_list_next (iter); + } + + model->priv->text = g_strjoinv (SEPARATOR, strv); + + g_free (strv); + } + } + + return model->priv->text; } -/* Of type ECard */ -EList *e_select_names_model_get_cards (ESelectNamesModel *model) +const gchar * +e_select_names_model_get_address_text (ESelectNamesModel *model) { - EList *list = e_list_new(copy_func, free_func, NULL); - EIterator *iterator = e_list_get_iterator(model->data); - EIterator *new_iterator = e_list_get_iterator(list); - - for (e_iterator_reset(iterator); e_iterator_is_valid(iterator); e_iterator_next(iterator)) { - ESelectNamesModelData *node = (void *) e_iterator_get(iterator); - ECard *card; - ECardSimple *simple; - if (node->card) { - card = node->card; - gtk_object_ref(GTK_OBJECT(card)); + g_return_val_if_fail (model != NULL, NULL); + g_return_val_if_fail (E_IS_SELECT_NAMES_MODEL (model), NULL); + + if (model->priv->addr_text == NULL) { + + if (model->priv->data == NULL) { + + model->priv->addr_text = g_strdup (""); + } else { - card = e_card_new(""); + gchar **strv = g_new0 (gchar *, g_list_length (model->priv->data)+1); + gint i = 0; + GList *iter = model->priv->data; + + while (iter) { + EDestination *dest = E_DESTINATION (iter->data); + strv[i] = (gchar *) e_destination_get_email_verbose (dest); + if (strv[i]) + ++i; + iter = g_list_next (iter); + } + + model->priv->addr_text = g_strjoinv (SEPARATOR, strv); + + g_free (strv); } - simple = e_card_simple_new(card); - e_card_simple_set_arbitrary(simple, "text_version", "string", node->string); - e_iterator_insert(new_iterator, card, FALSE); - gtk_object_unref(GTK_OBJECT(card)); - gtk_object_unref(GTK_OBJECT(simple)); } - return list; + + return model->priv->addr_text; } -EList *e_select_names_model_get_data (ESelectNamesModel *model) +gint +e_select_names_model_count (ESelectNamesModel *model) { - return model->data; + g_return_val_if_fail (model != NULL, 0); + g_return_val_if_fail (E_IS_SELECT_NAMES_MODEL (model), 0); + + return g_list_length (model->priv->data); } -static void -e_select_names_model_changed (ESelectNamesModel *model) +const EDestination * +e_select_names_model_get_destination (ESelectNamesModel *model, gint index) { - gtk_signal_emit(GTK_OBJECT(model), - e_select_names_model_signals[E_SELECT_NAMES_MODEL_CHANGED]); + g_return_val_if_fail (model != NULL, NULL); + g_return_val_if_fail (E_IS_SELECT_NAMES_MODEL (model), NULL); + g_return_val_if_fail (0 <= index, NULL); + g_return_val_if_fail (index < g_list_length (model->priv->data), NULL); + + return E_DESTINATION (g_list_nth_data (model->priv->data, index)); } -void -e_select_names_model_insert (ESelectNamesModel *model, - EIterator *iterator, /* Must be one of the iterators in the model, or NULL if the list is empty. */ - int index, - char *data) +ECard * +e_select_names_model_get_card (ESelectNamesModel *model, gint index) { - gchar **strings = e_strsplit(data, ",", -1); - int i; - if (iterator == NULL) { - ESelectNamesModelData new = {E_SELECT_NAMES_MODEL_DATA_TYPE_STRING_ADDRESS, NULL, ""}; + const EDestination *dest; - e_list_append(model->data, &new); - iterator = e_list_get_iterator(model->data); + g_return_val_if_fail (model && E_IS_SELECT_NAMES_MODEL (model), NULL); + g_return_val_if_fail (0 <= index, NULL); + g_return_val_if_fail (index < g_list_length (model->priv->data), NULL); + + dest = e_select_names_model_get_destination (model, index); + return dest ? e_destination_get_card (dest) : NULL; - index = 0; - } else { - gtk_object_ref(GTK_OBJECT(iterator)); - } - if (strings[0]) { - ESelectNamesModelData *node = (void *) e_iterator_get(iterator); - gchar *temp = g_strdup_printf("%.*s%s%s", index, node->string, strings[0], node->string + index); - g_free(node->string); - node->string = temp; - index += strlen(strings[0]); - - for (i = 1; strings[i]; i++) { - ESelectNamesModelData *node = (void *) e_iterator_get(iterator); - gchar *temp = g_strdup_printf("%.*s", index, node->string); - gchar *temp2 = g_strdup_printf("%s%s", strings[i], node->string + index); - - g_free(node->string); - node->type = E_SELECT_NAMES_MODEL_DATA_TYPE_STRING_ADDRESS; - node->string = temp; - if (node->card) - gtk_object_unref(GTK_OBJECT(node->card)); - node->card = NULL; - - node = g_new(ESelectNamesModelData, 1); - node->type = E_SELECT_NAMES_MODEL_DATA_TYPE_STRING_ADDRESS; - node->card = NULL; - node->string = temp2; - e_iterator_insert(iterator, node, 0); - index = strlen(strings[i]); - g_free(node->string); - g_free(node); - } - } - e_select_names_model_changed(model); - gtk_object_unref(GTK_OBJECT(iterator)); } -void -e_select_names_model_insert_length (ESelectNamesModel *model, - EIterator *iterator, /* Must be one of the iterators in the model. */ - int index, - char *data, - int length) +const gchar * +e_select_names_model_get_string (ESelectNamesModel *model, gint index) { - gchar *string = g_new(char, length + 1); - strncpy(string, data, length); - string[length] = 0; - e_select_names_model_insert(model, iterator, index, string); - g_free(string); + const EDestination *dest; + + g_return_val_if_fail (model && E_IS_SELECT_NAMES_MODEL (model), NULL); + g_return_val_if_fail (0 <= index, NULL); + g_return_val_if_fail (index < g_list_length (model->priv->data), NULL); + + dest = e_select_names_model_get_destination (model, index); + + return dest ? e_destination_get_string (dest) : ""; +} + +static void +e_select_names_model_changed (ESelectNamesModel *model) +{ + g_free (model->priv->text); + model->priv->text = NULL; + + g_free (model->priv->addr_text); + model->priv->addr_text = NULL; + + gtk_signal_emit(GTK_OBJECT(model), e_select_names_model_signals[E_SELECT_NAMES_MODEL_CHANGED]); } void -e_select_names_model_delete (ESelectNamesModel *model, - EIterator *iterator, /* Must be one of the iterators in the model. */ - int index, - int length) +e_select_names_model_insert (ESelectNamesModel *model, gint index, EDestination *dest) { - while (length > 0 && e_iterator_is_valid(iterator)) { - ESelectNamesModelData *node = (void *) e_iterator_get(iterator); - int this_length = strlen(node->string); - if (this_length <= index + length) { - gchar *temp = g_strdup_printf("%.*s", index, node->string); - g_free(node->string); - node->string = temp; - length -= this_length - index; - } else { - gchar *temp = g_strdup_printf("%.*s%s", index, node->string, node->string + index + length); - g_free(node->string); - node->string = temp; - break; - } - - if (length > 0) { - e_iterator_next(iterator); - if (e_iterator_is_valid(iterator)) { - ESelectNamesModelData *node2 = (void *) e_iterator_get(iterator); - gchar *temp = g_strdup_printf("%s%s", node->string, node2->string); - g_free(node2->string); - node2->string = temp; - e_iterator_prev(iterator); - e_iterator_delete(iterator); - length --; - } - } - } - e_select_names_model_changed(model); + g_return_if_fail (model != NULL); + g_return_if_fail (E_IS_SELECT_NAMES_MODEL (model)); + g_return_if_fail (0 <= index && index <= g_list_length (model->priv->data)); + g_return_if_fail (dest && E_IS_DESTINATION (dest)); + + model->priv->data = g_list_insert (model->priv->data, dest, index); + + gtk_object_ref (GTK_OBJECT (dest)); + gtk_object_sink (GTK_OBJECT (dest)); + + e_select_names_model_changed (model); } void -e_select_names_model_replace (ESelectNamesModel *model, - EIterator *iterator, /* Must be one of the iterators in the model. */ - int index, - int length, - char *data) +e_select_names_model_replace (ESelectNamesModel *model, gint index, EDestination *dest) { - if (iterator == NULL) { - ESelectNamesModelData new = {E_SELECT_NAMES_MODEL_DATA_TYPE_STRING_ADDRESS, NULL, ""}; + GList *node; + gint old_strlen=0, new_strlen=0; - e_list_append(model->data, &new); - iterator = e_list_get_iterator(model->data); + g_return_if_fail (model != NULL); + g_return_if_fail (E_IS_SELECT_NAMES_MODEL (model)); + g_return_if_fail (model->priv->data == NULL || (0 <= index && index < g_list_length (model->priv->data))); + g_return_if_fail (dest && E_IS_DESTINATION (dest)); + + new_strlen = e_destination_get_strlen (dest); - index = 0; + if (model->priv->data == NULL) { + model->priv->data = g_list_append (model->priv->data, dest); } else { - gtk_object_ref(GTK_OBJECT(iterator)); - } - while (length > 0 && e_iterator_is_valid(iterator)) { - ESelectNamesModelData *node = (void *) e_iterator_get(iterator); - int this_length = strlen(node->string); - if (this_length <= index + length) { - gchar *temp = g_strdup_printf("%.*s", index, node->string); - g_free(node->string); - node->string = temp; - length -= this_length - index; - } else { - gchar *temp = g_strdup_printf("%.*s%s", index, node->string, node->string + index + length); - g_free(node->string); - node->string = temp; - length = 0; - } + + node = g_list_nth (model->priv->data, index); - if (length > 0) { - e_iterator_next(iterator); - if (e_iterator_is_valid(iterator)) { - ESelectNamesModelData *node2 = (void *) e_iterator_get(iterator); - gchar *temp = g_strdup_printf("%s%s", node->string, node2->string); - g_free(node2->string); - node2->string = temp; - e_iterator_prev(iterator); - e_iterator_delete(iterator); - } + if (node->data != dest) { + old_strlen = e_destination_get_strlen (E_DESTINATION (node->data)); + + gtk_object_unref (GTK_OBJECT (node->data)); + + node->data = dest; + gtk_object_ref (GTK_OBJECT (dest)); + gtk_object_sink (GTK_OBJECT (dest)); } } - if (!e_iterator_is_valid(iterator)) { - ESelectNamesModelData *node; - e_iterator_last(iterator); - if (e_iterator_is_valid(iterator)) { - node = (void *) e_iterator_get(iterator); - index = strlen(node->string); - } else - index = 0; - } - e_select_names_model_insert (model, iterator, index, data); - gtk_object_unref(GTK_OBJECT(iterator)); + + e_select_names_model_changed (model); + + gtk_signal_emit (GTK_OBJECT (model), e_select_names_model_signals[E_SELECT_NAMES_MODEL_RESIZED], + index, old_strlen, new_strlen); } -static void -esnm_add_item_real (ESelectNamesModel *model, - EIterator *iterator, /* NULL for at the beginning. */ - gboolean before, - ESelectNamesModelData *data) +void +e_select_names_model_delete (ESelectNamesModel *model, gint index) { - if (iterator == NULL) - iterator = e_list_get_iterator(model->data); - else - gtk_object_ref(GTK_OBJECT(iterator)); + GList *node; - e_iterator_insert(iterator, data, before); + g_return_if_fail (model != NULL); + g_return_if_fail (E_IS_SELECT_NAMES_MODEL (model)); + g_return_if_fail (0 <= index && index < g_list_length (model->priv->data)); + + node = g_list_nth (model->priv->data, index); + gtk_object_unref (GTK_OBJECT (node->data)); - gtk_object_unref(GTK_OBJECT(iterator)); -} + model->priv->data = g_list_remove_link (model->priv->data, node); + g_list_free_1 (node); -static void -esnm_remove_item_real (ESelectNamesModel *model, - EIterator *iterator) -{ - e_iterator_delete(iterator); + e_select_names_model_changed (model); } void -e_select_names_model_add_item (ESelectNamesModel *model, - EIterator *iterator, /* NULL for at the beginning. */ - ESelectNamesModelData *data) +e_select_names_model_delete_all (ESelectNamesModel *model) { - esnm_add_item_real(model, iterator, FALSE, data); - e_select_names_model_changed(model); + g_return_if_fail (model != NULL); + + g_list_foreach (model->priv->data, (GFunc) gtk_object_unref, NULL); + g_list_free (model->priv->data); + model->priv->data = NULL; + + e_select_names_model_changed (model); } void -e_select_names_model_replace_item (ESelectNamesModel *model, - EIterator *iterator, - ESelectNamesModelData *data) +e_select_names_model_name_pos (ESelectNamesModel *model, gint index, gint *pos, gint *length) { - esnm_remove_item_real(model, iterator); - esnm_add_item_real(model, iterator, FALSE, data); - e_select_names_model_changed(model); + gint rp = 0, i, len = 0; + GList *iter; + + g_return_if_fail (model != NULL); + g_return_if_fail (E_IS_SELECT_NAMES_MODEL (model)); + + i = 0; + iter = model->priv->data; + while (iter && i <= index) { + rp += len + (i > 0 ? SEPLEN : 0); + len = e_destination_get_strlen (E_DESTINATION (iter->data)); + ++i; + iter = g_list_next (iter); + } + + if (i <= index) { + rp = -1; + len = 0; + } + + if (pos) + *pos = rp; + if (length) + *length = len; } void -e_select_names_model_remove_item (ESelectNamesModel *model, - EIterator *iterator) +e_select_names_model_text_pos (ESelectNamesModel *model, gint pos, gint *index, gint *start_pos, gint *length) { - esnm_remove_item_real(model, iterator); - e_select_names_model_changed(model); -} + GList *iter; + gint len = 0, i = 0, sp = 0, adj = 0; + + g_return_if_fail (model != NULL); + g_return_if_fail (E_IS_SELECT_NAMES_MODEL (model)); + + iter = model->priv->data; + while (iter != NULL) { + len = e_destination_get_strlen (E_DESTINATION (iter->data)); + if (sp <= pos && pos <= sp + len + adj) + break; + + sp += len + adj + 1; + adj = 1; + ++i; + + iter = g_list_next (iter); + } + + if (i != 0) + ++sp; /* skip past "magic space" */ + + if (iter == NULL) { + i = -1; + sp = -1; + len = 0; + } + + if (index) + *index = i; + if (start_pos) + *start_pos = sp; + if (length) + *length = len; +} diff --git a/addressbook/gui/component/select-names/e-select-names-model.h b/addressbook/gui/component/select-names/e-select-names-model.h index 576d5e6a9a..4b3413f983 100644 --- a/addressbook/gui/component/select-names/e-select-names-model.h +++ b/addressbook/gui/component/select-names/e-select-names-model.h @@ -1,9 +1,11 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + /* * Authors: - * Chris Lahey <clahey@helixcode.com> + * Chris Lahey <clahey@ximian.com> + * Jon Trowbridge <trow@ximian.com> * - * Copyright (C) 2000 Helix Code, Inc. + * Copyright (C) 2000, 2001 Ximian, Inc. */ #ifndef __E_SELECT_NAMES_MODEL_H__ @@ -14,6 +16,7 @@ #include <stdio.h> #include <e-util/e-list.h> #include <addressbook/backend/ebook/e-card.h> +#include <addressbook/backend/ebook/e-destination.h> #define E_TYPE_SELECT_NAMES_MODEL (e_select_names_model_get_type ()) #define E_SELECT_NAMES_MODEL(obj) (GTK_CHECK_CAST ((obj), E_TYPE_SELECT_NAMES_MODEL, ESelectNamesModel)) @@ -21,76 +24,42 @@ #define E_IS_SELECT_NAMES_MODEL(obj) (GTK_CHECK_TYPE ((obj), E_TYPE_SELECT_NAMES_MODEL)) #define E_IS_SELECT_NAMES_MODEL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), E_TYPE_SELECT_NAMES_MODEL)) -typedef enum _ESelectNamesModelDataType ESelectNamesModelDataType; -typedef struct _ESelectNamesModelData ESelectNamesModelData; typedef struct _ESelectNamesModel ESelectNamesModel; typedef struct _ESelectNamesModelClass ESelectNamesModelClass; - -enum _ESelectNamesModelDataType { - E_SELECT_NAMES_MODEL_DATA_TYPE_CARD, - E_SELECT_NAMES_MODEL_DATA_TYPE_STRING_ADDRESS, -}; - -struct _ESelectNamesModelData { - ESelectNamesModelDataType type; - ECard *card; - char *string; -}; +struct _ESelectNamesModelPrivate; struct _ESelectNamesModel { GtkObject object; - - char *id; - char *title; - EList *data; /* Of type ESelectNamesModelData. */ + struct _ESelectNamesModelPrivate *priv; }; struct _ESelectNamesModelClass { GtkObjectClass parent_class; void (*changed) (ESelectNamesModel *model); + void (*resized) (ESelectNamesModel *model, gint index, gint old_len, gint new_len); }; -ESelectNamesModel *e_select_names_model_new (void); -ESelectNamesModel *e_select_names_model_duplicate (ESelectNamesModel *old); +GtkType e_select_names_model_get_type (void); -/* These lengths are allowed to go over objects and act just like the text model does. */ -void e_select_names_model_insert (ESelectNamesModel *model, - EIterator *iterator, /* Must be one of the iterators in the model. */ - int index, - char *data); -void e_select_names_model_insert_length (ESelectNamesModel *model, - EIterator *iterator, /* Must be one of the iterators in the model. */ - int index, - char *data, - int length); -void e_select_names_model_delete (ESelectNamesModel *model, - EIterator *iterator, /* Must be one of the iterators in the model. */ - int index, - int length); -void e_select_names_model_replace (ESelectNamesModel *model, - EIterator *iterator, /* Must be one of the iterators in the model. */ - int index, - int replacement_length, - char *data); +ESelectNamesModel *e_select_names_model_new (void); +ESelectNamesModel *e_select_names_model_duplicate (ESelectNamesModel *old); -void e_select_names_model_add_item (ESelectNamesModel *model, - EIterator *iterator, /* NULL for at the beginning. */ - ESelectNamesModelData *data); -void e_select_names_model_replace_item (ESelectNamesModel *model, - EIterator *iterator, - ESelectNamesModelData *data); -void e_select_names_model_remove_item (ESelectNamesModel *model, - EIterator *iterator); +const gchar *e_select_names_model_get_textification (ESelectNamesModel *model); +const gchar *e_select_names_model_get_address_text (ESelectNamesModel *model); -/* Of type ECard */ -EList *e_select_names_model_get_cards (ESelectNamesModel *model); +gint e_select_names_model_count (ESelectNamesModel *model); +const EDestination *e_select_names_model_get_destination (ESelectNamesModel *model, gint index); +ECard *e_select_names_model_get_card (ESelectNamesModel *model, gint index); +const gchar *e_select_names_model_get_string (ESelectNamesModel *model, gint index); -/* Of type ESelectNamesModelData */ -EList *e_select_names_model_get_data (ESelectNamesModel *model); +void e_select_names_model_insert (ESelectNamesModel *model, gint index, EDestination *dest); +void e_select_names_model_replace (ESelectNamesModel *model, gint index, EDestination *dest); +void e_select_names_model_delete (ESelectNamesModel *model, gint index); +void e_select_names_model_delete_all (ESelectNamesModel *model); -/* Standard Gtk function */ -GtkType e_select_names_model_get_type (void); +void e_select_names_model_name_pos (ESelectNamesModel *model, gint index, gint *pos, gint *length); +void e_select_names_model_text_pos (ESelectNamesModel *model, gint pos, gint *index, gint *start_pos, gint *length); #endif /* ! __E_SELECT_NAMES_MODEL_H__ */ diff --git a/addressbook/gui/component/select-names/e-select-names-table-model.c b/addressbook/gui/component/select-names/e-select-names-table-model.c index 37abcddc70..7b0e27195b 100644 --- a/addressbook/gui/component/select-names/e-select-names-table-model.c +++ b/addressbook/gui/component/select-names/e-select-names-table-model.c @@ -108,38 +108,31 @@ static void fill_in_info (ESelectNamesTableModel *model) { if (model->source) { - EList *list = e_select_names_model_get_data(model->source); - EIterator *iterator = e_list_get_iterator(list); - int count = 0; - for (e_iterator_reset(iterator); e_iterator_is_valid(iterator); e_iterator_next(iterator)) { - count ++; - } + int count = e_select_names_model_count (model->source); + gint i; + model->count = count; model->data = g_new(ESelectNamesTableModelData, count); - count = 0; - for (e_iterator_reset(iterator); e_iterator_is_valid(iterator); e_iterator_next(iterator)) { - const ESelectNamesModelData *data = e_iterator_get(iterator); - switch (data->type) { - case E_SELECT_NAMES_MODEL_DATA_TYPE_CARD: { - ECardSimple *simple = e_card_simple_new(data->card); - model->data[count].name = e_card_simple_get(simple, E_CARD_SIMPLE_FIELD_FULL_NAME); - if ((model->data[count].name == 0) || *model->data[count].name == 0) { - model->data[count].name = e_card_simple_get(simple, E_CARD_SIMPLE_FIELD_ORG); + + for (i = 0; i < count; ++i) { + const EDestination *dest = e_select_names_model_get_destination (model->source, i); + ECard *card = dest ? e_destination_get_card (dest) : NULL; + + if (card) { + ECardSimple *simple = e_card_simple_new(card); + model->data[i].name = e_card_simple_get(simple, E_CARD_SIMPLE_FIELD_FULL_NAME); + if ((model->data[i].name == 0) || *model->data[i].name == 0) { + model->data[i].name = e_card_simple_get(simple, E_CARD_SIMPLE_FIELD_ORG); } - if (model->data[count].name == 0) - model->data[count].name = g_strdup(""); - model->data[count].email = e_card_simple_get(simple, E_CARD_SIMPLE_FIELD_EMAIL); - if (model->data[count].email == 0) - model->data[count].email = g_strdup(""); + if (model->data[i].name == 0) + model->data[i].name = g_strdup(""); + model->data[i].email = e_card_simple_get(simple, E_CARD_SIMPLE_FIELD_EMAIL); + if (model->data[i].email == 0) + model->data[i].email = g_strdup(""); gtk_object_unref(GTK_OBJECT(simple)); - count ++; - break; - } - case E_SELECT_NAMES_MODEL_DATA_TYPE_STRING_ADDRESS: - model->data[count].name = e_strdup_strip(data->string); - model->data[count].email = e_strdup_strip(data->string); - count ++; - break; + } else { + model->data[i].name = g_strdup (e_destination_get_string (dest)); + model->data[i].email = g_strdup (model->data[i].name); } } } else { diff --git a/addressbook/gui/component/select-names/e-select-names-text-model.c b/addressbook/gui/component/select-names/e-select-names-text-model.c index 13054eb10e..62e1676d20 100644 --- a/addressbook/gui/component/select-names/e-select-names-text-model.c +++ b/addressbook/gui/component/select-names/e-select-names-text-model.c @@ -1,45 +1,51 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Authors: - * Chris Lahey <clahey@helixcode.com> + * Chris Lahey <clahey@ximian.com> + * Jon Trowbridge <trow@ximian.com> * - * Copyright (C) 2000 Helix Code, Inc. + * Copyright (C) 2000, 2001 Ximian, Inc. */ #include <config.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <ctype.h> #include <gtk/gtk.h> +#include <gal/e-text/e-text-model-repos.h> #include <addressbook/contact-editor/e-contact-editor.h> #include "e-select-names-text-model.h" +static FILE *out = NULL; /* stream for debugging spew */ + +#define SEPLEN 2 + /* Object argument IDs */ enum { ARG_0, ARG_SOURCE, }; -static void e_select_names_text_model_init (ESelectNamesTextModel *model); static void e_select_names_text_model_class_init (ESelectNamesTextModelClass *klass); - +static void e_select_names_text_model_init (ESelectNamesTextModel *model); static void e_select_names_text_model_destroy (GtkObject *object); static void e_select_names_text_model_set_arg (GtkObject *object, GtkArg *arg, guint arg_id); static void e_select_names_text_model_get_arg (GtkObject *object, GtkArg *arg, guint arg_id); -static void e_select_names_text_model_set_text (ETextModel *model, const gchar *text); -static void e_select_names_text_model_insert (ETextModel *model, gint position, const gchar *text); -static void e_select_names_text_model_insert_length (ETextModel *model, gint position, const gchar *text, gint length); -static void e_select_names_text_model_delete (ETextModel *model, gint position, gint length); +static void e_select_names_text_model_set_source (ESelectNamesTextModel *model, ESelectNamesModel *source); + +static const gchar *e_select_names_text_model_get_text (ETextModel *model); +static void e_select_names_text_model_set_text (ETextModel *model, const gchar *text); +static void e_select_names_text_model_insert (ETextModel *model, gint position, const gchar *text); +static void e_select_names_text_model_insert_length (ETextModel *model, gint position, const gchar *text, gint length); +static void e_select_names_text_model_delete (ETextModel *model, gint position, gint length); static gint e_select_names_text_model_obj_count (ETextModel *model); static const gchar *e_select_names_text_model_get_nth_obj (ETextModel *model, gint n, gint *len); static void e_select_names_text_model_activate_obj (ETextModel *model, gint n); -static void e_select_names_text_model_model_changed (ESelectNamesModel *source, - ESelectNamesTextModel *model); - ETextModelClass *parent_class; #define PARENT_TYPE e_text_model_get_type() @@ -76,23 +82,6 @@ e_select_names_text_model_get_type (void) return model_type; } -/** - * e_select_names_text_model_new: - * @VCard: a string in vCard format - * - * Returns: a new #ESelectNamesTextModel that wraps the @VCard. - */ -ETextModel * -e_select_names_text_model_new (ESelectNamesModel *source) -{ - ETextModel *model = E_TEXT_MODEL(gtk_type_new(e_select_names_text_model_get_type())); - gtk_object_set(GTK_OBJECT(model), - "source", source, - NULL); - e_select_names_text_model_model_changed (source, E_SELECT_NAMES_TEXT_MODEL(model)); - return model; -} - static void e_select_names_text_model_class_init (ESelectNamesTextModelClass *klass) { @@ -111,6 +100,7 @@ e_select_names_text_model_class_init (ESelectNamesTextModelClass *klass) object_class->get_arg = e_select_names_text_model_get_arg; object_class->set_arg = e_select_names_text_model_set_arg; + text_model_class->get_text = e_select_names_text_model_get_text; text_model_class->set_text = e_select_names_text_model_set_text; text_model_class->insert = e_select_names_text_model_insert; text_model_class->insert_length = e_select_names_text_model_insert_length; @@ -119,323 +109,590 @@ e_select_names_text_model_class_init (ESelectNamesTextModelClass *klass) text_model_class->obj_count = e_select_names_text_model_obj_count; text_model_class->get_nth_obj = e_select_names_text_model_get_nth_obj; text_model_class->object_activated = e_select_names_text_model_activate_obj; + + if (getenv ("EVO_DEBUG_SELECT_NAMES_TEXT_MODEL")) { + out = fopen ("/tmp/evo-debug-select-names-text-model", "w"); + if (out) + setvbuf (out, NULL, _IONBF, 0); + } } -static int -get_length(EIterator *iterator) +static void +dump_model (ESelectNamesTextModel *text_model) { - const ESelectNamesModelData *data = e_iterator_get(iterator); - return strlen(data->string); + ESelectNamesModel *model = text_model->source; + gint i; + + if (out == NULL) + return; + + fprintf (out, "\n*** Model State: count=%d\n", e_select_names_model_count (model)); + + for (i=0; i<e_select_names_model_count (model); ++i) + fprintf (out, "[%d] \"%s\" %s\n", i, + e_select_names_model_get_string (model, i), + e_select_names_model_get_card (model, i) ? "<card>" : ""); + fprintf (out, "\n"); } static void -e_select_names_text_model_set_text (ETextModel *model, const gchar *text) +e_select_names_text_model_init (ESelectNamesTextModel *model) { - ESelectNamesModel *source = E_SELECT_NAMES_TEXT_MODEL(model)->source; - EIterator *iterator = e_list_get_iterator(e_select_names_model_get_data(source)); - int length = e_text_model_get_text_length (model); +} + +static void +e_select_names_text_model_destroy (GtkObject *object) +{ + ESelectNamesTextModel *model; - e_iterator_reset(iterator); - if (!e_iterator_is_valid(iterator)) { - gtk_object_unref(GTK_OBJECT(iterator)); - iterator = NULL; - } - e_select_names_model_replace(source, - iterator, - 0, - length, - (gchar *) text); - if (iterator) - gtk_object_unref(GTK_OBJECT(iterator)); + model = E_SELECT_NAMES_TEXT_MODEL (object); + + e_select_names_text_model_set_source (model, NULL); + + if (GTK_OBJECT_CLASS(parent_class)->destroy) + GTK_OBJECT_CLASS(parent_class)->destroy(object); } static void -e_select_names_text_model_insert (ETextModel *model, gint position, const gchar *text) +e_select_names_text_model_set_arg (GtkObject *object, GtkArg *arg, guint arg_id) { - ESelectNamesModel *source = E_SELECT_NAMES_TEXT_MODEL(model)->source; - EIterator *iterator = e_list_get_iterator(e_select_names_model_get_data(source)); + ESelectNamesTextModel *model; + + model = E_SELECT_NAMES_TEXT_MODEL (object); - for (e_iterator_reset(iterator); e_iterator_is_valid(iterator); e_iterator_next(iterator)) { - int this_length = get_length(iterator); - if (position <= this_length) { - break; - } else { - position -= this_length + 1; - } - } - if (!e_iterator_is_valid(iterator)) { - gtk_object_unref(GTK_OBJECT(iterator)); - iterator = NULL; + switch (arg_id) { + case ARG_SOURCE: + e_select_names_text_model_set_source(model, E_SELECT_NAMES_MODEL (GTK_VALUE_OBJECT (*arg))); + break; + default: + return; } - e_select_names_model_insert(source, - iterator, - position, - (gchar *) text); - if (iterator) - gtk_object_unref(GTK_OBJECT(iterator)); } static void -e_select_names_text_model_insert_length (ETextModel *model, gint position, const gchar *text, gint length) +e_select_names_text_model_get_arg (GtkObject *object, GtkArg *arg, guint arg_id) { - ESelectNamesModel *source = E_SELECT_NAMES_TEXT_MODEL(model)->source; - EIterator *iterator = e_list_get_iterator(e_select_names_model_get_data(source)); + ESelectNamesTextModel *model; - for (e_iterator_reset(iterator); e_iterator_is_valid(iterator); e_iterator_next(iterator)) { - int this_length = get_length(iterator); - if (position <= this_length) { - break; - } else { - position -= this_length + 1; - } - } - if (!e_iterator_is_valid(iterator)) { - gtk_object_unref(GTK_OBJECT(iterator)); - iterator = NULL; + model = E_SELECT_NAMES_TEXT_MODEL (object); + + switch (arg_id) { + case ARG_SOURCE: + GTK_VALUE_OBJECT(*arg) = GTK_OBJECT(model->source); + break; + default: + arg->type = GTK_TYPE_INVALID; + break; } - e_select_names_model_insert_length(source, - iterator, - position, - (gchar *) text, - length); - if (iterator) - gtk_object_unref(GTK_OBJECT(iterator)); } static void -e_select_names_text_model_delete (ETextModel *model, gint position, gint length) +resize_cb (ESelectNamesModel *source, gint index, gint old_len, gint new_len, ETextModel *model) { - ESelectNamesModel *source = E_SELECT_NAMES_TEXT_MODEL(model)->source; - EIterator *iterator = e_list_get_iterator(e_select_names_model_get_data(source)); - - for (e_iterator_reset(iterator); e_iterator_is_valid(iterator); e_iterator_next(iterator)) { - int this_length = get_length(iterator); - if (position <= this_length) { - e_select_names_model_delete(source, - iterator, - position, - length); - break; - } else { - position -= this_length + 1; - } + EReposDeleteShift repos_del; + EReposInsertShift repos_ins; + gint pos; + + e_select_names_model_name_pos (source, index, &pos, NULL); + + if (new_len < old_len) { + + repos_del.model = model; + repos_del.pos = pos; + repos_del.len = old_len - new_len; + e_text_model_reposition (model, e_repos_delete_shift, &repos_del); + + } else if (old_len < new_len) { + + repos_ins.model = model; + repos_ins.pos = pos; + repos_ins.len = new_len - old_len; + e_text_model_reposition (model, e_repos_insert_shift, &repos_ins); + } - if (iterator) - gtk_object_unref(GTK_OBJECT(iterator)); } -static gint -e_select_names_text_model_obj_count (ETextModel *model) + +static void +e_select_names_text_model_set_source (ESelectNamesTextModel *model, + ESelectNamesModel *source) { - ESelectNamesModel *source = E_SELECT_NAMES_TEXT_MODEL (model)->source; - EIterator *iterator = e_list_get_iterator (e_select_names_model_get_data (source)); - gint count = 0; + if (source == model->source) + return; - for (e_iterator_reset (iterator); e_iterator_is_valid (iterator); e_iterator_next (iterator)) { - const ESelectNamesModelData *data = e_iterator_get (iterator); - if (data->type == E_SELECT_NAMES_MODEL_DATA_TYPE_CARD) - ++count; + if (model->source) { + gtk_signal_disconnect (GTK_OBJECT (model->source), model->source_changed_id); + gtk_signal_disconnect (GTK_OBJECT (model->source), model->source_resize_id); + gtk_object_unref (GTK_OBJECT (model->source)); } - return count; + model->source = source; + + if (model->source) { + gtk_object_ref (GTK_OBJECT (model->source)); + model->source_changed_id = gtk_signal_connect_object (GTK_OBJECT(model->source), + "changed", + GTK_SIGNAL_FUNC (e_text_model_changed), + GTK_OBJECT (model)); + model->source_resize_id = gtk_signal_connect (GTK_OBJECT(model->source), + "resized", + GTK_SIGNAL_FUNC (resize_cb), + model); + } +} + +ETextModel * +e_select_names_text_model_new (ESelectNamesModel *source) +{ + ETextModel *model = E_TEXT_MODEL (gtk_type_new (e_select_names_text_model_get_type())); + e_select_names_text_model_set_source (E_SELECT_NAMES_TEXT_MODEL (model), source); + return model; } static const gchar * -e_select_names_text_model_get_nth_obj (ETextModel *model, gint n, gint *len) +e_select_names_text_model_get_text (ETextModel *model) { - ESelectNamesTextModel *select_text_model = E_SELECT_NAMES_TEXT_MODEL (model); - ESelectNamesModel *source = select_text_model->source; - EIterator *iterator = e_list_get_iterator (e_select_names_model_get_data (source)); - const ESelectNamesModelData *data; - gint i, pos; - - pos = 0; - i = 0; - e_iterator_reset (iterator); - while (e_iterator_is_valid (iterator) && n > 0) { - gint len; - data = e_iterator_get (iterator); - len = strlen (data->string); + ESelectNamesTextModel *snm = E_SELECT_NAMES_TEXT_MODEL(model); - pos += len + 1; /* advance and extra space for comma */ - ++i; - if (data->type == E_SELECT_NAMES_MODEL_DATA_TYPE_CARD) - --n; + return snm ? e_select_names_model_get_textification (snm->source) : ""; +} - if (n >= 0) - e_iterator_next (iterator); - } +static void +e_select_names_text_model_set_text (ETextModel *model, const gchar *text) +{ + ESelectNamesTextModel *snm = E_SELECT_NAMES_TEXT_MODEL(model); - if (len) { - data = e_iterator_get (iterator); - *len = strlen (data->string); - } + e_select_names_model_delete_all (snm->source); + e_select_names_text_model_insert (model, 0, text); +} - return e_text_model_get_text (model) + pos; +static void +e_select_names_text_model_insert (ETextModel *model, gint position, const gchar *text) +{ + e_select_names_text_model_insert_length (model, position, text, strlen (text)); } static void -e_select_names_text_model_activate_obj (ETextModel *model, gint n) +e_select_names_text_model_insert_length (ETextModel *model, gint pos, const gchar *text, gint length) { - ESelectNamesTextModel *select_text_model = E_SELECT_NAMES_TEXT_MODEL (model); - ESelectNamesModel *source = select_text_model->source; - EIterator *iterator = e_list_get_iterator (e_select_names_model_get_data (source)); - const ESelectNamesModelData *data; - const ECard *card; - EContactEditor *contact_editor; + ESelectNamesModel *source = E_SELECT_NAMES_TEXT_MODEL (model)->source; + gint i; - e_iterator_reset (iterator); - while (e_iterator_is_valid (iterator) && n > 0) { - data = e_iterator_get (iterator); - if (data->type == E_SELECT_NAMES_MODEL_DATA_TYPE_CARD) - --n; + g_return_if_fail (model != NULL); + g_return_if_fail (E_IS_SELECT_NAMES_TEXT_MODEL (model)); - if (n >= 0) - e_iterator_next (iterator); + if (out) { + gchar *tmp = g_strndup (text, length); + fprintf (out, ">> insert \"%s\" (len=%d) at %d\n", tmp, length, pos); + g_free (tmp); } - data = e_iterator_get (iterator); - card = E_CARD (data->card); + pos = CLAMP (pos, 0, strlen (e_select_names_model_get_textification (source))); - /* FIXME: const incorrectness here. */ - contact_editor = e_contact_editor_new ((ECard *) card, FALSE); - e_contact_editor_raise (contact_editor); + /* We want to control all cursor motions ourselves, rather than taking hints + from the ESelectNamesModel. */ + gtk_signal_handler_block (GTK_OBJECT (source), E_SELECT_NAMES_TEXT_MODEL (model)->source_resize_id); + + /* We handle this one character at a time. */ + + for (i = 0; i < length && text[i]; ++i) { + gint index, start_pos, text_len; + + if (out) + fprintf (out, "processing [%c]\n", text[i]); + + e_select_names_model_text_pos (source, pos, &index, &start_pos, &text_len); + + if (out) + fprintf (out, "index=%d start_pos=%d text_len=%d\n", index, start_pos, text_len); + + + if (text[i] == ',') { + + /* This is the case of hitting , first thing in an empty entry */ + if (index == -1) { + EReposAbsolute repos; + + e_select_names_model_insert (source, 0, e_destination_new ()); + e_select_names_model_insert (source, 0, e_destination_new ()); + + repos.model = model; + repos.pos = -1; /* At end */ + e_text_model_reposition (model, e_repos_absolute, &repos); + + + } else if (pos <= start_pos || pos == start_pos + text_len) { + EReposInsertShift repos; + gint ins_point = index; + + if (text_len != 0 && pos == start_pos + text_len) + ++ins_point; + + /* Block adjacent blank cards. */ + if (! ((ins_point < e_select_names_model_count (source) && + (e_select_names_model_get_string (source, ins_point) == NULL)) + || (ins_point > 0 && (e_select_names_model_get_string (source, ins_point-1) == NULL)))) { + + e_select_names_model_insert (source, ins_point, e_destination_new ()); + + repos.model = model; + repos.pos = pos; + repos.len = SEPLEN; + e_text_model_reposition (model, e_repos_insert_shift, &repos); + } + + } else { + EReposInsertShift repos; + gint offset = MAX (pos - start_pos, 0); + const gchar *str = e_select_names_model_get_string (source, index); + gchar *str1 = g_strndup (str, offset); + gchar *str2 = g_strdup (str+offset); + EDestination *d1 = e_destination_new (), *d2 = e_destination_new (); + + e_destination_set_string (d1, str1); + e_destination_set_string (d2, str2); + + e_select_names_model_replace (source, index, d1); + e_select_names_model_insert (source, index+1, d2); + + g_free (str1); + g_free (str2); + + repos.model = model; + repos.pos = pos; + repos.len = SEPLEN; + e_text_model_reposition (model, e_repos_insert_shift, &repos); + } + + } else { + EReposInsertShift repos; + gint offset = MAX (pos - start_pos, 0); + const gchar *str; + gchar *new_str = NULL; + gint this_length = 1; + gboolean whitespace = isspace ((gint) text[i]); + + str = index >= 0 ? e_select_names_model_get_string (source, index) : NULL; + if (str) { + if (pos <= start_pos) { + if (whitespace) { + /* swallow leading whitespace */ + this_length = 0; + } else { + /* Adjust for our "magic white space" */ + new_str = g_strdup_printf("%c%s%s", text[i], pos < start_pos ? " " : "", str); + if (pos < start_pos) + ++this_length; + } + } else { + new_str = g_strdup_printf ("%.*s%c%s", offset, str, text[i], str + offset); + } + } else { + if (whitespace) { + /* swallow leading whitespace */ + this_length = 0; + } else { + new_str = g_strdup_printf ("%c", text[i]); + } + } + + if (new_str) { + + EDestination *dest = e_destination_new (); + e_destination_set_string (dest, new_str); + + e_select_names_model_replace (source, index, dest); + + if (this_length > 0) { + repos.model = model; + repos.pos = pos; + repos.len = this_length; + e_text_model_reposition (model, e_repos_insert_shift, &repos); + } + + g_free (new_str); + } + + } + } + + dump_model (E_SELECT_NAMES_TEXT_MODEL (model)); + + gtk_signal_handler_unblock (GTK_OBJECT (source), E_SELECT_NAMES_TEXT_MODEL (model)->source_resize_id); } + static void -e_select_names_text_model_model_changed (ESelectNamesModel *source, - ESelectNamesTextModel *model) +e_select_names_text_model_delete (ETextModel *model, gint pos, gint length) { - EList *list = e_select_names_model_get_data(source); - EIterator *iterator = e_list_get_iterator(list); - int length = 0; - int length_count = 0; - int *lengthsp; - char *string; - char *stringp; - for (e_iterator_reset(iterator); e_iterator_is_valid(iterator); e_iterator_next(iterator)) { - const ESelectNamesModelData *data = e_iterator_get(iterator); - length += strlen(data->string); - length ++; - length_count++; + ESelectNamesModel *source = E_SELECT_NAMES_TEXT_MODEL (model)->source; + gint index, start_pos, text_len, offset; + + if (out) { + const gchar *str = e_select_names_model_get_textification (source); + gint i, len; + + fprintf (out, ">> delete %d at pos %d\n", length, pos); + + len = strlen (str); + for (i=0; i<pos && i<len; ++i) + fprintf (out, "%c", str[i]); + fprintf (out, "["); + for (i=pos; i<pos+length && i<len; ++i) + fprintf (out, "%c", str[i]); + fprintf (out, "]"); + for (i=pos+length; i<len; ++i) + fprintf (out, "%c", str[i]); + fprintf (out, "\n"); } - if (length > 0) - length --; - - g_free(model->lengths); - model->lengths = g_new(int, length_count + 1); - lengthsp = model->lengths; - - string = g_new(char, length + 1); - stringp = string; - *stringp = 0; - for (e_iterator_reset(iterator); e_iterator_is_valid(iterator); e_iterator_next(iterator)) { - const ESelectNamesModelData *data = e_iterator_get(iterator); - int this_length; - - strcpy(stringp, data->string); - this_length = strlen(stringp); - stringp += this_length; - *(stringp++) = ','; - *(lengthsp++) = this_length; + + if (length < 0) + return; + + e_select_names_model_text_pos (source, pos, &index, &start_pos, &text_len); + + if (out) + fprintf (out, "index=%d, start_pos=%d, text_len=%d\n", index, start_pos, text_len); + + /* We want to control all cursor motions ourselves, rather than taking hints + from the ESelectNamesModel. */ + gtk_signal_handler_block (GTK_OBJECT (source), E_SELECT_NAMES_TEXT_MODEL (model)->source_resize_id); + + /* First, we handle a few tricky cases. */ + + if (pos < start_pos) { + EReposAbsolute repos; + + repos.model = model; + repos.pos = pos; + e_text_model_reposition (model, e_repos_absolute, &repos); + + length -= start_pos - pos; + + if (length > 0) + e_select_names_text_model_delete (model, start_pos, length); + goto finished; } - if (stringp != string) { - stringp --; - *stringp = 0; + + if (pos == start_pos + text_len) { + /* We are positioned right at the end of an entry, possibly right in front of a comma. */ + + if (index+1 < e_select_names_model_count (source)) { + EReposDeleteShift repos; + EDestination *new_dest; + const gchar *str1 = e_select_names_model_get_string (source, index); + const gchar *str2 = e_select_names_model_get_string (source, index+1); + gchar *new_str; + + while (str1 && *str1 && isspace ((gint) *str1)) + ++str1; + while (str2 && *str2 && isspace ((gint) *str2)) + ++str2; + + if (str1 && str2) + new_str = g_strdup_printf ("%s %s", str1, str2); + else if (str1) + new_str = g_strdup (str1); + else if (str2) + new_str = g_strdup (str2); + else + new_str = g_strdup (""); + + if (out) + fprintf (out, "joining \"%s\" and \"%s\" to \"%s\"\n", str1, str2, new_str); + + e_select_names_model_delete (source, index+1); + + new_dest = e_destination_new (); + e_destination_set_string (new_dest, new_str); + e_select_names_model_replace (source, index, new_dest); + g_free (new_str); + + repos.model = model; + repos.pos = pos; + repos.len = SEPLEN - 1; + + e_text_model_reposition (model, e_repos_delete_shift, &repos); + + if (length > 1) + e_select_names_text_model_delete (model, pos, length-1); + } else { + /* If we are at the end of the last entry (which we must be if we end up in this block), + we can just do nothing. So this else-block is here just to give us a place to + put this comment. */ + } + + goto finished; } - *lengthsp = -1; + + if (pos + length > start_pos + text_len) { + /* Uh oh... our changes straddle two objects. */ - E_TEXT_MODEL_CLASS (parent_class)->set_text (E_TEXT_MODEL (model), string); - g_free (string); -} + if (pos == start_pos) { /* Delete the whole thing */ + EReposDeleteShift repos; + + e_select_names_model_delete (source, index); + if (out) + fprintf (out, "deleted all of %d\n", index); -static void -e_select_names_text_model_add_source (ESelectNamesTextModel *model, - ESelectNamesModel *source) -{ - model->source = source; - if (model->source) - gtk_object_ref(GTK_OBJECT(model->source)); - model->source_changed_id = gtk_signal_connect(GTK_OBJECT(model->source), "changed", - GTK_SIGNAL_FUNC(e_select_names_text_model_model_changed), - model); + repos.model = model; + repos.pos = pos; + repos.len = text_len + SEPLEN; + + e_text_model_reposition (model, e_repos_delete_shift, &repos); + + length -= text_len + SEPLEN; + if (length > 0) + e_select_names_text_model_delete (model, pos, length); + + } else { + /* Delete right up to the end, and then call e_select_names_text_model_delete again + to finish the job. */ + gint len1, len2; + + len1 = text_len - (pos - start_pos); + len2 = length - len1; + + if (out) + fprintf (out, "two-stage delete: %d, %d\n", len1, len2); + + + e_select_names_text_model_delete (model, pos, len1); + e_select_names_text_model_delete (model, pos, len2); + } + + goto finished; + } + + /* Our changes are confined to just one entry. */ + if (length > 0) { + const gchar *str; + gchar *new_str; + + offset = pos - start_pos; + + str = e_select_names_model_get_string (source, index); + new_str = str ? g_strdup_printf ("%.*s%s", offset, str, str + offset + length) : NULL; + + if (new_str) { + EReposDeleteShift repos; + EDestination *dest; + + dest = e_destination_new (); + e_destination_set_string (dest, new_str); + e_select_names_model_replace (source, index, dest); + + if (out) + fprintf (out, "new_str: \"%s\"\n", new_str); + + g_free (new_str); + + repos.model = model; + repos.pos = pos; + repos.len = length; + + e_text_model_reposition (model, e_repos_delete_shift, &repos); + + } else { + EReposDeleteShift repos; + + e_select_names_model_delete (source, index); + + if (out) + fprintf (out, "deleted %d\n", index); + + + repos.model = model; + repos.pos = pos; + repos.len = SEPLEN; + + e_text_model_reposition (model, e_repos_delete_shift, &repos); + } + } + + finished: + gtk_signal_handler_unblock (GTK_OBJECT (source), E_SELECT_NAMES_TEXT_MODEL (model)->source_resize_id); + dump_model (E_SELECT_NAMES_TEXT_MODEL (model)); } -static void -e_select_names_text_model_drop_source (ESelectNamesTextModel *model) +static gint +e_select_names_text_model_obj_count (ETextModel *model) { - if (model->source_changed_id) - gtk_signal_disconnect(GTK_OBJECT(model->source), model->source_changed_id); - if (model->source) - gtk_object_unref(GTK_OBJECT(model->source)); - model->source = NULL; - model->source_changed_id = 0; -} + ESelectNamesModel *source = E_SELECT_NAMES_TEXT_MODEL (model)->source; + gint i, count; + + count = i = e_select_names_model_count (source); + while (i > 0) { + const EDestination *dest; + --i; + dest = e_select_names_model_get_destination (source, i); + if (e_destination_get_card (dest) == NULL) + --count; + } -/* - * ESelectNamesTextModel lifecycle management and vcard loading/saving. - */ + return count; +} -static void -e_select_names_text_model_destroy (GtkObject *object) +static gint +nth_obj_index (ESelectNamesModel *source, gint n) { - ESelectNamesTextModel *model; - - model = E_SELECT_NAMES_TEXT_MODEL (object); - - e_select_names_text_model_drop_source(model); - g_free(model->lengths); + gint i, N; + + i = 0; + N = e_select_names_model_count (source); - if (GTK_OBJECT_CLASS(parent_class)->destroy) - GTK_OBJECT_CLASS(parent_class)->destroy(object); -} + do { + const EDestination *dest = e_select_names_model_get_destination (source, i); + if (e_destination_get_card (dest)) + --n; + ++i; + } while (n >= 0 && i < N); + if (i <= N) + --i; + else + i = -1; -/* Set_arg handler for the model */ -static void -e_select_names_text_model_set_arg (GtkObject *object, GtkArg *arg, guint arg_id) + return i; +} + +static const gchar * +e_select_names_text_model_get_nth_obj (ETextModel *model, gint n, gint *len) { - ESelectNamesTextModel *model; - - model = E_SELECT_NAMES_TEXT_MODEL (object); + ESelectNamesModel *source = E_SELECT_NAMES_TEXT_MODEL (model)->source; + const gchar *txt; + gint i, pos; - switch (arg_id) { - case ARG_SOURCE: - e_select_names_text_model_drop_source(model); - e_select_names_text_model_add_source(model, E_SELECT_NAMES_MODEL(GTK_VALUE_OBJECT(*arg))); - break; - default: - return; - } + i = nth_obj_index (source, n); + if (i < 0) + return NULL; + + e_select_names_model_name_pos (source, i, &pos, len); + if (pos < 0) + return NULL; + + txt = e_select_names_model_get_textification (source); + return txt + pos; } -/* Get_arg handler for the model */ static void -e_select_names_text_model_get_arg (GtkObject *object, GtkArg *arg, guint arg_id) +e_select_names_text_model_activate_obj (ETextModel *model, gint n) { - ESelectNamesTextModel *model; + ESelectNamesModel *source = E_SELECT_NAMES_TEXT_MODEL (model)->source; + EContactEditor *contact_editor; + ECard *card; + gint i; - model = E_SELECT_NAMES_TEXT_MODEL (object); + i = nth_obj_index (source, n); + g_return_if_fail (i >= 0); - switch (arg_id) { - case ARG_SOURCE: - GTK_VALUE_OBJECT(*arg) = GTK_OBJECT(model->source); - break; - default: - arg->type = GTK_TYPE_INVALID; - break; - } + card = e_select_names_model_get_card (source, i); + g_return_if_fail (card); + + contact_editor = e_contact_editor_new ((ECard *) card, FALSE); + e_contact_editor_raise (contact_editor); } -/** - * e_select_names_text_model_init: - */ -static void -e_select_names_text_model_init (ESelectNamesTextModel *model) -{ - model->source = NULL; - model->source_changed_id = 0; - model->lengths = NULL; -} + diff --git a/addressbook/gui/component/select-names/e-select-names-text-model.h b/addressbook/gui/component/select-names/e-select-names-text-model.h index 3d6bed468e..6458bac59c 100644 --- a/addressbook/gui/component/select-names/e-select-names-text-model.h +++ b/addressbook/gui/component/select-names/e-select-names-text-model.h @@ -1,9 +1,10 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Authors: - * Chris Lahey <clahey@helixcode.com> + * Chris Lahey <clahey@ximian.com> + * Jon Trowbridge <trow@ximian.com> * - * Copyright (C) 2000 Helix Code, Inc. + * Copyright (C) 2000, 2001 Ximian, Inc. */ #ifndef __E_SELECT_NAMES_TEXT_MODEL_H__ @@ -28,8 +29,8 @@ struct _ESelectNamesTextModel { ETextModel parent; ESelectNamesModel *source; - int source_changed_id; - int *lengths; + gint source_changed_id; + gint source_resize_id; }; struct _ESelectNamesTextModelClass { diff --git a/addressbook/gui/component/select-names/e-select-names.c b/addressbook/gui/component/select-names/e-select-names.c index ef9ffcabdf..8b35349786 100644 --- a/addressbook/gui/component/select-names/e-select-names.c +++ b/addressbook/gui/component/select-names/e-select-names.c @@ -135,32 +135,15 @@ real_add_address_cb (int model_row, ESelectNamesChild *child = closure; ESelectNames *names = child->names; ECard *card = e_addressbook_model_get_card(E_ADDRESSBOOK_MODEL(names->model), model_row); - ESelectNamesModelData new = {E_SELECT_NAMES_MODEL_DATA_TYPE_CARD, - card, - NULL}; - char *name, *email; - ECardSimple *simple = e_card_simple_new(card); - EIterator *iterator; - - name = e_card_simple_get(simple, E_CARD_SIMPLE_FIELD_FULL_NAME); - email = e_card_simple_get(simple, E_CARD_SIMPLE_FIELD_EMAIL); - if (name && *name && email && *email) { - new.string = g_strdup_printf("\"%s\" <%s>", name, email); - } else if (email && *email) { - new.string = g_strdup_printf("%s", email); - } else { - new.string = g_strdup(""); - } + EDestination *dest = e_destination_new (); + + e_destination_set_card (dest, card, 0); - iterator = e_list_get_iterator(e_select_names_model_get_data(child->source)); - e_iterator_last(iterator); - e_select_names_model_add_item(child->source, iterator, &new); + e_select_names_model_insert (child->source, + e_select_names_model_count (child->source), + dest); - gtk_object_unref(GTK_OBJECT(simple)); gtk_object_unref(GTK_OBJECT(card)); - g_free(email); - g_free(name); - g_free(new.string); } static void @@ -494,12 +477,7 @@ button_clicked(GtkWidget *button, ESelectNamesChild *child) static void remove_address(ETable *table, int row, int col, GdkEvent *event, ESelectNamesChild *child) { - EIterator *iterator = e_list_get_iterator(e_select_names_model_get_data(child->source)); - e_iterator_reset(iterator); - for (; row > 0; row--) { - e_iterator_next(iterator); - } - e_select_names_model_remove_item(child->source, iterator); + e_select_names_model_delete (child->source, row); } void |