2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2011 Colin Leroy <colin@colino.net>
4 * and the Claws Mail team
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include <glib/gi18n.h>
30 #include <gdk/gdkkeysyms.h>
32 #if !GTK_CHECK_VERSION(3, 0, 0)
33 #include "gtkcmoptionmenu.h"
38 #include "prefs_common.h"
39 #include "description_window.h"
41 #include "matcher_parser.h"
42 #include "quicksearch.h"
43 #include "folderview.h"
45 #include "prefs_matcher.h"
47 #include "statusbar.h"
49 struct _QuickSearchRequest
53 FolderItem *folderItem;
56 typedef struct _QuickSearchRequest QuickSearchRequest;
60 GtkWidget *hbox_search;
61 GtkWidget *search_type;
62 #if !GTK_CHECK_VERSION(3, 0, 0)
63 GtkWidget *search_type_opt;
65 GtkWidget *search_string_entry;
66 GtkWidget *search_condition_expression;
67 GtkWidget *search_description;
68 GtkWidget *clear_search;
72 MatcherList *matcher_list;
74 QuickSearchRequest *request;
75 QuickSearchExecuteCallback callback;
76 gpointer callback_data;
80 gboolean deferred_free;
81 FolderItem *root_folder_item;
84 guint press_timeout_id;
86 GList *normal_search_strings;
87 GList *extended_search_strings;
89 /* dynamic and autorun qs settings are exclusive*/
90 GtkWidget *dynamic_menuitem;
91 GtkWidget *autorun_menuitem;
96 static void quicksearch_set_running(QuickSearch *quicksearch, gboolean run);
97 static void quicksearch_set_matchstring(QuickSearch *quicksearch, const gchar *matchstring);
98 static void quicksearch_set_active(QuickSearch *quicksearch, gboolean active);
99 static void quicksearch_reset_folder_items(QuickSearch *quicksearch, FolderItem *folder_item);
100 static gchar *expand_search_string(const gchar *str);
101 static gchar *expand_tag_search_string(const gchar *str);
103 static gboolean quicksearch_from_gui(QuickSearch *quicksearch)
105 return quicksearch->gui;
108 gboolean quicksearch_is_fast(QuickSearch *quicksearch)
110 return quicksearch->is_fast;
113 void quicksearch_set_recursive(QuickSearch *quicksearch, gboolean recursive)
115 quicksearch->request->recursive = recursive;
118 static void quicksearch_set_type(QuickSearch *quicksearch, gint type)
120 #if !GTK_CHECK_VERSION(3, 0, 0)
122 quicksearch->request->type = type;
123 if (quicksearch->gui == FALSE)
125 index = menu_find_option_menu_index(GTK_CMOPTION_MENU(quicksearch->search_type_opt),
126 GINT_TO_POINTER(type),
128 gtk_cmoption_menu_set_history(GTK_CMOPTION_MENU(quicksearch->search_type_opt), index);
132 static gchar *quicksearch_get_text(QuickSearch * quicksearch)
134 gchar *search_string = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((quicksearch->search_string_entry)))), 0, -1);
136 g_strstrip(search_string);
137 return search_string;
140 static void quicksearch_set_popdown_strings(QuickSearch *quicksearch)
142 GtkWidget *search_string_entry = quicksearch->search_string_entry;
144 combobox_unset_popdown_strings(GTK_COMBO_BOX(search_string_entry));
146 if (prefs_common.summary_quicksearch_type == QUICK_SEARCH_EXTENDED)
147 combobox_set_popdown_strings(GTK_COMBO_BOX(search_string_entry),
148 quicksearch->extended_search_strings);
150 combobox_set_popdown_strings(GTK_COMBO_BOX(search_string_entry),
151 quicksearch->normal_search_strings);
154 static void prepare_matcher(QuickSearch *quicksearch)
156 /* param search_string is "matchstring" */
157 const gchar *search_string;
158 QuickSearchType quicksearch_type;
160 if (quicksearch == NULL)
163 /* When called from the GUI, reset type and matchstring */
164 if (quicksearch_from_gui(quicksearch)) {
165 gchar *s = quicksearch_get_text(quicksearch);
166 quicksearch_set_matchstring(quicksearch, s);
168 quicksearch->request->type = prefs_common.summary_quicksearch_type;
170 quicksearch_type = quicksearch->request->type;
171 search_string = quicksearch->request->matchstring;
173 if (search_string == NULL || search_string[0] == '\0') {
174 quicksearch_set_active(quicksearch, FALSE);
177 if (quicksearch->matcher_list != NULL) {
178 if (quicksearch->matching) {
179 quicksearch->deferred_free = TRUE;
182 quicksearch->deferred_free = FALSE;
183 matcherlist_free(quicksearch->matcher_list);
184 quicksearch->matcher_list = NULL;
187 if (search_string == NULL || search_string[0] == '\0') {
190 if (quicksearch_type == QUICK_SEARCH_EXTENDED) {
193 newstr = expand_search_string(search_string);
194 if (newstr && newstr[0] != '\0') {
195 quicksearch->matcher_list = matcher_parser_get_cond(newstr, &quicksearch->is_fast);
198 quicksearch->matcher_list = NULL;
199 quicksearch_set_active(quicksearch, FALSE);
202 } else if (quicksearch_type == QUICK_SEARCH_TAG) {
203 char *newstr = expand_tag_search_string(search_string);
204 quicksearch->matcher_list = matcher_parser_get_cond(newstr, &quicksearch->is_fast);
206 } else if (quicksearch_type == QUICK_SEARCH_MIXED) {
207 char *newstr = expand_tag_search_string(search_string);
208 quicksearch->matcher_list = matcher_parser_get_cond(newstr, &quicksearch->is_fast);
210 g_free(quicksearch->search_string);
211 quicksearch->search_string = g_utf8_casefold(search_string, -1);
213 quicksearch->is_fast = TRUE;
214 g_free(quicksearch->search_string);
215 quicksearch->search_string = g_utf8_casefold(search_string, -1);
217 quicksearch_set_active(quicksearch, TRUE);
220 static void update_extended_buttons (QuickSearch *quicksearch)
222 GtkWidget *expr_btn = quicksearch->search_condition_expression;
223 GtkWidget *ext_btn = quicksearch->search_description;
225 cm_return_if_fail(expr_btn != NULL);
226 cm_return_if_fail(ext_btn != NULL);
228 if (prefs_common.summary_quicksearch_type == QUICK_SEARCH_EXTENDED) {
229 gtk_widget_show(expr_btn);
230 gtk_widget_show(ext_btn);
232 gtk_widget_hide(expr_btn);
233 gtk_widget_hide(ext_btn);
237 static gboolean searchbar_focus_evt_in(GtkWidget *widget, GdkEventFocus *event,
240 qs->has_focus = TRUE;
244 static gboolean searchbar_focus_evt_out(GtkWidget *widget, GdkEventFocus *event,
247 qs->has_focus = FALSE;
248 qs->in_typing = FALSE;
252 gboolean quicksearch_has_focus(QuickSearch *quicksearch)
254 return quicksearch->has_focus;
257 static void searchbar_run(QuickSearch *quicksearch, gboolean run_only_if_fast)
259 gchar *search_string = quicksearch_get_text(quicksearch);
260 quicksearch_set_matchstring(quicksearch, search_string);
261 prepare_matcher(quicksearch);
263 /* add to history, for extended search add only correct matching rules */
264 if (!quicksearch->in_typing && search_string && strlen(search_string) != 0) {
265 switch (prefs_common.summary_quicksearch_type) {
266 case QUICK_SEARCH_EXTENDED:
267 if (quicksearch->matcher_list) {
268 quicksearch->extended_search_strings =
269 add_history(quicksearch->extended_search_strings,
270 g_strdup(search_string));
271 prefs_common.summary_quicksearch_history =
272 add_history(prefs_common.summary_quicksearch_history,
273 g_strdup(search_string));
277 quicksearch->normal_search_strings =
278 add_history(quicksearch->normal_search_strings,
279 g_strdup(search_string));
280 prefs_common.summary_quicksearch_history =
281 add_history(prefs_common.summary_quicksearch_history,
282 g_strdup(search_string));
286 quicksearch_set_popdown_strings(quicksearch);
290 if (run_only_if_fast && !quicksearch->is_fast) {
291 g_free(search_string);
294 if (quicksearch->matcher_list == NULL &&
295 prefs_common.summary_quicksearch_type == QUICK_SEARCH_EXTENDED &&
296 search_string && strlen(search_string) != 0) {
297 g_free(search_string);
300 quicksearch_set_running(quicksearch, TRUE);
301 if (quicksearch->callback != NULL)
302 quicksearch->callback(quicksearch, quicksearch->callback_data);
303 quicksearch_set_running(quicksearch, FALSE);
304 g_free(search_string);
307 static int searchbar_changed_timeout(void *data)
309 QuickSearch *qs = (QuickSearch *)data;
310 if (qs && prefs_common.summary_quicksearch_dynamic) {
311 qs->in_typing = TRUE;
312 searchbar_run(qs, TRUE);
317 static void searchbar_changed_cb(GtkWidget *widget, QuickSearch *qs)
319 if (!qs->has_focus && prefs_common.summary_quicksearch_autorun) {
320 gtk_widget_grab_focus(qs->search_string_entry);
321 searchbar_run(qs, TRUE);
325 if (prefs_common.summary_quicksearch_dynamic) {
326 if (qs->press_timeout_id != -1) {
327 g_source_remove(qs->press_timeout_id);
329 qs->press_timeout_id = g_timeout_add(500,
330 searchbar_changed_timeout, qs);
334 gtk_widget_grab_focus(qs->search_string_entry);
337 static gboolean searchbar_pressed(GtkWidget *widget, GdkEventKey *event,
338 QuickSearch *quicksearch)
340 if (event && (event->keyval == GDK_KEY_Escape)) {
343 quicksearch->in_typing = FALSE;
345 str = quicksearch_get_text(quicksearch);
346 cm_return_val_if_fail(str != NULL, TRUE);
348 /* If the string entry is empty -> hide quicksearch bar. If not -> empty it */
350 summaryview_activate_quicksearch(
351 mainwindow_get_mainwindow()->summaryview,
354 quicksearch_set(quicksearch, prefs_common.summary_quicksearch_type, "");
355 gtk_widget_grab_focus(
356 mainwindow_get_mainwindow()->summaryview->ctree);
362 if (event != NULL && (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter)) {
363 if (quicksearch->press_timeout_id != -1) {
364 g_source_remove(quicksearch->press_timeout_id);
365 quicksearch->press_timeout_id = -1;
367 quicksearch->in_typing = FALSE;
368 /* add expression to history list and exec quicksearch */
369 searchbar_run(quicksearch, FALSE);
371 g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
375 if (event && (event->keyval == GDK_KEY_Down || event->keyval == GDK_KEY_Up)) {
376 combobox_set_value_from_arrow_key(
377 GTK_COMBO_BOX(quicksearch->search_string_entry),
385 static gboolean searchtype_changed(GtkMenuItem *widget, gpointer data)
387 QuickSearch *quicksearch = (QuickSearch *)data;
388 gchar *search_string = quicksearch_get_text(quicksearch);
389 quicksearch_set_matchstring(quicksearch, search_string);
391 prefs_common.summary_quicksearch_type = GPOINTER_TO_INT(g_object_get_data(
392 G_OBJECT(GTK_MENU_ITEM(gtk_menu_get_active(
393 GTK_MENU(quicksearch->search_type)))), MENU_VAL_ID));
395 /* Show extended search description button, only when Extended is selected */
396 update_extended_buttons(quicksearch);
397 quicksearch_set_popdown_strings(quicksearch);
399 if (!search_string || *(search_string) == 0) {
400 g_free(search_string);
404 prepare_matcher(quicksearch);
406 quicksearch_set_running(quicksearch, TRUE);
407 if (quicksearch->callback != NULL)
408 quicksearch->callback(quicksearch, quicksearch->callback_data);
409 quicksearch_set_running(quicksearch, FALSE);
410 g_free(search_string);
414 static gboolean searchtype_recursive_changed(GtkMenuItem *widget, gpointer data)
416 QuickSearch *quicksearch = (QuickSearch *)data;
417 gboolean checked = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
418 gchar *search_string = quicksearch_get_text(quicksearch);
419 /* not needed to quicksearch_set_matchstring(search_string);
420 wait for prepare_matcher() */
422 prefs_common.summary_quicksearch_recurse = checked;
423 quicksearch_set_recursive(quicksearch, checked);
425 /* reselect the search type */
426 quicksearch_set_type(quicksearch, prefs_common.summary_quicksearch_type);
428 if (!search_string || *(search_string) == 0) {
429 g_free(search_string);
433 prepare_matcher(quicksearch);
435 quicksearch_set_running(quicksearch, TRUE);
436 if (quicksearch->callback != NULL)
437 quicksearch->callback(quicksearch, quicksearch->callback_data);
438 quicksearch_set_running(quicksearch, FALSE);
439 g_free(search_string);
443 static gboolean searchtype_sticky_changed(GtkMenuItem *widget, gpointer data)
445 QuickSearch *quicksearch = (QuickSearch *)data;
446 gboolean checked = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
448 prefs_common.summary_quicksearch_sticky = checked;
450 /* reselect the search type */
451 quicksearch_set_type(quicksearch, prefs_common.summary_quicksearch_type);
456 static gboolean searchtype_dynamic_changed(GtkMenuItem *widget, gpointer data)
458 QuickSearch *quicksearch = (QuickSearch *)data;
459 gboolean checked = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
461 prefs_common.summary_quicksearch_dynamic = checked;
463 gtk_check_menu_item_set_active(
464 GTK_CHECK_MENU_ITEM(quicksearch->autorun_menuitem),
467 /* reselect the search type */
468 quicksearch_set_type(quicksearch, prefs_common.summary_quicksearch_type);
473 static gboolean searchtype_autorun_changed(GtkMenuItem *widget, gpointer data)
475 QuickSearch *quicksearch = (QuickSearch *)data;
476 gboolean checked = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
478 prefs_common.summary_quicksearch_autorun = checked;
480 gtk_check_menu_item_set_active(
481 GTK_CHECK_MENU_ITEM(quicksearch->dynamic_menuitem),
484 /* reselect the search type */
485 quicksearch_set_type(quicksearch, prefs_common.summary_quicksearch_type);
491 * Strings describing how to use Extended Search
493 * When adding new lines, remember to put 2 strings for each line
495 static gchar *search_descr_strings[] = {
496 "a", N_("all messages"),
497 "ag #", N_("messages whose age is greater than #"),
498 "al #", N_("messages whose age is less than #"),
499 "b S", N_("messages which contain S in the message body"),
500 "B S", N_("messages which contain S in the whole message"),
501 "c S", N_("messages carbon-copied to S"),
502 "C S", N_("message is either to: or cc: to S"),
503 "D", N_("deleted messages"), /** how I can filter deleted messages **/
504 "e S", N_("messages which contain S in the Sender field"),
505 "E S", N_("true if execute \"S\" succeeds"),
506 "f S", N_("messages originating from user S"),
507 "F", N_("forwarded messages"),
508 "h S", N_("messages which contain header S"),
509 "i S", N_("messages which contain S in Message-ID header"),
510 "I S", N_("messages which contain S in In-Reply-To header"),
511 "k #", N_("messages which are marked with color #"),
512 "L", N_("locked messages"),
513 "n S", N_("messages which are in newsgroup S"),
514 "N", N_("new messages"),
515 "O", N_("old messages"),
516 "p", N_("incomplete messages (not entirely downloaded)"),
517 "r", N_("messages which have been replied to"),
518 "R", N_("read messages"),
519 "s S", N_("messages which contain S in subject"),
520 "se #", N_("messages whose score is equal to #"),
521 "sg #", N_("messages whose score is greater than #"),
522 "sl #", N_("messages whose score is lower than #"),
523 "Se #", N_("messages whose size is equal to #"),
524 "Sg #", N_("messages whose size is greater than #"),
525 "Ss #", N_("messages whose size is smaller than #"),
526 "t S", N_("messages which have been sent to S"),
527 "tg S", N_("messages which tags contain S"),
528 "tagged",N_("messages which have tag(s)"),
529 "T", N_("marked messages"),
530 "U", N_("unread messages"),
531 "x S", N_("messages which contain S in References header"),
532 "X \"cmd args\"", N_("messages returning 0 when passed to command - %F is message file"),
533 "y S", N_("messages which contain S in X-Label header"),
535 "&", N_("logical AND operator"),
536 "|", N_("logical OR operator"),
537 "! or ~", N_("logical NOT operator"),
538 "%", N_("case sensitive search"),
540 " ", N_("all filtering expressions are allowed"),
544 static DescriptionWindow search_descr = {
548 N_("Extended Search"),
549 N_("Extended Search allows the user to define criteria that messages must "
550 "have in order to match and be displayed in the message list.\n"
551 "The following symbols can be used:"),
555 static void search_description_cb(GtkWidget *widget)
557 description_window_create(&search_descr);
560 static gboolean clear_search_cb(GtkMenuItem *widget, gpointer data)
562 QuickSearch *quicksearch = (QuickSearch *)data;
564 if (!quicksearch->active)
567 quicksearch_set(quicksearch, prefs_common.summary_quicksearch_type, "");
572 static void search_condition_expr_done(MatcherList * matchers)
577 mainwindow_get_mainwindow()->summaryview->quicksearch != NULL);
579 if (matchers == NULL)
582 str = matcherlist_to_string(matchers);
585 quicksearch_set(mainwindow_get_mainwindow()->summaryview->quicksearch,
586 prefs_common.summary_quicksearch_type, str);
589 /* add expression to history list and exec quicksearch */
590 searchbar_run(mainwindow_get_mainwindow()->summaryview->quicksearch, FALSE);
594 static gboolean search_condition_expr(GtkMenuItem *widget, gpointer data)
597 MatcherList * matchers = NULL;
599 cm_return_val_if_fail(
600 mainwindow_get_mainwindow()->summaryview->quicksearch != NULL,
603 /* re-use the current quicksearch value if it's a condition expression,
604 otherwise ignore it silently */
605 cond_str = quicksearch_get_text(mainwindow_get_mainwindow()->summaryview->quicksearch);
607 if (*cond_str != '\0') {
608 matchers = matcher_parser_get_cond((gchar*)cond_str, NULL);
611 prefs_matcher_open(matchers, search_condition_expr_done);
613 if (matchers != NULL)
614 matcherlist_free(matchers);
621 static void quicksearch_set_button(GtkButton *button, const gchar *icon, const gchar *text)
623 GList *children = gtk_container_get_children(GTK_CONTAINER(button));
626 gboolean icon_visible;
628 g_object_get(gtk_settings_get_default(),
629 "gtk-button-images", &icon_visible,
632 for (cur = children; cur; cur = cur->next)
633 gtk_container_remove(GTK_CONTAINER(button), GTK_WIDGET(cur->data));
635 g_list_free(children);
636 box = gtk_hbox_new(FALSE, 0);
638 gtk_container_add(GTK_CONTAINER(button), box);
639 if (icon_visible || !text || !*text)
640 gtk_box_pack_start(GTK_BOX(box), gtk_image_new_from_stock(icon,
641 GTK_ICON_SIZE_BUTTON), FALSE, FALSE, 0);
642 gtk_box_pack_start(GTK_BOX(box), gtk_label_new_with_mnemonic(text), FALSE, FALSE, 0);
643 gtk_widget_show_all(box);
647 * Builds a new QuickSearchRequest
649 static QuickSearchRequest *quicksearchrequest_new(void)
651 QuickSearchRequest *request;
652 request = g_new0(QuickSearchRequest, 1);
657 * Builds a new QuickSearch object independent from the GUI
659 QuickSearch *quicksearch_new_nogui(void)
661 QuickSearch *quicksearch;
662 QuickSearchRequest *request;
664 request = quicksearchrequest_new();
665 quicksearch = g_new0(QuickSearch, 1);
666 quicksearch->request = request;
667 quicksearch->gui = FALSE;
669 /* init. values initally found in quicksearch_new().
670 There's no need to init. all pointers to NULL since we use g_new0
672 quicksearch->matcher_list = NULL;
673 quicksearch->active = FALSE;
674 quicksearch->running = FALSE;
675 quicksearch->in_typing = FALSE;
676 quicksearch->press_timeout_id = -1;
677 quicksearch->normal_search_strings = NULL;
678 quicksearch->extended_search_strings = NULL;
683 QuickSearch *quicksearch_new()
685 QuickSearch *quicksearch;
687 GtkWidget *hbox_search;
688 #if !GTK_CHECK_VERSION(3, 0, 0)
689 GtkWidget *search_type_opt;
691 GtkWidget *search_type;
692 GtkWidget *search_string_entry;
693 GtkWidget *search_hbox;
694 GtkWidget *search_description;
695 GtkWidget *clear_search;
696 GtkWidget *search_condition_expression;
700 quicksearch = quicksearch_new_nogui();
701 quicksearch->gui = TRUE;
704 hbox_search = gtk_hbox_new(FALSE, 0);
706 #if !GTK_CHECK_VERSION(3, 0, 0)
707 search_type_opt = gtk_cmoption_menu_new();
708 gtk_widget_show(search_type_opt);
709 gtk_box_pack_start(GTK_BOX(hbox_search), search_type_opt, FALSE, FALSE, 0);
712 search_type = gtk_menu_new();
713 MENUITEM_ADD (search_type, menuitem,
714 prefs_common_translated_header_name("Subject"), QUICK_SEARCH_SUBJECT);
715 g_signal_connect(G_OBJECT(menuitem), "activate",
716 G_CALLBACK(searchtype_changed),
718 MENUITEM_ADD (search_type, menuitem,
719 prefs_common_translated_header_name("From"), QUICK_SEARCH_FROM);
720 g_signal_connect(G_OBJECT(menuitem), "activate",
721 G_CALLBACK(searchtype_changed),
723 MENUITEM_ADD (search_type, menuitem,
724 prefs_common_translated_header_name("To"), QUICK_SEARCH_TO);
725 g_signal_connect(G_OBJECT(menuitem), "activate",
726 G_CALLBACK(searchtype_changed),
728 MENUITEM_ADD (search_type, menuitem,
729 prefs_common_translated_header_name("Tag"), QUICK_SEARCH_TAG);
730 g_signal_connect(G_OBJECT(menuitem), "activate",
731 G_CALLBACK(searchtype_changed),
733 MENUITEM_ADD (search_type, menuitem,
734 _("From/To/Subject/Tag"), QUICK_SEARCH_MIXED);
735 g_signal_connect(G_OBJECT(menuitem), "activate",
736 G_CALLBACK(searchtype_changed),
738 MENUITEM_ADD (search_type, menuitem, _("Extended"), QUICK_SEARCH_EXTENDED);
739 g_signal_connect(G_OBJECT(menuitem), "activate",
740 G_CALLBACK(searchtype_changed),
743 gtk_menu_shell_append(GTK_MENU_SHELL(search_type), gtk_separator_menu_item_new());
745 menuitem = gtk_check_menu_item_new_with_label(_("Recursive"));
746 gtk_menu_shell_append(GTK_MENU_SHELL(search_type), menuitem);
748 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
749 prefs_common.summary_quicksearch_recurse);
750 quicksearch_set_recursive(quicksearch, prefs_common.summary_quicksearch_recurse);
751 g_signal_connect(G_OBJECT(menuitem), "activate",
752 G_CALLBACK(searchtype_recursive_changed),
755 menuitem = gtk_check_menu_item_new_with_label(_("Sticky"));
756 gtk_menu_shell_append(GTK_MENU_SHELL(search_type), menuitem);
758 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
759 prefs_common.summary_quicksearch_sticky);
761 g_signal_connect(G_OBJECT(menuitem), "activate",
762 G_CALLBACK(searchtype_sticky_changed),
765 menuitem = gtk_check_menu_item_new_with_label(_("Type-ahead"));
766 gtk_menu_shell_append(GTK_MENU_SHELL(search_type), menuitem);
768 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
769 prefs_common.summary_quicksearch_dynamic);
771 quicksearch->dynamic_menuitem = menuitem;
773 g_signal_connect(G_OBJECT(menuitem), "activate",
774 G_CALLBACK(searchtype_dynamic_changed),
777 menuitem = gtk_check_menu_item_new_with_label(_("Run on select"));
778 gtk_menu_shell_append(GTK_MENU_SHELL(search_type), menuitem);
780 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
781 prefs_common.summary_quicksearch_autorun);
783 quicksearch->autorun_menuitem = menuitem;
785 g_signal_connect(G_OBJECT(menuitem), "activate",
786 G_CALLBACK(searchtype_autorun_changed),
789 #if !GTK_CHECK_VERSION(3, 0, 0)
790 gtk_cmoption_menu_set_menu(GTK_CMOPTION_MENU(search_type_opt), search_type);
792 quicksearch->search_type_opt = search_type_opt;
794 quicksearch_set_type(quicksearch, prefs_common.summary_quicksearch_type);
796 gtk_widget_show(search_type);
798 search_string_entry = gtk_combo_box_entry_new_text ();
799 gtk_combo_box_set_active(GTK_COMBO_BOX(search_string_entry), -1);
801 vbox = gtk_vbox_new(TRUE, 0);
802 gtk_box_pack_start(GTK_BOX(vbox), search_string_entry, FALSE, FALSE, 0);
803 gtk_box_pack_start(GTK_BOX(hbox_search), vbox, TRUE, TRUE, 4);
805 gtk_widget_show(vbox);
806 gtk_widget_show(search_string_entry);
808 search_hbox = gtk_hbox_new(FALSE, 5);
809 clear_search = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
810 gtk_box_pack_start(GTK_BOX(search_hbox), clear_search,
812 g_signal_connect(G_OBJECT(clear_search), "clicked",
813 G_CALLBACK(clear_search_cb), quicksearch);
814 CLAWS_SET_TIP(clear_search,
815 _("Clear the current search"));
816 gtk_widget_show(clear_search);
818 search_condition_expression = gtk_button_new_from_stock(GTK_STOCK_EDIT);
819 gtk_box_pack_start(GTK_BOX(search_hbox), search_condition_expression,
821 g_signal_connect(G_OBJECT (search_condition_expression), "clicked",
822 G_CALLBACK(search_condition_expr),
824 CLAWS_SET_TIP(search_condition_expression,
825 _("Edit search criteria"));
826 gtk_widget_show(search_condition_expression);
828 search_description = gtk_button_new_from_stock(GTK_STOCK_INFO);
829 gtk_box_pack_start(GTK_BOX(search_hbox), search_description,
831 g_signal_connect(G_OBJECT(search_description), "clicked",
832 G_CALLBACK(search_description_cb), NULL);
833 CLAWS_SET_TIP(search_description,
834 _("Information about extended symbols"));
835 gtk_widget_show(search_description);
837 gtk_box_pack_start(GTK_BOX(hbox_search), search_hbox, FALSE, FALSE, 2);
838 gtk_widget_show(search_hbox);
840 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((search_string_entry)))),
842 G_CALLBACK(searchbar_pressed),
845 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((search_string_entry)))),
847 G_CALLBACK(searchbar_changed_cb),
850 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((search_string_entry)))),
852 G_CALLBACK(searchbar_focus_evt_in),
854 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((search_string_entry)))),
856 G_CALLBACK(searchbar_focus_evt_out),
859 quicksearch->hbox_search = hbox_search;
860 quicksearch->search_type = search_type;
861 quicksearch->search_string_entry = search_string_entry;
862 quicksearch->search_condition_expression = search_condition_expression;
863 quicksearch->search_description = search_description;
864 quicksearch->matcher_list = NULL;
865 quicksearch->active = FALSE;
866 quicksearch->running = FALSE;
867 quicksearch->clear_search = clear_search;
868 quicksearch->in_typing = FALSE;
869 quicksearch->press_timeout_id = -1;
870 quicksearch->normal_search_strings = NULL;
871 quicksearch->extended_search_strings = NULL;
873 quicksearch_set_button(GTK_BUTTON(quicksearch->search_description), GTK_STOCK_INFO, _("_Information"));
874 quicksearch_set_button(GTK_BUTTON(quicksearch->search_condition_expression), GTK_STOCK_EDIT, _("_Edit"));
875 quicksearch_set_button(GTK_BUTTON(quicksearch->clear_search), GTK_STOCK_CLEAR, _("C_lear"));
877 update_extended_buttons(quicksearch);
882 void quicksearch_relayout(QuickSearch *quicksearch)
884 switch (prefs_common.layout_mode) {
887 case WIDE_MSGLIST_LAYOUT:
888 quicksearch_set_button(GTK_BUTTON(quicksearch->search_description), GTK_STOCK_INFO, _("_Information"));
889 quicksearch_set_button(GTK_BUTTON(quicksearch->search_condition_expression), GTK_STOCK_EDIT, _("_Edit"));
890 quicksearch_set_button(GTK_BUTTON(quicksearch->clear_search), GTK_STOCK_CLEAR, _("C_lear"));
893 case VERTICAL_LAYOUT:
894 quicksearch_set_button(GTK_BUTTON(quicksearch->search_description), GTK_STOCK_INFO, "");
895 quicksearch_set_button(GTK_BUTTON(quicksearch->search_condition_expression), GTK_STOCK_EDIT, "");
896 quicksearch_set_button(GTK_BUTTON(quicksearch->clear_search), GTK_STOCK_CLEAR, "");
901 GtkWidget *quicksearch_get_widget(QuickSearch *quicksearch)
903 return quicksearch->hbox_search;
906 void quicksearch_show(QuickSearch *quicksearch)
908 MainWindow *mainwin = mainwindow_get_mainwindow();
909 GtkWidget *ctree = NULL;
910 prepare_matcher(quicksearch);
911 gtk_widget_show(quicksearch->hbox_search);
912 update_extended_buttons(quicksearch);
913 gtk_widget_grab_focus(
914 GTK_WIDGET(gtk_bin_get_child(GTK_BIN((quicksearch->search_string_entry)))));
918 if (!mainwin || !mainwin->summaryview) {
922 ctree = summary_get_main_widget(mainwin->summaryview);
924 if (ctree && mainwin->summaryview->selected)
925 gtk_cmctree_node_moveto(GTK_CMCTREE(ctree),
926 mainwin->summaryview->selected,
930 void quicksearch_hide(QuickSearch *quicksearch)
932 if (quicksearch_is_active(quicksearch)) {
933 quicksearch_set(quicksearch, prefs_common.summary_quicksearch_type, "");
934 quicksearch_set_active(quicksearch, FALSE);
936 gtk_widget_hide(quicksearch->hbox_search);
940 *\brief Sets the matchstring.
942 *\param quicksearch quicksearch to set
943 *\param matchstring the match string; it is duplicated, not stored
945 static void quicksearch_set_matchstring(QuickSearch *quicksearch,
946 const gchar *matchstring)
948 g_free(quicksearch->request->matchstring);
949 quicksearch->request->matchstring = g_strdup(matchstring);
952 void quicksearch_set(QuickSearch *quicksearch, QuickSearchType type,
953 const gchar *matchstring)
955 quicksearch_set_type(quicksearch, type);
957 if (!matchstring || !(*matchstring))
958 quicksearch->in_typing = FALSE;
960 quicksearch_set_matchstring(quicksearch, matchstring);
962 if (!quicksearch_from_gui(quicksearch)) {
963 prepare_matcher(quicksearch);
968 g_signal_handlers_block_by_func(G_OBJECT(gtk_bin_get_child(GTK_BIN((quicksearch->search_string_entry)))),
969 G_CALLBACK(searchbar_changed_cb), quicksearch);
970 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((quicksearch->search_string_entry)))),
972 g_signal_handlers_unblock_by_func(G_OBJECT(gtk_bin_get_child(GTK_BIN((quicksearch->search_string_entry)))),
973 G_CALLBACK(searchbar_changed_cb), quicksearch);
975 prefs_common.summary_quicksearch_type = type;
977 prepare_matcher(quicksearch);
979 quicksearch_set_running(quicksearch, TRUE);
980 if (quicksearch->callback != NULL)
981 quicksearch->callback(quicksearch, quicksearch->callback_data);
982 quicksearch_set_running(quicksearch, FALSE);
985 gboolean quicksearch_is_active(QuickSearch *quicksearch)
987 return quicksearch->active &&
988 (prefs_common.summary_quicksearch_type != QUICK_SEARCH_EXTENDED
989 || quicksearch->matcher_list != NULL);
992 static void quicksearch_set_active(QuickSearch *quicksearch, gboolean active)
994 #if !GTK_CHECK_VERSION(3, 0, 0)
995 static GdkColor yellow;
997 static GdkColor black;
998 static gboolean colors_initialised = FALSE;
1000 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
1001 static GdkColor red = { (guint32)0, (guint16)0xff, (guint16)0x70, (guint16)0x70 };
1002 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
1004 gboolean error = FALSE;
1007 quicksearch->active = active;
1008 if (quicksearch->gui == FALSE)
1011 #if !GTK_CHECK_VERSION(3, 0, 0)
1012 if (!colors_initialised) {
1013 gdk_color_parse("#f5f6be", &yellow);
1014 gdk_color_parse("#000000", &black);
1015 gdk_color_parse("#ff7070", &red);
1016 colors_initialised = gdk_colormap_alloc_color(
1017 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
1018 colors_initialised &= gdk_colormap_alloc_color(
1019 gdk_colormap_get_system(), &black, FALSE, TRUE);
1020 colors_initialised &= gdk_colormap_alloc_color(
1021 gdk_colormap_get_system(), &red, FALSE, TRUE);
1026 (prefs_common.summary_quicksearch_type == QUICK_SEARCH_EXTENDED
1027 && quicksearch->matcher_list == NULL))
1031 gtk_widget_set_sensitive(quicksearch->clear_search, TRUE);
1032 #if !GTK_CHECK_VERSION(3, 0, 0)
1033 if (colors_initialised) {
1035 gtk_widget_modify_base(
1036 gtk_bin_get_child(GTK_BIN((quicksearch->search_string_entry))),
1037 GTK_STATE_NORMAL, error ? &red : &yellow);
1038 gtk_widget_modify_text(
1039 gtk_bin_get_child(GTK_BIN((quicksearch->search_string_entry))),
1040 GTK_STATE_NORMAL, &black);
1041 #if !GTK_CHECK_VERSION(3, 0, 0)
1045 gtk_widget_set_sensitive(quicksearch->clear_search, FALSE);
1046 if (colors_initialised) {
1047 gtk_widget_modify_base(
1048 gtk_bin_get_child(GTK_BIN((quicksearch->search_string_entry))),
1049 GTK_STATE_NORMAL, NULL);
1050 gtk_widget_modify_text(
1051 gtk_bin_get_child(GTK_BIN((quicksearch->search_string_entry))),
1052 GTK_STATE_NORMAL, NULL);
1053 #if !GTK_CHECK_VERSION(3, 0, 0)
1059 quicksearch_reset_cur_folder_item(quicksearch);
1063 void quicksearch_set_execute_callback(QuickSearch *quicksearch,
1064 QuickSearchExecuteCallback callback,
1067 quicksearch->callback = callback;
1068 quicksearch->callback_data = data;
1071 gboolean quicksearch_match(QuickSearch *quicksearch, MsgInfo *msginfo)
1073 gchar *searched_header = NULL;
1074 gboolean result = FALSE;
1075 gchar *to = NULL, *from = NULL, *subject = NULL;
1076 QuickSearchType quicksearch_type;
1078 if (!quicksearch->active)
1081 quicksearch_type = quicksearch->request->type;
1083 switch (quicksearch_type) {
1084 case QUICK_SEARCH_SUBJECT:
1085 if (msginfo->subject)
1086 searched_header = g_utf8_casefold(msginfo->subject, -1);
1090 case QUICK_SEARCH_FROM:
1092 searched_header = g_utf8_casefold(msginfo->from, -1);
1096 case QUICK_SEARCH_TO:
1098 searched_header = g_utf8_casefold(msginfo->to, -1);
1102 case QUICK_SEARCH_MIXED:
1104 to = g_utf8_casefold(msginfo->to, -1);
1106 from = g_utf8_casefold(msginfo->from, -1);
1107 if (msginfo->subject)
1108 subject = g_utf8_casefold(msginfo->subject, -1);
1110 case QUICK_SEARCH_EXTENDED:
1113 debug_print("unknown search type (%d)\n", quicksearch_type);
1117 quicksearch->matching = TRUE;
1118 if (quicksearch_type != QUICK_SEARCH_EXTENDED &&
1119 quicksearch_type != QUICK_SEARCH_MIXED &&
1120 quicksearch_type != QUICK_SEARCH_TAG &&
1121 quicksearch->search_string &&
1122 searched_header && strstr(searched_header, quicksearch->search_string) != NULL)
1124 else if (quicksearch_type == QUICK_SEARCH_MIXED &&
1125 quicksearch->search_string && (
1126 (to && strstr(to, quicksearch->search_string) != NULL) ||
1127 (from && strstr(from, quicksearch->search_string) != NULL) ||
1128 (subject && strstr(subject, quicksearch->search_string) != NULL) ||
1129 ((quicksearch->matcher_list != NULL) &&
1130 matcherlist_match(quicksearch->matcher_list, msginfo)) ))
1132 else if ((quicksearch->matcher_list != NULL) &&
1133 matcherlist_match(quicksearch->matcher_list, msginfo))
1136 quicksearch->matching = FALSE;
1137 if (quicksearch_from_gui(quicksearch)==TRUE && quicksearch->deferred_free) {
1138 /* Ref. http://lists.claws-mail.org/pipermail/users/2010-August/003063.html
1139 See also 2.0.0cvs140 ChangeLog entry
1140 and comment in search_msgs_in_folder() */
1141 prepare_matcher(quicksearch);
1147 g_free(searched_header);
1152 /* allow Mutt-like patterns in quick search */
1153 static gchar *expand_search_string(const gchar *search_string)
1156 gchar term_char, save_char;
1157 gchar *cmd_start, *cmd_end;
1158 GString *matcherstr;
1159 gchar *returnstr = NULL;
1161 gboolean casesens, dontmatch;
1162 /* list of allowed pattern abbreviations */
1164 gchar *abbreviated; /* abbreviation */
1165 gchar *command; /* actual matcher command */
1166 gint numparams; /* number of params for cmd */
1167 gboolean qualifier; /* do we append regexpcase */
1168 gboolean quotes; /* do we need quotes */
1171 { "a", "all", 0, FALSE, FALSE },
1172 { "ag", "age_greater", 1, FALSE, FALSE },
1173 { "al", "age_lower", 1, FALSE, FALSE },
1174 { "b", "body_part", 1, TRUE, TRUE },
1175 { "B", "message", 1, TRUE, TRUE },
1176 { "c", "cc", 1, TRUE, TRUE },
1177 { "C", "to_or_cc", 1, TRUE, TRUE },
1178 { "D", "deleted", 0, FALSE, FALSE },
1179 { "e", "header \"Sender\"", 1, TRUE, TRUE },
1180 { "E", "execute", 1, FALSE, TRUE },
1181 { "f", "from", 1, TRUE, TRUE },
1182 { "F", "forwarded", 0, FALSE, FALSE },
1183 { "h", "headers_part", 1, TRUE, TRUE },
1184 { "i", "header \"Message-ID\"", 1, TRUE, TRUE },
1185 { "I", "inreplyto", 1, TRUE, TRUE },
1186 { "k", "colorlabel", 1, FALSE, FALSE },
1187 { "L", "locked", 0, FALSE, FALSE },
1188 { "n", "newsgroups", 1, TRUE, TRUE },
1189 { "N", "new", 0, FALSE, FALSE },
1190 { "O", "~new", 0, FALSE, FALSE },
1191 { "r", "replied", 0, FALSE, FALSE },
1192 { "R", "~unread", 0, FALSE, FALSE },
1193 { "s", "subject", 1, TRUE, TRUE },
1194 { "se", "score_equal", 1, FALSE, FALSE },
1195 { "sg", "score_greater", 1, FALSE, FALSE },
1196 { "sl", "score_lower", 1, FALSE, FALSE },
1197 { "Se", "size_equal", 1, FALSE, FALSE },
1198 { "Sg", "size_greater", 1, FALSE, FALSE },
1199 { "Ss", "size_smaller", 1, FALSE, FALSE },
1200 { "t", "to", 1, TRUE, TRUE },
1201 { "tg", "tag", 1, TRUE, TRUE },
1202 { "T", "marked", 0, FALSE, FALSE },
1203 { "U", "unread", 0, FALSE, FALSE },
1204 { "x", "header \"References\"", 1, TRUE, TRUE },
1205 { "X", "test", 1, FALSE, FALSE },
1206 { "y", "header \"X-Label\"", 1, TRUE, TRUE },
1207 { "&", "&", 0, FALSE, FALSE },
1208 { "|", "|", 0, FALSE, FALSE },
1209 { "p", "partial", 0, FALSE, FALSE },
1210 { NULL, NULL, 0, FALSE, FALSE }
1213 if (search_string == NULL)
1216 copy_str = g_strdup(search_string);
1218 matcherstr = g_string_sized_new(16);
1219 cmd_start = copy_str;
1220 while (cmd_start && *cmd_start) {
1221 /* skip all white spaces */
1222 while (*cmd_start && isspace((guchar)*cmd_start))
1224 cmd_end = cmd_start;
1226 /* extract a command */
1227 while (*cmd_end && !isspace((guchar)*cmd_end))
1230 /* save character */
1231 save_char = *cmd_end;
1237 /* ~ and ! mean logical NOT */
1238 if (*cmd_start == '~' || *cmd_start == '!')
1243 /* % means case sensitive match */
1244 if (*cmd_start == '%')
1250 /* find matching abbreviation */
1251 for (i = 0; cmds[i].command; i++) {
1252 if (!strcmp(cmd_start, cmds[i].abbreviated)) {
1253 /* restore character */
1254 *cmd_end = save_char;
1257 if (matcherstr->len > 0) {
1258 g_string_append(matcherstr, " ");
1261 g_string_append(matcherstr, "~");
1262 g_string_append(matcherstr, cmds[i].command);
1263 g_string_append(matcherstr, " ");
1265 /* stop if no params required */
1266 if (cmds[i].numparams == 0)
1269 /* extract a parameter, allow quotes */
1270 while (*cmd_end && isspace((guchar)*cmd_end))
1273 cmd_start = cmd_end;
1274 if (*cmd_start == '"') {
1281 /* extract actual parameter */
1282 while ((*cmd_end) && (*cmd_end != term_char))
1285 if (*cmd_end == '"')
1288 save_char = *cmd_end;
1291 if (cmds[i].qualifier) {
1293 g_string_append(matcherstr, "regexp ");
1295 g_string_append(matcherstr, "regexpcase ");
1298 /* do we need to add quotes ? */
1299 if (cmds[i].quotes && term_char != '"')
1300 g_string_append(matcherstr, "\"");
1302 /* copy actual parameter */
1303 g_string_append(matcherstr, cmd_start);
1305 /* do we need to add quotes ? */
1306 if (cmds[i].quotes && term_char != '"')
1307 g_string_append(matcherstr, "\"");
1309 /* restore original character */
1310 *cmd_end = save_char;
1318 cmd_start = cmd_end;
1323 /* return search string if no match is found to allow
1324 all available filtering expressions in quicksearch */
1325 if (matcherstr->len > 0) returnstr = matcherstr->str;
1326 else returnstr = g_strdup(search_string);
1327 g_string_free(matcherstr, FALSE);
1331 static gchar *expand_tag_search_string(const gchar *search_string)
1333 gchar *newstr = NULL;
1334 gchar **words = search_string ? g_strsplit(search_string, " ", -1):NULL;
1336 while (words && words[i] && *words[i]) {
1337 g_strstrip(words[i]);
1339 newstr = g_strdup_printf("tag regexpcase \"%s\"", words[i]);
1341 gint o_len = strlen(newstr);
1342 gint s_len = 18; /* strlen("|tag regexpcase \"\"") */
1343 gint n_len = s_len + strlen(words[i]);
1344 newstr = g_realloc(newstr,o_len+n_len+1);
1345 strcpy(newstr+o_len, "|tag regexpcase \"");
1346 strcpy(newstr+o_len+(s_len-1), words[i]);
1347 strcpy(newstr+o_len+(n_len-1), "\"");
1355 static void quicksearch_set_running(QuickSearch *quicksearch, gboolean run)
1357 quicksearch->running = run;
1360 gboolean quicksearch_is_running(QuickSearch *quicksearch)
1362 return quicksearch->running;
1365 void quicksearch_pass_key(QuickSearch *quicksearch, guint val, GdkModifierType mod)
1367 GtkEntry *entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN((quicksearch->search_string_entry))));
1368 glong curpos = gtk_editable_get_position(GTK_EDITABLE(entry));
1370 char *str = g_strdup(gtk_entry_get_text(entry));
1377 if (gtk_editable_get_selection_bounds(GTK_EDITABLE(entry), NULL, NULL)) {
1378 /* remove selection */
1379 gtk_editable_delete_selection(GTK_EDITABLE(entry));
1380 curpos = gtk_editable_get_position(GTK_EDITABLE(entry));
1381 /* refresh string */
1383 str = g_strdup(gtk_entry_get_text(entry));
1387 if (!(c = gdk_keyval_to_unicode(val))) {
1391 char_len = g_unichar_to_utf8(c, key);
1394 key[char_len] = '\0';
1395 if (curpos < g_utf8_strlen(str, -1)) {
1396 gchar *stop = g_utf8_offset_to_pointer(begin, curpos);
1397 end = g_strdup(g_utf8_offset_to_pointer(str, curpos));
1399 new = g_strdup_printf("%s%s%s", begin, key, end);
1400 gtk_entry_set_text(entry, new);
1403 new = g_strdup_printf("%s%s", begin, key);
1404 gtk_entry_set_text(entry, new);
1408 gtk_editable_set_position(GTK_EDITABLE(entry), curpos+1);
1412 static gboolean quicksearch_match_subfolder(QuickSearch *quicksearch,
1415 GSList *msglist = NULL;
1417 gboolean result = FALSE;
1418 gint num = 0, total = 0;
1419 gint interval = quicksearch_is_fast(quicksearch) ? 5000:100;
1421 statusbar_print_all(_("Searching in %s... \n"),
1422 src->path ? src->path : "(null)");
1424 msglist = folder_item_get_msg_list(src);
1425 total = src->total_msgs;
1426 folder_item_update_freeze();
1427 for (cur = msglist; cur != NULL; cur = cur->next) {
1428 MsgInfo *msg = (MsgInfo *)cur->data;
1429 statusbar_progress_all(num++,total, interval);
1430 if (quicksearch_match(quicksearch, msg)) {
1434 if (num % interval == 0)
1436 if (!quicksearch_is_active(quicksearch))
1439 folder_item_update_thaw();
1440 statusbar_progress_all(0,0,0);
1441 statusbar_pop_all();
1443 procmsg_msg_list_free(msglist);
1447 gboolean quicksearch_is_in_subfolder(QuickSearch *quicksearch, FolderItem *cur)
1449 if (quicksearch->root_folder_item == NULL)
1453 if (cur == quicksearch->root_folder_item) {
1456 cur = folder_item_parent(cur);
1461 void quicksearch_search_subfolders(QuickSearch *quicksearch,
1462 FolderView *folderview,
1463 FolderItem *folder_item)
1465 FolderItem *cur = NULL;
1466 GNode *node = folder_item->node->children;
1468 if (!prefs_common.summary_quicksearch_recurse
1469 || quicksearch->in_typing == TRUE)
1472 for (; node != NULL; node = node->next) {
1473 cur = FOLDER_ITEM(node->data);
1474 if (quicksearch_match_subfolder(quicksearch, cur)) {
1475 folderview_update_search_icon(cur, TRUE);
1477 folderview_update_search_icon(cur, FALSE);
1479 if (cur->node->children)
1480 quicksearch_search_subfolders(quicksearch,
1484 quicksearch->root_folder_item = folder_item;
1485 if (!quicksearch_is_active(quicksearch))
1486 quicksearch_reset_cur_folder_item(quicksearch);
1489 static void quicksearch_reset_folder_items(QuickSearch *quicksearch,
1490 FolderItem *folder_item)
1492 FolderItem *cur = NULL;
1493 GNode *node = (folder_item && folder_item->node) ?
1494 folder_item->node->children : NULL;
1496 for (; node != NULL; node = node->next) {
1497 cur = FOLDER_ITEM(node->data);
1498 folderview_update_search_icon(cur, FALSE);
1499 if (cur->node->children)
1500 quicksearch_reset_folder_items(quicksearch,
1505 void quicksearch_reset_cur_folder_item(QuickSearch *quicksearch)
1507 if (quicksearch->root_folder_item)
1508 quicksearch_reset_folder_items(quicksearch,
1509 quicksearch->root_folder_item);
1511 quicksearch->root_folder_item = NULL;
1514 gboolean quicksearch_is_in_typing(QuickSearch *quicksearch)
1516 return quicksearch->in_typing;
1519 void quicksearch_set_search_strings(QuickSearch *quicksearch)
1521 GList *strings = prefs_common.summary_quicksearch_history;
1522 gchar *newstr = NULL;
1523 MatcherList *matcher_list = NULL;
1528 matcher_parser_disable_warnings(TRUE);
1531 newstr = expand_search_string((gchar *) strings->data);
1532 if (newstr && newstr[0] != '\0') {
1533 if (!strchr(newstr, ' ')) {
1534 quicksearch->normal_search_strings =
1536 quicksearch->normal_search_strings,
1537 g_strdup(strings->data));
1542 matcher_list = matcher_parser_get_cond(newstr, FALSE);
1546 quicksearch->extended_search_strings =
1548 quicksearch->extended_search_strings,
1549 g_strdup(strings->data));
1550 matcherlist_free(matcher_list);
1552 quicksearch->normal_search_strings =
1554 quicksearch->normal_search_strings,
1555 g_strdup(strings->data));
1558 } while ((strings = g_list_next(strings)) != NULL);
1560 matcher_parser_disable_warnings(FALSE);
1562 quicksearch->normal_search_strings = g_list_reverse(quicksearch->normal_search_strings);
1563 quicksearch->extended_search_strings = g_list_reverse(quicksearch->extended_search_strings);
1565 quicksearch_set_popdown_strings(quicksearch);
1569 * Searches in the supplied folderItem the messages (MessageInfo) matching a
1570 * QuickSearchType + search string (ex.: QUICK_SEARCH_FROM and "foo@bar.com").
1572 * Found messages are appended to the array 'messages' and their ref.counts
1573 * are incremented by 1 --so they need to be released (procmsg_msginfo_free())
1574 * before the array 'messages' is freed.
1576 void search_msgs_in_folder(GSList **messages, QuickSearch* quicksearch,
1577 FolderItem* folderItem)
1579 /* from quicksearch_match_subfolder */
1580 GSList *msglist = NULL;
1583 /* The list is built w/ MsgInfo items whose ref.counts are incremented,
1584 but they are decremented when the list is freed by
1585 procmsg_msg_list_free(): we'll ask for a new ref., below
1587 msglist = folder_item_get_msg_list(folderItem);
1589 for (cur = msglist; cur != NULL; cur = cur->next) {
1590 MsgInfo *msg = (MsgInfo *)cur->data;
1591 if (quicksearch_match(quicksearch, msg)) {
1592 /*debug_print("found: %s from:%s\n",procmsg_get_message_file_path(msg),msg->from);*/
1593 *messages = g_slist_prepend(*messages, procmsg_msginfo_new_ref(msg));
1595 /* See 2.0.0cvs140 ChangeLog entry for details
1596 see also comments in quicksearch_match() */
1597 if (quicksearch_from_gui(quicksearch)==TRUE
1598 && !quicksearch_is_active(quicksearch))
1601 procmsg_msg_list_free(msglist);
1605 * Searches within the folderItem and its sub-folders (if recursive is TRUE)
1606 * the messages matching the search request.
1608 * NB: search within a Folder can be done this way:
1609 * search_msg_in_folders(messages, quicksearch, searchType,
1610 * FOLDER_ITEM(folder->node->data), TRUE);
1612 void search_msgs_in_folders(GSList **messages, QuickSearch* quicksearch,
1613 FolderItem* folderItem)
1615 FolderItem *cur = NULL;
1617 search_msgs_in_folder(messages, quicksearch, folderItem);
1618 if (quicksearch->request->recursive == FALSE)
1621 GNode *node = folderItem->node->children;
1622 for (; node != NULL; node = node->next) {
1623 cur = FOLDER_ITEM(node->data);
1624 debug_print("in: %s\n",cur->path);
1625 if (cur->node->children)
1626 search_msgs_in_folders(messages, quicksearch, cur);
1628 search_msgs_in_folder(messages, quicksearch, cur);
1630 *messages = g_slist_reverse(*messages);
1634 * Returns the QuickSearchType associated to the supplied string.
1636 QuickSearchType quicksearch_type(const gchar* type)
1638 QuickSearchType searchType = QUICK_SEARCH_EXTENDED;
1641 switch(toupper(*type)) {
1643 searchType = QUICK_SEARCH_SUBJECT;
1646 searchType = QUICK_SEARCH_FROM;
1649 searchType = QUICK_SEARCH_TO;
1652 searchType = QUICK_SEARCH_EXTENDED;
1655 searchType = QUICK_SEARCH_MIXED;
1658 searchType = QUICK_SEARCH_TAG;