/* * e-mail-config-provider-page.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; either * version 2 of the License, or (at your option) version 3. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with the program; if not, see * */ #include "e-mail-config-provider-page.h" #include #include #include #include #define E_MAIL_CONFIG_PROVIDER_PAGE_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_MAIL_CONFIG_PROVIDER_PAGE, EMailConfigProviderPagePrivate)) #define STANDARD_MARGIN 12 #define DEPENDENCY_MARGIN 24 struct _EMailConfigProviderPagePrivate { EMailConfigServiceBackend *backend; gboolean is_empty; }; enum { PROP_0, PROP_BACKEND }; /* Forward Declarations */ static void e_mail_config_provider_page_interface_init (EMailConfigPageInterface *interface); G_DEFINE_TYPE_WITH_CODE ( EMailConfigProviderPage, e_mail_config_provider_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_provider_page_interface_init)) static void mail_config_provider_page_handle_dependency (EMailConfigProviderPage *page, CamelProviderConfEntry *entry, GtkWidget *widget) { EMailConfigServiceBackend *backend; CamelSettings *settings; GBindingFlags binding_flags = G_BINDING_SYNC_CREATE; const gchar *depname = entry->depname; gint margin; if (depname == NULL) return; if (*depname == '!') { binding_flags |= G_BINDING_INVERT_BOOLEAN; depname++; } backend = e_mail_config_provider_page_get_backend (page); settings = e_mail_config_service_backend_get_settings (backend); g_object_bind_property ( settings, depname, widget, "sensitive", binding_flags); /* Further indent the widget to show its dependency. */ margin = gtk_widget_get_margin_left (widget); gtk_widget_set_margin_left (widget, margin + DEPENDENCY_MARGIN); } static void mail_config_provider_page_add_section (EMailConfigProviderPage *page, CamelProviderConfEntry *entry) { EMailConfigServiceBackend *backend; CamelProvider *provider; GtkWidget *widget; gchar *markup; g_return_if_fail (entry->text != NULL); backend = e_mail_config_provider_page_get_backend (page); provider = e_mail_config_service_backend_get_provider (backend); markup = g_markup_printf_escaped ("%s", entry->text); widget = gtk_label_new (markup); gtk_label_set_use_markup (GTK_LABEL (widget), TRUE); gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5); gtk_box_pack_start (GTK_BOX (page), widget, FALSE, FALSE, 0); gtk_widget_show (widget); /* Skip the top margin if this is the first entry. */ if (entry != provider->extra_conf) gtk_widget_set_margin_top (widget, 6); g_free (markup); } static void mail_config_provider_page_add_checkbox (EMailConfigProviderPage *page, CamelProviderConfEntry *entry) { EMailConfigServiceBackend *backend; CamelSettings *settings; GtkWidget *widget; g_return_if_fail (entry->text != NULL); backend = e_mail_config_provider_page_get_backend (page); settings = e_mail_config_service_backend_get_settings (backend); widget = gtk_check_button_new_with_mnemonic (entry->text); gtk_widget_set_margin_left (widget, STANDARD_MARGIN); gtk_box_pack_start (GTK_BOX (page), widget, FALSE, FALSE, 0); gtk_widget_show (widget); g_object_bind_property ( settings, entry->name, widget, "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); mail_config_provider_page_handle_dependency (page, entry, widget); } static void mail_config_provider_page_add_checkspin (EMailConfigProviderPage *page, CamelProviderConfEntry *entry) { EMailConfigServiceBackend *backend; CamelSettings *settings; GObjectClass *class; GParamSpec *pspec; GParamSpec *use_pspec; GtkAdjustment *adjustment; GtkWidget *hbox, *spin; GtkWidget *prefix; gchar *use_property_name; gchar *pre, *post; g_return_if_fail (entry->text != NULL); backend = e_mail_config_provider_page_get_backend (page); settings = e_mail_config_service_backend_get_settings (backend); /* The entry->name property (e.g. "foo") should be numeric for the * spin button. If a "use" boolean property exists (e.g. "use-foo") * then a checkbox is also shown. */ class = G_OBJECT_GET_CLASS (settings); pspec = g_object_class_find_property (class, entry->name); g_return_if_fail (pspec != NULL); use_property_name = g_strconcat ("use-", entry->name, NULL); use_pspec = g_object_class_find_property (class, use_property_name); if (use_pspec != NULL && use_pspec->value_type != G_TYPE_BOOLEAN) use_pspec = NULL; g_free (use_property_name); /* Make sure we can convert to and from doubles. */ g_return_if_fail ( g_value_type_transformable ( pspec->value_type, G_TYPE_DOUBLE)); g_return_if_fail ( g_value_type_transformable ( G_TYPE_DOUBLE, pspec->value_type)); if (G_IS_PARAM_SPEC_CHAR (pspec)) { GParamSpecChar *pspec_char; pspec_char = G_PARAM_SPEC_CHAR (pspec); adjustment = gtk_adjustment_new ( (gdouble) pspec_char->default_value, (gdouble) pspec_char->minimum, (gdouble) pspec_char->maximum, 1.0, 1.0, 0.0); } else if (G_IS_PARAM_SPEC_UCHAR (pspec)) { GParamSpecUChar *pspec_uchar; pspec_uchar = G_PARAM_SPEC_UCHAR (pspec); adjustment = gtk_adjustment_new ( (gdouble) pspec_uchar->default_value, (gdouble) pspec_uchar->minimum, (gdouble) pspec_uchar->maximum, 1.0, 1.0, 0.0); } else if (G_IS_PARAM_SPEC_INT (pspec)) { GParamSpecInt *pspec_int; pspec_int = G_PARAM_SPEC_INT (pspec); adjustment = gtk_adjustment_new ( (gdouble) pspec_int->default_value, (gdouble) pspec_int->minimum, (gdouble) pspec_int->maximum, 1.0, 1.0, 0.0); } else if (G_IS_PARAM_SPEC_UINT (pspec)) { GParamSpecUInt *pspec_uint; pspec_uint = G_PARAM_SPEC_UINT (pspec); adjustment = gtk_adjustment_new ( (gdouble) pspec_uint->default_value, (gdouble) pspec_uint->minimum, (gdouble) pspec_uint->maximum, 1.0, 1.0, 0.0); } else if (G_IS_PARAM_SPEC_LONG (pspec)) { GParamSpecLong *pspec_long; pspec_long = G_PARAM_SPEC_LONG (pspec); adjustment = gtk_adjustment_new ( (gdouble) pspec_long->default_value, (gdouble) pspec_long->minimum, (gdouble) pspec_long->maximum, 1.0, 1.0, 0.0); } else if (G_IS_PARAM_SPEC_ULONG (pspec)) { GParamSpecULong *pspec_ulong; pspec_ulong = G_PARAM_SPEC_ULONG (pspec); adjustment = gtk_adjustment_new ( (gdouble) pspec_ulong->default_value, (gdouble) pspec_ulong->minimum, (gdouble) pspec_ulong->maximum, 1.0, 1.0, 0.0); } else if (G_IS_PARAM_SPEC_FLOAT (pspec)) { GParamSpecFloat *pspec_float; pspec_float = G_PARAM_SPEC_FLOAT (pspec); adjustment = gtk_adjustment_new ( (gdouble) pspec_float->default_value, (gdouble) pspec_float->minimum, (gdouble) pspec_float->maximum, 1.0, 1.0, 0.0); } else if (G_IS_PARAM_SPEC_DOUBLE (pspec)) { GParamSpecDouble *pspec_double; pspec_double = G_PARAM_SPEC_DOUBLE (pspec); adjustment = gtk_adjustment_new ( (gdouble) pspec_double->default_value, (gdouble) pspec_double->minimum, (gdouble) pspec_double->maximum, 1.0, 1.0, 0.0); } else g_return_if_reached (); pre = g_alloca (strlen (entry->text) + 1); strcpy (pre, entry->text); post = strstr (pre, "%s"); if (post != NULL) { *post = '\0'; post += 2; } hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3); gtk_widget_set_margin_left (hbox, STANDARD_MARGIN); gtk_box_pack_start (GTK_BOX (page), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); if (use_pspec != NULL) { prefix = gtk_check_button_new_with_mnemonic (pre); g_object_bind_property ( settings, use_pspec->name, prefix, "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); } else { prefix = gtk_label_new_with_mnemonic (pre); } gtk_box_pack_start (GTK_BOX (hbox), prefix, FALSE, TRUE, 0); gtk_widget_show (prefix); spin = gtk_spin_button_new (adjustment, 1.0, 0); gtk_box_pack_start (GTK_BOX (hbox), spin, FALSE, TRUE, 0); gtk_widget_show (spin); g_object_bind_property ( settings, entry->name, spin, "value", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); if (use_pspec != NULL) g_object_bind_property ( prefix, "active", spin, "sensitive", G_BINDING_SYNC_CREATE); if (post != NULL) { GtkWidget *label = gtk_label_new_with_mnemonic (post); gtk_label_set_mnemonic_widget (GTK_LABEL (label), prefix); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0); gtk_widget_show (label); } mail_config_provider_page_handle_dependency (page, entry, hbox); } static void mail_config_provider_page_add_entry (EMailConfigProviderPage *page, CamelProviderConfEntry *entry) { EMailConfigServiceBackend *backend; CamelSettings *settings; GtkWidget *hbox; GtkWidget *input; GtkWidget *label; g_return_if_fail (entry->text != NULL); backend = e_mail_config_provider_page_get_backend (page); settings = e_mail_config_service_backend_get_settings (backend); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_set_spacing (GTK_BOX (hbox), 6); gtk_widget_set_margin_left (hbox, STANDARD_MARGIN); gtk_box_pack_start (GTK_BOX (page), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); label = gtk_label_new_with_mnemonic (entry->text); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_widget_show (label); input = gtk_entry_new (); gtk_label_set_mnemonic_widget (GTK_LABEL (label), input); gtk_box_pack_start (GTK_BOX (hbox), input, TRUE, TRUE, 0); gtk_widget_show (input); g_object_bind_property ( settings, entry->name, input, "text", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); g_object_bind_property ( input, "sensitive", label, "sensitive", G_BINDING_SYNC_CREATE); mail_config_provider_page_handle_dependency (page, entry, hbox); } static void mail_config_provider_page_add_options (EMailConfigProviderPage *page, CamelProviderConfEntry *entry) { EMailConfigServiceBackend *backend; CamelProvider *provider; CamelSettings *settings; GtkCellRenderer *renderer; GtkListStore *store; GtkWidget *hbox; GtkWidget *combo; GtkWidget *label; gchar **tokens; guint length, ii; /* The 'value' string is of the format: * * 'nick0:caption0:nick1:caption1:...nickN:captionN' * * where 'nick' is the nickname a GEnumValue and 'caption' * is the localized combo box item displayed to the user. */ g_return_if_fail (entry->text != NULL); g_return_if_fail (entry->value != NULL); backend = e_mail_config_provider_page_get_backend (page); provider = e_mail_config_service_backend_get_provider (backend); settings = e_mail_config_service_backend_get_settings (backend); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_set_spacing (GTK_BOX (hbox), 6); gtk_widget_set_margin_left (hbox, STANDARD_MARGIN); gtk_box_pack_start (GTK_BOX (page), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); label = gtk_label_new_with_mnemonic (entry->text); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_widget_show (label); /* 0: 'nick', 1: caption */ store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING); tokens = g_strsplit (entry->value, ":", -1); length = g_strv_length (tokens); /* Take the strings two at a time. */ for (ii = 0; ii + 1 < length; ii += 2) { GtkTreeIter iter; const gchar *nick; const gchar *caption; nick = tokens[ii + 0]; caption = tokens[ii + 1]; /* Localize the caption. */ caption = dgettext (provider->translation_domain, caption); gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, 0, nick, 1, caption, -1); } g_strfreev (tokens); combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store)); gtk_combo_box_set_id_column (GTK_COMBO_BOX (combo), 0); gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo); gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0); gtk_widget_show (combo); g_object_bind_property_full ( settings, entry->name, combo, "active-id", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, e_binding_transform_enum_value_to_nick, e_binding_transform_enum_nick_to_value, NULL, (GDestroyNotify) NULL); renderer = gtk_cell_renderer_text_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE); gtk_cell_layout_set_attributes ( GTK_CELL_LAYOUT (combo), renderer, "text", 1, NULL); mail_config_provider_page_handle_dependency (page, entry, hbox); } static void mail_config_provider_page_add_widgets (EMailConfigProviderPage *page) { EMailConfigServiceBackend *backend; CamelProviderConfEntry *entries; CamelProvider *provider; GtkWidget *container; GtkWidget *widget; ESource *source; ESourceExtension *extension; gboolean first_section = TRUE; const gchar *extension_name; const gchar *text; gchar *markup; gint ii; /* XXX We begin the page with our own section header and refresh * interval setting, and then skip the CamelProvider's first * CAMEL_PROVIDER_CONF_SECTION_START entry. * * This is all very brittle. I'm convinced that generating * a user interface from an array of records like this is a * bad idea. We already have EMailConfigServiceBackend for * building provider-specific "Receving Email" and "Sending * EMail" pages by hand. We should do similarly here. */ backend = e_mail_config_provider_page_get_backend (page); source = e_mail_config_service_backend_get_source (backend); provider = e_mail_config_service_backend_get_provider (backend); g_return_if_fail (provider != NULL); /* XXX I guess refresh options go in the mail account source, * even if the source is part of a collection. I did not * think about it too hard, so hopefully this is right. */ extension_name = E_SOURCE_EXTENSION_REFRESH; extension = e_source_get_extension (source, extension_name); text = _("Checking for New Mail"); markup = g_markup_printf_escaped ("%s", text); widget = gtk_label_new (markup); gtk_label_set_use_markup (GTK_LABEL (widget), TRUE); gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5); gtk_box_pack_start (GTK_BOX (page), widget, FALSE, FALSE, 0); gtk_widget_show (widget); g_free (markup); widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gtk_widget_set_margin_left (widget, STANDARD_MARGIN); gtk_box_pack_start (GTK_BOX (page), widget, FALSE, FALSE, 0); gtk_widget_show (widget); container = widget; text = _("Check for _new messages every"); widget = gtk_check_button_new_with_mnemonic (text); gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); gtk_widget_show (widget); g_object_bind_property ( extension, "enabled", widget, "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); widget = gtk_spin_button_new_with_range (1.0, 1440.0, 1.0); gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); gtk_widget_show (widget); g_object_bind_property ( extension, "enabled", widget, "sensitive", G_BINDING_SYNC_CREATE); g_object_bind_property ( extension, "interval-minutes", widget, "value", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); widget = gtk_label_new (_("minutes")); gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); gtk_widget_show (widget); /* Note the "text" member of each CamelProviderConfEntry is * already localized, so we can use it directly in widgets. */ entries = provider->extra_conf; /* Loop until we see CAMEL_PROVIDER_CONF_END. */ for (ii = 0; entries[ii].type != CAMEL_PROVIDER_CONF_END; ii++) { /* Skip entries with no name. */ if (entries[ii].name == NULL) continue; switch (entries[ii].type) { case CAMEL_PROVIDER_CONF_SECTION_START: /* Skip the first section start. */ if (first_section) { first_section = FALSE; continue; } mail_config_provider_page_add_section ( page, &entries[ii]); break; case CAMEL_PROVIDER_CONF_CHECKBOX: mail_config_provider_page_add_checkbox ( page, &entries[ii]); break; case CAMEL_PROVIDER_CONF_CHECKSPIN: mail_config_provider_page_add_checkspin ( page, &entries[ii]); break; case CAMEL_PROVIDER_CONF_ENTRY: mail_config_provider_page_add_entry ( page, &entries[ii]); break; case CAMEL_PROVIDER_CONF_OPTIONS: mail_config_provider_page_add_options ( page, &entries[ii]); break; default: break; /* skip it */ } } } static void mail_config_provider_page_set_backend (EMailConfigProviderPage *page, EMailConfigServiceBackend *backend) { g_return_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend)); g_return_if_fail (page->priv->backend == NULL); page->priv->backend = g_object_ref (backend); } static void mail_config_provider_page_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_BACKEND: mail_config_provider_page_set_backend ( E_MAIL_CONFIG_PROVIDER_PAGE (object), g_value_get_object (value)); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void mail_config_provider_page_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { case PROP_BACKEND: g_value_set_object ( value, e_mail_config_provider_page_get_backend ( E_MAIL_CONFIG_PROVIDER_PAGE (object))); return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void mail_config_provider_page_dispose (GObject *object) { EMailConfigProviderPagePrivate *priv; priv = E_MAIL_CONFIG_PROVIDER_PAGE_GET_PRIVATE (object); if (priv->backend != NULL) { g_object_unref (priv->backend); priv->backend = NULL; } /* Chain up parent's dispose() method. */ G_OBJECT_CLASS (e_mail_config_provider_page_parent_class)-> dispose (object); } static void mail_config_provider_page_constructed (GObject *object) { EMailConfigProviderPage *page; EMailConfigServiceBackend *backend; CamelProvider *provider; page = E_MAIL_CONFIG_PROVIDER_PAGE (object); /* Chain up parent's constructed() method. */ G_OBJECT_CLASS (e_mail_config_provider_page_parent_class)-> constructed (object); gtk_box_set_spacing (GTK_BOX (object), 6); gtk_orientable_set_orientation ( GTK_ORIENTABLE (object), GTK_ORIENTATION_VERTICAL); backend = e_mail_config_provider_page_get_backend (page); provider = e_mail_config_service_backend_get_provider (backend); if (provider != NULL && provider->extra_conf != NULL) mail_config_provider_page_add_widgets (page); else page->priv->is_empty = TRUE; e_extensible_load_extensions (E_EXTENSIBLE (page)); } static void e_mail_config_provider_page_class_init (EMailConfigProviderPageClass *class) { GObjectClass *object_class; g_type_class_add_private (class, sizeof (EMailConfigProviderPagePrivate)); object_class = G_OBJECT_CLASS (class); object_class->set_property = mail_config_provider_page_set_property; object_class->get_property = mail_config_provider_page_get_property; object_class->dispose = mail_config_provider_page_dispose; object_class->constructed = mail_config_provider_page_constructed; g_object_class_install_property ( object_class, PROP_BACKEND, g_param_spec_object ( "backend", "Backend", "Service backend to generate options from", E_TYPE_MAIL_CONFIG_SERVICE_BACKEND, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); } static void e_mail_config_provider_page_interface_init (EMailConfigPageInterface *interface) { interface->title = _("Receiving Options"); interface->sort_order = E_MAIL_CONFIG_PROVIDER_PAGE_SORT_ORDER; } static void e_mail_config_provider_page_init (EMailConfigProviderPage *page) { page->priv = E_MAIL_CONFIG_PROVIDER_PAGE_GET_PRIVATE (page); } EMailConfigPage * e_mail_config_provider_page_new (EMailConfigServiceBackend *backend) { g_return_val_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend), NULL); return g_object_new ( E_TYPE_MAIL_CONFIG_PROVIDER_PAGE, "backend", backend, NULL); } gboolean e_mail_config_provider_page_is_empty (EMailConfigProviderPage *page) { g_return_val_if_fail (E_IS_MAIL_CONFIG_PROVIDER_PAGE (page), TRUE); return page->priv->is_empty; } EMailConfigServiceBackend * e_mail_config_provider_page_get_backend (EMailConfigProviderPage *page) { g_return_val_if_fail (E_IS_MAIL_CONFIG_PROVIDER_PAGE (page), NULL); return page->priv->backend; }