Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / libedataserverui / e-destination-store.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /* e-destination-store.c - EDestination store with GtkTreeModel interface.
4  *
5  * Copyright (C) 2004 Novell, Inc.
6  *
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.
10  *
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.
15  *
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.
19  *
20  * Authors: Hans Petter Jansson <hpj@novell.com>
21  */
22
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26
27 #include <string.h>
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"
33
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)      \
37 G_STMT_START {                                        \
38         (iter)->stamp = (destination_store)->stamp;   \
39         (iter)->user_data = GINT_TO_POINTER (index);  \
40 } G_STMT_END
41
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,
49                                                          gint                index);
50 static gboolean     e_destination_store_get_iter        (GtkTreeModel       *tree_model,
51                                                          GtkTreeIter        *iter,
52                                                          GtkTreePath        *path);
53 GtkTreePath *e_destination_store_get_path        (GtkTreeModel       *tree_model,
54                                                          GtkTreeIter        *iter);
55 static void         e_destination_store_get_value       (GtkTreeModel       *tree_model,
56                                                          GtkTreeIter        *iter,
57                                                          gint                column,
58                                                          GValue             *value);
59 static gboolean     e_destination_store_iter_next       (GtkTreeModel       *tree_model,
60                                                          GtkTreeIter        *iter);
61 static gboolean     e_destination_store_iter_children   (GtkTreeModel       *tree_model,
62                                                          GtkTreeIter        *iter,
63                                                          GtkTreeIter        *parent);
64 static gboolean     e_destination_store_iter_has_child  (GtkTreeModel       *tree_model,
65                                                          GtkTreeIter        *iter);
66 static gint         e_destination_store_iter_n_children (GtkTreeModel       *tree_model,
67                                                          GtkTreeIter        *iter);
68 static gboolean     e_destination_store_iter_nth_child  (GtkTreeModel       *tree_model,
69                                                          GtkTreeIter        *iter,
70                                                          GtkTreeIter        *parent,
71                                                          gint                n);
72 static gboolean     e_destination_store_iter_parent     (GtkTreeModel       *tree_model,
73                                                          GtkTreeIter        *iter,
74                                                          GtkTreeIter        *child);
75
76 static void destination_changed (EDestinationStore *destination_store, EDestination *destination);
77 static void stop_destination    (EDestinationStore *destination_store, EDestination *destination);
78
79 /* ------------------ *
80  * Class/object setup *
81  * ------------------ */
82
83 static GObjectClass *parent_class = NULL;
84 static GType         column_types [E_DESTINATION_STORE_NUM_COLUMNS];
85
86 GType
87 e_destination_store_get_type (void)
88 {
89         static GType destination_store_type = 0;
90
91         if (!destination_store_type) {
92                 static const GTypeInfo destination_store_info =
93                 {
94                         sizeof (EDestinationStoreClass),
95                         NULL,           /* base_init */
96                         NULL,           /* base_finalize */
97                         (GClassInitFunc) e_destination_store_class_init,
98                         NULL,           /* class_finalize */
99                         NULL,           /* class_data */
100                         sizeof (EDestinationStore),
101                         0,
102                         (GInstanceInitFunc) e_destination_store_init,
103                 };
104
105                 static const GInterfaceInfo tree_model_info =
106                 {
107                         (GInterfaceInitFunc) e_destination_store_tree_model_init,
108                         NULL,
109                         NULL
110                 };
111
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;
115
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,
119                                              GTK_TYPE_TREE_MODEL,
120                                              &tree_model_info);
121         }
122
123         return destination_store_type;
124 }
125
126 static void
127 e_destination_store_class_init (EDestinationStoreClass *class)
128 {
129         GObjectClass *object_class;
130
131         parent_class = g_type_class_peek_parent (class);
132         object_class = (GObjectClass *) class;
133
134         object_class->finalize = e_destination_store_finalize;
135 }
136
137 static void
138 e_destination_store_tree_model_init (GtkTreeModelIface *iface)
139 {
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;
152 }
153
154 static void
155 e_destination_store_init (EDestinationStore *destination_store)
156 {
157         destination_store->stamp        = g_random_int ();
158         destination_store->destinations = g_ptr_array_new ();
159 }
160
161 static void
162 e_destination_store_finalize (GObject *object)
163 {
164         EDestinationStore *destination_store = E_DESTINATION_STORE (object);
165         gint               i;
166
167         for (i = 0; i < destination_store->destinations->len; i++) {
168                 EDestination *destination = g_ptr_array_index (destination_store->destinations, i);
169
170                 stop_destination (destination_store, destination);
171                 g_object_unref (destination);
172         }
173
174         g_ptr_array_free (destination_store->destinations, TRUE);
175
176         if (G_OBJECT_CLASS (parent_class)->finalize)
177                 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
178 }
179
180 /**
181  * e_destination_store_new:
182  *
183  * Creates a new #EDestinationStore.
184  *
185  * Return value: A new #EDestinationStore.
186  **/
187 EDestinationStore *
188 e_destination_store_new (void)
189 {
190         EDestinationStore *destination_store;
191
192         destination_store = E_DESTINATION_STORE (g_object_new (E_TYPE_DESTINATION_STORE, NULL));
193
194         return destination_store;
195 }
196
197 /* ------------------ *
198  * Row update helpers *
199  * ------------------ */
200
201 static void
202 row_deleted (EDestinationStore *destination_store, gint n)
203 {
204         GtkTreePath *path;
205
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);
210 }
211
212 static void
213 row_inserted (EDestinationStore *destination_store, gint n)
214 {
215         GtkTreePath *path;
216         GtkTreeIter  iter;
217
218         path = gtk_tree_path_new ();
219         gtk_tree_path_append_index (path, n);
220
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);
223
224         gtk_tree_path_free (path);
225 }
226
227 static void
228 row_changed (EDestinationStore *destination_store, gint n)
229 {
230         GtkTreePath *path;
231         GtkTreeIter  iter;
232
233         path = gtk_tree_path_new ();
234         gtk_tree_path_append_index (path, n);
235
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);
238
239         gtk_tree_path_free (path);
240 }
241
242 /* ------------------- *
243  * Destination helpers *
244  * ------------------- */
245
246 static gint
247 find_destination_by_pointer (EDestinationStore *destination_store, EDestination *destination)
248 {
249         gint i;
250
251         for (i = 0; i < destination_store->destinations->len; i++) {
252                 EDestination *destination_here = g_ptr_array_index (destination_store->destinations, i);
253
254                 if (destination_here == destination)
255                         return i;
256         }
257
258         return -1;
259 }
260
261 static gint
262 find_destination_by_email (EDestinationStore *destination_store, EDestination *destination)
263 {
264         gint i;
265         const char *e_mail = e_destination_get_email (destination);
266
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);
270
271                 if (g_str_equal (e_mail, mail))
272                         return i;
273         }
274
275         return -1;
276 }
277
278 static void
279 start_destination (EDestinationStore *destination_store, EDestination *destination)
280 {
281         g_signal_connect_swapped (destination, "changed",
282                                   G_CALLBACK (destination_changed), destination_store);
283 }
284
285 static void
286 stop_destination (EDestinationStore *destination_store, EDestination *destination)
287 {
288         g_signal_handlers_disconnect_matched (destination, G_SIGNAL_MATCH_DATA,
289                                               0, 0, NULL, NULL, destination_store);
290 }
291
292 /* --------------- *
293  * Signal handlers *
294  * --------------- */
295
296 static void
297 destination_changed (EDestinationStore *destination_store, EDestination *destination)
298 {
299         gint n;
300
301         n = find_destination_by_pointer (destination_store, destination);
302         if (n < 0) {
303                 g_warning ("EDestinationStore got change from unknown EDestination!");
304                 return;
305         }
306
307         row_changed (destination_store, n);
308 }
309
310 /* --------------------- *
311  * EDestinationStore API *
312  * --------------------- */
313
314 /**
315  * e_destination_store_get_destination:
316  * @destination_store: an #EDestinationStore
317  * @iter: a #GtkTreeIter
318  *
319  * Gets the #EDestination from @destination_store at @iter.
320  *
321  * Return value: An #EDestination.
322  **/
323 EDestination *
324 e_destination_store_get_destination (EDestinationStore *destination_store, GtkTreeIter *iter)
325 {
326         gint index;
327
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);
330
331         index = ITER_GET (iter);
332
333         return g_ptr_array_index (destination_store->destinations, index);
334 }
335
336 /**
337  * e_destination_store_list_destinations:
338  * @destination_store: an #EDestinationStore
339  *
340  * Gets a list of all the #EDestinations in @destination_store.
341  *
342  * Return value: A #GList of pointers to #EDestination. The list is owned
343  * by the caller, but the #EDestination elements aren't.
344  **/
345 GList *
346 e_destination_store_list_destinations (EDestinationStore *destination_store)
347 {
348         GList *destination_list = NULL;
349         gint   i;
350
351         g_return_val_if_fail (E_IS_DESTINATION_STORE (destination_store), NULL);
352
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);
356         }
357         
358         destination_list = g_list_reverse(destination_list);
359
360         return destination_list;
361 }
362
363 /**
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
368  *
369  * Inserts @destination into @destination_store at the position
370  * indicated by @index. @destination_store will ref @destination.
371  **/
372 void
373 e_destination_store_insert_destination (EDestinationStore *destination_store,
374                                         gint index, EDestination *destination)
375 {
376         g_return_if_fail (E_IS_DESTINATION_STORE (destination_store));
377         g_return_if_fail (index >= 0);
378
379         if (find_destination_by_pointer (destination_store, destination) >= 0) {
380                 g_warning ("Same destination added more than once to EDestinationStore!");
381                 return;
382         }
383
384         g_object_ref (destination);
385
386         index = MIN (index, destination_store->destinations->len);
387
388         g_ptr_array_set_size (destination_store->destinations,
389                               destination_store->destinations->len + 1);
390
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));
395         }
396
397         destination_store->destinations->pdata [index] = destination;
398         start_destination (destination_store, destination);
399         row_inserted (destination_store, index);
400 }
401
402 /**
403  * e_destination_store_append_destination:
404  * @destination_store: an #EDestinationStore
405  * @destination: an #EDestination
406  *
407  * Appends @destination to the list of destinations in @destination_store.
408  * @destination_store will ref @destination.
409  **/
410 void
411 e_destination_store_append_destination (EDestinationStore *destination_store, EDestination *destination)
412 {
413         g_return_if_fail (E_IS_DESTINATION_STORE (destination_store));
414
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!");
417                 return;
418         }
419
420         g_object_ref (destination);
421
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);
425 }
426
427 /**
428  * e_destination_store_remove_destination:
429  * @destination_store: an #EDestinationStore
430  * @destination: an #EDestination to remove
431  *
432  * Removes @destination from @destination_store. @destination_store will
433  * unref @destination.
434  **/
435 void
436 e_destination_store_remove_destination (EDestinationStore *destination_store, EDestination *destination)
437 {
438         gint n;
439
440         g_return_if_fail (E_IS_DESTINATION_STORE (destination_store));
441
442         n = find_destination_by_pointer (destination_store, destination);
443         if (n < 0) {
444                 g_warning ("Tried to remove unknown destination from EDestinationStore!");
445                 return;
446         }
447
448         stop_destination (destination_store, destination);
449         g_object_unref (destination);
450
451         g_ptr_array_remove_index (destination_store->destinations, n);
452         row_deleted (destination_store, n);
453 }
454
455 void
456 e_destination_store_remove_destination_nth (EDestinationStore *destination_store, int n)
457 {
458         EDestination *destination;
459         
460         g_return_if_fail ( n >= 0);
461
462         destination = g_ptr_array_index(destination_store->destinations, n);
463         stop_destination (destination_store, destination);
464         g_object_unref (destination);
465
466         g_ptr_array_remove_index (destination_store->destinations, n);
467         row_deleted (destination_store, n);
468 }
469
470 guint
471 e_destination_store_get_destination_count (EDestinationStore *destination_store)
472 {
473         return destination_store->destinations->len;
474 }
475
476 /* ---------------- *
477  * GtkTreeModel API *
478  * ---------------- */
479
480 static GtkTreeModelFlags
481 e_destination_store_get_flags (GtkTreeModel *tree_model)
482 {
483         g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), 0);
484
485         return GTK_TREE_MODEL_LIST_ONLY;
486 }
487
488 static gint
489 e_destination_store_get_n_columns (GtkTreeModel *tree_model)
490 {
491         g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), 0);
492
493         return E_CONTACT_FIELD_LAST;
494 }
495
496 static GType
497 e_destination_store_get_column_type (GtkTreeModel *tree_model,
498                                      gint          index)
499 {
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);
502
503         return column_types [index];
504 }
505
506 static gboolean
507 e_destination_store_get_iter (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path)
508 {
509         EDestinationStore *destination_store = E_DESTINATION_STORE (tree_model);
510         gint               index;
511
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);
514
515         index = gtk_tree_path_get_indices (path)[0];
516         if (index >= destination_store->destinations->len)
517                 return FALSE;
518
519         ITER_SET (destination_store, iter, index);
520         return TRUE;
521 }
522
523 GtkTreePath *
524 e_destination_store_get_path (GtkTreeModel *tree_model,
525                               GtkTreeIter  *iter)
526 {
527         EDestinationStore *destination_store = E_DESTINATION_STORE (tree_model);
528         GtkTreePath       *path;
529         gint               index;
530
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);
533
534         index = ITER_GET (iter);
535         path = gtk_tree_path_new ();
536         gtk_tree_path_append_index (path, index);
537
538         return path;
539 }
540
541 static gboolean
542 e_destination_store_iter_next (GtkTreeModel  *tree_model,
543                                GtkTreeIter   *iter)
544 {
545         EDestinationStore *destination_store = E_DESTINATION_STORE (tree_model);
546         gint           index;
547
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);
550
551         index = ITER_GET (iter);
552
553         if (index + 1 < destination_store->destinations->len) {
554                 ITER_SET (destination_store, iter, index + 1);
555                 return TRUE;
556         }
557
558         return FALSE;
559 }
560
561 static gboolean
562 e_destination_store_iter_children (GtkTreeModel *tree_model,
563                                    GtkTreeIter  *iter,
564                                    GtkTreeIter  *parent)
565 {
566         EDestinationStore *destination_store = E_DESTINATION_STORE (tree_model);
567
568         g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), FALSE);
569
570         /* This is a list, nodes have no children. */
571         if (parent)
572                 return FALSE;
573
574         /* But if parent == NULL we return the list itself as children of the root. */
575         if (destination_store->destinations->len <= 0)
576                 return FALSE;
577
578         ITER_SET (destination_store, iter, 0);
579         return TRUE;
580 }
581
582 static gboolean
583 e_destination_store_iter_has_child (GtkTreeModel *tree_model,
584                                     GtkTreeIter  *iter)
585 {
586         g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), FALSE);
587
588         if (iter == NULL)
589                 return TRUE;
590
591         return FALSE;
592 }
593
594 static gint
595 e_destination_store_iter_n_children (GtkTreeModel *tree_model,
596                                      GtkTreeIter  *iter)
597 {
598         EDestinationStore *destination_store = E_DESTINATION_STORE (tree_model);
599
600         g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), -1);
601
602         if (iter == NULL)
603                 return destination_store->destinations->len;
604
605         g_return_val_if_fail (ITER_IS_VALID (destination_store, iter), -1);
606         return 0;
607 }
608
609 static gboolean
610 e_destination_store_iter_nth_child (GtkTreeModel *tree_model,
611                                     GtkTreeIter  *iter,
612                                     GtkTreeIter  *parent,
613                                     gint          n)
614 {
615         EDestinationStore *destination_store = E_DESTINATION_STORE (tree_model);
616
617         g_return_val_if_fail (E_IS_DESTINATION_STORE (tree_model), FALSE);
618
619         if (parent)
620                 return FALSE;
621
622         if (n < destination_store->destinations->len) {
623                 ITER_SET (destination_store, iter, n);
624                 return TRUE;
625         }
626
627         return FALSE;
628 }
629
630 static gboolean
631 e_destination_store_iter_parent (GtkTreeModel *tree_model,
632                                  GtkTreeIter  *iter,
633                                  GtkTreeIter  *child)
634 {
635         return FALSE;
636 }
637
638 static void
639 e_destination_store_get_value (GtkTreeModel *tree_model,
640                                GtkTreeIter  *iter,
641                                gint          column,
642                                GValue       *value)
643 {
644         EDestinationStore *destination_store = E_DESTINATION_STORE (tree_model);
645         EDestination      *destination;
646         const gchar       *string;
647         GString           *string_new;
648         gint               row;
649         EContact          *contact;
650
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));
654
655         g_value_init (value, column_types [column]);
656
657         row = ITER_GET (iter);
658         if (row >= destination_store->destinations->len)
659                 return;
660
661         destination = g_ptr_array_index (destination_store->destinations, row);
662         g_assert (destination);
663
664         switch (column) {
665                 case E_DESTINATION_STORE_COLUMN_NAME:
666                         string = e_destination_get_name (destination);
667                         g_value_set_string (value, string);
668                         break;
669
670                 case E_DESTINATION_STORE_COLUMN_EMAIL:
671                         string = e_destination_get_email (destination);
672                         g_value_set_string (value, string);
673                         break;
674
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);
684                                 }
685                                 else {
686                                         string = e_destination_get_address (destination);
687                                         g_value_set_string (value, string);
688                                 }
689                         }
690                         else {
691                                 string = e_destination_get_address (destination);
692                                 g_value_set_string (value, string);
693
694                         }
695                         break;
696
697                 default:
698                         g_assert_not_reached ();
699                         break;
700         }
701 }