Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / libedataserverui / e-name-selector-model.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /* e-name-selector-model.c - Model for contact selection.
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 <glib.h>
29 #include <glib/gi18n-lib.h>
30 #include "e-name-selector-model.h"
31
32 typedef struct {
33         gchar              *name;
34         gchar              *pretty_name;
35
36         EDestinationStore  *destination_store;
37 }
38 Section;
39
40 static gint generate_contact_rows  (EContactStore *contact_store, GtkTreeIter *iter,
41                                     ENameSelectorModel *name_selector_model);
42 static void override_email_address (EContactStore *contact_store, GtkTreeIter *iter,
43                                     gint permutation_n, gint column, GValue *value,
44                                     ENameSelectorModel *name_selector_model);
45 static void free_section           (ENameSelectorModel *name_selector_model, gint n);
46
47 /* ------------------ *
48  * Class/object setup *
49  * ------------------ */
50
51 /* Signals */
52
53 enum {
54         SECTION_ADDED,
55         SECTION_REMOVED,
56         LAST_SIGNAL
57 };
58
59 static guint signals [LAST_SIGNAL] = { 0 };
60
61 G_DEFINE_TYPE (ENameSelectorModel, e_name_selector_model, G_TYPE_OBJECT);
62
63 static void
64 e_name_selector_model_init (ENameSelectorModel *name_selector_model)
65 {
66         name_selector_model->sections       = g_array_new (FALSE, FALSE, sizeof (Section));
67         name_selector_model->contact_store  = e_contact_store_new ();
68
69         name_selector_model->contact_filter =
70                 e_tree_model_generator_new (GTK_TREE_MODEL (name_selector_model->contact_store));
71         e_tree_model_generator_set_generate_func (name_selector_model->contact_filter,
72                                                   (ETreeModelGeneratorGenerateFunc) generate_contact_rows,
73                                                   name_selector_model, NULL);
74         e_tree_model_generator_set_modify_func (name_selector_model->contact_filter,
75                                                 (ETreeModelGeneratorModifyFunc) override_email_address,
76                                                 name_selector_model, NULL);
77
78         g_object_unref (name_selector_model->contact_store);
79
80         name_selector_model->destination_uid_hash = NULL;
81 }
82
83 static void
84 e_name_selector_model_finalize (GObject *object)
85 {
86         ENameSelectorModel *name_selector_model = E_NAME_SELECTOR_MODEL (object);
87         gint                i;
88
89         for (i = 0; i < name_selector_model->sections->len; i++)
90                 free_section (name_selector_model, i);
91
92         g_array_free (name_selector_model->sections, TRUE);
93         g_object_unref (name_selector_model->contact_filter);
94
95         if (name_selector_model->destination_uid_hash)
96                 g_hash_table_destroy (name_selector_model->destination_uid_hash);
97
98         if (G_OBJECT_CLASS (e_name_selector_model_parent_class)->finalize)
99                 G_OBJECT_CLASS (e_name_selector_model_parent_class)->finalize (object);
100 }
101
102 static void
103 e_name_selector_model_class_init (ENameSelectorModelClass *name_selector_model_class)
104 {
105         GObjectClass *object_class = G_OBJECT_CLASS (name_selector_model_class);
106
107         object_class->finalize = e_name_selector_model_finalize;
108
109         signals [SECTION_ADDED] =
110                 g_signal_new ("section-added",
111                               G_OBJECT_CLASS_TYPE (object_class),
112                               G_SIGNAL_RUN_LAST,
113                               G_STRUCT_OFFSET (ENameSelectorModelClass, section_added),
114                               NULL, NULL,
115                               g_cclosure_marshal_VOID__STRING,
116                               G_TYPE_NONE, 1, G_TYPE_STRING);
117
118         signals [SECTION_REMOVED] =
119                 g_signal_new ("section-removed",
120                               G_OBJECT_CLASS_TYPE (object_class),
121                               G_SIGNAL_RUN_LAST,
122                               G_STRUCT_OFFSET (ENameSelectorModelClass, section_removed),
123                               NULL, NULL,
124                               g_cclosure_marshal_VOID__STRING,
125                               G_TYPE_NONE, 1, G_TYPE_STRING);
126 }
127
128 /**
129  * e_name_selector_model_new:
130  *
131  * Creates a new #ENameSelectorModel.
132  *
133  * Return value: A new #ENameSelectorModel.
134  **/
135 ENameSelectorModel *
136 e_name_selector_model_new (void)
137 {
138         return E_NAME_SELECTOR_MODEL (g_object_new (E_TYPE_NAME_SELECTOR_MODEL, NULL));
139 }
140
141 /* ---------------------------- *
142  * GtkTreeModelFilter filtering *
143  * ---------------------------- */
144
145 static void
146 deep_free_list (GList *list)
147 {
148         GList *l;
149
150         for (l = list; l; l = g_list_next (l))
151                 g_free (l->data);
152
153         g_list_free (list);
154 }
155
156 static gint
157 generate_contact_rows (EContactStore *contact_store, GtkTreeIter *iter,
158                        ENameSelectorModel *name_selector_model)
159 {
160         EContact    *contact;
161         const gchar *contact_uid;
162         gboolean     result = TRUE;
163         gint         n_rows;
164         gint         i;
165
166         contact = e_contact_store_get_contact (contact_store, iter);
167         g_assert (contact != NULL);
168
169         contact_uid = e_contact_get_const (contact, E_CONTACT_UID);
170         if (!contact_uid)
171                 return TRUE;  /* Can happen with broken databases */
172
173         for (i = 0; i < name_selector_model->sections->len && result == TRUE; i++) {
174                 Section *section;
175                 GList   *destinations;
176                 GList   *l;
177
178                 section = &g_array_index (name_selector_model->sections, Section, i);
179                 destinations = e_destination_store_list_destinations (section->destination_store);
180
181                 for (l = destinations; l; l = g_list_next (l)) {
182                         EDestination *destination = l->data;
183                         const gchar  *destination_uid;
184
185                         destination_uid = e_destination_get_contact_uid (destination);
186                         if (destination_uid && !strcmp (contact_uid, destination_uid)) {
187                                 result = FALSE;
188                                 break;
189                         }
190                 }
191
192                 g_list_free (destinations);
193         }
194
195         if (result) {
196                 GList    *email_list;
197
198                 if (e_contact_get (contact, E_CONTACT_IS_LIST)) {
199                         n_rows = 1;
200                 } else {
201                         email_list = e_contact_get (contact, E_CONTACT_EMAIL);
202                         n_rows = g_list_length (email_list);
203                         deep_free_list (email_list);
204                 }
205         } else {
206                 n_rows = 0;
207         }
208
209         return n_rows;
210 }
211
212 static void
213 override_email_address (EContactStore *contact_store, GtkTreeIter *iter,
214                         gint permutation_n, gint column, GValue *value,
215                         ENameSelectorModel *name_selector_model)
216 {
217         if (column == E_CONTACT_EMAIL_1) {
218                 EContact *contact;
219                 GList    *email_list;
220                 gchar    *email;
221
222                 contact = e_contact_store_get_contact (contact_store, iter);
223                 email_list = e_contact_get (contact, E_CONTACT_EMAIL);
224                 email = g_strdup (g_list_nth_data (email_list, permutation_n));
225                 g_value_set_string (value, email);
226                 deep_free_list (email_list);
227         } else {
228                 gtk_tree_model_get_value (GTK_TREE_MODEL (contact_store), iter, column, value);
229         }
230 }
231
232 /* --------------- *
233  * Section helpers *
234  * --------------- */
235
236 typedef struct
237 {
238         ENameSelectorModel *name_selector_model;
239         GHashTable         *other_hash;
240 }
241 HashCompare;
242
243 static void
244 emit_destination_uid_changes_cb (const gchar *uid, gpointer value, HashCompare *hash_compare)
245 {
246         EContactStore *contact_store = hash_compare->name_selector_model->contact_store;
247
248         if (!hash_compare->other_hash || !g_hash_table_lookup (hash_compare->other_hash, uid)) {
249                 GtkTreeIter  iter;
250                 GtkTreePath *path;
251
252                 if (e_contact_store_find_contact (contact_store, uid, &iter)) {
253                         path = gtk_tree_model_get_path (GTK_TREE_MODEL (contact_store), &iter);
254                         gtk_tree_model_row_changed (GTK_TREE_MODEL (contact_store), path, &iter);
255                         gtk_tree_path_free (path);
256                 }
257         }
258 }
259
260 static void
261 destinations_changed (ENameSelectorModel *name_selector_model)
262 {
263         GHashTable  *destination_uid_hash_new;
264         GHashTable  *destination_uid_hash_old;
265         HashCompare  hash_compare;
266         gint         i;
267
268         destination_uid_hash_new = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
269
270         for (i = 0; i < name_selector_model->sections->len; i++) {
271                 Section *section = &g_array_index (name_selector_model->sections, Section, i);
272                 GList   *destinations;
273                 GList   *l;
274
275                 destinations = e_destination_store_list_destinations (section->destination_store);
276
277                 for (l = destinations; l; l = g_list_next (l)) {
278                         EDestination *destination = l->data;
279                         const gchar  *destination_uid;
280
281                         destination_uid = e_destination_get_contact_uid (destination);
282                         if (destination_uid)
283                                 g_hash_table_insert (destination_uid_hash_new,
284                                                      g_strdup (destination_uid),
285                                                      GINT_TO_POINTER (TRUE));
286                 }
287
288                 g_list_free (destinations);
289         }
290
291         destination_uid_hash_old = name_selector_model->destination_uid_hash;
292         name_selector_model->destination_uid_hash = destination_uid_hash_new;
293
294         hash_compare.name_selector_model = name_selector_model;
295
296         hash_compare.other_hash = destination_uid_hash_old;
297         g_hash_table_foreach (destination_uid_hash_new, (GHFunc) emit_destination_uid_changes_cb,
298                               &hash_compare);
299
300         if (destination_uid_hash_old) {
301                 hash_compare.other_hash = destination_uid_hash_new;
302                 g_hash_table_foreach (destination_uid_hash_old, (GHFunc) emit_destination_uid_changes_cb,
303                                       &hash_compare);
304
305                 g_hash_table_destroy (destination_uid_hash_old);
306         }
307 }
308
309 static void
310 free_section (ENameSelectorModel *name_selector_model, gint n)
311 {
312         Section *section;
313
314         g_assert (n >= 0);
315         g_assert (n < name_selector_model->sections->len);
316
317         section = &g_array_index (name_selector_model->sections, Section, n);
318
319         g_signal_handlers_disconnect_matched (section->destination_store, G_SIGNAL_MATCH_DATA,
320                                               0, 0, NULL, NULL, name_selector_model);
321
322         g_free (section->name);
323         g_free (section->pretty_name);
324         g_object_unref (section->destination_store);
325 }
326
327 static gint
328 find_section_by_name (ENameSelectorModel *name_selector_model, const gchar *name)
329 {
330         gint i;
331
332         g_assert (name != NULL);
333
334         for (i = 0; i < name_selector_model->sections->len; i++) {
335                 Section *section = &g_array_index (name_selector_model->sections, Section, i);
336
337                 if (!strcmp (name, section->name))
338                         return i;
339         }
340
341         return -1;
342 }
343
344 /* ---------------------- *
345  * ENameSelectorModel API *
346  * ---------------------- */
347
348 /**
349  * e_name_selector_model_peek_contact_store:
350  * @name_selector_model: an #ENameSelectorModel
351  * 
352  * Gets the #EContactStore associated with @name_selector_model.
353  *
354  * Return value: An #EContactStore.
355  **/
356 EContactStore *
357 e_name_selector_model_peek_contact_store (ENameSelectorModel *name_selector_model)
358 {
359         g_return_val_if_fail (E_IS_NAME_SELECTOR_MODEL (name_selector_model), NULL);
360
361         return name_selector_model->contact_store;
362 }
363
364 /**
365  * e_name_selector_model_peek_contact_filter:
366  * @name_selector_model: an #ENameSelectorModel
367  *
368  * Gets the #ETreeModelGenerator being used to filter and/or extend the
369  * list of contacts in @name_selector_model's #EContactStore.
370  *
371  * Return value: An #ETreeModelGenerator.
372  **/
373 ETreeModelGenerator *
374 e_name_selector_model_peek_contact_filter (ENameSelectorModel *name_selector_model)
375 {
376         g_return_val_if_fail (E_IS_NAME_SELECTOR_MODEL (name_selector_model), NULL);
377
378         return name_selector_model->contact_filter;
379 }
380
381 /**
382  * e_name_selector_model_list_sections:
383  * @name_selector_model: an #ENameSelectorModel
384  *
385  * Gets a list of the destination sections in @name_selector_model.
386  *
387  * Return value: A #GList of pointers to strings. The #GList and the
388  * strings belong to the caller, and must be freed when no longer needed.
389  **/
390 GList *
391 e_name_selector_model_list_sections (ENameSelectorModel *name_selector_model)
392 {
393         GList *section_names = NULL;
394         gint   i;
395
396         g_return_val_if_fail (E_IS_NAME_SELECTOR_MODEL (name_selector_model), NULL);
397
398         /* Do this backwards so we can use g_list_prepend () and get correct order */
399         for (i = name_selector_model->sections->len - 1; i >= 0; i--) {
400                 Section *section = &g_array_index (name_selector_model->sections, Section, i);
401                 gchar   *name;
402
403                 name = g_strdup (section->name);
404                 section_names = g_list_prepend (section_names, name);
405         }
406
407         return section_names;
408 }
409
410 /**
411  * e_name_selector_model_add_section:
412  * @name_selector_model: an #ENameSelectorModel
413  * @name: internal name of this section
414  * @pretty_name: user-visible name of this section
415  * @destination_store: the #EDestinationStore to use to store the destinations for this
416  * section, or %NULL if @name_selector_model should create its own.
417  *
418  * Adds a destination section to @name_selector_model.
419  **/
420 void
421 e_name_selector_model_add_section (ENameSelectorModel *name_selector_model,
422                                    const gchar *name, const gchar *pretty_name,
423                                    EDestinationStore *destination_store)
424 {
425         Section section;
426
427         g_return_if_fail (E_IS_NAME_SELECTOR_MODEL (name_selector_model));
428         g_return_if_fail (name != NULL);
429         g_return_if_fail (pretty_name != NULL);
430
431         if (find_section_by_name (name_selector_model, name) >= 0) {
432                 g_warning ("ENameSelectorModel already has a section called '%s'!", name);
433                 return;
434         }
435
436         memset (&section, 0, sizeof (Section));
437
438         section.name              = g_strdup (name);
439         section.pretty_name       = g_strdup (pretty_name);
440
441         if (destination_store)
442                 section.destination_store = g_object_ref (destination_store);
443         else
444                 section.destination_store = e_destination_store_new ();
445
446         g_signal_connect_swapped (section.destination_store, "row-changed",
447                                   G_CALLBACK (destinations_changed), name_selector_model);
448         g_signal_connect_swapped (section.destination_store, "row-deleted",
449                                   G_CALLBACK (destinations_changed), name_selector_model);
450         g_signal_connect_swapped (section.destination_store, "row-inserted",
451                                   G_CALLBACK (destinations_changed), name_selector_model);
452
453         g_array_append_val (name_selector_model->sections, section);
454
455         destinations_changed (name_selector_model);
456         g_signal_emit (name_selector_model, signals [SECTION_ADDED], 0, name);
457 }
458
459 /**
460  * e_name_selector_model_remove_section:
461  * @name_selector_model: an #ENameSelectorModel
462  * @name: internal name of the section to remove
463  *
464  * Removes a destination section from @name_selector_model.
465  **/
466 void
467 e_name_selector_model_remove_section (ENameSelectorModel *name_selector_model, const gchar *name)
468 {
469         gint     n;
470
471         g_return_if_fail (E_IS_NAME_SELECTOR_MODEL (name_selector_model));
472         g_return_if_fail (name != NULL);
473
474         n = find_section_by_name (name_selector_model, name);
475         if (n < 0) {
476                 g_warning ("ENameSelectorModel does not have a section called '%s'!", name);
477                 return;
478         }
479
480         free_section (name_selector_model, n);
481         g_array_remove_index_fast (name_selector_model->sections, n);  /* Order doesn't matter */
482
483         destinations_changed (name_selector_model);
484         g_signal_emit (name_selector_model, signals [SECTION_REMOVED], 0, name);
485 }
486
487 /**
488  * e_name_selector_model_peek_section:
489  * @name_selector_model: an #ENameSelectorModel
490  * @name: internal name of the section to peek
491  * @pretty_name: location in which to store a pointer to the user-visible name of the section,
492  * or %NULL if undesired.
493  * @destination_store: location in which to store a pointer to the #EDestinationStore being used
494  * by the section, or %NULL if undesired
495  *
496  * Gets the parameters for a destination section.
497  **/
498 gboolean
499 e_name_selector_model_peek_section (ENameSelectorModel *name_selector_model, const gchar *name,
500                                     gchar **pretty_name, EDestinationStore **destination_store)
501 {
502         Section *section;
503         gint     n;
504
505         g_return_val_if_fail (E_IS_NAME_SELECTOR_MODEL (name_selector_model), FALSE);
506         g_return_val_if_fail (name != NULL, FALSE);
507
508         n = find_section_by_name (name_selector_model, name);
509         if (n < 0) {
510                 g_warning ("ENameSelectorModel does not have a section called '%s'!", name);
511                 return FALSE;
512         }
513
514         section = &g_array_index (name_selector_model->sections, Section, n);
515
516         if (pretty_name)
517                 *pretty_name = g_strdup (section->pretty_name);
518         if (destination_store)
519                 *destination_store = section->destination_store;
520
521         return TRUE;
522 }