2006-08-25 [colin] 2.4.0cvs92
[claws.git] / src / summary_search.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2006 Hiroyuki Yamamoto and the Sylpheed-Claws team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <gtk/gtkwidget.h>
30 #include <gtk/gtkwindow.h>
31 #include <gtk/gtkvbox.h>
32 #include <gtk/gtktable.h>
33 #include <gtk/gtkoptionmenu.h>
34 #include <gtk/gtklabel.h>
35 #include <gtk/gtkentry.h>
36 #include <gtk/gtkhbox.h>
37 #include <gtk/gtkcheckbutton.h>
38 #include <gtk/gtkhbbox.h>
39 #include <gtk/gtkbutton.h>
40 #include <gtk/gtkctree.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44
45 #include "main.h"
46 #include "summary_search.h"
47 #include "summaryview.h"
48 #include "messageview.h"
49 #include "mainwindow.h"
50 #include "menu.h"
51 #include "utils.h"
52 #include "gtkutils.h"
53 #include "prefs_gtk.h"
54 #include "manage_window.h"
55 #include "alertpanel.h"
56 #include "matcher.h"
57 #include "matcher_parser.h"
58 #include "prefs_matcher.h"
59 #include "manual.h"
60
61 static struct SummarySearchWindow {
62         GtkWidget *window;
63
64         GtkWidget *bool_optmenu;
65
66         GtkWidget *from_entry;
67         GtkWidget *to_entry;
68         GtkWidget *subject_entry;
69         GtkWidget *body_entry;
70
71         GtkWidget *adv_condition_entry;
72         GtkWidget *adv_condition_btn;
73         GtkWidget *adv_search_checkbtn;
74
75         GtkWidget *case_checkbtn;
76
77         GtkWidget *clear_btn;
78         GtkWidget *help_btn;
79         GtkWidget *all_btn;
80         GtkWidget *prev_btn;
81         GtkWidget *next_btn;
82         GtkWidget *close_btn;
83         GtkWidget *stop_btn;
84
85         SummaryView *summaryview;
86
87         MatcherList                     *matcher_list;
88
89         gboolean is_searching;
90 } search_window;
91
92 static void summary_search_create       (void);
93
94 static void summary_search_execute      (gboolean        backward,
95                                          gboolean        search_all);
96
97 static void summary_search_clear        (GtkButton      *button,
98                                          gpointer        data);
99 static void summary_search_prev_clicked (GtkButton      *button,
100                                          gpointer        data);
101 static void summary_search_next_clicked (GtkButton      *button,
102                                          gpointer        data);
103 static void summary_search_all_clicked  (GtkButton      *button,
104                                          gpointer        data);
105 static void summary_search_stop_clicked (GtkButton      *button,
106                                          gpointer        data);
107 static void adv_condition_btn_clicked   (GtkButton      *button,
108                                          gpointer        data);
109
110 static void from_activated              (void);
111 static void to_activated                (void);
112 static void subject_activated           (void);
113 static void body_activated              (void);
114 static void adv_condition_activated     (void);
115
116 static gboolean key_pressed             (GtkWidget      *widget,
117                                          GdkEventKey    *event,
118                                          gpointer        data);
119
120
121 void summary_search(SummaryView *summaryview)
122 {
123         if (!search_window.window) {
124                 summary_search_create();
125         } else {
126                 gtk_widget_hide(search_window.window);
127         }
128
129         search_window.summaryview = summaryview;
130
131         gtk_widget_grab_focus(search_window.next_btn);
132         gtk_widget_grab_focus(search_window.subject_entry);
133         gtk_widget_show(search_window.window);
134 }
135
136 static void summary_search_create(void)
137 {
138         GtkWidget *window;
139         GtkWidget *vbox1;
140         GtkWidget *bool_hbox;
141         GtkWidget *bool_optmenu;
142         GtkWidget *bool_menu;
143         GtkWidget *menuitem;
144         GtkWidget *clear_btn;
145
146         GtkWidget *table1;
147         GtkWidget *from_label;
148         GtkWidget *from_entry;
149         GtkWidget *to_label;
150         GtkWidget *to_entry;
151         GtkWidget *subject_label;
152         GtkWidget *subject_entry;
153         GtkWidget *body_label;
154         GtkWidget *body_entry;
155         GtkWidget *adv_condition_label;
156         GtkWidget *adv_condition_entry;
157         GtkWidget *adv_condition_btn;
158
159         GtkWidget *checkbtn_hbox;
160         GtkWidget *adv_search_checkbtn;
161         GtkWidget *case_checkbtn;
162
163         GtkWidget *confirm_area;
164         GtkWidget *help_btn;
165         GtkWidget *all_btn;
166         GtkWidget *prev_btn;
167         GtkWidget *next_btn;
168         GtkWidget *close_btn;
169         GtkWidget *stop_btn;
170         GtkTooltips *tooltip;
171
172         gboolean is_searching = FALSE;
173
174         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
175         gtk_window_set_title(GTK_WINDOW (window), _("Search messages"));
176         gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, TRUE);
177         gtk_container_set_border_width(GTK_CONTAINER (window), 8);
178         g_signal_connect(G_OBJECT(window), "delete_event",
179                          G_CALLBACK(gtk_widget_hide_on_delete), NULL);
180         g_signal_connect(G_OBJECT(window), "key_press_event",
181                          G_CALLBACK(key_pressed), NULL);
182         MANAGE_WINDOW_SIGNALS_CONNECT(window);
183
184         vbox1 = gtk_vbox_new (FALSE, 0);
185         gtk_widget_show (vbox1);
186         gtk_container_add (GTK_CONTAINER (window), vbox1);
187
188         bool_hbox = gtk_hbox_new(FALSE, 4);
189         gtk_widget_show(bool_hbox);
190         gtk_box_pack_start(GTK_BOX(vbox1), bool_hbox, FALSE, FALSE, 0);
191
192         bool_optmenu = gtk_option_menu_new();
193         gtk_widget_show(bool_optmenu);
194         gtk_box_pack_start(GTK_BOX(bool_hbox), bool_optmenu, FALSE, FALSE, 0);
195
196         bool_menu = gtk_menu_new();
197         MENUITEM_ADD(bool_menu, menuitem, _("Match any of the following"), 0);
198         MENUITEM_ADD(bool_menu, menuitem, _("Match all of the following"), 1);
199         gtk_option_menu_set_menu(GTK_OPTION_MENU(bool_optmenu), bool_menu);
200
201         clear_btn = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
202         gtk_widget_show(clear_btn);
203         gtk_box_pack_end(GTK_BOX(bool_hbox), clear_btn, FALSE, FALSE, 0);
204
205         table1 = gtk_table_new (5, 3, FALSE);
206         gtk_widget_show (table1);
207         gtk_box_pack_start (GTK_BOX (vbox1), table1, TRUE, TRUE, 0);
208         gtk_container_set_border_width (GTK_CONTAINER (table1), 4);
209         gtk_table_set_row_spacings (GTK_TABLE (table1), 8);
210         gtk_table_set_col_spacings (GTK_TABLE (table1), 8);
211
212         from_entry = gtk_entry_new ();
213         gtk_widget_show (from_entry);
214         gtk_table_attach (GTK_TABLE (table1), from_entry, 1, 3, 0, 1,
215                           GTK_EXPAND|GTK_FILL, 0, 0, 0);
216         g_signal_connect(G_OBJECT(from_entry), "activate",
217                          G_CALLBACK(from_activated), NULL);
218
219         to_entry = gtk_entry_new ();
220         gtk_widget_show (to_entry);
221         gtk_table_attach (GTK_TABLE (table1), to_entry, 1, 3, 1, 2,
222                           GTK_EXPAND|GTK_FILL, 0, 0, 0);
223         g_signal_connect(G_OBJECT(to_entry), "activate",
224                          G_CALLBACK(to_activated), NULL);
225
226         subject_entry = gtk_entry_new ();
227         gtk_widget_show (subject_entry);
228         gtk_table_attach (GTK_TABLE (table1), subject_entry, 1, 3, 2, 3,
229                           GTK_EXPAND|GTK_FILL, 0, 0, 0);
230         g_signal_connect(G_OBJECT(subject_entry), "activate",
231                          G_CALLBACK(subject_activated), NULL);
232
233         body_entry = gtk_entry_new ();
234         gtk_widget_show (body_entry);
235         gtk_table_attach (GTK_TABLE (table1), body_entry, 1, 3, 3, 4,
236                           GTK_EXPAND|GTK_FILL, 0, 0, 0);
237         g_signal_connect(G_OBJECT(body_entry), "activate",
238                          G_CALLBACK(body_activated), NULL);
239
240         adv_condition_entry = gtk_entry_new ();
241         gtk_widget_show (adv_condition_entry);
242         gtk_table_attach (GTK_TABLE (table1), adv_condition_entry, 1, 2, 4, 5,
243                           GTK_EXPAND|GTK_FILL, 0, 0, 0);
244         g_signal_connect(G_OBJECT(adv_condition_entry), "activate",
245                          G_CALLBACK(adv_condition_activated), NULL);
246
247         adv_condition_btn = gtk_button_new_with_label(" ... ");
248         gtk_widget_show (adv_condition_btn);
249         gtk_table_attach (GTK_TABLE (table1), adv_condition_btn, 2, 3, 4, 5,
250                           GTK_FILL, 0, 0, 0);
251         g_signal_connect(G_OBJECT (adv_condition_btn), "clicked",
252                          G_CALLBACK(adv_condition_btn_clicked), search_window.window);
253         tooltip = gtk_tooltips_new();
254         gtk_tooltips_set_tip(GTK_TOOLTIPS(tooltip), adv_condition_btn,
255                              _("Edit search criteria"), NULL);
256
257         from_label = gtk_label_new (_("From:"));
258         gtk_widget_show (from_label);
259         gtk_table_attach (GTK_TABLE (table1), from_label, 0, 1, 0, 1,
260                           GTK_FILL, 0, 0, 0);
261         gtk_label_set_justify (GTK_LABEL (from_label), GTK_JUSTIFY_RIGHT);
262         gtk_misc_set_alignment (GTK_MISC (from_label), 1, 0.5);
263
264         to_label = gtk_label_new (_("To:"));
265         gtk_widget_show (to_label);
266         gtk_table_attach (GTK_TABLE (table1), to_label, 0, 1, 1, 2,
267                           GTK_FILL, 0, 0, 0);
268         gtk_label_set_justify (GTK_LABEL (to_label), GTK_JUSTIFY_RIGHT);
269         gtk_misc_set_alignment (GTK_MISC (to_label), 1, 0.5);
270
271         subject_label = gtk_label_new (_("Subject:"));
272         gtk_widget_show (subject_label);
273         gtk_table_attach (GTK_TABLE (table1), subject_label, 0, 1, 2, 3,
274                           GTK_FILL, 0, 0, 0);
275         gtk_label_set_justify (GTK_LABEL (subject_label), GTK_JUSTIFY_RIGHT);
276         gtk_misc_set_alignment (GTK_MISC (subject_label), 1, 0.5);
277
278         body_label = gtk_label_new (_("Body:"));
279         gtk_widget_show (body_label);
280         gtk_table_attach (GTK_TABLE (table1), body_label, 0, 1, 3, 4,
281                           GTK_FILL, 0, 0, 0);
282         gtk_label_set_justify (GTK_LABEL (body_label), GTK_JUSTIFY_RIGHT);
283         gtk_misc_set_alignment (GTK_MISC (body_label), 1, 0.5);
284
285         adv_condition_label = gtk_label_new (_("Condition:"));
286         gtk_widget_show (adv_condition_label);
287         gtk_table_attach (GTK_TABLE (table1), adv_condition_label, 0, 1, 4, 5,
288                           GTK_FILL, 0, 0, 0);
289         gtk_label_set_justify (GTK_LABEL (adv_condition_label), GTK_JUSTIFY_RIGHT);
290         gtk_misc_set_alignment (GTK_MISC (adv_condition_label), 1, 0.5);
291
292         checkbtn_hbox = gtk_hbox_new (FALSE, 8);
293         gtk_widget_show (checkbtn_hbox);
294         gtk_box_pack_start (GTK_BOX (vbox1), checkbtn_hbox, TRUE, TRUE, 0);
295         gtk_container_set_border_width (GTK_CONTAINER (checkbtn_hbox), 8);
296
297         case_checkbtn = gtk_check_button_new_with_label (_("Case sensitive"));
298         gtk_widget_show (case_checkbtn);
299         gtk_box_pack_start (GTK_BOX (checkbtn_hbox), case_checkbtn,
300                             FALSE, FALSE, 0);
301
302         adv_search_checkbtn = gtk_check_button_new_with_label (_("Extended Search"));
303         gtk_widget_show (adv_search_checkbtn);
304         gtk_box_pack_start (GTK_BOX (checkbtn_hbox), adv_search_checkbtn,
305                             FALSE, FALSE, 0);
306
307         confirm_area = gtk_hbutton_box_new();
308         gtk_widget_show (confirm_area);
309         gtk_button_box_set_layout(GTK_BUTTON_BOX(confirm_area),
310                                   GTK_BUTTONBOX_END);
311         gtk_box_set_spacing(GTK_BOX(confirm_area), 5);
312
313         gtkut_stock_button_add_help(confirm_area, &help_btn);
314
315         all_btn = gtk_button_new_with_mnemonic(_("Find _all"));
316         GTK_WIDGET_SET_FLAGS(all_btn, GTK_CAN_DEFAULT);
317         gtk_box_pack_start(GTK_BOX(confirm_area), all_btn, TRUE, TRUE, 0);
318         gtk_widget_show(all_btn);
319
320         prev_btn = gtk_button_new_from_stock(GTK_STOCK_GO_BACK);
321         GTK_WIDGET_SET_FLAGS(prev_btn, GTK_CAN_DEFAULT);
322         gtk_box_pack_start(GTK_BOX(confirm_area), prev_btn, TRUE, TRUE, 0);
323         gtk_widget_show(prev_btn);
324
325         next_btn = gtk_button_new_from_stock(GTK_STOCK_GO_FORWARD);
326         GTK_WIDGET_SET_FLAGS(next_btn, GTK_CAN_DEFAULT);
327         gtk_box_pack_start(GTK_BOX(confirm_area), next_btn, TRUE, TRUE, 0);
328         gtk_widget_show(next_btn);
329
330         close_btn = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
331         GTK_WIDGET_SET_FLAGS(close_btn, GTK_CAN_DEFAULT);
332         gtk_box_pack_start(GTK_BOX(confirm_area), close_btn, TRUE, TRUE, 0);
333         gtk_widget_show(close_btn);
334
335         /* stop button hidden */
336         stop_btn = gtk_button_new_from_stock(GTK_STOCK_STOP);
337         GTK_WIDGET_SET_FLAGS(stop_btn, GTK_CAN_DEFAULT);
338         gtk_box_pack_start(GTK_BOX(confirm_area), stop_btn, TRUE, TRUE, 0);
339
340         gtk_box_pack_start (GTK_BOX (vbox1), confirm_area, FALSE, FALSE, 0);
341         gtk_widget_grab_default(next_btn);
342
343         SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, bool_menu)
344         SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, from_entry)
345         SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, to_entry)
346         SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, subject_entry)
347         SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, body_entry)
348         SET_TOGGLE_SENSITIVITY(adv_search_checkbtn, adv_condition_label)
349         SET_TOGGLE_SENSITIVITY(adv_search_checkbtn, adv_condition_entry)
350         SET_TOGGLE_SENSITIVITY(adv_search_checkbtn, adv_condition_btn)
351         SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, case_checkbtn)
352
353         g_signal_connect(G_OBJECT(help_btn), "clicked",
354                          G_CALLBACK(manual_open_with_anchor_cb),
355                          MANUAL_ANCHOR_SEARCHING);
356         g_signal_connect(G_OBJECT(clear_btn), "clicked",
357                          G_CALLBACK(summary_search_clear), NULL);
358         g_signal_connect(G_OBJECT(all_btn), "clicked",
359                          G_CALLBACK(summary_search_all_clicked), NULL);
360         g_signal_connect(G_OBJECT(prev_btn), "clicked",
361                          G_CALLBACK(summary_search_prev_clicked), NULL);
362         g_signal_connect(G_OBJECT(next_btn), "clicked",
363                          G_CALLBACK(summary_search_next_clicked), NULL);
364         g_signal_connect_closure
365                 (G_OBJECT(close_btn), "clicked",
366                  g_cclosure_new_swap(G_CALLBACK(gtk_widget_hide),
367              window, NULL), FALSE);
368         g_signal_connect(G_OBJECT(stop_btn), "clicked",
369                          G_CALLBACK(summary_search_stop_clicked), NULL);
370
371         search_window.window = window;
372         search_window.bool_optmenu = bool_optmenu;
373         search_window.from_entry = from_entry;
374         search_window.to_entry = to_entry;
375         search_window.subject_entry = subject_entry;
376         search_window.body_entry = body_entry;
377         search_window.adv_condition_entry = adv_condition_entry;
378         search_window.adv_condition_btn = adv_condition_btn;
379         search_window.case_checkbtn = case_checkbtn;
380         search_window.adv_search_checkbtn = adv_search_checkbtn;
381         search_window.clear_btn = clear_btn;
382         search_window.help_btn = help_btn;
383         search_window.all_btn = all_btn;
384         search_window.prev_btn = prev_btn;
385         search_window.next_btn = next_btn;
386         search_window.close_btn = close_btn;
387         search_window.stop_btn = stop_btn;
388         search_window.matcher_list = NULL;
389         search_window.is_searching = is_searching;
390 }
391
392 static void summary_search_execute(gboolean backward, gboolean search_all)
393 {
394         SummaryView *summaryview = search_window.summaryview;
395         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
396         GtkCTreeNode *node;
397         MsgInfo *msginfo;
398         gboolean adv_search;
399         gboolean bool_and = FALSE;
400         gboolean case_sens = FALSE;
401         gboolean all_searched = FALSE;
402         gboolean matched = FALSE;
403         gboolean body_matched = FALSE;
404         const gchar *from_str = NULL, *to_str = NULL, *subject_str = NULL;
405         const gchar *body_str = NULL, *adv_condition = NULL;
406         StrFindFunc str_find_func = NULL;
407
408         if (summary_is_locked(summaryview)) {
409                 return;
410         }
411         summary_lock(summaryview);
412
413         adv_search = gtk_toggle_button_get_active
414                 (GTK_TOGGLE_BUTTON(search_window.adv_search_checkbtn));
415
416         if (adv_search) {
417                 if (search_window.matcher_list != NULL) {
418                         matcherlist_free(search_window.matcher_list);
419                         search_window.matcher_list = NULL;
420                 }
421                 adv_condition = gtk_entry_get_text(GTK_ENTRY(search_window.adv_condition_entry));
422                 if (adv_condition[0] != '\0') {
423                         search_window.matcher_list = matcher_parser_get_cond((gchar*)adv_condition);
424                         /* TODO: check for condition parsing error and show an error dialog */
425                 } else {
426                         /* TODO: warn if no search condition? (or make buttons enabled only when
427                                 at least one search condition has been set */
428                         summary_unlock(summaryview);
429                         return;
430                 }
431         } else {
432                 bool_and = GPOINTER_TO_INT
433                         (menu_get_option_menu_active_user_data
434                                 (GTK_OPTION_MENU(search_window.bool_optmenu)));
435                 case_sens = gtk_toggle_button_get_active
436                         (GTK_TOGGLE_BUTTON(search_window.case_checkbtn));
437
438                 if (case_sens) {
439                         str_find_func = str_find;
440                 } else {
441                         str_find_func = str_case_find;
442                 }
443
444                 from_str    = gtk_entry_get_text(GTK_ENTRY(search_window.from_entry));
445                 to_str      = gtk_entry_get_text(GTK_ENTRY(search_window.to_entry));
446                 subject_str = gtk_entry_get_text(GTK_ENTRY(search_window.subject_entry));
447                 body_str    = gtk_entry_get_text(GTK_ENTRY(search_window.body_entry));
448
449                 if (    (from_str[0] == '\0') &&
450                                 (to_str[0] == '\0') &&
451                                 (subject_str[0] == '\0') &&
452                                 (body_str[0] == '\0')) {
453                         /* TODO: warn if no search criteria? (or make buttons enabled only when
454                                 at least one search criteria has been set */
455                         summary_unlock(summaryview);
456                         return;
457                 }
458         }
459
460         search_window.is_searching = TRUE;
461         main_window_cursor_wait(summaryview->mainwin);
462         gtk_widget_hide(search_window.close_btn);
463         gtk_widget_show(search_window.stop_btn);
464
465         if (search_all) {
466                 gtk_clist_freeze(GTK_CLIST(ctree));
467                 gtk_clist_unselect_all(GTK_CLIST(ctree));
468                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
469                 backward = FALSE;
470         } else if (!summaryview->selected) {
471                 if (backward) {
472                         node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list_end);
473                 } else {
474                         node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
475                 }
476
477                 if (!node) {
478                         search_window.is_searching = FALSE;
479                         gtk_widget_hide(search_window.stop_btn);
480                         gtk_widget_show(search_window.close_btn);
481                         main_window_cursor_normal(summaryview->mainwin);
482                         summary_unlock(summaryview);
483                         return;
484                 }
485         } else {
486                 if (backward) {
487                         node = gtkut_ctree_node_prev
488                                 (ctree, summaryview->selected);
489                 } else {
490                         node = gtkut_ctree_node_next
491                                 (ctree, summaryview->selected);
492                 }
493         }
494
495         for (; search_window.is_searching;) {
496                 if (!node) {
497                         gchar *str;
498                         AlertValue val;
499
500                         if (search_all) {
501                                 break;
502                         }
503
504                         if (all_searched) {
505                                 alertpanel_full(_("Search failed"),
506                                                 _("Search string not found."),
507                                                 GTK_STOCK_CLOSE, NULL, NULL, FALSE,
508                                                 NULL, ALERT_WARNING, G_ALERTDEFAULT);
509                                 break;
510                         }
511
512                         if (backward)
513                                 str = _("Beginning of list reached; continue from end?");
514                         else
515                                 str = _("End of list reached; continue from beginning?");
516
517                         val = alertpanel(_("Search finished"), str,
518                                          GTK_STOCK_NO, "+" GTK_STOCK_YES, NULL);
519                         if (G_ALERTALTERNATE == val) {
520                                 if (backward) {
521                                         node = GTK_CTREE_NODE
522                                                 (GTK_CLIST(ctree)->row_list_end);
523                                 } else {
524                                         node = GTK_CTREE_NODE
525                                                 (GTK_CLIST(ctree)->row_list);
526                                 }
527
528                                 all_searched = TRUE;
529
530                                 manage_window_focus_in(search_window.window, NULL, NULL);
531                         } else {
532                                 break;
533                         }
534                 }
535
536                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
537                 body_matched = FALSE;
538
539                 if (adv_search) {
540                         matched = matcherlist_match(search_window.matcher_list, msginfo);
541                 } else {
542                         if (bool_and) {
543                                 matched = TRUE;
544                                 if (*from_str) {
545                                         if (!msginfo->from ||
546                                         !str_find_func(msginfo->from, from_str)) {
547                                                 matched = FALSE;
548                                         }
549                                 }
550                                 if (matched && *to_str) {
551                                         if (!msginfo->to ||
552                                         !str_find_func(msginfo->to, to_str)) {
553                                                 matched = FALSE;
554                                         }
555                                 }
556                                 if (matched && *subject_str) {
557                                         if (!msginfo->subject ||
558                                         !str_find_func(msginfo->subject, subject_str)) {
559                                                 matched = FALSE;
560                                         }
561                                 }
562                                 if (matched && *body_str) {
563                                         if (procmime_find_string(msginfo, body_str,
564                                                                  str_find_func)) {
565                                                 body_matched = TRUE;
566                                         } else {
567                                                 matched = FALSE;
568                                         }
569                                 }
570                                 if (matched && !*from_str && !*to_str &&
571                                 !*subject_str && !*body_str) {
572                                         matched = FALSE;
573                                 }
574                         } else {
575                                 matched = FALSE;
576                                 if (*from_str && msginfo->from) {
577                                         if (str_find_func(msginfo->from, from_str)) {
578                                                 matched = TRUE;
579                                         }
580                                 }
581                                 if (!matched && *to_str && msginfo->to) {
582                                         if (str_find_func(msginfo->to, to_str)) {
583                                                 matched = TRUE;
584                                         }
585                                 }
586                                 if (!matched && *subject_str && msginfo->subject) {
587                                         if (str_find_func(msginfo->subject, subject_str)) {
588                                                 matched = TRUE;
589                                         }
590                                 }
591                                 if (!matched && *body_str) {
592                                         if (procmime_find_string(msginfo, body_str,
593                                                                  str_find_func)) {
594                                                 matched = TRUE;
595                                                 body_matched = TRUE;
596                                         }
597                                 }
598                         }
599                 }
600
601                 if (matched) {
602                         if (search_all) {
603                                 gtk_ctree_select(ctree, node);
604                         } else {
605                                 if (messageview_is_visible
606                                         (summaryview->messageview)) {
607                                         summary_unlock(summaryview);
608                                         summary_select_node
609                                                 (summaryview, node, TRUE, TRUE);
610                                         summary_lock(summaryview);
611                                         if (body_matched) {
612                                                 messageview_search_string
613                                                         (summaryview->messageview,
614                                                          body_str, case_sens);
615                                         }
616                                 } else {
617                                         summary_select_node
618                                                 (summaryview, node, FALSE, TRUE);
619                                 }
620                                 break;
621                         }
622                 }
623
624                 node = backward ? gtkut_ctree_node_prev(ctree, node)
625                                 : gtkut_ctree_node_next(ctree, node);
626
627                 GTK_EVENTS_FLUSH();
628         }
629
630         search_window.is_searching = FALSE;
631         gtk_widget_hide(search_window.stop_btn);
632         gtk_widget_show(search_window.close_btn);
633         main_window_cursor_normal(summaryview->mainwin);
634         if (search_all) {
635                 gtk_clist_thaw(GTK_CLIST(ctree));
636         }
637         summary_unlock(summaryview);
638 }
639
640 static void summary_search_clear(GtkButton *button, gpointer data)
641 {
642         if (gtk_toggle_button_get_active
643                 (GTK_TOGGLE_BUTTON(search_window.adv_search_checkbtn))) {
644                 gtk_editable_delete_text(GTK_EDITABLE(search_window.adv_condition_entry),
645                                          0, -1);
646         } else {
647                 gtk_editable_delete_text(GTK_EDITABLE(search_window.from_entry),
648                                          0, -1);
649                 gtk_editable_delete_text(GTK_EDITABLE(search_window.to_entry),
650                                          0, -1);
651                 gtk_editable_delete_text(GTK_EDITABLE(search_window.subject_entry),
652                                          0, -1);
653                 gtk_editable_delete_text(GTK_EDITABLE(search_window.body_entry),
654                                          0, -1);
655         }
656         /* stop searching */
657         if (search_window.is_searching) {
658                 search_window.is_searching = FALSE;
659         }
660 }
661
662 static void summary_search_prev_clicked(GtkButton *button, gpointer data)
663 {
664         summary_search_execute(TRUE, FALSE);
665 }
666
667 static void summary_search_next_clicked(GtkButton *button, gpointer data)
668 {
669         summary_search_execute(FALSE, FALSE);
670 }
671
672 static void summary_search_all_clicked(GtkButton *button, gpointer data)
673 {
674         summary_search_execute(FALSE, TRUE);
675 }
676
677 static void adv_condition_btn_done(MatcherList * matchers)
678 {
679         gchar *str;
680
681         g_return_if_fail(
682                         mainwindow_get_mainwindow()->summaryview->quicksearch != NULL);
683
684         if (matchers == NULL) {
685                 return;
686         }
687
688         str = matcherlist_to_string(matchers);
689
690         if (str != NULL) {
691                 gtk_entry_set_text(
692                         GTK_ENTRY(search_window.adv_condition_entry), str);
693                 g_free(str);
694         }
695 }
696
697 static void summary_search_stop_clicked(GtkButton *button, gpointer data)
698 {
699         search_window.is_searching = FALSE;
700 }
701
702 static void adv_condition_btn_clicked(GtkButton *button, gpointer data)
703 {
704         const gchar * cond_str;
705         MatcherList * matchers = NULL;
706
707         g_return_if_fail( search_window.window != NULL );
708
709         /* re-use it the current quicksearch value if it's a condition expression,
710            otherwise ignore it silently */
711         cond_str = gtk_entry_get_text(
712                         GTK_ENTRY(search_window.adv_condition_entry));
713         if (*cond_str != '\0') {
714                 matchers = matcher_parser_get_cond((gchar*)cond_str);
715         }
716
717         prefs_matcher_open(matchers, adv_condition_btn_done);
718
719         if (matchers != NULL) {
720                 matcherlist_free(matchers);
721         }
722 };
723
724 static void from_activated(void)
725 {
726         gtk_widget_grab_focus(search_window.to_entry);
727 }
728
729 static void to_activated(void)
730 {
731         gtk_widget_grab_focus(search_window.subject_entry);
732 }
733
734 static void subject_activated(void)
735 {
736         gtk_button_clicked(GTK_BUTTON(search_window.next_btn));
737 }
738
739 static void body_activated(void)
740 {
741         gtk_button_clicked(GTK_BUTTON(search_window.next_btn));
742 }
743
744 static void adv_condition_activated(void)
745 {
746         gtk_button_clicked(GTK_BUTTON(search_window.next_btn));
747 }
748
749 static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event,
750                             gpointer data)
751 {
752         if (event && event->keyval == GDK_Escape) {
753                 /* ESC key will:
754                         - stop a running search
755                         - close the search window if no search is running
756                 */
757                 if (!search_window.is_searching) {
758                         gtk_widget_hide(search_window.window);
759                 } else {
760                         search_window.is_searching = FALSE;
761                 }
762         }
763         return FALSE;
764 }