19f365a79627f9500bad8aa1a4691979bddc86cc
[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
60 static struct SummarySearchWindow {
61         GtkWidget *window;
62
63         GtkWidget *bool_optmenu;
64
65         GtkWidget *from_entry;
66         GtkWidget *to_entry;
67         GtkWidget *subject_entry;
68         GtkWidget *body_entry;
69
70         GtkWidget *adv_condition_entry;
71         GtkWidget *adv_condition_btn;
72         GtkWidget *adv_search_checkbtn;
73
74         GtkWidget *case_checkbtn;
75
76         GtkWidget *clear_btn;
77         GtkWidget *all_btn;
78         GtkWidget *prev_btn;
79         GtkWidget *next_btn;
80         GtkWidget *close_btn;
81
82         SummaryView *summaryview;
83
84         MatcherList                     *matcher_list;
85 } search_window;
86
87 static void summary_search_create       (void);
88
89 static void summary_search_execute      (gboolean        backward,
90                                          gboolean        search_all);
91
92 static void summary_search_clear        (GtkButton      *button,
93                                          gpointer        data);
94 static void summary_search_prev_clicked (GtkButton      *button,
95                                          gpointer        data);
96 static void summary_search_next_clicked (GtkButton      *button,
97                                          gpointer        data);
98 static void summary_search_all_clicked  (GtkButton      *button,
99                                          gpointer        data);
100 static void adv_condition_btn_clicked   (GtkButton      *button,
101                                          gpointer        data);
102
103 static void from_activated              (void);
104 static void to_activated                (void);
105 static void subject_activated           (void);
106 static void body_activated              (void);
107 static void adv_condition_activated     (void);
108
109 static gboolean key_pressed             (GtkWidget      *widget,
110                                          GdkEventKey    *event,
111                                          gpointer        data);
112
113
114 void summary_search(SummaryView *summaryview)
115 {
116         if (!search_window.window)
117                 summary_search_create();
118         else
119                 gtk_widget_hide(search_window.window);
120
121         search_window.summaryview = summaryview;
122
123         gtk_widget_grab_focus(search_window.next_btn);
124         gtk_widget_grab_focus(search_window.subject_entry);
125         gtk_widget_show(search_window.window);
126 }
127
128 static void summary_search_create(void)
129 {
130         GtkWidget *window;
131         GtkWidget *vbox1;
132         GtkWidget *bool_hbox;
133         GtkWidget *bool_optmenu;
134         GtkWidget *bool_menu;
135         GtkWidget *menuitem;
136         GtkWidget *clear_btn;
137
138         GtkWidget *table1;
139         GtkWidget *from_label;
140         GtkWidget *from_entry;
141         GtkWidget *to_label;
142         GtkWidget *to_entry;
143         GtkWidget *subject_label;
144         GtkWidget *subject_entry;
145         GtkWidget *body_label;
146         GtkWidget *body_entry;
147         GtkWidget *adv_condition_label;
148         GtkWidget *adv_condition_entry;
149         GtkWidget *adv_condition_btn;
150
151         GtkWidget *checkbtn_hbox;
152         GtkWidget *adv_search_checkbtn;
153         GtkWidget *case_checkbtn;
154
155         GtkWidget *confirm_area;
156         GtkWidget *all_btn;
157         GtkWidget *prev_btn;
158         GtkWidget *next_btn;
159         GtkWidget *close_btn;
160
161         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
162         gtk_window_set_title(GTK_WINDOW (window), _("Search messages"));
163         gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, TRUE);
164         gtk_container_set_border_width(GTK_CONTAINER (window), 8);
165         g_signal_connect(G_OBJECT(window), "delete_event",
166                          G_CALLBACK(gtk_widget_hide_on_delete), NULL);
167         g_signal_connect(G_OBJECT(window), "key_press_event",
168                          G_CALLBACK(key_pressed), NULL);
169         MANAGE_WINDOW_SIGNALS_CONNECT(window);
170
171         vbox1 = gtk_vbox_new (FALSE, 0);
172         gtk_widget_show (vbox1);
173         gtk_container_add (GTK_CONTAINER (window), vbox1);
174
175         bool_hbox = gtk_hbox_new(FALSE, 4);
176         gtk_widget_show(bool_hbox);
177         gtk_box_pack_start(GTK_BOX(vbox1), bool_hbox, FALSE, FALSE, 0);
178
179         bool_optmenu = gtk_option_menu_new();
180         gtk_widget_show(bool_optmenu);
181         gtk_box_pack_start(GTK_BOX(bool_hbox), bool_optmenu, FALSE, FALSE, 0);
182
183         bool_menu = gtk_menu_new();
184         MENUITEM_ADD(bool_menu, menuitem, _("Match any of the following"), 0);
185         MENUITEM_ADD(bool_menu, menuitem, _("Match all of the following"), 1);
186         gtk_option_menu_set_menu(GTK_OPTION_MENU(bool_optmenu), bool_menu);
187
188         clear_btn = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
189         gtk_widget_show(clear_btn);
190         gtk_box_pack_end(GTK_BOX(bool_hbox), clear_btn, FALSE, FALSE, 0);
191
192         table1 = gtk_table_new (5, 3, FALSE);
193         gtk_widget_show (table1);
194         gtk_box_pack_start (GTK_BOX (vbox1), table1, TRUE, TRUE, 0);
195         gtk_container_set_border_width (GTK_CONTAINER (table1), 4);
196         gtk_table_set_row_spacings (GTK_TABLE (table1), 8);
197         gtk_table_set_col_spacings (GTK_TABLE (table1), 8);
198
199         from_entry = gtk_entry_new ();
200         gtk_widget_show (from_entry);
201         gtk_table_attach (GTK_TABLE (table1), from_entry, 1, 3, 0, 1,
202                           GTK_EXPAND|GTK_FILL, 0, 0, 0);
203         g_signal_connect(G_OBJECT(from_entry), "activate",
204                          G_CALLBACK(from_activated), NULL);
205
206         to_entry = gtk_entry_new ();
207         gtk_widget_show (to_entry);
208         gtk_table_attach (GTK_TABLE (table1), to_entry, 1, 3, 1, 2,
209                           GTK_EXPAND|GTK_FILL, 0, 0, 0);
210         g_signal_connect(G_OBJECT(to_entry), "activate",
211                          G_CALLBACK(to_activated), NULL);
212
213         subject_entry = gtk_entry_new ();
214         gtk_widget_show (subject_entry);
215         gtk_table_attach (GTK_TABLE (table1), subject_entry, 1, 3, 2, 3,
216                           GTK_EXPAND|GTK_FILL, 0, 0, 0);
217         g_signal_connect(G_OBJECT(subject_entry), "activate",
218                          G_CALLBACK(subject_activated), NULL);
219
220         body_entry = gtk_entry_new ();
221         gtk_widget_show (body_entry);
222         gtk_table_attach (GTK_TABLE (table1), body_entry, 1, 3, 3, 4,
223                           GTK_EXPAND|GTK_FILL, 0, 0, 0);
224         g_signal_connect(G_OBJECT(body_entry), "activate",
225                          G_CALLBACK(body_activated), NULL);
226
227         adv_condition_entry = gtk_entry_new ();
228         gtk_widget_show (adv_condition_entry);
229         gtk_table_attach (GTK_TABLE (table1), adv_condition_entry, 1, 2, 4, 5,
230                           GTK_EXPAND|GTK_FILL, 0, 0, 0);
231         g_signal_connect(G_OBJECT(adv_condition_entry), "activate",
232                          G_CALLBACK(adv_condition_activated), NULL);
233
234         adv_condition_btn = gtk_button_new_with_label (_(" ... "));
235         gtk_widget_show (adv_condition_btn);
236         gtk_table_attach (GTK_TABLE (table1), adv_condition_btn, 2, 3, 4, 5,
237                           GTK_FILL, 0, 0, 0);
238         g_signal_connect(G_OBJECT (adv_condition_btn), "clicked",
239                          G_CALLBACK(adv_condition_btn_clicked), search_window.window);
240
241         from_label = gtk_label_new (_("From:"));
242         gtk_widget_show (from_label);
243         gtk_table_attach (GTK_TABLE (table1), from_label, 0, 1, 0, 1,
244                           GTK_FILL, 0, 0, 0);
245         gtk_label_set_justify (GTK_LABEL (from_label), GTK_JUSTIFY_RIGHT);
246         gtk_misc_set_alignment (GTK_MISC (from_label), 1, 0.5);
247
248         to_label = gtk_label_new (_("To:"));
249         gtk_widget_show (to_label);
250         gtk_table_attach (GTK_TABLE (table1), to_label, 0, 1, 1, 2,
251                           GTK_FILL, 0, 0, 0);
252         gtk_label_set_justify (GTK_LABEL (to_label), GTK_JUSTIFY_RIGHT);
253         gtk_misc_set_alignment (GTK_MISC (to_label), 1, 0.5);
254
255         subject_label = gtk_label_new (_("Subject:"));
256         gtk_widget_show (subject_label);
257         gtk_table_attach (GTK_TABLE (table1), subject_label, 0, 1, 2, 3,
258                           GTK_FILL, 0, 0, 0);
259         gtk_label_set_justify (GTK_LABEL (subject_label), GTK_JUSTIFY_RIGHT);
260         gtk_misc_set_alignment (GTK_MISC (subject_label), 1, 0.5);
261
262         body_label = gtk_label_new (_("Body:"));
263         gtk_widget_show (body_label);
264         gtk_table_attach (GTK_TABLE (table1), body_label, 0, 1, 3, 4,
265                           GTK_FILL, 0, 0, 0);
266         gtk_label_set_justify (GTK_LABEL (body_label), GTK_JUSTIFY_RIGHT);
267         gtk_misc_set_alignment (GTK_MISC (body_label), 1, 0.5);
268
269         adv_condition_label = gtk_label_new (_("Condition:"));
270         gtk_widget_show (adv_condition_label);
271         gtk_table_attach (GTK_TABLE (table1), adv_condition_label, 0, 1, 4, 5,
272                           GTK_FILL, 0, 0, 0);
273         gtk_label_set_justify (GTK_LABEL (adv_condition_label), GTK_JUSTIFY_RIGHT);
274         gtk_misc_set_alignment (GTK_MISC (adv_condition_label), 1, 0.5);
275
276         checkbtn_hbox = gtk_hbox_new (FALSE, 8);
277         gtk_widget_show (checkbtn_hbox);
278         gtk_box_pack_start (GTK_BOX (vbox1), checkbtn_hbox, TRUE, TRUE, 0);
279         gtk_container_set_border_width (GTK_CONTAINER (checkbtn_hbox), 8);
280
281         case_checkbtn = gtk_check_button_new_with_label (_("Case sensitive"));
282         gtk_widget_show (case_checkbtn);
283         gtk_box_pack_start (GTK_BOX (checkbtn_hbox), case_checkbtn,
284                             FALSE, FALSE, 0);
285
286         adv_search_checkbtn = gtk_check_button_new_with_label (_("Advanced search"));
287         gtk_widget_show (adv_search_checkbtn);
288         gtk_box_pack_start (GTK_BOX (checkbtn_hbox), adv_search_checkbtn,
289                             FALSE, FALSE, 0);
290
291         confirm_area = gtk_hbutton_box_new();
292         gtk_widget_show (confirm_area);
293         gtk_button_box_set_layout(GTK_BUTTON_BOX(confirm_area),
294                                   GTK_BUTTONBOX_END);
295         gtk_box_set_spacing(GTK_BOX(confirm_area), 5);
296
297         all_btn = gtk_button_new_with_mnemonic(_("Find _all"));
298         GTK_WIDGET_SET_FLAGS(all_btn, GTK_CAN_DEFAULT);
299         gtk_box_pack_start(GTK_BOX(confirm_area), all_btn, TRUE, TRUE, 0);
300         gtk_widget_show(all_btn);
301
302         prev_btn = gtk_button_new_from_stock(GTK_STOCK_GO_BACK);
303         GTK_WIDGET_SET_FLAGS(prev_btn, GTK_CAN_DEFAULT);
304         gtk_box_pack_start(GTK_BOX(confirm_area), prev_btn, TRUE, TRUE, 0);
305         gtk_widget_show(prev_btn);
306
307         next_btn = gtk_button_new_from_stock(GTK_STOCK_GO_FORWARD);
308         GTK_WIDGET_SET_FLAGS(next_btn, GTK_CAN_DEFAULT);
309         gtk_box_pack_start(GTK_BOX(confirm_area), next_btn, TRUE, TRUE, 0);
310         gtk_widget_show(next_btn);
311
312         close_btn = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
313         GTK_WIDGET_SET_FLAGS(close_btn, GTK_CAN_DEFAULT);
314         gtk_box_pack_start(GTK_BOX(confirm_area), close_btn, TRUE, TRUE, 0);
315         gtk_widget_show(close_btn);
316
317         gtk_box_pack_start (GTK_BOX (vbox1), confirm_area, FALSE, FALSE, 0);
318         gtk_widget_grab_default(next_btn);
319
320         SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, bool_menu)
321         SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, from_entry)
322         SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, to_entry)
323         SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, subject_entry)
324         SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, body_entry)
325         SET_TOGGLE_SENSITIVITY(adv_search_checkbtn, adv_condition_label)
326         SET_TOGGLE_SENSITIVITY(adv_search_checkbtn, adv_condition_entry)
327         SET_TOGGLE_SENSITIVITY(adv_search_checkbtn, adv_condition_btn)
328         SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, case_checkbtn)
329
330         g_signal_connect(G_OBJECT(clear_btn), "clicked",
331                          G_CALLBACK(summary_search_clear), NULL);
332         g_signal_connect(G_OBJECT(all_btn), "clicked",
333                          G_CALLBACK(summary_search_all_clicked), NULL);
334         g_signal_connect(G_OBJECT(prev_btn), "clicked",
335                          G_CALLBACK(summary_search_prev_clicked), NULL);
336         g_signal_connect(G_OBJECT(next_btn), "clicked",
337                          G_CALLBACK(summary_search_next_clicked), NULL);
338         g_signal_connect_closure
339                 (G_OBJECT(close_btn), "clicked",
340                  g_cclosure_new_swap(G_CALLBACK(gtk_widget_hide),
341              window, NULL), FALSE);
342
343         search_window.window = window;
344         search_window.bool_optmenu = bool_optmenu;
345         search_window.from_entry = from_entry;
346         search_window.to_entry = to_entry;
347         search_window.subject_entry = subject_entry;
348         search_window.body_entry = body_entry;
349         search_window.adv_condition_entry = adv_condition_entry;
350         search_window.adv_condition_btn = adv_condition_btn;
351         search_window.case_checkbtn = case_checkbtn;
352         search_window.adv_search_checkbtn = adv_search_checkbtn;
353         search_window.clear_btn = clear_btn;
354         search_window.all_btn = all_btn;
355         search_window.prev_btn = prev_btn;
356         search_window.next_btn = next_btn;
357         search_window.close_btn = close_btn;
358         search_window.matcher_list = NULL;
359 }
360
361 static void summary_search_execute(gboolean backward, gboolean search_all)
362 {
363         SummaryView *summaryview = search_window.summaryview;
364         GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
365         GtkCTreeNode *node;
366         MsgInfo *msginfo;
367         gboolean adv_search;
368         gboolean bool_and = FALSE;
369         gboolean case_sens = FALSE;
370         gboolean all_searched = FALSE;
371         gboolean matched = FALSE;
372         gboolean body_matched = FALSE;
373         const gchar *from_str = NULL, *to_str = NULL, *subject_str = NULL;
374         const gchar *body_str = NULL, *adv_condition = NULL;
375         StrFindFunc str_find_func = NULL;
376
377         if (summary_is_locked(summaryview)) return;
378         summary_lock(summaryview);
379
380         adv_search = gtk_toggle_button_get_active
381                 (GTK_TOGGLE_BUTTON(search_window.adv_search_checkbtn));
382
383         if (adv_search) {
384                 if (search_window.matcher_list != NULL) {
385                         matcherlist_free(search_window.matcher_list);
386                         search_window.matcher_list = NULL;
387                 }
388                 adv_condition = gtk_entry_get_text(GTK_ENTRY(search_window.adv_condition_entry));
389                 if (adv_condition[0] != '\0') {
390                         search_window.matcher_list = matcher_parser_get_cond((gchar*)adv_condition);
391                 } else {
392                         summary_unlock(summaryview);
393                         return;
394                 }
395         } else {
396                 bool_and = GPOINTER_TO_INT
397                         (menu_get_option_menu_active_user_data
398                                 (GTK_OPTION_MENU(search_window.bool_optmenu)));
399                 case_sens = gtk_toggle_button_get_active
400                         (GTK_TOGGLE_BUTTON(search_window.case_checkbtn));
401
402                 if (case_sens)
403                         str_find_func = str_find;
404                 else
405                         str_find_func = str_case_find;
406
407                 from_str    = gtk_entry_get_text(GTK_ENTRY(search_window.from_entry));
408                 to_str      = gtk_entry_get_text(GTK_ENTRY(search_window.to_entry));
409                 subject_str = gtk_entry_get_text(GTK_ENTRY(search_window.subject_entry));
410                 body_str    = gtk_entry_get_text(GTK_ENTRY(search_window.body_entry));
411         }
412
413         if (search_all) {
414                 gtk_clist_freeze(GTK_CLIST(ctree));
415                 gtk_clist_unselect_all(GTK_CLIST(ctree));
416                 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
417                 backward = FALSE;
418         } else if (!summaryview->selected) {
419                 if (backward)
420                         node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list_end);
421                 else
422                         node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
423
424                 if (!node) {
425                         summary_unlock(summaryview);
426                         return;
427                 }
428         } else {
429                 if (backward)
430                         node = gtkut_ctree_node_prev
431                                 (ctree, summaryview->selected);
432                 else
433                         node = gtkut_ctree_node_next
434                                 (ctree, summaryview->selected);
435         }
436
437         if (body_str && *body_str)
438                 main_window_cursor_wait(summaryview->mainwin);
439
440         for (;;) {
441                 if (!node) {
442                         gchar *str;
443                         AlertValue val;
444
445                         if (search_all) {
446                                 gtk_clist_thaw(GTK_CLIST(ctree));
447                                 break;
448                         }
449
450                         if (all_searched) {
451                                 alertpanel_full(_("Search failed"),
452                                                 _("Search string not found."),
453                                                 GTK_STOCK_CLOSE, NULL, NULL, FALSE,
454                                                 NULL, ALERT_WARNING, G_ALERTDEFAULT);
455                                 break;
456                         }
457
458                         if (backward)
459                                 str = _("Beginning of list reached; continue from end?");
460                         else
461                                 str = _("End of list reached; continue from beginning?");
462
463                         val = alertpanel(_("Search finished"), str,
464                                          GTK_STOCK_NO, GTK_STOCK_YES, NULL);
465                         if (G_ALERTALTERNATE == val) {
466                                 if (backward)
467                                         node = GTK_CTREE_NODE
468                                                 (GTK_CLIST(ctree)->row_list_end);
469                                 else
470                                         node = GTK_CTREE_NODE
471                                                 (GTK_CLIST(ctree)->row_list);
472
473                                 all_searched = TRUE;
474
475                                 manage_window_focus_in(search_window.window, NULL, NULL);
476                         } else
477                                 break;
478                 }
479
480                 msginfo = gtk_ctree_node_get_row_data(ctree, node);
481                 body_matched = FALSE;
482
483                 if (adv_search) {
484                         matched = matcherlist_match(search_window.matcher_list, msginfo);
485                 } else {
486                         if (bool_and) {
487                                 matched = TRUE;
488                                 if (*from_str) {
489                                         if (!msginfo->from ||
490                                         !str_find_func(msginfo->from, from_str))
491                                                 matched = FALSE;
492                                 }
493                                 if (matched && *to_str) {
494                                         if (!msginfo->to ||
495                                         !str_find_func(msginfo->to, to_str))
496                                                 matched = FALSE;
497                                 }
498                                 if (matched && *subject_str) {
499                                         if (!msginfo->subject ||
500                                         !str_find_func(msginfo->subject, subject_str))
501                                                 matched = FALSE;
502                                 }
503                                 if (matched && *body_str) {
504                                         if (procmime_find_string(msginfo, body_str,
505                                                                  str_find_func))
506                                                 body_matched = TRUE;
507                                         else
508                                                 matched = FALSE;
509                                 }
510                                 if (matched && !*from_str && !*to_str &&
511                                 !*subject_str && !*body_str)
512                                         matched = FALSE;
513                         } else {
514                                 matched = FALSE;
515                                 if (*from_str && msginfo->from) {
516                                         if (str_find_func(msginfo->from, from_str))
517                                                 matched = TRUE;
518                                 }
519                                 if (!matched && *to_str && msginfo->to) {
520                                         if (str_find_func(msginfo->to, to_str))
521                                                 matched = TRUE;
522                                 }
523                                 if (!matched && *subject_str && msginfo->subject) {
524                                         if (str_find_func(msginfo->subject, subject_str))
525                                                 matched = TRUE;
526                                 }
527                                 if (!matched && *body_str) {
528                                         if (procmime_find_string(msginfo, body_str,
529                                                                  str_find_func)) {
530                                                 matched = TRUE;
531                                                 body_matched = TRUE;
532                                         }
533                                 }
534                         }
535                 }
536
537                 if (matched) {
538                         if (search_all)
539                                 gtk_ctree_select(ctree, node);
540                         else {
541                                 if (messageview_is_visible
542                                         (summaryview->messageview)) {
543                                         summary_unlock(summaryview);
544                                         summary_select_node
545                                                 (summaryview, node, TRUE, TRUE);
546                                         summary_lock(summaryview);
547                                         if (body_matched) {
548                                                 messageview_search_string
549                                                         (summaryview->messageview,
550                                                          body_str, case_sens);
551                                         }
552                                 } else {
553                                         summary_select_node
554                                                 (summaryview, node, FALSE, TRUE);
555                                 }
556                                 break;
557                         }
558                 }
559
560                 node = backward ? gtkut_ctree_node_prev(ctree, node)
561                                 : gtkut_ctree_node_next(ctree, node);
562         }
563
564         if (body_str && *body_str)
565                 main_window_cursor_normal(summaryview->mainwin);
566
567         summary_unlock(summaryview);
568 }
569
570 static void summary_search_clear(GtkButton *button, gpointer data)
571 {
572         if (gtk_toggle_button_get_active
573                 (GTK_TOGGLE_BUTTON(search_window.adv_search_checkbtn))) {
574                 gtk_editable_delete_text(GTK_EDITABLE(search_window.adv_condition_entry),
575                                          0, -1);
576         } else {
577                 gtk_editable_delete_text(GTK_EDITABLE(search_window.from_entry),
578                                          0, -1);
579                 gtk_editable_delete_text(GTK_EDITABLE(search_window.to_entry),
580                                          0, -1);
581                 gtk_editable_delete_text(GTK_EDITABLE(search_window.subject_entry),
582                                          0, -1);
583                 gtk_editable_delete_text(GTK_EDITABLE(search_window.body_entry),
584                                          0, -1);
585         }
586 }
587
588 static void summary_search_prev_clicked(GtkButton *button, gpointer data)
589 {
590         summary_search_execute(TRUE, FALSE);
591 }
592
593 static void summary_search_next_clicked(GtkButton *button, gpointer data)
594 {
595         summary_search_execute(FALSE, FALSE);
596 }
597
598 static void summary_search_all_clicked(GtkButton *button, gpointer data)
599 {
600         summary_search_execute(FALSE, TRUE);
601 }
602
603 static void adv_condition_btn_done(MatcherList * matchers)
604 {
605         gchar *str;
606
607         g_return_if_fail(
608                         mainwindow_get_mainwindow()->summaryview->quicksearch != NULL);
609
610         if (matchers == NULL)
611                 return;
612
613         str = matcherlist_to_string(matchers);
614
615         if (str != NULL) {
616                 gtk_entry_set_text(
617                         GTK_ENTRY(search_window.adv_condition_entry), str);
618                 g_free(str);
619         }
620 }
621
622 static void adv_condition_btn_clicked(GtkButton *button, gpointer data)
623 {
624         const gchar * cond_str;
625         MatcherList * matchers = NULL;
626
627         g_return_if_fail( search_window.window != NULL );
628
629         /* re-use it the current quicksearch value if it's a condition expression,
630            otherwise ignore it silently */
631         cond_str = gtk_entry_get_text(
632                         GTK_ENTRY(search_window.adv_condition_entry));
633         if (*cond_str != '\0') {
634                 matchers = matcher_parser_get_cond((gchar*)cond_str);
635         }
636
637         prefs_matcher_open(matchers, adv_condition_btn_done);
638
639         if (matchers != NULL)
640                 matcherlist_free(matchers);
641 };
642                 
643 static void from_activated(void)
644 {
645         gtk_widget_grab_focus(search_window.to_entry);
646 }
647
648 static void to_activated(void)
649 {
650         gtk_widget_grab_focus(search_window.subject_entry);
651 }
652
653 static void subject_activated(void)
654 {
655         gtk_button_clicked(GTK_BUTTON(search_window.next_btn));
656 }
657
658 static void body_activated(void)
659 {
660         gtk_button_clicked(GTK_BUTTON(search_window.next_btn));
661 }
662
663 static void adv_condition_activated(void)
664 {
665         gtk_button_clicked(GTK_BUTTON(search_window.next_btn));
666 }
667
668 static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event,
669                             gpointer data)
670 {
671         if (event && event->keyval == GDK_Escape)
672                 gtk_widget_hide(search_window.window);
673         return FALSE;
674 }