1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
4 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
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>
30 #include "e-cell-renderer-color.h"
31 #include "e-data-server-ui-marshal.h"
32 #include "e-source-selector.h"
34 #define E_SOURCE_SELECTOR_GET_PRIVATE(obj) \
35 (G_TYPE_INSTANCE_GET_PRIVATE \
36 ((obj), E_TYPE_SOURCE_SELECTOR, ESourceSelectorPrivate))
38 typedef struct _AsyncContext AsyncContext;
40 struct _ESourceSelectorPrivate {
41 ESourceRegistry *registry;
42 GHashTable *source_index;
43 gchar *extension_name;
45 GtkTreeRowReference *saved_primary_selection;
47 /* ESource -> GSource */
48 GHashTable *pending_writes;
49 GMainContext *main_context;
51 gboolean toggled_last;
54 gboolean show_toggles;
57 struct _AsyncContext {
58 ESourceSelector *selector;
65 PROP_PRIMARY_SELECTION,
73 PRIMARY_SELECTION_CHANGED,
90 static guint signals[NUM_SIGNALS];
92 G_DEFINE_TYPE (ESourceSelector, e_source_selector, GTK_TYPE_TREE_VIEW)
94 /* ESafeToggleRenderer does not emit 'toggled' signal
95 * on 'activate' when mouse is not over the toggle. */
97 typedef GtkCellRendererToggle ECellRendererSafeToggle;
98 typedef GtkCellRendererToggleClass ECellRendererSafeToggleClass;
100 /* Forward Declarations */
101 GType e_cell_renderer_safe_toggle_get_type (void);
104 ECellRendererSafeToggle,
105 e_cell_renderer_safe_toggle,
106 GTK_TYPE_CELL_RENDERER_TOGGLE)
109 safe_toggle_activate (GtkCellRenderer *cell,
113 const GdkRectangle *background_area,
114 const GdkRectangle *cell_area,
115 GtkCellRendererState flags)
117 gboolean point_in_cell_area = TRUE;
119 if (event->type == GDK_BUTTON_PRESS && cell_area != NULL) {
120 cairo_region_t *region;
122 region = cairo_region_create_rectangle (cell_area);
123 point_in_cell_area = cairo_region_contains_point (
124 region, event->button.x, event->button.y);
125 cairo_region_destroy (region);
128 if (!point_in_cell_area)
131 return GTK_CELL_RENDERER_CLASS (
132 e_cell_renderer_safe_toggle_parent_class)->activate (
133 cell, event, widget, path, background_area, cell_area, flags);
137 e_cell_renderer_safe_toggle_class_init (ECellRendererSafeToggleClass *class)
139 GtkCellRendererClass *cell_renderer_class;
141 cell_renderer_class = GTK_CELL_RENDERER_CLASS (class);
142 cell_renderer_class->activate = safe_toggle_activate;
146 e_cell_renderer_safe_toggle_init (ECellRendererSafeToggle *obj)
150 static GtkCellRenderer *
151 e_cell_renderer_safe_toggle_new (void)
153 return g_object_new (e_cell_renderer_safe_toggle_get_type (), NULL);
157 clear_saved_primary_selection (ESourceSelector *selector)
159 gtk_tree_row_reference_free (selector->priv->saved_primary_selection);
160 selector->priv->saved_primary_selection = NULL;
164 async_context_free (AsyncContext *async_context)
166 if (async_context->selector != NULL)
167 g_object_unref (async_context->selector);
169 if (async_context->source != NULL)
170 g_object_unref (async_context->source);
172 g_slice_free (AsyncContext, async_context);
176 pending_writes_destroy_source (GSource *source)
178 g_source_destroy (source);
179 g_source_unref (source);
183 source_selector_write_done_cb (GObject *source_object,
184 GAsyncResult *result,
188 ESourceSelector *selector;
189 GError *error = NULL;
191 source = E_SOURCE (source_object);
192 selector = E_SOURCE_SELECTOR (user_data);
194 e_source_write_finish (source, result, &error);
196 /* FIXME Display the error in the selector somehow? */
198 g_warning ("%s: %s", G_STRFUNC, error->message);
199 g_error_free (error);
202 g_object_unref (selector);
206 source_selector_write_idle_cb (gpointer user_data)
208 AsyncContext *async_context = user_data;
209 GHashTable *pending_writes;
211 /* XXX This operation is not cancellable. */
213 async_context->source, NULL,
214 source_selector_write_done_cb,
215 g_object_ref (async_context->selector));
217 pending_writes = async_context->selector->priv->pending_writes;
218 g_hash_table_remove (pending_writes, async_context->source);
224 source_selector_cancel_write (ESourceSelector *selector,
227 GHashTable *pending_writes;
229 /* Cancel any pending writes for this ESource so as not
230 * to overwrite whatever change we're being notified of. */
231 pending_writes = selector->priv->pending_writes;
232 g_hash_table_remove (pending_writes, source);
236 source_selector_update_row (ESourceSelector *selector,
239 GHashTable *source_index;
240 ESourceExtension *extension = NULL;
241 GtkTreeRowReference *reference;
245 const gchar *extension_name;
246 const gchar *display_name;
249 source_index = selector->priv->source_index;
250 reference = g_hash_table_lookup (source_index, source);
252 /* This function runs when ANY ESource in the registry changes.
253 * If the ESource is not in our tree model then return silently. */
254 if (reference == NULL)
257 /* If we do have a row reference, it should be valid. */
258 g_return_if_fail (gtk_tree_row_reference_valid (reference));
260 model = gtk_tree_row_reference_get_model (reference);
261 path = gtk_tree_row_reference_get_path (reference);
262 gtk_tree_model_get_iter (model, &iter, path);
263 gtk_tree_path_free (path);
265 display_name = e_source_get_display_name (source);
267 extension_name = e_source_selector_get_extension_name (selector);
268 selected = e_source_selector_source_is_selected (selector, source);
270 if (e_source_has_extension (source, extension_name))
271 extension = e_source_get_extension (source, extension_name);
273 if (extension != NULL) {
275 const gchar *color_spec = NULL;
276 gboolean show_color = FALSE;
277 gboolean show_toggle;
280 E_IS_SOURCE_SELECTABLE (extension) &&
281 e_source_selector_get_show_colors (selector);
284 color_spec = e_source_selectable_get_color (
285 E_SOURCE_SELECTABLE (extension));
287 if (color_spec != NULL && *color_spec != '\0')
288 show_color = gdk_color_parse (color_spec, &color);
290 show_toggle = e_source_selector_get_show_toggles (selector);
293 GTK_TREE_STORE (model), &iter,
294 COLUMN_NAME, display_name,
295 COLUMN_COLOR, show_color ? &color : NULL,
296 COLUMN_ACTIVE, selected,
297 COLUMN_SHOW_COLOR, show_color,
298 COLUMN_SHOW_TOGGLE, show_toggle,
299 COLUMN_WEIGHT, PANGO_WEIGHT_NORMAL,
300 COLUMN_SOURCE, source,
304 GTK_TREE_STORE (model), &iter,
305 COLUMN_NAME, display_name,
307 COLUMN_ACTIVE, FALSE,
308 COLUMN_SHOW_COLOR, FALSE,
309 COLUMN_SHOW_TOGGLE, FALSE,
310 COLUMN_WEIGHT, PANGO_WEIGHT_BOLD,
311 COLUMN_SOURCE, source,
317 source_selector_traverse (GNode *node,
318 ESourceSelector *selector)
321 GHashTable *source_index;
322 GtkTreeRowReference *reference = NULL;
327 /* Skip the root node. */
328 if (G_NODE_IS_ROOT (node))
331 source_index = selector->priv->source_index;
333 model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector));
335 if (node->parent != NULL && node->parent->data != NULL)
336 reference = g_hash_table_lookup (
337 source_index, node->parent->data);
339 if (gtk_tree_row_reference_valid (reference)) {
342 path = gtk_tree_row_reference_get_path (reference);
343 gtk_tree_model_get_iter (model, &parent, path);
344 gtk_tree_path_free (path);
346 gtk_tree_store_append (GTK_TREE_STORE (model), &iter, &parent);
348 gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
350 source = E_SOURCE (node->data);
352 path = gtk_tree_model_get_path (model, &iter);
353 reference = gtk_tree_row_reference_new (model, path);
354 g_hash_table_insert (source_index, g_object_ref (source), reference);
355 gtk_tree_path_free (path);
357 source_selector_update_row (selector, source);
363 source_selector_save_expanded (GtkTreeView *tree_view,
371 model = gtk_tree_view_get_model (tree_view);
372 gtk_tree_model_get_iter (model, &iter, path);
373 gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
374 g_queue_push_tail (queue, source);
378 source_selector_build_model (ESourceSelector *selector)
380 ESourceRegistry *registry;
381 GQueue queue = G_QUEUE_INIT;
382 GHashTable *source_index;
383 GtkTreeView *tree_view;
386 const gchar *extension_name;
389 tree_view = GTK_TREE_VIEW (selector);
391 registry = e_source_selector_get_registry (selector);
392 extension_name = e_source_selector_get_extension_name (selector);
394 /* Make sure we have what we need to build the model, since
395 * this can get called early in the initialization phase. */
396 if (registry == NULL || extension_name == NULL)
399 source_index = selector->priv->source_index;
400 selected = e_source_selector_ref_primary_selection (selector);
402 /* Save expanded sources to restore later. */
403 gtk_tree_view_map_expanded_rows (
404 tree_view, (GtkTreeViewMappingFunc)
405 source_selector_save_expanded, &queue);
407 model = gtk_tree_view_get_model (tree_view);
408 gtk_tree_store_clear (GTK_TREE_STORE (model));
410 g_hash_table_remove_all (source_index);
412 root = e_source_registry_build_display_tree (registry, extension_name);
415 root, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
416 (GNodeTraverseFunc) source_selector_traverse,
419 e_source_registry_free_display_tree (root);
421 /* Restore previously expanded sources. */
422 while (!g_queue_is_empty (&queue)) {
423 GtkTreeRowReference *reference;
426 source = g_queue_pop_head (&queue);
427 reference = g_hash_table_lookup (source_index, source);
429 if (gtk_tree_row_reference_valid (reference)) {
432 path = gtk_tree_row_reference_get_path (reference);
433 gtk_tree_view_expand_to_path (tree_view, path);
434 gtk_tree_path_free (path);
437 g_object_unref (source);
440 /* Restore the primary selection. */
441 if (selected != NULL) {
442 e_source_selector_set_primary_selection (selector, selected);
443 g_object_unref (selected);
446 /* Make sure we have a primary selection. If not, pick one. */
447 selected = e_source_selector_ref_primary_selection (selector);
448 if (selected == NULL) {
449 selected = e_source_registry_ref_default_for_extension_name (
450 registry, extension_name);
451 e_source_selector_set_primary_selection (selector, selected);
453 g_object_unref (selected);
457 source_selector_expand_to_source (ESourceSelector *selector,
460 GHashTable *source_index;
461 GtkTreeRowReference *reference;
464 source_index = selector->priv->source_index;
465 reference = g_hash_table_lookup (source_index, source);
467 /* If the ESource is not in our tree model then return silently. */
468 if (reference == NULL)
471 /* If we do have a row reference, it should be valid. */
472 g_return_if_fail (gtk_tree_row_reference_valid (reference));
474 /* Expand the tree view to the path containing the ESource */
475 path = gtk_tree_row_reference_get_path (reference);
476 gtk_tree_view_expand_to_path (GTK_TREE_VIEW (selector), path);
477 gtk_tree_path_free (path);
481 source_selector_source_added_cb (ESourceRegistry *registry,
483 ESourceSelector *selector)
485 source_selector_build_model (selector);
487 source_selector_expand_to_source (selector, source);
491 source_selector_source_changed_cb (ESourceRegistry *registry,
493 ESourceSelector *selector)
495 source_selector_cancel_write (selector, source);
497 source_selector_update_row (selector, source);
501 source_selector_source_removed_cb (ESourceRegistry *registry,
503 ESourceSelector *selector)
505 source_selector_build_model (selector);
509 source_selector_source_enabled_cb (ESourceRegistry *registry,
511 ESourceSelector *selector)
513 source_selector_build_model (selector);
515 source_selector_expand_to_source (selector, source);
519 source_selector_source_disabled_cb (ESourceRegistry *registry,
521 ESourceSelector *selector)
523 source_selector_build_model (selector);
527 same_source_name_exists (ESourceSelector *selector,
528 const gchar *display_name)
530 GHashTable *source_index;
534 source_index = selector->priv->source_index;
535 g_hash_table_iter_init (&iter, source_index);
537 while (g_hash_table_iter_next (&iter, &key, NULL)) {
538 ESource *source = E_SOURCE (key);
539 const gchar *source_name;
541 source_name = e_source_get_display_name (source);
542 if (g_strcmp0 (display_name, source_name) == 0)
550 selection_func (GtkTreeSelection *selection,
553 gboolean path_currently_selected,
554 ESourceSelector *selector)
558 const gchar *extension_name;
560 if (selector->priv->toggled_last) {
561 selector->priv->toggled_last = FALSE;
565 if (path_currently_selected)
568 if (!gtk_tree_model_get_iter (model, &iter, path))
571 extension_name = e_source_selector_get_extension_name (selector);
572 gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
574 if (!e_source_has_extension (source, extension_name)) {
575 g_object_unref (source);
579 clear_saved_primary_selection (selector);
581 g_object_unref (source);
587 text_cell_edited_cb (ESourceSelector *selector,
588 const gchar *path_string,
589 const gchar *new_name)
591 GtkTreeView *tree_view;
597 tree_view = GTK_TREE_VIEW (selector);
598 model = gtk_tree_view_get_model (tree_view);
599 path = gtk_tree_path_new_from_string (path_string);
601 gtk_tree_model_get_iter (model, &iter, path);
602 gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
603 gtk_tree_path_free (path);
605 if (new_name == NULL || *new_name == '\0')
608 if (same_source_name_exists (selector, new_name))
611 e_source_set_display_name (source, new_name);
613 e_source_selector_queue_write (selector, source);
617 cell_toggled_callback (GtkCellRendererToggle *renderer,
618 const gchar *path_string,
619 ESourceSelector *selector)
626 model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector));
627 path = gtk_tree_path_new_from_string (path_string);
629 if (!gtk_tree_model_get_iter (model, &iter, path)) {
630 gtk_tree_path_free (path);
634 gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
636 if (e_source_selector_source_is_selected (selector, source))
637 e_source_selector_unselect_source (selector, source);
639 e_source_selector_select_source (selector, source);
641 selector->priv->toggled_last = TRUE;
643 gtk_tree_path_free (path);
645 g_object_unref (source);
649 selection_changed_callback (GtkTreeSelection *selection,
650 ESourceSelector *selector)
652 g_signal_emit (selector, signals[PRIMARY_SELECTION_CHANGED], 0);
653 g_object_notify (G_OBJECT (selector), "primary-selection");
657 source_selector_set_extension_name (ESourceSelector *selector,
658 const gchar *extension_name)
660 g_return_if_fail (extension_name != NULL);
661 g_return_if_fail (selector->priv->extension_name == NULL);
663 selector->priv->extension_name = g_strdup (extension_name);
667 source_selector_set_registry (ESourceSelector *selector,
668 ESourceRegistry *registry)
670 g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
671 g_return_if_fail (selector->priv->registry == NULL);
673 selector->priv->registry = g_object_ref (registry);
677 source_selector_set_property (GObject *object,
682 switch (property_id) {
683 case PROP_EXTENSION_NAME:
684 source_selector_set_extension_name (
685 E_SOURCE_SELECTOR (object),
686 g_value_get_string (value));
689 case PROP_PRIMARY_SELECTION:
690 e_source_selector_set_primary_selection (
691 E_SOURCE_SELECTOR (object),
692 g_value_get_object (value));
696 source_selector_set_registry (
697 E_SOURCE_SELECTOR (object),
698 g_value_get_object (value));
701 case PROP_SHOW_COLORS:
702 e_source_selector_set_show_colors (
703 E_SOURCE_SELECTOR (object),
704 g_value_get_boolean (value));
707 case PROP_SHOW_TOGGLES:
708 e_source_selector_set_show_toggles (
709 E_SOURCE_SELECTOR (object),
710 g_value_get_boolean (value));
714 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
718 source_selector_get_property (GObject *object,
723 switch (property_id) {
724 case PROP_EXTENSION_NAME:
727 e_source_selector_get_extension_name (
728 E_SOURCE_SELECTOR (object)));
731 case PROP_PRIMARY_SELECTION:
732 g_value_take_object (
734 e_source_selector_ref_primary_selection (
735 E_SOURCE_SELECTOR (object)));
741 e_source_selector_get_registry (
742 E_SOURCE_SELECTOR (object)));
745 case PROP_SHOW_COLORS:
746 g_value_set_boolean (
748 e_source_selector_get_show_colors (
749 E_SOURCE_SELECTOR (object)));
752 case PROP_SHOW_TOGGLES:
753 g_value_set_boolean (
755 e_source_selector_get_show_toggles (
756 E_SOURCE_SELECTOR (object)));
760 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
764 source_selector_dispose (GObject *object)
766 ESourceSelectorPrivate *priv;
768 priv = E_SOURCE_SELECTOR_GET_PRIVATE (object);
770 if (priv->registry != NULL) {
771 g_signal_handlers_disconnect_matched (
774 0, 0, NULL, NULL, object);
775 g_object_unref (priv->registry);
776 priv->registry = NULL;
779 g_hash_table_remove_all (priv->source_index);
780 g_hash_table_remove_all (priv->pending_writes);
782 clear_saved_primary_selection (E_SOURCE_SELECTOR (object));
784 /* Chain up to parent's dispose() method. */
785 G_OBJECT_CLASS (e_source_selector_parent_class)->dispose (object);
789 source_selector_finalize (GObject *object)
791 ESourceSelectorPrivate *priv;
793 priv = E_SOURCE_SELECTOR_GET_PRIVATE (object);
795 g_hash_table_destroy (priv->source_index);
796 g_hash_table_destroy (priv->pending_writes);
798 g_free (priv->extension_name);
800 if (priv->main_context != NULL)
801 g_main_context_unref (priv->main_context);
803 /* Chain up to parent's finalize() method. */
804 G_OBJECT_CLASS (e_source_selector_parent_class)->finalize (object);
808 source_selector_constructed (GObject *object)
810 ESourceRegistry *registry;
811 ESourceSelector *selector;
813 selector = E_SOURCE_SELECTOR (object);
814 registry = e_source_selector_get_registry (selector);
817 registry, "source-added",
818 G_CALLBACK (source_selector_source_added_cb), selector);
821 registry, "source-changed",
822 G_CALLBACK (source_selector_source_changed_cb), selector);
825 registry, "source-removed",
826 G_CALLBACK (source_selector_source_removed_cb), selector);
829 registry, "source-enabled",
830 G_CALLBACK (source_selector_source_enabled_cb), selector);
833 registry, "source-disabled",
834 G_CALLBACK (source_selector_source_disabled_cb), selector);
836 source_selector_build_model (selector);
838 gtk_tree_view_expand_all (GTK_TREE_VIEW (selector));
842 source_selector_button_press_event (GtkWidget *widget,
843 GdkEventButton *event)
845 ESourceSelector *selector;
846 GtkWidgetClass *widget_class;
848 ESource *source = NULL;
850 gboolean right_click = FALSE;
851 gboolean triple_click = FALSE;
853 gboolean res = FALSE;
855 selector = E_SOURCE_SELECTOR (widget);
857 selector->priv->toggled_last = FALSE;
859 /* Triple-clicking a source selects it exclusively. */
861 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
863 else if (event->button == 1 && event->type == GDK_3BUTTON_PRESS)
868 row_exists = gtk_tree_view_get_path_at_pos (
869 GTK_TREE_VIEW (widget), event->x, event->y,
870 &path, NULL, NULL, NULL);
872 /* Get the source/group */
877 model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
879 gtk_tree_model_get_iter (model, &iter, path);
880 gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
886 primary = e_source_selector_ref_primary_selection (selector);
887 if (source != primary)
888 e_source_selector_set_primary_selection (selector, source);
890 g_object_unref (primary);
894 widget, signals[POPUP_EVENT], 0, source, event, &res);
897 e_source_selector_select_exclusive (selector, source);
901 g_object_unref (source);
907 /* Chain up to parent's button_press_event() method. */
908 widget_class = GTK_WIDGET_CLASS (e_source_selector_parent_class);
909 return widget_class->button_press_event (widget, event);
913 source_selector_drag_leave (GtkWidget *widget,
914 GdkDragContext *context,
917 GtkTreeView *tree_view;
918 GtkTreeViewDropPosition pos;
920 tree_view = GTK_TREE_VIEW (widget);
921 pos = GTK_TREE_VIEW_DROP_BEFORE;
923 gtk_tree_view_set_drag_dest_row (tree_view, NULL, pos);
927 source_selector_drag_motion (GtkWidget *widget,
928 GdkDragContext *context,
933 ESource *source = NULL;
934 GtkTreeView *tree_view;
936 GtkTreePath *path = NULL;
938 GtkTreeViewDropPosition pos;
939 GdkDragAction action = 0;
941 tree_view = GTK_TREE_VIEW (widget);
942 model = gtk_tree_view_get_model (tree_view);
944 if (!gtk_tree_view_get_dest_row_at_pos (tree_view, x, y, &path, NULL))
947 if (!gtk_tree_model_get_iter (model, &iter, path))
950 gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
952 if (!e_source_get_writable (source))
955 pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE;
956 gtk_tree_view_set_drag_dest_row (tree_view, path, pos);
958 if (gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE)
959 action = GDK_ACTION_MOVE;
961 action = gdk_drag_context_get_suggested_action (context);
965 gtk_tree_path_free (path);
968 g_object_unref (source);
970 gdk_drag_status (context, action, time_);
976 source_selector_drag_drop (GtkWidget *widget,
977 GdkDragContext *context,
983 ESourceSelector *selector;
984 GtkTreeView *tree_view;
988 const gchar *extension_name;
992 tree_view = GTK_TREE_VIEW (widget);
993 model = gtk_tree_view_get_model (tree_view);
995 if (!gtk_tree_view_get_path_at_pos (
996 tree_view, x, y, &path, NULL, NULL, NULL))
999 valid = gtk_tree_model_get_iter (model, &iter, path);
1000 gtk_tree_path_free (path);
1001 g_return_val_if_fail (valid, FALSE);
1003 gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
1005 selector = E_SOURCE_SELECTOR (widget);
1006 extension_name = e_source_selector_get_extension_name (selector);
1007 drop_zone = e_source_has_extension (source, extension_name);
1009 g_object_unref (source);
1015 source_selector_drag_data_received (GtkWidget *widget,
1016 GdkDragContext *context,
1019 GtkSelectionData *selection_data,
1023 ESource *source = NULL;
1024 GtkTreeView *tree_view;
1025 GtkTreeModel *model;
1026 GtkTreePath *path = NULL;
1028 GdkDragAction action;
1030 gboolean success = FALSE;
1032 tree_view = GTK_TREE_VIEW (widget);
1033 model = gtk_tree_view_get_model (tree_view);
1035 action = gdk_drag_context_get_selected_action (context);
1036 delete = (action == GDK_ACTION_MOVE);
1038 if (!gtk_tree_view_get_dest_row_at_pos (tree_view, x, y, &path, NULL))
1041 if (!gtk_tree_model_get_iter (model, &iter, path))
1044 gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
1046 if (!e_source_get_writable (source))
1050 widget, signals[DATA_DROPPED], 0, selection_data,
1051 source, gdk_drag_context_get_selected_action (context),
1056 gtk_tree_path_free (path);
1059 g_object_unref (source);
1061 gtk_drag_finish (context, success, delete, time_);
1065 source_selector_popup_menu (GtkWidget *widget)
1067 ESourceSelector *selector;
1069 gboolean res = FALSE;
1071 selector = E_SOURCE_SELECTOR (widget);
1072 source = e_source_selector_ref_primary_selection (selector);
1073 g_signal_emit (selector, signals[POPUP_EVENT], 0, source, NULL, &res);
1076 g_object_unref (source);
1082 source_selector_test_collapse_row (GtkTreeView *tree_view,
1086 ESourceSelectorPrivate *priv;
1087 GtkTreeSelection *selection;
1088 GtkTreeModel *model;
1089 GtkTreeIter child_iter;
1091 priv = E_SOURCE_SELECTOR_GET_PRIVATE (tree_view);
1093 /* Clear this because something else has been clicked on now */
1094 priv->toggled_last = FALSE;
1096 if (priv->saved_primary_selection)
1099 selection = gtk_tree_view_get_selection (tree_view);
1101 if (!gtk_tree_selection_get_selected (selection, &model, &child_iter))
1104 if (gtk_tree_store_is_ancestor (GTK_TREE_STORE (model), iter, &child_iter)) {
1105 GtkTreeRowReference *reference;
1106 GtkTreePath *child_path;
1108 child_path = gtk_tree_model_get_path (model, &child_iter);
1109 reference = gtk_tree_row_reference_new (model, child_path);
1110 priv->saved_primary_selection = reference;
1111 gtk_tree_path_free (child_path);
1118 source_selector_row_expanded (GtkTreeView *tree_view,
1122 ESourceSelectorPrivate *priv;
1123 GtkTreeModel *model;
1124 GtkTreePath *child_path;
1125 GtkTreeIter child_iter;
1127 priv = E_SOURCE_SELECTOR_GET_PRIVATE (tree_view);
1129 if (!priv->saved_primary_selection)
1132 model = gtk_tree_view_get_model (tree_view);
1134 child_path = gtk_tree_row_reference_get_path (
1135 priv->saved_primary_selection);
1136 gtk_tree_model_get_iter (model, &child_iter, child_path);
1138 if (gtk_tree_store_is_ancestor (GTK_TREE_STORE (model), iter, &child_iter)) {
1139 GtkTreeSelection *selection;
1141 selection = gtk_tree_view_get_selection (tree_view);
1142 gtk_tree_selection_select_iter (selection, &child_iter);
1144 clear_saved_primary_selection (E_SOURCE_SELECTOR (tree_view));
1147 gtk_tree_path_free (child_path);
1151 source_selector_get_source_selected (ESourceSelector *selector,
1154 ESourceSelectable *extension;
1155 const gchar *extension_name;
1156 gboolean selected = TRUE;
1158 extension_name = e_source_selector_get_extension_name (selector);
1160 if (!e_source_has_extension (source, extension_name))
1163 extension = e_source_get_extension (source, extension_name);
1165 if (E_IS_SOURCE_SELECTABLE (extension))
1166 selected = e_source_selectable_get_selected (extension);
1172 source_selector_set_source_selected (ESourceSelector *selector,
1176 ESourceSelectable *extension;
1177 const gchar *extension_name;
1179 extension_name = e_source_selector_get_extension_name (selector);
1181 if (!e_source_has_extension (source, extension_name))
1184 extension = e_source_get_extension (source, extension_name);
1186 if (!E_IS_SOURCE_SELECTABLE (extension))
1189 if (selected != e_source_selectable_get_selected (extension)) {
1190 e_source_selectable_set_selected (extension, selected);
1191 e_source_selector_queue_write (selector, source);
1196 ess_bool_accumulator (GSignalInvocationHint *ihint,
1203 v_boolean = g_value_get_boolean (in);
1204 g_value_set_boolean (out, v_boolean);
1210 e_source_selector_class_init (ESourceSelectorClass *class)
1212 GObjectClass *object_class;
1213 GtkWidgetClass *widget_class;
1214 GtkTreeViewClass *tree_view_class;
1216 g_type_class_add_private (class, sizeof (ESourceSelectorPrivate));
1218 object_class = G_OBJECT_CLASS (class);
1219 object_class->set_property = source_selector_set_property;
1220 object_class->get_property = source_selector_get_property;
1221 object_class->dispose = source_selector_dispose;
1222 object_class->finalize = source_selector_finalize;
1223 object_class->constructed = source_selector_constructed;
1225 widget_class = GTK_WIDGET_CLASS (class);
1226 widget_class->button_press_event = source_selector_button_press_event;
1227 widget_class->drag_leave = source_selector_drag_leave;
1228 widget_class->drag_motion = source_selector_drag_motion;
1229 widget_class->drag_drop = source_selector_drag_drop;
1230 widget_class->drag_data_received = source_selector_drag_data_received;
1231 widget_class->popup_menu = source_selector_popup_menu;
1233 tree_view_class = GTK_TREE_VIEW_CLASS (class);
1234 tree_view_class->test_collapse_row = source_selector_test_collapse_row;
1235 tree_view_class->row_expanded = source_selector_row_expanded;
1237 class->get_source_selected = source_selector_get_source_selected;
1238 class->set_source_selected = source_selector_set_source_selected;
1240 g_object_class_install_property (
1242 PROP_EXTENSION_NAME,
1243 g_param_spec_string (
1249 G_PARAM_CONSTRUCT_ONLY |
1250 G_PARAM_STATIC_STRINGS));
1252 g_object_class_install_property (
1254 PROP_PRIMARY_SELECTION,
1255 g_param_spec_object (
1256 "primary-selection",
1261 G_PARAM_STATIC_STRINGS));
1263 g_object_class_install_property (
1266 g_param_spec_object (
1270 E_TYPE_SOURCE_REGISTRY,
1272 G_PARAM_CONSTRUCT_ONLY |
1273 G_PARAM_STATIC_STRINGS));
1275 g_object_class_install_property (
1278 g_param_spec_boolean (
1284 G_PARAM_STATIC_STRINGS));
1286 g_object_class_install_property (
1289 g_param_spec_boolean (
1295 G_PARAM_STATIC_STRINGS));
1297 signals[SELECTION_CHANGED] = g_signal_new (
1298 "selection-changed",
1299 G_OBJECT_CLASS_TYPE (object_class),
1301 G_STRUCT_OFFSET (ESourceSelectorClass, selection_changed),
1303 g_cclosure_marshal_VOID__VOID,
1306 /* XXX Consider this signal deprecated. Connect
1307 * to "notify::primary-selection" instead. */
1308 signals[PRIMARY_SELECTION_CHANGED] = g_signal_new (
1309 "primary-selection-changed",
1310 G_OBJECT_CLASS_TYPE (object_class),
1312 G_STRUCT_OFFSET (ESourceSelectorClass, primary_selection_changed),
1314 g_cclosure_marshal_VOID__VOID,
1317 signals[POPUP_EVENT] = g_signal_new (
1319 G_OBJECT_CLASS_TYPE (object_class),
1321 G_STRUCT_OFFSET (ESourceSelectorClass, popup_event),
1322 ess_bool_accumulator, NULL,
1323 e_data_server_ui_marshal_BOOLEAN__OBJECT_BOXED,
1324 G_TYPE_BOOLEAN, 2, G_TYPE_OBJECT,
1325 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
1327 signals[DATA_DROPPED] = g_signal_new (
1329 G_OBJECT_CLASS_TYPE (object_class),
1331 G_STRUCT_OFFSET (ESourceSelectorClass, data_dropped),
1333 e_data_server_ui_marshal_BOOLEAN__BOXED_OBJECT_FLAGS_UINT,
1335 GTK_TYPE_SELECTION_DATA | G_SIGNAL_TYPE_STATIC_SCOPE,
1337 GDK_TYPE_DRAG_ACTION,
1342 e_source_selector_init (ESourceSelector *selector)
1344 GHashTable *pending_writes;
1345 GtkTreeViewColumn *column;
1346 GtkTreeSelection *selection;
1347 GtkCellRenderer *renderer;
1348 GtkTreeStore *tree_store;
1349 GtkTreeView *tree_view;
1351 pending_writes = g_hash_table_new_full (
1352 (GHashFunc) g_direct_hash,
1353 (GEqualFunc) g_direct_equal,
1354 (GDestroyNotify) g_object_unref,
1355 (GDestroyNotify) pending_writes_destroy_source);
1357 selector->priv = E_SOURCE_SELECTOR_GET_PRIVATE (selector);
1359 selector->priv->pending_writes = pending_writes;
1361 selector->priv->main_context = g_main_context_get_thread_default ();
1362 if (selector->priv->main_context != NULL)
1363 g_main_context_ref (selector->priv->main_context);
1365 tree_view = GTK_TREE_VIEW (selector);
1367 gtk_tree_view_set_search_column (tree_view, COLUMN_SOURCE);
1368 gtk_tree_view_set_enable_search (tree_view, TRUE);
1370 selector->priv->toggled_last = FALSE;
1371 selector->priv->select_new = FALSE;
1372 selector->priv->show_colors = TRUE;
1373 selector->priv->show_toggles = TRUE;
1375 selector->priv->source_index = g_hash_table_new_full (
1376 (GHashFunc) e_source_hash,
1377 (GEqualFunc) e_source_equal,
1378 (GDestroyNotify) g_object_unref,
1379 (GDestroyNotify) gtk_tree_row_reference_free);
1381 tree_store = gtk_tree_store_new (
1383 G_TYPE_STRING, /* COLUMN_NAME */
1384 GDK_TYPE_COLOR, /* COLUMN_COLOR */
1385 G_TYPE_BOOLEAN, /* COLUMN_ACTIVE */
1386 G_TYPE_BOOLEAN, /* COLUMN_SHOW_COLOR */
1387 G_TYPE_BOOLEAN, /* COLUMN_SHOW_TOGGLE */
1388 G_TYPE_INT, /* COLUMN_WEIGHT */
1389 E_TYPE_SOURCE); /* COLUMN_SOURCE */
1391 gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (tree_store));
1393 column = gtk_tree_view_column_new ();
1394 gtk_tree_view_append_column (tree_view, column);
1396 renderer = e_cell_renderer_color_new ();
1398 G_OBJECT (renderer), "mode",
1399 GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
1400 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1401 gtk_tree_view_column_add_attribute (
1402 column, renderer, "color", COLUMN_COLOR);
1403 gtk_tree_view_column_add_attribute (
1404 column, renderer, "visible", COLUMN_SHOW_COLOR);
1406 renderer = e_cell_renderer_safe_toggle_new ();
1407 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1408 gtk_tree_view_column_add_attribute (
1409 column, renderer, "active", COLUMN_ACTIVE);
1410 gtk_tree_view_column_add_attribute (
1411 column, renderer, "visible", COLUMN_SHOW_TOGGLE);
1413 renderer, "toggled",
1414 G_CALLBACK (cell_toggled_callback), selector);
1416 renderer = gtk_cell_renderer_text_new ();
1418 G_OBJECT (renderer),
1419 "ellipsize", PANGO_ELLIPSIZE_END, NULL);
1420 g_signal_connect_swapped (
1422 G_CALLBACK (text_cell_edited_cb), selector);
1423 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1424 gtk_tree_view_column_set_attributes (
1426 "text", COLUMN_NAME,
1427 "weight", COLUMN_WEIGHT,
1430 selection = gtk_tree_view_get_selection (tree_view);
1431 gtk_tree_selection_set_select_function (
1432 selection, (GtkTreeSelectionFunc)
1433 selection_func, selector, NULL);
1434 g_signal_connect_object (
1435 selection, "changed",
1436 G_CALLBACK (selection_changed_callback),
1437 G_OBJECT (selector), 0);
1439 gtk_tree_view_set_headers_visible (tree_view, FALSE);
1443 * e_source_selector_new:
1444 * @registry: an #ESourceRegistry
1445 * @extension_name: the name of an #ESource extension
1447 * Displays a list of sources from @registry having an extension named
1448 * @extension_name. The sources are grouped by backend or groupware
1449 * account, which are described by the parent source.
1451 * Returns: a new #ESourceSelector
1454 e_source_selector_new (ESourceRegistry *registry,
1455 const gchar *extension_name)
1457 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
1458 g_return_val_if_fail (extension_name != NULL, NULL);
1460 return g_object_new (
1461 E_TYPE_SOURCE_SELECTOR, "registry", registry,
1462 "extension-name", extension_name, NULL);
1466 * e_source_selector_get_registry:
1467 * @selector: an #ESourceSelector
1469 * Returns the #ESourceRegistry that @selector is getting sources from.
1471 * Returns: an #ESourceRegistry
1476 e_source_selector_get_registry (ESourceSelector *selector)
1478 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
1480 return selector->priv->registry;
1484 * e_source_selector_get_extension_name:
1485 * @selector: an #ESourceSelector
1487 * Returns the extension name used to filter which sources are displayed.
1489 * Returns: the #ESource extension name
1494 e_source_selector_get_extension_name (ESourceSelector *selector)
1496 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
1498 return selector->priv->extension_name;
1502 * e_source_selector_get_show_colors:
1503 * @selector: an #ESourceSelector
1505 * Returns whether colors are shown next to data sources.
1507 * Returns: %TRUE if colors are being shown
1512 e_source_selector_get_show_colors (ESourceSelector *selector)
1514 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
1516 return selector->priv->show_colors;
1520 * e_source_selector_set_show_colors:
1521 * @selector: an #ESourceSelector
1522 * @show_colors: whether to show colors
1524 * Sets whether to show colors next to data sources.
1529 e_source_selector_set_show_colors (ESourceSelector *selector,
1530 gboolean show_colors)
1532 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
1534 if ((show_colors ? 1 : 0) == (selector->priv->show_colors ? 1 : 0))
1537 selector->priv->show_colors = show_colors;
1539 g_object_notify (G_OBJECT (selector), "show-colors");
1541 source_selector_build_model (selector);
1545 * e_source_selector_get_show_toggles:
1546 * @selector: an #ESourceSelector
1548 * Returns whether toggles are shown next to data sources.
1550 * Returns: %TRUE if toggles are being shown
1555 e_source_selector_get_show_toggles (ESourceSelector *selector)
1557 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
1559 return selector->priv->show_toggles;
1563 * e_source_selector_set_show_toggles:
1564 * @selector: an #ESourceSelector
1565 * @show_toggles: whether to show toggles
1567 * Sets whether to show toggles next to data sources.
1572 e_source_selector_set_show_toggles (ESourceSelector *selector,
1573 gboolean show_toggles)
1575 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
1577 if ((show_toggles ? 1 : 0) == (selector->priv->show_toggles ? 1 : 0))
1580 selector->priv->show_toggles = show_toggles;
1582 g_object_notify (G_OBJECT (selector), "show-toggles");
1584 source_selector_build_model (selector);
1587 /* Helper for e_source_selector_get_selection() */
1589 source_selector_check_selected (GtkTreeModel *model,
1597 ESourceSelector *selector;
1599 } *closure = user_data;
1601 gtk_tree_model_get (model, iter, COLUMN_SOURCE, &source, -1);
1603 if (e_source_selector_source_is_selected (closure->selector, source))
1604 closure->list = g_slist_prepend (closure->list, source);
1606 g_object_unref (source);
1612 * e_source_selector_get_selection:
1613 * @selector: an #ESourceSelector
1615 * Get the list of selected sources, i.e. those that were enabled through the
1616 * corresponding checkboxes in the tree.
1618 * Returns: A list of the ESources currently selected. The sources will
1619 * be in the same order as they appear on the screen, and the list should be
1620 * freed using e_source_selector_free_selection().
1623 e_source_selector_get_selection (ESourceSelector *selector)
1626 ESourceSelector *selector;
1630 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
1632 closure.selector = selector;
1633 closure.list = NULL;
1635 gtk_tree_model_foreach (
1636 gtk_tree_view_get_model (GTK_TREE_VIEW (selector)),
1637 (GtkTreeModelForeachFunc) source_selector_check_selected,
1640 return g_slist_reverse (closure.list);
1644 * e_source_list_free_selection:
1645 * @list: A selection list returned by e_source_selector_get_selection().
1647 * Free the selection list.
1650 e_source_selector_free_selection (GSList *list)
1652 g_slist_foreach (list, (GFunc) g_object_unref, NULL);
1653 g_slist_free (list);
1657 * e_source_selector_set_select_new:
1658 * @selector: An #ESourceSelector widget
1659 * @state: A gboolean
1661 * Set whether or not to select new sources added to @selector.
1664 e_source_selector_set_select_new (ESourceSelector *selector,
1667 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
1669 selector->priv->select_new = state;
1673 * e_source_selector_select_source:
1674 * @selector: An #ESourceSelector widget
1675 * @source: An #ESource.
1677 * Select @source in @selector.
1680 e_source_selector_select_source (ESourceSelector *selector,
1683 ESourceSelectorClass *class;
1684 GtkTreeRowReference *reference;
1685 GHashTable *source_index;
1687 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
1688 g_return_if_fail (E_IS_SOURCE (source));
1690 /* Make sure the ESource is in our tree model. */
1691 source_index = selector->priv->source_index;
1692 reference = g_hash_table_lookup (source_index, source);
1693 g_return_if_fail (gtk_tree_row_reference_valid (reference));
1695 class = E_SOURCE_SELECTOR_GET_CLASS (selector);
1696 g_return_if_fail (class->set_source_selected != NULL);
1698 class->set_source_selected (selector, source, TRUE);
1700 g_signal_emit (selector, signals[SELECTION_CHANGED], 0);
1704 * e_source_selector_unselect_source:
1705 * @selector: An #ESourceSelector widget
1706 * @source: An #ESource.
1708 * Unselect @source in @selector.
1711 e_source_selector_unselect_source (ESourceSelector *selector,
1714 ESourceSelectorClass *class;
1715 GtkTreeRowReference *reference;
1716 GHashTable *source_index;
1718 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
1719 g_return_if_fail (E_IS_SOURCE (source));
1721 /* Make sure the ESource is in our tree model. */
1722 source_index = selector->priv->source_index;
1723 reference = g_hash_table_lookup (source_index, source);
1724 g_return_if_fail (gtk_tree_row_reference_valid (reference));
1726 class = E_SOURCE_SELECTOR_GET_CLASS (selector);
1727 g_return_if_fail (class->set_source_selected != NULL);
1729 class->set_source_selected (selector, source, FALSE);
1731 g_signal_emit (selector, signals[SELECTION_CHANGED], 0);
1735 * e_source_selector_select_exclusive:
1736 * @selector: An #ESourceSelector widget
1737 * @source: An #ESource.
1739 * Select @source in @selector and unselect all others.
1744 e_source_selector_select_exclusive (ESourceSelector *selector,
1747 ESourceSelectorClass *class;
1748 GHashTable *source_index;
1749 GHashTableIter iter;
1752 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
1753 g_return_if_fail (E_IS_SOURCE (source));
1755 class = E_SOURCE_SELECTOR_GET_CLASS (selector);
1756 g_return_if_fail (class->set_source_selected != NULL);
1758 source_index = selector->priv->source_index;
1759 g_hash_table_iter_init (&iter, source_index);
1761 while (g_hash_table_iter_next (&iter, &key, NULL)) {
1762 gboolean selected = e_source_equal (key, source);
1763 class->set_source_selected (selector, key, selected);
1766 g_signal_emit (selector, signals[SELECTION_CHANGED], 0);
1770 * e_source_selector_source_is_selected:
1771 * @selector: An #ESourceSelector widget
1772 * @source: An #ESource.
1774 * Check whether @source is selected in @selector.
1776 * Returns: %TRUE if @source is currently selected, %FALSE otherwise.
1779 e_source_selector_source_is_selected (ESourceSelector *selector,
1782 ESourceSelectorClass *class;
1783 GtkTreeRowReference *reference;
1784 GHashTable *source_index;
1786 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
1787 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
1789 /* Make sure the ESource is in our tree model. */
1790 source_index = selector->priv->source_index;
1791 reference = g_hash_table_lookup (source_index, source);
1792 g_return_val_if_fail (gtk_tree_row_reference_valid (reference), FALSE);
1794 class = E_SOURCE_SELECTOR_GET_CLASS (selector);
1795 g_return_val_if_fail (class->get_source_selected != NULL, FALSE);
1797 return class->get_source_selected (selector, source);
1801 * e_source_selector_edit_primary_selection:
1802 * @selector: An #ESourceSelector widget
1804 * Allows the user to rename the primary selected source by opening an
1805 * entry box directly in @selector.
1810 e_source_selector_edit_primary_selection (ESourceSelector *selector)
1812 GtkTreeRowReference *reference;
1813 GtkTreeSelection *selection;
1814 GtkTreeViewColumn *column;
1815 GtkCellRenderer *renderer;
1816 GtkTreeView *tree_view;
1817 GtkTreeModel *model;
1818 GtkTreePath *path = NULL;
1822 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
1824 tree_view = GTK_TREE_VIEW (selector);
1825 column = gtk_tree_view_get_column (tree_view, 0);
1826 reference = selector->priv->saved_primary_selection;
1827 selection = gtk_tree_view_get_selection (tree_view);
1829 if (reference != NULL)
1830 path = gtk_tree_row_reference_get_path (reference);
1831 else if (gtk_tree_selection_get_selected (selection, &model, &iter))
1832 path = gtk_tree_model_get_path (model, &iter);
1837 /* XXX Because we stuff three renderers in a single column,
1838 * we have to manually hunt for the text renderer. */
1840 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
1841 while (list != NULL) {
1842 renderer = list->data;
1843 if (GTK_IS_CELL_RENDERER_TEXT (renderer))
1845 list = g_list_delete_link (list, list);
1849 /* Make the text cell renderer editable, but only temporarily.
1850 * We don't want editing to be activated by simply clicking on
1851 * the source name. Too easy for accidental edits to occur. */
1852 g_object_set (renderer, "editable", TRUE, NULL);
1853 gtk_tree_view_expand_to_path (tree_view, path);
1854 gtk_tree_view_set_cursor_on_cell (
1855 tree_view, path, column, renderer, TRUE);
1856 g_object_set (renderer, "editable", FALSE, NULL);
1858 gtk_tree_path_free (path);
1862 * e_source_selector_ref_primary_selection:
1863 * @selector: An #ESourceSelector widget
1865 * Get the primary selected source. The primary selection is the one that is
1866 * highlighted through the normal #GtkTreeView selection mechanism (as opposed
1867 * to the "normal" selection, which is the set of source whose checkboxes are
1870 * The returned #ESource is referenced for thread-safety and must be
1871 * unreferenced with g_object_unref() when finished with it.
1873 * Returns: The selected source.
1878 e_source_selector_ref_primary_selection (ESourceSelector *selector)
1881 GtkTreeRowReference *reference;
1882 GtkTreeSelection *selection;
1883 GtkTreeView *tree_view;
1884 GtkTreeModel *model;
1886 const gchar *extension_name;
1887 gboolean have_iter = FALSE;
1889 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
1891 tree_view = GTK_TREE_VIEW (selector);
1892 model = gtk_tree_view_get_model (tree_view);
1893 selection = gtk_tree_view_get_selection (tree_view);
1895 reference = selector->priv->saved_primary_selection;
1897 if (gtk_tree_row_reference_valid (reference)) {
1900 path = gtk_tree_row_reference_get_path (reference);
1901 have_iter = gtk_tree_model_get_iter (model, &iter, path);
1902 gtk_tree_path_free (path);
1906 have_iter = gtk_tree_selection_get_selected (
1907 selection, NULL, &iter);
1912 gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
1914 extension_name = e_source_selector_get_extension_name (selector);
1916 if (!e_source_has_extension (source, extension_name)) {
1917 g_object_unref (source);
1925 * e_source_selector_set_primary_selection:
1926 * @selector: an #ESourceSelector widget
1927 * @source: an #ESource to select
1929 * Highlights @source in @selector. The highlighted #ESource is called
1930 * the primary selection.
1932 * Do not confuse this function with e_source_selector_select_source(),
1933 * which activates the check box next to an #ESource's display name in
1934 * @selector. This function does not alter the check box.
1937 e_source_selector_set_primary_selection (ESourceSelector *selector,
1940 GHashTable *source_index;
1941 GtkTreeRowReference *reference;
1942 GtkTreeSelection *selection;
1943 GtkTreeView *tree_view;
1944 GtkTreePath *child_path;
1945 GtkTreePath *parent_path;
1946 const gchar *extension_name;
1948 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
1949 g_return_if_fail (E_IS_SOURCE (source));
1951 tree_view = GTK_TREE_VIEW (selector);
1952 selection = gtk_tree_view_get_selection (tree_view);
1954 source_index = selector->priv->source_index;
1955 reference = g_hash_table_lookup (source_index, source);
1957 /* XXX Maybe we should return a success/fail boolean? */
1958 if (!gtk_tree_row_reference_valid (reference))
1961 extension_name = e_source_selector_get_extension_name (selector);
1963 /* Return silently if attempting to select a parent node
1964 * lacking the expected extension (e.g. On This Computer). */
1965 if (!e_source_has_extension (source, extension_name))
1968 /* We block the signal because this all needs to be atomic */
1969 g_signal_handlers_block_matched (
1970 selection, G_SIGNAL_MATCH_FUNC,
1971 0, 0, NULL, selection_changed_callback, NULL);
1972 gtk_tree_selection_unselect_all (selection);
1973 g_signal_handlers_unblock_matched (
1974 selection, G_SIGNAL_MATCH_FUNC,
1975 0, 0, NULL, selection_changed_callback, NULL);
1977 clear_saved_primary_selection (selector);
1979 child_path = gtk_tree_row_reference_get_path (reference);
1981 parent_path = gtk_tree_path_copy (child_path);
1982 gtk_tree_path_up (parent_path);
1984 if (gtk_tree_view_row_expanded (tree_view, parent_path)) {
1985 gtk_tree_selection_select_path (selection, child_path);
1987 selector->priv->saved_primary_selection =
1988 gtk_tree_row_reference_copy (reference);
1989 g_signal_emit (selector, signals[PRIMARY_SELECTION_CHANGED], 0);
1990 g_object_notify (G_OBJECT (selector), "primary-selection");
1993 gtk_tree_path_free (child_path);
1994 gtk_tree_path_free (parent_path);
1998 * e_source_selector_ref_source_by_path:
1999 * @selector: an #ESourceSelector
2000 * @path: a #GtkTreePath
2002 * Returns the #ESource object at @path, or %NULL if @path is invalid.
2004 * The returned #ESource is referenced for thread-safety and must be
2005 * unreferenced with g_object_unref() when finished with it.
2007 * Returns: the #ESource object at @path, or %NULL
2012 e_source_selector_ref_source_by_path (ESourceSelector *selector,
2015 ESource *source = NULL;
2016 GtkTreeModel *model;
2019 g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL);
2020 g_return_val_if_fail (path != NULL, NULL);
2022 model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector));
2024 if (gtk_tree_model_get_iter (model, &iter, path))
2025 gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1);
2031 * e_source_selector_queue_write:
2032 * @selector: an #ESourceSelecetor
2033 * @source: an #ESource with changes to be written
2035 * Queues a main loop idle callback to write changes to @source back to
2036 * the D-Bus registry service.
2041 e_source_selector_queue_write (ESourceSelector *selector,
2044 GSource *idle_source;
2045 GHashTable *pending_writes;
2046 GMainContext *main_context;
2047 AsyncContext *async_context;
2049 g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
2050 g_return_if_fail (E_IS_SOURCE (source));
2052 main_context = selector->priv->main_context;
2053 pending_writes = selector->priv->pending_writes;
2055 idle_source = g_hash_table_lookup (pending_writes, source);
2056 if (idle_source != NULL && !g_source_is_destroyed (idle_source))
2059 async_context = g_slice_new0 (AsyncContext);
2060 async_context->selector = g_object_ref (selector);
2061 async_context->source = g_object_ref (source);
2063 /* Set a higher priority so this idle source runs before our
2064 * source_selector_cancel_write() signal handler, which will
2065 * cancel this idle source. Cancellation is the right thing
2066 * to do when receiving changes from OTHER registry clients,
2067 * but we don't want to cancel our own changes.
2069 * XXX This might be an argument for using etags.
2071 idle_source = g_idle_source_new ();
2072 g_hash_table_insert (
2074 g_object_ref (source),
2075 g_source_ref (idle_source));
2076 g_source_set_callback (
2078 source_selector_write_idle_cb,
2080 (GDestroyNotify) async_context_free);
2081 g_source_set_priority (idle_source, G_PRIORITY_HIGH_IDLE);
2082 g_source_attach (idle_source, main_context);
2083 g_source_unref (idle_source);