1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /* e-name-selector-model.c - Model for contact selection.
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>
29 #include <glib/gi18n-lib.h>
30 #include "e-name-selector-model.h"
36 EDestinationStore *destination_store;
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);
47 /* ------------------ *
48 * Class/object setup *
49 * ------------------ */
59 static guint signals [LAST_SIGNAL] = { 0 };
61 G_DEFINE_TYPE (ENameSelectorModel, e_name_selector_model, G_TYPE_OBJECT);
64 e_name_selector_model_init (ENameSelectorModel *name_selector_model)
66 name_selector_model->sections = g_array_new (FALSE, FALSE, sizeof (Section));
67 name_selector_model->contact_store = e_contact_store_new ();
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);
78 g_object_unref (name_selector_model->contact_store);
80 name_selector_model->destination_uid_hash = NULL;
84 e_name_selector_model_finalize (GObject *object)
86 ENameSelectorModel *name_selector_model = E_NAME_SELECTOR_MODEL (object);
89 for (i = 0; i < name_selector_model->sections->len; i++)
90 free_section (name_selector_model, i);
92 g_array_free (name_selector_model->sections, TRUE);
93 g_object_unref (name_selector_model->contact_filter);
95 if (name_selector_model->destination_uid_hash)
96 g_hash_table_destroy (name_selector_model->destination_uid_hash);
98 if (G_OBJECT_CLASS (e_name_selector_model_parent_class)->finalize)
99 G_OBJECT_CLASS (e_name_selector_model_parent_class)->finalize (object);
103 e_name_selector_model_class_init (ENameSelectorModelClass *name_selector_model_class)
105 GObjectClass *object_class = G_OBJECT_CLASS (name_selector_model_class);
107 object_class->finalize = e_name_selector_model_finalize;
109 signals [SECTION_ADDED] =
110 g_signal_new ("section-added",
111 G_OBJECT_CLASS_TYPE (object_class),
113 G_STRUCT_OFFSET (ENameSelectorModelClass, section_added),
115 g_cclosure_marshal_VOID__STRING,
116 G_TYPE_NONE, 1, G_TYPE_STRING);
118 signals [SECTION_REMOVED] =
119 g_signal_new ("section-removed",
120 G_OBJECT_CLASS_TYPE (object_class),
122 G_STRUCT_OFFSET (ENameSelectorModelClass, section_removed),
124 g_cclosure_marshal_VOID__STRING,
125 G_TYPE_NONE, 1, G_TYPE_STRING);
129 * e_name_selector_model_new:
131 * Creates a new #ENameSelectorModel.
133 * Return value: A new #ENameSelectorModel.
136 e_name_selector_model_new (void)
138 return E_NAME_SELECTOR_MODEL (g_object_new (E_TYPE_NAME_SELECTOR_MODEL, NULL));
141 /* ---------------------------- *
142 * GtkTreeModelFilter filtering *
143 * ---------------------------- */
146 deep_free_list (GList *list)
150 for (l = list; l; l = g_list_next (l))
157 generate_contact_rows (EContactStore *contact_store, GtkTreeIter *iter,
158 ENameSelectorModel *name_selector_model)
161 const gchar *contact_uid;
162 gboolean result = TRUE;
166 contact = e_contact_store_get_contact (contact_store, iter);
167 g_assert (contact != NULL);
169 contact_uid = e_contact_get_const (contact, E_CONTACT_UID);
171 return TRUE; /* Can happen with broken databases */
173 for (i = 0; i < name_selector_model->sections->len && result == TRUE; i++) {
178 section = &g_array_index (name_selector_model->sections, Section, i);
179 destinations = e_destination_store_list_destinations (section->destination_store);
181 for (l = destinations; l; l = g_list_next (l)) {
182 EDestination *destination = l->data;
183 const gchar *destination_uid;
185 destination_uid = e_destination_get_contact_uid (destination);
186 if (destination_uid && !strcmp (contact_uid, destination_uid)) {
192 g_list_free (destinations);
198 if (e_contact_get (contact, E_CONTACT_IS_LIST)) {
201 email_list = e_contact_get (contact, E_CONTACT_EMAIL);
202 n_rows = g_list_length (email_list);
203 deep_free_list (email_list);
213 override_email_address (EContactStore *contact_store, GtkTreeIter *iter,
214 gint permutation_n, gint column, GValue *value,
215 ENameSelectorModel *name_selector_model)
217 if (column == E_CONTACT_EMAIL_1) {
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);
228 gtk_tree_model_get_value (GTK_TREE_MODEL (contact_store), iter, column, value);
238 ENameSelectorModel *name_selector_model;
239 GHashTable *other_hash;
244 emit_destination_uid_changes_cb (const gchar *uid, gpointer value, HashCompare *hash_compare)
246 EContactStore *contact_store = hash_compare->name_selector_model->contact_store;
248 if (!hash_compare->other_hash || !g_hash_table_lookup (hash_compare->other_hash, uid)) {
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);
261 destinations_changed (ENameSelectorModel *name_selector_model)
263 GHashTable *destination_uid_hash_new;
264 GHashTable *destination_uid_hash_old;
265 HashCompare hash_compare;
268 destination_uid_hash_new = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
270 for (i = 0; i < name_selector_model->sections->len; i++) {
271 Section *section = &g_array_index (name_selector_model->sections, Section, i);
275 destinations = e_destination_store_list_destinations (section->destination_store);
277 for (l = destinations; l; l = g_list_next (l)) {
278 EDestination *destination = l->data;
279 const gchar *destination_uid;
281 destination_uid = e_destination_get_contact_uid (destination);
283 g_hash_table_insert (destination_uid_hash_new,
284 g_strdup (destination_uid),
285 GINT_TO_POINTER (TRUE));
288 g_list_free (destinations);
291 destination_uid_hash_old = name_selector_model->destination_uid_hash;
292 name_selector_model->destination_uid_hash = destination_uid_hash_new;
294 hash_compare.name_selector_model = name_selector_model;
296 hash_compare.other_hash = destination_uid_hash_old;
297 g_hash_table_foreach (destination_uid_hash_new, (GHFunc) emit_destination_uid_changes_cb,
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,
305 g_hash_table_destroy (destination_uid_hash_old);
310 free_section (ENameSelectorModel *name_selector_model, gint n)
315 g_assert (n < name_selector_model->sections->len);
317 section = &g_array_index (name_selector_model->sections, Section, n);
319 g_signal_handlers_disconnect_matched (section->destination_store, G_SIGNAL_MATCH_DATA,
320 0, 0, NULL, NULL, name_selector_model);
322 g_free (section->name);
323 g_free (section->pretty_name);
324 g_object_unref (section->destination_store);
328 find_section_by_name (ENameSelectorModel *name_selector_model, const gchar *name)
332 g_assert (name != NULL);
334 for (i = 0; i < name_selector_model->sections->len; i++) {
335 Section *section = &g_array_index (name_selector_model->sections, Section, i);
337 if (!strcmp (name, section->name))
344 /* ---------------------- *
345 * ENameSelectorModel API *
346 * ---------------------- */
349 * e_name_selector_model_peek_contact_store:
350 * @name_selector_model: an #ENameSelectorModel
352 * Gets the #EContactStore associated with @name_selector_model.
354 * Return value: An #EContactStore.
357 e_name_selector_model_peek_contact_store (ENameSelectorModel *name_selector_model)
359 g_return_val_if_fail (E_IS_NAME_SELECTOR_MODEL (name_selector_model), NULL);
361 return name_selector_model->contact_store;
365 * e_name_selector_model_peek_contact_filter:
366 * @name_selector_model: an #ENameSelectorModel
368 * Gets the #ETreeModelGenerator being used to filter and/or extend the
369 * list of contacts in @name_selector_model's #EContactStore.
371 * Return value: An #ETreeModelGenerator.
373 ETreeModelGenerator *
374 e_name_selector_model_peek_contact_filter (ENameSelectorModel *name_selector_model)
376 g_return_val_if_fail (E_IS_NAME_SELECTOR_MODEL (name_selector_model), NULL);
378 return name_selector_model->contact_filter;
382 * e_name_selector_model_list_sections:
383 * @name_selector_model: an #ENameSelectorModel
385 * Gets a list of the destination sections in @name_selector_model.
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.
391 e_name_selector_model_list_sections (ENameSelectorModel *name_selector_model)
393 GList *section_names = NULL;
396 g_return_val_if_fail (E_IS_NAME_SELECTOR_MODEL (name_selector_model), NULL);
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);
403 name = g_strdup (section->name);
404 section_names = g_list_prepend (section_names, name);
407 return section_names;
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.
418 * Adds a destination section to @name_selector_model.
421 e_name_selector_model_add_section (ENameSelectorModel *name_selector_model,
422 const gchar *name, const gchar *pretty_name,
423 EDestinationStore *destination_store)
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);
431 if (find_section_by_name (name_selector_model, name) >= 0) {
432 g_warning ("ENameSelectorModel already has a section called '%s'!", name);
436 memset (§ion, 0, sizeof (Section));
438 section.name = g_strdup (name);
439 section.pretty_name = g_strdup (pretty_name);
441 if (destination_store)
442 section.destination_store = g_object_ref (destination_store);
444 section.destination_store = e_destination_store_new ();
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);
453 g_array_append_val (name_selector_model->sections, section);
455 destinations_changed (name_selector_model);
456 g_signal_emit (name_selector_model, signals [SECTION_ADDED], 0, name);
460 * e_name_selector_model_remove_section:
461 * @name_selector_model: an #ENameSelectorModel
462 * @name: internal name of the section to remove
464 * Removes a destination section from @name_selector_model.
467 e_name_selector_model_remove_section (ENameSelectorModel *name_selector_model, const gchar *name)
471 g_return_if_fail (E_IS_NAME_SELECTOR_MODEL (name_selector_model));
472 g_return_if_fail (name != NULL);
474 n = find_section_by_name (name_selector_model, name);
476 g_warning ("ENameSelectorModel does not have a section called '%s'!", name);
480 free_section (name_selector_model, n);
481 g_array_remove_index_fast (name_selector_model->sections, n); /* Order doesn't matter */
483 destinations_changed (name_selector_model);
484 g_signal_emit (name_selector_model, signals [SECTION_REMOVED], 0, name);
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
496 * Gets the parameters for a destination section.
499 e_name_selector_model_peek_section (ENameSelectorModel *name_selector_model, const gchar *name,
500 gchar **pretty_name, EDestinationStore **destination_store)
505 g_return_val_if_fail (E_IS_NAME_SELECTOR_MODEL (name_selector_model), FALSE);
506 g_return_val_if_fail (name != NULL, FALSE);
508 n = find_section_by_name (name_selector_model, name);
510 g_warning ("ENameSelectorModel does not have a section called '%s'!", name);
514 section = &g_array_index (name_selector_model->sections, Section, n);
517 *pretty_name = g_strdup (section->pretty_name);
518 if (destination_store)
519 *destination_store = section->destination_store;