1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
4 * Copyright (C) 2003 Ximian, Inc.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
21 * Author: Ettore Perazzoli <ettore@ximian.com>
29 #include <gtk/gtkmenu.h>
30 #include <gtk/gtktreeselection.h>
31 #include <gtk/gtktreestore.h>
32 #include <gtk/gtkcellrenderertoggle.h>
33 #include <gtk/gtkcellrenderertext.h>
34 #include <gtk/gtkcellrendererpixbuf.h>
36 #include "e-data-server-ui-marshal.h"
37 #include "e-source-selector.h"
39 struct _ESourceSelectorPrivate {
42 GtkTreeStore *tree_store;
44 GHashTable *selected_sources;
45 GtkTreeRowReference *saved_primary_selection;
46 ESourceGroup *primary_source_group;
48 int rebuild_model_idle_id;
50 gboolean toggled_last;
51 gboolean checkboxes_shown;
56 ESourceSelector *selector;
58 GHashTable *remaining_uids;
61 gboolean selection_changed;
62 } ESourceSelectorRebuildData;
66 PRIMARY_SELECTION_CHANGED,
70 static unsigned int signals[NUM_SIGNALS] = { 0 };
72 static gboolean selector_popup_menu (GtkWidget *widget);
74 G_DEFINE_TYPE (ESourceSelector, e_source_selector, GTK_TYPE_TREE_VIEW)
76 /* Selection management. */
79 create_selected_sources_hash (void)
81 return g_hash_table_new_full (g_direct_hash, g_direct_equal,
82 (GDestroyNotify) g_object_unref, NULL);
85 static ESourceSelectorRebuildData *
86 create_rebuild_data (ESourceSelector *selector)
88 ESourceSelectorRebuildData *rebuild_data;
90 rebuild_data = g_new0 (ESourceSelectorRebuildData, 1);
92 rebuild_data->selector = selector;
93 rebuild_data->remaining_uids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
94 (GDestroyNotify) gtk_tree_row_reference_free);
95 rebuild_data->deleted_uids = NULL;
102 free_rebuild_data (ESourceSelectorRebuildData *rebuild_data)
106 g_hash_table_destroy (rebuild_data->remaining_uids);
107 for (p = rebuild_data->deleted_uids; p; p = p->next)
108 gtk_tree_row_reference_free (p->data);
109 g_slist_free (rebuild_data->deleted_uids);
111 g_free (rebuild_data);
115 clear_saved_primary_selection (ESourceSelector *selector)
117 if (selector->priv->saved_primary_selection != NULL) {
118 gtk_tree_row_reference_free (selector->priv->saved_primary_selection);
119 selector->priv->saved_primary_selection = NULL;
124 source_is_selected (ESourceSelector *selector,
127 if (g_hash_table_lookup (selector->priv->selected_sources, source) == NULL)
134 select_source (ESourceSelector *selector,
137 if (g_hash_table_lookup (selector->priv->selected_sources, source) != NULL)
140 g_hash_table_insert (selector->priv->selected_sources, source, source);
141 g_object_ref (source);
145 unselect_source (ESourceSelector *selector,
148 if (g_hash_table_lookup (selector->priv->selected_sources, source) == NULL)
151 /* (This will unref the source.) */
152 g_hash_table_remove (selector->priv->selected_sources, source);
156 find_source_iter (ESourceSelector *selector, ESource *source, GtkTreeIter *parent_iter, GtkTreeIter *source_iter)
158 GtkTreeModel *model = GTK_TREE_MODEL (selector->priv->tree_store);
160 if (gtk_tree_model_get_iter_first (model, parent_iter)) {
162 if (gtk_tree_model_iter_children (model, source_iter, parent_iter)) {
166 gtk_tree_model_get (model, source_iter, 0, &data, -1);
167 g_assert (E_IS_SOURCE (data));
169 if (E_SOURCE (data) == source) {
170 g_object_unref (data);
175 g_object_unref (data);
176 } while (gtk_tree_model_iter_next (model, source_iter));
178 } while (gtk_tree_model_iter_next (model, parent_iter));
184 /* Setting up the model. */
186 rebuild_existing_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
188 ESourceSelectorRebuildData *rebuild_data = data;
192 gtk_tree_model_get (model, iter, 0, &node, -1);
194 if (E_IS_SOURCE_GROUP (node)) {
195 uid = e_source_group_peek_uid (E_SOURCE_GROUP (node));
197 if (e_source_list_peek_group_by_uid (rebuild_data->selector->priv->list, uid)) {
198 g_hash_table_insert (rebuild_data->remaining_uids, g_strdup (uid),
199 gtk_tree_row_reference_new (model, path));
201 rebuild_data->deleted_uids = g_slist_prepend (rebuild_data->deleted_uids,
202 gtk_tree_row_reference_new (model, path));
205 uid = e_source_peek_uid (E_SOURCE (node));
206 if (e_source_list_peek_source_by_uid (rebuild_data->selector->priv->list, uid)) {
207 g_hash_table_insert (rebuild_data->remaining_uids, g_strdup (uid),
208 gtk_tree_row_reference_new (model, path));
210 rebuild_data->deleted_uids = g_slist_prepend (rebuild_data->deleted_uids,
211 gtk_tree_row_reference_new (model, path));
213 if (g_hash_table_remove (rebuild_data->selector->priv->selected_sources, node))
214 rebuild_data->selection_changed = TRUE;
218 g_object_unref (node);
224 rebuild_model (ESourceSelector *selector)
226 ESourceSelectorRebuildData *rebuild_data;
227 GtkTreeStore *tree_store;
230 gboolean set_primary;
232 tree_store = selector->priv->tree_store;
234 rebuild_data = create_rebuild_data (selector);
235 set_primary = e_source_selector_peek_primary_selection (selector) != NULL;
237 /* Remove any delete sources or groups */
238 gtk_tree_model_foreach (GTK_TREE_MODEL (tree_store), rebuild_existing_cb, rebuild_data);
239 for (p = rebuild_data->deleted_uids; p; p = p->next) {
240 GtkTreeRowReference *row_ref = p->data;
243 path = gtk_tree_row_reference_get_path (row_ref);
244 gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_store), &iter, path);
245 gtk_tree_store_remove (tree_store, &iter);
247 gtk_tree_path_free (path);
250 /* Add new sources/groups or call row_changed in case they were renamed */
251 groups = e_source_list_peek_groups (selector->priv->list);
252 for (p = groups; p != NULL; p = p->next) {
253 ESourceGroup *group = E_SOURCE_GROUP (p->data);
255 GtkTreeRowReference *row_ref;
257 row_ref = g_hash_table_lookup (rebuild_data->remaining_uids, e_source_group_peek_uid (group));
259 gtk_tree_store_append (GTK_TREE_STORE (tree_store), &iter, NULL);
260 gtk_tree_store_set (GTK_TREE_STORE (tree_store), &iter, 0, group, -1);
264 path = gtk_tree_row_reference_get_path (row_ref);
265 gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_store), &iter, path);
267 gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_store), path, &iter);
269 gtk_tree_path_free (path);
272 sources = e_source_group_peek_sources (group);
273 for (q = sources; q != NULL; q = q->next) {
274 ESource *source = E_SOURCE (q->data);
275 GtkTreeIter child_iter;
277 row_ref = g_hash_table_lookup (rebuild_data->remaining_uids, e_source_peek_uid (source));
279 if (selector->priv->select_new) {
280 select_source (selector, source);
281 rebuild_data->selection_changed = TRUE;
283 gtk_tree_store_append (GTK_TREE_STORE (tree_store), &child_iter, &iter);
284 gtk_tree_store_set (GTK_TREE_STORE (tree_store), &child_iter, 0, source, -1);
289 path = gtk_tree_row_reference_get_path (row_ref);
290 gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_store), &child_iter, path);
292 gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_store), path, &child_iter);
294 gtk_tree_path_free (path);
299 if (rebuild_data->selection_changed)
300 g_signal_emit (selector, signals[SELECTION_CHANGED], 0);
302 if (set_primary && !e_source_selector_peek_primary_selection (selector))
303 e_source_selector_set_primary_selection (selector, e_source_list_peek_source_any (selector->priv->list));
305 free_rebuild_data (rebuild_data);
309 on_idle_rebuild_model_callback (ESourceSelector *selector)
311 rebuild_model (selector);
312 selector->priv->rebuild_model_idle_id = 0;
318 list_changed_callback (ESourceList *list,
319 ESourceSelector *selector)
321 ESourceSelectorPrivate *priv = selector->priv;
323 if (priv->rebuild_model_idle_id == 0)
324 priv->rebuild_model_idle_id = g_idle_add ((GSourceFunc) on_idle_rebuild_model_callback,
329 setup_model (ESourceSelector *selector)
331 rebuild_model (selector);
333 g_signal_connect_object (selector->priv->list, "changed", G_CALLBACK (list_changed_callback), G_OBJECT (selector), 0);
337 /* Data functions for rendering the model. */
340 toggle_cell_data_func (GtkTreeViewColumn *column,
341 GtkCellRenderer *renderer,
344 ESourceSelector *selector)
348 gtk_tree_model_get (model, iter, 0, &data, -1);
350 if (E_IS_SOURCE_GROUP (data)) {
351 g_object_set (renderer, "visible", FALSE, NULL);
353 g_assert (E_IS_SOURCE (data));
355 g_object_set (renderer, "visible", selector->priv->checkboxes_shown, NULL);
356 if (source_is_selected (selector, E_SOURCE (data)))
357 g_object_set (renderer, "active", TRUE, NULL);
359 g_object_set (renderer, "active", FALSE, NULL);
362 g_object_unref (data);
366 text_cell_data_func (GtkTreeViewColumn *column,
367 GtkCellRenderer *renderer,
370 ESourceSelector *selector)
374 gtk_tree_model_get (model, iter, 0, &data, -1);
376 if (E_IS_SOURCE_GROUP (data)) {
377 g_object_set (renderer,
378 "text", e_source_group_peek_name (E_SOURCE_GROUP (data)),
379 "weight", PANGO_WEIGHT_BOLD,
380 "foreground_set", FALSE,
385 g_assert (E_IS_SOURCE (data));
386 source = E_SOURCE (data);
388 g_object_set (renderer,
389 "text", e_source_peek_name (source),
390 "weight", PANGO_WEIGHT_NORMAL,
391 "foreground_set", FALSE,
395 g_object_unref (data);
399 pixbuf_cell_data_func (GtkTreeViewColumn *column,
400 GtkCellRenderer *renderer,
403 ESourceSelector *selector)
407 gtk_tree_model_get (model, iter, 0, &data, -1);
409 if (E_IS_SOURCE_GROUP (data)) {
410 g_object_set (renderer,
415 GdkPixbuf *pixbuf = NULL;
416 const gchar *color_spec;
419 g_assert (E_IS_SOURCE (data));
420 source = E_SOURCE (data);
422 color_spec = e_source_peek_color_spec (source);
423 if (color_spec != NULL && gdk_color_parse (color_spec, &color)) {
426 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 16, 16);
428 rgba = (((color.red & 0xff00) << 8) |
429 ((color.green & 0xff00)) |
430 ((color.blue & 0xff00) >> 8)) << 8;
432 gdk_pixbuf_fill (pixbuf, rgba);
435 g_object_set (renderer,
436 "visible", pixbuf != NULL,
441 g_object_unref (pixbuf);
444 g_object_unref (data);
447 /* Custom selection function to make groups non selectable. */
449 selection_func (GtkTreeSelection *selection,
452 gboolean path_currently_selected,
453 ESourceSelector *selector)
458 if (selector->priv->toggled_last) {
459 selector->priv->toggled_last = FALSE;
464 if (path_currently_selected)
467 if (! gtk_tree_model_get_iter (model, &iter, path))
471 gtk_tree_model_get (model, &iter, 0, &data, -1);
472 if (E_IS_SOURCE_GROUP (data)) {
473 g_object_unref (data);
478 clear_saved_primary_selection (selector);
479 g_object_unref (data);
488 cell_toggled_callback (GtkCellRendererToggle *renderer,
489 const char *path_string,
490 ESourceSelector *selector)
492 GtkTreeModel *model = GTK_TREE_MODEL (selector->priv->tree_store);
493 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
498 if (! gtk_tree_model_get_iter (model, &iter, path)) {
499 gtk_tree_path_free (path);
503 gtk_tree_model_get (model, &iter, 0, &data, -1);
504 if (!E_IS_SOURCE_GROUP (data)) {
505 source = E_SOURCE (data);
507 if (source_is_selected (selector, source))
508 unselect_source (selector, source);
510 select_source (selector, source);
512 selector->priv->toggled_last = TRUE;
514 gtk_tree_model_row_changed (model, path, &iter);
515 g_signal_emit (selector, signals[SELECTION_CHANGED], 0);
518 gtk_tree_path_free (path);
520 g_object_unref (data);
524 selection_changed_callback (GtkTreeSelection *selection,
525 ESourceSelector *selector)
527 g_signal_emit (selector, signals[PRIMARY_SELECTION_CHANGED], 0);
531 test_collapse_row_callback (GtkTreeView *treeview, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
533 ESourceSelector *selector = data;
534 ESourceSelectorPrivate *priv;
535 GtkTreeIter child_iter;
537 priv = selector->priv;
539 /* Clear this because something else has been clicked on now */
540 priv->toggled_last = FALSE;
542 if (priv->saved_primary_selection)
545 if (!gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (selector)), NULL, &child_iter))
548 if (gtk_tree_store_is_ancestor (priv->tree_store, iter, &child_iter)) {
549 GtkTreePath *child_path;
551 child_path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->tree_store), &child_iter);
552 priv->saved_primary_selection = gtk_tree_row_reference_new (GTK_TREE_MODEL (priv->tree_store), child_path);
553 gtk_tree_path_free (child_path);
560 row_expanded_callback (GtkTreeView *treeview, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
562 ESourceSelector *selector = data;
563 ESourceSelectorPrivate *priv;
564 GtkTreePath *child_path;
565 GtkTreeIter child_iter;
567 priv = selector->priv;
569 if (!priv->saved_primary_selection)
572 child_path = gtk_tree_row_reference_get_path (priv->saved_primary_selection);
573 gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->tree_store), &child_iter, child_path);
575 if (gtk_tree_store_is_ancestor (priv->tree_store, iter, &child_iter)) {
576 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (selector));
578 gtk_tree_selection_select_iter (selection, &child_iter);
579 clear_saved_primary_selection (selector);
582 gtk_tree_path_free (child_path);
588 selector_popup_menu (GtkWidget *widget)
590 ESourceSelector *selector = E_SOURCE_SELECTOR (widget);
592 gboolean res = FALSE;
594 source = e_source_selector_peek_primary_selection (selector);
595 g_signal_emit (selector, signals[POPUP_EVENT], 0, source, NULL, &res);
600 selector_button_press_event (GtkWidget *widget, GdkEventButton *event, ESourceSelector *selector)
602 ESourceSelectorPrivate *priv = selector->priv;
604 ESource *source = NULL;
605 gboolean res = FALSE;
607 priv->toggled_last = FALSE;
609 /* only process right-clicks */
610 if (event->button != 3 || event->type != GDK_BUTTON_PRESS)
613 /* Get the source/group */
614 if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), event->x, event->y, &path, NULL, NULL, NULL)) {
618 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->tree_store), &iter, path)) {
619 gtk_tree_model_get (GTK_TREE_MODEL (priv->tree_store), &iter, 0, &data, -1);
621 /* Do not emit popup since we will not be able to get the source */
622 if (E_IS_SOURCE_GROUP (data)) {
623 /* do i need to ref it here */
626 group = E_SOURCE_GROUP (data);g_object_ref (group);
627 priv->primary_source_group = group;
628 /* data shuld be unreffed after creating the
633 source = E_SOURCE (data);
638 e_source_selector_set_primary_selection (selector, source);
640 g_signal_emit(selector, signals[POPUP_EVENT], 0, source, event, &res);
643 g_object_unref (source);
648 /* GObject methods. */
651 e_source_selector_dispose (GObject *object)
653 ESourceSelectorPrivate *priv = E_SOURCE_SELECTOR (object)->priv;
655 if (priv->selected_sources != NULL) {
656 g_hash_table_destroy (priv->selected_sources);
657 priv->selected_sources = NULL;
660 if (priv->rebuild_model_idle_id != 0) {
661 g_source_remove (priv->rebuild_model_idle_id);
662 priv->rebuild_model_idle_id = 0;
665 if (priv->list != NULL) {
666 g_object_unref (priv->list);
670 if (priv->tree_store != NULL) {
671 g_object_unref (priv->tree_store);
672 priv->tree_store = NULL;
675 clear_saved_primary_selection (E_SOURCE_SELECTOR (object));
677 (* G_OBJECT_CLASS (e_source_selector_parent_class)->dispose) (object);
681 e_source_selector_finalize (GObject *object)
683 ESourceSelectorPrivate *priv = E_SOURCE_SELECTOR (object)->priv;
687 (* G_OBJECT_CLASS (e_source_selector_parent_class)->finalize) (object);
691 /* Initialization. */
693 ess_bool_accumulator(GSignalInvocationHint *ihint, GValue *out, const GValue *in, void *data)
695 gboolean val = g_value_get_boolean(in);
697 g_value_set_boolean(out, val);
703 e_source_selector_class_init (ESourceSelectorClass *class)
705 GObjectClass *object_class = G_OBJECT_CLASS (class);
706 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
708 object_class->dispose = e_source_selector_dispose;
709 object_class->finalize = e_source_selector_finalize;
710 widget_class->popup_menu = selector_popup_menu;
713 signals[SELECTION_CHANGED] =
714 g_signal_new ("selection_changed",
715 G_OBJECT_CLASS_TYPE (object_class),
717 G_STRUCT_OFFSET (ESourceSelectorClass, selection_changed),
719 g_cclosure_marshal_VOID__VOID,
722 signals[PRIMARY_SELECTION_CHANGED] =
723 g_signal_new ("primary_selection_changed",
724 G_OBJECT_CLASS_TYPE (object_class),
726 G_STRUCT_OFFSET (ESourceSelectorClass, primary_selection_changed),
728 g_cclosure_marshal_VOID__VOID,
730 signals[POPUP_EVENT] =
731 g_signal_new ("popup_event",
732 G_OBJECT_CLASS_TYPE (object_class),
734 G_STRUCT_OFFSET (ESourceSelectorClass, popup_event),
735 ess_bool_accumulator, NULL,
736 e_data_server_ui_marshal_BOOLEAN__OBJECT_BOXED,
737 G_TYPE_BOOLEAN, 2, G_TYPE_OBJECT,
738 GDK_TYPE_EVENT|G_SIGNAL_TYPE_STATIC_SCOPE);
742 group_search_function (GtkTreeModel *model,
749 const char *name = NULL;
750 gboolean status = TRUE;
752 gtk_tree_model_get (model, iter, 0, &data, -1);
754 if (E_IS_SOURCE_GROUP (data))
755 name = e_source_group_peek_name (E_SOURCE_GROUP (data));
757 g_assert (E_IS_SOURCE (data));
759 name = e_source_peek_name (E_SOURCE (data));
763 status = g_ascii_strncasecmp (name, key, strlen(key)) != 0;
766 g_object_unref (data);
772 e_source_selector_init (ESourceSelector *selector)
774 ESourceSelectorPrivate *priv;
775 GtkTreeViewColumn *column;
776 GtkCellRenderer *cell_renderer;
777 GtkTreeSelection *selection;
779 priv = g_new0 (ESourceSelectorPrivate, 1);
780 selector->priv = priv;
782 gtk_tree_view_set_search_column (GTK_TREE_VIEW (selector), 0);
783 gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (selector), group_search_function, NULL, NULL);
784 gtk_tree_view_set_enable_search (GTK_TREE_VIEW (selector), TRUE);
786 g_signal_connect (G_OBJECT (selector), "button_press_event",
787 G_CALLBACK (selector_button_press_event), selector);
789 priv->toggled_last = FALSE;
790 priv->checkboxes_shown = TRUE;
791 priv->select_new = FALSE;
793 priv->selected_sources = create_selected_sources_hash ();
795 priv->tree_store = gtk_tree_store_new (1, G_TYPE_OBJECT);
796 gtk_tree_view_set_model (GTK_TREE_VIEW (selector), GTK_TREE_MODEL (priv->tree_store));
798 column = gtk_tree_view_column_new ();
799 gtk_tree_view_append_column (GTK_TREE_VIEW (selector), column);
801 cell_renderer = gtk_cell_renderer_pixbuf_new ();
802 g_object_set (G_OBJECT (cell_renderer), "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
803 gtk_tree_view_column_pack_start (column, cell_renderer, FALSE);
804 gtk_tree_view_column_set_cell_data_func (column, cell_renderer, (GtkTreeCellDataFunc) pixbuf_cell_data_func, selector, NULL);
805 cell_renderer = gtk_cell_renderer_toggle_new ();
806 gtk_tree_view_column_pack_start (column, cell_renderer, FALSE);
807 gtk_tree_view_column_set_cell_data_func (column, cell_renderer, (GtkTreeCellDataFunc) toggle_cell_data_func, selector, NULL);
808 g_signal_connect (cell_renderer, "toggled", G_CALLBACK (cell_toggled_callback), selector);
810 cell_renderer = gtk_cell_renderer_text_new ();
811 g_object_set (G_OBJECT (cell_renderer), "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
812 g_object_set (G_OBJECT (cell_renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
813 gtk_tree_view_column_pack_start (column, cell_renderer, TRUE);
814 gtk_tree_view_column_set_cell_data_func (column, cell_renderer, (GtkTreeCellDataFunc) text_cell_data_func, selector, NULL);
816 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (selector));
817 gtk_tree_selection_set_select_function (selection, (GtkTreeSelectionFunc) selection_func, selector, NULL);
818 g_signal_connect_object (selection, "changed", G_CALLBACK (selection_changed_callback), G_OBJECT (selector), 0);
820 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (selector), FALSE);
822 g_signal_connect (G_OBJECT (selector), "test-collapse-row", G_CALLBACK (test_collapse_row_callback), selector);
823 g_signal_connect (G_OBJECT (selector), "row-expanded", G_CALLBACK (row_expanded_callback), selector);
830 * e_source_selector_new:
831 * @list: A source list.
833 * Create a new view for @list. The view will update automatically when @list
836 * Return value: The newly created widget.
839 e_source_selector_new (ESourceList *list)
841 ESourceSelector *selector;
843 g_return_val_if_fail (E_IS_SOURCE_LIST (list), NULL);
845 selector = g_object_new (e_source_selector_get_type (), NULL);
847 selector->priv->list = list;
850 setup_model (selector);
852 gtk_tree_view_expand_all (GTK_TREE_VIEW (selector));
854 return GTK_WIDGET (selector);
859 * e_source_selector_get_selection:
860 * @selector: an #ESourceSelector
862 * Get the list of selected sources, i.e. those that were enabled through the
863 * corresponding checkboxes in the tree.
865 * Return value: A list of the ESources currently selected. The sources will
866 * be in the same order as they appear on the screen, and the list should be
867 * freed using e_source_selector_free_selection().
870 e_source_selector_get_selection (ESourceSelector *selector)
872 GSList *selection_list;
876 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
878 selection_list = NULL;
880 groups = e_source_list_peek_groups (selector->priv->list);
881 for (p = groups; p != NULL; p = p->next) {
882 ESourceGroup *group = E_SOURCE_GROUP (p->data);
886 sources = e_source_group_peek_sources (group);
887 for (q = sources; q != NULL; q = q->next) {
888 ESource *source = E_SOURCE (q->data);
890 if (source_is_selected (selector, source)) {
891 selection_list = g_slist_prepend (selection_list, source);
892 g_object_ref (source);
897 return g_slist_reverse (selection_list);
901 * e_source_selector_get_primary_source_group:
902 * @selector: an #ESourceSelector
904 * Gets the primary source group associated with the selector.
906 * Return value: primary_source_group if selector is valid, NULL otherwise.
909 e_source_selector_get_primary_source_group (ESourceSelector *selector)
911 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
913 return selector->priv->primary_source_group;
918 * e_source_list_free_selection:
919 * @list: A selection list returned by e_source_selector_get_selection().
921 * Free the selection list.
924 e_source_selector_free_selection (GSList *list)
926 g_slist_foreach (list, (GFunc) g_object_unref, NULL);
932 * e_source_selector_show_selection:
933 * @selector: An ESourceSelector widget
935 * Specify whether the checkboxes in the ESourceSelector should be shown or
939 e_source_selector_show_selection (ESourceSelector *selector,
942 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
945 if (show == selector->priv->checkboxes_shown)
948 selector->priv->checkboxes_shown = show;
950 gtk_tree_model_foreach (GTK_TREE_MODEL (selector->priv->tree_store),
951 (GtkTreeModelForeachFunc) gtk_tree_model_row_changed,
956 * e_source_selector_selection_shown:
957 * @selector: an #ESourceSelector
959 * Check whether the checkboxes in the ESourceSelector are being shown or not.
961 * Return value: %TRUE if the checkboxes are shown, %FALSE otherwise.
964 e_source_selector_selection_shown (ESourceSelector *selector)
966 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
968 return selector->priv->checkboxes_shown;
972 * e_source_selector_set_select_new:
973 * @selector: An #ESourceSelector widget
976 * Set whether or not to select new sources added to @selector.
979 e_source_selector_set_select_new (ESourceSelector *selector, gboolean state)
981 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
983 selector->priv->select_new = state;
987 * e_source_selector_select_source:
988 * @selector: An #ESourceSelector widget
989 * @source: An #ESource.
991 * Select @source in @selector.
994 e_source_selector_select_source (ESourceSelector *selector,
997 GtkTreeIter parent_iter, source_iter;
999 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
1000 g_return_if_fail (E_IS_SOURCE (source));
1002 if (source_is_selected (selector, source))
1005 select_source (selector, source);
1007 if (find_source_iter (selector, source, &parent_iter, &source_iter)) {
1008 GtkTreeModel *model = GTK_TREE_MODEL (selector->priv->tree_store);
1011 path = gtk_tree_model_get_path (model, &source_iter);
1012 gtk_tree_model_row_changed (model, path, &source_iter);
1013 gtk_tree_path_free (path);
1015 g_signal_emit (selector, signals[SELECTION_CHANGED], 0);
1020 * e_source_selector_unselect_source:
1021 * @selector: An #ESourceSelector widget
1022 * @source: An #ESource.
1024 * Unselect @source in @selector.
1027 e_source_selector_unselect_source (ESourceSelector *selector,
1030 GtkTreeIter parent_iter, source_iter;
1032 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
1033 g_return_if_fail (E_IS_SOURCE (source));
1035 if (! source_is_selected (selector, source))
1038 unselect_source (selector, source);
1040 if (find_source_iter (selector, source, &parent_iter, &source_iter)) {
1041 GtkTreeModel *model = GTK_TREE_MODEL (selector->priv->tree_store);
1044 path = gtk_tree_model_get_path (model, &source_iter);
1045 gtk_tree_model_row_changed (model, path, &source_iter);
1046 gtk_tree_path_free (path);
1048 g_signal_emit (selector, signals[SELECTION_CHANGED], 0);
1053 * e_source_selector_source_is_selected:
1054 * @selector: An #ESourceSelector widget
1055 * @source: An #ESource.
1057 * Check whether @source is selected in @selector.
1059 * Return value: %TRUE if @source is currently selected, %FALSE otherwise.
1062 e_source_selector_source_is_selected (ESourceSelector *selector,
1065 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
1066 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
1068 return source_is_selected (selector, source);
1072 * e_source_selector_peek_primary_selection:
1073 * @selector: An #ESourceSelector widget
1075 * Get the primary selected source. The primary selection is the one that is
1076 * highlighted through the normal #GtkTreeView selection mechanism (as opposed
1077 * to the "normal" selection, which is the set of source whose checkboxes are
1080 * Return value: The selected source.
1083 e_source_selector_peek_primary_selection (ESourceSelector *selector)
1085 GtkTreeModel *model;
1087 gboolean have_iter = FALSE;
1090 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
1092 model = GTK_TREE_MODEL (selector->priv->tree_store);
1094 if (selector->priv->saved_primary_selection) {
1095 GtkTreePath *child_path;
1097 child_path = gtk_tree_row_reference_get_path (selector->priv->saved_primary_selection);
1099 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (selector->priv->tree_store), &iter, child_path))
1101 gtk_tree_path_free (child_path);
1105 if (!have_iter && ! gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (selector)), NULL, &iter))
1108 gtk_tree_model_get (model, &iter, 0, &data, -1);
1112 if (! E_IS_SOURCE (data)) {
1113 g_object_unref (data);
1118 g_object_unref (data);
1120 return E_SOURCE (data);
1124 * e_source_selector_set_primary_selection:
1125 * @selector: an #ESourceSelector widget
1126 * @source: an #ESource to select
1128 * Set the primary selected source.
1131 e_source_selector_set_primary_selection (ESourceSelector *selector, ESource *source)
1133 ESourceSelectorPrivate *priv;
1134 GtkTreeIter parent_iter, source_iter;
1136 g_return_if_fail (selector != NULL);
1137 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
1138 g_return_if_fail (source != NULL);
1139 g_return_if_fail (E_IS_SOURCE (source));
1141 priv = selector->priv;
1143 if (find_source_iter (selector, source, &parent_iter, &source_iter)) {
1144 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (selector));
1147 /* We block the signal because this all needs to be atomic */
1148 g_signal_handlers_block_matched (selection, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, selection_changed_callback, NULL);
1149 gtk_tree_selection_unselect_all (selection);
1150 g_signal_handlers_unblock_matched (selection, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, selection_changed_callback, NULL);
1152 clear_saved_primary_selection (selector);
1154 path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->tree_store), &parent_iter);
1156 if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (selector), path)) {
1157 gtk_tree_selection_select_iter (selection, &source_iter);
1159 GtkTreePath *child_path;
1161 child_path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->tree_store), &source_iter);
1162 priv->saved_primary_selection = gtk_tree_row_reference_new (GTK_TREE_MODEL (priv->tree_store), child_path);
1163 gtk_tree_path_free (child_path);
1165 /* We do this by hand because we aren't changing the tree selection */
1166 if (!source_is_selected (selector, source)) {
1167 select_source (selector, source);
1168 gtk_tree_model_row_changed (GTK_TREE_MODEL (priv->tree_store), path, &source_iter);
1169 g_signal_emit (selector, signals[SELECTION_CHANGED], 0);
1172 g_signal_emit (selector, signals[PRIMARY_SELECTION_CHANGED], 0);
1175 gtk_tree_path_free (path);
1177 g_warning (G_STRLOC ": Cannot find source %p (%s) in selector %p",
1178 source, e_source_peek_name (source), selector);