evolution-3.6.4/addressbook/gui/widgets/e-addressbook-model.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found e-addressbook-model.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
clang-analyzer no-output-found e-addressbook-model.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
   1 /*
   2  * This program is free software; you can redistribute it and/or
   3  * modify it under the terms of the GNU Lesser General Public
   4  * License as published by the Free Software Foundation; either
   5  * version 2 of the License, or (at your option) version 3.
   6  *
   7  * This program is distributed in the hope that it will be useful,
   8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  10  * Lesser General Public License for more details.
  11  *
  12  * You should have received a copy of the GNU Lesser General Public
  13  * License along with the program; if not, see <http://www.gnu.org/licenses/>
  14  *
  15  *
  16  * Authors:
  17  *		Christopher James Lahey <clahey@ximian.com>
  18  *
  19  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  20  *
  21  */
  22 
  23 #ifdef HAVE_CONFIG_H
  24 #include <config.h>
  25 #endif
  26 
  27 #include <string.h>
  28 #include <glib/gi18n.h>
  29 #include "e-addressbook-model.h"
  30 #include <e-util/e-marshal.h>
  31 #include <e-util/e-util.h>
  32 #include "eab-gui-util.h"
  33 
  34 #define E_ADDRESSBOOK_MODEL_GET_PRIVATE(obj) \
  35 	(G_TYPE_INSTANCE_GET_PRIVATE \
  36 	((obj), E_TYPE_ADDRESSBOOK_MODEL, EAddressbookModelPrivate))
  37 
  38 struct _EAddressbookModelPrivate {
  39 	ESourceRegistry *registry;
  40 	EBookClient *book_client;
  41 	gchar *query_str;
  42 	EBookClientView *client_view;
  43 	guint client_view_idle_id;
  44 
  45 	/* Query Results */
  46 	GPtrArray *contacts;
  47 
  48 	/* Signal Handler IDs */
  49 	gulong create_contact_id;
  50 	gulong remove_contact_id;
  51 	gulong modify_contact_id;
  52 	gulong status_message_id;
  53 	gulong writable_status_id;
  54 	gulong view_complete_id;
  55 	gulong backend_died_id;
  56 	guint remove_status_id;
  57 
  58 	guint search_in_progress	: 1;
  59 	guint editable			: 1;
  60 	guint first_get_view		: 1;
  61 };
  62 
  63 enum {
  64 	PROP_0,
  65 	PROP_CLIENT,
  66 	PROP_EDITABLE,
  67 	PROP_QUERY,
  68 	PROP_REGISTRY
  69 };
  70 
  71 enum {
  72 	WRITABLE_STATUS,
  73 	STATUS_MESSAGE,
  74 	SEARCH_STARTED,
  75 	SEARCH_RESULT,
  76 	FOLDER_BAR_MESSAGE,
  77 	CONTACT_ADDED,
  78 	CONTACTS_REMOVED,
  79 	CONTACT_CHANGED,
  80 	MODEL_CHANGED,
  81 	STOP_STATE_CHANGED,
  82 	BACKEND_DIED,
  83 	LAST_SIGNAL
  84 };
  85 
  86 static guint signals[LAST_SIGNAL];
  87 
  88 G_DEFINE_TYPE (EAddressbookModel, e_addressbook_model, G_TYPE_OBJECT)
  89 
  90 static void
  91 free_data (EAddressbookModel *model)
  92 {
  93 	GPtrArray *array;
  94 
  95 	array = model->priv->contacts;
  96 	g_ptr_array_foreach (array, (GFunc) g_object_unref, NULL);
  97 	g_ptr_array_set_size (array, 0);
  98 }
  99 
 100 static void
 101 remove_book_view (EAddressbookModel *model)
 102 {
 103 	if (model->priv->client_view && model->priv->create_contact_id)
 104 		g_signal_handler_disconnect (
 105 			model->priv->client_view,
 106 			model->priv->create_contact_id);
 107 	if (model->priv->client_view && model->priv->remove_contact_id)
 108 		g_signal_handler_disconnect (
 109 			model->priv->client_view,
 110 			model->priv->remove_contact_id);
 111 	if (model->priv->client_view && model->priv->modify_contact_id)
 112 		g_signal_handler_disconnect (
 113 			model->priv->client_view,
 114 			model->priv->modify_contact_id);
 115 	if (model->priv->client_view && model->priv->status_message_id)
 116 		g_signal_handler_disconnect (
 117 			model->priv->client_view,
 118 			model->priv->status_message_id);
 119 	if (model->priv->client_view && model->priv->view_complete_id)
 120 		g_signal_handler_disconnect (
 121 			model->priv->client_view,
 122 			model->priv->view_complete_id);
 123 	if (model->priv->remove_status_id)
 124 		g_source_remove (model->priv->remove_status_id);
 125 
 126 	model->priv->create_contact_id = 0;
 127 	model->priv->remove_contact_id = 0;
 128 	model->priv->modify_contact_id = 0;
 129 	model->priv->status_message_id = 0;
 130 	model->priv->view_complete_id = 0;
 131 	model->priv->remove_status_id = 0;
 132 
 133 	model->priv->search_in_progress = FALSE;
 134 
 135 	if (model->priv->client_view) {
 136 		GError *error = NULL;
 137 
 138 		e_book_client_view_stop (model->priv->client_view, &error);
 139 
 140 		if (error != NULL) {
 141 			g_warning (
 142 				"%s: Failed to stop client view: %s",
 143 				G_STRFUNC, error->message);
 144 			g_error_free (error);
 145 		}
 146 
 147 		g_object_unref (model->priv->client_view);
 148 		model->priv->client_view = NULL;
 149 
 150 		g_signal_emit (model, signals[STATUS_MESSAGE], 0, NULL, -1);
 151 	}
 152 }
 153 
 154 static void
 155 update_folder_bar_message (EAddressbookModel *model)
 156 {
 157 	guint count;
 158 	gchar *message;
 159 
 160 	count = model->priv->contacts->len;
 161 
 162 	switch (count) {
 163 	case 0:
 164 		message = g_strdup (_("No contacts"));
 165 		break;
 166 	default:
 167 		message = g_strdup_printf (
 168 			ngettext ("%d contact", "%d contacts", count), count);
 169 		break;
 170 	}
 171 
 172 	g_signal_emit (model, signals[FOLDER_BAR_MESSAGE], 0, message);
 173 
 174 	g_free (message);
 175 }
 176 
 177 static void
 178 view_create_contact_cb (EBookClientView *client_view,
 179                         const GSList *contact_list,
 180                         EAddressbookModel *model)
 181 {
 182 	GPtrArray *array;
 183 	guint count;
 184 	guint index;
 185 
 186 	array = model->priv->contacts;
 187 	index = array->len;
 188 	count = g_list_length ((GList *) contact_list);
 189 
 190 	while (contact_list != NULL) {
 191 		EContact *contact = contact_list->data;
 192 
 193 		g_ptr_array_add (array, g_object_ref (contact));
 194 		contact_list = contact_list->next;
 195 	}
 196 
 197 	g_signal_emit (model, signals[CONTACT_ADDED], 0, index, count);
 198 	update_folder_bar_message (model);
 199 }
 200 
 201 static gint
 202 sort_descending (gconstpointer ca,
 203                  gconstpointer cb)
 204 {
 205 	gint a = *((gint *) ca);
 206 	gint b = *((gint *) cb);
 207 
 208 	return (a == b) ? 0 : (a < b) ? 1 : -1;
 209 }
 210 
 211 static void
 212 view_remove_contact_cb (EBookClientView *client_view,
 213                         const GSList *ids,
 214                         EAddressbookModel *model)
 215 {
 216 	/* XXX we should keep a hash around instead of this O(n*m) loop */
 217 	const GSList *iter;
 218 	GArray *indices;
 219 	GPtrArray *array;
 220 	gint ii;
 221 
 222 	array = model->priv->contacts;
 223 	indices = g_array_new (FALSE, FALSE, sizeof (gint));
 224 
 225 	for (iter = ids; iter != NULL; iter = iter->next) {
 226 		const gchar *target_uid = iter->data;
 227 
 228 		for (ii = 0; ii < array->len; ii++) {
 229 			EContact *contact;
 230 			const gchar *uid;
 231 
 232 			contact = array->pdata[ii];
 233 			/* check if already removed */
 234 			if (!contact)
 235 				continue;
 236 
 237 			uid = e_contact_get_const (contact, E_CONTACT_UID);
 238 			g_return_if_fail (uid != NULL);
 239 
 240 			if (strcmp (uid, target_uid) == 0) {
 241 				g_object_unref (contact);
 242 				g_array_append_val (indices, ii);
 243 				array->pdata[ii] = NULL;
 244 				break;
 245 			}
 246 		}
 247 	}
 248 
 249 	/* Sort the 'indices' array in descending order, since
 250 	 * g_ptr_array_remove_index() shifts subsequent elements
 251 	 * down one position to fill the gap. */
 252 	g_array_sort (indices, sort_descending);
 253 
 254 	for (ii = 0; ii < indices->len; ii++) {
 255 		gint index;
 256 
 257 		index = g_array_index (indices, gint, ii);
 258 		g_ptr_array_remove_index (array, index);
 259 	}
 260 
 261 	g_signal_emit (model, signals[CONTACTS_REMOVED], 0, indices);
 262 	g_array_free (indices, FALSE);
 263 
 264 	update_folder_bar_message (model);
 265 }
 266 
 267 static void
 268 view_modify_contact_cb (EBookClientView *client_view,
 269                         const GSList *contact_list,
 270                         EAddressbookModel *model)
 271 {
 272 	GPtrArray *array;
 273 
 274 	array = model->priv->contacts;
 275 
 276 	while (contact_list != NULL) {
 277 		EContact *new_contact = contact_list->data;
 278 		const gchar *target_uid;
 279 		gint ii;
 280 
 281 		target_uid = e_contact_get_const (new_contact, E_CONTACT_UID);
 282 		g_warn_if_fail (target_uid != NULL);
 283 
 284 		/* skip contacts without UID */
 285 		if (!target_uid) {
 286 			contact_list = contact_list->next;
 287 			continue;
 288 		}
 289 
 290 		for (ii = 0; ii < array->len; ii++) {
 291 			EContact *old_contact;
 292 			const gchar *uid;
 293 
 294 			old_contact = array->pdata[ii];
 295 			g_return_if_fail (old_contact != NULL);
 296 
 297 			uid = e_contact_get_const (old_contact, E_CONTACT_UID);
 298 			g_return_if_fail (uid != NULL);
 299 
 300 			if (strcmp (uid, target_uid) != 0)
 301 				continue;
 302 
 303 			g_object_unref (old_contact);
 304 			array->pdata[ii] = e_contact_duplicate (new_contact);
 305 
 306 			g_signal_emit (
 307 				model, signals[CONTACT_CHANGED], 0, ii);
 308 			break;
 309 		}
 310 
 311 		contact_list = contact_list->next;
 312 	}
 313 }
 314 
 315 static void
 316 view_progress_cb (EBookClientView *client_view,
 317                   guint percent,
 318                   const gchar *message,
 319                   EAddressbookModel *model)
 320 {
 321 	if (model->priv->remove_status_id)
 322 		g_source_remove (model->priv->remove_status_id);
 323 	model->priv->remove_status_id = 0;
 324 
 325 	g_signal_emit (model, signals[STATUS_MESSAGE], 0, message, percent);
 326 }
 327 
 328 static void
 329 view_complete_cb (EBookClientView *client_view,
 330                   const GError *error,
 331                   EAddressbookModel *model)
 332 {
 333 	model->priv->search_in_progress = FALSE;
 334 	view_progress_cb (client_view, -1, NULL, model);
 335 	g_signal_emit (model, signals[SEARCH_RESULT], 0, error);
 336 	g_signal_emit (model, signals[STOP_STATE_CHANGED], 0);
 337 }
 338 
 339 static void
 340 readonly_cb (EBookClient *book_client,
 341              GParamSpec *pspec,
 342              EAddressbookModel *model)
 343 {
 344 	gboolean editable;
 345 
 346 	editable = !e_client_is_readonly (E_CLIENT (book_client));
 347 	e_addressbook_model_set_editable (model, editable);
 348 }
 349 
 350 static void
 351 backend_died_cb (EBookClient *book_client,
 352                  EAddressbookModel *model)
 353 {
 354 	g_signal_emit (model, signals[BACKEND_DIED], 0);
 355 }
 356 
 357 static void
 358 client_view_ready_cb (GObject *source_object,
 359                       GAsyncResult *result,
 360                       gpointer user_data)
 361 {
 362 	EBookClient *book_client = E_BOOK_CLIENT (source_object);
 363 	EBookClientView *client_view = NULL;
 364 	EAddressbookModel *model = user_data;
 365 	GError *error = NULL;
 366 
 367 	if (!e_book_client_get_view_finish (book_client, result, &client_view, &error))
 368 		client_view = NULL;
 369 
 370 	if (error) {
 371 		eab_error_dialog (NULL, _("Error getting book view"), error);
 372 		g_error_free (error);
 373 		return;
 374 	}
 375 
 376 	remove_book_view (model);
 377 	free_data (model);
 378 
 379 	model->priv->client_view = client_view;
 380 	if (model->priv->client_view) {
 381 		model->priv->create_contact_id = g_signal_connect (
 382 			model->priv->client_view, "objects-added",
 383 			G_CALLBACK (view_create_contact_cb), model);
 384 		model->priv->remove_contact_id = g_signal_connect (
 385 			model->priv->client_view, "objects-removed",
 386 			G_CALLBACK (view_remove_contact_cb), model);
 387 		model->priv->modify_contact_id = g_signal_connect (
 388 			model->priv->client_view, "objects-modified",
 389 			G_CALLBACK (view_modify_contact_cb), model);
 390 		model->priv->status_message_id = g_signal_connect (
 391 			model->priv->client_view, "progress",
 392 			G_CALLBACK (view_progress_cb), model);
 393 		model->priv->view_complete_id = g_signal_connect (
 394 			model->priv->client_view, "complete",
 395 			G_CALLBACK (view_complete_cb), model);
 396 
 397 		model->priv->search_in_progress = TRUE;
 398 	}
 399 
 400 	g_signal_emit (model, signals[MODEL_CHANGED], 0);
 401 	g_signal_emit (model, signals[SEARCH_STARTED], 0);
 402 	g_signal_emit (model, signals[STOP_STATE_CHANGED], 0);
 403 
 404 	if (model->priv->client_view) {
 405 		e_book_client_view_start (model->priv->client_view, &error);
 406 
 407 		if (error != NULL) {
 408 			g_warning (
 409 				"%s: Failed to start client view: %s",
 410 				G_STRFUNC, error->message);
 411 			g_error_free (error);
 412 		}
 413 	}
 414 }
 415 
 416 static gboolean
 417 addressbook_model_idle_cb (EAddressbookModel *model)
 418 {
 419 	model->priv->client_view_idle_id = 0;
 420 
 421 	if (model->priv->book_client && model->priv->query_str) {
 422 		remove_book_view (model);
 423 
 424 		if (model->priv->first_get_view) {
 425 			model->priv->first_get_view = FALSE;
 426 
 427 			if (e_client_check_capability (E_CLIENT (model->priv->book_client), "do-initial-query")) {
 428 				e_book_client_get_view (
 429 					model->priv->book_client, model->priv->query_str,
 430 					NULL, client_view_ready_cb, model);
 431 			} else {
 432 				free_data (model);
 433 
 434 				g_signal_emit (
 435 					model, signals[MODEL_CHANGED], 0);
 436 				g_signal_emit (
 437 					model, signals[STOP_STATE_CHANGED], 0);
 438 			}
 439 		} else
 440 			e_book_client_get_view (
 441 				model->priv->book_client, model->priv->query_str,
 442 				NULL, client_view_ready_cb, model);
 443 
 444 	}
 445 
 446 	g_object_unref (model);
 447 
 448 	return FALSE;
 449 }
 450 
 451 static gboolean
 452 remove_status_cb (gpointer data)
 453 {
 454 	EAddressbookModel *model = data;
 455 
 456 	g_return_val_if_fail (model != NULL, FALSE);
 457 	g_return_val_if_fail (E_IS_ADDRESSBOOK_MODEL (model), FALSE);
 458 
 459 	g_signal_emit (model, signals[STATUS_MESSAGE], 0, NULL, -1);
 460 	model->priv->remove_status_id = 0;
 461 
 462 	return FALSE;
 463 }
 464 
 465 static void
 466 addressbook_model_set_registry (EAddressbookModel *model,
 467                                 ESourceRegistry *registry)
 468 {
 469 	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
 470 	g_return_if_fail (model->priv->registry == NULL);
 471 
 472 	model->priv->registry = g_object_ref (registry);
 473 }
 474 
 475 static void
 476 addressbook_model_set_property (GObject *object,
 477                                 guint property_id,
 478                                 const GValue *value,
 479                                 GParamSpec *pspec)
 480 {
 481 	switch (property_id) {
 482 		case PROP_CLIENT:
 483 			e_addressbook_model_set_client (
 484 				E_ADDRESSBOOK_MODEL (object),
 485 				g_value_get_object (value));
 486 			return;
 487 
 488 		case PROP_EDITABLE:
 489 			e_addressbook_model_set_editable (
 490 				E_ADDRESSBOOK_MODEL (object),
 491 				g_value_get_boolean (value));
 492 			return;
 493 
 494 		case PROP_QUERY:
 495 			e_addressbook_model_set_query (
 496 				E_ADDRESSBOOK_MODEL (object),
 497 				g_value_get_string (value));
 498 			return;
 499 
 500 		case PROP_REGISTRY:
 501 			addressbook_model_set_registry (
 502 				E_ADDRESSBOOK_MODEL (object),
 503 				g_value_get_object (value));
 504 			return;
 505 	}
 506 
 507 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 508 
 509 }
 510 
 511 static void
 512 addressbook_model_get_property (GObject *object,
 513                                 guint property_id,
 514                                 GValue *value,
 515                                 GParamSpec *pspec)
 516 {
 517 	switch (property_id) {
 518 		case PROP_CLIENT:
 519 			g_value_set_object (
 520 				value, e_addressbook_model_get_client (
 521 				E_ADDRESSBOOK_MODEL (object)));
 522 			return;
 523 
 524 		case PROP_EDITABLE:
 525 			g_value_set_boolean (
 526 				value, e_addressbook_model_get_editable (
 527 				E_ADDRESSBOOK_MODEL (object)));
 528 			return;
 529 
 530 		case PROP_QUERY:
 531 			g_value_set_string (
 532 				value, e_addressbook_model_get_query (
 533 				E_ADDRESSBOOK_MODEL (object)));
 534 			return;
 535 
 536 		case PROP_REGISTRY:
 537 			g_value_set_object (
 538 				value, e_addressbook_model_get_registry (
 539 				E_ADDRESSBOOK_MODEL (object)));
 540 			return;
 541 	}
 542 
 543 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 544 }
 545 
 546 static void
 547 addressbook_model_dispose (GObject *object)
 548 {
 549 	EAddressbookModel *model = E_ADDRESSBOOK_MODEL (object);
 550 
 551 	remove_book_view (model);
 552 	free_data (model);
 553 
 554 	if (model->priv->book_client) {
 555 		if (model->priv->writable_status_id)
 556 			g_signal_handler_disconnect (
 557 				model->priv->book_client,
 558 				model->priv->writable_status_id);
 559 		model->priv->writable_status_id = 0;
 560 
 561 		if (model->priv->backend_died_id)
 562 			g_signal_handler_disconnect (
 563 				model->priv->book_client,
 564 				model->priv->backend_died_id);
 565 		model->priv->backend_died_id = 0;
 566 
 567 		g_object_unref (model->priv->book_client);
 568 		model->priv->book_client = NULL;
 569 	}
 570 
 571 	if (model->priv->query_str) {
 572 		g_free (model->priv->query_str);
 573 		model->priv->query_str = NULL;
 574 	}
 575 
 576 	/* Chain up to parent's dispose() method. */
 577 	G_OBJECT_CLASS (e_addressbook_model_parent_class)->dispose (object);
 578 }
 579 
 580 static void
 581 addressbook_model_finalize (GObject *object)
 582 {
 583 	EAddressbookModelPrivate *priv;
 584 
 585 	priv = E_ADDRESSBOOK_MODEL_GET_PRIVATE (object);
 586 
 587 	g_ptr_array_free (priv->contacts, TRUE);
 588 
 589 	/* Chain up to parent's finalize() method. */
 590 	G_OBJECT_CLASS (e_addressbook_model_parent_class)->finalize (object);
 591 }
 592 
 593 static void
 594 e_addressbook_model_class_init (EAddressbookModelClass *class)
 595 {
 596 	GObjectClass *object_class;
 597 
 598 	g_type_class_add_private (class, sizeof (EAddressbookModelPrivate));
 599 
 600 	object_class = G_OBJECT_CLASS (class);
 601 	object_class->set_property = addressbook_model_set_property;
 602 	object_class->get_property = addressbook_model_get_property;
 603 	object_class->dispose = addressbook_model_dispose;
 604 	object_class->finalize = addressbook_model_finalize;
 605 
 606 	g_object_class_install_property (
 607 		object_class,
 608 		PROP_CLIENT,
 609 		g_param_spec_object (
 610 			"client",
 611 			"EBookClient",
 612 			NULL,
 613 			E_TYPE_BOOK_CLIENT,
 614 			G_PARAM_READWRITE |
 615 			G_PARAM_STATIC_STRINGS));
 616 
 617 	g_object_class_install_property (
 618 		object_class,
 619 		PROP_EDITABLE,
 620 		g_param_spec_boolean (
 621 			"editable",
 622 			"Editable",
 623 			NULL,
 624 			FALSE,
 625 			G_PARAM_READWRITE |
 626 			G_PARAM_STATIC_STRINGS));
 627 
 628 	g_object_class_install_property (
 629 		object_class,
 630 		PROP_QUERY,
 631 		g_param_spec_string (
 632 			"query",
 633 			"Query",
 634 			NULL,
 635 			NULL,
 636 			G_PARAM_READWRITE |
 637 			G_PARAM_CONSTRUCT |
 638 			G_PARAM_STATIC_STRINGS));
 639 
 640 	g_object_class_install_property (
 641 		object_class,
 642 		PROP_REGISTRY,
 643 		g_param_spec_object (
 644 			"registry",
 645 			"Registry",
 646 			"Data source registry",
 647 			E_TYPE_SOURCE_REGISTRY,
 648 			G_PARAM_READWRITE |
 649 			G_PARAM_CONSTRUCT_ONLY |
 650 			G_PARAM_STATIC_STRINGS));
 651 
 652 	signals[WRITABLE_STATUS] = g_signal_new (
 653 		"writable_status",
 654 		G_OBJECT_CLASS_TYPE (object_class),
 655 		G_SIGNAL_RUN_LAST,
 656 		G_STRUCT_OFFSET (EAddressbookModelClass, writable_status),
 657 		NULL, NULL,
 658 		g_cclosure_marshal_VOID__BOOLEAN,
 659 		G_TYPE_NONE, 1,
 660 		G_TYPE_BOOLEAN);
 661 
 662 	signals[STATUS_MESSAGE] = g_signal_new (
 663 		"status_message",
 664 		G_OBJECT_CLASS_TYPE (object_class),
 665 		G_SIGNAL_RUN_LAST,
 666 		G_STRUCT_OFFSET (EAddressbookModelClass, status_message),
 667 		NULL, NULL,
 668 		e_marshal_VOID__STRING_INT,
 669 		G_TYPE_NONE, 2,
 670 		G_TYPE_STRING,
 671 		G_TYPE_INT);
 672 
 673 	signals[SEARCH_STARTED] = g_signal_new (
 674 		"search_started",
 675 		G_OBJECT_CLASS_TYPE (object_class),
 676 		G_SIGNAL_RUN_LAST,
 677 		G_STRUCT_OFFSET (EAddressbookModelClass, search_started),
 678 		NULL, NULL,
 679 		g_cclosure_marshal_VOID__VOID,
 680 		G_TYPE_NONE, 0);
 681 
 682 	signals[SEARCH_RESULT] = g_signal_new (
 683 		"search_result",
 684 		G_OBJECT_CLASS_TYPE (object_class),
 685 		G_SIGNAL_RUN_LAST,
 686 		G_STRUCT_OFFSET (EAddressbookModelClass, search_result),
 687 		NULL, NULL,
 688 		g_cclosure_marshal_VOID__BOXED,
 689 		G_TYPE_NONE, 1,
 690 		G_TYPE_ERROR);
 691 
 692 	signals[FOLDER_BAR_MESSAGE] = g_signal_new (
 693 		"folder_bar_message",
 694 		G_OBJECT_CLASS_TYPE (object_class),
 695 		G_SIGNAL_RUN_LAST,
 696 		G_STRUCT_OFFSET (EAddressbookModelClass, folder_bar_message),
 697 		NULL, NULL,
 698 		g_cclosure_marshal_VOID__POINTER,
 699 		G_TYPE_NONE, 1,
 700 		G_TYPE_POINTER);
 701 
 702 	signals[CONTACT_ADDED] = g_signal_new (
 703 		"contact_added",
 704 		G_OBJECT_CLASS_TYPE (object_class),
 705 		G_SIGNAL_RUN_LAST,
 706 		G_STRUCT_OFFSET (EAddressbookModelClass, contact_added),
 707 		NULL, NULL,
 708 		e_marshal_NONE__INT_INT,
 709 		G_TYPE_NONE, 2,
 710 		G_TYPE_INT,
 711 		G_TYPE_INT);
 712 
 713 	signals[CONTACTS_REMOVED] = g_signal_new (
 714 		"contacts_removed",
 715 		G_OBJECT_CLASS_TYPE (object_class),
 716 		G_SIGNAL_RUN_LAST,
 717 		G_STRUCT_OFFSET (EAddressbookModelClass, contacts_removed),
 718 		NULL, NULL,
 719 		g_cclosure_marshal_VOID__POINTER,
 720 		G_TYPE_NONE, 1,
 721 		G_TYPE_POINTER);
 722 
 723 	signals[CONTACT_CHANGED] = g_signal_new (
 724 		"contact_changed",
 725 		G_OBJECT_CLASS_TYPE (object_class),
 726 		G_SIGNAL_RUN_LAST,
 727 		G_STRUCT_OFFSET (EAddressbookModelClass, contact_changed),
 728 		NULL, NULL,
 729 		g_cclosure_marshal_VOID__INT,
 730 		G_TYPE_NONE, 1,
 731 		G_TYPE_INT);
 732 
 733 	signals[MODEL_CHANGED] = g_signal_new (
 734 		"model_changed",
 735 		G_OBJECT_CLASS_TYPE (object_class),
 736 		G_SIGNAL_RUN_LAST,
 737 		G_STRUCT_OFFSET (EAddressbookModelClass, model_changed),
 738 		NULL, NULL,
 739 		g_cclosure_marshal_VOID__VOID,
 740 		G_TYPE_NONE, 0);
 741 
 742 	signals[STOP_STATE_CHANGED] = g_signal_new (
 743 		"stop_state_changed",
 744 		G_OBJECT_CLASS_TYPE (object_class),
 745 		G_SIGNAL_RUN_LAST,
 746 		G_STRUCT_OFFSET (EAddressbookModelClass, stop_state_changed),
 747 		NULL, NULL,
 748 		g_cclosure_marshal_VOID__VOID,
 749 		G_TYPE_NONE, 0);
 750 
 751 	signals[BACKEND_DIED] = g_signal_new (
 752 		"backend_died",
 753 		G_OBJECT_CLASS_TYPE (object_class),
 754 		G_SIGNAL_RUN_LAST,
 755 		G_STRUCT_OFFSET (EAddressbookModelClass, backend_died),
 756 		NULL, NULL,
 757 		g_cclosure_marshal_VOID__VOID,
 758 		G_TYPE_NONE, 0);
 759 }
 760 
 761 static void
 762 e_addressbook_model_init (EAddressbookModel *model)
 763 {
 764 	model->priv = E_ADDRESSBOOK_MODEL_GET_PRIVATE (model);
 765 	model->priv->contacts = g_ptr_array_new ();
 766 	model->priv->first_get_view = TRUE;
 767 }
 768 
 769 EAddressbookModel *
 770 e_addressbook_model_new (ESourceRegistry *registry)
 771 {
 772 	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
 773 
 774 	return g_object_new (
 775 		E_TYPE_ADDRESSBOOK_MODEL,
 776 		"registry", registry, NULL);
 777 }
 778 
 779 ESourceRegistry *
 780 e_addressbook_model_get_registry (EAddressbookModel *model)
 781 {
 782 	g_return_val_if_fail (E_IS_ADDRESSBOOK_MODEL (model), NULL);
 783 
 784 	return model->priv->registry;
 785 }
 786 
 787 EContact *
 788 e_addressbook_model_get_contact (EAddressbookModel *model,
 789                                  gint row)
 790 {
 791 	GPtrArray *array;
 792 
 793 	g_return_val_if_fail (E_IS_ADDRESSBOOK_MODEL (model), NULL);
 794 
 795 	array = model->priv->contacts;
 796 
 797 	if (0 <= row && row < array->len)
 798 		return e_contact_duplicate (array->pdata[row]);
 799 
 800 	return NULL;
 801 }
 802 
 803 void
 804 e_addressbook_model_stop (EAddressbookModel *model)
 805 {
 806 	const gchar *message;
 807 
 808 	g_return_if_fail (E_IS_ADDRESSBOOK_MODEL (model));
 809 
 810 	remove_book_view (model);
 811 
 812 	message = _("Search Interrupted");
 813 	g_signal_emit (model, signals[STOP_STATE_CHANGED], 0);
 814 	g_signal_emit (model, signals[STATUS_MESSAGE], 0, message, -1);
 815 
 816 	if (!model->priv->remove_status_id)
 817 		model->priv->remove_status_id =
 818 			g_timeout_add_seconds (3, remove_status_cb, model);
 819 }
 820 
 821 gboolean
 822 e_addressbook_model_can_stop (EAddressbookModel *model)
 823 {
 824 	g_return_val_if_fail (E_IS_ADDRESSBOOK_MODEL (model), FALSE);
 825 
 826 	return model->priv->search_in_progress;
 827 }
 828 
 829 void
 830 e_addressbook_model_force_folder_bar_message (EAddressbookModel *model)
 831 {
 832 	g_return_if_fail (E_IS_ADDRESSBOOK_MODEL (model));
 833 
 834 	update_folder_bar_message (model);
 835 }
 836 
 837 gint
 838 e_addressbook_model_contact_count (EAddressbookModel *model)
 839 {
 840 	g_return_val_if_fail (E_IS_ADDRESSBOOK_MODEL (model), 0);
 841 
 842 	return model->priv->contacts->len;
 843 }
 844 
 845 EContact *
 846 e_addressbook_model_contact_at (EAddressbookModel *model,
 847                                 gint index)
 848 {
 849 	g_return_val_if_fail (E_IS_ADDRESSBOOK_MODEL (model), NULL);
 850 
 851 	return model->priv->contacts->pdata[index];
 852 }
 853 
 854 gint
 855 e_addressbook_model_find (EAddressbookModel *model,
 856                           EContact *contact)
 857 {
 858 	GPtrArray *array;
 859 	gint ii;
 860 
 861 	/* XXX This searches for a particular EContact instance,
 862 	 *     as opposed to an equivalent but possibly different
 863 	 *     EContact instance.  Might have to revise this in
 864 	 *     the future. */
 865 
 866 	g_return_val_if_fail (E_IS_ADDRESSBOOK_MODEL (model), -1);
 867 	g_return_val_if_fail (E_IS_CONTACT (contact), -1);
 868 
 869 	array = model->priv->contacts;
 870 	for (ii = 0; ii < array->len; ii++) {
 871 		EContact *candidate = array->pdata[ii];
 872 
 873 		if (contact == candidate)
 874 			return ii;
 875 	}
 876 
 877 	return -1;
 878 }
 879 
 880 EBookClient *
 881 e_addressbook_model_get_client (EAddressbookModel *model)
 882 {
 883 	g_return_val_if_fail (E_IS_ADDRESSBOOK_MODEL (model), NULL);
 884 
 885 	return model->priv->book_client;
 886 }
 887 
 888 void
 889 e_addressbook_model_set_client (EAddressbookModel *model,
 890                                 EBookClient *book_client)
 891 {
 892 	gboolean editable;
 893 
 894 	g_return_if_fail (E_IS_ADDRESSBOOK_MODEL (model));
 895 	g_return_if_fail (E_IS_BOOK_CLIENT (book_client));
 896 
 897 	if (model->priv->book_client == book_client)
 898 		return;
 899 
 900 	if (model->priv->book_client != NULL) {
 901 		if (model->priv->book_client == book_client)
 902 			return;
 903 
 904 		if (model->priv->writable_status_id != 0)
 905 			g_signal_handler_disconnect (
 906 				model->priv->book_client,
 907 				model->priv->writable_status_id);
 908 		model->priv->writable_status_id = 0;
 909 
 910 		if (model->priv->backend_died_id != 0)
 911 			g_signal_handler_disconnect (
 912 				model->priv->book_client,
 913 				model->priv->backend_died_id);
 914 		model->priv->backend_died_id = 0;
 915 
 916 		g_object_unref (model->priv->book_client);
 917 	}
 918 
 919 	model->priv->book_client = g_object_ref (book_client);
 920 	model->priv->first_get_view = TRUE;
 921 
 922 	model->priv->writable_status_id = g_signal_connect (
 923 		book_client, "notify::readonly",
 924 		G_CALLBACK (readonly_cb), model);
 925 
 926 	model->priv->backend_died_id = g_signal_connect (
 927 		book_client, "backend-died",
 928 		G_CALLBACK (backend_died_cb), model);
 929 
 930 	editable = !e_client_is_readonly (E_CLIENT (book_client));
 931 	e_addressbook_model_set_editable (model, editable);
 932 
 933 	if (model->priv->client_view_idle_id == 0)
 934 		model->priv->client_view_idle_id = g_idle_add (
 935 			(GSourceFunc) addressbook_model_idle_cb,
 936 			g_object_ref (model));
 937 
 938 	g_object_notify (G_OBJECT (model), "client");
 939 }
 940 
 941 gboolean
 942 e_addressbook_model_get_editable (EAddressbookModel *model)
 943 {
 944 	g_return_val_if_fail (E_IS_ADDRESSBOOK_MODEL (model), FALSE);
 945 
 946 	return model->priv->editable;
 947 }
 948 
 949 void
 950 e_addressbook_model_set_editable (EAddressbookModel *model,
 951                                   gboolean editable)
 952 {
 953 	g_return_if_fail (E_IS_ADDRESSBOOK_MODEL (model));
 954 
 955 	if (model->priv->editable != editable) {
 956 		model->priv->editable = editable;
 957 
 958 		g_signal_emit (
 959 			model, signals[WRITABLE_STATUS], 0,
 960 			model->priv->editable);
 961 
 962 		g_object_notify (G_OBJECT (model), "editable");
 963 	}
 964 }
 965 
 966 gchar *
 967 e_addressbook_model_get_query (EAddressbookModel *model)
 968 {
 969 	g_return_val_if_fail (E_IS_ADDRESSBOOK_MODEL (model), NULL);
 970 
 971 	return model->priv->query_str;
 972 }
 973 
 974 void
 975 e_addressbook_model_set_query (EAddressbookModel *model,
 976                                const gchar *query)
 977 {
 978 	EBookQuery *book_query;
 979 
 980 	g_return_if_fail (E_IS_ADDRESSBOOK_MODEL (model));
 981 
 982 	if (query == NULL)
 983 		book_query = e_book_query_any_field_contains ("");
 984 	else
 985 		book_query = e_book_query_from_string (query);
 986 
 987 	/* also checks whether the query is a valid query string */
 988 	if (!book_query)
 989 		return;
 990 
 991 	if (model->priv->query_str != NULL) {
 992 		gchar *new_query;
 993 
 994 		new_query = e_book_query_to_string (book_query);
 995 
 996 		if (new_query && g_str_equal (model->priv->query_str, new_query)) {
 997 			g_free (new_query);
 998 			e_book_query_unref (book_query);
 999 			return;
1000 		}
1001 
1002 		g_free (new_query);
1003 	}
1004 
1005 	g_free (model->priv->query_str);
1006 	model->priv->query_str = e_book_query_to_string (book_query);
1007 	e_book_query_unref (book_query);
1008 
1009 	if (model->priv->client_view_idle_id == 0)
1010 		model->priv->client_view_idle_id = g_idle_add (
1011 			(GSourceFunc) addressbook_model_idle_cb,
1012 			g_object_ref (model));
1013 
1014 	g_object_notify (G_OBJECT (model), "query");
1015 }