1 /*
2  * e-mail-config-service-page.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 <camel/camel.h>
23 #include <libebackend/libebackend.h>
24 
25 #include "e-mail-config-page.h"
26 #include "e-mail-config-service-notebook.h"
27 
28 #include "e-mail-config-service-page.h"
29 
30 #define E_MAIL_CONFIG_SERVICE_PAGE_GET_PRIVATE(obj) \
31 	(G_TYPE_INSTANCE_GET_PRIVATE \
32 	((obj), E_TYPE_MAIL_CONFIG_SERVICE_PAGE, EMailConfigServicePagePrivate))
33 
34 typedef struct _Candidate Candidate;
35 
36 struct _EMailConfigServicePagePrivate {
37 	ESourceRegistry *registry;
38 	EMailConfigServiceBackend *active_backend;
39 	gchar *email_address;
40 
41 	GHashTable *backends;
42 	GPtrArray *candidates;
43 
44 	/* Hidden candidates are not listed in the
45 	 * combo box but can still be accessed through
46 	 * e_mail_config_service_page_lookup_backend(). */
47 	GPtrArray *hidden_candidates;
48 
49 	GtkWidget *type_combo;
50 	GtkWidget *type_label;
51 	GtkWidget *desc_label;
52 	GtkWidget *notebook;
53 
54 	/* Combo box list store */
55 	GtkListStore *list_store;
56 };
57 
58 struct _Candidate {
59 	gchar *name;
60 	EMailConfigServiceBackend *backend;
61 
62 	CamelProvider *provider;
63 	CamelSettings *settings;
64 	gulong settings_notify_handler_id;
65 
66 	GtkWidget *widget;
67 };
68 
69 enum {
70 	PROP_0,
71 	PROP_ACTIVE_BACKEND,
72 	PROP_EMAIL_ADDRESS,
73 	PROP_REGISTRY
74 };
75 
76 enum {
77 	COLUMN_BACKEND_NAME,
78 	COLUMN_DISPLAY_NAME,
79 	COLUMN_SELECTABLE,
80 	NUM_COLUMNS
81 };
82 
83 /* Forward Declarations */
84 static void	e_mail_config_service_page_interface_init
85 					(EMailConfigPageInterface *iface);
86 
G_DEFINE_ABSTRACT_TYPE_WITH_CODE(EMailConfigServicePage,e_mail_config_service_page,E_TYPE_MAIL_CONFIG_ACTIVITY_PAGE,G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE,NULL)G_IMPLEMENT_INTERFACE (E_TYPE_MAIL_CONFIG_PAGE,e_mail_config_service_page_interface_init))87 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (
88 	EMailConfigServicePage,
89 	e_mail_config_service_page,
90 	E_TYPE_MAIL_CONFIG_ACTIVITY_PAGE,
91 	G_IMPLEMENT_INTERFACE (
92 		E_TYPE_EXTENSIBLE, NULL)
93 	G_IMPLEMENT_INTERFACE (
94 		E_TYPE_MAIL_CONFIG_PAGE,
95 		e_mail_config_service_page_interface_init))
96 
97 static void
98 mail_config_service_page_settings_notify_cb (CamelSettings *settings,
99                                              GParamSpec *pspec,
100                                              EMailConfigPage *page)
101 {
102 	e_mail_config_page_changed (page);
103 }
104 
105 static Candidate *
mail_config_service_page_new_candidate(EMailConfigServicePage * page,ESource * scratch_source,ESource * opt_collection)106 mail_config_service_page_new_candidate (EMailConfigServicePage *page,
107                                         ESource *scratch_source,
108                                         ESource *opt_collection)
109 {
110 	Candidate *candidate;
111 	CamelProvider *provider;
112 	CamelSettings *settings;
113 	ESourceBackend *extension;
114 	EMailConfigServiceBackend *backend;
115 	EMailConfigServicePageClass *class;
116 	const gchar *extension_name;
117 	const gchar *backend_name;
118 	gulong handler_id;
119 
120 	/* Get the backend name for this scratch source. */
121 	class = E_MAIL_CONFIG_SERVICE_PAGE_GET_CLASS (page);
122 	g_return_val_if_fail (class != NULL, NULL);
123 
124 	extension_name = class->extension_name;
125 	extension = e_source_get_extension (scratch_source, extension_name);
126 	backend_name = e_source_backend_get_backend_name (extension);
127 	g_return_val_if_fail (backend_name != NULL, NULL);
128 
129 	/* Make sure we have a corresponding EMailConfigServicePageBackend. */
130 	backend = g_hash_table_lookup (page->priv->backends, backend_name);
131 	g_return_val_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend), NULL);
132 
133 	/* Make sure we have a corresponding CamelProvider. */
134 	provider = e_mail_config_service_backend_get_provider (backend);
135 	g_return_val_if_fail (provider != NULL, NULL);
136 
137 	/* Need to give the backend a scratch source and (if provided) a
138 	 * scratch collection source before we can extract a CamelSettings
139 	 * instance, since the CamelSettings instance comes from either the
140 	 * scratch collection source or else the scratch source. */
141 	e_mail_config_service_backend_set_source (backend, scratch_source);
142 	if (opt_collection != NULL)
143 		e_mail_config_service_backend_set_collection (
144 			backend, opt_collection);
145 
146 	/* Backend may have created its own collection source,
147 	 * so we need to get it from the backend before binding. */
148 	opt_collection = e_mail_config_service_backend_get_collection (backend);
149 
150 	/* Keep display names synchronized. */
151 	if (opt_collection != NULL)
152 		e_binding_bind_property (
153 			scratch_source, "display-name",
154 			opt_collection, "display-name",
155 			G_BINDING_BIDIRECTIONAL |
156 			G_BINDING_SYNC_CREATE);
157 
158 	/* Make sure we have a corresponding CamelSettings. */
159 	settings = e_mail_config_service_backend_get_settings (backend);
160 	g_return_val_if_fail (CAMEL_IS_SETTINGS (settings), NULL);
161 
162 	candidate = g_slice_new0 (Candidate);
163 	candidate->name = g_strdup (backend_name);
164 	candidate->backend = g_object_ref (backend);
165 	candidate->provider = provider;
166 	candidate->settings = g_object_ref (settings);
167 
168 	/* Remove the backend so it can't be reused.  If another scratch
169 	 * source with the same backend name gets added, the hash table
170 	 * lookup will fail and emit a runtime warning, which we want. */
171 	g_hash_table_remove (page->priv->backends, backend_name);
172 
173 	/* Emit "changed" signals for subsequent CamelSettings changes. */
174 	handler_id = g_signal_connect (
175 		candidate->settings, "notify",
176 		G_CALLBACK (mail_config_service_page_settings_notify_cb), page);
177 	candidate->settings_notify_handler_id = handler_id;
178 
179 	return candidate;
180 }
181 
182 static void
mail_config_service_page_free_candidate(Candidate * candidate)183 mail_config_service_page_free_candidate (Candidate *candidate)
184 {
185 	g_free (candidate->name);
186 
187 	if (candidate->backend != NULL)
188 		g_object_unref (candidate->backend);
189 
190 	if (candidate->settings != NULL) {
191 		g_signal_handler_disconnect (
192 			candidate->settings,
193 			candidate->settings_notify_handler_id);
194 		g_object_unref (candidate->settings);
195 	}
196 
197 	if (candidate->widget != NULL)
198 		g_object_unref (candidate->widget);
199 
200 	g_slice_free (Candidate, candidate);
201 }
202 
203 static void
mail_config_service_page_init_backends(EMailConfigServicePage * page)204 mail_config_service_page_init_backends (EMailConfigServicePage *page)
205 {
206 	GList *list, *iter;
207 
208 	page->priv->backends = g_hash_table_new_full (
209 		(GHashFunc) g_str_hash,
210 		(GEqualFunc) g_str_equal,
211 		(GDestroyNotify) g_free,
212 		(GDestroyNotify) g_object_unref);
213 
214 	e_extensible_load_extensions (E_EXTENSIBLE (page));
215 
216 	list = e_extensible_list_extensions (
217 		E_EXTENSIBLE (page), E_TYPE_MAIL_CONFIG_SERVICE_BACKEND);
218 
219 	for (iter = list; iter != NULL; iter = g_list_next (iter)) {
220 		EMailConfigServiceBackend *backend;
221 		EMailConfigServiceBackendClass *class;
222 
223 		backend = E_MAIL_CONFIG_SERVICE_BACKEND (iter->data);
224 		class = E_MAIL_CONFIG_SERVICE_BACKEND_GET_CLASS (backend);
225 
226 		if (class->backend_name != NULL)
227 			g_hash_table_insert (
228 				page->priv->backends,
229 				g_strdup (class->backend_name),
230 				g_object_ref (backend));
231 	}
232 
233 	g_list_free (list);
234 }
235 
236 static gboolean
mail_config_service_page_backend_to_id(GBinding * binding,const GValue * source_value,GValue * target_value,gpointer user_data)237 mail_config_service_page_backend_to_id (GBinding *binding,
238                                         const GValue *source_value,
239                                         GValue *target_value,
240                                         gpointer user_data)
241 {
242 	EMailConfigServiceBackend *backend;
243 	EMailConfigServiceBackendClass *backend_class;
244 
245 	backend = g_value_get_object (source_value);
246 	g_return_val_if_fail (backend != NULL, FALSE);
247 
248 	backend_class = E_MAIL_CONFIG_SERVICE_BACKEND_GET_CLASS (backend);
249 	g_value_set_string (target_value, backend_class->backend_name);
250 
251 	return TRUE;
252 }
253 
254 static gboolean
mail_config_service_page_id_to_backend(GBinding * binding,const GValue * source_value,GValue * target_value,gpointer user_data)255 mail_config_service_page_id_to_backend (GBinding *binding,
256                                         const GValue *source_value,
257                                         GValue *target_value,
258                                         gpointer user_data)
259 {
260 	EMailConfigServiceBackend *backend = NULL;
261 	GObject *source_object;
262 	const gchar *backend_name;
263 
264 	source_object = g_binding_get_source (binding);
265 	backend_name = g_value_get_string (source_value);
266 
267 	if (backend_name != NULL)
268 		backend = e_mail_config_service_page_lookup_backend (
269 			E_MAIL_CONFIG_SERVICE_PAGE (source_object),
270 			backend_name);
271 
272 	g_value_set_object (target_value, backend);
273 
274 	return TRUE;
275 }
276 
277 static gboolean
mail_config_service_page_backend_name_to_description(GBinding * binding,const GValue * source_value,GValue * target_value,gpointer user_data)278 mail_config_service_page_backend_name_to_description (GBinding *binding,
279                                                       const GValue *source_value,
280                                                       GValue *target_value,
281                                                       gpointer user_data)
282 {
283 	CamelProvider *provider;
284 	const gchar *description;
285 	const gchar *backend_name;
286 
287 	backend_name = g_value_get_string (source_value);
288 
289 	/* XXX Silly special case. */
290 	if (backend_name == NULL)
291 		backend_name = "none";
292 
293 	provider = camel_provider_get (backend_name, NULL);
294 
295 	if (provider != NULL && provider->description != NULL)
296 		description = g_dgettext (
297 			provider->translation_domain,
298 			provider->description);
299 	else
300 		description = "";
301 
302 	g_value_set_string (target_value, description);
303 
304 	return TRUE;
305 }
306 
307 static void
mail_config_service_page_set_registry(EMailConfigServicePage * page,ESourceRegistry * registry)308 mail_config_service_page_set_registry (EMailConfigServicePage *page,
309                                        ESourceRegistry *registry)
310 {
311 	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
312 	g_return_if_fail (page->priv->registry == NULL);
313 
314 	page->priv->registry = g_object_ref (registry);
315 }
316 
317 static void
mail_config_service_page_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)318 mail_config_service_page_set_property (GObject *object,
319                                        guint property_id,
320                                        const GValue *value,
321                                        GParamSpec *pspec)
322 {
323 	switch (property_id) {
324 		case PROP_ACTIVE_BACKEND:
325 			e_mail_config_service_page_set_active_backend (
326 				E_MAIL_CONFIG_SERVICE_PAGE (object),
327 				g_value_get_object (value));
328 			return;
329 
330 		case PROP_EMAIL_ADDRESS:
331 			e_mail_config_service_page_set_email_address (
332 				E_MAIL_CONFIG_SERVICE_PAGE (object),
333 				g_value_get_string (value));
334 			return;
335 
336 		case PROP_REGISTRY:
337 			mail_config_service_page_set_registry (
338 				E_MAIL_CONFIG_SERVICE_PAGE (object),
339 				g_value_get_object (value));
340 			return;
341 	}
342 
343 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
344 }
345 
346 static void
mail_config_service_page_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)347 mail_config_service_page_get_property (GObject *object,
348                                        guint property_id,
349                                        GValue *value,
350                                        GParamSpec *pspec)
351 {
352 	switch (property_id) {
353 		case PROP_ACTIVE_BACKEND:
354 			g_value_set_object (
355 				value,
356 				e_mail_config_service_page_get_active_backend (
357 				E_MAIL_CONFIG_SERVICE_PAGE (object)));
358 			return;
359 
360 		case PROP_EMAIL_ADDRESS:
361 			g_value_set_string (
362 				value,
363 				e_mail_config_service_page_get_email_address (
364 				E_MAIL_CONFIG_SERVICE_PAGE (object)));
365 			return;
366 
367 		case PROP_REGISTRY:
368 			g_value_set_object (
369 				value,
370 				e_mail_config_service_page_get_registry (
371 				E_MAIL_CONFIG_SERVICE_PAGE (object)));
372 			return;
373 	}
374 
375 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
376 }
377 
378 static void
mail_config_service_page_dispose(GObject * object)379 mail_config_service_page_dispose (GObject *object)
380 {
381 	EMailConfigServicePagePrivate *priv;
382 
383 	priv = E_MAIL_CONFIG_SERVICE_PAGE_GET_PRIVATE (object);
384 	g_clear_object (&priv->registry);
385 	g_clear_object (&priv->active_backend);
386 
387 	g_hash_table_remove_all (priv->backends);
388 	g_ptr_array_set_size (priv->candidates, 0);
389 	g_ptr_array_set_size (priv->hidden_candidates, 0);
390 
391 	g_clear_object (&priv->list_store);
392 
393 	/* Chain up to parent's dispose() method. */
394 	G_OBJECT_CLASS (e_mail_config_service_page_parent_class)->dispose (object);
395 }
396 
397 static void
mail_config_service_page_finalize(GObject * object)398 mail_config_service_page_finalize (GObject *object)
399 {
400 	EMailConfigServicePagePrivate *priv;
401 
402 	priv = E_MAIL_CONFIG_SERVICE_PAGE_GET_PRIVATE (object);
403 
404 	g_free (priv->email_address);
405 	g_hash_table_destroy (priv->backends);
406 	g_ptr_array_free (priv->candidates, TRUE);
407 	g_ptr_array_free (priv->hidden_candidates, TRUE);
408 
409 	/* Chain up to parent's finalize() method. */
410 	G_OBJECT_CLASS (e_mail_config_service_page_parent_class)->finalize (object);
411 }
412 
413 static void
mail_config_service_page_constructed(GObject * object)414 mail_config_service_page_constructed (GObject *object)
415 {
416 	EMailConfigServicePage *page;
417 	GPtrArray *candidates;
418 	GPtrArray *hidden_candidates;
419 	PangoAttribute *attr;
420 	PangoAttrList *attr_list;
421 	GtkLabel *label;
422 	GtkWidget *widget;
423 	GtkWidget *container;
424 	GtkWidget *main_box;
425 	GtkTreeModel *tree_model;
426 	GtkCellRenderer *renderer;
427 
428 	page = E_MAIL_CONFIG_SERVICE_PAGE (object);
429 
430 	/* Chain up to parent's constructed() method. */
431 	G_OBJECT_CLASS (e_mail_config_service_page_parent_class)->constructed (object);
432 
433 	/* The candidates array holds scratch ESources, one for each
434 	 * item in the "type" combo box.  Scratch ESources are never
435 	 * added to the registry, so backend extensions can make any
436 	 * changes they want to them.  Whichever scratch ESource is
437 	 * "active" (selected in the "type" combo box) when the user
438 	 * clicks OK wins and is written to disk.  The others are
439 	 * discarded. */
440 	candidates = g_ptr_array_new_with_free_func (
441 		(GDestroyNotify) mail_config_service_page_free_candidate);
442 
443 	/* Hidden candidates are not listed in the "type" combo box
444 	 * but their scratch ESource can still be "active".  This is
445 	 * a hack to accommodate groupware backends.  Usually when a
446 	 * hidden candidate is active the service page will not be
447 	 * visible anyway. */
448 	hidden_candidates = g_ptr_array_new_with_free_func (
449 		(GDestroyNotify) mail_config_service_page_free_candidate);
450 
451 	main_box = e_mail_config_activity_page_get_internal_box (E_MAIL_CONFIG_ACTIVITY_PAGE (page));
452 	gtk_box_set_spacing (GTK_BOX (main_box), 12);
453 
454 	page->priv->candidates = candidates;
455 	page->priv->hidden_candidates = hidden_candidates;
456 
457 	/* Build a filtered model for the combo box, where row
458 	 * visibility is determined by the "selectable" column. */
459 
460 	page->priv->list_store = gtk_list_store_new (
461 		NUM_COLUMNS,
462 		G_TYPE_STRING,		/* COLUMN_BACKEND_NAME */
463 		G_TYPE_STRING,		/* COLUMN_DISPLAY_NAME */
464 		G_TYPE_BOOLEAN);	/* COLUMN_SELECTABLE */
465 
466 	tree_model = gtk_tree_model_filter_new (
467 		GTK_TREE_MODEL (page->priv->list_store), NULL);
468 
469 	gtk_tree_model_filter_set_visible_column (
470 		GTK_TREE_MODEL_FILTER (tree_model), COLUMN_SELECTABLE);
471 
472 	/* Either the combo box or the label is shown, never both.
473 	 * But we create both widgets and keep them both up-to-date
474 	 * regardless just because it makes the logic simpler. */
475 
476 	container = GTK_WIDGET (main_box);
477 
478 	widget = gtk_grid_new ();
479 	gtk_grid_set_row_spacing (GTK_GRID (widget), 12);
480 	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
481 	gtk_widget_show (widget);
482 
483 	container = widget;
484 
485 	attr_list = pango_attr_list_new ();
486 
487 	attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
488 	pango_attr_list_insert (attr_list, attr);
489 
490 	widget = gtk_label_new_with_mnemonic (_("Server _Type:"));
491 	gtk_widget_set_margin_right (widget, 12);
492 	gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
493 	gtk_grid_attach (GTK_GRID (container), widget, 0, 0, 1, 1);
494 	gtk_widget_show (widget);
495 
496 	label = GTK_LABEL (widget);
497 
498 	widget = gtk_combo_box_new_with_model (tree_model);
499 	gtk_widget_set_hexpand (widget, TRUE);
500 	gtk_label_set_mnemonic_widget (label, widget);
501 	gtk_combo_box_set_id_column (
502 		GTK_COMBO_BOX (widget), COLUMN_BACKEND_NAME);
503 	gtk_grid_attach (GTK_GRID (container), widget, 1, 0, 1, 1);
504 	page->priv->type_combo = widget;  /* not referenced */
505 	gtk_widget_show (widget);
506 
507 	renderer = gtk_cell_renderer_text_new ();
508 	gtk_cell_layout_pack_start (
509 		GTK_CELL_LAYOUT (widget), renderer, TRUE);
510 	gtk_cell_layout_add_attribute (
511 		GTK_CELL_LAYOUT (widget), renderer,
512 		"text", COLUMN_DISPLAY_NAME);
513 
514 	widget = gtk_label_new (NULL);
515 	gtk_widget_set_hexpand (widget, TRUE);
516 	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
517 	gtk_label_set_attributes (GTK_LABEL (widget), attr_list);
518 	gtk_grid_attach (GTK_GRID (container), widget, 2, 0, 1, 1);
519 	page->priv->type_label = widget;  /* not referenced */
520 	gtk_widget_show (widget);
521 
522 	widget = gtk_label_new (_("Description:"));
523 	gtk_widget_set_margin_right (widget, 12);
524 	gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.0);
525 	gtk_grid_attach (GTK_GRID (container), widget, 0, 1, 1, 1);
526 	gtk_widget_show (widget);
527 
528 	widget = gtk_label_new (NULL);
529 	gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
530 	gtk_label_set_width_chars (GTK_LABEL (widget), 20);
531 	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
532 	gtk_grid_attach (GTK_GRID (container), widget, 1, 1, 2, 1);
533 	page->priv->desc_label = widget;  /* not referenced */
534 	gtk_widget_show (widget);
535 
536 	pango_attr_list_unref (attr_list);
537 
538 	container = GTK_WIDGET (main_box);
539 
540 	widget = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
541 	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
542 	gtk_widget_show (widget);
543 
544 	widget = e_mail_config_service_notebook_new ();
545 	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
546 	page->priv->notebook = widget;  /* not referenced */
547 	gtk_widget_show (widget);
548 
549 	/* Keep the notebook's active page number synchronized with our
550 	 * own "active-backend" property.  Avoid G_BINDING_SYNC_CREATE
551 	 * since we haven't added any notebook pages. */
552 	e_binding_bind_property (
553 		page, "active-backend",
554 		page->priv->notebook, "active-backend",
555 		G_BINDING_BIDIRECTIONAL);
556 
557 	/* Keep the combo box's active row number synchronized with our
558 	 * own "active-backend" property.  Avoid G_BINDING_SYNC_CREATE
559 	 * since we haven't added any combo box rows. */
560 	e_binding_bind_property_full (
561 		page, "active-backend",
562 		page->priv->type_combo, "active-id",
563 		G_BINDING_BIDIRECTIONAL,
564 		mail_config_service_page_backend_to_id,
565 		mail_config_service_page_id_to_backend,
566 		NULL, (GDestroyNotify) NULL);
567 
568 	/* This keeps the description field up-to-date. */
569 	e_binding_bind_property_full (
570 		page->priv->type_combo, "active-id",
571 		page->priv->desc_label, "label",
572 		G_BINDING_DEFAULT,
573 		mail_config_service_page_backend_name_to_description,
574 		NULL,
575 		NULL, (GDestroyNotify) NULL);
576 
577 	/* For the "Server Type", either the combo
578 	 * box or the label is visible, never both. */
579 	e_binding_bind_property (
580 		page->priv->type_combo, "visible",
581 		page->priv->type_label, "visible",
582 		G_BINDING_SYNC_CREATE |
583 		G_BINDING_BIDIRECTIONAL |
584 		G_BINDING_INVERT_BOOLEAN);
585 
586 	g_signal_connect_swapped (
587 		page->priv->type_combo, "changed",
588 		G_CALLBACK (e_mail_config_page_changed), page);
589 
590 	g_object_unref (tree_model);
591 
592 	e_mail_config_page_set_content (E_MAIL_CONFIG_PAGE (page), main_box);
593 
594 	mail_config_service_page_init_backends (page);
595 }
596 
597 static void
mail_config_service_page_setup_defaults(EMailConfigPage * page)598 mail_config_service_page_setup_defaults (EMailConfigPage *page)
599 {
600 	EMailConfigServicePageClass *class;
601 	EMailConfigServicePagePrivate *priv;
602 	guint ii;
603 
604 	class = E_MAIL_CONFIG_SERVICE_PAGE_GET_CLASS (page);
605 	g_return_if_fail (class != NULL);
606 
607 	priv = E_MAIL_CONFIG_SERVICE_PAGE_GET_PRIVATE (page);
608 
609 	for (ii = 0; ii < priv->candidates->len; ii++) {
610 		Candidate *candidate;
611 
612 		candidate = priv->candidates->pdata[ii];
613 		g_return_if_fail (candidate != NULL);
614 
615 		e_mail_config_service_backend_setup_defaults (
616 			candidate->backend);
617 	}
618 
619 	/* XXX Not sure if we need to call setup_defaults() for
620 	 *     hidden candidates.  Hold off until a need arises. */
621 
622 	if (class->default_backend_name != NULL)
623 		gtk_combo_box_set_active_id (
624 			GTK_COMBO_BOX (priv->type_combo),
625 			class->default_backend_name);
626 }
627 
628 static gboolean
mail_config_service_page_check_complete(EMailConfigPage * page)629 mail_config_service_page_check_complete (EMailConfigPage *page)
630 {
631 	EMailConfigServicePagePrivate *priv;
632 	EMailConfigServiceBackend *backend;
633 	GtkComboBox *type_combo;
634 	const gchar *backend_name;
635 
636 	priv = E_MAIL_CONFIG_SERVICE_PAGE_GET_PRIVATE (page);
637 
638 	type_combo = GTK_COMBO_BOX (priv->type_combo);
639 	backend_name = gtk_combo_box_get_active_id (type_combo);
640 
641 	if (backend_name == NULL)
642 		return FALSE;
643 
644 	backend = e_mail_config_service_page_lookup_backend (
645 		E_MAIL_CONFIG_SERVICE_PAGE (page), backend_name);
646 
647 	return e_mail_config_service_backend_check_complete (backend);
648 }
649 
650 static void
mail_config_service_page_commit_changes(EMailConfigPage * page,GQueue * source_queue)651 mail_config_service_page_commit_changes (EMailConfigPage *page,
652                                          GQueue *source_queue)
653 {
654 	EMailConfigServicePagePrivate *priv;
655 	EMailConfigServiceBackend *backend;
656 	GtkComboBox *type_combo;
657 	const gchar *backend_name;
658 
659 	priv = E_MAIL_CONFIG_SERVICE_PAGE_GET_PRIVATE (page);
660 
661 	type_combo = GTK_COMBO_BOX (priv->type_combo);
662 	backend_name = gtk_combo_box_get_active_id (type_combo);
663 	g_return_if_fail (backend_name != NULL);
664 
665 	backend = e_mail_config_service_page_lookup_backend (
666 		E_MAIL_CONFIG_SERVICE_PAGE (page), backend_name);
667 
668 	e_mail_config_service_backend_commit_changes (backend);
669 }
670 
671 static void
e_mail_config_service_page_class_init(EMailConfigServicePageClass * class)672 e_mail_config_service_page_class_init (EMailConfigServicePageClass *class)
673 {
674 	GObjectClass *object_class;
675 
676 	g_type_class_add_private (class, sizeof (EMailConfigServicePagePrivate));
677 
678 	object_class = G_OBJECT_CLASS (class);
679 	object_class->set_property = mail_config_service_page_set_property;
680 	object_class->get_property = mail_config_service_page_get_property;
681 	object_class->dispose = mail_config_service_page_dispose;
682 	object_class->finalize = mail_config_service_page_finalize;
683 	object_class->constructed = mail_config_service_page_constructed;
684 
685 	g_object_class_install_property (
686 		object_class,
687 		PROP_ACTIVE_BACKEND,
688 		g_param_spec_object (
689 			"active-backend",
690 			"Active Backend",
691 			"The active service backend",
692 			E_TYPE_MAIL_CONFIG_SERVICE_BACKEND,
693 			G_PARAM_READWRITE |
694 			G_PARAM_STATIC_STRINGS));
695 
696 	g_object_class_install_property (
697 		object_class,
698 		PROP_EMAIL_ADDRESS,
699 		g_param_spec_string (
700 			"email-address",
701 			"Email Address",
702 			"The user's email address",
703 			NULL,
704 			G_PARAM_READWRITE |
705 			G_PARAM_STATIC_STRINGS));
706 
707 	g_object_class_install_property (
708 		object_class,
709 		PROP_REGISTRY,
710 		g_param_spec_object (
711 			"registry",
712 			"Registry",
713 			"Data source registry",
714 			E_TYPE_SOURCE_REGISTRY,
715 			G_PARAM_READWRITE |
716 			G_PARAM_CONSTRUCT_ONLY |
717 			G_PARAM_STATIC_STRINGS));
718 }
719 
720 static void
e_mail_config_service_page_interface_init(EMailConfigPageInterface * iface)721 e_mail_config_service_page_interface_init (EMailConfigPageInterface *iface)
722 {
723 	iface->setup_defaults = mail_config_service_page_setup_defaults;
724 	iface->check_complete = mail_config_service_page_check_complete;
725 	iface->commit_changes = mail_config_service_page_commit_changes;
726 }
727 
728 static void
e_mail_config_service_page_init(EMailConfigServicePage * page)729 e_mail_config_service_page_init (EMailConfigServicePage *page)
730 {
731 	page->priv = E_MAIL_CONFIG_SERVICE_PAGE_GET_PRIVATE (page);
732 }
733 
734 EMailConfigServiceBackend *
e_mail_config_service_page_get_active_backend(EMailConfigServicePage * page)735 e_mail_config_service_page_get_active_backend (EMailConfigServicePage *page)
736 {
737 	g_return_val_if_fail (E_IS_MAIL_CONFIG_SERVICE_PAGE (page), NULL);
738 
739 	return page->priv->active_backend;
740 }
741 
742 void
e_mail_config_service_page_set_active_backend(EMailConfigServicePage * page,EMailConfigServiceBackend * backend)743 e_mail_config_service_page_set_active_backend (EMailConfigServicePage *page,
744                                                EMailConfigServiceBackend *backend)
745 {
746 	g_return_if_fail (E_IS_MAIL_CONFIG_SERVICE_PAGE (page));
747 
748 	if (page->priv->active_backend == backend)
749 		return;
750 
751 	if (backend != NULL) {
752 		g_return_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend));
753 		g_object_ref (backend);
754 	}
755 
756 	if (page->priv->active_backend != NULL)
757 		g_object_unref (page->priv->active_backend);
758 
759 	page->priv->active_backend = backend;
760 
761 	g_object_notify (G_OBJECT (page), "active-backend");
762 }
763 
764 const gchar *
e_mail_config_service_page_get_email_address(EMailConfigServicePage * page)765 e_mail_config_service_page_get_email_address (EMailConfigServicePage *page)
766 {
767 	g_return_val_if_fail (E_IS_MAIL_CONFIG_SERVICE_PAGE (page), NULL);
768 
769 	return page->priv->email_address;
770 }
771 
772 void
e_mail_config_service_page_set_email_address(EMailConfigServicePage * page,const gchar * email_address)773 e_mail_config_service_page_set_email_address (EMailConfigServicePage *page,
774                                               const gchar *email_address)
775 {
776 	g_return_if_fail (E_IS_MAIL_CONFIG_SERVICE_PAGE (page));
777 
778 	if (g_strcmp0 (page->priv->email_address, email_address) == 0)
779 		return;
780 
781 	g_free (page->priv->email_address);
782 	page->priv->email_address = g_strdup (email_address);
783 
784 	g_object_notify (G_OBJECT (page), "email-address");
785 }
786 
787 ESourceRegistry *
e_mail_config_service_page_get_registry(EMailConfigServicePage * page)788 e_mail_config_service_page_get_registry (EMailConfigServicePage *page)
789 {
790 	g_return_val_if_fail (E_IS_MAIL_CONFIG_SERVICE_PAGE (page), NULL);
791 
792 	return page->priv->registry;
793 }
794 
795 EMailConfigServiceBackend *
e_mail_config_service_page_add_scratch_source(EMailConfigServicePage * page,ESource * scratch_source,ESource * opt_collection)796 e_mail_config_service_page_add_scratch_source (EMailConfigServicePage *page,
797                                                ESource *scratch_source,
798                                                ESource *opt_collection)
799 {
800 	GtkWidget *widget;
801 	GtkLabel *type_label;
802 	GtkComboBox *type_combo;
803 	GtkTreeIter iter;
804 	Candidate *candidate;
805 	const gchar *display_name;
806 	gboolean selectable;
807 	gint page_num;
808 
809 	g_return_val_if_fail (E_IS_MAIL_CONFIG_SERVICE_PAGE (page), NULL);
810 	g_return_val_if_fail (E_IS_SOURCE (scratch_source), NULL);
811 
812 	if (opt_collection != NULL)
813 		g_return_val_if_fail (E_IS_SOURCE (opt_collection), NULL);
814 
815 	type_label = GTK_LABEL (page->priv->type_label);
816 	type_combo = GTK_COMBO_BOX (page->priv->type_combo);
817 
818 	candidate = mail_config_service_page_new_candidate (
819 		page, scratch_source, opt_collection);
820 	g_return_val_if_fail (candidate != NULL, NULL);
821 
822 	widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
823 	e_mail_config_service_backend_insert_widgets (
824 		candidate->backend, GTK_BOX (widget));
825 	candidate->widget = g_object_ref_sink (widget);
826 	gtk_widget_show (widget);
827 
828 	g_ptr_array_add (page->priv->candidates, candidate);
829 
830 	display_name = g_dgettext (
831 		candidate->provider->translation_domain,
832 		candidate->provider->name);
833 
834 	page_num = e_mail_config_service_notebook_add_page (
835 		E_MAIL_CONFIG_SERVICE_NOTEBOOK (page->priv->notebook),
836 		candidate->backend, widget);
837 
838 	selectable = e_mail_config_service_backend_get_selectable (
839 		candidate->backend);
840 
841 	gtk_list_store_append (page->priv->list_store, &iter);
842 
843 	gtk_list_store_set (
844 		page->priv->list_store, &iter,
845 		COLUMN_BACKEND_NAME, candidate->name,
846 		COLUMN_DISPLAY_NAME, display_name,
847 		COLUMN_SELECTABLE, selectable,
848 		-1);
849 
850 	/* The type label is only visible if we have one scratch source,
851 	 * so just always set the label text to the most recently added
852 	 * scratch source. */
853 	gtk_label_set_text (type_label, display_name);
854 
855 	/* If no combo box row is active yet, choose the new row. */
856 	if (gtk_combo_box_get_active_id (type_combo) == NULL)
857 		gtk_combo_box_set_active_id (type_combo, candidate->name);
858 
859 	/* If the page number of the newly-added notebook page is zero,
860 	 * show the "type" label.  Otherwise show the "type" combo box.
861 	 * There's an inverted "visible" binding between the combo box
862 	 * and label, so we only need to change one of the widgets. */
863 	gtk_widget_set_visible (GTK_WIDGET (type_combo), page_num > 0);
864 
865 	return candidate->backend;
866 }
867 
868 EMailConfigServiceBackend *
e_mail_config_service_page_lookup_backend(EMailConfigServicePage * page,const gchar * backend_name)869 e_mail_config_service_page_lookup_backend (EMailConfigServicePage *page,
870                                            const gchar *backend_name)
871 {
872 	guint index;
873 
874 	g_return_val_if_fail (E_IS_MAIL_CONFIG_SERVICE_PAGE (page), NULL);
875 	g_return_val_if_fail (backend_name != NULL, NULL);
876 
877 	for (index = 0; index < page->priv->candidates->len; index++) {
878 		Candidate *candidate;
879 
880 		candidate = page->priv->candidates->pdata[index];
881 
882 		if (g_strcmp0 (backend_name, candidate->name) == 0)
883 			return candidate->backend;
884 	}
885 
886 	return NULL;
887 }
888 
889 gboolean
e_mail_config_service_page_auto_configure(EMailConfigServicePage * page,EConfigLookup * config_lookup,gboolean * out_is_complete)890 e_mail_config_service_page_auto_configure (EMailConfigServicePage *page,
891 					   EConfigLookup *config_lookup,
892 					   gboolean *out_is_complete)
893 {
894 	EMailConfigServiceBackend *select_backend = NULL;
895 	gint selected_priority = G_MAXINT;
896 	gboolean selected_is_complete = FALSE;
897 	gboolean any_configured = FALSE;
898 	guint ii;
899 
900 	g_return_val_if_fail (E_IS_MAIL_CONFIG_SERVICE_PAGE (page), FALSE);
901 	g_return_val_if_fail (E_IS_CONFIG_LOOKUP (config_lookup), FALSE);
902 
903 	for (ii = 0; ii < page->priv->candidates->len; ii++) {
904 		EMailConfigServiceBackend *backend;
905 		Candidate *candidate;
906 		gboolean configured;
907 		gint priority = G_MAXINT;
908 		gboolean is_complete = FALSE;
909 
910 		candidate = page->priv->candidates->pdata[ii];
911 		backend = candidate->backend;
912 
913 		configured = e_mail_config_service_backend_auto_configure (backend, config_lookup, &priority, &is_complete);
914 
915 		if (configured && priority < selected_priority) {
916 			selected_priority = priority;
917 			selected_is_complete = is_complete;
918 			select_backend = backend;
919 		}
920 
921 		any_configured = any_configured || configured;
922 	}
923 
924 	if (select_backend)
925 		e_mail_config_service_page_set_active_backend (page, select_backend);
926 
927 	if (out_is_complete)
928 		*out_is_complete = selected_is_complete;
929 
930 	return any_configured;
931 }
932 
933