aboutsummaryrefslogtreecommitdiffstats
path: root/addressbook
diff options
context:
space:
mode:
Diffstat (limited to 'addressbook')
-rw-r--r--addressbook/ChangeLog66
-rw-r--r--addressbook/backend/ebook/Makefile.am8
-rw-r--r--addressbook/backend/ebook/e-address-completion.c315
-rw-r--r--addressbook/backend/ebook/e-address-completion.h65
-rw-r--r--addressbook/backend/ebook/e-destination.c284
-rw-r--r--addressbook/backend/ebook/e-destination.h73
-rw-r--r--addressbook/gui/component/select-names/Makefile.am2
-rw-r--r--addressbook/gui/component/select-names/e-select-names-bonobo.c25
-rw-r--r--addressbook/gui/component/select-names/e-select-names-completion.c831
-rw-r--r--addressbook/gui/component/select-names/e-select-names-completion.h65
-rw-r--r--addressbook/gui/component/select-names/e-select-names-manager.c105
-rw-r--r--addressbook/gui/component/select-names/e-select-names-manager.h4
-rw-r--r--addressbook/gui/component/select-names/e-select-names-model.c590
-rw-r--r--addressbook/gui/component/select-names/e-select-names-model.h77
-rw-r--r--addressbook/gui/component/select-names/e-select-names-table-model.c49
-rw-r--r--addressbook/gui/component/select-names/e-select-names-text-model.c797
-rw-r--r--addressbook/gui/component/select-names/e-select-names-text-model.h9
-rw-r--r--addressbook/gui/component/select-names/e-select-names.c36
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