2 * e-proxy-preferences.c
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.
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
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/>.
19 * SECTION: e-proxy-preferences
20 * @include: e-util/e-util.h
21 * @short_description: Manage network proxy preferences
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
32 #include "e-proxy-preferences.h"
35 #include <glib/gi18n-lib.h>
37 #include <e-util/e-proxy-editor.h>
38 #include <e-util/e-proxy-link-selector.h>
39 #include <e-util/e-proxy-selector.h>
41 #define E_PROXY_PREFERENCES_GET_PRIVATE(obj) \
42 (G_TYPE_INSTANCE_GET_PRIVATE \
43 ((obj), E_TYPE_PROXY_PREFERENCES, EProxyPreferencesPrivate))
45 /* Rate-limit committing proxy changes to the registry. */
46 #define COMMIT_DELAY_SECS 2
48 struct _EProxyPreferencesPrivate
{
49 ESourceRegistry
*registry
;
50 gulong source_changed_handler_id
;
52 /* The widgets are not referenced. */
53 GtkWidget
*proxy_selector
;
54 GtkWidget
*proxy_editor
;
57 gulong toplevel_notify_id
;
60 guint commit_timeout_id
;
61 GHashTable
*commit_sources
;
63 gboolean show_advanced
;
72 /* Forward Declarations */
73 static void proxy_preferences_commit_changes
74 (EProxyPreferences
*preferences
);
75 static void proxy_preferences_toplevel_notify_visible_cb
78 EProxyPreferences
*preferences
);
86 proxy_preferences_commit_timeout_cb (gpointer user_data
)
88 EProxyPreferences
*preferences
= user_data
;
90 proxy_preferences_commit_changes (preferences
);
96 proxy_preferences_commit_stash (EProxyPreferences
*preferences
,
98 gboolean start_timeout
)
100 gboolean commit_now
= FALSE
;
102 g_mutex_lock (&preferences
->priv
->commit_lock
);
104 g_hash_table_replace (
105 preferences
->priv
->commit_sources
,
106 e_source_dup_uid (source
),
107 e_weak_ref_new (source
));
109 if (preferences
->priv
->commit_timeout_id
> 0) {
110 g_source_remove (preferences
->priv
->commit_timeout_id
);
111 preferences
->priv
->commit_timeout_id
= 0;
115 if (!preferences
->priv
->toplevel
) {
118 toplevel
= gtk_widget_get_toplevel (GTK_WIDGET (preferences
));
121 g_object_weak_ref (G_OBJECT (toplevel
),
122 (GWeakNotify
) g_nullify_pointer
, &preferences
->priv
->toplevel
);
124 preferences
->priv
->toplevel_notify_id
= g_signal_connect (
125 toplevel
, "notify::visible",
126 G_CALLBACK (proxy_preferences_toplevel_notify_visible_cb
), preferences
);
128 preferences
->priv
->toplevel
= toplevel
;
130 if (!gtk_widget_get_visible (toplevel
)) {
131 start_timeout
= FALSE
;
139 preferences
->priv
->commit_timeout_id
=
140 e_named_timeout_add_seconds (
142 proxy_preferences_commit_timeout_cb
,
146 g_mutex_unlock (&preferences
->priv
->commit_lock
);
149 e_proxy_preferences_submit (preferences
);
153 proxy_preferences_commit_claim (EProxyPreferences
*preferences
)
155 GQueue queue
= G_QUEUE_INIT
;
158 g_mutex_lock (&preferences
->priv
->commit_lock
);
160 if (preferences
->priv
->commit_timeout_id
> 0) {
161 g_source_remove (preferences
->priv
->commit_timeout_id
);
162 preferences
->priv
->commit_timeout_id
= 0;
165 /* Returns a list of GWeakRefs which may hold an ESource. */
166 list
= g_hash_table_get_values (preferences
->priv
->commit_sources
);
168 for (link
= list
; link
!= NULL
; link
= g_list_next (link
)) {
171 source
= g_weak_ref_get ((GWeakRef
*) link
->data
);
173 g_queue_push_tail (&queue
, source
);
178 g_hash_table_remove_all (preferences
->priv
->commit_sources
);
180 g_mutex_unlock (&preferences
->priv
->commit_lock
);
182 return g_queue_peek_head_link (&queue
);
186 proxy_preferences_activate_link_cb (GtkLinkButton
*button
,
187 EProxyPreferences
*preferences
)
189 EProxySelector
*selector
;
191 selector
= E_PROXY_SELECTOR (preferences
->priv
->proxy_selector
);
193 if (e_proxy_preferences_get_show_advanced (preferences
)) {
194 /* Basic mode always shows the built-in proxy profile. */
195 e_proxy_preferences_set_show_advanced (preferences
, FALSE
);
196 e_proxy_selector_set_selected (selector
, NULL
);
198 e_proxy_preferences_set_show_advanced (preferences
, TRUE
);
205 proxy_preferences_switch_to_label (GBinding
*binding
,
206 const GValue
*source_value
,
207 GValue
*target_value
,
212 if (g_value_get_boolean (source_value
))
213 string
= _("Switch to Basic Proxy Preferences");
215 string
= _("Switch to Advanced Proxy Preferences");
217 g_value_set_string (target_value
, string
);
223 proxy_preferences_source_to_display_name (GBinding
*binding
,
224 const GValue
*source_value
,
225 GValue
*target_value
,
231 source
= g_value_get_object (source_value
);
232 g_return_val_if_fail (source
!= NULL
, FALSE
);
234 display_name
= e_source_dup_display_name (source
);
235 g_value_take_string (target_value
, display_name
);
241 proxy_preferences_write_done_cb (GObject
*source_object
,
242 GAsyncResult
*result
,
246 EProxyPreferences
*preferences
;
247 GError
*error
= NULL
;
249 source
= E_SOURCE (source_object
);
250 preferences
= E_PROXY_PREFERENCES (user_data
);
252 e_source_write_finish (source
, result
, &error
);
254 /* FIXME Display the error in an alert sink. */
256 g_warning ("%s: %s", G_STRFUNC
, error
->message
);
257 g_error_free (error
);
260 g_object_unref (preferences
);
264 proxy_preferences_commit_changes (EProxyPreferences
*preferences
)
268 list
= proxy_preferences_commit_claim (preferences
);
270 for (link
= list
; link
!= NULL
; link
= g_list_next (link
)) {
272 E_SOURCE (link
->data
), NULL
,
273 proxy_preferences_write_done_cb
,
274 g_object_ref (preferences
));
277 g_list_free_full (list
, (GDestroyNotify
) g_object_unref
);
281 proxy_preferences_toplevel_notify_visible_cb (GtkWidget
*widget
,
283 EProxyPreferences
*preferences
)
285 g_return_if_fail (GTK_IS_WIDGET (widget
));
286 g_return_if_fail (E_IS_PROXY_PREFERENCES (preferences
));
288 /* The toplevel widget was hidden, save anything pending immediately */
289 if (!gtk_widget_get_visible (widget
))
290 e_proxy_preferences_submit (preferences
);
294 proxy_preferences_source_changed_cb (ESourceRegistry
*registry
,
296 EProxyPreferences
*preferences
)
298 if (e_source_has_extension (source
, E_SOURCE_EXTENSION_PROXY
))
299 proxy_preferences_commit_stash (preferences
, source
, TRUE
);
303 proxy_preferences_set_registry (EProxyPreferences
*preferences
,
304 ESourceRegistry
*registry
)
308 g_return_if_fail (E_IS_SOURCE_REGISTRY (registry
));
309 g_return_if_fail (preferences
->priv
->registry
== NULL
);
311 preferences
->priv
->registry
= g_object_ref (registry
);
313 handler_id
= g_signal_connect (
314 registry
, "source-changed",
315 G_CALLBACK (proxy_preferences_source_changed_cb
), preferences
);
316 preferences
->priv
->source_changed_handler_id
= handler_id
;
320 proxy_preferences_set_property (GObject
*object
,
325 switch (property_id
) {
327 proxy_preferences_set_registry (
328 E_PROXY_PREFERENCES (object
),
329 g_value_get_object (value
));
332 case PROP_SHOW_ADVANCED
:
333 e_proxy_preferences_set_show_advanced (
334 E_PROXY_PREFERENCES (object
),
335 g_value_get_boolean (value
));
339 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
343 proxy_preferences_get_property (GObject
*object
,
348 switch (property_id
) {
352 e_proxy_preferences_get_registry (
353 E_PROXY_PREFERENCES (object
)));
356 case PROP_SHOW_ADVANCED
:
357 g_value_set_boolean (
359 e_proxy_preferences_get_show_advanced (
360 E_PROXY_PREFERENCES (object
)));
364 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
368 proxy_preferences_dispose (GObject
*object
)
370 EProxyPreferencesPrivate
*priv
;
372 priv
= E_PROXY_PREFERENCES_GET_PRIVATE (object
);
374 if (priv
->toplevel
) {
375 g_object_weak_unref (G_OBJECT (priv
->toplevel
),
376 (GWeakNotify
) g_nullify_pointer
, &priv
->toplevel
);
378 if (priv
->toplevel_notify_id
) {
379 g_signal_handler_disconnect (priv
->toplevel
, priv
->toplevel_notify_id
);
380 priv
->toplevel_notify_id
= 0;
383 priv
->toplevel
= NULL
;
386 if (priv
->source_changed_handler_id
> 0) {
387 g_signal_handler_disconnect (
389 priv
->source_changed_handler_id
);
390 priv
->source_changed_handler_id
= 0;
393 if (priv
->commit_timeout_id
> 0) {
394 g_source_remove (priv
->commit_timeout_id
);
395 priv
->commit_timeout_id
= 0;
397 /* Make sure the changes are committed, or at least its write invoked */
398 proxy_preferences_commit_changes (E_PROXY_PREFERENCES (object
));
401 g_clear_object (&priv
->registry
);
403 g_hash_table_remove_all (priv
->commit_sources
);
405 /* Chain up to parent's dispose() method. */
406 G_OBJECT_CLASS (e_proxy_preferences_parent_class
)->dispose (object
);
410 proxy_preferences_finalize (GObject
*object
)
412 EProxyPreferencesPrivate
*priv
;
414 priv
= E_PROXY_PREFERENCES_GET_PRIVATE (object
);
416 g_mutex_clear (&priv
->commit_lock
);
417 g_hash_table_destroy (priv
->commit_sources
);
419 /* Chain up to parent's finalize() method. */
420 G_OBJECT_CLASS (e_proxy_preferences_parent_class
)->finalize (object
);
424 proxy_preferences_constructed (GObject
*object
)
426 EProxyPreferences
*preferences
;
427 ESourceRegistry
*registry
;
429 GtkWidget
*container
;
430 GtkWidget
*container2
;
431 PangoAttribute
*attr
;
432 PangoAttrList
*attr_list
;
434 const gchar
*extension_name
;
435 gboolean show_advanced
;
437 /* Chain up to parent's constructed() method. */
438 G_OBJECT_CLASS (e_proxy_preferences_parent_class
)->constructed (object
);
440 preferences
= E_PROXY_PREFERENCES (object
);
441 registry
= e_proxy_preferences_get_registry (preferences
);
443 gtk_orientable_set_orientation (
444 GTK_ORIENTABLE (preferences
), GTK_ORIENTATION_VERTICAL
);
445 gtk_box_set_spacing (GTK_BOX (preferences
), 12);
447 widget
= gtk_grid_new ();
448 gtk_grid_set_row_spacing (GTK_GRID (widget
), 12);
449 gtk_grid_set_column_spacing (GTK_GRID (widget
), 12);
450 gtk_box_pack_start (GTK_BOX (preferences
), widget
, TRUE
, TRUE
, 0);
451 gtk_widget_show (widget
);
455 widget
= e_proxy_selector_new (registry
);
456 gtk_widget_set_vexpand (widget
, TRUE
);
457 gtk_widget_set_size_request (widget
, 200, -1);
458 gtk_grid_attach (GTK_GRID (container
), widget
, 0, 0, 1, 3);
459 preferences
->priv
->proxy_selector
= widget
; /* do not reference */
461 g_object_bind_property (
462 preferences
, "show-advanced",
464 G_BINDING_SYNC_CREATE
);
466 attr_list
= pango_attr_list_new ();
467 attr
= pango_attr_weight_new (PANGO_WEIGHT_BOLD
);
468 pango_attr_list_insert (attr_list
, attr
);
470 widget
= gtk_label_new ("");
471 gtk_widget_set_hexpand (widget
, TRUE
);
472 gtk_widget_set_valign (widget
, GTK_ALIGN_START
);
473 gtk_label_set_attributes (GTK_LABEL (widget
), attr_list
);
474 gtk_misc_set_alignment (GTK_MISC (widget
), 0.0, 0.5);
475 gtk_grid_attach (GTK_GRID (container
), widget
, 1, 0, 1, 1);
476 gtk_widget_show (widget
);
478 g_object_bind_property_full (
479 preferences
->priv
->proxy_selector
, "selected",
481 G_BINDING_SYNC_CREATE
,
482 proxy_preferences_source_to_display_name
,
485 pango_attr_list_unref (attr_list
);
487 widget
= e_proxy_editor_new (registry
);
488 gtk_widget_set_margin_left (widget
, 12);
489 gtk_widget_set_valign (widget
, GTK_ALIGN_START
);
490 gtk_grid_attach (GTK_GRID (container
), widget
, 1, 1, 1, 1);
491 preferences
->priv
->proxy_editor
= widget
; /* do not reference */
492 gtk_widget_show (widget
);
494 g_object_bind_property (
495 preferences
->priv
->proxy_selector
, "selected",
497 G_BINDING_SYNC_CREATE
);
499 widget
= gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
500 gtk_widget_set_margin_left (widget
, 12);
501 gtk_widget_set_vexpand (widget
, TRUE
);
502 gtk_grid_attach (GTK_GRID (container
), widget
, 1, 2, 1, 1);
503 gtk_widget_show (widget
);
507 /* Nested containers to control multiple levels of visibility. */
509 widget
= gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
510 gtk_widget_set_vexpand (widget
, TRUE
);
511 gtk_container_add (GTK_CONTAINER (container
), widget
);
513 g_object_bind_property (
514 preferences
, "show-advanced",
516 G_BINDING_SYNC_CREATE
);
520 widget
= gtk_box_new (GTK_ORIENTATION_VERTICAL
, 6);
521 gtk_container_add (GTK_CONTAINER (container
), widget
);
525 widget
= gtk_label_new (
526 _("Apply custom proxy settings to these accounts:"));
527 gtk_widget_set_halign (widget
, GTK_ALIGN_START
);
528 gtk_box_pack_start (GTK_BOX (container
), widget
, FALSE
, FALSE
, 0);
529 gtk_widget_show (widget
);
531 widget
= gtk_scrolled_window_new (NULL
, NULL
);
532 gtk_scrolled_window_set_policy (
533 GTK_SCROLLED_WINDOW (widget
),
534 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
535 gtk_scrolled_window_set_shadow_type (
536 GTK_SCROLLED_WINDOW (widget
), GTK_SHADOW_IN
);
537 gtk_box_pack_start (GTK_BOX (container
), widget
, TRUE
, TRUE
, 0);
538 gtk_widget_show (widget
);
542 widget
= e_proxy_link_selector_new (registry
);
543 gtk_container_add (GTK_CONTAINER (container2
), widget
);
544 gtk_widget_show (widget
);
546 g_object_bind_property (
547 preferences
->priv
->proxy_selector
, "selected",
548 widget
, "target-source",
549 G_BINDING_SYNC_CREATE
);
551 /* This is bound to the GtkBox created above. */
552 g_object_bind_property (
553 widget
, "show-toggles",
554 container
, "visible",
555 G_BINDING_SYNC_CREATE
);
557 widget
= gtk_link_button_new ("");
558 gtk_widget_set_halign (widget
, GTK_ALIGN_START
);
559 gtk_widget_set_tooltip_markup (
561 "<b>Advanced Proxy Preferences</b> lets you "
562 "define alternate network proxies and apply "
563 "them to specific accounts"));
564 gtk_box_pack_start (GTK_BOX (preferences
), widget
, FALSE
, FALSE
, 0);
565 gtk_widget_show (widget
);
567 g_object_bind_property_full (
568 preferences
, "show-advanced",
570 G_BINDING_SYNC_CREATE
,
571 proxy_preferences_switch_to_label
,
574 g_object_bind_property (
575 preferences
, "show-advanced",
576 widget
, "has-tooltip",
577 G_BINDING_SYNC_CREATE
|
578 G_BINDING_INVERT_BOOLEAN
);
581 widget
, "activate-link",
582 G_CALLBACK (proxy_preferences_activate_link_cb
),
585 extension_name
= E_SOURCE_EXTENSION_PROXY
;
586 list
= e_source_registry_list_sources (registry
, extension_name
);
587 show_advanced
= (g_list_length (list
) > 1);
588 g_list_free_full (list
, (GDestroyNotify
) g_object_unref
);
590 /* Switch to advanced mode if there are multiple proxy profiles. */
591 e_proxy_preferences_set_show_advanced (preferences
, show_advanced
);
595 e_proxy_preferences_class_init (EProxyPreferencesClass
*class)
597 GObjectClass
*object_class
;
599 g_type_class_add_private (class, sizeof (EProxyPreferencesPrivate
));
601 object_class
= G_OBJECT_CLASS (class);
602 object_class
->set_property
= proxy_preferences_set_property
;
603 object_class
->get_property
= proxy_preferences_get_property
;
604 object_class
->dispose
= proxy_preferences_dispose
;
605 object_class
->finalize
= proxy_preferences_finalize
;
606 object_class
->constructed
= proxy_preferences_constructed
;
608 g_object_class_install_property (
611 g_param_spec_object (
614 "Data source registry",
615 E_TYPE_SOURCE_REGISTRY
,
617 G_PARAM_CONSTRUCT_ONLY
|
618 G_PARAM_STATIC_STRINGS
));
620 g_object_class_install_property (
623 g_param_spec_boolean (
626 "Show advanced proxy preferences",
629 G_PARAM_STATIC_STRINGS
));
633 e_proxy_preferences_init (EProxyPreferences
*preferences
)
635 GHashTable
*commit_sources
;
637 /* Keys are UIDs, values are allocated GWeakRefs. */
638 commit_sources
= g_hash_table_new_full (
639 (GHashFunc
) g_str_hash
,
640 (GEqualFunc
) g_str_equal
,
641 (GDestroyNotify
) g_free
,
642 (GDestroyNotify
) e_weak_ref_free
);
644 preferences
->priv
= E_PROXY_PREFERENCES_GET_PRIVATE (preferences
);
646 g_mutex_init (&preferences
->priv
->commit_lock
);
647 preferences
->priv
->commit_sources
= commit_sources
;
651 * e_proxy_preferences_new:
652 * @registry: an #ESourceRegistry
654 * Creates a new #EProxyPreferences widget using #ESource instances in
657 * Returns: a new #EProxyPreferences
660 e_proxy_preferences_new (ESourceRegistry
*registry
)
662 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry
), NULL
);
664 return g_object_new (
665 E_TYPE_PROXY_PREFERENCES
,
666 "registry", registry
, NULL
);
670 * e_proxy_preferences_submit:
671 * @preferences: an #EProxyPreferences
673 * Writes the displayed proxy profile details to the #ESource being edited,
674 * and submits the changes to the registry service asynchronously.
676 * Normally changes are submitted to the registry service automatically
677 * after a brief delay, but changes may sometimes need to be submitted
678 * explicitly such as when the top-level window is closing.
681 e_proxy_preferences_submit (EProxyPreferences
*preferences
)
683 EProxyEditor
*proxy_editor
;
686 g_return_if_fail (E_IS_PROXY_PREFERENCES (preferences
));
688 proxy_editor
= E_PROXY_EDITOR (preferences
->priv
->proxy_editor
);
690 /* Save user changes to the proxy source. */
691 e_proxy_editor_save (proxy_editor
);
693 /* This part normally happens from a "source-changed"
694 * signal handler, but we can't wait for that here. */
695 source
= e_proxy_editor_ref_source (proxy_editor
);
696 proxy_preferences_commit_stash (preferences
, source
, FALSE
);
697 g_object_unref (source
);
699 /* Commit any pending changes immediately. */
700 proxy_preferences_commit_changes (preferences
);
704 * e_proxy_preferences_get_registry:
705 * @preferences: an #EProxyPreferences
707 * Returns the #ESourceRegistry passed to e_proxy_preferences_new().
709 * Returns: an #ESourceRegistry
712 e_proxy_preferences_get_registry (EProxyPreferences
*preferences
)
714 g_return_val_if_fail (E_IS_PROXY_PREFERENCES (preferences
), NULL
);
716 return preferences
->priv
->registry
;
720 * e_proxy_preferences_get_show_advanced:
721 * @preferences: an #EProxyPreferences
723 * Returns whether @preferences is currently in advanced mode.
725 * Returns: whether advanced proxy preferences are visible
728 e_proxy_preferences_get_show_advanced (EProxyPreferences
*preferences
)
730 g_return_val_if_fail (E_IS_PROXY_PREFERENCES (preferences
), FALSE
);
732 return preferences
->priv
->show_advanced
;
736 * e_proxy_preferences_set_show_advanced:
737 * @preferences: an #EProxyPreferences
738 * @show_advanced: whether to show advanced proxy preferences
740 * Switches @preferences to advanced mode if @show_advanced is %TRUE,
741 * or to basic mode if @show_advanced is %FALSE.
744 e_proxy_preferences_set_show_advanced (EProxyPreferences
*preferences
,
745 gboolean show_advanced
)
747 g_return_if_fail (E_IS_PROXY_PREFERENCES (preferences
));
749 if (show_advanced
== preferences
->priv
->show_advanced
)
752 preferences
->priv
->show_advanced
= show_advanced
;
754 g_object_notify (G_OBJECT (preferences
), "show-advanced");