commit 5bf542dc47d97400d96b67a0a8535d1622819056
parent 083d09f5b53508ba346f1a6bedfe449bfaadd3c4
Author: Colin Leroy <colin@colino.net>
Date: Fri, 21 Sep 2012 10:19:43 +0000
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>
Diffstat:
23 files changed, 1314 insertions(+), 969 deletions(-)
diff --git 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
diff --git a/PATCHSETS b/PATCHSETS
@@ -4442,3 +4442,4 @@
( 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
diff --git a/configure.ac b/configure.ac
@@ -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=
diff --git a/src/Makefile.am b/src/Makefile.am
@@ -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
@@ -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
@@ -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
diff --git a/src/folder.c b/src/folder.c
@@ -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;
+}
+
diff --git a/src/folder.h b/src/folder.h
@@ -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__ */
diff --git a/src/folderview.c b/src/folderview.c
@@ -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);
}
diff --git a/src/gtk/quicksearch.c b/src/gtk/quicksearch.c
@@ -46,12 +46,13 @@
#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;
-}
diff --git a/src/gtk/quicksearch.h b/src/gtk/quicksearch.h
@@ -21,49 +21,33 @@
#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 */
diff --git a/src/imap.c b/src/imap.c
@@ -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;
diff --git a/src/main.c b/src/main.c
@@ -121,6 +121,7 @@
#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();
diff --git a/src/matcher.c b/src/matcher.c
@@ -48,6 +48,7 @@
#include "log.h"
#include "tags.h"
#include "folder_item_prefs.h"
+#include "procmsg.h"
/*!
*\brief Keyword lookup element
diff --git a/src/matcher.h b/src/matcher.h
@@ -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
@@ -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
diff --git 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;
diff --git a/src/mimeview.c b/src/mimeview.c
@@ -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);
}
}
diff --git a/src/news.c b/src/news.c
@@ -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;
};
diff --git a/src/proctypes.h b/src/proctypes.h
@@ -20,6 +20,8 @@
#ifndef PROCTYPES_H
#define PROCTYPES_H
+#include <glib.h>
+
struct _MsgInfo;
typedef struct _MsgInfo MsgInfo;
diff --git a/src/summary_search.c b/src/summary_search.c
@@ -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,
diff --git a/src/summaryview.c b/src/summaryview.c
@@ -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);
diff --git a/src/summaryview.h b/src/summaryview.h
@@ -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;