From 07f943554d6d5f945fc9de59b2a14571537c51ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1ti?= Date: Fri, 18 Mar 2011 11:39:00 -0400 Subject: Bug 418954 - Add a separate entry combo for port numbers Adds a new EPortEntry widget which appears alongside "host" entry boxes so users don't have to know about the "host:port" syntax to specify a custom port number. Currently only used in the mail account editor, but we'll generalize it futher so it can be used everywhere. --- mail/em-account-editor.c | 142 ++++++++----- mail/mail-config.ui | 70 ++++++- widgets/misc/Makefile.am | 2 + widgets/misc/e-port-entry.c | 474 ++++++++++++++++++++++++++++++++++++++++++++ widgets/misc/e-port-entry.h | 82 ++++++++ 5 files changed, 718 insertions(+), 52 deletions(-) create mode 100644 widgets/misc/e-port-entry.c create mode 100644 widgets/misc/e-port-entry.h diff --git a/mail/em-account-editor.c b/mail/em-account-editor.c index 55ffb3ac57..90e634233f 100644 --- a/mail/em-account-editor.c +++ b/mail/em-account-editor.c @@ -56,6 +56,7 @@ #include "e-util/e-signature-utils.h" #include "e-util/e-util-private.h" #include "widgets/misc/e-signature-editor.h" +#include "widgets/misc/e-port-entry.h" #include "e-mail-local.h" #include "e-mail-session.h" @@ -106,6 +107,8 @@ typedef struct _EMAccountEditorService { GtkLabel *description; GtkLabel *hostlabel; GtkEntry *hostname; + GtkLabel *portlabel; + EPortEntry *port; GtkLabel *userlabel; GtkEntry *username; GtkEntry *path; @@ -1229,28 +1232,23 @@ smime_encrypt_key_clear (GtkWidget *w, EMAccountEditor *emae) #endif static void -emae_url_set_hostport (CamelURL *url, const gchar *txt) +emae_url_set_host (CamelURL *url, const gchar *txt) { - const gchar *port; gchar *host; - /* FIXME: what if this was a raw IPv6 address? */ - if (txt && (port = strchr (txt, ':'))) { - camel_url_set_port (url, atoi (port+1)); + if (txt && *txt) { host = g_strdup (txt); - host[port-txt] = 0; - } else { - /* "" is converted to NULL, but if we set NULL on the url, - camel_url_to_string strips lots of details */ - host = g_strdup ((txt?txt:"")); - camel_url_set_port (url, 0); - } - - g_strstrip (host); - if (txt && *txt) + g_strstrip (host); camel_url_set_host (url, host); + g_free (host); + } +} - g_free (host); +static void +emae_url_set_port (CamelURL *url, const gchar *port) +{ + if (port && *port) + camel_url_set_port (url, atoi (port)); } /* This is used to map a funciton which will set on the url a string value. @@ -1263,7 +1261,8 @@ struct _provider_host_info { }; static struct _provider_host_info emae_source_host_info[] = { - { CAMEL_URL_PART_HOST, emae_url_set_hostport, { G_STRUCT_OFFSET (EMAccountEditorService, hostname), G_STRUCT_OFFSET (EMAccountEditorService, hostlabel), }, }, + { CAMEL_URL_PART_HOST, emae_url_set_host, { G_STRUCT_OFFSET (EMAccountEditorService, hostname), G_STRUCT_OFFSET (EMAccountEditorService, hostlabel), }, }, + { CAMEL_URL_PART_HOST, emae_url_set_port, { G_STRUCT_OFFSET (EMAccountEditorService, port), G_STRUCT_OFFSET (EMAccountEditorService, portlabel), }, }, { CAMEL_URL_PART_USER, camel_url_set_user, { G_STRUCT_OFFSET (EMAccountEditorService, username), G_STRUCT_OFFSET (EMAccountEditorService, userlabel), } }, { CAMEL_URL_PART_PATH, camel_url_set_path, { G_STRUCT_OFFSET (EMAccountEditorService, path), G_STRUCT_OFFSET (EMAccountEditorService, pathlabel), G_STRUCT_OFFSET (EMAccountEditorService, pathentry) }, }, { CAMEL_URL_PART_AUTH, NULL, { 0, G_STRUCT_OFFSET (EMAccountEditorService, auth_frame), }, }, @@ -1271,7 +1270,8 @@ static struct _provider_host_info emae_source_host_info[] = { }; static struct _provider_host_info emae_transport_host_info[] = { - { CAMEL_URL_PART_HOST, emae_url_set_hostport, { G_STRUCT_OFFSET (EMAccountEditorService, hostname), G_STRUCT_OFFSET (EMAccountEditorService, hostlabel), }, }, + { CAMEL_URL_PART_HOST, emae_url_set_host, { G_STRUCT_OFFSET (EMAccountEditorService, hostname), G_STRUCT_OFFSET (EMAccountEditorService, hostlabel), }, }, + { CAMEL_URL_PART_HOST, emae_url_set_port, { G_STRUCT_OFFSET (EMAccountEditorService, port), G_STRUCT_OFFSET (EMAccountEditorService, portlabel), }, }, { CAMEL_URL_PART_USER, camel_url_set_user, { G_STRUCT_OFFSET (EMAccountEditorService, username), G_STRUCT_OFFSET (EMAccountEditorService, userlabel), } }, { CAMEL_URL_PART_AUTH, NULL, { 0, G_STRUCT_OFFSET (EMAccountEditorService, auth_frame), }, }, { 0 }, @@ -1292,6 +1292,8 @@ static struct _service_info { const gchar *description; const gchar *hostname; const gchar *hostlabel; + const gchar *port; + const gchar *portlabel; const gchar *username; const gchar *userlabel; const gchar *path; @@ -1325,6 +1327,8 @@ static struct _service_info { "source_description", "source_host", "source_host_label", + "source_port", + "source_port_label", "source_user", "source_user_label", "source_path", @@ -1356,6 +1360,8 @@ static struct _service_info { "transport_description", "transport_host", "transport_host_label", + "transport_port", + "transport_port_label", "transport_user", "transport_user_label", NULL, @@ -1400,16 +1406,23 @@ emae_uri_changed (EMAccountEditorService *service, CamelURL *url) } static void -emae_service_url_changed (EMAccountEditorService *service, void (*setval)(CamelURL *, const gchar *), GtkEntry *entry) +emae_service_url_changed (EMAccountEditorService *service, void (*setval)(CamelURL *, const gchar *), GtkWidget *entry) { GtkComboBox *dropdown; gint id; GtkTreeModel *model; GtkTreeIter iter; CamelServiceAuthType *authtype; + gchar *text; CamelURL *url = emae_account_url (service->emae, emae_service_info[service->type].account_uri_key); - gchar *text = g_strdup (gtk_entry_get_text (entry)); + + if (GTK_IS_ENTRY (entry)) + text = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry))); + else if (E_IS_PORT_ENTRY (entry)) { + text = g_strdup_printf ("%i", e_port_entry_get_port (E_PORT_ENTRY (entry))); + } else + return; g_strstrip (text); @@ -1471,13 +1484,19 @@ emae_service_url_path_changed (EMAccountEditorService *service, void (*setval)(C static void emae_hostname_changed (GtkEntry *entry, EMAccountEditorService *service) { - emae_service_url_changed (service, emae_url_set_hostport, entry); + emae_service_url_changed (service, emae_url_set_host, GTK_WIDGET (entry)); +} + +static void +emae_port_changed (EPortEntry *pentry, EMAccountEditorService *service) +{ + emae_service_url_changed (service, emae_url_set_port, GTK_WIDGET (pentry)); } static void emae_username_changed (GtkEntry *entry, EMAccountEditorService *service) { - emae_service_url_changed (service, camel_url_set_user, entry); + emae_service_url_changed (service, camel_url_set_user, GTK_WIDGET (entry)); } static void @@ -1502,7 +1521,11 @@ emae_ssl_update (EMAccountEditorService *service, CamelURL *url) gtk_tree_model_get (model, &iter, 1, &ssl, -1); if (!strcmp (ssl, "none")) ssl = NULL; + + e_port_entry_security_port_changed (service->port, ssl); + camel_url_set_param (url, "use_ssl", ssl); + camel_url_set_port (url, e_port_entry_get_port (service->port)); return 1; } @@ -1524,6 +1547,7 @@ emae_service_provider_changed (EMAccountEditorService *service) { EAccount *account; gint i, j; + gint old_port; void (*show)(GtkWidget *); CamelURL *url = emae_account_url (service->emae, emae_service_info[service->type].account_uri_key); @@ -1549,6 +1573,9 @@ emae_service_provider_changed (EMAccountEditorService *service) enable = e_account_writable (account, emae_service_info[service->type].save_passwd_key); gtk_widget_set_sensitive ((GtkWidget *)service->remember, enable); + if (service->port && service->provider->port_entries) + e_port_entry_set_camel_entries (service->port, service->provider->port_entries); + for (i=0;emae_service_info[service->type].host_info[i].flag;i++) { GtkWidget *w; gint hide; @@ -1565,8 +1592,13 @@ emae_service_provider_changed (EMAccountEditorService *service) if (dwidget == NULL && enable) dwidget = w; - if (info->setval && !hide) - info->setval (url, enable?gtk_entry_get_text ((GtkEntry *)w):NULL); + if (info->setval && !hide) { + if (GTK_IS_ENTRY (w)) + info->setval (url, enable?gtk_entry_get_text ((GtkEntry *)w):NULL); + else if (E_IS_PORT_ENTRY (w)) + info->setval (url, enable?g_strdup_printf("%i", + e_port_entry_get_port (E_PORT_ENTRY (w))):NULL); + } } } } @@ -1599,6 +1631,7 @@ emae_service_provider_changed (EMAccountEditorService *service) gtk_widget_hide ((GtkWidget *)service->needs_auth); } #ifdef HAVE_SSL + old_port = url->port; gtk_widget_hide (service->no_ssl); if (service->provider->flags & CAMEL_PROVIDER_SUPPORTS_SSL) { emae_ssl_update (service, url); @@ -1614,6 +1647,13 @@ emae_service_provider_changed (EMAccountEditorService *service) gtk_widget_show (service->no_ssl); camel_url_set_param (url, "use_ssl", NULL); #endif + + /* This must be done AFTER use_ssl is set; changing use_ssl overwrites + the old port, which could be SSL port, but also could be some special + port and we would otherwise lost it */ + if (url->port) + e_port_entry_set_port (service->port, old_port); + } else { camel_url_set_protocol (url, NULL); gtk_label_set_text (service->description, ""); @@ -1978,12 +2018,17 @@ emae_setup_service (EMAccountEditor *emae, EMAccountEditorService *service, GtkB account = em_account_editor_get_modified_account (emae); uri = e_account_get_string (account, info->account_uri_key); - service->provider = uri?camel_provider_get (uri, NULL):NULL; + service->provider = uri ? camel_provider_get (uri, NULL) : NULL; + + /* Extract all widgets we need from the builder file. */ + service->frame = e_builder_get_widget (builder, info->frame); service->container = e_builder_get_widget (builder, info->container); service->description = GTK_LABEL (e_builder_get_widget (builder, info->description)); service->hostname = GTK_ENTRY (e_builder_get_widget (builder, info->hostname)); service->hostlabel = (GtkLabel *)e_builder_get_widget (builder, info->hostlabel); + service->port = E_PORT_ENTRY (e_builder_get_widget (builder, info->port)); + service->portlabel = (GtkLabel *)e_builder_get_widget (builder, info->portlabel); service->username = GTK_ENTRY (e_builder_get_widget (builder, info->username)); service->userlabel = (GtkLabel *)e_builder_get_widget (builder, info->userlabel); if (info->pathentry) { @@ -1997,19 +2042,36 @@ emae_setup_service (EMAccountEditor *emae, EMAccountEditorService *service, GtkB service->use_ssl = (GtkComboBox *)e_builder_get_widget (builder, info->use_ssl); service->no_ssl = e_builder_get_widget (builder, info->ssl_disabled); + service->auth_frame = e_builder_get_widget (builder, info->auth_frame); + service->check_supported = (GtkButton *)e_builder_get_widget (builder, info->authtype_check); + service->authtype = (GtkComboBox *)e_builder_get_widget (builder, info->authtype); + service->providers = (GtkComboBox *)e_builder_get_widget (builder, info->type_dropdown); + + service->remember = emae_account_toggle (emae, info->remember_password, info->save_passwd_key, builder); + + if (info->needs_auth) + service->needs_auth = (GtkToggleButton *)e_builder_get_widget (builder, info->needs_auth); + else + service->needs_auth = NULL; + + service->auth_changed_id = 0; + + /* Do this first. Otherwise subsequent changes get clobbered. */ + emae_service_provider_changed (service); + /* configure ui for current settings */ if (url->host) { - if (url->port) { - gchar *host = g_strdup_printf ("%s:%d", url->host, url->port); - - gtk_entry_set_text (service->hostname, host); - g_free (host); - } else - gtk_entry_set_text (service->hostname, url->host); + gtk_entry_set_text (service->hostname, url->host); } + if (url->user && *url->user) { gtk_entry_set_text (service->username, url->user); } + + if (url->port) { + e_port_entry_set_port (service->port, url->port); + } + if (service->pathentry) { GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; @@ -2035,29 +2097,21 @@ emae_setup_service (EMAccountEditor *emae, EMAccountEditorService *service, GtkB } g_signal_connect (service->hostname, "changed", G_CALLBACK (emae_hostname_changed), service); + g_signal_connect (service->port, "changed", G_CALLBACK (emae_port_changed), service); g_signal_connect (service->username, "changed", G_CALLBACK (emae_username_changed), service); if (service->pathentry) g_signal_connect (GTK_FILE_CHOOSER (service->pathentry), "selection-changed", G_CALLBACK (emae_path_changed), service); g_signal_connect (service->use_ssl, "changed", G_CALLBACK(emae_ssl_changed), service); - service->auth_frame = e_builder_get_widget (builder, info->auth_frame); - service->remember = emae_account_toggle (emae, info->remember_password, info->save_passwd_key, builder); - service->check_supported = (GtkButton *)e_builder_get_widget (builder, info->authtype_check); - service->authtype = (GtkComboBox *)e_builder_get_widget (builder, info->authtype); /* old authtype will be destroyed when we exit */ - service->auth_changed_id = 0; - service->providers = (GtkComboBox *)e_builder_get_widget (builder, info->type_dropdown); emae_refresh_providers (emae, service); emae_refresh_authtype (emae, service); - if (info->needs_auth) { - service->needs_auth = (GtkToggleButton *)e_builder_get_widget (builder, info->needs_auth); + if (service->needs_auth != NULL) { gtk_toggle_button_set_active (service->needs_auth, url->authmech != NULL); g_signal_connect (service->needs_auth, "toggled", G_CALLBACK(emae_needs_auth), service); emae_needs_auth (service->needs_auth, service); - } else { - service->needs_auth = NULL; } if (!e_account_writable (account, info->account_uri_key)) @@ -2065,8 +2119,6 @@ emae_setup_service (EMAccountEditor *emae, EMAccountEditorService *service, GtkB else gtk_widget_set_sensitive (service->container, TRUE); - emae_service_provider_changed (service); - camel_url_free (url); } @@ -3346,7 +3398,7 @@ emae_service_complete (EMAccountEditor *emae, EMAccountEditorService *service) return FALSE; if (CAMEL_PROVIDER_NEEDS (service->provider, CAMEL_URL_PART_HOST)) { - if (url->host == NULL || url->host[0] == 0) + if (url->host == NULL || url->host[0] == 0 || !e_port_entry_is_valid (service->port)) ok = FALSE; } /* We only need the user if the service needs auth as well, i think */ diff --git a/mail/mail-config.ui b/mail/mail-config.ui index e582621c26..5305593e8c 100644 --- a/mail/mail-config.ui +++ b/mail/mail-config.ui @@ -625,7 +625,7 @@ For example: "Work" or "Personal" True 3 - 2 + 4 12 6 @@ -637,6 +637,25 @@ For example: "Work" or "Personal" source_host + 0 + 1 + 0 + GTK_FILL + + + + + + True + 1 + _Port: + True + source_port + + + 0 + 1 + 2 GTK_FILL @@ -651,7 +670,6 @@ For example: "Work" or "Personal" 1 - 2 GTK_FILL @@ -664,8 +682,20 @@ For example: "Work" or "Personal" 1 - 2 - + 0 + + + + + True + True + True + + + 3 + 0 + + @@ -676,7 +706,7 @@ For example: "Work" or "Personal" 1 - 2 + 4 1 2 @@ -1053,7 +1083,7 @@ For example: "Work" or "Personal" True 2 - 2 + 4 12 6 @@ -1062,7 +1092,6 @@ For example: "Work" or "Personal" 1 _Server: True - right transport_host @@ -1070,6 +1099,20 @@ For example: "Work" or "Personal" + + + True + _Port: + True + right + transport_port + + + 2 + GTK_FILL + + + True @@ -1082,6 +1125,18 @@ For example: "Work" or "Personal" + + + True + True + True + + + 3 + + + + Ser_ver requires authentication @@ -1092,6 +1147,7 @@ For example: "Work" or "Personal" True + 1 2 1 2 diff --git a/widgets/misc/Makefile.am b/widgets/misc/Makefile.am index 7e9ce9ca2b..4076249f19 100644 --- a/widgets/misc/Makefile.am +++ b/widgets/misc/Makefile.am @@ -47,6 +47,7 @@ widgetsinclude_HEADERS = \ e-picture-gallery.h \ e-popup-action.h \ e-popup-menu.h \ + e-port-entry.h \ e-preferences-window.h \ e-preview-pane.h \ e-printable.h \ @@ -126,6 +127,7 @@ libemiscwidgets_la_SOURCES = \ e-picture-gallery.c \ e-popup-action.c \ e-popup-menu.c \ + e-port-entry.c \ e-preferences-window.c \ e-preview-pane.c \ e-printable.c \ diff --git a/widgets/misc/e-port-entry.c b/widgets/misc/e-port-entry.c new file mode 100644 index 0000000000..5028a41a0e --- /dev/null +++ b/widgets/misc/e-port-entry.c @@ -0,0 +1,474 @@ +/* + * This library 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 Library General Public + * License along with the program; if not, see + * + * Authors: + * Dan Vratil + */ + +#include "e-port-entry.h" + +#include +#include +#include +#include + +struct _EPortEntryPrivate { + guint port; + gboolean is_valid; +}; + +enum { + PORT_NUM_COLUMN, + PORT_DESC_COLUMN, + PORT_IS_SSL_COLUMN +}; + +enum { + PROP_0, + PROP_IS_VALID, + PROP_PORT +}; + +G_DEFINE_TYPE ( + EPortEntry, + e_port_entry, + GTK_TYPE_COMBO_BOX) + +static void +port_entry_set_is_valid (EPortEntry *port_entry, + gboolean is_valid) +{ + g_return_if_fail (E_IS_PORT_ENTRY (port_entry)); + + port_entry->priv->is_valid = is_valid; + + g_object_notify (G_OBJECT (port_entry), "is-valid"); +} + +/** + * Returns number of port currently selected in the widget, no matter + * what value is in the PORT property + */ +static gint +port_entry_get_model_active_port (EPortEntry *port_entry) +{ + const gchar *port; + + port = gtk_combo_box_get_active_id (GTK_COMBO_BOX (port_entry)); + + if (!port) { + GtkWidget *entry = gtk_bin_get_child (GTK_BIN (port_entry)); + port = gtk_entry_get_text (GTK_ENTRY (entry)); + } + + return atoi (port); +} + +static void +port_entry_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_IS_VALID: + port_entry_set_is_valid ( + E_PORT_ENTRY (object), + g_value_get_boolean (value)); + return; + case PROP_PORT: + e_port_entry_set_port ( + E_PORT_ENTRY (object), + g_value_get_uint (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +port_entry_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_IS_VALID: + g_value_set_boolean ( + value, e_port_entry_is_valid ( + E_PORT_ENTRY (object))); + return; + case PROP_PORT: + g_value_set_uint ( + value, e_port_entry_get_port ( + E_PORT_ENTRY (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +port_entry_port_changed (EPortEntry *port_entry) +{ + GtkTreeModel *model; + GtkTreeIter iter; + const gchar *port; + const gchar *tooltip; + + g_return_if_fail (E_IS_PORT_ENTRY (port_entry)); + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (port_entry)); + g_return_if_fail (model); + + if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (port_entry), &iter)) { + GtkWidget *entry = gtk_bin_get_child (GTK_BIN (port_entry)); + port = gtk_entry_get_text (GTK_ENTRY (entry)); + + /* Try if user just haven't happened to enter a default port */ + gtk_combo_box_set_active_id (GTK_COMBO_BOX (port_entry), port); + } else { + gtk_tree_model_get (model, &iter, PORT_NUM_COLUMN, &port, -1); + } + + if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (port_entry), &iter)) { + gtk_tree_model_get (model, &iter, PORT_DESC_COLUMN, &tooltip, -1); + gtk_widget_set_tooltip_text (GTK_WIDGET (port_entry), tooltip); + } else { + gtk_widget_set_has_tooltip (GTK_WIDGET (port_entry), FALSE); + } + + if (port == NULL || *port == '\0') { + port_entry->priv->port = 0; + port_entry_set_is_valid (port_entry, FALSE); + } else { + port_entry->priv->port = atoi (port); + if ((port_entry->priv->port <= 0) || + (port_entry->priv->port > G_MAXUINT16)) { + port_entry->priv->port = 0; + port_entry_set_is_valid (port_entry, FALSE); + } else { + port_entry_set_is_valid (port_entry, TRUE); + } + } + + g_object_notify (G_OBJECT (port_entry), "port"); +} + +static void +port_entry_get_preferred_width (GtkWidget *widget, + gint *minimum_size, + gint *natural_size) +{ + PangoContext *context; + PangoFontMetrics *metrics; + PangoFontDescription *font_desc; + GtkStyleContext *style_context; + GtkStateFlags state; + gint digit_width; + gint parent_entry_width_min; + gint parent_width_min; + GtkWidget *entry; + + style_context = gtk_widget_get_style_context (widget); + state = gtk_widget_get_state_flags (widget); + gtk_style_context_get ( + style_context, state, "font", &font_desc, NULL); + context = gtk_widget_get_pango_context (GTK_WIDGET (widget)); + metrics = pango_context_get_metrics ( + context, font_desc, pango_context_get_language (context)); + + digit_width = PANGO_PIXELS ( + pango_font_metrics_get_approximate_digit_width (metrics)); + + /* Preferred width of the entry */ + entry = gtk_bin_get_child (GTK_BIN (widget)); + gtk_widget_get_preferred_width (entry, NULL, &parent_entry_width_min); + + /* Preferred width of a standard combobox */ + GTK_WIDGET_CLASS (e_port_entry_parent_class)-> + get_preferred_width (widget, &parent_width_min, NULL); + + /* 6 * digit_width - port number has max 5 + * digits + extra free space for better look */ + if (minimum_size != NULL) + *minimum_size = + parent_width_min - parent_entry_width_min + + 6 * digit_width; + + if (natural_size != NULL) + *natural_size = + parent_width_min - parent_entry_width_min + + 6 * digit_width; + + pango_font_metrics_unref (metrics); + pango_font_description_free (font_desc); +} + +static void +e_port_entry_class_init (EPortEntryClass *class) +{ + GObjectClass *object_class; + GtkWidgetClass *widget_class; + + g_type_class_add_private (class, sizeof (EPortEntryPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = port_entry_set_property; + object_class->get_property = port_entry_get_property; + + widget_class = GTK_WIDGET_CLASS (class); + widget_class->get_preferred_width = port_entry_get_preferred_width; + + g_object_class_install_property ( + object_class, + PROP_IS_VALID, + g_param_spec_boolean ( + "is-valid", + NULL, + NULL, + FALSE, + G_PARAM_READABLE)); + + g_object_class_install_property ( + object_class, + PROP_PORT, + g_param_spec_uint ( + "port", + NULL, + NULL, + 0, /* Min port, 0 = invalid port */ + G_MAXUINT16, /* Max port */ + 0, + G_PARAM_READWRITE)); +} + +static void +e_port_entry_init (EPortEntry *port_entry) +{ + GtkCellRenderer *renderer; + GtkListStore *store; + + port_entry->priv = G_TYPE_INSTANCE_GET_PRIVATE ( + port_entry, E_TYPE_PORT_ENTRY, EPortEntryPrivate); + port_entry->priv->port = 0; + port_entry->priv->is_valid = FALSE; + + store = gtk_list_store_new ( + 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN); + + gtk_combo_box_set_model ( + GTK_COMBO_BOX (port_entry), GTK_TREE_MODEL (store)); + gtk_combo_box_set_entry_text_column ( + GTK_COMBO_BOX (port_entry), PORT_NUM_COLUMN); + gtk_combo_box_set_id_column ( + GTK_COMBO_BOX (port_entry), PORT_NUM_COLUMN); + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_renderer_set_sensitive (renderer, TRUE); + gtk_cell_layout_pack_start ( + GTK_CELL_LAYOUT (port_entry), renderer, FALSE); + gtk_cell_layout_add_attribute ( + GTK_CELL_LAYOUT (port_entry), + renderer, "text", PORT_NUM_COLUMN); + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_renderer_set_sensitive (renderer, FALSE); + gtk_cell_layout_pack_start ( + GTK_CELL_LAYOUT (port_entry), renderer, TRUE); + gtk_cell_layout_add_attribute ( + GTK_CELL_LAYOUT (port_entry), + renderer, "text", PORT_DESC_COLUMN); + + /* Update the port property when port is changed */ + g_signal_connect ( + port_entry, "changed", + G_CALLBACK (port_entry_port_changed), NULL); +} + +GtkWidget * +e_port_entry_new (void) +{ + return g_object_new ( + E_TYPE_PORT_ENTRY, "has-entry", TRUE, NULL); +} + +void +e_port_entry_set_camel_entries (EPortEntry *port_entry, + CamelProviderPortEntry *entries) +{ + GtkTreeIter iter; + GtkTreeModel *model; + GtkListStore *store; + gint i = 0; + + g_return_if_fail (E_IS_PORT_ENTRY (port_entry)); + g_return_if_fail (entries); + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (port_entry)); + store = GTK_LIST_STORE (model); + + gtk_list_store_clear (store); + + while (entries[i].port > 0) { + gchar *port_string; + + port_string = g_strdup_printf ("%i", entries[i].port); + + gtk_list_store_append (store, &iter); + gtk_list_store_set ( + store, &iter, + PORT_NUM_COLUMN, port_string, + PORT_DESC_COLUMN, entries[i].desc, + PORT_IS_SSL_COLUMN, entries[i].is_ssl, + -1); + i++; + + g_free (port_string); + } + + /* Activate the first port */ + if (i > 0) + e_port_entry_set_port (port_entry, entries[0].port); +} + +void +e_port_entry_security_port_changed (EPortEntry *port_entry, + gchar *ssl) +{ + g_return_if_fail (E_IS_PORT_ENTRY (port_entry)); + g_return_if_fail (ssl != NULL); + + if (strcmp (ssl, "always") == 0) { + e_port_entry_activate_secured_port (port_entry, 0); + } else { + e_port_entry_activate_nonsecured_port (port_entry, 0); + } +} + +gint +e_port_entry_get_port (EPortEntry *port_entry) +{ + g_return_val_if_fail (E_IS_PORT_ENTRY (port_entry), 0); + + return port_entry->priv->port; +} + +void +e_port_entry_set_port (EPortEntry *port_entry, + gint port) +{ + g_return_if_fail (E_IS_PORT_ENTRY (port_entry)); + + port_entry->priv->port = port; + if ((port <= 0) || (port > G_MAXUINT16)) + port_entry_set_is_valid (port_entry, FALSE); + else { + gchar *port_string; + + port_string = g_strdup_printf ("%i", port); + + gtk_combo_box_set_active_id ( + GTK_COMBO_BOX (port_entry), port_string); + + if (port_entry_get_model_active_port (port_entry) != port) { + GtkWidget *entry; + + entry = gtk_bin_get_child (GTK_BIN (port_entry)); + gtk_entry_set_text (GTK_ENTRY (entry), port_string); + } + + port_entry_set_is_valid (port_entry, TRUE); + + g_free (port_string); + } + + g_object_notify (G_OBJECT (port_entry), "port"); +} + +gboolean +e_port_entry_is_valid (EPortEntry *port_entry) +{ + g_return_val_if_fail (E_IS_PORT_ENTRY (port_entry), FALSE); + + return port_entry->priv->is_valid; +} + +/** + * If there are more then one secured port in the model, you can specify + * which of the secured ports should be activated by specifying the index. + * The index counts only for secured ports, so if you have 5 ports of which + * ports 1, 3 and 5 are secured, the association is 0=>1, 1=>3, 2=>5 + */ +void +e_port_entry_activate_secured_port (EPortEntry *port_entry, + gint index) +{ + GtkTreeModel *model; + GtkTreeIter iter; + gboolean is_ssl; + gint iters = 0; + + g_return_if_fail (E_IS_PORT_ENTRY (port_entry)); + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (port_entry)); + + if (!gtk_tree_model_get_iter_first (model, &iter)) + return; + + do { + gtk_tree_model_get ( + model, &iter, PORT_IS_SSL_COLUMN, &is_ssl, -1); + if (is_ssl && (iters == index)) { + gtk_combo_box_set_active_iter ( + GTK_COMBO_BOX (port_entry), &iter); + return; + } + + if (is_ssl) + iters++; + + } while (gtk_tree_model_iter_next (model, &iter)); +} + +/** + * If there are more then one unsecured port in the model, you can specify + * which of the unsecured ports should be activated by specifiying the index. + * The index counts only for unsecured ports, so if you have 5 ports, of which + * ports 2 and 4 are unsecured, the associtation is 0=>2, 1=>4 + */ +void +e_port_entry_activate_nonsecured_port (EPortEntry *port_entry, + gint index) +{ + GtkTreeModel *model; + GtkTreeIter iter; + gboolean is_ssl; + gint iters = 0; + + g_return_if_fail (E_IS_PORT_ENTRY (port_entry)); + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (port_entry)); + + if (!gtk_tree_model_get_iter_first (model, &iter)) + return; + + do { + gtk_tree_model_get (model, &iter, PORT_IS_SSL_COLUMN, &is_ssl, -1); + if (!is_ssl && (iters == index)) { + gtk_combo_box_set_active_iter ( + GTK_COMBO_BOX (port_entry), &iter); + return; + } + + if (!is_ssl) + iters++; + + } while (gtk_tree_model_iter_next (model, &iter)); +} diff --git a/widgets/misc/e-port-entry.h b/widgets/misc/e-port-entry.h new file mode 100644 index 0000000000..64e0a6b234 --- /dev/null +++ b/widgets/misc/e-port-entry.h @@ -0,0 +1,82 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library 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 General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + * Authors: + * Dan Vratil + */ + +#ifndef E_PORT_ENTRY_H +#define E_PORT_ENTRY_H + +#include +#include + +/* Standard GObject macros */ +#define E_TYPE_PORT_ENTRY \ + (e_port_entry_get_type ()) +#define E_PORT_ENTRY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_PORT_ENTRY, EPortEntry)) +#define E_PORT_ENTRY_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_PORT_ENTRY, EPortEntryClass)) +#define E_IS_PORT_ENTRY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_PORT_ENTRY)) +#define E_IS_PORT_ENTRY_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_PORT_ENTRY)) +#define E_PORT_ENTRY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_PORT_ENTRY, EPortEntryClass)) + +G_BEGIN_DECLS + +typedef struct _EPortEntry EPortEntry; +typedef struct _EPortEntryClass EPortEntryClass; +typedef struct _EPortEntryPrivate EPortEntryPrivate; + +struct _EPortEntry { + GtkComboBox parent; + EPortEntryPrivate *priv; +}; + +struct _EPortEntryClass { + GtkComboBoxClass parent_class; +}; + +GType e_port_entry_get_type (void) G_GNUC_CONST; +GtkWidget * e_port_entry_new (void); +void e_port_entry_set_camel_entries (EPortEntry *pentry, + CamelProviderPortEntry *entries); +void e_port_entry_security_port_changed + (EPortEntry *pentry, + gchar *ssl); +gint e_port_entry_get_port (EPortEntry *pentry); +void e_port_entry_set_port (EPortEntry *pentry, gint port); +gboolean e_port_entry_is_valid (EPortEntry *pentry); +void e_port_entry_activate_secured_port + (EPortEntry *pentry, + gint index); +void e_port_entry_activate_nonsecured_port + (EPortEntry *pentry, + gint index); + +G_END_DECLS + +#endif /* E_PORT_ENTRY_H */ -- cgit v1.2.3