No issues found
1 /*
2 * This library is distributed in the hope that it will be useful,
3 * but WITHOUT ANY WARRANTY; without even the implied warranty of
4 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5 * Lesser General Public License for more details.
6 *
7 * You should have received a copy of the GNU Library General Public
8 * License along with the program; if not, see <http://www.gnu.org/licenses/>
9 *
10 * Authors:
11 * Dan Vratil <dvratil@redhat.com>
12 */
13
14 #include "e-port-entry.h"
15
16 #include <config.h>
17 #include <errno.h>
18 #include <stddef.h>
19 #include <string.h>
20
21 #define E_PORT_ENTRY_GET_PRIVATE(obj) \
22 (G_TYPE_INSTANCE_GET_PRIVATE \
23 ((obj), E_TYPE_PORT_ENTRY, EPortEntryPrivate))
24
25 struct _EPortEntryPrivate {
26 CamelNetworkSecurityMethod method;
27 CamelProviderPortEntry *entries;
28 };
29
30 enum {
31 PORT_NUM_COLUMN,
32 PORT_DESC_COLUMN,
33 PORT_IS_SSL_COLUMN
34 };
35
36 enum {
37 PROP_0,
38 PROP_IS_VALID,
39 PROP_PORT,
40 PROP_SECURITY_METHOD
41 };
42
43 G_DEFINE_TYPE (
44 EPortEntry,
45 e_port_entry,
46 GTK_TYPE_COMBO_BOX)
47
48 static GtkEntry *
49 port_entry_get_entry (EPortEntry *port_entry)
50 {
51 return GTK_ENTRY (gtk_bin_get_child (GTK_BIN (port_entry)));
52 }
53
54 static gboolean
55 port_entry_get_numeric_port (EPortEntry *port_entry,
56 gint *out_port)
57 {
58 GtkEntry *entry;
59 const gchar *port_string;
60 gboolean valid;
61 gint port;
62
63 entry = port_entry_get_entry (port_entry);
64
65 port_string = gtk_entry_get_text (entry);
66 g_return_val_if_fail (port_string != NULL, FALSE);
67
68 errno = 0;
69 port = strtol (port_string, NULL, 10);
70 valid = (errno == 0) && (port == CLAMP (port, 1, G_MAXUINT16));
71
72 if (valid && out_port != NULL)
73 *out_port = port;
74
75 return valid;
76 }
77
78 static void
79 port_entry_text_changed (GtkEditable *editable,
80 EPortEntry *port_entry)
81 {
82 GObject *object = G_OBJECT (port_entry);
83 const gchar *desc = NULL;
84 gint port = 0;
85 gint ii = 0;
86
87 g_object_freeze_notify (object);
88
89 port_entry_get_numeric_port (port_entry, &port);
90
91 if (port_entry->priv->entries != NULL) {
92 while (port_entry->priv->entries[ii].port > 0) {
93 if (port == port_entry->priv->entries[ii].port) {
94 desc = port_entry->priv->entries[ii].desc;
95 break;
96 }
97 ii++;
98 }
99 }
100
101 if (desc != NULL)
102 gtk_widget_set_tooltip_text (GTK_WIDGET (port_entry), desc);
103 else
104 gtk_widget_set_has_tooltip (GTK_WIDGET (port_entry), FALSE);
105
106 g_object_notify (object, "port");
107 g_object_notify (object, "is-valid");
108
109 g_object_thaw_notify (object);
110 }
111
112 static void
113 port_entry_method_changed (EPortEntry *port_entry)
114 {
115 CamelNetworkSecurityMethod method;
116 gboolean standard_port = FALSE;
117 gboolean valid, have_ssl = FALSE, have_nossl = FALSE;
118 gint port = 0;
119 gint ii;
120
121 method = e_port_entry_get_security_method (port_entry);
122 valid = port_entry_get_numeric_port (port_entry, &port);
123
124 /* Only change the port number if it's currently on a standard
125 * port (i.e. listed in a CamelProviderPortEntry). Otherwise,
126 * leave custom port numbers alone. */
127
128 if (valid && port_entry->priv->entries != NULL) {
129 for (ii = 0; port_entry->priv->entries[ii].port > 0 && (!have_ssl || !have_nossl); ii++) {
130 /* Use only the first SSL/no-SSL port as a default in the list
131 * and skip the others */
132 if (port_entry->priv->entries[ii].is_ssl) {
133 if (have_ssl)
134 continue;
135 have_ssl = TRUE;
136 } else {
137 if (have_nossl)
138 continue;
139 have_nossl = TRUE;
140 }
141
142 if (port == port_entry->priv->entries[ii].port) {
143 standard_port = TRUE;
144 break;
145 }
146 }
147 }
148
149 if (valid && !standard_port)
150 return;
151
152 switch (method) {
153 case CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT:
154 e_port_entry_activate_secured_port (port_entry, 0);
155 break;
156 default:
157 e_port_entry_activate_nonsecured_port (port_entry, 0);
158 break;
159 }
160 }
161
162 static void
163 port_entry_set_property (GObject *object,
164 guint property_id,
165 const GValue *value,
166 GParamSpec *pspec)
167 {
168 switch (property_id) {
169 case PROP_PORT:
170 e_port_entry_set_port (
171 E_PORT_ENTRY (object),
172 g_value_get_uint (value));
173 return;
174
175 case PROP_SECURITY_METHOD:
176 e_port_entry_set_security_method (
177 E_PORT_ENTRY (object),
178 g_value_get_enum (value));
179 return;
180 }
181
182 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
183 }
184
185 static void
186 port_entry_get_property (GObject *object,
187 guint property_id,
188 GValue *value,
189 GParamSpec *pspec)
190 {
191 switch (property_id) {
192 case PROP_IS_VALID:
193 g_value_set_boolean (
194 value, e_port_entry_is_valid (
195 E_PORT_ENTRY (object)));
196 return;
197
198 case PROP_PORT:
199 g_value_set_uint (
200 value, e_port_entry_get_port (
201 E_PORT_ENTRY (object)));
202 return;
203
204 case PROP_SECURITY_METHOD:
205 g_value_set_enum (
206 value, e_port_entry_get_security_method (
207 E_PORT_ENTRY (object)));
208 return;
209 }
210
211 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
212 }
213
214 static void
215 port_entry_constructed (GObject *object)
216 {
217 GtkEntry *entry;
218
219 /* Chain up to parent's constructed() method. */
220 G_OBJECT_CLASS (e_port_entry_parent_class)->constructed (object);
221
222 entry = port_entry_get_entry (E_PORT_ENTRY (object));
223
224 g_signal_connect_after (
225 entry, "changed",
226 G_CALLBACK (port_entry_text_changed), object);
227 }
228
229 static void
230 port_entry_get_preferred_width (GtkWidget *widget,
231 gint *minimum_size,
232 gint *natural_size)
233 {
234 PangoContext *context;
235 PangoFontMetrics *metrics;
236 PangoFontDescription *font_desc;
237 GtkStyleContext *style_context;
238 GtkStateFlags state;
239 gint digit_width;
240 gint parent_entry_width_min;
241 gint parent_width_min;
242 GtkWidget *entry;
243
244 style_context = gtk_widget_get_style_context (widget);
245 state = gtk_widget_get_state_flags (widget);
246 gtk_style_context_get (
247 style_context, state, "font", &font_desc, NULL);
248 context = gtk_widget_get_pango_context (GTK_WIDGET (widget));
249 metrics = pango_context_get_metrics (
250 context, font_desc, pango_context_get_language (context));
251
252 digit_width = PANGO_PIXELS (
253 pango_font_metrics_get_approximate_digit_width (metrics));
254
255 /* Preferred width of the entry */
256 entry = gtk_bin_get_child (GTK_BIN (widget));
257 gtk_widget_get_preferred_width (entry, NULL, &parent_entry_width_min);
258
259 /* Preferred width of a standard combobox */
260 GTK_WIDGET_CLASS (e_port_entry_parent_class)->
261 get_preferred_width (widget, &parent_width_min, NULL);
262
263 /* 6 * digit_width - port number has max 5
264 * digits + extra free space for better look */
265 if (minimum_size != NULL)
266 *minimum_size =
267 parent_width_min - parent_entry_width_min +
268 6 * digit_width;
269
270 if (natural_size != NULL)
271 *natural_size =
272 parent_width_min - parent_entry_width_min +
273 6 * digit_width;
274
275 pango_font_metrics_unref (metrics);
276 pango_font_description_free (font_desc);
277 }
278
279 static void
280 e_port_entry_class_init (EPortEntryClass *class)
281 {
282 GObjectClass *object_class;
283 GtkWidgetClass *widget_class;
284
285 g_type_class_add_private (class, sizeof (EPortEntryPrivate));
286
287 object_class = G_OBJECT_CLASS (class);
288 object_class->set_property = port_entry_set_property;
289 object_class->get_property = port_entry_get_property;
290 object_class->constructed = port_entry_constructed;
291
292 widget_class = GTK_WIDGET_CLASS (class);
293 widget_class->get_preferred_width = port_entry_get_preferred_width;
294
295 g_object_class_install_property (
296 object_class,
297 PROP_IS_VALID,
298 g_param_spec_boolean (
299 "is-valid",
300 NULL,
301 NULL,
302 FALSE,
303 G_PARAM_READABLE |
304 G_PARAM_STATIC_STRINGS));
305
306 g_object_class_install_property (
307 object_class,
308 PROP_PORT,
309 g_param_spec_uint (
310 "port",
311 NULL,
312 NULL,
313 0, /* Min port, 0 = invalid port */
314 G_MAXUINT16, /* Max port */
315 0,
316 G_PARAM_READWRITE |
317 G_PARAM_STATIC_STRINGS));
318
319 g_object_class_install_property (
320 object_class,
321 PROP_SECURITY_METHOD,
322 g_param_spec_enum (
323 "security-method",
324 "Security Method",
325 "Method used to establish a network connection",
326 CAMEL_TYPE_NETWORK_SECURITY_METHOD,
327 CAMEL_NETWORK_SECURITY_METHOD_NONE,
328 G_PARAM_READWRITE |
329 G_PARAM_STATIC_STRINGS));
330 }
331
332 static void
333 e_port_entry_init (EPortEntry *port_entry)
334 {
335 GtkCellRenderer *renderer;
336 GtkListStore *store;
337
338 port_entry->priv = E_PORT_ENTRY_GET_PRIVATE (port_entry);
339
340 store = gtk_list_store_new (
341 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
342
343 gtk_combo_box_set_model (
344 GTK_COMBO_BOX (port_entry), GTK_TREE_MODEL (store));
345 gtk_combo_box_set_entry_text_column (
346 GTK_COMBO_BOX (port_entry), PORT_NUM_COLUMN);
347 gtk_combo_box_set_id_column (
348 GTK_COMBO_BOX (port_entry), PORT_NUM_COLUMN);
349
350 renderer = gtk_cell_renderer_text_new ();
351 gtk_cell_renderer_set_sensitive (renderer, TRUE);
352 gtk_cell_layout_pack_start (
353 GTK_CELL_LAYOUT (port_entry), renderer, FALSE);
354 gtk_cell_layout_add_attribute (
355 GTK_CELL_LAYOUT (port_entry),
356 renderer, "text", PORT_NUM_COLUMN);
357
358 renderer = gtk_cell_renderer_text_new ();
359 gtk_cell_renderer_set_sensitive (renderer, FALSE);
360 gtk_cell_layout_pack_start (
361 GTK_CELL_LAYOUT (port_entry), renderer, TRUE);
362 gtk_cell_layout_add_attribute (
363 GTK_CELL_LAYOUT (port_entry),
364 renderer, "text", PORT_DESC_COLUMN);
365 }
366
367 GtkWidget *
368 e_port_entry_new (void)
369 {
370 return g_object_new (
371 E_TYPE_PORT_ENTRY, "has-entry", TRUE, NULL);
372 }
373
374 void
375 e_port_entry_set_camel_entries (EPortEntry *port_entry,
376 CamelProviderPortEntry *entries)
377 {
378 GtkComboBox *combo_box;
379 GtkTreeIter iter;
380 GtkTreeModel *model;
381 GtkListStore *store;
382 gint port = 0;
383 gint i = 0;
384
385 g_return_if_fail (E_IS_PORT_ENTRY (port_entry));
386 g_return_if_fail (entries);
387
388 port_entry->priv->entries = entries;
389
390 combo_box = GTK_COMBO_BOX (port_entry);
391 model = gtk_combo_box_get_model (combo_box);
392
393 store = GTK_LIST_STORE (model);
394 gtk_list_store_clear (store);
395
396 while (entries[i].port > 0) {
397 gchar *port_string;
398
399 /* Grab the first port number. */
400 if (port == 0)
401 port = entries[i].port;
402
403 port_string = g_strdup_printf ("%i", entries[i].port);
404
405 gtk_list_store_append (store, &iter);
406 gtk_list_store_set (
407 store, &iter,
408 PORT_NUM_COLUMN, port_string,
409 PORT_DESC_COLUMN, entries[i].desc,
410 PORT_IS_SSL_COLUMN, entries[i].is_ssl,
411 -1);
412 i++;
413
414 g_free (port_string);
415 }
416
417 e_port_entry_set_port (port_entry, port);
418 }
419
420 gint
421 e_port_entry_get_port (EPortEntry *port_entry)
422 {
423 gint port = 0;
424
425 g_return_val_if_fail (E_IS_PORT_ENTRY (port_entry), 0);
426
427 port_entry_get_numeric_port (port_entry, &port);
428
429 return port;
430 }
431
432 void
433 e_port_entry_set_port (EPortEntry *port_entry,
434 gint port)
435 {
436 GtkEntry *entry;
437 gchar *port_string;
438
439 g_return_if_fail (E_IS_PORT_ENTRY (port_entry));
440
441 entry = port_entry_get_entry (port_entry);
442 port_string = g_strdup_printf ("%i", port);
443 gtk_entry_set_text (entry, port_string);
444 g_free (port_string);
445 }
446
447 gboolean
448 e_port_entry_is_valid (EPortEntry *port_entry)
449 {
450 g_return_val_if_fail (E_IS_PORT_ENTRY (port_entry), FALSE);
451
452 return port_entry_get_numeric_port (port_entry, NULL);
453 }
454
455 CamelNetworkSecurityMethod
456 e_port_entry_get_security_method (EPortEntry *port_entry)
457 {
458 g_return_val_if_fail (
459 E_IS_PORT_ENTRY (port_entry),
460 CAMEL_NETWORK_SECURITY_METHOD_NONE);
461
462 return port_entry->priv->method;
463 }
464
465 void
466 e_port_entry_set_security_method (EPortEntry *port_entry,
467 CamelNetworkSecurityMethod method)
468 {
469 g_return_if_fail (E_IS_PORT_ENTRY (port_entry));
470
471 port_entry->priv->method = method;
472
473 port_entry_method_changed (port_entry);
474
475 g_object_notify (G_OBJECT (port_entry), "security-method");
476 }
477
478 /**
479 * If there are more then one secured port in the model, you can specify
480 * which of the secured ports should be activated by specifying the index.
481 * The index counts only for secured ports, so if you have 5 ports of which
482 * ports 1, 3 and 5 are secured, the association is 0=>1, 1=>3, 2=>5
483 */
484 void
485 e_port_entry_activate_secured_port (EPortEntry *port_entry,
486 gint index)
487 {
488 GtkTreeModel *model;
489 GtkTreeIter iter;
490 gboolean is_ssl;
491 gint iters = 0;
492
493 g_return_if_fail (E_IS_PORT_ENTRY (port_entry));
494
495 model = gtk_combo_box_get_model (GTK_COMBO_BOX (port_entry));
496
497 if (!gtk_tree_model_get_iter_first (model, &iter))
498 return;
499
500 do {
501 gtk_tree_model_get (
502 model, &iter, PORT_IS_SSL_COLUMN, &is_ssl, -1);
503 if (is_ssl && (iters == index)) {
504 gtk_combo_box_set_active_iter (
505 GTK_COMBO_BOX (port_entry), &iter);
506 return;
507 }
508
509 if (is_ssl)
510 iters++;
511
512 } while (gtk_tree_model_iter_next (model, &iter));
513 }
514
515 /**
516 * If there are more then one unsecured port in the model, you can specify
517 * which of the unsecured ports should be activated by specifiying the index.
518 * The index counts only for unsecured ports, so if you have 5 ports, of which
519 * ports 2 and 4 are unsecured, the associtation is 0=>2, 1=>4
520 */
521 void
522 e_port_entry_activate_nonsecured_port (EPortEntry *port_entry,
523 gint index)
524 {
525 GtkTreeModel *model;
526 GtkTreeIter iter;
527 gboolean is_ssl;
528 gint iters = 0;
529
530 g_return_if_fail (E_IS_PORT_ENTRY (port_entry));
531
532 model = gtk_combo_box_get_model (GTK_COMBO_BOX (port_entry));
533
534 if (!gtk_tree_model_get_iter_first (model, &iter))
535 return;
536
537 do {
538 gtk_tree_model_get (model, &iter, PORT_IS_SSL_COLUMN, &is_ssl, -1);
539 if (!is_ssl && (iters == index)) {
540 gtk_combo_box_set_active_iter (
541 GTK_COMBO_BOX (port_entry), &iter);
542 return;
543 }
544
545 if (!is_ssl)
546 iters++;
547
548 } while (gtk_tree_model_iter_next (model, &iter));
549 }