1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* e-source-selector.c
3 *
4 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * published by the Free Software Foundation; either the
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 *
18 * Author: Ettore Perazzoli <ettore@ximian.com>
19 */
20
21 #include "evolution-config.h"
22
23 #include <string.h>
24 #include <glib/gi18n-lib.h>
25
26 #include <libedataserverui/libedataserverui.h>
27
28 #include "e-source-selector.h"
29
30 #define E_SOURCE_SELECTOR_GET_PRIVATE(obj) \
31 (G_TYPE_INSTANCE_GET_PRIVATE \
32 ((obj), E_TYPE_SOURCE_SELECTOR, ESourceSelectorPrivate))
33
34 typedef struct _AsyncContext AsyncContext;
35
36 struct _ESourceSelectorPrivate {
37 ESourceRegistry *registry;
38 gulong source_added_handler_id;
39 gulong source_changed_handler_id;
40 gulong source_removed_handler_id;
41 gulong source_enabled_handler_id;
42 gulong source_disabled_handler_id;
43
44 GHashTable *source_index;
45 gchar *extension_name;
46
47 GtkTreeRowReference *saved_primary_selection;
48
49 /* ESource -> GSource */
50 GHashTable *pending_writes;
51 GMainContext *main_context;
52
53 gboolean toggled_last;
54 gboolean show_colors;
55 gboolean show_icons;
56 gboolean show_toggles;
57
58 GtkCellRenderer *busy_renderer;
59 guint n_busy_sources;
60 gulong update_busy_renderer_id;
61
62 GHashTable *hidden_groups;
63 GSList *groups_order;
64 };
65
66 struct _AsyncContext {
67 ESourceSelector *selector;
68 ESource *source;
69 };
70
71 enum {
72 PROP_0,
73 PROP_EXTENSION_NAME,
74 PROP_PRIMARY_SELECTION,
75 PROP_REGISTRY,
76 PROP_SHOW_COLORS,
77 PROP_SHOW_ICONS,
78 PROP_SHOW_TOGGLES
79 };
80
81 enum {
82 SELECTION_CHANGED,
83 PRIMARY_SELECTION_CHANGED,
84 POPUP_EVENT,
85 DATA_DROPPED,
86 SOURCE_SELECTED,
87 SOURCE_UNSELECTED,
88 FILTER_SOURCE,
89 NUM_SIGNALS
90 };
91
92 enum {
93 COLUMN_NAME,
94 COLUMN_COLOR,
95 COLUMN_ACTIVE,
96 COLUMN_ICON_NAME,
97 COLUMN_SHOW_COLOR,
98 COLUMN_SHOW_ICONS,
99 COLUMN_SHOW_TOGGLE,
100 COLUMN_WEIGHT,
101 COLUMN_SOURCE,
102 COLUMN_TOOLTIP,
103 COLUMN_IS_BUSY,
104 COLUMN_CONNECTION_STATUS,
105 COLUMN_SORT_ORDER,
106 NUM_COLUMNS
107 };
108
109 static guint signals[NUM_SIGNALS];
110
111 G_DEFINE_TYPE (ESourceSelector, e_source_selector, GTK_TYPE_TREE_VIEW)
112
113 /* ESafeToggleRenderer does not emit 'toggled' signal
114 * on 'activate' when mouse is not over the toggle. */
115
116 typedef GtkCellRendererToggle ECellRendererSafeToggle;
117 typedef GtkCellRendererToggleClass ECellRendererSafeToggleClass;
118
119 /* Forward Declarations */
120 GType e_cell_renderer_safe_toggle_get_type
121 (void) G_GNUC_CONST;
122 static void selection_changed_callback (GtkTreeSelection *selection,
123 ESourceSelector *selector);
124
G_DEFINE_TYPE(ECellRendererSafeToggle,e_cell_renderer_safe_toggle,GTK_TYPE_CELL_RENDERER_TOGGLE)125 G_DEFINE_TYPE (
126 ECellRendererSafeToggle,
127 e_cell_renderer_safe_toggle,
128 GTK_TYPE_CELL_RENDERER_TOGGLE)
129
130 static gboolean
131 safe_toggle_activate (GtkCellRenderer *cell,
132 GdkEvent *event,
133 GtkWidget *widget,
134 const gchar *path,
135 const GdkRectangle *background_area,
136 const GdkRectangle *cell_area,
137 GtkCellRendererState flags)
138 {
139 gboolean point_in_cell_area = TRUE;
140
141 if (event && event->type == GDK_BUTTON_PRESS && cell_area != NULL) {
142 cairo_region_t *region;
143
144 region = cairo_region_create_rectangle (cell_area);
145 point_in_cell_area = cairo_region_contains_point (
146 region, event->button.x, event->button.y);
147 cairo_region_destroy (region);
148 }
149
150 if (!point_in_cell_area)
151 return FALSE;
152
153 return GTK_CELL_RENDERER_CLASS (
154 e_cell_renderer_safe_toggle_parent_class)->activate (
155 cell, event, widget, path, background_area, cell_area, flags);
156 }
157
158 static void
e_cell_renderer_safe_toggle_class_init(ECellRendererSafeToggleClass * class)159 e_cell_renderer_safe_toggle_class_init (ECellRendererSafeToggleClass *class)
160 {
161 GtkCellRendererClass *cell_renderer_class;
162
163 cell_renderer_class = GTK_CELL_RENDERER_CLASS (class);
164 cell_renderer_class->activate = safe_toggle_activate;
165 }
166
167 static void
e_cell_renderer_safe_toggle_init(ECellRendererSafeToggle * obj)168 e_cell_renderer_safe_toggle_init (ECellRendererSafeToggle *obj)
169 {
170 }
171
172 static GtkCellRenderer *
e_cell_renderer_safe_toggle_new(void)173 e_cell_renderer_safe_toggle_new (void)
174 {
175 return g_object_new (e_cell_renderer_safe_toggle_get_type (), NULL);
176 }
177
178 static gboolean
source_selector_pulse_busy_renderer_cb(gpointer user_data)179 source_selector_pulse_busy_renderer_cb (gpointer user_data)
180 {
181 ESourceSelector *selector = user_data;
182 GObject *busy_renderer;
183 guint pulse;
184 GHashTableIter iter;
185 gpointer key, value;
186
187 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
188
189 if (!selector->priv->busy_renderer)
190 return FALSE;
191
192 busy_renderer = G_OBJECT (selector->priv->busy_renderer);
193
194 g_object_get (busy_renderer, "pulse", &pulse, NULL);
195
196 pulse++;
197
198 g_object_set (busy_renderer, "pulse", pulse, NULL);
199
200 g_hash_table_iter_init (&iter, selector->priv->source_index);
201 while (g_hash_table_iter_next (&iter, &key, &value)) {
202 GtkTreeRowReference *reference = value;
203 GtkTreeModel *model;
204 GtkTreePath *path;
205 GtkTreeIter tree_iter;
206
207 if (reference && gtk_tree_row_reference_valid (reference)) {
208 gboolean is_busy = FALSE;
209
210 model = gtk_tree_row_reference_get_model (reference);
211 path = gtk_tree_row_reference_get_path (reference);
212 gtk_tree_model_get_iter (model, &tree_iter, path);
213
214 gtk_tree_model_get (
215 model, &tree_iter,
216 COLUMN_IS_BUSY, &is_busy,
217 -1);
218
219 if (is_busy)
220 gtk_tree_model_row_changed (model, path, &tree_iter);
221
222 gtk_tree_path_free (path);
223 }
224 }
225
226 return TRUE;
227 }
228
229 static void
source_selector_inc_busy_sources(ESourceSelector * selector)230 source_selector_inc_busy_sources (ESourceSelector *selector)
231 {
232 selector->priv->n_busy_sources++;
233
234 if (selector->priv->busy_renderer && !selector->priv->update_busy_renderer_id)
235 selector->priv->update_busy_renderer_id =
236 e_named_timeout_add (123, source_selector_pulse_busy_renderer_cb, selector);
237 }
238
239 static void
source_selector_dec_busy_sources(ESourceSelector * selector)240 source_selector_dec_busy_sources (ESourceSelector *selector)
241 {
242 g_return_if_fail (selector->priv->n_busy_sources > 0);
243
244 selector->priv->n_busy_sources--;
245
246 if (selector->priv->n_busy_sources == 0 && selector->priv->update_busy_renderer_id) {
247 g_source_remove (selector->priv->update_busy_renderer_id);
248 selector->priv->update_busy_renderer_id = 0;
249 }
250 }
251
252 static void
clear_saved_primary_selection(ESourceSelector * selector)253 clear_saved_primary_selection (ESourceSelector *selector)
254 {
255 gtk_tree_row_reference_free (selector->priv->saved_primary_selection);
256 selector->priv->saved_primary_selection = NULL;
257 }
258
259 static void
async_context_free(AsyncContext * async_context)260 async_context_free (AsyncContext *async_context)
261 {
262 if (async_context->selector != NULL)
263 g_object_unref (async_context->selector);
264
265 if (async_context->source != NULL)
266 g_object_unref (async_context->source);
267
268 g_slice_free (AsyncContext, async_context);
269 }
270
271 static void
pending_writes_destroy_source(GSource * source)272 pending_writes_destroy_source (GSource *source)
273 {
274 g_source_destroy (source);
275 g_source_unref (source);
276 }
277
278 static void
source_selector_write_done_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)279 source_selector_write_done_cb (GObject *source_object,
280 GAsyncResult *result,
281 gpointer user_data)
282 {
283 ESource *source;
284 ESourceSelector *selector;
285 GError *error = NULL;
286
287 source = E_SOURCE (source_object);
288 selector = E_SOURCE_SELECTOR (user_data);
289
290 e_source_write_finish (source, result, &error);
291
292 /* FIXME Display the error in the selector somehow? */
293 if (error != NULL) {
294 g_warning ("%s: %s", G_STRFUNC, error->message);
295 g_error_free (error);
296 }
297
298 g_object_unref (selector);
299 }
300
301 static gboolean
source_selector_write_idle_cb(gpointer user_data)302 source_selector_write_idle_cb (gpointer user_data)
303 {
304 AsyncContext *async_context = user_data;
305 GHashTable *pending_writes;
306
307 /* XXX This operation is not cancellable. */
308 e_source_write (
309 async_context->source, NULL,
310 source_selector_write_done_cb,
311 g_object_ref (async_context->selector));
312
313 pending_writes = async_context->selector->priv->pending_writes;
314 g_hash_table_remove (pending_writes, async_context->source);
315
316 return FALSE;
317 }
318
319 static void
source_selector_cancel_write(ESourceSelector * selector,ESource * source)320 source_selector_cancel_write (ESourceSelector *selector,
321 ESource *source)
322 {
323 GHashTable *pending_writes;
324
325 /* Cancel any pending writes for this ESource so as not
326 * to overwrite whatever change we're being notified of. */
327 pending_writes = selector->priv->pending_writes;
328 g_hash_table_remove (pending_writes, source);
329 }
330
331 static const gchar *
source_selector_get_icon_name(ESourceSelector * selector,ESource * source)332 source_selector_get_icon_name (ESourceSelector *selector,
333 ESource *source)
334 {
335 const gchar *extension_name;
336 const gchar *icon_name = NULL;
337
338 /* XXX These are the same icons used in EShellView subclasses.
339 * We should really centralize these icon names somewhere. */
340
341 extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
342 if (e_source_has_extension (source, extension_name))
343 icon_name = "x-office-address-book";
344
345 extension_name = E_SOURCE_EXTENSION_CALENDAR;
346 if (e_source_has_extension (source, extension_name))
347 icon_name = "x-office-calendar";
348
349 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
350 if (e_source_has_extension (source, extension_name))
351 icon_name = "evolution-mail";
352
353 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
354 if (e_source_has_extension (source, extension_name))
355 icon_name = "mail-send";
356
357 extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
358 if (e_source_has_extension (source, extension_name))
359 icon_name = "evolution-memos";
360
361 extension_name = E_SOURCE_EXTENSION_TASK_LIST;
362 if (e_source_has_extension (source, extension_name))
363 icon_name = "evolution-tasks";
364
365 return icon_name;
366 }
367
368 static gboolean
source_selector_source_is_enabled_and_selected(ESource * source,const gchar * extension_name)369 source_selector_source_is_enabled_and_selected (ESource *source,
370 const gchar *extension_name)
371 {
372 gpointer extension;
373
374 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
375
376 if (!extension_name ||
377 !e_source_get_enabled (source))
378 return e_source_get_enabled (source);
379
380 if (!e_source_has_extension (source, extension_name))
381 return FALSE;
382
383 extension = e_source_get_extension (source, extension_name);
384 if (!E_IS_SOURCE_SELECTABLE (extension))
385 return TRUE;
386
387 return e_source_selectable_get_selected (extension);
388 }
389
390 typedef struct {
391 const gchar *extension_name;
392 gboolean show_toggles;
393 gboolean any_selected;
394 } LookupSelectedData;
395
396 static gboolean
source_selector_lookup_selected_cb(GNode * node,gpointer user_data)397 source_selector_lookup_selected_cb (GNode *node,
398 gpointer user_data)
399 {
400 LookupSelectedData *data = user_data;
401 ESource *source;
402
403 g_return_val_if_fail (data != NULL, TRUE);
404 g_return_val_if_fail (data->extension_name != NULL, TRUE);
405
406 source = node->data;
407 if (!E_IS_SOURCE (source))
408 return TRUE;
409
410 data->any_selected = data->show_toggles && source_selector_source_is_enabled_and_selected (source, data->extension_name);
411
412 return data->any_selected;
413 }
414
415 static gboolean
source_selector_node_is_hidden(ESourceSelector * selector,GNode * main_node)416 source_selector_node_is_hidden (ESourceSelector *selector,
417 GNode *main_node)
418 {
419 GNode *node;
420 ESource *source;
421 const gchar *extension_name;
422 LookupSelectedData data;
423 gboolean hidden;
424
425 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
426 g_return_val_if_fail (main_node != NULL, FALSE);
427
428 if (G_NODE_IS_ROOT (main_node))
429 return FALSE;
430
431 extension_name = e_source_selector_get_extension_name (selector);
432 data.show_toggles = e_source_selector_get_show_toggles (selector);
433 hidden = FALSE;
434
435 /* Check the path to the root, any is hidden, this one can be also hidden */
436 node = main_node;
437 while (node) {
438 gboolean hidden_by_filter = FALSE;
439
440 source = node->data;
441
442 if (!source || G_NODE_IS_ROOT (node))
443 break;
444
445 g_signal_emit (selector, signals[FILTER_SOURCE], 0, source, &hidden_by_filter);
446
447 if (hidden_by_filter)
448 return TRUE;
449
450 if (data.show_toggles && source_selector_source_is_enabled_and_selected (source, extension_name)) {
451 hidden = FALSE;
452 break;
453 }
454
455 hidden = hidden || g_hash_table_contains (selector->priv->hidden_groups, e_source_get_uid (source));
456
457 node = node->parent;
458 }
459
460 if (!hidden)
461 return FALSE;
462
463 /* If any source in this subtree/group is enabled and selected,
464 then the group cannot be hidden */
465
466 node = main_node;
467 if (node->parent && !G_NODE_IS_ROOT (node->parent)) {
468 node = node->parent;
469 }
470
471 data.extension_name = extension_name;
472 data.any_selected = FALSE;
473
474 g_node_traverse (node, G_IN_ORDER, G_TRAVERSE_ALL, -1, source_selector_lookup_selected_cb, &data);
475
476 return !data.any_selected;
477 }
478
479 static gboolean
source_selector_traverse(GNode * node,ESourceSelector * selector)480 source_selector_traverse (GNode *node,
481 ESourceSelector *selector)
482 {
483 ESource *source;
484 GHashTable *source_index;
485 GtkTreeRowReference *reference = NULL;
486 GtkTreeModel *model;
487 GtkTreePath *path;
488 GtkTreeIter iter;
489
490 /* Skip the root node. */
491 if (G_NODE_IS_ROOT (node))
492 return FALSE;
493
494 if (source_selector_node_is_hidden (selector, node))
495 return FALSE;
496
497 source_index = selector->priv->source_index;
498
499 model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector));
500
501 if (node->parent != NULL && node->parent->data != NULL)
502 reference = g_hash_table_lookup (
503 source_index, node->parent->data);
504
505 if (gtk_tree_row_reference_valid (reference)) {
506 GtkTreeIter parent;
507
508 path = gtk_tree_row_reference_get_path (reference);
509 gtk_tree_model_get_iter (model, &parent, path);
510 gtk_tree_path_free (path);
511
512 gtk_tree_store_append (GTK_TREE_STORE (model), &iter, &parent);
513 } else
514 gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
515
516 source = E_SOURCE (node->data);
517
518 path = gtk_tree_model_get_path (model, &iter);
519 reference = gtk_tree_row_reference_new (model, path);
520 g_hash_table_insert (source_index, g_object_ref (source), reference);
521 gtk_tree_path_free (path);
522
523 e_source_selector_update_row (selector, source);
524
525 return FALSE;
526 }
527
528 static void
source_selector_save_expanded(GtkTreeView * tree_view,GtkTreePath * path,GQueue * queue)529 source_selector_save_expanded (GtkTreeView *tree_view,
530 GtkTreePath *path,
531 GQueue *queue)
532 {
533 GtkTreeModel *model;
534 GtkTreeIter iter;
535 ESource *source;
536
537 model = gtk_tree_view_get_model (tree_view);
538 gtk_tree_model_get_iter (model, &iter, path);
539 gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
540 g_queue_push_tail (queue, source);
541 }
542
543 typedef struct _SavedStatus
544 {
545 gboolean is_busy;
546 gchar *tooltip;
547 } SavedStatusData;
548
549 static void
saved_status_data_free(gpointer ptr)550 saved_status_data_free (gpointer ptr)
551 {
552 SavedStatusData *data = ptr;
553
554 if (data) {
555 g_free (data->tooltip);
556 g_slice_free (SavedStatusData, data);
557 }
558 }
559
560 static GHashTable *
source_selector_save_sources_status(ESourceSelector * selector)561 source_selector_save_sources_status (ESourceSelector *selector)
562 {
563 GHashTable *status;
564 GHashTableIter iter;
565 gpointer key, value;
566
567 status = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, saved_status_data_free);
568
569 g_hash_table_iter_init (&iter, selector->priv->source_index);
570 while (g_hash_table_iter_next (&iter, &key, &value)) {
571 ESource *source = key;
572 GtkTreeRowReference *reference = value;
573 GtkTreeModel *model;
574 GtkTreePath *path;
575 GtkTreeIter tree_iter;
576
577 if (reference && gtk_tree_row_reference_valid (reference)) {
578 SavedStatusData *data;
579
580 model = gtk_tree_row_reference_get_model (reference);
581 path = gtk_tree_row_reference_get_path (reference);
582 gtk_tree_model_get_iter (model, &tree_iter, path);
583
584 data = g_slice_new0 (SavedStatusData);
585
586 gtk_tree_model_get (
587 model, &tree_iter,
588 COLUMN_IS_BUSY, &data->is_busy,
589 COLUMN_TOOLTIP, &data->tooltip,
590 -1);
591
592 if (data->is_busy)
593 source_selector_dec_busy_sources (selector);
594
595 gtk_tree_path_free (path);
596
597 g_hash_table_insert (status, g_strdup (e_source_get_uid (source)), data);
598 }
599 }
600
601 return status;
602 }
603
604 static void
source_selector_load_sources_status(ESourceSelector * selector,GHashTable * status)605 source_selector_load_sources_status (ESourceSelector *selector,
606 GHashTable *status)
607 {
608 GHashTableIter iter;
609 gpointer key, value;
610
611 g_hash_table_iter_init (&iter, selector->priv->source_index);
612 while (g_hash_table_iter_next (&iter, &key, &value)) {
613 ESource *source = key;
614 GtkTreeRowReference *reference = value;
615 GtkTreeModel *model;
616 GtkTreePath *path;
617 GtkTreeIter tree_iter;
618
619 if (reference && gtk_tree_row_reference_valid (reference)) {
620 SavedStatusData *data;
621
622 model = gtk_tree_row_reference_get_model (reference);
623 path = gtk_tree_row_reference_get_path (reference);
624 gtk_tree_model_get_iter (model, &tree_iter, path);
625
626 gtk_tree_path_free (path);
627
628 data = g_hash_table_lookup (status, e_source_get_uid (source));
629 if (!data)
630 continue;
631
632 gtk_tree_store_set (
633 GTK_TREE_STORE (model), &tree_iter,
634 COLUMN_IS_BUSY, data->is_busy,
635 COLUMN_TOOLTIP, data->tooltip,
636 -1);
637
638 if (data->is_busy)
639 source_selector_inc_busy_sources (selector);
640 }
641 }
642 }
643
644 static void
source_selector_sort_groups(ESourceSelector * selector,GNode * root)645 source_selector_sort_groups (ESourceSelector *selector,
646 GNode *root)
647 {
648 GHashTable *groups; /* gchar *uid, GUINT index into node_sources */
649 GPtrArray *node_sources; /* GNode * as stored in the root first sub-level */
650 ESource *source;
651 GNode *node;
652 GSList *link;
653 guint ii;
654
655 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
656 g_return_if_fail (G_NODE_IS_ROOT (root));
657
658 if (!selector->priv->groups_order ||
659 !g_node_n_children (root))
660 return;
661
662 groups = g_hash_table_new (g_str_hash, g_str_equal);
663 node_sources = g_ptr_array_sized_new (g_node_n_children (root));
664
665 node = g_node_first_child (root);
666 while (node) {
667 GNode *next_node = g_node_next_sibling (node);
668
669 source = node->data;
670
671 if (source) {
672 g_node_unlink (node);
673
674 g_hash_table_insert (groups, (gpointer) e_source_get_uid (source), GUINT_TO_POINTER (node_sources->len));
675 g_ptr_array_add (node_sources, node);
676 }
677
678 node = next_node;
679 }
680
681 /* First add known nodes as defined by the user... */
682 for (link = selector->priv->groups_order; link; link = g_slist_next (link)) {
683 const gchar *uid = link->data;
684
685 if (!uid || !g_hash_table_contains (groups, uid))
686 continue;
687
688 ii = GPOINTER_TO_UINT (g_hash_table_lookup (groups, uid));
689 g_warn_if_fail (ii < node_sources->len);
690
691 node = node_sources->pdata[ii];
692 node_sources->pdata[ii] = NULL;
693
694 if (node)
695 g_node_append (root, node);
696 }
697
698 /* ... then add all unknown (new) sources in the order
699 as they were in the passed-in tree */
700 for (ii = 0; ii < node_sources->len; ii++) {
701 node = node_sources->pdata[ii];
702
703 if (node)
704 g_node_append (root, node);
705 }
706
707 g_ptr_array_unref (node_sources);
708 g_hash_table_destroy (groups);
709 }
710
711 static void
source_selector_build_model(ESourceSelector * selector)712 source_selector_build_model (ESourceSelector *selector)
713 {
714 ESourceRegistry *registry;
715 GQueue queue = G_QUEUE_INIT;
716 GHashTable *source_index;
717 GtkTreeView *tree_view;
718 GtkTreeSelection *selection;
719 GtkTreeModel *model;
720 GHashTable *saved_status;
721 ESource *selected;
722 const gchar *extension_name;
723 GNode *root;
724
725 tree_view = GTK_TREE_VIEW (selector);
726
727 registry = e_source_selector_get_registry (selector);
728 extension_name = e_source_selector_get_extension_name (selector);
729
730 /* Make sure we have what we need to build the model, since
731 * this can get called early in the initialization phase. */
732 if (registry == NULL || extension_name == NULL)
733 return;
734
735 source_index = selector->priv->source_index;
736 selected = e_source_selector_ref_primary_selection (selector);
737 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (selector));
738 saved_status = source_selector_save_sources_status (selector);
739
740 /* Signal is blocked to avoid "primary-selection-changed" signal
741 * on model clear. */
742 g_signal_handlers_block_matched (
743 selection, G_SIGNAL_MATCH_FUNC,
744 0, 0, NULL, selection_changed_callback, NULL);
745
746 /* Save expanded sources to restore later. */
747 gtk_tree_view_map_expanded_rows (
748 tree_view, (GtkTreeViewMappingFunc)
749 source_selector_save_expanded, &queue);
750
751 model = gtk_tree_view_get_model (tree_view);
752 gtk_tree_store_clear (GTK_TREE_STORE (model));
753
754 g_hash_table_remove_all (source_index);
755
756 root = e_source_registry_build_display_tree (registry, extension_name);
757
758 source_selector_sort_groups (selector, root);
759
760 g_node_traverse (
761 root, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
762 (GNodeTraverseFunc) source_selector_traverse,
763 selector);
764
765 e_source_registry_free_display_tree (root);
766
767 /* Restore previously expanded sources. */
768 while (!g_queue_is_empty (&queue)) {
769 GtkTreeRowReference *reference;
770 ESource *source;
771
772 source = g_queue_pop_head (&queue);
773 reference = g_hash_table_lookup (source_index, source);
774
775 if (gtk_tree_row_reference_valid (reference)) {
776 GtkTreePath *path;
777
778 path = gtk_tree_row_reference_get_path (reference);
779 gtk_tree_view_expand_to_path (tree_view, path);
780 gtk_tree_path_free (path);
781 }
782
783 g_object_unref (source);
784 }
785
786 /* Restore the primary selection. */
787 if (selected != NULL) {
788 e_source_selector_set_primary_selection (selector, selected);
789 g_object_unref (selected);
790 }
791
792 /* If the first succeeded, then there is no selection change,
793 * thus no need for notification; notify about the change in
794 * any other cases. */
795 g_signal_handlers_unblock_matched (
796 selection, G_SIGNAL_MATCH_FUNC,
797 0, 0, NULL, selection_changed_callback, NULL);
798
799 /* Make sure we have a primary selection. If not, pick one. */
800 selected = e_source_selector_ref_primary_selection (selector);
801 if (selected == NULL) {
802 selected = e_source_registry_ref_default_for_extension_name (
803 registry, extension_name);
804 }
805 if (selected != NULL) {
806 e_source_selector_set_primary_selection (selector, selected);
807 g_object_unref (selected);
808 }
809
810 source_selector_load_sources_status (selector, saved_status);
811 g_hash_table_destroy (saved_status);
812 }
813
814 static void
source_selector_expand_to_source(ESourceSelector * selector,ESource * source)815 source_selector_expand_to_source (ESourceSelector *selector,
816 ESource *source)
817 {
818 GHashTable *source_index;
819 GtkTreeRowReference *reference;
820 GtkTreePath *path;
821
822 source_index = selector->priv->source_index;
823 reference = g_hash_table_lookup (source_index, source);
824
825 /* If the ESource is not in our tree model then return silently. */
826 if (reference == NULL)
827 return;
828
829 /* If we do have a row reference, it should be valid. */
830 g_return_if_fail (gtk_tree_row_reference_valid (reference));
831
832 /* Expand the tree view to the path containing the ESource */
833 path = gtk_tree_row_reference_get_path (reference);
834 gtk_tree_view_expand_to_path (GTK_TREE_VIEW (selector), path);
835 gtk_tree_path_free (path);
836 }
837
838 static void
source_selector_source_added_cb(ESourceRegistry * registry,ESource * source,ESourceSelector * selector)839 source_selector_source_added_cb (ESourceRegistry *registry,
840 ESource *source,
841 ESourceSelector *selector)
842 {
843 const gchar *extension_name;
844
845 extension_name = e_source_selector_get_extension_name (selector);
846
847 if (extension_name == NULL)
848 return;
849
850 if (!e_source_has_extension (source, extension_name))
851 return;
852
853 source_selector_build_model (selector);
854
855 source_selector_expand_to_source (selector, source);
856
857 if (e_source_selector_source_is_selected (selector, source))
858 g_signal_emit (selector, signals[SOURCE_SELECTED], 0, source);
859 }
860
861 static void
source_selector_source_changed_cb(ESourceRegistry * registry,ESource * source,ESourceSelector * selector)862 source_selector_source_changed_cb (ESourceRegistry *registry,
863 ESource *source,
864 ESourceSelector *selector)
865 {
866 const gchar *extension_name;
867
868 extension_name = e_source_selector_get_extension_name (selector);
869
870 if (extension_name == NULL)
871 return;
872
873 if (!e_source_has_extension (source, extension_name))
874 return;
875
876 source_selector_cancel_write (selector, source);
877
878 e_source_selector_update_row (selector, source);
879 }
880
881 static void
source_selector_source_removed_cb(ESourceRegistry * registry,ESource * source,ESourceSelector * selector)882 source_selector_source_removed_cb (ESourceRegistry *registry,
883 ESource *source,
884 ESourceSelector *selector)
885 {
886 const gchar *extension_name;
887
888 extension_name = e_source_selector_get_extension_name (selector);
889
890 if (extension_name == NULL)
891 return;
892
893 if (!e_source_has_extension (source, extension_name))
894 return;
895
896 if (e_source_selector_get_source_is_busy (selector, source))
897 source_selector_dec_busy_sources (selector);
898
899 /* Always notify about deselect, regardless whether it is really selected,
900 because listeners won't always know the source is gone otherwise. */
901 g_signal_emit (selector, signals[SOURCE_UNSELECTED], 0, source);
902
903 source_selector_build_model (selector);
904 }
905
906 static void
source_selector_source_enabled_cb(ESourceRegistry * registry,ESource * source,ESourceSelector * selector)907 source_selector_source_enabled_cb (ESourceRegistry *registry,
908 ESource *source,
909 ESourceSelector *selector)
910 {
911 const gchar *extension_name;
912
913 extension_name = e_source_selector_get_extension_name (selector);
914
915 if (extension_name == NULL)
916 return;
917
918 if (!e_source_has_extension (source, extension_name))
919 return;
920
921 source_selector_build_model (selector);
922
923 source_selector_expand_to_source (selector, source);
924
925 if (e_source_selector_source_is_selected (selector, source))
926 g_signal_emit (selector, signals[SOURCE_SELECTED], 0, source);
927 }
928
929 static void
source_selector_source_disabled_cb(ESourceRegistry * registry,ESource * source,ESourceSelector * selector)930 source_selector_source_disabled_cb (ESourceRegistry *registry,
931 ESource *source,
932 ESourceSelector *selector)
933 {
934 const gchar *extension_name;
935
936 extension_name = e_source_selector_get_extension_name (selector);
937
938 if (extension_name == NULL)
939 return;
940
941 if (!e_source_has_extension (source, extension_name))
942 return;
943
944 if (e_source_selector_get_source_is_busy (selector, source))
945 source_selector_dec_busy_sources (selector);
946
947 /* Always notify about deselect, regardless whether it is really selected,
948 because listeners won't always know the source is gone otherwise. */
949 g_signal_emit (selector, signals[SOURCE_UNSELECTED], 0, source);
950
951 source_selector_build_model (selector);
952 }
953
954 static gboolean
same_source_name_exists(ESourceSelector * selector,const gchar * display_name)955 same_source_name_exists (ESourceSelector *selector,
956 const gchar *display_name)
957 {
958 GHashTable *source_index;
959 GHashTableIter iter;
960 gpointer key;
961
962 source_index = selector->priv->source_index;
963 g_hash_table_iter_init (&iter, source_index);
964
965 while (g_hash_table_iter_next (&iter, &key, NULL)) {
966 ESource *source = E_SOURCE (key);
967 const gchar *source_name;
968
969 source_name = e_source_get_display_name (source);
970 if (g_strcmp0 (display_name, source_name) == 0)
971 return TRUE;
972 }
973
974 return FALSE;
975 }
976
977 static gboolean
selection_func(GtkTreeSelection * selection,GtkTreeModel * model,GtkTreePath * path,gboolean path_currently_selected,ESourceSelector * selector)978 selection_func (GtkTreeSelection *selection,
979 GtkTreeModel *model,
980 GtkTreePath *path,
981 gboolean path_currently_selected,
982 ESourceSelector *selector)
983 {
984 ESource *source;
985 GtkTreeIter iter;
986 const gchar *extension_name;
987
988 if (selector->priv->toggled_last) {
989 selector->priv->toggled_last = FALSE;
990 return FALSE;
991 }
992
993 if (path_currently_selected)
994 return TRUE;
995
996 if (!gtk_tree_model_get_iter (model, &iter, path))
997 return FALSE;
998
999 extension_name = e_source_selector_get_extension_name (selector);
1000 gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
1001
1002 if (!e_source_has_extension (source, extension_name)) {
1003 g_object_unref (source);
1004 return FALSE;
1005 }
1006
1007 clear_saved_primary_selection (selector);
1008
1009 g_object_unref (source);
1010
1011 return TRUE;
1012 }
1013
1014 static void
text_cell_edited_cb(ESourceSelector * selector,const gchar * path_string,const gchar * new_name)1015 text_cell_edited_cb (ESourceSelector *selector,
1016 const gchar *path_string,
1017 const gchar *new_name)
1018 {
1019 GtkTreeView *tree_view;
1020 GtkTreeModel *model;
1021 GtkTreePath *path;
1022 GtkTreeIter iter;
1023 ESource *source;
1024
1025 if (new_name == NULL || *new_name == '\0')
1026 return;
1027
1028 if (same_source_name_exists (selector, new_name))
1029 return;
1030
1031 tree_view = GTK_TREE_VIEW (selector);
1032 model = gtk_tree_view_get_model (tree_view);
1033
1034 path = gtk_tree_path_new_from_string (path_string);
1035 gtk_tree_model_get_iter (model, &iter, path);
1036 gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
1037 gtk_tree_path_free (path);
1038
1039 e_source_set_display_name (source, new_name);
1040
1041 e_source_selector_queue_write (selector, source);
1042
1043 g_object_unref (source);
1044 }
1045
1046 static void
cell_toggled_callback(GtkCellRendererToggle * renderer,const gchar * path_string,ESourceSelector * selector)1047 cell_toggled_callback (GtkCellRendererToggle *renderer,
1048 const gchar *path_string,
1049 ESourceSelector *selector)
1050 {
1051 ESource *source;
1052 GtkTreeModel *model;
1053 GtkTreePath *path;
1054 GtkTreeIter iter;
1055
1056 model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector));
1057 path = gtk_tree_path_new_from_string (path_string);
1058
1059 if (!gtk_tree_model_get_iter (model, &iter, path)) {
1060 gtk_tree_path_free (path);
1061 return;
1062 }
1063
1064 gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
1065
1066 if (e_source_selector_source_is_selected (selector, source))
1067 e_source_selector_unselect_source (selector, source);
1068 else
1069 e_source_selector_select_source (selector, source);
1070
1071 selector->priv->toggled_last = TRUE;
1072
1073 gtk_tree_path_free (path);
1074
1075 g_object_unref (source);
1076 }
1077
1078 static void
selection_changed_callback(GtkTreeSelection * selection,ESourceSelector * selector)1079 selection_changed_callback (GtkTreeSelection *selection,
1080 ESourceSelector *selector)
1081 {
1082 g_signal_emit (selector, signals[PRIMARY_SELECTION_CHANGED], 0);
1083 g_object_notify (G_OBJECT (selector), "primary-selection");
1084 }
1085
1086 static void
source_selector_set_extension_name(ESourceSelector * selector,const gchar * extension_name)1087 source_selector_set_extension_name (ESourceSelector *selector,
1088 const gchar *extension_name)
1089 {
1090 g_return_if_fail (extension_name != NULL);
1091 g_return_if_fail (selector->priv->extension_name == NULL);
1092
1093 selector->priv->extension_name = g_strdup (extension_name);
1094 }
1095
1096 static void
source_selector_set_registry(ESourceSelector * selector,ESourceRegistry * registry)1097 source_selector_set_registry (ESourceSelector *selector,
1098 ESourceRegistry *registry)
1099 {
1100 g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
1101 g_return_if_fail (selector->priv->registry == NULL);
1102
1103 selector->priv->registry = g_object_ref (registry);
1104 }
1105
1106 static void
source_selector_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)1107 source_selector_set_property (GObject *object,
1108 guint property_id,
1109 const GValue *value,
1110 GParamSpec *pspec)
1111 {
1112 switch (property_id) {
1113 case PROP_EXTENSION_NAME:
1114 source_selector_set_extension_name (
1115 E_SOURCE_SELECTOR (object),
1116 g_value_get_string (value));
1117 return;
1118
1119 case PROP_PRIMARY_SELECTION:
1120 e_source_selector_set_primary_selection (
1121 E_SOURCE_SELECTOR (object),
1122 g_value_get_object (value));
1123 return;
1124
1125 case PROP_REGISTRY:
1126 source_selector_set_registry (
1127 E_SOURCE_SELECTOR (object),
1128 g_value_get_object (value));
1129 return;
1130
1131 case PROP_SHOW_COLORS:
1132 e_source_selector_set_show_colors (
1133 E_SOURCE_SELECTOR (object),
1134 g_value_get_boolean (value));
1135 return;
1136
1137 case PROP_SHOW_ICONS:
1138 e_source_selector_set_show_icons (
1139 E_SOURCE_SELECTOR (object),
1140 g_value_get_boolean (value));
1141 return;
1142
1143 case PROP_SHOW_TOGGLES:
1144 e_source_selector_set_show_toggles (
1145 E_SOURCE_SELECTOR (object),
1146 g_value_get_boolean (value));
1147 return;
1148 }
1149
1150 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1151 }
1152
1153 static void
source_selector_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)1154 source_selector_get_property (GObject *object,
1155 guint property_id,
1156 GValue *value,
1157 GParamSpec *pspec)
1158 {
1159 switch (property_id) {
1160 case PROP_EXTENSION_NAME:
1161 g_value_set_string (
1162 value,
1163 e_source_selector_get_extension_name (
1164 E_SOURCE_SELECTOR (object)));
1165 return;
1166
1167 case PROP_PRIMARY_SELECTION:
1168 g_value_take_object (
1169 value,
1170 e_source_selector_ref_primary_selection (
1171 E_SOURCE_SELECTOR (object)));
1172 return;
1173
1174 case PROP_REGISTRY:
1175 g_value_set_object (
1176 value,
1177 e_source_selector_get_registry (
1178 E_SOURCE_SELECTOR (object)));
1179 return;
1180
1181 case PROP_SHOW_COLORS:
1182 g_value_set_boolean (
1183 value,
1184 e_source_selector_get_show_colors (
1185 E_SOURCE_SELECTOR (object)));
1186 return;
1187
1188 case PROP_SHOW_ICONS:
1189 g_value_set_boolean (
1190 value,
1191 e_source_selector_get_show_icons (
1192 E_SOURCE_SELECTOR (object)));
1193 return;
1194
1195 case PROP_SHOW_TOGGLES:
1196 g_value_set_boolean (
1197 value,
1198 e_source_selector_get_show_toggles (
1199 E_SOURCE_SELECTOR (object)));
1200 return;
1201 }
1202
1203 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1204 }
1205
1206 static void
source_selector_dispose(GObject * object)1207 source_selector_dispose (GObject *object)
1208 {
1209 ESourceSelectorPrivate *priv;
1210
1211 priv = E_SOURCE_SELECTOR_GET_PRIVATE (object);
1212
1213 if (priv->update_busy_renderer_id) {
1214 g_source_remove (priv->update_busy_renderer_id);
1215 priv->update_busy_renderer_id = 0;
1216 }
1217
1218 if (priv->source_added_handler_id > 0) {
1219 g_signal_handler_disconnect (
1220 priv->registry,
1221 priv->source_added_handler_id);
1222 priv->source_added_handler_id = 0;
1223 }
1224
1225 if (priv->source_changed_handler_id > 0) {
1226 g_signal_handler_disconnect (
1227 priv->registry,
1228 priv->source_changed_handler_id);
1229 priv->source_changed_handler_id = 0;
1230 }
1231
1232 if (priv->source_removed_handler_id > 0) {
1233 g_signal_handler_disconnect (
1234 priv->registry,
1235 priv->source_removed_handler_id);
1236 priv->source_removed_handler_id = 0;
1237 }
1238
1239 if (priv->source_enabled_handler_id > 0) {
1240 g_signal_handler_disconnect (
1241 priv->registry,
1242 priv->source_enabled_handler_id);
1243 priv->source_enabled_handler_id = 0;
1244 }
1245
1246 if (priv->source_disabled_handler_id > 0) {
1247 g_signal_handler_disconnect (
1248 priv->registry,
1249 priv->source_disabled_handler_id);
1250 priv->source_disabled_handler_id = 0;
1251 }
1252
1253 g_clear_object (&priv->registry);
1254 g_clear_object (&priv->busy_renderer);
1255
1256 g_hash_table_remove_all (priv->source_index);
1257 g_hash_table_remove_all (priv->pending_writes);
1258 g_hash_table_remove_all (priv->hidden_groups);
1259
1260 g_slist_free_full (priv->groups_order, g_free);
1261 priv->groups_order = NULL;
1262
1263 clear_saved_primary_selection (E_SOURCE_SELECTOR (object));
1264
1265 /* Chain up to parent's dispose() method. */
1266 G_OBJECT_CLASS (e_source_selector_parent_class)->dispose (object);
1267 }
1268
1269 static void
source_selector_finalize(GObject * object)1270 source_selector_finalize (GObject *object)
1271 {
1272 ESourceSelectorPrivate *priv;
1273
1274 priv = E_SOURCE_SELECTOR_GET_PRIVATE (object);
1275
1276 g_hash_table_destroy (priv->source_index);
1277 g_hash_table_destroy (priv->pending_writes);
1278 g_hash_table_destroy (priv->hidden_groups);
1279
1280 g_free (priv->extension_name);
1281
1282 if (priv->main_context != NULL)
1283 g_main_context_unref (priv->main_context);
1284
1285 /* Chain up to parent's finalize() method. */
1286 G_OBJECT_CLASS (e_source_selector_parent_class)->finalize (object);
1287 }
1288
1289 static void
source_selector_constructed(GObject * object)1290 source_selector_constructed (GObject *object)
1291 {
1292 ESourceRegistry *registry;
1293 ESourceSelector *selector;
1294 gulong handler_id;
1295
1296 /* Chain up to parent's method. */
1297 G_OBJECT_CLASS (e_source_selector_parent_class)->constructed (object);
1298
1299 selector = E_SOURCE_SELECTOR (object);
1300 registry = e_source_selector_get_registry (selector);
1301
1302 handler_id = g_signal_connect (
1303 registry, "source-added",
1304 G_CALLBACK (source_selector_source_added_cb), selector);
1305 selector->priv->source_added_handler_id = handler_id;
1306
1307 handler_id = g_signal_connect (
1308 registry, "source-changed",
1309 G_CALLBACK (source_selector_source_changed_cb), selector);
1310 selector->priv->source_changed_handler_id = handler_id;
1311
1312 handler_id = g_signal_connect (
1313 registry, "source-removed",
1314 G_CALLBACK (source_selector_source_removed_cb), selector);
1315 selector->priv->source_removed_handler_id = handler_id;
1316
1317 handler_id = g_signal_connect (
1318 registry, "source-enabled",
1319 G_CALLBACK (source_selector_source_enabled_cb), selector);
1320 selector->priv->source_enabled_handler_id = handler_id;
1321
1322 handler_id = g_signal_connect (
1323 registry, "source-disabled",
1324 G_CALLBACK (source_selector_source_disabled_cb), selector);
1325 selector->priv->source_disabled_handler_id = handler_id;
1326
1327 source_selector_build_model (selector);
1328
1329 gtk_tree_view_expand_all (GTK_TREE_VIEW (selector));
1330 }
1331
1332 static gboolean
source_selector_button_press_event(GtkWidget * widget,GdkEventButton * event)1333 source_selector_button_press_event (GtkWidget *widget,
1334 GdkEventButton *event)
1335 {
1336 ESourceSelector *selector;
1337 GtkWidgetClass *widget_class;
1338 GtkTreePath *path = NULL;
1339 ESource *source = NULL;
1340 ESource *primary;
1341 gboolean right_click = FALSE;
1342 gboolean triple_click = FALSE;
1343 gboolean row_exists;
1344 gboolean res = FALSE;
1345
1346 selector = E_SOURCE_SELECTOR (widget);
1347
1348 selector->priv->toggled_last = FALSE;
1349
1350 /* Triple-clicking a source selects it exclusively. */
1351
1352 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
1353 right_click = TRUE;
1354 else if (event->button == 1 && event->type == GDK_3BUTTON_PRESS)
1355 triple_click = TRUE;
1356 else
1357 goto chainup;
1358
1359 row_exists = gtk_tree_view_get_path_at_pos (
1360 GTK_TREE_VIEW (widget), event->x, event->y,
1361 &path, NULL, NULL, NULL);
1362
1363 /* Get the source/group */
1364 if (row_exists) {
1365 GtkTreeModel *model;
1366 GtkTreeIter iter;
1367
1368 model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
1369
1370 gtk_tree_model_get_iter (model, &iter, path);
1371 gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
1372 }
1373
1374 if (path != NULL)
1375 gtk_tree_path_free (path);
1376
1377 if (source == NULL)
1378 goto chainup;
1379
1380 primary = e_source_selector_ref_primary_selection (selector);
1381 if (source != primary)
1382 e_source_selector_set_primary_selection (selector, source);
1383 if (primary != NULL)
1384 g_object_unref (primary);
1385
1386 if (right_click)
1387 g_signal_emit (
1388 widget, signals[POPUP_EVENT], 0, source, event, &res);
1389
1390 if (triple_click) {
1391 e_source_selector_select_exclusive (selector, source);
1392 res = TRUE;
1393 }
1394
1395 g_object_unref (source);
1396
1397 return res;
1398
1399 chainup:
1400
1401 /* Chain up to parent's button_press_event() method. */
1402 widget_class = GTK_WIDGET_CLASS (e_source_selector_parent_class);
1403 return widget_class->button_press_event (widget, event);
1404 }
1405
1406 static void
source_selector_drag_leave(GtkWidget * widget,GdkDragContext * context,guint time_)1407 source_selector_drag_leave (GtkWidget *widget,
1408 GdkDragContext *context,
1409 guint time_)
1410 {
1411 GtkTreeView *tree_view;
1412 GtkTreeViewDropPosition pos;
1413
1414 tree_view = GTK_TREE_VIEW (widget);
1415 pos = GTK_TREE_VIEW_DROP_BEFORE;
1416
1417 gtk_tree_view_set_drag_dest_row (tree_view, NULL, pos);
1418 }
1419
1420 static gboolean
source_selector_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time_)1421 source_selector_drag_motion (GtkWidget *widget,
1422 GdkDragContext *context,
1423 gint x,
1424 gint y,
1425 guint time_)
1426 {
1427 ESource *source = NULL;
1428 GtkTreeView *tree_view;
1429 GtkTreeModel *model;
1430 GtkTreePath *path = NULL;
1431 GtkTreeIter iter;
1432 GtkTreeViewDropPosition pos;
1433 GdkDragAction action = 0;
1434
1435 tree_view = GTK_TREE_VIEW (widget);
1436 model = gtk_tree_view_get_model (tree_view);
1437
1438 if (!gtk_tree_view_get_dest_row_at_pos (tree_view, x, y, &path, NULL))
1439 goto exit;
1440
1441 if (!gtk_tree_model_get_iter (model, &iter, path))
1442 goto exit;
1443
1444 gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
1445
1446 if (!e_source_get_writable (source))
1447 goto exit;
1448
1449 pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE;
1450 gtk_tree_view_set_drag_dest_row (tree_view, path, pos);
1451
1452 if (gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE)
1453 action = GDK_ACTION_MOVE;
1454 else
1455 action = gdk_drag_context_get_suggested_action (context);
1456
1457 exit:
1458 if (path != NULL)
1459 gtk_tree_path_free (path);
1460
1461 if (source != NULL)
1462 g_object_unref (source);
1463
1464 gdk_drag_status (context, action, time_);
1465
1466 return TRUE;
1467 }
1468
1469 static gboolean
source_selector_drag_drop(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time_)1470 source_selector_drag_drop (GtkWidget *widget,
1471 GdkDragContext *context,
1472 gint x,
1473 gint y,
1474 guint time_)
1475 {
1476 ESource *source;
1477 ESourceSelector *selector;
1478 GtkTreeView *tree_view;
1479 GtkTreeModel *model;
1480 GtkTreePath *path;
1481 GtkTreeIter iter;
1482 const gchar *extension_name;
1483 gboolean drop_zone;
1484 gboolean valid;
1485
1486 tree_view = GTK_TREE_VIEW (widget);
1487 model = gtk_tree_view_get_model (tree_view);
1488
1489 if (!gtk_tree_view_get_path_at_pos (
1490 tree_view, x, y, &path, NULL, NULL, NULL))
1491 return FALSE;
1492
1493 valid = gtk_tree_model_get_iter (model, &iter, path);
1494 gtk_tree_path_free (path);
1495 g_return_val_if_fail (valid, FALSE);
1496
1497 gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
1498
1499 selector = E_SOURCE_SELECTOR (widget);
1500 extension_name = e_source_selector_get_extension_name (selector);
1501 drop_zone = e_source_has_extension (source, extension_name);
1502
1503 g_object_unref (source);
1504
1505 return drop_zone;
1506 }
1507
1508 static void
source_selector_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time_)1509 source_selector_drag_data_received (GtkWidget *widget,
1510 GdkDragContext *context,
1511 gint x,
1512 gint y,
1513 GtkSelectionData *selection_data,
1514 guint info,
1515 guint time_)
1516 {
1517 ESource *source = NULL;
1518 GtkTreeView *tree_view;
1519 GtkTreeModel *model;
1520 GtkTreePath *path = NULL;
1521 GtkTreeIter iter;
1522 GdkDragAction action;
1523 gboolean delete;
1524 gboolean success = FALSE;
1525
1526 tree_view = GTK_TREE_VIEW (widget);
1527 model = gtk_tree_view_get_model (tree_view);
1528
1529 action = gdk_drag_context_get_selected_action (context);
1530 delete = (action == GDK_ACTION_MOVE);
1531
1532 if (!gtk_tree_view_get_dest_row_at_pos (tree_view, x, y, &path, NULL))
1533 goto exit;
1534
1535 if (!gtk_tree_model_get_iter (model, &iter, path))
1536 goto exit;
1537
1538 gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
1539
1540 if (!e_source_get_writable (source))
1541 goto exit;
1542
1543 g_signal_emit (
1544 widget, signals[DATA_DROPPED], 0, selection_data,
1545 source, gdk_drag_context_get_selected_action (context),
1546 info, &success);
1547
1548 exit:
1549 if (path != NULL)
1550 gtk_tree_path_free (path);
1551
1552 if (source != NULL)
1553 g_object_unref (source);
1554
1555 gtk_drag_finish (context, success, delete, time_);
1556 }
1557
1558 static gboolean
source_selector_popup_menu(GtkWidget * widget)1559 source_selector_popup_menu (GtkWidget *widget)
1560 {
1561 ESourceSelector *selector;
1562 ESource *source;
1563 gboolean res = FALSE;
1564
1565 selector = E_SOURCE_SELECTOR (widget);
1566 source = e_source_selector_ref_primary_selection (selector);
1567 g_signal_emit (selector, signals[POPUP_EVENT], 0, source, NULL, &res);
1568
1569 if (source != NULL)
1570 g_object_unref (source);
1571
1572 return res;
1573 }
1574
1575 static gboolean
source_selector_test_collapse_row(GtkTreeView * tree_view,GtkTreeIter * iter,GtkTreePath * path)1576 source_selector_test_collapse_row (GtkTreeView *tree_view,
1577 GtkTreeIter *iter,
1578 GtkTreePath *path)
1579 {
1580 ESourceSelectorPrivate *priv;
1581 GtkTreeSelection *selection;
1582 GtkTreeModel *model;
1583 GtkTreeIter child_iter;
1584
1585 priv = E_SOURCE_SELECTOR_GET_PRIVATE (tree_view);
1586
1587 /* Clear this because something else has been clicked on now */
1588 priv->toggled_last = FALSE;
1589
1590 if (priv->saved_primary_selection)
1591 return FALSE;
1592
1593 selection = gtk_tree_view_get_selection (tree_view);
1594
1595 if (!gtk_tree_selection_get_selected (selection, &model, &child_iter))
1596 return FALSE;
1597
1598 if (gtk_tree_store_is_ancestor (GTK_TREE_STORE (model), iter, &child_iter)) {
1599 GtkTreeRowReference *reference;
1600 GtkTreePath *child_path;
1601
1602 child_path = gtk_tree_model_get_path (model, &child_iter);
1603 reference = gtk_tree_row_reference_new (model, child_path);
1604 priv->saved_primary_selection = reference;
1605 gtk_tree_path_free (child_path);
1606 }
1607
1608 return FALSE;
1609 }
1610
1611 static void
source_selector_row_expanded(GtkTreeView * tree_view,GtkTreeIter * iter,GtkTreePath * path)1612 source_selector_row_expanded (GtkTreeView *tree_view,
1613 GtkTreeIter *iter,
1614 GtkTreePath *path)
1615 {
1616 ESourceSelectorPrivate *priv;
1617 GtkTreeModel *model;
1618 GtkTreePath *child_path;
1619 GtkTreeIter child_iter;
1620
1621 priv = E_SOURCE_SELECTOR_GET_PRIVATE (tree_view);
1622
1623 if (!priv->saved_primary_selection)
1624 return;
1625
1626 model = gtk_tree_view_get_model (tree_view);
1627
1628 child_path = gtk_tree_row_reference_get_path (
1629 priv->saved_primary_selection);
1630 gtk_tree_model_get_iter (model, &child_iter, child_path);
1631
1632 if (gtk_tree_store_is_ancestor (GTK_TREE_STORE (model), iter, &child_iter)) {
1633 GtkTreeSelection *selection;
1634
1635 selection = gtk_tree_view_get_selection (tree_view);
1636 gtk_tree_selection_select_iter (selection, &child_iter);
1637
1638 clear_saved_primary_selection (E_SOURCE_SELECTOR (tree_view));
1639 }
1640
1641 gtk_tree_path_free (child_path);
1642 }
1643
1644 static gboolean
source_selector_get_source_selected(ESourceSelector * selector,ESource * source)1645 source_selector_get_source_selected (ESourceSelector *selector,
1646 ESource *source)
1647 {
1648 ESourceSelectable *extension;
1649 const gchar *extension_name;
1650 gboolean selected = TRUE;
1651
1652 extension_name = e_source_selector_get_extension_name (selector);
1653
1654 if (!e_source_has_extension (source, extension_name))
1655 return FALSE;
1656
1657 extension = e_source_get_extension (source, extension_name);
1658
1659 if (E_IS_SOURCE_SELECTABLE (extension))
1660 selected = e_source_selectable_get_selected (extension);
1661
1662 return selected;
1663 }
1664
1665 static gboolean
source_selector_set_source_selected(ESourceSelector * selector,ESource * source,gboolean selected)1666 source_selector_set_source_selected (ESourceSelector *selector,
1667 ESource *source,
1668 gboolean selected)
1669 {
1670 ESourceSelectable *extension;
1671 const gchar *extension_name;
1672
1673 extension_name = e_source_selector_get_extension_name (selector);
1674
1675 if (!e_source_has_extension (source, extension_name))
1676 return FALSE;
1677
1678 extension = e_source_get_extension (source, extension_name);
1679
1680 if (!E_IS_SOURCE_SELECTABLE (extension))
1681 return FALSE;
1682
1683 if (selected != e_source_selectable_get_selected (extension)) {
1684 e_source_selectable_set_selected (extension, selected);
1685 e_source_selector_queue_write (selector, source);
1686
1687 return TRUE;
1688 }
1689
1690 return FALSE;
1691 }
1692
1693 static gboolean
ess_bool_accumulator(GSignalInvocationHint * ihint,GValue * out,const GValue * in,gpointer data)1694 ess_bool_accumulator (GSignalInvocationHint *ihint,
1695 GValue *out,
1696 const GValue *in,
1697 gpointer data)
1698 {
1699 gboolean v_boolean;
1700
1701 v_boolean = g_value_get_boolean (in);
1702 g_value_set_boolean (out, v_boolean);
1703
1704 return !v_boolean;
1705 }
1706
1707 static void
e_source_selector_class_init(ESourceSelectorClass * class)1708 e_source_selector_class_init (ESourceSelectorClass *class)
1709 {
1710 GObjectClass *object_class;
1711 GtkWidgetClass *widget_class;
1712 GtkTreeViewClass *tree_view_class;
1713
1714 g_type_class_add_private (class, sizeof (ESourceSelectorPrivate));
1715
1716 object_class = G_OBJECT_CLASS (class);
1717 object_class->set_property = source_selector_set_property;
1718 object_class->get_property = source_selector_get_property;
1719 object_class->dispose = source_selector_dispose;
1720 object_class->finalize = source_selector_finalize;
1721 object_class->constructed = source_selector_constructed;
1722
1723 widget_class = GTK_WIDGET_CLASS (class);
1724 widget_class->button_press_event = source_selector_button_press_event;
1725 widget_class->drag_leave = source_selector_drag_leave;
1726 widget_class->drag_motion = source_selector_drag_motion;
1727 widget_class->drag_drop = source_selector_drag_drop;
1728 widget_class->drag_data_received = source_selector_drag_data_received;
1729 widget_class->popup_menu = source_selector_popup_menu;
1730
1731 tree_view_class = GTK_TREE_VIEW_CLASS (class);
1732 tree_view_class->test_collapse_row = source_selector_test_collapse_row;
1733 tree_view_class->row_expanded = source_selector_row_expanded;
1734
1735 class->get_source_selected = source_selector_get_source_selected;
1736 class->set_source_selected = source_selector_set_source_selected;
1737
1738 g_object_class_install_property (
1739 object_class,
1740 PROP_EXTENSION_NAME,
1741 g_param_spec_string (
1742 "extension-name",
1743 NULL,
1744 NULL,
1745 NULL,
1746 G_PARAM_READWRITE |
1747 G_PARAM_CONSTRUCT_ONLY |
1748 G_PARAM_STATIC_STRINGS));
1749
1750 g_object_class_install_property (
1751 object_class,
1752 PROP_PRIMARY_SELECTION,
1753 g_param_spec_object (
1754 "primary-selection",
1755 NULL,
1756 NULL,
1757 E_TYPE_SOURCE,
1758 G_PARAM_READWRITE |
1759 G_PARAM_STATIC_STRINGS));
1760
1761 g_object_class_install_property (
1762 object_class,
1763 PROP_REGISTRY,
1764 g_param_spec_object (
1765 "registry",
1766 NULL,
1767 NULL,
1768 E_TYPE_SOURCE_REGISTRY,
1769 G_PARAM_READWRITE |
1770 G_PARAM_CONSTRUCT_ONLY |
1771 G_PARAM_STATIC_STRINGS));
1772
1773 g_object_class_install_property (
1774 object_class,
1775 PROP_SHOW_COLORS,
1776 g_param_spec_boolean (
1777 "show-colors",
1778 NULL,
1779 NULL,
1780 TRUE,
1781 G_PARAM_READWRITE |
1782 G_PARAM_STATIC_STRINGS));
1783
1784 g_object_class_install_property (
1785 object_class,
1786 PROP_SHOW_ICONS,
1787 g_param_spec_boolean (
1788 "show-icons",
1789 NULL,
1790 NULL,
1791 TRUE,
1792 G_PARAM_READWRITE |
1793 G_PARAM_STATIC_STRINGS));
1794
1795 g_object_class_install_property (
1796 object_class,
1797 PROP_SHOW_TOGGLES,
1798 g_param_spec_boolean (
1799 "show-toggles",
1800 NULL,
1801 NULL,
1802 TRUE,
1803 G_PARAM_READWRITE |
1804 G_PARAM_STATIC_STRINGS));
1805
1806 signals[SELECTION_CHANGED] = g_signal_new (
1807 "selection-changed",
1808 G_OBJECT_CLASS_TYPE (object_class),
1809 G_SIGNAL_RUN_LAST,
1810 G_STRUCT_OFFSET (ESourceSelectorClass, selection_changed),
1811 NULL, NULL,
1812 g_cclosure_marshal_VOID__VOID,
1813 G_TYPE_NONE, 0);
1814
1815 /* XXX Consider this signal deprecated. Connect
1816 * to "notify::primary-selection" instead. */
1817 signals[PRIMARY_SELECTION_CHANGED] = g_signal_new (
1818 "primary-selection-changed",
1819 G_OBJECT_CLASS_TYPE (object_class),
1820 G_SIGNAL_RUN_LAST,
1821 G_STRUCT_OFFSET (ESourceSelectorClass, primary_selection_changed),
1822 NULL, NULL,
1823 g_cclosure_marshal_VOID__VOID,
1824 G_TYPE_NONE, 0);
1825
1826 signals[POPUP_EVENT] = g_signal_new (
1827 "popup-event",
1828 G_OBJECT_CLASS_TYPE (object_class),
1829 G_SIGNAL_RUN_LAST,
1830 G_STRUCT_OFFSET (ESourceSelectorClass, popup_event),
1831 ess_bool_accumulator, NULL, NULL,
1832 G_TYPE_BOOLEAN, 2, G_TYPE_OBJECT,
1833 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
1834
1835 signals[DATA_DROPPED] = g_signal_new (
1836 "data-dropped",
1837 G_OBJECT_CLASS_TYPE (object_class),
1838 G_SIGNAL_RUN_LAST,
1839 G_STRUCT_OFFSET (ESourceSelectorClass, data_dropped),
1840 NULL, NULL, NULL,
1841 G_TYPE_BOOLEAN, 4,
1842 GTK_TYPE_SELECTION_DATA | G_SIGNAL_TYPE_STATIC_SCOPE,
1843 E_TYPE_SOURCE,
1844 GDK_TYPE_DRAG_ACTION,
1845 G_TYPE_UINT);
1846
1847 signals[SOURCE_SELECTED] = g_signal_new (
1848 "source-selected",
1849 G_OBJECT_CLASS_TYPE (object_class),
1850 G_SIGNAL_RUN_LAST,
1851 G_STRUCT_OFFSET (ESourceSelectorClass, source_selected),
1852 NULL, NULL, NULL,
1853 G_TYPE_NONE, 1, E_TYPE_SOURCE);
1854
1855 signals[SOURCE_UNSELECTED] = g_signal_new (
1856 "source-unselected",
1857 G_OBJECT_CLASS_TYPE (object_class),
1858 G_SIGNAL_RUN_LAST,
1859 G_STRUCT_OFFSET (ESourceSelectorClass, source_unselected),
1860 NULL, NULL, NULL,
1861 G_TYPE_NONE, 1, E_TYPE_SOURCE);
1862
1863 /* Return TRUE when the source should be hidden, FALSE when not. */
1864 signals[FILTER_SOURCE] = g_signal_new (
1865 "filter-source",
1866 G_OBJECT_CLASS_TYPE (object_class),
1867 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1868 G_STRUCT_OFFSET (ESourceSelectorClass, filter_source),
1869 NULL, NULL, NULL,
1870 G_TYPE_BOOLEAN, 1, E_TYPE_SOURCE);
1871 }
1872
1873 static void
e_source_selector_init(ESourceSelector * selector)1874 e_source_selector_init (ESourceSelector *selector)
1875 {
1876 GHashTable *pending_writes;
1877 GtkTreeViewColumn *column;
1878 GtkTreeSelection *selection;
1879 GtkCellRenderer *renderer;
1880 GtkTreeStore *tree_store;
1881 GtkTreeView *tree_view;
1882
1883 pending_writes = g_hash_table_new_full (
1884 (GHashFunc) g_direct_hash,
1885 (GEqualFunc) g_direct_equal,
1886 (GDestroyNotify) g_object_unref,
1887 (GDestroyNotify) pending_writes_destroy_source);
1888
1889 selector->priv = E_SOURCE_SELECTOR_GET_PRIVATE (selector);
1890
1891 selector->priv->pending_writes = pending_writes;
1892 selector->priv->hidden_groups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1893
1894 selector->priv->main_context = g_main_context_get_thread_default ();
1895 if (selector->priv->main_context != NULL)
1896 g_main_context_ref (selector->priv->main_context);
1897
1898 tree_view = GTK_TREE_VIEW (selector);
1899
1900 gtk_tree_view_set_search_column (tree_view, COLUMN_SOURCE);
1901 gtk_tree_view_set_enable_search (tree_view, TRUE);
1902
1903 selector->priv->toggled_last = FALSE;
1904 selector->priv->show_colors = TRUE;
1905 selector->priv->show_toggles = TRUE;
1906
1907 selector->priv->source_index = g_hash_table_new_full (
1908 (GHashFunc) e_source_hash,
1909 (GEqualFunc) e_source_equal,
1910 (GDestroyNotify) g_object_unref,
1911 (GDestroyNotify) gtk_tree_row_reference_free);
1912
1913 tree_store = gtk_tree_store_new (
1914 NUM_COLUMNS,
1915 G_TYPE_STRING, /* COLUMN_NAME */
1916 GDK_TYPE_RGBA, /* COLUMN_COLOR */
1917 G_TYPE_BOOLEAN, /* COLUMN_ACTIVE */
1918 G_TYPE_STRING, /* COLUMN_ICON_NAME */
1919 G_TYPE_BOOLEAN, /* COLUMN_SHOW_COLOR */
1920 G_TYPE_BOOLEAN, /* COLUMN_SHOW_ICON */
1921 G_TYPE_BOOLEAN, /* COLUMN_SHOW_TOGGLE */
1922 G_TYPE_INT, /* COLUMN_WEIGHT */
1923 E_TYPE_SOURCE, /* COLUMN_SOURCE */
1924 G_TYPE_STRING, /* COLUMN_TOOLTIP */
1925 G_TYPE_BOOLEAN, /* COLUMN_IS_BUSY */
1926 G_TYPE_UINT, /* COLUMN_CONNECTION_STATUS */
1927 G_TYPE_UINT); /* COLUMN_SORT_ORDER */
1928
1929 gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (tree_store));
1930
1931 column = gtk_tree_view_column_new ();
1932 gtk_tree_view_column_set_expand (column, TRUE);
1933 gtk_tree_view_append_column (tree_view, column);
1934
1935 renderer = e_cell_renderer_color_new ();
1936 g_object_set (
1937 G_OBJECT (renderer), "mode",
1938 GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
1939 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1940 gtk_tree_view_column_add_attribute (
1941 column, renderer, "rgba", COLUMN_COLOR);
1942 gtk_tree_view_column_add_attribute (
1943 column, renderer, "visible", COLUMN_SHOW_COLOR);
1944
1945 renderer = e_cell_renderer_safe_toggle_new ();
1946 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1947 gtk_tree_view_column_add_attribute (
1948 column, renderer, "active", COLUMN_ACTIVE);
1949 gtk_tree_view_column_add_attribute (
1950 column, renderer, "visible", COLUMN_SHOW_TOGGLE);
1951 g_signal_connect (
1952 renderer, "toggled",
1953 G_CALLBACK (cell_toggled_callback), selector);
1954
1955 renderer = gtk_cell_renderer_pixbuf_new ();
1956 g_object_set (
1957 G_OBJECT (renderer),
1958 "stock-size", GTK_ICON_SIZE_MENU, NULL);
1959 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1960 gtk_tree_view_column_add_attribute (
1961 column, renderer, "icon-name", COLUMN_ICON_NAME);
1962 gtk_tree_view_column_add_attribute (
1963 column, renderer, "visible", COLUMN_SHOW_ICONS);
1964
1965 renderer = gtk_cell_renderer_text_new ();
1966 g_object_set (
1967 G_OBJECT (renderer),
1968 "ellipsize", PANGO_ELLIPSIZE_END, NULL);
1969 g_signal_connect_swapped (
1970 renderer, "edited",
1971 G_CALLBACK (text_cell_edited_cb), selector);
1972 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1973 gtk_tree_view_column_set_attributes (
1974 column, renderer,
1975 "text", COLUMN_NAME,
1976 "weight", COLUMN_WEIGHT,
1977 NULL);
1978
1979 renderer = gtk_cell_renderer_spinner_new ();
1980 selector->priv->busy_renderer = g_object_ref (renderer);
1981 gtk_tree_view_column_pack_end (column, renderer, FALSE);
1982 gtk_tree_view_column_set_attributes (
1983 column, renderer,
1984 "visible", COLUMN_IS_BUSY,
1985 "active", COLUMN_IS_BUSY,
1986 NULL);
1987
1988 selection = gtk_tree_view_get_selection (tree_view);
1989 gtk_tree_selection_set_select_function (
1990 selection, (GtkTreeSelectionFunc)
1991 selection_func, selector, NULL);
1992 g_signal_connect_object (
1993 selection, "changed",
1994 G_CALLBACK (selection_changed_callback),
1995 G_OBJECT (selector), 0);
1996
1997 gtk_tree_view_set_headers_visible (tree_view, FALSE);
1998 gtk_tree_view_set_tooltip_column (tree_view, COLUMN_TOOLTIP);
1999 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
2000 }
2001
2002 /**
2003 * e_source_selector_new:
2004 * @registry: an #ESourceRegistry
2005 * @extension_name: the name of an #ESource extension
2006 *
2007 * Displays a list of sources from @registry having an extension named
2008 * @extension_name. The sources are grouped by backend or groupware
2009 * account, which are described by the parent source.
2010 *
2011 * Returns: a new #ESourceSelector
2012 **/
2013 GtkWidget *
e_source_selector_new(ESourceRegistry * registry,const gchar * extension_name)2014 e_source_selector_new (ESourceRegistry *registry,
2015 const gchar *extension_name)
2016 {
2017 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
2018 g_return_val_if_fail (extension_name != NULL, NULL);
2019
2020 return g_object_new (
2021 E_TYPE_SOURCE_SELECTOR, "registry", registry,
2022 "extension-name", extension_name, NULL);
2023 }
2024
2025 /**
2026 * e_source_selector_get_registry:
2027 * @selector: an #ESourceSelector
2028 *
2029 * Returns the #ESourceRegistry that @selector is getting sources from.
2030 *
2031 * Returns: an #ESourceRegistry
2032 *
2033 * Since: 3.6
2034 **/
2035 ESourceRegistry *
e_source_selector_get_registry(ESourceSelector * selector)2036 e_source_selector_get_registry (ESourceSelector *selector)
2037 {
2038 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
2039
2040 return selector->priv->registry;
2041 }
2042
2043 /**
2044 * e_source_selector_get_extension_name:
2045 * @selector: an #ESourceSelector
2046 *
2047 * Returns the extension name used to filter which sources are displayed.
2048 *
2049 * Returns: the #ESource extension name
2050 *
2051 * Since: 3.6
2052 **/
2053 const gchar *
e_source_selector_get_extension_name(ESourceSelector * selector)2054 e_source_selector_get_extension_name (ESourceSelector *selector)
2055 {
2056 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
2057
2058 return selector->priv->extension_name;
2059 }
2060
2061 /**
2062 * e_source_selector_get_show_colors:
2063 * @selector: an #ESourceSelector
2064 *
2065 * Returns whether colors are shown next to data sources.
2066 *
2067 * Returns: %TRUE if colors are being shown
2068 *
2069 * Since: 3.6
2070 **/
2071 gboolean
e_source_selector_get_show_colors(ESourceSelector * selector)2072 e_source_selector_get_show_colors (ESourceSelector *selector)
2073 {
2074 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
2075
2076 return selector->priv->show_colors;
2077 }
2078
2079 /**
2080 * e_source_selector_set_show_colors:
2081 * @selector: an #ESourceSelector
2082 * @show_colors: whether to show colors
2083 *
2084 * Sets whether to show colors next to data sources.
2085 *
2086 * Since: 3.6
2087 **/
2088 void
e_source_selector_set_show_colors(ESourceSelector * selector,gboolean show_colors)2089 e_source_selector_set_show_colors (ESourceSelector *selector,
2090 gboolean show_colors)
2091 {
2092 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
2093
2094 if (show_colors == selector->priv->show_colors)
2095 return;
2096
2097 selector->priv->show_colors = show_colors;
2098
2099 g_object_notify (G_OBJECT (selector), "show-colors");
2100
2101 source_selector_build_model (selector);
2102 }
2103
2104 /**
2105 * e_source_selector_get_show_icons:
2106 * @selector: an #ESourceSelector
2107 *
2108 * Returns whether icons are shown next to data sources.
2109 *
2110 * Generally the icon shown will be based on the presence of a backend-based
2111 * extension, such as #ESourceAddressBook or #ESourceCalendar. For #ESource
2112 * instances with no such extension, no icon is shown.
2113 *
2114 * Returns: %TRUE if icons are being shown
2115 *
2116 * Since: 3.12
2117 **/
2118 gboolean
e_source_selector_get_show_icons(ESourceSelector * selector)2119 e_source_selector_get_show_icons (ESourceSelector *selector)
2120 {
2121 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
2122
2123 return selector->priv->show_icons;
2124 }
2125
2126 /**
2127 * e_source_selector_set_show_icons:
2128 * @selector: an #ESourceSelector
2129 * @show_icons: whether to show icons
2130 *
2131 * Sets whether to show icons next to data sources.
2132 *
2133 * Generally the icon shown will be based on the presence of a backend-based
2134 * extension, such as #ESourceAddressBook or #ESourceCalendar. For #ESource
2135 * instances with no such extension, no icon is shown.
2136 *
2137 * Since: 3.12
2138 **/
2139 void
e_source_selector_set_show_icons(ESourceSelector * selector,gboolean show_icons)2140 e_source_selector_set_show_icons (ESourceSelector *selector,
2141 gboolean show_icons)
2142 {
2143 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
2144
2145 if (show_icons == selector->priv->show_icons)
2146 return;
2147
2148 selector->priv->show_icons = show_icons;
2149
2150 g_object_notify (G_OBJECT (selector), "show-icons");
2151
2152 source_selector_build_model (selector);
2153 }
2154
2155 /**
2156 * e_source_selector_get_show_toggles:
2157 * @selector: an #ESourceSelector
2158 *
2159 * Returns whether toggles are shown next to data sources.
2160 *
2161 * Returns: %TRUE if toggles are being shown
2162 *
2163 * Since: 3.6
2164 **/
2165 gboolean
e_source_selector_get_show_toggles(ESourceSelector * selector)2166 e_source_selector_get_show_toggles (ESourceSelector *selector)
2167 {
2168 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
2169
2170 return selector->priv->show_toggles;
2171 }
2172
2173 /**
2174 * e_source_selector_set_show_toggles:
2175 * @selector: an #ESourceSelector
2176 * @show_toggles: whether to show toggles
2177 *
2178 * Sets whether to show toggles next to data sources.
2179 *
2180 * Since: 3.6
2181 **/
2182 void
e_source_selector_set_show_toggles(ESourceSelector * selector,gboolean show_toggles)2183 e_source_selector_set_show_toggles (ESourceSelector *selector,
2184 gboolean show_toggles)
2185 {
2186 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
2187
2188 if (show_toggles == selector->priv->show_toggles)
2189 return;
2190
2191 selector->priv->show_toggles = show_toggles;
2192
2193 g_object_notify (G_OBJECT (selector), "show-toggles");
2194
2195 source_selector_build_model (selector);
2196 }
2197
2198 /* Helper for e_source_selector_get_selection() */
2199 static gboolean
source_selector_check_selected(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer user_data)2200 source_selector_check_selected (GtkTreeModel *model,
2201 GtkTreePath *path,
2202 GtkTreeIter *iter,
2203 gpointer user_data)
2204 {
2205 ESource *source;
2206
2207 struct {
2208 ESourceSelector *selector;
2209 GQueue queue;
2210 } *closure = user_data;
2211
2212 gtk_tree_model_get (model, iter, COLUMN_SOURCE, &source, -1);
2213
2214 if (e_source_selector_source_is_selected (closure->selector, source))
2215 g_queue_push_tail (&closure->queue, g_object_ref (source));
2216
2217 g_object_unref (source);
2218
2219 return FALSE;
2220 }
2221
2222 /**
2223 * e_source_selector_get_selection:
2224 * @selector: an #ESourceSelector
2225 *
2226 * Returns a list of selected sources, i.e. those that were enabled through
2227 * the corresponding checkboxes in the tree. The sources are ordered as they
2228 * appear in @selector.
2229 *
2230 * The sources returned in the list are referenced for thread-safety.
2231 * They must each be unreferenced with g_object_unref() when finished
2232 * with them. Free the returned list itself with g_list_free().
2233 *
2234 * An easy way to free the list properly in one step is as follows:
2235 *
2236 * |[
2237 * g_list_free_full (list, g_object_unref);
2238 * ]|
2239 *
2240 * Returns: a ordered list of selected sources
2241 **/
2242 GList *
e_source_selector_get_selection(ESourceSelector * selector)2243 e_source_selector_get_selection (ESourceSelector *selector)
2244 {
2245 struct {
2246 ESourceSelector *selector;
2247 GQueue queue;
2248 } closure;
2249
2250 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
2251
2252 closure.selector = selector;
2253 g_queue_init (&closure.queue);
2254
2255 gtk_tree_model_foreach (
2256 gtk_tree_view_get_model (GTK_TREE_VIEW (selector)),
2257 (GtkTreeModelForeachFunc) source_selector_check_selected,
2258 &closure);
2259
2260 return g_queue_peek_head_link (&closure.queue);
2261 }
2262
2263 struct CountData {
2264 ESourceSelector *selector;
2265 guint count;
2266 gboolean selected;
2267 };
2268
2269 static gboolean
source_selector_count_sources(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer user_data)2270 source_selector_count_sources (GtkTreeModel *model,
2271 GtkTreePath *path,
2272 GtkTreeIter *iter,
2273 gpointer user_data)
2274 {
2275 struct CountData *cd = user_data;
2276 ESource *source;
2277
2278 gtk_tree_model_get (model, iter, COLUMN_SOURCE, &source, -1);
2279
2280 if (e_source_has_extension (source, e_source_selector_get_extension_name (cd->selector))) {
2281 if (cd->selected) {
2282 if (e_source_selector_source_is_selected (cd->selector, source))
2283 cd->count++;
2284 } else {
2285 cd->count++;
2286 }
2287 }
2288
2289 g_object_unref (source);
2290
2291 return FALSE;
2292 }
2293
2294 /**
2295 * e_source_selector_count_total:
2296 * @selector: an #ESourceSelector
2297 *
2298 * Counts how many ESource-s are shown in the @selector.
2299 *
2300 * Returns: How many ESource-s are shown in the @selector.
2301 *
2302 * Since: 3.20
2303 **/
2304 guint
e_source_selector_count_total(ESourceSelector * selector)2305 e_source_selector_count_total (ESourceSelector *selector)
2306 {
2307 struct CountData cd;
2308
2309 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), 0);
2310
2311 cd.selector = selector;
2312 cd.count = 0;
2313 cd.selected = FALSE;
2314
2315 gtk_tree_model_foreach (
2316 gtk_tree_view_get_model (GTK_TREE_VIEW (selector)),
2317 source_selector_count_sources, &cd);
2318
2319 return cd.count;
2320 }
2321
2322 /**
2323 * e_source_selector_count_selected:
2324 * @selector: an #ESourceSelector
2325 *
2326 * Counts how many ESource-s are selected in the @selector.
2327 *
2328 * Returns: How many ESource-s are selected in the @selector.
2329 *
2330 * Since: 3.20
2331 **/
2332 guint
e_source_selector_count_selected(ESourceSelector * selector)2333 e_source_selector_count_selected (ESourceSelector *selector)
2334 {
2335 struct CountData cd;
2336
2337 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), 0);
2338
2339 cd.selector = selector;
2340 cd.count = 0;
2341 cd.selected = TRUE;
2342
2343 gtk_tree_model_foreach (
2344 gtk_tree_view_get_model (GTK_TREE_VIEW (selector)),
2345 source_selector_count_sources, &cd);
2346
2347 return cd.count;
2348 }
2349
2350 /**
2351 * e_source_selector_select_source:
2352 * @selector: An #ESourceSelector widget
2353 * @source: An #ESource.
2354 *
2355 * Select @source in @selector.
2356 **/
2357 void
e_source_selector_select_source(ESourceSelector * selector,ESource * source)2358 e_source_selector_select_source (ESourceSelector *selector,
2359 ESource *source)
2360 {
2361 ESourceSelectorClass *class;
2362 GtkTreeRowReference *reference;
2363 GHashTable *source_index;
2364
2365 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
2366 g_return_if_fail (E_IS_SOURCE (source));
2367
2368 /* Make sure the ESource is in our tree model. */
2369 source_index = selector->priv->source_index;
2370 reference = g_hash_table_lookup (source_index, source);
2371 g_return_if_fail (gtk_tree_row_reference_valid (reference));
2372
2373 class = E_SOURCE_SELECTOR_GET_CLASS (selector);
2374 g_return_if_fail (class != NULL);
2375 g_return_if_fail (class->set_source_selected != NULL);
2376
2377 if (class->set_source_selected (selector, source, TRUE)) {
2378 g_signal_emit (selector, signals[SOURCE_SELECTED], 0, source);
2379 g_signal_emit (selector, signals[SELECTION_CHANGED], 0);
2380 }
2381 }
2382
2383 /**
2384 * e_source_selector_unselect_source:
2385 * @selector: An #ESourceSelector widget
2386 * @source: An #ESource.
2387 *
2388 * Unselect @source in @selector.
2389 **/
2390 void
e_source_selector_unselect_source(ESourceSelector * selector,ESource * source)2391 e_source_selector_unselect_source (ESourceSelector *selector,
2392 ESource *source)
2393 {
2394 ESourceSelectorClass *class;
2395 GtkTreeRowReference *reference;
2396 GHashTable *source_index;
2397
2398 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
2399 g_return_if_fail (E_IS_SOURCE (source));
2400
2401 /* Make sure the ESource is in our tree model. */
2402 source_index = selector->priv->source_index;
2403 reference = g_hash_table_lookup (source_index, source);
2404
2405 /* can be NULL when the source was just removed */
2406 if (!reference)
2407 return;
2408
2409 g_return_if_fail (gtk_tree_row_reference_valid (reference));
2410
2411 class = E_SOURCE_SELECTOR_GET_CLASS (selector);
2412 g_return_if_fail (class != NULL);
2413 g_return_if_fail (class->set_source_selected != NULL);
2414
2415 if (class->set_source_selected (selector, source, FALSE)) {
2416 g_signal_emit (selector, signals[SOURCE_UNSELECTED], 0, source);
2417 g_signal_emit (selector, signals[SELECTION_CHANGED], 0);
2418 }
2419 }
2420
2421 /**
2422 * e_source_selector_select_exclusive:
2423 * @selector: An #ESourceSelector widget
2424 * @source: An #ESource.
2425 *
2426 * Select @source in @selector and unselect all others.
2427 *
2428 * Since: 2.30
2429 **/
2430 void
e_source_selector_select_exclusive(ESourceSelector * selector,ESource * source)2431 e_source_selector_select_exclusive (ESourceSelector *selector,
2432 ESource *source)
2433 {
2434 ESourceSelectorClass *class;
2435 GHashTable *source_index;
2436 GHashTableIter iter;
2437 gpointer key;
2438 gboolean any_changed = FALSE;
2439
2440 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
2441 g_return_if_fail (E_IS_SOURCE (source));
2442
2443 class = E_SOURCE_SELECTOR_GET_CLASS (selector);
2444 g_return_if_fail (class != NULL);
2445 g_return_if_fail (class->set_source_selected != NULL);
2446
2447 source_index = selector->priv->source_index;
2448 g_hash_table_iter_init (&iter, source_index);
2449
2450 while (g_hash_table_iter_next (&iter, &key, NULL)) {
2451 gboolean selected = e_source_equal (key, source);
2452 if (class->set_source_selected (selector, key, selected)) {
2453 any_changed = TRUE;
2454 if (selected)
2455 g_signal_emit (selector, signals[SOURCE_SELECTED], 0, key);
2456 else
2457 g_signal_emit (selector, signals[SOURCE_UNSELECTED], 0, key);
2458 }
2459 }
2460
2461 if (any_changed)
2462 g_signal_emit (selector, signals[SELECTION_CHANGED], 0);
2463 }
2464
2465 /**
2466 * e_source_selector_select_all:
2467 * @selector: An #ESourceSelector widget
2468 *
2469 * Selects all ESource-s in the @selector.
2470 *
2471 * Since: 3.20
2472 **/
2473 void
e_source_selector_select_all(ESourceSelector * selector)2474 e_source_selector_select_all (ESourceSelector *selector)
2475 {
2476 ESourceSelectorClass *class;
2477 GHashTable *source_index;
2478 GHashTableIter iter;
2479 gpointer key;
2480 gboolean any_changed = FALSE;
2481
2482 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
2483
2484 class = E_SOURCE_SELECTOR_GET_CLASS (selector);
2485 g_return_if_fail (class != NULL);
2486 g_return_if_fail (class->set_source_selected != NULL);
2487
2488 source_index = selector->priv->source_index;
2489 g_hash_table_iter_init (&iter, source_index);
2490
2491 while (g_hash_table_iter_next (&iter, &key, NULL)) {
2492 if (class->set_source_selected (selector, key, TRUE)) {
2493 any_changed = TRUE;
2494 g_signal_emit (selector, signals[SOURCE_SELECTED], 0, key);
2495 }
2496 }
2497
2498 if (any_changed)
2499 g_signal_emit (selector, signals[SELECTION_CHANGED], 0);
2500 }
2501
2502 /**
2503 * e_source_selector_source_is_selected:
2504 * @selector: An #ESourceSelector widget
2505 * @source: An #ESource.
2506 *
2507 * Check whether @source is selected in @selector.
2508 *
2509 * Returns: %TRUE if @source is currently selected, %FALSE otherwise.
2510 **/
2511 gboolean
e_source_selector_source_is_selected(ESourceSelector * selector,ESource * source)2512 e_source_selector_source_is_selected (ESourceSelector *selector,
2513 ESource *source)
2514 {
2515 ESourceSelectorClass *class;
2516 GtkTreeRowReference *reference;
2517 GHashTable *source_index;
2518
2519 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
2520 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
2521
2522 /* Make sure the ESource is in our tree model. */
2523 source_index = selector->priv->source_index;
2524 reference = g_hash_table_lookup (source_index, source);
2525
2526 /* Can be NULL when the source was just removed */
2527 if (!reference)
2528 return FALSE;
2529
2530 g_return_val_if_fail (gtk_tree_row_reference_valid (reference), FALSE);
2531
2532 class = E_SOURCE_SELECTOR_GET_CLASS (selector);
2533 g_return_val_if_fail (class != NULL, FALSE);
2534 g_return_val_if_fail (class->get_source_selected != NULL, FALSE);
2535
2536 return class->get_source_selected (selector, source);
2537 }
2538
2539 /**
2540 * e_source_selector_edit_primary_selection:
2541 * @selector: An #ESourceSelector widget
2542 *
2543 * Allows the user to rename the primary selected source by opening an
2544 * entry box directly in @selector.
2545 *
2546 * Since: 2.26
2547 **/
2548 void
e_source_selector_edit_primary_selection(ESourceSelector * selector)2549 e_source_selector_edit_primary_selection (ESourceSelector *selector)
2550 {
2551 GtkTreeRowReference *reference;
2552 GtkTreeSelection *selection;
2553 GtkTreeViewColumn *column;
2554 GtkCellRenderer *renderer;
2555 GtkTreeView *tree_view;
2556 GtkTreeModel *model;
2557 GtkTreePath *path = NULL;
2558 GtkTreeIter iter;
2559 GList *list;
2560
2561 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
2562
2563 tree_view = GTK_TREE_VIEW (selector);
2564 column = gtk_tree_view_get_column (tree_view, 0);
2565 reference = selector->priv->saved_primary_selection;
2566 selection = gtk_tree_view_get_selection (tree_view);
2567
2568 if (reference != NULL)
2569 path = gtk_tree_row_reference_get_path (reference);
2570 else if (gtk_tree_selection_get_selected (selection, &model, &iter))
2571 path = gtk_tree_model_get_path (model, &iter);
2572
2573 if (path == NULL)
2574 return;
2575
2576 /* XXX Because we stuff three renderers in a single column,
2577 * we have to manually hunt for the text renderer. */
2578 renderer = NULL;
2579 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
2580 while (list != NULL) {
2581 renderer = list->data;
2582 if (GTK_IS_CELL_RENDERER_TEXT (renderer))
2583 break;
2584 list = g_list_delete_link (list, list);
2585 }
2586 g_list_free (list);
2587
2588 /* Make the text cell renderer editable, but only temporarily.
2589 * We don't want editing to be activated by simply clicking on
2590 * the source name. Too easy for accidental edits to occur. */
2591 g_object_set (renderer, "editable", TRUE, NULL);
2592 gtk_tree_view_expand_to_path (tree_view, path);
2593 gtk_tree_view_set_cursor_on_cell (
2594 tree_view, path, column, renderer, TRUE);
2595 g_object_set (renderer, "editable", FALSE, NULL);
2596
2597 gtk_tree_path_free (path);
2598 }
2599
2600 /**
2601 * e_source_selector_ref_primary_selection:
2602 * @selector: An #ESourceSelector widget
2603 *
2604 * Get the primary selected source. The primary selection is the one that is
2605 * highlighted through the normal #GtkTreeView selection mechanism (as opposed
2606 * to the "normal" selection, which is the set of source whose checkboxes are
2607 * checked).
2608 *
2609 * The returned #ESource is referenced for thread-safety and must be
2610 * unreferenced with g_object_unref() when finished with it.
2611 *
2612 * Returns: The selected source.
2613 *
2614 * Since: 3.6
2615 **/
2616 ESource *
e_source_selector_ref_primary_selection(ESourceSelector * selector)2617 e_source_selector_ref_primary_selection (ESourceSelector *selector)
2618 {
2619 ESource *source;
2620 GtkTreeRowReference *reference;
2621 GtkTreeSelection *selection;
2622 GtkTreeView *tree_view;
2623 GtkTreeModel *model;
2624 GtkTreeIter iter;
2625 const gchar *extension_name;
2626 gboolean have_iter = FALSE;
2627
2628 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
2629
2630 tree_view = GTK_TREE_VIEW (selector);
2631 model = gtk_tree_view_get_model (tree_view);
2632 selection = gtk_tree_view_get_selection (tree_view);
2633
2634 reference = selector->priv->saved_primary_selection;
2635
2636 if (gtk_tree_row_reference_valid (reference)) {
2637 GtkTreePath *path;
2638
2639 path = gtk_tree_row_reference_get_path (reference);
2640 have_iter = gtk_tree_model_get_iter (model, &iter, path);
2641 gtk_tree_path_free (path);
2642 }
2643
2644 if (!have_iter)
2645 have_iter = gtk_tree_selection_get_selected (
2646 selection, NULL, &iter);
2647
2648 if (!have_iter)
2649 return NULL;
2650
2651 gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
2652
2653 extension_name = e_source_selector_get_extension_name (selector);
2654
2655 if (!e_source_has_extension (source, extension_name)) {
2656 g_object_unref (source);
2657 return NULL;
2658 }
2659
2660 return source;
2661 }
2662
2663 /**
2664 * e_source_selector_set_primary_selection:
2665 * @selector: an #ESourceSelector widget
2666 * @source: an #ESource to select
2667 *
2668 * Highlights @source in @selector. The highlighted #ESource is called
2669 * the primary selection.
2670 *
2671 * Do not confuse this function with e_source_selector_select_source(),
2672 * which activates the check box next to an #ESource's display name in
2673 * @selector. This function does not alter the check box.
2674 **/
2675 void
e_source_selector_set_primary_selection(ESourceSelector * selector,ESource * source)2676 e_source_selector_set_primary_selection (ESourceSelector *selector,
2677 ESource *source)
2678 {
2679 GHashTable *source_index;
2680 GtkTreeRowReference *reference;
2681 GtkTreeSelection *selection;
2682 GtkTreeView *tree_view;
2683 GtkTreePath *child_path;
2684 GtkTreePath *parent_path;
2685 const gchar *extension_name;
2686
2687 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
2688 g_return_if_fail (E_IS_SOURCE (source));
2689
2690 tree_view = GTK_TREE_VIEW (selector);
2691 selection = gtk_tree_view_get_selection (tree_view);
2692
2693 source_index = selector->priv->source_index;
2694 reference = g_hash_table_lookup (source_index, source);
2695
2696 /* XXX Maybe we should return a success/fail boolean? */
2697 if (!gtk_tree_row_reference_valid (reference))
2698 return;
2699
2700 extension_name = e_source_selector_get_extension_name (selector);
2701
2702 /* Return silently if attempting to select a parent node
2703 * lacking the expected extension (e.g. On This Computer). */
2704 if (!e_source_has_extension (source, extension_name))
2705 return;
2706
2707 /* We block the signal because this all needs to be atomic */
2708 g_signal_handlers_block_matched (
2709 selection, G_SIGNAL_MATCH_FUNC,
2710 0, 0, NULL, selection_changed_callback, NULL);
2711 gtk_tree_selection_unselect_all (selection);
2712 g_signal_handlers_unblock_matched (
2713 selection, G_SIGNAL_MATCH_FUNC,
2714 0, 0, NULL, selection_changed_callback, NULL);
2715
2716 clear_saved_primary_selection (selector);
2717
2718 child_path = gtk_tree_row_reference_get_path (reference);
2719
2720 parent_path = gtk_tree_path_copy (child_path);
2721 gtk_tree_path_up (parent_path);
2722
2723 if (gtk_tree_view_row_expanded (tree_view, parent_path)) {
2724 gtk_tree_selection_select_path (selection, child_path);
2725 } else {
2726 selector->priv->saved_primary_selection =
2727 gtk_tree_row_reference_copy (reference);
2728 g_signal_emit (selector, signals[PRIMARY_SELECTION_CHANGED], 0);
2729 g_object_notify (G_OBJECT (selector), "primary-selection");
2730 }
2731
2732 gtk_tree_path_free (child_path);
2733 gtk_tree_path_free (parent_path);
2734 }
2735
2736 /**
2737 * e_source_selector_ref_source_by_iter:
2738 * @selector: an #ESourceSelector
2739 * @iter: a #GtkTreeIter
2740 *
2741 * Returns the #ESource object at @iter.
2742 *
2743 * The returned #ESource is referenced for thread-safety and must be
2744 * unreferenced with g_object_unref() when finished with it.
2745 *
2746 * Returns: the #ESource object at @iter, or %NULL
2747 *
2748 * Since: 3.8
2749 **/
2750 ESource *
e_source_selector_ref_source_by_iter(ESourceSelector * selector,GtkTreeIter * iter)2751 e_source_selector_ref_source_by_iter (ESourceSelector *selector,
2752 GtkTreeIter *iter)
2753 {
2754 ESource *source = NULL;
2755 GtkTreeModel *model;
2756
2757 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
2758 g_return_val_if_fail (iter != NULL, NULL);
2759
2760 model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector));
2761
2762 gtk_tree_model_get (model, iter, COLUMN_SOURCE, &source, -1);
2763
2764 return source;
2765 }
2766
2767 /**
2768 * e_source_selector_ref_source_by_path:
2769 * @selector: an #ESourceSelector
2770 * @path: a #GtkTreePath
2771 *
2772 * Returns the #ESource object at @path, or %NULL if @path is invalid.
2773 *
2774 * The returned #ESource is referenced for thread-safety and must be
2775 * unreferenced with g_object_unref() when finished with it.
2776 *
2777 * Returns: the #ESource object at @path, or %NULL
2778 *
2779 * Since: 3.6
2780 **/
2781 ESource *
e_source_selector_ref_source_by_path(ESourceSelector * selector,GtkTreePath * path)2782 e_source_selector_ref_source_by_path (ESourceSelector *selector,
2783 GtkTreePath *path)
2784 {
2785 ESource *source = NULL;
2786 GtkTreeModel *model;
2787 GtkTreeIter iter;
2788
2789 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
2790 g_return_val_if_fail (path != NULL, NULL);
2791
2792 model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector));
2793
2794 if (gtk_tree_model_get_iter (model, &iter, path))
2795 gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
2796
2797 return source;
2798 }
2799
2800 /**
2801 * e_source_selector_queue_write:
2802 * @selector: an #ESourceSelector
2803 * @source: an #ESource with changes to be written
2804 *
2805 * Queues a main loop idle callback to write changes to @source back to
2806 * the D-Bus registry service.
2807 *
2808 * Since: 3.6
2809 **/
2810 void
e_source_selector_queue_write(ESourceSelector * selector,ESource * source)2811 e_source_selector_queue_write (ESourceSelector *selector,
2812 ESource *source)
2813 {
2814 GSource *idle_source;
2815 GHashTable *pending_writes;
2816 GMainContext *main_context;
2817 AsyncContext *async_context;
2818
2819 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
2820 g_return_if_fail (E_IS_SOURCE (source));
2821
2822 main_context = selector->priv->main_context;
2823 pending_writes = selector->priv->pending_writes;
2824
2825 idle_source = g_hash_table_lookup (pending_writes, source);
2826 if (idle_source != NULL && !g_source_is_destroyed (idle_source))
2827 return;
2828
2829 async_context = g_slice_new0 (AsyncContext);
2830 async_context->selector = g_object_ref (selector);
2831 async_context->source = g_object_ref (source);
2832
2833 /* Set a higher priority so this idle source runs before our
2834 * source_selector_cancel_write() signal handler, which will
2835 * cancel this idle source. Cancellation is the right thing
2836 * to do when receiving changes from OTHER registry clients,
2837 * but we don't want to cancel our own changes.
2838 *
2839 * XXX This might be an argument for using etags.
2840 */
2841 idle_source = g_idle_source_new ();
2842 g_hash_table_insert (
2843 pending_writes,
2844 g_object_ref (source),
2845 g_source_ref (idle_source));
2846 g_source_set_callback (
2847 idle_source,
2848 source_selector_write_idle_cb,
2849 async_context,
2850 (GDestroyNotify) async_context_free);
2851 g_source_set_priority (idle_source, G_PRIORITY_HIGH_IDLE);
2852 g_source_attach (idle_source, main_context);
2853 g_source_unref (idle_source);
2854 }
2855
2856 static void
source_selector_sort_sibling(ESourceSelector * selector,GtkTreeModel * model,GtkTreeIter * changed_child)2857 source_selector_sort_sibling (ESourceSelector *selector,
2858 GtkTreeModel *model,
2859 GtkTreeIter *changed_child)
2860 {
2861 GtkTreeIter iter, dest_iter;
2862 ESource *child_source = NULL;
2863 gboolean changed = FALSE, insert_before;
2864
2865 gtk_tree_model_get (model, changed_child, COLUMN_SOURCE, &child_source, -1);
2866
2867 if (!child_source)
2868 return;
2869
2870 insert_before = TRUE;
2871 iter = *changed_child;
2872
2873 while (gtk_tree_model_iter_previous (model, &iter)) {
2874 ESource *source = NULL;
2875 gint cmp_value;
2876
2877 gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
2878
2879 cmp_value = e_util_source_compare_for_sort (source, child_source);
2880
2881 g_clear_object (&source);
2882
2883 if (cmp_value <= 0)
2884 break;
2885
2886 dest_iter = iter;
2887 changed = TRUE;
2888 }
2889
2890 if (!changed) {
2891 insert_before = FALSE;
2892 iter = *changed_child;
2893
2894 while (gtk_tree_model_iter_next (model, &iter)) {
2895 ESource *source = NULL;
2896 gint cmp_value;
2897
2898 gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
2899
2900 cmp_value = e_util_source_compare_for_sort (child_source, source);
2901
2902 g_clear_object (&source);
2903
2904 if (cmp_value <= 0)
2905 break;
2906
2907 dest_iter = iter;
2908 changed = TRUE;
2909 }
2910 }
2911
2912 if (changed) {
2913 GtkTreeStore *tree_store = GTK_TREE_STORE (model);
2914 GtkTreeSelection *selection;
2915 GtkTreeRowReference *reference;
2916 GtkTreeIter new_child;
2917 GtkTreePath *path;
2918 gboolean been_selected;
2919
2920 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (selector));
2921 been_selected = gtk_tree_selection_iter_is_selected (selection, changed_child);
2922
2923 gtk_tree_store_remove (tree_store, changed_child);
2924
2925 if (insert_before)
2926 gtk_tree_store_insert_before (tree_store, &new_child, NULL, &dest_iter);
2927 else
2928 gtk_tree_store_insert_after (tree_store, &new_child, NULL, &dest_iter);
2929
2930 g_hash_table_remove (selector->priv->source_index, child_source);
2931
2932 path = gtk_tree_model_get_path (model, &new_child);
2933 reference = gtk_tree_row_reference_new (model, path);
2934 g_hash_table_insert (selector->priv->source_index, g_object_ref (child_source), reference);
2935 gtk_tree_path_free (path);
2936
2937 e_source_selector_update_row (selector, child_source);
2938
2939 if (been_selected)
2940 gtk_tree_selection_select_iter (selection, &new_child);
2941 }
2942
2943 g_object_unref (child_source);
2944 }
2945
2946 /**
2947 * e_source_selector_update_row:
2948 * @selector: an #ESourceSelector
2949 * @source: an #ESource
2950 *
2951 * Updates the corresponding #GtkTreeModel row for @source.
2952 *
2953 * This function is public so it can be called from subclasses like
2954 * #EClientSelector.
2955 *
2956 * Since: 3.8
2957 **/
2958 void
e_source_selector_update_row(ESourceSelector * selector,ESource * source)2959 e_source_selector_update_row (ESourceSelector *selector,
2960 ESource *source)
2961 {
2962 GHashTable *source_index;
2963 ESourceExtension *extension = NULL;
2964 GtkTreeRowReference *reference;
2965 GtkTreeModel *model;
2966 GtkTreePath *path;
2967 GtkTreeIter iter;
2968 const gchar *extension_name;
2969 const gchar *display_name;
2970 gboolean selected;
2971
2972 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
2973 g_return_if_fail (E_IS_SOURCE (source));
2974
2975 source_index = selector->priv->source_index;
2976 reference = g_hash_table_lookup (source_index, source);
2977
2978 /* This function runs when ANY ESource in the registry changes.
2979 * If the ESource is not in our tree model then return silently. */
2980 if (reference == NULL)
2981 return;
2982
2983 /* If we do have a row reference, it should be valid. */
2984 g_return_if_fail (gtk_tree_row_reference_valid (reference));
2985
2986 model = gtk_tree_row_reference_get_model (reference);
2987 path = gtk_tree_row_reference_get_path (reference);
2988 gtk_tree_model_get_iter (model, &iter, path);
2989 gtk_tree_path_free (path);
2990
2991 display_name = e_source_get_display_name (source);
2992
2993 extension_name = e_source_selector_get_extension_name (selector);
2994 selected = e_source_selector_source_is_selected (selector, source);
2995
2996 if (e_source_has_extension (source, extension_name))
2997 extension = e_source_get_extension (source, extension_name);
2998
2999 if (extension != NULL) {
3000 ESource *current_source = NULL;
3001 GdkRGBA rgba;
3002 const gchar *color_spec = NULL;
3003 const gchar *icon_name;
3004 gboolean show_color;
3005 gboolean show_icons;
3006 gboolean show_toggle;
3007 guint old_sort_order = 0, new_sort_order = 0;
3008
3009 show_color =
3010 E_IS_SOURCE_SELECTABLE (extension) &&
3011 e_source_selector_get_show_colors (selector);
3012
3013 if (show_color)
3014 color_spec = e_source_selectable_get_color (
3015 E_SOURCE_SELECTABLE (extension));
3016
3017 if (color_spec != NULL && *color_spec != '\0')
3018 show_color = gdk_rgba_parse (&rgba, color_spec);
3019
3020 show_icons = e_source_selector_get_show_icons (selector);
3021 icon_name = source_selector_get_icon_name (selector, source);
3022
3023 show_toggle = e_source_selector_get_show_toggles (selector);
3024
3025 gtk_tree_model_get (model, &iter,
3026 COLUMN_SORT_ORDER, &old_sort_order,
3027 COLUMN_SOURCE, ¤t_source,
3028 -1);
3029
3030 if (E_IS_SOURCE_SELECTABLE (extension))
3031 new_sort_order = e_source_selectable_get_order (E_SOURCE_SELECTABLE (extension));
3032 else if (E_IS_SOURCE_ADDRESS_BOOK (extension))
3033 new_sort_order = e_source_address_book_get_order (E_SOURCE_ADDRESS_BOOK (extension));
3034 else
3035 new_sort_order = old_sort_order;
3036
3037 gtk_tree_store_set (
3038 GTK_TREE_STORE (model), &iter,
3039 COLUMN_NAME, display_name,
3040 COLUMN_COLOR, show_color ? &rgba : NULL,
3041 COLUMN_ACTIVE, selected,
3042 COLUMN_ICON_NAME, icon_name,
3043 COLUMN_SHOW_COLOR, show_color,
3044 COLUMN_SHOW_ICONS, show_icons,
3045 COLUMN_SHOW_TOGGLE, show_toggle,
3046 COLUMN_WEIGHT, PANGO_WEIGHT_NORMAL,
3047 COLUMN_SOURCE, source,
3048 COLUMN_SORT_ORDER, new_sort_order,
3049 -1);
3050
3051 /* The 'current_source' is NULL on newly added rows and non-NULL when refreshing existing row */
3052 if (new_sort_order != old_sort_order && current_source != NULL)
3053 source_selector_sort_sibling (selector, model, &iter);
3054
3055 g_clear_object (¤t_source);
3056 } else {
3057 gtk_tree_store_set (
3058 GTK_TREE_STORE (model), &iter,
3059 COLUMN_NAME, display_name,
3060 COLUMN_COLOR, NULL,
3061 COLUMN_ACTIVE, FALSE,
3062 COLUMN_ICON_NAME, NULL,
3063 COLUMN_SHOW_COLOR, FALSE,
3064 COLUMN_SHOW_ICONS, FALSE,
3065 COLUMN_SHOW_TOGGLE, FALSE,
3066 COLUMN_WEIGHT, PANGO_WEIGHT_BOLD,
3067 COLUMN_SOURCE, source,
3068 -1);
3069 }
3070 }
3071
3072 /**
3073 * e_source_selector_update_all_rows:
3074 * @selector: an #ESourceSelector
3075 *
3076 * Calls e_source_selector_update_row() for each #ESource being shown by
3077 * @selector, according to the #ESourceSelector:extension_name property.
3078 *
3079 * Since: 3.10
3080 **/
3081 void
e_source_selector_update_all_rows(ESourceSelector * selector)3082 e_source_selector_update_all_rows (ESourceSelector *selector)
3083 {
3084 ESourceRegistry *registry;
3085 GList *list, *link;
3086 const gchar *extension_name;
3087
3088 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
3089
3090 registry = e_source_selector_get_registry (selector);
3091 extension_name = e_source_selector_get_extension_name (selector);
3092
3093 list = e_source_registry_list_sources (registry, extension_name);
3094
3095 for (link = list; link != NULL; link = g_list_next (link)) {
3096 ESource *source = E_SOURCE (link->data);
3097 e_source_selector_update_row (selector, source);
3098 }
3099
3100 g_list_free_full (list, (GDestroyNotify) g_object_unref);
3101 }
3102
3103 static gboolean
e_source_selector_get_source_iter(ESourceSelector * selector,ESource * source,GtkTreeIter * iter,GtkTreeModel ** out_model)3104 e_source_selector_get_source_iter (ESourceSelector *selector,
3105 ESource *source,
3106 GtkTreeIter *iter,
3107 GtkTreeModel **out_model)
3108 {
3109 GtkTreeRowReference *reference;
3110 GtkTreeModel *model;
3111 GtkTreePath *path;
3112 gboolean found;
3113
3114 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
3115 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
3116 g_return_val_if_fail (iter, FALSE);
3117
3118 reference = g_hash_table_lookup (selector->priv->source_index, source);
3119
3120 /* If the ESource is not in our tree model then return silently. */
3121 if (!reference)
3122 return FALSE;
3123
3124 /* If we do have a row reference, it should be valid. */
3125 g_return_val_if_fail (gtk_tree_row_reference_valid (reference), FALSE);
3126
3127 model = gtk_tree_row_reference_get_model (reference);
3128 path = gtk_tree_row_reference_get_path (reference);
3129 found = gtk_tree_model_get_iter (model, iter, path);
3130 gtk_tree_path_free (path);
3131
3132 if (found && out_model)
3133 *out_model = model;
3134
3135 return found;
3136 }
3137
3138 /**
3139 * e_source_selector_set_source_tooltip:
3140 * @selector: an #ESourceSelector
3141 * @source: an #ESource for which to set the tooltip
3142 *
3143 * Updates tooltip for the given @source.
3144 *
3145 * Since: 3.16
3146 **/
3147 void
e_source_selector_set_source_tooltip(ESourceSelector * selector,ESource * source,const gchar * tooltip)3148 e_source_selector_set_source_tooltip (ESourceSelector *selector,
3149 ESource *source,
3150 const gchar *tooltip)
3151 {
3152 GtkTreeModel *model = NULL;
3153 GtkTreeIter iter;
3154 gchar *current_tooltip = NULL;
3155
3156 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
3157 g_return_if_fail (E_IS_SOURCE (source));
3158
3159 if (!e_source_selector_get_source_iter (selector, source, &iter, &model))
3160 return;
3161
3162 gtk_tree_model_get (model, &iter,
3163 COLUMN_TOOLTIP, ¤t_tooltip,
3164 -1);
3165
3166 /* This avoids possible recursion with ATK enabled */
3167 if (e_util_strcmp0 (current_tooltip, tooltip) != 0) {
3168 gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
3169 COLUMN_TOOLTIP, tooltip && *tooltip ? tooltip : NULL,
3170 -1);
3171 }
3172
3173 g_free (current_tooltip);
3174 }
3175
3176 /**
3177 * e_source_selector_dup_source_tooltip:
3178 * @selector: an #ESourceSelector
3179 * @source: an #ESource for which to read the tooltip
3180 *
3181 * Returns: Current tooltip for the given @source. Free the returned
3182 * string with g_free() when done with it.
3183 *
3184 * Since: 3.16
3185 **/
3186 gchar *
e_source_selector_dup_source_tooltip(ESourceSelector * selector,ESource * source)3187 e_source_selector_dup_source_tooltip (ESourceSelector *selector,
3188 ESource *source)
3189 {
3190 GtkTreeModel *model = NULL;
3191 GtkTreeIter iter;
3192 gchar *tooltip = NULL;
3193
3194 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
3195 g_return_val_if_fail (E_IS_SOURCE (source), NULL);
3196
3197 if (!e_source_selector_get_source_iter (selector, source, &iter, &model))
3198 return NULL;
3199
3200 gtk_tree_model_get (
3201 model, &iter,
3202 COLUMN_TOOLTIP, &tooltip,
3203 -1);
3204
3205 return tooltip;
3206 }
3207
3208 /**
3209 * e_source_selector_set_source_is_busy:
3210 * @selector: an #ESourceSelector
3211 * @source: an #ESource for which to set the is-busy status
3212 *
3213 * Updates the is-busy flag status for the given @source.
3214 *
3215 * Since: 3.16
3216 **/
3217 void
e_source_selector_set_source_is_busy(ESourceSelector * selector,ESource * source,gboolean is_busy)3218 e_source_selector_set_source_is_busy (ESourceSelector *selector,
3219 ESource *source,
3220 gboolean is_busy)
3221 {
3222 GtkTreeModel *model = NULL;
3223 GtkTreeIter iter;
3224 gboolean old_is_busy = FALSE;
3225
3226 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
3227 g_return_if_fail (E_IS_SOURCE (source));
3228
3229 if (!e_source_selector_get_source_iter (selector, source, &iter, &model))
3230 return;
3231
3232 gtk_tree_model_get (model, &iter,
3233 COLUMN_IS_BUSY, &old_is_busy,
3234 -1);
3235
3236 if ((old_is_busy ? 1 : 0) == (is_busy ? 1 : 0))
3237 return;
3238
3239 gtk_tree_store_set (
3240 GTK_TREE_STORE (model), &iter,
3241 COLUMN_IS_BUSY, is_busy,
3242 -1);
3243
3244 if (is_busy)
3245 source_selector_inc_busy_sources (selector);
3246 else
3247 source_selector_dec_busy_sources (selector);
3248 }
3249
3250 /**
3251 * e_source_selector_get_source_is_busy:
3252 * @selector: an #ESourceSelector
3253 * @source: an #ESource for which to read the is-busy status
3254 *
3255 * Returns: Current is-busy flag status for the given @source.
3256 *
3257 * Since: 3.16
3258 **/
3259 gboolean
e_source_selector_get_source_is_busy(ESourceSelector * selector,ESource * source)3260 e_source_selector_get_source_is_busy (ESourceSelector *selector,
3261 ESource *source)
3262 {
3263 GtkTreeModel *model = NULL;
3264 GtkTreeIter iter;
3265 gboolean is_busy = FALSE;
3266
3267 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
3268 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
3269
3270 if (!e_source_selector_get_source_iter (selector, source, &iter, &model))
3271 return FALSE;
3272
3273 gtk_tree_model_get (
3274 model, &iter,
3275 COLUMN_IS_BUSY, &is_busy,
3276 -1);
3277
3278 return is_busy;
3279 }
3280
3281 /**
3282 * e_source_selector_set_source_connection_status:
3283 * @selector: an #ESourceSelector
3284 * @source: an #ESource for which to set the connection status
3285 * @value: the value to set
3286 *
3287 * Sets connection status for the @source. It's not interpreted by the @selector,
3288 * it only saves it into the model. Read it back with e_source_selector_get_source_connection_status().
3289 *
3290 * Since: 3.40
3291 **/
3292 void
e_source_selector_set_source_connection_status(ESourceSelector * selector,ESource * source,guint value)3293 e_source_selector_set_source_connection_status (ESourceSelector *selector,
3294 ESource *source,
3295 guint value)
3296 {
3297 GtkTreeModel *model = NULL;
3298 GtkTreeIter iter;
3299 guint current_value = 0;
3300
3301 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
3302 g_return_if_fail (E_IS_SOURCE (source));
3303
3304 if (!e_source_selector_get_source_iter (selector, source, &iter, &model))
3305 return;
3306
3307 gtk_tree_model_get (
3308 model, &iter,
3309 COLUMN_CONNECTION_STATUS, ¤t_value,
3310 -1);
3311
3312 /* This avoids possible recursion with ATK enabled */
3313 if (current_value != value) {
3314 gtk_tree_store_set (
3315 GTK_TREE_STORE (model), &iter,
3316 COLUMN_CONNECTION_STATUS, value,
3317 -1);
3318 }
3319 }
3320
3321 /**
3322 * e_source_selector_get_source_connection_status:
3323 * @selector: an #ESourceSelector
3324 * @source: an #ESource for which to get the stored connection status
3325 *
3326 * Gets connection status for the @source. It's not interpreted by the @selector,
3327 * it only returns what had been saved into the model with e_source_selector_set_source_connection_status().
3328 *
3329 * Returns: Value previously stored with e_source_selector_set_source_connection_status(),
3330 * or 0 when not set or when the @source was not found.
3331 *
3332 * Since: 3.40
3333 **/
3334 guint
e_source_selector_get_source_connection_status(ESourceSelector * selector,ESource * source)3335 e_source_selector_get_source_connection_status (ESourceSelector *selector,
3336 ESource *source)
3337 {
3338 GtkTreeModel *model = NULL;
3339 GtkTreeIter iter;
3340 guint value = 0;
3341
3342 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), 0);
3343 g_return_val_if_fail (E_IS_SOURCE (source), 0);
3344
3345 if (!e_source_selector_get_source_iter (selector, source, &iter, &model))
3346 return 0;
3347
3348 gtk_tree_model_get (
3349 model, &iter,
3350 COLUMN_CONNECTION_STATUS, &value,
3351 -1);
3352
3353 return value;
3354 }
3355
3356 static gboolean
source_selector_get_source_hidden(ESourceSelector * selector,ESource * source)3357 source_selector_get_source_hidden (ESourceSelector *selector,
3358 ESource *source)
3359 {
3360 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
3361 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
3362 g_return_val_if_fail (e_source_get_uid (source) != NULL, FALSE);
3363
3364 return g_hash_table_contains (selector->priv->hidden_groups, e_source_get_uid (source));
3365 }
3366
3367 static void
tree_show_toggled(GtkCellRendererToggle * renderer,gchar * path_str,gpointer user_data)3368 tree_show_toggled (GtkCellRendererToggle *renderer,
3369 gchar *path_str,
3370 gpointer user_data)
3371 {
3372 GtkWidget *table = user_data;
3373 GtkTreeModel *model;
3374 GtkTreePath *path;
3375 GtkTreeIter iter;
3376
3377 path = gtk_tree_path_new_from_string (path_str);
3378 model = gtk_tree_view_get_model (GTK_TREE_VIEW (table));
3379
3380 if (gtk_tree_model_get_iter (model, &iter, path)) {
3381 gboolean shown = TRUE;
3382
3383 gtk_tree_model_get (model, &iter, 2, &shown, -1);
3384 shown = !shown;
3385 gtk_list_store_set (GTK_LIST_STORE (model), &iter, 2, shown, -1);
3386
3387 /* to have buttons synced with the change */
3388 g_signal_emit_by_name (table, "cursor-changed");
3389 }
3390
3391 gtk_tree_path_free (path);
3392 }
3393
3394 static GtkWidget *
create_tree(ESourceSelector * selector,GtkWidget ** tree)3395 create_tree (ESourceSelector *selector,
3396 GtkWidget **tree)
3397 {
3398 ESourceRegistry *registry;
3399 GtkWidget *table, *scrolled;
3400 GtkTreeSelection *selection;
3401 GtkCellRenderer *renderer;
3402 GtkListStore *model;
3403 GNode *root;
3404
3405 scrolled = gtk_scrolled_window_new (NULL, NULL);
3406 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), GTK_SHADOW_IN);
3407 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
3408 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3409
3410 model = gtk_list_store_new (3, G_TYPE_STRING, E_TYPE_SOURCE, G_TYPE_BOOLEAN);
3411 table = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
3412 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (table), FALSE);
3413
3414 renderer = gtk_cell_renderer_toggle_new ();
3415 g_object_set (G_OBJECT (renderer), "activatable", TRUE, NULL);
3416 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (table), -1,
3417 _("Show"), renderer,
3418 "active", 2, NULL);
3419 g_signal_connect (renderer, "toggled", G_CALLBACK (tree_show_toggled), table);
3420
3421 renderer = gtk_cell_renderer_text_new ();
3422 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (table), -1,
3423 _("Group name"), renderer,
3424 "text", 0, NULL);
3425
3426 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (table));
3427 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
3428
3429 gtk_container_add (GTK_CONTAINER (scrolled), table);
3430
3431 *tree = table;
3432
3433 registry = e_source_selector_get_registry (selector);
3434 root = e_source_registry_build_display_tree (registry, e_source_selector_get_extension_name (selector));
3435
3436 source_selector_sort_groups (selector, root);
3437
3438 if (root) {
3439 GNode *node;
3440
3441 for (node = g_node_first_child (root); node; node = g_node_next_sibling (node)) {
3442 GtkTreeIter iter;
3443 ESource *source;
3444
3445 source = node->data;
3446
3447 if (source) {
3448 gtk_list_store_append (model, &iter);
3449 gtk_list_store_set (model, &iter,
3450 0, e_source_get_display_name (source),
3451 1, source,
3452 2, !source_selector_get_source_hidden (selector, source),
3453 -1);
3454 }
3455 }
3456 }
3457
3458 e_source_registry_free_display_tree (root);
3459
3460 g_object_unref (model);
3461
3462 return scrolled;
3463 }
3464
3465 static void
process_move_button(GtkButton * button,GtkTreeView * tree,gboolean is_up,gboolean do_move)3466 process_move_button (GtkButton *button,
3467 GtkTreeView *tree,
3468 gboolean is_up,
3469 gboolean do_move)
3470 {
3471 GtkTreeModel *model;
3472 GtkTreeSelection *selection;
3473 GtkTreeIter iter;
3474 gboolean enable = FALSE;
3475
3476 g_return_if_fail (button != NULL);
3477 g_return_if_fail (tree != NULL);
3478
3479 selection = gtk_tree_view_get_selection (tree);
3480
3481 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3482 gpointer ptr = NULL, ptr2;
3483 GtkTreeIter iter2;
3484 int i, cnt = gtk_tree_model_iter_n_children (model, NULL);
3485 gboolean can_move = FALSE;
3486
3487 gtk_tree_model_get (model, &iter, 1, &ptr, -1);
3488
3489 for (i = 0; i < cnt; i++) {
3490 if (!gtk_tree_model_iter_nth_child (model, &iter2, NULL, i))
3491 break;
3492
3493 ptr2 = NULL;
3494 gtk_tree_model_get (model, &iter2, 1, &ptr2, -1);
3495
3496 if (ptr == ptr2 || (is_up && !do_move && i > 0)) {
3497 can_move = TRUE;
3498 break;
3499 }
3500 }
3501
3502 if (can_move)
3503 can_move = ((is_up && i > 0) || (!is_up && i + 1 < cnt)) && i < cnt;
3504
3505 if (can_move && do_move) {
3506 i = i + (is_up ? -1 : 1);
3507 if (gtk_tree_model_iter_nth_child (model, &iter2, NULL, i)) {
3508 GtkTreePath *path;
3509
3510 gtk_list_store_swap (GTK_LIST_STORE (model), &iter, &iter2);
3511 gtk_tree_selection_select_iter (selection, &iter);
3512
3513 /* scroll to the selected cell */
3514 path = gtk_tree_model_get_path (model, &iter);
3515 gtk_tree_view_scroll_to_cell (tree, path, NULL, FALSE, 0.0, 0.0);
3516 gtk_tree_path_free (path);
3517
3518 /* cursor has been moved to the other row */
3519 can_move = (is_up && i > 0) || (!is_up && i + 1 < cnt);
3520
3521 g_signal_emit_by_name (tree, "cursor-changed");
3522 }
3523 }
3524
3525 enable = can_move;
3526 }
3527
3528 if (!do_move)
3529 gtk_widget_set_sensitive (GTK_WIDGET (button), enable);
3530 }
3531
3532 static void
up_clicked(GtkButton * button,GtkTreeView * tree)3533 up_clicked (GtkButton *button,
3534 GtkTreeView *tree)
3535 {
3536 process_move_button (button, tree, TRUE, TRUE);
3537 }
3538
3539 static void
up_cursor_changed(GtkTreeView * tree,GtkButton * button)3540 up_cursor_changed (GtkTreeView *tree,
3541 GtkButton *button)
3542 {
3543 process_move_button (button, tree, TRUE, FALSE);
3544 }
3545
3546 static void
down_clicked(GtkButton * button,GtkTreeView * tree)3547 down_clicked (GtkButton *button,
3548 GtkTreeView *tree)
3549 {
3550 process_move_button (button, tree, FALSE, TRUE);
3551 }
3552
3553 static void
down_cursor_changed(GtkTreeView * tree,GtkButton * button)3554 down_cursor_changed (GtkTreeView *tree,
3555 GtkButton *button)
3556 {
3557 process_move_button (button, tree, FALSE, FALSE);
3558 }
3559
3560 static void
show_hide_cursor_changed(GtkTreeView * tree,GtkButton * button)3561 show_hide_cursor_changed (GtkTreeView *tree,
3562 GtkButton *button)
3563 {
3564 GtkTreeModel *model;
3565 GtkTreeSelection *selection;
3566 GtkTreeIter iter;
3567
3568 g_return_if_fail (button != NULL);
3569 g_return_if_fail (tree != NULL);
3570
3571 selection = gtk_tree_view_get_selection (tree);
3572
3573 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3574 gboolean shown = FALSE;
3575
3576 gtk_tree_model_get (model, &iter, 2, &shown, -1);
3577
3578 gtk_button_set_label (button, shown ? _("_Hide") : _("_Show"));
3579 }
3580 }
3581
3582 static void
show_hide_clicked(GtkButton * button,GtkTreeView * tree)3583 show_hide_clicked (GtkButton *button,
3584 GtkTreeView *tree)
3585 {
3586 GtkTreeModel *model;
3587 GtkTreeSelection *selection;
3588 GtkTreeIter iter;
3589
3590 g_return_if_fail (button != NULL);
3591 g_return_if_fail (tree != NULL);
3592
3593 selection = gtk_tree_view_get_selection (tree);
3594
3595 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
3596 gboolean shown = TRUE;
3597
3598 gtk_tree_model_get (model, &iter, 2, &shown, -1);
3599 shown = !shown;
3600 gtk_list_store_set (GTK_LIST_STORE (model), &iter, 2, shown, -1);
3601
3602 show_hide_cursor_changed (tree, button);
3603 }
3604 }
3605
3606 /**
3607 * e_source_selector_manage_groups:
3608 * @selector: an #ESourceSelector
3609 *
3610 * Manages list of groups, like their order in the source selector,
3611 * and a hidden property of the group.
3612 *
3613 * Returns: Whether user confirmed changes in the dialog.
3614 *
3615 * Since: 3.20
3616 **/
3617 gboolean
e_source_selector_manage_groups(ESourceSelector * selector)3618 e_source_selector_manage_groups (ESourceSelector *selector)
3619 {
3620 GtkWidget *dlg, *box, *pbox, *tree, *w, *w2;
3621 gchar *txt;
3622 gboolean confirmed = FALSE;
3623
3624 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
3625
3626 w = gtk_widget_get_toplevel (GTK_WIDGET (selector));
3627 if (!w || !gtk_widget_is_toplevel (w))
3628 w = NULL;
3629
3630 dlg = gtk_dialog_new_with_buttons (_("Manage Groups"),
3631 w ? GTK_WINDOW (w) : NULL,
3632 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3633 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
3634 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
3635 NULL);
3636
3637 w = gtk_dialog_get_content_area (GTK_DIALOG (dlg));
3638 box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
3639 gtk_container_set_border_width (GTK_CONTAINER (box), 12);
3640 gtk_box_pack_start (GTK_BOX (w), box, TRUE, TRUE, 0);
3641
3642 txt = g_strconcat ("<b>", _("Available Groups:"), "</b>", NULL);
3643 w = gtk_label_new ("");
3644 gtk_label_set_markup (GTK_LABEL (w), txt);
3645 g_free (txt);
3646 gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.5);
3647 gtk_box_pack_start (GTK_BOX (box), w, FALSE, FALSE, 2);
3648
3649 pbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
3650 gtk_box_pack_start (GTK_BOX (box), pbox, TRUE, TRUE, 2);
3651
3652 /* space on the left */
3653 w = gtk_label_new ("");
3654 gtk_box_pack_start (GTK_BOX (pbox), w, FALSE, FALSE, 6);
3655
3656 w = create_tree (selector, &tree);
3657 gtk_widget_set_size_request (w, 200, 240);
3658 gtk_box_pack_start (GTK_BOX (pbox), w, TRUE, TRUE, 2);
3659
3660 /* box of buttons */
3661 w2 = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
3662 gtk_button_box_set_layout (GTK_BUTTON_BOX (w2), GTK_BUTTONBOX_START);
3663 gtk_box_pack_start (GTK_BOX (pbox), w2, FALSE, FALSE, 2);
3664
3665 #define add_button(_x,_y,_cb,_cb2) \
3666 w = (_x) ? gtk_button_new_from_icon_name (_x, GTK_ICON_SIZE_BUTTON) : gtk_button_new (); \
3667 gtk_button_set_label (GTK_BUTTON (w), _y); \
3668 gtk_button_set_use_underline (GTK_BUTTON (w), TRUE); \
3669 gtk_box_pack_start (GTK_BOX (w2), w, FALSE, FALSE, 2); \
3670 g_signal_connect (w, "clicked", (GCallback)_cb, tree); \
3671 g_signal_connect (tree, "cursor-changed", (GCallback)_cb2, w);
3672
3673 add_button ("go-up", _("_Up"), up_clicked, up_cursor_changed);
3674 add_button ("go-down", _("_Down"), down_clicked, down_cursor_changed);
3675
3676 add_button (NULL, _("_Show"), show_hide_clicked, show_hide_cursor_changed);
3677 gtk_button_set_use_underline (GTK_BUTTON (w), TRUE);
3678
3679 #undef add_button
3680
3681 gtk_widget_show_all (box);
3682
3683 if (gtk_dialog_run (GTK_DIALOG (dlg)) == GTK_RESPONSE_ACCEPT) {
3684 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
3685 GtkTreeIter iter;
3686 gint ii, cnt = gtk_tree_model_iter_n_children (model, NULL);
3687
3688 g_hash_table_remove_all (selector->priv->hidden_groups);
3689 g_slist_free_full (selector->priv->groups_order, g_free);
3690 selector->priv->groups_order = NULL;
3691
3692 for (ii = 0; ii < cnt; ii++) {
3693 gpointer group = NULL;
3694 gboolean shown = TRUE;
3695
3696 if (!gtk_tree_model_iter_nth_child (model, &iter, NULL, ii))
3697 break;
3698
3699 gtk_tree_model_get (model, &iter, 1, &group, 2, &shown, -1);
3700
3701 if (group) {
3702 const gchar *uid = e_source_get_uid (group);
3703
3704 selector->priv->groups_order = g_slist_prepend (selector->priv->groups_order, g_strdup (uid));
3705
3706 if (!shown)
3707 g_hash_table_insert (selector->priv->hidden_groups, g_strdup (uid), GINT_TO_POINTER (1));
3708 }
3709 }
3710
3711 selector->priv->groups_order = g_slist_reverse (selector->priv->groups_order);
3712
3713 source_selector_build_model (selector);
3714
3715 confirmed = TRUE;
3716 }
3717
3718 gtk_widget_destroy (dlg);
3719
3720 return confirmed;
3721 }
3722
3723 static gboolean
source_selector_store_value(GKeyFile * key_file,const gchar * group_key,const gchar * const * value,gsize value_length)3724 source_selector_store_value (GKeyFile *key_file,
3725 const gchar *group_key,
3726 const gchar * const *value,
3727 gsize value_length)
3728 {
3729 gchar **stored;
3730 gsize length = 0, ii;
3731 gboolean changed = FALSE;
3732
3733 g_return_val_if_fail (key_file != NULL, FALSE);
3734 g_return_val_if_fail (group_key != NULL, FALSE);
3735
3736 stored = g_key_file_get_string_list (key_file, E_SOURCE_SELECTOR_GROUPS_SETUP_NAME, group_key, &length, NULL);
3737 if (stored) {
3738 changed = value_length != length;
3739 if (!changed) {
3740 for (ii = 0; ii < length && !changed; ii++) {
3741 changed = g_strcmp0 (value[ii], stored[ii]) != 0;
3742 }
3743 }
3744
3745 g_strfreev (stored);
3746 } else {
3747 changed = value != NULL;
3748 }
3749
3750 if (changed) {
3751 if (value)
3752 g_key_file_set_string_list (key_file, E_SOURCE_SELECTOR_GROUPS_SETUP_NAME, group_key, value, value_length);
3753 else
3754 changed = g_key_file_remove_key (key_file, E_SOURCE_SELECTOR_GROUPS_SETUP_NAME, group_key, NULL);
3755 }
3756
3757 return changed;
3758 }
3759
3760 /**
3761 * e_source_selector_save_groups_setup:
3762 * @selector: an #ESourceSelector
3763 * @key_file: a #GKeyFile to store the sgroups setup to
3764 *
3765 * Stores current setup of the groups in the @key_file.
3766 *
3767 * Use e_source_selector_load_groups_setup() to pass the settings
3768 * back to the @selector.
3769 *
3770 * Returns: Whether the saved values are different, aka whether it's
3771 * required to store the changes.
3772 *
3773 * Since: 3.20
3774 **/
3775 gboolean
e_source_selector_save_groups_setup(ESourceSelector * selector,GKeyFile * key_file)3776 e_source_selector_save_groups_setup (ESourceSelector *selector,
3777 GKeyFile *key_file)
3778 {
3779 GPtrArray *value;
3780 const gchar *extension_name;
3781 gchar *group_key;
3782 gboolean changed;
3783
3784 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
3785 g_return_val_if_fail (key_file != NULL, FALSE);
3786
3787 extension_name = e_source_selector_get_extension_name (selector);
3788 g_return_val_if_fail (extension_name != NULL, FALSE);
3789
3790 group_key = g_strconcat (extension_name, "-hidden-groups", NULL);
3791
3792 if (g_hash_table_size (selector->priv->hidden_groups) > 0) {
3793 GHashTableIter iter;
3794 gpointer key, unused;
3795
3796 value = g_ptr_array_sized_new (g_hash_table_size (selector->priv->hidden_groups));
3797
3798 g_hash_table_iter_init (&iter, selector->priv->hidden_groups);
3799 while (g_hash_table_iter_next (&iter, &key, &unused)) {
3800 if (key)
3801 g_ptr_array_add (value, key);
3802 }
3803
3804 /* expects NULL-terminated array of strings, thus terminate it */
3805 g_ptr_array_add (value, NULL);
3806
3807 changed = source_selector_store_value (key_file, group_key, (const gchar * const *) value->pdata, value->len - 1);
3808
3809 g_ptr_array_unref (value);
3810 } else {
3811 changed = source_selector_store_value (key_file, group_key, NULL, 0);
3812 }
3813
3814 g_free (group_key);
3815 group_key = g_strconcat (extension_name, "-groups-order", NULL);
3816
3817 if (selector->priv->groups_order) {
3818 GSList *link;
3819
3820 value = g_ptr_array_sized_new (g_slist_length (selector->priv->groups_order));
3821
3822 for (link = selector->priv->groups_order; link; link = g_slist_next (link)) {
3823 if (link->data)
3824 g_ptr_array_add (value, link->data);
3825 }
3826
3827 /* expects NULL-terminated array of strings, thus terminate it */
3828 g_ptr_array_add (value, NULL);
3829
3830 changed = source_selector_store_value (key_file, group_key, (const gchar * const *) value->pdata, value->len - 1) || changed;
3831
3832 g_ptr_array_unref (value);
3833 } else {
3834 changed = source_selector_store_value (key_file, group_key, NULL, 0) || changed;
3835 }
3836
3837 g_free (group_key);
3838
3839 return changed;
3840 }
3841
3842 /**
3843 * e_source_selector_load_groups_setup:
3844 * @selector: an #ESourceSelector
3845 * @key_file: a #GKeyFile to load the groups setup from
3846 *
3847 * Loads setup of the groups from the @key_file.
3848 *
3849 * Use e_source_selector_save_groups_setup() to store
3850 * the settings of the @selector.
3851 *
3852 * Since: 3.20
3853 **/
3854 void
e_source_selector_load_groups_setup(ESourceSelector * selector,GKeyFile * key_file)3855 e_source_selector_load_groups_setup (ESourceSelector *selector,
3856 GKeyFile *key_file)
3857 {
3858 const gchar *extension_name;
3859 gchar **stored;
3860 gchar *group_key;
3861 gsize ii;
3862
3863 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
3864
3865 extension_name = e_source_selector_get_extension_name (selector);
3866 g_return_if_fail (extension_name != NULL);
3867
3868 g_hash_table_remove_all (selector->priv->hidden_groups);
3869 g_slist_free_full (selector->priv->groups_order, g_free);
3870 selector->priv->groups_order = NULL;
3871
3872 group_key = g_strconcat (extension_name, "-hidden-groups", NULL);
3873
3874 stored = g_key_file_get_string_list (key_file, E_SOURCE_SELECTOR_GROUPS_SETUP_NAME, group_key, NULL, NULL);
3875 if (stored) {
3876 for (ii = 0; stored[ii]; ii++) {
3877 g_hash_table_insert (selector->priv->hidden_groups, g_strdup (stored[ii]), GINT_TO_POINTER (1));
3878 }
3879
3880 g_strfreev (stored);
3881 }
3882
3883 g_free (group_key);
3884 group_key = g_strconcat (extension_name, "-groups-order", NULL);
3885
3886 stored = g_key_file_get_string_list (key_file, E_SOURCE_SELECTOR_GROUPS_SETUP_NAME, group_key, NULL, NULL);
3887 if (stored) {
3888 for (ii = 0; stored[ii]; ii++) {
3889 selector->priv->groups_order = g_slist_prepend (selector->priv->groups_order, g_strdup (stored[ii]));
3890 }
3891
3892 g_strfreev (stored);
3893 }
3894
3895 g_free (group_key);
3896
3897 selector->priv->groups_order = g_slist_reverse (selector->priv->groups_order);
3898
3899 source_selector_build_model (selector);
3900 }
3901