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) 2004 Novell, Inc.
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 <gtk/gtktreemodel.h>
29 #include <gtk/gtksignal.h>
30 #include <gtk/gtktreednd.h>
31 #include <glib/gi18n-lib.h>
32 #include "e-destination-store.h"
34 #define ITER_IS_VALID(destination_store, iter) ((iter)->stamp == (destination_store)->stamp)
35 #define ITER_GET(iter) GPOINTER_TO_INT (iter->user_data)
36 #define ITER_SET(destination_store, iter, index) \
38 (iter)->stamp = (destination_store)->stamp; \
39 (iter)->user_data = GINT_TO_POINTER (index); \
42 static void e_destination_store_init (EDestinationStore *destination_store);
43 static void e_destination_store_class_init (EDestinationStoreClass *class);
44 static void e_destination_store_tree_model_init (GtkTreeModelIface *iface);
45 static void e_destination_store_finalize (GObject *object);
46 static GtkTreeModelFlags e_destination_store_get_flags (GtkTreeModel *tree_model);
47 static gint e_destination_store_get_n_columns (GtkTreeModel *tree_model);
48 static GType e_destination_store_get_column_type (GtkTreeModel *tree_model,
50 static gboolean e_destination_store_get_iter (GtkTreeModel *tree_model,
53 GtkTreePath *e_destination_store_get_path (GtkTreeModel *tree_model,
55 static void e_destination_store_get_value (GtkTreeModel *tree_model,
59 static gboolean e_destination_store_iter_next (GtkTreeModel *tree_model,
61 static gboolean e_destination_store_iter_children (GtkTreeModel *tree_model,
64 static gboolean e_destination_store_iter_has_child (GtkTreeModel *tree_model,
66 static gint e_destination_store_iter_n_children (GtkTreeModel *tree_model,
68 static gboolean e_destination_store_iter_nth_child (GtkTreeModel *tree_model,
72 static gboolean e_destination_store_iter_parent (GtkTreeModel *tree_model,
76 static void destination_changed (EDestinationStore *destination_store, EDestination *destination);
77 static void stop_destination (EDestinationStore *destination_store, EDestination *destination);
79 /* ------------------ *
80 * Class/object setup *
81 * ------------------ */
83 static GObjectClass *parent_class = NULL;
84 static GType column_types [E_DESTINATION_STORE_NUM_COLUMNS];
87 e_destination_store_get_type (void)
89 static GType destination_store_type = 0;
91 if (!destination_store_type) {
92 static const GTypeInfo destination_store_info =
94 sizeof (EDestinationStoreClass),
96 NULL, /* base_finalize */
97 (GClassInitFunc) e_destination_store_class_init,
98 NULL, /* class_finalize */
99 NULL, /* class_data */
100 sizeof (EDestinationStore),
102 (GInstanceInitFunc) e_destination_store_init,
105 static const GInterfaceInfo tree_model_info =
107 (GInterfaceInitFunc) e_destination_store_tree_model_init,
112 column_types [E_DESTINATION_STORE_COLUMN_NAME] = G_TYPE_STRING;
113 column_types [E_DESTINATION_STORE_COLUMN_EMAIL] = G_TYPE_STRING;
114 column_types [E_DESTINATION_STORE_COLUMN_ADDRESS] = G_TYPE_STRING;
116 destination_store_type = g_type_register_static (G_TYPE_OBJECT, "EDestinationStore",
117 &destination_store_info, 0);
118 g_type_add_interface_static (destination_store_type,
123 return destination_store_type;
127 e_destination_store_class_init (EDestinationStoreClass *class)
129 GObjectClass *object_class;
131 parent_class = g_type_class_peek_parent (class);
132 object_class = (GObjectClass *) class;
134 object_class->finalize = e_destination_store_finalize;
138 e_destination_store_tree_model_init (GtkTreeModelIface *iface)
140 iface->get_flags = e_destination_store_get_flags;
141 iface->get_n_columns = e_destination_store_get_n_columns;
142 iface->get_column_type = e_destination_store_get_column_type;
143 iface->get_iter = e_destination_store_get_iter;
144 iface->get_path = e_destination_store_get_path;
145 iface->get_value = e_destination_store_get_value;
146 iface->iter_next = e_destination_store_iter_next;
147 iface->iter_children = e_destination_store_iter_children;
148 iface->iter_has_child = e_destination_store_iter_has_child;
149 iface->iter_n_children = e_destination_store_iter_n_children;
150 iface->iter_nth_child = e_destination_store_iter_nth_child;
151 iface->iter_parent = e_destination_store_iter_parent;
155 e_destination_store_init (EDestinationStore *destination_store)
157 destination_store->stamp = g_random_int ();
158 destination_store->destinations = g_ptr_array_new ();
162 e_destination_store_finalize (GObject *object)
164 EDestinationStore *destination_store = E_DESTINATION_STORE (object);
167 for (i = 0; i < destination_store->destinations->len; i++) {
168 EDestination *destination = g_ptr_array_index (destination_store->destinations, i);
170 stop_destination (destination_store, destination);
171 g_object_unref (destination);
174 g_ptr_array_free (destination_store->destinations, TRUE);
176 if (G_OBJECT_CLASS (parent_class)->finalize)
177 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
181 * e_destination_store_new:
183 * Creates a new #EDestinationStore.
185 * Return value: A new #EDestinationStore.
188 e_destination_store_new (void)
190 EDestinationStore *destination_store;
192 destination_store = E_DESTINATION_STORE (g_object_new (E_TYPE_DESTINATION_STORE, NULL));
194 return destination_store;
197 /* ------------------ *
198 * Row update helpers *
199 * ------------------ */
202 row_deleted (EDestinationStore *destination_store, gint n)
206 path = gtk_tree_path_new ();
207 gtk_tree_path_append_index (path, n);
208 gtk_tree_model_row_deleted (GTK_TREE_MODEL (destination_store), path);
209 gtk_tree_path_free (path);
213 row_inserted (EDestinationStore *destination_store, gint n)
218 path = gtk_tree_path_new ();
219 gtk_tree_path_append_index (path, n);
221 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (destination_store), &iter, path))
222 gtk_tree_model_row_inserted (GTK_TREE_MODEL (destination_store), path, &iter);
224 gtk_tree_path_free (path);
228 row_changed (EDestinationStore *destination_store, gint n)
233 path = gtk_tree_path_new ();
234 gtk_tree_path_append_index (path, n);
236 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (destination_store), &iter, path))
237 gtk_tree_model_row_changed (GTK_TREE_MODEL (destination_store), path, &iter);
239 gtk_tree_path_free (path);
242 /* ------------------- *
243 * Destination helpers *
244 * ------------------- */
247 find_destination_by_pointer (EDestinationStore *destination_store, EDestination *destination)
251 for (i = 0; i < destination_store->destinations->len; i++) {
252 EDestination *destination_here = g_ptr_array_index (destination_store->destinations, i);
254 if (destination_here == destination)
262 find_destination_by_email (EDestinationStore *destination_store, EDestination *destination)
265 const char *e_mail = e_destination_get_email (destination);
267 for (i = 0; i < destination_store->destinations->len; i++) {
268 EDestination *destination_here = g_ptr_array_index (destination_store->destinations, i);
269 const char *mail = e_destination_get_email (destination_here);
271 if (g_str_equal (e_mail, mail))
279 start_destination (EDestinationStore *destination_store, EDestination *destination)
281 g_signal_connect_swapped (destination, "changed",
282 G_CALLBACK (destination_changed), destination_store);
286 stop_destination (EDestinationStore *destination_store, EDestination *destination)
288 g_signal_handlers_disconnect_matched (destination, G_SIGNAL_MATCH_DATA,
289 0, 0, NULL, NULL, destination_store);
297 destination_changed (EDestinationStore *destination_store, EDestination *destination)
301 n = find_destination_by_pointer (destination_store, destination);
303 g_warning ("EDestinationStore got change from unknown EDestination!");
307 row_changed (destination_store, n);
310 /* --------------------- *
311 * EDestinationStore API *
312 * --------------------- */
315 * e_destination_store_get_destination:
316 * @destination_store: an #EDestinationStore
317 * @iter: a #GtkTreeIter
319 * Gets the #EDestination from @destination_store at @iter.
321 * Return value: An #EDestination.
324 e_destination_store_get_destination (EDestinationStore *destination_store, GtkTreeIter *iter)
328 g_return_val_if_fail (E_IS_DESTINATION_STORE (destination_store), NULL);
329 g_return_val_if_fail (ITER_IS_VALID (destination_store, iter), NULL);
331 index = ITER_GET (iter);
333 return g_ptr_array_index (destination_store->destinations, index);
337 * e_destination_store_list_destinations:
338 * @destination_store: an #EDestinationStore
340 * Gets a list of all the #EDestinations in @destination_store.
342 * Return value: A #GList of pointers to #EDestination. The list is owned
343 * by the caller, but the #EDestination elements aren't.
346 e_destination_store_list_destinations (EDestinationStore *destination_store)
348 GList *destination_list = NULL;
351 g_return_val_if_fail (E_IS_DESTINATION_STORE (destination_store), NULL);
353 for (i = 0; i < destination_store->destinations->len; i++) {
354 EDestination *destination = g_ptr_array_index (destination_store->destinations, i);
355 destination_list = g_list_prepend (destination_list, destination);
358 destination_list = g_list_reverse(destination_list);
360 return destination_list;
364 * e_destination_store_insert_destination:
365 * @destination_store: an #EDestinationStore
366 * @index: the index at which to insert
367 * @destination: an #EDestination to insert
369 * Inserts @destination into @destination_store at the position
370 * indicated by @index. @destination_store will ref @destination.
373 e_destination_store_insert_destination (EDestinationStore *destination_store,
374 gint index, EDestination *destination)
376 g_return_if_fail (E_IS_DESTINATION_STORE (destination_store));
377 g_return_if_fail (index >= 0);
379 if (find_destination_by_pointer (destination_store, destination) >= 0) {
380 g_warning ("Same destination added more than once to EDestinationStore!");
384 g_object_ref (destination);
386 index = MIN (index, destination_store->destinations->len);
388 g_ptr_array_set_size (destination_store->destinations,
389 destination_store->destinations->len + 1);
391 if (destination_store->destinations->len - 1 - index > 0) {
392 memmove (destination_store->destinations->pdata + index + 1,
393 destination_store->destinations->pdata + index,
394 (destination_store->destinations->len - 1 - index) * sizeof (gpointer));
397 destination_store->destinations->pdata [index] = destination;
398 start_destination (destination_store, destination);
399 row_inserted (destination_store, index);
403 * e_destination_store_append_destination:
404 * @destination_store: an #EDestinationStore
405 * @destination: an #EDestination
407 * Appends @destination to the list of destinations in @destination_store.
408 * @destination_store will ref @destination.
411 e_destination_store_append_destination (EDestinationStore *destination_store, EDestination *destination)
413 g_return_if_fail (E_IS_DESTINATION_STORE (destination_store));
415 if (find_destination_by_email (destination_store, destination) >= 0 && !e_destination_is_evolution_list (destination)) {
416 g_warning ("Same destination added more than once to EDestinationStore!");
420 g_object_ref (destination);
422 g_ptr_array_add (destination_store->destinations, destination);
423 start_destination (destination_store, destination);
424 row_inserted (destination_store, destination_store->destinations->len - 1);
428 * e_destination_store_remove_destination:
429 * @destination_store: an #EDestinationStore
430 * @destination: an #EDestination to remove
432 * Removes @destination from @destination_store. @destination_store will
433 * unref @destination.
436 e_destination_store_remove_destination (EDestinationStore *destination_store, EDestination *destination)
440 g_return_if_fail (E_IS_DESTINATION_STORE (destination_store));
442 n = find_destination_by_pointer (destination_store, destination);
444 g_warning ("Tried to remove unknown destination from EDestinationStore!");
448 stop_destination (destination_store, destination);
449 g_object_unref (destination);
451 g_ptr_array_remove_index (destination_store->destinations, n);
452 row_deleted (destination_store, n);
456 e_destination_store_remove_destination_nth (EDestinationStore *destination_store, int n)
458 EDestination *destination;
460 g_return_if_fail ( n >= 0);
462 destination = g_ptr_array_index(destination_store->destinations, n);
463 stop_destination (destination_store, destination);
464 g_object_unref (destination);
466 g_ptr_array_remove_index (destination_store->destinations, n);
467 row_deleted (destination_store, n);
471 e_destination_store_get_destination_count (EDestinationStore *destination_store)
473 return destination_store->destinations->len;
476 /* ---------------- *
478 * ---------------- */
480 static GtkTreeModelFlags
481 e_destination_store_get_flags (GtkTreeModel *tree_model)
483 g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), 0);
485 return GTK_TREE_MODEL_LIST_ONLY;
489 e_destination_store_get_n_columns (GtkTreeModel *tree_model)
491 g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), 0);
493 return E_CONTACT_FIELD_LAST;
497 e_destination_store_get_column_type (GtkTreeModel *tree_model,
500 g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), G_TYPE_INVALID);
501 g_return_val_if_fail (index >= 0 && index < E_DESTINATION_STORE_NUM_COLUMNS, G_TYPE_INVALID);
503 return column_types [index];
507 e_destination_store_get_iter (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path)
509 EDestinationStore *destination_store = E_DESTINATION_STORE (tree_model);
512 g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), FALSE);
513 g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
515 index = gtk_tree_path_get_indices (path)[0];
516 if (index >= destination_store->destinations->len)
519 ITER_SET (destination_store, iter, index);
524 e_destination_store_get_path (GtkTreeModel *tree_model,
527 EDestinationStore *destination_store = E_DESTINATION_STORE (tree_model);
531 g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), NULL);
532 g_return_val_if_fail (ITER_IS_VALID (destination_store, iter), NULL);
534 index = ITER_GET (iter);
535 path = gtk_tree_path_new ();
536 gtk_tree_path_append_index (path, index);
542 e_destination_store_iter_next (GtkTreeModel *tree_model,
545 EDestinationStore *destination_store = E_DESTINATION_STORE (tree_model);
548 g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), FALSE);
549 g_return_val_if_fail (ITER_IS_VALID (destination_store, iter), FALSE);
551 index = ITER_GET (iter);
553 if (index + 1 < destination_store->destinations->len) {
554 ITER_SET (destination_store, iter, index + 1);
562 e_destination_store_iter_children (GtkTreeModel *tree_model,
566 EDestinationStore *destination_store = E_DESTINATION_STORE (tree_model);
568 g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), FALSE);
570 /* This is a list, nodes have no children. */
574 /* But if parent == NULL we return the list itself as children of the root. */
575 if (destination_store->destinations->len <= 0)
578 ITER_SET (destination_store, iter, 0);
583 e_destination_store_iter_has_child (GtkTreeModel *tree_model,
586 g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), FALSE);
595 e_destination_store_iter_n_children (GtkTreeModel *tree_model,
598 EDestinationStore *destination_store = E_DESTINATION_STORE (tree_model);
600 g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), -1);
603 return destination_store->destinations->len;
605 g_return_val_if_fail (ITER_IS_VALID (destination_store, iter), -1);
610 e_destination_store_iter_nth_child (GtkTreeModel *tree_model,
615 EDestinationStore *destination_store = E_DESTINATION_STORE (tree_model);
617 g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), FALSE);
622 if (n < destination_store->destinations->len) {
623 ITER_SET (destination_store, iter, n);
631 e_destination_store_iter_parent (GtkTreeModel *tree_model,
639 e_destination_store_get_value (GtkTreeModel *tree_model,
644 EDestinationStore *destination_store = E_DESTINATION_STORE (tree_model);
645 EDestination *destination;
651 g_return_if_fail (E_IS_DESTINATION_STORE (tree_model));
652 g_return_if_fail (column < E_DESTINATION_STORE_NUM_COLUMNS);
653 g_return_if_fail (ITER_IS_VALID (destination_store, iter));
655 g_value_init (value, column_types [column]);
657 row = ITER_GET (iter);
658 if (row >= destination_store->destinations->len)
661 destination = g_ptr_array_index (destination_store->destinations, row);
662 g_assert (destination);
665 case E_DESTINATION_STORE_COLUMN_NAME:
666 string = e_destination_get_name (destination);
667 g_value_set_string (value, string);
670 case E_DESTINATION_STORE_COLUMN_EMAIL:
671 string = e_destination_get_email (destination);
672 g_value_set_string (value, string);
675 case E_DESTINATION_STORE_COLUMN_ADDRESS:
676 contact = e_destination_get_contact(destination);
677 if (contact && E_IS_CONTACT (contact)) {
678 if(e_contact_get (contact, E_CONTACT_IS_LIST)) {
679 string = e_destination_get_name (destination);
680 string_new = g_string_new(string);
681 string_new = g_string_append(string_new, " mailing list");
682 g_value_set_string (value, string_new->str);
683 g_string_free(string_new, TRUE);
686 string = e_destination_get_address (destination);
687 g_value_set_string (value, string);
691 string = e_destination_get_address (destination);
692 g_value_set_string (value, string);
698 g_assert_not_reached ();