2012-09-21 [colin] 3.8.1cvs68
authorColin Leroy <colin@colino.net>
Fri, 21 Sep 2012 10:19:43 +0000 (10:19 +0000)
committerColin Leroy <colin@colino.net>
Fri, 21 Sep 2012 10:19:43 +0000 (10:19 +0000)
* src/Makefile.am
* src/advsearch.c ** ADDED **
* src/advsearch.h ** ADDED **
* src/folder.c
* src/folder.h
* src/folderview.c
* src/imap.c
* src/main.c
* src/matcher.c
* src/matcher.h
* src/matchertypes.h ** ADDED **
* src/mh.c
* src/mimeview.c
* src/news.c
* src/proctypes.h
* src/summary_search.c
* src/summaryview.c
* src/summaryview.h
* src/gtk/quicksearch.c
* src/gtk/quicksearch.h
Separated GUI and logic for search
Use same logic for quicksearch and folder search
Patch by Sean Buckheister <s_buckhe@cs.uni-kl.de>

23 files changed:
ChangeLog
PATCHSETS
configure.ac
src/Makefile.am
src/advsearch.c [new file with mode: 0644]
src/advsearch.h [new file with mode: 0644]
src/folder.c
src/folder.h
src/folderview.c
src/gtk/quicksearch.c
src/gtk/quicksearch.h
src/imap.c
src/main.c
src/matcher.c
src/matcher.h
src/matchertypes.h [new file with mode: 0644]
src/mh.c
src/mimeview.c
src/news.c
src/proctypes.h
src/summary_search.c
src/summaryview.c
src/summaryview.h

index aa8da2c..2c9f15c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,29 @@
+2012-09-21 [colin]     3.8.1cvs68
+
+       * src/Makefile.am
+       * src/advsearch.c ** ADDED **
+       * src/advsearch.h ** ADDED **
+       * src/folder.c
+       * src/folder.h
+       * src/folderview.c
+       * src/imap.c
+       * src/main.c
+       * src/matcher.c
+       * src/matcher.h
+       * src/matchertypes.h ** ADDED **
+       * src/mh.c
+       * src/mimeview.c
+       * src/news.c
+       * src/proctypes.h
+       * src/summary_search.c
+       * src/summaryview.c
+       * src/summaryview.h
+       * src/gtk/quicksearch.c
+       * src/gtk/quicksearch.h
+               Separated GUI and logic for search
+               Use same logic for quicksearch and folder search
+               Patch by Sean Buckheister <s_buckhe@cs.uni-kl.de>
+
 2012-09-19 [mones]     3.8.1cvs67
 
        * src/mainwindow.c
index 60368af..f1a301d 100644 (file)
--- a/PATCHSETS
+++ b/PATCHSETS
 ( cvs diff -u -r 1.28.2.50 -r 1.28.2.51 src/addrindex.c;  cvs diff -u -r 1.14.2.64 -r 1.14.2.65 src/editaddress.c;  cvs diff -u -r 1.8.2.46 -r 1.8.2.47 src/editldap.c;  cvs diff -u -r 1.5.2.32 -r 1.5.2.33 src/exporthtml.c;  cvs diff -u -r 1.18.2.36 -r 1.18.2.37 src/jpilot.c;  cvs diff -u -r 1.49.2.150 -r 1.49.2.151 src/procmime.c;  cvs diff -u -r 1.1.2.43 -r 1.1.2.44 src/plugins/bogofilter/bogofilter_gtk.c;  cvs diff -u -r 1.1.2.38 -r 1.1.2.39 src/plugins/pgpcore/prefs_gpg.c;  cvs diff -u -r 1.1.2.12 -r 1.1.2.13 src/plugins/smime/smime.c;  cvs diff -u -r 1.23.2.60 -r 1.23.2.61 src/plugins/spamassassin/spamassassin_gtk.c;  ) > 3.8.1cvs65.patchset
 ( cvs diff -u -r 1.60.2.151 -r 1.60.2.152 src/addressbook.c;  cvs diff -u -r 1.3.2.12 -r 1.3.2.13 src/addrselect.c;  ) > 3.8.1cvs66.patchset
 ( cvs diff -u -r 1.274.2.352 -r 1.274.2.353 src/mainwindow.c;  ) > 3.8.1cvs67.patchset
+( cvs diff -u -r 1.155.2.102 -r 1.155.2.103 src/Makefile.am;  diff -u /dev/null src/advsearch.c;  diff -u /dev/null src/advsearch.h;  cvs diff -u -r 1.213.2.211 -r 1.213.2.212 src/folder.c;  cvs diff -u -r 1.87.2.67 -r 1.87.2.68 src/folder.h;  cvs diff -u -r 1.207.2.229 -r 1.207.2.230 src/folderview.c;  cvs diff -u -r 1.179.2.262 -r 1.179.2.263 src/imap.c;  cvs diff -u -r 1.115.2.257 -r 1.115.2.258 src/main.c;  cvs diff -u -r 1.75.2.75 -r 1.75.2.76 src/matcher.c;  cvs diff -u -r 1.39.2.21 -r 1.39.2.22 src/matcher.h;  diff -u /dev/null src/matchertypes.h;  cvs diff -u -r 1.79.2.76 -r 1.79.2.77 src/mh.c;  cvs diff -u -r 1.83.2.195 -r 1.83.2.196 src/mimeview.c;  cvs diff -u -r 1.101.2.72 -r 1.101.2.73 src/news.c;  cvs diff -u -r 1.1.2.1 -r 1.1.2.2 src/proctypes.h;  cvs diff -u -r 1.15.2.70 -r 1.15.2.71 src/summary_search.c;  cvs diff -u -r 1.395.2.453 -r 1.395.2.454 src/summaryview.c;  cvs diff -u -r 1.68.2.61 -r 1.68.2.62 src/summaryview.h;  cvs diff -u -r 1.1.2.111 -r 1.1.2.112 src/gtk/quicksearch.c;  cvs diff -u -r 1.1.2.24 -r 1.1.2.25 src/gtk/quicksearch.h;  ) > 3.8.1cvs68.patchset
index 2c7800b..dae7f0a 100644 (file)
@@ -12,7 +12,7 @@ MINOR_VERSION=8
 MICRO_VERSION=1
 INTERFACE_AGE=0
 BINARY_AGE=0
-EXTRA_VERSION=67
+EXTRA_VERSION=68
 EXTRA_RELEASE=
 EXTRA_GTK2_VERSION=
 
index 12030c0..bb162be 100644 (file)
@@ -126,6 +126,7 @@ claws_mail_SOURCES = \
        addrgather.c \
        addrharvest.c \
        addritem.c \
+       advsearch.c \
        alertpanel.c \
        autofaces.c \
        codeconv.c \
@@ -238,6 +239,7 @@ claws_mailinclude_HEADERS = \
        addressitem.h \
        addrgather.h \
        addrharvest.h \
+       advsearch.h \
        alertpanel.h \
        autofaces.h \
        codeconv.h \
@@ -267,6 +269,7 @@ claws_mailinclude_HEADERS = \
        mainwindow.h \
        manual.h \
        matcher.h \
+       matchertypes.h \
        matcher_parser.h \
        matcher_parser_lex.h \
        matcher_parser_parse.h \
