diff options
Diffstat (limited to 'addressbook/gui/widgets/e-addressbook-model.c')
-rw-r--r-- | addressbook/gui/widgets/e-addressbook-model.c | 1306 |
1 files changed, 829 insertions, 477 deletions
diff --git a/addressbook/gui/widgets/e-addressbook-model.c b/addressbook/gui/widgets/e-addressbook-model.c index 9244df8a38..683eed3499 100644 --- a/addressbook/gui/widgets/e-addressbook-model.c +++ b/addressbook/gui/widgets/e-addressbook-model.c @@ -1,641 +1,993 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. * - * Author: - * Christopher James Lahey <clahey@ximian.com> + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Authors: + * Christopher James Lahey <clahey@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * - * (C) 1999 Ximian, Inc. */ +#ifdef HAVE_CONFIG_H #include <config.h> -#include "e-addressbook-model.h" -#include <gnome-xml/tree.h> -#include <gnome-xml/parser.h> -#include <gnome-xml/xmlmemory.h> -#include <gnome.h> -#include <gal/widgets/e-gui-utils.h> -#include "e-addressbook-util.h" - -#define PARENT_TYPE gtk_object_get_type() -GtkObjectClass *parent_class; - -/* - * EAddressbookModel callbacks - * These are the callbacks that define the behavior of our custom model. - */ -static void e_addressbook_model_set_arg (GtkObject *o, GtkArg *arg, guint arg_id); -static void e_addressbook_model_get_arg (GtkObject *object, GtkArg *arg, guint arg_id); +#endif +#include <string.h> +#include <glib/gi18n.h> +#include "e-addressbook-model.h" +#include <e-util/e-marshal.h> +#include <e-util/e-util.h> +#include "eab-gui-util.h" + +#define E_ADDRESSBOOK_MODEL_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_ADDRESSBOOK_MODEL, EAddressbookModelPrivate)) + +struct _EAddressbookModelPrivate { + EClientCache *client_cache; + gulong client_notify_readonly_handler_id; + + EBookClient *book_client; + gchar *query_str; + EBookClientView *client_view; + guint client_view_idle_id; + + /* Query Results */ + GPtrArray *contacts; + + /* Signal Handler IDs */ + gulong create_contact_id; + gulong remove_contact_id; + gulong modify_contact_id; + gulong status_message_id; + gulong view_complete_id; + guint remove_status_id; + + guint search_in_progress : 1; + guint editable : 1; + guint first_get_view : 1; +}; enum { - ARG_0, - ARG_BOOK, - ARG_QUERY, - ARG_EDITABLE, + PROP_0, + PROP_CLIENT, + PROP_CLIENT_CACHE, + PROP_EDITABLE, + PROP_QUERY }; enum { WRITABLE_STATUS, STATUS_MESSAGE, + SEARCH_STARTED, SEARCH_RESULT, FOLDER_BAR_MESSAGE, - CARD_ADDED, - CARD_REMOVED, - CARD_CHANGED, + CONTACT_ADDED, + CONTACTS_REMOVED, + CONTACT_CHANGED, MODEL_CHANGED, STOP_STATE_CHANGED, - BACKEND_DIED, LAST_SIGNAL }; -#define COLS (E_CARD_SIMPLE_FIELD_LAST) +static guint signals[LAST_SIGNAL]; -static guint e_addressbook_model_signals [LAST_SIGNAL] = {0, }; +G_DEFINE_TYPE (EAddressbookModel, e_addressbook_model, G_TYPE_OBJECT) static void free_data (EAddressbookModel *model) { - int i; - - for ( i = 0; i < model->data_count; i++ ) { - gtk_object_unref(GTK_OBJECT(model->data[i])); - } + GPtrArray *array; - g_free(model->data); - model->data = NULL; - model->data_count = 0; - model->allocated_count = 0; + array = model->priv->contacts; + g_ptr_array_foreach (array, (GFunc) g_object_unref, NULL); + g_ptr_array_set_size (array, 0); } static void -remove_book_view(EAddressbookModel *model) -{ - if (model->book_view && model->create_card_id) - gtk_signal_disconnect(GTK_OBJECT (model->book_view), - model->create_card_id); - if (model->book_view && model->remove_card_id) - gtk_signal_disconnect(GTK_OBJECT (model->book_view), - model->remove_card_id); - if (model->book_view && model->modify_card_id) - gtk_signal_disconnect(GTK_OBJECT (model->book_view), - model->modify_card_id); - if (model->book_view && model->status_message_id) - gtk_signal_disconnect(GTK_OBJECT (model->book_view), - model->status_message_id); - if (model->book_view && model->sequence_complete_id) - gtk_signal_disconnect(GTK_OBJECT (model->book_view), - model->sequence_complete_id); - - model->create_card_id = 0; - model->remove_card_id = 0; - model->modify_card_id = 0; - model->status_message_id = 0; - model->sequence_complete_id = 0; - - model->search_in_progress = FALSE; - - if (model->book_view) { - e_book_view_stop (model->book_view); - gtk_object_unref(GTK_OBJECT(model->book_view)); - } - - model->book_view = NULL; -} - -static void -addressbook_destroy(GtkObject *object) +remove_book_view (EAddressbookModel *model) { - EAddressbookModel *model = E_ADDRESSBOOK_MODEL(object); - - if (model->get_view_idle) { - g_source_remove(model->get_view_idle); - model->get_view_idle = 0; - } - - remove_book_view(model); - free_data (model); - - if (model->book) { - if (model->writable_status_id) - gtk_signal_disconnect(GTK_OBJECT (model->book), - model->writable_status_id); + if (model->priv->client_view && model->priv->create_contact_id) + g_signal_handler_disconnect ( + model->priv->client_view, + model->priv->create_contact_id); + if (model->priv->client_view && model->priv->remove_contact_id) + g_signal_handler_disconnect ( + model->priv->client_view, + model->priv->remove_contact_id); + if (model->priv->client_view && model->priv->modify_contact_id) + g_signal_handler_disconnect ( + model->priv->client_view, + model->priv->modify_contact_id); + if (model->priv->client_view && model->priv->status_message_id) + g_signal_handler_disconnect ( + model->priv->client_view, + model->priv->status_message_id); + if (model->priv->client_view && model->priv->view_complete_id) + g_signal_handler_disconnect ( + model->priv->client_view, + model->priv->view_complete_id); + if (model->priv->remove_status_id) + g_source_remove (model->priv->remove_status_id); + + model->priv->create_contact_id = 0; + model->priv->remove_contact_id = 0; + model->priv->modify_contact_id = 0; + model->priv->status_message_id = 0; + model->priv->view_complete_id = 0; + model->priv->remove_status_id = 0; + + model->priv->search_in_progress = FALSE; + + if (model->priv->client_view) { + GError *error = NULL; + + e_book_client_view_stop (model->priv->client_view, &error); + + if (error != NULL) { + g_warning ( + "%s: Failed to stop client view: %s", + G_STRFUNC, error->message); + g_error_free (error); + } - model->writable_status_id = 0; + g_object_unref (model->priv->client_view); + model->priv->client_view = NULL; - gtk_object_unref(GTK_OBJECT(model->book)); - model->book = NULL; + g_signal_emit (model, signals[STATUS_MESSAGE], 0, NULL, -1); } - - g_free (model->query); } static void update_folder_bar_message (EAddressbookModel *model) { - int count; - char *message; + guint count; + gchar *message; - count = model->data_count; + count = model->priv->contacts->len; switch (count) { case 0: - message = g_strdup (_("No cards")); - break; - case 1: - message = g_strdup (_("1 card")); + message = g_strdup (_("No contacts")); break; default: - message = g_strdup_printf (_("%d cards"), count); + message = g_strdup_printf ( + ngettext ("%d contact", "%d contacts", count), count); break; } - gtk_signal_emit (GTK_OBJECT (model), - e_addressbook_model_signals [FOLDER_BAR_MESSAGE], - message); + g_signal_emit (model, signals[FOLDER_BAR_MESSAGE], 0, message); g_free (message); } static void -create_card(EBookView *book_view, - const GList *cards, - EAddressbookModel *model) +view_create_contact_cb (EBookClientView *client_view, + const GSList *contact_list, + EAddressbookModel *model) { - int old_count = model->data_count; - int length = g_list_length ((GList *)cards); + GPtrArray *array; + guint count; + guint index; - if (model->data_count + length > model->allocated_count) { - while (model->data_count + length > model->allocated_count) - model->allocated_count = model->allocated_count * 2 + 1; - model->data = g_renew(ECard *, model->data, model->allocated_count); - } + array = model->priv->contacts; + index = array->len; + count = g_list_length ((GList *) contact_list); - for ( ; cards; cards = cards->next) { - model->data[model->data_count++] = cards->data; - gtk_object_ref (cards->data); - } + while (contact_list != NULL) { + EContact *contact = contact_list->data; - gtk_signal_emit (GTK_OBJECT (model), - e_addressbook_model_signals [CARD_ADDED], - old_count, model->data_count - old_count); + g_ptr_array_add (array, g_object_ref (contact)); + contact_list = contact_list->next; + } + g_signal_emit (model, signals[CONTACT_ADDED], 0, index, count); update_folder_bar_message (model); } -static void -remove_card(EBookView *book_view, - const char *id, - EAddressbookModel *model) -{ - int i; - - for ( i = 0; i < model->data_count; i++) { - if ( !strcmp(e_card_get_id(model->data[i]), id) ) { - gtk_object_unref(GTK_OBJECT(model->data[i])); - memmove(model->data + i, model->data + i + 1, (model->data_count - i - 1) * sizeof (ECard *)); - model->data_count--; - - gtk_signal_emit (GTK_OBJECT (model), - e_addressbook_model_signals [CARD_REMOVED], - i); - update_folder_bar_message (model); - break; - } - } +static gint +sort_descending (gconstpointer ca, + gconstpointer cb) +{ + gint a = *((gint *) ca); + gint b = *((gint *) cb); + + return (a == b) ? 0 : (a < b) ? 1 : -1; } static void -modify_card(EBookView *book_view, - const GList *cards, - EAddressbookModel *model) -{ - for ( ; cards; cards = cards->next) { - int i; - for ( i = 0; i < model->data_count; i++) { - if ( !strcmp(e_card_get_id(model->data[i]), e_card_get_id(E_CARD(cards->data))) ) { - gtk_object_unref(GTK_OBJECT(model->data[i])); - model->data[i] = e_card_duplicate(E_CARD(cards->data)); - gtk_signal_emit (GTK_OBJECT (model), - e_addressbook_model_signals [CARD_CHANGED], - i); +view_remove_contact_cb (EBookClientView *client_view, + const GSList *ids, + EAddressbookModel *model) +{ + /* XXX we should keep a hash around instead of this O(n*m) loop */ + const GSList *iter; + GArray *indices; + GPtrArray *array; + gint ii; + + array = model->priv->contacts; + indices = g_array_new (FALSE, FALSE, sizeof (gint)); + + for (iter = ids; iter != NULL; iter = iter->next) { + const gchar *target_uid = iter->data; + + for (ii = 0; ii < array->len; ii++) { + EContact *contact; + const gchar *uid; + + contact = array->pdata[ii]; + /* check if already removed */ + if (!contact) + continue; + + uid = e_contact_get_const (contact, E_CONTACT_UID); + g_return_if_fail (uid != NULL); + + if (strcmp (uid, target_uid) == 0) { + g_object_unref (contact); + g_array_append_val (indices, ii); + array->pdata[ii] = NULL; break; } } } -} -static void -status_message (EBookView *book_view, - char* status, - EAddressbookModel *model) -{ - gtk_signal_emit (GTK_OBJECT (model), - e_addressbook_model_signals [STATUS_MESSAGE], - status); -} + /* Sort the 'indices' array in descending order, since + * g_ptr_array_remove_index() shifts subsequent elements + * down one position to fill the gap. */ + g_array_sort (indices, sort_descending); -static void -sequence_complete (EBookView *book_view, - EBookViewStatus status, - EAddressbookModel *model) -{ - model->search_in_progress = FALSE; - status_message (book_view, NULL, model); - gtk_signal_emit (GTK_OBJECT (model), - e_addressbook_model_signals [SEARCH_RESULT], - status); - gtk_signal_emit (GTK_OBJECT (model), - e_addressbook_model_signals [STOP_STATE_CHANGED]); + for (ii = 0; ii < indices->len; ii++) { + gint index; + + index = g_array_index (indices, gint, ii); + g_ptr_array_remove_index (array, index); + } + + g_signal_emit (model, signals[CONTACTS_REMOVED], 0, indices); + g_array_free (indices, FALSE); + + update_folder_bar_message (model); } static void -writable_status (EBook *book, - gboolean writable, - EAddressbookModel *model) +view_modify_contact_cb (EBookClientView *client_view, + const GSList *contact_list, + EAddressbookModel *model) { - if (!model->editable_set) { - model->editable = writable; + GPtrArray *array; + + array = model->priv->contacts; + + while (contact_list != NULL) { + EContact *new_contact = contact_list->data; + const gchar *target_uid; + gint ii; + + target_uid = e_contact_get_const (new_contact, E_CONTACT_UID); + g_warn_if_fail (target_uid != NULL); + + /* skip contacts without UID */ + if (!target_uid) { + contact_list = contact_list->next; + continue; + } - gtk_signal_emit (GTK_OBJECT (model), - e_addressbook_model_signals [WRITABLE_STATUS], - writable); + for (ii = 0; ii < array->len; ii++) { + EContact *old_contact; + const gchar *uid; + + old_contact = array->pdata[ii]; + g_return_if_fail (old_contact != NULL); + + uid = e_contact_get_const (old_contact, E_CONTACT_UID); + g_return_if_fail (uid != NULL); + + if (strcmp (uid, target_uid) != 0) + continue; + + g_object_unref (old_contact); + array->pdata[ii] = e_contact_duplicate (new_contact); + + g_signal_emit ( + model, signals[CONTACT_CHANGED], 0, ii); + break; + } + + contact_list = contact_list->next; } } static void -backend_died (EBook *book, - EAddressbookModel *model) +view_progress_cb (EBookClientView *client_view, + guint percent, + const gchar *message, + EAddressbookModel *model) { - gtk_signal_emit (GTK_OBJECT (model), - e_addressbook_model_signals [BACKEND_DIED]); + if (model->priv->remove_status_id) + g_source_remove (model->priv->remove_status_id); + model->priv->remove_status_id = 0; + + g_signal_emit (model, signals[STATUS_MESSAGE], 0, message, percent); } static void -e_addressbook_model_class_init (GtkObjectClass *object_class) -{ - parent_class = gtk_type_class (PARENT_TYPE); - - object_class->destroy = addressbook_destroy; - object_class->set_arg = e_addressbook_model_set_arg; - object_class->get_arg = e_addressbook_model_get_arg; - - gtk_object_add_arg_type ("EAddressbookModel::book", GTK_TYPE_OBJECT, - GTK_ARG_READWRITE, ARG_BOOK); - gtk_object_add_arg_type ("EAddressbookModel::query", GTK_TYPE_STRING, - GTK_ARG_READWRITE, ARG_QUERY); - gtk_object_add_arg_type ("EAddressbookModel::editable", GTK_TYPE_BOOL, - GTK_ARG_READWRITE, ARG_EDITABLE); - - e_addressbook_model_signals [WRITABLE_STATUS] = - gtk_signal_new ("writable_status", - GTK_RUN_LAST, - object_class->type, - GTK_SIGNAL_OFFSET (EAddressbookModelClass, writable_status), - gtk_marshal_NONE__BOOL, - GTK_TYPE_NONE, 1, GTK_TYPE_BOOL); - - e_addressbook_model_signals [STATUS_MESSAGE] = - gtk_signal_new ("status_message", - GTK_RUN_LAST, - object_class->type, - GTK_SIGNAL_OFFSET (EAddressbookModelClass, status_message), - gtk_marshal_NONE__POINTER, - GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); - - e_addressbook_model_signals [SEARCH_RESULT] = - gtk_signal_new ("search_result", - GTK_RUN_LAST, - object_class->type, - GTK_SIGNAL_OFFSET (EAddressbookModelClass, search_result), - gtk_marshal_NONE__ENUM, - GTK_TYPE_NONE, 1, GTK_TYPE_ENUM); - - e_addressbook_model_signals [FOLDER_BAR_MESSAGE] = - gtk_signal_new ("folder_bar_message", - GTK_RUN_LAST, - object_class->type, - GTK_SIGNAL_OFFSET (EAddressbookModelClass, folder_bar_message), - gtk_marshal_NONE__POINTER, - GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); - - e_addressbook_model_signals [CARD_ADDED] = - gtk_signal_new ("card_added", - GTK_RUN_LAST, - object_class->type, - GTK_SIGNAL_OFFSET (EAddressbookModelClass, card_added), - gtk_marshal_NONE__INT_INT, - GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT); - - e_addressbook_model_signals [CARD_REMOVED] = - gtk_signal_new ("card_removed", - GTK_RUN_LAST, - object_class->type, - GTK_SIGNAL_OFFSET (EAddressbookModelClass, card_removed), - gtk_marshal_NONE__INT, - GTK_TYPE_NONE, 1, GTK_TYPE_INT); - - e_addressbook_model_signals [CARD_CHANGED] = - gtk_signal_new ("card_changed", - GTK_RUN_LAST, - object_class->type, - GTK_SIGNAL_OFFSET (EAddressbookModelClass, card_changed), - gtk_marshal_NONE__INT, - GTK_TYPE_NONE, 1, GTK_TYPE_INT); - - e_addressbook_model_signals [MODEL_CHANGED] = - gtk_signal_new ("model_changed", - GTK_RUN_LAST, - object_class->type, - GTK_SIGNAL_OFFSET (EAddressbookModelClass, model_changed), - gtk_marshal_NONE__NONE, - GTK_TYPE_NONE, 0); - - e_addressbook_model_signals [STOP_STATE_CHANGED] = - gtk_signal_new ("stop_state_changed", - GTK_RUN_LAST, - object_class->type, - GTK_SIGNAL_OFFSET (EAddressbookModelClass, stop_state_changed), - gtk_marshal_NONE__NONE, - GTK_TYPE_NONE, 0); - - e_addressbook_model_signals [BACKEND_DIED] = - gtk_signal_new ("backend_died", - GTK_RUN_LAST, - object_class->type, - GTK_SIGNAL_OFFSET (EAddressbookModelClass, backend_died), - gtk_marshal_NONE__NONE, - GTK_TYPE_NONE, 0); - - gtk_object_class_add_signals (object_class, e_addressbook_model_signals, LAST_SIGNAL); +view_complete_cb (EBookClientView *client_view, + const GError *error, + EAddressbookModel *model) +{ + model->priv->search_in_progress = FALSE; + view_progress_cb (client_view, -1, NULL, model); + g_signal_emit (model, signals[SEARCH_RESULT], 0, error); + g_signal_emit (model, signals[STOP_STATE_CHANGED], 0); } static void -e_addressbook_model_init (GtkObject *object) -{ - EAddressbookModel *model = E_ADDRESSBOOK_MODEL(object); - model->book = NULL; - model->query = g_strdup("(contains \"x-evolution-any-field\" \"\")"); - model->book_view = NULL; - model->get_view_idle = 0; - model->create_card_id = 0; - model->remove_card_id = 0; - model->modify_card_id = 0; - model->status_message_id = 0; - model->writable_status_id = 0; - model->backend_died_id = 0; - model->sequence_complete_id = 0; - model->data = NULL; - model->data_count = 0; - model->allocated_count = 0; - model->search_in_progress = FALSE; - model->editable = FALSE; - model->editable_set = FALSE; - model->first_get_view = TRUE; +addressbook_model_client_notify_readonly_cb (EClientCache *client_cache, + EClient *client, + GParamSpec *pspec, + EAddressbookModel *model) +{ + if (!E_IS_BOOK_CLIENT (client)) + return; + + if (E_BOOK_CLIENT (client) == model->priv->book_client) { + gboolean editable = !e_client_is_readonly (client); + e_addressbook_model_set_editable (model, editable); + } } static void -book_view_loaded (EBook *book, EBookStatus status, EBookView *book_view, gpointer closure) +client_view_ready_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) { - EAddressbookModel *model = closure; + EBookClient *book_client = E_BOOK_CLIENT (source_object); + EBookClientView *client_view = NULL; + EAddressbookModel *model = user_data; + GError *error = NULL; - remove_book_view(model); + if (!e_book_client_get_view_finish (book_client, result, &client_view, &error)) + client_view = NULL; - if (status != E_BOOK_STATUS_SUCCESS) { - e_addressbook_error_dialog (_("Error getting book view"), status); + if (error) { + eab_error_dialog (NULL, NULL, _("Error getting book view"), error); + g_error_free (error); return; } - model->book_view = book_view; - if (model->book_view) - gtk_object_ref(GTK_OBJECT(model->book_view)); - model->create_card_id = gtk_signal_connect(GTK_OBJECT(model->book_view), - "card_added", - GTK_SIGNAL_FUNC(create_card), - model); - model->remove_card_id = gtk_signal_connect(GTK_OBJECT(model->book_view), - "card_removed", - GTK_SIGNAL_FUNC(remove_card), - model); - model->modify_card_id = gtk_signal_connect(GTK_OBJECT(model->book_view), - "card_changed", - GTK_SIGNAL_FUNC(modify_card), - model); - model->status_message_id = gtk_signal_connect(GTK_OBJECT(model->book_view), - "status_message", - GTK_SIGNAL_FUNC(status_message), - model); - model->sequence_complete_id = gtk_signal_connect(GTK_OBJECT(model->book_view), - "sequence_complete", - GTK_SIGNAL_FUNC(sequence_complete), - model); - + remove_book_view (model); free_data (model); - model->search_in_progress = TRUE; - gtk_signal_emit (GTK_OBJECT (model), - e_addressbook_model_signals [MODEL_CHANGED]); - gtk_signal_emit (GTK_OBJECT (model), - e_addressbook_model_signals [STOP_STATE_CHANGED]); + model->priv->client_view = client_view; + if (model->priv->client_view) { + model->priv->create_contact_id = g_signal_connect ( + model->priv->client_view, "objects-added", + G_CALLBACK (view_create_contact_cb), model); + model->priv->remove_contact_id = g_signal_connect ( + model->priv->client_view, "objects-removed", + G_CALLBACK (view_remove_contact_cb), model); + model->priv->modify_contact_id = g_signal_connect ( + model->priv->client_view, "objects-modified", + G_CALLBACK (view_modify_contact_cb), model); + model->priv->status_message_id = g_signal_connect ( + model->priv->client_view, "progress", + G_CALLBACK (view_progress_cb), model); + model->priv->view_complete_id = g_signal_connect ( + model->priv->client_view, "complete", + G_CALLBACK (view_complete_cb), model); + + model->priv->search_in_progress = TRUE; + } + + g_signal_emit (model, signals[MODEL_CHANGED], 0); + g_signal_emit (model, signals[SEARCH_STARTED], 0); + g_signal_emit (model, signals[STOP_STATE_CHANGED], 0); + + if (model->priv->client_view) { + e_book_client_view_start (model->priv->client_view, &error); + + if (error != NULL) { + g_warning ( + "%s: Failed to start client view: %s", + G_STRFUNC, error->message); + g_error_free (error); + } + } } static gboolean -get_view (EAddressbookModel *model) -{ - if (model->book && model->query) { - if (model->first_get_view) { - char *capabilities; - capabilities = e_book_get_static_capabilities (model->book); - if (capabilities && strstr (capabilities, "do-initial-query")) { - e_book_get_book_view (model->book, model->query, book_view_loaded, model); +addressbook_model_idle_cb (EAddressbookModel *model) +{ + model->priv->client_view_idle_id = 0; + + if (model->priv->book_client && model->priv->query_str) { + remove_book_view (model); + + if (model->priv->first_get_view) { + model->priv->first_get_view = FALSE; + + if (e_client_check_capability (E_CLIENT (model->priv->book_client), "do-initial-query")) { + e_book_client_get_view ( + model->priv->book_client, model->priv->query_str, + NULL, client_view_ready_cb, model); } else { - remove_book_view(model); free_data (model); - gtk_signal_emit (GTK_OBJECT (model), - e_addressbook_model_signals [MODEL_CHANGED]); - gtk_signal_emit (GTK_OBJECT (model), - e_addressbook_model_signals [STOP_STATE_CHANGED]); + + g_signal_emit ( + model, signals[MODEL_CHANGED], 0); + g_signal_emit ( + model, signals[STOP_STATE_CHANGED], 0); } - model->first_get_view = FALSE; - g_free (capabilities); - } - else - e_book_get_book_view (model->book, model->query, book_view_loaded, model); + } else + e_book_client_get_view ( + model->priv->book_client, model->priv->query_str, + NULL, client_view_ready_cb, model); + } - model->get_view_idle = 0; + g_object_unref (model); + return FALSE; } -ECard * -e_addressbook_model_get_card(EAddressbookModel *model, - int row) +static gboolean +remove_status_cb (gpointer data) { - if (model->data && 0 <= row && row < model->data_count) { - ECard *card; - card = e_card_duplicate (model->data[row]); - return card; + EAddressbookModel *model = data; + + g_return_val_if_fail (model != NULL, FALSE); + g_return_val_if_fail (E_IS_ADDRESSBOOK_MODEL (model), FALSE); + + g_signal_emit (model, signals[STATUS_MESSAGE], 0, NULL, -1); + model->priv->remove_status_id = 0; + + return FALSE; +} + +static void +addressbook_model_set_client_cache (EAddressbookModel *model, + EClientCache *client_cache) +{ + g_return_if_fail (E_IS_CLIENT_CACHE (client_cache)); + g_return_if_fail (model->priv->client_cache == NULL); + + model->priv->client_cache = g_object_ref (client_cache); +} + +static void +addressbook_model_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_CLIENT: + e_addressbook_model_set_client ( + E_ADDRESSBOOK_MODEL (object), + g_value_get_object (value)); + return; + + case PROP_CLIENT_CACHE: + addressbook_model_set_client_cache ( + E_ADDRESSBOOK_MODEL (object), + g_value_get_object (value)); + return; + + case PROP_EDITABLE: + e_addressbook_model_set_editable ( + E_ADDRESSBOOK_MODEL (object), + g_value_get_boolean (value)); + return; + + case PROP_QUERY: + e_addressbook_model_set_query ( + E_ADDRESSBOOK_MODEL (object), + g_value_get_string (value)); + return; } - return NULL; + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } -const ECard * -e_addressbook_model_peek_card(EAddressbookModel *model, - int row) +static void +addressbook_model_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) { - if (model->data && 0 <= row && row < model->data_count) { - return model->data[row]; + switch (property_id) { + case PROP_CLIENT: + g_value_set_object ( + value, e_addressbook_model_get_client ( + E_ADDRESSBOOK_MODEL (object))); + return; + + case PROP_CLIENT_CACHE: + g_value_set_object ( + value, e_addressbook_model_get_client_cache ( + E_ADDRESSBOOK_MODEL (object))); + return; + + case PROP_EDITABLE: + g_value_set_boolean ( + value, e_addressbook_model_get_editable ( + E_ADDRESSBOOK_MODEL (object))); + return; + + case PROP_QUERY: + g_value_set_string ( + value, e_addressbook_model_get_query ( + E_ADDRESSBOOK_MODEL (object))); + return; } - return NULL; + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void -e_addressbook_model_set_arg (GtkObject *o, GtkArg *arg, guint arg_id) +addressbook_model_dispose (GObject *object) { - EAddressbookModel *model; + EAddressbookModel *model = E_ADDRESSBOOK_MODEL (object); - model = E_ADDRESSBOOK_MODEL (o); - - switch (arg_id){ - case ARG_BOOK: - if (model->book) { - if (model->writable_status_id) - gtk_signal_disconnect(GTK_OBJECT (model->book), - model->writable_status_id); - model->writable_status_id = 0; - - if (model->backend_died_id) - gtk_signal_disconnect(GTK_OBJECT (model->book), - model->backend_died_id); - model->backend_died_id = 0; - - gtk_object_unref(GTK_OBJECT(model->book)); - } - model->book = E_BOOK(GTK_VALUE_OBJECT (*arg)); - if (model->book) { - model->first_get_view = TRUE; - gtk_object_ref(GTK_OBJECT(model->book)); - if (model->get_view_idle == 0) - model->get_view_idle = g_idle_add((GSourceFunc)get_view, model); - gtk_signal_connect (GTK_OBJECT(model->book), - "writable_status", - writable_status, model); - gtk_signal_connect (GTK_OBJECT(model->book), - "backend_died", - backend_died, model); - } - break; - case ARG_QUERY: - if (model->query) - g_free(model->query); - model->query = g_strdup(GTK_VALUE_STRING (*arg)); - if (model->get_view_idle == 0) - model->get_view_idle = g_idle_add((GSourceFunc)get_view, model); - break; - case ARG_EDITABLE: - model->editable = GTK_VALUE_BOOL (*arg); - model->editable_set = TRUE; - break; + remove_book_view (model); + free_data (model); + + if (model->priv->client_notify_readonly_handler_id > 0) { + g_signal_handler_disconnect ( + model->priv->client_cache, + model->priv->client_notify_readonly_handler_id); + model->priv->client_notify_readonly_handler_id = 0; } + + g_clear_object (&model->priv->client_cache); + g_clear_object (&model->priv->book_client); + + if (model->priv->query_str) { + g_free (model->priv->query_str); + model->priv->query_str = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_addressbook_model_parent_class)->dispose (object); } static void -e_addressbook_model_get_arg (GtkObject *object, GtkArg *arg, guint arg_id) +addressbook_model_finalize (GObject *object) { - EAddressbookModel *e_addressbook_model; + EAddressbookModelPrivate *priv; - e_addressbook_model = E_ADDRESSBOOK_MODEL (object); + priv = E_ADDRESSBOOK_MODEL_GET_PRIVATE (object); - switch (arg_id) { - case ARG_BOOK: - GTK_VALUE_OBJECT (*arg) = GTK_OBJECT(e_addressbook_model->book); - break; - case ARG_QUERY: - GTK_VALUE_STRING (*arg) = g_strdup(e_addressbook_model->query); - break; - case ARG_EDITABLE: - GTK_VALUE_BOOL (*arg) = e_addressbook_model->editable; - break; - default: - arg->type = GTK_TYPE_INVALID; - break; - } + g_ptr_array_free (priv->contacts, TRUE); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_addressbook_model_parent_class)->finalize (object); } -GtkType -e_addressbook_model_get_type (void) +static void +addressbook_model_constructed (GObject *object) { - static GtkType type = 0; + EAddressbookModel *model; + EClientCache *client_cache; + gulong handler_id; - if (!type){ - GtkTypeInfo info = { - "EAddressbookModel", - sizeof (EAddressbookModel), - sizeof (EAddressbookModelClass), - (GtkClassInitFunc) e_addressbook_model_class_init, - (GtkObjectInitFunc) e_addressbook_model_init, - NULL, /* reserved 1 */ - NULL, /* reserved 2 */ - (GtkClassInitFunc) NULL - }; + model = E_ADDRESSBOOK_MODEL (object); - type = gtk_type_unique (PARENT_TYPE, &info); - } + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_addressbook_model_parent_class)->constructed (object); - return type; + client_cache = e_addressbook_model_get_client_cache (model); + + handler_id = g_signal_connect ( + client_cache, "client-notify::readonly", + G_CALLBACK (addressbook_model_client_notify_readonly_cb), + model); + model->priv->client_notify_readonly_handler_id = handler_id; } -EAddressbookModel* -e_addressbook_model_new (void) +static void +e_addressbook_model_class_init (EAddressbookModelClass *class) { - EAddressbookModel *et; + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (EAddressbookModelPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = addressbook_model_set_property; + object_class->get_property = addressbook_model_get_property; + object_class->dispose = addressbook_model_dispose; + object_class->finalize = addressbook_model_finalize; + object_class->constructed = addressbook_model_constructed; + + g_object_class_install_property ( + object_class, + PROP_CLIENT, + g_param_spec_object ( + "client", + "EBookClient", + NULL, + E_TYPE_BOOK_CLIENT, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_CLIENT_CACHE, + g_param_spec_object ( + "client-cache", + "Client Cache", + "Shared EClient instances", + E_TYPE_CLIENT_CACHE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_EDITABLE, + g_param_spec_boolean ( + "editable", + "Editable", + NULL, + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_QUERY, + g_param_spec_string ( + "query", + "Query", + NULL, + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + signals[WRITABLE_STATUS] = g_signal_new ( + "writable_status", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EAddressbookModelClass, writable_status), + NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 1, + G_TYPE_BOOLEAN); + + signals[STATUS_MESSAGE] = g_signal_new ( + "status_message", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EAddressbookModelClass, status_message), + NULL, NULL, + e_marshal_VOID__STRING_INT, + G_TYPE_NONE, 2, + G_TYPE_STRING, + G_TYPE_INT); + + signals[SEARCH_STARTED] = g_signal_new ( + "search_started", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EAddressbookModelClass, search_started), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[SEARCH_RESULT] = g_signal_new ( + "search_result", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EAddressbookModelClass, search_result), + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + G_TYPE_ERROR); + + signals[FOLDER_BAR_MESSAGE] = g_signal_new ( + "folder_bar_message", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EAddressbookModelClass, folder_bar_message), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, + G_TYPE_POINTER); + + signals[CONTACT_ADDED] = g_signal_new ( + "contact_added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EAddressbookModelClass, contact_added), + NULL, NULL, + e_marshal_NONE__INT_INT, + G_TYPE_NONE, 2, + G_TYPE_INT, + G_TYPE_INT); + + signals[CONTACTS_REMOVED] = g_signal_new ( + "contacts_removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EAddressbookModelClass, contacts_removed), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, + G_TYPE_POINTER); + + signals[CONTACT_CHANGED] = g_signal_new ( + "contact_changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EAddressbookModelClass, contact_changed), + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, 1, + G_TYPE_INT); + + signals[MODEL_CHANGED] = g_signal_new ( + "model_changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EAddressbookModelClass, model_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[STOP_STATE_CHANGED] = g_signal_new ( + "stop_state_changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EAddressbookModelClass, stop_state_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} - et = gtk_type_new (e_addressbook_model_get_type ()); - - return et; +static void +e_addressbook_model_init (EAddressbookModel *model) +{ + model->priv = E_ADDRESSBOOK_MODEL_GET_PRIVATE (model); + model->priv->contacts = g_ptr_array_new (); + model->priv->first_get_view = TRUE; } -void e_addressbook_model_stop (EAddressbookModel *model) +EAddressbookModel * +e_addressbook_model_new (EClientCache *client_cache) { - remove_book_view(model); - gtk_signal_emit (GTK_OBJECT (model), - e_addressbook_model_signals [STOP_STATE_CHANGED]); - gtk_signal_emit (GTK_OBJECT (model), - e_addressbook_model_signals [STATUS_MESSAGE], - "Search Interrupted."); + g_return_val_if_fail (E_IS_CLIENT_CACHE (client_cache), NULL); + + return g_object_new ( + E_TYPE_ADDRESSBOOK_MODEL, + "client-cache", client_cache, NULL); +} + +EClientCache * +e_addressbook_model_get_client_cache (EAddressbookModel *model) +{ + g_return_val_if_fail (E_IS_ADDRESSBOOK_MODEL (model), NULL); + + return model->priv->client_cache; +} + +EContact * +e_addressbook_model_get_contact (EAddressbookModel *model, + gint row) +{ + GPtrArray *array; + + g_return_val_if_fail (E_IS_ADDRESSBOOK_MODEL (model), NULL); + + array = model->priv->contacts; + + if (0 <= row && row < array->len) + return e_contact_duplicate (array->pdata[row]); + + return NULL; +} + +void +e_addressbook_model_stop (EAddressbookModel *model) +{ + const gchar *message; + + g_return_if_fail (E_IS_ADDRESSBOOK_MODEL (model)); + + remove_book_view (model); + + message = _("Search Interrupted"); + g_signal_emit (model, signals[STOP_STATE_CHANGED], 0); + g_signal_emit (model, signals[STATUS_MESSAGE], 0, message, -1); + + if (!model->priv->remove_status_id) + model->priv->remove_status_id = + g_timeout_add_seconds (3, remove_status_cb, model); } gboolean e_addressbook_model_can_stop (EAddressbookModel *model) { - return model->search_in_progress; + g_return_val_if_fail (E_IS_ADDRESSBOOK_MODEL (model), FALSE); + + return model->priv->search_in_progress; } void e_addressbook_model_force_folder_bar_message (EAddressbookModel *model) { + g_return_if_fail (E_IS_ADDRESSBOOK_MODEL (model)); + update_folder_bar_message (model); } -int -e_addressbook_model_card_count (EAddressbookModel *model) +gint +e_addressbook_model_contact_count (EAddressbookModel *model) +{ + g_return_val_if_fail (E_IS_ADDRESSBOOK_MODEL (model), 0); + + return model->priv->contacts->len; +} + +EContact * +e_addressbook_model_contact_at (EAddressbookModel *model, + gint index) +{ + g_return_val_if_fail (E_IS_ADDRESSBOOK_MODEL (model), NULL); + + return model->priv->contacts->pdata[index]; +} + +gint +e_addressbook_model_find (EAddressbookModel *model, + EContact *contact) +{ + GPtrArray *array; + gint ii; + + /* XXX This searches for a particular EContact instance, + * as opposed to an equivalent but possibly different + * EContact instance. Might have to revise this in + * the future. */ + + g_return_val_if_fail (E_IS_ADDRESSBOOK_MODEL (model), -1); + g_return_val_if_fail (E_IS_CONTACT (contact), -1); + + array = model->priv->contacts; + for (ii = 0; ii < array->len; ii++) { + EContact *candidate = array->pdata[ii]; + + if (contact == candidate) + return ii; + } + + return -1; +} + +EBookClient * +e_addressbook_model_get_client (EAddressbookModel *model) { - return model->data_count; + g_return_val_if_fail (E_IS_ADDRESSBOOK_MODEL (model), NULL); + + return model->priv->book_client; } -ECard * -e_addressbook_model_card_at (EAddressbookModel *model, int index) +void +e_addressbook_model_set_client (EAddressbookModel *model, + EBookClient *book_client) { - return model->data[index]; + gboolean editable; + + g_return_if_fail (E_IS_ADDRESSBOOK_MODEL (model)); + g_return_if_fail (E_IS_BOOK_CLIENT (book_client)); + + if (model->priv->book_client == book_client) + return; + + if (model->priv->book_client != NULL) + g_object_unref (model->priv->book_client); + + model->priv->book_client = g_object_ref (book_client); + model->priv->first_get_view = TRUE; + + editable = !e_client_is_readonly (E_CLIENT (book_client)); + e_addressbook_model_set_editable (model, editable); + + if (model->priv->client_view_idle_id == 0) + model->priv->client_view_idle_id = g_idle_add ( + (GSourceFunc) addressbook_model_idle_cb, + g_object_ref (model)); + + g_object_notify (G_OBJECT (model), "client"); } gboolean -e_addressbook_model_editable (EAddressbookModel *model) +e_addressbook_model_get_editable (EAddressbookModel *model) { - return model->editable; + g_return_val_if_fail (E_IS_ADDRESSBOOK_MODEL (model), FALSE); + + return model->priv->editable; } -EBook * -e_addressbook_model_get_ebook (EAddressbookModel *model) +void +e_addressbook_model_set_editable (EAddressbookModel *model, + gboolean editable) { - return model->book; + g_return_if_fail (E_IS_ADDRESSBOOK_MODEL (model)); + + if (model->priv->editable != editable) { + model->priv->editable = editable; + + g_signal_emit ( + model, signals[WRITABLE_STATUS], 0, + model->priv->editable); + + g_object_notify (G_OBJECT (model), "editable"); + } +} + +gchar * +e_addressbook_model_get_query (EAddressbookModel *model) +{ + g_return_val_if_fail (E_IS_ADDRESSBOOK_MODEL (model), NULL); + + return model->priv->query_str; +} + +void +e_addressbook_model_set_query (EAddressbookModel *model, + const gchar *query) +{ + EBookQuery *book_query; + + g_return_if_fail (E_IS_ADDRESSBOOK_MODEL (model)); + + if (query == NULL) + book_query = e_book_query_any_field_contains (""); + else + book_query = e_book_query_from_string (query); + + /* also checks whether the query is a valid query string */ + if (!book_query) + return; + + if (model->priv->query_str != NULL) { + gchar *new_query; + + new_query = e_book_query_to_string (book_query); + + if (new_query && g_str_equal (model->priv->query_str, new_query)) { + g_free (new_query); + e_book_query_unref (book_query); + return; + } + + g_free (new_query); + } + + g_free (model->priv->query_str); + model->priv->query_str = e_book_query_to_string (book_query); + e_book_query_unref (book_query); + + if (model->priv->client_view_idle_id == 0) + model->priv->client_view_idle_id = g_idle_add ( + (GSourceFunc) addressbook_model_idle_cb, + g_object_ref (model)); + + g_object_notify (G_OBJECT (model), "query"); } |