aboutsummaryrefslogtreecommitdiffstats
path: root/mail
diff options
context:
space:
mode:
authorMilan Crha <mcrha@redhat.com>2010-05-06 23:19:07 +0800
committerMilan Crha <mcrha@redhat.com>2010-05-06 23:19:07 +0800
commit0b743a787cf5cc69b2521d41e1c7f5ec8a798101 (patch)
tree34013d59118135640291f814f3124bd60b8187ca /mail
parent518c616d27de69fe92834440a22c1c45982c3d0f (diff)
downloadgsoc2013-evolution-0b743a787cf5cc69b2521d41e1c7f5ec8a798101.tar
gsoc2013-evolution-0b743a787cf5cc69b2521d41e1c7f5ec8a798101.tar.gz
gsoc2013-evolution-0b743a787cf5cc69b2521d41e1c7f5ec8a798101.tar.bz2
gsoc2013-evolution-0b743a787cf5cc69b2521d41e1c7f5ec8a798101.tar.lz
gsoc2013-evolution-0b743a787cf5cc69b2521d41e1c7f5ec8a798101.tar.xz
gsoc2013-evolution-0b743a787cf5cc69b2521d41e1c7f5ec8a798101.tar.zst
gsoc2013-evolution-0b743a787cf5cc69b2521d41e1c7f5ec8a798101.zip
Bug #240317 - Allow searching in subscribe dialog
Diffstat (limited to 'mail')
-rw-r--r--mail/em-subscribe-editor.c509
-rw-r--r--mail/mail-dialogs.ui98
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++;
@@ -488,52 +621,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:"<root>"));
-
- /* 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)
{
struct _zsubscribe_msg *m;
@@ -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, &gtiter);
gtk_list_store_set (
store, &gtiter,
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 @@
<property name="orientation">vertical</property>
<property name="spacing">12</property>
<child>
- <object class="GtkHBox" id="hbox1">
+ <object class="GtkTable" id="top_items_table">
<property name="visible">True</property>
- <property name="spacing">12</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">3</property>
+ <property name="column_spacing">12</property>
+ <property name="row_spacing">6</property>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="label" translatable="yes">S_erver:</property>
<property name="use_underline">True</property>
+ <property name="xalign">1</property>
</object>
<packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
</packing>
</child>
<child>
@@ -222,9 +229,12 @@
<property name="visible">True</property>
</object>
<packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
</packing>
</child>
<child>
@@ -233,10 +243,40 @@
<property name="pulse_step">0.10000000149</property>
</object>
<packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="pack_type">end</property>
- <property name="position">2</property>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="filterlabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">S_how only items containing:</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">1</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="filter_entry">
+ <property name="visible">True</property>
+ <property name="secondary_icon_stock">gtk-clear</property>
+ <property name="secondary_icon_activatable">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
</packing>
</child>
</object>
@@ -268,6 +308,36 @@
<property name="visible">True</property>
<property name="layout_style">end</property>
<child>
+ <object class="GtkButton" id="expand_button">
+ <property name="label" translatable="yes">E_xpand all</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="collapse_button">
+ <property name="label" translatable="yes">Collapse _all</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkButton" id="refresh_button">
<property name="label">gtk-refresh</property>
<property name="visible">True</property>
@@ -279,7 +349,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
- <property name="position">0</property>
+ <property name="position">2</property>
</packing>
</child>
<child>
@@ -294,7 +364,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
- <property name="position">1</property>
+ <property name="position">3</property>
</packing>
</child>
</object>