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