[Commits] Makefile.am 1.155.2.102 1.155.2.103 advsearch.c NONE 1.1.2.1 advsearch.h NONE 1.1.2.1 folder.c 1.213.2.211 1.213.2.212 folder.h 1.87.2.67 1.87.2.68 folderview.c 1.207.2.229 1.207.2.230 imap.c 1.179.2.262 1.179.2.263 main.c 1.115.2.257 1.115.2.258 matcher.c 1.75.2.75 1.75.2.76 matcher.h 1.39.2.21 1.39.2.22 matchertypes.h NONE 1.1.2.1 mh.c 1.79.2.76 1.79.2.77 mimeview.c 1.83.2.195 1.83.2.196 news.c 1.101.2.72 1.101.2.73 proctypes.h 1.1.2.1 1.1.2.2 summary_search.c 1.15.2.70 1.15.2.71 summaryview.c 1.395.2.453 1.395.2.454 summaryview.h 1.68.2.61 1.68.2.62

colin at claws-mail.org colin at claws-mail.org
Fri Sep 21 12:19:45 CEST 2012


Update of /home/claws-mail/claws/src
In directory srv:/tmp/cvs-serv4864/src

Modified Files:
      Tag: gtk2
	Makefile.am folder.c folder.h folderview.c imap.c main.c 
	matcher.c matcher.h mh.c mimeview.c news.c proctypes.h 
	summary_search.c summaryview.c summaryview.h 
Added Files:
      Tag: gtk2
	advsearch.c advsearch.h matchertypes.h 
Log Message:
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 at cs.uni-kl.de>

Index: summaryview.h
===================================================================
RCS file: /home/claws-mail/claws/src/summaryview.h,v
retrieving revision 1.68.2.61
retrieving revision 1.68.2.62
diff -u -d -r1.68.2.61 -r1.68.2.62
--- summaryview.h	8 Sep 2012 20:23:11 -0000	1.68.2.61
+++ summaryview.h	21 Sep 2012 10:19:43 -0000	1.68.2.62
@@ -166,6 +166,10 @@
 	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;

--- NEW FILE: advsearch.h ---
/*
 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
 * Copyright (C) 2012 the Claws Mail team
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 * 
 */

#ifndef ADVSEARCH_H
#define ADVSEARCH_H 1

#include "proctypes.h"
#include "folder.h"

// temporary
#include "matcher.h"

typedef enum
{
	ADVANCED_SEARCH_SUBJECT,
	ADVANCED_SEARCH_FROM,
	ADVANCED_SEARCH_TO,
	ADVANCED_SEARCH_EXTENDED,
	ADVANCED_SEARCH_MIXED,
	ADVANCED_SEARCH_TAG,
} AdvancedSearchType;


typedef struct _AdvancedSearch AdvancedSearch;

void advsearch_set_on_progress_cb(AdvancedSearch* search,
		gboolean (*cb)(gpointer data, guint at, guint matched, guint total), gpointer data);
void advsearch_set_on_error_cb(AdvancedSearch* search, void (*cb)(gpointer data), gpointer data);

AdvancedSearch *advsearch_new();
void advsearch_free(AdvancedSearch *search);

void advsearch_set(AdvancedSearch *search, AdvancedSearchType type, const gchar *matchstring);
gboolean advsearch_is_fast(AdvancedSearch *search);
gboolean advsearch_has_proper_predicate(AdvancedSearch *search);

gboolean advsearch_search_msgs_in_folders(AdvancedSearch* search, MsgInfoList **messages,
				          FolderItem* folderItem, gboolean recursive);

void advsearch_abort(AdvancedSearch *search);

gchar *advsearch_expand_search_string(const gchar *search_string);

#endif

Index: mimeview.c
===================================================================
RCS file: /home/claws-mail/claws/src/mimeview.c,v
retrieving revision 1.83.2.195
retrieving revision 1.83.2.196
diff -u -d -r1.83.2.195 -r1.83.2.196
--- mimeview.c	12 Sep 2012 09:23:12 -0000	1.83.2.195
+++ mimeview.c	21 Sep 2012 10:19:42 -0000	1.83.2.196
@@ -2787,7 +2787,7 @@
 				GTK_TOGGLE_BUTTON(mimeview->messageview->mainwin->summaryview->toggle_search), 
 				TRUE);
 		quicksearch_set(mimeview->messageview->mainwin->summaryview->quicksearch, 
-				QUICK_SEARCH_EXTENDED, buf);
+				ADVANCED_SEARCH_EXTENDED, buf);
 		g_free(buf);
 	}
 }