diff --git a/src/advsearch.c b/src/advsearch.c
new file mode 100644 (file)
index 0000000..306afa7
--- /dev/null
@@ -0,0 +1,496 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+# include "claws-features.h"
+#endif
+
+#include "advsearch.h"
+
+#include <glib.h>
+#include <ctype.h>
+
+#include "matcher.h"
+#include "matcher_parser.h"
+#include "utils.h"
+
+struct _AdvancedSearch {
+       struct {
+               AdvancedSearchType       type;
+               gchar                   *matchstring;
+       } request;
+
+       MatcherList                     *predicate;
+       gboolean                         is_fast;
+       gboolean                         search_aborted;
+
+       struct {
+               gboolean (*cb)(gpointer data, guint at, guint matched, guint total);
+               gpointer data;
+       } on_progress_cb;
+       struct {
+               void (*cb)(gpointer data);
+               gpointer data;
+       } on_error_cb;
+};
+
+void advsearch_set_on_progress_cb(AdvancedSearch *search, gboolean (*cb)(gpointer, guint, guint, guint), gpointer data)
+{
+       search->on_progress_cb.cb = cb;
+       search->on_progress_cb.data = data;
+}
+
+void advsearch_set_on_error_cb(AdvancedSearch* search, void (*cb)(gpointer data), gpointer data)
+{
+       search->on_error_cb.cb = cb;
+       search->on_error_cb.data = data;
+}
+
+static void prepare_matcher(AdvancedSearch *search);
+static gboolean search_impl(MsgInfoList **messages, AdvancedSearch* search,
+                           FolderItem* folderItem, gboolean recursive);
+
+// --------------------------
+
+AdvancedSearch* advsearch_new()
+{
+       AdvancedSearch *result;
+
+       result = g_new0(AdvancedSearch, 1);
+
+       return result;
+}
+
+void advsearch_free(AdvancedSearch *search)
+{
+       if (search->predicate != NULL)
+               matcherlist_free(search->predicate);
+
+       g_free(search->request.matchstring);
+       g_free(search);
+}
+
+void advsearch_set(AdvancedSearch *search, AdvancedSearchType type, const gchar *matchstring)
+{
+       cm_return_if_fail(search != NULL);
+
+       search->request.type = type;
+
+       g_free(search->request.matchstring);
+       search->request.matchstring = g_strdup(matchstring);
+
+       prepare_matcher(search);
+}
+
+gboolean advsearch_is_fast(AdvancedSearch *search)
+{
+       cm_return_val_if_fail(search != NULL, FALSE);
+
+       return search->is_fast;
+}
+
+gboolean advsearch_has_proper_predicate(AdvancedSearch *search)
+{
+       cm_return_val_if_fail(search != NULL, FALSE);
+
+       return search->predicate != NULL;
+}
+
+gboolean advsearch_search_msgs_in_folders(AdvancedSearch* search, MsgInfoList **messages,
+                                         FolderItem* folderItem, gboolean recursive)
+{
+       if (search == NULL || search->predicate == NULL)
+               return FALSE;
+
+       search->search_aborted = FALSE;
+       return search_impl(messages, search, folderItem, recursive);
+}
+
+void advsearch_abort(AdvancedSearch *search)
+{
+       search->search_aborted = TRUE;
+}
+
+gchar *advsearch_expand_search_string(const gchar *search_string)
+{
+       int i = 0;
+       gchar term_char, save_char;
+       gchar *cmd_start, *cmd_end;
+       GString *matcherstr;
+       gchar *returnstr = NULL;
+       gchar *copy_str;
+       gboolean casesens, dontmatch, regex;
+       /* list of allowed pattern abbreviations */
+       struct {
+               gchar           *abbreviated;   /* abbreviation */
+               gchar           *command;       /* actual matcher command */
+               gint            numparams;      /* number of params for cmd */
+               gboolean        qualifier;      /* do we append stringmatch operations */
+               gboolean        quotes;         /* do we need quotes */
+       }
+       cmds[] = {
+               { "a",  "all",                          0,      FALSE,  FALSE },
+               { "ag", "age_greater",                  1,      FALSE,  FALSE },
+               { "al", "age_lower",                    1,      FALSE,  FALSE },
+               { "b",  "body_part",                    1,      TRUE,   TRUE  },
+               { "B",  "message",                      1,      TRUE,   TRUE  },
+               { "c",  "cc",                           1,      TRUE,   TRUE  },
+               { "C",  "to_or_cc",                     1,      TRUE,   TRUE  },
+               { "D",  "deleted",                      0,      FALSE,  FALSE },
+               { "e",  "header \"Sender\"",            1,      TRUE,   TRUE  },
+               { "E",  "execute",                      1,      FALSE,  TRUE  },
+               { "f",  "from",                         1,      TRUE,   TRUE  },
+               { "F",  "forwarded",                    0,      FALSE,  FALSE },
+               { "h",  "headers_part",                 1,      TRUE,   TRUE  },
+               { "ha", "has_attachments",              0,      FALSE,  FALSE },
+               { "i",  "header \"Message-ID\"",        1,      TRUE,   TRUE  },
+               { "I",  "inreplyto",                    1,      TRUE,   TRUE  },
+               { "k",  "colorlabel",                   1,      FALSE,  FALSE },
+               { "L",  "locked",                       0,      FALSE,  FALSE },
+               { "n",  "newsgroups",                   1,      TRUE,   TRUE  },
+               { "N",  "new",                          0,      FALSE,  FALSE },
+               { "O",  "~new",                         0,      FALSE,  FALSE },
+               { "r",  "replied",                      0,      FALSE,  FALSE },
+               { "R",  "~unread",                      0,      FALSE,  FALSE },
+               { "s",  "subject",                      1,      TRUE,   TRUE  },
+               { "se", "score_equal",                  1,      FALSE,  FALSE },
+               { "sg", "score_greater",                1,      FALSE,  FALSE },
+               { "sl", "score_lower",                  1,      FALSE,  FALSE },
+               { "Se", "size_equal",                   1,      FALSE,  FALSE },
+               { "Sg", "size_greater",                 1,      FALSE,  FALSE },
+               { "Ss", "size_smaller",                 1,      FALSE,  FALSE },
+               { "t",  "to",                           1,      TRUE,   TRUE  },
+               { "tg", "tag",                          1,      TRUE,   TRUE  },
+               { "T",  "marked",                       0,      FALSE,  FALSE },
+               { "U",  "unread",                       0,      FALSE,  FALSE },
+               { "x",  "header \"References\"",        1,      TRUE,   TRUE  },
+               { "X",  "test",                         1,      FALSE,  FALSE },
+               { "y",  "header \"X-Label\"",           1,      TRUE,   TRUE  },
+               { "&",  "&",                            0,      FALSE,  FALSE },
+               { "|",  "|",                            0,      FALSE,  FALSE },
+               { "p",  "partial",                      0,      FALSE,  FALSE },
+               { NULL, NULL,                           0,      FALSE,  FALSE }
+       };
+
+       if (search_string == NULL)
+               return NULL;
+
+       copy_str = g_strdup(search_string);
+
+       matcherstr = g_string_sized_new(16);
+       cmd_start = copy_str;
+       while (cmd_start && *cmd_start) {
+               /* skip all white spaces */
+               while (*cmd_start && isspace((guchar)*cmd_start))
+                       cmd_start++;
+               cmd_end = cmd_start;
+
+               /* extract a command */
+               while (*cmd_end && !isspace((guchar)*cmd_end))
+                       cmd_end++;
+
+               /* save character */
+               save_char = *cmd_end;
+               *cmd_end = '\0';
+
+               dontmatch = FALSE;
+               casesens = FALSE;
+               regex = FALSE;
+
+               /* ~ and ! mean logical NOT */
+               if (*cmd_start == '~' || *cmd_start == '!')
+               {
+                       dontmatch = TRUE;
+                       cmd_start++;
+               }
+               /* % means case sensitive match */
+               if (*cmd_start == '%')
+               {
+                       casesens = TRUE;
+                       cmd_start++;
+               }
+               /* # means regex match */
+               if (*cmd_start == '#') {
+                       regex = TRUE;
+                       cmd_start++;
+               }
+
+               /* find matching abbreviation */
+               for (i = 0; cmds[i].command; i++) {
+                       if (!strcmp(cmd_start, cmds[i].abbreviated)) {
+                               /* restore character */
+                               *cmd_end = save_char;
+
+                               /* copy command */
+                               if (matcherstr->len > 0) {
+                                       g_string_append(matcherstr, " ");
+                               }
+                               if (dontmatch)
+                                       g_string_append(matcherstr, "~");
+                               g_string_append(matcherstr, cmds[i].command);
+                               g_string_append(matcherstr, " ");
+
+                               /* stop if no params required */
+                               if (cmds[i].numparams == 0)
+                                       break;
+
+                               /* extract a parameter, allow quotes */
+                               while (*cmd_end && isspace((guchar)*cmd_end))
+                                       cmd_end++;
+
+                               cmd_start = cmd_end;
+                               if (*cmd_start == '"') {
+                                       term_char = '"';
+                                       cmd_end++;
+                               }
+                               else
+                                       term_char = ' ';
+
+                               /* extract actual parameter */
+                               while ((*cmd_end) && (*cmd_end != term_char))
+                                       cmd_end++;
+
+                               if (*cmd_end == '"')
+                                       cmd_end++;
+
+                               save_char = *cmd_end;
+                               *cmd_end = '\0';
+
+                               if (cmds[i].qualifier) {
+                                       if (casesens)
+                                               g_string_append(matcherstr, regex ? "regexp " : "match ");
+                                       else
+                                               g_string_append(matcherstr, regex ? "regexpcase " : "matchcase ");
+                               }
+
+                               /* do we need to add quotes ? */
+                               if (cmds[i].quotes && term_char != '"')
+                                       g_string_append(matcherstr, "\"");
+
+                               /* copy actual parameter */
+                               g_string_append(matcherstr, cmd_start);
+
+                               /* do we need to add quotes ? */
+                               if (cmds[i].quotes && term_char != '"')
+                                       g_string_append(matcherstr, "\"");
+
+                               /* restore original character */
+                               *cmd_end = save_char;
+
+                               break;
+                       }
+               }
+
+               if (*cmd_end)
+                       cmd_end++;
+               cmd_start = cmd_end;
+       }
+
+       g_free(copy_str);
+
+       /* return search string if no match is found to allow
+          all available filtering expressions in advanced search */
+       if (matcherstr->len > 0) returnstr = matcherstr->str;
+       else returnstr = g_strdup(search_string);
+       g_string_free(matcherstr, FALSE);
+       return returnstr;
+}
+
+// --------------------------
+
+static gchar *expand_tag_search_string(const gchar *search_string)
+{
+       gchar *newstr = NULL;
+       gchar **words = search_string ? g_strsplit(search_string, " ", -1):NULL;
+       gint i = 0;
+       while (words && words[i] && *words[i]) {
+               g_strstrip(words[i]);
+               if (!newstr) {
+                       newstr = g_strdup_printf("tag matchcase \"%s\"", words[i]);
+               } else {
+                       gint o_len = strlen(newstr);
+                       gint s_len = 17; /* strlen("|tag matchcase \"\"") */
+                       gint n_len = s_len + strlen(words[i]);
+                       newstr = g_realloc(newstr, o_len + n_len + 1);
+                       strcpy(newstr + o_len, "|tag matchcase \"");
+                       strcpy(newstr + o_len + (s_len - 1), words[i]);
+                       strcpy(newstr + o_len + (n_len - 1), "\"");
+               }
+               i++;
+       }
+       g_strfreev(words);
+       return newstr;
+}
+
+static void prepare_matcher_extended(AdvancedSearch *search)
+{
+       gchar *newstr = advsearch_expand_search_string(search->request.matchstring);
+
+       if (newstr && newstr[0] != '\0') {
+               search->predicate = matcher_parser_get_cond(newstr, &search->is_fast);
+               g_free(newstr);
+       }
+}
+
+static void prepare_matcher_tag(AdvancedSearch *search)
+{
+       char *newstr = expand_tag_search_string(search->request.matchstring);
+       search->predicate = matcher_parser_get_cond(newstr, &search->is_fast);
+       g_free(newstr);
+}
+
+static void prepare_matcher_header(AdvancedSearch *search, gint match_header)
+{
+       MatcherProp *matcher;
+
+       if (search->predicate == NULL)
+               search->predicate = g_new0(MatcherList, 1);
+
+       matcher = matcherprop_new(match_header, NULL, MATCHTYPE_MATCHCASE,
+                       search->request.matchstring, 0);
+
+       search->predicate->matchers = g_slist_prepend(search->predicate->matchers, matcher);
+}
+
+static void prepare_matcher_mixed(AdvancedSearch *search)
+{
+       prepare_matcher_tag(search);
+
+       prepare_matcher_header(search, MATCHCRITERIA_SUBJECT);
+       prepare_matcher_header(search, MATCHCRITERIA_FROM);
+       prepare_matcher_header(search, MATCHCRITERIA_TO);
+       prepare_matcher_header(search, MATCHCRITERIA_TAG);
+}
+
+static void prepare_matcher(AdvancedSearch *search)
+{
+       const gchar *search_string;
+
+       cm_return_if_fail(search != NULL);
+
+       if (search->predicate) {
+               matcherlist_free(search->predicate);
+               search->predicate = NULL;
+       }
+
+       search_string = search->request.matchstring;
+
+       if (search_string == NULL || search_string[0] == '\0')
+               return;
+
+       switch (search->request.type) {
+               case ADVANCED_SEARCH_SUBJECT:
+                       prepare_matcher_header(search, MATCHCRITERIA_SUBJECT);
+                       break;
+
+               case ADVANCED_SEARCH_FROM:
+                       prepare_matcher_header(search, MATCHCRITERIA_FROM);
+                       break;
+
+               case ADVANCED_SEARCH_TO:
+                       prepare_matcher_header(search, MATCHCRITERIA_TO);
+                       break;
+
+               case ADVANCED_SEARCH_TAG:
+                       prepare_matcher_header(search, MATCHCRITERIA_TAG);
+                       break;
+
+               case ADVANCED_SEARCH_MIXED:
+                       prepare_matcher_mixed(search);
+                       break;
+
+               case ADVANCED_SEARCH_EXTENDED:
+                       prepare_matcher_extended(search);
+                       break;
+
+               default:
+                       debug_print("unknown search type (%d)\n", search->request.type);
+                       break;
+       }
+}
+
+static gboolean search_progress_notify_cb(gpointer data, gboolean on_server, guint at,
+               guint matched, guint total)
+{
+       AdvancedSearch *search = (AdvancedSearch*) data;
+
+       if (search->search_aborted)
+               return FALSE;
+
+       if (on_server || search->on_progress_cb.cb == NULL)
+               return TRUE;
+
+       return search->on_progress_cb.cb(search->on_progress_cb.data, at, matched, total);
+}
+
+static gboolean search_filter_folder(MsgNumberList **msgnums, AdvancedSearch *search,
+                                         FolderItem *folderItem, gboolean onServer)
+{
+       gint matched;
+
+       matched = folder_item_search_msgs(folderItem->folder,
+               folderItem,
+               msgnums,
+               &onServer,
+               search->predicate,
+               search_progress_notify_cb,
+               search);
+
+       if (matched < 0) {
+               if (search->on_error_cb.cb != NULL)
+                       search->on_error_cb.cb(search->on_error_cb.data);
+               return FALSE;
+       }
+
+       if (folderItem->folder->klass->supports_server_search && !onServer) {
+               return search_filter_folder(msgnums, search, folderItem, onServer);
+       } else {
+               return TRUE;
+       }
+}
+
+static gboolean search_impl(MsgInfoList **messages, AdvancedSearch* search,
+                           FolderItem* folderItem, gboolean recursive)
+{
+       if (recursive) {
+               if (!search_impl(messages, search, folderItem, FALSE))
+                       return FALSE;
+
+               if (folderItem->node->children != NULL && !search->search_aborted) {
+                       GNode *node;
+                       for (node = folderItem->node->children; node != NULL; node = node->next) {
+                               FolderItem *cur = FOLDER_ITEM(node->data);
+                               debug_print("in: %s\n", cur->path);
+                               if (!search_impl(messages, search, cur, TRUE))
+                                       return FALSE;
+                       }
+               }
+       } else {
+               MsgNumberList *msgnums = NULL;
+               MsgNumberList *cur;
+               MsgInfoList *msgs = NULL;
+
+               if (!search_filter_folder(&msgnums, search, folderItem,
+                                       folderItem->folder->klass->supports_server_search)) {
+                       g_slist_free(msgnums);
+                       return FALSE;
+               }
+
+               for (cur = msgnums; cur != NULL; cur = cur->next) {
+                       MsgInfo *msg = folder_item_get_msginfo(folderItem, GPOINTER_TO_UINT(cur->data));
+
+                       msgs = g_slist_prepend(msgs, msg);
+               }
+
+               while (msgs != NULL) {
+                       MsgInfoList *front = msgs;
+
+                       msgs = msgs->next;
+
+                       front->next = *messages;
+                       *messages = front;
+               }
+
+               g_slist_free(msgnums);
+       }
+
+       return TRUE;
+}
diff --git a/src/advsearch.h b/src/advsearch.h
new file mode 100644 (file)
index 0000000..bb68032
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2012 the Claws Mail team
+ *
+ * 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
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
+
+#ifndef ADVSEARCH_H
+#define ADVSEARCH_H 1
+
+#include "proctypes.h"
+#include "folder.h"
+
+// temporary
+#include "matcher.h"
+
+typedef enum
+{
+       ADVANCED_SEARCH_SUBJECT,
+       ADVANCED_SEARCH_FROM,
+       ADVANCED_SEARCH_TO,
+       ADVANCED_SEARCH_EXTENDED,
+       ADVANCED_SEARCH_MIXED,
+       ADVANCED_SEARCH_TAG,
+} AdvancedSearchType;
+
+
+typedef struct _AdvancedSearch AdvancedSearch;
+
+void advsearch_set_on_progress_cb(AdvancedSearch* search,
+               gboolean (*cb)(gpointer data, guint at, guint matched, guint total), gpointer data);
+void advsearch_set_on_error_cb(AdvancedSearch* search, void (*cb)(gpointer data), gpointer data);
+
+AdvancedSearch *advsearch_new();
+void advsearch_free(AdvancedSearch *search);
+
+void advsearch_set(AdvancedSearch *search, AdvancedSearchType type, const gchar *matchstring);
+gboolean advsearch_is_fast(AdvancedSearch *search);
+gboolean advsearch_has_proper_predicate(AdvancedSearch *search);
+
+gboolean advsearch_search_msgs_in_folders(AdvancedSearch* search, MsgInfoList **messages,
+                                         FolderItem* folderItem, gboolean recursive);
+
+void advsearch_abort(AdvancedSearch *search);
+
+gchar *advsearch_expand_search_string(const gchar *search_string);
+
+#endif
index c596f9f..e454d49 100644 (file)
@@ -4689,6 +4689,22 @@ gboolean folder_has_parent_of_type(FolderItem *item,
        return FALSE;
 }
 
+gboolean folder_is_child_of(FolderItem *item, FolderItem *parent)
+{
+       if (item == NULL || parent == NULL)
+               return FALSE;
+
+       while (item != NULL) {
+               if (parent == item)
+                       return TRUE;
+
+               item = folder_item_parent(item);
+       }
+
+       return FALSE;
+}
+
+
 gboolean folder_subscribe (const gchar *uri)
 {
        GList *cur;
@@ -4715,3 +4731,74 @@ gboolean folder_get_sort_type            (Folder         *folder,
        folder->klass->get_sort_type(folder, sort_key, sort_type); 
        return TRUE;
 }
+
+gint folder_item_search_msgs   (Folder                 *folder,
+                                FolderItem             *container,
+                                MsgNumberList          **msgs,
+                                gboolean               *on_server,
+                                MatcherList            *predicate,
+                                SearchProgressNotify   progress_cb,
+                                gpointer               progress_data)
+{
+       if (folder->klass->search_msgs) {
+               return folder->klass->search_msgs(folder, container,
+                               msgs, on_server, predicate, progress_cb, progress_data);
+       } else {
+               return folder_item_search_msgs_local(folder, container,
+                               msgs, on_server, predicate, progress_cb, progress_data);
+       }
+}
+
+gint folder_item_search_msgs_local     (Folder                 *folder,
+                                        FolderItem             *container,
+                                        MsgNumberList          **msgs,
+                                        gboolean               *on_server,
+                                        MatcherList            *predicate,
+                                        SearchProgressNotify   progress_cb,
+                                        gpointer               progress_data)
+{
+       GSList *result = NULL;
+       GSList *cur = NULL;
+       gint matched_count = 0;
+       guint processed_count = 0;
+       gint msgcount;
+       GSList *nums = NULL;
+
+       if (*msgs == NULL) {
+               gboolean old_valid = TRUE;
+              
+               msgcount = folder->klass->get_num_list(folder, container, &nums, &old_valid);
+               
+               if (msgcount < 0)
+                       return -1;
+       } else {
+               nums = *msgs;
+       }
+
+       for (cur = nums; cur != NULL; cur = cur->next) {
+               guint msgnum = GPOINTER_TO_UINT(cur->data);
+               MsgInfo *msg = folder_item_get_msginfo(container, msgnum);
+
+               if (msg == NULL) {
+                       g_slist_free(result);
+                       return -1;
+               }
+
+               if (matcherlist_match(predicate, msg)) {
+                       result = g_slist_prepend(result, GUINT_TO_POINTER(msg->msgnum));
+                       matched_count++;
+               }
+               processed_count++;
+
+               if (progress_cb != NULL
+                   && !progress_cb(progress_data, FALSE, processed_count,
+                           matched_count, msgcount))
+                       break;
+       }
+
+       g_slist_free(nums);
+       *msgs = g_slist_reverse(result);
+
+       return matched_count;
+}
+
index e76a464..e8b5c52 100644 (file)
@@ -132,6 +132,7 @@ typedef void (*FolderItemFunc)      (FolderItem     *item,
 #include "proctypes.h"
 #include "xml.h"
 #include "prefs_account.h"
+#include "matchertypes.h"
 
 struct _MsgCache;
 
@@ -159,6 +160,23 @@ struct _Folder
        GHashTable *newsart;
 };
 
+/**
+ * Callback used to convey progress information of a specific search.
+ *
+ * \param data User-provided data
+ * \param on_server Whether or not the current progress information originated from the
+ *                  server
+ * \param at Number of the last message processed
+ * \param matched Number of messages with definitive matches found so far
+ * \param total Number of messages to be processed
+ *
+ * \note
+ * Even if the mailserver does not support progress reports, an instance of this type
+ * should be invoked when serverside search starts and ends, with \c at set to \c 0 and
+ * \c total, respectively.
+ */
+typedef gboolean (*SearchProgressNotify)(gpointer data, gboolean on_server, guint at, guint matched, guint total);
+
 struct _FolderClass
 {
        /**
@@ -175,6 +193,13 @@ struct _FolderClass
         * user. Can be upper and lowercase unlike the idstr.
         */
        gchar      *uistr;
+       /**
+       * A boolean to indicate whether or not the FolderClass supports search on the
+       * server. If \c TRUE, setting \c on_server in \c search_msgs offloads search to
+       * the server.
+       */
+       gboolean    supports_server_search;
+
        
        /* virtual functions */
 
@@ -505,6 +530,58 @@ struct _FolderClass
                                                 FolderItem     *dest,
                                                 MsgInfoList    *msglist,
                                                 GHashTable     *relation);
+
+       /**
+        * Search the given FolderItem for messages matching \c predicate.
+        * The search may be offloaded to the server if the \c folder
+        * supports server side search, as indicated by \c supports_server_search.
+        *
+        * \param folder The \c Folder of the container FolderItem
+        * \param container The \c FolderItem containing the messages to be searched
+        * \param msgs The \c MsgNumberList results will be saved to.
+        *             If <tt>*msgs != NULL</tt>, the search will be restricted to
+        *             messages whose numbers are contained therein.
+        *             If \c on_server is considered \c FALSE, messages are guaranteed to
+        *             be processed in the order they are listed in \c msgs.
+        *             On error, \c msgs will not be changed.
+        * \param on_server Whether or not the search should be offloaded to the server.
+        *                  If \c on_server is not \c NULL and points to a \c TRUE value,
+        *                  search will be done on the server. If \c predicate contains
+        *                  one or more atoms the server does not support, the value
+        *                  pointed to by \c on_server will be set to \c FALSE upon return.
+        *                  In this case, \c msgs must still contain a valid superset of
+        *                  messages actually matched by \c predicate, or this method must
+        *                  return an error.
+        *                  \c on_server may only point to a \c TRUE value if
+        *                  \c supports_server_search is also \c TRUE.
+        *                  \c NULL and pointer to \c FALSE are considered equivalent and
+        *                  will start a client-only search.
+        * \param predicate The \c MatcherList to use in the search
+        * \param progress_cb Called for every message searched.
+        *                    When search is offloaded to the server, this function
+        *                    may or may not be called, depending on the implementation.
+        *                    The second argument of this function will be the number of
+        *                    messages already processed.
+        *                    Return \c FALSE from this function to end the search.
+        *                    May be \c NULL, no calls will be made in this case.
+        * \param progress_data First argument value for \c progress_cb
+        * \return Number of messages that matched \c predicate on success, a negative
+        *         number otherwise.
+        *
+        * \note
+        * When search is stopped by returning \c FALSE from \c progress_cb, \c msgs will
+        * contain all messages found until the point of cancellation. The number of
+        * messages found will be returned as indicated above.
+        */
+       gint            (*search_msgs)          (Folder                 *folder,
+                                                FolderItem             *container,
+                                                MsgNumberList          **msgs,
+                                                gboolean               *on_server,
+                                                MatcherList            *predicate,
+                                                SearchProgressNotify   progress_cb,
+                                                gpointer               progress_data);
+
+
        /**
         * Remove a message from a \c FolderItem.
         *
@@ -861,6 +938,13 @@ gint   folder_item_copy_msg                (FolderItem     *dest,
                                         MsgInfo        *msginfo);
 gint   folder_item_copy_msgs           (FolderItem     *dest,
                                         GSList         *msglist);
+gint   folder_item_search_msgs         (Folder                 *folder,
+                                        FolderItem             *container,
+                                        MsgNumberList          **msgs,
+                                        gboolean               *on_server,
+                                        MatcherList            *predicate,
+                                        SearchProgressNotify   progress_cb,
+                                        gpointer               progress_data);
 gint   folder_item_remove_msg          (FolderItem     *item,
                                         gint            num);
 gint   folder_item_remove_msgs         (FolderItem     *item,
@@ -887,6 +971,7 @@ void folder_item_update_freeze              (void);
 void folder_item_update_thaw           (void);
 void folder_item_set_batch             (FolderItem *item, gboolean batch);
 gboolean folder_has_parent_of_type     (FolderItem *item, SpecialFolderItemType type);
+gboolean folder_is_child_of            (FolderItem *item, FolderItem *possibleChild);
 void folder_synchronise                        (Folder *folder);
 gboolean folder_want_synchronise       (Folder *folder);
 gboolean folder_subscribe              (const gchar *uri);
@@ -901,4 +986,14 @@ void folder_item_synchronise               (FolderItem *item);
 void folder_item_discard_cache         (FolderItem *item);
 void folder_item_commit_tags(FolderItem *item, MsgInfo *msginfo, GSList *tags_set, GSList *tags_unset);
 
+
+
+gint folder_item_search_msgs_local     (Folder                 *folder,
+                                        FolderItem             *container,
+                                        MsgNumberList          **msgs,
+                                        gboolean               *on_server,
+                                        MatcherList            *predicate,
+                                        SearchProgressNotify   progress_cb,
+                                        gpointer               progress_data);
+
 #endif /* __FOLDER_H__ */
index a0a3536..83b01e3 100644 (file)
@@ -1713,7 +1713,7 @@ static gboolean folderview_update_item_claws(gpointer source, gpointer data)
                if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) && 
                     update_info->item == folderview->summaryview->folder_item &&
                     update_info->item != NULL)
