Merge branch 'master' of ssh+git://git.claws-mail.org/home/git/claws
[claws.git] / src / summary_search.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2013 Hiroyuki Yamamoto and the Claws Mail 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 3 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, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24
25 #include "defs.h"
26
27 #include <glib.h>
28 #include <glib/gi18n.h>
29 #include <gdk/gdkkeysyms.h>
30 #include <gtk/gtk.h>
31 #if !GTK_CHECK_VERSION(3, 0, 0)
32 #include "gtk/gtksctree.h"
33 #endif
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37
38 #include "main.h"
39 #include "summary_search.h"
40 #include "summaryview.h"
41 #include "messageview.h"
42 #include "mainwindow.h"
43 #include "menu.h"
44 #include "utils.h"
45 #include "gtkutils.h"
46 #include "combobox.h"
47 #include "prefs_gtk.h"
48 #include "manage_window.h"
49 #include "alertpanel.h"
50 #include "advsearch.h"
51 #include "matcher.h"
52 #include "matcher_parser.h"
53 #include "prefs_matcher.h"
54 #include "manual.h"
55 #include "prefs_common.h"
56 #include "statusbar.h"
57
58 static struct SummarySearchWindow {
59         GtkWidget *window;
60
61         GtkWidget *bool_optmenu;
62
63         GtkWidget *from_label;
64         GtkWidget *from_entry;
65         GtkWidget *to_label;
66         GtkWidget *to_entry;
67         GtkWidget *subject_label;
68         GtkWidget *subject_entry;
69         GtkWidget *body_label;
70         GtkWidget *body_entry;
71
72         GtkWidget *adv_condition_label;
73         GtkWidget *adv_condition_entry;
74         GtkWidget *adv_condition_btn;
75         GtkWidget *adv_search_checkbtn;
76
77         GtkWidget *case_checkbtn;
78
79         GtkWidget *clear_btn;
80         GtkWidget *help_btn;
81         GtkWidget *all_btn;
82         GtkWidget *prev_btn;
83         GtkWidget *next_btn;
84         GtkWidget *close_btn;
85         GtkWidget *stop_btn;
86
87         SummaryView *summaryview;
88
89         AdvancedSearch  *advsearch;
90         gboolean        is_fast;
91         gboolean        matcher_is_outdated;
92         gboolean        search_in_progress;
93         GHashTable      *matched_msgnums;
94
95         gboolean is_searching;
96         gboolean from_entry_has_focus;
97         gboolean to_entry_has_focus;
98         gboolean subject_entry_has_focus;
99         gboolean body_entry_has_focus;
100         gboolean adv_condition_entry_has_focus;
101 } search_window;
102
103 static gchar* add_history_get(GtkWidget *from, GList **history);
104
105 static void summary_search_create       (void);
106
107 static gboolean summary_search_verify_match             (MsgInfo *msg);
108 static gboolean summary_search_prepare_matcher          ();
109 static gboolean summary_search_prereduce_msg_list       ();
110
111 static void summary_search_execute      (gboolean        backward,
112                                          gboolean        search_all);
113
114 static void summary_search_clear        (GtkButton      *button,
115                                          gpointer        data);
116 static void summary_search_prev_clicked (GtkButton      *button,
117                                          gpointer        data);
118 static void summary_search_next_clicked (GtkButton      *button,
119                                          gpointer        data);
120 static void summary_search_all_clicked  (GtkButton      *button,
121                                          gpointer        data);
122 static void summary_search_stop_clicked (GtkButton      *button,
123                                          gpointer        data);
124 static void adv_condition_btn_clicked   (GtkButton      *button,
125                                          gpointer        data);
126
127 static void optmenu_changed                     (GtkComboBox *widget, gpointer user_data);
128 static void from_changed                        (void);
129 static void to_changed                          (void);
130 static void subject_changed                     (void);
131 static void body_changed                        (void);
132 static void adv_condition_changed       (void);
133 static void case_changed                        (GtkToggleButton *togglebutton, gpointer user_data);
134 static void adv_search_changed          (GtkToggleButton *togglebutton, gpointer user_data);
135
136 static gboolean from_entry_focus_evt_in(GtkWidget *widget, GdkEventFocus *event,
137                                   gpointer data);
138 static gboolean from_entry_focus_evt_out(GtkWidget *widget, GdkEventFocus *event,
139                                   gpointer data);
140 static gboolean to_entry_focus_evt_in(GtkWidget *widget, GdkEventFocus *event,
141                                   gpointer data);
142 static gboolean to_entry_focus_evt_out(GtkWidget *widget, GdkEventFocus *event,
143                                   gpointer data);
144 static gboolean subject_entry_focus_evt_in(GtkWidget *widget, GdkEventFocus *event,
145                                   gpointer data);
146 static gboolean subject_entry_focus_evt_out(GtkWidget *widget, GdkEventFocus *event,
147                                   gpointer data);
148 static gboolean body_entry_focus_evt_in(GtkWidget *widget, GdkEventFocus *event,
149                                   gpointer data);
150 static gboolean body_entry_focus_evt_out(GtkWidget *widget, GdkEventFocus *event,
151                                   gpointer data);
152 static gboolean adv_condition_entry_focus_evt_in(GtkWidget *widget, GdkEventFocus *event,
153                                   gpointer data);
154 static gboolean adv_condition_entry_focus_evt_out(GtkWidget *widget, GdkEventFocus *event,
155                                   gpointer data);
156 static gboolean key_pressed             (GtkWidget      *widget,
157                                          GdkEventKey    *event,
158                                          gpointer        data);
159
160 #define GTK_BUTTON_SET_SENSITIVE(widget,sensitive) {                                    \
161         gtk_widget_set_sensitive(widget, sensitive);                                    \
162 }
163
164 static gchar* add_history_get(GtkWidget *from, GList **history)
165 {
166         gchar *result;
167
168         result = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(from));
169         if (!result)
170                 result = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(from))), 0, -1);
171
172         if (result && result[0] != '\0') {
173                 /* add to history */
174                 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(from));
175                 *history = add_history(*history, result);
176                 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(from), *history);
177
178                 return result;
179         } else {
180                 g_free(result);
181                 return NULL;
182         }
183 }
184
185 void summary_search(SummaryView *summaryview)
186 {
187         if (!search_window.window) {
188                 summary_search_create();
189         } else {
190                 gtk_widget_hide(search_window.window);
191         }
192
193         search_window.summaryview = summaryview;
194
195         gtk_widget_grab_focus(search_window.next_btn);
196         gtk_widget_grab_focus(search_window.subject_entry);
197         gtk_widget_show(search_window.window);
198 }
199
200 static void summary_show_stop_button(void)
201 {
202         gtk_widget_hide(search_window.close_btn);
203         gtk_widget_show(search_window.stop_btn);
204         GTK_BUTTON_SET_SENSITIVE(search_window.clear_btn, FALSE)
205         GTK_BUTTON_SET_SENSITIVE(search_window.all_btn, FALSE)
206         GTK_BUTTON_SET_SENSITIVE(search_window.prev_btn, FALSE)
207         GTK_BUTTON_SET_SENSITIVE(search_window.next_btn, FALSE)
208         gtk_widget_set_sensitive(search_window.adv_condition_label, FALSE);
209         gtk_widget_set_sensitive(search_window.adv_condition_entry, FALSE);
210         gtk_widget_set_sensitive(search_window.adv_condition_btn, FALSE);
211         gtk_widget_set_sensitive(search_window.to_label, FALSE);
212         gtk_widget_set_sensitive(search_window.to_entry, FALSE);
213         gtk_widget_set_sensitive(search_window.from_label, FALSE);
214         gtk_widget_set_sensitive(search_window.from_entry, FALSE);
215         gtk_widget_set_sensitive(search_window.subject_label, FALSE);
216         gtk_widget_set_sensitive(search_window.subject_entry, FALSE);
217         gtk_widget_set_sensitive(search_window.body_label, FALSE);
218         gtk_widget_set_sensitive(search_window.body_entry, FALSE);
219         gtk_widget_set_sensitive(search_window.bool_optmenu, FALSE);
220         gtk_widget_set_sensitive(search_window.clear_btn, FALSE);
221         gtk_widget_set_sensitive(search_window.case_checkbtn, FALSE);
222         gtk_widget_set_sensitive(search_window.adv_search_checkbtn, FALSE);
223 }
224
225 static void summary_hide_stop_button(void)
226 {
227         GTK_BUTTON_SET_SENSITIVE(search_window.clear_btn, TRUE)
228         gtk_widget_hide(search_window.stop_btn);
229         gtk_widget_show(search_window.close_btn);
230         if (gtk_toggle_button_get_active
231                         (GTK_TOGGLE_BUTTON(search_window.adv_search_checkbtn))) {
232                 gtk_widget_set_sensitive(search_window.adv_condition_label, TRUE);
233                 gtk_widget_set_sensitive(search_window.adv_condition_entry, TRUE);
234                 gtk_widget_set_sensitive(search_window.adv_condition_btn, TRUE);
235                 gtk_widget_set_sensitive(search_window.case_checkbtn, FALSE);
236                 gtk_widget_set_sensitive(search_window.bool_optmenu, FALSE);
237         } else {
238                 gtk_widget_set_sensitive(search_window.to_label, TRUE);
239                 gtk_widget_set_sensitive(search_window.to_entry, TRUE);
240                 gtk_widget_set_sensitive(search_window.from_label, TRUE);
241                 gtk_widget_set_sensitive(search_window.from_entry, TRUE);
242                 gtk_widget_set_sensitive(search_window.subject_label, TRUE);
243                 gtk_widget_set_sensitive(search_window.subject_entry, TRUE);
244                 gtk_widget_set_sensitive(search_window.body_label, TRUE);
245                 gtk_widget_set_sensitive(search_window.body_entry, TRUE);
246                 gtk_widget_set_sensitive(search_window.case_checkbtn, TRUE);
247                 gtk_widget_set_sensitive(search_window.bool_optmenu, TRUE);
248         }
249         gtk_widget_set_sensitive(search_window.clear_btn, TRUE);
250         gtk_widget_set_sensitive(search_window.all_btn, TRUE);
251         gtk_widget_set_sensitive(search_window.prev_btn, TRUE);
252         gtk_widget_set_sensitive(search_window.next_btn, TRUE);
253         gtk_widget_set_sensitive(search_window.adv_search_checkbtn, TRUE);
254 }
255
256 static void summary_search_create(void)
257 {
258         GtkWidget *window;
259         GtkWidget *vbox1;
260         GtkWidget *bool_hbox;
261         GtkWidget *bool_optmenu;
262         GtkListStore *menu;
263         GtkTreeIter iter;
264         GtkWidget *clear_btn;
265
266         GtkWidget *table1;
267         GtkWidget *from_label;
268         GtkWidget *from_entry;
269         GtkWidget *to_label;
270         GtkWidget *to_entry;
271         GtkWidget *subject_label;
272         GtkWidget *subject_entry;
273         GtkWidget *body_label;
274         GtkWidget *body_entry;
275         GtkWidget *adv_condition_label;
276         GtkWidget *adv_condition_entry;
277         GtkWidget *adv_condition_btn;
278
279         GtkWidget *checkbtn_hbox;
280         GtkWidget *adv_search_checkbtn;
281         GtkWidget *case_checkbtn;
282
283         GtkWidget *confirm_area;
284         GtkWidget *help_btn;
285         GtkWidget *all_btn;
286         GtkWidget *prev_btn;
287         GtkWidget *next_btn;
288         GtkWidget *close_btn;
289         GtkWidget *stop_btn;
290         gboolean is_searching = FALSE;
291
292         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "summary_search");
293         gtk_window_set_title(GTK_WINDOW (window), _("Search messages"));
294         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
295         gtk_container_set_border_width(GTK_CONTAINER (window), 8);
296         g_signal_connect(G_OBJECT(window), "delete_event",
297                          G_CALLBACK(gtk_widget_hide_on_delete), NULL);
298         g_signal_connect(G_OBJECT(window), "key_press_event",
299                          G_CALLBACK(key_pressed), NULL);
300         MANAGE_WINDOW_SIGNALS_CONNECT(window);
301
302         vbox1 = gtk_vbox_new (FALSE, 0);
303         gtk_widget_show (vbox1);
304         gtk_container_add (GTK_CONTAINER (window), vbox1);
305
306         bool_hbox = gtk_hbox_new(FALSE, 4);
307         gtk_widget_show(bool_hbox);
308         gtk_box_pack_start(GTK_BOX(vbox1), bool_hbox, FALSE, FALSE, 0);
309
310         bool_optmenu = gtkut_sc_combobox_create(NULL, FALSE);
311         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(bool_optmenu)));
312         gtk_widget_show(bool_optmenu);
313         gtk_box_pack_start(GTK_BOX(bool_hbox), bool_optmenu, FALSE, FALSE, 0);
314
315         COMBOBOX_ADD(menu, _("Match any of the following"), 0);
316         gtk_combo_box_set_active_iter(GTK_COMBO_BOX(bool_optmenu), &iter);
317         COMBOBOX_ADD(menu, _("Match all of the following"), 1);
318         g_signal_connect(G_OBJECT(bool_optmenu), "changed",
319                          G_CALLBACK(optmenu_changed), NULL);
320
321         clear_btn = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
322         gtk_widget_show(clear_btn);
323         gtk_box_pack_end(GTK_BOX(bool_hbox), clear_btn, FALSE, FALSE, 0);
324
325         table1 = gtk_table_new (5, 3, FALSE);
326         gtk_widget_show (table1);
327         gtk_box_pack_start (GTK_BOX (vbox1), table1, TRUE, TRUE, 0);
328         gtk_container_set_border_width (GTK_CONTAINER (table1), 4);
329         gtk_table_set_row_spacings (GTK_TABLE (table1), 8);
330         gtk_table_set_col_spacings (GTK_TABLE (table1), 8);
331
332         from_entry = gtk_combo_box_text_new_with_entry ();
333         gtk_combo_box_set_active(GTK_COMBO_BOX(from_entry), -1);
334         if (prefs_common.summary_search_from_history)
335                 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(from_entry),
336                                 prefs_common.summary_search_from_history);
337         gtk_widget_show (from_entry);
338         gtk_table_attach (GTK_TABLE (table1), from_entry, 1, 3, 0, 1,
339                           GTK_EXPAND|GTK_FILL, 0, 0, 0);
340         g_signal_connect(G_OBJECT(from_entry), "changed",
341                          G_CALLBACK(from_changed), NULL);
342         g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((from_entry)))),
343                          "focus_in_event", G_CALLBACK(from_entry_focus_evt_in), NULL);
344         g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((from_entry)))),
345                          "focus_out_event", G_CALLBACK(from_entry_focus_evt_out), NULL);
346
347         to_entry = gtk_combo_box_text_new_with_entry ();
348         gtk_combo_box_set_active(GTK_COMBO_BOX(to_entry), -1);
349         if (prefs_common.summary_search_to_history)
350                 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(to_entry),
351                                 prefs_common.summary_search_to_history);
352         gtk_widget_show (to_entry);
353         gtk_table_attach (GTK_TABLE (table1), to_entry, 1, 3, 1, 2,
354                           GTK_EXPAND|GTK_FILL, 0, 0, 0);
355         g_signal_connect(G_OBJECT(to_entry), "changed",
356                          G_CALLBACK(to_changed), NULL);
357         g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((to_entry)))),
358                          "focus_in_event", G_CALLBACK(to_entry_focus_evt_in), NULL);
359         g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((to_entry)))),
360                          "focus_out_event", G_CALLBACK(to_entry_focus_evt_out), NULL);
361
362         subject_entry = gtk_combo_box_text_new_with_entry ();
363         gtk_combo_box_set_active(GTK_COMBO_BOX(subject_entry), -1);
364         if (prefs_common.summary_search_subject_history)
365                 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(subject_entry),
366                                 prefs_common.summary_search_subject_history);
367         gtk_widget_show (subject_entry);
368         gtk_table_attach (GTK_TABLE (table1), subject_entry, 1, 3, 2, 3,
369                           GTK_EXPAND|GTK_FILL, 0, 0, 0);
370         g_signal_connect(G_OBJECT(subject_entry), "changed",
371                          G_CALLBACK(subject_changed), NULL);
372         g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((subject_entry)))),
373                          "focus_in_event", G_CALLBACK(subject_entry_focus_evt_in), NULL);
374         g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((subject_entry)))),
375                          "focus_out_event", G_CALLBACK(subject_entry_focus_evt_out), NULL);
376
377         body_entry = gtk_combo_box_text_new_with_entry ();
378         gtk_combo_box_set_active(GTK_COMBO_BOX(body_entry), -1);
379         if (prefs_common.summary_search_body_history)
380                 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(body_entry),
381                                 prefs_common.summary_search_body_history);
382         gtk_widget_show (body_entry);
383         gtk_table_attach (GTK_TABLE (table1), body_entry, 1, 3, 3, 4,
384                           GTK_EXPAND|GTK_FILL, 0, 0, 0);
385         g_signal_connect(G_OBJECT(body_entry), "changed",
386                          G_CALLBACK(body_changed), NULL);
387         g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((body_entry)))),
388                          "focus_in_event", G_CALLBACK(body_entry_focus_evt_in), NULL);
389         g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((body_entry)))),
390                          "focus_out_event", G_CALLBACK(body_entry_focus_evt_out), NULL);
391
392         adv_condition_entry = gtk_combo_box_text_new_with_entry ();
393         gtk_combo_box_set_active(GTK_COMBO_BOX(adv_condition_entry), -1);
394         if (prefs_common.summary_search_adv_condition_history)
395                 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(adv_condition_entry),
396                                 prefs_common.summary_search_adv_condition_history);
397         gtk_widget_show (adv_condition_entry);
398         gtk_table_attach (GTK_TABLE (table1), adv_condition_entry, 1, 2, 4, 5,
399                           GTK_EXPAND|GTK_FILL, 0, 0, 0);
400         g_signal_connect(G_OBJECT(adv_condition_entry), "changed",
401                          G_CALLBACK(adv_condition_changed), NULL);
402         g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((adv_condition_entry)))),
403                          "focus_in_event", G_CALLBACK(adv_condition_entry_focus_evt_in), NULL);
404         g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((adv_condition_entry)))),
405                          "focus_out_event", G_CALLBACK(adv_condition_entry_focus_evt_out), NULL);
406
407         adv_condition_btn = gtk_button_new_with_label(" ... ");
408         gtk_widget_show (adv_condition_btn);
409         gtk_table_attach (GTK_TABLE (table1), adv_condition_btn, 2, 3, 4, 5,
410                           GTK_FILL, 0, 0, 0);
411         g_signal_connect(G_OBJECT (adv_condition_btn), "clicked",
412                          G_CALLBACK(adv_condition_btn_clicked), search_window.window);
413
414         CLAWS_SET_TIP(adv_condition_btn,
415                              _("Edit search criteria"));
416
417         from_label = gtk_label_new (_("From:"));
418         gtk_widget_show (from_label);
419         gtk_table_attach (GTK_TABLE (table1), from_label, 0, 1, 0, 1,
420                           GTK_FILL, 0, 0, 0);
421         gtk_label_set_justify (GTK_LABEL (from_label), GTK_JUSTIFY_RIGHT);
422         gtk_misc_set_alignment (GTK_MISC (from_label), 1, 0.5);
423
424         to_label = gtk_label_new (_("To:"));
425         gtk_widget_show (to_label);
426         gtk_table_attach (GTK_TABLE (table1), to_label, 0, 1, 1, 2,
427                           GTK_FILL, 0, 0, 0);
428         gtk_label_set_justify (GTK_LABEL (to_label), GTK_JUSTIFY_RIGHT);
429         gtk_misc_set_alignment (GTK_MISC (to_label), 1, 0.5);
430
431         subject_label = gtk_label_new (_("Subject:"));
432         gtk_widget_show (subject_label);
433         gtk_table_attach (GTK_TABLE (table1), subject_label, 0, 1, 2, 3,
434                           GTK_FILL, 0, 0, 0);
435         gtk_label_set_justify (GTK_LABEL (subject_label), GTK_JUSTIFY_RIGHT);
436         gtk_misc_set_alignment (GTK_MISC (subject_label), 1, 0.5);
437
438         body_label = gtk_label_new (_("Body:"));
439         gtk_widget_show (body_label);
440         gtk_table_attach (GTK_TABLE (table1), body_label, 0, 1, 3, 4,
441                           GTK_FILL, 0, 0, 0);
442         gtk_label_set_justify (GTK_LABEL (body_label), GTK_JUSTIFY_RIGHT);
443         gtk_misc_set_alignment (GTK_MISC (body_label), 1, 0.5);
444
445         adv_condition_label = gtk_label_new (_("Condition:"));
446         gtk_widget_show (adv_condition_label);
447         gtk_table_attach (GTK_TABLE (table1), adv_condition_label, 0, 1, 4, 5,
448                           GTK_FILL, 0, 0, 0);
449         gtk_label_set_justify (GTK_LABEL (adv_condition_label), GTK_JUSTIFY_RIGHT);
450         gtk_misc_set_alignment (GTK_MISC (adv_condition_label), 1, 0.5);
451
452         checkbtn_hbox = gtk_hbox_new (FALSE, 8);
453         gtk_widget_show (checkbtn_hbox);
454         gtk_box_pack_start (GTK_BOX (vbox1), checkbtn_hbox, TRUE, TRUE, 0);
455         gtk_container_set_border_width (GTK_CONTAINER (checkbtn_hbox), 8);
456
457         case_checkbtn = gtk_check_button_new_with_label (_("Case sensitive"));
458         gtk_widget_show (case_checkbtn);
459         gtk_box_pack_start (GTK_BOX (checkbtn_hbox), case_checkbtn,
460                             FALSE, FALSE, 0);
461         g_signal_connect(G_OBJECT(case_checkbtn), "changed",
462                          G_CALLBACK(case_changed), NULL);
463
464         adv_search_checkbtn = gtk_check_button_new_with_label (_("Extended Search"));
465         gtk_widget_show (adv_search_checkbtn);
466         gtk_box_pack_start (GTK_BOX (checkbtn_hbox), adv_search_checkbtn,
467                             FALSE, FALSE, 0);
468         g_signal_connect(G_OBJECT(adv_search_checkbtn), "changed",
469                          G_CALLBACK(case_changed), NULL);
470
471         confirm_area = gtk_hbutton_box_new();
472         gtk_widget_show (confirm_area);
473         gtk_button_box_set_layout(GTK_BUTTON_BOX(confirm_area),
474                                   GTK_BUTTONBOX_END);
475         gtk_box_set_spacing(GTK_BOX(confirm_area), 5);
476
477         gtkut_stock_button_add_help(confirm_area, &help_btn);
478
479         all_btn = gtk_button_new_with_mnemonic(_("Find _all"));
480         gtk_widget_set_can_default(all_btn, TRUE);
481         gtk_box_pack_start(GTK_BOX(confirm_area), all_btn, TRUE, TRUE, 0);
482         gtk_widget_show(all_btn);
483
484         prev_btn = gtk_button_new_from_stock(GTK_STOCK_GO_BACK);
485         gtk_widget_set_can_default(prev_btn, TRUE);
486         gtk_box_pack_start(GTK_BOX(confirm_area), prev_btn, TRUE, TRUE, 0);
487         gtk_widget_show(prev_btn);
488
489         next_btn = gtk_button_new_from_stock(GTK_STOCK_GO_FORWARD);
490         gtk_widget_set_can_default(next_btn, TRUE);
491         gtk_box_pack_start(GTK_BOX(confirm_area), next_btn, TRUE, TRUE, 0);
492         gtk_widget_show(next_btn);
493
494         close_btn = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
495         gtk_widget_set_can_default(close_btn, TRUE);
496         gtk_box_pack_start(GTK_BOX(confirm_area), close_btn, TRUE, TRUE, 0);
497         gtk_widget_show(close_btn);
498
499         /* stop button hidden */
500         stop_btn = gtk_button_new_from_stock(GTK_STOCK_STOP);
501         gtk_widget_set_can_default(stop_btn, TRUE);
502         gtk_box_pack_start(GTK_BOX(confirm_area), stop_btn, TRUE, TRUE, 0);
503
504         gtk_box_pack_start (GTK_BOX (vbox1), confirm_area, FALSE, FALSE, 0);
505         gtk_widget_grab_default(next_btn);
506
507         SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, bool_optmenu)
508         SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, from_label)
509         SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, from_entry)
510         SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, to_label)
511         SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, to_entry)
512         SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, subject_label)
513         SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, subject_entry)
514         SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, body_label)
515         SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, body_entry)
516         SET_TOGGLE_SENSITIVITY(adv_search_checkbtn, adv_condition_label)
517         SET_TOGGLE_SENSITIVITY(adv_search_checkbtn, adv_condition_entry)
518         SET_TOGGLE_SENSITIVITY(adv_search_checkbtn, adv_condition_btn)
519         SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, case_checkbtn)
520
521         g_signal_connect(G_OBJECT(help_btn), "clicked",
522                          G_CALLBACK(manual_open_with_anchor_cb),
523                          MANUAL_ANCHOR_SEARCHING);
524         g_signal_connect(G_OBJECT(clear_btn), "clicked",
525                          G_CALLBACK(summary_search_clear), NULL);
526         g_signal_connect(G_OBJECT(all_btn), "clicked",
527                          G_CALLBACK(summary_search_all_clicked), NULL);
528         g_signal_connect(G_OBJECT(prev_btn), "clicked",
529                          G_CALLBACK(summary_search_prev_clicked), NULL);
530         g_signal_connect(G_OBJECT(next_btn), "clicked",
531                          G_CALLBACK(summary_search_next_clicked), NULL);
532         g_signal_connect_closure
533                 (G_OBJECT(close_btn), "clicked",
534                  g_cclosure_new_swap(G_CALLBACK(gtk_widget_hide),
535              window, NULL), FALSE);
536         g_signal_connect(G_OBJECT(stop_btn), "clicked",
537                          G_CALLBACK(summary_search_stop_clicked), NULL);
538
539         search_window.window = window;
540         search_window.bool_optmenu = bool_optmenu;
541         search_window.from_label = from_label;
542         search_window.from_entry = from_entry;
543         search_window.to_label = to_label;
544         search_window.to_entry = to_entry;
545         search_window.subject_label = subject_label;
546         search_window.subject_entry = subject_entry;
547         search_window.body_label = body_label;
548         search_window.body_entry = body_entry;
549         search_window.adv_condition_label = adv_condition_label;
550         search_window.adv_condition_entry = adv_condition_entry;
551         search_window.adv_condition_btn = adv_condition_btn;
552         search_window.case_checkbtn = case_checkbtn;
553         search_window.adv_search_checkbtn = adv_search_checkbtn;
554         search_window.clear_btn = clear_btn;
555         search_window.help_btn = help_btn;
556         search_window.all_btn = all_btn;
557         search_window.prev_btn = prev_btn;
558         search_window.next_btn = next_btn;
559         search_window.close_btn = close_btn;
560         search_window.stop_btn = stop_btn;
561         search_window.advsearch = NULL;
562         search_window.matcher_is_outdated = TRUE;
563         search_window.search_in_progress = FALSE;
564         search_window.matched_msgnums = NULL;
565         search_window.is_searching = is_searching;
566 }
567
568 static gboolean summary_search_verify_match(MsgInfo *msg)
569 {
570         gpointer msgnum = GUINT_TO_POINTER(msg->msgnum);
571
572         if (g_hash_table_lookup(search_window.matched_msgnums, msgnum) != NULL)
573                 return TRUE;
574         else
575                 return FALSE;
576 }
577
578 static gboolean summary_search_progress_cb(gpointer data, guint at, guint matched, guint total)
579 {
580         if (!search_window.is_searching) {
581                 search_window.matcher_is_outdated = TRUE;
582                 return FALSE;
583         }
584
585         return summaryview_search_root_progress(search_window.summaryview, at, matched, total);
586 }
587
588 static gboolean summary_search_prepare_matcher()
589 {
590         gboolean adv_search;
591         gboolean bool_and = FALSE;
592         gboolean case_sens = FALSE;
593         gchar *matcher_str;
594         gint match_type;
595         gchar *from_str = NULL, *to_str = NULL, *subject_str = NULL;
596         gchar *body_str = NULL;
597         GSList *matchers = NULL;
598
599         if (!search_window.matcher_is_outdated)
600                 return TRUE;
601
602         if (search_window.advsearch == NULL) {
603                 search_window.advsearch = advsearch_new();
604                 advsearch_set_on_error_cb(search_window.advsearch, NULL, NULL); /* TODO */
605                 advsearch_set_on_progress_cb(search_window.advsearch, 
606                         summary_search_progress_cb, NULL);
607         }
608
609         adv_search = gtk_toggle_button_get_active
610                         (GTK_TOGGLE_BUTTON(search_window.adv_search_checkbtn));
611
612         if (adv_search) {
613                 matcher_str = add_history_get(search_window.adv_condition_entry, &prefs_common.summary_search_adv_condition_history);
614         } else {
615                 MatcherList *matcher_list;
616                 bool_and = combobox_get_active_data(GTK_COMBO_BOX(search_window.bool_optmenu));
617                 case_sens = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(search_window.case_checkbtn));
618
619                 from_str    = add_history_get(search_window.from_entry, &prefs_common.summary_search_from_history);
620                 to_str      = add_history_get(search_window.to_entry, &prefs_common.summary_search_to_history);
621                 subject_str = add_history_get(search_window.subject_entry, &prefs_common.summary_search_subject_history);
622                 body_str    = add_history_get(search_window.body_entry, &prefs_common.summary_search_body_history);
623
624                 if (!from_str && !to_str && !subject_str && !body_str) {
625                         /* TODO: warn if no search criteria? (or make buttons enabled only when
626                          * at least one search criteria has been set */
627                         return FALSE;
628                 }
629
630                 match_type = case_sens ? MATCHTYPE_MATCH : MATCHTYPE_MATCHCASE;
631
632                 if (from_str) {
633                         MatcherProp *prop = matcherprop_new(MATCHCRITERIA_FROM, NULL, match_type, from_str, 0);
634                         matchers = g_slist_append(matchers, prop);
635                 }
636                 if (to_str) {
637                         MatcherProp *prop = matcherprop_new(MATCHCRITERIA_TO, NULL, match_type, to_str, 0);
638                         matchers = g_slist_append(matchers, prop);
639                 }
640                 if (subject_str) {
641                         MatcherProp *prop = matcherprop_new(MATCHCRITERIA_SUBJECT, NULL, match_type, subject_str, 0);
642                         matchers = g_slist_append(matchers, prop);
643                 }
644                 if (body_str) {
645                         MatcherProp *prop = matcherprop_new(MATCHCRITERIA_BODY_PART, NULL, match_type, body_str, 0);
646                         matchers = g_slist_append(matchers, prop);
647                 }
648                 g_free(from_str);
649                 g_free(to_str);
650                 g_free(subject_str);
651                 g_free(body_str);
652
653                 matcher_list = matcherlist_new(matchers, bool_and);
654                 if (!matcher_list)
655                         return FALSE;
656
657                 matcher_str = matcherlist_to_string(matcher_list);
658                 matcherlist_free(matcher_list);
659         }
660         if (!matcher_str)
661                 return FALSE;
662
663         advsearch_set(search_window.advsearch, ADVANCED_SEARCH_EXTENDED,
664                       matcher_str);
665
666         debug_print("Advsearch set: %s\n", matcher_str);
667         g_free(matcher_str);
668
669         if (!advsearch_has_proper_predicate(search_window.advsearch))
670                 return FALSE;
671
672         search_window.matcher_is_outdated = FALSE;
673
674         return TRUE;
675 }
676
677 static gboolean summary_search_prereduce_msg_list()
678 {
679         MsgInfoList *msglist = NULL;
680         MsgNumberList *msgnums = NULL;
681         MsgNumberList *cur;
682         SummaryView *summaryview = search_window.summaryview;
683         gboolean result;
684         FolderItem *item = summaryview->folder_item;
685         static GdkCursor *watch_cursor = NULL;
686         if (!watch_cursor)
687                 watch_cursor = gdk_cursor_new(GDK_WATCH);
688
689         if (search_window.matcher_is_outdated && !summary_search_prepare_matcher())
690                 return FALSE;
691
692         main_window_cursor_wait(mainwindow_get_mainwindow());
693         gdk_window_set_cursor(gtk_widget_get_window(search_window.window), watch_cursor);
694         statusbar_print_all(_("Searching in %s... \n"),
695                         item->path ? item->path : "(null)");
696
697         result = advsearch_search_msgs_in_folders(search_window.advsearch,
698                         &msglist, item, FALSE);
699         statusbar_pop_all();
700         statusbar_progress_all(0, 0, 0);
701         gdk_window_set_cursor(gtk_widget_get_window(search_window.window), NULL);
702         main_window_cursor_normal(mainwindow_get_mainwindow());
703
704         if (!result)
705                 return FALSE;
706
707         msgnums = procmsg_get_number_list_for_msgs(msglist);
708         procmsg_msg_list_free(msglist);
709
710         if (search_window.matched_msgnums == NULL)
711                 search_window.matched_msgnums = g_hash_table_new(g_direct_hash, NULL);
712
713         g_hash_table_remove_all(search_window.matched_msgnums);
714
715         for (cur = msgnums; cur != NULL; cur = cur->next)
716                 g_hash_table_insert(search_window.matched_msgnums, cur->data, GINT_TO_POINTER(1));
717
718         g_slist_free(msgnums);
719
720         return TRUE;
721 }
722
723 static void summary_search_execute(gboolean backward, gboolean search_all)
724 {
725         SummaryView *summaryview = search_window.summaryview;
726         GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
727         GtkCMCTreeNode *node;
728         MsgInfo *msginfo;
729         gboolean all_searched = FALSE;
730         gboolean matched = FALSE;
731         gint i = 0;
732
733         if (summary_is_locked(summaryview))
734                 return;
735
736         summary_lock(summaryview);
737
738         search_window.is_searching = TRUE;
739         main_window_cursor_wait(summaryview->mainwin);
740         summary_show_stop_button();
741
742         if (search_window.matcher_is_outdated && !summary_search_prereduce_msg_list())
743                 goto exit;
744
745         if (search_all) {
746                 summary_freeze(summaryview);
747                 summary_unselect_all(summaryview);
748                 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
749                 backward = FALSE;
750         } else if (!summaryview->selected) {
751                 /* no selection, search from from list start */
752                 if (backward)
753                         node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list_end);
754                 else
755                         node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
756
757                 if (!node) {
758                         search_window.is_searching = FALSE;
759                         summary_hide_stop_button();
760                         main_window_cursor_normal(summaryview->mainwin);
761                         summary_unlock(summaryview);
762                         return;
763                 }
764         } else {
765                 /* search from current selection */
766                 if (backward)
767                         node = gtkut_ctree_node_prev(ctree, summaryview->selected);
768                 else
769                         node = gtkut_ctree_node_next(ctree, summaryview->selected);
770         }
771
772         for (; search_window.is_searching; i++) {
773                 if (!node) {
774                         gchar *str;
775                         AlertValue val;
776
777                         if (search_all)
778                                 break;
779
780                         if (all_searched) {
781                                 alertpanel_full(_("Search failed"),
782                                                 _("Search string not found."),
783                                                 GTK_STOCK_CLOSE, NULL, NULL, ALERTFOCUS_FIRST,
784                                                 FALSE, NULL, ALERT_WARNING);
785                                 break;
786                         }
787
788                         if (backward)
789                                 str = _("Beginning of list reached; continue from end?");
790                         else
791                                 str = _("End of list reached; continue from beginning?");
792
793                         val = alertpanel(_("Search finished"), str,
794                                          GTK_STOCK_NO, GTK_STOCK_YES, NULL,
795                                          ALERTFOCUS_SECOND);
796                         if (G_ALERTALTERNATE == val) {
797                                 if (backward) {
798                                         node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list_end);
799                                 } else {
800                                         node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
801                                 }
802
803                                 all_searched = TRUE;
804
805                                 manage_window_focus_in(search_window.window, NULL, NULL);
806                         } else
807                                 break;
808                 }
809
810                 msginfo = gtk_cmctree_node_get_row_data(ctree, node);
811
812                 if (msginfo)
813                         matched = summary_search_verify_match(msginfo);
814                 else
815                         matched = FALSE;
816
817                 if (matched) {
818                         if (search_all) {
819                                 gtk_cmctree_select(ctree, node);
820                         } else {
821                                 summary_unlock(summaryview);
822                                 summary_select_node(summaryview, node,
823                                                 OPEN_SELECTED_ON_SEARCH_RESULTS);
824                                 summary_lock(summaryview);
825                                 break;
826                         }
827                 }
828
829                 if (i % (search_window.is_fast ? 1000 : 100) == 0)
830                         GTK_EVENTS_FLUSH();
831
832                 node = backward ? gtkut_ctree_node_prev(ctree, node)
833                                 : gtkut_ctree_node_next(ctree, node);
834         }
835
836 exit:
837         search_window.is_searching = FALSE;
838         summary_hide_stop_button();
839         main_window_cursor_normal(summaryview->mainwin);
840         if (search_all)
841                 summary_thaw_with_status(summaryview);
842         summary_unlock(summaryview);
843 }
844
845 static void summary_search_clear(GtkButton *button, gpointer data)
846 {
847         if (gtk_toggle_button_get_active
848                 (GTK_TOGGLE_BUTTON(search_window.adv_search_checkbtn))) {
849                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((search_window.adv_condition_entry)))), "");
850         } else {
851                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((search_window.from_entry)))), "");
852                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((search_window.to_entry)))), "");
853                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((search_window.subject_entry)))), "");
854                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((search_window.body_entry)))), "");
855         }
856         /* stop searching */
857         if (search_window.is_searching) {
858                 search_window.is_searching = FALSE;
859         }
860         search_window.matcher_is_outdated = TRUE;
861 }
862
863 static void summary_search_prev_clicked(GtkButton *button, gpointer data)
864 {
865         summary_search_execute(TRUE, FALSE);
866 }
867
868 static void summary_search_next_clicked(GtkButton *button, gpointer data)
869 {
870         summary_search_execute(FALSE, FALSE);
871 }
872
873 static void summary_search_all_clicked(GtkButton *button, gpointer data)
874 {
875         summary_search_execute(FALSE, TRUE);
876 }
877
878 static void adv_condition_btn_done(MatcherList * matchers)
879 {
880         gchar *str;
881
882         cm_return_if_fail(
883                         mainwindow_get_mainwindow()->summaryview->quicksearch != NULL);
884
885         if (matchers == NULL) {
886                 return;
887         }
888
889         str = matcherlist_to_string(matchers);
890         search_window.matcher_is_outdated = TRUE;
891
892         if (str != NULL) {
893                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((search_window.adv_condition_entry)))), str);
894                 g_free(str);
895         }
896 }
897
898 static void summary_search_stop_clicked(GtkButton *button, gpointer data)
899 {
900         search_window.is_searching = FALSE;
901 }
902
903 static void adv_condition_btn_clicked(GtkButton *button, gpointer data)
904 {
905         const gchar * cond_str;
906         MatcherList * matchers = NULL;
907
908         cm_return_if_fail( search_window.window != NULL );
909
910         /* re-use the current search value if it's a condition expression,
911            otherwise ignore it silently */
912         cond_str = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(search_window.adv_condition_entry));
913         if (cond_str && *cond_str != '\0') {
914                 matchers = matcher_parser_get_cond((gchar*)cond_str, NULL);
915         }
916
917         prefs_matcher_open(matchers, adv_condition_btn_done);
918
919         if (matchers != NULL) {
920                 matcherlist_free(matchers);
921         }
922 };
923
924 static void optmenu_changed(GtkComboBox *widget, gpointer user_data)
925 {
926         search_window.matcher_is_outdated = TRUE;
927 }
928
929 static void from_changed(void)
930 {
931         if (!search_window.from_entry_has_focus)
932                 gtk_widget_grab_focus(search_window.from_entry);
933         search_window.matcher_is_outdated = TRUE;
934 }
935
936 static void to_changed(void)
937 {
938         if (!search_window.to_entry_has_focus)
939                 gtk_widget_grab_focus(search_window.to_entry);
940         search_window.matcher_is_outdated = TRUE;
941 }
942
943 static void subject_changed(void)
944 {
945         if (!search_window.subject_entry_has_focus)
946                 gtk_widget_grab_focus(search_window.subject_entry);
947         search_window.matcher_is_outdated = TRUE;
948 }
949
950 static void body_changed(void)
951 {
952         if (!search_window.body_entry_has_focus)
953                 gtk_widget_grab_focus(search_window.body_entry);
954         search_window.matcher_is_outdated = TRUE;
955 }
956
957 static void adv_condition_changed(void)
958 {
959         if (!search_window.adv_condition_entry_has_focus)
960                 gtk_widget_grab_focus(search_window.adv_condition_entry);
961         search_window.matcher_is_outdated = TRUE;
962 }
963
964 static void case_changed(GtkToggleButton *togglebutton, gpointer user_data)
965 {
966         search_window.matcher_is_outdated = TRUE;
967 }
968
969 static void adv_search_changed(GtkToggleButton *togglebutton, gpointer user_data)
970 {
971         search_window.matcher_is_outdated = TRUE;
972 }
973
974 static gboolean from_entry_focus_evt_in(GtkWidget *widget, GdkEventFocus *event,
975                                   gpointer data)
976 {
977         search_window.from_entry_has_focus = TRUE;
978         return FALSE;
979 }
980
981 static gboolean from_entry_focus_evt_out(GtkWidget *widget, GdkEventFocus *event,
982                                   gpointer data)
983 {
984         search_window.from_entry_has_focus = FALSE;
985         return FALSE;
986 }
987
988 static gboolean to_entry_focus_evt_in(GtkWidget *widget, GdkEventFocus *event,
989                                   gpointer data)
990 {
991         search_window.to_entry_has_focus = TRUE;
992         return FALSE;
993 }
994
995 static gboolean to_entry_focus_evt_out(GtkWidget *widget, GdkEventFocus *event,
996                                   gpointer data)
997 {
998         search_window.to_entry_has_focus = FALSE;
999         return FALSE;
1000 }
1001
1002 static gboolean subject_entry_focus_evt_in(GtkWidget *widget, GdkEventFocus *event,
1003                                   gpointer data)
1004 {
1005         search_window.subject_entry_has_focus = TRUE;
1006         return FALSE;
1007 }
1008
1009 static gboolean subject_entry_focus_evt_out(GtkWidget *widget, GdkEventFocus *event,
1010                                   gpointer data)
1011 {
1012         search_window.subject_entry_has_focus = FALSE;
1013         return FALSE;
1014 }
1015
1016 static gboolean body_entry_focus_evt_in(GtkWidget *widget, GdkEventFocus *event,
1017                                   gpointer data)
1018 {
1019         search_window.body_entry_has_focus = TRUE;
1020         return FALSE;
1021 }
1022
1023 static gboolean body_entry_focus_evt_out(GtkWidget *widget, GdkEventFocus *event,
1024                                   gpointer data)
1025 {
1026         search_window.body_entry_has_focus = FALSE;
1027         return FALSE;
1028 }
1029
1030 static gboolean adv_condition_entry_focus_evt_in(GtkWidget *widget, GdkEventFocus *event,
1031                                   gpointer data)
1032 {
1033         search_window.adv_condition_entry_has_focus = TRUE;
1034         return FALSE;
1035 }
1036
1037 static gboolean adv_condition_entry_focus_evt_out(GtkWidget *widget, GdkEventFocus *event,
1038                                   gpointer data)
1039 {
1040         search_window.adv_condition_entry_has_focus = FALSE;
1041         return FALSE;
1042 }
1043
1044 static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event,
1045                             gpointer data)
1046 {
1047         if (event && (event->keyval == GDK_KEY_Escape)) {
1048                 /* ESC key will:
1049                         - stop a running search
1050                         - close the search window if no search is running
1051                 */
1052                 if (!search_window.is_searching) {
1053                         gtk_widget_hide(search_window.window);
1054                 } else {
1055                         search_window.is_searching = FALSE;
1056                 }
1057         }
1058
1059         if (event && (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter)) {
1060                 if (!search_window.is_searching) {
1061                         summary_search_execute(FALSE, FALSE);
1062                 }
1063         }
1064
1065         if (event && (event->keyval == GDK_KEY_Down || event->keyval == GDK_KEY_Up)) {
1066                 if (search_window.from_entry_has_focus) {
1067                         combobox_set_value_from_arrow_key(
1068                                         GTK_COMBO_BOX(search_window.from_entry),
1069                                         event->keyval);
1070                         return TRUE;
1071                 }
1072                 if (search_window.to_entry_has_focus) {
1073                         combobox_set_value_from_arrow_key(
1074                                         GTK_COMBO_BOX(search_window.to_entry),
1075                                         event->keyval);
1076                         return TRUE;
1077                 }
1078                 if (search_window.subject_entry_has_focus) {
1079                         combobox_set_value_from_arrow_key(
1080                                         GTK_COMBO_BOX(search_window.subject_entry),
1081                                         event->keyval);
1082                         return TRUE;
1083                 }
1084                 if (search_window.body_entry_has_focus) {
1085                         combobox_set_value_from_arrow_key(
1086                                         GTK_COMBO_BOX(search_window.body_entry),
1087                                         event->keyval);
1088                         return TRUE;
1089                 }
1090                 if (search_window.adv_condition_entry_has_focus) {
1091                         combobox_set_value_from_arrow_key(
1092                                         GTK_COMBO_BOX(search_window.adv_condition_entry),
1093                                         event->keyval);
1094                         return TRUE;
1095                 }
1096         }
1097
1098         return FALSE;
1099 }