Index: summary_search.c
===================================================================
RCS file: /home/claws-mail/claws/src/summary_search.c,v
retrieving revision 1.15.2.70
retrieving revision 1.15.2.71
diff -u -d -r1.15.2.70 -r1.15.2.71
--- summary_search.c	7 Jul 2012 07:09:30 -0000	1.15.2.70
+++ summary_search.c	21 Sep 2012 10:19:43 -0000	1.15.2.71
@@ -79,7 +79,12 @@
 
 	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 @@
 	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 @@
 }
 #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 @@
 	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;
 	}
-	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);
+	adv_search = gtk_toggle_button_get_active
+		(GTK_TOGGLE_BUTTON(search_window.adv_search_checkbtn));
 
-			search_window.matcher_list = matcher_parser_get_cond((gchar*)adv_condition, &is_fast);
-			if (!is_fast)
-				interval = 100;
+	if (adv_search) {
+		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));
+		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));
 
-		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);
+		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) {
+		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 @@
 		}
 	} 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 @@
 					 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 @@
 		}
 
 		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 @@
 	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 @@
 	}
 
 	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 @@
 {
 	if (!search_window.from_entry_has_focus)
 		gtk_widget_grab_focus(search_window.from_entry);
+	search_window.matcher_is_outdated = TRUE;
 }
 
 static void to_changed(void)
 {
 	if (!search_window.to_entry_has_focus)
 		gtk_widget_grab_focus(search_window.to_entry);
+	search_window.matcher_is_outdated = TRUE;
 }
 
 static void subject_changed(void)
 {
 	if (!search_window.subject_entry_has_focus)
 		gtk_widget_grab_focus(search_window.subject_entry);
+	search_window.matcher_is_outdated = TRUE;
 }
 
 static void body_changed(void)
 {
 	if (!search_window.body_entry_has_focus)
 		gtk_widget_grab_focus(search_window.body_entry);
+	search_window.matcher_is_outdated = TRUE;
 }
 
 static void adv_condition_changed(void)
 {
 	if (!search_window.adv_condition_entry_has_focus)
 		gtk_widget_grab_focus(search_window.adv_condition_entry);
+	search_window.matcher_is_outdated = TRUE;
 }
 
 static gboolean from_entry_focus_evt_in(GtkWidget *widget, GdkEventFocus *event,

Index: proctypes.h
===================================================================
RCS file: /home/claws-mail/claws/src/Attic/proctypes.h,v
retrieving revision 1.1.2.1
retrieving revision 1.1.2.2
diff -u -d -r1.1.2.1 -r1.1.2.2
--- proctypes.h	8 Sep 2012 20:23:11 -0000	1.1.2.1
+++ proctypes.h	21 Sep 2012 10:19:42 -0000	1.1.2.2
@@ -20,6 +20,8 @@
 #ifndef PROCTYPES_H
 #define PROCTYPES_H
 
+#include <glib.h>
+
 struct _MsgInfo;
 typedef struct _MsgInfo			MsgInfo;
 

--- NEW FILE: matchertypes.h ---
/*
 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
 * Copyright (C) 2012 the Claws Mail team
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 * 
 */

#ifndef MATCHERTYPES_H
#define MATCHERTYPES_H

struct _MatcherProp;
typedef struct _MatcherProp MatcherProp;

struct _MatcherList;
typedef struct _MatcherList MatcherList;

#endif

Index: imap.c
===================================================================
RCS file: /home/claws-mail/claws/src/imap.c,v
retrieving revision 1.179.2.262
retrieving revision 1.179.2.263
diff -u -d -r1.179.2.262 -r1.179.2.263
--- imap.c	19 Sep 2012 09:45:32 -0000	1.179.2.262
+++ imap.c	21 Sep 2012 10:19:42 -0000	1.179.2.263
@@ -445,6 +445,7 @@
 		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 @@
 		imap_class.add_msgs = imap_add_msgs;
 		imap_class.copy_msg = imap_copy_msg;
 		imap_class.copy_msgs = imap_copy_msgs;
+		imap_class.search_msgs = folder_item_search_msgs_local;
 		imap_class.remove_msg = imap_remove_msg;
 		imap_class.remove_msgs = imap_remove_msgs;
 		imap_class.expunge = imap_expunge;

Index: summaryview.c
===================================================================
RCS file: /home/claws-mail/claws/src/summaryview.c,v
retrieving revision 1.395.2.453
retrieving revision 1.395.2.454
diff -u -d -r1.395.2.453 -r1.395.2.454
--- summaryview.c	12 Sep 2012 09:23:12 -0000	1.395.2.453
+++ summaryview.c	21 Sep 2012 10:19:43 -0000	1.395.2.454
@@ -394,6 +394,7 @@
 
 static void quicksearch_execute_cb	(QuickSearch    *quicksearch,
 					 gpointer	 data);
+
 static void tog_searchbar_cb		(GtkWidget	*w,
 					 gpointer	 data);
 
@@ -1068,16 +1069,101 @@
 	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 @@
 	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 @@
 	}
 	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 @@
 			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 @@
 		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 @@
 		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 @@
 		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 @@
 
 	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 @@
 	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 @@
 {
 	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 @@
 	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 @@
 	
 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(summaryview->toggle_search), TRUE);
 