-                       if (!quicksearch_is_active(folderview->summaryview->quicksearch))
+                       if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
                                summary_show(folderview->summaryview, update_info->item);
        }
        
index e8e57be..0756eb0 100644 (file)
 #include "prefs_matcher.h"
 #include "claws.h"
 #include "statusbar.h"
+#include "advsearch.h"
+#include "alertpanel.h"
 
 struct _QuickSearchRequest
 {
-       QuickSearchType                  type;
+       AdvancedSearchType               type;
        gchar                           *matchstring;
-       gboolean                         recursive;
 };
 typedef struct _QuickSearchRequest QuickSearchRequest;
 
@@ -69,17 +70,12 @@ struct _QuickSearch
 
        gboolean                         active;
        gchar                           *search_string;
-       MatcherList                     *matcher_list;
 
-       QuickSearchRequest              *request;
+       QuickSearchRequest               request;
        QuickSearchExecuteCallback       callback;
        gpointer                         callback_data;
        gboolean                         running;
        gboolean                         has_focus;
-       gboolean                         matching;
-       gboolean                         deferred_free;
-       FolderItem                      *root_folder_item;
-       gboolean                         is_fast;
        gboolean                         in_typing;
        guint                            press_timeout_id;
 
@@ -90,38 +86,67 @@ struct _QuickSearch
        GtkWidget                        *dynamic_menuitem;
        GtkWidget                        *autorun_menuitem;
 
-       gboolean                        gui;
+       AdvancedSearch                  *asearch;
+       gboolean                         want_reexec;
 };
 
+void quicksearch_set_on_progress_cb(QuickSearch* search,
+               gboolean (*cb)(gpointer data, guint at, guint matched, guint total), gpointer data)
+{
+       advsearch_set_on_progress_cb(search->asearch, cb, data);
+}
+
 static void quicksearch_set_running(QuickSearch *quicksearch, gboolean run);
 static void quicksearch_set_matchstring(QuickSearch *quicksearch, const gchar *matchstring);
 static void quicksearch_set_active(QuickSearch *quicksearch, gboolean active);
-static void quicksearch_reset_folder_items(QuickSearch *quicksearch, FolderItem *folder_item);
-static gchar *expand_search_string(const gchar *str);
-static gchar *expand_tag_search_string(const gchar *str);
 
-static gboolean quicksearch_from_gui(QuickSearch *quicksearch)
+static void quicksearch_invoke_execute(QuickSearch *quicksearch, gboolean run_only_if_fast)
 {
-       return quicksearch->gui;
+       if (quicksearch->running) {
+               quicksearch->want_reexec = TRUE;
+               advsearch_abort(quicksearch->asearch);
+               return;
+       }
+
+       do {
+               advsearch_set(quicksearch->asearch, quicksearch->request.type,
+                               quicksearch->request.matchstring);
+
+               if (run_only_if_fast && !advsearch_is_fast(quicksearch->asearch))
+                       return;
+
+               quicksearch_set_active(quicksearch, advsearch_has_proper_predicate(quicksearch->asearch));
+
+               quicksearch->want_reexec = FALSE;
+               quicksearch_set_running(quicksearch, TRUE);
+               if (quicksearch->callback != NULL)
+                       quicksearch->callback(quicksearch, quicksearch->callback_data);
+               quicksearch_set_running(quicksearch, FALSE);
+       } while (quicksearch->want_reexec);
 }
 
-gboolean quicksearch_is_fast(QuickSearch *quicksearch)
+gboolean quicksearch_run_on_folder(QuickSearch* quicksearch, FolderItem *folderItem, MsgInfoList **result)
 {
-       return quicksearch->is_fast;
+       if (quicksearch_has_sat_predicate(quicksearch)) {
+               gboolean searchres = advsearch_search_msgs_in_folders(quicksearch->asearch, result, folderItem, FALSE);
+               if (quicksearch->want_reexec) {
+                       advsearch_set(quicksearch->asearch, quicksearch->request.type, "");
+               }
+               return searchres;
+       } else
+               return FALSE;
 }
 
-void quicksearch_set_recursive(QuickSearch *quicksearch, gboolean recursive)
+gboolean quicksearch_is_fast(QuickSearch *quicksearch)
 {
-       quicksearch->request->recursive = recursive;
+       return advsearch_is_fast(quicksearch->asearch);
 }
 
 static void quicksearch_set_type(QuickSearch *quicksearch, gint type)
 {
 #if !GTK_CHECK_VERSION(3, 0, 0)
        gint index;
-       quicksearch->request->type = type;
-       if (quicksearch->gui == FALSE)
-               return;
+       quicksearch->request.type = type;
        index = menu_find_option_menu_index(GTK_CMOPTION_MENU(quicksearch->search_type_opt), 
                                        GINT_TO_POINTER(type),
                                        NULL);
@@ -143,7 +168,7 @@ static void quicksearch_set_popdown_strings(QuickSearch *quicksearch)
 
        combobox_unset_popdown_strings(GTK_COMBO_BOX(search_string_entry));
 
-       if (prefs_common.summary_quicksearch_type == QUICK_SEARCH_EXTENDED)
+       if (prefs_common.summary_quicksearch_type == ADVANCED_SEARCH_EXTENDED)
                combobox_set_popdown_strings(GTK_COMBO_BOX(search_string_entry),
                        quicksearch->extended_search_strings);  
        else
@@ -151,72 +176,6 @@ static void quicksearch_set_popdown_strings(QuickSearch *quicksearch)
                        quicksearch->normal_search_strings);
 }
 
