[evolution/wip/webkit2] Bug 442398 - Option to reorder and hide calendar groups
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution/wip/webkit2] Bug 442398 - Option to reorder and hide calendar groups
- Date: Tue, 1 Mar 2016 13:47:53 +0000 (UTC)
commit 4e279eec1f294e8f68b6c39bd8756511c593f877
Author: Milan Crha <mcrha redhat com>
Date: Tue Sep 22 17:42:02 2015 +0200
Bug 442398 - Option to reorder and hide calendar groups
.../evolution-util/evolution-util-sections.txt | 3 +
e-util/e-source-selector.c | 735 ++++++++++++++++++++
e-util/e-source-selector.h | 9 +
modules/addressbook/e-book-shell-sidebar.c | 3 +
modules/addressbook/e-book-shell-view-actions.c | 26 +
modules/calendar/e-cal-base-shell-sidebar.c | 3 +
modules/calendar/e-cal-shell-view-actions.c | 26 +
modules/calendar/e-memo-shell-view-actions.c | 26 +
modules/calendar/e-task-shell-view-actions.c | 27 +
po/POTFILES.in | 1 +
ui/evolution-calendars.ui | 3 +
ui/evolution-contacts.ui | 3 +
ui/evolution-memos.ui | 3 +
ui/evolution-tasks.ui | 3 +
14 files changed, 871 insertions(+), 0 deletions(-)
---
diff --git a/doc/reference/evolution-util/evolution-util-sections.txt
b/doc/reference/evolution-util/evolution-util-sections.txt
index 9f063de..d73f89d 100644
--- a/doc/reference/evolution-util/evolution-util-sections.txt
+++ b/doc/reference/evolution-util/evolution-util-sections.txt
@@ -4283,6 +4283,9 @@ e_source_selector_ref_source_by_path
e_source_selector_queue_write
e_source_selector_update_row
e_source_selector_update_all_rows
+e_source_selector_manage_groups
+e_source_selector_save_groups_setup
+e_source_selector_load_groups_setup
<SUBSECTION Standard>
E_SOURCE_SELECTOR
E_IS_SOURCE_SELECTOR
diff --git a/e-util/e-source-selector.c b/e-util/e-source-selector.c
index cb53789..43a732a 100644
--- a/e-util/e-source-selector.c
+++ b/e-util/e-source-selector.c
@@ -23,6 +23,7 @@
#endif
#include <string.h>
+#include <glib/gi18n-lib.h>
#include <libedataserverui/libedataserverui.h>
@@ -59,6 +60,9 @@ struct _ESourceSelectorPrivate {
GtkCellRenderer *busy_renderer;
guint n_busy_sources;
gulong update_busy_renderer_id;
+
+ GHashTable *hidden_groups;
+ GSList *groups_order;
};
struct _AsyncContext {
@@ -361,6 +365,108 @@ source_selector_get_icon_name (ESourceSelector *selector,
}
static gboolean
+source_selector_source_is_enabled_and_selected (ESource *source,
+ const gchar *extension_name)
+{
+ gpointer extension;
+
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+ if (!extension_name ||
+ !e_source_get_enabled (source))
+ return e_source_get_enabled (source);
+
+ if (!e_source_has_extension (source, extension_name))
+ return FALSE;
+
+ extension = e_source_get_extension (source, extension_name);
+ if (!E_IS_SOURCE_SELECTABLE (extension))
+ return TRUE;
+
+ return e_source_selectable_get_selected (extension);
+}
+
+typedef struct {
+ const gchar *extension_name;
+ gboolean any_selected;
+} LookupSelectedData;
+
+static gboolean
+source_selector_lookup_selected_cb (GNode *node,
+ gpointer user_data)
+{
+ LookupSelectedData *data = user_data;
+ ESource *source;
+
+ g_return_val_if_fail (data != NULL, TRUE);
+ g_return_val_if_fail (data->extension_name != NULL, TRUE);
+
+ source = node->data;
+ if (!E_IS_SOURCE (source))
+ return TRUE;
+
+ data->any_selected = source_selector_source_is_enabled_and_selected (source, data->extension_name);
+
+ return data->any_selected;
+}
+
+static gboolean
+source_selector_node_is_hidden (ESourceSelector *selector,
+ GNode *main_node)
+{
+ GNode *node;
+ ESource *source;
+ const gchar *extension_name;
+ LookupSelectedData data;
+ gboolean hidden;
+
+ g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
+ g_return_val_if_fail (main_node != NULL, FALSE);
+
+ if (G_NODE_IS_ROOT (main_node))
+ return FALSE;
+
+ extension_name = e_source_selector_get_extension_name (selector);
+ hidden = FALSE;
+
+ /* Check the path to the root, any is hidden, this one can be also hidden */
+ node = main_node;
+ while (node) {
+ source = node->data;
+
+ if (!source || G_NODE_IS_ROOT (node))
+ break;
+
+ if (source_selector_source_is_enabled_and_selected (source, extension_name)) {
+ hidden = FALSE;
+ break;
+ }
+
+ hidden = hidden || g_hash_table_contains (selector->priv->hidden_groups, e_source_get_uid
(source));
+
+ node = node->parent;
+ }
+
+ if (!hidden)
+ return FALSE;
+
+ /* If any source in this subtree/group is enabled and selected,
+ then the group cannot be hidden */
+
+ node = main_node;
+ if (node->parent && !G_NODE_IS_ROOT (node->parent)) {
+ node = node->parent;
+ }
+
+ data.extension_name = extension_name;
+ data.any_selected = FALSE;
+
+ g_node_traverse (node, G_IN_ORDER, G_TRAVERSE_ALL, -1, source_selector_lookup_selected_cb, &data);
+
+ return !data.any_selected;
+}
+
+static gboolean
source_selector_traverse (GNode *node,
ESourceSelector *selector)
{
@@ -375,6 +481,9 @@ source_selector_traverse (GNode *node,
if (G_NODE_IS_ROOT (node))
return FALSE;
+ if (source_selector_node_is_hidden (selector, node))
+ return FALSE;
+
source_index = selector->priv->source_index;
model = gtk_tree_view_get_model (GTK_TREE_VIEW (selector));
@@ -523,6 +632,73 @@ source_selector_load_sources_status (ESourceSelector *selector,
}
static void
+source_selector_sort_groups (ESourceSelector *selector,
+ GNode *root)
+{
+ GHashTable *groups; /* gchar *uid, GUINT index into node_sources */
+ GPtrArray *node_sources; /* GNode * as stored in the root first sub-level */
+ ESource *source;
+ GNode *node;
+ GSList *link;
+ guint ii;
+
+ g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
+ g_return_if_fail (G_NODE_IS_ROOT (root));
+
+ if (!selector->priv->groups_order ||
+ !g_node_n_children (root))
+ return;
+
+ groups = g_hash_table_new (g_str_hash, g_str_equal);
+ node_sources = g_ptr_array_sized_new (g_node_n_children (root));
+
+ node = g_node_first_child (root);
+ while (node) {
+ GNode *next_node = g_node_next_sibling (node);
+
+ source = node->data;
+
+ if (source) {
+ g_node_unlink (node);
+
+ g_hash_table_insert (groups, (gpointer) e_source_get_uid (source), GUINT_TO_POINTER
(node_sources->len));
+ g_ptr_array_add (node_sources, node);
+ }
+
+ node = next_node;
+ }
+
+ /* First add known nodes as defined by the user... */
+ for (link = selector->priv->groups_order; link; link = g_slist_next (link)) {
+ const gchar *uid = link->data;
+
+ if (!uid || !g_hash_table_contains (groups, uid))
+ continue;
+
+ ii = GPOINTER_TO_UINT (g_hash_table_lookup (groups, uid));
+ g_warn_if_fail (ii < node_sources->len);
+
+ node = node_sources->pdata[ii];
+ node_sources->pdata[ii] = NULL;
+
+ if (node)
+ g_node_append (root, node);
+ }
+
+ /* ... then add all unknown (new) sources in the order
+ as they were in the passed-in tree */
+ for (ii = 0; ii < node_sources->len; ii++) {
+ node = node_sources->pdata[ii];
+
+ if (node)
+ g_node_append (root, node);
+ }
+
+ g_ptr_array_unref (node_sources);
+ g_hash_table_destroy (groups);
+}
+
+static void
source_selector_build_model (ESourceSelector *selector)
{
ESourceRegistry *registry;
@@ -569,6 +745,8 @@ source_selector_build_model (ESourceSelector *selector)
root = e_source_registry_build_display_tree (registry, extension_name);
+ source_selector_sort_groups (selector, root);
+
g_node_traverse (
root, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
(GNodeTraverseFunc) source_selector_traverse,
@@ -1065,6 +1243,10 @@ source_selector_dispose (GObject *object)
g_hash_table_remove_all (priv->source_index);
g_hash_table_remove_all (priv->pending_writes);
+ g_hash_table_remove_all (priv->hidden_groups);
+
+ g_slist_free_full (priv->groups_order, g_free);
+ priv->groups_order = NULL;
clear_saved_primary_selection (E_SOURCE_SELECTOR (object));
@@ -1081,6 +1263,7 @@ source_selector_finalize (GObject *object)
g_hash_table_destroy (priv->source_index);
g_hash_table_destroy (priv->pending_writes);
+ g_hash_table_destroy (priv->hidden_groups);
g_free (priv->extension_name);
@@ -1685,6 +1868,7 @@ e_source_selector_init (ESourceSelector *selector)
selector->priv = E_SOURCE_SELECTOR_GET_PRIVATE (selector);
selector->priv->pending_writes = pending_writes;
+ selector->priv->hidden_groups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
selector->priv->main_context = g_main_context_get_thread_default ();
if (selector->priv->main_context != NULL)
@@ -2840,3 +3024,554 @@ e_source_selector_get_source_is_busy (ESourceSelector *selector,
return is_busy;
}
+
+static gboolean
+source_selector_get_source_hidden (ESourceSelector *selector,
+ ESource *source)
+{
+ g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (e_source_get_uid (source) != NULL, FALSE);
+
+ return g_hash_table_contains (selector->priv->hidden_groups, e_source_get_uid (source));
+}
+
+static void
+tree_show_toggled (GtkCellRendererToggle *renderer,
+ gchar *path_str,
+ gpointer user_data)
+{
+ GtkWidget *table = user_data;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ path = gtk_tree_path_new_from_string (path_str);
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (table));
+
+ if (gtk_tree_model_get_iter (model, &iter, path)) {
+ gboolean shown = TRUE;
+
+ gtk_tree_model_get (model, &iter, 2, &shown, -1);
+ shown = !shown;
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter, 2, shown, -1);
+
+ /* to have buttons synced with the change */
+ g_signal_emit_by_name (table, "cursor-changed");
+ }
+
+ gtk_tree_path_free (path);
+}
+
+static GtkWidget *
+create_tree (ESourceSelector *selector,
+ GtkWidget **tree)
+{
+ ESourceRegistry *registry;
+ GtkWidget *table, *scrolled;
+ GtkTreeSelection *selection;
+ GtkCellRenderer *renderer;
+ GtkListStore *model;
+ GNode *root;
+
+ scrolled = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), GTK_SHADOW_IN);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+ model = gtk_list_store_new (3, G_TYPE_STRING, E_TYPE_SOURCE, G_TYPE_BOOLEAN);
+ table = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (table), FALSE);
+
+ /* Cannot select/unselect sources, thus also cannot hide them */
+ if (e_source_selector_get_show_toggles (selector)) {
+ renderer = gtk_cell_renderer_toggle_new ();
+ g_object_set (G_OBJECT (renderer), "activatable", TRUE, NULL);
+ gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (table), -1,
+ _("Show"), renderer,
+ "active", 2, NULL);
+ g_signal_connect (renderer, "toggled", G_CALLBACK (tree_show_toggled), table);
+ }
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (table), -1,
+ _("Group name"), renderer,
+ "text", 0, NULL);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (table));
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+
+ gtk_container_add (GTK_CONTAINER (scrolled), table);
+
+ *tree = table;
+
+ registry = e_source_selector_get_registry (selector);
+ root = e_source_registry_build_display_tree (registry, e_source_selector_get_extension_name
(selector));
+
+ source_selector_sort_groups (selector, root);
+
+ if (root) {
+ GNode *node;
+
+ for (node = g_node_first_child (root); node; node = g_node_next_sibling (node)) {
+ GtkTreeIter iter;
+ ESource *source;
+
+ source = node->data;
+
+ if (source) {
+ gtk_list_store_append (model, &iter);
+ gtk_list_store_set (model, &iter,
+ 0, e_source_get_display_name (source),
+ 1, source,
+ 2, !source_selector_get_source_hidden (selector, source),
+ -1);
+ }
+ }
+ }
+
+ e_source_registry_free_display_tree (root);
+
+ g_object_unref (model);
+
+ return scrolled;
+}
+
+static void
+process_move_button (GtkButton *button,
+ GtkTreeView *tree,
+ gboolean is_up,
+ gboolean do_move)
+{
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ gboolean enable = FALSE;
+
+ g_return_if_fail (button != NULL);
+ g_return_if_fail (tree != NULL);
+
+ selection = gtk_tree_view_get_selection (tree);
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ gpointer ptr = NULL, ptr2;
+ GtkTreeIter iter2;
+ int i, cnt = gtk_tree_model_iter_n_children (model, NULL);
+ gboolean can_move = FALSE;
+
+ gtk_tree_model_get (model, &iter, 1, &ptr, -1);
+
+ for (i = 0; i < cnt; i++) {
+ if (!gtk_tree_model_iter_nth_child (model, &iter2, NULL, i))
+ break;
+
+ ptr2 = NULL;
+ gtk_tree_model_get (model, &iter2, 1, &ptr2, -1);
+
+ if (ptr == ptr2 || (is_up && !do_move && i > 0)) {
+ can_move = TRUE;
+ break;
+ }
+ }
+
+ if (can_move)
+ can_move = ((is_up && i > 0) || (!is_up && i + 1 < cnt)) && i < cnt;
+
+ if (can_move && do_move) {
+ i = i + (is_up ? -1 : 1);
+ if (gtk_tree_model_iter_nth_child (model, &iter2, NULL, i)) {
+ GtkTreePath *path;
+
+ gtk_list_store_swap (GTK_LIST_STORE (model), &iter, &iter2);
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ /* scroll to the selected cell */
+ path = gtk_tree_model_get_path (model, &iter);
+ gtk_tree_view_scroll_to_cell (tree, path, NULL, FALSE, 0.0, 0.0);
+ gtk_tree_path_free (path);
+
+ /* cursor has been moved to the other row */
+ can_move = (is_up && i > 0) || (!is_up && i + 1 < cnt);
+
+ g_signal_emit_by_name (tree, "cursor-changed");
+ }
+ }
+
+ enable = can_move;
+ }
+
+ if (!do_move)
+ gtk_widget_set_sensitive (GTK_WIDGET (button), enable);
+}
+
+static void
+up_clicked (GtkButton *button,
+ GtkTreeView *tree)
+{
+ process_move_button (button, tree, TRUE, TRUE);
+}
+
+static void
+up_cursor_changed (GtkTreeView *tree,
+ GtkButton *button)
+{
+ process_move_button (button, tree, TRUE, FALSE);
+}
+
+static void
+down_clicked (GtkButton *button,
+ GtkTreeView *tree)
+{
+ process_move_button (button, tree, FALSE, TRUE);
+}
+
+static void
+down_cursor_changed (GtkTreeView *tree,
+ GtkButton *button)
+{
+ process_move_button (button, tree, FALSE, FALSE);
+}
+
+static void
+show_hide_cursor_changed (GtkTreeView *tree,
+ GtkButton *button)
+{
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+
+ g_return_if_fail (button != NULL);
+ g_return_if_fail (tree != NULL);
+
+ selection = gtk_tree_view_get_selection (tree);
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ gboolean shown = FALSE;
+
+ gtk_tree_model_get (model, &iter, 2, &shown, -1);
+
+ gtk_button_set_label (button, shown ? _("_Hide") : _("_Show"));
+ }
+}
+
+static void
+show_hide_clicked (GtkButton *button,
+ GtkTreeView *tree)
+{
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+
+ g_return_if_fail (button != NULL);
+ g_return_if_fail (tree != NULL);
+
+ selection = gtk_tree_view_get_selection (tree);
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ gboolean shown = TRUE;
+
+ gtk_tree_model_get (model, &iter, 2, &shown, -1);
+ shown = !shown;
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter, 2, shown, -1);
+
+ show_hide_cursor_changed (tree, button);
+ }
+}
+
+/**
+ * e_source_selector_manage_groups:
+ * @selector: an #ESourceSelector
+ *
+ * Manages list of groups, like their order in the source selector,
+ * and a hidden property of the group.
+ *
+ * Returns: Whether user confirmed changes in the dialog.
+ *
+ * Since: 3.20
+ **/
+gboolean
+e_source_selector_manage_groups (ESourceSelector *selector)
+{
+ GtkWidget *dlg, *box, *pbox, *tree, *w, *w2;
+ gchar *txt;
+ gboolean confirmed = FALSE;
+
+ g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
+
+ w = gtk_widget_get_toplevel (GTK_WIDGET (selector));
+ if (!w || !gtk_widget_is_toplevel (w))
+ w = NULL;
+
+ dlg = gtk_dialog_new_with_buttons (_("Manage Groups"),
+ w ? GTK_WINDOW (w) : NULL,
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
+ GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ w = gtk_dialog_get_content_area (GTK_DIALOG (dlg));
+ box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_container_set_border_width (GTK_CONTAINER (box), 12);
+ gtk_box_pack_start (GTK_BOX (w), box, TRUE, TRUE, 0);
+
+ txt = g_strconcat ("<b>", _("Available Groups:"), "</b>", NULL);
+ w = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (w), txt);
+ g_free (txt);
+ gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (box), w, FALSE, FALSE, 2);
+
+ pbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
+ gtk_box_pack_start (GTK_BOX (box), pbox, TRUE, TRUE, 2);
+
+ /* space on the left */
+ w = gtk_label_new ("");
+ gtk_box_pack_start (GTK_BOX (pbox), w, FALSE, FALSE, 6);
+
+ w = create_tree (selector, &tree);
+ gtk_widget_set_size_request (w, 200, 240);
+ gtk_box_pack_start (GTK_BOX (pbox), w, TRUE, TRUE, 2);
+
+ /* box of buttons */
+ w2 = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (w2), GTK_BUTTONBOX_START);
+ gtk_box_pack_start (GTK_BOX (pbox), w2, FALSE, FALSE, 2);
+
+ #define add_button(_x,_y,_cb,_cb2) \
+ w = (_x) ? gtk_button_new_from_icon_name (_x, GTK_ICON_SIZE_BUTTON) : gtk_button_new (); \
+ gtk_button_set_label (GTK_BUTTON (w), _y); \
+ gtk_button_set_use_underline (GTK_BUTTON (w), TRUE); \
+ gtk_box_pack_start (GTK_BOX (w2), w, FALSE, FALSE, 2); \
+ g_signal_connect (w, "clicked", (GCallback)_cb, tree); \
+ g_signal_connect (tree, "cursor-changed", (GCallback)_cb2, w);
+
+ add_button ("go-up", _("_Up"), up_clicked, up_cursor_changed);
+ add_button ("go-down", _("_Down"), down_clicked, down_cursor_changed);
+
+ if (e_source_selector_get_show_toggles (selector)) {
+ add_button (NULL, _("_Show"), show_hide_clicked, show_hide_cursor_changed);
+ gtk_button_set_use_underline (GTK_BUTTON (w), TRUE);
+ }
+
+ #undef add_button
+
+ gtk_widget_show_all (box);
+
+ if (gtk_dialog_run (GTK_DIALOG (dlg)) == GTK_RESPONSE_ACCEPT) {
+ GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
+ GtkTreeIter iter;
+ gint ii, cnt = gtk_tree_model_iter_n_children (model, NULL);
+
+ g_hash_table_remove_all (selector->priv->hidden_groups);
+ g_slist_free_full (selector->priv->groups_order, g_free);
+ selector->priv->groups_order = NULL;
+
+ for (ii = 0; ii < cnt; ii++) {
+ gpointer group = NULL;
+ gboolean shown = TRUE;
+
+ if (!gtk_tree_model_iter_nth_child (model, &iter, NULL, ii))
+ break;
+
+ gtk_tree_model_get (model, &iter, 1, &group, 2, &shown, -1);
+
+ if (group) {
+ const gchar *uid = e_source_get_uid (group);
+
+ selector->priv->groups_order = g_slist_prepend (selector->priv->groups_order,
g_strdup (uid));
+
+ if (!shown)
+ g_hash_table_insert (selector->priv->hidden_groups, g_strdup (uid),
GINT_TO_POINTER (1));
+ }
+ }
+
+ selector->priv->groups_order = g_slist_reverse (selector->priv->groups_order);
+
+ source_selector_build_model (selector);
+
+ confirmed = TRUE;
+ }
+
+ gtk_widget_destroy (dlg);
+
+ return confirmed;
+}
+
+static gboolean
+source_selector_store_value (GKeyFile *key_file,
+ const gchar *group_key,
+ const gchar * const *value,
+ gsize value_length)
+{
+ gchar **stored;
+ gsize length = 0, ii;
+ gboolean changed = FALSE;
+
+ g_return_val_if_fail (key_file != NULL, FALSE);
+ g_return_val_if_fail (group_key != NULL, FALSE);
+
+ stored = g_key_file_get_string_list (key_file, E_SOURCE_SELECTOR_GROUPS_SETUP_NAME, group_key,
&length, NULL);
+ if (stored) {
+ changed = value_length != length;
+ if (!changed) {
+ for (ii = 0; ii < length && !changed; ii++) {
+ changed = g_strcmp0 (value[ii], stored[ii]) != 0;
+ }
+ }
+
+ g_strfreev (stored);
+ } else {
+ changed = value != NULL;
+ }
+
+ if (changed) {
+ if (value)
+ g_key_file_set_string_list (key_file, E_SOURCE_SELECTOR_GROUPS_SETUP_NAME, group_key,
value, value_length);
+ else
+ changed = g_key_file_remove_key (key_file, E_SOURCE_SELECTOR_GROUPS_SETUP_NAME,
group_key, NULL);
+ }
+
+ return changed;
+}
+
+/**
+ * e_source_selector_save_groups_setup:
+ * @selector: an #ESourceSelector
+ * @key_file: a #GKeyFile to store the sgroups setup to
+ *
+ * Stores current setup of the groups in the @key_file.
+ *
+ * Use e_source_selector_load_groups_setup() to pass the settings
+ * back to the @selector.
+ *
+ * Returns: Whether the saved values are different, aka whether it's
+ * required to store the changes.
+ *
+ * Since: 3.20
+ **/
+gboolean
+e_source_selector_save_groups_setup (ESourceSelector *selector,
+ GKeyFile *key_file)
+{
+ GPtrArray *value;
+ const gchar *extension_name;
+ gchar *group_key;
+ gboolean changed;
+
+ g_return_val_if_fail (E_IS_SOURCE_SELECTOR (selector), FALSE);
+ g_return_val_if_fail (key_file != NULL, FALSE);
+
+ extension_name = e_source_selector_get_extension_name (selector);
+ g_return_val_if_fail (extension_name != NULL, FALSE);
+
+ group_key = g_strconcat (extension_name, "-hidden-groups", NULL);
+
+ if (g_hash_table_size (selector->priv->hidden_groups) > 0) {
+ GHashTableIter iter;
+ gpointer key, unused;
+
+ value = g_ptr_array_sized_new (g_hash_table_size (selector->priv->hidden_groups));
+
+ g_hash_table_iter_init (&iter, selector->priv->hidden_groups);
+ while (g_hash_table_iter_next (&iter, &key, &unused)) {
+ if (key)
+ g_ptr_array_add (value, key);
+ }
+
+ /* expects NULL-terminated array of strings, thus terminate it */
+ g_ptr_array_add (value, NULL);
+
+ changed = source_selector_store_value (key_file, group_key, (const gchar * const *)
value->pdata, value->len - 1);
+
+ g_ptr_array_unref (value);
+ } else {
+ changed = source_selector_store_value (key_file, group_key, NULL, 0);
+ }
+
+ g_free (group_key);
+ group_key = g_strconcat (extension_name, "-groups-order", NULL);
+
+ if (selector->priv->groups_order) {
+ GSList *link;
+
+ value = g_ptr_array_sized_new (g_slist_length (selector->priv->groups_order));
+
+ for (link = selector->priv->groups_order; link; link = g_slist_next (link)) {
+ if (link->data)
+ g_ptr_array_add (value, link->data);
+ }
+
+ /* expects NULL-terminated array of strings, thus terminate it */
+ g_ptr_array_add (value, NULL);
+
+ changed = source_selector_store_value (key_file, group_key, (const gchar * const *)
value->pdata, value->len - 1) || changed;
+
+ g_ptr_array_unref (value);
+ } else {
+ changed = source_selector_store_value (key_file, group_key, NULL, 0) || changed;
+ }
+
+ g_free (group_key);
+
+ return changed;
+}
+
+/**
+ * e_source_selector_load_groups_setup:
+ * @selector: an #ESourceSelector
+ * @key_file: a #GKeyFile to load the groups setup from
+ *
+ * Loads setup of the groups from the @key_file.
+ *
+ * Use e_source_selector_save_groups_setup() to store
+ * the settings of the @selector.
+ *
+ * Since: 3.20
+ **/
+void
+e_source_selector_load_groups_setup (ESourceSelector *selector,
+ GKeyFile *key_file)
+{
+ const gchar *extension_name;
+ gchar **stored;
+ gchar *group_key;
+ gsize ii;
+
+ g_return_if_fail (E_IS_SOURCE_SELECTOR (selector));
+
+ extension_name = e_source_selector_get_extension_name (selector);
+ g_return_if_fail (extension_name != NULL);
+
+ g_hash_table_remove_all (selector->priv->hidden_groups);
+ g_slist_free_full (selector->priv->groups_order, g_free);
+ selector->priv->groups_order = NULL;
+
+ group_key = g_strconcat (extension_name, "-hidden-groups", NULL);
+
+ stored = g_key_file_get_string_list (key_file, E_SOURCE_SELECTOR_GROUPS_SETUP_NAME, group_key, NULL,
NULL);
+ if (stored) {
+ for (ii = 0; stored[ii]; ii++) {
+ g_hash_table_insert (selector->priv->hidden_groups, g_strdup (stored[ii]),
GINT_TO_POINTER (1));
+ }
+
+ g_strfreev (stored);
+ }
+
+ g_free (group_key);
+ group_key = g_strconcat (extension_name, "-groups-order", NULL);
+
+ stored = g_key_file_get_string_list (key_file, E_SOURCE_SELECTOR_GROUPS_SETUP_NAME, group_key, NULL,
NULL);
+ if (stored) {
+ for (ii = 0; stored[ii]; ii++) {
+ selector->priv->groups_order = g_slist_prepend (selector->priv->groups_order,
g_strdup (stored[ii]));
+ }
+
+ g_strfreev (stored);
+ }
+
+ g_free (group_key);
+
+ selector->priv->groups_order = g_slist_reverse (selector->priv->groups_order);
+
+ source_selector_build_model (selector);
+}
diff --git a/e-util/e-source-selector.h b/e-util/e-source-selector.h
index e186601..afccfa6 100644
--- a/e-util/e-source-selector.h
+++ b/e-util/e-source-selector.h
@@ -47,6 +47,8 @@
(G_TYPE_INSTANCE_GET_CLASS \
((obj), E_TYPE_SOURCE_SELECTOR, ESourceSelectorClass))
+#define E_SOURCE_SELECTOR_GROUPS_SETUP_NAME "SourceSelector"
+
G_BEGIN_DECLS
typedef struct _ESourceSelector ESourceSelector;
@@ -157,6 +159,13 @@ void e_source_selector_set_source_is_busy
gboolean e_source_selector_get_source_is_busy
(ESourceSelector *selector,
ESource *source);
+gboolean e_source_selector_manage_groups (ESourceSelector *selector);
+gboolean e_source_selector_save_groups_setup
+ (ESourceSelector *selector,
+ GKeyFile *key_file);
+void e_source_selector_load_groups_setup
+ (ESourceSelector *selector,
+ GKeyFile *key_file);
G_END_DECLS
diff --git a/modules/addressbook/e-book-shell-sidebar.c b/modules/addressbook/e-book-shell-sidebar.c
index 7a40caf..97e31d0 100644
--- a/modules/addressbook/e-book-shell-sidebar.c
+++ b/modules/addressbook/e-book-shell-sidebar.c
@@ -167,6 +167,9 @@ book_shell_sidebar_constructed (GObject *object)
priv->selector = g_object_ref (widget);
gtk_widget_show (widget);
+ e_source_selector_load_groups_setup (E_SOURCE_SELECTOR (priv->selector),
+ e_shell_view_get_state_key_file (shell_view));
+
settings = e_util_ref_settings ("org.gnome.evolution.addressbook");
g_settings_bind_with_mapping (
diff --git a/modules/addressbook/e-book-shell-view-actions.c b/modules/addressbook/e-book-shell-view-actions.c
index a2ae672..06c9c5d 100644
--- a/modules/addressbook/e-book-shell-view-actions.c
+++ b/modules/addressbook/e-book-shell-view-actions.c
@@ -83,6 +83,21 @@ action_address_book_delete_cb (GtkAction *action,
}
static void
+action_address_book_manage_groups_cb (GtkAction *action,
+ EBookShellView *book_shell_view)
+{
+ EShellView *shell_view;
+ ESourceSelector *selector;
+
+ shell_view = E_SHELL_VIEW (book_shell_view);
+ selector = e_book_shell_sidebar_get_selector (book_shell_view->priv->book_shell_sidebar);
+
+ if (e_source_selector_manage_groups (selector) &&
+ e_source_selector_save_groups_setup (selector, e_shell_view_get_state_key_file (shell_view)))
+ e_shell_view_set_state_dirty (shell_view);
+}
+
+static void
action_address_book_move_cb (GtkAction *action,
EBookShellView *book_shell_view)
{
@@ -932,6 +947,13 @@ static GtkActionEntry contact_entries[] = {
N_("Delete the selected address book"),
G_CALLBACK (action_address_book_delete_cb) },
+ { "address-book-manage-groups",
+ NULL,
+ N_("_Manage Address Book groups..."),
+ NULL,
+ N_("Manage task list groups order and visibility"),
+ G_CALLBACK (action_address_book_manage_groups_cb) },
+
{ "address-book-move",
"folder-move",
N_("Mo_ve All Contacts To..."),
@@ -1067,6 +1089,10 @@ static EPopupActionEntry contact_popup_entries[] = {
N_("_Delete"),
"address-book-delete" },
+ { "address-book-popup-manage-groups",
+ N_("_Manage groups..."),
+ "address-book-manage-groups" },
+
{ "address-book-popup-properties",
N_("_Properties"),
"address-book-properties" },
diff --git a/modules/calendar/e-cal-base-shell-sidebar.c b/modules/calendar/e-cal-base-shell-sidebar.c
index 73c9d96..d3ce3e1 100644
--- a/modules/calendar/e-cal-base-shell-sidebar.c
+++ b/modules/calendar/e-cal-base-shell-sidebar.c
@@ -732,6 +732,9 @@ cal_base_shell_sidebar_constructed (GObject *object)
cal_base_shell_sidebar->priv->selector = E_SOURCE_SELECTOR (widget);
gtk_container_add (GTK_CONTAINER (container), widget);
+ e_source_selector_load_groups_setup (cal_base_shell_sidebar->priv->selector,
+ e_shell_view_get_state_key_file (shell_view));
+
if (add_navigator) {
ECalendarItem *calitem;
diff --git a/modules/calendar/e-cal-shell-view-actions.c b/modules/calendar/e-cal-shell-view-actions.c
index af7855f..acd0216 100644
--- a/modules/calendar/e-cal-shell-view-actions.c
+++ b/modules/calendar/e-cal-shell-view-actions.c
@@ -131,6 +131,21 @@ action_calendar_jump_to_cb (GtkAction *action,
}
static void
+action_calendar_manage_groups_cb (GtkAction *action,
+ ECalShellView *cal_shell_view)
+{
+ EShellView *shell_view;
+ ESourceSelector *selector;
+
+ shell_view = E_SHELL_VIEW (cal_shell_view);
+ selector = e_cal_base_shell_sidebar_get_selector (cal_shell_view->priv->cal_shell_sidebar);
+
+ if (e_source_selector_manage_groups (selector) &&
+ e_source_selector_save_groups_setup (selector, e_shell_view_get_state_key_file (shell_view)))
+ e_shell_view_set_state_dirty (shell_view);
+}
+
+static void
action_calendar_new_cb (GtkAction *action,
ECalShellView *cal_shell_view)
{
@@ -1228,6 +1243,13 @@ static GtkActionEntry calendar_entries[] = {
N_("Select a specific date"),
G_CALLBACK (action_calendar_jump_to_cb) },
+ { "calendar-manage-groups",
+ NULL,
+ N_("_Manage Calendar groups..."),
+ NULL,
+ N_("Manage Calendar groups order and visibility"),
+ G_CALLBACK (action_calendar_manage_groups_cb) },
+
{ "calendar-new",
"x-office-calendar",
N_("_New Calendar"),
@@ -1442,6 +1464,10 @@ static EPopupActionEntry calendar_popup_entries[] = {
NULL,
"calendar-jump-to" },
+ { "calendar-popup-manage-groups",
+ N_("_Manage groups..."),
+ "calendar-manage-groups" },
+
{ "calendar-popup-properties",
NULL,
"calendar-properties" },
diff --git a/modules/calendar/e-memo-shell-view-actions.c b/modules/calendar/e-memo-shell-view-actions.c
index 51e71f0..56e2353 100644
--- a/modules/calendar/e-memo-shell-view-actions.c
+++ b/modules/calendar/e-memo-shell-view-actions.c
@@ -133,6 +133,21 @@ action_memo_list_delete_cb (GtkAction *action,
}
static void
+action_memo_list_manage_groups_cb (GtkAction *action,
+ EMemoShellView *memo_shell_view)
+{
+ EShellView *shell_view;
+ ESourceSelector *selector;
+
+ shell_view = E_SHELL_VIEW (memo_shell_view);
+ selector = e_cal_base_shell_sidebar_get_selector (memo_shell_view->priv->memo_shell_sidebar);
+
+ if (e_source_selector_manage_groups (selector) &&
+ e_source_selector_save_groups_setup (selector, e_shell_view_get_state_key_file (shell_view)))
+ e_shell_view_set_state_dirty (shell_view);
+}
+
+static void
action_memo_list_new_cb (GtkAction *action,
EMemoShellView *memo_shell_view)
{
@@ -559,6 +574,13 @@ static GtkActionEntry memo_entries[] = {
N_("Delete the selected memo list"),
G_CALLBACK (action_memo_list_delete_cb) },
+ { "memo-list-manage-groups",
+ NULL,
+ N_("_Manage Memo List groups..."),
+ NULL,
+ N_("Manage Memo List groups order and visibility"),
+ G_CALLBACK (action_memo_list_manage_groups_cb) },
+
{ "memo-list-new",
"stock_notes",
N_("_New Memo List"),
@@ -635,6 +657,10 @@ static EPopupActionEntry memo_popup_entries[] = {
N_("_Delete"),
"memo-list-delete" },
+ { "memo-list-popup-manage-groups",
+ N_("_Manage groups..."),
+ "memo-list-manage-groups" },
+
{ "memo-list-popup-properties",
NULL,
"memo-list-properties" },
diff --git a/modules/calendar/e-task-shell-view-actions.c b/modules/calendar/e-task-shell-view-actions.c
index 9e58b75..46373f3 100644
--- a/modules/calendar/e-task-shell-view-actions.c
+++ b/modules/calendar/e-task-shell-view-actions.c
@@ -156,6 +156,22 @@ action_task_list_delete_cb (GtkAction *action,
}
static void
+action_task_list_manage_groups_cb (GtkAction *action,
+ ETaskShellView *task_shell_view)
+{
+ EShellView *shell_view;
+ ESourceSelector *selector;
+
+ shell_view = E_SHELL_VIEW (task_shell_view);
+ selector = e_cal_base_shell_sidebar_get_selector (task_shell_view->priv->task_shell_sidebar);
+
+ if (e_source_selector_manage_groups (selector) &&
+ e_source_selector_save_groups_setup (selector, e_shell_view_get_state_key_file (shell_view)))
+ e_shell_view_set_state_dirty (shell_view);
+}
+
+
+static void
action_task_list_new_cb (GtkAction *action,
ETaskShellView *task_shell_view)
{
@@ -684,6 +700,13 @@ static GtkActionEntry task_entries[] = {
N_("Delete the selected task list"),
G_CALLBACK (action_task_list_delete_cb) },
+ { "task-list-manage-groups",
+ NULL,
+ N_("_Manage Task List groups..."),
+ NULL,
+ N_("Manage task list groups order and visibility"),
+ G_CALLBACK (action_task_list_manage_groups_cb) },
+
{ "task-list-new",
"stock_todo",
N_("_New Task List"),
@@ -788,6 +811,10 @@ static EPopupActionEntry task_popup_entries[] = {
N_("_Delete"),
"task-list-delete" },
+ { "task-list-popup-manage-groups",
+ N_("_Manage groups..."),
+ "task-list-manage-groups" },
+
{ "task-list-popup-properties",
NULL,
"task-list-properties" },
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 01d845e..1e0eeb4 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -270,6 +270,7 @@ e-util/e-send-options.c
[type: gettext/glade]e-util/e-send-options.ui
e-util/e-source-config.c
e-util/e-source-config-dialog.c
+e-util/e-source-selector.c
e-util/e-source-selector-dialog.c
e-util/e-spell-dictionary.c
e-util/e-spell-entry.c
diff --git a/ui/evolution-calendars.ui b/ui/evolution-calendars.ui
index 4aba1ea..f9978d1 100644
--- a/ui/evolution-calendars.ui
+++ b/ui/evolution-calendars.ui
@@ -24,6 +24,7 @@
<menu action='view-menu'>
<menuitem action='calendar-go-today'/>
<menuitem action='calendar-jump-to'/>
+ <menuitem action='calendar-manage-groups'/>
</menu>
<placeholder name='custom-menus'>
<menu action='calendar-actions-menu'>
@@ -69,6 +70,8 @@
<menuitem action='calendar-popup-select-one'/>
<placeholder name='calendar-popup-actions'/>
<separator/>
+ <menuitem action='calendar-popup-manage-groups'/>
+ <separator/>
<menuitem action='calendar-popup-properties'/>
</popup>
<popup name='calendar-empty-popup'>
diff --git a/ui/evolution-contacts.ui b/ui/evolution-contacts.ui
index 36d640f..a153998 100644
--- a/ui/evolution-contacts.ui
+++ b/ui/evolution-contacts.ui
@@ -23,6 +23,7 @@
</placeholder>
</menu>
<menu action='view-menu'>
+ <menuitem action='address-book-manage-groups'/>
<placeholder name='view-custom-menus'>
<menu action='contact-preview-menu'>
<menuitem action='contact-preview'/>
@@ -63,8 +64,10 @@
<menuitem action='address-book-popup-save-as'/>
<separator/>
<menuitem action='address-book-popup-delete'/>
+ <separator/>
<placeholder name='address-book-popup-actions'/>
<separator/>
+ <menuitem action='address-book-popup-manage-groups'/>
<separator/>
<menuitem action='address-book-popup-map'/>
<separator/>
diff --git a/ui/evolution-memos.ui b/ui/evolution-memos.ui
index 81bad8a..2f34206 100644
--- a/ui/evolution-memos.ui
+++ b/ui/evolution-memos.ui
@@ -22,6 +22,7 @@
</placeholder>
</menu>
<menu action='view-menu'>
+ <menuitem action='memo-list-manage-groups'/>
<placeholder name='view-custom-menus'>
<menu action='memo-preview-menu'>
<menuitem action='memo-preview'/>
@@ -66,6 +67,8 @@
<menuitem action='memo-list-popup-select-one'/>
<placeholder name='memo-list-popup-actions'/>
<separator/>
+ <menuitem action='memo-list-popup-manage-groups'/>
+ <separator/>
<menuitem action='memo-list-popup-properties'/>
</popup>
<popup name='memo-search-options'>
diff --git a/ui/evolution-tasks.ui b/ui/evolution-tasks.ui
index 90617e9..2fc2d01 100644
--- a/ui/evolution-tasks.ui
+++ b/ui/evolution-tasks.ui
@@ -25,6 +25,7 @@
</placeholder>
</menu>
<menu action='view-menu'>
+ <menuitem action='task-list-manage-groups'/>
<placeholder name='view-custom-menus'>
<menu action='task-preview-menu'>
<menuitem action='task-preview'/>
@@ -79,6 +80,8 @@
<menuitem action='task-list-popup-select-one'/>
<placeholder name='task-list-popup-actions'/>
<separator/>
+ <menuitem action='task-list-popup-manage-groups'/>
+ <separator/>
<menuitem action='task-list-popup-properties'/>
</popup>
<popup name='task-search-options'>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]