/* * e-proxy-editor.c * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . * */ /** * SECTION: e-proxy-editor * @include: e-util/e-util.h * @short_description: Edit proxy profile details * * #EProxyEditor is an editing widget for proxy profiles, as described by * #ESource instances with an #ESourceProxy extension. * * The editor defaults to showing the built-in proxy profile returned by * e_source_registry_ref_builtin_proxy(), but that can be overridden with * e_proxy_editor_set_source(). **/ #include "e-proxy-editor.h" #include #include #define E_PROXY_EDITOR_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_PROXY_EDITOR, EProxyEditorPrivate)) struct _EProxyEditorPrivate { ESourceRegistry *registry; ESource *source; /* The widgets are not referenced. */ GtkWidget *method_combo_box; GtkWidget *http_host_entry; GtkWidget *http_port_spin_button; GtkWidget *https_host_entry; GtkWidget *https_port_spin_button; GtkWidget *socks_host_entry; GtkWidget *socks_port_spin_button; GtkWidget *ignore_hosts_entry; GtkWidget *autoconfig_url_entry; gchar *gcc_program_path; }; enum { PROP_0, PROP_REGISTRY, PROP_SOURCE }; G_DEFINE_TYPE ( EProxyEditor, e_proxy_editor, GTK_TYPE_GRID) static void proxy_editor_load (EProxyEditor *editor) { ESource *source; ESourceProxy *extension; GEnumClass *enum_class; GEnumValue *enum_value; EProxyMethod method; const gchar *extension_name; gchar *autoconfig_url; gchar *joined_hosts; gchar **ignore_hosts; gchar *host; guint16 port; source = e_proxy_editor_ref_source (editor); g_return_if_fail (source != NULL); extension_name = E_SOURCE_EXTENSION_PROXY; extension = e_source_get_extension (source, extension_name); enum_class = g_type_class_ref (E_TYPE_PROXY_METHOD); method = e_source_proxy_get_method (extension); enum_value = g_enum_get_value (enum_class, method); if (enum_value != NULL) gtk_combo_box_set_active_id ( GTK_COMBO_BOX (editor->priv->method_combo_box), enum_value->value_nick); g_type_class_unref (enum_class); autoconfig_url = e_source_proxy_dup_autoconfig_url (extension); gtk_entry_set_text ( GTK_ENTRY (editor->priv->autoconfig_url_entry), (autoconfig_url != NULL) ? autoconfig_url : ""); g_free (autoconfig_url); ignore_hosts = e_source_proxy_dup_ignore_hosts (extension); if (ignore_hosts != NULL) joined_hosts = g_strjoinv (", ", ignore_hosts); else joined_hosts = NULL; gtk_entry_set_text ( GTK_ENTRY (editor->priv->ignore_hosts_entry), (joined_hosts != NULL) ? joined_hosts : ""); g_strfreev (ignore_hosts); g_free (joined_hosts); host = e_source_proxy_dup_http_host (extension); gtk_entry_set_text ( GTK_ENTRY (editor->priv->http_host_entry), (host != NULL) ? host : ""); g_free (host); port = e_source_proxy_get_http_port (extension); gtk_spin_button_set_value ( GTK_SPIN_BUTTON (editor->priv->http_port_spin_button), (gdouble) port); host = e_source_proxy_dup_https_host (extension); gtk_entry_set_text ( GTK_ENTRY (editor->priv->https_host_entry), (host != NULL) ? host : ""); g_free (host); port = e_source_proxy_get_https_port (extension); gtk_spin_button_set_value ( GTK_SPIN_BUTTON (editor->priv->https_port_spin_button), (gdouble) port); g_object_unref (source); } static void proxy_editor_combo_box_changed_cb (GtkComboBox *widget, EProxyEditor *editor) { e_proxy_editor_save (editor); } static gboolean proxy_editor_focus_out_event_cb (GtkWidget *widget, GdkEvent *event, EProxyEditor *editor) { e_proxy_editor_save (editor); return FALSE; /* propagate the event */ } static void proxy_editor_open_desktop_settings_cb (GtkButton *button, EProxyEditor *editor) { gchar *command_line; GError *local_error = NULL; g_return_if_fail (editor->priv->gcc_program_path != NULL); command_line = g_strdup_printf ( "%s network", editor->priv->gcc_program_path); g_spawn_command_line_async (command_line, &local_error); g_free (command_line); if (local_error != NULL) { g_warning ("%s: %s", G_STRFUNC, local_error->message); g_error_free (local_error); } } static gboolean proxy_editor_active_id_to_visible (GBinding *binding, const GValue *source_value, GValue *target_value, gpointer user_data) { const gchar *value_nick = user_data; const gchar *active_id; gboolean visible; active_id = g_value_get_string (source_value); visible = (g_strcmp0 (active_id, value_nick) == 0); g_value_set_boolean (target_value, visible); return TRUE; } static void proxy_editor_set_registry (EProxyEditor *editor, ESourceRegistry *registry) { g_return_if_fail (E_IS_SOURCE_REGISTRY (registry)); g_return_if_fail (editor->priv->registry == NULL); editor->priv->registry = g_object_ref (registry); } static void proxy_editor_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_REGISTRY: proxy_editor_set_registry ( E_PROXY_EDITOR (object), g_value_get_object (value)); return; case PROP_SOURCE: e_proxy_editor_set_source ( E_PROXY_EDITOR (object), g_value_get_object (value)); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void proxy_editor_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_REGISTRY: g_value_set_object ( value, e_proxy_editor_get_registry ( E_PROXY_EDITOR (object))); return; case PROP_SOURCE: g_value_take_object ( value, e_proxy_editor_ref_source ( E_PROXY_EDITOR (object))); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void proxy_editor_dispose (GObject *object) { EProxyEditorPrivate *priv; priv = E_PROXY_EDITOR_GET_PRIVATE (object); if (priv->source != NULL) e_proxy_editor_save (E_PROXY_EDITOR (object)); g_clear_object (&priv->registry); g_clear_object (&priv->source); /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_proxy_editor_parent_class)->dispose (object); } static void proxy_editor_finalize (GObject *object) { EProxyEditorPrivate *priv; priv = E_PROXY_EDITOR_GET_PRIVATE (object); g_free (priv->gcc_program_path); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (e_proxy_editor_parent_class)->finalize (object); } static void proxy_editor_constructed (GObject *object) { EProxyEditor *editor; ESourceRegistry *registry; GtkLabel *label; GtkWidget *widget; GtkWidget *container; GtkSizeGroup *size_group; GEnumClass *enum_class; GEnumValue *enum_value; gint row = 0; /* Chain up to parent's constructed() method. */ G_OBJECT_CLASS (e_proxy_editor_parent_class)->constructed (object); editor = E_PROXY_EDITOR (object); registry = e_proxy_editor_get_registry (editor); enum_class = g_type_class_ref (E_TYPE_PROXY_METHOD); /* Default to the built-in proxy profile source. */ editor->priv->source = e_source_registry_ref_builtin_proxy (registry); gtk_grid_set_row_spacing (GTK_GRID (editor), 6); gtk_grid_set_column_spacing (GTK_GRID (editor), 6); /* This keeps all (visible) mnemonic labels the same width. */ size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); gtk_size_group_set_ignore_hidden (size_group, TRUE); widget = gtk_label_new_with_mnemonic (_("_Method:")); gtk_size_group_add_widget (size_group, widget); gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5); gtk_grid_attach (GTK_GRID (editor), widget, 0, 0, 1, 1); gtk_widget_show (widget); label = GTK_LABEL (widget); widget = gtk_combo_box_text_new (); gtk_widget_set_hexpand (widget, TRUE); gtk_label_set_mnemonic_widget (label, widget); gtk_grid_attach (GTK_GRID (editor), widget, 1, 0, 1, 1); editor->priv->method_combo_box = widget; /* do not reference */ gtk_widget_show (widget); /*** Defer to Desktop Settings ***/ enum_value = g_enum_get_value (enum_class, E_PROXY_METHOD_DEFAULT); g_return_if_fail (enum_value != NULL); gtk_combo_box_text_append ( GTK_COMBO_BOX_TEXT (editor->priv->method_combo_box), enum_value->value_nick, _("Defer to Desktop Settings")); if (editor->priv->gcc_program_path != NULL) { widget = gtk_button_new_with_mnemonic ( _("_Open Desktop Settings")); gtk_widget_set_halign (widget, GTK_ALIGN_START); gtk_grid_attach (GTK_GRID (editor), widget, 1, ++row, 2, 1); g_signal_connect ( widget, "clicked", G_CALLBACK (proxy_editor_open_desktop_settings_cb), editor); g_object_bind_property_full ( editor->priv->method_combo_box, "active-id", widget, "visible", G_BINDING_DEFAULT, proxy_editor_active_id_to_visible, NULL, (gpointer) enum_value->value_nick, (GDestroyNotify) NULL); } /*** Manual ***/ enum_value = g_enum_get_value (enum_class, E_PROXY_METHOD_MANUAL); g_return_if_fail (enum_value != NULL); gtk_combo_box_text_append ( GTK_COMBO_BOX_TEXT (editor->priv->method_combo_box), enum_value->value_nick, _("Manual")); widget = gtk_grid_new (); gtk_widget_set_valign (widget, GTK_ALIGN_START); gtk_grid_set_row_spacing (GTK_GRID (widget), 6); gtk_grid_set_column_spacing (GTK_GRID (widget), 6); gtk_grid_attach (GTK_GRID (editor), widget, 0, ++row, 2, 1); g_object_bind_property_full ( editor->priv->method_combo_box, "active-id", widget, "visible", G_BINDING_DEFAULT, proxy_editor_active_id_to_visible, NULL, (gpointer) enum_value->value_nick, (GDestroyNotify) NULL); container = widget; widget = gtk_label_new_with_mnemonic (_("_HTTP Proxy:")); gtk_size_group_add_widget (size_group, widget); gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5); gtk_grid_attach (GTK_GRID (container), widget, 0, 0, 1, 1); gtk_widget_show (widget); label = GTK_LABEL (widget); widget = gtk_entry_new (); gtk_widget_set_hexpand (widget, TRUE); gtk_label_set_mnemonic_widget (label, widget); gtk_grid_attach (GTK_GRID (container), widget, 1, 0, 1, 1); editor->priv->http_host_entry = widget; /* do not reference */ gtk_widget_show (widget); g_signal_connect ( widget, "focus-out-event", G_CALLBACK (proxy_editor_focus_out_event_cb), editor); widget = gtk_spin_button_new_with_range (0, G_MAXUINT16, 1); gtk_spin_button_set_update_policy ( GTK_SPIN_BUTTON (widget), GTK_UPDATE_IF_VALID); gtk_widget_set_size_request (widget, 100, -1); gtk_grid_attach (GTK_GRID (container), widget, 2, 0, 1, 1); editor->priv->http_port_spin_button = widget; /* do not reference */ gtk_widget_show (widget); g_signal_connect ( widget, "focus-out-event", G_CALLBACK (proxy_editor_focus_out_event_cb), editor); widget = gtk_label_new_with_mnemonic (_("H_TTPS Proxy:")); gtk_size_group_add_widget (size_group, widget); gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5); gtk_grid_attach (GTK_GRID (container), widget, 0, 1, 1, 1); gtk_widget_show (widget); label = GTK_LABEL (widget); widget = gtk_entry_new (); gtk_widget_set_hexpand (widget, TRUE); gtk_label_set_mnemonic_widget (label, widget); gtk_grid_attach (GTK_GRID (container), widget, 1, 1, 1, 1); editor->priv->https_host_entry = widget; /* do not reference */ gtk_widget_show (widget); g_signal_connect ( widget, "focus-out-event", G_CALLBACK (proxy_editor_focus_out_event_cb), editor); widget = gtk_spin_button_new_with_range (0, G_MAXUINT16, 1); gtk_spin_button_set_update_policy ( GTK_SPIN_BUTTON (widget), GTK_UPDATE_IF_VALID); gtk_widget_set_size_request (widget, 100, -1); gtk_grid_attach (GTK_GRID (container), widget, 2, 1, 1, 1); editor->priv->https_port_spin_button = widget; /* do not reference */ gtk_widget_show (widget); g_signal_connect ( widget, "focus-out-event", G_CALLBACK (proxy_editor_focus_out_event_cb), editor); widget = gtk_label_new_with_mnemonic (_("_Socks Proxy:")); gtk_size_group_add_widget (size_group, widget); gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5); gtk_grid_attach (GTK_GRID (container), widget, 0, 2, 1, 1); gtk_widget_show (widget); label = GTK_LABEL (widget); widget = gtk_entry_new (); gtk_widget_set_hexpand (widget, TRUE); gtk_label_set_mnemonic_widget (label, widget); gtk_grid_attach (GTK_GRID (container), widget, 1, 2, 1, 1); editor->priv->socks_host_entry = widget; /* do not reference */ gtk_widget_show (widget); g_signal_connect ( widget, "focus-out-event", G_CALLBACK (proxy_editor_focus_out_event_cb), editor); widget = gtk_spin_button_new_with_range (0, G_MAXUINT16, 1); gtk_spin_button_set_update_policy ( GTK_SPIN_BUTTON (widget), GTK_UPDATE_IF_VALID); gtk_widget_set_size_request (widget, 100, -1); gtk_grid_attach (GTK_GRID (container), widget, 2, 2, 1, 1); editor->priv->socks_port_spin_button = widget; gtk_widget_show (widget); g_signal_connect ( widget, "focus-out-event", G_CALLBACK (proxy_editor_focus_out_event_cb), editor); widget = gtk_label_new_with_mnemonic (_("_Ignore Hosts:")); gtk_size_group_add_widget (size_group, widget); gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5); gtk_grid_attach (GTK_GRID (container), widget, 0, 3, 1, 1); gtk_widget_show (widget); label = GTK_LABEL (widget); widget = gtk_entry_new (); gtk_widget_set_hexpand (widget, TRUE); gtk_label_set_mnemonic_widget (label, widget); gtk_grid_attach (GTK_GRID (container), widget, 1, 3, 2, 1); editor->priv->ignore_hosts_entry = widget; /* do not reference */ gtk_widget_show (widget); g_signal_connect ( widget, "focus-out-event", G_CALLBACK (proxy_editor_focus_out_event_cb), editor); /*** Automatic ***/ enum_value = g_enum_get_value (enum_class, E_PROXY_METHOD_AUTO); g_return_if_fail (enum_value != NULL); gtk_combo_box_text_append ( GTK_COMBO_BOX_TEXT (editor->priv->method_combo_box), enum_value->value_nick, _("Automatic")); widget = gtk_grid_new (); gtk_widget_set_valign (widget, GTK_ALIGN_START); gtk_grid_set_row_spacing (GTK_GRID (widget), 6); gtk_grid_set_column_spacing (GTK_GRID (widget), 6); gtk_grid_attach (GTK_GRID (editor), widget, 0, ++row, 2, 1); g_object_bind_property_full ( editor->priv->method_combo_box, "active-id", widget, "visible", G_BINDING_DEFAULT, proxy_editor_active_id_to_visible, NULL, (gpointer) enum_value->value_nick, (GDestroyNotify) NULL); container = widget; widget = gtk_label_new_with_mnemonic (_("Configuration _URL:")); gtk_size_group_add_widget (size_group, widget); gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5); gtk_grid_attach (GTK_GRID (container), widget, 0, 0, 1, 1); gtk_widget_show (widget); label = GTK_LABEL (widget); widget = gtk_entry_new (); gtk_widget_set_hexpand (widget, TRUE); gtk_label_set_mnemonic_widget (label, widget); gtk_grid_attach (GTK_GRID (container), widget, 1, 0, 1, 1); editor->priv->autoconfig_url_entry = widget; /* do not reference */ gtk_widget_show (widget); g_signal_connect ( widget, "focus-out-event", G_CALLBACK (proxy_editor_focus_out_event_cb), editor); /*** None ***/ enum_value = g_enum_get_value (enum_class, E_PROXY_METHOD_NONE); g_return_if_fail (enum_value != NULL); gtk_combo_box_text_append ( GTK_COMBO_BOX_TEXT (editor->priv->method_combo_box), enum_value->value_nick, _("None")); widget = gtk_label_new ( _("Use a direct connection, no proxying required.")); gtk_widget_set_hexpand (widget, TRUE); gtk_widget_set_halign (widget, GTK_ALIGN_FILL); gtk_grid_attach (GTK_GRID (editor), widget, 1, ++row, 2, 1); gtk_widget_show (widget); g_object_bind_property_full ( editor->priv->method_combo_box, "active-id", widget, "visible", G_BINDING_DEFAULT, proxy_editor_active_id_to_visible, NULL, (gpointer) enum_value->value_nick, (GDestroyNotify) NULL); g_object_unref (size_group); g_type_class_unref (enum_class); /* Populate the widgets. */ proxy_editor_load (editor); /* Connect to this signal after the initial load. */ g_signal_connect ( editor->priv->method_combo_box, "changed", G_CALLBACK (proxy_editor_combo_box_changed_cb), editor); } static void e_proxy_editor_class_init (EProxyEditorClass *class) { GObjectClass *object_class; g_type_class_add_private (class, sizeof (EProxyEditorPrivate)); object_class = G_OBJECT_CLASS (class); object_class->set_property = proxy_editor_set_property; object_class->get_property = proxy_editor_get_property; object_class->dispose = proxy_editor_dispose; object_class->finalize = proxy_editor_finalize; object_class->constructed = proxy_editor_constructed; g_object_class_install_property ( object_class, PROP_REGISTRY, g_param_spec_object ( "registry", "Registry", "Data source registry", E_TYPE_SOURCE_REGISTRY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property ( object_class, PROP_SOURCE, g_param_spec_object ( "source", "Source", "The data source being edited", E_TYPE_SOURCE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } static void e_proxy_editor_init (EProxyEditor *editor) { editor->priv = E_PROXY_EDITOR_GET_PRIVATE (editor); editor->priv->gcc_program_path = g_find_program_in_path ("gnome-control-center"); } /** * e_proxy_editor_new: * @registry: an #ESourceRegistry * * Creates a new #EProxyEditor widget, initially showing details of the * built-in proxy profile returned by e_source_registry_ref_builtin_proxy(). * * Returns: a new #EProxyEditor **/ GtkWidget * e_proxy_editor_new (ESourceRegistry *registry) { g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); return g_object_new ( E_TYPE_PROXY_EDITOR, "registry", registry, NULL); } /** * e_proxy_editor_save: * @editor: an #EProxyEditor * * Writes the proxy settings displayed in the @editor to the #ESource * being edited. * * This function is called automatically when the editing widgets lose input * focus, but it may sometimes need to be called explicitly such as when the * top-level window is closing. **/ void e_proxy_editor_save (EProxyEditor *editor) { ESource *source; ESourceProxy *extension; GEnumClass *enum_class; GEnumValue *enum_value; const gchar *active_id; const gchar *entry_text; const gchar *extension_name; gint spin_button_value; gchar **strv; g_return_if_fail (E_IS_PROXY_EDITOR (editor)); source = e_proxy_editor_ref_source (editor); g_return_if_fail (source != NULL); extension_name = E_SOURCE_EXTENSION_PROXY; extension = e_source_get_extension (source, extension_name); enum_class = g_type_class_ref (E_TYPE_PROXY_METHOD); active_id = gtk_combo_box_get_active_id ( GTK_COMBO_BOX (editor->priv->method_combo_box)); enum_value = g_enum_get_value_by_nick (enum_class, active_id); if (enum_value != NULL) e_source_proxy_set_method (extension, enum_value->value); g_type_class_unref (enum_class); entry_text = gtk_entry_get_text ( GTK_ENTRY (editor->priv->autoconfig_url_entry)); if (entry_text != NULL && *entry_text == '\0') entry_text = NULL; e_source_proxy_set_autoconfig_url (extension, entry_text); entry_text = gtk_entry_get_text ( GTK_ENTRY (editor->priv->ignore_hosts_entry)); strv = g_strsplit (entry_text, ",", -1); if (strv != NULL) { guint length, ii; /* Strip leading and trailing whitespace from each element * to avoid a changed notification storm between the entry * (which adds a space after each comma) and ESourceProxy. */ length = g_strv_length (strv); for (ii = 0; ii < length; ii++) g_strstrip (strv[ii]); } e_source_proxy_set_ignore_hosts ( extension, (const gchar * const *) strv); g_strfreev (strv); entry_text = gtk_entry_get_text ( GTK_ENTRY (editor->priv->http_host_entry)); if (entry_text != NULL && *entry_text == '\0') entry_text = NULL; e_source_proxy_set_http_host (extension, entry_text); spin_button_value = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON (editor->priv->http_port_spin_button)); e_source_proxy_set_http_port (extension, (guint16) spin_button_value); entry_text = gtk_entry_get_text ( GTK_ENTRY (editor->priv->https_host_entry)); if (entry_text != NULL && *entry_text == '\0') entry_text = NULL; e_source_proxy_set_https_host (extension, entry_text); spin_button_value = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON (editor->priv->https_port_spin_button)); e_source_proxy_set_https_port (extension, (guint16) spin_button_value); entry_text = gtk_entry_get_text ( GTK_ENTRY (editor->priv->socks_host_entry)); if (entry_text != NULL && *entry_text == '\0') entry_text = NULL; e_source_proxy_set_socks_host (extension, entry_text); spin_button_value = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON (editor->priv->socks_port_spin_button)); e_source_proxy_set_socks_port (extension, (guint16) spin_button_value); g_object_unref (source); } /** * e_proxy_editor_get_registry: * @editor: an #EProxyEditor * * Returns the #ESourceRegistry passed to e_proxy_editor_get_registry(). * * Returns: an #ESourceRegistry **/ ESourceRegistry * e_proxy_editor_get_registry (EProxyEditor *editor) { g_return_val_if_fail (E_IS_PROXY_EDITOR (editor), NULL); return editor->priv->registry; } /** * e_proxy_editor_ref_source: * @editor: an #EProxyEditor * * Returns the network proxy profile #ESource being edited. * * The returned #ESource is referenced for thread-safety and must be * unreferenced with g_object_unref() when finished with it. * * Returns: an #ESource **/ ESource * e_proxy_editor_ref_source (EProxyEditor *editor) { g_return_val_if_fail (E_IS_PROXY_EDITOR (editor), NULL); return g_object_ref (editor->priv->source); } /** * e_proxy_editor_set_source: * @editor: an #EProxyEditor * @source: an #ESource * * Sets the network proxy profile #ESource to edit. * * This first writes the displayed proxy settings to the previous #ESource, * then displays the proxy details for @source. If @source is already being * edited then nothing happens. **/ void e_proxy_editor_set_source (EProxyEditor *editor, ESource *source) { g_return_if_fail (E_IS_PROXY_EDITOR (editor)); g_return_if_fail (E_IS_SOURCE (source)); if (e_source_equal (source, editor->priv->source)) return; e_proxy_editor_save (editor); g_clear_object (&editor->priv->source); editor->priv->source = g_object_ref (source); proxy_editor_load (editor); g_object_notify (G_OBJECT (editor), "source"); }