-static void prepare_matcher(QuickSearch *quicksearch)
-{
-       /* param search_string is "matchstring" */
-       const gchar *search_string;
-       QuickSearchType quicksearch_type;
-
-       if (quicksearch == NULL)
-               return;
-
-       /* When called from the GUI, reset type and matchstring */
-       if (quicksearch_from_gui(quicksearch)) {
-               gchar *s = quicksearch_get_text(quicksearch);
-               quicksearch_set_matchstring(quicksearch, s);
-               g_free(s);
-               quicksearch->request->type = prefs_common.summary_quicksearch_type;
-       }
-       quicksearch_type = quicksearch->request->type;
-       search_string = quicksearch->request->matchstring;
-
-       if (search_string == NULL || search_string[0] == '\0') {
-               quicksearch_set_active(quicksearch, FALSE);
-       }
-
-       if (quicksearch->matcher_list != NULL) {
-               if (quicksearch->matching) {
-                       quicksearch->deferred_free = TRUE;
-                       return;
-               }
-               quicksearch->deferred_free = FALSE;
-               matcherlist_free(quicksearch->matcher_list);
-               quicksearch->matcher_list = NULL;
-       }
-
-       if (search_string == NULL || search_string[0] == '\0') {
-               return;
-       }
-       if (quicksearch_type == QUICK_SEARCH_EXTENDED) {
-               char *newstr = NULL;
-
-               newstr = expand_search_string(search_string);
-               if (newstr && newstr[0] != '\0') {
-                       quicksearch->matcher_list = matcher_parser_get_cond(newstr, &quicksearch->is_fast);
-                       g_free(newstr);
-               } else {
-                       quicksearch->matcher_list = NULL;
-                       quicksearch_set_active(quicksearch, FALSE);
-                       return;
-               }
-       } else if (quicksearch_type == QUICK_SEARCH_TAG) {
-               char *newstr = expand_tag_search_string(search_string);
-               quicksearch->matcher_list = matcher_parser_get_cond(newstr, &quicksearch->is_fast);
-               g_free(newstr);
-       } else if (quicksearch_type == QUICK_SEARCH_MIXED) {
-               char *newstr = expand_tag_search_string(search_string);
-               quicksearch->matcher_list = matcher_parser_get_cond(newstr, &quicksearch->is_fast);
-               g_free(newstr);
-               g_free(quicksearch->search_string);
-               quicksearch->search_string = g_utf8_casefold(search_string, -1);
-       } else {
-               quicksearch->is_fast = TRUE;
-               g_free(quicksearch->search_string);
-               quicksearch->search_string = g_utf8_casefold(search_string, -1);
-       }
-       quicksearch_set_active(quicksearch, TRUE);
-}
-
 static void update_extended_buttons (QuickSearch *quicksearch)
 {
        GtkWidget *expr_btn = quicksearch->search_condition_expression;
@@ -225,7 +184,7 @@ static void update_extended_buttons (QuickSearch *quicksearch)
        cm_return_if_fail(expr_btn != NULL);
        cm_return_if_fail(ext_btn != NULL);
 
-       if (prefs_common.summary_quicksearch_type == QUICK_SEARCH_EXTENDED) {
+       if (prefs_common.summary_quicksearch_type == ADVANCED_SEARCH_EXTENDED) {
                gtk_widget_show(expr_btn);
                gtk_widget_show(ext_btn);
        } else {
@@ -258,13 +217,12 @@ static void searchbar_run(QuickSearch *quicksearch, gboolean run_only_if_fast)
 {
        gchar *search_string = quicksearch_get_text(quicksearch);
        quicksearch_set_matchstring(quicksearch, search_string);
-       prepare_matcher(quicksearch);
 
        /* add to history, for extended search add only correct matching rules */
        if (!quicksearch->in_typing && search_string && strlen(search_string) != 0) {
                switch (prefs_common.summary_quicksearch_type) {
-                       case QUICK_SEARCH_EXTENDED:
-                               if (quicksearch->matcher_list) {
+                       case ADVANCED_SEARCH_EXTENDED:
+                               if (advsearch_has_proper_predicate(quicksearch->asearch)) {
                                        quicksearch->extended_search_strings =
                                                add_history(quicksearch->extended_search_strings,
                                                                g_strdup(search_string));
@@ -287,21 +245,9 @@ static void searchbar_run(QuickSearch *quicksearch, gboolean run_only_if_fast)
 
        }
 
-       if (run_only_if_fast && !quicksearch->is_fast) {
-               g_free(search_string);
-               return;
-       }
-       if (quicksearch->matcher_list == NULL && 
-           prefs_common.summary_quicksearch_type == QUICK_SEARCH_EXTENDED &&
-           search_string && strlen(search_string) != 0) {
-               g_free(search_string);
-               return;
-       }
-       quicksearch_set_running(quicksearch, TRUE);
-       if (quicksearch->callback != NULL)
-               quicksearch->callback(quicksearch, quicksearch->callback_data);
-       quicksearch_set_running(quicksearch, FALSE);
        g_free(search_string);
+
+       quicksearch_invoke_execute(quicksearch, run_only_if_fast);
 }
 
 static int searchbar_changed_timeout(void *data)
@@ -385,29 +331,18 @@ static gboolean searchbar_pressed(GtkWidget *widget, GdkEventKey *event,
 static gboolean searchtype_changed(GtkMenuItem *widget, gpointer data)
 {
        QuickSearch *quicksearch = (QuickSearch *)data;
-       gchar *search_string = quicksearch_get_text(quicksearch);
-       quicksearch_set_matchstring(quicksearch, search_string);
 
        prefs_common.summary_quicksearch_type = GPOINTER_TO_INT(g_object_get_data(
                                   G_OBJECT(GTK_MENU_ITEM(gtk_menu_get_active(
                                   GTK_MENU(quicksearch->search_type)))), MENU_VAL_ID));
+       quicksearch->request.type = prefs_common.summary_quicksearch_type;
 
        /* Show extended search description button, only when Extended is selected */
        update_extended_buttons(quicksearch);
        quicksearch_set_popdown_strings(quicksearch);
 
-       if (!search_string || *(search_string) == 0) {
-               g_free(search_string);
-               return TRUE;
-       }
-
-       prepare_matcher(quicksearch);
+       quicksearch_invoke_execute(quicksearch, FALSE);
 
-       quicksearch_set_running(quicksearch, TRUE);
-       if (quicksearch->callback != NULL)
-               quicksearch->callback(quicksearch, quicksearch->callback_data);
-       quicksearch_set_running(quicksearch, FALSE);
-       g_free(search_string);
        return TRUE;
 }
 
@@ -415,28 +350,14 @@ static gboolean searchtype_recursive_changed(GtkMenuItem *widget, gpointer data)
 {
        QuickSearch *quicksearch = (QuickSearch *)data;
        gboolean checked = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
-       gchar *search_string = quicksearch_get_text(quicksearch);
-       /* not needed to quicksearch_set_matchstring(search_string);
-          wait for prepare_matcher() */
 
        prefs_common.summary_quicksearch_recurse = checked;
-       quicksearch_set_recursive(quicksearch, checked);
 
        /* reselect the search type */
        quicksearch_set_type(quicksearch, prefs_common.summary_quicksearch_type);
 
-       if (!search_string || *(search_string) == 0) {
-               g_free(search_string);
-               return TRUE;
-       }
-
-       prepare_matcher(quicksearch);
+       quicksearch_invoke_execute(quicksearch, FALSE);
 
-       quicksearch_set_running(quicksearch, TRUE);
-       if (quicksearch->callback != NULL)
-               quicksearch->callback(quicksearch, quicksearch->callback_data);
-       quicksearch_set_running(quicksearch, FALSE);
-       g_free(search_string);
        return TRUE;
 }
 
@@ -537,6 +458,7 @@ static gchar *search_descr_strings[] = {
        "|",     N_("logical OR operator"),
        "! or ~",       N_("logical NOT operator"),
        "%",     N_("case sensitive search"),
+       "#",     N_("match using regular expressions instead of substring search"),
        "",      "" ,
        " ",     N_("all filtering expressions are allowed"),
        NULL,    NULL
@@ -645,41 +567,9 @@ static void quicksearch_set_button(GtkButton *button, const gchar *icon, const g
        gtk_widget_show_all(box);
 }
 
-/*
- * Builds a new QuickSearchRequest
- */
-static QuickSearchRequest *quicksearchrequest_new(void)
+static void quicksearch_error(gpointer data)
 {
-       QuickSearchRequest *request;
-       request = g_new0(QuickSearchRequest, 1);
-       return request;
-}
-
-/*
- * Builds a new QuickSearch object independent from the GUI
- */
-QuickSearch *quicksearch_new_nogui(void)
-{
-       QuickSearch *quicksearch;
-       QuickSearchRequest *request;
-
-       request = quicksearchrequest_new();
-       quicksearch = g_new0(QuickSearch, 1);
-       quicksearch->request = request;
-       quicksearch->gui = FALSE;
-
-       /* init. values initally found in quicksearch_new().
-          There's no need to init. all pointers to NULL since we use g_new0
-        */
-       quicksearch->matcher_list = NULL;
-       quicksearch->active = FALSE;
-       quicksearch->running = FALSE;
-       quicksearch->in_typing = FALSE;
-       quicksearch->press_timeout_id = -1;
-       quicksearch->normal_search_strings = NULL;
-       quicksearch->extended_search_strings = NULL;
-
-       return quicksearch;
+       alertpanel_error(_("Something went wrong during search. Please check you logs."));
 }
 
 QuickSearch *quicksearch_new()
@@ -699,8 +589,20 @@ QuickSearch *quicksearch_new()
        GtkWidget *menuitem;
        GtkWidget *vbox;
 
-       quicksearch = quicksearch_new_nogui();
-       quicksearch->gui = TRUE;
+       quicksearch = g_new0(QuickSearch, 1);
+
+       quicksearch->asearch = advsearch_new();
+       advsearch_set_on_error_cb(quicksearch->asearch, quicksearch_error, NULL);
+
+       /* init. values initally found in quicksearch_new().
+          There's no need to init. all pointers to NULL since we use g_new0
+        */
+       quicksearch->active = FALSE;
+       quicksearch->running = FALSE;
+       quicksearch->in_typing = FALSE;
+       quicksearch->press_timeout_id = -1;
+       quicksearch->normal_search_strings = NULL;
+       quicksearch->extended_search_strings = NULL;
 
        /* quick search */
        hbox_search = gtk_hbox_new(FALSE, 0);
@@ -713,31 +615,31 @@ QuickSearch *quicksearch_new()
 
        search_type = gtk_menu_new();
        MENUITEM_ADD (search_type, menuitem,
-                       prefs_common_translated_header_name("Subject"), QUICK_SEARCH_SUBJECT);
+                       prefs_common_translated_header_name("Subject"), ADVANCED_SEARCH_SUBJECT);
        g_signal_connect(G_OBJECT(menuitem), "activate",
                         G_CALLBACK(searchtype_changed),
                         quicksearch);
        MENUITEM_ADD (search_type, menuitem,
-                       prefs_common_translated_header_name("From"), QUICK_SEARCH_FROM);
+                       prefs_common_translated_header_name("From"), ADVANCED_SEARCH_FROM);
        g_signal_connect(G_OBJECT(menuitem), "activate",
                         G_CALLBACK(searchtype_changed),
                         quicksearch);
        MENUITEM_ADD (search_type, menuitem,
-                       prefs_common_translated_header_name("To"), QUICK_SEARCH_TO);
+                       prefs_common_translated_header_name("To"), ADVANCED_SEARCH_TO);
        g_signal_connect(G_OBJECT(menuitem), "activate",
                         G_CALLBACK(searchtype_changed),
                         quicksearch);
        MENUITEM_ADD (search_type, menuitem,
-                       prefs_common_translated_header_name("Tag"), QUICK_SEARCH_TAG);
+                       prefs_common_translated_header_name("Tag"), ADVANCED_SEARCH_TAG);
        g_signal_connect(G_OBJECT(menuitem), "activate",
                         G_CALLBACK(searchtype_changed),
                         quicksearch);
        MENUITEM_ADD (search_type, menuitem,
-                       _("From/To/Subject/Tag"), QUICK_SEARCH_MIXED);
+                       _("From/To/Subject/Tag"), ADVANCED_SEARCH_MIXED);
        g_signal_connect(G_OBJECT(menuitem), "activate",
                         G_CALLBACK(searchtype_changed),
                         quicksearch);
-       MENUITEM_ADD (search_type, menuitem, _("Extended"), QUICK_SEARCH_EXTENDED);
+       MENUITEM_ADD (search_type, menuitem, _("Extended"), ADVANCED_SEARCH_EXTENDED);
        g_signal_connect(G_OBJECT(menuitem), "activate",
                         G_CALLBACK(searchtype_changed),
                         quicksearch);
@@ -749,7 +651,6 @@ QuickSearch *quicksearch_new()
 
        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
                                        prefs_common.summary_quicksearch_recurse);
-       quicksearch_set_recursive(quicksearch, prefs_common.summary_quicksearch_recurse);
        g_signal_connect(G_OBJECT(menuitem), "activate",
                         G_CALLBACK(searchtype_recursive_changed),
                         quicksearch);
@@ -863,7 +764,6 @@ QuickSearch *quicksearch_new()
        quicksearch->search_string_entry = search_string_entry;
        quicksearch->search_condition_expression = search_condition_expression;
        quicksearch->search_description = search_description;
-       quicksearch->matcher_list = NULL;
        quicksearch->active = FALSE;
        quicksearch->running = FALSE;
        quicksearch->clear_search = clear_search;
@@ -909,14 +809,11 @@ void quicksearch_show(QuickSearch *quicksearch)
 {
        MainWindow *mainwin = mainwindow_get_mainwindow();
        GtkWidget *ctree = NULL;
-       prepare_matcher(quicksearch);
        gtk_widget_show(quicksearch->hbox_search);
        update_extended_buttons(quicksearch);
        gtk_widget_grab_focus(
                GTK_WIDGET(gtk_bin_get_child(GTK_BIN((quicksearch->search_string_entry)))));
 
-       GTK_EVENTS_FLUSH();
-
        if (!mainwin || !mainwin->summaryview) {
                return;
        }
@@ -931,7 +828,7 @@ void quicksearch_show(QuickSearch *quicksearch)
 
 void quicksearch_hide(QuickSearch *quicksearch)
 {
-       if (quicksearch_is_active(quicksearch)) {
+       if (quicksearch_has_sat_predicate(quicksearch)) {
                quicksearch_set(quicksearch, prefs_common.summary_quicksearch_type, "");
                quicksearch_set_active(quicksearch, FALSE);
        }
@@ -947,12 +844,11 @@ void quicksearch_hide(QuickSearch *quicksearch)
 static void quicksearch_set_matchstring(QuickSearch *quicksearch,
                                        const gchar *matchstring)
 {
-       g_free(quicksearch->request->matchstring);
-       quicksearch->request->matchstring = g_strdup(matchstring);
+       g_free(quicksearch->request.matchstring);
+       quicksearch->request.matchstring = g_strdup(matchstring);
 }
 
-void quicksearch_set(QuickSearch *quicksearch, QuickSearchType type,
-                    const gchar *matchstring)
+void quicksearch_set(QuickSearch *quicksearch, AdvancedSearchType type, const gchar *matchstring)
 {
        quicksearch_set_type(quicksearch, type);
 
@@ -960,12 +856,6 @@ void quicksearch_set(QuickSearch *quicksearch, QuickSearchType type,
                quicksearch->in_typing = FALSE;
 
        quicksearch_set_matchstring(quicksearch, matchstring);
-
-       if (!quicksearch_from_gui(quicksearch)) {
-               prepare_matcher(quicksearch);
-               /* no callback */
-               return;
-       }
                
        g_signal_handlers_block_by_func(G_OBJECT(gtk_bin_get_child(GTK_BIN((quicksearch->search_string_entry)))),
                                        G_CALLBACK(searchbar_changed_cb), quicksearch);
@@ -976,19 +866,12 @@ void quicksearch_set(QuickSearch *quicksearch, QuickSearchType type,
 
        prefs_common.summary_quicksearch_type = type;
 
-       prepare_matcher(quicksearch);
-
-       quicksearch_set_running(quicksearch, TRUE);
-       if (quicksearch->callback != NULL)
-               quicksearch->callback(quicksearch, quicksearch->callback_data);
-       quicksearch_set_running(quicksearch, FALSE);
+       quicksearch_invoke_execute(quicksearch, FALSE);
 }
 
-gboolean quicksearch_is_active(QuickSearch *quicksearch)
+gboolean quicksearch_has_sat_predicate(QuickSearch *quicksearch)
 {
-       return quicksearch->active && 
-               (prefs_common.summary_quicksearch_type != QUICK_SEARCH_EXTENDED
-                || quicksearch->matcher_list != NULL);
+       return quicksearch->active && advsearch_has_proper_predicate(quicksearch->asearch);
 }
 
 static void quicksearch_set_active(QuickSearch *quicksearch, gboolean active)
@@ -1007,8 +890,6 @@ static void quicksearch_set_active(QuickSearch *quicksearch, gboolean active)
 
        
        quicksearch->active = active;
-       if (quicksearch->gui == FALSE)
-               return;
 
 #if !GTK_CHECK_VERSION(3, 0, 0)
        if (!colors_initialised) {
@@ -1025,8 +906,8 @@ static void quicksearch_set_active(QuickSearch *quicksearch, gboolean active)
 #endif
 
        if (active && 
-               (prefs_common.summary_quicksearch_type == QUICK_SEARCH_EXTENDED
-                && quicksearch->matcher_list == NULL))
+               (prefs_common.summary_quicksearch_type == ADVANCED_SEARCH_EXTENDED
+                && !advsearch_has_proper_predicate(quicksearch->asearch)))
                error = TRUE;
 
        if (active) {
@@ -1058,7 +939,7 @@ static void quicksearch_set_active(QuickSearch *quicksearch, gboolean active)
        }
 
        if (!active) {
-               quicksearch_reset_cur_folder_item(quicksearch);
+               advsearch_abort(quicksearch->asearch);
        }
 }
 
@@ -1070,291 +951,6 @@ void quicksearch_set_execute_callback(QuickSearch *quicksearch,
        quicksearch->callback_data = data;
 }
 
-gboolean quicksearch_match(QuickSearch *quicksearch, MsgInfo *msginfo)
-{
-       gchar *searched_header = NULL;
-       gboolean result = FALSE;
-       gchar *to = NULL, *from = NULL, *subject = NULL;
-       QuickSearchType quicksearch_type;
-
-       if (!quicksearch->active)
-               return TRUE;
-
-       quicksearch_type = quicksearch->request->type;
-
-       switch (quicksearch_type) {
-       case QUICK_SEARCH_SUBJECT:
-               if (msginfo->subject)
-                       searched_header = g_utf8_casefold(msginfo->subject, -1);
-               else
-                       return FALSE;
-               break;
-       case QUICK_SEARCH_FROM:
-               if (msginfo->from)
-                       searched_header = g_utf8_casefold(msginfo->from, -1);
-               else
-                       return FALSE;
-               break;
-       case QUICK_SEARCH_TO:
-               if (msginfo->to)
-                       searched_header = g_utf8_casefold(msginfo->to, -1);
-               else
-                       return FALSE;
-               break;
-       case QUICK_SEARCH_MIXED:
-               if (msginfo->to)
-                       to = g_utf8_casefold(msginfo->to, -1);
-               if (msginfo->from)
-                       from = g_utf8_casefold(msginfo->from, -1);
-               if (msginfo->subject)
-                       subject = g_utf8_casefold(msginfo->subject, -1);
-               break;
-       case QUICK_SEARCH_EXTENDED:
-               break;
-       default:
-               debug_print("unknown search type (%d)\n", quicksearch_type);
-               break;
-       }
-
-       quicksearch->matching = TRUE;
-       if (quicksearch_type != QUICK_SEARCH_EXTENDED &&
-           quicksearch_type != QUICK_SEARCH_MIXED &&
-           quicksearch_type != QUICK_SEARCH_TAG &&
-           quicksearch->search_string &&
-           searched_header && strstr(searched_header, quicksearch->search_string) != NULL)
-               result = TRUE;
-       else if (quicksearch_type == QUICK_SEARCH_MIXED &&
-               quicksearch->search_string && (
-               (to && strstr(to, quicksearch->search_string) != NULL) ||
-               (from && strstr(from, quicksearch->search_string) != NULL) ||
-               (subject && strstr(subject, quicksearch->search_string) != NULL) ||
-               ((quicksearch->matcher_list != NULL) &&
-                matcherlist_match(quicksearch->matcher_list, msginfo))  ))
-               result = TRUE;
-       else if ((quicksearch->matcher_list != NULL) &&
-                matcherlist_match(quicksearch->matcher_list, msginfo))
-               result = TRUE;
-
-       quicksearch->matching = FALSE;
-       if (quicksearch_from_gui(quicksearch)==TRUE && quicksearch->deferred_free) {
-               /* Ref. http://lists.claws-mail.org/pipermail/users/2010-August/003063.html
-                  See also 2.0.0cvs140 ChangeLog entry
-                  and comment in search_msgs_in_folder() */
-               prepare_matcher(quicksearch);
-       }
-
-       g_free(to);
-       g_free(from);
-       g_free(subject);
-       g_free(searched_header);
-
-       return result;
-}
-
-/* allow Mutt-like patterns in quick search */
-static gchar *expand_search_string(const gchar *search_string)
-{
-       int i = 0;
-       gchar term_char, save_char;
-       gchar *cmd_start, *cmd_end;
-       GString *matcherstr;
-       gchar *returnstr = NULL;
-       gchar *copy_str;
-       gboolean casesens, dontmatch;
-       /* list of allowed pattern abbreviations */
-       struct {
-               gchar           *abbreviated;   /* abbreviation */
-               gchar           *command;       /* actual matcher command */
-               gint            numparams;      /* number of params for cmd */
-               gboolean        qualifier;      /* do we append regexpcase */
-               gboolean        quotes;         /* do we need quotes */
-       }
-       cmds[] = {
-               { "a",  "all",                          0,      FALSE,  FALSE },
-               { "ag", "age_greater",                  1,      FALSE,  FALSE },
-               { "al", "age_lower",                    1,      FALSE,  FALSE },
-               { "b",  "body_part",                    1,      TRUE,   TRUE  },
-               { "B",  "message",                      1,      TRUE,   TRUE  },
-               { "c",  "cc",                           1,      TRUE,   TRUE  },
-               { "C",  "to_or_cc",                     1,      TRUE,   TRUE  },
-               { "D",  "deleted",                      0,      FALSE,  FALSE },
-               { "e",  "header \"Sender\"",            1,      TRUE,   TRUE  },
-               { "E",  "execute",                      1,      FALSE,  TRUE  },
-               { "f",  "from",                         1,      TRUE,   TRUE  },
-               { "F",  "forwarded",                    0,      FALSE,  FALSE },
-               { "h",  "headers_part",                 1,      TRUE,   TRUE  },
-               { "ha", "has_attachments",              0,      FALSE,  FALSE },
-               { "i",  "header \"Message-ID\"",        1,      TRUE,   TRUE  },
-               { "I",  "inreplyto",                    1,      TRUE,   TRUE  },
-               { "k",  "colorlabel",                   1,      FALSE,  FALSE },
-               { "L",  "locked",                       0,      FALSE,  FALSE },
-               { "n",  "newsgroups",                   1,      TRUE,   TRUE  },
-               { "N",  "new",                          0,      FALSE,  FALSE },
-               { "O",  "~new",                         0,      FALSE,  FALSE },
-               { "r",  "replied",                      0,      FALSE,  FALSE },
-               { "R",  "~unread",                      0,      FALSE,  FALSE },
-               { "s",  "subject",                      1,      TRUE,   TRUE  },
-               { "se", "score_equal",                  1,      FALSE,  FALSE },
-               { "sg", "score_greater",                1,      FALSE,  FALSE },
-               { "sl", "score_lower",                  1,      FALSE,  FALSE },
-               { "Se", "size_equal",                   1,      FALSE,  FALSE },
-               { "Sg", "size_greater",                 1,      FALSE,  FALSE },
-               { "Ss", "size_smaller",                 1,      FALSE,  FALSE },
-               { "t",  "to",                           1,      TRUE,   TRUE  },
-               { "tg", "tag",                          1,      TRUE,   TRUE  },
-               { "T",  "marked",                       0,      FALSE,  FALSE },
-               { "U",  "unread",                       0,      FALSE,  FALSE },
-               { "x",  "header \"References\"",        1,      TRUE,   TRUE  },
-               { "X",  "test",                         1,      FALSE,  FALSE },
-               { "y",  "header \"X-Label\"",           1,      TRUE,   TRUE  },
-               { "&",  "&",                            0,      FALSE,  FALSE },
-               { "|",  "|",                            0,      FALSE,  FALSE },
-               { "p",  "partial",                      0,      FALSE,  FALSE },
-               { NULL, NULL,                           0,      FALSE,  FALSE }
-       };
-
-       if (search_string == NULL)
-               return NULL;
-
-       copy_str = g_strdup(search_string);
-
-       matcherstr = g_string_sized_new(16);
-       cmd_start = copy_str;
-       while (cmd_start && *cmd_start) {
-               /* skip all white spaces */
-               while (*cmd_start && isspace((guchar)*cmd_start))
-                       cmd_start++;
-               cmd_end = cmd_start;
-
-               /* extract a command */
-               while (*cmd_end && !isspace((guchar)*cmd_end))
-                       cmd_end++;
-
-               /* save character */
-               save_char = *cmd_end;
-               *cmd_end = '\0';
-
-               dontmatch = FALSE;
-               casesens = FALSE;
-
-               /* ~ and ! mean logical NOT */
-               if (*cmd_start == '~' || *cmd_start == '!')
-               {
-                       dontmatch = TRUE;
-                       cmd_start++;
-               }
-               /* % means case sensitive match */
-               if (*cmd_start == '%')
-               {
-                       casesens = TRUE;
-                       cmd_start++;
-               }
-
-               /* find matching abbreviation */
-               for (i = 0; cmds[i].command; i++) {
-                       if (!strcmp(cmd_start, cmds[i].abbreviated)) {
-                               /* restore character */
-                               *cmd_end = save_char;
-
-                               /* copy command */
-                               if (matcherstr->len > 0) {
-                                       g_string_append(matcherstr, " ");
-                               }
-                               if (dontmatch)
-                                       g_string_append(matcherstr, "~");
-                               g_string_append(matcherstr, cmds[i].command);
-                               g_string_append(matcherstr, " ");
-
-                               /* stop if no params required */
-                               if (cmds[i].numparams == 0)
-                                       break;
-
-                               /* extract a parameter, allow quotes */
-                               while (*cmd_end && isspace((guchar)*cmd_end))
-                                       cmd_end++;
-
-                               cmd_start = cmd_end;
-                               if (*cmd_start == '"') {
-                                       term_char = '"';
-                                       cmd_end++;
-                               }
-                               else
-                                       term_char = ' ';
-
-                               /* extract actual parameter */
-                               while ((*cmd_end) && (*cmd_end != term_char))
-                                       cmd_end++;
-
-                               if (*cmd_end == '"')
-                                       cmd_end++;
-
-                               save_char = *cmd_end;
-                               *cmd_end = '\0';
-
-                               if (cmds[i].qualifier) {
-                                       if (casesens)
-                                               g_string_append(matcherstr, "regexp ");
-                                       else
-                                               g_string_append(matcherstr, "regexpcase ");
-                               }
-
-                               /* do we need to add quotes ? */
-                               if (cmds[i].quotes && term_char != '"')
-                                       g_string_append(matcherstr, "\"");
-
-                               /* copy actual parameter */
-                               g_string_append(matcherstr, cmd_start);
-
-                               /* do we need to add quotes ? */
-                               if (cmds[i].quotes && term_char != '"')
-                                       g_string_append(matcherstr, "\"");
-
-                               /* restore original character */
-                               *cmd_end = save_char;
-
-                               break;
-                       }
-               }
-
-               if (*cmd_end)
-                       cmd_end++;
-               cmd_start = cmd_end;
-       }
-
-       g_free(copy_str);
-
-       /* return search string if no match is found to allow
-          all available filtering expressions in quicksearch */
-       if (matcherstr->len > 0) returnstr = matcherstr->str;
-       else returnstr = g_strdup(search_string);
-       g_string_free(matcherstr, FALSE);
-       return returnstr;
-}
-
-static gchar *expand_tag_search_string(const gchar *search_string)
-{
-       gchar *newstr = NULL;
-       gchar **words = search_string ? g_strsplit(search_string, " ", -1):NULL;
-       gint i = 0;
-       while (words && words[i] && *words[i]) {
-               g_strstrip(words[i]);
-               if (!newstr) {
-                       newstr = g_strdup_printf("tag regexpcase \"%s\"", words[i]);
-               } else {
-                       gint o_len = strlen(newstr);
-                       gint s_len = 18; /* strlen("|tag regexpcase \"\"") */
-                       gint n_len = s_len + strlen(words[i]);
-                       newstr = g_realloc(newstr,o_len+n_len+1);
-                       strcpy(newstr+o_len, "|tag regexpcase \"");
-                       strcpy(newstr+o_len+(s_len-1), words[i]);
-                       strcpy(newstr+o_len+(n_len-1), "\"");
-               }
-               i++;
-       }
-       g_strfreev(words);
-       return newstr;
-}
-
 static void quicksearch_set_running(QuickSearch *quicksearch, gboolean run)
 {
        quicksearch->running = run;
@@ -1412,114 +1008,6 @@ void quicksearch_pass_key(QuickSearch *quicksearch, guint val, GdkModifierType m
 
 }
 
-static gboolean quicksearch_match_subfolder(QuickSearch *quicksearch,
-                                FolderItem *src)
-{
-       GSList *msglist = NULL;
-       GSList *cur;
-       gboolean result = FALSE;
-       gint num = 0, total = 0;
-       gint interval = quicksearch_is_fast(quicksearch) ? 5000:100;
-
-       statusbar_print_all(_("Searching in %s... \n"),
-               src->path ? src->path : "(null)");
-               
-       msglist = folder_item_get_msg_list(src);
-       total = src->total_msgs;
-       folder_item_update_freeze();
-       for (cur = msglist; cur != NULL; cur = cur->next) {
-               MsgInfo *msg = (MsgInfo *)cur->data;
-               statusbar_progress_all(num++,total, interval);
-               if (quicksearch_match(quicksearch, msg)) {
-                       result = TRUE;
-                       break;
-               }
-               if (num % interval == 0)
-                       GTK_EVENTS_FLUSH();
-               if (!quicksearch_is_active(quicksearch))
-                       break;
-       }
-       folder_item_update_thaw();
-       statusbar_progress_all(0,0,0);
-       statusbar_pop_all();
-
-       procmsg_msg_list_free(msglist);
-       return result;
-}
-
-gboolean quicksearch_is_in_subfolder(QuickSearch *quicksearch, FolderItem *cur)
-{
-       if (quicksearch->root_folder_item == NULL)
-               return FALSE;
-       
-       while (cur) {
-               if (cur == quicksearch->root_folder_item) {
-                       return TRUE;
-               }
-               cur = folder_item_parent(cur);
-       }
-       return FALSE;
-}
-
-void quicksearch_search_subfolders(QuickSearch *quicksearch,
-                                  FolderView *folderview,
-                                  FolderItem *folder_item)
-{
-       FolderItem *cur = NULL;
-       GNode *node = folder_item->node->children;
-
-       if (!prefs_common.summary_quicksearch_recurse
-       ||  quicksearch->in_typing == TRUE)
-               return;
-
-       for (; node != NULL; node = node->next) {
-               cur = FOLDER_ITEM(node->data);
-               if (quicksearch_match_subfolder(quicksearch, cur)) {
-                       folderview_update_search_icon(cur, TRUE);
-               } else {
-                       folderview_update_search_icon(cur, FALSE);
-               }
-               if (cur->node->children)
-                       quicksearch_search_subfolders(quicksearch,
-                                                     folderview,
-                                                     cur);
-       }
-       quicksearch->root_folder_item = folder_item;
-       if (!quicksearch_is_active(quicksearch))
-               quicksearch_reset_cur_folder_item(quicksearch);
-}
-
-static void quicksearch_reset_folder_items(QuickSearch *quicksearch,
-                                   FolderItem *folder_item)
-{
-       FolderItem *cur = NULL;
-       GNode *node = (folder_item && folder_item->node) ?
-                       folder_item->node->children : NULL;
-
-       for (; node != NULL; node = node->next) {
-               cur = FOLDER_ITEM(node->data);
-               folderview_update_search_icon(cur, FALSE);
-               if (cur->node->children)
-                       quicksearch_reset_folder_items(quicksearch,
-                                                      cur);
-       }
-}
-
-void quicksearch_reset_cur_folder_item(QuickSearch *quicksearch)
-{
-       if (quicksearch->root_folder_item)
-               quicksearch_reset_folder_items(quicksearch,
-                                              quicksearch->root_folder_item);
-
-       quicksearch->root_folder_item = NULL;
-}
-
-void quicksearch_folder_item_invalidate(QuickSearch *quicksearch, FolderItem *item)
-{
-       if (quicksearch->root_folder_item == item)
-               quicksearch->root_folder_item = NULL;
-}
-
 gboolean quicksearch_is_in_typing(QuickSearch *quicksearch)
 {
        return quicksearch->in_typing;
@@ -1537,7 +1025,7 @@ void quicksearch_set_search_strings(QuickSearch *quicksearch)
        matcher_parser_disable_warnings(TRUE);
        
        do {
-               newstr = expand_search_string((gchar *) strings->data);
+               newstr = advsearch_expand_search_string((gchar *) strings->data);
                if (newstr && newstr[0] != '\0') {
                        if (!strchr(newstr, ' ')) {
                                quicksearch->normal_search_strings =
@@ -1574,98 +1062,3 @@ void quicksearch_set_search_strings(QuickSearch *quicksearch)
        quicksearch_set_popdown_strings(quicksearch);
 }
 
-/*
- * Searches in the supplied folderItem the messages (MessageInfo) matching a
- * QuickSearchType + search string (ex.: QUICK_SEARCH_FROM and "foo@bar.com").
- *
- * Found messages are appended to the array 'messages' and their ref.counts
- * are incremented by 1 --so they need to be released (procmsg_msginfo_free())
- * before the array 'messages' is freed.
- */
-void search_msgs_in_folder(GSList **messages, QuickSearch* quicksearch,
-                          FolderItem* folderItem)
-{
-       /* from quicksearch_match_subfolder */
-       GSList *msglist = NULL;
-       GSList *cur;
-
-       /* The list is built w/ MsgInfo items whose ref.counts are incremented,
-          but they are decremented when the list is freed by
-          procmsg_msg_list_free(): we'll  ask for a new ref., below
-       */
-       msglist = folder_item_get_msg_list(folderItem);
-
-       for (cur = msglist; cur != NULL; cur = cur->next) {
-               MsgInfo *msg = (MsgInfo *)cur->data;
-               if (quicksearch_match(quicksearch, msg)) {
-                       /*debug_print("found: %s from:%s\n",procmsg_get_message_file_path(msg),msg->from);*/
-                       *messages = g_slist_prepend(*messages, procmsg_msginfo_new_ref(msg));
-               }
-               /* See 2.0.0cvs140 ChangeLog entry for details
-                  see also comments in quicksearch_match() */
-               if (quicksearch_from_gui(quicksearch)==TRUE
-                   && !quicksearch_is_active(quicksearch))
-                       break;
-       }
-       procmsg_msg_list_free(msglist);
-}
-
-/*
- * Searches within the folderItem and its sub-folders (if recursive is TRUE)
- * the messages matching the search request.
- *
- * NB: search within a Folder can be done this way:
- *         search_msg_in_folders(messages, quicksearch, searchType,
- *                               FOLDER_ITEM(folder->node->data), TRUE);
- */
-void search_msgs_in_folders(GSList **messages, QuickSearch* quicksearch,
-                           FolderItem* folderItem)
-{
-       FolderItem *cur = NULL;
-
-       search_msgs_in_folder(messages, quicksearch, folderItem);
-       if (quicksearch->request->recursive == FALSE)
-               return;
-
-       GNode *node = folderItem->node->children;
-       for (; node != NULL; node = node->next) {
-               cur = FOLDER_ITEM(node->data);
-               debug_print("in: %s\n",cur->path);
-               if (cur->node->children)
-                       search_msgs_in_folders(messages, quicksearch, cur);
-               else
-                       search_msgs_in_folder(messages, quicksearch, cur);
-       }
-       *messages = g_slist_reverse(*messages);
-}
-
- /*
-  * Returns the QuickSearchType associated to the supplied string.
-  */
-QuickSearchType quicksearch_type(const gchar* type)
-{
-       QuickSearchType searchType = QUICK_SEARCH_EXTENDED;
-       if (!type)
-               return searchType;
-       switch(toupper(*type)) {
-       case 'S':
-               searchType = QUICK_SEARCH_SUBJECT;
-       break;
-       case 'F':
-               searchType = QUICK_SEARCH_FROM;
-       break;
-       case 'T':
-               searchType = QUICK_SEARCH_TO;
-       break;
-       case 'E':
-               searchType = QUICK_SEARCH_EXTENDED;
-       break;
-       case 'M':
-               searchType = QUICK_SEARCH_MIXED;
-       break;
-       case 'G':
-               searchType = QUICK_SEARCH_TAG;
-       break;
-       }
-       return searchType;
-}
index 7e1de8c..c1a26bf 100644 (file)
 #ifndef QUICKSEARCH_H
 #define QUICKSEARCH_H 1
 
-typedef enum
-{
-       QUICK_SEARCH_SUBJECT,
-       QUICK_SEARCH_FROM,
-       QUICK_SEARCH_TO,
-       QUICK_SEARCH_EXTENDED,
-       QUICK_SEARCH_MIXED,
-       QUICK_SEARCH_TAG
-} QuickSearchType;
-
+#include "advsearch.h"
 
 typedef struct _QuickSearch QuickSearch;
 typedef void (*QuickSearchExecuteCallback) (QuickSearch *quicksearch, gpointer data);
 
 #include "procmsg.h"
 
-void search_msgs_in_folders(GSList **messages, QuickSearch* quicksearch,
-                           FolderItem* folderItem);
-QuickSearchType quicksearch_type(const gchar *type);
-
 QuickSearch *quicksearch_new();
-QuickSearch *quicksearch_new_nogui();
 GtkWidget *quicksearch_get_widget(QuickSearch *quicksearch);
 void quicksearch_show(QuickSearch *quicksearch);
 void quicksearch_hide(QuickSearch *quicksearch);
-void quicksearch_set(QuickSearch *quicksearch, QuickSearchType type, const gchar *matchstring);
+void quicksearch_set(QuickSearch *quicksearch, AdvancedSearchType type, const gchar *matchstring);
 void quicksearch_set_recursive(QuickSearch *quicksearch, gboolean recursive);
-gboolean quicksearch_is_active(QuickSearch *quicksearch);
+gboolean quicksearch_has_sat_predicate(QuickSearch *quicksearch);
 void quicksearch_set_execute_callback(QuickSearch *quicksearch,
                                      QuickSearchExecuteCallback callback,
                                      gpointer data);
-gboolean quicksearch_match(QuickSearch *quicksearch, MsgInfo *msginfo);
+void quicksearch_set_on_progress_cb(QuickSearch* search,
+               gboolean (*cb)(gpointer data, guint at, guint matched, guint total), gpointer data);
+
+gboolean quicksearch_run_on_folder(QuickSearch* quicksearch, FolderItem *folderItem, MsgInfoList **result);
+
 gboolean quicksearch_is_running(QuickSearch *quicksearch);
 gboolean quicksearch_has_focus(QuickSearch *quicksearch);
 void quicksearch_pass_key(QuickSearch *quicksearch, guint val, GdkModifierType mod);
-void quicksearch_reset_cur_folder_item(QuickSearch *quicksearch);
-void quicksearch_search_subfolders(QuickSearch *quicksearch, 
-                                  FolderView  *folderview,
-                                  FolderItem  *folder_item);
-gboolean quicksearch_is_in_subfolder(QuickSearch *quicksearch, FolderItem *cur);
 gboolean quicksearch_is_fast(QuickSearch *quicksearch);
 gboolean quicksearch_is_in_typing(QuickSearch *quicksearch);
 void quicksearch_relayout(QuickSearch *quicksearch);
 void quicksearch_set_search_strings(QuickSearch *quicksearch);
-void quicksearch_folder_item_invalidate(QuickSearch *quicksearch, FolderItem *item);
 #endif /* QUICKSEARCH_H */
index 923b515..ddeceba 100644 (file)
@@ -445,6 +445,7 @@ FolderClass *imap_get_class(void)
                imap_class.type = F_IMAP;
                imap_class.idstr = "imap";
                imap_class.uistr = "IMAP4";
+               imap_class.supports_server_search = FALSE;
 
                /* Folder functions */
                imap_class.new_folder = imap_folder_new;
@@ -476,6 +477,7 @@ FolderClass *imap_get_class(void)
                imap_class.add_msgs = imap_add_msgs;
                imap_class.copy_msg = imap_copy_msg;
                imap_class.copy_msgs = imap_copy_msgs;
+               imap_class.search_msgs = folder_item_search_msgs_local;
                imap_class.remove_msg = imap_remove_msg;
                imap_class.remove_msgs = imap_remove_msgs;
                imap_class.expunge = imap_expunge;
index 3bc9664..2e681e3 100644 (file)
 #include "hooks.h"
 #include "menu.h"
 #include "quicksearch.h"
+#include "advsearch.h"
 
 #ifdef HAVE_LIBETPAN
 #include "imap-thread.h"
@@ -2550,8 +2551,6 @@ static void lock_socket_input_cb(gpointer data,
        MainWindow *mainwin = (MainWindow *)data;
        gint sock;
        gchar buf[BUFFSIZE];
-       /* re-use the same quicksearch (& avoid matcher_list mem.leaks) */
-       static QuickSearch *quicksearch = NULL;
 
        sock = fd_accept(source);
        fd_gets(sock, buf, sizeof(buf));
@@ -2674,37 +2673,44 @@ static void lock_socket_input_cb(gpointer data,
        } else if (!strncmp(buf, "search ", 7)) {
                FolderItem* folderItem = NULL;
                GSList *messages = NULL;
-               gchar *folder_name, *request;
-               QuickSearchType searchType = QUICK_SEARCH_EXTENDED;
+               gchar *folder_name = NULL;
+               gchar *request = NULL;
+               AdvancedSearch *search;
                gboolean recursive;
-
-               if (quicksearch==NULL)
-                       quicksearch = quicksearch_new_nogui();
+               AdvancedSearchType searchType = ADVANCED_SEARCH_EXTENDED;
                
+               search = advsearch_new();
+
                folder_name = g_strdup(buf+7);
                strretchomp(folder_name);
 
-               if (fd_gets(sock, buf, sizeof(buf)) <= 0) {
-                       g_free(folder_name);
-                       folder_name=NULL;
-               }
-               searchType = quicksearch_type(buf);
-               if (fd_gets(sock, buf, sizeof(buf)) <= 0) {
-                       g_free(folder_name);
-                       folder_name=NULL;
+               if (fd_gets(sock, buf, sizeof(buf)) <= 0) 
+                       goto search_exit;
+
+               switch (toupper(buf[0])) {
+               case 'S': searchType = ADVANCED_SEARCH_SUBJECT; break;
+               case 'F': searchType = ADVANCED_SEARCH_FROM; break;
+               case 'T': searchType = ADVANCED_SEARCH_TO; break;
+               case 'M': searchType = ADVANCED_SEARCH_MIXED; break;
+               case 'G': searchType = ADVANCED_SEARCH_TAG; break;
+               case 'E': searchType = ADVANCED_SEARCH_EXTENDED; break;
                }
+
+               if (fd_gets(sock, buf, sizeof(buf)) <= 0) 
+                       goto search_exit;
+
                request = g_strdup(buf);
                strretchomp(request);
 
                recursive = TRUE;
                if (fd_gets(sock, buf, sizeof(buf)) > 0)
-                       if (buf[0]=='0')
-                               recursive = FALSE;
+                       recursive = buf[0] != '0';
 
-               debug_print("search: %s %i %s %i\n",folder_name,searchType,request,recursive);
+               debug_print("search: %s %i %s %i\n", folder_name, searchType, request, recursive);
 
                if (folder_name)
                        folderItem = folder_find_item_from_identifier(folder_name);
+
                if (folder_name && folderItem == NULL) {
                        debug_print("Unknown folder item : '%s', searching folder\n",folder_name);
                        Folder* folder = folder_find_from_path(folder_name);
@@ -2715,16 +2721,16 @@ static void lock_socket_input_cb(gpointer data,
                } else {
                        debug_print("%s %s\n",folderItem->name, folderItem->path);
                }
+
                if (folderItem != NULL) {
-                       quicksearch_set(quicksearch, searchType, request);
-                       quicksearch_set_recursive(quicksearch, recursive);
-                       search_msgs_in_folders(&messages, quicksearch, folderItem);
+                       advsearch_set(search, searchType, request);
+                       advsearch_search_msgs_in_folders(search, &messages, folderItem, recursive);
                } else {
                        g_print("Folder '%s' not found.\n'", folder_name);
                }
 
                GSList *cur;
-               for (cur=messages; cur != NULL; cur = cur->next) {
+               for (cur = messages; cur != NULL; cur = cur->next) {
                        MsgInfo* msg = (MsgInfo *)cur->data;
                        gchar *file = procmsg_get_message_file_path(msg);
                        fd_write_all(sock, file, strlen(file));
@@ -2733,10 +2739,12 @@ static void lock_socket_input_cb(gpointer data,
                }
                fd_write_all(sock, ".\n", 2);
 
-               if (messages != NULL)
-                       procmsg_msg_list_free(messages);
+search_exit:
                g_free(folder_name);
                g_free(request);
+               advsearch_free(search);
+               if (messages != NULL)
+                       procmsg_msg_list_free(messages);
        } else if (!strncmp(buf, "exit", 4)) {
                if (prefs_common.clean_on_exit && !prefs_common.ask_on_clean) {
                        procmsg_empty_all_trash();
index 26e9735..386d43a 100644 (file)
@@ -48,6 +48,7 @@
 #include "log.h"
 #include "tags.h"
 #include "folder_item_prefs.h"
+#include "procmsg.h"
 
 /*!
  *\brief       Keyword lookup element
index cae91f8..d4257a3 100644 (file)
@@ -23,7 +23,8 @@
 #include <sys/types.h>
 #include <regex.h>
 #include <glib.h>
-#include "procmsg.h"
+#include "proctypes.h"
+#include "matchertypes.h"
 
 /* constants generated by yacc */
 #include "matcher_parser_lex.h"
@@ -41,15 +42,11 @@ struct _MatcherProp {
        gboolean done;
 };
 
-typedef struct _MatcherProp MatcherProp;
-
 struct _MatcherList {
        GSList *matchers;
        gboolean bool_and;
 };
 
-typedef struct _MatcherList MatcherList;
-
 
 /* map MATCHCRITERIA_ to yacc's MATCHER_ */
 #define MC_(name) \
diff --git a/src/matchertypes.h b/src/matchertypes.h
new file mode 100644 (file)
index 0000000..2be1bdd
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2012 the Claws Mail team
+ *
+ * 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
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * 
+ */
+
+#ifndef MATCHERTYPES_H
+#define MATCHERTYPES_H
+
+struct _MatcherProp;
+typedef struct _MatcherProp MatcherProp;
+
+struct _MatcherList;
+typedef struct _MatcherList MatcherList;
+
+#endif
index c4a692b..bf2236d 100644 (file)
--- a/src/mh.c
+++ b/src/mh.c
@@ -148,6 +148,7 @@ FolderClass *mh_get_class(void)
                mh_class.type = F_MH;
                mh_class.idstr = "mh";
                mh_class.uistr = "MH";
+               mh_class.supports_server_search = FALSE;
                
                /* Folder functions */
                mh_class.new_folder = mh_folder_new;
@@ -175,6 +176,7 @@ FolderClass *mh_get_class(void)
                mh_class.add_msgs = mh_add_msgs;
                mh_class.copy_msg = mh_copy_msg;
                mh_class.copy_msgs = mh_copy_msgs;
+               mh_class.search_msgs = folder_item_search_msgs_local;
                mh_class.remove_msg = mh_remove_msg;
                mh_class.remove_msgs = mh_remove_msgs;
                mh_class.remove_all_msg = mh_remove_all_msg;
index 574407d..07d9895 100644 (file)
@@ -2787,7 +2787,7 @@ void mimeview_handle_cmd(MimeView *mimeview, const gchar *cmd, GdkEventButton *e
                                GTK_TOGGLE_BUTTON(mimeview->messageview->mainwin->summaryview->toggle_search), 
                                TRUE);
                quicksearch_set(mimeview->messageview->mainwin->summaryview->quicksearch, 
-                               QUICK_SEARCH_EXTENDED, buf);
+                               ADVANCED_SEARCH_EXTENDED, buf);
                g_free(buf);
        }
 }
index fb4155f..541803d 100644 (file)
@@ -158,6 +158,7 @@ FolderClass *news_get_class(void)
                news_class.type = F_NEWS;
                news_class.idstr = "news";
                news_class.uistr = "News";
+               news_class.supports_server_search = FALSE;
 
                /* Folder functions */
                news_class.new_folder = news_folder_new;
@@ -175,6 +176,7 @@ FolderClass *news_get_class(void)
                news_class.get_msginfos = news_get_msginfos;
                news_class.fetch_msg = news_fetch_msg;
                news_class.synchronise = news_synchronise;
+               news_class.search_msgs = folder_item_search_msgs_local;
                news_class.remove_msg = news_remove_msg;
                news_class.remove_cached_msg = news_remove_cached_msg;
        };
index 050e13c..a95949b 100644 (file)
@@ -20,6 +20,8 @@
 #ifndef PROCTYPES_H
 #define PROCTYPES_H
 
+#include <glib.h>
+
 struct _MsgInfo;
 typedef struct _MsgInfo                        MsgInfo;
 
index 2d20aa5..a68fd1e 100644 (file)
@@ -79,7 +79,12 @@ static struct SummarySearchWindow {
 
        SummaryView *summaryview;
 
-       MatcherList                     *matcher_list;
+       MatcherList     *matcher_list;
+       gboolean        is_fast;
+       gboolean        matcher_is_outdated;
+       gboolean        search_in_progress;
+       GHashTable      *matched_msgnums;
+       GHashTable      *unverified_msgnums;
 
        gboolean is_searching;
        gboolean from_entry_has_focus;
@@ -89,8 +94,14 @@ static struct SummarySearchWindow {
        gboolean adv_condition_entry_has_focus;
 } search_window;
 
+static gchar* add_history_get(GtkWidget *from, GList **history);
+
 static void summary_search_create      (void);
 
+static gboolean summary_search_verify_match            (MsgInfo *msg);
+static gboolean summary_search_prepare_matcher         ();
+static gboolean summary_search_prereduce_msg_list      ();
+
 static void summary_search_execute     (gboolean        backward,
                                         gboolean        search_all);
 
@@ -155,6 +166,28 @@ static gboolean key_pressed                (GtkWidget      *widget,
 }
 #endif
 
+static gchar* add_history_get(GtkWidget *from, GList **history)
+{
+       gchar *result;
+
+       result = gtk_combo_box_get_active_text(GTK_COMBO_BOX(from));
+       if (!result)
+               result = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(from))), 0, -1);
+
+       if (result && result[0] != '\0') {
+               /* add to history */
+
+               combobox_unset_popdown_strings(GTK_COMBO_BOX(from));
+               *history = add_history(*history, result);
+               combobox_set_popdown_strings(GTK_COMBO_BOX(from), *history);
+
+               return result;
+       } else {
+               g_free(result);
+               return NULL;
+       }
+}
+
 void summary_search(SummaryView *summaryview)
 {
        if (!search_window.window) {
@@ -483,161 +516,215 @@ static void summary_search_create(void)
        search_window.close_btn = close_btn;
        search_window.stop_btn = stop_btn;
        search_window.matcher_list = NULL;
+       search_window.matcher_is_outdated = TRUE;
+       search_window.search_in_progress = FALSE;
+       search_window.matched_msgnums = NULL;
+       search_window.unverified_msgnums = NULL;
        search_window.is_searching = is_searching;
 #ifdef MAEMO
        maemo_window_full_screen_if_needed(GTK_WINDOW(search_window.window));
 #endif
 }
 
-static void summary_search_execute(gboolean backward, gboolean search_all)
+static gboolean summary_search_verify_match(MsgInfo *msg)
+{
+       gpointer msgnum = GUINT_TO_POINTER(msg->msgnum);
+
+       if (g_hash_table_lookup(search_window.matched_msgnums, msgnum) != NULL)
+               return TRUE;
+
+       if (g_hash_table_lookup(search_window.unverified_msgnums, msgnum) != NULL) {
+               GSList *num = g_slist_prepend(NULL, msgnum);
+               gint match;
+
+               match = folder_item_search_msgs(msg->folder->folder,
+                     msg->folder,
+                     &num,
+                     NULL,
+                     search_window.matcher_list,
+                     NULL,
+                     NULL);
+
+               g_slist_free(num);
+               g_hash_table_remove(search_window.unverified_msgnums, msgnum);
+
+               if (match > 0) {
+                       g_hash_table_insert(search_window.matched_msgnums, msgnum, GINT_TO_POINTER(1));
+                       return TRUE;
+               } else {
+                       return FALSE;
+               }
+       }
+
+       return FALSE;
+}
+
+static gboolean summary_search_prepare_matcher()
 {
-       SummaryView *summaryview = search_window.summaryview;
-       GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
-       GtkCMCTreeNode *node;
-       MsgInfo *msginfo;
        gboolean adv_search;
        gboolean bool_and = FALSE;
        gboolean case_sens = FALSE;
-       gboolean all_searched = FALSE;
-       gboolean matched = FALSE;
-       gboolean body_matched = FALSE;
+       gchar *adv_condition = NULL;
+       gint match_type;
        gchar *from_str = NULL, *to_str = NULL, *subject_str = NULL;
        gchar *body_str = NULL;
-       gchar *adv_condition = NULL;
-       gboolean is_fast = TRUE;
-       gint interval = 1000;
-       gint i = 0;
        GSList *matchers = NULL;
 
-       if (summary_is_locked(summaryview)) {
-               return;
-       }
-       summary_lock(summaryview);
-
-       adv_search = gtk_toggle_button_get_active
-               (GTK_TOGGLE_BUTTON(search_window.adv_search_checkbtn));
+       if (!search_window.matcher_is_outdated)
+               return TRUE;
 
        if (search_window.matcher_list != NULL) {
                matcherlist_free(search_window.matcher_list);
                search_window.matcher_list = NULL;
        }
+
+       adv_search = gtk_toggle_button_get_active
+               (GTK_TOGGLE_BUTTON(search_window.adv_search_checkbtn));
+
        if (adv_search) {
-               adv_condition = gtk_combo_box_get_active_text(GTK_COMBO_BOX(search_window.adv_condition_entry));
-               if (!adv_condition)
-                       adv_condition = gtk_editable_get_chars(
-                                       GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(search_window.adv_condition_entry))),0,-1);
-               if (adv_condition && adv_condition[0] != '\0') {
-
-                       /* add to history */
-                       combobox_unset_popdown_strings(GTK_COMBO_BOX(search_window.adv_condition_entry));
-                       prefs_common.summary_search_adv_condition_history = add_history(
-                                       prefs_common.summary_search_adv_condition_history, adv_condition);
-                       combobox_set_popdown_strings(GTK_COMBO_BOX(search_window.adv_condition_entry),
-                                       prefs_common.summary_search_adv_condition_history);
-
-                       search_window.matcher_list = matcher_parser_get_cond((gchar*)adv_condition, &is_fast);
-                       if (!is_fast)
-                               interval = 100;
+               adv_condition = add_history_get(search_window.adv_condition_entry, &prefs_common.summary_search_adv_condition_history);
+               if (adv_condition) {
+                       search_window.matcher_list = matcher_parser_get_cond(adv_condition, &search_window.is_fast);
                        /* TODO: check for condition parsing error and show an error dialog */
                        g_free(adv_condition);
                } else {
                        /* TODO: warn if no search condition? (or make buttons enabled only when
                                at least one search condition has been set */
-                       summary_unlock(summaryview);
-                       return;
+                       return FALSE;
                }
        } else {
-               bool_and = combobox_get_active_data(
-                               GTK_COMBO_BOX(search_window.bool_optmenu));
-               case_sens = gtk_toggle_button_get_active
-                       (GTK_TOGGLE_BUTTON(search_window.case_checkbtn));
-
-               from_str    = gtk_combo_box_get_active_text(GTK_COMBO_BOX(search_window.from_entry));
-               to_str      = gtk_combo_box_get_active_text(GTK_COMBO_BOX(search_window.to_entry));
-               subject_str = gtk_combo_box_get_active_text(GTK_COMBO_BOX(search_window.subject_entry));
-               body_str    = gtk_combo_box_get_active_text(GTK_COMBO_BOX(search_window.body_entry));
-
-               if (!from_str)
-                       from_str = gtk_editable_get_chars(
-                                       GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(search_window.from_entry))),0,-1);
-               if (!to_str)
-                       to_str = gtk_editable_get_chars(
-                                       GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(search_window.to_entry))),0,-1);
-               if (!subject_str)
-                       subject_str = gtk_editable_get_chars(
-                                       GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(search_window.subject_entry))),0,-1);
-               if (!body_str)
-                       body_str = gtk_editable_get_chars(
-                                       GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(search_window.body_entry))),0,-1);
-
-               if (!from_str || !to_str || !subject_str || !body_str) {
+               bool_and = combobox_get_active_data(GTK_COMBO_BOX(search_window.bool_optmenu));
+               case_sens = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(search_window.case_checkbtn));
+
+               from_str    = add_history_get(search_window.from_entry, &prefs_common.summary_search_from_history);
+               to_str      = add_history_get(search_window.to_entry, &prefs_common.summary_search_to_history);
+               subject_str = add_history_get(search_window.subject_entry, &prefs_common.summary_search_subject_history);
+               body_str    = add_history_get(search_window.body_entry, &prefs_common.summary_search_body_history);
+
+               if (!from_str && !to_str && !subject_str && !body_str) {
                        /* TODO: warn if no search criteria? (or make buttons enabled only when
                         * at least one search criteria has been set */
-                       summary_unlock(summaryview);
-                       return;
-               }
-               if (    (from_str[0] == '\0') &&
-                               (to_str[0] == '\0') &&
-                               (subject_str[0] == '\0') &&
-                               (body_str[0] == '\0')) {
-                       /* TODO: warn if no search criteria? (or make buttons enabled only when
-                               at least one search criteria has been set */
-                       summary_unlock(summaryview);
-                       return;
+                       return FALSE;
                }
 
-               /* add to history */
-               if (from_str[0] != '\0') {
-                       MatcherProp *prop = matcherprop_new(MATCHCRITERIA_FROM,
-                                               NULL, case_sens ? MATCHTYPE_MATCH:MATCHTYPE_MATCHCASE,
-                                               from_str, 0);
+               match_type = case_sens ? MATCHTYPE_MATCH : MATCHTYPE_MATCHCASE;
+
+               if (from_str) {
+                       MatcherProp *prop = matcherprop_new(MATCHCRITERIA_FROM, NULL, match_type, from_str, 0);
                        matchers = g_slist_append(matchers, prop);
-                       combobox_unset_popdown_strings(GTK_COMBO_BOX(search_window.from_entry));
-                       prefs_common.summary_search_from_history = add_history(
-                                       prefs_common.summary_search_from_history, from_str);
-                       combobox_set_popdown_strings(GTK_COMBO_BOX(search_window.from_entry),
-                                       prefs_common.summary_search_from_history);
                }
-               if (to_str[0] != '\0') {
-                       MatcherProp *prop = matcherprop_new(MATCHCRITERIA_TO,
-                                               NULL, case_sens ? MATCHTYPE_MATCH:MATCHTYPE_MATCHCASE,
-                                               to_str, 0);
+               if (to_str) {
+                       MatcherProp *prop = matcherprop_new(MATCHCRITERIA_TO, NULL, match_type, to_str, 0);
                        matchers = g_slist_append(matchers, prop);
-                       combobox_unset_popdown_strings(GTK_COMBO_BOX(search_window.to_entry));
-                       prefs_common.summary_search_to_history = add_history(
-                                       prefs_common.summary_search_to_history, to_str);
-                       combobox_set_popdown_strings(GTK_COMBO_BOX(search_window.to_entry),
-                                       prefs_common.summary_search_to_history);
                }
-               if (subject_str[0] != '\0') {
-                       MatcherProp *prop = matcherprop_new(MATCHCRITERIA_SUBJECT,
-                                               NULL, case_sens ? MATCHTYPE_MATCH:MATCHTYPE_MATCHCASE,
-                                               subject_str, 0);
+               if (subject_str) {
+                       MatcherProp *prop = matcherprop_new(MATCHCRITERIA_SUBJECT, NULL, match_type, subject_str, 0);
                        matchers = g_slist_append(matchers, prop);
-                       combobox_unset_popdown_strings(GTK_COMBO_BOX(search_window.subject_entry));
-                       prefs_common.summary_search_subject_history = add_history(
-                                       prefs_common.summary_search_subject_history, subject_str);
-                       combobox_set_popdown_strings(GTK_COMBO_BOX(search_window.subject_entry),
-                                       prefs_common.summary_search_subject_history);
                }
-               if (body_str[0] != '\0') {
-                       MatcherProp *prop = matcherprop_new(MATCHCRITERIA_BODY_PART,
-                                               NULL, case_sens ? MATCHTYPE_MATCH:MATCHTYPE_MATCHCASE,
-                                               body_str, 0);
+               if (body_str) {
+                       MatcherProp *prop = matcherprop_new(MATCHCRITERIA_BODY_PART, NULL, match_type, body_str, 0);
                        matchers = g_slist_append(matchers, prop);
-                       combobox_unset_popdown_strings(GTK_COMBO_BOX(search_window.body_entry));
-                       prefs_common.summary_search_body_history = add_history(
-                                       prefs_common.summary_search_body_history, body_str);
-                       combobox_set_popdown_strings(GTK_COMBO_BOX(search_window.body_entry),
-                                       prefs_common.summary_search_body_history);
                }
                search_window.matcher_list = matcherlist_new(matchers, bool_and);
+
+               g_free(from_str);
+               g_free(to_str);
+               g_free(subject_str);
+               g_free(body_str);
+       }
+
+       search_window.matcher_is_outdated = FALSE;
+
+       return TRUE;
+}
+
+static gboolean summary_search_prereduce_msg_list()
+{
+       MsgInfo *msginfo;
+       FolderItem *folder;
+       gint matched_count;
+       MsgNumberList *msgnums = NULL;
+       MsgNumberList *cur;
+       SummaryView *summaryview = search_window.summaryview;
+       GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
+       gboolean on_server;
+
+       if (search_window.matcher_is_outdated && !summary_search_prepare_matcher()) {
+               return FALSE;
+       }
+
+       msginfo = gtk_cmctree_node_get_row_data(
+               ctree,
+               GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list));
+       folder = msginfo->folder;
+
+       on_server = folder->folder->klass->supports_server_search;
+
+       if (on_server) {
+               matched_count = folder_item_search_msgs(folder->folder,
+                             folder,
+                             &msgnums,
+                             &on_server,
+                             search_window.matcher_list,
+                             NULL,
+                             NULL);
+
+               if (matched_count < 0) {
+                       alertpanel_error(_("Something went wrong during search. Please check you logs."));
+                       return FALSE;
+               }
+       } else {
+               gboolean old_valid = TRUE;
+
+               folder->folder->klass->get_num_list(folder->folder, folder, &msgnums, &old_valid);
+       }
+
+       if (search_window.unverified_msgnums != NULL) {
+               g_hash_table_unref(search_window.unverified_msgnums);
+       }
+       if (search_window.matched_msgnums != NULL) {
+               g_hash_table_unref(search_window.matched_msgnums);
+       }
+
+       search_window.unverified_msgnums = g_hash_table_new(g_direct_hash, NULL);
+       search_window.matched_msgnums = g_hash_table_new(g_direct_hash, NULL);
+       for (cur = msgnums; cur != NULL; cur = cur->next) {
+               g_hash_table_insert(search_window.unverified_msgnums, cur->data, GINT_TO_POINTER(1));
+       }
+       g_slist_free(msgnums);
+       
+       if (msginfo->folder->folder->klass->supports_server_search && on_server) {
+               GHashTable *tmp = search_window.matched_msgnums;
+               search_window.matched_msgnums = search_window.unverified_msgnums;
+               search_window.unverified_msgnums = tmp;
+       }
+
+       return TRUE;
+}
+
+static void summary_search_execute(gboolean backward, gboolean search_all)
+{
+       SummaryView *summaryview = search_window.summaryview;
+       GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
+       GtkCMCTreeNode *node;
+       MsgInfo *msginfo;
+       gboolean all_searched = FALSE;
+       gboolean matched = FALSE;
+       gint i = 0;
+
+       if (summary_is_locked(summaryview)) {
+               return;
        }
+       summary_lock(summaryview);
 
        search_window.is_searching = TRUE;
        main_window_cursor_wait(summaryview->mainwin);
        summary_show_stop_button();
 
+       if (search_window.matcher_is_outdated && !summary_search_prereduce_msg_list()) {
+               goto exit;
+       }
+
        if (search_all) {
                summary_freeze(summaryview);
                summary_unselect_all(summaryview);
@@ -659,11 +746,9 @@ static void summary_search_execute(gboolean backward, gboolean search_all)
                }
        } else {
                if (backward) {
-                       node = gtkut_ctree_node_prev
-                               (ctree, summaryview->selected);
+                       node = gtkut_ctree_node_prev(ctree, summaryview->selected);
                } else {
-                       node = gtkut_ctree_node_next
-                               (ctree, summaryview->selected);
+                       node = gtkut_ctree_node_next(ctree, summaryview->selected);
                }
        }
 
@@ -693,11 +778,9 @@ static void summary_search_execute(gboolean backward, gboolean search_all)
                                         GTK_STOCK_NO, "+" GTK_STOCK_YES, NULL);
                        if (G_ALERTALTERNATE == val) {
                                if (backward) {
-                                       node = GTK_CMCTREE_NODE
-                                               (GTK_CMCLIST(ctree)->row_list_end);
+                                       node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list_end);
                                } else {
-                                       node = GTK_CMCTREE_NODE
-                                               (GTK_CMCLIST(ctree)->row_list);
+                                       node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
                                }
 
                                all_searched = TRUE;
@@ -709,44 +792,33 @@ static void summary_search_execute(gboolean backward, gboolean search_all)
                }
 
                msginfo = gtk_cmctree_node_get_row_data(ctree, node);
-               body_matched = FALSE;
 
-               matched = matcherlist_match(search_window.matcher_list, msginfo);
+               matched = summary_search_verify_match(msginfo);
 
                if (matched) {
                        if (search_all) {
                                gtk_cmctree_select(ctree, node);
                        } else {
-                               if (messageview_is_visible
-                                       (summaryview->messageview)) {
+                               if (messageview_is_visible(summaryview->messageview)) {
                                        summary_unlock(summaryview);
-                                       summary_select_node
-                                               (summaryview, node, TRUE, TRUE);
+                                       summary_select_node(summaryview, node, TRUE, TRUE);
                                        summary_lock(summaryview);
-                                       if (body_matched) {
-                                               messageview_search_string
-                                                       (summaryview->messageview,
-                                                        body_str, case_sens);
-                                       }
                                } else {
-                                       summary_select_node
-                                               (summaryview, node, FALSE, TRUE);
+                                       summary_select_node(summaryview, node, FALSE, TRUE);
                                }
                                break;
                        }
                }
 
+               if (i % (search_window.is_fast ? 1000 : 100) == 0) {
+                       GTK_EVENTS_FLUSH();
+               }
+
                node = backward ? gtkut_ctree_node_prev(ctree, node)
                                : gtkut_ctree_node_next(ctree, node);
-               if (i % interval == 0)
-                       GTK_EVENTS_FLUSH();
        }
 
-       g_free(from_str);
-       g_free(to_str);
-       g_free(subject_str);
-       g_free(body_str);
-
+exit:
        search_window.is_searching = FALSE;
        summary_hide_stop_button();
        main_window_cursor_normal(summaryview->mainwin);
@@ -771,6 +843,7 @@ static void summary_search_clear(GtkButton *button, gpointer data)
        if (search_window.is_searching) {
                search_window.is_searching = FALSE;
        }
+       search_window.matcher_is_outdated = TRUE;
 }
 
 static void summary_search_prev_clicked(GtkButton *button, gpointer data)
@@ -800,6 +873,7 @@ static void adv_condition_btn_done(MatcherList * matchers)
        }
 
        str = matcherlist_to_string(matchers);
+       search_window.matcher_is_outdated = TRUE;
 
        if (str != NULL) {
                gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((search_window.adv_condition_entry)))), str);
