From 0b743a787cf5cc69b2521d41e1c7f5ec8a798101 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Thu, 6 May 2010 17:19:07 +0200 Subject: Bug #240317 - Allow searching in subscribe dialog --- mail/em-subscribe-editor.c | 509 +++++++++++++++++++++++++++++++++++---------- mail/mail-dialogs.ui | 98 +++++++-- 2 files changed, 482 insertions(+), 125 deletions(-) diff --git a/mail/em-subscribe-editor.c b/mail/em-subscribe-editor.c index e1e0e1d629..ebec3e2035 100644 --- a/mail/em-subscribe-editor.c +++ b/mail/em-subscribe-editor.c @@ -35,6 +35,7 @@ #include "e-util/e-account-utils.h" #include "e-util/e-util-private.h" +#include "em-folder-utils.h" #include "em-subscribe-editor.h" #include "mail-config.h" @@ -43,6 +44,15 @@ #define d(x) +enum { + COL_SUBSCRIBED = 0, /* G_TYPE_BOOLEAN */ + COL_NAME, /* G_TYPE_STRING */ + COL_INFO_NODE, /* G_TYPE_POINTER */ + COL_CAN_SELECT, /* G_TYPE_BOOLEAN */ + COL_ICON_NAME, /* G_TYPE_STRING */ + N_COLUMNS +}; + typedef struct _EMSubscribeEditor EMSubscribeEditor; struct _EMSubscribeEditor { GQueue stores; @@ -50,6 +60,9 @@ struct _EMSubscribeEditor { gint busy; guint busy_id; + gboolean is_filtering; /* whether filtering is active */ + guint refilter_id; /* source ID of a refilter action, after change in the filter edit */ + struct _EMSubscribe *current; /* the current one, if any */ GtkDialog *dialog; @@ -57,6 +70,10 @@ struct _EMSubscribeEditor { GtkWidget *combobox; GtkWidget *none_selected; /* 'please select a xxx' message */ GtkWidget *progress; + GtkWidget *filter_entry; /* when not empty, then it's filtering */ + GtkWidget *expand_button; + GtkWidget *collapse_button; + GtkWidget *refresh_button; }; typedef struct _EMSubscribe EMSubscribe; @@ -75,13 +92,16 @@ struct _EMSubscribe { GtkWidget *widget; /* widget to show for this store */ GtkTreeView *tree; /* tree, if we have it */ + GtkTreeModel *tree_store; /* a tree store, used when not filtering */ + GtkTreeModel *list_store; /* list store, used when filtering */ + GSList *all_selectable; /* list of selectable info's, stored in the tree_store, in reverse order */ + + GSList *tree_expanded_paths; /* list of expanded paths in the tree model */ /* list of all returns from get_folder_info, accessed by other structures */ GSList *info_list; - /* pending LISTs, EMSubscribeNode's */ gint pending_id; - GQueue pending; /* queue of pending UN/SUBSCRIBEs, EMsg's */ gint subscribe_id; @@ -108,6 +128,80 @@ static void sub_editor_busy(EMSubscribeEditor *se, gint dir); static gint sub_queue_fill_level(EMSubscribe *sub, EMSubscribeNode *node); static void sub_selection_changed(GtkTreeSelection *selection, EMSubscribe *sub); +static gboolean +test_contains (const gchar *where, const gchar *what) +{ + gunichar c; + const gchar *at = what; + + if (!what || !where) + return TRUE; + + while (c = g_utf8_get_char_validated (where, -1), c != 0 && c != (gunichar) -1 && c != (gunichar) -2) { + if (g_utf8_get_char (at) == g_unichar_tolower (c)) { + at = g_utf8_next_char (at); + if (!at || !*at) + return TRUE; + } else { + at = what; + } + where = g_utf8_next_char (where); + } + + return FALSE; +} + +static void +update_filtering_column (EMSubscribeEditor *se, struct _EMSubscribe *sub) +{ + gchar *text; + GtkTreeIter iter; + GtkTreeModel *list_store; + GSList *l; + + g_return_if_fail (se != NULL); + g_return_if_fail (sub != NULL); + g_return_if_fail (g_utf8_validate (gtk_entry_get_text (GTK_ENTRY (se->filter_entry)), -1, NULL)); + + if (!sub->tree) + return; + + if (gtk_tree_view_get_model (sub->tree) == sub->list_store) + gtk_tree_view_set_model (sub->tree, NULL); + + text = g_utf8_strdown (gtk_entry_get_text (GTK_ENTRY (se->filter_entry)), -1); + list_store = sub->list_store; + + gtk_list_store_clear (GTK_LIST_STORE (list_store)); + for (l = sub->all_selectable; l; l = l->next) { + EMSubscribeNode *node = l->data; + gboolean bl; + + if (!node || !node->path || !node->info) + continue; + + bl = (!text || !*text || (node && node->info && node->info->full_name && test_contains (node->info->full_name, text))); + if (!bl) + continue; + + gtk_list_store_prepend ((GtkListStore *)list_store, &iter); + gtk_list_store_set (GTK_LIST_STORE (list_store), &iter, + COL_SUBSCRIBED, (node->info->flags & CAMEL_FOLDER_SUBSCRIBED) != 0, + COL_NAME, node->info->full_name, + COL_INFO_NODE, node, + COL_CAN_SELECT, TRUE, + COL_ICON_NAME, em_folder_utils_get_icon_name (node->info->flags), + -1); + } + + g_free (text); + + if (!gtk_tree_view_get_model (sub->tree)) { + gtk_tree_view_set_model (sub->tree, sub->list_store); + gtk_tree_view_set_search_column (sub->tree, COL_NAME); + } +} + static void sub_node_free(EMSubscribeNode *node) { @@ -133,8 +227,15 @@ sub_unref(EMSubscribe *sub) d(printf("subscribe object finalised\n")); /* we dont have to delete the "subscribe" task list, as it must be empty, otherwise we wouldn't be unreffed (intentional circular reference) */ + if (sub->tree_store) + g_object_unref (sub->tree_store); + if (sub->list_store) + g_object_unref (sub->list_store); if (sub->folders) g_hash_table_destroy(sub->folders); + g_slist_free (sub->all_selectable); + g_slist_foreach (sub->tree_expanded_paths, (GFunc) gtk_tree_path_free, NULL); + g_slist_free (sub->tree_expanded_paths); l = sub->info_list; while (l) { GSList *n = l->next; @@ -192,13 +293,14 @@ sub_folder_done (struct _zsubscribe_msg *m) } /* make sure the tree view matches the correct state */ - model = gtk_tree_view_get_model(m->sub->tree); + /* all actions are done on tree store, synced to list store */ + model = m->sub->tree_store; if (gtk_tree_model_get_iter_from_string(model, &iter, m->path)) { issub = (m->node->info->flags & CAMEL_FOLDER_SUBSCRIBED) != 0; - gtk_tree_model_get(model, &iter, 0, &subscribed, 2, &node, -1); - if (node == m->node) - gtk_tree_store_set((GtkTreeStore *)model, &iter, 0, issub, -1); - else { + gtk_tree_model_get(model, &iter, COL_SUBSCRIBED, &subscribed, COL_INFO_NODE, &node, -1); + if (node == m->node) { + gtk_tree_store_set ((GtkTreeStore *)model, &iter, COL_SUBSCRIBED, issub, -1); + } else { d(printf("node mismatch, or subscribe state changed failed\n")); } } @@ -264,14 +366,15 @@ sub_subscribe_folder (EMSubscribe *sub, EMSubscribeNode *node, gint state, const /* ********************************************************************** */ static void -sub_fill_level(EMSubscribe *sub, CamelFolderInfo *info, GtkTreeIter *parent, gint pending) +sub_fill_levels (EMSubscribe *sub, CamelFolderInfo *info, GtkTreeIter *parent) { CamelFolderInfo *fi; GtkTreeStore *treestore; GtkTreeIter iter; EMSubscribeNode *node; - treestore = (GtkTreeStore *)gtk_tree_view_get_model(sub->tree); + treestore = (GtkTreeStore *) sub->tree_store; + g_return_if_fail (treestore != NULL); /* first, fill a level up */ fi = info; @@ -285,12 +388,25 @@ sub_fill_level(EMSubscribe *sub, CamelFolderInfo *info, GtkTreeIter *parent, gi node = g_malloc0(sizeof(*node)); node->info = fi; state = (fi->flags & CAMEL_FOLDER_SUBSCRIBED) != 0; - gtk_tree_store_set(treestore, &iter, 0, state, 1, fi->name, 2, node, -1); + gtk_tree_store_set (treestore, &iter, + COL_SUBSCRIBED, state, + COL_NAME, fi->name, + COL_INFO_NODE, node, + COL_CAN_SELECT, (fi->flags & CAMEL_FOLDER_NOSELECT) == 0, + COL_ICON_NAME, em_folder_utils_get_icon_name (fi->flags), + -1); + if ((fi->flags & CAMEL_FOLDER_NOSELECT) == 0) + sub->all_selectable = g_slist_prepend (sub->all_selectable, node); + if (state) { + GtkTreePath *path = gtk_tree_model_get_path ((GtkTreeModel *)treestore, &iter); + gtk_tree_view_expand_to_path (sub->tree, path); + gtk_tree_path_free (path); + } if ((fi->flags & CAMEL_FOLDER_NOINFERIORS) == 0) node->path = gtk_tree_model_get_path((GtkTreeModel *)treestore, &iter); g_hash_table_insert(sub->folders, fi->full_name, node); } else if (node->path) { - gtk_tree_model_get_iter(gtk_tree_view_get_model(sub->tree), &iter, node->path); + gtk_tree_model_get_iter (GTK_TREE_MODEL (treestore), &iter, node->path); known = TRUE; } @@ -303,18 +419,9 @@ sub_fill_level(EMSubscribe *sub, CamelFolderInfo *info, GtkTreeIter *parent, gi /* save time, if we have any children alread, dont re-scan */ if (fi->child) { d(printf("scanning child '%s'\n", fi->child->full_name)); - sub_fill_level(sub, fi->child, &iter, FALSE); + sub_fill_levels (sub, fi->child, &iter); } else if (!(fi->flags & CAMEL_FOLDER_NOCHILDREN)) { - GtkTreeIter new_iter; d(printf("flags: CAMEL_FOLDER_NOCHILDREN is not set '%s', known:%d\n", fi->full_name, known?1:0)); - if (!known) { - gtk_tree_store_append(treestore, &new_iter, &iter); - gtk_tree_store_set(treestore, &new_iter, 0, 0, 1, "Loading...", 2, NULL, -1); - } - } - else { - if (pending) - g_queue_push_tail (&sub->pending, node); } } else { d(printf("%s:%s: fi->flags & CAMEL_FOLDER_NOINFERIORS=%d\t node->path=[%p]\n", @@ -341,21 +448,17 @@ struct _emse_folderinfo_msg { static void sub_folderinfo_exec (struct _emse_folderinfo_msg *m) { - gchar *pub_full_name=NULL; - if (m->seq == m->sub->seq) { - camel_operation_register(m->base.cancel); - m->info = camel_store_get_folder_info(m->sub->store, m->node?m->node->info->full_name:pub_full_name, - CAMEL_STORE_FOLDER_INFO_NO_VIRTUAL | CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST, &m->base.ex); - camel_operation_unregister(m->base.cancel); + camel_operation_register (m->base.cancel); + /* get the full folder tree for search ability */ + m->info = camel_store_get_folder_info (m->sub->store, NULL, CAMEL_STORE_FOLDER_INFO_NO_VIRTUAL | CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST | CAMEL_STORE_FOLDER_INFO_RECURSIVE, &m->base.ex); + camel_operation_unregister (m->base.cancel); } } static void sub_folderinfo_done (struct _emse_folderinfo_msg *m) { - EMSubscribeNode *node; - m->sub->pending_id = -1; if (m->sub->cancel || m->seq != m->sub->seq) return; @@ -369,17 +472,15 @@ sub_folderinfo_done (struct _emse_folderinfo_msg *m) if (m->node) { GtkTreeIter iter; - gtk_tree_model_get_iter(gtk_tree_view_get_model(m->sub->tree), &iter, m->node->path); - sub_fill_level(m->sub, m->info, &iter, FALSE); + gtk_tree_model_get_iter (m->sub->tree_store, &iter, m->node->path); + sub_fill_levels (m->sub, m->info, &iter); } else { - sub_fill_level(m->sub, m->info, NULL, TRUE); + sub_fill_levels (m->sub, m->info, NULL); } - } - /* check for more to do */ - node = g_queue_pop_head (&m->sub->pending); - if (node) - sub_queue_fill_level(m->sub, node); + if (m->sub->editor->is_filtering) + update_filtering_column (m->sub->editor, m->sub); + } } static void @@ -433,6 +534,21 @@ sub_queue_fill_level(EMSubscribe *sub, EMSubscribeNode *node) return id; } +static void +update_buttons_sesitivity (EMSubscribeEditor *se) +{ + gboolean is_tree_model; + + if (!se) + return; + + is_tree_model = se->current && se->current->tree && !se->is_filtering; + + gtk_widget_set_sensitive (se->expand_button, is_tree_model); + gtk_widget_set_sensitive (se->collapse_button, is_tree_model); + gtk_widget_set_sensitive (se->refresh_button, se->current && se->current->tree); +} + /* ********************************************************************** */ /* (un) subscribes the current selection */ @@ -448,11 +564,28 @@ sub_subscribe_toggled(GtkCellRendererToggle *render, const gchar *spath, EMSubsc d(printf("subscribe toggled?\n")); if (gtk_tree_model_get_iter_from_string(model, &iter, spath)) { - gtk_tree_model_get(model, &iter, 0, &subscribed, 2, &node, -1); + gchar *free_path; + + gtk_tree_model_get(model, &iter, COL_SUBSCRIBED, &subscribed, COL_INFO_NODE, &node, -1); + g_return_if_fail (node != NULL); subscribed = !subscribed; d(printf("new state is %s\n", subscribed?"subscribed":"not subscribed")); - gtk_tree_store_set((GtkTreeStore *)model, &iter, 0, subscribed, -1); + if (GTK_IS_TREE_STORE (model)) { + gtk_tree_store_set ((GtkTreeStore *)model, &iter, COL_SUBSCRIBED, subscribed, -1); + } else { + /* it's a list store, convert spath to tree path and update tree store value */ + gtk_list_store_set (GTK_LIST_STORE (model), &iter, COL_SUBSCRIBED, subscribed, -1); + if (gtk_tree_model_get_iter (sub->tree_store, &iter, node->path)) { + gtk_tree_store_set ((GtkTreeStore *)sub->tree_store, &iter, COL_SUBSCRIBED, subscribed, -1); + } + free_path = gtk_tree_path_to_string (node->path); + if (subscribed) + sub->tree_expanded_paths = g_slist_prepend (sub->tree_expanded_paths, gtk_tree_path_copy (node->path)); + spath = free_path; + } + sub_subscribe_folder(sub, node, subscribed, spath); + g_free (free_path); } } @@ -462,7 +595,7 @@ static void sub_do_changed(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter * EMSubscribeNode *node; gboolean subscribed; - gtk_tree_model_get(model, iter, 0, &subscribed, 2, &node, -1); + gtk_tree_model_get(model, iter, COL_SUBSCRIBED, &subscribed, COL_INFO_NODE, &node, -1); if (subscribed) sub->selected_subscribed_count++; @@ -487,52 +620,6 @@ static void sub_row_activated(GtkTreeView *tree, GtkTreePath *path, GtkTreeViewC } -static void -sub_row_expanded(GtkTreeView *tree, GtkTreeIter *iter, GtkTreePath *path, EMSubscribe *sub) -{ - EMSubscribeNode *node; - GtkTreeIter child; - GtkTreeModel *model = (GtkTreeModel *)gtk_tree_view_get_model(tree); - gchar *row_name; - - gtk_tree_model_get(model, iter, 1, &row_name, -1); - d(printf("%s:%s: row-expanded '%s'\n", G_STRLOC, G_STRFUNC, - row_name?row_name:"")); - - /* Do we really need to fetch the children for this row? */ - if (gtk_tree_model_iter_n_children(model, iter) > 1) { - gtk_tree_model_get(model, iter, 2, &node, -1); - if (node->path) { - /* Mark it as already-processed path */ - gtk_tree_path_free(node->path); - node->path=NULL; - } - return; - } else { - gtk_tree_model_iter_children(model, &child, iter); - gtk_tree_model_get(model, &child, 2, &node, -1); - if (!node) { - /* This is the place holder node, delete it and fire-up a pending */ - gtk_tree_store_remove((GtkTreeStore *)model, &child); - gtk_tree_model_get(model, iter, 2, &node, -1); - } else { - gtk_tree_model_get(model, iter, 2, &node, -1); - if (node->path) { - /* Mark it as already-processed path */ - gtk_tree_path_free(node->path); - node->path=NULL; - } - return; - } - } - - g_queue_push_head (&sub->pending, node); - - if (sub->pending_id == -1 - && (node = g_queue_pop_tail (&sub->pending)) != NULL) - sub_queue_fill_level(sub, node); -} - static void sub_destroy(GtkWidget *w, EMSubscribe *sub) { @@ -568,7 +655,6 @@ subscribe_new(EMSubscribeEditor *se, const gchar *uri) sub->editor = se; sub->ref_count = 1; sub->pending_id = -1; - g_queue_init (&sub->pending); sub->subscribe_id = -1; g_queue_init (&sub->subscribe); sub->store_id = -1; @@ -590,9 +676,11 @@ subscribe_set_store(EMSubscribe *sub, CamelStore *store) gtk_widget_show(sub->widget); } else { GtkTreeSelection *selection; + GtkTreeViewColumn *column; GtkCellRenderer *renderer; - GtkTreeStore *model; + sub->all_selectable = NULL; + sub->tree_expanded_paths = NULL; sub->store = store; g_object_ref (store); sub->folders = g_hash_table_new_full ( @@ -600,9 +688,25 @@ subscribe_set_store(EMSubscribe *sub, CamelStore *store) (GDestroyNotify) NULL, (GDestroyNotify) sub_node_free); - model = gtk_tree_store_new (3, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER); - sub->tree = (GtkTreeView *) gtk_tree_view_new_with_model ((GtkTreeModel *) model); - g_object_unref (model); + sub->tree_store = (GtkTreeModel *) gtk_tree_store_new (N_COLUMNS, + G_TYPE_BOOLEAN, /* COL_SUBSCRIBED */ + G_TYPE_STRING, /* COL_NAME */ + G_TYPE_POINTER, /* COL_INFO_NODE */ + G_TYPE_BOOLEAN, /* COL_CAN_SELECT */ + G_TYPE_STRING /* COL_ICON_NAME */ + ); + g_object_ref_sink (sub->tree_store); + + sub->list_store = (GtkTreeModel *) gtk_list_store_new (N_COLUMNS, + G_TYPE_BOOLEAN, /* COL_SUBSCRIBED */ + G_TYPE_STRING, /* COL_NAME */ + G_TYPE_POINTER, /* COL_INFO_NODE */ + G_TYPE_BOOLEAN, /* COL_CAN_SELECT */ + G_TYPE_STRING /* COL_ICON_NAME */ + ); + g_object_ref_sink (sub->list_store); + + sub->tree = (GtkTreeView *) gtk_tree_view_new_with_model (sub->editor->is_filtering ? sub->list_store : sub->tree_store); gtk_widget_show ((GtkWidget *)sub->tree); sub->widget = gtk_scrolled_window_new (NULL, NULL); @@ -613,18 +717,31 @@ subscribe_set_store(EMSubscribe *sub, CamelStore *store) renderer = gtk_cell_renderer_toggle_new (); g_object_set(renderer, "activatable", TRUE, NULL); - gtk_tree_view_insert_column_with_attributes (sub->tree, -1, _("Subscribed"), renderer, "active", 0, NULL); + gtk_tree_view_insert_column_with_attributes (sub->tree, -1, _("Subscribed"), renderer, "active", COL_SUBSCRIBED, "visible", COL_CAN_SELECT, NULL); g_signal_connect(renderer, "toggled", G_CALLBACK(sub_subscribe_toggled), sub); + column = gtk_tree_view_column_new (); + gtk_tree_view_column_set_title (column, _("Folder")); + gtk_tree_view_append_column (sub->tree, column); + + renderer = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_add_attribute ( + column, renderer, "icon-name", COL_ICON_NAME); + renderer = gtk_cell_renderer_text_new (); - gtk_tree_view_insert_column_with_attributes (sub->tree, -1, _("Folder"), renderer, "text", 1, NULL); + gtk_tree_view_column_pack_start (column, renderer, TRUE); + gtk_tree_view_column_add_attribute ( + column, renderer, "text", COL_NAME); gtk_tree_view_set_expander_column(sub->tree, gtk_tree_view_get_column(sub->tree, 1)); selection = gtk_tree_view_get_selection (sub->tree); gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE); gtk_tree_view_set_headers_visible (sub->tree, FALSE); - g_signal_connect(sub->tree, "row-expanded", G_CALLBACK(sub_row_expanded), sub); + gtk_tree_view_set_search_column (sub->tree, COL_NAME); + gtk_tree_view_set_enable_search (sub->tree, TRUE); + g_signal_connect(sub->tree, "row-activated", G_CALLBACK(sub_row_activated), sub); g_signal_connect(sub->tree, "destroy", G_CALLBACK(sub_destroy), sub); @@ -632,6 +749,8 @@ subscribe_set_store(EMSubscribe *sub, CamelStore *store) g_signal_connect(selection, "changed", G_CALLBACK(sub_selection_changed), sub); sub_queue_fill_level(sub, NULL); + + update_buttons_sesitivity (sub->editor); } gtk_box_pack_start((GtkBox *)sub->editor->vbox, sub->widget, TRUE, TRUE, 0); @@ -644,6 +763,9 @@ sub_editor_destroy(GtkWidget *w, EMSubscribeEditor *se) d(printf("editor destroyed, freeing editor\n")); if (se->busy_id) g_source_remove(se->busy_id); + if (se->refilter_id != 0) + g_source_remove (se->refilter_id); + se->refilter_id = 0; g_free(se); } @@ -672,9 +794,15 @@ sub_editor_refresh(GtkWidget *w, EMSubscribeEditor *se) mail_msg_wait(sub->pending_id); } - gtk_tree_store_clear((GtkTreeStore *)gtk_tree_view_get_model(sub->tree)); + g_slist_free (sub->all_selectable); + sub->all_selectable = NULL; + + g_slist_foreach (sub->tree_expanded_paths, (GFunc) gtk_tree_path_free, NULL); + g_slist_free (sub->tree_expanded_paths); + sub->tree_expanded_paths = NULL; - g_queue_init (&sub->pending); + gtk_tree_store_clear ((GtkTreeStore *)sub->tree_store); + gtk_list_store_clear ((GtkListStore *)sub->list_store); if (sub->folders) g_hash_table_destroy(sub->folders); @@ -714,11 +842,9 @@ sub_editor_combobox_changed (GtkWidget *w, EMSubscribeEditor *se) d(printf("combobox changed\n")); - i = 1; + i = 0; n = gtk_combo_box_get_active (GTK_COMBO_BOX (se->combobox)); - if (n == 0) { - gtk_widget_show (se->none_selected); - } else { + if (n != -1) { GtkTreeIter iter; GtkTreeModel *model; @@ -726,14 +852,27 @@ sub_editor_combobox_changed (GtkWidget *w, EMSubscribeEditor *se) model = gtk_combo_box_get_model (GTK_COMBO_BOX (se->combobox)); if (gtk_tree_model_get_iter_first (model, &iter)) { - /* hide the first item */ - gtk_list_store_set ( - GTK_LIST_STORE (model), &iter, - 1, FALSE, - -1); + gboolean is_account = TRUE; + + gtk_tree_model_get (model, &iter, 1, &is_account, -1); + + if (!is_account && n > 0) { + /* the first node it not an account node, it's the notice + about "select account please", thus remove it completely */ + gtk_list_store_remove (GTK_LIST_STORE (model), &iter); + n--; + } else if (!is_account) { + gtk_widget_show (se->none_selected); + i++; + } } } + if (se->refilter_id != 0) { + g_source_remove (se->refilter_id); + se->refilter_id = 0; + } + se->current = NULL; link = g_queue_peek_head_link (&se->stores); while (link != NULL) { @@ -755,6 +894,11 @@ sub_editor_combobox_changed (GtkWidget *w, EMSubscribeEditor *se) link = g_list_next (link); } + + update_buttons_sesitivity (se); + + if (se->current && se->is_filtering) + update_filtering_column (se, se->current); } static gboolean sub_editor_timeout(EMSubscribeEditor *se) @@ -800,6 +944,137 @@ window_size_allocate (GtkWidget *widget, GtkAllocation *allocation) g_object_unref (gconf); } +static void +store_expanded_rows_cb (GtkTreeView *tree_view, GtkTreePath *path, gpointer data) +{ + GSList **slist = data; + + g_return_if_fail (path != NULL); + g_return_if_fail (data != NULL); + + *slist = g_slist_prepend (*slist, gtk_tree_path_copy (path)); +} + +static void +expand_to_path_cb (GtkTreePath *path, GtkTreeView *tree_view) +{ + g_return_if_fail (path != NULL); + g_return_if_fail (tree_view != NULL); + + gtk_tree_view_expand_to_path (tree_view, path); +} + +static void +change_filtering_models (EMSubscribeEditor *se, gboolean turn_on) +{ + GList *link; + + link = g_queue_peek_head_link (&se->stores); + while (link != NULL) { + struct _EMSubscribe *sub = link->data; + + if (sub->widget && sub->tree) { + if (turn_on) { + g_slist_foreach (sub->tree_expanded_paths, (GFunc) gtk_tree_path_free, NULL); + g_slist_free (sub->tree_expanded_paths); + sub->tree_expanded_paths = NULL; + + gtk_tree_view_map_expanded_rows (sub->tree, store_expanded_rows_cb, &sub->tree_expanded_paths); + + gtk_list_store_clear (GTK_LIST_STORE (sub->list_store)); + gtk_tree_view_set_model (sub->tree, sub->list_store); + } else { + gtk_tree_view_set_model (sub->tree, sub->tree_store); + + g_slist_foreach (sub->tree_expanded_paths, (GFunc) expand_to_path_cb, sub->tree); + g_slist_foreach (sub->tree_expanded_paths, (GFunc) gtk_tree_path_free, NULL); + g_slist_free (sub->tree_expanded_paths); + sub->tree_expanded_paths = NULL; + } + + gtk_tree_view_set_search_column (sub->tree, COL_NAME); + } + + link = g_list_next (link); + } + + update_buttons_sesitivity (se); +} + +static gboolean +update_filter_on_timeout_cb (gpointer data) +{ + EMSubscribeEditor *se = data; + + g_return_val_if_fail (se != NULL, FALSE); + + se->refilter_id = 0; + if (se->current) { + /* update filtering options */ + update_filtering_column (se, se->current); + } + + return FALSE; +} + +static void +clear_filter_cb (GtkEntry *entry, GtkEntryIconPosition icon_pos, GdkEvent *event, EMSubscribeEditor *se) +{ + g_return_if_fail (entry != NULL); + + gtk_entry_set_text (entry, ""); +} + +static void +filter_changed_cb (GtkEntry *entry, EMSubscribeEditor *se) +{ + const gchar *text; + gboolean was_filtering; + + g_return_if_fail (entry != NULL); + g_return_if_fail (se != NULL); + + text = gtk_entry_get_text (entry); + was_filtering = se->is_filtering; + se->is_filtering = text && *text; + gtk_entry_set_icon_sensitive (GTK_ENTRY (se->filter_entry), GTK_ENTRY_ICON_SECONDARY, se->is_filtering); + + if (se->refilter_id != 0) { + g_source_remove (se->refilter_id); + se->refilter_id = 0; + } + + if ((was_filtering && !se->is_filtering) || (!was_filtering && se->is_filtering)) { + /* turn on/off filtering - change models */ + change_filtering_models (se, se->is_filtering); + } + + if (se->is_filtering && se->current) + se->refilter_id = g_timeout_add (333, update_filter_on_timeout_cb, se); +} + +static void +expand_all_cb (GtkButton *button, EMSubscribeEditor *se) +{ + g_return_if_fail (se != NULL); + g_return_if_fail (!se->is_filtering); + g_return_if_fail (se->current != NULL); + g_return_if_fail (se->current->tree != NULL); + + gtk_tree_view_expand_all (se->current->tree); +} + +static void +collapse_all_cb (GtkButton *button, EMSubscribeEditor *se) +{ + g_return_if_fail (se != NULL); + g_return_if_fail (!se->is_filtering); + g_return_if_fail (se->current != NULL); + g_return_if_fail (se->current->tree != NULL); + + gtk_tree_view_collapse_all (se->current->tree); +} + GtkWidget * em_subscribe_editor_new(void) { @@ -846,11 +1121,22 @@ em_subscribe_editor_new(void) se->progress = e_builder_get_widget(builder, "progress_bar"); gtk_widget_hide(se->progress); - w = e_builder_get_widget(builder, "close_button"); - g_signal_connect(w, "clicked", G_CALLBACK(sub_editor_close), se); + se->filter_entry = e_builder_get_widget (builder, "filter_entry"); + gtk_entry_set_icon_sensitive (GTK_ENTRY (se->filter_entry), GTK_ENTRY_ICON_SECONDARY, FALSE); + g_signal_connect (se->filter_entry, "icon-press", G_CALLBACK (clear_filter_cb), se); + g_signal_connect (se->filter_entry, "changed", G_CALLBACK (filter_changed_cb), se); - w = e_builder_get_widget(builder, "refresh_button"); - g_signal_connect(w, "clicked", G_CALLBACK(sub_editor_refresh), se); + se->expand_button = e_builder_get_widget (builder, "expand_button"); + g_signal_connect (se->expand_button, "clicked", G_CALLBACK (expand_all_cb), se); + + se->collapse_button = e_builder_get_widget (builder, "collapse_button"); + g_signal_connect (se->collapse_button, "clicked", G_CALLBACK (collapse_all_cb), se); + + se->refresh_button = e_builder_get_widget (builder, "refresh_button"); + g_signal_connect (se->refresh_button, "clicked", G_CALLBACK (sub_editor_refresh), se); + + w = e_builder_get_widget (builder, "close_button"); + g_signal_connect (w, "clicked", G_CALLBACK (sub_editor_close), se); /* setup stores combobox */ se->combobox = e_builder_get_widget (builder, "store_combobox"); @@ -864,14 +1150,13 @@ em_subscribe_editor_new(void) gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (se->combobox), cell, TRUE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (se->combobox), cell, "text", 0, - "visible", 1, NULL); gtk_list_store_append (store, >iter); gtk_list_store_set ( store, >iter, 0, _("No server has been selected"), - 1, TRUE, + 1, FALSE, -1); accounts = e_get_account_list (); @@ -926,5 +1211,7 @@ em_subscribe_editor_new(void) gtk_window_set_default_size ((GtkWindow *) se->dialog, window_size.width, window_size.height); g_signal_connect (se->dialog, "size-allocate", G_CALLBACK (window_size_allocate), NULL); + update_buttons_sesitivity (se); + return GTK_WIDGET (se->dialog); } diff --git a/mail/mail-dialogs.ui b/mail/mail-dialogs.ui index e416aeb649..fa7eda0a21 100644 --- a/mail/mail-dialogs.ui +++ b/mail/mail-dialogs.ui @@ -202,19 +202,26 @@ vertical 12 - + True - 12 + 2 + 3 + 12 + 6 True S_erver: True + 1 - False - False - 0 + 0 + 1 + 0 + 1 + GTK_FILL + @@ -222,9 +229,12 @@ True - False - False - 1 + 1 + 2 + 0 + 1 + GTK_FILL + @@ -233,10 +243,40 @@ 0.10000000149 - False - False - end - 2 + 2 + 3 + 0 + 1 + + + + + True + S_how only items containing: + True + 1 + + + 0 + 1 + 1 + 2 + GTK_FILL + + + + + + True + gtk-clear + True + + + 1 + 3 + 1 + 2 + @@ -267,6 +307,36 @@ True end + + + E_xpand all + True + True + True + False + True + + + False + False + 0 + + + + + Collapse _all + True + True + True + False + True + + + False + False + 1 + + gtk-refresh @@ -279,7 +349,7 @@ False False - 0 + 2 @@ -294,7 +364,7 @@ False False - 1 + 3 -- cgit