1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /* e-name-selector.c - Unified context for contact/destination selection UI.
4 *
5 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * 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, see <http://www.gnu.org/licenses/>.
18 *
19 * Authors: Hans Petter Jansson <hpj@novell.com>
20 */
21
22 #include "evolution-config.h"
23
24 #include <string.h>
25 #include <gtk/gtk.h>
26 #include <glib/gi18n-lib.h>
27
28 #include <libebook/libebook.h>
29
30 #include "e-name-selector.h"
31
32 #include "e-contact-store.h"
33 #include "e-destination-store.h"
34
35 #define E_NAME_SELECTOR_GET_PRIVATE(obj) \
36 (G_TYPE_INSTANCE_GET_PRIVATE \
37 ((obj), E_TYPE_NAME_SELECTOR, ENameSelectorPrivate))
38
39 typedef struct {
40 gchar *name;
41 ENameSelectorEntry *entry;
42 } Section;
43
44 typedef struct {
45 EBookClient *client;
46 guint is_completion_book : 1;
47 } SourceBook;
48
49 struct _ENameSelectorPrivate {
50 EClientCache *client_cache;
51 ENameSelectorModel *model;
52 ENameSelectorDialog *dialog;
53
54 GArray *sections;
55
56 gboolean books_loaded;
57 GCancellable *cancellable;
58 GArray *source_books;
59 };
60
61 enum {
62 PROP_0,
63 PROP_CLIENT_CACHE
64 };
65
G_DEFINE_TYPE(ENameSelector,e_name_selector,G_TYPE_OBJECT)66 G_DEFINE_TYPE (ENameSelector, e_name_selector, G_TYPE_OBJECT)
67
68 static void
69 reset_pointer_cb (gpointer data,
70 GObject *where_was)
71 {
72 ENameSelector *name_selector = data;
73 ENameSelectorPrivate *priv;
74 guint ii;
75
76 g_return_if_fail (E_IS_NAME_SELECTOR (name_selector));
77
78 priv = E_NAME_SELECTOR_GET_PRIVATE (name_selector);
79
80 for (ii = 0; ii < priv->sections->len; ii++) {
81 Section *section;
82
83 section = &g_array_index (priv->sections, Section, ii);
84 if (section->entry == (ENameSelectorEntry *) where_was)
85 section->entry = NULL;
86 }
87 }
88
89 static void
name_selector_get_client_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)90 name_selector_get_client_cb (GObject *source_object,
91 GAsyncResult *result,
92 gpointer user_data)
93 {
94 ENameSelector *name_selector = user_data;
95 EBookClient *book_client;
96 EClient *client;
97 GArray *sections;
98 SourceBook source_book;
99 guint ii;
100 gboolean ignore_error;
101 GError *error = NULL;
102
103 client = e_client_cache_get_client_finish (
104 E_CLIENT_CACHE (source_object), result, &error);
105
106 /* Sanity check. */
107 g_return_if_fail (
108 ((client != NULL) && (error == NULL)) ||
109 ((client == NULL) && (error != NULL)));
110
111 ignore_error =
112 g_error_matches (
113 error, E_CLIENT_ERROR,
114 E_CLIENT_ERROR_REPOSITORY_OFFLINE) ||
115 g_error_matches (
116 error, E_CLIENT_ERROR,
117 E_CLIENT_ERROR_OFFLINE_UNAVAILABLE) ||
118 g_error_matches (
119 error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
120
121 if (error != NULL) {
122 if (!ignore_error)
123 g_warning ("%s: %s", G_STRFUNC, error->message);
124 g_error_free (error);
125 goto exit;
126 }
127
128 book_client = E_BOOK_CLIENT (client);
129 g_return_if_fail (E_IS_BOOK_CLIENT (book_client));
130
131 source_book.client = book_client;
132 source_book.is_completion_book = TRUE;
133
134 g_array_append_val (name_selector->priv->source_books, source_book);
135
136 sections = name_selector->priv->sections;
137
138 for (ii = 0; ii < sections->len; ii++) {
139 EContactStore *store;
140 Section *section;
141
142 section = &g_array_index (sections, Section, ii);
143 if (section->entry == NULL)
144 continue;
145
146 store = e_name_selector_entry_peek_contact_store (
147 section->entry);
148 if (store != NULL)
149 e_contact_store_add_client (store, book_client);
150 }
151
152 exit:
153 g_object_unref (name_selector);
154 }
155
156 /**
157 * e_name_selector_load_books:
158 * @name_selector: an #ENameSelector
159 *
160 * Loads address books available for the @name_selector.
161 * This can be called only once and it can be cancelled
162 * by e_name_selector_cancel_loading().
163 *
164 * Since: 3.2
165 **/
166 void
e_name_selector_load_books(ENameSelector * name_selector)167 e_name_selector_load_books (ENameSelector *name_selector)
168 {
169 EClientCache *client_cache;
170 ESourceRegistry *registry;
171 GList *list, *iter;
172 const gchar *extension_name;
173
174 g_return_if_fail (E_IS_NAME_SELECTOR (name_selector));
175
176 extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
177 client_cache = e_name_selector_ref_client_cache (name_selector);
178 registry = e_client_cache_ref_registry (client_cache);
179
180 list = e_source_registry_list_enabled (registry, extension_name);
181
182 for (iter = list; iter != NULL; iter = g_list_next (iter)) {
183 ESource *source = E_SOURCE (iter->data);
184 ESourceAutocomplete *extension;
185 const gchar *extension_name;
186
187 extension_name = E_SOURCE_EXTENSION_AUTOCOMPLETE;
188 extension = e_source_get_extension (source, extension_name);
189
190 /* Only load address books with autocomplete enabled,
191 * so as to avoid unnecessary authentication prompts. */
192 if (!e_source_autocomplete_get_include_me (extension))
193 continue;
194
195 /* FIXME GCancellable is only to be used for one
196 * operation at a time, not for multiple
197 * concurrent operations like this. */
198 e_client_cache_get_client (
199 client_cache, source,
200 E_SOURCE_EXTENSION_ADDRESS_BOOK, (guint32) -1,
201 name_selector->priv->cancellable,
202 name_selector_get_client_cb,
203 g_object_ref (name_selector));
204 }
205
206 g_list_free_full (list, (GDestroyNotify) g_object_unref);
207
208 g_object_unref (registry);
209 g_object_unref (client_cache);
210 }
211
212 /**
213 * e_name_selector_cancel_loading:
214 * @name_selector: an #ENameSelector
215 *
216 * Cancels any pending address book load operations. This might be called
217 * before an owner unrefs this @name_selector.
218 *
219 * Since: 3.2
220 **/
221 void
e_name_selector_cancel_loading(ENameSelector * name_selector)222 e_name_selector_cancel_loading (ENameSelector *name_selector)
223 {
224 g_return_if_fail (E_IS_NAME_SELECTOR (name_selector));
225 g_return_if_fail (name_selector->priv->cancellable != NULL);
226
227 g_cancellable_cancel (name_selector->priv->cancellable);
228 }
229
230 static void
name_selector_set_client_cache(ENameSelector * name_selector,EClientCache * client_cache)231 name_selector_set_client_cache (ENameSelector *name_selector,
232 EClientCache *client_cache)
233 {
234 g_return_if_fail (E_IS_CLIENT_CACHE (client_cache));
235 g_return_if_fail (name_selector->priv->client_cache == NULL);
236
237 name_selector->priv->client_cache = g_object_ref (client_cache);
238 }
239
240 static void
name_selector_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)241 name_selector_set_property (GObject *object,
242 guint property_id,
243 const GValue *value,
244 GParamSpec *pspec)
245 {
246 switch (property_id) {
247 case PROP_CLIENT_CACHE:
248 name_selector_set_client_cache (
249 E_NAME_SELECTOR (object),
250 g_value_get_object (value));
251 return;
252 }
253
254 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
255 }
256
257 static void
name_selector_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)258 name_selector_get_property (GObject *object,
259 guint property_id,
260 GValue *value,
261 GParamSpec *pspec)
262 {
263 switch (property_id) {
264 case PROP_CLIENT_CACHE:
265 g_value_take_object (
266 value,
267 e_name_selector_ref_client_cache (
268 E_NAME_SELECTOR (object)));
269 return;
270 }
271
272 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
273 }
274
275 static void
name_selector_dispose(GObject * object)276 name_selector_dispose (GObject *object)
277 {
278 ENameSelectorPrivate *priv;
279 guint ii;
280
281 priv = E_NAME_SELECTOR_GET_PRIVATE (object);
282
283 if (priv->cancellable) {
284 g_cancellable_cancel (priv->cancellable);
285 g_object_unref (priv->cancellable);
286 priv->cancellable = NULL;
287 }
288
289 for (ii = 0; ii < priv->source_books->len; ii++) {
290 SourceBook *source_book;
291
292 source_book = &g_array_index (
293 priv->source_books, SourceBook, ii);
294 if (source_book->client != NULL)
295 g_object_unref (source_book->client);
296 }
297
298 for (ii = 0; ii < priv->sections->len; ii++) {
299 Section *section;
300
301 section = &g_array_index (priv->sections, Section, ii);
302 if (section->entry)
303 g_object_weak_unref (
304 G_OBJECT (section->entry),
305 reset_pointer_cb, object);
306 g_free (section->name);
307 }
308
309 g_array_set_size (priv->source_books, 0);
310 g_array_set_size (priv->sections, 0);
311
312 if (priv->dialog) {
313 gtk_widget_destroy (GTK_WIDGET (priv->dialog));
314 priv->dialog = NULL;
315 }
316
317 g_clear_object (&priv->model);
318
319 /* Chain up to parent's dispose() method. */
320 G_OBJECT_CLASS (e_name_selector_parent_class)->dispose (object);
321 }
322
323 static void
name_selector_finalize(GObject * object)324 name_selector_finalize (GObject *object)
325 {
326 ENameSelectorPrivate *priv;
327
328 priv = E_NAME_SELECTOR_GET_PRIVATE (object);
329
330 g_array_free (priv->source_books, TRUE);
331 g_array_free (priv->sections, TRUE);
332
333 /* Chain up to parent's finalize() method. */
334 G_OBJECT_CLASS (e_name_selector_parent_class)->finalize (object);
335 }
336
337 static void
e_name_selector_class_init(ENameSelectorClass * class)338 e_name_selector_class_init (ENameSelectorClass *class)
339 {
340 GObjectClass *object_class;
341
342 g_type_class_add_private (class, sizeof (ENameSelectorPrivate));
343
344 object_class = G_OBJECT_CLASS (class);
345 object_class->set_property = name_selector_set_property;
346 object_class->get_property = name_selector_get_property;
347 object_class->dispose = name_selector_dispose;
348 object_class->finalize = name_selector_finalize;
349
350 /**
351 * ENameSelector:client-cache:
352 *
353 * Cache of shared #EClient instances.
354 **/
355 g_object_class_install_property (
356 object_class,
357 PROP_CLIENT_CACHE,
358 g_param_spec_object (
359 "client-cache",
360 "Client Cache",
361 "Cache of shared EClient instances",
362 E_TYPE_CLIENT_CACHE,
363 G_PARAM_READWRITE |
364 G_PARAM_CONSTRUCT_ONLY |
365 G_PARAM_STATIC_STRINGS));
366 }
367
368 static void
e_name_selector_init(ENameSelector * name_selector)369 e_name_selector_init (ENameSelector *name_selector)
370 {
371 GArray *sections;
372 GArray *source_books;
373
374 sections = g_array_new (FALSE, FALSE, sizeof (Section));
375 source_books = g_array_new (FALSE, FALSE, sizeof (SourceBook));
376
377 name_selector->priv = E_NAME_SELECTOR_GET_PRIVATE (name_selector);
378 name_selector->priv->sections = sections;
379 name_selector->priv->model = e_name_selector_model_new ();
380 name_selector->priv->source_books = source_books;
381 name_selector->priv->cancellable = g_cancellable_new ();
382 name_selector->priv->books_loaded = FALSE;
383 }
384
385 /**
386 * e_name_selector_new:
387 * @client_cache: an #EClientCache
388 *
389 * Creates a new #ENameSelector.
390 *
391 * Returns: A new #ENameSelector.
392 **/
393 ENameSelector *
e_name_selector_new(EClientCache * client_cache)394 e_name_selector_new (EClientCache *client_cache)
395 {
396 g_return_val_if_fail (E_IS_CLIENT_CACHE (client_cache), NULL);
397
398 return g_object_new (
399 E_TYPE_NAME_SELECTOR,
400 "client-cache", client_cache, NULL);
401 }
402
403 /**
404 * e_name_selector_ref_client_cache:
405 * @name_selector: an #ENameSelector
406 *
407 * Returns the #EClientCache passed to e_name_selector_new().
408 *
409 * The returned #EClientCache is referenced for thread-safety and must be
410 * unreferenced with g_object_unref() when finished with it.
411 *
412 * Returns: an #EClientCache
413 *
414 * Since: 3.8
415 **/
416 EClientCache *
e_name_selector_ref_client_cache(ENameSelector * name_selector)417 e_name_selector_ref_client_cache (ENameSelector *name_selector)
418 {
419 g_return_val_if_fail (E_IS_NAME_SELECTOR (name_selector), NULL);
420
421 return g_object_ref (name_selector->priv->client_cache);
422 }
423
424 /* ------- *
425 * Helpers *
426 * ------- */
427
428 static gint
add_section(ENameSelector * name_selector,const gchar * name)429 add_section (ENameSelector *name_selector,
430 const gchar *name)
431 {
432 GArray *array;
433 Section section;
434
435 g_return_val_if_fail (name != NULL, -1);
436
437 memset (§ion, 0, sizeof (Section));
438 section.name = g_strdup (name);
439
440 array = name_selector->priv->sections;
441 g_array_append_val (array, section);
442 return array->len - 1;
443 }
444
445 static gint
find_section_by_name(ENameSelector * name_selector,const gchar * name)446 find_section_by_name (ENameSelector *name_selector,
447 const gchar *name)
448 {
449 GArray *array;
450 gint i;
451
452 g_return_val_if_fail (name != NULL, -1);
453
454 array = name_selector->priv->sections;
455
456 for (i = 0; i < array->len; i++) {
457 Section *section = &g_array_index (array, Section, i);
458
459 if (!strcmp (name, section->name))
460 return i;
461 }
462
463 return -1;
464 }
465
466 /* ----------------- *
467 * ENameSelector API *
468 * ----------------- */
469
470 /**
471 * e_name_selector_peek_model:
472 * @name_selector: an #ENameSelector
473 *
474 * Gets the #ENameSelectorModel used by @name_selector.
475 *
476 * Returns: The #ENameSelectorModel used by @name_selector.
477 **/
478 ENameSelectorModel *
e_name_selector_peek_model(ENameSelector * name_selector)479 e_name_selector_peek_model (ENameSelector *name_selector)
480 {
481 g_return_val_if_fail (E_IS_NAME_SELECTOR (name_selector), NULL);
482
483 return name_selector->priv->model;
484 }
485
486 /**
487 * e_name_selector_peek_dialog:
488 * @name_selector: an #ENameSelector
489 *
490 * Gets the #ENameSelectorDialog used by @name_selector.
491 *
492 * Returns: The #ENameSelectorDialog used by @name_selector.
493 **/
494 ENameSelectorDialog *
e_name_selector_peek_dialog(ENameSelector * name_selector)495 e_name_selector_peek_dialog (ENameSelector *name_selector)
496 {
497 g_return_val_if_fail (E_IS_NAME_SELECTOR (name_selector), NULL);
498
499 if (name_selector->priv->dialog == NULL) {
500 EClientCache *client_cache;
501 ENameSelectorDialog *dialog;
502 ENameSelectorModel *model;
503
504 client_cache = e_name_selector_ref_client_cache (name_selector);
505 dialog = e_name_selector_dialog_new (client_cache);
506 name_selector->priv->dialog = dialog;
507 g_object_unref (client_cache);
508
509 model = e_name_selector_peek_model (name_selector);
510 e_name_selector_dialog_set_model (dialog, model);
511
512 g_signal_connect (
513 dialog, "delete-event",
514 G_CALLBACK (gtk_widget_hide_on_delete), name_selector);
515 }
516
517 return name_selector->priv->dialog;
518 }
519
520 /**
521 * e_name_selector_show_dialog:
522 * @name_selector: an #ENameSelector
523 * @for_transient_widget: a widget parent or %NULL
524 *
525 * Shows the associated dialog, and sets the transient parent to the
526 * GtkWindow top-level of "for_transient_widget if set (it should be)
527 *
528 * Since: 2.32
529 **/
530 void
e_name_selector_show_dialog(ENameSelector * name_selector,GtkWidget * for_transient_widget)531 e_name_selector_show_dialog (ENameSelector *name_selector,
532 GtkWidget *for_transient_widget)
533 {
534 GtkWindow *top = NULL;
535 ENameSelectorDialog *dialog;
536
537 g_return_if_fail (E_IS_NAME_SELECTOR (name_selector));
538
539 dialog = e_name_selector_peek_dialog (name_selector);
540 if (for_transient_widget)
541 top = GTK_WINDOW (gtk_widget_get_toplevel (for_transient_widget));
542 if (top)
543 gtk_window_set_transient_for (GTK_WINDOW (dialog), top);
544
545 gtk_widget_show (GTK_WIDGET (dialog));
546 }
547
548 /**
549 * e_name_selector_peek_section_entry:
550 * @name_selector: an #ENameSelector
551 * @name: the name of the section to peek
552 *
553 * Gets the #ENameSelectorEntry for the section specified by @name.
554 *
555 * Returns: The #ENameSelectorEntry for the named section, or %NULL if it
556 * doesn't exist in the #ENameSelectorModel.
557 **/
558 ENameSelectorEntry *
e_name_selector_peek_section_entry(ENameSelector * name_selector,const gchar * name)559 e_name_selector_peek_section_entry (ENameSelector *name_selector,
560 const gchar *name)
561 {
562 ENameSelectorPrivate *priv;
563 ENameSelectorModel *model;
564 EDestinationStore *destination_store;
565 Section *section;
566 gint n;
567
568 g_return_val_if_fail (E_IS_NAME_SELECTOR (name_selector), NULL);
569 g_return_val_if_fail (name != NULL, NULL);
570
571 priv = E_NAME_SELECTOR_GET_PRIVATE (name_selector);
572 model = e_name_selector_peek_model (name_selector);
573
574 if (!e_name_selector_model_peek_section (
575 model, name, NULL, &destination_store))
576 return NULL;
577
578 n = find_section_by_name (name_selector, name);
579 if (n < 0)
580 n = add_section (name_selector, name);
581
582 section = &g_array_index (name_selector->priv->sections, Section, n);
583
584 if (!section->entry) {
585 EClientCache *client_cache;
586 EContactStore *contact_store;
587 GtkWidget *widget;
588 gchar *text;
589 gint i;
590
591 client_cache = e_name_selector_ref_client_cache (name_selector);
592 widget = e_name_selector_entry_new (client_cache);
593 section->entry = E_NAME_SELECTOR_ENTRY (widget);
594 g_object_unref (client_cache);
595
596 g_object_weak_ref (G_OBJECT (section->entry), reset_pointer_cb, name_selector);
597 if (pango_parse_markup (name, -1, '_', NULL,
598 &text, NULL, NULL)) {
599 atk_object_set_name (gtk_widget_get_accessible (GTK_WIDGET (section->entry)), text);
600 g_free (text);
601 }
602 e_name_selector_entry_set_destination_store (section->entry, destination_store);
603
604 /* Create a contact store for the entry and assign our already-open books to it */
605
606 contact_store = e_contact_store_new ();
607
608 for (i = 0; i < priv->source_books->len; i++) {
609 SourceBook *source_book = &g_array_index (priv->source_books, SourceBook, i);
610
611 if (source_book->is_completion_book && source_book->client)
612 e_contact_store_add_client (contact_store, source_book->client);
613 }
614
615 e_name_selector_entry_set_contact_store (section->entry, contact_store);
616 g_object_unref (contact_store);
617 }
618
619 return section->entry;
620 }
621
622 /**
623 * e_name_selector_peek_section_list:
624 * @name_selector: an #ENameSelector
625 * @name: the name of the section to peek
626 *
627 * Gets the #ENameSelectorList for the section specified by @name.
628 *
629 * Returns: The #ENameSelectorList for the named section, or %NULL if it
630 * doesn't exist in the #ENameSelectorModel.
631 **/
632
633 ENameSelectorList *
e_name_selector_peek_section_list(ENameSelector * name_selector,const gchar * name)634 e_name_selector_peek_section_list (ENameSelector *name_selector,
635 const gchar *name)
636 {
637 ENameSelectorPrivate *priv;
638 ENameSelectorModel *model;
639 EDestinationStore *destination_store;
640 Section *section;
641 gint n;
642
643 g_return_val_if_fail (E_IS_NAME_SELECTOR (name_selector), NULL);
644 g_return_val_if_fail (name != NULL, NULL);
645
646 priv = E_NAME_SELECTOR_GET_PRIVATE (name_selector);
647 model = e_name_selector_peek_model (name_selector);
648
649 if (!e_name_selector_model_peek_section (
650 model, name, NULL, &destination_store))
651 return NULL;
652
653 n = find_section_by_name (name_selector, name);
654 if (n < 0)
655 n = add_section (name_selector, name);
656
657 section = &g_array_index (name_selector->priv->sections, Section, n);
658
659 if (!section->entry) {
660 EContactStore *contact_store;
661 EClientCache *client_cache;
662 GtkWidget *widget;
663 gchar *text;
664 gint i;
665
666 client_cache = e_name_selector_ref_client_cache (name_selector);
667 widget = e_name_selector_list_new (client_cache);
668 section->entry = E_NAME_SELECTOR_ENTRY (widget);
669 g_object_unref (client_cache);
670
671 g_object_weak_ref (G_OBJECT (section->entry), reset_pointer_cb, name_selector);
672 if (pango_parse_markup (name, -1, '_', NULL,
673 &text, NULL, NULL)) {
674 atk_object_set_name (gtk_widget_get_accessible (GTK_WIDGET (section->entry)), text);
675 g_free (text);
676 }
677 e_name_selector_entry_set_destination_store (section->entry, destination_store);
678
679 /* Create a contact store for the entry and assign our already-open books to it */
680
681 contact_store = e_contact_store_new ();
682 for (i = 0; i < priv->source_books->len; i++) {
683 SourceBook *source_book = &g_array_index (priv->source_books, SourceBook, i);
684
685 if (source_book->is_completion_book && source_book->client)
686 e_contact_store_add_client (contact_store, source_book->client);
687 }
688
689 e_name_selector_entry_set_contact_store (section->entry, contact_store);
690 g_object_unref (contact_store);
691 }
692
693 return (ENameSelectorList *) section->entry;
694 }
695