1 /*
2  * e-proxy-preferences.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 /**
19  * SECTION: e-proxy-preferences
20  * @include: e-util/e-util.h
21  * @short_description: Manage network proxy preferences
22  *
23  * #EProxyPreferences is the main widget for displaying network proxy
24  * preferences.  A link button toggles between a basic mode (for most
25  * users) and advanced mode.  Basic mode only shows proxy details for
26  * the built-in proxy profile, which all new accounts use by default.
27  * Advanced mode reveals a sidebar of proxy profiles, allowing users
28  * to create or delete custom profiles and apply them to particular
29  * accounts.
30  **/
31 
32 #include "evolution-config.h"
33 
34 #include <glib/gi18n-lib.h>
35 
36 #include "e-proxy-editor.h"
37 #include "e-proxy-link-selector.h"
38 #include "e-proxy-selector.h"
39 
40 #include "e-proxy-preferences.h"
41 
42 #define E_PROXY_PREFERENCES_GET_PRIVATE(obj) \
43 	(G_TYPE_INSTANCE_GET_PRIVATE \
44 	((obj), E_TYPE_PROXY_PREFERENCES, EProxyPreferencesPrivate))
45 
46 /* Rate-limit committing proxy changes to the registry. */
47 #define COMMIT_DELAY_SECS 2
48 
49 struct _EProxyPreferencesPrivate {
50 	ESourceRegistry *registry;
51 	gulong source_changed_handler_id;
52 
53 	/* The widgets are not referenced. */
54 	GtkWidget *proxy_selector;
55 	GtkWidget *proxy_editor;
56 	GtkWidget *toplevel;
57 
58 	gulong toplevel_notify_id;
59 
60 	GMutex commit_lock;
61 	guint commit_timeout_id;
62 	GHashTable *commit_sources;
63 
64 	gboolean show_advanced;
65 };
66 
67 enum {
68 	PROP_0,
69 	PROP_REGISTRY,
70 	PROP_SHOW_ADVANCED
71 };
72 
73 /* Forward Declarations */
74 static void	proxy_preferences_commit_changes
75 					(EProxyPreferences *preferences);
76 static void	proxy_preferences_toplevel_notify_visible_cb
77 					(GtkWidget *widget,
78 					 GParamSpec *param,
79 					 EProxyPreferences *preferences);
80 
G_DEFINE_TYPE(EProxyPreferences,e_proxy_preferences,GTK_TYPE_BOX)81 G_DEFINE_TYPE (
82 	EProxyPreferences,
83 	e_proxy_preferences,
84 	GTK_TYPE_BOX)
85 
86 static gboolean
87 proxy_preferences_commit_timeout_cb (gpointer user_data)
88 {
89 	EProxyPreferences *preferences = user_data;
90 
91 	proxy_preferences_commit_changes (preferences);
92 
93 	return FALSE;
94 }
95 
96 static void
proxy_preferences_commit_stash(EProxyPreferences * preferences,ESource * source,gboolean start_timeout)97 proxy_preferences_commit_stash (EProxyPreferences *preferences,
98                                 ESource *source,
99                                 gboolean start_timeout)
100 {
101 	gboolean commit_now = FALSE;
102 
103 	g_mutex_lock (&preferences->priv->commit_lock);
104 
105 	g_hash_table_replace (
106 		preferences->priv->commit_sources,
107 		e_source_dup_uid (source),
108 		e_weak_ref_new (source));
109 
110 	if (preferences->priv->commit_timeout_id > 0) {
111 		g_source_remove (preferences->priv->commit_timeout_id);
112 		preferences->priv->commit_timeout_id = 0;
113 	}
114 
115 	if (start_timeout) {
116 		if (!preferences->priv->toplevel) {
117 			GtkWidget *toplevel;
118 
119 			toplevel = gtk_widget_get_toplevel (GTK_WIDGET (preferences));
120 
121 			if (toplevel) {
122 				g_object_weak_ref (G_OBJECT (toplevel),
123 					(GWeakNotify) g_nullify_pointer, &preferences->priv->toplevel);
124 
125 				preferences->priv->toplevel_notify_id = g_signal_connect (
126 					toplevel, "notify::visible",
127 					G_CALLBACK (proxy_preferences_toplevel_notify_visible_cb), preferences);
128 
129 				preferences->priv->toplevel = toplevel;
130 
131 				if (!gtk_widget_get_visible (toplevel)) {
132 					start_timeout = FALSE;
133 					commit_now = TRUE;
134 				}
135 			}
136 		}
137 	}
138 
139 	if (start_timeout) {
140 		preferences->priv->commit_timeout_id =
141 			e_named_timeout_add_seconds (
142 				COMMIT_DELAY_SECS,
143 				proxy_preferences_commit_timeout_cb,
144 				preferences);
145 	}
146 
147 	g_mutex_unlock (&preferences->priv->commit_lock);
148 
149 	if (commit_now)
150 		e_proxy_preferences_submit (preferences);
151 }
152 
153 static GList *
proxy_preferences_commit_claim(EProxyPreferences * preferences)154 proxy_preferences_commit_claim (EProxyPreferences *preferences)
155 {
156 	GQueue queue = G_QUEUE_INIT;
157 	GList *list, *link;
158 
159 	g_mutex_lock (&preferences->priv->commit_lock);
160 
161 	if (preferences->priv->commit_timeout_id > 0) {
162 		g_source_remove (preferences->priv->commit_timeout_id);
163 		preferences->priv->commit_timeout_id = 0;
164 	}
165 
166 	/* Returns a list of GWeakRefs which may hold an ESource. */
167 	list = g_hash_table_get_values (preferences->priv->commit_sources);
168 
169 	for (link = list; link != NULL; link = g_list_next (link)) {
170 		ESource *source;
171 
172 		source = g_weak_ref_get ((GWeakRef *) link->data);
173 		if (source != NULL)
174 			g_queue_push_tail (&queue, source);
175 	}
176 
177 	g_list_free (list);
178 
179 	g_hash_table_remove_all (preferences->priv->commit_sources);
180 
181 	g_mutex_unlock (&preferences->priv->commit_lock);
182 
183 	return g_queue_peek_head_link (&queue);
184 }
185 
186 static gboolean
proxy_preferences_activate_link_cb(GtkLinkButton * button,EProxyPreferences * preferences)187 proxy_preferences_activate_link_cb (GtkLinkButton *button,
188                                     EProxyPreferences *preferences)
189 {
190 	EProxySelector *selector;
191 
192 	selector = E_PROXY_SELECTOR (preferences->priv->proxy_selector);
193 
194 	if (e_proxy_preferences_get_show_advanced (preferences)) {
195 		/* Basic mode always shows the built-in proxy profile. */
196 		e_proxy_preferences_set_show_advanced (preferences, FALSE);
197 		e_proxy_selector_set_selected (selector, NULL);
198 	} else {
199 		e_proxy_preferences_set_show_advanced (preferences, TRUE);
200 	}
201 
202 	return TRUE;
203 }
204 
205 static gboolean
proxy_preferences_switch_to_label(GBinding * binding,const GValue * source_value,GValue * target_value,gpointer user_data)206 proxy_preferences_switch_to_label (GBinding *binding,
207                                    const GValue *source_value,
208                                    GValue *target_value,
209                                    gpointer user_data)
210 {
211 	const gchar *string;
212 
213 	if (g_value_get_boolean (source_value))
214 		string = _("Switch to Basic Proxy Preferences");
215 	else
216 		string = _("Switch to Advanced Proxy Preferences");
217 
218 	g_value_set_string (target_value, string);
219 
220 	return TRUE;
221 }
222 
223 static gboolean
proxy_preferences_source_to_display_name(GBinding * binding,const GValue * source_value,GValue * target_value,gpointer user_data)224 proxy_preferences_source_to_display_name (GBinding *binding,
225                                           const GValue *source_value,
226                                           GValue *target_value,
227                                           gpointer user_data)
228 {
229 	ESource *source;
230 	gchar *display_name;
231 
232 	source = g_value_get_object (source_value);
233 	g_return_val_if_fail (source != NULL, FALSE);
234 
235 	display_name = e_source_dup_display_name (source);
236 	g_value_take_string (target_value, display_name);
237 
238 	return TRUE;
239 }
240 
241 static void
proxy_preferences_write_done_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)242 proxy_preferences_write_done_cb (GObject *source_object,
243                                  GAsyncResult *result,
244                                  gpointer user_data)
245 {
246 	ESource *source;
247 	EProxyPreferences *preferences;
248 	GError *error = NULL;
249 
250 	source = E_SOURCE (source_object);
251 	preferences = E_PROXY_PREFERENCES (user_data);
252 
253 	e_source_write_finish (source, result, &error);
254 
255 	/* FIXME Display the error in an alert sink. */
256 	if (error != NULL) {
257 		g_warning ("%s: %s", G_STRFUNC, error->message);
258 		g_error_free (error);
259 	}
260 
261 	g_object_unref (preferences);
262 }
263 
264 static void
proxy_preferences_commit_changes(EProxyPreferences * preferences)265 proxy_preferences_commit_changes (EProxyPreferences *preferences)
266 {
267 	GList *list, *link;
268 
269 	list = proxy_preferences_commit_claim (preferences);
270 
271 	for (link = list; link != NULL; link = g_list_next (link)) {
272 		e_source_write (
273 			E_SOURCE (link->data), NULL,
274 			proxy_preferences_write_done_cb,
275 			g_object_ref (preferences));
276 	}
277 
278 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
279 }
280 
281 static void
proxy_preferences_toplevel_notify_visible_cb(GtkWidget * widget,GParamSpec * param,EProxyPreferences * preferences)282 proxy_preferences_toplevel_notify_visible_cb (GtkWidget *widget,
283 					      GParamSpec *param,
284 					      EProxyPreferences *preferences)
285 {
286 	g_return_if_fail (GTK_IS_WIDGET (widget));
287 	g_return_if_fail (E_IS_PROXY_PREFERENCES (preferences));
288 
289 	/* The toplevel widget was hidden, save anything pending immediately */
290 	if (!gtk_widget_get_visible (widget))
291 		e_proxy_preferences_submit (preferences);
292 }
293 
294 static void
proxy_preferences_source_changed_cb(ESourceRegistry * registry,ESource * source,EProxyPreferences * preferences)295 proxy_preferences_source_changed_cb (ESourceRegistry *registry,
296                                      ESource *source,
297                                      EProxyPreferences *preferences)
298 {
299 	if (e_source_has_extension (source, E_SOURCE_EXTENSION_PROXY))
300 		proxy_preferences_commit_stash (preferences, source, TRUE);
301 }
302 
303 static void
proxy_preferences_set_registry(EProxyPreferences * preferences,ESourceRegistry * registry)304 proxy_preferences_set_registry (EProxyPreferences *preferences,
305                                 ESourceRegistry *registry)
306 {
307 	gulong handler_id;
308 
309 	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
310 	g_return_if_fail (preferences->priv->registry == NULL);
311 
312 	preferences->priv->registry = g_object_ref (registry);
313 
314 	handler_id = g_signal_connect (
315 		registry, "source-changed",
316 		G_CALLBACK (proxy_preferences_source_changed_cb), preferences);
317 	preferences->priv->source_changed_handler_id = handler_id;
318 }
319 
320 static void
proxy_preferences_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)321 proxy_preferences_set_property (GObject *object,
322                                 guint property_id,
323                                 const GValue *value,
324                                 GParamSpec *pspec)
325 {
326 	switch (property_id) {
327 		case PROP_REGISTRY:
328 			proxy_preferences_set_registry (
329 				E_PROXY_PREFERENCES (object),
330 				g_value_get_object (value));
331 			return;
332 
333 		case PROP_SHOW_ADVANCED:
334 			e_proxy_preferences_set_show_advanced (
335 				E_PROXY_PREFERENCES (object),
336 				g_value_get_boolean (value));
337 			return;
338 	}
339 
340 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
341 }
342 
343 static void
proxy_preferences_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)344 proxy_preferences_get_property (GObject *object,
345                                 guint property_id,
346                                 GValue *value,
347                                 GParamSpec *pspec)
348 {
349 	switch (property_id) {
350 		case PROP_REGISTRY:
351 			g_value_set_object (
352 				value,
353 				e_proxy_preferences_get_registry (
354 				E_PROXY_PREFERENCES (object)));
355 			return;
356 
357 		case PROP_SHOW_ADVANCED:
358 			g_value_set_boolean (
359 				value,
360 				e_proxy_preferences_get_show_advanced (
361 				E_PROXY_PREFERENCES (object)));
362 			return;
363 	}
364 
365 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
366 }
367 
368 static void
proxy_preferences_dispose(GObject * object)369 proxy_preferences_dispose (GObject *object)
370 {
371 	EProxyPreferencesPrivate *priv;
372 
373 	priv = E_PROXY_PREFERENCES_GET_PRIVATE (object);
374 
375 	if (priv->toplevel) {
376 		g_object_weak_unref (G_OBJECT (priv->toplevel),
377 			(GWeakNotify) g_nullify_pointer, &priv->toplevel);
378 
379 		if (priv->toplevel_notify_id) {
380 			g_signal_handler_disconnect (priv->toplevel, priv->toplevel_notify_id);
381 			priv->toplevel_notify_id = 0;
382 		}
383 
384 		priv->toplevel = NULL;
385 	}
386 
387 	if (priv->source_changed_handler_id > 0) {
388 		g_signal_handler_disconnect (
389 			priv->registry,
390 			priv->source_changed_handler_id);
391 		priv->source_changed_handler_id = 0;
392 	}
393 
394 	if (priv->commit_timeout_id > 0) {
395 		g_source_remove (priv->commit_timeout_id);
396 		priv->commit_timeout_id = 0;
397 
398 		/* Make sure the changes are committed, or at least its write invoked */
399 		proxy_preferences_commit_changes (E_PROXY_PREFERENCES (object));
400 	}
401 
402 	g_clear_object (&priv->registry);
403 
404 	g_hash_table_remove_all (priv->commit_sources);
405 
406 	/* Chain up to parent's dispose() method. */
407 	G_OBJECT_CLASS (e_proxy_preferences_parent_class)->dispose (object);
408 }
409 
410 static void
proxy_preferences_finalize(GObject * object)411 proxy_preferences_finalize (GObject *object)
412 {
413 	EProxyPreferencesPrivate *priv;
414 
415 	priv = E_PROXY_PREFERENCES_GET_PRIVATE (object);
416 
417 	g_mutex_clear (&priv->commit_lock);
418 	g_hash_table_destroy (priv->commit_sources);
419 
420 	/* Chain up to parent's finalize() method. */
421 	G_OBJECT_CLASS (e_proxy_preferences_parent_class)->finalize (object);
422 }
423 
424 static void
proxy_preferences_constructed(GObject * object)425 proxy_preferences_constructed (GObject *object)
426 {
427 	EProxyPreferences *preferences;
428 	ESourceRegistry *registry;
429 	GtkWidget *widget;
430 	GtkWidget *container;
431 	GtkWidget *container2;
432 	PangoAttribute *attr;
433 	PangoAttrList *attr_list;
434 	GList *list;
435 	const gchar *extension_name;
436 	gboolean show_advanced;
437 
438 	/* Chain up to parent's constructed() method. */
439 	G_OBJECT_CLASS (e_proxy_preferences_parent_class)->constructed (object);
440 
441 	preferences = E_PROXY_PREFERENCES (object);
442 	registry = e_proxy_preferences_get_registry (preferences);
443 
444 	gtk_orientable_set_orientation (
445 		GTK_ORIENTABLE (preferences), GTK_ORIENTATION_VERTICAL);
446 	gtk_box_set_spacing (GTK_BOX (preferences), 12);
447 
448 	widget = gtk_grid_new ();
449 	gtk_grid_set_row_spacing (GTK_GRID (widget), 12);
450 	gtk_grid_set_column_spacing (GTK_GRID (widget), 12);
451 	gtk_box_pack_start (GTK_BOX (preferences), widget, TRUE, TRUE, 0);
452 	gtk_widget_show (widget);
453 
454 	container = widget;
455 
456 	widget = e_proxy_selector_new (registry);
457 	gtk_widget_set_vexpand (widget, TRUE);
458 	gtk_widget_set_size_request (widget, 200, -1);
459 	gtk_grid_attach (GTK_GRID (container), widget, 0, 0, 1, 3);
460 	preferences->priv->proxy_selector = widget;  /* do not reference */
461 
462 	e_binding_bind_property (
463 		preferences, "show-advanced",
464 		widget, "visible",
465 		G_BINDING_SYNC_CREATE);
466 
467 	attr_list = pango_attr_list_new ();
468 	attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
469 	pango_attr_list_insert (attr_list, attr);
470 
471 	widget = gtk_label_new ("");
472 	gtk_widget_set_hexpand (widget, TRUE);
473 	gtk_widget_set_valign (widget, GTK_ALIGN_START);
474 	gtk_label_set_attributes (GTK_LABEL (widget), attr_list);
475 	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
476 	gtk_grid_attach (GTK_GRID (container), widget, 1, 0, 1, 1);
477 	gtk_widget_show (widget);
478 
479 	e_binding_bind_property_full (
480 		preferences->priv->proxy_selector, "selected",
481 		widget, "label",
482 		G_BINDING_SYNC_CREATE,
483 		proxy_preferences_source_to_display_name,
484 		NULL, NULL, NULL);
485 
486 	pango_attr_list_unref (attr_list);
487 
488 	widget = e_proxy_editor_new (registry);
489 	gtk_widget_set_margin_left (widget, 12);
490 	gtk_widget_set_valign (widget, GTK_ALIGN_START);
491 	gtk_grid_attach (GTK_GRID (container), widget, 1, 1, 1, 1);
492 	preferences->priv->proxy_editor = widget;  /* do not reference */
493 	gtk_widget_show (widget);
494 
495 	e_binding_bind_property (
496 		preferences->priv->proxy_selector, "selected",
497 		widget, "source",
498 		G_BINDING_SYNC_CREATE);
499 
500 	widget = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
501 	gtk_widget_set_margin_left (widget, 12);
502 	gtk_widget_set_vexpand (widget, TRUE);
503 	gtk_grid_attach (GTK_GRID (container), widget, 1, 2, 1, 1);
504 	gtk_widget_show (widget);
505 
506 	container = widget;
507 
508 	/* Nested containers to control multiple levels of visibility. */
509 
510 	widget = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
511 	gtk_widget_set_vexpand (widget, TRUE);
512 	gtk_container_add (GTK_CONTAINER (container), widget);
513 
514 	e_binding_bind_property (
515 		preferences, "show-advanced",
516 		widget, "visible",
517 		G_BINDING_SYNC_CREATE);
518 
519 	container = widget;
520 
521 	widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
522 	gtk_container_add (GTK_CONTAINER (container), widget);
523 
524 	container = widget;
525 
526 	widget = gtk_label_new (
527 		_("Apply custom proxy settings to these accounts:"));
528 	gtk_widget_set_halign (widget, GTK_ALIGN_START);
529 	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
530 	gtk_widget_show (widget);
531 
532 	widget = gtk_scrolled_window_new (NULL, NULL);
533 	gtk_scrolled_window_set_policy (
534 		GTK_SCROLLED_WINDOW (widget),
535 		GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
536 	gtk_scrolled_window_set_shadow_type (
537 		GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
538 	gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
539 	gtk_widget_show (widget);
540 
541 	container2 = widget;
542 
543 	widget = e_proxy_link_selector_new (registry);
544 	gtk_container_add (GTK_CONTAINER (container2), widget);
545 	gtk_widget_show (widget);
546 
547 	e_binding_bind_property (
548 		preferences->priv->proxy_selector, "selected",
549 		widget, "target-source",
550 		G_BINDING_SYNC_CREATE);
551 
552 	/* This is bound to the GtkBox created above. */
553 	e_binding_bind_property (
554 		widget, "show-toggles",
555 		container, "visible",
556 		G_BINDING_SYNC_CREATE);
557 
558 	widget = gtk_link_button_new ("");
559 	gtk_widget_set_halign (widget, GTK_ALIGN_START);
560 	gtk_widget_set_tooltip_markup (
561 		widget, _(
562 		"<b>Advanced Proxy Preferences</b> lets you "
563 		"define alternate network proxies and apply "
564 		"them to specific accounts"));
565 	gtk_box_pack_start (GTK_BOX (preferences), widget, FALSE, FALSE, 0);
566 	gtk_widget_show (widget);
567 
568 	e_binding_bind_property_full (
569 		preferences, "show-advanced",
570 		widget, "label",
571 		G_BINDING_SYNC_CREATE,
572 		proxy_preferences_switch_to_label,
573 		NULL, NULL, NULL);
574 
575 	e_binding_bind_property (
576 		preferences, "show-advanced",
577 		widget, "has-tooltip",
578 		G_BINDING_SYNC_CREATE |
579 		G_BINDING_INVERT_BOOLEAN);
580 
581 	g_signal_connect (
582 		widget, "activate-link",
583 		G_CALLBACK (proxy_preferences_activate_link_cb),
584 		preferences);
585 
586 	extension_name = E_SOURCE_EXTENSION_PROXY;
587 	list = e_source_registry_list_sources (registry, extension_name);
588 	show_advanced = (g_list_length (list) > 1);
589 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
590 
591 	/* Switch to advanced mode if there are multiple proxy profiles. */
592 	e_proxy_preferences_set_show_advanced (preferences, show_advanced);
593 }
594 
595 static void
e_proxy_preferences_class_init(EProxyPreferencesClass * class)596 e_proxy_preferences_class_init (EProxyPreferencesClass *class)
597 {
598 	GObjectClass *object_class;
599 
600 	g_type_class_add_private (class, sizeof (EProxyPreferencesPrivate));
601 
602 	object_class = G_OBJECT_CLASS (class);
603 	object_class->set_property = proxy_preferences_set_property;
604 	object_class->get_property = proxy_preferences_get_property;
605 	object_class->dispose = proxy_preferences_dispose;
606 	object_class->finalize = proxy_preferences_finalize;
607 	object_class->constructed = proxy_preferences_constructed;
608 
609 	g_object_class_install_property (
610 		object_class,
611 		PROP_REGISTRY,
612 		g_param_spec_object (
613 			"registry",
614 			"Registry",
615 			"Data source registry",
616 			E_TYPE_SOURCE_REGISTRY,
617 			G_PARAM_READWRITE |
618 			G_PARAM_CONSTRUCT_ONLY |
619 			G_PARAM_STATIC_STRINGS));
620 
621 	g_object_class_install_property (
622 		object_class,
623 		PROP_SHOW_ADVANCED,
624 		g_param_spec_boolean (
625 			"show-advanced",
626 			"Show Advanced",
627 			"Show advanced proxy preferences",
628 			FALSE,
629 			G_PARAM_READWRITE |
630 			G_PARAM_STATIC_STRINGS));
631 }
632 
633 static void
e_proxy_preferences_init(EProxyPreferences * preferences)634 e_proxy_preferences_init (EProxyPreferences *preferences)
635 {
636 	GHashTable *commit_sources;
637 
638 	/* Keys are UIDs, values are allocated GWeakRefs. */
639 	commit_sources = g_hash_table_new_full (
640 		(GHashFunc) g_str_hash,
641 		(GEqualFunc) g_str_equal,
642 		(GDestroyNotify) g_free,
643 		(GDestroyNotify) e_weak_ref_free);
644 
645 	preferences->priv = E_PROXY_PREFERENCES_GET_PRIVATE (preferences);
646 
647 	g_mutex_init (&preferences->priv->commit_lock);
648 	preferences->priv->commit_sources = commit_sources;
649 }
650 
651 /**
652  * e_proxy_preferences_new:
653  * @registry: an #ESourceRegistry
654  *
655  * Creates a new #EProxyPreferences widget using #ESource instances in
656  * @registry.
657  *
658  * Returns: a new #EProxyPreferences
659  **/
660 GtkWidget *
e_proxy_preferences_new(ESourceRegistry * registry)661 e_proxy_preferences_new (ESourceRegistry *registry)
662 {
663 	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
664 
665 	return g_object_new (
666 		E_TYPE_PROXY_PREFERENCES,
667 		"registry", registry, NULL);
668 }
669 
670 /**
671  * e_proxy_preferences_submit:
672  * @preferences: an #EProxyPreferences
673  *
674  * Writes the displayed proxy profile details to the #ESource being edited,
675  * and submits the changes to the registry service asynchronously.
676  *
677  * Normally changes are submitted to the registry service automatically
678  * after a brief delay, but changes may sometimes need to be submitted
679  * explicitly such as when the top-level window is closing.
680  **/
681 void
e_proxy_preferences_submit(EProxyPreferences * preferences)682 e_proxy_preferences_submit (EProxyPreferences *preferences)
683 {
684 	EProxyEditor *proxy_editor;
685 	ESource *source;
686 
687 	g_return_if_fail (E_IS_PROXY_PREFERENCES (preferences));
688 
689 	proxy_editor = E_PROXY_EDITOR (preferences->priv->proxy_editor);
690 
691 	/* Save user changes to the proxy source. */
692 	e_proxy_editor_save (proxy_editor);
693 
694 	/* This part normally happens from a "source-changed"
695 	 * signal handler, but we can't wait for that here. */
696 	source = e_proxy_editor_ref_source (proxy_editor);
697 	proxy_preferences_commit_stash (preferences, source, FALSE);
698 	g_object_unref (source);
699 
700 	/* Commit any pending changes immediately. */
701 	proxy_preferences_commit_changes (preferences);
702 }
703 
704 /**
705  * e_proxy_preferences_get_registry:
706  * @preferences: an #EProxyPreferences
707  *
708  * Returns the #ESourceRegistry passed to e_proxy_preferences_new().
709  *
710  * Returns: an #ESourceRegistry
711  **/
712 ESourceRegistry *
e_proxy_preferences_get_registry(EProxyPreferences * preferences)713 e_proxy_preferences_get_registry (EProxyPreferences *preferences)
714 {
715 	g_return_val_if_fail (E_IS_PROXY_PREFERENCES (preferences), NULL);
716 
717 	return preferences->priv->registry;
718 }
719 
720 /**
721  * e_proxy_preferences_get_show_advanced:
722  * @preferences: an #EProxyPreferences
723  *
724  * Returns whether @preferences is currently in advanced mode.
725  *
726  * Returns: whether advanced proxy preferences are visible
727  **/
728 gboolean
e_proxy_preferences_get_show_advanced(EProxyPreferences * preferences)729 e_proxy_preferences_get_show_advanced (EProxyPreferences *preferences)
730 {
731 	g_return_val_if_fail (E_IS_PROXY_PREFERENCES (preferences), FALSE);
732 
733 	return preferences->priv->show_advanced;
734 }
735 
736 /**
737  * e_proxy_preferences_set_show_advanced:
738  * @preferences: an #EProxyPreferences
739  * @show_advanced: whether to show advanced proxy preferences
740  *
741  * Switches @preferences to advanced mode if @show_advanced is %TRUE,
742  * or to basic mode if @show_advanced is %FALSE.
743  **/
744 void
e_proxy_preferences_set_show_advanced(EProxyPreferences * preferences,gboolean show_advanced)745 e_proxy_preferences_set_show_advanced (EProxyPreferences *preferences,
746                                        gboolean show_advanced)
747 {
748 	g_return_if_fail (E_IS_PROXY_PREFERENCES (preferences));
749 
750 	if (show_advanced == preferences->priv->show_advanced)
751 		return;
752 
753 	preferences->priv->show_advanced = show_advanced;
754 
755 	g_object_notify (G_OBJECT (preferences), "show-advanced");
756 }
757 
758