diff options
-rw-r--r-- | ChangeLog | 14 | ||||
-rw-r--r-- | ChangeLog.ja | 13 | ||||
-rw-r--r-- | libsylph/Makefile.am | 1 | ||||
-rw-r--r-- | libsylph/filter.c | 108 | ||||
-rw-r--r-- | libsylph/filter.h | 6 | ||||
-rw-r--r-- | libsylph/folder.c | 17 | ||||
-rw-r--r-- | libsylph/folder.h | 4 | ||||
-rw-r--r-- | libsylph/virtual.c | 297 | ||||
-rw-r--r-- | libsylph/virtual.h | 38 | ||||
-rw-r--r-- | src/folderview.c | 29 | ||||
-rw-r--r-- | src/mainwindow.c | 9 | ||||
-rw-r--r-- | src/prefs_filter_edit.c | 7 | ||||
-rw-r--r-- | src/prefs_filter_edit.h | 19 | ||||
-rw-r--r-- | src/prefs_folder_item.c | 3 | ||||
-rw-r--r-- | src/summary_search.c | 1156 | ||||
-rw-r--r-- | src/summary_search.h | 4 | ||||
-rw-r--r-- | src/summaryview.c | 2 |
17 files changed, 1337 insertions, 390 deletions
@@ -1,5 +1,19 @@ 2005-12-19 + * libsylph/virtual.[ch] + libsylph/folder.[ch] + libsylph/filter.[ch] + libsylph/Makefile.am + src/summary_search.[ch] + src/prefs_folder_item.c + src/summaryview.c + src/folderview.c + src/mainwindow.c + src/prefs_filter_edit.[ch]: implemented query search and virtual + folder. + +2005-12-19 + * libsylph/codeconv.[ch]: support nonstandard encoding "ks_c_5601-1987" (thanks to Yoo Chung). diff --git a/ChangeLog.ja b/ChangeLog.ja index 47e17a66..050b6e03 100644 --- a/ChangeLog.ja +++ b/ChangeLog.ja @@ -1,5 +1,18 @@ 2005-12-19 + * libsylph/virtual.[ch] + libsylph/folder.[ch] + libsylph/filter.[ch] + libsylph/Makefile.am + src/summary_search.[ch] + src/prefs_folder_item.c + src/summaryview.c + src/folderview.c + src/mainwindow.c + src/prefs_filter_edit.[ch]: クエリ検索と仮想フォルダを実装。 + +2005-12-19 + * libsylph/codeconv.[ch]: 非標準なエンコーディング "ks_c_5601-1987" に対応(Yoo Chung さん thanks)。 diff --git a/libsylph/Makefile.am b/libsylph/Makefile.am index 64c37b07..5df644bd 100644 --- a/libsylph/Makefile.am +++ b/libsylph/Makefile.am @@ -43,6 +43,7 @@ libsylph_la_SOURCES = \ unmime.c unmime.h \ utils.c utils.h \ uuencode.c uuencode.h \ + virtual.c virtual.h \ xml.c xml.h libsylph_la_LDFLAGS = diff --git a/libsylph/filter.c b/libsylph/filter.c index a7533bcc..dc2be18b 100644 --- a/libsylph/filter.c +++ b/libsylph/filter.c @@ -304,7 +304,6 @@ gboolean filter_match_rule(FilterRule *rule, MsgInfo *msginfo, GSList *hlist, gboolean matched; g_return_val_if_fail(rule->cond_list != NULL, FALSE); - g_return_val_if_fail(rule->action_list != NULL, FALSE); switch (rule->timing) { case FLT_TIMING_ANY: @@ -485,6 +484,8 @@ static gboolean filter_xml_node_func(GNode *node, gpointer data) gboolean enabled = TRUE; FilterRule *rule; FilterBoolOp bool_op = FLT_OR; + const gchar *target_folder = NULL; + gboolean recursive = FALSE; GSList *cond_list = NULL; GSList *action_list = NULL; GNode *child, *cond_child, *action_child; @@ -558,6 +559,12 @@ static gboolean filter_xml_node_func(GNode *node, gpointer data) type = attr->value; else if (!strcmp(attr->name, "name")) name = attr->value; + else if (!strcmp(attr->name, "recursive")) { + if (!strcmp(attr->value, "true")) + recursive = TRUE; + else + recursive = FALSE; + } } if (type) { @@ -583,6 +590,9 @@ static gboolean filter_xml_node_func(GNode *node, gpointer data) cond_type = FLT_COND_AGE_GREATER; STR_CASE("account-id") cond_type = FLT_COND_ACCOUNT; + STR_CASE("target-folder") + target_folder = value; + continue; STR_CASE_END cond = filter_cond_new(cond_type, match_type, match_flag, @@ -641,10 +651,14 @@ static gboolean filter_xml_node_func(GNode *node, gpointer data) action_list = g_slist_append(action_list, action); } - if (name && cond_list && action_list) { + if (name && cond_list) { rule = filter_rule_new(name, bool_op, cond_list, action_list); rule->timing = timing; rule->enabled = enabled; + if (target_folder) { + rule->target_folder = g_strdup(target_folder); + rule->recursive = recursive; + } *fltlist = g_slist_prepend(*fltlist, rule); } @@ -670,17 +684,41 @@ GSList *filter_xml_node_to_filter_list(GNode *node) return fltlist; } +GSList *filter_read_file(const gchar *file) +{ + GNode *node; + GSList *list; + + g_return_val_if_fail(file != NULL, NULL); + + debug_print("Reading %s\n", file); + + if (!is_file_exist(file)) + return NULL; + + node = xml_parse_file(file); + if (!node) { + g_warning("Can't parse %s\n", file); + return NULL; + } + + list = filter_xml_node_to_filter_list(node); + + xml_free_tree(node); + + return list; +} + void filter_read_config(void) { gchar *rcpath; - GNode *node; - FilterRule *rule; debug_print("Reading filter configuration...\n"); /* remove all previous filter list */ while (prefs_common.fltlist != NULL) { - rule = (FilterRule *)prefs_common.fltlist->data; + FilterRule *rule = (FilterRule *)prefs_common.fltlist->data; + filter_rule_free(rule); prefs_common.fltlist = g_slist_remove(prefs_common.fltlist, rule); @@ -688,22 +726,8 @@ void filter_read_config(void) rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, FILTER_LIST, NULL); - if (!is_file_exist(rcpath)) { - g_free(rcpath); - return; - } - - node = xml_parse_file(rcpath); - if (!node) { - g_warning("Can't parse %s\n", rcpath); - g_free(rcpath); - return; - } + prefs_common.fltlist = filter_read_file(rcpath); g_free(rcpath); - - prefs_common.fltlist = filter_xml_node_to_filter_list(node); - - xml_free_tree(node); } #define NODE_NEW(tag, text) \ @@ -711,26 +735,23 @@ void filter_read_config(void) #define ADD_ATTR(name, value) \ xml_tag_add_attr(node->tag, xml_attr_new(name, value)) -void filter_write_config(void) +void filter_write_file(GSList *list, const gchar *file) { - gchar *rcpath; PrefFile *pfile; GSList *cur; - debug_print("Writing filter configuration...\n"); + g_return_if_fail(file != NULL); - rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, FILTER_LIST, - NULL); - if ((pfile = prefs_file_open(rcpath)) == NULL) { - g_warning("failed to write filter configuration to file\n"); - g_free(rcpath); + if ((pfile = prefs_file_open(file)) == NULL) { + g_warning("failed to write filter configuration to file: %s\n", + file); return; } xml_file_put_xml_decl(pfile->fp); fputs("\n<filter>\n", pfile->fp); - for (cur = prefs_common.fltlist; cur != NULL; cur = cur->next) { + for (cur = list; cur != NULL; cur = cur->next) { FilterRule *rule = (FilterRule *)cur->data; GSList *cur_cond; GSList *cur_action; @@ -820,6 +841,17 @@ void filter_write_config(void) } } + if (rule->target_folder) { + XMLNode *node; + + NODE_NEW("target-folder", rule->target_folder); + ADD_ATTR("recursive", rule->recursive + ? "true" : "false"); + fputs(" ", pfile->fp); + xml_file_put_node(pfile->fp, node); + xml_free_node(node); + } + fputs(" </condition-list>\n", pfile->fp); fputs(" <action-list>\n", pfile->fp); @@ -888,14 +920,25 @@ void filter_write_config(void) fputs("</filter>\n", pfile->fp); - g_free(rcpath); - if (prefs_file_close(pfile) < 0) { - g_warning(_("failed to write configuration to file\n")); + g_warning("failed to write filter configuration to file: %s\n", + file); return; } } +void filter_write_config(void) +{ + gchar *rcpath; + + debug_print("Writing filter configuration...\n"); + + rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, FILTER_LIST, + NULL); + filter_write_file(prefs_common.fltlist, rcpath); + g_free(rcpath); +} + #undef NODE_NEW #undef ADD_ATTR @@ -1399,6 +1442,7 @@ void filter_rule_free(FilterRule *rule) if (!rule) return; g_free(rule->name); + g_free(rule->target_folder); filter_cond_list_free(rule->cond_list); filter_action_list_free(rule->action_list); diff --git a/libsylph/filter.h b/libsylph/filter.h index 637991e6..7144d9cc 100644 --- a/libsylph/filter.h +++ b/libsylph/filter.h @@ -133,6 +133,9 @@ struct _FilterRule FilterTiming timing; gboolean enabled; + + gchar *target_folder; + gboolean recursive; }; struct _FilterInfo @@ -167,7 +170,10 @@ gboolean filter_rule_requires_full_headers (FilterRule *rule); /* read / write config */ GSList *filter_xml_node_to_filter_list (GNode *node); +GSList *filter_read_file (const gchar *file); void filter_read_config (void); +void filter_write_file (GSList *list, + const gchar *file); void filter_write_config (void); /* for old filterrc */ diff --git a/libsylph/folder.c b/libsylph/folder.c index 257f57c8..a86ca878 100644 --- a/libsylph/folder.c +++ b/libsylph/folder.c @@ -34,6 +34,7 @@ #include "imap.h" #include "news.h" #include "mh.h" +#include "virtual.h" #include "utils.h" #include "xml.h" #include "codeconv.h" @@ -819,6 +820,7 @@ void folder_set_missing_folders(void) CREATE_FOLDER_IF_NOT_EXIST(draft, DRAFT_DIR, F_DRAFT); CREATE_FOLDER_IF_NOT_EXIST(queue, QUEUE_DIR, F_QUEUE); CREATE_FOLDER_IF_NOT_EXIST(trash, TRASH_DIR, F_TRASH); + /* CREATE_FOLDER_IF_NOT_EXIST(junk, JUNK_DIR, F_JUNK); */ } } @@ -953,6 +955,11 @@ GSList *folder_item_get_msg_list(FolderItem *item, gboolean use_cache) g_return_val_if_fail(item != NULL, NULL); folder = item->folder; + + if (item->stype == F_VIRTUAL) + return virtual_get_class()->get_msg_list(folder, item, + use_cache); + return folder->klass->get_msg_list(folder, item, use_cache); } @@ -1259,6 +1266,12 @@ static gboolean folder_build_tree(GNode *node, gpointer data) stype = F_QUEUE; else if (!g_ascii_strcasecmp(attr->value, "trash")) stype = F_TRASH; +#if 0 + else if (!g_ascii_strcasecmp(attr->value, "junk")) + stype = F_JUNK; +#endif + else if (!g_ascii_strcasecmp(attr->value, "virtual")) + stype = F_VIRTUAL; } else if (!strcmp(attr->name, "name")) name = attr->value; else if (!strcmp(attr->name, "path")) @@ -1358,6 +1371,7 @@ static gboolean folder_build_tree(GNode *node, gpointer data) case F_DRAFT: folder->draft = item; break; case F_QUEUE: folder->queue = item; break; case F_TRASH: folder->trash = item; break; + /* case F_JUNK: folder->junk = item; break; */ default: break; } item->account = account; @@ -1479,7 +1493,8 @@ static void folder_write_list_recursive(GNode *node, gpointer data) static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap", "news", "unknown"}; static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox", - "draft", "queue", "trash"}; + "draft", "queue", "trash", + "junk", "virtual"}; static gchar *sort_key_str[] = {"none", "number", "size", "date", "thread-date", "from", "subject", "score", "label", diff --git a/libsylph/folder.h b/libsylph/folder.h index 5fc097e9..13f99235 100644 --- a/libsylph/folder.h +++ b/libsylph/folder.h @@ -79,7 +79,9 @@ typedef enum F_OUTBOX, F_DRAFT, F_QUEUE, - F_TRASH + F_TRASH, + F_JUNK, + F_VIRTUAL } SpecialFolderItemType; typedef enum diff --git a/libsylph/virtual.c b/libsylph/virtual.c new file mode 100644 index 00000000..897a7070 --- /dev/null +++ b/libsylph/virtual.c @@ -0,0 +1,297 @@ +/* + * LibSylph -- E-Mail client library + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include <glib.h> +#include <glib/gi18n.h> +#include <dirent.h> +#include <sys/stat.h> +#include <time.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#undef MEASURE_TIME + +#include "folder.h" +#include "virtual.h" +#include "procmsg.h" +#include "procheader.h" +#include "filter.h" +#include "utils.h" + +typedef struct _VirtualSearchInfo VirtualSearchInfo; + +struct _VirtualSearchInfo { + FilterRule *rule; + GSList *mlist; +}; + +static void virtual_folder_init (Folder *folder, + const gchar *name, + const gchar *path); + +static GSList *virtual_search_folder (FilterRule *rule, + FolderItem *item); +static gboolean virtual_search_recursive_func + (GNode *node, + gpointer data); + +static Folder *virtual_folder_new (const gchar *name, + const gchar *path); +static void virtual_folder_destroy (Folder *folder); + +static GSList *virtual_get_msg_list (Folder *folder, + FolderItem *item, + gboolean use_cache); +static gchar *virtual_fetch_msg (Folder *folder, + FolderItem *item, + gint num); +static MsgInfo *virtual_get_msginfo (Folder *folder, + FolderItem *item, + gint num); +static gint virtual_close (Folder *folder, + FolderItem *item); + +static gint virtual_scan_folder (Folder *folder, + FolderItem *item); + +static FolderClass virtual_class = +{ + F_VIRTUAL, + + virtual_folder_new, + virtual_folder_destroy, + + NULL, + NULL, + + virtual_get_msg_list, + virtual_fetch_msg, + virtual_get_msginfo, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + virtual_close, + virtual_scan_folder, + + NULL, + NULL, + NULL, + NULL, +}; + + +FolderClass *virtual_get_class(void) +{ + return &virtual_class; +} + +static Folder *virtual_folder_new(const gchar *name, const gchar *path) +{ + Folder *folder; + + folder = (Folder *)g_new0(VirtualFolder, 1); + virtual_folder_init(folder, name, path); + + return folder; +} + +static void virtual_folder_destroy(Folder *folder) +{ + folder_local_folder_destroy(LOCAL_FOLDER(folder)); +} + +static void virtual_folder_init(Folder *folder, const gchar *name, + const gchar *path) +{ + folder->klass = virtual_get_class(); + folder_local_folder_init(folder, name, path); +} + +static GSList *virtual_search_folder(FilterRule *rule, FolderItem *item) +{ + GSList *match_list = NULL; + GSList *mlist; + GSList *cur; + FilterInfo fltinfo; + gboolean full_headers; + + g_return_val_if_fail(rule != NULL, NULL); + g_return_val_if_fail(item != NULL, NULL); + + /* prevent circular reference */ + if (item->stype == F_VIRTUAL) + return NULL; + + mlist = folder_item_get_msg_list(item, TRUE); + + memset(&fltinfo, 0, sizeof(FilterInfo)); + + debug_print("start query search: %s\n", item->path ? item->path : ""); + + full_headers = filter_rule_requires_full_headers(rule); + + for (cur = mlist; cur != NULL; cur = cur->next) { + MsgInfo *msginfo = (MsgInfo *)cur->data; + GSList *hlist; + + fltinfo.flags = msginfo->flags; + if (full_headers) { + gchar *file; + + file = procmsg_get_message_file(msginfo); + hlist = procheader_get_header_list_from_file(file); + g_free(file); + } else + hlist = procheader_get_header_list_from_msginfo + (msginfo); + if (!hlist) + continue; + + if (filter_match_rule(rule, msginfo, hlist, &fltinfo)) { + match_list = g_slist_prepend(match_list, msginfo); + cur->data = NULL; + } + + procheader_header_list_destroy(hlist); + } + + procmsg_msg_list_free(mlist); + + return g_slist_reverse(match_list); +} + +static gboolean virtual_search_recursive_func(GNode *node, gpointer data) +{ + VirtualSearchInfo *info = (VirtualSearchInfo *)data; + FolderItem *item; + GSList *mlist; + + g_return_val_if_fail(node->data != NULL, FALSE); + + item = FOLDER_ITEM(node->data); + + if (!item->path) + return FALSE; + + mlist = virtual_search_folder(info->rule, item); + info->mlist = g_slist_concat(info->mlist, mlist); + + return FALSE; +} + +static GSList *virtual_get_msg_list(Folder *folder, FolderItem *item, + gboolean use_cache) +{ + GSList *mlist = NULL; + GSList *flist; + GSList *cur; + FilterRule *rule; + gchar *rule_file; + gchar *path; + FolderItem *target; + gint new = 0, unread = 0, total = 0; + + g_return_val_if_fail(item != NULL, NULL); + g_return_val_if_fail(item->stype == F_VIRTUAL, NULL); + + path = folder_item_get_path(item); + rule_file = g_strconcat(path, G_DIR_SEPARATOR_S, "filter.xml", NULL); + flist = filter_read_file(rule_file); + g_free(rule_file); + + g_free(path); + + if (!flist) { + g_warning("filter rule not found\n"); + return NULL; + } + + rule = (FilterRule *)flist->data; + target = folder_find_item_from_identifier(rule->target_folder); + + if (!target || target == item) { + g_warning("invalid target folder\n"); + goto finish; + } + + if (rule->recursive) { + VirtualSearchInfo info; + + info.rule = rule; + info.mlist = NULL; + g_node_traverse(target->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, + virtual_search_recursive_func, &info); + mlist = info.mlist; + } else + mlist = virtual_search_folder(rule, target); + + for (cur = mlist; cur != NULL; cur = cur->next) { + MsgInfo *msginfo = (MsgInfo *)cur->data; + + if (MSG_IS_NEW(msginfo->flags)) + ++new; + if (MSG_IS_UNREAD(msginfo->flags)) + ++unread; + ++total; + } + + item->new = new; + item->unread = unread; + item->total = total; + item->updated = TRUE; + +finish: + filter_rule_list_free(flist); + return mlist; +} + +static gchar *virtual_fetch_msg(Folder *folder, FolderItem *item, gint num) +{ + return NULL; +} + +static MsgInfo *virtual_get_msginfo(Folder *folder, FolderItem *item, gint num) +{ + return NULL; +} + +static gint virtual_close(Folder *folder, FolderItem *item) +{ + return 0; +} + +static gint virtual_scan_folder(Folder *folder, FolderItem *item) +{ + /* do search */ + return 0; +} diff --git a/libsylph/virtual.h b/libsylph/virtual.h new file mode 100644 index 00000000..1a7ba38f --- /dev/null +++ b/libsylph/virtual.h @@ -0,0 +1,38 @@ +/* + * LibSylph -- E-Mail client library + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __VIRTUAL_H__ +#define __VIRTUAL_H__ + +#include <glib.h> + +#include "folder.h" + +typedef struct _VirtualFolder VirtualFolder; + +#define VIRTUAL_FOLDER(obj) ((VirtualFolder *)obj) + +struct _VirtualFolder +{ + LocalFolder lfolder; +}; + +FolderClass *virtual_get_class (void); + +#endif /* __VIRTUAL_H__ */ diff --git a/src/folderview.c b/src/folderview.c index 43ff7732..42965160 100644 --- a/src/folderview.c +++ b/src/folderview.c @@ -105,6 +105,7 @@ static GdkPixbuf *folderopen_pixbuf; static GdkPixbuf *foldernoselect_pixbuf; static GdkPixbuf *draft_pixbuf; static GdkPixbuf *trash_pixbuf; +static GdkPixbuf *virtual_pixbuf; static void folderview_set_columns (FolderView *folderview); @@ -516,6 +517,7 @@ void folderview_init(FolderView *folderview) &foldernoselect_pixbuf); stock_pixbuf_gdk(treeview, STOCK_PIXMAP_DRAFT, &draft_pixbuf); stock_pixbuf_gdk(treeview, STOCK_PIXMAP_TRASH, &trash_pixbuf); + stock_pixbuf_gdk(treeview, STOCK_PIXMAP_GROUP, &virtual_pixbuf); } void folderview_reflect_prefs(FolderView *folderview) @@ -1170,6 +1172,19 @@ static void folderview_update_row(FolderView *folderview, GtkTreeIter *iter) !strcmp2(item->name, DRAFT_DIR) ? _("Drafts") : item->name); break; +#if 0 + case F_JUNK: + pixbuf = folder_pixbuf; + open_pixbuf = folderopen_pixbuf; + name = g_strdup(FOLDER_IS_LOCAL(item->folder) && + !strcmp2(item->name, JUNK_DIR) ? _("Junk") : + item->name); + break; +#endif + case F_VIRTUAL: + pixbuf = open_pixbuf = virtual_pixbuf; + name = g_strdup(item->name); + break; default: if (item->no_select) { pixbuf = open_pixbuf = foldernoselect_pixbuf; @@ -1540,14 +1555,13 @@ static gboolean folderview_menu_popup(FolderView *folderview, if (folderview->mainwin->lock_count == 0) { new_folder = TRUE; folder_property = TRUE; + search_folder = TRUE; if (item->parent == NULL) { update_tree = remove_tree = TRUE; } else { if (gtkut_tree_row_reference_equal - (folderview->selected, folderview->opened)) { + (folderview->selected, folderview->opened)) update_summary = TRUE; - search_folder = TRUE; - } } if (FOLDER_IS_LOCAL(folder) || FOLDER_TYPE(folder) == F_IMAP) { if (item->parent == NULL) @@ -1562,6 +1576,9 @@ static gboolean folderview_menu_popup(FolderView *folderview, if (item->parent != NULL) delete_folder = TRUE; } + if (item->stype == F_VIRTUAL) { + rename_folder = delete_folder = TRUE; + } if (FOLDER_TYPE(folder) == F_IMAP || FOLDER_TYPE(folder) == F_NEWS) { if (item->parent != NULL && item->no_select == FALSE) @@ -2608,7 +2625,11 @@ static void folderview_rm_news_server_cb(FolderView *folderview, guint action, static void folderview_search_cb(FolderView *folderview, guint action, GtkWidget *widget) { - summary_search(folderview->summaryview); + FolderItem *item; + + item = folderview_get_selected_item(folderview); + if (item) + summary_search(folderview->summaryview, item); } static void folderview_property_cb(FolderView *folderview, guint action, diff --git a/src/mainwindow.c b/src/mainwindow.c index 19afdda3..d7f90d19 100644 --- a/src/mainwindow.c +++ b/src/mainwindow.c @@ -2758,9 +2758,12 @@ static void app_exit_cb(MainWindow *mainwin, guint action, GtkWidget *widget) static void search_cb(MainWindow *mainwin, guint action, GtkWidget *widget) { - if (action == 1) - summary_search(mainwin->summaryview); - else + if (action == 1) { + FolderItem *item; + + item = folderview_get_selected_item(mainwin->folderview); + summary_search(mainwin->summaryview, item); + } else message_search(mainwin->messageview); } diff --git a/src/prefs_filter_edit.c b/src/prefs_filter_edit.c index 52821448..af804aed 100644 --- a/src/prefs_filter_edit.c +++ b/src/prefs_filter_edit.c @@ -335,6 +335,7 @@ FilterCondEdit *prefs_filter_edit_cond_edit_create(void) cond_edit->cond_hbox_list = NULL; cond_edit->hdr_list = NULL; cond_edit->rule_hdr_list = NULL; + cond_edit->add_hbox = NULL; return cond_edit; } @@ -1243,6 +1244,8 @@ static void prefs_filter_edit_add_rule_cond(FilterCondEdit *cond_edit, hbox = prefs_filter_edit_cond_hbox_create(cond_edit); prefs_filter_edit_set_cond_hbox_widgets(hbox, PF_COND_HEADER); prefs_filter_edit_insert_cond_hbox(cond_edit, hbox, -1); + if (cond_edit->add_hbox) + cond_edit->add_hbox(hbox); return; } @@ -1252,6 +1255,8 @@ static void prefs_filter_edit_add_rule_cond(FilterCondEdit *cond_edit, hbox = prefs_filter_edit_cond_hbox_create(cond_edit); prefs_filter_edit_cond_hbox_set(hbox, cond); prefs_filter_edit_insert_cond_hbox(cond_edit, hbox, -1); + if (cond_edit->add_hbox) + cond_edit->add_hbox(hbox); } } @@ -1964,6 +1969,8 @@ static void prefs_filter_cond_add_cb(GtkWidget *widget, gpointer data) new_hbox = prefs_filter_edit_cond_hbox_create(cond_edit); prefs_filter_edit_set_cond_hbox_widgets(new_hbox, PF_COND_HEADER); prefs_filter_edit_insert_cond_hbox(cond_edit, new_hbox, index + 1); + if (cond_edit->add_hbox) + cond_edit->add_hbox(new_hbox); } static void prefs_filter_action_del_cb(GtkWidget *widget, gpointer data) diff --git a/src/prefs_filter_edit.h b/src/prefs_filter_edit.h index fd720fa2..dbc967f3 100644 --- a/src/prefs_filter_edit.h +++ b/src/prefs_filter_edit.h @@ -24,6 +24,10 @@ #include "filter.h" +typedef struct _FilterCondEdit FilterCondEdit; +typedef struct _CondHBox CondHBox; +typedef struct _ActionHBox ActionHBox; + typedef enum { PF_COND_HEADER, @@ -81,15 +85,18 @@ typedef enum PF_ACTION_NONE } ActionMenuType; -typedef struct _FilterCondEdit { +struct _FilterCondEdit { GtkWidget *cond_vbox; GSList *cond_hbox_list; GSList *hdr_list; GSList *rule_hdr_list; -} FilterCondEdit; -typedef struct _CondHBox { + /* callback */ + void (*add_hbox) (CondHBox *hbox); +}; + +struct _CondHBox { GtkWidget *hbox; GtkWidget *cond_type_optmenu; @@ -107,9 +114,9 @@ typedef struct _CondHBox { gchar *cur_header_name; FilterCondEdit *cond_edit; -} CondHBox; +}; -typedef struct _ActionHBox { +struct _ActionHBox { GtkWidget *hbox; GtkWidget *action_type_optmenu; @@ -125,7 +132,7 @@ typedef struct _ActionHBox { GtkWidget *del_btn; GtkWidget *add_btn; -} ActionHBox; +}; FilterRule *prefs_filter_edit_open (FilterRule *rule, diff --git a/src/prefs_folder_item.c b/src/prefs_folder_item.c index fefe44bc..9d777eba 100644 --- a/src/prefs_folder_item.c +++ b/src/prefs_folder_item.c @@ -561,6 +561,9 @@ static void prefs_folder_item_apply_cb(GtkWidget *widget, prev_item = folder->trash; folder->trash = item; break; + default: + type = item->stype; + break; } item->stype = type; diff --git a/src/summary_search.c b/src/summary_search.c index c388944a..d6a76017 100644 --- a/src/summary_search.c +++ b/src/summary_search.c @@ -40,69 +40,139 @@ #include <gtk/gtkmenuitem.h> #include <gtk/gtkstock.h> #include <gtk/gtktreemodel.h> +#include <gtk/gtkliststore.h> +#include <gtk/gtktreeview.h> #include <gtk/gtktreeselection.h> +#include <gtk/gtkcellrenderertext.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include "main.h" #include "summary_search.h" +#include "prefs_filter_edit.h" #include "summaryview.h" #include "messageview.h" #include "mainwindow.h" +#include "folderview.h" #include "menu.h" #include "utils.h" #include "gtkutils.h" #include "manage_window.h" #include "alertpanel.h" +#include "foldersel.h" +#include "procmsg.h" +#include "procheader.h" +#include "folder.h" +#include "filter.h" +#include "prefs_filter.h" +#include "prefs_filter_edit.h" + +enum +{ + COL_FOLDER, + COL_SUBJECT, + COL_FROM, + COL_DATE, + COL_MSGINFO, + N_COLS +}; static struct SummarySearchWindow { GtkWidget *window; GtkWidget *bool_optmenu; - GtkWidget *from_entry; - GtkWidget *to_entry; - GtkWidget *subject_entry; - GtkWidget *body_entry; + FilterCondEdit *cond_edit; + + GtkWidget *folder_entry; + GtkWidget *folder_btn; + GtkWidget *subfolder_checkbtn; GtkWidget *case_checkbtn; + GtkWidget *treeview; + GtkListStore *store; + + GtkWidget *status_label; + GtkWidget *clear_btn; + GtkWidget *search_btn; + GtkWidget *save_btn; GtkWidget *close_btn; - GtkWidget *all_btn; - GtkWidget *prev_btn; - GtkWidget *next_btn; SummaryView *summaryview; + + FilterRule *rule; + gboolean requires_full_headers; + + gboolean on_search; + gboolean cancelled; } search_window; +typedef struct { + GtkWidget *window; + + GtkWidget *folder_entry; + GtkWidget *name_entry; + + gboolean cancelled; + gboolean finished; +} SummarySearchSaveDialog; + static void summary_search_create (void); -static void summary_search_execute (gboolean backward, - gboolean search_all); +static FilterRule *summary_search_dialog_to_rule + (const gchar *name, + FolderItem **item); + +static void summary_search_query (void); +static void summary_search_folder (FolderItem *item); + +static gboolean summary_search_recursive_func (GNode *node, + gpointer data); + +static void summary_search_append_msg (MsgInfo *msginfo); +static void summary_search_clear_list (void); + +static void summary_search_hbox_added (CondHBox *hbox); + +static void row_activated (GtkTreeView *treeview, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer data); + +static gboolean row_selected (GtkTreeSelection *selection, + GtkTreeModel *model, + GtkTreePath *path, + gboolean cur_selected, + gpointer data); static void summary_search_clear (GtkButton *button, gpointer data); -static void summary_search_prev_clicked (GtkButton *button, +static void summary_select_folder (GtkButton *button, + gpointer data); +static void summary_search_clicked (GtkButton *button, gpointer data); -static void summary_search_next_clicked (GtkButton *button, +static void summary_search_save (GtkButton *button, gpointer data); -static void summary_search_all_clicked (GtkButton *button, +static void summary_search_close (GtkButton *button, gpointer data); -static void from_activated (void); -static void to_activated (void); -static void subject_activated (void); -static void body_activated (void); +static void summary_search_entry_activated (GtkWidget *widget, + gpointer data); +static gint summary_search_deleted (GtkWidget *widget, + GdkEventAny *event, + gpointer data); static gboolean key_pressed (GtkWidget *widget, GdkEventKey *event, gpointer data); -void summary_search(SummaryView *summaryview) +void summary_search(SummaryView *summaryview, FolderItem *item) { + gchar *id; + if (!search_window.window) summary_search_create(); else @@ -110,8 +180,14 @@ void summary_search(SummaryView *summaryview) search_window.summaryview = summaryview; - gtk_widget_grab_focus(search_window.next_btn); - gtk_widget_grab_focus(search_window.subject_entry); + if (item) { + id = folder_item_get_identifier(item); + gtk_entry_set_text(GTK_ENTRY(search_window.folder_entry), id); + g_free(id); + } else + gtk_entry_set_text(GTK_ENTRY(search_window.folder_entry), ""); + + gtk_widget_grab_focus(search_window.search_btn); gtk_widget_show(search_window.window); } @@ -124,42 +200,51 @@ static void summary_search_create(void) GtkWidget *bool_menu; GtkWidget *menuitem; GtkWidget *clear_btn; + GtkWidget *search_btn; - GtkWidget *table1; - GtkWidget *from_label; - GtkWidget *from_entry; - GtkWidget *to_label; - GtkWidget *to_entry; - GtkWidget *subject_label; - GtkWidget *subject_entry; - GtkWidget *body_label; - GtkWidget *body_entry; + GtkWidget *scrolledwin; + FilterCondEdit *cond_edit; + CondHBox *cond_hbox; + + GtkWidget *folder_hbox; + GtkWidget *folder_label; + GtkWidget *folder_entry; + GtkWidget *folder_btn; GtkWidget *checkbtn_hbox; + GtkWidget *subfolder_checkbtn; GtkWidget *case_checkbtn; + GtkWidget *treeview; + GtkListStore *store; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkTreeSelection *selection; + GtkWidget *confirm_area; + + GtkWidget *status_label; + + GtkWidget *hbbox; + GtkWidget *save_btn; GtkWidget *close_btn; - GtkWidget *all_btn; - GtkWidget *prev_btn; - GtkWidget *next_btn; window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW (window), _("Search messages")); - gtk_widget_set_size_request(window, 450, -1); + gtk_widget_set_size_request(window, 600, -1); gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, TRUE); gtk_container_set_border_width(GTK_CONTAINER (window), 8); g_signal_connect(G_OBJECT(window), "delete_event", - G_CALLBACK(gtk_widget_hide_on_delete), NULL); + G_CALLBACK(summary_search_deleted), NULL); g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(key_pressed), NULL); MANAGE_WINDOW_SIGNALS_CONNECT(window); - vbox1 = gtk_vbox_new (FALSE, 0); + vbox1 = gtk_vbox_new (FALSE, 6); gtk_widget_show (vbox1); gtk_container_add (GTK_CONTAINER (window), vbox1); - bool_hbox = gtk_hbox_new(FALSE, 4); + bool_hbox = gtk_hbox_new(FALSE, 12); gtk_widget_show(bool_hbox); gtk_box_pack_start(GTK_BOX(vbox1), bool_hbox, FALSE, FALSE, 0); @@ -168,389 +253,778 @@ static void summary_search_create(void) gtk_box_pack_start(GTK_BOX(bool_hbox), bool_optmenu, FALSE, FALSE, 0); bool_menu = gtk_menu_new(); - MENUITEM_ADD(bool_menu, menuitem, _("Match any of the following"), 0); - MENUITEM_ADD(bool_menu, menuitem, _("Match all of the following"), 1); + MENUITEM_ADD(bool_menu, menuitem, _("Match any of the following"), + FLT_OR); + MENUITEM_ADD(bool_menu, menuitem, _("Match all of the following"), + FLT_AND); gtk_option_menu_set_menu(GTK_OPTION_MENU(bool_optmenu), bool_menu); + hbbox = gtk_hbutton_box_new(); + gtk_widget_show(hbbox); + gtk_button_box_set_layout(GTK_BUTTON_BOX(hbbox), GTK_BUTTONBOX_END); + gtk_box_set_spacing(GTK_BOX(hbbox), 6); + gtk_box_pack_end(GTK_BOX(bool_hbox), hbbox, FALSE, FALSE, 0); + clear_btn = gtk_button_new_from_stock(GTK_STOCK_CLEAR); gtk_widget_show(clear_btn); - gtk_box_pack_end(GTK_BOX(bool_hbox), clear_btn, FALSE, FALSE, 0); - - table1 = gtk_table_new (4, 3, FALSE); - gtk_widget_show (table1); - gtk_box_pack_start (GTK_BOX (vbox1), table1, TRUE, TRUE, 0); - gtk_container_set_border_width (GTK_CONTAINER (table1), 4); - gtk_table_set_row_spacings (GTK_TABLE (table1), 8); - gtk_table_set_col_spacings (GTK_TABLE (table1), 8); - - from_entry = gtk_entry_new (); - gtk_widget_show (from_entry); - gtk_table_attach (GTK_TABLE (table1), from_entry, 1, 3, 0, 1, - GTK_EXPAND|GTK_FILL, 0, 0, 0); - g_signal_connect(G_OBJECT(from_entry), "activate", - G_CALLBACK(from_activated), NULL); - - to_entry = gtk_entry_new (); - gtk_widget_show (to_entry); - gtk_table_attach (GTK_TABLE (table1), to_entry, 1, 3, 1, 2, - GTK_EXPAND|GTK_FILL, 0, 0, 0); - g_signal_connect(G_OBJECT(to_entry), "activate", - G_CALLBACK(to_activated), NULL); - - subject_entry = gtk_entry_new (); - gtk_widget_show (subject_entry); - gtk_table_attach (GTK_TABLE (table1), subject_entry, 1, 3, 2, 3, - GTK_EXPAND|GTK_FILL, 0, 0, 0); - g_signal_connect(G_OBJECT(subject_entry), "activate", - G_CALLBACK(subject_activated), NULL); - - body_entry = gtk_entry_new (); - gtk_widget_show (body_entry); - gtk_table_attach (GTK_TABLE (table1), body_entry, 1, 3, 3, 4, - GTK_EXPAND|GTK_FILL, 0, 0, 0); - g_signal_connect(G_OBJECT(body_entry), "activate", - G_CALLBACK(body_activated), NULL); - - from_label = gtk_label_new (_("From:")); - gtk_widget_show (from_label); - gtk_table_attach (GTK_TABLE (table1), from_label, 0, 1, 0, 1, - GTK_FILL, 0, 0, 0); - gtk_label_set_justify (GTK_LABEL (from_label), GTK_JUSTIFY_RIGHT); - gtk_misc_set_alignment (GTK_MISC (from_label), 1, 0.5); - - to_label = gtk_label_new (_("To:")); - gtk_widget_show (to_label); - gtk_table_attach (GTK_TABLE (table1), to_label, 0, 1, 1, 2, - GTK_FILL, 0, 0, 0); - gtk_label_set_justify (GTK_LABEL (to_label), GTK_JUSTIFY_RIGHT); - gtk_misc_set_alignment (GTK_MISC (to_label), 1, 0.5); - - subject_label = gtk_label_new (_("Subject:")); - gtk_widget_show (subject_label); - gtk_table_attach (GTK_TABLE (table1), subject_label, 0, 1, 2, 3, - GTK_FILL, 0, 0, 0); - gtk_label_set_justify (GTK_LABEL (subject_label), GTK_JUSTIFY_RIGHT); - gtk_misc_set_alignment (GTK_MISC (subject_label), 1, 0.5); - - body_label = gtk_label_new (_("Body:")); - gtk_widget_show (body_label); - gtk_table_attach (GTK_TABLE (table1), body_label, 0, 1, 3, 4, - GTK_FILL, 0, 0, 0); - gtk_label_set_justify (GTK_LABEL (body_label), GTK_JUSTIFY_RIGHT); - gtk_misc_set_alignment (GTK_MISC (body_label), 1, 0.5); - - checkbtn_hbox = gtk_hbox_new (FALSE, 8); + gtk_box_pack_start(GTK_BOX(hbbox), clear_btn, FALSE, FALSE, 0); + + search_btn = gtk_button_new_from_stock(GTK_STOCK_FIND); + GTK_WIDGET_SET_FLAGS(search_btn, GTK_CAN_DEFAULT); + gtk_widget_show(search_btn); + gtk_box_pack_start(GTK_BOX(hbbox), search_btn, FALSE, FALSE, 0); + gtk_widget_grab_default(search_btn); + + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_show(scrolledwin); + gtk_box_pack_start(GTK_BOX(vbox1), scrolledwin, FALSE, FALSE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_widget_set_size_request(scrolledwin, -1, 120); + + cond_edit = prefs_filter_edit_cond_edit_create(); + cond_edit->add_hbox = summary_search_hbox_added; + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolledwin), + cond_edit->cond_vbox); + prefs_filter_set_header_list(NULL); + prefs_filter_edit_set_header_list(cond_edit, NULL); + cond_hbox = prefs_filter_edit_cond_hbox_create(cond_edit); + prefs_filter_edit_set_cond_hbox_widgets(cond_hbox, PF_COND_HEADER); + prefs_filter_edit_insert_cond_hbox(cond_edit, cond_hbox, -1); + if (cond_edit->add_hbox) + cond_edit->add_hbox(cond_hbox); + + folder_hbox = gtk_hbox_new (FALSE, 8); + gtk_widget_show (folder_hbox); + gtk_box_pack_start (GTK_BOX (vbox1), folder_hbox, FALSE, FALSE, 0); + + folder_label = gtk_label_new (_("Folder:")); + gtk_widget_show (folder_label); + gtk_box_pack_start (GTK_BOX (folder_hbox), folder_label, + FALSE, FALSE, 0); + + folder_entry = gtk_entry_new (); + gtk_widget_show (folder_entry); + gtk_box_pack_start (GTK_BOX (folder_hbox), folder_entry, TRUE, TRUE, 0); + + folder_btn = gtk_button_new_with_label("..."); + gtk_widget_show (folder_btn); + gtk_box_pack_start (GTK_BOX (folder_hbox), folder_btn, FALSE, FALSE, 0); + + checkbtn_hbox = gtk_hbox_new (FALSE, 12); gtk_widget_show (checkbtn_hbox); - gtk_box_pack_start (GTK_BOX (vbox1), checkbtn_hbox, TRUE, TRUE, 0); - gtk_container_set_border_width (GTK_CONTAINER (checkbtn_hbox), 8); + gtk_box_pack_start (GTK_BOX (vbox1), checkbtn_hbox, FALSE, FALSE, 0); + + subfolder_checkbtn = + gtk_check_button_new_with_label (_("Search subfolders")); + gtk_widget_show (subfolder_checkbtn); + gtk_box_pack_start (GTK_BOX (checkbtn_hbox), subfolder_checkbtn, + FALSE, FALSE, 0); case_checkbtn = gtk_check_button_new_with_label (_("Case sensitive")); gtk_widget_show (case_checkbtn); gtk_box_pack_start (GTK_BOX (checkbtn_hbox), case_checkbtn, FALSE, FALSE, 0); - confirm_area = gtk_hbutton_box_new(); - gtk_widget_show (confirm_area); - gtk_button_box_set_layout(GTK_BUTTON_BOX(confirm_area), - GTK_BUTTONBOX_END); - gtk_box_set_spacing(GTK_BOX(confirm_area), 6); + scrolledwin = gtk_scrolled_window_new(NULL, NULL); + gtk_box_pack_start(GTK_BOX(vbox1), scrolledwin, TRUE, TRUE, 0); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), + GTK_SHADOW_IN); + gtk_widget_set_size_request(scrolledwin, -1, 150); + + store = gtk_list_store_new(N_COLS, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_POINTER); + treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(store); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); + g_signal_connect(G_OBJECT(treeview), "row-activated", + G_CALLBACK(row_activated), NULL); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); + gtk_tree_selection_set_select_function(selection, row_selected, + NULL, NULL); + + gtk_container_add(GTK_CONTAINER(scrolledwin), treeview); + +#define APPEND_COLUMN(label, col, width) \ +{ \ + renderer = gtk_cell_renderer_text_new(); \ + column = gtk_tree_view_column_new_with_attributes \ + (label, renderer, "text", col, NULL); \ + gtk_tree_view_column_set_resizable(column, TRUE); \ + if (width) { \ + gtk_tree_view_column_set_sizing \ + (column, GTK_TREE_VIEW_COLUMN_FIXED); \ + gtk_tree_view_column_set_fixed_width(column, width); \ + } \ + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); \ +} - close_btn = gtk_button_new_from_stock(GTK_STOCK_CLOSE); - GTK_WIDGET_SET_FLAGS(close_btn, GTK_CAN_DEFAULT); - gtk_box_pack_start(GTK_BOX(confirm_area), close_btn, TRUE, TRUE, 0); - gtk_widget_show(close_btn); + APPEND_COLUMN(_("Folder"), COL_FOLDER, 0); + APPEND_COLUMN(_("Subject"), COL_SUBJECT, 200); + APPEND_COLUMN(_("From"), COL_FROM, 180); + APPEND_COLUMN(_("Date"), COL_DATE, 0); + + gtk_widget_show_all(scrolledwin); - all_btn = gtk_button_new_from_stock(_("Find all")); - GTK_WIDGET_SET_FLAGS(all_btn, GTK_CAN_DEFAULT); - gtk_box_pack_start(GTK_BOX(confirm_area), all_btn, TRUE, TRUE, 0); - gtk_widget_show(all_btn); + confirm_area = gtk_hbox_new(FALSE, 12); + gtk_widget_show(confirm_area); + gtk_box_pack_start(GTK_BOX(vbox1), confirm_area, FALSE, FALSE, 0); - prev_btn = gtk_button_new_from_stock(GTK_STOCK_GO_BACK); - GTK_WIDGET_SET_FLAGS(prev_btn, GTK_CAN_DEFAULT); - gtk_box_pack_start(GTK_BOX(confirm_area), prev_btn, TRUE, TRUE, 0); - gtk_widget_show(prev_btn); + status_label = gtk_label_new(""); + gtk_widget_show(status_label); + gtk_box_pack_start(GTK_BOX(confirm_area), status_label, + FALSE, FALSE, 0); - next_btn = gtk_button_new_from_stock(GTK_STOCK_GO_FORWARD); - GTK_WIDGET_SET_FLAGS(next_btn, GTK_CAN_DEFAULT); - gtk_box_pack_start(GTK_BOX(confirm_area), next_btn, TRUE, TRUE, 0); - gtk_widget_show(next_btn); + hbbox = gtk_hbutton_box_new(); + gtk_widget_show(hbbox); + gtk_button_box_set_layout(GTK_BUTTON_BOX(hbbox), GTK_BUTTONBOX_END); + gtk_box_set_spacing(GTK_BOX(hbbox), 6); + gtk_box_pack_end(GTK_BOX(confirm_area), hbbox, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (vbox1), confirm_area, FALSE, FALSE, 0); - gtk_widget_grab_default(next_btn); + save_btn = gtk_button_new_with_mnemonic(_("_Save as search folder")); + gtk_box_pack_start(GTK_BOX(hbbox), save_btn, FALSE, FALSE, 0); + gtk_widget_show(save_btn); + + close_btn = gtk_button_new_from_stock(GTK_STOCK_CLOSE); + GTK_WIDGET_SET_FLAGS(close_btn, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(hbbox), close_btn, FALSE, FALSE, 0); + gtk_widget_show(close_btn); g_signal_connect(G_OBJECT(clear_btn), "clicked", G_CALLBACK(summary_search_clear), NULL); - g_signal_connect(G_OBJECT(all_btn), "clicked", - G_CALLBACK(summary_search_all_clicked), NULL); - g_signal_connect(G_OBJECT(prev_btn), "clicked", - G_CALLBACK(summary_search_prev_clicked), NULL); - g_signal_connect(G_OBJECT(next_btn), "clicked", - G_CALLBACK(summary_search_next_clicked), NULL); - g_signal_connect_closure - (G_OBJECT(close_btn), "clicked", - g_cclosure_new_swap(G_CALLBACK(gtk_widget_hide), - window, NULL), - FALSE); + g_signal_connect(G_OBJECT(folder_btn), "clicked", + G_CALLBACK(summary_select_folder), NULL); + g_signal_connect(G_OBJECT(search_btn), "clicked", + G_CALLBACK(summary_search_clicked), NULL); + g_signal_connect(G_OBJECT(save_btn), "clicked", + G_CALLBACK(summary_search_save), NULL); + g_signal_connect(G_OBJECT(close_btn), "clicked", + G_CALLBACK(summary_search_close), NULL); search_window.window = window; search_window.bool_optmenu = bool_optmenu; - search_window.from_entry = from_entry; - search_window.to_entry = to_entry; - search_window.subject_entry = subject_entry; - search_window.body_entry = body_entry; + + search_window.cond_edit = cond_edit; + + search_window.folder_entry = folder_entry; + search_window.folder_btn = folder_btn; + search_window.subfolder_checkbtn = subfolder_checkbtn; search_window.case_checkbtn = case_checkbtn; + + search_window.treeview = treeview; + search_window.store = store; + + search_window.status_label = status_label; + search_window.clear_btn = clear_btn; + search_window.search_btn = search_btn; + search_window.save_btn = save_btn; search_window.close_btn = close_btn; - search_window.all_btn = all_btn; - search_window.prev_btn = prev_btn; - search_window.next_btn = next_btn; } -static void summary_search_execute(gboolean backward, gboolean search_all) +static FilterRule *summary_search_dialog_to_rule(const gchar *name, + FolderItem **item) { - SummaryView *summaryview = search_window.summaryview; - GtkTreeModel *model; - GtkTreeIter iter; - MsgInfo *msginfo; - gboolean bool_and; + const gchar *id; + FolderItem *item_; + FilterBoolOp bool_op = FLT_OR; + gboolean recursive; gboolean case_sens; - gboolean all_searched = FALSE; - gboolean matched; - gboolean body_matched; - const gchar *from_str, *to_str, *subject_str, *body_str; - StrFindFunc str_find_func; - gboolean valid; - - if (summary_is_locked(summaryview)) return; - summary_lock(summaryview); - - model = GTK_TREE_MODEL(summaryview->store); - - bool_and = menu_get_option_menu_active_index + GSList *cond_list = NULL; + FilterCond *cond; + FilterRule *rule; + GSList *cur; + + id = gtk_entry_get_text(GTK_ENTRY(search_window.folder_entry)); + item_ = folder_find_item_from_identifier(id); + if (!item_) + return NULL; + if (item) + *item = item_; + + bool_op = menu_get_option_menu_active_index (GTK_OPTION_MENU(search_window.bool_optmenu)); + recursive = gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(search_window.subfolder_checkbtn)); case_sens = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(search_window.case_checkbtn)); - if (case_sens) - str_find_func = str_find; - else - str_find_func = str_case_find; - - from_str = gtk_entry_get_text(GTK_ENTRY(search_window.from_entry)); - to_str = gtk_entry_get_text(GTK_ENTRY(search_window.to_entry)); - subject_str = gtk_entry_get_text(GTK_ENTRY(search_window.subject_entry)); - body_str = gtk_entry_get_text(GTK_ENTRY(search_window.body_entry)); - - if (search_all) { - summary_unselect_all(summaryview); - valid = gtk_tree_model_get_iter_first(model, &iter); - backward = FALSE; - } else if (!summaryview->selected) { - if (backward) - valid = gtkut_tree_model_get_iter_last(model, &iter); - else - valid = gtk_tree_model_get_iter_first(model, &iter); - if (!valid) { - summary_unlock(summaryview); - return; - } - } else { - valid = gtkut_tree_row_reference_get_iter - (model, summaryview->selected, &iter); - if (!valid) { - summary_unlock(summaryview); - return; + for (cur = search_window.cond_edit->cond_hbox_list; cur != NULL; + cur = cur->next) { + CondHBox *hbox = (CondHBox *)cur->data; + gchar *error_msg; + + cond = prefs_filter_edit_cond_hbox_to_cond(hbox, case_sens, + &error_msg); + if (cond) { + cond_list = g_slist_append(cond_list, cond); + } else { + if (!error_msg) + error_msg = _("Invalid condition exists."); + alertpanel_error("%s", error_msg); + filter_cond_list_free(cond_list); + return NULL; } + } - if (backward) - valid = gtkut_tree_model_prev(model, &iter); - else - valid = gtkut_tree_model_next(model, &iter); + if (!cond_list) + return NULL; + + rule = filter_rule_new(name, bool_op, cond_list, NULL); + rule->target_folder = g_strdup(id); + rule->recursive = recursive; + + return rule; +} + +static void summary_search_query(void) +{ + FolderItem *item; + + if (search_window.on_search) + return; + + search_window.on_search = TRUE; + + search_window.rule = summary_search_dialog_to_rule("Query rule", &item); + if (!search_window.rule) { + search_window.on_search = FALSE; + return; } + search_window.requires_full_headers = + filter_rule_requires_full_headers(search_window.rule); - if (*body_str) - main_window_cursor_wait(summaryview->mainwin); + search_window.cancelled = FALSE; - for (;;) { - if (!valid) { - gchar *str; - AlertValue val; + gtk_button_set_label(GTK_BUTTON(search_window.search_btn), + GTK_STOCK_STOP); + summary_search_clear_list(); - if (search_all) { - break; - } + if (search_window.rule->recursive) + g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, + summary_search_recursive_func, NULL); + else + summary_search_folder(item); - if (all_searched) { - alertpanel_message - (_("Search failed"), - _("Search string not found."), - ALERT_WARNING); - break; - } + filter_rule_free(search_window.rule); + search_window.rule = NULL; + search_window.requires_full_headers = FALSE; - if (backward) - str = _("Beginning of list reached; continue from end?"); - else - str = _("End of list reached; continue from beginning?"); - - val = alertpanel(_("Search finished"), str, - GTK_STOCK_YES, GTK_STOCK_NO, NULL); - if (G_ALERTDEFAULT == val) { - if (backward) - valid = gtkut_tree_model_get_iter_last - (model, &iter); - else - valid = gtk_tree_model_get_iter_first - (model, &iter); - all_searched = TRUE; - manage_window_focus_in(search_window.window, - NULL, NULL); - } else - break; - } + gtk_button_set_label(GTK_BUTTON(search_window.search_btn), + GTK_STOCK_FIND); + gtk_label_set_text(GTK_LABEL(search_window.status_label), _("Done.")); - gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1); - body_matched = FALSE; + if (search_window.cancelled) + debug_print("* query search cancelled.\n"); + debug_print("query search finished.\n"); - if (bool_and) { - matched = TRUE; - if (*from_str) { - if (!msginfo->from || - !str_find_func(msginfo->from, from_str)) - matched = FALSE; - } - if (matched && *to_str) { - if (!msginfo->to || - !str_find_func(msginfo->to, to_str)) - matched = FALSE; - } - if (matched && *subject_str) { - if (!msginfo->subject || - !str_find_func(msginfo->subject, subject_str)) - matched = FALSE; - } - if (matched && *body_str) { - if (procmime_find_string(msginfo, body_str, - str_find_func)) - body_matched = TRUE; - else - matched = FALSE; - } - if (matched && !*from_str && !*to_str && - !*subject_str && !*body_str) - matched = FALSE; - } else { - matched = FALSE; - if (*from_str && msginfo->from) { - if (str_find_func(msginfo->from, from_str)) - matched = TRUE; - } - if (!matched && *to_str && msginfo->to) { - if (str_find_func(msginfo->to, to_str)) - matched = TRUE; - } - if (!matched && *subject_str && msginfo->subject) { - if (str_find_func(msginfo->subject, subject_str)) - matched = TRUE; - } - if (!matched && *body_str) { - if (procmime_find_string(msginfo, body_str, - str_find_func)) { - matched = TRUE; - body_matched = TRUE; - } - } - } + search_window.on_search = FALSE; + search_window.cancelled = FALSE; +} - if (matched) { - if (search_all) { - gtk_tree_selection_select_iter - (summaryview->selection, &iter); - } else { - if (messageview_is_visible - (summaryview->messageview)) { - summary_unlock(summaryview); - summary_select_row - (summaryview, &iter, - TRUE, TRUE); - summary_lock(summaryview); - if (body_matched) { - messageview_search_string - (summaryview->messageview, - body_str, case_sens); - } - } else { - summary_select_row - (summaryview, &iter, - FALSE, TRUE); - } - break; - } +static void summary_search_folder(FolderItem *item) +{ + gchar *folder_name, *str; + GSList *mlist; + FilterInfo fltinfo; + GSList *cur; + gint count = 1, total; + GTimeVal tv_prev, tv_cur; + + if (!item->path) + return; + + folder_name = g_path_get_basename(item->path); + str = g_strdup_printf(_("Searching %s ..."), folder_name); + gtk_label_set_text(GTK_LABEL(search_window.status_label), str); + g_free(str); + g_get_current_time(&tv_prev); + ui_update(); + + if (search_window.cancelled) { + g_free(folder_name); + return; + } + + mlist = folder_item_get_msg_list(item, TRUE); + total = g_slist_length(mlist); + + memset(&fltinfo, 0, sizeof(FilterInfo)); + + debug_print("requires_full_headers: %d\n", + search_window.requires_full_headers); + debug_print("start query search: %s\n", item->path ? item->path : ""); + + for (cur = mlist; cur != NULL; cur = cur->next) { + MsgInfo *msginfo = (MsgInfo *)cur->data; + GSList *hlist; + + g_get_current_time(&tv_cur); + if (tv_cur.tv_sec > tv_prev.tv_sec || + tv_cur.tv_usec - tv_prev.tv_usec > + PROGRESS_UPDATE_INTERVAL * 1000) { + str = g_strdup_printf(_("Searching %s (%d / %d)..."), + folder_name, count, total); + gtk_label_set_text + (GTK_LABEL(search_window.status_label), str); + g_free(str); + ui_update(); + tv_prev = tv_cur; + } + ++count; + + if (search_window.cancelled) + break; + + fltinfo.flags = msginfo->flags; + if (search_window.requires_full_headers) { + gchar *file; + + file = procmsg_get_message_file(msginfo); + hlist = procheader_get_header_list_from_file(file); + g_free(file); + } else + hlist = procheader_get_header_list_from_msginfo + (msginfo); + if (!hlist) + continue; + + if (filter_match_rule(search_window.rule, msginfo, hlist, + &fltinfo)) { + summary_search_append_msg(msginfo); + cur->data = NULL; } - if (backward) - valid = gtkut_tree_model_prev(model, &iter); - else - valid = gtkut_tree_model_next(model, &iter); + procheader_header_list_destroy(hlist); } - if (*body_str) - main_window_cursor_normal(summaryview->mainwin); + procmsg_msg_list_free(mlist); + g_free(folder_name); +} - summary_unlock(summaryview); +static gboolean summary_search_recursive_func(GNode *node, gpointer data) +{ + FolderItem *item; + + g_return_val_if_fail(node->data != NULL, FALSE); + + item = FOLDER_ITEM(node->data); + + if (!item->path) + return FALSE; + + summary_search_folder(item); + + if (search_window.cancelled) + return TRUE; + + return FALSE; +} + +static void summary_search_append_msg(MsgInfo *msginfo) +{ + GtkListStore *store = search_window.store; + GtkTreeIter iter; + gchar *folder; + gchar date_buf[80]; + const gchar *subject, *from, *date; + gchar *id; + + id = folder_item_get_identifier(msginfo->folder); + folder = g_path_get_basename(id); + g_free(id); + subject = msginfo->subject ? msginfo->subject : _("(No Subject)"); + from = msginfo->from ? msginfo->from : _("(No From)"); + if (msginfo->date_t) { + procheader_date_get_localtime(date_buf, sizeof(date_buf), + msginfo->date_t); + date = date_buf; + } else if (msginfo->date) + date = msginfo->date; + else + date = _("(No Date)"); + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, + COL_FOLDER, folder, + COL_SUBJECT, subject, + COL_FROM, from, + COL_DATE, date, + COL_MSGINFO, msginfo, + -1); + + g_free(folder); +} + +static void summary_search_clear_list(void) +{ + GtkTreeIter iter; + GtkTreeModel *model = GTK_TREE_MODEL(search_window.store); + MsgInfo *msginfo; + + if (!gtk_tree_model_get_iter_first(model, &iter)) + return; + + do { + gtk_tree_model_get(model, &iter, COL_MSGINFO, &msginfo, -1); + procmsg_msginfo_free(msginfo); + } while (gtk_tree_model_iter_next(model, &iter)); + + gtk_list_store_clear(search_window.store); +} + +static void summary_search_hbox_added(CondHBox *hbox) +{ + g_signal_connect(hbox->key_entry, "activate", + G_CALLBACK(summary_search_entry_activated), NULL); +} + +static void row_activated(GtkTreeView *treeview, GtkTreePath *path, + GtkTreeViewColumn *column, gpointer data) +{ + GtkTreeIter iter; + GtkTreeModel *model = GTK_TREE_MODEL(search_window.store); + MsgInfo *msginfo; + MessageView *msgview; + + if (!gtk_tree_model_get_iter(model, &iter, path)) + return; + + gtk_tree_model_get(model, &iter, COL_MSGINFO, &msginfo, -1); + msgview = messageview_create_with_new_window(); + messageview_show(msgview, msginfo, FALSE); +} + +static gboolean row_selected(GtkTreeSelection *selection, + GtkTreeModel *model, GtkTreePath *path, + gboolean cur_selected, gpointer data) +{ + return TRUE; } static void summary_search_clear(GtkButton *button, gpointer data) { - gtk_editable_delete_text(GTK_EDITABLE(search_window.from_entry), - 0, -1); - gtk_editable_delete_text(GTK_EDITABLE(search_window.to_entry), - 0, -1); - gtk_editable_delete_text(GTK_EDITABLE(search_window.subject_entry), - 0, -1); - gtk_editable_delete_text(GTK_EDITABLE(search_window.body_entry), - 0, -1); + CondHBox *cond_hbox; + + prefs_filter_edit_clear_cond_edit(search_window.cond_edit); + prefs_filter_set_header_list(NULL); + prefs_filter_edit_set_header_list(search_window.cond_edit, NULL); + cond_hbox = prefs_filter_edit_cond_hbox_create(search_window.cond_edit); + prefs_filter_edit_set_cond_hbox_widgets(cond_hbox, PF_COND_HEADER); + prefs_filter_edit_insert_cond_hbox + (search_window.cond_edit, cond_hbox, -1); + if (search_window.cond_edit->add_hbox) + search_window.cond_edit->add_hbox(cond_hbox); + + gtk_label_set_text(GTK_LABEL(search_window.status_label), ""); + + summary_search_clear_list(); } -static void summary_search_prev_clicked(GtkButton *button, gpointer data) +static void summary_select_folder(GtkButton *button, gpointer data) { - summary_search_execute(TRUE, FALSE); + FolderItem *item; + gchar *id; + + item = foldersel_folder_sel(NULL, FOLDER_SEL_ALL, NULL); + if (!item) + return; + + id = folder_item_get_identifier(item); + if (id) { + gtk_entry_set_text(GTK_ENTRY(search_window.folder_entry), id); + g_free(id); + } } -static void summary_search_next_clicked(GtkButton *button, gpointer data) +static void summary_search_clicked(GtkButton *button, gpointer data) { - summary_search_execute(FALSE, FALSE); + if (search_window.on_search) + search_window.cancelled = TRUE; + else + summary_search_query(); } -static void summary_search_all_clicked(GtkButton *button, gpointer data) +static gint summary_search_save_dialog_deleted(GtkWidget *widget, + GdkEventAny *event, + gpointer data) { - summary_search_execute(FALSE, TRUE); + SummarySearchSaveDialog *dialog = (SummarySearchSaveDialog *)data; + + dialog->cancelled = TRUE; + dialog->finished = TRUE; + return TRUE; } -static void from_activated(void) +static gint summary_search_save_dialog_key_pressed(GtkWidget *widget, + GdkEventKey *event, + gpointer data) { - gtk_widget_grab_focus(search_window.to_entry); + SummarySearchSaveDialog *dialog = (SummarySearchSaveDialog *)data; + + if (event && event->keyval == GDK_Escape) { + dialog->cancelled = TRUE; + dialog->finished = TRUE; + } + return FALSE; } -static void to_activated(void) +static void summary_search_save_dialog_select_folder(GtkButton *button, + gpointer data) { - gtk_widget_grab_focus(search_window.subject_entry); + SummarySearchSaveDialog *dialog = (SummarySearchSaveDialog *)data; + FolderItem *item; + gchar *id; + + item = foldersel_folder_sel(NULL, FOLDER_SEL_ALL, NULL); + if (!item) + return; + + id = folder_item_get_identifier(item); + if (id) { + gtk_entry_set_text(GTK_ENTRY(dialog->folder_entry), id); + g_free(id); + } } -static void subject_activated(void) +static void summary_search_save_ok(GtkButton *button, gpointer data) { - gtk_button_clicked(GTK_BUTTON(search_window.next_btn)); + SummarySearchSaveDialog *dialog = (SummarySearchSaveDialog *)data; + + dialog->finished = TRUE; } -static void body_activated(void) +static void summary_search_save_cancel(GtkButton *button, gpointer data) { - gtk_button_clicked(GTK_BUTTON(search_window.next_btn)); + SummarySearchSaveDialog *dialog = (SummarySearchSaveDialog *)data; + + dialog->cancelled = TRUE; + dialog->finished = TRUE; +} + +static SummarySearchSaveDialog *summary_search_save_dialog_create(void) +{ + SummarySearchSaveDialog *dialog; + GtkWidget *window; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *folder_entry; + GtkWidget *folder_btn; + GtkWidget *name_entry; + + GtkWidget *confirm_area; + GtkWidget *hbbox; + GtkWidget *cancel_btn; + GtkWidget *ok_btn; + + dialog = g_new0(SummarySearchSaveDialog, 1); + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), _("Save as search folder")); + gtk_widget_set_size_request(window, 400, -1); + gtk_container_set_border_width(GTK_CONTAINER(window), 8); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, FALSE); + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(summary_search_save_dialog_deleted), + dialog); + g_signal_connect(G_OBJECT(window), "key_press_event", + G_CALLBACK(summary_search_save_dialog_key_pressed), + dialog); + MANAGE_WINDOW_SIGNALS_CONNECT(window); + manage_window_set_transient(GTK_WINDOW(window)); + + vbox = gtk_vbox_new(FALSE, 8); + gtk_container_add(GTK_CONTAINER(window), vbox); + + hbox = gtk_hbox_new(FALSE, 8); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new(_("Location:")); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + folder_entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(hbox), folder_entry, TRUE, TRUE, 0); + + folder_btn = gtk_button_new_with_label("..."); + gtk_box_pack_start(GTK_BOX(hbox), folder_btn, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(folder_btn), "clicked", + G_CALLBACK(summary_search_save_dialog_select_folder), + dialog); + + hbox = gtk_hbox_new(FALSE, 8); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new(_("Folder name:")); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + name_entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(hbox), name_entry, TRUE, TRUE, 0); + + confirm_area = gtk_hbox_new(FALSE, 12); + gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0); + + gtkut_stock_button_set_create(&hbbox, + &ok_btn, GTK_STOCK_OK, + &cancel_btn, GTK_STOCK_CANCEL, + NULL, NULL); + gtk_box_pack_end(GTK_BOX(confirm_area), hbbox, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(ok_btn), "clicked", + G_CALLBACK(summary_search_save_ok), dialog); + g_signal_connect(G_OBJECT(cancel_btn), "clicked", + G_CALLBACK(summary_search_save_cancel), dialog); + + gtk_widget_show_all(window); + + dialog->window = window; + dialog->folder_entry = folder_entry; + dialog->name_entry = name_entry; + dialog->cancelled = FALSE; + dialog->finished = FALSE; + + return dialog; +} + +static void summary_search_save_dialog_destroy(SummarySearchSaveDialog *dialog) +{ + gtk_widget_destroy(dialog->window); + g_free(dialog); +} + +static FolderItem *summary_search_create_vfolder(FolderItem *parent, + const gchar *name) +{ + gchar *path; + gchar *fs_name; + gchar *fullpath; + FolderItem *item; + + g_return_val_if_fail(parent != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + + path = folder_item_get_path(parent); + fs_name = g_filename_from_utf8(name, -1, NULL, NULL, NULL); + fullpath = g_strconcat(path, G_DIR_SEPARATOR_S, + fs_name ? fs_name : name, NULL); + g_free(fs_name); + g_free(path); + + if (make_dir_hier(fullpath) < 0) { + g_free(fullpath); + return NULL; + } + + if (parent->path) + path = g_strconcat(parent->path, G_DIR_SEPARATOR_S, name, NULL); + else + path = g_strdup(name); + + item = folder_item_new(name, path); + item->stype = F_VIRTUAL; + folder_item_append(parent, item); + + g_free(path); + + return item; +} + +static void summary_search_vfolder_update_rule(FolderItem *item) +{ + GSList list; + FilterRule *rule; + gchar *file; + gchar *path; + + rule = summary_search_dialog_to_rule(item->name, NULL); + list.data = rule; + list.next = NULL; + + path = folder_item_get_path(item); + file = g_strconcat(path, G_DIR_SEPARATOR_S, FILTER_LIST, NULL); + filter_write_file(&list, file); + g_free(file); + g_free(path); + + filter_rule_free(rule); +} + +static void summary_search_save(GtkButton *button, gpointer data) +{ + SummarySearchSaveDialog *dialog; + + dialog = summary_search_save_dialog_create(); + + while (!dialog->finished) + gtk_main_iteration(); + + if (!dialog->cancelled) { + const gchar *id, *name; + FolderItem *parent, *item; + + id = gtk_entry_get_text(GTK_ENTRY(dialog->folder_entry)); + parent = folder_find_item_from_identifier(id); + name = gtk_entry_get_text(GTK_ENTRY(dialog->name_entry)); + if (parent && name && *name) { + if (folder_find_child_item_by_name(parent, name)) { + alertpanel_error + (_("The folder `%s' already exists."), + name); + } else { + item = summary_search_create_vfolder + (parent, name); + if (item) { + summary_search_vfolder_update_rule + (item); + folderview_append_item(folderview_get(), + NULL, item, + TRUE); + folder_write_list(); + } + } + } + } + + summary_search_save_dialog_destroy(dialog); +} + +static void summary_search_close(GtkButton *button, gpointer data) +{ + if (search_window.on_search) + search_window.cancelled = TRUE; + gtk_widget_hide(search_window.window); +} + +static void summary_search_entry_activated(GtkWidget *widget, gpointer data) +{ + gtk_button_clicked(GTK_BUTTON(search_window.search_btn)); +} + +static gint summary_search_deleted(GtkWidget *widget, GdkEventAny *event, + gpointer data) +{ + gtk_button_clicked(GTK_BUTTON(search_window.close_btn)); + return TRUE; } static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data) { - if (event && event->keyval == GDK_Escape) - gtk_widget_hide(search_window.window); + if (event && event->keyval == GDK_Escape) { + if (search_window.on_search) + gtk_button_clicked + (GTK_BUTTON(search_window.search_btn)); + else + gtk_button_clicked(GTK_BUTTON(search_window.close_btn)); + return TRUE; + } return FALSE; } diff --git a/src/summary_search.h b/src/summary_search.h index 8e3fcc0c..2b0d3cc6 100644 --- a/src/summary_search.h +++ b/src/summary_search.h @@ -23,7 +23,9 @@ #include <glib.h> #include "summaryview.h" +#include "folder.h" -void summary_search (SummaryView *summaryview); +void summary_search (SummaryView *summaryview, + FolderItem *item); #endif /* __SUMMARY_SEARCH_H__ */ diff --git a/src/summaryview.c b/src/summaryview.c index 498d965e..214c735d 100644 --- a/src/summaryview.c +++ b/src/summaryview.c @@ -649,7 +649,7 @@ gboolean summary_show(SummaryView *summaryview, FolderItem *item, buf = NULL; if (!item || !item->path || !item->parent || item->no_select || - (FOLDER_TYPE(item->folder) == F_MH && + (FOLDER_TYPE(item->folder) == F_MH && item->stype != F_VIRTUAL && ((buf = folder_item_get_path(item)) == NULL || change_dir(buf) < 0))) { g_free(buf); |