@@ -837,30 +911,35 @@ static void from_changed(void)
 {
        if (!search_window.from_entry_has_focus)
                gtk_widget_grab_focus(search_window.from_entry);
+       search_window.matcher_is_outdated = TRUE;
 }
 
 static void to_changed(void)
 {
        if (!search_window.to_entry_has_focus)
                gtk_widget_grab_focus(search_window.to_entry);
+       search_window.matcher_is_outdated = TRUE;
 }
 
 static void subject_changed(void)
 {
        if (!search_window.subject_entry_has_focus)
                gtk_widget_grab_focus(search_window.subject_entry);
+       search_window.matcher_is_outdated = TRUE;
 }
 
 static void body_changed(void)
 {
        if (!search_window.body_entry_has_focus)
                gtk_widget_grab_focus(search_window.body_entry);
+       search_window.matcher_is_outdated = TRUE;
 }
 
 static void adv_condition_changed(void)
 {
        if (!search_window.adv_condition_entry_has_focus)
                gtk_widget_grab_focus(search_window.adv_condition_entry);
+       search_window.matcher_is_outdated = TRUE;
 }
 
 static gboolean from_entry_focus_evt_in(GtkWidget *widget, GdkEventFocus *event,
index 799d709..22497d8 100644 (file)
@@ -394,6 +394,7 @@ static gint summary_cmp_by_tags             (GtkCMCList             *clist,
 
 static void quicksearch_execute_cb     (QuickSearch    *quicksearch,
                                         gpointer        data);
+
 static void tog_searchbar_cb           (GtkWidget      *w,
                                         gpointer        data);
 
@@ -1068,16 +1069,101 @@ static void summary_switch_from_to(SummaryView *summaryview, FolderItem *item)
        summary_set_column_titles(summaryview);
 }
 
-static gboolean summaryview_quicksearch_recurse(gpointer data)
+static void summaryview_reset_recursive_folder_match(SummaryView *summaryview)
 {
-       SummaryView *summaryview = (SummaryView *)data;
+       GSList *cur;
+
+       for (cur = summaryview->recursive_matched_folders; cur != NULL; cur = cur->next) {
+               folderview_update_search_icon(cur->data, FALSE);
+       }
+
+       g_slist_free(summaryview->recursive_matched_folders);
+       summaryview->recursive_matched_folders = NULL;
+       summaryview->search_root_folder = NULL;
+}
+
+static gboolean summaryview_quicksearch_recursive_progress(gpointer data, guint at, guint matched, guint total)
+{
+       QuickSearch *search = (QuickSearch*) data;
+       gint interval = quicksearch_is_fast(search) ? 5000 : 100;
+
+       statusbar_progress_all(at, total, interval);
+       if (at % interval == 0)
+               GTK_EVENTS_FLUSH();
+
+       if (matched > 0)
+               return FALSE;
+
+       return TRUE;
+}
+
+static void summaryview_quicksearch_recurse_step(SummaryView *summaryview, FolderItem *item)
+{
+       MsgInfoList *msgs = NULL;
+       gboolean result = TRUE;
+
+       statusbar_print_all(_("Searching in %s... \n"),
+               item->path ? item->path : "(null)");
+       folder_item_update_freeze();
+
+       quicksearch_set_on_progress_cb(summaryview->quicksearch, summaryview_quicksearch_recursive_progress, summaryview->quicksearch);
+       if (!quicksearch_run_on_folder(summaryview->quicksearch, item, &msgs))
+               result = FALSE;
+
+       result = result && msgs != NULL;
+
+       if (msgs != NULL)
+               procmsg_msg_list_free(msgs);
+
+       folder_item_update_thaw();
+       statusbar_progress_all(0, 0, 0);
+       statusbar_pop_all();
+
+       if (result) {
+               summaryview->recursive_matched_folders = g_slist_prepend(
+                               summaryview->recursive_matched_folders, item);
+       
+               folderview_update_search_icon(item, TRUE);
+       }
+}
+
+static void summaryview_quicksearch_search_subfolders(SummaryView *summaryview, FolderItem *folder_item)
+{
+       FolderItem *cur = NULL;
+       GNode *node = folder_item->node->children;
+
+       if (!prefs_common.summary_quicksearch_recurse
+                       || !quicksearch_has_sat_predicate(summaryview->quicksearch)
+                       || quicksearch_is_in_typing(summaryview->quicksearch))
+               return;
+
+       for (; node != NULL; node = node->next) {
+               if (!quicksearch_has_sat_predicate(summaryview->quicksearch))
+                       return;
+
+               cur = FOLDER_ITEM(node->data);
+               summaryview_quicksearch_recurse_step(summaryview, cur);
+               if (cur->node->children)
+                       summaryview_quicksearch_search_subfolders(summaryview, cur);
+       }
+}
+
+static void summaryview_quicksearch_recurse(SummaryView *summaryview)
+{
+       if (!prefs_common.summary_quicksearch_recurse
+               || !quicksearch_has_sat_predicate(summaryview->quicksearch)
+               || summaryview->folder_item == NULL) {
+               return;
+       }
+
        main_window_cursor_wait(summaryview->mainwin);
-       quicksearch_reset_cur_folder_item(summaryview->quicksearch);
-       quicksearch_search_subfolders(summaryview->quicksearch, 
-                             summaryview->folderview,
-                             summaryview->folder_item);
+
+       summaryview_reset_recursive_folder_match(summaryview);
+       summaryview->search_root_folder = summaryview->folder_item;
+
+       summaryview_quicksearch_search_subfolders(summaryview, summaryview->folder_item);
+       
        main_window_cursor_normal(summaryview->mainwin);
-       return FALSE;
 }
 
 static gboolean summary_check_consistency(FolderItem *item, GSList *mlist)
@@ -1125,6 +1211,20 @@ static gboolean summary_check_consistency(FolderItem *item, GSList *mlist)
        return TRUE;
 }
 
