From 186fb3f95f60797959a3b35bebbca5dfb08d855c Mon Sep 17 00:00:00 2001 From: hiro Date: Tue, 17 Jan 2006 08:17:41 +0000 Subject: implemented quick search of the summary view. git-svn-id: svn://sylpheed.sraoss.jp/sylpheed/trunk@900 ee746299-78ed-0310-b773-934348b2243d --- src/mainwindow.c | 53 +++++++++++ src/summaryview.c | 279 ++++++++++++++++++++++++++++++++++++------------------ src/summaryview.h | 16 +++- 3 files changed, 256 insertions(+), 92 deletions(-) (limited to 'src') diff --git a/src/mainwindow.c b/src/mainwindow.c index c0bbb417..f8a41f1d 100644 --- a/src/mainwindow.c +++ b/src/mainwindow.c @@ -164,6 +164,10 @@ static void ac_label_button_pressed (GtkWidget *widget, static void ac_menu_popup_closed (GtkMenuShell *menu_shell, gpointer data); +static gboolean main_window_key_pressed (GtkWidget *widget, + GdkEventKey *event, + gpointer data); + static gint main_window_close_cb (GtkWidget *widget, GdkEventAny *event, gpointer data); @@ -837,6 +841,8 @@ MainWindow *main_window_create(SeparateType type) gtk_window_set_title(GTK_WINDOW(window), PROG_VERSION); gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, FALSE); gtk_window_set_wmclass(GTK_WINDOW(window), "main_window", "Sylpheed"); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(main_window_key_pressed), mainwin); if (!geometry.min_height) { geometry.min_width = 320; @@ -2638,6 +2644,49 @@ static void ac_menu_popup_closed(GtkMenuShell *menu_shell, gpointer data) manage_window_focus_in(mainwin->window, NULL, NULL); } +static gboolean main_window_key_pressed(GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + MainWindow *mainwin = (MainWindow *)data; + + if (!mainwin) + return FALSE; + + if (GTK_WIDGET_HAS_FOCUS(mainwin->summaryview->search_entry)) { + /* g_print("keyval: %d, state: %d\n", + event->keyval, event->state); */ + if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) + return FALSE; + + if (gtk_accel_group_query(mainwin->menu_factory->accel_group, + event->keyval, + event->state & ~GDK_LOCK_MASK, + NULL) != NULL) { + gunichar ch; + + ch = gdk_keyval_to_unicode(event->keyval); + if (ch != 0) { + gchar str[7]; + gint len; + gint pos; + GtkEditable *editable; + + editable = GTK_EDITABLE + (mainwin->summaryview->search_entry); + len = g_unichar_to_utf8(ch, str); + gtk_editable_delete_selection(editable); + pos = gtk_editable_get_position(editable); + gtk_editable_insert_text + (editable, str, len, &pos); + gtk_editable_set_position(editable, pos); + return TRUE; + } + } + } + + return FALSE; +} + static gint main_window_close_cb(GtkWidget *widget, GdkEventAny *event, gpointer data) { @@ -3292,6 +3341,10 @@ static void allsel_cb(MainWindow *mainwin, guint action, GtkWidget *widget) if (GTK_WIDGET_HAS_FOCUS(mainwin->summaryview->treeview)) summary_select_all(mainwin->summaryview); + else if (GTK_WIDGET_HAS_FOCUS(mainwin->summaryview->search_entry)) + gtk_editable_select_region + (GTK_EDITABLE(mainwin->summaryview->search_entry), + 0, -1); else if (messageview_is_visible(msgview) && (GTK_WIDGET_HAS_FOCUS(msgview->textview->text) || GTK_WIDGET_HAS_FOCUS(msgview->mimeview->textview->text))) diff --git a/src/summaryview.c b/src/summaryview.c index f6542ec4..58b2d592 100644 --- a/src/summaryview.c +++ b/src/summaryview.c @@ -1,6 +1,6 @@ /* * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * Copyright (C) 1999-2006 Hiroyuki Yamamoto * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include #include @@ -228,6 +230,12 @@ static GtkWidget *summary_tree_view_create (SummaryView *summaryview); /* callback functions */ +static void summary_search_entry_changed(GtkWidget *entry, + SummaryView *summaryview); +static void summary_search_entry_activated + (GtkWidget *entry, + SummaryView *summaryview); + static gboolean summary_toggle_pressed (GtkWidget *eventbox, GdkEventButton *event, SummaryView *summaryview); @@ -443,6 +451,9 @@ SummaryView *summary_create(void) { SummaryView *summaryview; GtkWidget *vbox; + GtkWidget *search_hbox; + GtkWidget *search_label; + GtkWidget *search_entry; GtkWidget *scrolledwin; GtkWidget *treeview; GtkTreeStore *store; @@ -466,6 +477,22 @@ SummaryView *summary_create(void) vbox = gtk_vbox_new(FALSE, 1); + search_hbox = gtk_hbox_new(FALSE, 4); + gtk_container_set_border_width(GTK_CONTAINER(search_hbox), 2); + gtk_box_pack_start(GTK_BOX(vbox), search_hbox, FALSE, FALSE, 0); + + search_label = gtk_label_new(_("Search:")); + gtk_box_pack_start(GTK_BOX(search_hbox), search_label, FALSE, FALSE, 0); + + search_entry = gtk_entry_new(); + gtk_widget_set_size_request(search_entry, 200, -1); + gtk_box_pack_start(GTK_BOX(search_hbox), search_entry, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(search_entry), "changed", + G_CALLBACK(summary_search_entry_changed), summaryview); + g_signal_connect(G_OBJECT(search_entry), "activate", + G_CALLBACK(summary_search_entry_activated), + summaryview); + scrolledwin = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), GTK_POLICY_AUTOMATIC, @@ -521,6 +548,9 @@ SummaryView *summary_create(void) summaryview); summaryview->vbox = vbox; + summaryview->search_hbox = search_hbox; + summaryview->search_label = search_label; + summaryview->search_entry = search_entry; summaryview->scrolledwin = scrolledwin; summaryview->treeview = treeview; summaryview->store = store; @@ -598,7 +628,7 @@ gboolean summary_show(SummaryView *summaryview, FolderItem *item, GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview); GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); GtkTreeIter iter; - GSList *mlist = NULL; + GSList *mlist; gchar *buf; gboolean is_refresh; guint selected_msgnum = 0; @@ -688,12 +718,11 @@ gboolean summary_show(SummaryView *summaryview, FolderItem *item, /* set tree store and hash table from the msginfo list, and create the thread */ summary_set_tree_model_from_list(summaryview, mlist); + summaryview->all_mlist = mlist; if (mlist) gtk_widget_grab_focus(GTK_WIDGET(treeview)); - g_slist_free(mlist); - summary_write_cache(summaryview); item->opened = TRUE; @@ -774,26 +803,11 @@ gboolean summary_show(SummaryView *summaryview, FolderItem *item, return TRUE; } -static gboolean summary_free_msginfo_func(GtkTreeModel *model, - GtkTreePath *path, GtkTreeIter *iter, - gpointer data) -{ - MsgInfo *msginfo; - - gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1); - procmsg_msginfo_free(msginfo); - - return FALSE; -} - void summary_clear_list(SummaryView *summaryview) { GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview); GtkAdjustment *adj; - gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store), - summary_free_msginfo_func, NULL); - if (summaryview->folder_item) { folder_item_close(summaryview->folder_item); summaryview->folder_item = NULL; @@ -812,7 +826,7 @@ void summary_clear_list(SummaryView *summaryview) summary_msgid_table_destroy(summaryview); - summaryview->mlist = NULL; + summaryview->tmp_mlist = NULL; if (summaryview->folder_table) { g_hash_table_destroy(summaryview->folder_table); summaryview->folder_table = NULL; @@ -833,6 +847,16 @@ void summary_clear_list(SummaryView *summaryview) summaryview->drag_list = NULL; } + if (summaryview->flt_mlist) { + g_slist_free(summaryview->flt_mlist); + summaryview->flt_mlist = NULL; + } + gtk_entry_set_text(GTK_ENTRY(summaryview->search_entry), ""); + summaryview->on_filter = FALSE; + + procmsg_msg_list_free(summaryview->all_mlist); + summaryview->all_mlist = NULL; + gtk_tree_view_set_model(treeview, NULL); gtk_tree_store_clear(summaryview->store); gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(summaryview->store)); @@ -932,19 +956,14 @@ GSList *summary_get_selected_msg_list(SummaryView *summaryview) GSList *summary_get_changed_msg_list(SummaryView *summaryview) { - GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); - GtkTreeIter iter; - GSList *mlist = NULL; MsgInfo *msginfo; - gboolean valid; - - valid = gtk_tree_model_get_iter_first(model, &iter); + GSList *mlist = NULL; + GSList *cur; - while (valid) { - gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1); + for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) { + msginfo = (MsgInfo *)cur->data; if (MSG_IS_FLAG_CHANGED(msginfo->flags)) mlist = g_slist_prepend(mlist, msginfo); - valid = gtkut_tree_model_next(model, &iter); } return g_slist_reverse(mlist); @@ -952,21 +971,10 @@ GSList *summary_get_changed_msg_list(SummaryView *summaryview) GSList *summary_get_msg_list(SummaryView *summaryview) { - GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store); - GtkTreeIter iter; - GSList *mlist = NULL; - MsgInfo *msginfo; - gboolean valid; - - valid = gtk_tree_model_get_iter_first(model, &iter); - - while (valid) { - gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1); - mlist = g_slist_prepend(mlist, msginfo); - valid = gtkut_tree_model_next(model, &iter); - } - - return g_slist_reverse(mlist); + if (summaryview->on_filter) + return g_slist_copy(summaryview->flt_mlist); + else + return g_slist_copy(summaryview->all_mlist); } static gboolean summary_msgid_table_create_func(GtkTreeModel *model, @@ -2138,32 +2146,12 @@ struct wcachefp FILE *mark_fp; }; -static gboolean summary_write_cache_func(GtkTreeModel *model, - GtkTreePath *path, GtkTreeIter *iter, - gpointer data) -{ - struct wcachefp *fps = data; - MsgInfo *msginfo; - - gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1); - - if (msginfo->folder->mark_queue != NULL) { - MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW); - } - - if (fps->cache_fp) - procmsg_write_cache(msginfo, fps->cache_fp); - if (fps->mark_fp) - procmsg_write_flags(msginfo, fps->mark_fp); - - return FALSE; -} - gint summary_write_cache(SummaryView *summaryview) { struct wcachefp fps; FolderItem *item; gchar *buf; + GSList *cur; item = summaryview->folder_item; if (!item || !item->path) @@ -2200,8 +2188,17 @@ gint summary_write_cache(SummaryView *summaryview) g_free(buf); } - gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store), - summary_write_cache_func, &fps); + for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) { + MsgInfo *msginfo = (MsgInfo *)cur->data; + + if (msginfo->folder->mark_queue != NULL) { + MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW); + } + if (fps.cache_fp) + procmsg_write_cache(msginfo, fps.cache_fp); + if (fps.mark_fp) + procmsg_write_flags(msginfo, fps.mark_fp); + } if (item->mark_queue) procmsg_flush_mark_queue(item, fps.mark_fp); @@ -3284,6 +3281,11 @@ static void summary_remove_invalid_messages(SummaryView *summaryview) } gtk_tree_store_remove(GTK_TREE_STORE(model), &iter); + summaryview->all_mlist = g_slist_remove(summaryview->all_mlist, + msginfo); + if (summaryview->flt_mlist) + summaryview->flt_mlist = + g_slist_remove(summaryview->flt_mlist, msginfo); procmsg_msginfo_free(msginfo); item->cache_dirty = TRUE; @@ -3339,8 +3341,8 @@ static gboolean summary_execute_move_func(GtkTreeModel *model, g_hash_table_insert(summaryview->folder_table, msginfo->to_folder, GINT_TO_POINTER(1)); - summaryview->mlist = - g_slist_prepend(summaryview->mlist, msginfo); + summaryview->tmp_mlist = + g_slist_prepend(summaryview->tmp_mlist, msginfo); MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE); summary_set_row(summaryview, iter, msginfo); @@ -3359,16 +3361,17 @@ static gint summary_execute_move(SummaryView *summaryview) gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store), summary_execute_move_func, summaryview); - if (summaryview->mlist) { - summaryview->mlist = g_slist_reverse(summaryview->mlist); - val = procmsg_move_messages(summaryview->mlist); + if (summaryview->tmp_mlist) { + summaryview->tmp_mlist = + g_slist_reverse(summaryview->tmp_mlist); + val = procmsg_move_messages(summaryview->tmp_mlist); folder_item_scan_foreach(summaryview->folder_table); folderview_update_item_foreach(summaryview->folder_table, FALSE); - g_slist_free(summaryview->mlist); - summaryview->mlist = NULL; + g_slist_free(summaryview->tmp_mlist); + summaryview->tmp_mlist = NULL; } g_hash_table_destroy(summaryview->folder_table); @@ -3390,8 +3393,8 @@ static gboolean summary_execute_copy_func(GtkTreeModel *model, g_hash_table_insert(summaryview->folder_table, msginfo->to_folder, GINT_TO_POINTER(1)); - summaryview->mlist = - g_slist_prepend(summaryview->mlist, msginfo); + summaryview->tmp_mlist = + g_slist_prepend(summaryview->tmp_mlist, msginfo); MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_COPY); summary_set_row(summaryview, iter, msginfo); @@ -3410,16 +3413,17 @@ static gint summary_execute_copy(SummaryView *summaryview) gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store), summary_execute_copy_func, summaryview); - if (summaryview->mlist) { - summaryview->mlist = g_slist_reverse(summaryview->mlist); - val = procmsg_copy_messages(summaryview->mlist); + if (summaryview->tmp_mlist) { + summaryview->tmp_mlist = + g_slist_reverse(summaryview->tmp_mlist); + val = procmsg_copy_messages(summaryview->tmp_mlist); folder_item_scan_foreach(summaryview->folder_table); folderview_update_item_foreach(summaryview->folder_table, FALSE); - g_slist_free(summaryview->mlist); - summaryview->mlist = NULL; + g_slist_free(summaryview->tmp_mlist); + summaryview->tmp_mlist = NULL; } g_hash_table_destroy(summaryview->folder_table); @@ -3438,8 +3442,8 @@ static gboolean summary_execute_delete_func(GtkTreeModel *model, gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1); if (msginfo && MSG_IS_DELETED(msginfo->flags)) { - summaryview->mlist = - g_slist_prepend(summaryview->mlist, msginfo); + summaryview->tmp_mlist = + g_slist_prepend(summaryview->tmp_mlist, msginfo); } return FALSE; @@ -3459,17 +3463,17 @@ static gint summary_execute_delete(SummaryView *summaryview) gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store), summary_execute_delete_func, summaryview); - if (!summaryview->mlist) return 0; + if (!summaryview->tmp_mlist) return 0; - summaryview->mlist = g_slist_reverse(summaryview->mlist); + summaryview->tmp_mlist = g_slist_reverse(summaryview->tmp_mlist); if (summaryview->folder_item != trash) - val = folder_item_move_msgs(trash, summaryview->mlist); + val = folder_item_move_msgs(trash, summaryview->tmp_mlist); else - val = folder_item_remove_msgs(trash, summaryview->mlist); + val = folder_item_remove_msgs(trash, summaryview->tmp_mlist); - g_slist_free(summaryview->mlist); - summaryview->mlist = NULL; + g_slist_free(summaryview->tmp_mlist); + summaryview->tmp_mlist = NULL; if (summaryview->folder_item != trash) { folder_item_scan(trash); @@ -3751,8 +3755,14 @@ static GNode *summary_get_modified_node(SummaryView *summaryview, g_node_insert_after(parent, sibling, node); parent = node; sibling = NULL; - } else + } else { + summaryview->all_mlist = g_slist_remove(summaryview->all_mlist, + msginfo); + if (summaryview->flt_mlist) + summaryview->flt_mlist = + g_slist_remove(summaryview->flt_mlist, msginfo); procmsg_msginfo_free(msginfo); + } valid = gtk_tree_model_iter_children(model, &child, iter); @@ -4678,9 +4688,98 @@ void summary_get_column_order(SummaryView *summaryview) g_list_free(columns); } +void summary_qsearch_reset(SummaryView *summaryview) +{ + summary_lock(summaryview); + main_window_cursor_wait(summaryview->mainwin); + + gtk_tree_view_set_model(GTK_TREE_VIEW(summaryview->treeview), NULL); + gtk_tree_store_clear(summaryview->store); + gtk_tree_view_set_model(GTK_TREE_VIEW(summaryview->treeview), + GTK_TREE_MODEL(summaryview->store)); + summary_set_tree_model_from_list(summaryview, summaryview->all_mlist); + + main_window_cursor_normal(summaryview->mainwin); + summary_unlock(summaryview); +} + +void summary_qsearch(SummaryView *summaryview) +{ + const gchar *key; + FilterRule *rule; + FilterCond *cond; + FilterInfo fltinfo; + GSList *cond_list = NULL; + GSList *flt_mlist = NULL; + GSList *cur; + + summaryview->on_filter = FALSE; + g_slist_free(summaryview->flt_mlist); + summaryview->flt_mlist = NULL; + + key = gtk_entry_get_text(GTK_ENTRY(summaryview->search_entry)); + if (!key || *key == '\0') { + summary_qsearch_reset(summaryview); + return; + } + + cond = filter_cond_new(FLT_COND_HEADER, FLT_CONTAIN, 0, "Subject", key); + cond_list = g_slist_append(cond_list, cond); + cond = filter_cond_new(FLT_COND_HEADER, FLT_CONTAIN, 0, "From", key); + cond_list = g_slist_append(cond_list, cond); + + rule = filter_rule_new("Quick search rule", FLT_OR, cond_list, NULL); + + summary_lock(summaryview); + main_window_cursor_wait(summaryview->mainwin); + + memset(&fltinfo, 0, sizeof(FilterInfo)); + + for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) { + MsgInfo *msginfo = (MsgInfo *)cur->data; + GSList *hlist; + + hlist = procheader_get_header_list_from_msginfo(msginfo); + if (!hlist) + continue; + + if (filter_match_rule(rule, msginfo, hlist, &fltinfo)) + flt_mlist = g_slist_prepend(flt_mlist, msginfo); + + procheader_header_list_destroy(hlist); + } + flt_mlist = g_slist_reverse(flt_mlist); + + filter_rule_free(rule); + + summaryview->on_filter = TRUE; + summaryview->flt_mlist = flt_mlist; + + gtk_tree_view_set_model(GTK_TREE_VIEW(summaryview->treeview), NULL); + gtk_tree_store_clear(summaryview->store); + gtk_tree_view_set_model(GTK_TREE_VIEW(summaryview->treeview), + GTK_TREE_MODEL(summaryview->store)); + summary_set_tree_model_from_list(summaryview, flt_mlist); + + main_window_cursor_normal(summaryview->mainwin); + summary_unlock(summaryview); +} + /* callback functions */ +static void summary_search_entry_changed(GtkWidget *entry, + SummaryView *summaryview) +{ +} + +static void summary_search_entry_activated(GtkWidget *entry, + SummaryView *summaryview) +{ + gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1); + summary_qsearch(summaryview); +} + static gboolean summary_toggle_pressed(GtkWidget *eventbox, GdkEventButton *event, SummaryView *summaryview) diff --git a/src/summaryview.h b/src/summaryview.h index bcf18ef9..9de8f4cf 100644 --- a/src/summaryview.h +++ b/src/summaryview.h @@ -1,6 +1,6 @@ /* * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * Copyright (C) 1999-2006 Hiroyuki Yamamoto * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -58,6 +58,11 @@ struct _SummaryColumnState struct _SummaryView { GtkWidget *vbox; + + GtkWidget *search_hbox; + GtkWidget *search_label; + GtkWidget *search_entry; + GtkWidget *scrolledwin; GtkWidget *treeview; @@ -117,8 +122,15 @@ private: /* table for looking up message-id */ GHashTable *msgid_table; + /* all message list */ + GSList *all_mlist; + /* filtered message list */ + GSList *flt_mlist; + + gboolean on_filter; + /* list for moving/deleting messages */ - GSList *mlist; + GSList *tmp_mlist; /* table for updating folder tree */ GHashTable *folder_table; /* counter for filtering */ -- cgit v1.2.3