aboutsummaryrefslogtreecommitdiffstats
path: root/e-util/e-port-entry.c
diff options
context:
space:
mode:
Diffstat (limited to 'e-util/e-port-entry.c')
-rw-r--r--e-util/e-port-entry.c549
1 files changed, 549 insertions, 0 deletions
diff --git a/e-util/e-port-entry.c b/e-util/e-port-entry.c
new file mode 100644
index 0000000000..cf857b5d55
--- /dev/null
+++ b/e-util/e-port-entry.c
@@ -0,0 +1,549 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>
+ *
+ * Authors:
+ * Dan Vratil <dvratil@redhat.com>
+ */
+
+#include "e-port-entry.h"
+
+#include <config.h>
+#include <errno.h>
+#include <stddef.h>
+#include <string.h>
+
+#define E_PORT_ENTRY_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_PORT_ENTRY, EPortEntryPrivate))
+
+struct _EPortEntryPrivate {
+ CamelNetworkSecurityMethod method;
+ CamelProviderPortEntry *entries;
+};
+
+enum {
+ PORT_NUM_COLUMN,
+ PORT_DESC_COLUMN,
+ PORT_IS_SSL_COLUMN
+};
+
+enum {
+ PROP_0,
+ PROP_IS_VALID,
+ PROP_PORT,
+ PROP_SECURITY_METHOD
+};
+
+G_DEFINE_TYPE (
+ EPortEntry,
+ e_port_entry,
+ GTK_TYPE_COMBO_BOX)
+
+static GtkEntry *
+port_entry_get_entry (EPortEntry *port_entry)
+{
+ return GTK_ENTRY (gtk_bin_get_child (GTK_BIN (port_entry)));
+}
+
+static gboolean
+port_entry_get_numeric_port (EPortEntry *port_entry,
+ gint *out_port)
+{
+ GtkEntry *entry;
+ const gchar *port_string;
+ gboolean valid;
+ gint port;
+
+ entry = port_entry_get_entry (port_entry);
+
+ port_string = gtk_entry_get_text (entry);
+ g_return_val_if_fail (port_string != NULL, FALSE);
+
+ errno = 0;
+ port = strtol (port_string, NULL, 10);
+ valid = (errno == 0) && (port == CLAMP (port, 1, G_MAXUINT16));
+
+ if (valid && out_port != NULL)
+ *out_port = port;
+
+ return valid;
+}
+
+static void
+port_entry_text_changed (GtkEditable *editable,
+ EPortEntry *port_entry)
+{
+ GObject *object = G_OBJECT (port_entry);
+ const gchar *desc = NULL;
+ gint port = 0;
+ gint ii = 0;
+
+ g_object_freeze_notify (object);
+
+ port_entry_get_numeric_port (port_entry, &port);
+
+ if (port_entry->priv->entries != NULL) {
+ while (port_entry->priv->entries[ii].port > 0) {
+ if (port == port_entry->priv->entries[ii].port) {
+ desc = port_entry->priv->entries[ii].desc;
+ break;
+ }
+ ii++;
+ }
+ }
+
+ if (desc != NULL)
+ gtk_widget_set_tooltip_text (GTK_WIDGET (port_entry), desc);
+ else
+ gtk_widget_set_has_tooltip (GTK_WIDGET (port_entry), FALSE);
+
+ g_object_notify (object, "port");
+ g_object_notify (object, "is-valid");
+
+ g_object_thaw_notify (object);
+}
+
+static void
+port_entry_method_changed (EPortEntry *port_entry)
+{
+ CamelNetworkSecurityMethod method;
+ gboolean standard_port = FALSE;
+ gboolean valid, have_ssl = FALSE, have_nossl = FALSE;
+ gint port = 0;
+ gint ii;
+
+ method = e_port_entry_get_security_method (port_entry);
+ valid = port_entry_get_numeric_port (port_entry, &port);
+
+ /* Only change the port number if it's currently on a standard
+ * port (i.e. listed in a CamelProviderPortEntry). Otherwise,
+ * leave custom port numbers alone. */
+
+ if (valid && port_entry->priv->entries != NULL) {
+ for (ii = 0; port_entry->priv->entries[ii].port > 0 && (!have_ssl || !have_nossl); ii++) {
+ /* Use only the first SSL/no-SSL port as a default in the list
+ * and skip the others */
+ if (port_entry->priv->entries[ii].is_ssl) {
+ if (have_ssl)
+ continue;
+ have_ssl = TRUE;
+ } else {
+ if (have_nossl)
+ continue;
+ have_nossl = TRUE;
+ }
+
+ if (port == port_entry->priv->entries[ii].port) {
+ standard_port = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (valid && !standard_port)
+ return;
+
+ switch (method) {
+ case CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT:
+ e_port_entry_activate_secured_port (port_entry, 0);
+ break;
+ default:
+ e_port_entry_activate_nonsecured_port (port_entry, 0);
+ break;
+ }
+}
+
+static void
+port_entry_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_PORT:
+ e_port_entry_set_port (
+ E_PORT_ENTRY (object),
+ g_value_get_uint (value));
+ return;
+
+ case PROP_SECURITY_METHOD:
+ e_port_entry_set_security_method (
+ E_PORT_ENTRY (object),
+ g_value_get_enum (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;
+
+ case PROP_SECURITY_METHOD:
+ g_value_set_enum (
+ value, e_port_entry_get_security_method (
+ E_PORT_ENTRY (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+port_entry_constructed (GObject *object)
+{
+ GtkEntry *entry;
+
+ /* Chain up to parent's constructed() method. */
+ G_OBJECT_CLASS (e_port_entry_parent_class)->constructed (object);
+
+ entry = port_entry_get_entry (E_PORT_ENTRY (object));
+
+ g_signal_connect_after (
+ entry, "changed",
+ G_CALLBACK (port_entry_text_changed), object);
+}
+
+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;
+ object_class->constructed = port_entry_constructed;
+
+ 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_PARAM_STATIC_STRINGS));
+
+ 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 |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_SECURITY_METHOD,
+ g_param_spec_enum (
+ "security-method",
+ "Security Method",
+ "Method used to establish a network connection",
+ CAMEL_TYPE_NETWORK_SECURITY_METHOD,
+ CAMEL_NETWORK_SECURITY_METHOD_NONE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_port_entry_init (EPortEntry *port_entry)
+{
+ GtkCellRenderer *renderer;
+ GtkListStore *store;
+
+ port_entry->priv = E_PORT_ENTRY_GET_PRIVATE (port_entry);
+
+ 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);
+}
+
+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)
+{
+ GtkComboBox *combo_box;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkListStore *store;
+ gint port = 0;
+ gint i = 0;
+
+ g_return_if_fail (E_IS_PORT_ENTRY (port_entry));
+ g_return_if_fail (entries);
+
+ port_entry->priv->entries = entries;
+
+ combo_box = GTK_COMBO_BOX (port_entry);
+ model = gtk_combo_box_get_model (combo_box);
+
+ store = GTK_LIST_STORE (model);
+ gtk_list_store_clear (store);
+
+ while (entries[i].port > 0) {
+ gchar *port_string;
+
+ /* Grab the first port number. */
+ if (port == 0)
+ port = entries[i].port;
+
+ 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);
+ }
+
+ e_port_entry_set_port (port_entry, port);
+}
+
+gint
+e_port_entry_get_port (EPortEntry *port_entry)
+{
+ gint port = 0;
+
+ g_return_val_if_fail (E_IS_PORT_ENTRY (port_entry), 0);
+
+ port_entry_get_numeric_port (port_entry, &port);
+
+ return port;
+}
+
+void
+e_port_entry_set_port (EPortEntry *port_entry,
+ gint port)
+{
+ GtkEntry *entry;
+ gchar *port_string;
+
+ g_return_if_fail (E_IS_PORT_ENTRY (port_entry));
+
+ entry = port_entry_get_entry (port_entry);
+ port_string = g_strdup_printf ("%i", port);
+ gtk_entry_set_text (entry, port_string);
+ g_free (port_string);
+}
+
+gboolean
+e_port_entry_is_valid (EPortEntry *port_entry)
+{
+ g_return_val_if_fail (E_IS_PORT_ENTRY (port_entry), FALSE);
+
+ return port_entry_get_numeric_port (port_entry, NULL);
+}
+
+CamelNetworkSecurityMethod
+e_port_entry_get_security_method (EPortEntry *port_entry)
+{
+ g_return_val_if_fail (
+ E_IS_PORT_ENTRY (port_entry),
+ CAMEL_NETWORK_SECURITY_METHOD_NONE);
+
+ return port_entry->priv->method;
+}
+
+void
+e_port_entry_set_security_method (EPortEntry *port_entry,
+ CamelNetworkSecurityMethod method)
+{
+ g_return_if_fail (E_IS_PORT_ENTRY (port_entry));
+
+ port_entry->priv->method = method;
+
+ port_entry_method_changed (port_entry);
+
+ g_object_notify (G_OBJECT (port_entry), "security-method");
+}
+
+/**
+ * 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));
+}