+static gboolean summaryview_quicksearch_root_progress(gpointer data, guint at, guint matched, guint total)
+{
+       SummaryView *summaryview = (SummaryView*) data;
+
+       gint interval = quicksearch_is_fast(summaryview->quicksearch) ? 5000 : 100;
+       
+       statusbar_progress_all(at, total, interval);
+
+       if (at % interval == 0)
+               GTK_EVENTS_FLUSH();
+
+       return TRUE;
+}
+
 gboolean summary_show(SummaryView *summaryview, FolderItem *item)
 {
        GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
@@ -1169,8 +1269,8 @@ gboolean summary_show(SummaryView *summaryview, FolderItem *item)
        }
        if (!prefs_common.summary_quicksearch_sticky
         && (!prefs_common.summary_quicksearch_recurse
-         || !quicksearch_is_active(summaryview->quicksearch)
-         || (item && !quicksearch_is_in_subfolder(summaryview->quicksearch, item)))
+         || !quicksearch_has_sat_predicate(summaryview->quicksearch)
+         || (item && !folder_is_child_of(item, summaryview->search_root_folder)))
         && !quicksearch_is_running(summaryview->quicksearch)
         && !is_refresh) {
                quicksearch_set(summaryview->quicksearch, prefs_common.summary_quicksearch_type, "");
@@ -1207,7 +1307,7 @@ gboolean summary_show(SummaryView *summaryview, FolderItem *item)
                        END_TIMING();
                        return FALSE;
                }
