diff options
author | Matthew Barnes <mbarnes@redhat.com> | 2010-01-26 14:57:36 -0500 |
---|---|---|
committer | Matthew Barnes <mbarnes@redhat.com> | 2010-01-27 14:11:45 -0500 |
commit | 07e5feb0cb0590442eafd15c4b9bb6079d4089eb (patch) | |
tree | 69c1fa9d68a376eeba6924b39992d60cfd0dc787 | |
parent | 68216b18f49206224641f70eebd2254b37927195 (diff) | |
download | evolution-data-server-e-source-store.tar.gz |
Prototype and document ESourceStore.e-source-store
-rw-r--r-- | docs/reference/libedataserverui/libedataserverui-docs.sgml | 1 | ||||
-rw-r--r-- | docs/reference/libedataserverui/libedataserverui-sections.txt | 30 | ||||
-rw-r--r-- | docs/reference/libedataserverui/tmpl/e-source-selector.sgml | 11 | ||||
-rw-r--r-- | docs/reference/libedataserverui/tmpl/e-source-store.sgml | 137 | ||||
-rw-r--r-- | libedataserverui/Makefile.am | 2 | ||||
-rw-r--r-- | libedataserverui/e-source-selector.c | 684 | ||||
-rw-r--r-- | libedataserverui/e-source-selector.h | 6 | ||||
-rw-r--r-- | libedataserverui/e-source-store.c | 1120 | ||||
-rw-r--r-- | libedataserverui/e-source-store.h | 136 | ||||
-rw-r--r-- | libedataserverui/test-source-selector.c | 4 |
10 files changed, 1647 insertions, 484 deletions
diff --git a/docs/reference/libedataserverui/libedataserverui-docs.sgml b/docs/reference/libedataserverui/libedataserverui-docs.sgml index 7b539f525..207146971 100644 --- a/docs/reference/libedataserverui/libedataserverui-docs.sgml +++ b/docs/reference/libedataserverui/libedataserverui-docs.sgml @@ -25,6 +25,7 @@ <xi:include href="xml/e-source-option-menu.xml"/> <xi:include href="xml/e-source-selector.xml"/> <xi:include href="xml/e-source-selector-dialog.xml"/> + <xi:include href="xml/e-source-store.xml"/> <xi:include href="xml/e-tree-model-generator.xml"/> </chapter> diff --git a/docs/reference/libedataserverui/libedataserverui-sections.txt b/docs/reference/libedataserverui/libedataserverui-sections.txt index e649ec0f4..b3ca8df76 100644 --- a/docs/reference/libedataserverui/libedataserverui-sections.txt +++ b/docs/reference/libedataserverui/libedataserverui-sections.txt @@ -268,6 +268,7 @@ e_name_selector_model_get_type <TITLE>ESourceSelector</TITLE> ESourceSelector e_source_selector_new +e_source_selector_new_with_store e_source_selector_get_source_list e_source_selector_select_source e_source_selector_unselect_source @@ -287,6 +288,7 @@ E_IS_SOURCE_SELECTOR E_TYPE_SOURCE_SELECTOR E_SOURCE_SELECTOR_CLASS E_IS_SOURCE_SELECTOR_CLASS +E_SOURCE_SELECTOR_GET_CLASS ESourceSelectorClass <SUBSECTION Private> ESourceSelectorPrivate @@ -294,6 +296,34 @@ e_source_selector_get_type </SECTION> <SECTION> +<FILE>e-source-store</FILE> +<TITLE>ESourceStore</TITLE> +ESourceStore +e_source_store_new +e_source_store_get_source_list +e_source_store_queue_refresh +e_source_store_get_iter_from_source +e_source_store_select_source +e_source_store_unselect_source +e_source_store_is_selected +e_source_store_get_selected +e_source_store_get_client +e_source_store_get_client_async +e_source_store_get_client_finish +<SUBSECTION Standard> +E_SOURCE_STORE +E_IS_SOURCE_STORE +E_TYPE_SOURCE_STORE +E_SOURCE_STORE_CLASS +E_IS_SOURCE_STORE_CLASS +E_SOURCE_STORE_GET_CLASS +ESourceStoreClass +<SUBSECTION Private> +ESourceStorePrivate +e_source_store_get_type +</SECTION> + +<SECTION> <FILE>e-name-selector-list</FILE> <TITLE>ENameSelectorList</TITLE> ENameSelectorList diff --git a/docs/reference/libedataserverui/tmpl/e-source-selector.sgml b/docs/reference/libedataserverui/tmpl/e-source-selector.sgml index 7fc516882..6e3932ca9 100644 --- a/docs/reference/libedataserverui/tmpl/e-source-selector.sgml +++ b/docs/reference/libedataserverui/tmpl/e-source-selector.sgml @@ -69,7 +69,16 @@ ESourceSelector </para> -@list: +@source_list: +@Returns: + + +<!-- ##### FUNCTION e_source_selector_new_with_store ##### --> +<para> + +</para> + +@source_store: @Returns: diff --git a/docs/reference/libedataserverui/tmpl/e-source-store.sgml b/docs/reference/libedataserverui/tmpl/e-source-store.sgml new file mode 100644 index 000000000..ccc599e01 --- /dev/null +++ b/docs/reference/libedataserverui/tmpl/e-source-store.sgml @@ -0,0 +1,137 @@ +<!-- ##### SECTION Title ##### --> +ESourceStore + +<!-- ##### SECTION Short_Description ##### --> + + +<!-- ##### SECTION Long_Description ##### --> +<para> + +</para> + +<!-- ##### SECTION See_Also ##### --> +<para> + +</para> + +<!-- ##### SECTION Stability_Level ##### --> + + +<!-- ##### STRUCT ESourceStore ##### --> +<para> + +</para> + +@parent: +@priv: + +<!-- ##### FUNCTION e_source_store_new ##### --> +<para> + +</para> + +@source_list: +@Returns: + + +<!-- ##### FUNCTION e_source_store_get_source_list ##### --> +<para> + +</para> + +@source_store: +@Returns: + + +<!-- ##### FUNCTION e_source_store_queue_refresh ##### --> +<para> + +</para> + +@source_store: + + +<!-- ##### FUNCTION e_source_store_get_iter_from_source ##### --> +<para> + +</para> + +@source_store: +@source: +@iter: +@Returns: + + +<!-- ##### FUNCTION e_source_store_select_source ##### --> +<para> + +</para> + +@source_store: +@source: + + +<!-- ##### FUNCTION e_source_store_unselect_source ##### --> +<para> + +</para> + +@source_store: +@source: + + +<!-- ##### FUNCTION e_source_store_is_selected ##### --> +<para> + +</para> + +@source_store: +@source: +@Returns: + + +<!-- ##### FUNCTION e_source_store_get_selected ##### --> +<para> + +</para> + +@source_store: +@Returns: + + +<!-- ##### FUNCTION e_source_store_get_client ##### --> +<para> + +</para> + +@source_store: +@source: +@cancellable: +@error: +@Returns: + + +<!-- ##### FUNCTION e_source_store_get_client_async ##### --> +<para> + +</para> + +@source_store: +@source: +@io_priority: +@cancellable: +@callback: +@user_data: + + +<!-- ##### FUNCTION e_source_store_get_client_finish ##### --> +<para> + +</para> + +@source_store: +@result: +@error: +@Returns: + + diff --git a/libedataserverui/Makefile.am b/libedataserverui/Makefile.am index a5814cbf8..7c33d5344 100644 --- a/libedataserverui/Makefile.am +++ b/libedataserverui/Makefile.am @@ -37,6 +37,7 @@ libedataserverui_1_2_la_SOURCES = \ e-source-selector-dialog.c \ e-source-combo-box.c \ e-source-option-menu.c \ + e-source-store.c \ e-tree-model-generator.c \ e-cell-renderer-color.c @@ -66,6 +67,7 @@ libedataserveruiinclude_HEADERS = \ e-source-selector-dialog.h \ e-source-combo-box.h \ e-source-option-menu.h \ + e-source-store.h \ e-tree-model-generator.h \ e-cell-renderer-color.h diff --git a/libedataserverui/e-source-selector.c b/libedataserverui/e-source-selector.c index fc2b99160..0c85d387a 100644 --- a/libedataserverui/e-source-selector.c +++ b/libedataserverui/e-source-selector.c @@ -35,28 +35,16 @@ ((obj), E_TYPE_SOURCE_SELECTOR, ESourceSelectorPrivate)) struct _ESourceSelectorPrivate { - ESourceList *list; + ESourceList *source_list; - GHashTable *selected_sources; GtkTreeRowReference *saved_primary_selection; ESourceGroup *primary_source_group; - gint rebuild_model_idle_id; - gboolean toggled_last; gboolean checkboxes_shown; gboolean select_new; }; -typedef struct { - ESourceSelector *selector; - - GHashTable *remaining_uids; - GSList *deleted_uids; - - gboolean selection_changed; -} ESourceSelectorRebuildData; - enum { PROP_0, PROP_SOURCE_LIST @@ -132,32 +120,10 @@ e_cell_renderer_safe_toggle_new (void) /* Selection management. */ -static ESourceSelectorRebuildData * -create_rebuild_data (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; -} - static void -free_rebuild_data (ESourceSelectorRebuildData *rebuild_data) +source_selector_emit_selection_changed (ESourceSelector *selector) { - GSList *p; - - 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); - - g_free (rebuild_data); + g_signal_emit (selector, signals[SELECTION_CHANGED], 0); } static void @@ -168,38 +134,6 @@ clear_saved_primary_selection (ESourceSelector *selector) } static gboolean -source_is_selected (ESourceSelector *selector, - ESource *source) -{ - if (g_hash_table_lookup (selector->priv->selected_sources, source) == NULL) - return FALSE; - else - return TRUE; -} - -static void -select_source (ESourceSelector *selector, - ESource *source) -{ - if (g_hash_table_lookup (selector->priv->selected_sources, source) != NULL) - return; - - g_hash_table_insert (selector->priv->selected_sources, source, source); - g_object_ref (source); -} - -static void -unselect_source (ESourceSelector *selector, - ESource *source) -{ - if (g_hash_table_lookup (selector->priv->selected_sources, source) == NULL) - return; - - /* (This will unref the source.) */ - g_hash_table_remove (selector->priv->selected_sources, source); -} - -static gboolean find_source_iter (ESourceSelector *selector, ESource *source, GtkTreeIter *parent_iter, @@ -233,65 +167,19 @@ find_source_iter (ESourceSelector *selector, return FALSE; } -/* Setting up the model. */ -static gboolean -rebuild_existing_cb (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - ESourceSelectorRebuildData *rebuild_data = data; - ESourceList *source_list; - GtkTreeRowReference *reference; - gpointer node; - const gchar *uid; - - gtk_tree_model_get (model, iter, 0, &node, -1); - - source_list = rebuild_data->selector->priv->list; - reference = gtk_tree_row_reference_new (model, path); - - if (E_IS_SOURCE_GROUP (node)) { - uid = e_source_group_peek_uid (E_SOURCE_GROUP (node)); - - 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_prepend ( - rebuild_data->deleted_uids, reference); - } else { - uid = e_source_peek_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; - } - } - - g_object_unref (node); - - return FALSE; -} - static ESource * find_source (ESourceSelector *selector, ESource *source) { + ESourceList *source_list; GSList *groups, *p; g_return_val_if_fail (selector != NULL, source); g_return_val_if_fail (E_IS_SOURCE (source), source); - groups = e_source_list_peek_groups (selector->priv->list); + source_list = e_source_selector_get_source_list (selector); + + groups = e_source_list_peek_groups (source_list); for (p = groups; p != NULL; p = p->next) { ESourceGroup *group = E_SOURCE_GROUP (p->data); GSList *sources, *q; @@ -308,132 +196,6 @@ find_source (ESourceSelector *selector, return source; } -/** - * compare_source_names - * Compares sources by name. - **/ -static gint -compare_source_names (gconstpointer a, gconstpointer b) -{ - 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_peek_name (E_SOURCE (a)); - name_b = e_source_peek_name (E_SOURCE (b)); - - return g_utf8_collate (name_a, name_b); -} - -static void -rebuild_model (ESourceSelector *selector) -{ - ESourceSelectorRebuildData *rebuild_data; - ESource *source; - GtkTreeModel *model; - GtkTreeStore *store; - GtkTreeIter iter; - GSList *groups, *p; - gboolean set_primary; - - model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); - store = GTK_TREE_STORE (model); - - rebuild_data = create_rebuild_data (selector); - set_primary = e_source_selector_peek_primary_selection (selector) != NULL; - - gtk_tree_model_foreach (model, rebuild_existing_cb, rebuild_data); - - /* Remove any delete sources or groups */ - for (p = rebuild_data->deleted_uids; p; p = p->next) { - GtkTreeRowReference *row_ref = p->data; - GtkTreePath *path; - - path = gtk_tree_row_reference_get_path (row_ref); - gtk_tree_model_get_iter (model, &iter, path); - gtk_tree_store_remove (store, &iter); - - gtk_tree_path_free (path); - } - - /* 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); - 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); - - reference = g_hash_table_lookup ( - rebuild_data->remaining_uids, - e_source_group_peek_uid (group)); - - if (reference == NULL) { - gtk_tree_store_append (store, &iter, NULL); - gtk_tree_store_set (store, &iter, 0, group, -1); - } else { - 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_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_peek_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); - } - - if (rebuild_data->selection_changed) - g_signal_emit (selector, signals[SELECTION_CHANGED], 0); - - source = e_source_selector_peek_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); - } - - free_rebuild_data (rebuild_data); -} - static gboolean same_source_name_exists (ESourceSelector *selector, const gchar *name) @@ -472,65 +234,39 @@ same_source_name_exists (ESourceSelector *selector, return FALSE; } -static gint -on_idle_rebuild_model_callback (ESourceSelector *selector) -{ - rebuild_model (selector); - selector->priv->rebuild_model_idle_id = 0; - - return FALSE; -} - -static void -list_changed_callback (ESourceList *list, - ESourceSelector *selector) -{ - ESourceSelectorPrivate *priv = selector->priv; - - if (priv->rebuild_model_idle_id == 0) - priv->rebuild_model_idle_id = g_idle_add ((GSourceFunc) on_idle_rebuild_model_callback, - selector); -} - /* Data functions for rendering the model. */ static void toggle_cell_data_func (GtkTreeViewColumn *column, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - ESourceSelector *selector) + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + ESourceSelector *selector) { - gpointer data; + GObject *object; + gboolean visible = FALSE; + gint column_id; - gtk_tree_model_get (model, iter, 0, &data, -1); - if (data == NULL) { - g_object_set (renderer, "visible", FALSE, NULL); - return; - } + column_id = E_SOURCE_STORE_COLUMN_SOURCE; + gtk_tree_model_get (model, iter, column_id, &object, -1); - if (E_IS_SOURCE_GROUP (data)) { - g_object_set (renderer, "visible", FALSE, NULL); - } else { - g_assert (E_IS_SOURCE (data)); + if (E_IS_SOURCE (object)) + visible = selector->priv->checkboxes_shown; - 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_set (renderer, "visible", visible, NULL); - g_object_unref (data); + if (object != NULL) + g_object_unref (object); } static void text_cell_data_func (GtkTreeViewColumn *column, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - ESourceSelector *selector) + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + ESourceSelector *selector) { + const gchar *name; gpointer data; gtk_tree_model_get (model, iter, 0, &data, -1); @@ -540,33 +276,33 @@ text_cell_data_func (GtkTreeViewColumn *column, } 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_peek_name (source), - "weight", PANGO_WEIGHT_NORMAL, - "foreground_set", FALSE, - NULL); - } + name = e_source_group_peek_name (E_SOURCE_GROUP (data)); + g_object_set ( + renderer, + "text", name, + "weight", PANGO_WEIGHT_BOLD, + "foreground_set", FALSE, + NULL); + } else if (E_IS_SOURCE (data)) { + name = e_source_peek_name (E_SOURCE (data)); + g_object_set ( + renderer, + "text", name, + "weight", PANGO_WEIGHT_NORMAL, + "foreground_set", FALSE, + NULL); + } else + g_warn_if_reached (); g_object_unref (data); } static void pixbuf_cell_data_func (GtkTreeViewColumn *column, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - ESourceSelector *selector) + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + ESourceSelector *selector) { gpointer data; @@ -684,30 +420,32 @@ cell_toggled_callback (GtkCellRendererToggle *renderer, 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); - if (! gtk_tree_model_get_iter (model, &iter, path)) { + if (!gtk_tree_model_get_iter (model, &iter, path)) { gtk_tree_path_free (path); return; } - gtk_tree_model_get (model, &iter, 0, &data, -1); - if (!E_IS_SOURCE_GROUP (data)) { - source = E_SOURCE (data); + gtk_tree_model_get ( + model, &iter, + E_SOURCE_STORE_COLUMN_SOURCE, &data, -1); + + if (E_IS_SOURCE (data)) { + ESource *source = E_SOURCE (data); + ESourceStore *source_store; - if (source_is_selected (selector, source)) - unselect_source (selector, source); + source_store = E_SOURCE_STORE (model); + + if (e_source_store_is_selected (source_store, source)) + e_source_store_unselect_source (source_store, source); else - select_source (selector, source); + e_source_store_select_source (source_store, source); selector->priv->toggled_last = TRUE; - - gtk_tree_model_row_changed (model, path, &iter); - g_signal_emit (selector, signals[SELECTION_CHANGED], 0); } gtk_tree_path_free (path); @@ -733,7 +471,9 @@ group_search_function (GtkTreeModel *model, const gchar *name = NULL; gboolean status = TRUE; - gtk_tree_model_get (model, iter, 0, &data, -1); + gtk_tree_model_get ( + model, iter, + E_SOURCE_STORE_COLUMN_SOURCE, &data, -1); if (E_IS_SOURCE_GROUP (data)) name = e_source_group_peek_name (E_SOURCE_GROUP (data)); @@ -756,18 +496,9 @@ source_selector_set_source_list (ESourceSelector *selector, ESourceList *source_list) { 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 (selector->priv->source_list == 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->source_list = g_object_ref (source_list); } static void @@ -809,16 +540,9 @@ source_selector_dispose (GObject *object) { ESourceSelectorPrivate *priv = E_SOURCE_SELECTOR (object)->priv; - 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->list != NULL) { - g_object_unref (priv->list); - priv->list = NULL; + if (priv->source_list != NULL) { + g_object_unref (priv->source_list); + priv->source_list = NULL; } clear_saved_primary_selection (E_SOURCE_SELECTOR (object)); @@ -828,14 +552,82 @@ source_selector_dispose (GObject *object) } static void -source_selector_finalize (GObject *object) +source_selector_constructed (GObject *object) { - ESourceSelectorPrivate *priv = E_SOURCE_SELECTOR (object)->priv; + ESourceSelector *selector; + ESourceList *source_list; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkTreeView *tree_view; + GtkTreeModel *model; + + selector = E_SOURCE_SELECTOR (object); + source_list = e_source_selector_get_source_list (selector); + + tree_view = GTK_TREE_VIEW (object); + model = gtk_tree_view_get_model (tree_view); + + /* Create a default store if one was not provided. */ + if (model == NULL) { + model = e_source_store_new (source_list); + gtk_tree_view_set_model (tree_view, model); + } - g_hash_table_destroy (priv->selected_sources); + gtk_tree_view_expand_all (tree_view); - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (e_source_selector_parent_class)->finalize (object); + gtk_tree_view_set_enable_search (tree_view, TRUE); + gtk_tree_view_set_headers_visible (tree_view, FALSE); + gtk_tree_view_set_search_column (tree_view, 0); + gtk_tree_view_set_search_equal_func ( + tree_view, group_search_function, NULL, NULL); + + /* Configure cell renderers. */ + + column = gtk_tree_view_column_new (); + gtk_tree_view_append_column (tree_view, column); + + renderer = gtk_cell_renderer_pixbuf_new (); + g_object_set ( + renderer, "mode", + GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_set_cell_data_func ( + column, renderer, (GtkTreeCellDataFunc) + pixbuf_cell_data_func, selector, NULL); + + 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", + E_SOURCE_STORE_COLUMN_SELECTED); + gtk_tree_view_column_set_cell_data_func ( + column, renderer, (GtkTreeCellDataFunc) + toggle_cell_data_func, selector, NULL); + g_signal_connect ( + renderer, "toggled", + G_CALLBACK (cell_toggled_callback), selector); + + renderer = gtk_cell_renderer_text_new (); + g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL); + g_signal_connect_swapped ( + renderer, "edited", + G_CALLBACK (text_cell_edited_cb), selector); + gtk_tree_view_column_pack_start (column, renderer, TRUE); + gtk_tree_view_column_set_cell_data_func ( + column, renderer, (GtkTreeCellDataFunc) + text_cell_data_func, selector, NULL); + + /* Listen for model signals. */ + + g_signal_connect_swapped ( + model, "source-selected", + G_CALLBACK (source_selector_emit_selection_changed), + selector); + + g_signal_connect_swapped ( + model, "source-unselected", + G_CALLBACK (source_selector_emit_selection_changed), + selector); } static gboolean @@ -870,7 +662,9 @@ source_selector_button_press_event (GtkWidget *widget, 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); + gtk_tree_model_get ( + model, &iter, + E_SOURCE_STORE_COLUMN_SOURCE, &data, -1); /* Do not emit popup since we will * not be able to get the ESource. */ @@ -941,7 +735,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, + E_SOURCE_STORE_COLUMN_SOURCE, &object, -1); if (E_IS_SOURCE_GROUP (object) || e_source_get_readonly (object)) goto exit; @@ -992,7 +788,9 @@ 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); + gtk_tree_model_get ( + model, &iter, + E_SOURCE_STORE_COLUMN_SOURCE, &object, -1); drop_zone = E_IS_SOURCE (object); g_object_unref (object); @@ -1026,7 +824,9 @@ 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, + E_SOURCE_STORE_COLUMN_SOURCE, &object, -1); if (!E_IS_SOURCE (object) || e_source_get_readonly (object)) goto exit; @@ -1155,7 +955,7 @@ e_source_selector_class_init (ESourceSelectorClass *class) object_class->set_property = source_selector_set_property; 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; @@ -1226,10 +1026,7 @@ static void e_source_selector_init (ESourceSelector *selector) { ESourceSelectorPrivate *priv; - GtkTreeViewColumn *column; - GtkCellRenderer *cell_renderer; GtkTreeSelection *selection; - GtkTreeStore *tree_store; GtkTreeView *tree_view; selector->priv = E_SOURCE_SELECTOR_GET_PRIVATE (selector); @@ -1237,55 +1034,10 @@ e_source_selector_init (ESourceSelector *selector) 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_enable_search (tree_view, TRUE); - priv->toggled_last = FALSE; priv->checkboxes_shown = TRUE; priv->select_new = FALSE; - priv->selected_sources = g_hash_table_new_full ( - g_direct_hash, g_direct_equal, - (GDestroyNotify) g_object_unref, - (GDestroyNotify) NULL); - - 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 (); - 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_signal_connect ( - cell_renderer, "toggled", - G_CALLBACK (cell_toggled_callback), selector); - - cell_renderer = gtk_cell_renderer_text_new (); - g_object_set ( - G_OBJECT (cell_renderer), - "ellipsize", PANGO_ELLIPSIZE_END, NULL); - g_signal_connect_swapped ( - cell_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); - selection = gtk_tree_view_get_selection (tree_view); gtk_tree_selection_set_select_function ( selection, (GtkTreeSelectionFunc) @@ -1294,28 +1046,49 @@ e_source_selector_init (ESourceSelector *selector) selection, "changed", G_CALLBACK (selection_changed_callback), G_OBJECT (selector), 0); - - gtk_tree_view_set_headers_visible (tree_view, FALSE); } /* Public API. */ /** * e_source_selector_new: - * @list: A source list. + * @source_list: an #ESourceList + * + * Create a new selector for @source_list. The contents will update + * automatically when the source list changes. + * + * Returns: a new #ESourceSelector widget + **/ +GtkWidget * +e_source_selector_new (ESourceList *source_list) +{ + g_return_val_if_fail (E_IS_SOURCE_LIST (source_list), NULL); + + return g_object_new ( + E_TYPE_SOURCE_SELECTOR, "source-list", source_list, NULL); +} + +/** + * e_source_selector_new_with_store: + * @source_store: an #ESourceStore * - * Create a new view for @list. The view will update automatically when @list - * changes. + * Create a new selector for the #ESourceList in @source_store. The + * contents will update automatically when the source list changes. * - * Return value: The newly created widget. + * Returns: a new #ESourceSelector widget **/ GtkWidget * -e_source_selector_new (ESourceList *list) +e_source_selector_new_with_store (ESourceStore *source_store) { - g_return_val_if_fail (E_IS_SOURCE_LIST (list), NULL); + ESourceList *source_list; + + g_return_val_if_fail (E_IS_SOURCE_STORE (source_store), NULL); + + source_list = e_source_store_get_source_list (source_store); return g_object_new ( - E_TYPE_SOURCE_SELECTOR, "source-list", list, NULL); + E_TYPE_SOURCE_SELECTOR, + "source-list", source_list, "model", source_store, NULL); } /** @@ -1331,7 +1104,7 @@ e_source_selector_get_source_list (ESourceSelector *selector) { g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), NULL); - return selector->priv->list; + return selector->priv->source_list; } /** @@ -1348,32 +1121,13 @@ e_source_selector_get_source_list (ESourceSelector *selector) GSList * e_source_selector_get_selection (ESourceSelector *selector) { - GSList *selection_list; - GSList *groups; - GSList *p; + GtkTreeModel *model; 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); - - if (source_is_selected (selector, source)) { - selection_list = g_slist_prepend (selection_list, source); - g_object_ref (source); - } - } - } + model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); - return g_slist_reverse (selection_list); + return e_source_store_get_selected (E_SOURCE_STORE (model)); } /** @@ -1477,30 +1231,13 @@ void e_source_selector_select_source (ESourceSelector *selector, ESource *source) { - GtkTreeIter parent_iter, source_iter; + GtkTreeModel *model; g_return_if_fail (E_IS_SOURCE_SELECTOR (selector)); g_return_if_fail (E_IS_SOURCE (source)); - source = find_source (selector, source); - - if (!source || source_is_selected (selector, source)) - return; - - select_source (selector, source); - - 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); - } + model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); + e_source_store_select_source (E_SOURCE_STORE (model), source); } /** @@ -1514,30 +1251,13 @@ void e_source_selector_unselect_source (ESourceSelector *selector, ESource *source) { - GtkTreeIter parent_iter, source_iter; + GtkTreeModel *model; g_return_if_fail (E_IS_SOURCE_SELECTOR (selector)); g_return_if_fail (E_IS_SOURCE (source)); - source = find_source (selector, source); - - if (!source || !source_is_selected (selector, source)) - return; - - unselect_source (selector, source); - - 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); - } + model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); + e_source_store_unselect_source (E_SOURCE_STORE (model), source); } /** @@ -1553,12 +1273,14 @@ gboolean e_source_selector_source_is_selected (ESourceSelector *selector, ESource *source) { + GtkTreeModel *model; + 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); + model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector)); - return source && source_is_selected (selector, source); + return e_source_store_is_selected (E_SOURCE_STORE (model), source); } /** @@ -1657,7 +1379,9 @@ e_source_selector_peek_primary_selection (ESourceSelector *selector) if (!have_iter && ! gtk_tree_selection_get_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (selector)), NULL, &iter)) return NULL; - gtk_tree_model_get (model, &iter, 0, &data, -1); + gtk_tree_model_get ( + model, &iter, + E_SOURCE_STORE_COLUMN_SOURCE, &data, -1); if (!data) return NULL; @@ -1726,10 +1450,8 @@ e_source_selector_set_primary_selection (ESourceSelector *selector, gtk_tree_path_free (child_path); /* 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); + if (!e_source_selector_source_is_selected (selector, source)) { + e_source_selector_select_source (selector, source); } g_signal_emit (selector, signals[PRIMARY_SELECTION_CHANGED], 0); diff --git a/libedataserverui/e-source-selector.h b/libedataserverui/e-source-selector.h index 4f46d5295..ee7d4effa 100644 --- a/libedataserverui/e-source-selector.h +++ b/libedataserverui/e-source-selector.h @@ -25,7 +25,8 @@ #define E_SOURCE_SELECTOR_H #include <gtk/gtk.h> -#include "libedataserver/e-source-list.h" +#include <libedataserver/e-source-list.h> +#include <libedataserverui/e-source-store.h> /* Standard GObject macros */ #define E_TYPE_SOURCE_SELECTOR \ @@ -78,7 +79,8 @@ struct _ESourceSelectorClass { }; GType e_source_selector_get_type (void); -GtkWidget * e_source_selector_new (ESourceList *list); +GtkWidget * e_source_selector_new (ESourceList *source_list); +GtkWidget * e_source_selector_new_with_store(ESourceStore *source_store); ESourceList * e_source_selector_get_source_list (ESourceSelector *selector); void e_source_selector_select_source (ESourceSelector *selector, diff --git a/libedataserverui/e-source-store.c b/libedataserverui/e-source-store.c new file mode 100644 index 000000000..8575f4143 --- /dev/null +++ b/libedataserverui/e-source-store.c @@ -0,0 +1,1120 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* e-source-store.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION: e-source-store + * @short_description: Store sources and clients in a tree model + * @include: libedataserverui/e-source-store.h + * + * #ESourceStore is a #GtkTreeModel meant for use with widgets that render + * sources such as #ESourceSelector and #ESourceComboBox. It can also be + * subclassed to open and store #EBook and #ECal objects. + **/ + +#include "e-source-store.h" + +#include <config.h> +#include <glib/gi18n-lib.h> + +#define E_SOURCE_STORE_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_STORE, ESourceStorePrivate)) + +struct _ESourceStorePrivate { + + ESourceList *source_list; + + /* Source UID -> GtkTreeRowReference */ + GHashTable *index; + + /* Source UID -> GCancellable */ + GHashTable *requests; + + guint refresh_source_id; +}; + +enum { + PROP_0, + PROP_SOURCE_LIST +}; + +enum { + SOURCE_ADDED, + SOURCE_REMOVED, + SOURCE_SELECTED, + SOURCE_UNSELECTED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +G_DEFINE_TYPE (ESourceStore, e_source_store, GTK_TYPE_TREE_STORE) + +typedef struct { + ESourceStore *source_store; + GHashTable *remaining_uids; + GPtrArray *deleted_uids; + GtkTreeIter iter; + gint position; +} RefreshData; + +static RefreshData * +refresh_data_new (ESourceStore *source_store) +{ + RefreshData *data; + + data = g_slice_new0 (RefreshData); + data->source_store = g_object_ref (source_store); + data->remaining_uids = g_hash_table_new_full ( + g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) gtk_tree_row_reference_free); + data->deleted_uids = g_ptr_array_new_with_free_func ( + (GDestroyNotify) gtk_tree_row_reference_free); + + return data; +} + +static void +refresh_data_free (RefreshData *data) +{ + g_object_unref (data->source_store); + g_hash_table_destroy (data->remaining_uids); + g_ptr_array_free (data->deleted_uids, TRUE); + g_slice_free (RefreshData, data); +} + +static gint +source_store_compare_source_names (gconstpointer source_a, + gconstpointer source_b) +{ + const gchar *source_name_a; + const gchar *source_name_b; + + g_return_val_if_fail (E_IS_SOURCE (source_a), -1); + g_return_val_if_fail (E_IS_SOURCE (source_b), 1); + + source_name_a = e_source_peek_name (E_SOURCE (source_a)); + source_name_b = e_source_peek_name (E_SOURCE (source_b)); + + return g_utf8_collate (source_name_a, source_name_b); +} + +static gboolean +source_store_refresh_collect_data (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + RefreshData *data) +{ + GtkTreeRowReference *reference; + ESourceStore *source_store; + ESourceList *source_list; + GObject *object; + const gchar *uid; + + source_store = E_SOURCE_STORE (model); + source_list = e_source_store_get_source_list (source_store); + reference = gtk_tree_row_reference_new (model, path); + + /* The object may be an ESource or ESourceGroup. */ + gtk_tree_model_get ( + model, iter, + E_SOURCE_STORE_COLUMN_SOURCE, &object, -1); + + if (E_IS_SOURCE (object)) { + uid = e_source_peek_uid (E_SOURCE (object)); + + if (e_source_list_peek_source_by_uid (source_list, uid)) + g_hash_table_insert ( + data->remaining_uids, + g_strdup (uid), reference); + else { + guint signal_id; + + g_ptr_array_add (data->deleted_uids, reference); + + g_hash_table_remove ( + source_store->priv->index, + (gpointer) uid); + + signal_id = signals[SOURCE_REMOVED]; + g_signal_emit (source_store, signal_id, 0, object); + } + + } else if (E_IS_SOURCE_GROUP (object)) { + uid = e_source_group_peek_uid (E_SOURCE_GROUP (object)); + + if (e_source_list_peek_group_by_uid (source_list, uid)) + g_hash_table_insert ( + data->remaining_uids, + g_strdup (uid), reference); + else + g_ptr_array_add (data->deleted_uids, reference); + + } else + g_warn_if_reached (); + + g_object_unref (object); + + return FALSE; +} + +static void +source_store_refresh_add_sources (ESource *source, + RefreshData *data) +{ + GtkTreeRowReference *reference; + GtkTreeModel *tree_model; + GtkTreeStore *tree_store; + GtkTreeIter iter; + const gchar *uid; + + tree_model = GTK_TREE_MODEL (data->source_store); + tree_store = GTK_TREE_STORE (data->source_store); + + uid = e_source_peek_uid (source); + reference = g_hash_table_lookup (data->remaining_uids, uid); + + if (reference == NULL) { + GtkTreePath *path; + guint signal_id; + + gtk_tree_store_insert ( + tree_store, &iter, &data->iter, data->position); + gtk_tree_store_set ( + tree_store, &iter, + E_SOURCE_STORE_COLUMN_SOURCE, source, -1); + + path = gtk_tree_model_get_path (tree_model, &iter); + reference = gtk_tree_row_reference_new (tree_model, path); + gtk_tree_path_free (path); + + g_hash_table_insert ( + data->source_store->priv->index, + g_strdup (uid), reference); + + signal_id = signals[SOURCE_ADDED]; + g_signal_emit (data->source_store, signal_id, 0, source); + } else { + GtkTreePath *path; + + path = gtk_tree_row_reference_get_path (reference); + gtk_tree_model_get_iter (tree_model, &iter, path); + gtk_tree_model_row_changed (tree_model, path, &iter); + gtk_tree_path_free (path); + } + + data->position++; +} + +static void +source_store_refresh_add_groups (ESourceGroup *source_group, + RefreshData *data) +{ + GtkTreeRowReference *reference; + GtkTreeModel *tree_model; + GtkTreeStore *tree_store; + GSList *list; + const gchar *uid; + + /* Skip empty source groups. */ + list = e_source_group_peek_sources (source_group); + if (list == NULL) + return; + + tree_model = GTK_TREE_MODEL (data->source_store); + tree_store = GTK_TREE_STORE (data->source_store); + + /* Copy the list and sort by name. */ + list = g_slist_copy (list); + list = g_slist_sort (list, source_store_compare_source_names); + + uid = e_source_group_peek_uid (source_group); + reference = g_hash_table_lookup (data->remaining_uids, uid); + + if (reference == NULL) { + gtk_tree_store_append (tree_store, &data->iter, NULL); + gtk_tree_store_set ( + tree_store, &data->iter, + E_SOURCE_STORE_COLUMN_SOURCE, source_group, -1); + } else { + GtkTreePath *path; + + path = gtk_tree_row_reference_get_path (reference); + gtk_tree_model_get_iter (tree_model, &data->iter, path); + gtk_tree_model_row_changed (tree_model, path, &data->iter); + gtk_tree_path_free (path); + } + + /* Insert new sources in this source group. */ + data->position = 0; + g_slist_foreach (list, (GFunc) source_store_refresh_add_sources, data); + + g_slist_free (list); +} + +static gboolean +source_store_refresh (ESourceStore *source_store) +{ + ESourceList *source_list; + GtkTreeModel *tree_model; + GtkTreeStore *tree_store; + RefreshData *data; + GSList *list; + guint ii; + + tree_model = GTK_TREE_MODEL (source_store); + tree_store = GTK_TREE_STORE (source_store); + + source_list = e_source_store_get_source_list (source_store); + + data = refresh_data_new (source_store); + + gtk_tree_model_foreach ( + tree_model, (GtkTreeModelForeachFunc) + source_store_refresh_collect_data, data); + + /* Remove any deleted sources or source groups. */ + for (ii = 0; ii < data->deleted_uids->len; ii++) { + GtkTreeRowReference *reference; + GtkTreePath *path; + GtkTreeIter iter; + + reference = g_ptr_array_index (data->deleted_uids, ii); + path = gtk_tree_row_reference_get_path (reference); + gtk_tree_model_get_iter (tree_model, &iter, path); + gtk_tree_store_remove (tree_store, &iter); + gtk_tree_path_free (path); + } + + /* Insert new source groups. */ + list = e_source_list_peek_groups (source_list); + g_slist_foreach (list, (GFunc) source_store_refresh_add_groups, data); + + refresh_data_free (data); + + source_store->priv->refresh_source_id = 0; + + return FALSE; +} + +static void +source_store_get_client_cb (ESourceStore *source_store, + GAsyncResult *result, + ESource *source) +{ + GHashTable *hash_table; + GtkTreeIter iter; + GObject *client; + const gchar *uid; + GError *error = NULL; + + uid = e_source_peek_uid (source); + hash_table = source_store->priv->requests; + g_hash_table_remove (hash_table, uid); + + client = e_source_store_get_client_finish ( + source_store, result, &error); + + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) { + g_error_free (error); + goto exit; + + } else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + g_error_free (error); + goto exit; + + } else if (error != NULL) { + e_source_store_unselect_source (source_store, source); + g_warning ("%s", error->message); + g_error_free (error); + goto exit; + } + + /* The source may have been deleted while we were waiting. */ + if (!e_source_store_get_iter_from_source (source_store, source, &iter)) + goto exit; + + gtk_tree_store_set ( + GTK_TREE_STORE (source_store), &iter, + E_SOURCE_STORE_COLUMN_CLIENT, client, -1); + +exit: + if (client != NULL) + g_object_unref (client); + + g_object_unref (source); +} + +static void +source_store_set_source_list (ESourceStore *source_store, + ESourceList *source_list) +{ + g_return_if_fail (E_IS_SOURCE_LIST (source_list)); + g_return_if_fail (source_store->priv->source_list == NULL); + + source_store->priv->source_list = g_object_ref (source_list); + + source_store_refresh (source_store); + + g_signal_connect_swapped ( + source_list, "changed", + G_CALLBACK (e_source_store_queue_refresh), source_store); +} + +static void +source_store_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_SOURCE_LIST: + source_store_set_source_list ( + E_SOURCE_STORE (object), + g_value_get_object (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_store_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_SOURCE_LIST: + g_value_set_object ( + value, e_source_store_get_source_list ( + E_SOURCE_STORE (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_store_dispose (GObject *object) +{ + ESourceStorePrivate *priv; + + priv = E_SOURCE_STORE_GET_PRIVATE (object); + + if (priv->refresh_source_id > 0) { + g_source_remove (priv->refresh_source_id); + priv->refresh_source_id = 0; + } + + if (priv->source_list != NULL) { + g_object_unref (priv->source_list); + priv->source_list = NULL; + } + + g_hash_table_remove_all (priv->index); + g_hash_table_remove_all (priv->requests); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_source_store_parent_class)->dispose (object); +} + +static void +source_store_finalize (GObject *object) +{ + ESourceStorePrivate *priv; + + priv = E_SOURCE_STORE_GET_PRIVATE (object); + + g_hash_table_destroy (priv->index); + g_hash_table_destroy (priv->requests); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_source_store_parent_class)->finalize (object); +} + +static void +source_store_source_added (ESourceStore *source_store, + ESource *source) +{ + /* This is a placeholder in case we decide to do something + * here in the future. Subclasses should still chain up. */ +} + +static void +source_store_source_removed (ESourceStore *source_store, + ESource *source) +{ + /* This is a placeholder in case we decide to do something + * here in the future. Subclasses should still chain up. */ +} + +static void +source_store_source_selected (ESourceStore *source_store, + ESource *source) +{ + GCancellable *cancellable; + GHashTable *hash_table; + const gchar *uid; + + uid = e_source_peek_uid (source); + hash_table = source_store->priv->requests; + cancellable = g_hash_table_lookup (hash_table, uid); + + if (cancellable != NULL) + return; + + cancellable = g_cancellable_new (); + g_hash_table_insert (hash_table, g_strdup (uid), cancellable); + + e_source_store_get_client_async ( + source_store, source, G_PRIORITY_DEFAULT, cancellable, + (GAsyncReadyCallback) source_store_get_client_cb, + g_object_ref (source)); +} + +static void +source_store_source_unselected (ESourceStore *source_store, + ESource *source) +{ + GCancellable *cancellable; + GHashTable *hash_table; + const gchar *uid; + + uid = e_source_peek_uid (source); + hash_table = source_store->priv->requests; + cancellable = g_hash_table_lookup (hash_table, uid); + + if (cancellable != NULL) { + g_cancellable_cancel (cancellable); + g_hash_table_remove (hash_table, uid); + } +} + +typedef struct { + ESource *source; + GObject *client; +} GetClientData; + +static void +source_store_get_client_data_free (GetClientData *data) +{ + if (data->source != NULL) + g_object_unref (data->source); + + if (data->client != NULL) + g_object_unref (data->client); + + g_slice_free (GetClientData, data); +} + +static void +source_store_get_client_thread (GSimpleAsyncResult *simple, + ESourceStore *source_store, + GCancellable *cancellable) +{ + GetClientData *data; + GError *error = NULL; + + data = g_simple_async_result_get_op_res_gpointer (simple); + g_return_if_fail (data != NULL && data->source != NULL); + + data->client = e_source_store_get_client ( + source_store, data->source, cancellable, &error); + + if (error != NULL) { + g_simple_async_result_set_from_error (simple, error); + g_error_free (error); + } +} + +static GObject * +source_store_get_client (ESourceStore *source_store, + ESource *source, + GCancellable *cancellable, + GError **error) +{ + /* Subclasses should override this. */ + + g_set_error ( + error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("Operation not supported")); + + return NULL; +} + +static void +source_store_get_client_async (ESourceStore *source_store, + ESource *source, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GObject *object = G_OBJECT (source_store); + GSimpleAsyncResult *simple; + GetClientData *data; + + data = g_slice_new0 (GetClientData); + data->source = g_object_ref (source); + + simple = g_simple_async_result_new ( + object, callback, user_data, + e_source_store_get_client_async); + + g_simple_async_result_set_op_res_gpointer ( + simple, data, (GDestroyNotify) + source_store_get_client_data_free); + + g_simple_async_result_run_in_thread ( + simple, (GSimpleAsyncThreadFunc) + source_store_get_client_thread, + io_priority, cancellable); + + g_object_unref (simple); +} + +static GObject * +source_store_get_client_finish (ESourceStore *source_store, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + GetClientData *data; + + g_return_val_if_fail ( + g_simple_async_result_is_valid ( + result, G_OBJECT (source_store), + e_source_store_get_client_async), NULL); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + data = g_simple_async_result_get_op_res_gpointer (simple); + g_return_val_if_fail (data != NULL, NULL); + + if (data->client != NULL) + return g_object_ref (data->client); + + return NULL; +} + +static void +e_source_store_class_init (ESourceStoreClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (ESourceStorePrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = source_store_set_property; + object_class->get_property = source_store_get_property; + object_class->dispose = source_store_dispose; + object_class->finalize = source_store_finalize; + + class->source_added = source_store_source_added; + class->source_removed = source_store_source_removed; + class->source_selected = source_store_source_selected; + class->source_unselected = source_store_source_unselected; + + class->get_client = source_store_get_client; + class->get_client_async = source_store_get_client_async; + class->get_client_finish = source_store_get_client_finish; + + g_object_class_install_property ( + object_class, + PROP_SOURCE_LIST, + g_param_spec_object ( + "source-list", + "Source List", + NULL, + E_TYPE_SOURCE_LIST, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + signals[SOURCE_ADDED] = g_signal_new ( + "source-added", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ESourceStoreClass, source_added), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + E_TYPE_SOURCE); + + signals[SOURCE_REMOVED] = g_signal_new ( + "source-removed", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ESourceStoreClass, source_removed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + E_TYPE_SOURCE); + + signals[SOURCE_SELECTED] = g_signal_new ( + "source-selected", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ESourceStoreClass, source_selected), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + E_TYPE_SOURCE); + + signals[SOURCE_UNSELECTED] = g_signal_new ( + "source-unselected", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ESourceStoreClass, source_unselected), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + E_TYPE_SOURCE); +} + +static void +e_source_store_init (ESourceStore *source_store) +{ + GType types[E_SOURCE_STORE_NUM_COLUMNS]; + GHashTable *index; + GHashTable *requests; + gint column = 0; + + index = g_hash_table_new_full ( + g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) gtk_tree_row_reference_free); + + requests = g_hash_table_new_full ( + g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_object_unref); + + source_store->priv = E_SOURCE_STORE_GET_PRIVATE (source_store); + + source_store->priv->index = index; + source_store->priv->requests = requests; + + types[column++] = G_TYPE_OBJECT; /* COLUMN_SOURCE */ + types[column++] = G_TYPE_BOOLEAN; /* COLUMN_SELECTED */ + types[column++] = G_TYPE_OBJECT; /* COLUMN_CLIENT */ + + g_assert (column == E_SOURCE_STORE_NUM_COLUMNS); + + gtk_tree_store_set_column_types ( + GTK_TREE_STORE (source_store), + G_N_ELEMENTS (types), types); +} + +/** + * e_source_store_new: + * @source_list: an #ESourceList + * + * Creates a new #ESourceStore instance that keeps itself synchronized + * with @source_list. + * + * Returns: a new #ESourceStore instance + **/ +GtkTreeModel * +e_source_store_new (ESourceList *source_list) +{ + g_return_val_if_fail (E_IS_SOURCE_LIST (source_list), NULL); + + return g_object_new ( + E_TYPE_SOURCE_STORE, + "source-list", source_list, NULL); +} + +/** + * e_source_store_get_source_list: + * @source_store: an #ESourceStore + * + * Returns the #ESourceList with which @source_store is synchronizing + * itself. + * + * Returns: the #ESourceList passed to e_source_store_new() + **/ +ESourceList * +e_source_store_get_source_list (ESourceStore *source_store) +{ + g_return_val_if_fail (E_IS_SOURCE_STORE (source_store), NULL); + + return source_store->priv->source_list; +} + +/** + * e_source_store_queue_refresh: + * @source_store: an #ESourceStore + * + * Queues a request for @source_store to syncrhonize itself with its + * internal #ESourceList. This function gets called automatically when + * the internal #ESourceList emits change notifications. The actual + * synchronization happens in an idle callback. + **/ +void +e_source_store_queue_refresh (ESourceStore *source_store) +{ + guint source_id; + + g_return_if_fail (E_IS_SOURCE_STORE (source_store)); + + if (source_store->priv->refresh_source_id > 0) + return; + + source_id = gdk_threads_add_idle ( + (GSourceFunc) source_store_refresh, source_store); + + source_store->priv->refresh_source_id = source_id; +} + +/** + * e_source_store_get_iter_from_source: + * @source_store: an #ESourceStore + * @source: an #ESource + * @iter: an uninitialized #GtkTreeIter + * + * Sets @iter to a valid iterator pointing to @source. + * + * Returns: %TRUE, if @iter was set + **/ +gboolean +e_source_store_get_iter_from_source (ESourceStore *source_store, + ESource *source, + GtkTreeIter *iter) +{ + GtkTreeRowReference *reference; + GHashTable *hash_table; + const gchar *uid; + + g_return_val_if_fail (E_IS_SOURCE_STORE (source_store), FALSE); + g_return_val_if_fail (E_IS_SOURCE (source), FALSE); + + uid = e_source_peek_uid (source); + hash_table = source_store->priv->index; + reference = g_hash_table_lookup (hash_table, uid); + + if (!gtk_tree_row_reference_valid (reference)) + return FALSE; + + if (iter != NULL) { + GtkTreeModel *model; + GtkTreePath *path; + + 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); + } + + return TRUE; +} + +/** + * e_source_store_select_source: + * @source_store: an #ESourceStore + * @source: an #ESource + * + * Marks @source as selected and attempts to open a client connection + * for @source via e_source_store_get_client(). + * + * <note> + * <para> + * #ESourceStore itself does not know how to open client connections. + * Subclasses must provide this capability. + * </para> + * </note> + **/ +void +e_source_store_select_source (ESourceStore *source_store, + ESource *source) +{ + GtkTreeIter iter; + gboolean selected; + + g_return_if_fail (E_IS_SOURCE_STORE (source_store)); + g_return_if_fail (E_IS_SOURCE (source)); + + if (!e_source_store_get_iter_from_source (source_store, source, &iter)) + return; + + /* Avoid emitting unnecessary "row-changed" signals. */ + + gtk_tree_model_get ( + GTK_TREE_MODEL (source_store), &iter, + E_SOURCE_STORE_COLUMN_SELECTED, &selected, -1); + + if (selected) + return; + + gtk_tree_store_set ( + GTK_TREE_STORE (source_store), &iter, + E_SOURCE_STORE_COLUMN_SELECTED, TRUE, -1); + + g_signal_emit (source_store, signals[SOURCE_SELECTED], 0, source); +} + +/** + * e_source_store_unselect_source: + * @source_store: an #ESourceStore + * @source: an #ESource + * + * Marks @source as unselected and closes the corresponding client + * connection, if present. + **/ +void +e_source_store_unselect_source (ESourceStore *source_store, + ESource *source) +{ + GtkTreeIter iter; + gboolean selected; + + g_return_if_fail (E_IS_SOURCE_STORE (source_store)); + g_return_if_fail (E_IS_SOURCE (source)); + + if (!e_source_store_get_iter_from_source (source_store, source, &iter)) + return; + + /* Avoid emitting unnecessary "row-changed" signals. */ + + gtk_tree_model_get ( + GTK_TREE_MODEL (source_store), &iter, + E_SOURCE_STORE_COLUMN_SELECTED, &selected, -1); + + if (!selected) + return; + + gtk_tree_store_set ( + GTK_TREE_STORE (source_store), &iter, + E_SOURCE_STORE_COLUMN_SELECTED, FALSE, + E_SOURCE_STORE_COLUMN_CLIENT, NULL, -1); + + g_signal_emit (source_store, signals[SOURCE_UNSELECTED], 0, source); +} + +/** + * e_source_store_is_selected: + * @source_store: an #ESourceStore + * @source: an #ESource + * + * Returns %TRUE if @source is selected. + * + * Returns: %TRUE, if @source is selected + **/ +gboolean +e_source_store_is_selected (ESourceStore *source_store, + ESource *source) +{ + GtkTreeIter iter; + gboolean selected = FALSE; + + g_return_val_if_fail (E_IS_SOURCE_STORE (source_store), FALSE); + g_return_val_if_fail (E_IS_SOURCE (source), FALSE); + + if (!e_source_store_get_iter_from_source (source_store, source, &iter)) + return FALSE; + + gtk_tree_model_get ( + GTK_TREE_MODEL (source_store), &iter, + E_SOURCE_STORE_COLUMN_SELECTED, &selected, -1); + + return selected; +} + +/* Helper for e_source_store_get_selection() */ +static gboolean +source_store_get_selection_foreach_cb (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + GSList **list) +{ + ESource *source; + gboolean selected; + + /* Groups are at depth 1, sources are at depth 2. */ + if (gtk_tree_path_get_depth (path) != 2) + return FALSE; + + gtk_tree_model_get ( + model, iter, + E_SOURCE_STORE_COLUMN_SOURCE, &source, + E_SOURCE_STORE_COLUMN_SELECTED, &selected, -1); + + g_return_val_if_fail (E_IS_SOURCE (source), TRUE); + + if (selected) + *list = g_slist_prepend (*list, source); + else + g_object_unref (source); + + return FALSE; +} + +/** + * e_source_store_get_selected: + * @source_store: an #ESourceStore + * + * Returns a #GSList of currently selected sources. The returned list + * should be freed as follows: + * + * <informalexample> + * <programlisting> + * g_slist_foreach (list, (GFunc) g_object_unref, NULL); + * g_slist_free (list); + * </programlisting> + * </informalexample> + * + * Returns: a #GSList of selected sources + **/ +GSList * +e_source_store_get_selected (ESourceStore *source_store) +{ + GSList *list = NULL; + + /* XXX Only returning GSList here because ESourceSelector does. + * I would prefer GList, but too late to change it. */ + + g_return_val_if_fail (E_IS_SOURCE_STORE (source_store), NULL); + + gtk_tree_model_foreach ( + GTK_TREE_MODEL (source_store), (GtkTreeModelForeachFunc) + source_store_get_selection_foreach_cb, &list); + + return g_slist_reverse (list); +} + +/** + * e_source_store_get_client: + * @source_store: an #ESourceStore + * @source: an #ESource + * @cancellable: optional #GCancellable, or %NULL to ignore + * @error: return location for a #GError, or %NULL + * + * Returns a client connection for @source, creating and opening it first + * if necessary. If an error occurs, the function returns %NULL and sets + * @error. + * + * If @cancellable is not %NULL, then the operation can be cancelled by + * triggering the cancellable object from another thread. If the operation + * was cancelled, a #G_IO_ERROR_CANCELLED will be reported. + * + * This function may block for a long time, and should not be called from + * a thread running a #GMainLoop. See e_source_store_get_client_async() + * for an asynchronous version of this call. + * + * <note> + * <para> + * #ESourceStore itself does not know how to open client connections + * and will simply report a #G_IO_ERROR_NOT_SUPPORTED and return %NULL. + * Subclasses must provide this capability. + * </para> + * </note> + * + * Returns: a client connection for @source, or %NULL on error + **/ +GObject * +e_source_store_get_client (ESourceStore *source_store, + ESource *source, + GCancellable *cancellable, + GError **error) +{ + ESourceStoreClass *class; + + g_return_val_if_fail (E_IS_SOURCE_STORE (source_store), NULL); + g_return_val_if_fail (E_IS_SOURCE (source), NULL); + + class = E_SOURCE_STORE_GET_CLASS (source_store); + g_return_val_if_fail (class->get_client != NULL, NULL); + + return class->get_client (source_store, source, cancellable, error); +} + +/** + * e_source_store_get_client_async: + * @source_store: an #ESourceStore + * @source: an #ESource + * @io_priority: the <link linkend="io-priority">I/O priority</link> + * of the request + * @cancellable: optional #GCancellable, or %NULL to ignore + * @callback: a #GAsyncReadyCallback to call when the request is satisfied + * @user_data: the data to pass to the callback function + * + * Asynchronously gets a client connection for @source, creating and + * opening it first if necessary. + * + * For more details, see e_source_store_get_client() which is the + * synchronous version of this call. + * + * When the operation is finished, @callback will be called. You can + * then call e_source_store_get_client_finish() to get the result of the + * operation. + **/ +void +e_source_store_get_client_async (ESourceStore *source_store, + ESource *source, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + ESourceStoreClass *class; + + g_return_if_fail (E_IS_SOURCE_STORE (source_store)); + g_return_if_fail (E_IS_SOURCE (source)); + + class = E_SOURCE_STORE_GET_CLASS (source_store); + g_return_if_fail (class->get_client_async != NULL); + + class->get_client_async ( + source_store, source, io_priority, + cancellable, callback, user_data); +} + +/** + * e_source_store_get_client_finish: + * @source_store: an #ESourceStore + * @result: a #GAsyncResult + * @error: return location for a #GError, or %NULL + * + * Finishes an asynchronous client operation started with + * e_source_store_get_client_async(). + * + * Returns: a client connection, or %NULL on error + **/ +GObject * +e_source_store_get_client_finish (ESourceStore *source_store, + GAsyncResult *result, + GError **error) +{ + ESourceStoreClass *class; + + g_return_val_if_fail (E_IS_SOURCE_STORE (source_store), NULL); + g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL); + + if (G_IS_SIMPLE_ASYNC_RESULT (result)) { + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); + if (g_simple_async_result_propagate_error (simple, error)) + return NULL; + } + + class = E_SOURCE_STORE_GET_CLASS (source_store); + g_return_val_if_fail (class->get_client_finish != NULL, NULL); + + return class->get_client_finish (source_store, result, error); +} diff --git a/libedataserverui/e-source-store.h b/libedataserverui/e-source-store.h new file mode 100644 index 000000000..c68b8f6e0 --- /dev/null +++ b/libedataserverui/e-source-store.h @@ -0,0 +1,136 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* e-source-store.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU Lesser General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef E_SOURCE_STORE_H +#define E_SOURCE_STORE_H + +#include <gtk/gtk.h> +#include <libedataserver/e-source-list.h> + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_STORE \ + (e_source_store_get_type ()) +#define E_SOURCE_STORE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_STORE, ESourceStore)) +#define E_SOURCE_STORE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_STORE, ESourceStoreClass)) +#define E_IS_SOURCE_STORE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_STORE)) +#define E_IS_SOURCE_STORE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_STORE)) +#define E_SOURCE_STORE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_STORE, ESourceStoreClass)) + +G_BEGIN_DECLS + +typedef struct _ESourceStore ESourceStore; +typedef struct _ESourceStoreClass ESourceStoreClass; +typedef struct _ESourceStorePrivate ESourceStorePrivate; + +enum { + E_SOURCE_STORE_COLUMN_SOURCE, /* G_TYPE_OBJECT */ + E_SOURCE_STORE_COLUMN_SELECTED, /* G_TYPE_BOOLEAN */ + E_SOURCE_STORE_COLUMN_CLIENT, /* G_TYPE_OBJECT */ + E_SOURCE_STORE_NUM_COLUMNS +}; + +struct _ESourceStore { + GtkTreeStore parent; + ESourceStorePrivate *priv; +}; + +struct _ESourceStoreClass { + GtkTreeStoreClass parent_class; + + /* Signals */ + void (*source_added) (ESourceStore *source_store, + ESource *source); + void (*source_removed) (ESourceStore *source_store, + ESource *source); + void (*source_selected) (ESourceStore *source_store, + ESource *source); + void (*source_unselected) (ESourceStore *source_store, + ESource *source); + + /* Padding for additional signals. */ + void (*_reserved_signal_0) (void); + void (*_reserved_signal_1) (void); + void (*_reserved_signal_2) (void); + void (*_reserved_signal_3) (void); + + /* Methods */ + GObject * (*get_client) (ESourceStore *source_store, + ESource *source, + GCancellable *cancellable, + GError **error); + void (*get_client_async) (ESourceStore *source_store, + ESource *source, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + GObject * (*get_client_finish) (ESourceStore *source_store, + GAsyncResult *result, + GError **error); + + /* Padding for additional methods. */ + void (*_reserved_method_0) (void); + void (*_reserved_method_1) (void); + void (*_reserved_method_2) (void); + void (*_reserved_method_3) (void); + void (*_reserved_method_4) (void); + void (*_reserved_method_5) (void); +}; + +GType e_source_store_get_type (void); +GtkTreeModel * e_source_store_new (ESourceList *source_list); +ESourceList * e_source_store_get_source_list (ESourceStore *source_store); +void e_source_store_queue_refresh (ESourceStore *source_store); +gboolean e_source_store_get_iter_from_source + (ESourceStore *source_store, + ESource *source, + GtkTreeIter *iter); +void e_source_store_select_source (ESourceStore *source_store, + ESource *source); +void e_source_store_unselect_source (ESourceStore *source_store, + ESource *source); +gboolean e_source_store_is_selected (ESourceStore *source_store, + ESource *source); +GSList * e_source_store_get_selected (ESourceStore *source_store); +GObject * e_source_store_get_client (ESourceStore *source_store, + ESource *source, + GCancellable *cancellable, + GError **error); +void e_source_store_get_client_async (ESourceStore *source_store, + ESource *source, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GObject * e_source_store_get_client_finish(ESourceStore *source_store, + GAsyncResult *result, + GError **error); + +G_END_DECLS + +#endif /* E_SOURCE_STORE_H */ diff --git a/libedataserverui/test-source-selector.c b/libedataserverui/test-source-selector.c index 70833e277..8764e6acc 100644 --- a/libedataserverui/test-source-selector.c +++ b/libedataserverui/test-source-selector.c @@ -81,6 +81,10 @@ on_idle_create_widget (const gchar *gconf_path) window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size (GTK_WINDOW (window), 200, 300); + g_signal_connect ( + window, "delete-event", + G_CALLBACK (gtk_main_quit), NULL); + vbox = gtk_vbox_new (FALSE, 3); gtk_container_add (GTK_CONTAINER (window), vbox); |