-	quicksearch_set(summaryview->quicksearch, QUICK_SEARCH_EXTENDED, buf);
+	quicksearch_set(summaryview->quicksearch, ADVANCED_SEARCH_EXTENDED, buf);
 	g_free(buf);
 
 	node = gtk_cmctree_node_nth(GTK_CMCTREE(summaryview->ctree), 0);

Index: matcher.h
===================================================================
RCS file: /home/claws-mail/claws/src/matcher.h,v
retrieving revision 1.39.2.21
retrieving revision 1.39.2.22
diff -u -d -r1.39.2.21 -r1.39.2.22
--- matcher.h	5 Sep 2012 15:38:36 -0000	1.39.2.21
+++ matcher.h	21 Sep 2012 10:19:42 -0000	1.39.2.22
@@ -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 @@
 	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) \

Index: news.c
===================================================================
RCS file: /home/claws-mail/claws/src/news.c,v
retrieving revision 1.101.2.72
retrieving revision 1.101.2.73
diff -u -d -r1.101.2.72 -r1.101.2.73
--- news.c	19 Sep 2012 09:45:32 -0000	1.101.2.72
+++ news.c	21 Sep 2012 10:19:42 -0000	1.101.2.73
@@ -158,6 +158,7 @@
 		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 @@
 		news_class.get_msginfos = news_get_msginfos;
 		news_class.fetch_msg = news_fetch_msg;
 		news_class.synchronise = news_synchronise;
+		news_class.search_msgs = folder_item_search_msgs_local;
 		news_class.remove_msg = news_remove_msg;
 		news_class.remove_cached_msg = news_remove_cached_msg;
 	};

Index: folder.h
===================================================================
RCS file: /home/claws-mail/claws/src/folder.h,v
retrieving revision 1.87.2.67
retrieving revision 1.87.2.68
diff -u -d -r1.87.2.67 -r1.87.2.68
--- folder.h	8 Sep 2012 20:23:10 -0000	1.87.2.67
+++ folder.h	21 Sep 2012 10:19:42 -0000	1.87.2.68
@@ -132,6 +132,7 @@
 #include "proctypes.h"
 #include "xml.h"
 #include "prefs_account.h"
+#include "matchertypes.h"
 
 struct _MsgCache;
 
@@ -159,6 +160,23 @@
 	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 @@
 	 * 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 @@
 						 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 @@
 					 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_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_discard_cache		(FolderItem *item);
 void folder_item_commit_tags(FolderItem *item, MsgInfo *msginfo, GSList *tags_set, GSList *tags_unset);
 
+
+
+gint folder_item_search_msgs_local	(Folder			*folder,
+					 FolderItem		*container,
+					 MsgNumberList		**msgs,
+					 gboolean		*on_server,
+					 MatcherList		*predicate,
+					 SearchProgressNotify	progress_cb,
+					 gpointer		progress_data);
+
 #endif /* __FOLDER_H__ */

Index: mh.c
===================================================================
RCS file: /home/claws-mail/claws/src/mh.c,v
retrieving revision 1.79.2.76
retrieving revision 1.79.2.77
diff -u -d -r1.79.2.76 -r1.79.2.77
--- mh.c	12 Sep 2012 08:52:47 -0000	1.79.2.76
+++ mh.c	21 Sep 2012 10:19:42 -0000	1.79.2.77
@@ -148,6 +148,7 @@
 		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 @@
 		mh_class.add_msgs = mh_add_msgs;
 		mh_class.copy_msg = mh_copy_msg;
 		mh_class.copy_msgs = mh_copy_msgs;