-               if (changed || !quicksearch_is_active(summaryview->quicksearch))
+               if (changed || !quicksearch_has_sat_predicate(summaryview->quicksearch))
                        folder_update_op_count();
        }
        
@@ -1231,16 +1331,7 @@ gboolean summary_show(SummaryView *summaryview, FolderItem *item)
                summary_thaw(summaryview);
                summary_unlock(summaryview);
                inc_unlock();
-               if (item && quicksearch_is_running(summaryview->quicksearch)) {
-                       main_window_cursor_wait(summaryview->mainwin);
-                       quicksearch_reset_cur_folder_item(summaryview->quicksearch);
-                       if (quicksearch_is_active(summaryview->quicksearch))
-                               quicksearch_search_subfolders(summaryview->quicksearch, 
-                                             summaryview->folderview,
-                                             summaryview->folder_item);
-                       main_window_cursor_normal(summaryview->mainwin);
-               }       
-               END_TIMING();           
+               END_TIMING();
                return TRUE;
        }
        g_free(buf);
@@ -1266,10 +1357,44 @@ gboolean summary_show(SummaryView *summaryview, FolderItem *item)
                mlist = folder_item_get_msg_list(item);
        }
 
+       if (quicksearch_has_sat_predicate(summaryview->quicksearch)) {
+               procmsg_msg_list_free(mlist);
+               mlist = NULL;
+
+               START_TIMING("quicksearch");
+
+               statusbar_print_all(_("Searching in %s... \n"), 
+                       summaryview->folder_item->path ? 
+                       summaryview->folder_item->path : "(null)");
+
+               folder_item_update_freeze();
+
+               quicksearch_set_on_progress_cb(summaryview->quicksearch, summaryview_quicksearch_root_progress, summaryview);
+               quicksearch_run_on_folder(summaryview->quicksearch, summaryview->folder_item, &mlist);
+
+               folder_item_update_thaw();
+               statusbar_progress_all(0, 0, 0);
+               statusbar_pop_all();
+
+               if (!quicksearch_has_sat_predicate(summaryview->quicksearch)) {
+                       debug_print("search cancelled!\n");
+                       printf("search cancelled!\n");
+                       summary_thaw(summaryview);
+                       STATUSBAR_POP(summaryview->mainwin);
+                       main_window_cursor_normal(summaryview->mainwin);
+                       summary_unlock(summaryview);
+                       inc_unlock();
+                       summary_show(summaryview, summaryview->folder_item);
+                       END_TIMING();
+                       return FALSE;
+               }
+               END_TIMING();
+       }
+
        if ((summaryview->folder_item->hide_read_msgs
              || summaryview->folder_item->hide_del_msgs
              || summaryview->folder_item->hide_read_threads) &&
-           quicksearch_is_active(summaryview->quicksearch) == FALSE) {
+           quicksearch_has_sat_predicate(summaryview->quicksearch) == FALSE) {
                GSList *not_killed;
                
                summary_set_hide_read_msgs_menu(summaryview, summaryview->folder_item->hide_read_msgs);
@@ -1312,52 +1437,6 @@ gboolean summary_show(SummaryView *summaryview, FolderItem *item)
                summary_set_hide_read_threads_menu(summaryview, FALSE);
        }
 
