1 /*
2 * e-proxy-selector.c
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18 /**
19 * SECTION: e-proxy-selector
20 * @include: e-util/e-util.h
21 * @short_description: Select and manage proxy profiles
22 *
23 * #EProxySelector displays a list of available proxy profiles, with inline
24 * toolbar controls for adding and removing profiles.
25 **/
26
27 #include "evolution-config.h"
28
29 #include <glib/gi18n-lib.h>
30
31 #include "e-proxy-selector.h"
32
33 #define E_PROXY_SELECTOR_GET_PRIVATE(obj) \
34 (G_TYPE_INSTANCE_GET_PRIVATE \
35 ((obj), E_TYPE_PROXY_SELECTOR, EProxySelectorPrivate))
36
37 typedef struct _AsyncContext AsyncContext;
38
39 struct _EProxySelectorPrivate {
40 ESourceRegistry *registry;
41 gulong source_added_handler_id;
42 gulong source_changed_handler_id;
43 gulong source_removed_handler_id;
44
45 GtkTreeSelection *selection;
46 gulong selection_changed_handler_id;
47
48 guint refresh_idle_id;
49 };
50
51 struct _AsyncContext {
52 EProxySelector *selector;
53 ESource *scratch_source;
54 };
55
56 enum {
57 PROP_0,
58 PROP_REGISTRY,
59 PROP_SELECTED
60 };
61
62 enum {
63 COLUMN_DISPLAY_NAME,
64 COLUMN_SOURCE
65 };
66
G_DEFINE_TYPE(EProxySelector,e_proxy_selector,E_TYPE_TREE_VIEW_FRAME)67 G_DEFINE_TYPE (
68 EProxySelector,
69 e_proxy_selector,
70 E_TYPE_TREE_VIEW_FRAME)
71
72 static void
73 async_context_free (AsyncContext *async_context)
74 {
75 g_clear_object (&async_context->selector);
76 g_clear_object (&async_context->scratch_source);
77
78 g_slice_free (AsyncContext, async_context);
79 }
80
81 static gchar *
proxy_selector_pick_display_name(EProxySelector * selector)82 proxy_selector_pick_display_name (EProxySelector *selector)
83 {
84 ESourceRegistry *registry;
85 GList *list, *link;
86 const gchar *base_name = _("Custom Proxy");
87 const gchar *extension_name;
88 gchar *display_name;
89 guint ii = 0;
90
91 extension_name = E_SOURCE_EXTENSION_PROXY;
92 registry = e_proxy_selector_get_registry (selector);
93 list = e_source_registry_list_sources (registry, extension_name);
94
95 /* Convert the list of ESources to a list of display names. */
96 for (link = list; link != NULL; link = g_list_next (link)) {
97 ESource *source = E_SOURCE (link->data);
98 link->data = e_source_dup_display_name (source);
99 g_object_unref (source);
100 }
101
102 display_name = g_strdup (base_name);
103
104 try_again:
105 link = g_list_find_custom (
106 list, display_name, (GCompareFunc) g_utf8_collate);
107
108 if (link != NULL) {
109 g_free (display_name);
110 display_name = g_strdup_printf ("%s (%u)", base_name, ++ii);
111 goto try_again;
112 }
113
114 g_list_free_full (list, (GDestroyNotify) g_free);
115
116 return display_name;
117 }
118
119 static void
proxy_selector_commit_source_cb(GObject * object,GAsyncResult * result,gpointer user_data)120 proxy_selector_commit_source_cb (GObject *object,
121 GAsyncResult *result,
122 gpointer user_data)
123 {
124 AsyncContext *async_context;
125 GError *local_error = NULL;
126
127 async_context = (AsyncContext *) user_data;
128
129 e_source_registry_commit_source_finish (
130 E_SOURCE_REGISTRY (object), result, &local_error);
131
132 if (local_error == NULL) {
133 /* Refresh the tree model immediately. */
134 e_proxy_selector_refresh (async_context->selector);
135
136 /* Select the newly added proxy data source. Note that
137 * e_proxy_selector_set_selected() uses e_source_equal()
138 * to match the input ESource to a tree model row, so it
139 * should match our scratch ESource to the newly-created
140 * ESource since they both have the same UID. */
141 e_proxy_selector_set_selected (
142 async_context->selector,
143 async_context->scratch_source);
144 } else {
145 /* FIXME Hand the error off to an EAlertSink. */
146 g_warning ("%s: %s", G_STRFUNC, local_error->message);
147 g_error_free (local_error);
148 }
149
150 gtk_widget_set_sensitive (GTK_WIDGET (async_context->selector), TRUE);
151
152 async_context_free (async_context);
153 }
154
155 static void
proxy_selector_remove_source_cb(GObject * object,GAsyncResult * result,gpointer user_data)156 proxy_selector_remove_source_cb (GObject *object,
157 GAsyncResult *result,
158 gpointer user_data)
159 {
160 EProxySelector *selector;
161 GError *local_error = NULL;
162
163 selector = E_PROXY_SELECTOR (user_data);
164
165 e_source_remove_finish (E_SOURCE (object), result, &local_error);
166
167 if (local_error != NULL) {
168 /* FIXME Hand the error off to an EAlertSink. */
169 g_warning ("%s: %s", G_STRFUNC, local_error->message);
170 g_error_free (local_error);
171 }
172
173 gtk_widget_set_sensitive (GTK_WIDGET (selector), TRUE);
174
175 g_object_unref (selector);
176 }
177
178 static gboolean
proxy_selector_action_add_cb(EProxySelector * selector,GtkAction * action)179 proxy_selector_action_add_cb (EProxySelector *selector,
180 GtkAction *action)
181 {
182 AsyncContext *async_context;
183 ESourceRegistry *registry;
184 ESourceProxy *extension;
185 ESource *scratch_source;
186 const gchar *extension_name;
187 gchar *display_name;
188
189 const gchar * const ignore_hosts[] = {
190 "localhost",
191 "127.0.0.0/8",
192 "::1",
193 NULL
194 };
195
196 scratch_source = e_source_new (NULL, NULL, NULL);
197
198 display_name = proxy_selector_pick_display_name (selector);
199 e_source_set_display_name (scratch_source, display_name);
200 g_free (display_name);
201
202 extension_name = E_SOURCE_EXTENSION_PROXY;
203 extension = e_source_get_extension (scratch_source, extension_name);
204 e_source_proxy_set_ignore_hosts (extension, ignore_hosts);
205
206 registry = e_proxy_selector_get_registry (selector);
207
208 /* Disable the selector until the commit operation completes. */
209 gtk_widget_set_sensitive (GTK_WIDGET (selector), FALSE);
210
211 async_context = g_slice_new0 (AsyncContext);
212 async_context->selector = g_object_ref (selector);
213 async_context->scratch_source = g_object_ref (scratch_source);
214
215 e_source_registry_commit_source (
216 registry, scratch_source, NULL,
217 proxy_selector_commit_source_cb,
218 async_context);
219
220 g_object_unref (scratch_source);
221
222 return TRUE;
223 }
224
225 static gboolean
proxy_selector_action_remove_cb(EProxySelector * selector,GtkAction * action)226 proxy_selector_action_remove_cb (EProxySelector *selector,
227 GtkAction *action)
228 {
229 ESource *selected_source;
230
231 selected_source = e_proxy_selector_ref_selected (selector);
232 g_return_val_if_fail (selected_source != NULL, FALSE);
233
234 /* Disable the selector until the remove operation completes. */
235 gtk_widget_set_sensitive (GTK_WIDGET (selector), FALSE);
236
237 e_source_remove (
238 selected_source, NULL,
239 proxy_selector_remove_source_cb,
240 g_object_ref (selector));
241
242 g_object_unref (selected_source);
243
244 return TRUE;
245 }
246
247 static gboolean
proxy_selector_refresh_idle_cb(gpointer user_data)248 proxy_selector_refresh_idle_cb (gpointer user_data)
249 {
250 EProxySelector *selector = user_data;
251
252 /* The refresh function will clear the idle ID. */
253 e_proxy_selector_refresh (selector);
254
255 return FALSE;
256 }
257
258 static void
proxy_selector_schedule_refresh(EProxySelector * selector)259 proxy_selector_schedule_refresh (EProxySelector *selector)
260 {
261 /* Use an idle callback to limit how frequently we refresh
262 * the tree model in case the registry is emitting lots of
263 * signals at once. */
264
265 if (selector->priv->refresh_idle_id == 0) {
266 selector->priv->refresh_idle_id = g_idle_add (
267 proxy_selector_refresh_idle_cb, selector);
268 }
269 }
270
271 static void
proxy_selector_cell_edited_cb(GtkCellRendererText * renderer,const gchar * path_string,const gchar * new_name,EProxySelector * selector)272 proxy_selector_cell_edited_cb (GtkCellRendererText *renderer,
273 const gchar *path_string,
274 const gchar *new_name,
275 EProxySelector *selector)
276 {
277 ETreeViewFrame *tree_view_frame;
278 GtkTreeView *tree_view;
279 GtkTreeModel *model;
280 GtkTreePath *path;
281 GtkTreeIter iter;
282 ESource *source;
283
284 if (new_name == NULL || *new_name == '\0')
285 return;
286
287 tree_view_frame = E_TREE_VIEW_FRAME (selector);
288 tree_view = e_tree_view_frame_get_tree_view (tree_view_frame);
289 model = gtk_tree_view_get_model (tree_view);
290
291 path = gtk_tree_path_new_from_string (path_string);
292 gtk_tree_model_get_iter (model, &iter, path);
293 gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
294 gtk_tree_path_free (path);
295
296 /* EProxyPreferences will detect the change and commit it. */
297 e_source_set_display_name (source, new_name);
298
299 e_proxy_selector_refresh (selector);
300
301 g_object_unref (source);
302 }
303
304 static void
proxy_selector_source_added_cb(ESourceRegistry * registry,ESource * source,EProxySelector * selector)305 proxy_selector_source_added_cb (ESourceRegistry *registry,
306 ESource *source,
307 EProxySelector *selector)
308 {
309 if (e_source_has_extension (source, E_SOURCE_EXTENSION_PROXY))
310 proxy_selector_schedule_refresh (selector);
311 }
312
313 static void
proxy_selector_source_changed_cb(ESourceRegistry * registry,ESource * source,EProxySelector * selector)314 proxy_selector_source_changed_cb (ESourceRegistry *registry,
315 ESource *source,
316 EProxySelector *selector)
317 {
318 if (e_source_has_extension (source, E_SOURCE_EXTENSION_PROXY))
319 proxy_selector_schedule_refresh (selector);
320 }
321
322 static void
proxy_selector_source_removed_cb(ESourceRegistry * registry,ESource * source,EProxySelector * selector)323 proxy_selector_source_removed_cb (ESourceRegistry *registry,
324 ESource *source,
325 EProxySelector *selector)
326 {
327 if (e_source_has_extension (source, E_SOURCE_EXTENSION_PROXY))
328 proxy_selector_schedule_refresh (selector);
329 }
330
331 static void
proxy_selector_selection_changed_cb(GtkTreeSelection * selection,EProxySelector * selector)332 proxy_selector_selection_changed_cb (GtkTreeSelection *selection,
333 EProxySelector *selector)
334 {
335 g_object_notify (G_OBJECT (selector), "selected");
336 }
337
338 static void
proxy_selector_set_registry(EProxySelector * selector,ESourceRegistry * registry)339 proxy_selector_set_registry (EProxySelector *selector,
340 ESourceRegistry *registry)
341 {
342 gulong handler_id;
343
344 g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
345 g_return_if_fail (selector->priv->registry == NULL);
346
347 selector->priv->registry = g_object_ref (registry);
348
349 handler_id = g_signal_connect (
350 registry, "source-added",
351 G_CALLBACK (proxy_selector_source_added_cb), selector);
352 selector->priv->source_added_handler_id = handler_id;
353
354 handler_id = g_signal_connect (
355 registry, "source-changed",
356 G_CALLBACK (proxy_selector_source_changed_cb), selector);
357 selector->priv->source_changed_handler_id = handler_id;
358
359 handler_id = g_signal_connect (
360 registry, "source-removed",
361 G_CALLBACK (proxy_selector_source_removed_cb), selector);
362 selector->priv->source_removed_handler_id = handler_id;
363 }
364
365 static void
proxy_selector_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)366 proxy_selector_set_property (GObject *object,
367 guint property_id,
368 const GValue *value,
369 GParamSpec *pspec)
370 {
371 switch (property_id) {
372 case PROP_REGISTRY:
373 proxy_selector_set_registry (
374 E_PROXY_SELECTOR (object),
375 g_value_get_object (value));
376 return;
377
378 case PROP_SELECTED:
379 e_proxy_selector_set_selected (
380 E_PROXY_SELECTOR (object),
381 g_value_get_object (value));
382 return;
383 }
384
385 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
386 }
387
388 static void
proxy_selector_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)389 proxy_selector_get_property (GObject *object,
390 guint property_id,
391 GValue *value,
392 GParamSpec *pspec)
393 {
394 switch (property_id) {
395 case PROP_REGISTRY:
396 g_value_set_object (
397 value,
398 e_proxy_selector_get_registry (
399 E_PROXY_SELECTOR (object)));
400 return;
401
402 case PROP_SELECTED:
403 g_value_take_object (
404 value,
405 e_proxy_selector_ref_selected (
406 E_PROXY_SELECTOR (object)));
407 return;
408 }
409
410 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
411 }
412
413 static void
proxy_selector_dispose(GObject * object)414 proxy_selector_dispose (GObject *object)
415 {
416 EProxySelectorPrivate *priv;
417
418 priv = E_PROXY_SELECTOR_GET_PRIVATE (object);
419
420 if (priv->source_added_handler_id > 0) {
421 g_signal_handler_disconnect (
422 priv->registry,
423 priv->source_added_handler_id);
424 priv->source_added_handler_id = 0;
425 }
426
427 if (priv->source_changed_handler_id > 0) {
428 g_signal_handler_disconnect (
429 priv->registry,
430 priv->source_changed_handler_id);
431 priv->source_changed_handler_id = 0;
432 }
433
434 if (priv->source_removed_handler_id > 0) {
435 g_signal_handler_disconnect (
436 priv->registry,
437 priv->source_removed_handler_id);
438 priv->source_removed_handler_id = 0;
439 }
440
441 if (priv->selection_changed_handler_id > 0) {
442 g_signal_handler_disconnect (
443 priv->selection,
444 priv->selection_changed_handler_id);
445 priv->selection_changed_handler_id = 0;
446 }
447
448 if (priv->refresh_idle_id > 0) {
449 g_source_remove (priv->refresh_idle_id);
450 priv->refresh_idle_id = 0;
451 }
452
453 g_clear_object (&priv->registry);
454
455 /* Chain up to parent's dispose() method. */
456 G_OBJECT_CLASS (e_proxy_selector_parent_class)->dispose (object);
457 }
458
459 static void
proxy_selector_constructed(GObject * object)460 proxy_selector_constructed (GObject *object)
461 {
462 EProxySelector *selector;
463 ETreeViewFrame *tree_view_frame;
464 GtkTreeView *tree_view;
465 GtkTreeViewColumn *column;
466 GtkCellRenderer *renderer;
467 GtkTreeSelection *selection;
468 GtkListStore *list_store;
469 GtkAction *action;
470 const gchar *tooltip;
471 gulong handler_id;
472
473 /* Chain up to parent's constructed() method. */
474 G_OBJECT_CLASS (e_proxy_selector_parent_class)->constructed (object);
475
476 selector = E_PROXY_SELECTOR (object);
477
478 tree_view_frame = E_TREE_VIEW_FRAME (object);
479 tree_view = e_tree_view_frame_get_tree_view (tree_view_frame);
480
481 gtk_tree_view_set_reorderable (tree_view, FALSE);
482 gtk_tree_view_set_headers_visible (tree_view, FALSE);
483
484 /* Configure the toolbar actions. */
485
486 action = e_tree_view_frame_lookup_toolbar_action (
487 tree_view_frame, E_TREE_VIEW_FRAME_ACTION_ADD);
488 tooltip = _("Create a new proxy profile");
489 gtk_action_set_tooltip (action, tooltip);
490
491 action = e_tree_view_frame_lookup_toolbar_action (
492 tree_view_frame, E_TREE_VIEW_FRAME_ACTION_REMOVE);
493 tooltip = _("Delete the selected proxy profile");
494 gtk_action_set_tooltip (action, tooltip);
495
496 /* Configure the tree view column. */
497
498 column = gtk_tree_view_column_new ();
499 renderer = gtk_cell_renderer_text_new ();
500 g_object_set (
501 G_OBJECT (renderer),
502 "editable", TRUE,
503 "ellipsize", PANGO_ELLIPSIZE_END, NULL);
504 g_signal_connect (
505 renderer, "edited",
506 G_CALLBACK (proxy_selector_cell_edited_cb), selector);
507 gtk_tree_view_column_pack_start (column, renderer, TRUE);
508 gtk_tree_view_column_add_attribute (
509 column, renderer, "text", COLUMN_DISPLAY_NAME);
510 gtk_tree_view_append_column (tree_view, column);
511
512 /* Listen for tree view selection changes. */
513
514 selection = gtk_tree_view_get_selection (tree_view);
515 selector->priv->selection = g_object_ref (selection);
516
517 handler_id = g_signal_connect (
518 selection, "changed",
519 G_CALLBACK (proxy_selector_selection_changed_cb), selector);
520 selector->priv->selection_changed_handler_id = handler_id;
521
522 /* Create and populate the tree model. */
523
524 list_store = gtk_list_store_new (2, G_TYPE_STRING, E_TYPE_SOURCE);
525 gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (list_store));
526 g_object_unref (list_store);
527
528 e_proxy_selector_refresh (E_PROXY_SELECTOR (object));
529 }
530
531 static void
proxy_selector_update_toolbar_actions(ETreeViewFrame * tree_view_frame)532 proxy_selector_update_toolbar_actions (ETreeViewFrame *tree_view_frame)
533 {
534 EProxySelector *selector;
535 ESource *selected;
536 GtkAction *action;
537 gboolean sensitive;
538
539 selector = E_PROXY_SELECTOR (tree_view_frame);
540 selected = e_proxy_selector_ref_selected (selector);
541
542 action = e_tree_view_frame_lookup_toolbar_action (
543 tree_view_frame, E_TREE_VIEW_FRAME_ACTION_REMOVE);
544 sensitive = e_source_get_removable (selected);
545 gtk_action_set_sensitive (action, sensitive);
546
547 g_object_unref (selected);
548
549 /* Chain up to parent's update_toolbar_actions() method. */
550 E_TREE_VIEW_FRAME_CLASS (e_proxy_selector_parent_class)->
551 update_toolbar_actions (tree_view_frame);
552 }
553
554 static void
e_proxy_selector_class_init(EProxySelectorClass * class)555 e_proxy_selector_class_init (EProxySelectorClass *class)
556 {
557 GObjectClass *object_class;
558 ETreeViewFrameClass *tree_view_frame_class;
559
560 g_type_class_add_private (class, sizeof (EProxySelectorPrivate));
561
562 object_class = G_OBJECT_CLASS (class);
563 object_class->set_property = proxy_selector_set_property;
564 object_class->get_property = proxy_selector_get_property;
565 object_class->dispose = proxy_selector_dispose;
566 object_class->constructed = proxy_selector_constructed;
567
568 tree_view_frame_class = E_TREE_VIEW_FRAME_CLASS (class);
569 tree_view_frame_class->update_toolbar_actions =
570 proxy_selector_update_toolbar_actions;
571
572 g_object_class_install_property (
573 object_class,
574 PROP_REGISTRY,
575 g_param_spec_object (
576 "registry",
577 "Registry",
578 "Data source registry",
579 E_TYPE_SOURCE_REGISTRY,
580 G_PARAM_READWRITE |
581 G_PARAM_CONSTRUCT_ONLY |
582 G_PARAM_STATIC_STRINGS));
583
584 g_object_class_install_property (
585 object_class,
586 PROP_SELECTED,
587 g_param_spec_object (
588 "selected",
589 "Selected",
590 "The selected data source",
591 E_TYPE_SOURCE,
592 G_PARAM_READWRITE |
593 G_PARAM_STATIC_STRINGS));
594 }
595
596 static void
e_proxy_selector_init(EProxySelector * selector)597 e_proxy_selector_init (EProxySelector *selector)
598 {
599 selector->priv = E_PROXY_SELECTOR_GET_PRIVATE (selector);
600
601 /* In this particular case, it's easier to connect handlers
602 * to detailed signal names than to override the class method. */
603
604 g_signal_connect (
605 selector,
606 "toolbar-action-activate::"
607 E_TREE_VIEW_FRAME_ACTION_ADD,
608 G_CALLBACK (proxy_selector_action_add_cb), NULL);
609
610 g_signal_connect (
611 selector,
612 "toolbar-action-activate::"
613 E_TREE_VIEW_FRAME_ACTION_REMOVE,
614 G_CALLBACK (proxy_selector_action_remove_cb), NULL);
615 }
616
617 /**
618 * e_proxy_selector_new:
619 * @registry: an #ESourceRegistry
620 *
621 * Creates a new #EProxySelector widget using #ESource instances in @registry.
622 *
623 * Returns: a new #EProxySelector
624 **/
625 GtkWidget *
e_proxy_selector_new(ESourceRegistry * registry)626 e_proxy_selector_new (ESourceRegistry *registry)
627 {
628 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
629
630 return g_object_new (
631 E_TYPE_PROXY_SELECTOR,
632 "registry", registry, NULL);
633 }
634
635 /**
636 * e_proxy_selector_refresh:
637 * @selector: an #EProxySelector
638 *
639 * Rebuilds the @selector's list store with an updated list of #ESource
640 * instances that describe a network proxy profile, without disrupting the
641 * previously selected item (if possible).
642 *
643 * This funtion is called automatically in response to #ESourceRegistry
644 * signals which are pertinent to the @selector.
645 **/
646 void
e_proxy_selector_refresh(EProxySelector * selector)647 e_proxy_selector_refresh (EProxySelector *selector)
648 {
649 ETreeViewFrame *tree_view_frame;
650 ESourceRegistry *registry;
651 GtkTreeView *tree_view;
652 GtkTreeModel *tree_model;
653 ESource *builtin_source;
654 ESource *selected;
655 GList *list, *link;
656 const gchar *extension_name;
657
658 g_return_if_fail (E_IS_PROXY_SELECTOR (selector));
659
660 if (selector->priv->refresh_idle_id > 0) {
661 g_source_remove (selector->priv->refresh_idle_id);
662 selector->priv->refresh_idle_id = 0;
663 }
664
665 tree_view_frame = E_TREE_VIEW_FRAME (selector);
666 tree_view = e_tree_view_frame_get_tree_view (tree_view_frame);
667 tree_model = gtk_tree_view_get_model (tree_view);
668
669 selected = e_proxy_selector_ref_selected (selector);
670
671 gtk_list_store_clear (GTK_LIST_STORE (tree_model));
672
673 extension_name = E_SOURCE_EXTENSION_PROXY;
674 registry = e_proxy_selector_get_registry (selector);
675 list = e_source_registry_list_enabled (registry, extension_name);
676
677 builtin_source = e_source_registry_ref_builtin_proxy (registry);
678 g_warn_if_fail (builtin_source != NULL);
679
680 /* Always list the built-in proxy profile first. */
681 link = g_list_find (list, builtin_source);
682 if (link != NULL && list != link) {
683 list = g_list_remove_link (list, link);
684 list = g_list_concat (link, list);
685 }
686
687 for (link = list; link != NULL; link = g_list_next (link)) {
688 ESource *source;
689 GtkTreeIter iter;
690 const gchar *display_name;
691
692 source = E_SOURCE (link->data);
693 display_name = e_source_get_display_name (source);
694
695 gtk_list_store_append (GTK_LIST_STORE (tree_model), &iter);
696
697 gtk_list_store_set (
698 GTK_LIST_STORE (tree_model), &iter,
699 COLUMN_DISPLAY_NAME, display_name,
700 COLUMN_SOURCE, source, -1);
701 }
702
703 g_clear_object (&builtin_source);
704
705 g_list_free_full (list, (GDestroyNotify) g_object_unref);
706
707 /* Try and restore the previous selected source or else pick
708 * the built-in proxy profile, which is always listed first. */
709
710 e_proxy_selector_set_selected (selector, selected);
711
712 g_clear_object (&selected);
713 }
714
715 /**
716 * e_proxy_selector_get_registry:
717 * @selector: an #EProxySelector
718 *
719 * Returns the #ESourceRegistry passed to e_proxy_selector_get_registry().
720 *
721 * Returns: an #ESourceRegistry
722 **/
723 ESourceRegistry *
e_proxy_selector_get_registry(EProxySelector * selector)724 e_proxy_selector_get_registry (EProxySelector *selector)
725 {
726 g_return_val_if_fail (E_IS_PROXY_SELECTOR (selector), NULL);
727
728 return selector->priv->registry;
729 }
730
731 /**
732 * e_proxy_selector_ref_selected:
733 * @selector: an #EProxySelector
734 *
735 * Returns the selected #ESource in @selector.
736 *
737 * The function tries to ensure a valid #ESource is always returned,
738 * falling back to e_source_registry_ref_builtin_proxy() if necessary.
739 *
740 * The returned #ESource is referenced for thread-safety and must be
741 * unreferenced with g_object_unref() when finished with it.
742 *
743 * Returns: an #ESource
744 **/
745 ESource *
e_proxy_selector_ref_selected(EProxySelector * selector)746 e_proxy_selector_ref_selected (EProxySelector *selector)
747 {
748 ETreeViewFrame *tree_view_frame;
749 GtkTreeView *tree_view;
750 GtkTreeModel *tree_model;
751 GtkTreeSelection *selection;
752 GtkTreeIter iter;
753 ESource *source = NULL;
754
755 g_return_val_if_fail (E_IS_PROXY_SELECTOR (selector), NULL);
756
757 tree_view_frame = E_TREE_VIEW_FRAME (selector);
758 tree_view = e_tree_view_frame_get_tree_view (tree_view_frame);
759 selection = gtk_tree_view_get_selection (tree_view);
760
761 if (gtk_tree_selection_get_selected (selection, &tree_model, &iter)) {
762 gtk_tree_model_get (
763 tree_model, &iter,
764 COLUMN_SOURCE, &source, -1);
765 }
766
767 /* The built-in proxy profile is implicitly selected when
768 * no proxy profile is actually selected in the tree view. */
769 if (source == NULL) {
770 ESourceRegistry *registry;
771
772 registry = e_proxy_selector_get_registry (selector);
773 source = e_source_registry_ref_builtin_proxy (registry);
774 g_return_val_if_fail (source != NULL, NULL);
775 }
776
777 return source;
778 }
779
780 /**
781 * e_proxy_selector_set_selected:
782 * @selector: an #EProxySelector
783 * @source: an #ESource, or %NULL for the built-in proxy profile
784 *
785 * Finds the corresponding tree model row for @source, selects the row,
786 * and returns %TRUE. If no corresponding tree model row for @source is
787 * found, the selection remains unchanged and the function returns %FALSE.
788 *
789 * Returns: whether @source was selected
790 **/
791 gboolean
e_proxy_selector_set_selected(EProxySelector * selector,ESource * source)792 e_proxy_selector_set_selected (EProxySelector *selector,
793 ESource *source)
794 {
795 ETreeViewFrame *tree_view_frame;
796 GtkTreeView *tree_view;
797 GtkTreeModel *tree_model;
798 GtkTreeIter iter;
799 gboolean iter_valid;
800
801 g_return_val_if_fail (E_IS_PROXY_SELECTOR (selector), FALSE);
802 g_return_val_if_fail (source == NULL || E_IS_SOURCE (source), FALSE);
803
804 if (source == NULL) {
805 ESourceRegistry *registry;
806
807 registry = e_proxy_selector_get_registry (selector);
808 source = e_source_registry_ref_builtin_proxy (registry);
809 g_return_val_if_fail (source != NULL, FALSE);
810 }
811
812 tree_view_frame = E_TREE_VIEW_FRAME (selector);
813 tree_view = e_tree_view_frame_get_tree_view (tree_view_frame);
814 tree_model = gtk_tree_view_get_model (tree_view);
815
816 iter_valid = gtk_tree_model_get_iter_first (tree_model, &iter);
817
818 while (iter_valid) {
819 ESource *candidate = NULL;
820 gboolean match;
821
822 gtk_tree_model_get (
823 tree_model, &iter,
824 COLUMN_SOURCE, &candidate, -1);
825
826 match = e_source_equal (source, candidate);
827
828 g_object_unref (candidate);
829
830 if (match)
831 break;
832
833 iter_valid = gtk_tree_model_iter_next (tree_model, &iter);
834 }
835
836 if (iter_valid) {
837 GtkTreeSelection *selection;
838
839 selection = gtk_tree_view_get_selection (tree_view);
840 gtk_tree_selection_select_iter (selection, &iter);
841 }
842
843 return iter_valid;
844 }
845
846