From 55eb1ebe48bcebd65e80937891978ee0b01f85b9 Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Sat, 4 Dec 2010 16:59:53 -0600 Subject: [PATCH] Adapt ESourceSelector to the new ESource API. --- .../libedataserverui/libedataserverui-sections.txt | 11 +- libedataserverui/e-source-selector.c | 1711 +++++++++++--------- libedataserverui/e-source-selector.h | 36 +- tests/libedataserverui/test-source-selector.c | 92 +- 4 files changed, 1014 insertions(+), 836 deletions(-) diff --git a/docs/reference/libedataserverui/libedataserverui-sections.txt b/docs/reference/libedataserverui/libedataserverui-sections.txt index 3cde69f..6e4beca 100644 --- a/docs/reference/libedataserverui/libedataserverui-sections.txt +++ b/docs/reference/libedataserverui/libedataserverui-sections.txt @@ -397,21 +397,24 @@ e_tree_model_generator_get_type ESourceSelector ESourceSelector e_source_selector_new -e_source_selector_get_source_list +e_source_selector_get_registry +e_source_selector_get_extension_name +e_source_selector_get_show_colors +e_source_selector_set_show_colors +e_source_selector_get_show_toggles +e_source_selector_set_show_toggles e_source_selector_select_source e_source_selector_unselect_source e_source_selector_select_exclusive e_source_selector_source_is_selected e_source_selector_get_selection e_source_selector_free_selection -e_source_selector_show_selection -e_source_selector_selection_shown e_source_selector_set_select_new e_source_selector_edit_primary_selection e_source_selector_ref_primary_selection e_source_selector_set_primary_selection -e_source_selector_get_primary_source_group e_source_selector_ref_source_by_path +e_source_selector_queue_write E_SOURCE_SELECTOR E_IS_SOURCE_SELECTOR diff --git a/libedataserverui/e-source-selector.c b/libedataserverui/e-source-selector.c index 2d6a5af..fb1af20 100644 --- a/libedataserverui/e-source-selector.c +++ b/libedataserverui/e-source-selector.c @@ -27,6 +27,9 @@ #include +#include + +#include "e-cell-renderer-color.h" #include "e-data-server-ui-marshal.h" #include "e-source-selector.h" @@ -34,33 +37,37 @@ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_SOURCE_SELECTOR, ESourceSelectorPrivate)) +typedef struct _AsyncContext AsyncContext; + struct _ESourceSelectorPrivate { - ESourceList *list; + ESourceRegistry *registry; + GHashTable *source_index; + gchar *extension_name; - GHashTable *selected_sources; GtkTreeRowReference *saved_primary_selection; - ESourceGroup *primary_source_group; - gint rebuild_model_idle_id; + /* ESource -> GSource */ + GHashTable *pending_writes; + GMainContext *main_context; gboolean toggled_last; - gboolean checkboxes_shown; gboolean select_new; + gboolean show_colors; + gboolean show_toggles; }; -typedef struct { +struct _AsyncContext { ESourceSelector *selector; - - GHashTable *remaining_uids; - GSList *deleted_uids; - - gboolean selection_changed; -} ESourceSelectorRebuildData; + ESource *source; +}; enum { PROP_0, + PROP_EXTENSION_NAME, PROP_PRIMARY_SELECTION, - PROP_SOURCE_LIST + PROP_REGISTRY, + PROP_SHOW_COLORS, + PROP_SHOW_TOGGLES }; enum { @@ -70,23 +77,35 @@ enum { DATA_DROPPED, NUM_SIGNALS }; -static guint signals[NUM_SIGNALS] = { 0 }; -G_DEFINE_TYPE (ESourceSelector, e_source_selector, GTK_TYPE_TREE_VIEW) +enum { + COLUMN_NAME, + COLUMN_COLOR, + COLUMN_ACTIVE, + COLUMN_SHOW_COLOR, + COLUMN_SHOW_TOGGLE, + COLUMN_WEIGHT, + COLUMN_SOURCE, + NUM_COLUMNS +}; + +static guint signals[NUM_SIGNALS]; -/* safe-toggle-renderer definition - it'll not call 'toggled' signal on 'activate', when mouse is not over the toggle */ +G_DEFINE_TYPE (ESourceSelector, e_source_selector, GTK_TYPE_TREE_VIEW) -typedef struct _ECellRendererSafeToggle { - GtkCellRendererToggle parent; -} ECellRendererSafeToggle; +/* ESafeToggleRenderer does not emit 'toggled' signal + * on 'activate' when mouse is not over the toggle. */ -typedef struct _ECellRendererSafeToggleClass { - GtkCellRendererToggleClass parent_class; -} ECellRendererSafeToggleClass; +typedef GtkCellRendererToggle ECellRendererSafeToggle; +typedef GtkCellRendererToggleClass ECellRendererSafeToggleClass; +/* Forward Declarations */ GType e_cell_renderer_safe_toggle_get_type (void); -G_DEFINE_TYPE (ECellRendererSafeToggle, e_cell_renderer_safe_toggle, GTK_TYPE_CELL_RENDERER_TOGGLE) +G_DEFINE_TYPE ( + ECellRendererSafeToggle, + e_cell_renderer_safe_toggle, + GTK_TYPE_CELL_RENDERER_TOGGLE) static gboolean safe_toggle_activate (GtkCellRenderer *cell, @@ -97,34 +116,32 @@ safe_toggle_activate (GtkCellRenderer *cell, const GdkRectangle *cell_area, GtkCellRendererState flags) { - if (event->type == GDK_BUTTON_PRESS && cell_area) { - cairo_region_t *region; - gboolean contains_point; + gboolean point_in_cell_area = TRUE; - if (cell_area->width > 0 && cell_area->height > 0) - region = cairo_region_create_rectangle (cell_area); - else - region = cairo_region_create (); + if (event->type == GDK_BUTTON_PRESS && cell_area != NULL) { + cairo_region_t *region; - contains_point = cairo_region_contains_point ( + region = cairo_region_create_rectangle (cell_area); + point_in_cell_area = cairo_region_contains_point ( region, event->button.x, event->button.y); - cairo_region_destroy (region); - - if (!contains_point) - return FALSE; } - return GTK_CELL_RENDERER_CLASS (e_cell_renderer_safe_toggle_parent_class)->activate (cell, event, widget, path, background_area, cell_area, flags); + if (!point_in_cell_area) + return FALSE; + + return GTK_CELL_RENDERER_CLASS ( + e_cell_renderer_safe_toggle_parent_class)->activate ( + cell, event, widget, path, background_area, cell_area, flags); } static void e_cell_renderer_safe_toggle_class_init (ECellRendererSafeToggleClass *class) { - GtkCellRendererClass *rndr_class; + GtkCellRendererClass *cell_renderer_class; - rndr_class = GTK_CELL_RENDERER_CLASS (class); - rndr_class->activate = safe_toggle_activate; + cell_renderer_class = GTK_CELL_RENDERER_CLASS (class); + cell_renderer_class->activate = safe_toggle_activate; } static void @@ -138,514 +155,399 @@ e_cell_renderer_safe_toggle_new (void) return g_object_new (e_cell_renderer_safe_toggle_get_type (), NULL); } -/* Selection management. */ - -static ESourceSelectorRebuildData * -create_rebuild_data (ESourceSelector *selector) +static void +clear_saved_primary_selection (ESourceSelector *selector) { - ESourceSelectorRebuildData *rebuild_data; - - rebuild_data = g_new0 (ESourceSelectorRebuildData, 1); - - rebuild_data->selector = selector; - rebuild_data->remaining_uids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, - (GDestroyNotify) gtk_tree_row_reference_free); - rebuild_data->deleted_uids = NULL; - - return rebuild_data; + gtk_tree_row_reference_free (selector->priv->saved_primary_selection); + selector->priv->saved_primary_selection = NULL; } static void -free_rebuild_data (ESourceSelectorRebuildData *rebuild_data) +async_context_free (AsyncContext *async_context) { - GSList *p; + if (async_context->selector != NULL) + g_object_unref (async_context->selector); - g_hash_table_destroy (rebuild_data->remaining_uids); - for (p = rebuild_data->deleted_uids; p; p = p->next) - gtk_tree_row_reference_free (p->data); - g_slist_free (rebuild_data->deleted_uids); + if (async_context->source != NULL) + g_object_unref (async_context->source); - g_free (rebuild_data); + g_slice_free (AsyncContext, async_context); } static void -clear_saved_primary_selection (ESourceSelector *selector) +pending_writes_destroy_source (GSource *source) { - gtk_tree_row_reference_free (selector->priv->saved_primary_selection); - selector->priv->saved_primary_selection = NULL; + g_source_destroy (source); + g_source_unref (source); } -static gboolean -source_is_selected (ESourceSelector *selector, - ESource *source) +static void +source_selector_write_done_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) { - GHashTable *hash_table; + ESource *source; + ESourceSelector *selector; + GError *error = NULL; - hash_table = selector->priv->selected_sources; + source = E_SOURCE (source_object); + selector = E_SOURCE_SELECTOR (user_data); - return (g_hash_table_lookup (hash_table, source) != NULL); + e_source_write_finish (source, result, &error); + + /* FIXME Display the error in the selector somehow? */ + if (error != NULL) { + g_warning ("%s: %s", G_STRFUNC, error->message); + g_error_free (error); + } + + g_object_unref (selector); } -static void -select_source (ESourceSelector *selector, - ESource *source) +static gboolean +source_selector_write_idle_cb (gpointer user_data) { - if (source_is_selected (selector, source)) - return; + AsyncContext *async_context = user_data; + GHashTable *pending_writes; - g_hash_table_insert ( - selector->priv->selected_sources, - source, g_object_ref (source)); + /* XXX This operation is not cancellable. */ + e_source_write ( + async_context->source, NULL, + source_selector_write_done_cb, + g_object_ref (async_context->selector)); + + pending_writes = async_context->selector->priv->pending_writes; + g_hash_table_remove (pending_writes, async_context->source); + + return FALSE; } static void -unselect_source (ESourceSelector *selector, - ESource *source) +source_selector_cancel_write (ESourceSelector *selector, + ESource *source) { - if (!source_is_selected (selector, source)) - return; + GHashTable *pending_writes; - /* (This will unref the source.) */ - g_hash_table_remove (selector->priv->selected_sources, source); + /* Cancel any pending writes for this ESource so as not + * to overwrite whatever change we're being notified of. */ + pending_writes = selector->priv->pending_writes; + g_hash_table_remove (pending_writes, source); } -static gboolean -find_source_iter (ESourceSelector *selector, - ESource *source, - GtkTreeIter *parent_iter, - GtkTreeIter *source_iter) +static void +source_selector_update_row (ESourceSelector *selector, + ESource *source) { + GHashTable *source_index; + ESourceExtension *extension = NULL; + GtkTreeRowReference *reference; GtkTreeModel *model; + GtkTreePath *path; + GtkTreeIter iter; + const gchar *extension_name; + const gchar *display_name; + gboolean selected; - model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); + source_index = selector->priv->source_index; + reference = g_hash_table_lookup (source_index, source); + + /* This function runs when ANY ESource in the registry changes. + * If the ESource is not in our tree model then return silently. */ + if (reference == NULL) + return; - if (gtk_tree_model_get_iter_first (model, parent_iter)) { - do { - if (gtk_tree_model_iter_children (model, source_iter, parent_iter)) { - do { - gpointer data; + /* If we do have a row reference, it should be valid. */ + g_return_if_fail (gtk_tree_row_reference_valid (reference)); - gtk_tree_model_get (model, source_iter, 0, &data, -1); - g_assert (E_IS_SOURCE (data)); + model = gtk_tree_row_reference_get_model (reference); + path = gtk_tree_row_reference_get_path (reference); + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_path_free (path); - if (E_SOURCE (data) == source) { - g_object_unref (data); + display_name = e_source_get_display_name (source); - return TRUE; - } + extension_name = e_source_selector_get_extension_name (selector); + selected = e_source_selector_source_is_selected (selector, source); - g_object_unref (data); - } while (gtk_tree_model_iter_next (model, source_iter)); - } - } while (gtk_tree_model_iter_next (model, parent_iter)); - } + if (e_source_has_extension (source, extension_name)) + extension = e_source_get_extension (source, extension_name); - return FALSE; + if (extension != NULL) { + GdkColor color; + const gchar *color_spec = NULL; + gboolean show_color = FALSE; + gboolean show_toggle; + + show_color = + E_IS_SOURCE_SELECTABLE (extension) && + e_source_selector_get_show_colors (selector); + + if (show_color) + color_spec = e_source_selectable_get_color ( + E_SOURCE_SELECTABLE (extension)); + + if (color_spec != NULL && *color_spec != '\0') + show_color = gdk_color_parse (color_spec, &color); + + show_toggle = e_source_selector_get_show_toggles (selector); + + gtk_tree_store_set ( + GTK_TREE_STORE (model), &iter, + COLUMN_NAME, display_name, + COLUMN_COLOR, show_color ? &color : NULL, + COLUMN_ACTIVE, selected, + COLUMN_SHOW_COLOR, show_color, + COLUMN_SHOW_TOGGLE, show_toggle, + COLUMN_WEIGHT, PANGO_WEIGHT_NORMAL, + COLUMN_SOURCE, source, + -1); + } else { + gtk_tree_store_set ( + GTK_TREE_STORE (model), &iter, + COLUMN_NAME, display_name, + COLUMN_COLOR, NULL, + COLUMN_ACTIVE, FALSE, + COLUMN_SHOW_COLOR, FALSE, + COLUMN_SHOW_TOGGLE, FALSE, + COLUMN_WEIGHT, PANGO_WEIGHT_BOLD, + COLUMN_SOURCE, source, + -1); + } } -/* Setting up the model. */ static gboolean -rebuild_existing_cb (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) +source_selector_traverse (GNode *node, + ESourceSelector *selector) { - ESourceSelectorRebuildData *rebuild_data = data; - ESourceList *source_list; - GtkTreeRowReference *reference; - gpointer node; - const gchar *uid; - - gtk_tree_model_get (model, iter, 0, &node, -1); + ESource *source; + GHashTable *source_index; + GtkTreeRowReference *reference = NULL; + GtkTreeModel *model; + GtkTreePath *path; + GtkTreeIter iter; - source_list = rebuild_data->selector->priv->list; - reference = gtk_tree_row_reference_new (model, path); + /* Skip the root node. */ + if (G_NODE_IS_ROOT (node)) + return FALSE; - if (E_IS_SOURCE_GROUP (node)) { - uid = e_source_group_peek_uid (E_SOURCE_GROUP (node)); + source_index = selector->priv->source_index; - if (e_source_list_peek_group_by_uid (source_list, uid)) - g_hash_table_insert ( - rebuild_data->remaining_uids, - g_strdup (uid), reference); - else - rebuild_data->deleted_uids = g_slist_append ( - rebuild_data->deleted_uids, reference); - } else { - uid = e_source_get_uid (E_SOURCE (node)); - - if (e_source_list_peek_source_by_uid (source_list, uid)) { - g_hash_table_insert ( - rebuild_data->remaining_uids, - g_strdup (uid), reference); - } else { - rebuild_data->deleted_uids = g_slist_prepend ( - rebuild_data->deleted_uids, reference); - - if (g_hash_table_remove (rebuild_data->selector->priv->selected_sources, node)) - rebuild_data->selection_changed = TRUE; - } - } + model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); - g_object_unref (node); + if (node->parent != NULL && node->parent->data != NULL) + reference = g_hash_table_lookup ( + source_index, node->parent->data); - return FALSE; -} + if (gtk_tree_row_reference_valid (reference)) { + GtkTreeIter parent; -static ESource * -find_source (ESourceSelector *selector, - ESource *source) -{ - GSList *groups, *p; + path = gtk_tree_row_reference_get_path (reference); + gtk_tree_model_get_iter (model, &parent, path); + gtk_tree_path_free (path); - g_return_val_if_fail (selector != NULL, source); - g_return_val_if_fail (E_IS_SOURCE (source), source); + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, &parent); + } else + gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL); - groups = e_source_list_peek_groups (selector->priv->list); - for (p = groups; p != NULL; p = p->next) { - ESourceGroup *group = E_SOURCE_GROUP (p->data); - GSList *sources, *q; + source = E_SOURCE (node->data); - sources = e_source_group_peek_sources (group); - for (q = sources; q != NULL; q = q->next) { - ESource *s = E_SOURCE (q->data); + path = gtk_tree_model_get_path (model, &iter); + reference = gtk_tree_row_reference_new (model, path); + g_hash_table_insert (source_index, g_object_ref (source), reference); + gtk_tree_path_free (path); - if (e_source_equal (s, source)) - return s; - } - } + source_selector_update_row (selector, source); - return source; + return FALSE; } -/** - * compare_source_names - * Compares sources by name. - **/ -static gint -compare_source_names (gconstpointer a, - gconstpointer b) +static void +source_selector_save_expanded (GtkTreeView *tree_view, + GtkTreePath *path, + GQueue *queue) { - const gchar *name_a; - const gchar *name_b; - - g_return_val_if_fail (E_IS_SOURCE (a), -1); - g_return_val_if_fail (E_IS_SOURCE (b), 1); - - name_a = e_source_get_display_name (E_SOURCE (a)); - name_b = e_source_get_display_name (E_SOURCE (b)); + GtkTreeModel *model; + GtkTreeIter iter; + ESource *source; - return g_utf8_collate (name_a, name_b); + model = gtk_tree_view_get_model (tree_view); + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1); + g_queue_push_tail (queue, source); } static void -rebuild_model (ESourceSelector *selector) +source_selector_build_model (ESourceSelector *selector) { - ESourceSelectorRebuildData *rebuild_data; - ESource *source; + ESourceRegistry *registry; + GQueue queue = G_QUEUE_INIT; + GHashTable *source_index; + GtkTreeView *tree_view; GtkTreeModel *model; - GtkTreeStore *store; - GtkTreeIter iter; - GSList *groups, *p; - gboolean set_primary; + ESource *selected; + const gchar *extension_name; + GNode *root; - model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); - store = GTK_TREE_STORE (model); + tree_view = GTK_TREE_VIEW (selector); - rebuild_data = create_rebuild_data (selector); - source = e_source_selector_ref_primary_selection (selector); + registry = e_source_selector_get_registry (selector); + extension_name = e_source_selector_get_extension_name (selector); - if (source != NULL) { - set_primary = TRUE; - g_object_unref (source); - } else { - set_primary = FALSE; - } + /* Make sure we have what we need to build the model, since + * this can get called early in the initialization phase. */ + if (registry == NULL || extension_name == NULL) + return; - gtk_tree_model_foreach (model, rebuild_existing_cb, rebuild_data); + source_index = selector->priv->source_index; + selected = e_source_selector_ref_primary_selection (selector); - /* Remove any delete sources or groups */ - for (p = rebuild_data->deleted_uids; p; p = p->next) { - GtkTreeRowReference *row_ref = p->data; - GtkTreePath *path; - GtkTreeIter parent_iter; - gboolean parent_exists = FALSE; - - path = gtk_tree_row_reference_get_path (row_ref); - if (!path) { - /* skip this, if the reference is not valid any more, like when - * removing a group, which got removed in the previous iteration - * because it had no children */ - continue; - } + /* Save expanded sources to restore later. */ + gtk_tree_view_map_expanded_rows ( + tree_view, (GtkTreeViewMappingFunc) + source_selector_save_expanded, &queue); - gtk_tree_model_get_iter (model, &iter, path); + model = gtk_tree_view_get_model (tree_view); + gtk_tree_store_clear (GTK_TREE_STORE (model)); - /* If it is the last source in the group, delete the group from the tree */ - if (gtk_tree_model_iter_parent (model, &parent_iter, &iter)) - parent_exists = TRUE; + g_hash_table_remove_all (source_index); - gtk_tree_store_remove (store, &iter); + root = e_source_registry_build_display_tree (registry, extension_name); - if (parent_exists && !gtk_tree_model_iter_has_child (model, &parent_iter)) - gtk_tree_store_remove (store, &parent_iter); + g_node_traverse ( + root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, + (GNodeTraverseFunc) source_selector_traverse, + selector); - gtk_tree_path_free (path); - } + e_source_registry_free_display_tree (root); - /* Add new sources/groups or call row_changed in case they were renamed */ - groups = e_source_list_peek_groups (selector->priv->list); - for (p = groups; p != NULL; p = p->next) { - ESourceGroup *group = E_SOURCE_GROUP (p->data); + /* Restore previously expanded sources. */ + while (!g_queue_is_empty (&queue)) { GtkTreeRowReference *reference; - GSList *sources, *q; - gint position; - - sources = e_source_group_peek_sources (group); - if (sources == NULL) - continue; - - /* Copy the list and sort by name. */ - sources = g_slist_copy (sources); - sources = g_slist_sort (sources, compare_source_names); + ESource *source; - reference = g_hash_table_lookup ( - rebuild_data->remaining_uids, - e_source_group_peek_uid (group)); + source = g_queue_pop_head (&queue); + reference = g_hash_table_lookup (source_index, source); - if (reference == NULL) { - gtk_tree_store_append (store, &iter, NULL); - gtk_tree_store_set (store, &iter, 0, group, -1); - } else { + if (gtk_tree_row_reference_valid (reference)) { GtkTreePath *path; path = gtk_tree_row_reference_get_path (reference); - gtk_tree_model_get_iter (model, &iter, path); - gtk_tree_model_row_changed (model, path, &iter); + gtk_tree_view_expand_to_path (tree_view, path); gtk_tree_path_free (path); } - for (q = sources, position = 0; q != NULL; q = q->next, position++) { - ESource *source = E_SOURCE (q->data); - GtkTreeIter child_iter; - - reference = g_hash_table_lookup ( - rebuild_data->remaining_uids, - e_source_get_uid (source)); - - if (reference == NULL) { - if (selector->priv->select_new) { - select_source (selector, source); - rebuild_data->selection_changed = TRUE; - } - - gtk_tree_store_insert ( - store, &child_iter, &iter, position); - gtk_tree_store_set ( - store, &child_iter, 0, source, -1); - } else { - GtkTreePath *path; - - path = gtk_tree_row_reference_get_path (reference); - gtk_tree_model_get_iter (model, &child_iter, path); - gtk_tree_model_row_changed (model, path, &child_iter); - gtk_tree_path_free (path); - } - } - - g_slist_free (sources); + g_object_unref (source); } - if (rebuild_data->selection_changed) - g_signal_emit (selector, signals[SELECTION_CHANGED], 0); - - source = e_source_selector_ref_primary_selection (selector); - if (set_primary && source == NULL) { - ESourceList *source_list = selector->priv->list; - source = e_source_list_peek_source_any (source_list); - e_source_selector_set_primary_selection (selector, source); - } else if (source != NULL) { - g_object_unref (source); + /* Restore the primary selection. */ + if (selected != NULL) { + e_source_selector_set_primary_selection (selector, selected); + g_object_unref (selected); } - free_rebuild_data (rebuild_data); + /* Make sure we have a primary selection. If not, pick one. */ + selected = e_source_selector_ref_primary_selection (selector); + if (selected == NULL) { + selected = e_source_registry_ref_default_for_extension_name ( + registry, extension_name); + e_source_selector_set_primary_selection (selector, selected); + } + g_object_unref (selected); } -static gboolean -same_source_name_exists (ESourceSelector *selector, - const gchar *name) +static void +source_selector_expand_to_source (ESourceSelector *selector, + ESource *source) { - GtkTreeModel *model; - GtkTreeIter parent_iter, source_iter; - - g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE); - g_return_val_if_fail (name != NULL, FALSE); - - model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); - - if (gtk_tree_model_get_iter_first (model, &parent_iter)) { - do { - if (gtk_tree_model_iter_children (model, &source_iter, &parent_iter)) { - do { - gpointer data; - const gchar *source_name; - - gtk_tree_model_get (model, &source_iter, 0, &data, -1); - g_assert (E_IS_SOURCE (data)); + GHashTable *source_index; + GtkTreeRowReference *reference; + GtkTreePath *path; - source_name = e_source_get_display_name (E_SOURCE (data)); - if (source_name && g_str_equal (name, source_name)) { - g_object_unref (data); + source_index = selector->priv->source_index; + reference = g_hash_table_lookup (source_index, source); - return TRUE; - } + /* If the ESource is not in our tree model then return silently. */ + if (reference == NULL) + return; - g_object_unref (data); - } while (gtk_tree_model_iter_next (model, &source_iter)); - } - } while (gtk_tree_model_iter_next (model, &parent_iter)); - } + /* If we do have a row reference, it should be valid. */ + g_return_if_fail (gtk_tree_row_reference_valid (reference)); - return FALSE; + /* Expand the tree view to the path containing the ESource */ + path = gtk_tree_row_reference_get_path (reference); + gtk_tree_view_expand_to_path (GTK_TREE_VIEW (selector), path); + gtk_tree_path_free (path); } -static gint -on_idle_rebuild_model_callback (ESourceSelector *selector) +static void +source_selector_source_added_cb (ESourceRegistry *registry, + ESource *source, + ESourceSelector *selector) { - rebuild_model (selector); - selector->priv->rebuild_model_idle_id = 0; + source_selector_build_model (selector); - return FALSE; + source_selector_expand_to_source (selector, source); } static void -list_changed_callback (ESourceList *list, - ESourceSelector *selector) +source_selector_source_changed_cb (ESourceRegistry *registry, + ESource *source, + ESourceSelector *selector) { - ESourceSelectorPrivate *priv = selector->priv; + source_selector_cancel_write (selector, source); - if (priv->rebuild_model_idle_id == 0) - priv->rebuild_model_idle_id = g_idle_add ((GSourceFunc) on_idle_rebuild_model_callback, - selector); + source_selector_update_row (selector, source); } -/* Data functions for rendering the model. */ - static void -toggle_cell_data_func (GtkTreeViewColumn *column, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - ESourceSelector *selector) +source_selector_source_removed_cb (ESourceRegistry *registry, + ESource *source, + ESourceSelector *selector) { - gpointer data; - - gtk_tree_model_get (model, iter, 0, &data, -1); - if (data == NULL) { - g_object_set (renderer, "visible", FALSE, NULL); - return; - } - - if (E_IS_SOURCE_GROUP (data)) { - g_object_set (renderer, "visible", FALSE, NULL); - } else { - g_assert (E_IS_SOURCE (data)); - - g_object_set (renderer, "visible", selector->priv->checkboxes_shown, NULL); - if (source_is_selected (selector, E_SOURCE (data))) - g_object_set (renderer, "active", TRUE, NULL); - else - g_object_set (renderer, "active", FALSE, NULL); - } - - g_object_unref (data); + source_selector_build_model (selector); } static void -text_cell_data_func (GtkTreeViewColumn *column, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - ESourceSelector *selector) +source_selector_source_enabled_cb (ESourceRegistry *registry, + ESource *source, + ESourceSelector *selector) { - gpointer data; - - gtk_tree_model_get (model, iter, 0, &data, -1); - if (data == NULL) { - g_object_set (renderer, "visible", FALSE, NULL); - return; - } - - if (E_IS_SOURCE_GROUP (data)) { - g_object_set (renderer, - "text", e_source_group_peek_name (E_SOURCE_GROUP (data)), - "weight", PANGO_WEIGHT_BOLD, - "foreground_set", FALSE, - NULL); - } else { - ESource *source; - - g_assert (E_IS_SOURCE (data)); - source = E_SOURCE (data); - - g_object_set (renderer, - "text", e_source_get_display_name (source), - "weight", PANGO_WEIGHT_NORMAL, - "foreground_set", FALSE, - NULL); - } + source_selector_build_model (selector); - g_object_unref (data); + source_selector_expand_to_source (selector, source); } static void -pixbuf_cell_data_func (GtkTreeViewColumn *column, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - ESourceSelector *selector) +source_selector_source_disabled_cb (ESourceRegistry *registry, + ESource *source, + ESourceSelector *selector) { - gpointer data; - - gtk_tree_model_get (model, iter, 0, &data, -1); - g_return_if_fail (G_IS_OBJECT (data)); - - if (E_IS_SOURCE_GROUP (data)) { - g_object_set (renderer, "visible", FALSE, NULL); - - } else if (E_IS_SOURCE (data)) { - ESource *source; - GdkPixbuf *pixbuf = NULL; - const gchar *color_spec; - GdkColor color; - - source = E_SOURCE (data); - color_spec = e_source_peek_color_spec (source); - if (color_spec != NULL && gdk_color_parse (color_spec, &color)) { - guint32 rgba; - - pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 16, 16); + source_selector_build_model (selector); +} - rgba = (((color.red & 0xff00) << 8) | - ((color.green & 0xff00)) | - ((color.blue & 0xff00) >> 8)) << 8; +static gboolean +same_source_name_exists (ESourceSelector *selector, + const gchar *display_name) +{ + GHashTable *source_index; + GHashTableIter iter; + gpointer key; - gdk_pixbuf_fill (pixbuf, rgba); - } + source_index = selector->priv->source_index; + g_hash_table_iter_init (&iter, source_index); - g_object_set (renderer, - "visible", pixbuf != NULL, - "pixbuf", pixbuf, - NULL); + while (g_hash_table_iter_next (&iter, &key, NULL)) { + ESource *source = E_SOURCE (key); + const gchar *source_name; - if (pixbuf) - g_object_unref (pixbuf); + source_name = e_source_get_display_name (source); + if (g_strcmp0 (display_name, source_name) == 0) + return TRUE; } - g_object_unref (data); + return FALSE; } -/* Custom selection function to make groups non selectable. */ static gboolean selection_func (GtkTreeSelection *selection, GtkTreeModel *model, @@ -653,12 +555,12 @@ selection_func (GtkTreeSelection *selection, gboolean path_currently_selected, ESourceSelector *selector) { + ESource *source; GtkTreeIter iter; - gpointer data; + const gchar *extension_name; if (selector->priv->toggled_last) { selector->priv->toggled_last = FALSE; - return FALSE; } @@ -668,21 +570,21 @@ selection_func (GtkTreeSelection *selection, if (!gtk_tree_model_get_iter (model, &iter, path)) return FALSE; - gtk_tree_model_get (model, &iter, 0, &data, -1); - if (E_IS_SOURCE_GROUP (data)) { - g_object_unref (data); + extension_name = e_source_selector_get_extension_name (selector); + gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1); + if (!e_source_has_extension (source, extension_name)) { + g_object_unref (source); return FALSE; } clear_saved_primary_selection (selector); - g_object_unref (data); + + g_object_unref (source); return TRUE; } -/* Callbacks. */ - static void text_cell_edited_cb (ESourceSelector *selector, const gchar *path_string, @@ -699,18 +601,18 @@ text_cell_edited_cb (ESourceSelector *selector, path = gtk_tree_path_new_from_string (path_string); gtk_tree_model_get_iter (model, &iter, path); - gtk_tree_model_get (model, &iter, 0, &source, -1); + gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1); gtk_tree_path_free (path); - g_return_if_fail (E_IS_SOURCE (source)); - if (new_name == NULL || *new_name == '\0') return; if (same_source_name_exists (selector, new_name)) return; - e_source_set_name (source, new_name); + e_source_set_display_name (source, new_name); + + e_source_selector_queue_write (selector, source); } static void @@ -718,11 +620,10 @@ cell_toggled_callback (GtkCellRendererToggle *renderer, const gchar *path_string, ESourceSelector *selector) { + ESource *source; GtkTreeModel *model; GtkTreePath *path; GtkTreeIter iter; - ESource *source; - gpointer data; model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); path = gtk_tree_path_new_from_string (path_string); @@ -732,24 +633,18 @@ cell_toggled_callback (GtkCellRendererToggle *renderer, return; } - gtk_tree_model_get (model, &iter, 0, &data, -1); - if (!E_IS_SOURCE_GROUP (data)) { - source = E_SOURCE (data); - - if (source_is_selected (selector, source)) - unselect_source (selector, source); - else - select_source (selector, source); + gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1); - selector->priv->toggled_last = TRUE; + if (e_source_selector_source_is_selected (selector, source)) + e_source_selector_unselect_source (selector, source); + else + e_source_selector_select_source (selector, source); - gtk_tree_model_row_changed (model, path, &iter); - g_signal_emit (selector, signals[SELECTION_CHANGED], 0); - } + selector->priv->toggled_last = TRUE; gtk_tree_path_free (path); - g_object_unref (data); + g_object_unref (source); } static void @@ -760,52 +655,24 @@ selection_changed_callback (GtkTreeSelection *selection, g_object_notify (G_OBJECT (selector), "primary-selection"); } -static gboolean -group_search_function (GtkTreeModel *model, - gint column, - const gchar *key, - GtkTreeIter *iter, - gpointer dummy) +static void +source_selector_set_extension_name (ESourceSelector *selector, + const gchar *extension_name) { - gpointer data; - const gchar *name = NULL; - gboolean status = TRUE; + g_return_if_fail (extension_name != NULL); + g_return_if_fail (selector->priv->extension_name == NULL); - gtk_tree_model_get (model, iter, 0, &data, -1); - - if (E_IS_SOURCE_GROUP (data)) - name = e_source_group_peek_name (E_SOURCE_GROUP (data)); - else { - g_assert (E_IS_SOURCE (data)); - - name = e_source_get_display_name (E_SOURCE (data)); - } - - if (name) - status = g_ascii_strncasecmp (name, key, strlen (key)) != 0; - - g_object_unref (data); - - return status; + selector->priv->extension_name = g_strdup (extension_name); } static void -source_selector_set_source_list (ESourceSelector *selector, - ESourceList *source_list) +source_selector_set_registry (ESourceSelector *selector, + ESourceRegistry *registry) { - g_return_if_fail (E_IS_SOURCE_LIST (source_list)); - g_return_if_fail (selector->priv->list == NULL); - - selector->priv->list = g_object_ref (source_list); - - rebuild_model (selector); + g_return_if_fail (E_IS_SOURCE_REGISTRY (registry)); + g_return_if_fail (selector->priv->registry == NULL); - g_signal_connect_object ( - source_list, "changed", - G_CALLBACK (list_changed_callback), - G_OBJECT (selector), 0); - - gtk_tree_view_expand_all (GTK_TREE_VIEW (selector)); + selector->priv->registry = g_object_ref (registry); } static void @@ -815,17 +682,35 @@ source_selector_set_property (GObject *object, GParamSpec *pspec) { switch (property_id) { + case PROP_EXTENSION_NAME: + source_selector_set_extension_name ( + E_SOURCE_SELECTOR (object), + g_value_get_string (value)); + return; + case PROP_PRIMARY_SELECTION: e_source_selector_set_primary_selection ( E_SOURCE_SELECTOR (object), g_value_get_object (value)); return; - case PROP_SOURCE_LIST: - source_selector_set_source_list ( + case PROP_REGISTRY: + source_selector_set_registry ( E_SOURCE_SELECTOR (object), g_value_get_object (value)); return; + + case PROP_SHOW_COLORS: + e_source_selector_set_show_colors ( + E_SOURCE_SELECTOR (object), + g_value_get_boolean (value)); + return; + + case PROP_SHOW_TOGGLES: + e_source_selector_set_show_toggles ( + E_SOURCE_SELECTOR (object), + g_value_get_boolean (value)); + return; } G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -838,6 +723,13 @@ source_selector_get_property (GObject *object, GParamSpec *pspec) { switch (property_id) { + case PROP_EXTENSION_NAME: + g_value_set_string ( + value, + e_source_selector_get_extension_name ( + E_SOURCE_SELECTOR (object))); + return; + case PROP_PRIMARY_SELECTION: g_value_take_object ( value, @@ -845,10 +737,24 @@ source_selector_get_property (GObject *object, E_SOURCE_SELECTOR (object))); return; - case PROP_SOURCE_LIST: + case PROP_REGISTRY: g_value_set_object ( value, - e_source_selector_get_source_list ( + e_source_selector_get_registry ( + E_SOURCE_SELECTOR (object))); + return; + + case PROP_SHOW_COLORS: + g_value_set_boolean ( + value, + e_source_selector_get_show_colors ( + E_SOURCE_SELECTOR (object))); + return; + + case PROP_SHOW_TOGGLES: + g_value_set_boolean ( + value, + e_source_selector_get_show_toggles ( E_SOURCE_SELECTOR (object))); return; } @@ -863,17 +769,17 @@ source_selector_dispose (GObject *object) priv = E_SOURCE_SELECTOR_GET_PRIVATE (object); - g_hash_table_remove_all (priv->selected_sources); - - if (priv->rebuild_model_idle_id != 0) { - g_source_remove (priv->rebuild_model_idle_id); - priv->rebuild_model_idle_id = 0; + if (priv->registry != NULL) { + g_signal_handlers_disconnect_matched ( + priv->registry, + G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, object); + g_object_unref (priv->registry); + priv->registry = NULL; } - if (priv->list != NULL) { - g_object_unref (priv->list); - priv->list = NULL; - } + g_hash_table_remove_all (priv->source_index); + g_hash_table_remove_all (priv->pending_writes); clear_saved_primary_selection (E_SOURCE_SELECTOR (object)); @@ -888,12 +794,52 @@ source_selector_finalize (GObject *object) priv = E_SOURCE_SELECTOR_GET_PRIVATE (object); - g_hash_table_destroy (priv->selected_sources); + g_hash_table_destroy (priv->source_index); + g_hash_table_destroy (priv->pending_writes); + + g_free (priv->extension_name); + + if (priv->main_context != NULL) + g_main_context_unref (priv->main_context); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (e_source_selector_parent_class)->finalize (object); } +static void +source_selector_constructed (GObject *object) +{ + ESourceRegistry *registry; + ESourceSelector *selector; + + selector = E_SOURCE_SELECTOR (object); + registry = e_source_selector_get_registry (selector); + + g_signal_connect ( + registry, "source-added", + G_CALLBACK (source_selector_source_added_cb), selector); + + g_signal_connect ( + registry, "source-changed", + G_CALLBACK (source_selector_source_changed_cb), selector); + + g_signal_connect ( + registry, "source-removed", + G_CALLBACK (source_selector_source_removed_cb), selector); + + g_signal_connect ( + registry, "source-enabled", + G_CALLBACK (source_selector_source_enabled_cb), selector); + + g_signal_connect ( + registry, "source-disabled", + G_CALLBACK (source_selector_source_disabled_cb), selector); + + source_selector_build_model (selector); + + gtk_tree_view_expand_all (GTK_TREE_VIEW (selector)); +} + static gboolean source_selector_button_press_event (GtkWidget *widget, GdkEventButton *event) @@ -929,25 +875,11 @@ source_selector_button_press_event (GtkWidget *widget, if (row_exists) { GtkTreeModel *model; GtkTreeIter iter; - gpointer data; model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget)); - if (gtk_tree_model_get_iter (model, &iter, path)) { - gtk_tree_model_get (model, &iter, 0, &data, -1); - - /* Do not emit popup since we will - * not be able to get the ESource. */ - if (E_IS_SOURCE_GROUP (data)) { - selector->priv->primary_source_group = - g_object_ref (data); - /* Data shuld be unreffed after - * creating the new source. */ - goto chainup; - } - - source = E_SOURCE (data); - } + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1); } if (source == NULL) @@ -1000,13 +932,13 @@ source_selector_drag_motion (GtkWidget *widget, gint y, guint time_) { + ESource *source = NULL; GtkTreeView *tree_view; GtkTreeModel *model; GtkTreePath *path = NULL; GtkTreeIter iter; GtkTreeViewDropPosition pos; GdkDragAction action = 0; - gpointer object = NULL; tree_view = GTK_TREE_VIEW (widget); model = gtk_tree_view_get_model (tree_view); @@ -1017,9 +949,9 @@ source_selector_drag_motion (GtkWidget *widget, if (!gtk_tree_model_get_iter (model, &iter, path)) goto exit; - gtk_tree_model_get (model, &iter, 0, &object, -1); + gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1); - if (E_IS_SOURCE_GROUP (object) || e_source_get_readonly (object)) + if (!e_source_get_writable (source)) goto exit; pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE; @@ -1034,8 +966,8 @@ exit: if (path != NULL) gtk_tree_path_free (path); - if (object != NULL) - g_object_unref (object); + if (source != NULL) + g_object_unref (source); gdk_drag_status (context, action, time_); @@ -1049,13 +981,15 @@ source_selector_drag_drop (GtkWidget *widget, gint y, guint time_) { + ESource *source; + ESourceSelector *selector; GtkTreeView *tree_view; GtkTreeModel *model; GtkTreePath *path; GtkTreeIter iter; + const gchar *extension_name; gboolean drop_zone; gboolean valid; - gpointer object; tree_view = GTK_TREE_VIEW (widget); model = gtk_tree_view_get_model (tree_view); @@ -1068,9 +1002,13 @@ source_selector_drag_drop (GtkWidget *widget, gtk_tree_path_free (path); g_return_val_if_fail (valid, FALSE); - gtk_tree_model_get (model, &iter, 0, &object, -1); - drop_zone = E_IS_SOURCE (object); - g_object_unref (object); + gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1); + + selector = E_SOURCE_SELECTOR (widget); + extension_name = e_source_selector_get_extension_name (selector); + drop_zone = e_source_has_extension (source, extension_name); + + g_object_unref (source); return drop_zone; } @@ -1084,17 +1022,20 @@ source_selector_drag_data_received (GtkWidget *widget, guint info, guint time_) { + ESource *source = NULL; GtkTreeView *tree_view; GtkTreeModel *model; GtkTreePath *path = NULL; GtkTreeIter iter; - gpointer object = NULL; + GdkDragAction action; gboolean delete; gboolean success = FALSE; tree_view = GTK_TREE_VIEW (widget); model = gtk_tree_view_get_model (tree_view); - delete = (gdk_drag_context_get_selected_action (context) == GDK_ACTION_MOVE); + + action = gdk_drag_context_get_selected_action (context); + delete = (action == GDK_ACTION_MOVE); if (!gtk_tree_view_get_dest_row_at_pos (tree_view, x, y, &path, NULL)) goto exit; @@ -1102,22 +1043,22 @@ source_selector_drag_data_received (GtkWidget *widget, if (!gtk_tree_model_get_iter (model, &iter, path)) goto exit; - gtk_tree_model_get (model, &iter, 0, &object, -1); + gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1); - if (!E_IS_SOURCE (object) || e_source_get_readonly (object)) + if (!e_source_get_writable (source)) goto exit; g_signal_emit ( widget, signals[DATA_DROPPED], 0, selection_data, - object, gdk_drag_context_get_selected_action (context), + source, gdk_drag_context_get_selected_action (context), info, &success); exit: if (path != NULL) gtk_tree_path_free (path); - if (object != NULL) - g_object_unref (object); + if (source != NULL) + g_object_unref (source); gtk_drag_finish (context, success, delete, time_); } @@ -1208,18 +1149,63 @@ source_selector_row_expanded (GtkTreeView *tree_view, gtk_tree_path_free (child_path); } -/* Initialization. */ +static gboolean +source_selector_get_source_selected (ESourceSelector *selector, + ESource *source) +{ + ESourceSelectable *extension; + const gchar *extension_name; + gboolean selected = TRUE; + + extension_name = e_source_selector_get_extension_name (selector); + + if (!e_source_has_extension (source, extension_name)) + return FALSE; + + extension = e_source_get_extension (source, extension_name); + + if (E_IS_SOURCE_SELECTABLE (extension)) + selected = e_source_selectable_get_selected (extension); + + return selected; +} + +static void +source_selector_set_source_selected (ESourceSelector *selector, + ESource *source, + gboolean selected) +{ + ESourceSelectable *extension; + const gchar *extension_name; + + extension_name = e_source_selector_get_extension_name (selector); + + if (!e_source_has_extension (source, extension_name)) + return; + + extension = e_source_get_extension (source, extension_name); + + if (!E_IS_SOURCE_SELECTABLE (extension)) + return; + + if (selected != e_source_selectable_get_selected (extension)) { + e_source_selectable_set_selected (extension, selected); + e_source_selector_queue_write (selector, source); + } +} + static gboolean ess_bool_accumulator (GSignalInvocationHint *ihint, GValue *out, const GValue *in, gpointer data) { - gboolean val = g_value_get_boolean (in); + gboolean v_boolean; - g_value_set_boolean (out, val); + v_boolean = g_value_get_boolean (in); + g_value_set_boolean (out, v_boolean); - return !val; + return !v_boolean; } static void @@ -1236,6 +1222,7 @@ e_source_selector_class_init (ESourceSelectorClass *class) object_class->get_property = source_selector_get_property; object_class->dispose = source_selector_dispose; object_class->finalize = source_selector_finalize; + object_class->constructed = source_selector_constructed; widget_class = GTK_WIDGET_CLASS (class); widget_class->button_press_event = source_selector_button_press_event; @@ -1249,6 +1236,21 @@ e_source_selector_class_init (ESourceSelectorClass *class) tree_view_class->test_collapse_row = source_selector_test_collapse_row; tree_view_class->row_expanded = source_selector_row_expanded; + class->get_source_selected = source_selector_get_source_selected; + class->set_source_selected = source_selector_set_source_selected; + + g_object_class_install_property ( + object_class, + PROP_EXTENSION_NAME, + g_param_spec_string ( + "extension-name", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property ( object_class, PROP_PRIMARY_SELECTION, @@ -1262,16 +1264,38 @@ e_source_selector_class_init (ESourceSelectorClass *class) g_object_class_install_property ( object_class, - PROP_SOURCE_LIST, + PROP_REGISTRY, g_param_spec_object ( - "source-list", + "registry", NULL, NULL, - E_TYPE_SOURCE_LIST, + E_TYPE_SOURCE_REGISTRY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property ( + object_class, + PROP_SHOW_COLORS, + g_param_spec_boolean ( + "show-colors", + NULL, + NULL, + TRUE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_SHOW_TOGGLES, + g_param_spec_boolean ( + "show-toggles", + NULL, + NULL, + TRUE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + signals[SELECTION_CHANGED] = g_signal_new ( "selection-changed", G_OBJECT_CLASS_TYPE (object_class), @@ -1319,64 +1343,91 @@ e_source_selector_class_init (ESourceSelectorClass *class) static void e_source_selector_init (ESourceSelector *selector) { + GHashTable *pending_writes; GtkTreeViewColumn *column; - GtkCellRenderer *cell_renderer; GtkTreeSelection *selection; + GtkCellRenderer *renderer; GtkTreeStore *tree_store; GtkTreeView *tree_view; + pending_writes = g_hash_table_new_full ( + (GHashFunc) g_direct_hash, + (GEqualFunc) g_direct_equal, + (GDestroyNotify) g_object_unref, + (GDestroyNotify) pending_writes_destroy_source); + selector->priv = E_SOURCE_SELECTOR_GET_PRIVATE (selector); + selector->priv->pending_writes = pending_writes; + + selector->priv->main_context = g_main_context_get_thread_default (); + if (selector->priv->main_context != NULL) + g_main_context_ref (selector->priv->main_context); + tree_view = GTK_TREE_VIEW (selector); - gtk_tree_view_set_search_column (tree_view, 0); - gtk_tree_view_set_search_equal_func (tree_view, group_search_function, NULL, NULL); + gtk_tree_view_set_search_column (tree_view, COLUMN_SOURCE); gtk_tree_view_set_enable_search (tree_view, TRUE); selector->priv->toggled_last = FALSE; - selector->priv->checkboxes_shown = TRUE; selector->priv->select_new = FALSE; + selector->priv->show_colors = TRUE; + selector->priv->show_toggles = TRUE; - selector->priv->selected_sources = g_hash_table_new_full ( - g_direct_hash, g_direct_equal, + selector->priv->source_index = g_hash_table_new_full ( + (GHashFunc) e_source_hash, + (GEqualFunc) e_source_equal, (GDestroyNotify) g_object_unref, - (GDestroyNotify) NULL); + (GDestroyNotify) gtk_tree_row_reference_free); + + tree_store = gtk_tree_store_new ( + NUM_COLUMNS, + G_TYPE_STRING, /* COLUMN_NAME */ + GDK_TYPE_COLOR, /* COLUMN_COLOR */ + G_TYPE_BOOLEAN, /* COLUMN_ACTIVE */ + G_TYPE_BOOLEAN, /* COLUMN_SHOW_COLOR */ + G_TYPE_BOOLEAN, /* COLUMN_SHOW_TOGGLE */ + G_TYPE_INT, /* COLUMN_WEIGHT */ + E_TYPE_SOURCE); /* COLUMN_SOURCE */ - tree_store = gtk_tree_store_new (1, G_TYPE_OBJECT); gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (tree_store)); column = gtk_tree_view_column_new (); gtk_tree_view_append_column (tree_view, column); - cell_renderer = gtk_cell_renderer_pixbuf_new (); + renderer = e_cell_renderer_color_new (); g_object_set ( - G_OBJECT (cell_renderer), - "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL); - gtk_tree_view_column_pack_start (column, cell_renderer, FALSE); - gtk_tree_view_column_set_cell_data_func ( - column, cell_renderer, (GtkTreeCellDataFunc) - pixbuf_cell_data_func, selector, NULL); - - cell_renderer = e_cell_renderer_safe_toggle_new (); - gtk_tree_view_column_pack_start (column, cell_renderer, FALSE); - gtk_tree_view_column_set_cell_data_func ( - column, cell_renderer, (GtkTreeCellDataFunc) - toggle_cell_data_func, selector, NULL); + G_OBJECT (renderer), "mode", + GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_add_attribute ( + column, renderer, "color", COLUMN_COLOR); + gtk_tree_view_column_add_attribute ( + column, renderer, "visible", COLUMN_SHOW_COLOR); + + renderer = e_cell_renderer_safe_toggle_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_add_attribute ( + column, renderer, "active", COLUMN_ACTIVE); + gtk_tree_view_column_add_attribute ( + column, renderer, "visible", COLUMN_SHOW_TOGGLE); g_signal_connect ( - cell_renderer, "toggled", + renderer, "toggled", G_CALLBACK (cell_toggled_callback), selector); - cell_renderer = gtk_cell_renderer_text_new (); + renderer = gtk_cell_renderer_text_new (); g_object_set ( - G_OBJECT (cell_renderer), + G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL); g_signal_connect_swapped ( - cell_renderer, "edited", + renderer, "edited", G_CALLBACK (text_cell_edited_cb), selector); - gtk_tree_view_column_pack_start (column, cell_renderer, TRUE); - gtk_tree_view_column_set_cell_data_func ( - column, cell_renderer, (GtkTreeCellDataFunc) - text_cell_data_func, selector, NULL); + gtk_tree_view_column_pack_start (column, renderer, TRUE); + gtk_tree_view_column_set_attributes ( + column, renderer, + "text", COLUMN_NAME, + "weight", COLUMN_WEIGHT, + NULL); selection = gtk_tree_view_get_selection (tree_view); gtk_tree_selection_set_select_function ( @@ -1392,154 +1443,216 @@ e_source_selector_init (ESourceSelector *selector) /** * e_source_selector_new: - * @list: A source list. + * @registry: an #ESourceRegistry + * @extension_name: the name of an #ESource extension * - * Create a new view for @list. The view will update automatically when @list - * changes. + * Displays a list of sources from @registry having an extension named + * @extension_name. The sources are grouped by backend or groupware + * account, which are described by the parent source. * - * Returns: The newly created widget. + * Returns: a new #ESourceSelector **/ GtkWidget * -e_source_selector_new (ESourceList *list) +e_source_selector_new (ESourceRegistry *registry, + const gchar *extension_name) { - g_return_val_if_fail (E_IS_SOURCE_LIST (list), NULL); + g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); + g_return_val_if_fail (extension_name != NULL, NULL); return g_object_new ( - E_TYPE_SOURCE_SELECTOR, "source-list", list, NULL); + E_TYPE_SOURCE_SELECTOR, "registry", registry, + "extension-name", extension_name, NULL); } /** - * e_source_selector_get_source_list: + * e_source_selector_get_registry: * @selector: an #ESourceSelector * - * Returns the #ESourceList that @selector is rendering. + * Returns the #ESourceRegistry that @selector is getting sources from. * - * Returns: an #ESourceList + * Returns: an #ESourceRegistry * - * Since: 2.24 + * Since: 3.6 **/ -ESourceList * -e_source_selector_get_source_list (ESourceSelector *selector) +ESourceRegistry * +e_source_selector_get_registry (ESourceSelector *selector) { g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL); - return selector->priv->list; + return selector->priv->registry; } /** - * e_source_selector_get_selection: + * e_source_selector_get_extension_name: * @selector: an #ESourceSelector * - * Get the list of selected sources, i.e. those that were enabled through the - * corresponding checkboxes in the tree. + * Returns the extension name used to filter which sources are displayed. * - * Returns: A list of the ESources currently selected. The sources will - * be in the same order as they appear on the screen, and the list should be - * freed using e_source_selector_free_selection(). + * Returns: the #ESource extension name + * + * Since: 3.6 **/ -GSList * -e_source_selector_get_selection (ESourceSelector *selector) +const gchar * +e_source_selector_get_extension_name (ESourceSelector *selector) { - GSList *selection_list; - GSList *groups; - GSList *p; - g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL); - selection_list = NULL; - - groups = e_source_list_peek_groups (selector->priv->list); - for (p = groups; p != NULL; p = p->next) { - ESourceGroup *group = E_SOURCE_GROUP (p->data); - GSList *sources; - GSList *q; - - sources = e_source_group_peek_sources (group); - for (q = sources; q != NULL; q = q->next) { - ESource *source = E_SOURCE (q->data); + return selector->priv->extension_name; +} - if (source_is_selected (selector, source)) { - selection_list = g_slist_prepend (selection_list, source); - g_object_ref (source); - } - } - } +/** + * e_source_selector_get_show_colors: + * @selector: an #ESourceSelector + * + * Returns whether colors are shown next to data sources. + * + * Returns: %TRUE if colors are being shown + * + * Since: 3.6 + **/ +gboolean +e_source_selector_get_show_colors (ESourceSelector *selector) +{ + g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE); - return g_slist_reverse (selection_list); + return selector->priv->show_colors; } /** - * e_source_selector_get_primary_source_group: + * e_source_selector_set_show_colors: * @selector: an #ESourceSelector + * @show_colors: whether to show colors * - * Gets the primary source group associated with the selector. + * Sets whether to show colors next to data sources. * - * Returns: primary_source_group if selector is valid, NULL otherwise. + * Since: 3.6 **/ -ESourceGroup * -e_source_selector_get_primary_source_group (ESourceSelector *selector) +void +e_source_selector_set_show_colors (ESourceSelector *selector, + gboolean show_colors) { - g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL); + g_return_if_fail (E_IS_SOURCE_SELECTOR (selector)); - return selector->priv->primary_source_group; + if (show_colors == selector->priv->show_colors) + return; + + selector->priv->show_colors = show_colors; + g_object_notify (G_OBJECT (selector), "show-colors"); + + source_selector_build_model (selector); } /** - * e_source_list_free_selection: - * @list: A selection list returned by e_source_selector_get_selection(). + * e_source_selector_get_show_toggles: + * @selector: an #ESourceSelector * - * Free the selection list. + * Returns whether toggles are shown next to data sources. + * + * Returns: %TRUE if toggles are being shown + * + * Since: 3.6 **/ -void -e_source_selector_free_selection (GSList *list) +gboolean +e_source_selector_get_show_toggles (ESourceSelector *selector) { - g_slist_foreach (list, (GFunc) g_object_unref, NULL); - g_slist_free (list); + g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE); + + return selector->priv->show_toggles; } /** - * e_source_selector_show_selection: - * @selector: An ESourceSelector widget + * e_source_selector_set_show_toggles: + * @selector: an #ESourceSelector + * @show_toggles: whether to show toggles * - * Specify whether the checkboxes in the ESourceSelector should be shown or - * not. + * Sets whether to show toggles next to data sources. + * + * Since: 3.6 **/ void -e_source_selector_show_selection (ESourceSelector *selector, - gboolean show) +e_source_selector_set_show_toggles (ESourceSelector *selector, + gboolean show_toggles) { - GtkTreeModel *model; - g_return_if_fail (E_IS_SOURCE_SELECTOR (selector)); - show = !! show; - if (show == selector->priv->checkboxes_shown) + if (show_toggles == selector->priv->show_toggles) return; - selector->priv->checkboxes_shown = show; + selector->priv->show_toggles = show_toggles; - model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); + g_object_notify (G_OBJECT (selector), "show-toggles"); - gtk_tree_model_foreach ( - model, (GtkTreeModelForeachFunc) - gtk_tree_model_row_changed, NULL); + source_selector_build_model (selector); +} + +/* Helper for e_source_selector_get_selection() */ +static gboolean +source_selector_check_selected (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + ESource *source; + + struct { + ESourceSelector *selector; + GSList *list; + } *closure = user_data; + + gtk_tree_model_get (model, iter, COLUMN_SOURCE, &source, -1); + + if (e_source_selector_source_is_selected (closure->selector, source)) + closure->list = g_slist_prepend (closure->list, source); + else + g_object_unref (source); + + return FALSE; } /** - * e_source_selector_selection_shown: + * e_source_selector_get_selection: * @selector: an #ESourceSelector * - * Check whether the checkboxes in the ESourceSelector are being shown or not. + * Get the list of selected sources, i.e. those that were enabled through the + * corresponding checkboxes in the tree. * - * Returns: %TRUE if the checkboxes are shown, %FALSE otherwise. + * Returns: A list of the ESources currently selected. The sources will + * be in the same order as they appear on the screen, and the list should be + * freed using e_source_selector_free_selection(). **/ -gboolean -e_source_selector_selection_shown (ESourceSelector *selector) +GSList * +e_source_selector_get_selection (ESourceSelector *selector) { - g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE); + struct { + ESourceSelector *selector; + GSList *list; + } closure; + + g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL); + + closure.selector = selector; + closure.list = NULL; + + gtk_tree_model_foreach ( + gtk_tree_view_get_model (GTK_TREE_VIEW (selector)), + (GtkTreeModelForeachFunc) source_selector_check_selected, + &closure); - return selector->priv->checkboxes_shown; + return g_slist_reverse (closure.list); +} + +/** + * e_source_list_free_selection: + * @list: A selection list returned by e_source_selector_get_selection(). + * + * Free the selection list. + **/ +void +e_source_selector_free_selection (GSList *list) +{ + g_slist_foreach (list, (GFunc) g_object_unref, NULL); + g_slist_free (list); } /** @@ -1569,31 +1682,24 @@ void e_source_selector_select_source (ESourceSelector *selector, ESource *source) { - GtkTreeIter parent_iter, source_iter; + ESourceSelectorClass *class; + GtkTreeRowReference *reference; + GHashTable *source_index; g_return_if_fail (E_IS_SOURCE_SELECTOR (selector)); g_return_if_fail (E_IS_SOURCE (source)); - source = find_source (selector, source); - g_return_if_fail (source != NULL); - - if (source_is_selected (selector, source)) - return; - - select_source (selector, source); - - if (find_source_iter (selector, source, &parent_iter, &source_iter)) { - GtkTreeModel *model; - GtkTreePath *path; + /* Make sure the ESource is in our tree model. */ + source_index = selector->priv->source_index; + reference = g_hash_table_lookup (source_index, source); + g_return_if_fail (gtk_tree_row_reference_valid (reference)); - model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); + class = E_SOURCE_SELECTOR_GET_CLASS (selector); + g_return_if_fail (class->set_source_selected != NULL); - path = gtk_tree_model_get_path (model, &source_iter); - gtk_tree_model_row_changed (model, path, &source_iter); - gtk_tree_path_free (path); + class->set_source_selected (selector, source, TRUE); - g_signal_emit (selector, signals[SELECTION_CHANGED], 0); - } + g_signal_emit (selector, signals[SELECTION_CHANGED], 0); } /** @@ -1607,62 +1713,24 @@ void e_source_selector_unselect_source (ESourceSelector *selector, ESource *source) { - GtkTreeIter parent_iter, source_iter; + ESourceSelectorClass *class; + GtkTreeRowReference *reference; + GHashTable *source_index; g_return_if_fail (E_IS_SOURCE_SELECTOR (selector)); g_return_if_fail (E_IS_SOURCE (source)); - source = find_source (selector, source); - g_return_if_fail (source != NULL); - - if (!source_is_selected (selector, source)) - return; - - unselect_source (selector, source); + /* Make sure the ESource is in our tree model. */ + source_index = selector->priv->source_index; + reference = g_hash_table_lookup (source_index, source); + g_return_if_fail (gtk_tree_row_reference_valid (reference)); - if (find_source_iter (selector, source, &parent_iter, &source_iter)) { - GtkTreeModel *model; - GtkTreePath *path; - - model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); - - path = gtk_tree_model_get_path (model, &source_iter); - gtk_tree_model_row_changed (model, path, &source_iter); - gtk_tree_path_free (path); - - g_signal_emit (selector, signals[SELECTION_CHANGED], 0); - } -} - -/* Helper for e_source_selector_select_exclusive() */ -static gboolean -source_selector_select_exclusive_foreach (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer user_data) -{ - ESource *source; + class = E_SOURCE_SELECTOR_GET_CLASS (selector); + g_return_if_fail (class->set_source_selected != NULL); - struct { - ESourceSelector *selector; - ESource *source_to_select; - } *data = user_data; + class->set_source_selected (selector, source, FALSE); - if (gtk_tree_path_get_depth (path) != 2) - return FALSE; - - gtk_tree_model_get (model, iter, 0, &source, -1); - - if (source == data->source_to_select) - select_source (data->selector, source); - else - unselect_source (data->selector, source); - - gtk_tree_model_row_changed (model, path, iter); - - g_object_unref (source); - - return FALSE; + g_signal_emit (selector, signals[SELECTION_CHANGED], 0); } /** @@ -1678,27 +1746,24 @@ void e_source_selector_select_exclusive (ESourceSelector *selector, ESource *source) { - GtkTreeModel *model; - - struct { - ESourceSelector *selector; - ESource *source_to_select; - } data; + ESourceSelectorClass *class; + GHashTable *source_index; + GHashTableIter iter; + gpointer key; g_return_if_fail (E_IS_SOURCE_SELECTOR (selector)); g_return_if_fail (E_IS_SOURCE (source)); - source = find_source (selector, source); - g_return_if_fail (source != NULL); - - model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); + class = E_SOURCE_SELECTOR_GET_CLASS (selector); + g_return_if_fail (class->set_source_selected != NULL); - data.selector = selector; - data.source_to_select = source; + source_index = selector->priv->source_index; + g_hash_table_iter_init (&iter, source_index); - gtk_tree_model_foreach ( - model, (GtkTreeModelForeachFunc) - source_selector_select_exclusive_foreach, &data); + while (g_hash_table_iter_next (&iter, &key, NULL)) { + gboolean selected = e_source_equal (key, source); + class->set_source_selected (selector, source, selected); + } g_signal_emit (selector, signals[SELECTION_CHANGED], 0); } @@ -1716,13 +1781,22 @@ gboolean e_source_selector_source_is_selected (ESourceSelector *selector, ESource *source) { + ESourceSelectorClass *class; + GtkTreeRowReference *reference; + GHashTable *source_index; + g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE); g_return_val_if_fail (E_IS_SOURCE (source), FALSE); - source = find_source (selector, source); - g_return_val_if_fail (source != NULL, FALSE); + /* Make sure the ESource is in our tree model. */ + source_index = selector->priv->source_index; + reference = g_hash_table_lookup (source_index, source); + g_return_val_if_fail (gtk_tree_row_reference_valid (reference), FALSE); + + class = E_SOURCE_SELECTOR_GET_CLASS (selector); + g_return_val_if_fail (class->get_source_selected != NULL, FALSE); - return source_is_selected (selector, source); + return class->get_source_selected (selector, source); } /** @@ -1805,13 +1879,14 @@ e_source_selector_edit_primary_selection (ESourceSelector *selector) ESource * e_source_selector_ref_primary_selection (ESourceSelector *selector) { + ESource *source; GtkTreeRowReference *reference; GtkTreeSelection *selection; GtkTreeView *tree_view; GtkTreeModel *model; GtkTreeIter iter; + const gchar *extension_name; gboolean have_iter = FALSE; - gpointer data = NULL; g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL); @@ -1836,17 +1911,16 @@ e_source_selector_ref_primary_selection (ESourceSelector *selector) if (!have_iter) return NULL; - gtk_tree_model_get (model, &iter, 0, &data, -1); + gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1); - if (data == NULL) - return NULL; + extension_name = e_source_selector_get_extension_name (selector); - if (!E_IS_SOURCE (data)) { - g_object_unref (data); + if (!e_source_has_extension (source, extension_name)) { + g_object_unref (source); return NULL; } - return E_SOURCE (data); + return source; } /** @@ -1860,64 +1934,66 @@ void e_source_selector_set_primary_selection (ESourceSelector *selector, ESource *source) { - ESourceSelectorPrivate *priv; - GtkTreeIter parent_iter, source_iter; + GHashTable *source_index; + GtkTreeRowReference *reference; + GtkTreeSelection *selection; + GtkTreeView *tree_view; + GtkTreePath *child_path; + GtkTreePath *parent_path; + const gchar *extension_name; - g_return_if_fail (selector != NULL); g_return_if_fail (E_IS_SOURCE_SELECTOR (selector)); - g_return_if_fail (source != NULL); g_return_if_fail (E_IS_SOURCE (source)); - priv = selector->priv; + tree_view = GTK_TREE_VIEW (selector); + selection = gtk_tree_view_get_selection (tree_view); - source = find_source (selector, source); - g_return_if_fail (source != NULL); + source_index = selector->priv->source_index; + reference = g_hash_table_lookup (source_index, source); - if (find_source_iter (selector, source, &parent_iter, &source_iter)) { - GtkTreeSelection *selection; - GtkTreeView *tree_view; - GtkTreeModel *model; - GtkTreePath *path; + /* XXX Maybe we should return a success/fail boolean? */ + if (!gtk_tree_row_reference_valid (reference)) + return; - tree_view = GTK_TREE_VIEW (selector); - model = gtk_tree_view_get_model (tree_view); - selection = gtk_tree_view_get_selection (tree_view); + extension_name = e_source_selector_get_extension_name (selector); + g_return_if_fail (e_source_has_extension (source, extension_name)); - /* We block the signal because this all needs to be atomic */ - g_signal_handlers_block_matched (selection, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, selection_changed_callback, NULL); - gtk_tree_selection_unselect_all (selection); - g_signal_handlers_unblock_matched (selection, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, selection_changed_callback, NULL); + /* We block the signal because this all needs to be atomic */ + g_signal_handlers_block_matched ( + selection, G_SIGNAL_MATCH_FUNC, + 0, 0, NULL, selection_changed_callback, NULL); + gtk_tree_selection_unselect_all (selection); + g_signal_handlers_unblock_matched ( + selection, G_SIGNAL_MATCH_FUNC, + 0, 0, NULL, selection_changed_callback, NULL); - clear_saved_primary_selection (selector); + clear_saved_primary_selection (selector); - path = gtk_tree_model_get_path (model, &parent_iter); + child_path = gtk_tree_row_reference_get_path (reference); - if (gtk_tree_view_row_expanded (tree_view, path)) { - gtk_tree_selection_select_iter (selection, &source_iter); - } else { - GtkTreePath *child_path; + parent_path = gtk_tree_path_copy (child_path); + gtk_tree_path_up (parent_path); - child_path = gtk_tree_model_get_path (model, &source_iter); - priv->saved_primary_selection = gtk_tree_row_reference_new (model, child_path); - gtk_tree_path_free (child_path); + if (gtk_tree_view_row_expanded (tree_view, parent_path)) { + gtk_tree_selection_select_path (selection, child_path); + } else { + ESourceSelectorClass *class; - /* We do this by hand because we aren't changing the tree selection */ - if (!source_is_selected (selector, source)) { - select_source (selector, source); - gtk_tree_model_row_changed (model, path, &source_iter); - g_signal_emit (selector, signals[SELECTION_CHANGED], 0); - } + class = E_SOURCE_SELECTOR_GET_CLASS (selector); + g_return_if_fail (class->set_source_selected != NULL); - g_signal_emit (selector, signals[PRIMARY_SELECTION_CHANGED], 0); - g_object_notify (G_OBJECT (selector), "primary-selection"); - } + selector->priv->saved_primary_selection = + gtk_tree_row_reference_copy (reference); - gtk_tree_path_free (path); - } else { - g_warning (G_STRLOC ": Cannot find source %p (%s) in selector %p", - (gpointer) source, e_source_get_display_name (source), - (gpointer) selector); + class->set_source_selected (selector, source, TRUE); + + g_signal_emit (selector, signals[SELECTION_CHANGED], 0); + g_signal_emit (selector, signals[PRIMARY_SELECTION_CHANGED], 0); + g_object_notify (G_OBJECT (selector), "primary-selection"); } + + gtk_tree_path_free (child_path); + gtk_tree_path_free (parent_path); } /** @@ -1948,7 +2024,64 @@ e_source_selector_ref_source_by_path (ESourceSelector *selector, model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); if (gtk_tree_model_get_iter (model, &iter, path)) - gtk_tree_model_get (model, &iter, 0, &source, -1); + gtk_tree_model_get (model, &iter, COLUMN_SOURCE, &source, -1); return source; } + +/** + * e_source_selector_queue_write: + * @selector: an #ESourceSelecetor + * @source: an #ESource with changes to be written + * + * Queues a main loop idle callback to write changes to @source back to + * the D-Bus registry service. + * + * Since: 3.6 + **/ +void +e_source_selector_queue_write (ESourceSelector *selector, + ESource *source) +{ + GSource *idle_source; + GHashTable *pending_writes; + GMainContext *main_context; + AsyncContext *async_context; + + g_return_if_fail (E_IS_SOURCE_SELECTOR (selector)); + g_return_if_fail (E_IS_SOURCE (source)); + + main_context = selector->priv->main_context; + pending_writes = selector->priv->pending_writes; + + idle_source = g_hash_table_lookup (pending_writes, source); + if (idle_source != NULL && !g_source_is_destroyed (idle_source)) + return; + + async_context = g_slice_new0 (AsyncContext); + async_context->selector = g_object_ref (selector); + async_context->source = g_object_ref (source); + + /* Set a higher priority so this idle source runs before our + * source_selector_cancel_write() signal handler, which will + * cancel this idle source. Cancellation is the right thing + * to do when receiving changes from OTHER registry clients, + * but we don't want to cancel our own changes. + * + * XXX This might be an argument for using etags. + */ + idle_source = g_idle_source_new (); + g_hash_table_insert ( + pending_writes, + g_object_ref (source), + g_source_ref (idle_source)); + g_source_set_callback ( + idle_source, + source_selector_write_idle_cb, + async_context, + (GDestroyNotify) async_context_free); + g_source_set_priority (idle_source, G_PRIORITY_HIGH_IDLE); + g_source_attach (idle_source, main_context); + g_source_unref (idle_source); +} + diff --git a/libedataserverui/e-source-selector.h b/libedataserverui/e-source-selector.h index 4ee3cc8..af1e247 100644 --- a/libedataserverui/e-source-selector.h +++ b/libedataserverui/e-source-selector.h @@ -25,7 +25,7 @@ #define E_SOURCE_SELECTOR_H #include -#include +#include /* Standard GObject macros */ #define E_TYPE_SOURCE_SELECTOR \ @@ -60,6 +60,14 @@ struct _ESourceSelector { struct _ESourceSelectorClass { GtkTreeViewClass parent_class; + /* Methods */ + gboolean (*get_source_selected) (ESourceSelector *selector, + ESource *source); + void (*set_source_selected) (ESourceSelector *selector, + ESource *source, + gboolean selected); + + /* Signals */ void (*selection_changed) (ESourceSelector *selector); void (*primary_selection_changed) (ESourceSelector *selector); @@ -78,9 +86,22 @@ struct _ESourceSelectorClass { }; GType e_source_selector_get_type (void); -GtkWidget * e_source_selector_new (ESourceList *list); -ESourceList * e_source_selector_get_source_list +GtkWidget * e_source_selector_new (ESourceRegistry *registry, + const gchar *extension_name); +ESourceRegistry * + e_source_selector_get_registry (ESourceSelector *selector); +const gchar * e_source_selector_get_extension_name + (ESourceSelector *selector); +gboolean e_source_selector_get_show_colors + (ESourceSelector *selector); +void e_source_selector_set_show_colors + (ESourceSelector *selector, + gboolean show_colors); +gboolean e_source_selector_get_show_toggles (ESourceSelector *selector); +void e_source_selector_set_show_toggles + (ESourceSelector *selector, + gboolean show_toggles); void e_source_selector_select_source (ESourceSelector *selector, ESource *source); void e_source_selector_unselect_source @@ -95,11 +116,6 @@ gboolean e_source_selector_source_is_selected GSList * e_source_selector_get_selection (ESourceSelector *selector); void e_source_selector_free_selection (GSList *list); -void e_source_selector_show_selection - (ESourceSelector *selector, - gboolean show); -gboolean e_source_selector_selection_shown - (ESourceSelector *selector); void e_source_selector_set_select_new (ESourceSelector *selector, gboolean state); @@ -110,11 +126,11 @@ ESource * e_source_selector_ref_primary_selection void e_source_selector_set_primary_selection (ESourceSelector *selector, ESource *source); -ESourceGroup * e_source_selector_get_primary_source_group - (ESourceSelector *selector); ESource * e_source_selector_ref_source_by_path (ESourceSelector *selector, GtkTreePath *path); +void e_source_selector_queue_write (ESourceSelector *selector, + ESource *source); G_END_DECLS diff --git a/tests/libedataserverui/test-source-selector.c b/tests/libedataserverui/test-source-selector.c index 93dc577..95a959b 100644 --- a/tests/libedataserverui/test-source-selector.c +++ b/tests/libedataserverui/test-source-selector.c @@ -21,8 +21,11 @@ * Author: Ettore Perazzoli */ +#include #include +static const gchar *extension_name; + static void dump_selection (ESourceSelector *selector) { @@ -36,10 +39,15 @@ dump_selection (ESourceSelector *selector) for (p = selection; p != NULL; p = p->next) { ESource *source = E_SOURCE (p->data); + ESourceBackend *extension; + + extension = e_source_get_extension ( + source, extension_name); - g_print ("\tSource %s (group %s)\n", - e_source_get_display_name (source), - e_source_group_peek_name (e_source_peek_group (source))); + g_print ( + "\tSource %s (backend %s)\n", + e_source_get_display_name (source), + e_source_backend_get_backend_name (extension)); } } @@ -47,57 +55,65 @@ dump_selection (ESourceSelector *selector) } static void -selection_changed_callback (ESourceSelector *selector, - gpointer unused_data) +selection_changed_callback (ESourceSelector *selector) { g_print ("Selection changed!\n"); dump_selection (selector); } -static void -check_toggled_callback (GtkToggleButton *button, - ESourceSelector *selector) -{ - e_source_selector_show_selection (selector, gtk_toggle_button_get_active (button)); -} - static gint -on_idle_create_widget (const gchar *gconf_path) +on_idle_create_widget (ESourceRegistry *registry) { GtkWidget *window; GtkWidget *vbox; GtkWidget *selector; GtkWidget *scrolled_window; GtkWidget *check; - ESourceList *list; - GConfClient *gconf_client; window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size (GTK_WINDOW (window), 200, 300); - vbox = gtk_vbox_new (FALSE, 3); + g_signal_connect ( + window, "delete-event", + G_CALLBACK (gtk_main_quit), NULL); + + vbox = gtk_vbox_new (FALSE, 6); gtk_container_add (GTK_CONTAINER (window), vbox); - gconf_client = gconf_client_get_default (); - list = e_source_list_new_for_gconf (gconf_client, gconf_path); - selector = e_source_selector_new (list); - g_signal_connect (selector, "selection_changed", G_CALLBACK (selection_changed_callback), NULL); + selector = e_source_selector_new (registry, extension_name); + g_signal_connect ( + selector, "selection_changed", + G_CALLBACK (selection_changed_callback), NULL); scrolled_window = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_IN); + gtk_scrolled_window_set_policy ( + GTK_SCROLLED_WINDOW (scrolled_window), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type ( + GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_IN); gtk_container_add (GTK_CONTAINER (scrolled_window), selector); - gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 3); + gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0); - check = gtk_check_button_new_with_label ("Show checkboxes"); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), - e_source_selector_selection_shown (E_SOURCE_SELECTOR (selector))); - g_signal_connect (check, "toggled", G_CALLBACK (check_toggled_callback), selector); - gtk_box_pack_start (GTK_BOX (vbox), check, FALSE, TRUE, 3); + check = gtk_check_button_new_with_label ("Show colors"); + gtk_box_pack_start (GTK_BOX (vbox), check, FALSE, TRUE, 0); + + g_object_bind_property ( + selector, "show-colors", + check, "active", + G_BINDING_BIDIRECTIONAL | + G_BINDING_SYNC_CREATE); + + check = gtk_check_button_new_with_label ("Show toggles"); + gtk_box_pack_start (GTK_BOX (vbox), check, FALSE, TRUE, 0); + + g_object_bind_property ( + selector, "show-toggles", + check, "active", + G_BINDING_BIDIRECTIONAL | + G_BINDING_SYNC_CREATE); gtk_widget_show_all (window); - g_object_unref (gconf_client); return FALSE; } @@ -105,15 +121,25 @@ gint main (gint argc, gchar **argv) { - const gchar *gconf_path; + ESourceRegistry *registry; + GError *error = NULL; gtk_init (&argc, &argv); + if (argc < 2) - gconf_path = "/apps/evolution/calendar/sources"; + extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK; else - gconf_path = argv[1]; + extension_name = argv[1]; + + registry = e_source_registry_new_sync (NULL, &error); + + if (error != NULL) { + g_error ("Failed to load ESource registry: %s", + error->message); + g_assert_not_reached (); + } - g_idle_add ((GSourceFunc) on_idle_create_widget, (gpointer) gconf_path); + g_idle_add ((GSourceFunc) on_idle_create_widget, registry); gtk_main (); -- 2.7.4