-       if (quicksearch_is_active(summaryview->quicksearch)) {
-               GSList *not_killed;
-               gint interval = quicksearch_is_fast(summaryview->quicksearch) ? 5000:100;
-               START_TIMING("quicksearch");
-               gint num = 0, total = summaryview->folder_item->total_msgs;
-               statusbar_print_all(_("Searching in %s... \n"), 
-                       summaryview->folder_item->path ? 
-                       summaryview->folder_item->path : "(null)");
-               not_killed = NULL;
-               folder_item_update_freeze();
-               for (cur = mlist ; cur != NULL && cur->data != NULL ; cur = g_slist_next(cur)) {
-                       MsgInfo * msginfo = (MsgInfo *) cur->data;
-
-                       statusbar_progress_all(num++,total, interval);
-
-                       if (!msginfo->hidden && quicksearch_match(summaryview->quicksearch, msginfo))
-                               not_killed = g_slist_prepend(not_killed, msginfo);
-                       else
-                               procmsg_msginfo_free(msginfo);
-                       if (num % interval == 0)
-                               GTK_EVENTS_FLUSH();
-                       if (!quicksearch_is_active(summaryview->quicksearch)) {
-                               break;
-                       }
-               }
-               folder_item_update_thaw();
-               statusbar_progress_all(0,0,0);
-               statusbar_pop_all();
-               
-               hidden_removed = TRUE;
-               if (!quicksearch_is_active(summaryview->quicksearch)) {
-                       debug_print("search cancelled!\n");
-                       summary_thaw(summaryview);
-                       STATUSBAR_POP(summaryview->mainwin);
-                       main_window_cursor_normal(summaryview->mainwin);
-                       summary_unlock(summaryview);
-                       inc_unlock();
-                       summary_show(summaryview, summaryview->folder_item);
-                       END_TIMING();
-                       return FALSE;
-               }
-               g_slist_free(mlist);
-               mlist = not_killed;
-               END_TIMING();
-       }
-
        if (!hidden_removed) {
                START_TIMING("removing hidden");
                not_killed = NULL;
@@ -1382,13 +1461,6 @@ gboolean summary_show(SummaryView *summaryview, FolderItem *item)
 
        g_slist_free(mlist);
 
-       if (quicksearch_is_active(summaryview->quicksearch) &&
-           quicksearch_is_running(summaryview->quicksearch)) {
-               /* only scan subfolders when quicksearch changed,
-                * not when search is the same and folder changed */
-               g_timeout_add(100, summaryview_quicksearch_recurse, summaryview);
-       }
-
        if (is_refresh) {
                if (!quicksearch_is_in_typing(summaryview->quicksearch)) {
                        summaryview->displayed =
@@ -2492,7 +2564,7 @@ static void summary_status_show(SummaryView *summaryview)
        if (summaryview->folder_item->hide_read_msgs 
        || summaryview->folder_item->hide_del_msgs
        || summaryview->folder_item->hide_read_threads
-       || quicksearch_is_active(summaryview->quicksearch)) {
+       || quicksearch_has_sat_predicate(summaryview->quicksearch)) {
                rowlist = GTK_CMCLIST(summaryview->ctree)->row_list;
                for (cur = rowlist; cur != NULL && cur->data != NULL; cur = cur->next) {
                        msginfo = gtk_cmctree_node_get_row_data
@@ -6738,7 +6810,11 @@ static void quicksearch_execute_cb(QuickSearch *quicksearch, gpointer data)
 {
        SummaryView *summaryview = data;
 
-       summary_show(summaryview, summaryview->folder_item);
+       summaryview_reset_recursive_folder_match(summaryview);
+       if (summary_show(summaryview, summaryview->folder_item))
+               summaryview_quicksearch_recurse(summaryview);
+       else
+               summaryview_reset_recursive_folder_match(summaryview);
 }
 
 static void tog_searchbar_cb(GtkWidget *w, gpointer data)
@@ -8014,8 +8090,6 @@ static gboolean summary_update_folder_hook(gpointer source, gpointer data)
        hookdata = source;
        if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
                summary_update_unread(summaryview, hookdata->item);
-               quicksearch_folder_item_invalidate(summaryview->quicksearch,
-                                                  hookdata->item);
        } else
                summary_update_unread(summaryview, NULL);
 
@@ -8052,7 +8126,7 @@ static void summary_find_answers (SummaryView *summaryview, MsgInfo *msg)
        
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(summaryview->toggle_search), TRUE);
 
-       quicksearch_set(summaryview->quicksearch, QUICK_SEARCH_EXTENDED, buf);
+       quicksearch_set(summaryview->quicksearch, ADVANCED_SEARCH_EXTENDED, buf);
        g_free(buf);
 
        node = gtk_cmctree_node_nth(GTK_CMCTREE(summaryview->ctree), 0);
index 147a04a..d99a510 100644 (file)
@@ -166,6 +166,10 @@ private:
        gint folder_update_callback_id;
 
        GtkTargetList *target_list; /* DnD */
+
+       // folders with matches for recursive quicksearch queries
+       GSList *recursive_matched_folders;
+       FolderItem *search_root_folder;
        
 #if !GTK_CHECK_VERSION(2,12,0)
        GtkTooltips *tooltips;