+		mh_class.search_msgs = folder_item_search_msgs_local;
 		mh_class.remove_msg = mh_remove_msg;
 		mh_class.remove_msgs = mh_remove_msgs;
 		mh_class.remove_all_msg = mh_remove_all_msg;

Index: matcher.c
===================================================================
RCS file: /home/claws-mail/claws/src/matcher.c,v
retrieving revision 1.75.2.75
retrieving revision 1.75.2.76
diff -u -d -r1.75.2.75 -r1.75.2.76
--- matcher.c	16 Sep 2012 21:56:20 -0000	1.75.2.75
+++ matcher.c	21 Sep 2012 10:19:42 -0000	1.75.2.76
@@ -48,6 +48,7 @@
 #include "log.h"
 #include "tags.h"
 #include "folder_item_prefs.h"
+#include "procmsg.h"
 
 /*!
  *\brief	Keyword lookup element

--- NEW FILE: advsearch.c ---
#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;
}

Index: folderview.c
===================================================================
RCS file: /home/claws-mail/claws/src/folderview.c,v
retrieving revision 1.207.2.229
retrieving revision 1.207.2.230
diff -u -d -r1.207.2.229 -r1.207.2.230
--- folderview.c	16 Sep 2012 21:56:20 -0000	1.207.2.229
+++ folderview.c	21 Sep 2012 10:19:42 -0000	1.207.2.230
@@ -1713,7 +1713,7 @@
 		if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) && 
 		     update_info->item == folderview->summaryview->folder_item &&
 		     update_info->item != NULL)
-			if (!quicksearch_is_active(folderview->summaryview->quicksearch))
+			if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
 				summary_show(folderview->summaryview, update_info->item);
 	}
 	

Index: main.c
===================================================================
RCS file: /home/claws-mail/claws/src/main.c,v
retrieving revision 1.115.2.257
retrieving revision 1.115.2.258
diff -u -d -r1.115.2.257 -r1.115.2.258
--- main.c	13 Sep 2012 08:07:25 -0000	1.115.2.257
+++ main.c	21 Sep 2012 10:19:42 -0000	1.115.2.258
@@ -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 @@
 	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 @@
 	} 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 @@
 		} 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 @@
 		}
 		fd_write_all(sock, ".\n", 2);
 
-		if (messages != NULL)
-			procmsg_msg_list_free(messages);
+search_exit:
 		g_free(folder_name);
 		g_free(request);
+		advsearch_free(search);
+		if (messages != NULL)
+			procmsg_msg_list_free(messages);
 	} else if (!strncmp(buf, "exit", 4)) {
 		if (prefs_common.clean_on_exit && !prefs_common.ask_on_clean) {
 			procmsg_empty_all_trash();

Index: Makefile.am
===================================================================
RCS file: /home/claws-mail/claws/src/Makefile.am,v
retrieving revision 1.155.2.102
retrieving revision 1.155.2.103
diff -u -d -r1.155.2.102 -r1.155.2.103
--- Makefile.am	8 Sep 2012 20:23:09 -0000	1.155.2.102
+++ Makefile.am	21 Sep 2012 10:19:42 -0000	1.155.2.103
@@ -126,6 +126,7 @@
 	addrgather.c \
 	addrharvest.c \
 	addritem.c \
+	advsearch.c \
 	alertpanel.c \
 	autofaces.c \
 	codeconv.c \
@@ -238,6 +239,7 @@
 	addressitem.h \
 	addrgather.h \
 	addrharvest.h \
+	advsearch.h \
 	alertpanel.h \
 	autofaces.h \
 	codeconv.h \
@@ -267,6 +269,7 @@
 	mainwindow.h \
 	manual.h \
 	matcher.h \
+	matchertypes.h \
 	matcher_parser.h \
 	matcher_parser_lex.h \
 	matcher_parser_parse.h \

Index: folder.c
===================================================================
RCS file: /home/claws-mail/claws/src/folder.c,v
retrieving revision 1.213.2.211
retrieving revision 1.213.2.212
diff -u -d -r1.213.2.211 -r1.213.2.212
--- folder.c	12 Sep 2012 09:23:12 -0000	1.213.2.211
+++ folder.c	21 Sep 2012 10:19:42 -0000	1.213.2.212
@@ -4689,6 +4689,22 @@
 	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 @@
 	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;
+}
+



More information about the Commits mailing list