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 #include "gtkcmoptionmenu.h"
36 #include "prefs_common.h"
37 #include "description_window.h"
39 #include "matcher_parser.h"
40 #include "quicksearch.h"
41 #include "folderview.h"
43 #include "prefs_matcher.h"
45 #include "statusbar.h"
47 struct _QuickSearchRequest
51 FolderItem *folderItem;
54 typedef struct _QuickSearchRequest QuickSearchRequest;
58 GtkWidget *hbox_search;
59 GtkWidget *search_type;
60 GtkWidget *search_type_opt;
61 GtkWidget *search_string_entry;
62 GtkWidget *search_condition_expression;
63 GtkWidget *search_description;
64 GtkWidget *clear_search;
68 MatcherList *matcher_list;
70 QuickSearchRequest *request;
71 QuickSearchExecuteCallback callback;
72 gpointer callback_data;
76 gboolean deferred_free;
77 FolderItem *root_folder_item;
80 guint press_timeout_id;
82 GList *normal_search_strings;
83 GList *extended_search_strings;
85 /* dynamic and autorun qs settings are exclusive*/
86 GtkWidget *dynamic_menuitem;
87 GtkWidget *autorun_menuitem;
92 static void quicksearch_set_running(QuickSearch *quicksearch, gboolean run);
93 static void quicksearch_set_matchstring(QuickSearch *quicksearch, const gchar *matchstring);
94 static void quicksearch_set_active(QuickSearch *quicksearch, gboolean active);
95 static void quicksearch_reset_folder_items(QuickSearch *quicksearch, FolderItem *folder_item);
96 static gchar *expand_search_string(const gchar *str);
97 static gchar *expand_tag_search_string(const gchar *str);
99 static gboolean quicksearch_from_gui(QuickSearch *quicksearch)
101 return quicksearch->gui;
104 gboolean quicksearch_is_fast(QuickSearch *quicksearch)
106 return quicksearch->is_fast;
109 void quicksearch_set_recursive(QuickSearch *quicksearch, gboolean recursive)
111 quicksearch->request->recursive = recursive;
114 static void quicksearch_set_type(QuickSearch *quicksearch, gint type)
117 quicksearch->request->type = type;
118 if (quicksearch->gui == FALSE)
120 index = menu_find_option_menu_index(GTK_CMOPTION_MENU(quicksearch->search_type_opt),
121 GINT_TO_POINTER(type),
123 gtk_cmoption_menu_set_history(GTK_CMOPTION_MENU(quicksearch->search_type_opt), index);
126 static gchar *quicksearch_get_text(QuickSearch * quicksearch)
128 gchar *search_string = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((quicksearch->search_string_entry)))), 0, -1);
130 g_strstrip(search_string);
131 return search_string;
134 static void quicksearch_set_popdown_strings(QuickSearch *quicksearch)
136 GtkWidget *search_string_entry = quicksearch->search_string_entry;
138 combobox_unset_popdown_strings(GTK_COMBO_BOX(search_string_entry));
140 if (prefs_common.summary_quicksearch_type == QUICK_SEARCH_EXTENDED)
141 combobox_set_popdown_strings(GTK_COMBO_BOX(search_string_entry),
142 quicksearch->extended_search_strings);
144 combobox_set_popdown_strings(GTK_COMBO_BOX(search_string_entry),
145 quicksearch->normal_search_strings);
148 static void prepare_matcher(QuickSearch *quicksearch)
150 /* param search_string is "matchstring" */
151 const gchar *search_string;
152 QuickSearchType quicksearch_type;
154 if (quicksearch == NULL)
157 /* When called from the GUI, reset type and matchstring */
158 if (quicksearch_from_gui(quicksearch)) {
159 gchar *s = quicksearch_get_text(quicksearch);
160 quicksearch_set_matchstring(quicksearch, s);
162 quicksearch->request->type = prefs_common.summary_quicksearch_type;
164 quicksearch_type = quicksearch->request->type;
165 search_string = quicksearch->request->matchstring;
167 if (search_string == NULL || search_string[0] == '\0') {
168 quicksearch_set_active(quicksearch, FALSE);
171 if (quicksearch->matcher_list != NULL) {
172 if (quicksearch->matching) {
173 quicksearch->deferred_free = TRUE;
176 quicksearch->deferred_free = FALSE;
177 matcherlist_free(quicksearch->matcher_list);
178 quicksearch->matcher_list = NULL;
181 if (search_string == NULL || search_string[0] == '\0') {
184 if (quicksearch_type == QUICK_SEARCH_EXTENDED) {
187 newstr = expand_search_string(search_string);
188 if (newstr && newstr[0] != '\0') {
189 quicksearch->matcher_list = matcher_parser_get_cond(newstr, &quicksearch->is_fast);
192 quicksearch->matcher_list = NULL;
193 quicksearch_set_active(quicksearch, FALSE);
196 } else if (quicksearch_type == QUICK_SEARCH_TAG) {
197 char *newstr = expand_tag_search_string(search_string);
198 quicksearch->matcher_list = matcher_parser_get_cond(newstr, &quicksearch->is_fast);
200 } else if (quicksearch_type == QUICK_SEARCH_MIXED) {
201 char *newstr = expand_tag_search_string(search_string);
202 quicksearch->matcher_list = matcher_parser_get_cond(newstr, &quicksearch->is_fast);
204 g_free(quicksearch->search_string);
205 quicksearch->search_string = g_utf8_casefold(search_string, -1);
207 quicksearch->is_fast = TRUE;
208 g_free(quicksearch->search_string);
209 quicksearch->search_string = g_utf8_casefold(search_string, -1);
211 quicksearch_set_active(quicksearch, TRUE);
214 static void update_extended_buttons (QuickSearch *quicksearch)
216 GtkWidget *expr_btn = quicksearch->search_condition_expression;
217 GtkWidget *ext_btn = quicksearch->search_description;
219 cm_return_if_fail(expr_btn != NULL);
220 cm_return_if_fail(ext_btn != NULL);
222 if (prefs_common.summary_quicksearch_type == QUICK_SEARCH_EXTENDED) {
223 gtk_widget_show(expr_btn);
224 gtk_widget_show(ext_btn);
226 gtk_widget_hide(expr_btn);
227 gtk_widget_hide(ext_btn);
231 static gboolean searchbar_focus_evt_in(GtkWidget *widget, GdkEventFocus *event,
234 qs->has_focus = TRUE;
238 static gboolean searchbar_focus_evt_out(GtkWidget *widget, GdkEventFocus *event,
241 qs->has_focus = FALSE;
242 qs->in_typing = FALSE;
246 gboolean quicksearch_has_focus(QuickSearch *quicksearch)
248 return quicksearch->has_focus;
251 static void searchbar_run(QuickSearch *quicksearch, gboolean run_only_if_fast)
253 gchar *search_string = quicksearch_get_text(quicksearch);
254 quicksearch_set_matchstring(quicksearch, search_string);
255 prepare_matcher(quicksearch);
257 /* add to history, for extended search add only correct matching rules */
258 if (!quicksearch->in_typing && search_string && strlen(search_string) != 0) {
259 switch (prefs_common.summary_quicksearch_type) {
260 case QUICK_SEARCH_EXTENDED:
261 if (quicksearch->matcher_list) {
262 quicksearch->extended_search_strings =
263 add_history(quicksearch->extended_search_strings,
264 g_strdup(search_string));
265 prefs_common.summary_quicksearch_history =
266 add_history(prefs_common.summary_quicksearch_history,
267 g_strdup(search_string));
271 quicksearch->normal_search_strings =
272 add_history(quicksearch->normal_search_strings,
273 g_strdup(search_string));
274 prefs_common.summary_quicksearch_history =
275 add_history(prefs_common.summary_quicksearch_history,
276 g_strdup(search_string));
280 quicksearch_set_popdown_strings(quicksearch);
284 if (run_only_if_fast && !quicksearch->is_fast) {
285 g_free(search_string);
288 if (quicksearch->matcher_list == NULL &&
289 prefs_common.summary_quicksearch_type == QUICK_SEARCH_EXTENDED &&
290 search_string && strlen(search_string) != 0) {
291 g_free(search_string);
294 quicksearch_set_running(quicksearch, TRUE);
295 if (quicksearch->callback != NULL)
296 quicksearch->callback(quicksearch, quicksearch->callback_data);
297 quicksearch_set_running(quicksearch, FALSE);
298 g_free(search_string);
301 static int searchbar_changed_timeout(void *data)
303 QuickSearch *qs = (QuickSearch *)data;
304 if (qs && prefs_common.summary_quicksearch_dynamic) {
305 qs->in_typing = TRUE;
306 searchbar_run(qs, TRUE);
311 static void searchbar_changed_cb(GtkWidget *widget, QuickSearch *qs)
313 if (!qs->has_focus && prefs_common.summary_quicksearch_autorun) {
314 gtk_widget_grab_focus(qs->search_string_entry);
315 searchbar_run(qs, TRUE);
319 if (prefs_common.summary_quicksearch_dynamic) {
320 if (qs->press_timeout_id != -1) {
321 g_source_remove(qs->press_timeout_id);
323 qs->press_timeout_id = g_timeout_add(500,
324 searchbar_changed_timeout, qs);
328 gtk_widget_grab_focus(qs->search_string_entry);
331 static gboolean searchbar_pressed(GtkWidget *widget, GdkEventKey *event,
332 QuickSearch *quicksearch)
334 if (event && (event->keyval == GDK_Escape)) {
337 quicksearch->in_typing = FALSE;
339 str = quicksearch_get_text(quicksearch);
340 cm_return_val_if_fail(str != NULL, TRUE);
342 /* If the string entry is empty -> hide quicksearch bar. If not -> empty it */
344 summaryview_activate_quicksearch(
345 mainwindow_get_mainwindow()->summaryview,
348 quicksearch_set(quicksearch, prefs_common.summary_quicksearch_type, "");
349 gtk_widget_grab_focus(
350 mainwindow_get_mainwindow()->summaryview->ctree);
356 if (event != NULL && (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter)) {
357 if (quicksearch->press_timeout_id != -1) {
358 g_source_remove(quicksearch->press_timeout_id);
359 quicksearch->press_timeout_id = -1;
361 quicksearch->in_typing = FALSE;
362 /* add expression to history list and exec quicksearch */
363 searchbar_run(quicksearch, FALSE);
365 g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
369 if (event && (event->keyval == GDK_Down || event->keyval == GDK_Up)) {
370 combobox_set_value_from_arrow_key(
371 GTK_COMBO_BOX(quicksearch->search_string_entry),
379 static gboolean searchtype_changed(GtkMenuItem *widget, gpointer data)
381 QuickSearch *quicksearch = (QuickSearch *)data;
382 gchar *search_string = quicksearch_get_text(quicksearch);
383 quicksearch_set_matchstring(quicksearch, search_string);
385 prefs_common.summary_quicksearch_type = GPOINTER_TO_INT(g_object_get_data(
386 G_OBJECT(GTK_MENU_ITEM(gtk_menu_get_active(
387 GTK_MENU(quicksearch->search_type)))), MENU_VAL_ID));
389 /* Show extended search description button, only when Extended is selected */
390 update_extended_buttons(quicksearch);
391 quicksearch_set_popdown_strings(quicksearch);
393 if (!search_string || *(search_string) == 0) {
394 g_free(search_string);
398 prepare_matcher(quicksearch);
400 quicksearch_set_running(quicksearch, TRUE);
401 if (quicksearch->callback != NULL)
402 quicksearch->callback(quicksearch, quicksearch->callback_data);
403 quicksearch_set_running(quicksearch, FALSE);
404 g_free(search_string);
408 static gboolean searchtype_recursive_changed(GtkMenuItem *widget, gpointer data)
410 QuickSearch *quicksearch = (QuickSearch *)data;
411 gboolean checked = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
412 gchar *search_string = quicksearch_get_text(quicksearch);
413 /* not needed to quicksearch_set_matchstring(search_string);
414 wait for prepare_matcher() */
416 prefs_common.summary_quicksearch_recurse = checked;
417 quicksearch_set_recursive(quicksearch, checked);
419 /* reselect the search type */
420 quicksearch_set_type(quicksearch, prefs_common.summary_quicksearch_type);
422 if (!search_string || *(search_string) == 0) {
423 g_free(search_string);
427 prepare_matcher(quicksearch);
429 quicksearch_set_running(quicksearch, TRUE);
430 if (quicksearch->callback != NULL)
431 quicksearch->callback(quicksearch, quicksearch->callback_data);
432 quicksearch_set_running(quicksearch, FALSE);
433 g_free(search_string);
437 static gboolean searchtype_sticky_changed(GtkMenuItem *widget, gpointer data)
439 QuickSearch *quicksearch = (QuickSearch *)data;
440 gboolean checked = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
442 prefs_common.summary_quicksearch_sticky = checked;
444 /* reselect the search type */
445 quicksearch_set_type(quicksearch, prefs_common.summary_quicksearch_type);
450 static gboolean searchtype_dynamic_changed(GtkMenuItem *widget, gpointer data)
452 QuickSearch *quicksearch = (QuickSearch *)data;
453 gboolean checked = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
455 prefs_common.summary_quicksearch_dynamic = checked;
457 gtk_check_menu_item_set_active(
458 GTK_CHECK_MENU_ITEM(quicksearch->autorun_menuitem),
461 /* reselect the search type */
462 quicksearch_set_type(quicksearch, prefs_common.summary_quicksearch_type);
467 static gboolean searchtype_autorun_changed(GtkMenuItem *widget, gpointer data)
469 QuickSearch *quicksearch = (QuickSearch *)data;
470 gboolean checked = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
472 prefs_common.summary_quicksearch_autorun = checked;
474 gtk_check_menu_item_set_active(
475 GTK_CHECK_MENU_ITEM(quicksearch->dynamic_menuitem),
478 /* reselect the search type */
479 quicksearch_set_type(quicksearch, prefs_common.summary_quicksearch_type);
485 * Strings describing how to use Extended Search
487 * When adding new lines, remember to put 2 strings for each line
489 static gchar *search_descr_strings[] = {
490 "a", N_("all messages"),
491 "ag #", N_("messages whose age is greater than #"),
492 "al #", N_("messages whose age is less than #"),
493 "b S", N_("messages which contain S in the message body"),
494 "B S", N_("messages which contain S in the whole message"),
495 "c S", N_("messages carbon-copied to S"),
496 "C S", N_("message is either to: or cc: to S"),
497 "D", N_("deleted messages"), /** how I can filter deleted messages **/
498 "e S", N_("messages which contain S in the Sender field"),
499 "E S", N_("true if execute \"S\" succeeds"),
500 "f S", N_("messages originating from user S"),
501 "F", N_("forwarded messages"),
502 "h S", N_("messages which contain header S"),
503 "i S", N_("messages which contain S in Message-ID header"),
504 "I S", N_("messages which contain S in In-Reply-To header"),
505 "k #", N_("messages which are marked with color #"),
506 "L", N_("locked messages"),
507 "n S", N_("messages which are in newsgroup S"),
508 "N", N_("new messages"),
509 "O", N_("old messages"),
510 "p", N_("incomplete messages (not entirely downloaded)"),
511 "r", N_("messages which have been replied to"),
512 "R", N_("read messages"),
513 "s S", N_("messages which contain S in subject"),
514 "se #", N_("messages whose score is equal to #"),
515 "sg #", N_("messages whose score is greater than #"),
516 "sl #", N_("messages whose score is lower than #"),
517 "Se #", N_("messages whose size is equal to #"),
518 "Sg #", N_("messages whose size is greater than #"),
519 "Ss #", N_("messages whose size is smaller than #"),
520 "t S", N_("messages which have been sent to S"),
521 "tg S", N_("messages which tags contain S"),
522 "tagged",N_("messages which have tag(s)"),
523 "T", N_("marked messages"),
524 "U", N_("unread messages"),
525 "x S", N_("messages which contain S in References header"),
526 "X \"cmd args\"", N_("messages returning 0 when passed to command - %F is message file"),
527 "y S", N_("messages which contain S in X-Label header"),
529 "&", N_("logical AND operator"),
530 "|", N_("logical OR operator"),
531 "! or ~", N_("logical NOT operator"),
532 "%", N_("case sensitive search"),
534 " ", N_("all filtering expressions are allowed"),
538 static DescriptionWindow search_descr = {
542 N_("Extended Search"),
543 N_("Extended Search allows the user to define criteria that messages must "
544 "have in order to match and be displayed in the message list.\n"
545 "The following symbols can be used:"),
549 static void search_description_cb(GtkWidget *widget)
551 description_window_create(&search_descr);
554 static gboolean clear_search_cb(GtkMenuItem *widget, gpointer data)
556 QuickSearch *quicksearch = (QuickSearch *)data;
558 if (!quicksearch->active)
561 quicksearch_set(quicksearch, prefs_common.summary_quicksearch_type, "");
566 static void search_condition_expr_done(MatcherList * matchers)
571 mainwindow_get_mainwindow()->summaryview->quicksearch != NULL);
573 if (matchers == NULL)
576 str = matcherlist_to_string(matchers);
579 quicksearch_set(mainwindow_get_mainwindow()->summaryview->quicksearch,
580 prefs_common.summary_quicksearch_type, str);
583 /* add expression to history list and exec quicksearch */
584 searchbar_run(mainwindow_get_mainwindow()->summaryview->quicksearch, FALSE);
588 static gboolean search_condition_expr(GtkMenuItem *widget, gpointer data)
591 MatcherList * matchers = NULL;
593 cm_return_val_if_fail(
594 mainwindow_get_mainwindow()->summaryview->quicksearch != NULL,
597 /* re-use the current quicksearch value if it's a condition expression,
598 otherwise ignore it silently */
599 cond_str = quicksearch_get_text(mainwindow_get_mainwindow()->summaryview->quicksearch);
601 if (*cond_str != '\0') {
602 matchers = matcher_parser_get_cond((gchar*)cond_str, NULL);
605 prefs_matcher_open(matchers, search_condition_expr_done);
607 if (matchers != NULL)
608 matcherlist_free(matchers);
615 static void quicksearch_set_button(GtkButton *button, const gchar *icon, const gchar *text)
617 GList *children = gtk_container_get_children(GTK_CONTAINER(button));
620 gboolean icon_visible;
622 g_object_get(gtk_settings_get_default(),
623 "gtk-button-images", &icon_visible,
626 for (cur = children; cur; cur = cur->next)
627 gtk_container_remove(GTK_CONTAINER(button), GTK_WIDGET(cur->data));
629 g_list_free(children);
630 box = gtk_hbox_new(FALSE, 0);
632 gtk_container_add(GTK_CONTAINER(button), box);
633 if (icon_visible || !text || !*text)
634 gtk_box_pack_start(GTK_BOX(box), gtk_image_new_from_stock(icon,
635 GTK_ICON_SIZE_BUTTON), FALSE, FALSE, 0);
636 gtk_box_pack_start(GTK_BOX(box), gtk_label_new_with_mnemonic(text), FALSE, FALSE, 0);
637 gtk_widget_show_all(box);
641 * Builds a new QuickSearchRequest
643 static QuickSearchRequest *quicksearchrequest_new(void)
645 QuickSearchRequest *request;
646 request = g_new0(QuickSearchRequest, 1);
651 * Builds a new QuickSearch object independent from the GUI
653 QuickSearch *quicksearch_new_nogui(void)
655 QuickSearch *quicksearch;
656 QuickSearchRequest *request;
658 request = quicksearchrequest_new();
659 quicksearch = g_new0(QuickSearch, 1);
660 quicksearch->request = request;
661 quicksearch->gui = FALSE;
663 /* init. values initally found in quicksearch_new().
664 There's no need to init. all pointers to NULL since we use g_new0
666 quicksearch->matcher_list = NULL;
667 quicksearch->active = FALSE;
668 quicksearch->running = FALSE;
669 quicksearch->in_typing = FALSE;
670 quicksearch->press_timeout_id = -1;
671 quicksearch->normal_search_strings = NULL;
672 quicksearch->extended_search_strings = NULL;
677 QuickSearch *quicksearch_new()
679 QuickSearch *quicksearch;
681 GtkWidget *hbox_search;
682 GtkWidget *search_type_opt;
683 GtkWidget *search_type;
684 GtkWidget *search_string_entry;
685 GtkWidget *search_hbox;
686 GtkWidget *search_description;
687 GtkWidget *clear_search;
688 GtkWidget *search_condition_expression;
693 quicksearch = quicksearch_new_nogui();
694 quicksearch->gui = TRUE;
697 hbox_search = gtk_hbox_new(FALSE, 0);
699 search_type_opt = gtk_cmoption_menu_new();
700 gtk_widget_show(search_type_opt);
701 gtk_box_pack_start(GTK_BOX(hbox_search), search_type_opt, FALSE, FALSE, 0);
703 search_type = gtk_menu_new();
704 MENUITEM_ADD (search_type, menuitem,
705 prefs_common_translated_header_name("Subject"), QUICK_SEARCH_SUBJECT);
706 g_signal_connect(G_OBJECT(menuitem), "activate",
707 G_CALLBACK(searchtype_changed),
709 MENUITEM_ADD (search_type, menuitem,
710 prefs_common_translated_header_name("From"), QUICK_SEARCH_FROM);
711 g_signal_connect(G_OBJECT(menuitem), "activate",
712 G_CALLBACK(searchtype_changed),
714 MENUITEM_ADD (search_type, menuitem,
715 prefs_common_translated_header_name("To"), QUICK_SEARCH_TO);
716 g_signal_connect(G_OBJECT(menuitem), "activate",
717 G_CALLBACK(searchtype_changed),
719 MENUITEM_ADD (search_type, menuitem,
720 prefs_common_translated_header_name("Tag"), QUICK_SEARCH_TAG);
721 g_signal_connect(G_OBJECT(menuitem), "activate",
722 G_CALLBACK(searchtype_changed),
724 MENUITEM_ADD (search_type, menuitem,
725 _("From/To/Subject/Tag"), QUICK_SEARCH_MIXED);
726 g_signal_connect(G_OBJECT(menuitem), "activate",
727 G_CALLBACK(searchtype_changed),
729 MENUITEM_ADD (search_type, menuitem, _("Extended"), QUICK_SEARCH_EXTENDED);
730 g_signal_connect(G_OBJECT(menuitem), "activate",
731 G_CALLBACK(searchtype_changed),
734 gtk_menu_shell_append(GTK_MENU_SHELL(search_type), gtk_separator_menu_item_new());
736 menuitem = gtk_check_menu_item_new_with_label(_("Recursive"));
737 gtk_menu_shell_append(GTK_MENU_SHELL(search_type), menuitem);
739 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
740 prefs_common.summary_quicksearch_recurse);
741 quicksearch_set_recursive(quicksearch, prefs_common.summary_quicksearch_recurse);
742 g_signal_connect(G_OBJECT(menuitem), "activate",
743 G_CALLBACK(searchtype_recursive_changed),
746 menuitem = gtk_check_menu_item_new_with_label(_("Sticky"));
747 gtk_menu_shell_append(GTK_MENU_SHELL(search_type), menuitem);
749 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
750 prefs_common.summary_quicksearch_sticky);
752 g_signal_connect(G_OBJECT(menuitem), "activate",
753 G_CALLBACK(searchtype_sticky_changed),
756 menuitem = gtk_check_menu_item_new_with_label(_("Type-ahead"));
757 gtk_menu_shell_append(GTK_MENU_SHELL(search_type), menuitem);
759 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
760 prefs_common.summary_quicksearch_dynamic);
762 quicksearch->dynamic_menuitem = menuitem;
764 g_signal_connect(G_OBJECT(menuitem), "activate",
765 G_CALLBACK(searchtype_dynamic_changed),
768 menuitem = gtk_check_menu_item_new_with_label(_("Run on select"));
769 gtk_menu_shell_append(GTK_MENU_SHELL(search_type), menuitem);
771 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
772 prefs_common.summary_quicksearch_autorun);
774 quicksearch->autorun_menuitem = menuitem;
776 g_signal_connect(G_OBJECT(menuitem), "activate",
777 G_CALLBACK(searchtype_autorun_changed),
780 gtk_cmoption_menu_set_menu(GTK_CMOPTION_MENU(search_type_opt), search_type);
782 quicksearch->search_type_opt = search_type_opt;
783 quicksearch_set_type(quicksearch, prefs_common.summary_quicksearch_type);
785 gtk_widget_show(search_type);
787 search_string_entry = gtk_combo_box_entry_new_text ();
788 gtk_combo_box_set_active(GTK_COMBO_BOX(search_string_entry), -1);
790 vbox = gtk_vbox_new(TRUE, 0);
791 gtk_box_pack_start(GTK_BOX(vbox), search_string_entry, FALSE, FALSE, 0);
792 gtk_box_pack_start(GTK_BOX(hbox_search), vbox, TRUE, TRUE, 4);
794 gtk_widget_show(vbox);
795 gtk_widget_show(search_string_entry);
797 search_hbox = gtk_hbox_new(FALSE, 5);
798 clear_search = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
799 gtk_box_pack_start(GTK_BOX(search_hbox), clear_search,
801 g_signal_connect(G_OBJECT(clear_search), "clicked",
802 G_CALLBACK(clear_search_cb), quicksearch);
803 CLAWS_SET_TIP(clear_search,
804 _("Clear the current search"));
805 gtk_widget_show(clear_search);
807 search_condition_expression = gtk_button_new_from_stock(GTK_STOCK_EDIT);
808 gtk_box_pack_start(GTK_BOX(search_hbox), search_condition_expression,
810 g_signal_connect(G_OBJECT (search_condition_expression), "clicked",
811 G_CALLBACK(search_condition_expr),
813 CLAWS_SET_TIP(search_condition_expression,
814 _("Edit search criteria"));
815 gtk_widget_show(search_condition_expression);
817 search_description = gtk_button_new_from_stock(GTK_STOCK_INFO);
818 gtk_box_pack_start(GTK_BOX(search_hbox), search_description,
820 g_signal_connect(G_OBJECT(search_description), "clicked",
821 G_CALLBACK(search_description_cb), NULL);
822 CLAWS_SET_TIP(search_description,
823 _("Information about extended symbols"));
824 gtk_widget_show(search_description);
826 gtk_box_pack_start(GTK_BOX(hbox_search), search_hbox, FALSE, FALSE, 2);
827 gtk_widget_show(search_hbox);
829 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((search_string_entry)))),
831 G_CALLBACK(searchbar_pressed),
834 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((search_string_entry)))),
836 G_CALLBACK(searchbar_changed_cb),
839 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((search_string_entry)))),
841 G_CALLBACK(searchbar_focus_evt_in),
843 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((search_string_entry)))),
845 G_CALLBACK(searchbar_focus_evt_out),
848 quicksearch->hbox_search = hbox_search;
849 quicksearch->search_type = search_type;
850 quicksearch->search_string_entry = search_string_entry;
851 quicksearch->search_condition_expression = search_condition_expression;
852 quicksearch->search_description = search_description;
853 quicksearch->matcher_list = NULL;
854 quicksearch->active = FALSE;
855 quicksearch->running = FALSE;
856 quicksearch->clear_search = clear_search;
857 quicksearch->in_typing = FALSE;
858 quicksearch->press_timeout_id = -1;
859 quicksearch->normal_search_strings = NULL;
860 quicksearch->extended_search_strings = NULL;
862 quicksearch_set_button(GTK_BUTTON(quicksearch->search_description), GTK_STOCK_INFO, _("_Information"));
863 quicksearch_set_button(GTK_BUTTON(quicksearch->search_condition_expression), GTK_STOCK_EDIT, _("_Edit"));
864 quicksearch_set_button(GTK_BUTTON(quicksearch->clear_search), GTK_STOCK_CLEAR, _("_Clear"));
866 update_extended_buttons(quicksearch);
871 void quicksearch_relayout(QuickSearch *quicksearch)
873 switch (prefs_common.layout_mode) {
876 case WIDE_MSGLIST_LAYOUT:
877 quicksearch_set_button(GTK_BUTTON(quicksearch->search_description), GTK_STOCK_INFO, _("_Information"));
878 quicksearch_set_button(GTK_BUTTON(quicksearch->search_condition_expression), GTK_STOCK_EDIT, _("_Edit"));
879 quicksearch_set_button(GTK_BUTTON(quicksearch->clear_search), GTK_STOCK_CLEAR, _("_Clear"));
882 case VERTICAL_LAYOUT:
883 quicksearch_set_button(GTK_BUTTON(quicksearch->search_description), GTK_STOCK_INFO, "");
884 quicksearch_set_button(GTK_BUTTON(quicksearch->search_condition_expression), GTK_STOCK_EDIT, "");
885 quicksearch_set_button(GTK_BUTTON(quicksearch->clear_search), GTK_STOCK_CLEAR, "");
890 GtkWidget *quicksearch_get_widget(QuickSearch *quicksearch)
892 return quicksearch->hbox_search;
895 void quicksearch_show(QuickSearch *quicksearch)
897 MainWindow *mainwin = mainwindow_get_mainwindow();
898 GtkWidget *ctree = NULL;
899 prepare_matcher(quicksearch);
900 gtk_widget_show(quicksearch->hbox_search);
901 update_extended_buttons(quicksearch);
902 gtk_widget_grab_focus(
903 GTK_WIDGET(gtk_bin_get_child(GTK_BIN((quicksearch->search_string_entry)))));
907 if (!mainwin || !mainwin->summaryview) {
911 ctree = summary_get_main_widget(mainwin->summaryview);
913 if (ctree && mainwin->summaryview->selected)
914 gtk_cmctree_node_moveto(GTK_CMCTREE(ctree),
915 mainwin->summaryview->selected,
919 void quicksearch_hide(QuickSearch *quicksearch)
921 if (quicksearch_is_active(quicksearch)) {
922 quicksearch_set(quicksearch, prefs_common.summary_quicksearch_type, "");
923 quicksearch_set_active(quicksearch, FALSE);
925 gtk_widget_hide(quicksearch->hbox_search);
929 *\brief Sets the matchstring.
931 *\param quicksearch quicksearch to set
932 *\param matchstring the match string; it is duplicated, not stored
934 static void quicksearch_set_matchstring(QuickSearch *quicksearch,
935 const gchar *matchstring)
937 g_free(quicksearch->request->matchstring);
938 quicksearch->request->matchstring = g_strdup(matchstring);
941 void quicksearch_set(QuickSearch *quicksearch, QuickSearchType type,
942 const gchar *matchstring)
944 quicksearch_set_type(quicksearch, type);
946 if (!matchstring || !(*matchstring))
947 quicksearch->in_typing = FALSE;
949 quicksearch_set_matchstring(quicksearch, matchstring);
951 if (!quicksearch_from_gui(quicksearch)) {
952 prepare_matcher(quicksearch);
957 g_signal_handlers_block_by_func(G_OBJECT(gtk_bin_get_child(GTK_BIN((quicksearch->search_string_entry)))),
958 G_CALLBACK(searchbar_changed_cb), quicksearch);
959 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((quicksearch->search_string_entry)))),
961 g_signal_handlers_unblock_by_func(G_OBJECT(gtk_bin_get_child(GTK_BIN((quicksearch->search_string_entry)))),
962 G_CALLBACK(searchbar_changed_cb), quicksearch);
964 prefs_common.summary_quicksearch_type = type;
966 prepare_matcher(quicksearch);
968 quicksearch_set_running(quicksearch, TRUE);
969 if (quicksearch->callback != NULL)
970 quicksearch->callback(quicksearch, quicksearch->callback_data);
971 quicksearch_set_running(quicksearch, FALSE);
974 gboolean quicksearch_is_active(QuickSearch *quicksearch)
976 return quicksearch->active &&
977 (prefs_common.summary_quicksearch_type != QUICK_SEARCH_EXTENDED
978 || quicksearch->matcher_list != NULL);
981 static void quicksearch_set_active(QuickSearch *quicksearch, gboolean active)
983 static GdkColor yellow;
985 static GdkColor black;
986 static gboolean colors_initialised = FALSE;
987 gboolean error = FALSE;
990 quicksearch->active = active;
991 if (quicksearch->gui == FALSE)
994 if (!colors_initialised) {
995 gdk_color_parse("#f5f6be", &yellow);
996 gdk_color_parse("#000000", &black);
997 gdk_color_parse("#ff7070", &red);
998 colors_initialised = gdk_colormap_alloc_color(
999 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
1000 colors_initialised &= gdk_colormap_alloc_color(
1001 gdk_colormap_get_system(), &black, FALSE, TRUE);
1002 colors_initialised &= gdk_colormap_alloc_color(
1003 gdk_colormap_get_system(), &red, FALSE, TRUE);
1007 (prefs_common.summary_quicksearch_type == QUICK_SEARCH_EXTENDED
1008 && quicksearch->matcher_list == NULL))
1012 gtk_widget_set_sensitive(quicksearch->clear_search, TRUE);
1013 if (colors_initialised) {
1014 gtk_widget_modify_base(
1015 gtk_bin_get_child(GTK_BIN((quicksearch->search_string_entry))),
1016 GTK_STATE_NORMAL, error ? &red : &yellow);
1017 gtk_widget_modify_text(
1018 gtk_bin_get_child(GTK_BIN((quicksearch->search_string_entry))),
1019 GTK_STATE_NORMAL, &black);
1022 gtk_widget_set_sensitive(quicksearch->clear_search, FALSE);
1023 if (colors_initialised) {
1024 gtk_widget_modify_base(
1025 gtk_bin_get_child(GTK_BIN((quicksearch->search_string_entry))),
1026 GTK_STATE_NORMAL, NULL);
1027 gtk_widget_modify_text(
1028 gtk_bin_get_child(GTK_BIN((quicksearch->search_string_entry))),
1029 GTK_STATE_NORMAL, NULL);
1034 quicksearch_reset_cur_folder_item(quicksearch);
1038 void quicksearch_set_execute_callback(QuickSearch *quicksearch,
1039 QuickSearchExecuteCallback callback,
1042 quicksearch->callback = callback;
1043 quicksearch->callback_data = data;
1046 gboolean quicksearch_match(QuickSearch *quicksearch, MsgInfo *msginfo)
1048 gchar *searched_header = NULL;
1049 gboolean result = FALSE;
1050 gchar *to = NULL, *from = NULL, *subject = NULL;
1051 QuickSearchType quicksearch_type;
1053 if (!quicksearch->active)
1056 quicksearch_type = quicksearch->request->type;
1058 switch (quicksearch_type) {
1059 case QUICK_SEARCH_SUBJECT:
1060 if (msginfo->subject)
1061 searched_header = g_utf8_casefold(msginfo->subject, -1);
1065 case QUICK_SEARCH_FROM:
1067 searched_header = g_utf8_casefold(msginfo->from, -1);
1071 case QUICK_SEARCH_TO:
1073 searched_header = g_utf8_casefold(msginfo->to, -1);
1077 case QUICK_SEARCH_MIXED:
1079 to = g_utf8_casefold(msginfo->to, -1);
1081 from = g_utf8_casefold(msginfo->from, -1);
1082 if (msginfo->subject)
1083 subject = g_utf8_casefold(msginfo->subject, -1);
1085 case QUICK_SEARCH_EXTENDED:
1088 debug_print("unknown search type (%d)\n", quicksearch_type);
1092 quicksearch->matching = TRUE;
1093 if (quicksearch_type != QUICK_SEARCH_EXTENDED &&
1094 quicksearch_type != QUICK_SEARCH_MIXED &&
1095 quicksearch_type != QUICK_SEARCH_TAG &&
1096 quicksearch->search_string &&
1097 searched_header && strstr(searched_header, quicksearch->search_string) != NULL)
1099 else if (quicksearch_type == QUICK_SEARCH_MIXED &&
1100 quicksearch->search_string && (
1101 (to && strstr(to, quicksearch->search_string) != NULL) ||
1102 (from && strstr(from, quicksearch->search_string) != NULL) ||
1103 (subject && strstr(subject, quicksearch->search_string) != NULL) ||
1104 ((quicksearch->matcher_list != NULL) &&
1105 matcherlist_match(quicksearch->matcher_list, msginfo)) ))
1107 else if ((quicksearch->matcher_list != NULL) &&
1108 matcherlist_match(quicksearch->matcher_list, msginfo))
1111 quicksearch->matching = FALSE;
1112 if (quicksearch_from_gui(quicksearch)==TRUE && quicksearch->deferred_free) {
1113 /* Ref. http://lists.claws-mail.org/pipermail/users/2010-August/003063.html
1114 See also 2.0.0cvs140 ChangeLog entry
1115 and comment in search_msgs_in_folder() */
1116 prepare_matcher(quicksearch);
1122 g_free(searched_header);
1127 /* allow Mutt-like patterns in quick search */
1128 static gchar *expand_search_string(const gchar *search_string)
1131 gchar term_char, save_char;
1132 gchar *cmd_start, *cmd_end;
1133 GString *matcherstr;
1134 gchar *returnstr = NULL;
1136 gboolean casesens, dontmatch;
1137 /* list of allowed pattern abbreviations */
1139 gchar *abbreviated; /* abbreviation */
1140 gchar *command; /* actual matcher command */
1141 gint numparams; /* number of params for cmd */
1142 gboolean qualifier; /* do we append regexpcase */
1143 gboolean quotes; /* do we need quotes */
1146 { "a", "all", 0, FALSE, FALSE },
1147 { "ag", "age_greater", 1, FALSE, FALSE },
1148 { "al", "age_lower", 1, FALSE, FALSE },
1149 { "b", "body_part", 1, TRUE, TRUE },
1150 { "B", "message", 1, TRUE, TRUE },
1151 { "c", "cc", 1, TRUE, TRUE },
1152 { "C", "to_or_cc", 1, TRUE, TRUE },
1153 { "D", "deleted", 0, FALSE, FALSE },
1154 { "e", "header \"Sender\"", 1, TRUE, TRUE },
1155 { "E", "execute", 1, FALSE, TRUE },
1156 { "f", "from", 1, TRUE, TRUE },
1157 { "F", "forwarded", 0, FALSE, FALSE },
1158 { "h", "headers_part", 1, TRUE, TRUE },
1159 { "i", "header \"Message-ID\"", 1, TRUE, TRUE },
1160 { "I", "inreplyto", 1, TRUE, TRUE },
1161 { "k", "colorlabel", 1, FALSE, FALSE },
1162 { "L", "locked", 0, FALSE, FALSE },
1163 { "n", "newsgroups", 1, TRUE, TRUE },
1164 { "N", "new", 0, FALSE, FALSE },
1165 { "O", "~new", 0, FALSE, FALSE },
1166 { "r", "replied", 0, FALSE, FALSE },
1167 { "R", "~unread", 0, FALSE, FALSE },
1168 { "s", "subject", 1, TRUE, TRUE },
1169 { "se", "score_equal", 1, FALSE, FALSE },
1170 { "sg", "score_greater", 1, FALSE, FALSE },
1171 { "sl", "score_lower", 1, FALSE, FALSE },
1172 { "Se", "size_equal", 1, FALSE, FALSE },
1173 { "Sg", "size_greater", 1, FALSE, FALSE },
1174 { "Ss", "size_smaller", 1, FALSE, FALSE },
1175 { "t", "to", 1, TRUE, TRUE },
1176 { "tg", "tag", 1, TRUE, TRUE },
1177 { "T", "marked", 0, FALSE, FALSE },
1178 { "U", "unread", 0, FALSE, FALSE },
1179 { "x", "header \"References\"", 1, TRUE, TRUE },
1180 { "X", "test", 1, FALSE, FALSE },
1181 { "y", "header \"X-Label\"", 1, TRUE, TRUE },
1182 { "&", "&", 0, FALSE, FALSE },
1183 { "|", "|", 0, FALSE, FALSE },
1184 { "p", "partial", 0, FALSE, FALSE },
1185 { NULL, NULL, 0, FALSE, FALSE }
1188 if (search_string == NULL)
1191 copy_str = g_strdup(search_string);
1193 matcherstr = g_string_sized_new(16);
1194 cmd_start = copy_str;
1195 while (cmd_start && *cmd_start) {
1196 /* skip all white spaces */
1197 while (*cmd_start && isspace((guchar)*cmd_start))
1199 cmd_end = cmd_start;
1201 /* extract a command */
1202 while (*cmd_end && !isspace((guchar)*cmd_end))
1205 /* save character */
1206 save_char = *cmd_end;
1212 /* ~ and ! mean logical NOT */
1213 if (*cmd_start == '~' || *cmd_start == '!')
1218 /* % means case sensitive match */
1219 if (*cmd_start == '%')
1225 /* find matching abbreviation */
1226 for (i = 0; cmds[i].command; i++) {
1227 if (!strcmp(cmd_start, cmds[i].abbreviated)) {
1228 /* restore character */
1229 *cmd_end = save_char;
1232 if (matcherstr->len > 0) {
1233 g_string_append(matcherstr, " ");
1236 g_string_append(matcherstr, "~");
1237 g_string_append(matcherstr, cmds[i].command);
1238 g_string_append(matcherstr, " ");
1240 /* stop if no params required */
1241 if (cmds[i].numparams == 0)
1244 /* extract a parameter, allow quotes */
1245 while (*cmd_end && isspace((guchar)*cmd_end))
1248 cmd_start = cmd_end;
1249 if (*cmd_start == '"') {
1256 /* extract actual parameter */
1257 while ((*cmd_end) && (*cmd_end != term_char))
1260 if (*cmd_end == '"')
1263 save_char = *cmd_end;
1266 if (cmds[i].qualifier) {
1268 g_string_append(matcherstr, "regexp ");
1270 g_string_append(matcherstr, "regexpcase ");
1273 /* do we need to add quotes ? */
1274 if (cmds[i].quotes && term_char != '"')
1275 g_string_append(matcherstr, "\"");
1277 /* copy actual parameter */
1278 g_string_append(matcherstr, cmd_start);
1280 /* do we need to add quotes ? */
1281 if (cmds[i].quotes && term_char != '"')
1282 g_string_append(matcherstr, "\"");
1284 /* restore original character */
1285 *cmd_end = save_char;
1293 cmd_start = cmd_end;
1298 /* return search string if no match is found to allow
1299 all available filtering expressions in quicksearch */
1300 if (matcherstr->len > 0) returnstr = matcherstr->str;
1301 else returnstr = g_strdup(search_string);
1302 g_string_free(matcherstr, FALSE);
1306 static gchar *expand_tag_search_string(const gchar *search_string)
1308 gchar *newstr = NULL;
1309 gchar **words = search_string ? g_strsplit(search_string, " ", -1):NULL;
1311 while (words && words[i] && *words[i]) {
1312 g_strstrip(words[i]);
1314 newstr = g_strdup_printf("tag regexpcase \"%s\"", words[i]);
1316 gint o_len = strlen(newstr);
1317 gint s_len = 18; /* strlen("|tag regexpcase \"\"") */
1318 gint n_len = s_len + strlen(words[i]);
1319 newstr = g_realloc(newstr,o_len+n_len+1);
1320 strcpy(newstr+o_len, "|tag regexpcase \"");
1321 strcpy(newstr+o_len+(s_len-1), words[i]);
1322 strcpy(newstr+o_len+(n_len-1), "\"");
1330 static void quicksearch_set_running(QuickSearch *quicksearch, gboolean run)
1332 quicksearch->running = run;
1335 gboolean quicksearch_is_running(QuickSearch *quicksearch)
1337 return quicksearch->running;
1340 void quicksearch_pass_key(QuickSearch *quicksearch, guint val, GdkModifierType mod)
1342 GtkEntry *entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN((quicksearch->search_string_entry))));
1343 glong curpos = gtk_editable_get_position(GTK_EDITABLE(entry));
1345 char *str = g_strdup(gtk_entry_get_text(entry));
1352 if (gtk_editable_get_selection_bounds(GTK_EDITABLE(entry), NULL, NULL)) {
1353 /* remove selection */
1354 gtk_editable_delete_selection(GTK_EDITABLE(entry));
1355 curpos = gtk_editable_get_position(GTK_EDITABLE(entry));
1356 /* refresh string */
1358 str = g_strdup(gtk_entry_get_text(entry));
1362 if (!(c = gdk_keyval_to_unicode(val))) {
1366 char_len = g_unichar_to_utf8(c, key);
1369 key[char_len] = '\0';
1370 if (curpos < g_utf8_strlen(str, -1)) {
1371 gchar *stop = g_utf8_offset_to_pointer(begin, curpos);
1372 end = g_strdup(g_utf8_offset_to_pointer(str, curpos));
1374 new = g_strdup_printf("%s%s%s", begin, key, end);
1375 gtk_entry_set_text(entry, new);
1378 new = g_strdup_printf("%s%s", begin, key);
1379 gtk_entry_set_text(entry, new);
1383 gtk_editable_set_position(GTK_EDITABLE(entry), curpos+1);
1387 static gboolean quicksearch_match_subfolder(QuickSearch *quicksearch,
1390 GSList *msglist = NULL;
1392 gboolean result = FALSE;
1393 gint num = 0, total = 0;
1394 gint interval = quicksearch_is_fast(quicksearch) ? 5000:100;
1396 statusbar_print_all(_("Searching in %s... \n"),
1397 src->path ? src->path : "(null)");
1399 msglist = folder_item_get_msg_list(src);
1400 total = src->total_msgs;
1401 folder_item_update_freeze();
1402 for (cur = msglist; cur != NULL; cur = cur->next) {
1403 MsgInfo *msg = (MsgInfo *)cur->data;
1404 statusbar_progress_all(num++,total, interval);
1405 if (quicksearch_match(quicksearch, msg)) {
1409 if (num % interval == 0)
1411 if (!quicksearch_is_active(quicksearch))
1414 folder_item_update_thaw();
1415 statusbar_progress_all(0,0,0);
1416 statusbar_pop_all();
1418 procmsg_msg_list_free(msglist);
1422 gboolean quicksearch_is_in_subfolder(QuickSearch *quicksearch, FolderItem *cur)
1424 if (quicksearch->root_folder_item == NULL)
1428 if (cur == quicksearch->root_folder_item) {
1431 cur = folder_item_parent(cur);
1436 void quicksearch_search_subfolders(QuickSearch *quicksearch,
1437 FolderView *folderview,
1438 FolderItem *folder_item)
1440 FolderItem *cur = NULL;
1441 GNode *node = folder_item->node->children;
1443 if (!prefs_common.summary_quicksearch_recurse
1444 || quicksearch->in_typing == TRUE)
1447 for (; node != NULL; node = node->next) {
1448 cur = FOLDER_ITEM(node->data);
1449 if (quicksearch_match_subfolder(quicksearch, cur)) {
1450 folderview_update_search_icon(cur, TRUE);
1452 folderview_update_search_icon(cur, FALSE);
1454 if (cur->node->children)
1455 quicksearch_search_subfolders(quicksearch,
1459 quicksearch->root_folder_item = folder_item;
1460 if (!quicksearch_is_active(quicksearch))
1461 quicksearch_reset_cur_folder_item(quicksearch);
1464 static void quicksearch_reset_folder_items(QuickSearch *quicksearch,
1465 FolderItem *folder_item)
1467 FolderItem *cur = NULL;
1468 GNode *node = (folder_item && folder_item->node) ?
1469 folder_item->node->children : NULL;
1471 for (; node != NULL; node = node->next) {
1472 cur = FOLDER_ITEM(node->data);
1473 folderview_update_search_icon(cur, FALSE);
1474 if (cur->node->children)
1475 quicksearch_reset_folder_items(quicksearch,
1480 void quicksearch_reset_cur_folder_item(QuickSearch *quicksearch)
1482 if (quicksearch->root_folder_item)
1483 quicksearch_reset_folder_items(quicksearch,
1484 quicksearch->root_folder_item);
1486 quicksearch->root_folder_item = NULL;
1489 gboolean quicksearch_is_in_typing(QuickSearch *quicksearch)
1491 return quicksearch->in_typing;
1494 void quicksearch_set_search_strings(QuickSearch *quicksearch)
1496 GList *strings = prefs_common.summary_quicksearch_history;
1497 gchar *newstr = NULL;
1498 MatcherList *matcher_list = NULL;
1503 matcher_parser_disable_warnings(TRUE);
1506 newstr = expand_search_string((gchar *) strings->data);
1507 if (newstr && newstr[0] != '\0') {
1508 if (!strchr(newstr, ' ')) {
1509 quicksearch->normal_search_strings =
1511 quicksearch->normal_search_strings,
1512 g_strdup(strings->data));
1517 matcher_list = matcher_parser_get_cond(newstr, FALSE);
1521 quicksearch->extended_search_strings =
1523 quicksearch->extended_search_strings,
1524 g_strdup(strings->data));
1525 matcherlist_free(matcher_list);
1527 quicksearch->normal_search_strings =
1529 quicksearch->normal_search_strings,
1530 g_strdup(strings->data));
1533 } while ((strings = g_list_next(strings)) != NULL);
1535 matcher_parser_disable_warnings(FALSE);
1537 quicksearch->normal_search_strings = g_list_reverse(quicksearch->normal_search_strings);
1538 quicksearch->extended_search_strings = g_list_reverse(quicksearch->extended_search_strings);
1540 quicksearch_set_popdown_strings(quicksearch);
1544 * Searches in the supplied folderItem the messages (MessageInfo) matching a
1545 * QuickSearchType + search string (ex.: QUICK_SEARCH_FROM and "foo@bar.com").
1547 * Found messages are appended to the array 'messages' and their ref.counts
1548 * are incremented by 1 --so they need to be released (procmsg_msginfo_free())
1549 * before the array 'messages' is freed.
1551 void search_msgs_in_folder(GSList **messages, QuickSearch* quicksearch,
1552 FolderItem* folderItem)
1554 /* from quicksearch_match_subfolder */
1555 GSList *msglist = NULL;
1558 /* The list is built w/ MsgInfo items whose ref.counts are incremented,
1559 but they are decremented when the list is freed by
1560 procmsg_msg_list_free(): we'll ask for a new ref., below
1562 msglist = folder_item_get_msg_list(folderItem);
1564 for (cur = msglist; cur != NULL; cur = cur->next) {
1565 MsgInfo *msg = (MsgInfo *)cur->data;
1566 if (quicksearch_match(quicksearch, msg)) {
1567 /*debug_print("found: %s from:%s\n",procmsg_get_message_file_path(msg),msg->from);*/
1568 *messages = g_slist_prepend(*messages, procmsg_msginfo_new_ref(msg));
1570 /* See 2.0.0cvs140 ChangeLog entry for details
1571 see also comments in quicksearch_match() */
1572 if (quicksearch_from_gui(quicksearch)==TRUE
1573 && !quicksearch_is_active(quicksearch))
1576 procmsg_msg_list_free(msglist);
1580 * Searches within the folderItem and its sub-folders (if recursive is TRUE)
1581 * the messages matching the search request.
1583 * NB: search within a Folder can be done this way:
1584 * search_msg_in_folders(messages, quicksearch, searchType,
1585 * FOLDER_ITEM(folder->node->data), TRUE);
1587 void search_msgs_in_folders(GSList **messages, QuickSearch* quicksearch,
1588 FolderItem* folderItem)
1590 FolderItem *cur = NULL;
1592 search_msgs_in_folder(messages, quicksearch, folderItem);
1593 if (quicksearch->request->recursive == FALSE)
1596 GNode *node = folderItem->node->children;
1597 for (; node != NULL; node = node->next) {
1598 cur = FOLDER_ITEM(node->data);
1599 debug_print("in: %s\n",cur->path);
1600 if (cur->node->children)
1601 search_msgs_in_folders(messages, quicksearch, cur);
1603 search_msgs_in_folder(messages, quicksearch, cur);
1605 *messages = g_slist_reverse(*messages);
1609 * Returns the QuickSearchType associated to the supplied string.
1611 QuickSearchType quicksearch_type(const gchar* type)
1613 QuickSearchType searchType = QUICK_SEARCH_EXTENDED;
1616 switch(toupper(*type)) {
1618 searchType = QUICK_SEARCH_SUBJECT;
1621 searchType = QUICK_SEARCH_FROM;
1624 searchType = QUICK_SEARCH_TO;
1627 searchType = QUICK_SEARCH_EXTENDED;
1630 searchType = QUICK_SEARCH_MIXED;
1633 searchType = QUICK_SEARCH_TAG;