2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2013 Hiroyuki Yamamoto and the Claws Mail team
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.
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.
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/>.
22 #include "claws-features.h"
28 #include <glib/gi18n.h>
29 #include <gdk/gdkkeysyms.h>
31 #if !GTK_CHECK_VERSION(3, 0, 0)
32 #include "gtk/gtksctree.h"
39 #include "summary_search.h"
40 #include "summaryview.h"
41 #include "messageview.h"
42 #include "mainwindow.h"
47 #include "prefs_gtk.h"
48 #include "manage_window.h"
49 #include "alertpanel.h"
50 #include "advsearch.h"
52 #include "matcher_parser.h"
53 #include "prefs_matcher.h"
55 #include "prefs_common.h"
56 #include "statusbar.h"
58 static struct SummarySearchWindow
{
61 GtkWidget
*bool_optmenu
;
63 GtkWidget
*from_entry
;
65 GtkWidget
*subject_entry
;
66 GtkWidget
*body_entry
;
68 GtkWidget
*adv_condition_entry
;
69 GtkWidget
*adv_condition_btn
;
70 GtkWidget
*adv_search_checkbtn
;
72 GtkWidget
*case_checkbtn
;
82 SummaryView
*summaryview
;
84 AdvancedSearch
*advsearch
;
86 gboolean matcher_is_outdated
;
87 gboolean search_in_progress
;
88 GHashTable
*matched_msgnums
;
90 gboolean is_searching
;
91 gboolean from_entry_has_focus
;
92 gboolean to_entry_has_focus
;
93 gboolean subject_entry_has_focus
;
94 gboolean body_entry_has_focus
;
95 gboolean adv_condition_entry_has_focus
;
98 static gchar
* add_history_get(GtkWidget
*from
, GList
**history
);
100 static void summary_search_create (void);
102 static gboolean
summary_search_verify_match (MsgInfo
*msg
);
103 static gboolean
summary_search_prepare_matcher ();
104 static gboolean
summary_search_prereduce_msg_list ();
106 static void summary_search_execute (gboolean backward
,
107 gboolean search_all
);
109 static void summary_search_clear (GtkButton
*button
,
111 static void summary_search_prev_clicked (GtkButton
*button
,
113 static void summary_search_next_clicked (GtkButton
*button
,
115 static void summary_search_all_clicked (GtkButton
*button
,
117 static void summary_search_stop_clicked (GtkButton
*button
,
119 static void adv_condition_btn_clicked (GtkButton
*button
,
122 static void from_changed (void);
123 static void to_changed (void);
124 static void subject_changed (void);
125 static void body_changed (void);
126 static void adv_condition_changed (void);
128 static gboolean
from_entry_focus_evt_in(GtkWidget
*widget
, GdkEventFocus
*event
,
130 static gboolean
from_entry_focus_evt_out(GtkWidget
*widget
, GdkEventFocus
*event
,
132 static gboolean
to_entry_focus_evt_in(GtkWidget
*widget
, GdkEventFocus
*event
,
134 static gboolean
to_entry_focus_evt_out(GtkWidget
*widget
, GdkEventFocus
*event
,
136 static gboolean
subject_entry_focus_evt_in(GtkWidget
*widget
, GdkEventFocus
*event
,
138 static gboolean
subject_entry_focus_evt_out(GtkWidget
*widget
, GdkEventFocus
*event
,
140 static gboolean
body_entry_focus_evt_in(GtkWidget
*widget
, GdkEventFocus
*event
,
142 static gboolean
body_entry_focus_evt_out(GtkWidget
*widget
, GdkEventFocus
*event
,
144 static gboolean
adv_condition_entry_focus_evt_in(GtkWidget
*widget
, GdkEventFocus
*event
,
146 static gboolean
adv_condition_entry_focus_evt_out(GtkWidget
*widget
, GdkEventFocus
*event
,
148 static gboolean
key_pressed (GtkWidget
*widget
,
152 #define GTK_BUTTON_SET_SENSITIVE(widget,sensitive) { \
153 gtk_widget_set_sensitive(widget, sensitive); \
156 static gchar
* add_history_get(GtkWidget
*from
, GList
**history
)
160 #if !GTK_CHECK_VERSION(2, 24, 0)
161 result
= gtk_combo_box_get_active_text(GTK_COMBO_BOX(from
));
163 result
= gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(from
));
166 result
= gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(from
))), 0, -1);
168 if (result
&& result
[0] != '\0') {
170 #if !GTK_CHECK_VERSION(2, 24, 0)
171 combobox_unset_popdown_strings(GTK_COMBO_BOX(from
));
173 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(from
));
175 *history
= add_history(*history
, result
);
176 #if !GTK_CHECK_VERSION(2, 24, 0)
177 combobox_set_popdown_strings(GTK_COMBO_BOX(from
), *history
);
179 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(from
), *history
);
189 void summary_search(SummaryView
*summaryview
)
191 if (!search_window
.window
) {
192 summary_search_create();
194 gtk_widget_hide(search_window
.window
);
197 search_window
.summaryview
= summaryview
;
199 gtk_widget_grab_focus(search_window
.next_btn
);
200 gtk_widget_grab_focus(search_window
.subject_entry
);
201 gtk_widget_show(search_window
.window
);
204 static void summary_show_stop_button(void)
206 gtk_widget_hide(search_window
.close_btn
);
207 gtk_widget_show(search_window
.stop_btn
);
208 GTK_BUTTON_SET_SENSITIVE(search_window
.all_btn
, FALSE
)
209 GTK_BUTTON_SET_SENSITIVE(search_window
.prev_btn
, FALSE
)
210 GTK_BUTTON_SET_SENSITIVE(search_window
.next_btn
, FALSE
)
213 static void summary_hide_stop_button(void)
215 gtk_widget_hide(search_window
.stop_btn
);
216 gtk_widget_show(search_window
.close_btn
);
217 gtk_widget_set_sensitive(search_window
.all_btn
, TRUE
);
218 gtk_widget_set_sensitive(search_window
.prev_btn
, TRUE
);
219 gtk_widget_set_sensitive(search_window
.next_btn
, TRUE
);
222 static void summary_search_create(void)
226 GtkWidget
*bool_hbox
;
227 GtkWidget
*bool_optmenu
;
230 GtkWidget
*clear_btn
;
233 GtkWidget
*from_label
;
234 GtkWidget
*from_entry
;
237 GtkWidget
*subject_label
;
238 GtkWidget
*subject_entry
;
239 GtkWidget
*body_label
;
240 GtkWidget
*body_entry
;
241 GtkWidget
*adv_condition_label
;
242 GtkWidget
*adv_condition_entry
;
243 GtkWidget
*adv_condition_btn
;
245 GtkWidget
*checkbtn_hbox
;
246 GtkWidget
*adv_search_checkbtn
;
247 GtkWidget
*case_checkbtn
;
249 GtkWidget
*confirm_area
;
254 GtkWidget
*close_btn
;
256 gboolean is_searching
= FALSE
;
258 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "summary_search");
259 gtk_window_set_title(GTK_WINDOW (window
), _("Search messages"));
260 gtk_window_set_resizable(GTK_WINDOW(window
), TRUE
);
261 gtk_container_set_border_width(GTK_CONTAINER (window
), 8);
262 g_signal_connect(G_OBJECT(window
), "delete_event",
263 G_CALLBACK(gtk_widget_hide_on_delete
), NULL
);
264 g_signal_connect(G_OBJECT(window
), "key_press_event",
265 G_CALLBACK(key_pressed
), NULL
);
266 MANAGE_WINDOW_SIGNALS_CONNECT(window
);
268 vbox1
= gtk_vbox_new (FALSE
, 0);
269 gtk_widget_show (vbox1
);
270 gtk_container_add (GTK_CONTAINER (window
), vbox1
);
272 bool_hbox
= gtk_hbox_new(FALSE
, 4);
273 gtk_widget_show(bool_hbox
);
274 gtk_box_pack_start(GTK_BOX(vbox1
), bool_hbox
, FALSE
, FALSE
, 0);
276 bool_optmenu
= gtkut_sc_combobox_create(NULL
, FALSE
);
277 menu
= GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(bool_optmenu
)));
278 gtk_widget_show(bool_optmenu
);
279 gtk_box_pack_start(GTK_BOX(bool_hbox
), bool_optmenu
, FALSE
, FALSE
, 0);
281 COMBOBOX_ADD(menu
, _("Match any of the following"), 0);
282 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(bool_optmenu
), &iter
);
283 COMBOBOX_ADD(menu
, _("Match all of the following"), 1);
285 clear_btn
= gtk_button_new_from_stock(GTK_STOCK_CLEAR
);
286 gtk_widget_show(clear_btn
);
287 gtk_box_pack_end(GTK_BOX(bool_hbox
), clear_btn
, FALSE
, FALSE
, 0);
289 table1
= gtk_table_new (5, 3, FALSE
);
290 gtk_widget_show (table1
);
291 gtk_box_pack_start (GTK_BOX (vbox1
), table1
, TRUE
, TRUE
, 0);
292 gtk_container_set_border_width (GTK_CONTAINER (table1
), 4);
293 gtk_table_set_row_spacings (GTK_TABLE (table1
), 8);
294 gtk_table_set_col_spacings (GTK_TABLE (table1
), 8);
296 #if !GTK_CHECK_VERSION(2, 24, 0)
297 from_entry
= gtk_combo_box_entry_new_text ();
299 from_entry
= gtk_combo_box_text_new_with_entry ();
301 gtk_combo_box_set_active(GTK_COMBO_BOX(from_entry
), -1);
302 if (prefs_common
.summary_search_from_history
)
303 #if !GTK_CHECK_VERSION(2, 24, 0)
304 combobox_set_popdown_strings(GTK_COMBO_BOX(from_entry
),
305 prefs_common
.summary_search_from_history
);
307 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(from_entry
),
308 prefs_common
.summary_search_from_history
);
310 gtk_widget_show (from_entry
);
311 gtk_table_attach (GTK_TABLE (table1
), from_entry
, 1, 3, 0, 1,
312 GTK_EXPAND
|GTK_FILL
, 0, 0, 0);
313 g_signal_connect(G_OBJECT(from_entry
), "changed",
314 G_CALLBACK(from_changed
), NULL
);
315 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((from_entry
)))),
316 "focus_in_event", G_CALLBACK(from_entry_focus_evt_in
), NULL
);
317 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((from_entry
)))),
318 "focus_out_event", G_CALLBACK(from_entry_focus_evt_out
), NULL
);
320 #if !GTK_CHECK_VERSION(2, 24, 0)
321 to_entry
= gtk_combo_box_entry_new_text ();
323 to_entry
= gtk_combo_box_text_new_with_entry ();
325 gtk_combo_box_set_active(GTK_COMBO_BOX(to_entry
), -1);
326 if (prefs_common
.summary_search_to_history
)
327 #if !GTK_CHECK_VERSION(2, 24, 0)
328 combobox_set_popdown_strings(GTK_COMBO_BOX(to_entry
),
329 prefs_common
.summary_search_to_history
);
331 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(to_entry
),
332 prefs_common
.summary_search_to_history
);
334 gtk_widget_show (to_entry
);
335 gtk_table_attach (GTK_TABLE (table1
), to_entry
, 1, 3, 1, 2,
336 GTK_EXPAND
|GTK_FILL
, 0, 0, 0);
337 g_signal_connect(G_OBJECT(to_entry
), "changed",
338 G_CALLBACK(to_changed
), NULL
);
339 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((to_entry
)))),
340 "focus_in_event", G_CALLBACK(to_entry_focus_evt_in
), NULL
);
341 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((to_entry
)))),
342 "focus_out_event", G_CALLBACK(to_entry_focus_evt_out
), NULL
);
344 #if !GTK_CHECK_VERSION(2, 24, 0)
345 subject_entry
= gtk_combo_box_entry_new_text ();
347 subject_entry
= gtk_combo_box_text_new_with_entry ();
349 gtk_combo_box_set_active(GTK_COMBO_BOX(subject_entry
), -1);
350 if (prefs_common
.summary_search_subject_history
)
351 #if !GTK_CHECK_VERSION(2, 24, 0)
352 combobox_set_popdown_strings(GTK_COMBO_BOX(subject_entry
),
353 prefs_common
.summary_search_subject_history
);
355 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(subject_entry
),
356 prefs_common
.summary_search_subject_history
);
358 gtk_widget_show (subject_entry
);
359 gtk_table_attach (GTK_TABLE (table1
), subject_entry
, 1, 3, 2, 3,
360 GTK_EXPAND
|GTK_FILL
, 0, 0, 0);
361 g_signal_connect(G_OBJECT(subject_entry
), "changed",
362 G_CALLBACK(subject_changed
), NULL
);
363 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((subject_entry
)))),
364 "focus_in_event", G_CALLBACK(subject_entry_focus_evt_in
), NULL
);
365 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((subject_entry
)))),
366 "focus_out_event", G_CALLBACK(subject_entry_focus_evt_out
), NULL
);
368 #if !GTK_CHECK_VERSION(2, 24, 0)
369 body_entry
= gtk_combo_box_entry_new_text ();
371 body_entry
= gtk_combo_box_text_new_with_entry ();
373 gtk_combo_box_set_active(GTK_COMBO_BOX(body_entry
), -1);
374 if (prefs_common
.summary_search_body_history
)
375 #if !GTK_CHECK_VERSION(2, 24, 0)
376 combobox_set_popdown_strings(GTK_COMBO_BOX(body_entry
),
377 prefs_common
.summary_search_body_history
);
379 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(body_entry
),
380 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
);
392 #if !GTK_CHECK_VERSION(2, 24, 0)
393 adv_condition_entry
= gtk_combo_box_entry_new_text ();
395 adv_condition_entry
= gtk_combo_box_text_new_with_entry ();
397 gtk_combo_box_set_active(GTK_COMBO_BOX(adv_condition_entry
), -1);
398 if (prefs_common
.summary_search_adv_condition_history
)
399 #if !GTK_CHECK_VERSION(2, 24, 0)
400 combobox_set_popdown_strings(GTK_COMBO_BOX(adv_condition_entry
),
401 prefs_common
.summary_search_adv_condition_history
);
403 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(adv_condition_entry
),
404 prefs_common
.summary_search_adv_condition_history
);
406 gtk_widget_show (adv_condition_entry
);
407 gtk_table_attach (GTK_TABLE (table1
), adv_condition_entry
, 1, 2, 4, 5,
408 GTK_EXPAND
|GTK_FILL
, 0, 0, 0);
409 g_signal_connect(G_OBJECT(adv_condition_entry
), "changed",
410 G_CALLBACK(adv_condition_changed
), NULL
);
411 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((adv_condition_entry
)))),
412 "focus_in_event", G_CALLBACK(adv_condition_entry_focus_evt_in
), NULL
);
413 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((adv_condition_entry
)))),
414 "focus_out_event", G_CALLBACK(adv_condition_entry_focus_evt_out
), NULL
);
416 adv_condition_btn
= gtk_button_new_with_label(" ... ");
417 gtk_widget_show (adv_condition_btn
);
418 gtk_table_attach (GTK_TABLE (table1
), adv_condition_btn
, 2, 3, 4, 5,
420 g_signal_connect(G_OBJECT (adv_condition_btn
), "clicked",
421 G_CALLBACK(adv_condition_btn_clicked
), search_window
.window
);
423 CLAWS_SET_TIP(adv_condition_btn
,
424 _("Edit search criteria"));
426 from_label
= gtk_label_new (_("From:"));
427 gtk_widget_show (from_label
);
428 gtk_table_attach (GTK_TABLE (table1
), from_label
, 0, 1, 0, 1,
430 gtk_label_set_justify (GTK_LABEL (from_label
), GTK_JUSTIFY_RIGHT
);
431 gtk_misc_set_alignment (GTK_MISC (from_label
), 1, 0.5);
433 to_label
= gtk_label_new (_("To:"));
434 gtk_widget_show (to_label
);
435 gtk_table_attach (GTK_TABLE (table1
), to_label
, 0, 1, 1, 2,
437 gtk_label_set_justify (GTK_LABEL (to_label
), GTK_JUSTIFY_RIGHT
);
438 gtk_misc_set_alignment (GTK_MISC (to_label
), 1, 0.5);
440 subject_label
= gtk_label_new (_("Subject:"));
441 gtk_widget_show (subject_label
);
442 gtk_table_attach (GTK_TABLE (table1
), subject_label
, 0, 1, 2, 3,
444 gtk_label_set_justify (GTK_LABEL (subject_label
), GTK_JUSTIFY_RIGHT
);
445 gtk_misc_set_alignment (GTK_MISC (subject_label
), 1, 0.5);
447 body_label
= gtk_label_new (_("Body:"));
448 gtk_widget_show (body_label
);
449 gtk_table_attach (GTK_TABLE (table1
), body_label
, 0, 1, 3, 4,
451 gtk_label_set_justify (GTK_LABEL (body_label
), GTK_JUSTIFY_RIGHT
);
452 gtk_misc_set_alignment (GTK_MISC (body_label
), 1, 0.5);
454 adv_condition_label
= gtk_label_new (_("Condition:"));
455 gtk_widget_show (adv_condition_label
);
456 gtk_table_attach (GTK_TABLE (table1
), adv_condition_label
, 0, 1, 4, 5,
458 gtk_label_set_justify (GTK_LABEL (adv_condition_label
), GTK_JUSTIFY_RIGHT
);
459 gtk_misc_set_alignment (GTK_MISC (adv_condition_label
), 1, 0.5);
461 checkbtn_hbox
= gtk_hbox_new (FALSE
, 8);
462 gtk_widget_show (checkbtn_hbox
);
463 gtk_box_pack_start (GTK_BOX (vbox1
), checkbtn_hbox
, TRUE
, TRUE
, 0);
464 gtk_container_set_border_width (GTK_CONTAINER (checkbtn_hbox
), 8);
466 case_checkbtn
= gtk_check_button_new_with_label (_("Case sensitive"));
467 gtk_widget_show (case_checkbtn
);
468 gtk_box_pack_start (GTK_BOX (checkbtn_hbox
), case_checkbtn
,
471 adv_search_checkbtn
= gtk_check_button_new_with_label (_("Extended Search"));
472 gtk_widget_show (adv_search_checkbtn
);
473 gtk_box_pack_start (GTK_BOX (checkbtn_hbox
), adv_search_checkbtn
,
476 confirm_area
= gtk_hbutton_box_new();
477 gtk_widget_show (confirm_area
);
478 gtk_button_box_set_layout(GTK_BUTTON_BOX(confirm_area
),
480 gtk_box_set_spacing(GTK_BOX(confirm_area
), 5);
482 gtkut_stock_button_add_help(confirm_area
, &help_btn
);
484 all_btn
= gtk_button_new_with_mnemonic(_("Find _all"));
485 gtkut_widget_set_can_default(all_btn
, TRUE
);
486 gtk_box_pack_start(GTK_BOX(confirm_area
), all_btn
, TRUE
, TRUE
, 0);
487 gtk_widget_show(all_btn
);
489 prev_btn
= gtk_button_new_from_stock(GTK_STOCK_GO_BACK
);
490 gtkut_widget_set_can_default(prev_btn
, TRUE
);
491 gtk_box_pack_start(GTK_BOX(confirm_area
), prev_btn
, TRUE
, TRUE
, 0);
492 gtk_widget_show(prev_btn
);
494 next_btn
= gtk_button_new_from_stock(GTK_STOCK_GO_FORWARD
);
495 gtkut_widget_set_can_default(next_btn
, TRUE
);
496 gtk_box_pack_start(GTK_BOX(confirm_area
), next_btn
, TRUE
, TRUE
, 0);
497 gtk_widget_show(next_btn
);
499 close_btn
= gtk_button_new_from_stock(GTK_STOCK_CLOSE
);
500 gtkut_widget_set_can_default(close_btn
, TRUE
);
501 gtk_box_pack_start(GTK_BOX(confirm_area
), close_btn
, TRUE
, TRUE
, 0);
502 gtk_widget_show(close_btn
);
504 /* stop button hidden */
505 stop_btn
= gtk_button_new_from_stock(GTK_STOCK_STOP
);
506 gtkut_widget_set_can_default(stop_btn
, TRUE
);
507 gtk_box_pack_start(GTK_BOX(confirm_area
), stop_btn
, TRUE
, TRUE
, 0);
509 gtk_box_pack_start (GTK_BOX (vbox1
), confirm_area
, FALSE
, FALSE
, 0);
510 gtk_widget_grab_default(next_btn
);
512 SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn
, bool_optmenu
)
513 SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn
, from_entry
)
514 SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn
, to_entry
)
515 SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn
, subject_entry
)
516 SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn
, body_entry
)
517 SET_TOGGLE_SENSITIVITY(adv_search_checkbtn
, adv_condition_label
)
518 SET_TOGGLE_SENSITIVITY(adv_search_checkbtn
, adv_condition_entry
)
519 SET_TOGGLE_SENSITIVITY(adv_search_checkbtn
, adv_condition_btn
)
520 SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn
, case_checkbtn
)
522 g_signal_connect(G_OBJECT(help_btn
), "clicked",
523 G_CALLBACK(manual_open_with_anchor_cb
),
524 MANUAL_ANCHOR_SEARCHING
);
525 g_signal_connect(G_OBJECT(clear_btn
), "clicked",
526 G_CALLBACK(summary_search_clear
), NULL
);
527 g_signal_connect(G_OBJECT(all_btn
), "clicked",
528 G_CALLBACK(summary_search_all_clicked
), NULL
);
529 g_signal_connect(G_OBJECT(prev_btn
), "clicked",
530 G_CALLBACK(summary_search_prev_clicked
), NULL
);
531 g_signal_connect(G_OBJECT(next_btn
), "clicked",
532 G_CALLBACK(summary_search_next_clicked
), NULL
);
533 g_signal_connect_closure
534 (G_OBJECT(close_btn
), "clicked",
535 g_cclosure_new_swap(G_CALLBACK(gtk_widget_hide
),
536 window
, NULL
), FALSE
);
537 g_signal_connect(G_OBJECT(stop_btn
), "clicked",
538 G_CALLBACK(summary_search_stop_clicked
), NULL
);
540 search_window
.window
= window
;
541 search_window
.bool_optmenu
= bool_optmenu
;
542 search_window
.from_entry
= from_entry
;
543 search_window
.to_entry
= to_entry
;
544 search_window
.subject_entry
= subject_entry
;
545 search_window
.body_entry
= body_entry
;
546 search_window
.adv_condition_entry
= adv_condition_entry
;
547 search_window
.adv_condition_btn
= adv_condition_btn
;
548 search_window
.case_checkbtn
= case_checkbtn
;
549 search_window
.adv_search_checkbtn
= adv_search_checkbtn
;
550 search_window
.clear_btn
= clear_btn
;
551 search_window
.help_btn
= help_btn
;
552 search_window
.all_btn
= all_btn
;
553 search_window
.prev_btn
= prev_btn
;
554 search_window
.next_btn
= next_btn
;
555 search_window
.close_btn
= close_btn
;
556 search_window
.stop_btn
= stop_btn
;
557 search_window
.advsearch
= NULL
;
558 search_window
.matcher_is_outdated
= TRUE
;
559 search_window
.search_in_progress
= FALSE
;
560 search_window
.matched_msgnums
= NULL
;
561 search_window
.is_searching
= is_searching
;
564 static gboolean
summary_search_verify_match(MsgInfo
*msg
)
566 gpointer msgnum
= GUINT_TO_POINTER(msg
->msgnum
);
568 if (g_hash_table_lookup(search_window
.matched_msgnums
, msgnum
) != NULL
)
574 static gboolean
summary_search_progress_cb(gpointer data
, guint at
, guint matched
, guint total
)
576 if (!search_window
.is_searching
) {
577 search_window
.matcher_is_outdated
= TRUE
;
581 return summaryview_search_root_progress(search_window
.summaryview
, at
, matched
, total
);
584 static gboolean
summary_search_prepare_matcher()
587 gboolean bool_and
= FALSE
;
588 gboolean case_sens
= FALSE
;
591 gchar
*from_str
= NULL
, *to_str
= NULL
, *subject_str
= NULL
;
592 gchar
*body_str
= NULL
;
593 GSList
*matchers
= NULL
;
595 if (!search_window
.matcher_is_outdated
)
598 if (search_window
.advsearch
== NULL
) {
599 search_window
.advsearch
= advsearch_new();
600 advsearch_set_on_error_cb(search_window
.advsearch
, NULL
, NULL
); /* TODO */
601 advsearch_set_on_progress_cb(search_window
.advsearch
,
602 summary_search_progress_cb
, NULL
);
605 adv_search
= gtk_toggle_button_get_active
606 (GTK_TOGGLE_BUTTON(search_window
.adv_search_checkbtn
));
609 matcher_str
= add_history_get(search_window
.adv_condition_entry
, &prefs_common
.summary_search_adv_condition_history
);
611 MatcherList
*matcher_list
;
612 bool_and
= combobox_get_active_data(GTK_COMBO_BOX(search_window
.bool_optmenu
));
613 case_sens
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(search_window
.case_checkbtn
));
615 from_str
= add_history_get(search_window
.from_entry
, &prefs_common
.summary_search_from_history
);
616 to_str
= add_history_get(search_window
.to_entry
, &prefs_common
.summary_search_to_history
);
617 subject_str
= add_history_get(search_window
.subject_entry
, &prefs_common
.summary_search_subject_history
);
618 body_str
= add_history_get(search_window
.body_entry
, &prefs_common
.summary_search_body_history
);
620 if (!from_str
&& !to_str
&& !subject_str
&& !body_str
) {
621 /* TODO: warn if no search criteria? (or make buttons enabled only when
622 * at least one search criteria has been set */
626 match_type
= case_sens
? MATCHTYPE_MATCH
: MATCHTYPE_MATCHCASE
;
629 MatcherProp
*prop
= matcherprop_new(MATCHCRITERIA_FROM
, NULL
, match_type
, from_str
, 0);
630 matchers
= g_slist_append(matchers
, prop
);
633 MatcherProp
*prop
= matcherprop_new(MATCHCRITERIA_TO
, NULL
, match_type
, to_str
, 0);
634 matchers
= g_slist_append(matchers
, prop
);
637 MatcherProp
*prop
= matcherprop_new(MATCHCRITERIA_SUBJECT
, NULL
, match_type
, subject_str
, 0);
638 matchers
= g_slist_append(matchers
, prop
);
641 MatcherProp
*prop
= matcherprop_new(MATCHCRITERIA_BODY_PART
, NULL
, match_type
, body_str
, 0);
642 matchers
= g_slist_append(matchers
, prop
);
649 matcher_list
= matcherlist_new(matchers
, bool_and
);
652 matcher_str
= matcherlist_to_string(matcher_list
);
653 matcherlist_free(matcher_list
);
658 advsearch_set(search_window
.advsearch
, ADVANCED_SEARCH_EXTENDED
,
661 debug_print("Advsearch set: %s\n", matcher_str
);
664 if (!advsearch_has_proper_predicate(search_window
.advsearch
))
667 search_window
.matcher_is_outdated
= FALSE
;
672 static gboolean
summary_search_prereduce_msg_list()
674 MsgInfoList
*msglist
= NULL
;
675 MsgNumberList
*msgnums
= NULL
;
677 SummaryView
*summaryview
= search_window
.summaryview
;
679 FolderItem
*item
= summaryview
->folder_item
;
680 static GdkCursor
*watch_cursor
= NULL
;
682 watch_cursor
= gdk_cursor_new(GDK_WATCH
);
684 if (search_window
.matcher_is_outdated
&& !summary_search_prepare_matcher()) {
688 main_window_cursor_wait(mainwindow_get_mainwindow());
689 gdk_window_set_cursor(gtk_widget_get_window(search_window
.window
), watch_cursor
);
690 statusbar_print_all(_("Searching in %s... \n"),
691 item
->path
? item
->path
: "(null)");
693 result
= advsearch_search_msgs_in_folders(search_window
.advsearch
,
694 &msglist
, item
, FALSE
);
696 statusbar_progress_all(0, 0, 0);
697 gdk_window_set_cursor(gtk_widget_get_window(search_window
.window
), NULL
);
698 main_window_cursor_normal(mainwindow_get_mainwindow());
702 msgnums
= procmsg_get_number_list_for_msgs(msglist
);
703 procmsg_msg_list_free(msglist
);
705 if (search_window
.matched_msgnums
== NULL
)
706 search_window
.matched_msgnums
= g_hash_table_new(g_direct_hash
, NULL
);
708 g_hash_table_remove_all(search_window
.matched_msgnums
);
710 for (cur
= msgnums
; cur
!= NULL
; cur
= cur
->next
) {
711 g_hash_table_insert(search_window
.matched_msgnums
, cur
->data
, GINT_TO_POINTER(1));
714 g_slist_free(msgnums
);
719 static void summary_search_execute(gboolean backward
, gboolean search_all
)
721 SummaryView
*summaryview
= search_window
.summaryview
;
722 GtkCMCTree
*ctree
= GTK_CMCTREE(summaryview
->ctree
);
723 GtkCMCTreeNode
*node
;
725 gboolean all_searched
= FALSE
;
726 gboolean matched
= FALSE
;
729 if (summary_is_locked(summaryview
)) {
732 summary_lock(summaryview
);
734 search_window
.is_searching
= TRUE
;
735 main_window_cursor_wait(summaryview
->mainwin
);
736 summary_show_stop_button();
738 if (search_window
.matcher_is_outdated
&& !summary_search_prereduce_msg_list()) {
743 summary_freeze(summaryview
);
744 summary_unselect_all(summaryview
);
745 node
= GTK_CMCTREE_NODE(GTK_CMCLIST(ctree
)->row_list
);
747 } else if (!summaryview
->selected
) {
749 node
= GTK_CMCTREE_NODE(GTK_CMCLIST(ctree
)->row_list_end
);
751 node
= GTK_CMCTREE_NODE(GTK_CMCLIST(ctree
)->row_list
);
755 search_window
.is_searching
= FALSE
;
756 summary_hide_stop_button();
757 main_window_cursor_normal(summaryview
->mainwin
);
758 summary_unlock(summaryview
);
763 node
= gtkut_ctree_node_prev(ctree
, summaryview
->selected
);
765 node
= gtkut_ctree_node_next(ctree
, summaryview
->selected
);
769 for (; search_window
.is_searching
; i
++) {
779 alertpanel_full(_("Search failed"),
780 _("Search string not found."),
781 GTK_STOCK_CLOSE
, NULL
, NULL
, FALSE
,
782 NULL
, ALERT_WARNING
, G_ALERTDEFAULT
);
787 str
= _("Beginning of list reached; continue from end?");
789 str
= _("End of list reached; continue from beginning?");
791 val
= alertpanel(_("Search finished"), str
,
792 GTK_STOCK_NO
, "+" GTK_STOCK_YES
, NULL
);
793 if (G_ALERTALTERNATE
== val
) {
795 node
= GTK_CMCTREE_NODE(GTK_CMCLIST(ctree
)->row_list_end
);
797 node
= GTK_CMCTREE_NODE(GTK_CMCLIST(ctree
)->row_list
);
802 manage_window_focus_in(search_window
.window
, NULL
, NULL
);
808 msginfo
= gtk_cmctree_node_get_row_data(ctree
, node
);
811 matched
= summary_search_verify_match(msginfo
);
817 gtk_cmctree_select(ctree
, node
);
819 if (messageview_is_visible(summaryview
->messageview
)) {
820 summary_unlock(summaryview
);
821 summary_select_node(summaryview
, node
, 1);
822 summary_lock(summaryview
);
824 summary_select_node(summaryview
, node
, 0);
830 if (i
% (search_window
.is_fast
? 1000 : 100) == 0) {
834 node
= backward
? gtkut_ctree_node_prev(ctree
, node
)
835 : gtkut_ctree_node_next(ctree
, node
);
839 search_window
.is_searching
= FALSE
;
840 summary_hide_stop_button();
841 main_window_cursor_normal(summaryview
->mainwin
);
843 summary_thaw_with_status(summaryview
);
845 summary_unlock(summaryview
);
848 static void summary_search_clear(GtkButton
*button
, gpointer data
)
850 if (gtk_toggle_button_get_active
851 (GTK_TOGGLE_BUTTON(search_window
.adv_search_checkbtn
))) {
852 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((search_window
.adv_condition_entry
)))), "");
854 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((search_window
.from_entry
)))), "");
855 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((search_window
.to_entry
)))), "");
856 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((search_window
.subject_entry
)))), "");
857 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((search_window
.body_entry
)))), "");
860 if (search_window
.is_searching
) {
861 search_window
.is_searching
= FALSE
;
863 search_window
.matcher_is_outdated
= TRUE
;
866 static void summary_search_prev_clicked(GtkButton
*button
, gpointer data
)
868 summary_search_execute(TRUE
, FALSE
);
871 static void summary_search_next_clicked(GtkButton
*button
, gpointer data
)
873 summary_search_execute(FALSE
, FALSE
);
876 static void summary_search_all_clicked(GtkButton
*button
, gpointer data
)
878 summary_search_execute(FALSE
, TRUE
);
881 static void adv_condition_btn_done(MatcherList
* matchers
)
886 mainwindow_get_mainwindow()->summaryview
->quicksearch
!= NULL
);
888 if (matchers
== NULL
) {
892 str
= matcherlist_to_string(matchers
);
893 search_window
.matcher_is_outdated
= TRUE
;
896 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((search_window
.adv_condition_entry
)))), str
);
901 static void summary_search_stop_clicked(GtkButton
*button
, gpointer data
)
903 search_window
.is_searching
= FALSE
;
906 static void adv_condition_btn_clicked(GtkButton
*button
, gpointer data
)
908 const gchar
* cond_str
;
909 MatcherList
* matchers
= NULL
;
911 cm_return_if_fail( search_window
.window
!= NULL
);
913 /* re-use the current search value if it's a condition expression,
914 otherwise ignore it silently */
915 #if !GTK_CHECK_VERSION(2, 24, 0)
916 cond_str
= gtk_combo_box_get_active_text(GTK_COMBO_BOX(search_window
.adv_condition_entry
));
918 cond_str
= gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(search_window
.adv_condition_entry
));
920 if (cond_str
&& *cond_str
!= '\0') {
921 matchers
= matcher_parser_get_cond((gchar
*)cond_str
, NULL
);
924 prefs_matcher_open(matchers
, adv_condition_btn_done
);
926 if (matchers
!= NULL
) {
927 matcherlist_free(matchers
);
931 static void from_changed(void)
933 if (!search_window
.from_entry_has_focus
)
934 gtk_widget_grab_focus(search_window
.from_entry
);
935 search_window
.matcher_is_outdated
= TRUE
;
938 static void to_changed(void)
940 if (!search_window
.to_entry_has_focus
)
941 gtk_widget_grab_focus(search_window
.to_entry
);
942 search_window
.matcher_is_outdated
= TRUE
;
945 static void subject_changed(void)
947 if (!search_window
.subject_entry_has_focus
)
948 gtk_widget_grab_focus(search_window
.subject_entry
);
949 search_window
.matcher_is_outdated
= TRUE
;
952 static void body_changed(void)
954 if (!search_window
.body_entry_has_focus
)
955 gtk_widget_grab_focus(search_window
.body_entry
);
956 search_window
.matcher_is_outdated
= TRUE
;
959 static void adv_condition_changed(void)
961 if (!search_window
.adv_condition_entry_has_focus
)
962 gtk_widget_grab_focus(search_window
.adv_condition_entry
);
963 search_window
.matcher_is_outdated
= TRUE
;
966 static gboolean
from_entry_focus_evt_in(GtkWidget
*widget
, GdkEventFocus
*event
,
969 search_window
.from_entry_has_focus
= TRUE
;
973 static gboolean
from_entry_focus_evt_out(GtkWidget
*widget
, GdkEventFocus
*event
,
976 search_window
.from_entry_has_focus
= FALSE
;
980 static gboolean
to_entry_focus_evt_in(GtkWidget
*widget
, GdkEventFocus
*event
,
983 search_window
.to_entry_has_focus
= TRUE
;
987 static gboolean
to_entry_focus_evt_out(GtkWidget
*widget
, GdkEventFocus
*event
,
990 search_window
.to_entry_has_focus
= FALSE
;
994 static gboolean
subject_entry_focus_evt_in(GtkWidget
*widget
, GdkEventFocus
*event
,
997 search_window
.subject_entry_has_focus
= TRUE
;
1001 static gboolean
subject_entry_focus_evt_out(GtkWidget
*widget
, GdkEventFocus
*event
,
1004 search_window
.subject_entry_has_focus
= FALSE
;
1008 static gboolean
body_entry_focus_evt_in(GtkWidget
*widget
, GdkEventFocus
*event
,
1011 search_window
.body_entry_has_focus
= TRUE
;
1015 static gboolean
body_entry_focus_evt_out(GtkWidget
*widget
, GdkEventFocus
*event
,
1018 search_window
.body_entry_has_focus
= FALSE
;
1022 static gboolean
adv_condition_entry_focus_evt_in(GtkWidget
*widget
, GdkEventFocus
*event
,
1025 search_window
.adv_condition_entry_has_focus
= TRUE
;
1029 static gboolean
adv_condition_entry_focus_evt_out(GtkWidget
*widget
, GdkEventFocus
*event
,
1032 search_window
.adv_condition_entry_has_focus
= FALSE
;
1036 static gboolean
key_pressed(GtkWidget
*widget
, GdkEventKey
*event
,
1039 if (event
&& (event
->keyval
== GDK_KEY_Escape
)) {
1041 - stop a running search
1042 - close the search window if no search is running
1044 if (!search_window
.is_searching
) {
1045 gtk_widget_hide(search_window
.window
);
1047 search_window
.is_searching
= FALSE
;
1051 if (event
&& (event
->keyval
== GDK_KEY_Return
|| event
->keyval
== GDK_KEY_KP_Enter
)) {
1052 if (!search_window
.is_searching
) {
1053 summary_search_execute(FALSE
, FALSE
);
1057 if (event
&& (event
->keyval
== GDK_KEY_Down
|| event
->keyval
== GDK_KEY_Up
)) {
1058 if (search_window
.from_entry_has_focus
) {
1059 combobox_set_value_from_arrow_key(
1060 GTK_COMBO_BOX(search_window
.from_entry
),
1064 if (search_window
.to_entry_has_focus
) {
1065 combobox_set_value_from_arrow_key(
1066 GTK_COMBO_BOX(search_window
.to_entry
),
1070 if (search_window
.subject_entry_has_focus
) {
1071 combobox_set_value_from_arrow_key(
1072 GTK_COMBO_BOX(search_window
.subject_entry
),
1076 if (search_window
.body_entry_has_focus
) {
1077 combobox_set_value_from_arrow_key(
1078 GTK_COMBO_BOX(search_window
.body_entry
),
1082 if (search_window
.adv_condition_entry_has_focus
) {
1083 combobox_set_value_from_arrow_key(
1084 GTK_COMBO_BOX(search_window
.adv_condition_entry
),