1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /* e-destination-store.c - EDestination store with GtkTreeModel interface.
5 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of version 2 of the GNU Lesser General Public
9 * License as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * Authors: Hans Petter Jansson <hpj@novell.com>
28 #include <glib/gi18n-lib.h>
30 #include "e-destination-store.h"
32 #define ITER_IS_VALID(destination_store, iter) \
33 ((iter)->stamp == (destination_store)->priv->stamp)
34 #define ITER_GET(iter) \
35 GPOINTER_TO_INT (iter->user_data)
36 #define ITER_SET(destination_store, iter, index) \
38 (iter)->stamp = (destination_store)->priv->stamp; \
39 (iter)->user_data = GINT_TO_POINTER (index); \
42 #define E_DESTINATION_STORE_GET_PRIVATE(obj) \
43 (G_TYPE_INSTANCE_GET_PRIVATE \
44 ((obj), E_TYPE_DESTINATION_STORE, EDestinationStorePrivate))
46 struct _EDestinationStorePrivate {
47 GPtrArray *destinations;
51 static GType column_types[E_DESTINATION_STORE_NUM_COLUMNS];
53 static void e_destination_store_tree_model_init (GtkTreeModelIface *iface);
55 G_DEFINE_TYPE_EXTENDED (
56 EDestinationStore, e_destination_store, G_TYPE_OBJECT, 0,
57 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, e_destination_store_tree_model_init);
58 column_types[E_DESTINATION_STORE_COLUMN_NAME] = G_TYPE_STRING;
59 column_types[E_DESTINATION_STORE_COLUMN_EMAIL] = G_TYPE_STRING;
60 column_types[E_DESTINATION_STORE_COLUMN_ADDRESS] = G_TYPE_STRING;
63 static GtkTreeModelFlags e_destination_store_get_flags (GtkTreeModel *tree_model);
64 static gint e_destination_store_get_n_columns (GtkTreeModel *tree_model);
65 static GType e_destination_store_get_column_type (GtkTreeModel *tree_model,
67 static gboolean e_destination_store_get_iter (GtkTreeModel *tree_model,
70 static void e_destination_store_get_value (GtkTreeModel *tree_model,
74 static gboolean e_destination_store_iter_next (GtkTreeModel *tree_model,
76 static gboolean e_destination_store_iter_children (GtkTreeModel *tree_model,
79 static gboolean e_destination_store_iter_has_child (GtkTreeModel *tree_model,
81 static gint e_destination_store_iter_n_children (GtkTreeModel *tree_model,
83 static gboolean e_destination_store_iter_nth_child (GtkTreeModel *tree_model,
87 static gboolean e_destination_store_iter_parent (GtkTreeModel *tree_model,
91 static void destination_changed (EDestinationStore *destination_store, EDestination *destination);
92 static void stop_destination (EDestinationStore *destination_store, EDestination *destination);
95 destination_store_dispose (GObject *object)
97 EDestinationStorePrivate *priv;
100 priv = E_DESTINATION_STORE_GET_PRIVATE (object);
102 for (ii = 0; ii < priv->destinations->len; ii++) {
103 EDestination *destination;
105 destination = g_ptr_array_index (priv->destinations, ii);
106 stop_destination (E_DESTINATION_STORE (object), destination);
107 g_object_unref (destination);
109 g_ptr_array_set_size (priv->destinations, 0);
111 /* Chain up to parent's dispose() method. */
112 G_OBJECT_CLASS (e_destination_store_parent_class)->dispose (object);
116 destination_store_finalize (GObject *object)
118 EDestinationStorePrivate *priv;
120 priv = E_DESTINATION_STORE_GET_PRIVATE (object);
122 g_ptr_array_free (priv->destinations, TRUE);
124 /* Chain up to parent's finalize() method. */
125 G_OBJECT_CLASS (e_destination_store_parent_class)->finalize (object);
129 e_destination_store_class_init (EDestinationStoreClass *class)
131 GObjectClass *object_class;
133 g_type_class_add_private (class, sizeof (EDestinationStorePrivate));
135 object_class = G_OBJECT_CLASS (class);
136 object_class->dispose = destination_store_dispose;
137 object_class->finalize = destination_store_finalize;
141 e_destination_store_tree_model_init (GtkTreeModelIface *iface)
143 iface->get_flags = e_destination_store_get_flags;
144 iface->get_n_columns = e_destination_store_get_n_columns;
145 iface->get_column_type = e_destination_store_get_column_type;
146 iface->get_iter = e_destination_store_get_iter;
147 iface->get_path = e_destination_store_get_path;
148 iface->get_value = e_destination_store_get_value;
149 iface->iter_next = e_destination_store_iter_next;
150 iface->iter_children = e_destination_store_iter_children;
151 iface->iter_has_child = e_destination_store_iter_has_child;
152 iface->iter_n_children = e_destination_store_iter_n_children;
153 iface->iter_nth_child = e_destination_store_iter_nth_child;
154 iface->iter_parent = e_destination_store_iter_parent;
158 e_destination_store_init (EDestinationStore *destination_store)
160 destination_store->priv =
161 E_DESTINATION_STORE_GET_PRIVATE (destination_store);
163 destination_store->priv->destinations = g_ptr_array_new ();
164 destination_store->priv->stamp = g_random_int ();
168 * e_destination_store_new:
170 * Creates a new #EDestinationStore.
172 * Returns: A new #EDestinationStore.
175 e_destination_store_new (void)
177 return g_object_new (E_TYPE_DESTINATION_STORE, NULL);
180 /* ------------------ *
181 * Row update helpers *
182 * ------------------ */
185 row_deleted (EDestinationStore *destination_store,
190 path = gtk_tree_path_new ();
191 gtk_tree_path_append_index (path, n);
192 gtk_tree_model_row_deleted (GTK_TREE_MODEL (destination_store), path);
193 gtk_tree_path_free (path);
197 row_inserted (EDestinationStore *destination_store,
203 path = gtk_tree_path_new ();
204 gtk_tree_path_append_index (path, n);
206 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (destination_store), &iter, path))
207 gtk_tree_model_row_inserted (GTK_TREE_MODEL (destination_store), path, &iter);
209 gtk_tree_path_free (path);
213 row_changed (EDestinationStore *destination_store,
219 path = gtk_tree_path_new ();
220 gtk_tree_path_append_index (path, n);
222 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (destination_store), &iter, path))
223 gtk_tree_model_row_changed (GTK_TREE_MODEL (destination_store), path, &iter);
225 gtk_tree_path_free (path);
228 /* ------------------- *
229 * Destination helpers *
230 * ------------------- */
233 find_destination_by_pointer (EDestinationStore *destination_store,
234 EDestination *destination)
239 array = destination_store->priv->destinations;
241 for (i = 0; i < array->len; i++) {
242 EDestination *destination_here;
244 destination_here = g_ptr_array_index (array, i);
246 if (destination_here == destination)
254 find_destination_by_email (EDestinationStore *destination_store,
255 EDestination *destination)
259 const gchar *e_mail = e_destination_get_email (destination);
261 array = destination_store->priv->destinations;
263 for (i = 0; i < array->len; i++) {
264 EDestination *destination_here;
267 destination_here = g_ptr_array_index (array, i);
268 mail = e_destination_get_email (destination_here);
270 if (g_str_equal (e_mail, mail))
278 start_destination (EDestinationStore *destination_store,
279 EDestination *destination)
281 g_signal_connect_swapped (
282 destination, "changed",
283 G_CALLBACK (destination_changed), destination_store);
287 stop_destination (EDestinationStore *destination_store,
288 EDestination *destination)
290 g_signal_handlers_disconnect_matched (
291 destination, G_SIGNAL_MATCH_DATA,
292 0, 0, NULL, NULL, destination_store);
300 destination_changed (EDestinationStore *destination_store,
301 EDestination *destination)
305 n = find_destination_by_pointer (destination_store, destination);
307 g_warning ("EDestinationStore got change from unknown EDestination!");
311 row_changed (destination_store, n);
314 /* --------------------- *
315 * EDestinationStore API *
316 * --------------------- */
319 * e_destination_store_get_destination:
320 * @destination_store: an #EDestinationStore
321 * @iter: a #GtkTreeIter
323 * Gets the #EDestination from @destination_store at @iter.
325 * Returns: An #EDestination.
328 e_destination_store_get_destination (EDestinationStore *destination_store,
334 g_return_val_if_fail (E_IS_DESTINATION_STORE (destination_store), NULL);
335 g_return_val_if_fail (ITER_IS_VALID (destination_store, iter), NULL);
337 array = destination_store->priv->destinations;
338 index = ITER_GET (iter);
340 return g_ptr_array_index (array, index);
344 * e_destination_store_list_destinations:
345 * @destination_store: an #EDestinationStore
347 * Gets a list of all the #EDestinations in @destination_store.
349 * Returns: A #GList of pointers to #EDestination. The list is owned
350 * by the caller, but the #EDestination elements aren't.
353 e_destination_store_list_destinations (EDestinationStore *destination_store)
355 GList *destination_list = NULL;
359 g_return_val_if_fail (E_IS_DESTINATION_STORE (destination_store), NULL);
361 array = destination_store->priv->destinations;
363 for (i = 0; i < array->len; i++) {
364 EDestination *destination;
366 destination = g_ptr_array_index (array, i);
367 destination_list = g_list_prepend (destination_list, destination);
370 destination_list = g_list_reverse (destination_list);
372 return destination_list;
376 * e_destination_store_insert_destination:
377 * @destination_store: an #EDestinationStore
378 * @index: the index at which to insert
379 * @destination: an #EDestination to insert
381 * Inserts @destination into @destination_store at the position
382 * indicated by @index. @destination_store will ref @destination.
385 e_destination_store_insert_destination (EDestinationStore *destination_store,
387 EDestination *destination)
391 g_return_if_fail (E_IS_DESTINATION_STORE (destination_store));
392 g_return_if_fail (index >= 0);
394 if (find_destination_by_pointer (destination_store, destination) >= 0) {
395 g_warning ("Same destination added more than once to EDestinationStore!");
399 g_object_ref (destination);
401 array = destination_store->priv->destinations;
402 index = MIN (index, array->len);
404 g_ptr_array_set_size (array, array->len + 1);
406 if (array->len - 1 - index > 0) {
408 array->pdata + index + 1,
409 array->pdata + index,
410 (array->len - 1 - index) * sizeof (gpointer));
413 array->pdata[index] = destination;
414 start_destination (destination_store, destination);
415 row_inserted (destination_store, index);
419 * e_destination_store_append_destination:
420 * @destination_store: an #EDestinationStore
421 * @destination: an #EDestination
423 * Appends @destination to the list of destinations in @destination_store.
424 * @destination_store will ref @destination.
427 e_destination_store_append_destination (EDestinationStore *destination_store,
428 EDestination *destination)
432 g_return_if_fail (E_IS_DESTINATION_STORE (destination_store));
434 if (find_destination_by_email (destination_store, destination) >= 0 && !e_destination_is_evolution_list (destination)) {
435 g_warning ("Same destination added more than once to EDestinationStore!");
439 array = destination_store->priv->destinations;
440 g_object_ref (destination);
442 g_ptr_array_add (array, destination);
443 start_destination (destination_store, destination);
444 row_inserted (destination_store, array->len - 1);
448 * e_destination_store_remove_destination:
449 * @destination_store: an #EDestinationStore
450 * @destination: an #EDestination to remove
452 * Removes @destination from @destination_store. @destination_store will
453 * unref @destination.
456 e_destination_store_remove_destination (EDestinationStore *destination_store,
457 EDestination *destination)
462 g_return_if_fail (E_IS_DESTINATION_STORE (destination_store));
464 n = find_destination_by_pointer (destination_store, destination);
466 g_warning ("Tried to remove unknown destination from EDestinationStore!");
470 stop_destination (destination_store, destination);
471 g_object_unref (destination);
473 array = destination_store->priv->destinations;
474 g_ptr_array_remove_index (array, n);
475 row_deleted (destination_store, n);
479 e_destination_store_remove_destination_nth (EDestinationStore *destination_store,
482 EDestination *destination;
485 g_return_if_fail (n >= 0);
487 array = destination_store->priv->destinations;
488 destination = g_ptr_array_index (array, n);
489 stop_destination (destination_store, destination);
490 g_object_unref (destination);
492 g_ptr_array_remove_index (array, n);
493 row_deleted (destination_store, n);
497 e_destination_store_get_destination_count (EDestinationStore *destination_store)
499 return destination_store->priv->destinations->len;
502 /* ---------------- *
504 * ---------------- */
506 static GtkTreeModelFlags
507 e_destination_store_get_flags (GtkTreeModel *tree_model)
509 g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), 0);
511 return GTK_TREE_MODEL_LIST_ONLY;
515 e_destination_store_get_n_columns (GtkTreeModel *tree_model)
517 g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), 0);
519 return E_CONTACT_FIELD_LAST;
523 e_destination_store_get_column_type (GtkTreeModel *tree_model,
526 g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), G_TYPE_INVALID);
527 g_return_val_if_fail (index >= 0 && index < E_DESTINATION_STORE_NUM_COLUMNS, G_TYPE_INVALID);
529 return column_types[index];
533 e_destination_store_get_iter (GtkTreeModel *tree_model,
537 EDestinationStore *destination_store;
541 g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), FALSE);
542 g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
544 destination_store = E_DESTINATION_STORE (tree_model);
546 index = gtk_tree_path_get_indices (path)[0];
547 array = destination_store->priv->destinations;
549 if (index >= array->len)
552 ITER_SET (destination_store, iter, index);
557 e_destination_store_get_path (GtkTreeModel *tree_model,
560 EDestinationStore *destination_store = E_DESTINATION_STORE (tree_model);
564 g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), NULL);
565 g_return_val_if_fail (ITER_IS_VALID (destination_store, iter), NULL);
567 index = ITER_GET (iter);
568 path = gtk_tree_path_new ();
569 gtk_tree_path_append_index (path, index);
575 e_destination_store_iter_next (GtkTreeModel *tree_model,
578 EDestinationStore *destination_store = E_DESTINATION_STORE (tree_model);
581 g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), FALSE);
582 g_return_val_if_fail (ITER_IS_VALID (destination_store, iter), FALSE);
584 index = ITER_GET (iter);
586 if (index + 1 < destination_store->priv->destinations->len) {
587 ITER_SET (destination_store, iter, index + 1);
595 e_destination_store_iter_children (GtkTreeModel *tree_model,
599 EDestinationStore *destination_store = E_DESTINATION_STORE (tree_model);
601 g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), FALSE);
603 /* This is a list, nodes have no children. */
607 /* But if parent == NULL we return the list itself as children of the root. */
608 if (destination_store->priv->destinations->len <= 0)
611 ITER_SET (destination_store, iter, 0);
616 e_destination_store_iter_has_child (GtkTreeModel *tree_model,
619 g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), FALSE);
628 e_destination_store_iter_n_children (GtkTreeModel *tree_model,
631 EDestinationStore *destination_store = E_DESTINATION_STORE (tree_model);
633 g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), -1);
636 return destination_store->priv->destinations->len;
638 g_return_val_if_fail (ITER_IS_VALID (destination_store, iter), -1);
643 e_destination_store_iter_nth_child (GtkTreeModel *tree_model,
648 EDestinationStore *destination_store = E_DESTINATION_STORE (tree_model);
650 g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), FALSE);
655 if (n < destination_store->priv->destinations->len) {
656 ITER_SET (destination_store, iter, n);
664 e_destination_store_iter_parent (GtkTreeModel *tree_model,
672 e_destination_store_get_value (GtkTreeModel *tree_model,
677 EDestinationStore *destination_store = E_DESTINATION_STORE (tree_model);
678 EDestination *destination;
685 g_return_if_fail (E_IS_DESTINATION_STORE (tree_model));
686 g_return_if_fail (column < E_DESTINATION_STORE_NUM_COLUMNS);
687 g_return_if_fail (ITER_IS_VALID (destination_store, iter));
689 g_value_init (value, column_types[column]);
691 array = destination_store->priv->destinations;
693 row = ITER_GET (iter);
694 if (row >= array->len)
697 destination = g_ptr_array_index (array, row);
698 g_assert (destination);
701 case E_DESTINATION_STORE_COLUMN_NAME:
702 string = e_destination_get_name (destination);
703 g_value_set_string (value, string);
706 case E_DESTINATION_STORE_COLUMN_EMAIL:
707 string = e_destination_get_email (destination);
708 g_value_set_string (value, string);
711 case E_DESTINATION_STORE_COLUMN_ADDRESS:
712 contact = e_destination_get_contact (destination);
713 if (contact && E_IS_CONTACT (contact)) {
714 if (e_contact_get (contact, E_CONTACT_IS_LIST)) {
715 string = e_destination_get_name (destination);
716 string_new = g_string_new (string);
717 string_new = g_string_append (string_new, " mailing list");
718 g_value_set_string (value, string_new->str);
719 g_string_free (string_new, TRUE);
722 string = e_destination_get_address (destination);
723 g_value_set_string (value, string);
727 string = e_destination_get_address (destination);
728 g_value_set_string (value, string);
734 g_assert_not_reached ();
740 * e_destination_store_get_stamp:
741 * @destination_store: an #EDestinationStore
746 e_destination_store_get_stamp (EDestinationStore *destination_store)
748 g_return_val_if_fail (E_IS_DESTINATION_STORE (destination_store), 0);
750 return destination_store->priv->stamp;