1 /*
2 * e-source-config.c
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18 #include "evolution-config.h"
19
20 #include <glib/gi18n-lib.h>
21
22 #include <libebackend/libebackend.h>
23
24 #include "e-interval-chooser.h"
25 #include "e-marshal.h"
26 #include "e-misc-utils.h"
27 #include "e-source-config-backend.h"
28
29 #include "e-source-config.h"
30
31 #define E_SOURCE_CONFIG_GET_PRIVATE(obj) \
32 (G_TYPE_INSTANCE_GET_PRIVATE \
33 ((obj), E_TYPE_SOURCE_CONFIG, ESourceConfigPrivate))
34
35 typedef struct _Candidate Candidate;
36
37 struct _ESourceConfigPrivate {
38 ESource *original_source;
39 ESource *collection_source;
40 ESourceRegistry *registry;
41 gchar *preselect_type;
42
43 GHashTable *backends;
44 GPtrArray *candidates;
45
46 GtkWidget *type_label;
47 GtkWidget *type_combo;
48 GtkWidget *name_label;
49 GtkWidget *name_entry;
50 GtkWidget *backend_box;
51 GtkSizeGroup *size_group;
52
53 gboolean complete;
54 };
55
56 struct _Candidate {
57 GtkWidget *page;
58 ESource *scratch_source;
59 ESourceConfigBackend *backend;
60 gulong changed_handler_id;
61 };
62
63 enum {
64 PROP_0,
65 PROP_COLLECTION_SOURCE,
66 PROP_COMPLETE,
67 PROP_ORIGINAL_SOURCE,
68 PROP_REGISTRY
69 };
70
71 enum {
72 CHECK_COMPLETE,
73 COMMIT_CHANGES,
74 INIT_CANDIDATE,
75 RESIZE_WINDOW,
76 LAST_SIGNAL
77 };
78
79 static guint signals[LAST_SIGNAL];
80
G_DEFINE_TYPE_WITH_CODE(ESourceConfig,e_source_config,GTK_TYPE_BOX,G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE,NULL))81 G_DEFINE_TYPE_WITH_CODE (
82 ESourceConfig,
83 e_source_config,
84 GTK_TYPE_BOX,
85 G_IMPLEMENT_INTERFACE (
86 E_TYPE_EXTENSIBLE, NULL))
87
88 static void
89 source_config_init_backends (ESourceConfig *config)
90 {
91 GList *list, *iter;
92
93 config->priv->backends = g_hash_table_new_full (
94 (GHashFunc) g_str_hash,
95 (GEqualFunc) g_str_equal,
96 (GDestroyNotify) g_free,
97 (GDestroyNotify) g_object_unref);
98
99 e_extensible_load_extensions (E_EXTENSIBLE (config));
100
101 list = e_extensible_list_extensions (
102 E_EXTENSIBLE (config), E_TYPE_SOURCE_CONFIG_BACKEND);
103
104 for (iter = list; iter != NULL; iter = g_list_next (iter)) {
105 ESourceConfigBackend *backend;
106 ESourceConfigBackendClass *class;
107
108 backend = E_SOURCE_CONFIG_BACKEND (iter->data);
109 class = E_SOURCE_CONFIG_BACKEND_GET_CLASS (backend);
110
111 if (class->backend_name != NULL)
112 g_hash_table_insert (
113 config->priv->backends,
114 g_strdup (class->backend_name),
115 g_object_ref (backend));
116 }
117
118 g_list_free (list);
119 }
120
121 static gint
source_config_compare_sources(gconstpointer a,gconstpointer b,gpointer user_data)122 source_config_compare_sources (gconstpointer a,
123 gconstpointer b,
124 gpointer user_data)
125 {
126 ESource *source_a;
127 ESource *source_b;
128 ESource *parent_a;
129 ESource *parent_b;
130 ESourceConfig *config;
131 ESourceRegistry *registry;
132 const gchar *parent_uid_a;
133 const gchar *parent_uid_b;
134 gint result;
135
136 source_a = E_SOURCE (a);
137 source_b = E_SOURCE (b);
138 config = E_SOURCE_CONFIG (user_data);
139
140 if (e_source_equal (source_a, source_b))
141 return 0;
142
143 /* "On This Computer" always comes first. */
144
145 parent_uid_a = e_source_get_parent (source_a);
146 parent_uid_b = e_source_get_parent (source_b);
147
148 if (g_strcmp0 (parent_uid_a, "local-stub") == 0)
149 return -1;
150
151 if (g_strcmp0 (parent_uid_b, "local-stub") == 0)
152 return 1;
153
154 registry = e_source_config_get_registry (config);
155
156 parent_a = e_source_registry_ref_source (registry, parent_uid_a);
157 parent_b = e_source_registry_ref_source (registry, parent_uid_b);
158
159 g_return_val_if_fail (parent_a != NULL, 1);
160 g_return_val_if_fail (parent_b != NULL, -1);
161
162 result = e_source_compare_by_display_name (parent_a, parent_b);
163
164 g_object_unref (parent_a);
165 g_object_unref (parent_b);
166
167 return result;
168 }
169
170 static void
source_config_add_candidate(ESourceConfig * config,ESource * scratch_source,ESourceConfigBackend * backend)171 source_config_add_candidate (ESourceConfig *config,
172 ESource *scratch_source,
173 ESourceConfigBackend *backend)
174 {
175 Candidate *candidate;
176 GtkBox *backend_box;
177 GtkLabel *type_label;
178 GtkComboBoxText *type_combo;
179 GtkWidget *widget;
180 ESource *parent_source;
181 ESourceRegistry *registry;
182 const gchar *display_name;
183 const gchar *parent_uid;
184 gulong handler_id;
185
186 backend_box = GTK_BOX (config->priv->backend_box);
187 type_label = GTK_LABEL (config->priv->type_label);
188 type_combo = GTK_COMBO_BOX_TEXT (config->priv->type_combo);
189
190 registry = e_source_config_get_registry (config);
191 parent_uid = e_source_get_parent (scratch_source);
192 parent_source = e_source_registry_ref_source (registry, parent_uid);
193 g_return_if_fail (parent_source != NULL);
194
195 candidate = g_slice_new (Candidate);
196 candidate->backend = g_object_ref (backend);
197 candidate->scratch_source = g_object_ref (scratch_source);
198
199 /* Do not show the page here. */
200 widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
201 gtk_box_pack_start (backend_box, widget, FALSE, FALSE, 0);
202 candidate->page = g_object_ref_sink (widget);
203
204 g_ptr_array_add (config->priv->candidates, candidate);
205
206 display_name = e_source_get_display_name (parent_source);
207 gtk_combo_box_text_append_text (type_combo, display_name);
208 gtk_label_set_text (type_label, display_name);
209
210 /* Make sure the combo box has a valid active item before
211 * adding widgets. Otherwise we'll get run-time warnings
212 * as property bindings are set up. */
213 if (gtk_combo_box_get_active (GTK_COMBO_BOX (type_combo)) == -1)
214 gtk_combo_box_set_active (GTK_COMBO_BOX (type_combo), 0);
215
216 /* Bind the standard widgets to the new scratch source. */
217 g_signal_emit (
218 config, signals[INIT_CANDIDATE], 0,
219 candidate->scratch_source);
220
221 /* Insert any backend-specific widgets. */
222 e_source_config_backend_insert_widgets (
223 candidate->backend, candidate->scratch_source);
224
225 handler_id = g_signal_connect_swapped (
226 candidate->scratch_source, "changed",
227 G_CALLBACK (e_source_config_check_complete), config);
228
229 candidate->changed_handler_id = handler_id;
230
231 /* Trigger the "changed" handler we just connected to set the
232 * initial "complete" state based on the widgets we just added. */
233 e_source_changed (candidate->scratch_source);
234
235 g_object_unref (parent_source);
236 }
237
238 static void
source_config_free_candidate(Candidate * candidate)239 source_config_free_candidate (Candidate *candidate)
240 {
241 g_signal_handler_disconnect (
242 candidate->scratch_source,
243 candidate->changed_handler_id);
244
245 g_object_unref (candidate->page);
246 g_object_unref (candidate->scratch_source);
247 g_object_unref (candidate->backend);
248
249 g_slice_free (Candidate, candidate);
250 }
251
252 static Candidate *
source_config_get_active_candidate(ESourceConfig * config)253 source_config_get_active_candidate (ESourceConfig *config)
254 {
255 GtkComboBox *type_combo;
256 gint index;
257
258 type_combo = GTK_COMBO_BOX (config->priv->type_combo);
259 index = gtk_combo_box_get_active (type_combo);
260 g_return_val_if_fail (index >= 0, NULL);
261
262 return g_ptr_array_index (config->priv->candidates, index);
263 }
264
265 static void
source_config_type_combo_changed_cb(GtkComboBox * type_combo,ESourceConfig * config)266 source_config_type_combo_changed_cb (GtkComboBox *type_combo,
267 ESourceConfig *config)
268 {
269 Candidate *candidate;
270 GPtrArray *array;
271 gint index;
272
273 array = config->priv->candidates;
274
275 for (index = 0; index < array->len; index++) {
276 candidate = g_ptr_array_index (array, index);
277 gtk_widget_hide (candidate->page);
278 }
279
280 index = gtk_combo_box_get_active (type_combo);
281 if (index == CLAMP (index, 0, array->len)) {
282 candidate = g_ptr_array_index (array, index);
283 gtk_widget_show (candidate->page);
284 }
285
286 e_source_config_resize_window (config);
287 e_source_config_check_complete (config);
288 }
289
290 static gboolean
source_config_init_for_adding_source_foreach(gpointer key,gpointer value,gpointer user_data)291 source_config_init_for_adding_source_foreach (gpointer key,
292 gpointer value,
293 gpointer user_data)
294 {
295 ESource *scratch_source;
296 ESourceBackend *extension;
297 ESourceConfig *config;
298 ESourceConfigBackend *backend;
299 ESourceConfigBackendClass *class;
300 const gchar *extension_name;
301
302 scratch_source = E_SOURCE (key);
303 backend = E_SOURCE_CONFIG_BACKEND (value);
304 config = E_SOURCE_CONFIG (user_data);
305
306 /* This may not be the correct backend name for the child of a
307 * collection. For example, the "yahoo" collection backend uses
308 * the "caldav" calender backend for calendar children. But the
309 * ESourceCollectionBackend can override our setting if needed. */
310 class = E_SOURCE_CONFIG_BACKEND_GET_CLASS (backend);
311 extension_name = e_source_config_get_backend_extension_name (config);
312 extension = e_source_get_extension (scratch_source, extension_name);
313 e_source_backend_set_backend_name (extension, class->backend_name);
314
315 source_config_add_candidate (config, scratch_source, backend);
316
317 return FALSE; /* don't stop traversal */
318 }
319
320 static void
source_config_init_for_adding_source(ESourceConfig * config)321 source_config_init_for_adding_source (ESourceConfig *config)
322 {
323 GList *list, *link;
324 ESourceRegistry *registry;
325 GTree *scratch_source_tree;
326
327 /* Candidates are drawn from two sources:
328 *
329 * ESourceConfigBackend classes that specify a fixed parent UID,
330 * meaning there exists one only possible parent source for any
331 * scratch source created by the backend. The fixed parent UID
332 * should be a built-in "stub" placeholder ("local-stub", etc).
333 *
334 * -and-
335 *
336 * Collection sources. We let ESourceConfig subclasses gather
337 * eligible collection sources to serve as parents for scratch
338 * sources. A scratch source is matched to a backend based on
339 * the collection's backend name. The "calendar-enabled" and
340 * "contacts-enabled" settings also factor into eligibility.
341 */
342
343 /* Use a GTree instead of a GHashTable so inserted scratch
344 * sources automatically sort themselves by their parent's
345 * display name. */
346 scratch_source_tree = g_tree_new_full (
347 source_config_compare_sources, config,
348 (GDestroyNotify) g_object_unref,
349 (GDestroyNotify) g_object_unref);
350
351 registry = e_source_config_get_registry (config);
352
353 /* First pick out the backends with a fixed parent UID. */
354
355 list = g_hash_table_get_values (config->priv->backends);
356
357 for (link = list; link != NULL; link = g_list_next (link)) {
358 ESourceConfigBackend *backend;
359 ESourceConfigBackendClass *class;
360 ESource *scratch_source;
361 ESource *parent_source;
362 gboolean parent_is_disabled;
363
364 backend = E_SOURCE_CONFIG_BACKEND (link->data);
365 class = E_SOURCE_CONFIG_BACKEND_GET_CLASS (backend);
366
367 if (class->parent_uid == NULL)
368 continue;
369
370 /* Verify the fixed parent UID is valid. */
371 parent_source = e_source_registry_ref_source (
372 registry, class->parent_uid);
373 if (parent_source == NULL) {
374 g_warning (
375 "%s: %sClass specifies "
376 "an invalid parent_uid '%s'",
377 G_STRFUNC,
378 G_OBJECT_TYPE_NAME (backend),
379 class->parent_uid);
380 continue;
381 }
382 parent_is_disabled = !e_source_get_enabled (parent_source);
383 g_object_unref (parent_source);
384
385 /* It's unusual for a fixed parent source to be disabled.
386 * A user would have to go out of his way to do this, but
387 * we should honor it regardless. */
388 if (parent_is_disabled)
389 continue;
390
391 /* Some backends don't allow new sources to be created.
392 * The "contacts" calendar backend is one such example. */
393 if (!e_source_config_backend_allow_creation (backend))
394 continue;
395
396 scratch_source = e_source_new (NULL, NULL, NULL);
397 g_return_if_fail (scratch_source != NULL);
398
399 e_source_set_parent (scratch_source, class->parent_uid);
400
401 g_tree_insert (
402 scratch_source_tree,
403 g_object_ref (scratch_source),
404 g_object_ref (backend));
405
406 g_object_unref (scratch_source);
407 }
408
409 g_list_free (list);
410
411 /* Next gather eligible collection sources to serve as parents. */
412
413 list = e_source_config_list_eligible_collections (config);
414
415 for (link = list; link != NULL; link = g_list_next (link)) {
416 ESource *parent_source;
417 ESource *scratch_source;
418 ESourceBackend *extension;
419 ESourceConfigBackend *backend = NULL;
420 const gchar *backend_name;
421 const gchar *parent_uid;
422
423 parent_source = E_SOURCE (link->data);
424 parent_uid = e_source_get_uid (parent_source);
425
426 extension = e_source_get_extension (
427 parent_source, E_SOURCE_EXTENSION_COLLECTION);
428 backend_name = e_source_backend_get_backend_name (extension);
429
430 if (backend_name != NULL)
431 backend = g_hash_table_lookup (
432 config->priv->backends, backend_name);
433
434 if (backend == NULL)
435 continue;
436
437 /* Some backends disallow creating certain types of
438 * resources. For example, the Exchange Web Services
439 * backend disallows creating new memo lists. */
440 if (!e_source_config_backend_allow_creation (backend))
441 continue;
442
443 scratch_source = e_source_new (NULL, NULL, NULL);
444 g_return_if_fail (scratch_source != NULL);
445
446 e_source_set_parent (scratch_source, parent_uid);
447
448 g_tree_insert (
449 scratch_source_tree,
450 g_object_ref (scratch_source),
451 g_object_ref (backend));
452
453 g_object_unref (scratch_source);
454 }
455
456 g_list_free_full (list, (GDestroyNotify) g_object_unref);
457
458 /* XXX GTree doesn't get as much love as GHashTable.
459 * It's missing an equivalent to GHashTableIter. */
460 g_tree_foreach (
461 scratch_source_tree,
462 source_config_init_for_adding_source_foreach, config);
463
464 g_tree_unref (scratch_source_tree);
465
466 if (config->priv->preselect_type) {
467 Candidate *candidate;
468 GPtrArray *array;
469 gint index;
470
471 array = config->priv->candidates;
472
473 for (index = 0; index < array->len; index++) {
474 ESourceConfigBackendClass *backend_class;
475
476 candidate = g_ptr_array_index (array, index);
477 backend_class = E_SOURCE_CONFIG_BACKEND_GET_CLASS (candidate->backend);
478
479 if (backend_class && (
480 g_strcmp0 (config->priv->preselect_type, backend_class->backend_name) == 0 ||
481 g_strcmp0 (config->priv->preselect_type, backend_class->parent_uid) == 0)) {
482 gtk_combo_box_set_active (GTK_COMBO_BOX (config->priv->type_combo), index);
483 break;
484 }
485 }
486 }
487 }
488
489 static void
source_config_init_for_editing_source(ESourceConfig * config)490 source_config_init_for_editing_source (ESourceConfig *config)
491 {
492 ESource *original_source;
493 ESource *scratch_source;
494 ESourceBackend *extension;
495 ESourceConfigBackend *backend;
496 GDBusObject *dbus_object;
497 const gchar *backend_name;
498 const gchar *extension_name;
499
500 original_source = e_source_config_get_original_source (config);
501 g_return_if_fail (original_source != NULL);
502
503 extension_name = e_source_config_get_backend_extension_name (config);
504 extension = e_source_get_extension (original_source, extension_name);
505 backend_name = e_source_backend_get_backend_name (extension);
506 g_return_if_fail (backend_name != NULL);
507
508 /* Special-case Google books and calendars to use 'google' editor, instead
509 of the 'carddav'/'caldav' editor, even they use 'carddav'/'caldav' backend. */
510 if ((g_ascii_strcasecmp (backend_name, "caldav") == 0 ||
511 g_ascii_strcasecmp (backend_name, "carddav") == 0) &&
512 g_strcmp0 (e_source_get_parent (original_source), "google-stub") == 0)
513 backend_name = "google";
514
515 backend = g_hash_table_lookup (config->priv->backends, backend_name);
516 g_return_if_fail (backend != NULL);
517
518 dbus_object = e_source_ref_dbus_object (original_source);
519 g_return_if_fail (dbus_object != NULL);
520
521 scratch_source = e_source_new (dbus_object, NULL, NULL);
522 g_return_if_fail (scratch_source != NULL);
523
524 source_config_add_candidate (config, scratch_source, backend);
525
526 g_object_unref (scratch_source);
527 g_object_unref (dbus_object);
528 }
529
530 static void
source_config_set_original_source(ESourceConfig * config,ESource * original_source)531 source_config_set_original_source (ESourceConfig *config,
532 ESource *original_source)
533 {
534 g_return_if_fail (config->priv->original_source == NULL);
535
536 if (original_source != NULL)
537 g_object_ref (original_source);
538
539 config->priv->original_source = original_source;
540 }
541
542 static void
source_config_set_registry(ESourceConfig * config,ESourceRegistry * registry)543 source_config_set_registry (ESourceConfig *config,
544 ESourceRegistry *registry)
545 {
546 g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
547 g_return_if_fail (config->priv->registry == NULL);
548
549 config->priv->registry = g_object_ref (registry);
550 }
551
552 static void
source_config_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)553 source_config_set_property (GObject *object,
554 guint property_id,
555 const GValue *value,
556 GParamSpec *pspec)
557 {
558 switch (property_id) {
559 case PROP_ORIGINAL_SOURCE:
560 source_config_set_original_source (
561 E_SOURCE_CONFIG (object),
562 g_value_get_object (value));
563 return;
564
565 case PROP_REGISTRY:
566 source_config_set_registry (
567 E_SOURCE_CONFIG (object),
568 g_value_get_object (value));
569 return;
570 }
571
572 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
573 }
574
575 static void
source_config_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)576 source_config_get_property (GObject *object,
577 guint property_id,
578 GValue *value,
579 GParamSpec *pspec)
580 {
581 switch (property_id) {
582 case PROP_COLLECTION_SOURCE:
583 g_value_set_object (
584 value,
585 e_source_config_get_collection_source (
586 E_SOURCE_CONFIG (object)));
587 return;
588
589 case PROP_COMPLETE:
590 g_value_set_boolean (
591 value,
592 e_source_config_check_complete (
593 E_SOURCE_CONFIG (object)));
594 return;
595
596 case PROP_ORIGINAL_SOURCE:
597 g_value_set_object (
598 value,
599 e_source_config_get_original_source (
600 E_SOURCE_CONFIG (object)));
601 return;
602
603 case PROP_REGISTRY:
604 g_value_set_object (
605 value,
606 e_source_config_get_registry (
607 E_SOURCE_CONFIG (object)));
608 return;
609 }
610
611 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
612 }
613
614 static void
source_config_dispose(GObject * object)615 source_config_dispose (GObject *object)
616 {
617 ESourceConfigPrivate *priv;
618
619 priv = E_SOURCE_CONFIG_GET_PRIVATE (object);
620 g_clear_object (&priv->original_source);
621 g_clear_object (&priv->collection_source);
622 g_clear_object (&priv->registry);
623 g_clear_object (&priv->type_label);
624 g_clear_object (&priv->type_combo);
625 g_clear_object (&priv->name_label);
626 g_clear_object (&priv->name_entry);
627 g_clear_object (&priv->backend_box);
628 g_clear_object (&priv->size_group);
629
630 g_hash_table_remove_all (priv->backends);
631 g_ptr_array_set_size (priv->candidates, 0);
632
633 g_clear_pointer (&priv->preselect_type, g_free);
634
635 /* Chain up to parent's dispose() method. */
636 G_OBJECT_CLASS (e_source_config_parent_class)->dispose (object);
637 }
638
639 static void
source_config_finalize(GObject * object)640 source_config_finalize (GObject *object)
641 {
642 ESourceConfigPrivate *priv;
643
644 priv = E_SOURCE_CONFIG_GET_PRIVATE (object);
645
646 g_hash_table_destroy (priv->backends);
647 g_ptr_array_free (priv->candidates, TRUE);
648
649 /* Chain up to parent's finalize() method. */
650 G_OBJECT_CLASS (e_source_config_parent_class)->finalize (object);
651 }
652
653 static void
source_config_constructed(GObject * object)654 source_config_constructed (GObject *object)
655 {
656 ESourceConfig *config;
657 ESourceRegistry *registry;
658 ESource *original_source;
659 ESource *collection_source = NULL;
660 gboolean is_webdav_collection = FALSE, allow_sources_rename = FALSE;
661
662 /* Chain up to parent's method. */
663 G_OBJECT_CLASS (e_source_config_parent_class)->constructed (object);
664
665 config = E_SOURCE_CONFIG (object);
666 registry = e_source_config_get_registry (config);
667 original_source = e_source_config_get_original_source (config);
668
669 /* If we have an original source, see if it's part
670 * of a collection and note the collection source. */
671 if (original_source != NULL) {
672 const gchar *extension_name;
673
674 extension_name = E_SOURCE_EXTENSION_COLLECTION;
675 collection_source = e_source_registry_find_extension (
676 registry, original_source, extension_name);
677 config->priv->collection_source = collection_source;
678
679 if (collection_source) {
680 ESourceCollection *collection_extension;
681
682 collection_extension = e_source_get_extension (collection_source, extension_name);
683 is_webdav_collection = g_strcmp0 (e_source_backend_get_backend_name (E_SOURCE_BACKEND (collection_extension)), "webdav") == 0;
684 allow_sources_rename = e_source_collection_get_allow_sources_rename (collection_extension);
685 }
686 }
687
688 if (original_source != NULL)
689 e_source_config_insert_widget (
690 config, NULL, _("Type:"),
691 config->priv->type_label);
692 else
693 e_source_config_insert_widget (
694 config, NULL, _("Type:"),
695 config->priv->type_combo);
696
697 /* If the original source is part of a collection then we assume
698 * the display name is server-assigned and not user-assigned, at
699 * least not assigned through Evolution.
700 * Let the WebDAV collection change the display name, because it
701 * can synchronize it when the user didn't change it. */
702 if (collection_source != NULL && !is_webdav_collection && !allow_sources_rename)
703 e_source_config_insert_widget (
704 config, NULL, _("Name:"),
705 config->priv->name_label);
706 else
707 e_source_config_insert_widget (
708 config, NULL, _("Name:"),
709 config->priv->name_entry);
710
711 source_config_init_backends (config);
712 }
713
714 static void
source_config_realize(GtkWidget * widget)715 source_config_realize (GtkWidget *widget)
716 {
717 ESourceConfig *config;
718 ESource *original_source;
719
720 /* Chain up to parent's realize() method. */
721 GTK_WIDGET_CLASS (e_source_config_parent_class)->realize (widget);
722
723 /* Do this after constructed() so subclasses can fully
724 * initialize themselves before we add candidates. */
725
726 config = E_SOURCE_CONFIG (widget);
727 original_source = e_source_config_get_original_source (config);
728
729 if (original_source == NULL)
730 source_config_init_for_adding_source (config);
731 else
732 source_config_init_for_editing_source (config);
733
734 /* Connect this signal AFTER we're done adding candidates
735 * so we don't trigger check_complete() before candidates
736 * have a chance to insert widgets. */
737 g_signal_connect (
738 config->priv->type_combo, "changed",
739 G_CALLBACK (source_config_type_combo_changed_cb), config);
740
741 /* Trigger the callback to make sure the right page
742 * is selected, window is an appropriate size, etc. */
743 g_signal_emit_by_name (config->priv->type_combo, "changed");
744 }
745
746 static GList *
source_config_list_eligible_collections(ESourceConfig * config)747 source_config_list_eligible_collections (ESourceConfig *config)
748 {
749 ESourceRegistry *registry;
750 GQueue trash = G_QUEUE_INIT;
751 GList *list, *link;
752 const gchar *extension_name;
753
754 extension_name = E_SOURCE_EXTENSION_COLLECTION;
755 registry = e_source_config_get_registry (config);
756
757 list = e_source_registry_list_sources (registry, extension_name);
758
759 for (link = list; link != NULL; link = g_list_next (link)) {
760 ESource *source = E_SOURCE (link->data);
761 gboolean elligible;
762
763 elligible =
764 e_source_get_enabled (source) &&
765 e_source_get_remote_creatable (source);
766
767 if (!elligible)
768 g_queue_push_tail (&trash, link);
769 }
770
771 /* Remove ineligible collections from the list. */
772 while ((link = g_queue_pop_head (&trash)) != NULL) {
773 g_object_unref (link->data);
774 list = g_list_delete_link (list, link);
775 }
776
777 return list;
778 }
779
780 static void
source_config_init_candidate(ESourceConfig * config,ESource * scratch_source)781 source_config_init_candidate (ESourceConfig *config,
782 ESource *scratch_source)
783 {
784 e_binding_bind_object_text_property (
785 scratch_source, "display-name",
786 config->priv->name_label, "label",
787 G_BINDING_SYNC_CREATE);
788
789 e_binding_bind_object_text_property (
790 scratch_source, "display-name",
791 config->priv->name_entry, "text",
792 G_BINDING_BIDIRECTIONAL |
793 G_BINDING_SYNC_CREATE);
794 }
795
796 static gboolean
source_config_check_complete(ESourceConfig * config,ESource * scratch_source)797 source_config_check_complete (ESourceConfig *config,
798 ESource *scratch_source)
799 {
800 GtkEntry *name_entry;
801 GtkComboBox *type_combo;
802 const gchar *text;
803 gboolean correct;
804
805 /* Make sure the Type: combo box has a valid item. */
806 type_combo = GTK_COMBO_BOX (config->priv->type_combo);
807 if (gtk_combo_box_get_active (type_combo) < 0)
808 return FALSE;
809
810 /* Make sure the Name: entry field is not empty. */
811 name_entry = GTK_ENTRY (config->priv->name_entry);
812 text = gtk_entry_get_text (name_entry);
813 correct = text != NULL && *text != '\0';
814
815 e_util_set_entry_issue_hint (config->priv->name_entry, correct ? NULL : _("Name cannot be empty"));
816
817 return correct;
818 }
819
820 static void
source_config_commit_changes(ESourceConfig * config,ESource * scratch_source)821 source_config_commit_changes (ESourceConfig *config,
822 ESource *scratch_source)
823 {
824 /* Placeholder so subclasses can safely chain up. */
825 }
826
827 static void
source_config_resize_window(ESourceConfig * config)828 source_config_resize_window (ESourceConfig *config)
829 {
830 GtkWidget *toplevel;
831
832 /* Expand or shrink our parent window vertically to accommodate
833 * the newly selected backend's options. Some backends have tons
834 * of options, some have few. This avoids the case where you
835 * select a backend with tons of options and then a backend with
836 * few options and wind up with lots of unused vertical space. */
837
838 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (config));
839
840 if (GTK_IS_WINDOW (toplevel)) {
841 GtkWindow *window = GTK_WINDOW (toplevel);
842 GtkAllocation allocation;
843
844 gtk_widget_get_allocation (toplevel, &allocation);
845 gtk_window_resize (window, allocation.width, 1);
846 }
847 }
848
849 static gboolean
source_config_check_complete_accumulator(GSignalInvocationHint * ihint,GValue * return_accu,const GValue * handler_return,gpointer unused)850 source_config_check_complete_accumulator (GSignalInvocationHint *ihint,
851 GValue *return_accu,
852 const GValue *handler_return,
853 gpointer unused)
854 {
855 gboolean v_boolean;
856
857 /* Abort emission if a handler returns FALSE. */
858 v_boolean = g_value_get_boolean (handler_return);
859 g_value_set_boolean (return_accu, v_boolean);
860
861 return v_boolean;
862 }
863
864 static void
e_source_config_class_init(ESourceConfigClass * class)865 e_source_config_class_init (ESourceConfigClass *class)
866 {
867 GObjectClass *object_class;
868 GtkWidgetClass *widget_class;
869
870 g_type_class_add_private (class, sizeof (ESourceConfigPrivate));
871
872 object_class = G_OBJECT_CLASS (class);
873 object_class->set_property = source_config_set_property;
874 object_class->get_property = source_config_get_property;
875 object_class->dispose = source_config_dispose;
876 object_class->finalize = source_config_finalize;
877 object_class->constructed = source_config_constructed;
878
879 widget_class = GTK_WIDGET_CLASS (class);
880 widget_class->realize = source_config_realize;
881
882 class->list_eligible_collections =
883 source_config_list_eligible_collections;
884 class->init_candidate = source_config_init_candidate;
885 class->check_complete = source_config_check_complete;
886 class->commit_changes = source_config_commit_changes;
887 class->resize_window = source_config_resize_window;
888
889 g_object_class_install_property (
890 object_class,
891 PROP_COLLECTION_SOURCE,
892 g_param_spec_object (
893 "collection-source",
894 "Collection Source",
895 "The collection ESource to which "
896 "the ESource being edited belongs",
897 E_TYPE_SOURCE,
898 G_PARAM_READABLE |
899 G_PARAM_STATIC_STRINGS));
900
901 g_object_class_install_property (
902 object_class,
903 PROP_COMPLETE,
904 g_param_spec_boolean (
905 "complete",
906 "Complete",
907 "Are the required fields complete?",
908 FALSE,
909 G_PARAM_READABLE |
910 G_PARAM_STATIC_STRINGS));
911
912 g_object_class_install_property (
913 object_class,
914 PROP_ORIGINAL_SOURCE,
915 g_param_spec_object (
916 "original-source",
917 "Original Source",
918 "The original ESource",
919 E_TYPE_SOURCE,
920 G_PARAM_READWRITE |
921 G_PARAM_CONSTRUCT_ONLY |
922 G_PARAM_STATIC_STRINGS));
923
924 g_object_class_install_property (
925 object_class,
926 PROP_REGISTRY,
927 g_param_spec_object (
928 "registry",
929 "Registry",
930 "Registry of ESources",
931 E_TYPE_SOURCE_REGISTRY,
932 G_PARAM_READWRITE |
933 G_PARAM_CONSTRUCT_ONLY |
934 G_PARAM_STATIC_STRINGS));
935
936 signals[CHECK_COMPLETE] = g_signal_new (
937 "check-complete",
938 G_TYPE_FROM_CLASS (class),
939 G_SIGNAL_RUN_LAST,
940 G_STRUCT_OFFSET (ESourceConfigClass, check_complete),
941 source_config_check_complete_accumulator, NULL,
942 e_marshal_BOOLEAN__OBJECT,
943 G_TYPE_BOOLEAN, 1,
944 E_TYPE_SOURCE);
945
946 signals[COMMIT_CHANGES] = g_signal_new (
947 "commit-changes",
948 G_TYPE_FROM_CLASS (class),
949 G_SIGNAL_RUN_LAST,
950 G_STRUCT_OFFSET (ESourceConfigClass, commit_changes),
951 NULL, NULL,
952 g_cclosure_marshal_VOID__OBJECT,
953 G_TYPE_NONE, 1,
954 E_TYPE_SOURCE);
955
956 signals[INIT_CANDIDATE] = g_signal_new (
957 "init-candidate",
958 G_TYPE_FROM_CLASS (class),
959 G_SIGNAL_RUN_LAST,
960 G_STRUCT_OFFSET (ESourceConfigClass, init_candidate),
961 NULL, NULL,
962 g_cclosure_marshal_VOID__OBJECT,
963 G_TYPE_NONE, 1,
964 E_TYPE_SOURCE);
965
966 signals[RESIZE_WINDOW] = g_signal_new (
967 "resize-window",
968 G_TYPE_FROM_CLASS (class),
969 G_SIGNAL_RUN_LAST,
970 G_STRUCT_OFFSET (ESourceConfigClass, resize_window),
971 NULL, NULL,
972 g_cclosure_marshal_VOID__VOID,
973 G_TYPE_NONE, 0);
974 }
975
976 static void
e_source_config_init(ESourceConfig * config)977 e_source_config_init (ESourceConfig *config)
978 {
979 GPtrArray *candidates;
980 GtkSizeGroup *size_group;
981 PangoAttribute *attr;
982 PangoAttrList *attr_list;
983 GtkWidget *widget;
984
985 /* The candidates array holds scratch ESources, one for each
986 * item in the "type" combo box. Scratch ESources are never
987 * added to the registry, so backend extensions can make any
988 * changes they want to them. Whichever scratch ESource is
989 * "active" (selected in the "type" combo box) when the user
990 * clicks OK wins and is written to disk. The others are
991 * discarded. */
992 candidates = g_ptr_array_new_with_free_func (
993 (GDestroyNotify) source_config_free_candidate);
994
995 /* The size group is used for caption labels. */
996 size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
997
998 gtk_box_set_spacing (GTK_BOX (config), 6);
999
1000 gtk_orientable_set_orientation (
1001 GTK_ORIENTABLE (config), GTK_ORIENTATION_VERTICAL);
1002
1003 config->priv = E_SOURCE_CONFIG_GET_PRIVATE (config);
1004 config->priv->candidates = candidates;
1005 config->priv->size_group = size_group;
1006
1007 attr_list = pango_attr_list_new ();
1008
1009 attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
1010 pango_attr_list_insert (attr_list, attr);
1011
1012 /* Either the source type combo box or the label is shown,
1013 * never both. But we create both widgets and keep them
1014 * both up-to-date because it makes the logic simpler. */
1015
1016 widget = gtk_label_new (NULL);
1017 gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
1018 gtk_label_set_attributes (GTK_LABEL (widget), attr_list);
1019 config->priv->type_label = g_object_ref_sink (widget);
1020 gtk_widget_show (widget);
1021
1022 widget = gtk_combo_box_text_new ();
1023 config->priv->type_combo = g_object_ref_sink (widget);
1024 gtk_widget_show (widget);
1025
1026 /* Similarly for the display name. Either the text entry
1027 * or the label is shown, depending on whether the source
1028 * is a collection member (new sources never are). */
1029
1030 widget = gtk_label_new (NULL);
1031 gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
1032 gtk_label_set_attributes (GTK_LABEL (widget), attr_list);
1033 config->priv->name_label = g_object_ref_sink (widget);
1034 gtk_widget_show (widget);
1035
1036 widget = gtk_entry_new ();
1037 gtk_entry_set_activates_default (GTK_ENTRY (widget), TRUE);
1038 config->priv->name_entry = g_object_ref_sink (widget);
1039 gtk_widget_show (widget);
1040
1041 /* The backend box holds backend-specific options. Each backend
1042 * gets a child widget. Only one child widget is visible at once. */
1043 widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
1044 gtk_box_pack_end (GTK_BOX (config), widget, TRUE, TRUE, 0);
1045 config->priv->backend_box = g_object_ref (widget);
1046 gtk_widget_show (widget);
1047
1048 pango_attr_list_unref (attr_list);
1049 }
1050
1051 GtkWidget *
e_source_config_new(ESourceRegistry * registry,ESource * original_source)1052 e_source_config_new (ESourceRegistry *registry,
1053 ESource *original_source)
1054 {
1055 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
1056
1057 if (original_source != NULL)
1058 g_return_val_if_fail (E_IS_SOURCE (original_source), NULL);
1059
1060 return g_object_new (
1061 E_TYPE_SOURCE_CONFIG, "registry", registry,
1062 "original-source", original_source, NULL);
1063 }
1064
1065 void
e_source_config_set_preselect_type(ESourceConfig * config,const gchar * source_uid)1066 e_source_config_set_preselect_type (ESourceConfig *config,
1067 const gchar *source_uid)
1068 {
1069 g_return_if_fail (E_IS_SOURCE_CONFIG (config));
1070
1071 if (config->priv->preselect_type != source_uid) {
1072 g_free (config->priv->preselect_type);
1073 config->priv->preselect_type = g_strdup (source_uid);
1074 }
1075 }
1076
1077 const gchar *
e_source_config_get_preselect_type(ESourceConfig * config)1078 e_source_config_get_preselect_type (ESourceConfig *config)
1079 {
1080 g_return_val_if_fail (E_IS_SOURCE_CONFIG (config), NULL);
1081
1082 return config->priv->preselect_type;
1083 }
1084
1085 void
e_source_config_insert_widget(ESourceConfig * config,ESource * scratch_source,const gchar * caption,GtkWidget * widget)1086 e_source_config_insert_widget (ESourceConfig *config,
1087 ESource *scratch_source,
1088 const gchar *caption,
1089 GtkWidget *widget)
1090 {
1091 GtkWidget *hbox;
1092 GtkWidget *vbox;
1093 GtkWidget *label;
1094
1095 g_return_if_fail (E_IS_SOURCE_CONFIG (config));
1096 g_return_if_fail (GTK_IS_WIDGET (widget));
1097
1098 if (scratch_source == NULL)
1099 vbox = GTK_WIDGET (config);
1100 else
1101 vbox = e_source_config_get_page (config, scratch_source);
1102
1103 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
1104 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
1105
1106 e_binding_bind_property (
1107 widget, "visible",
1108 hbox, "visible",
1109 G_BINDING_SYNC_CREATE);
1110
1111 label = gtk_label_new (caption);
1112 gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
1113 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
1114 gtk_size_group_add_widget (config->priv->size_group, label);
1115 gtk_widget_show (label);
1116
1117 gtk_box_pack_start (GTK_BOX (hbox), widget, TRUE, TRUE, 0);
1118 }
1119
1120 GtkWidget *
e_source_config_get_page(ESourceConfig * config,ESource * scratch_source)1121 e_source_config_get_page (ESourceConfig *config,
1122 ESource *scratch_source)
1123 {
1124 Candidate *candidate;
1125 GtkWidget *page = NULL;
1126 GPtrArray *array;
1127 gint index;
1128
1129 g_return_val_if_fail (E_IS_SOURCE_CONFIG (config), NULL);
1130 g_return_val_if_fail (E_IS_SOURCE (scratch_source), NULL);
1131
1132 array = config->priv->candidates;
1133
1134 for (index = 0; page == NULL && index < array->len; index++) {
1135 candidate = g_ptr_array_index (array, index);
1136 if (e_source_equal (scratch_source, candidate->scratch_source))
1137 page = candidate->page;
1138 }
1139
1140 g_return_val_if_fail (GTK_IS_BOX (page), NULL);
1141
1142 return page;
1143 }
1144
1145 void
e_source_config_select_page(ESourceConfig * config,ESource * scratch_source)1146 e_source_config_select_page (ESourceConfig *config,
1147 ESource *scratch_source)
1148 {
1149 Candidate *candidate;
1150 GPtrArray *array;
1151 gint index;
1152
1153 g_return_if_fail (E_IS_SOURCE_CONFIG (config));
1154 g_return_if_fail (E_IS_SOURCE (scratch_source));
1155
1156 array = config->priv->candidates;
1157
1158 for (index = 0; index < array->len; index++) {
1159 candidate = g_ptr_array_index (array, index);
1160 if (e_source_equal (scratch_source, candidate->scratch_source)) {
1161 gtk_combo_box_set_active (GTK_COMBO_BOX (config->priv->type_combo), index);
1162 return;
1163 }
1164 }
1165
1166 g_warn_if_reached ();
1167 }
1168
1169 const gchar *
e_source_config_get_backend_extension_name(ESourceConfig * config)1170 e_source_config_get_backend_extension_name (ESourceConfig *config)
1171 {
1172 ESourceConfigClass *class;
1173
1174 g_return_val_if_fail (E_IS_SOURCE_CONFIG (config), NULL);
1175
1176 class = E_SOURCE_CONFIG_GET_CLASS (config);
1177 g_return_val_if_fail (class != NULL, NULL);
1178 g_return_val_if_fail (class->get_backend_extension_name != NULL, NULL);
1179
1180 return class->get_backend_extension_name (config);
1181 }
1182
1183 GList *
e_source_config_list_eligible_collections(ESourceConfig * config)1184 e_source_config_list_eligible_collections (ESourceConfig *config)
1185 {
1186 ESourceConfigClass *class;
1187
1188 g_return_val_if_fail (E_IS_SOURCE_CONFIG (config), NULL);
1189
1190 class = E_SOURCE_CONFIG_GET_CLASS (config);
1191 g_return_val_if_fail (class != NULL, NULL);
1192 g_return_val_if_fail (class->list_eligible_collections != NULL, NULL);
1193
1194 return class->list_eligible_collections (config);
1195 }
1196
1197 gboolean
e_source_config_check_complete(ESourceConfig * config)1198 e_source_config_check_complete (ESourceConfig *config)
1199 {
1200 Candidate *candidate;
1201 gboolean complete;
1202
1203 g_return_val_if_fail (E_IS_SOURCE_CONFIG (config), FALSE);
1204
1205 candidate = source_config_get_active_candidate (config);
1206 g_return_val_if_fail (candidate != NULL, FALSE);
1207
1208 g_signal_emit (
1209 config, signals[CHECK_COMPLETE], 0,
1210 candidate->scratch_source, &complete);
1211
1212 complete &= e_source_config_backend_check_complete (
1213 candidate->backend, candidate->scratch_source);
1214
1215 /* XXX Emitting "notify::complete" may cause this function
1216 * to be called repeatedly by signal handlers. The IF
1217 * check below should break any recursive cycles. Not
1218 * very efficient but I think we can live with it. */
1219
1220 if (complete != config->priv->complete) {
1221 config->priv->complete = complete;
1222 g_object_notify (G_OBJECT (config), "complete");
1223 }
1224
1225 return complete;
1226 }
1227
1228 ESource *
e_source_config_get_original_source(ESourceConfig * config)1229 e_source_config_get_original_source (ESourceConfig *config)
1230 {
1231 g_return_val_if_fail (E_IS_SOURCE_CONFIG (config), NULL);
1232
1233 return config->priv->original_source;
1234 }
1235
1236 ESource *
e_source_config_get_collection_source(ESourceConfig * config)1237 e_source_config_get_collection_source (ESourceConfig *config)
1238 {
1239 g_return_val_if_fail (E_IS_SOURCE_CONFIG (config), NULL);
1240
1241 return config->priv->collection_source;
1242 }
1243
1244 GSList * /* (transfer full) (element-type #ESource) */
e_source_config_list_candidates(ESourceConfig * config)1245 e_source_config_list_candidates (ESourceConfig *config)
1246 {
1247 GSList *candidates = NULL;
1248 GPtrArray *array;
1249 gint index;
1250
1251 g_return_val_if_fail (E_IS_SOURCE_CONFIG (config), NULL);
1252
1253 array = config->priv->candidates;
1254
1255 for (index = 0; index < array->len; index++) {
1256 Candidate *candidate;
1257
1258 candidate = g_ptr_array_index (array, index);
1259 candidates = g_slist_prepend (candidates, g_object_ref (candidate->scratch_source));
1260 }
1261
1262 return g_slist_reverse (candidates);
1263 }
1264
1265 ESourceRegistry *
e_source_config_get_registry(ESourceConfig * config)1266 e_source_config_get_registry (ESourceConfig *config)
1267 {
1268 g_return_val_if_fail (E_IS_SOURCE_CONFIG (config), NULL);
1269
1270 return config->priv->registry;
1271 }
1272
1273 void
e_source_config_resize_window(ESourceConfig * config)1274 e_source_config_resize_window (ESourceConfig *config)
1275 {
1276 g_return_if_fail (E_IS_SOURCE_CONFIG (config));
1277
1278 g_signal_emit (config, signals[RESIZE_WINDOW], 0);
1279 }
1280
1281 /* Helper for e_source_config_commit() */
1282 static void
source_config_commit_cb(GObject * object,GAsyncResult * result,gpointer user_data)1283 source_config_commit_cb (GObject *object,
1284 GAsyncResult *result,
1285 gpointer user_data)
1286 {
1287 GSimpleAsyncResult *simple;
1288 GError *error = NULL;
1289
1290 simple = G_SIMPLE_ASYNC_RESULT (user_data);
1291
1292 e_source_registry_commit_source_finish (
1293 E_SOURCE_REGISTRY (object), result, &error);
1294
1295 if (error != NULL)
1296 g_simple_async_result_take_error (simple, error);
1297
1298 g_simple_async_result_complete (simple);
1299 g_object_unref (simple);
1300 }
1301
1302 void
e_source_config_commit(ESourceConfig * config,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1303 e_source_config_commit (ESourceConfig *config,
1304 GCancellable *cancellable,
1305 GAsyncReadyCallback callback,
1306 gpointer user_data)
1307 {
1308 GSimpleAsyncResult *simple;
1309 ESourceRegistry *registry;
1310 Candidate *candidate;
1311
1312 g_return_if_fail (E_IS_SOURCE_CONFIG (config));
1313
1314 registry = e_source_config_get_registry (config);
1315
1316 candidate = source_config_get_active_candidate (config);
1317 g_return_if_fail (candidate != NULL);
1318
1319 e_source_config_backend_commit_changes (
1320 candidate->backend, candidate->scratch_source);
1321
1322 g_signal_emit (
1323 config, signals[COMMIT_CHANGES], 0,
1324 candidate->scratch_source);
1325
1326 simple = g_simple_async_result_new (
1327 G_OBJECT (config), callback,
1328 user_data, e_source_config_commit);
1329
1330 e_source_registry_commit_source (
1331 registry, candidate->scratch_source,
1332 cancellable, source_config_commit_cb, simple);
1333 }
1334
1335 gboolean
e_source_config_commit_finish(ESourceConfig * config,GAsyncResult * result,GError ** error)1336 e_source_config_commit_finish (ESourceConfig *config,
1337 GAsyncResult *result,
1338 GError **error)
1339 {
1340 GSimpleAsyncResult *simple;
1341
1342 g_return_val_if_fail (
1343 g_simple_async_result_is_valid (
1344 result, G_OBJECT (config),
1345 e_source_config_commit), FALSE);
1346
1347 simple = G_SIMPLE_ASYNC_RESULT (result);
1348
1349 /* Assume success unless a GError is set. */
1350 return !g_simple_async_result_propagate_error (simple, error);
1351 }
1352
1353 void
e_source_config_add_refresh_interval(ESourceConfig * config,ESource * scratch_source)1354 e_source_config_add_refresh_interval (ESourceConfig *config,
1355 ESource *scratch_source)
1356 {
1357 GtkWidget *widget;
1358 GtkWidget *container;
1359 ESourceExtension *extension;
1360 const gchar *extension_name;
1361
1362 g_return_if_fail (E_IS_SOURCE_CONFIG (config));
1363 g_return_if_fail (E_IS_SOURCE (scratch_source));
1364
1365 extension_name = E_SOURCE_EXTENSION_REFRESH;
1366 extension = e_source_get_extension (scratch_source, extension_name);
1367
1368 widget = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
1369 e_source_config_insert_widget (config, scratch_source, NULL, widget);
1370 gtk_widget_show (widget);
1371
1372 container = widget;
1373
1374 widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
1375 gtk_container_add (GTK_CONTAINER (container), widget);
1376 gtk_widget_show (widget);
1377
1378 container = widget;
1379
1380 /* Translators: This is the first of a sequence of widgets:
1381 * "Refresh every [NUMERIC_ENTRY] [TIME_UNITS_COMBO]" */
1382 widget = gtk_label_new (_("Refresh every"));
1383 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
1384 gtk_widget_show (widget);
1385
1386 widget = e_interval_chooser_new ();
1387 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
1388 gtk_widget_show (widget);
1389
1390 e_binding_bind_property (
1391 extension, "interval-minutes",
1392 widget, "interval-minutes",
1393 G_BINDING_BIDIRECTIONAL |
1394 G_BINDING_SYNC_CREATE);
1395 }
1396
1397 void
e_source_config_add_secure_connection(ESourceConfig * config,ESource * scratch_source)1398 e_source_config_add_secure_connection (ESourceConfig *config,
1399 ESource *scratch_source)
1400 {
1401 GtkWidget *widget;
1402 ESourceExtension *extension;
1403 const gchar *extension_name;
1404 const gchar *label;
1405
1406 g_return_if_fail (E_IS_SOURCE_CONFIG (config));
1407 g_return_if_fail (E_IS_SOURCE (scratch_source));
1408
1409 extension_name = E_SOURCE_EXTENSION_SECURITY;
1410 extension = e_source_get_extension (scratch_source, extension_name);
1411
1412 label = _("Use a secure connection");
1413 widget = gtk_check_button_new_with_label (label);
1414 e_source_config_insert_widget (config, scratch_source, NULL, widget);
1415 gtk_widget_show (widget);
1416
1417 e_binding_bind_property (
1418 extension, "secure",
1419 widget, "active",
1420 G_BINDING_BIDIRECTIONAL |
1421 G_BINDING_SYNC_CREATE);
1422 }
1423
1424 static gboolean
secure_to_port_cb(GBinding * binding,const GValue * source_value,GValue * target_value,gpointer user_data)1425 secure_to_port_cb (GBinding *binding,
1426 const GValue *source_value,
1427 GValue *target_value,
1428 gpointer user_data)
1429 {
1430 GObject *authentication_extension;
1431 guint16 port;
1432
1433 authentication_extension = g_binding_get_target (binding);
1434
1435 port = e_source_authentication_get_port (
1436 E_SOURCE_AUTHENTICATION (authentication_extension));
1437
1438 if (port == 80 || port == 443 || port == 0)
1439 port = g_value_get_boolean (source_value) ? 443 : 80;
1440
1441 g_value_set_uint (target_value, port);
1442
1443 return TRUE;
1444 }
1445
1446 static gboolean
webdav_source_ssl_trust_to_sensitive_cb(GBinding * binding,const GValue * source_value,GValue * target_value,gpointer user_data)1447 webdav_source_ssl_trust_to_sensitive_cb (GBinding *binding,
1448 const GValue *source_value,
1449 GValue *target_value,
1450 gpointer user_data)
1451 {
1452 const gchar *ssl_trust = g_value_get_string (source_value);
1453
1454 g_value_set_boolean (target_value, ssl_trust && *ssl_trust);
1455
1456 return TRUE;
1457 }
1458
1459 static void
webdav_unset_ssl_trust_clicked_cb(GtkWidget * button,ESourceWebdav * extension)1460 webdav_unset_ssl_trust_clicked_cb (GtkWidget *button,
1461 ESourceWebdav *extension)
1462 {
1463 e_source_webdav_set_ssl_trust (extension, NULL);
1464 }
1465
1466 void
e_source_config_add_secure_connection_for_webdav(ESourceConfig * config,ESource * scratch_source)1467 e_source_config_add_secure_connection_for_webdav (ESourceConfig *config,
1468 ESource *scratch_source)
1469 {
1470 GtkWidget *widget;
1471 ESourceExtension *extension;
1472 ESourceAuthentication *authentication_extension;
1473 ESource *collection_source;
1474 const gchar *extension_name;
1475 const gchar *label;
1476
1477 g_return_if_fail (E_IS_SOURCE_CONFIG (config));
1478 g_return_if_fail (E_IS_SOURCE (scratch_source));
1479
1480 collection_source = e_source_config_get_collection_source (config);
1481
1482 if (!collection_source) {
1483 extension_name = E_SOURCE_EXTENSION_SECURITY;
1484 extension = e_source_get_extension (scratch_source, extension_name);
1485
1486 label = _("Use a secure connection");
1487 widget = gtk_check_button_new_with_label (label);
1488 e_source_config_insert_widget (config, scratch_source, NULL, widget);
1489 gtk_widget_show (widget);
1490
1491 e_binding_bind_property (
1492 extension, "secure",
1493 widget, "active",
1494 G_BINDING_BIDIRECTIONAL |
1495 G_BINDING_SYNC_CREATE);
1496
1497 extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
1498 authentication_extension =
1499 e_source_get_extension (scratch_source, extension_name);
1500
1501 e_binding_bind_property_full (
1502 extension, "secure",
1503 authentication_extension, "port",
1504 G_BINDING_DEFAULT,
1505 secure_to_port_cb,
1506 NULL, NULL, NULL);
1507 }
1508
1509 extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
1510 extension = e_source_get_extension (scratch_source, extension_name);
1511
1512 widget = gtk_button_new_with_mnemonic (
1513 _("Unset _trust for SSL/TLS certificate"));
1514 gtk_widget_set_margin_left (widget, collection_source ? 0 : 24);
1515 e_source_config_insert_widget (config, scratch_source, NULL, widget);
1516 gtk_widget_show (widget);
1517
1518 e_binding_bind_property_full (
1519 extension, "ssl-trust",
1520 widget, "sensitive",
1521 G_BINDING_SYNC_CREATE,
1522 webdav_source_ssl_trust_to_sensitive_cb,
1523 NULL, NULL, NULL);
1524
1525 g_signal_connect (
1526 widget, "clicked",
1527 G_CALLBACK (webdav_unset_ssl_trust_clicked_cb), extension);
1528 }
1529
1530 GtkWidget *
e_source_config_add_user_entry(ESourceConfig * config,ESource * scratch_source)1531 e_source_config_add_user_entry (ESourceConfig *config,
1532 ESource *scratch_source)
1533 {
1534 GtkWidget *widget;
1535 ESource *original_source;
1536 ESourceExtension *extension;
1537 const gchar *extension_name;
1538
1539 g_return_val_if_fail (E_IS_SOURCE_CONFIG (config), NULL);
1540 g_return_val_if_fail (E_IS_SOURCE (scratch_source), NULL);
1541
1542 extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
1543 extension = e_source_get_extension (scratch_source, extension_name);
1544
1545 original_source = e_source_config_get_original_source (config);
1546
1547 widget = gtk_entry_new ();
1548 e_source_config_insert_widget (
1549 config, scratch_source, _("User:"), widget);
1550 gtk_widget_show (widget);
1551
1552 e_binding_bind_object_text_property (
1553 extension, "user",
1554 widget, "text",
1555 G_BINDING_BIDIRECTIONAL |
1556 G_BINDING_SYNC_CREATE);
1557
1558 /* If this is a new data source, initialize the
1559 * GtkEntry to the user name of the current user. */
1560 if (original_source == NULL)
1561 gtk_entry_set_text (GTK_ENTRY (widget), g_get_user_name ());
1562
1563 return widget;
1564 }
1565
1566