/* * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client * Copyright (C) 1999-2013 Hiroyuki Yamamoto and the Claws Mail team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifdef HAVE_CONFIG_H # include "config.h" #include "claws-features.h" #endif #include "defs.h" #include #include #include #include #if !GTK_CHECK_VERSION(3, 0, 0) #include "gtk/gtksctree.h" #endif #include #include #include #include "main.h" #include "summary_search.h" #include "summaryview.h" #include "messageview.h" #include "mainwindow.h" #include "menu.h" #include "utils.h" #include "gtkutils.h" #include "combobox.h" #include "prefs_gtk.h" #include "manage_window.h" #include "alertpanel.h" #include "advsearch.h" #include "matcher.h" #include "matcher_parser.h" #include "prefs_matcher.h" #include "manual.h" #include "prefs_common.h" #include "statusbar.h" static struct SummarySearchWindow { GtkWidget *window; GtkWidget *bool_optmenu; GtkWidget *from_entry; GtkWidget *to_entry; GtkWidget *subject_entry; GtkWidget *body_entry; GtkWidget *adv_condition_entry; GtkWidget *adv_condition_btn; GtkWidget *adv_search_checkbtn; GtkWidget *case_checkbtn; GtkWidget *clear_btn; GtkWidget *help_btn; GtkWidget *all_btn; GtkWidget *prev_btn; GtkWidget *next_btn; GtkWidget *close_btn; GtkWidget *stop_btn; SummaryView *summaryview; AdvancedSearch *advsearch; gboolean is_fast; gboolean matcher_is_outdated; gboolean search_in_progress; GHashTable *matched_msgnums; gboolean is_searching; gboolean from_entry_has_focus; gboolean to_entry_has_focus; gboolean subject_entry_has_focus; gboolean body_entry_has_focus; gboolean adv_condition_entry_has_focus; } search_window; static gchar* add_history_get(GtkWidget *from, GList **history); static void summary_search_create (void); static gboolean summary_search_verify_match (MsgInfo *msg); static gboolean summary_search_prepare_matcher (); static gboolean summary_search_prereduce_msg_list (); static void summary_search_execute (gboolean backward, gboolean search_all); static void summary_search_clear (GtkButton *button, gpointer data); static void summary_search_prev_clicked (GtkButton *button, gpointer data); static void summary_search_next_clicked (GtkButton *button, gpointer data); static void summary_search_all_clicked (GtkButton *button, gpointer data); static void summary_search_stop_clicked (GtkButton *button, gpointer data); static void adv_condition_btn_clicked (GtkButton *button, gpointer data); static void from_changed (void); static void to_changed (void); static void subject_changed (void); static void body_changed (void); static void adv_condition_changed (void); static gboolean from_entry_focus_evt_in(GtkWidget *widget, GdkEventFocus *event, gpointer data); static gboolean from_entry_focus_evt_out(GtkWidget *widget, GdkEventFocus *event, gpointer data); static gboolean to_entry_focus_evt_in(GtkWidget *widget, GdkEventFocus *event, gpointer data); static gboolean to_entry_focus_evt_out(GtkWidget *widget, GdkEventFocus *event, gpointer data); static gboolean subject_entry_focus_evt_in(GtkWidget *widget, GdkEventFocus *event, gpointer data); static gboolean subject_entry_focus_evt_out(GtkWidget *widget, GdkEventFocus *event, gpointer data); static gboolean body_entry_focus_evt_in(GtkWidget *widget, GdkEventFocus *event, gpointer data); static gboolean body_entry_focus_evt_out(GtkWidget *widget, GdkEventFocus *event, gpointer data); static gboolean adv_condition_entry_focus_evt_in(GtkWidget *widget, GdkEventFocus *event, gpointer data); static gboolean adv_condition_entry_focus_evt_out(GtkWidget *widget, GdkEventFocus *event, gpointer data); static gboolean key_pressed (GtkWidget *widget, GdkEventKey *event, gpointer data); #define GTK_BUTTON_SET_SENSITIVE(widget,sensitive) { \ gtk_widget_set_sensitive(widget, sensitive); \ } static gchar* add_history_get(GtkWidget *from, GList **history) { gchar *result; #if !GTK_CHECK_VERSION(2, 24, 0) result = gtk_combo_box_get_active_text(GTK_COMBO_BOX(from)); #else result = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(from)); #endif if (!result) result = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(from))), 0, -1); if (result && result[0] != '\0') { /* add to history */ #if !GTK_CHECK_VERSION(2, 24, 0) combobox_unset_popdown_strings(GTK_COMBO_BOX(from)); #else combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(from)); #endif *history = add_history(*history, result); #if !GTK_CHECK_VERSION(2, 24, 0) combobox_set_popdown_strings(GTK_COMBO_BOX(from), *history); #else combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(from), *history); #endif return result; } else { g_free(result); return NULL; } } void summary_search(SummaryView *summaryview) { if (!search_window.window) { summary_search_create(); } else { gtk_widget_hide(search_window.window); } search_window.summaryview = summaryview; gtk_widget_grab_focus(search_window.next_btn); gtk_widget_grab_focus(search_window.subject_entry); gtk_widget_show(search_window.window); } static void summary_show_stop_button(void) { gtk_widget_hide(search_window.close_btn); gtk_widget_show(search_window.stop_btn); GTK_BUTTON_SET_SENSITIVE(search_window.all_btn, FALSE) GTK_BUTTON_SET_SENSITIVE(search_window.prev_btn, FALSE) GTK_BUTTON_SET_SENSITIVE(search_window.next_btn, FALSE) } static void summary_hide_stop_button(void) { gtk_widget_hide(search_window.stop_btn); gtk_widget_show(search_window.close_btn); gtk_widget_set_sensitive(search_window.all_btn, TRUE); gtk_widget_set_sensitive(search_window.prev_btn, TRUE); gtk_widget_set_sensitive(search_window.next_btn, TRUE); } static void summary_search_create(void) { GtkWidget *window; GtkWidget *vbox1; GtkWidget *bool_hbox; GtkWidget *bool_optmenu; GtkListStore *menu; GtkTreeIter iter; GtkWidget *clear_btn; GtkWidget *table1; GtkWidget *from_label; GtkWidget *from_entry; GtkWidget *to_label; GtkWidget *to_entry; GtkWidget *subject_label; GtkWidget *subject_entry; GtkWidget *body_label; GtkWidget *body_entry; GtkWidget *adv_condition_label; GtkWidget *adv_condition_entry; GtkWidget *adv_condition_btn; GtkWidget *checkbtn_hbox; GtkWidget *adv_search_checkbtn; GtkWidget *case_checkbtn; GtkWidget *confirm_area; GtkWidget *help_btn; GtkWidget *all_btn; GtkWidget *prev_btn; GtkWidget *next_btn; GtkWidget *close_btn; GtkWidget *stop_btn; gboolean is_searching = FALSE; window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "summary_search"); gtk_window_set_title(GTK_WINDOW (window), _("Search messages")); gtk_window_set_resizable(GTK_WINDOW(window), TRUE); gtk_container_set_border_width(GTK_CONTAINER (window), 8); g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(gtk_widget_hide_on_delete), NULL); g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(key_pressed), NULL); MANAGE_WINDOW_SIGNALS_CONNECT(window); vbox1 = gtk_vbox_new (FALSE, 0); gtk_widget_show (vbox1); gtk_container_add (GTK_CONTAINER (window), vbox1); bool_hbox = gtk_hbox_new(FALSE, 4); gtk_widget_show(bool_hbox); gtk_box_pack_start(GTK_BOX(vbox1), bool_hbox, FALSE, FALSE, 0); bool_optmenu = gtkut_sc_combobox_create(NULL, FALSE); menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(bool_optmenu))); gtk_widget_show(bool_optmenu); gtk_box_pack_start(GTK_BOX(bool_hbox), bool_optmenu, FALSE, FALSE, 0); COMBOBOX_ADD(menu, _("Match any of the following"), 0); gtk_combo_box_set_active_iter(GTK_COMBO_BOX(bool_optmenu), &iter); COMBOBOX_ADD(menu, _("Match all of the following"), 1); clear_btn = gtk_button_new_from_stock(GTK_STOCK_CLEAR); gtk_widget_show(clear_btn); gtk_box_pack_end(GTK_BOX(bool_hbox), clear_btn, FALSE, FALSE, 0); table1 = gtk_table_new (5, 3, FALSE); gtk_widget_show (table1); gtk_box_pack_start (GTK_BOX (vbox1), table1, TRUE, TRUE, 0); gtk_container_set_border_width (GTK_CONTAINER (table1), 4); gtk_table_set_row_spacings (GTK_TABLE (table1), 8); gtk_table_set_col_spacings (GTK_TABLE (table1), 8); #if !GTK_CHECK_VERSION(2, 24, 0) from_entry = gtk_combo_box_entry_new_text (); #else from_entry = gtk_combo_box_text_new_with_entry (); #endif gtk_combo_box_set_active(GTK_COMBO_BOX(from_entry), -1); if (prefs_common.summary_search_from_history) #if !GTK_CHECK_VERSION(2, 24, 0) combobox_set_popdown_strings(GTK_COMBO_BOX(from_entry), prefs_common.summary_search_from_history); #else combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(from_entry), prefs_common.summary_search_from_history); #endif gtk_widget_show (from_entry); gtk_table_attach (GTK_TABLE (table1), from_entry, 1, 3, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 0); g_signal_connect(G_OBJECT(from_entry), "changed", G_CALLBACK(from_changed), NULL); g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((from_entry)))), "focus_in_event", G_CALLBACK(from_entry_focus_evt_in), NULL); g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((from_entry)))), "focus_out_event", G_CALLBACK(from_entry_focus_evt_out), NULL); #if !GTK_CHECK_VERSION(2, 24, 0) to_entry = gtk_combo_box_entry_new_text (); #else to_entry = gtk_combo_box_text_new_with_entry (); #endif gtk_combo_box_set_active(GTK_COMBO_BOX(to_entry), -1); if (prefs_common.summary_search_to_history) #if !GTK_CHECK_VERSION(2, 24, 0) combobox_set_popdown_strings(GTK_COMBO_BOX(to_entry), prefs_common.summary_search_to_history); #else combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(to_entry), prefs_common.summary_search_to_history); #endif gtk_widget_show (to_entry); gtk_table_attach (GTK_TABLE (table1), to_entry, 1, 3, 1, 2, GTK_EXPAND|GTK_FILL, 0, 0, 0); g_signal_connect(G_OBJECT(to_entry), "changed", G_CALLBACK(to_changed), NULL); g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((to_entry)))), "focus_in_event", G_CALLBACK(to_entry_focus_evt_in), NULL); g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((to_entry)))), "focus_out_event", G_CALLBACK(to_entry_focus_evt_out), NULL); #if !GTK_CHECK_VERSION(2, 24, 0) subject_entry = gtk_combo_box_entry_new_text (); #else subject_entry = gtk_combo_box_text_new_with_entry (); #endif gtk_combo_box_set_active(GTK_COMBO_BOX(subject_entry), -1); if (prefs_common.summary_search_subject_history) #if !GTK_CHECK_VERSION(2, 24, 0) combobox_set_popdown_strings(GTK_COMBO_BOX(subject_entry), prefs_common.summary_search_subject_history); #else combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(subject_entry), prefs_common.summary_search_subject_history); #endif gtk_widget_show (subject_entry); gtk_table_attach (GTK_TABLE (table1), subject_entry, 1, 3, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 0); g_signal_connect(G_OBJECT(subject_entry), "changed", G_CALLBACK(subject_changed), NULL); g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((subject_entry)))), "focus_in_event", G_CALLBACK(subject_entry_focus_evt_in), NULL); g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((subject_entry)))), "focus_out_event", G_CALLBACK(subject_entry_focus_evt_out), NULL); #if !GTK_CHECK_VERSION(2, 24, 0) body_entry = gtk_combo_box_entry_new_text (); #else body_entry = gtk_combo_box_text_new_with_entry (); #endif gtk_combo_box_set_active(GTK_COMBO_BOX(body_entry), -1); if (prefs_common.summary_search_body_history) #if !GTK_CHECK_VERSION(2, 24, 0) combobox_set_popdown_strings(GTK_COMBO_BOX(body_entry), prefs_common.summary_search_body_history); #else combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(body_entry), prefs_common.summary_search_body_history); #endif gtk_widget_show (body_entry); gtk_table_attach (GTK_TABLE (table1), body_entry, 1, 3, 3, 4, GTK_EXPAND|GTK_FILL, 0, 0, 0); g_signal_connect(G_OBJECT(body_entry), "changed", G_CALLBACK(body_changed), NULL); g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((body_entry)))), "focus_in_event", G_CALLBACK(body_entry_focus_evt_in), NULL); g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((body_entry)))), "focus_out_event", G_CALLBACK(body_entry_focus_evt_out), NULL); #if !GTK_CHECK_VERSION(2, 24, 0) adv_condition_entry = gtk_combo_box_entry_new_text (); #else adv_condition_entry = gtk_combo_box_text_new_with_entry (); #endif gtk_combo_box_set_active(GTK_COMBO_BOX(adv_condition_entry), -1); if (prefs_common.summary_search_adv_condition_history) #if !GTK_CHECK_VERSION(2, 24, 0) combobox_set_popdown_strings(GTK_COMBO_BOX(adv_condition_entry), prefs_common.summary_search_adv_condition_history); #else combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(adv_condition_entry), prefs_common.summary_search_adv_condition_history); #endif gtk_widget_show (adv_condition_entry); gtk_table_attach (GTK_TABLE (table1), adv_condition_entry, 1, 2, 4, 5, GTK_EXPAND|GTK_FILL, 0, 0, 0); g_signal_connect(G_OBJECT(adv_condition_entry), "changed", G_CALLBACK(adv_condition_changed), NULL); g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((adv_condition_entry)))), "focus_in_event", G_CALLBACK(adv_condition_entry_focus_evt_in), NULL); g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((adv_condition_entry)))), "focus_out_event", G_CALLBACK(adv_condition_entry_focus_evt_out), NULL); adv_condition_btn = gtk_button_new_with_label(" ... "); gtk_widget_show (adv_condition_btn); gtk_table_attach (GTK_TABLE (table1), adv_condition_btn, 2, 3, 4, 5, GTK_FILL, 0, 0, 0); g_signal_connect(G_OBJECT (adv_condition_btn), "clicked", G_CALLBACK(adv_condition_btn_clicked), search_window.window); CLAWS_SET_TIP(adv_condition_btn, _("Edit search criteria")); from_label = gtk_label_new (_("From:")); gtk_widget_show (from_label); gtk_table_attach (GTK_TABLE (table1), from_label, 0, 1, 0, 1, GTK_FILL, 0, 0, 0); gtk_label_set_justify (GTK_LABEL (from_label), GTK_JUSTIFY_RIGHT); gtk_misc_set_alignment (GTK_MISC (from_label), 1, 0.5); to_label = gtk_label_new (_("To:")); gtk_widget_show (to_label); gtk_table_attach (GTK_TABLE (table1), to_label, 0, 1, 1, 2, GTK_FILL, 0, 0, 0); gtk_label_set_justify (GTK_LABEL (to_label), GTK_JUSTIFY_RIGHT); gtk_misc_set_alignment (GTK_MISC (to_label), 1, 0.5); subject_label = gtk_label_new (_("Subject:")); gtk_widget_show (subject_label); gtk_table_attach (GTK_TABLE (table1), subject_label, 0, 1, 2, 3, GTK_FILL, 0, 0, 0); gtk_label_set_justify (GTK_LABEL (subject_label), GTK_JUSTIFY_RIGHT); gtk_misc_set_alignment (GTK_MISC (subject_label), 1, 0.5); body_label = gtk_label_new (_("Body:")); gtk_widget_show (body_label); gtk_table_attach (GTK_TABLE (table1), body_label, 0, 1, 3, 4, GTK_FILL, 0, 0, 0); gtk_label_set_justify (GTK_LABEL (body_label), GTK_JUSTIFY_RIGHT); gtk_misc_set_alignment (GTK_MISC (body_label), 1, 0.5); adv_condition_label = gtk_label_new (_("Condition:")); gtk_widget_show (adv_condition_label); gtk_table_attach (GTK_TABLE (table1), adv_condition_label, 0, 1, 4, 5, GTK_FILL, 0, 0, 0); gtk_label_set_justify (GTK_LABEL (adv_condition_label), GTK_JUSTIFY_RIGHT); gtk_misc_set_alignment (GTK_MISC (adv_condition_label), 1, 0.5); checkbtn_hbox = gtk_hbox_new (FALSE, 8); gtk_widget_show (checkbtn_hbox); gtk_box_pack_start (GTK_BOX (vbox1), checkbtn_hbox, TRUE, TRUE, 0); gtk_container_set_border_width (GTK_CONTAINER (checkbtn_hbox), 8); case_checkbtn = gtk_check_button_new_with_label (_("Case sensitive")); gtk_widget_show (case_checkbtn); gtk_box_pack_start (GTK_BOX (checkbtn_hbox), case_checkbtn, FALSE, FALSE, 0); adv_search_checkbtn = gtk_check_button_new_with_label (_("Extended Search")); gtk_widget_show (adv_search_checkbtn); gtk_box_pack_start (GTK_BOX (checkbtn_hbox), adv_search_checkbtn, FALSE, FALSE, 0); confirm_area = gtk_hbutton_box_new(); gtk_widget_show (confirm_area); gtk_button_box_set_layout(GTK_BUTTON_BOX(confirm_area), GTK_BUTTONBOX_END); gtk_box_set_spacing(GTK_BOX(confirm_area), 5); gtkut_stock_button_add_help(confirm_area, &help_btn); all_btn = gtk_button_new_with_mnemonic(_("Find _all")); gtkut_widget_set_can_default(all_btn, TRUE); gtk_box_pack_start(GTK_BOX(confirm_area), all_btn, TRUE, TRUE, 0); gtk_widget_show(all_btn); prev_btn = gtk_button_new_from_stock(GTK_STOCK_GO_BACK); gtkut_widget_set_can_default(prev_btn, TRUE); gtk_box_pack_start(GTK_BOX(confirm_area), prev_btn, TRUE, TRUE, 0); gtk_widget_show(prev_btn); next_btn = gtk_button_new_from_stock(GTK_STOCK_GO_FORWARD); gtkut_widget_set_can_default(next_btn, TRUE); gtk_box_pack_start(GTK_BOX(confirm_area), next_btn, TRUE, TRUE, 0); gtk_widget_show(next_btn); close_btn = gtk_button_new_from_stock(GTK_STOCK_CLOSE); gtkut_widget_set_can_default(close_btn, TRUE); gtk_box_pack_start(GTK_BOX(confirm_area), close_btn, TRUE, TRUE, 0); gtk_widget_show(close_btn); /* stop button hidden */ stop_btn = gtk_button_new_from_stock(GTK_STOCK_STOP); gtkut_widget_set_can_default(stop_btn, TRUE); gtk_box_pack_start(GTK_BOX(confirm_area), stop_btn, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (vbox1), confirm_area, FALSE, FALSE, 0); gtk_widget_grab_default(next_btn); SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, bool_optmenu) SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, from_entry) SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, to_entry) SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, subject_entry) SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, body_entry) SET_TOGGLE_SENSITIVITY(adv_search_checkbtn, adv_condition_label) SET_TOGGLE_SENSITIVITY(adv_search_checkbtn, adv_condition_entry) SET_TOGGLE_SENSITIVITY(adv_search_checkbtn, adv_condition_btn) SET_TOGGLE_SENSITIVITY_REVERSE(adv_search_checkbtn, case_checkbtn) g_signal_connect(G_OBJECT(help_btn), "clicked", G_CALLBACK(manual_open_with_anchor_cb), MANUAL_ANCHOR_SEARCHING); g_signal_connect(G_OBJECT(clear_btn), "clicked", G_CALLBACK(summary_search_clear), NULL); g_signal_connect(G_OBJECT(all_btn), "clicked", G_CALLBACK(summary_search_all_clicked), NULL); g_signal_connect(G_OBJECT(prev_btn), "clicked", G_CALLBACK(summary_search_prev_clicked), NULL); g_signal_connect(G_OBJECT(next_btn), "clicked", G_CALLBACK(summary_search_next_clicked), NULL); g_signal_connect_closure (G_OBJECT(close_btn), "clicked", g_cclosure_new_swap(G_CALLBACK(gtk_widget_hide), window, NULL), FALSE); g_signal_connect(G_OBJECT(stop_btn), "clicked", G_CALLBACK(summary_search_stop_clicked), NULL); search_window.window = window; search_window.bool_optmenu = bool_optmenu; search_window.from_entry = from_entry; search_window.to_entry = to_entry; search_window.subject_entry = subject_entry; search_window.body_entry = body_entry; search_window.adv_condition_entry = adv_condition_entry; search_window.adv_condition_btn = adv_condition_btn; search_window.case_checkbtn = case_checkbtn; search_window.adv_search_checkbtn = adv_search_checkbtn; search_window.clear_btn = clear_btn; search_window.help_btn = help_btn; search_window.all_btn = all_btn; search_window.prev_btn = prev_btn; search_window.next_btn = next_btn; search_window.close_btn = close_btn; search_window.stop_btn = stop_btn; search_window.advsearch = NULL; search_window.matcher_is_outdated = TRUE; search_window.search_in_progress = FALSE; search_window.matched_msgnums = NULL; search_window.is_searching = is_searching; } static gboolean summary_search_verify_match(MsgInfo *msg) { gpointer msgnum = GUINT_TO_POINTER(msg->msgnum); if (g_hash_table_lookup(search_window.matched_msgnums, msgnum) != NULL) return TRUE; else return FALSE; } static gboolean summary_search_progress_cb(gpointer data, guint at, guint matched, guint total) { if (!search_window.is_searching) { search_window.matcher_is_outdated = TRUE; return FALSE; } return summaryview_search_root_progress(search_window.summaryview, at, matched, total); } static gboolean summary_search_prepare_matcher() { gboolean adv_search; gboolean bool_and = FALSE; gboolean case_sens = FALSE; gchar *matcher_str; gint match_type; gchar *from_str = NULL, *to_str = NULL, *subject_str = NULL; gchar *body_str = NULL; GSList *matchers = NULL; if (!search_window.matcher_is_outdated) return TRUE; if (search_window.advsearch == NULL) { search_window.advsearch = advsearch_new(); advsearch_set_on_error_cb(search_window.advsearch, NULL, NULL); /* TODO */ advsearch_set_on_progress_cb(search_window.advsearch, summary_search_progress_cb, NULL); } adv_search = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(search_window.adv_search_checkbtn)); if (adv_search) { matcher_str = add_history_get(search_window.adv_condition_entry, &prefs_common.summary_search_adv_condition_history); } else { MatcherList *matcher_list; bool_and = combobox_get_active_data(GTK_COMBO_BOX(search_window.bool_optmenu)); case_sens = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(search_window.case_checkbtn)); from_str = add_history_get(search_window.from_entry, &prefs_common.summary_search_from_history); to_str = add_history_get(search_window.to_entry, &prefs_common.summary_search_to_history); subject_str = add_history_get(search_window.subject_entry, &prefs_common.summary_search_subject_history); body_str = add_history_get(search_window.body_entry, &prefs_common.summary_search_body_history); if (!from_str && !to_str && !subject_str && !body_str) { /* TODO: warn if no search criteria? (or make buttons enabled only when * at least one search criteria has been set */ return FALSE; } match_type = case_sens ? MATCHTYPE_MATCH : MATCHTYPE_MATCHCASE; if (from_str) { MatcherProp *prop = matcherprop_new(MATCHCRITERIA_FROM, NULL, match_type, from_str, 0); matchers = g_slist_append(matchers, prop); } if (to_str) { MatcherProp *prop = matcherprop_new(MATCHCRITERIA_TO, NULL, match_type, to_str, 0); matchers = g_slist_append(matchers, prop); } if (subject_str) { MatcherProp *prop = matcherprop_new(MATCHCRITERIA_SUBJECT, NULL, match_type, subject_str, 0); matchers = g_slist_append(matchers, prop); } if (body_str) { MatcherProp *prop = matcherprop_new(MATCHCRITERIA_BODY_PART, NULL, match_type, body_str, 0); matchers = g_slist_append(matchers, prop); } g_free(from_str); g_free(to_str); g_free(subject_str); g_free(body_str); matcher_list = matcherlist_new(matchers, bool_and); if (!matcher_list) return FALSE; matcher_str = matcherlist_to_string(matcher_list); matcherlist_free(matcher_list); } if (!matcher_str) return FALSE; advsearch_set(search_window.advsearch, ADVANCED_SEARCH_EXTENDED, matcher_str); debug_print("Advsearch set: %s\n", matcher_str); g_free(matcher_str); if (!advsearch_has_proper_predicate(search_window.advsearch)) return FALSE; search_window.matcher_is_outdated = FALSE; return TRUE; } static gboolean summary_search_prereduce_msg_list() { MsgInfoList *msglist = NULL; MsgNumberList *msgnums = NULL; MsgNumberList *cur; SummaryView *summaryview = search_window.summaryview; gboolean result; FolderItem *item = summaryview->folder_item; static GdkCursor *watch_cursor = NULL; if (!watch_cursor) watch_cursor = gdk_cursor_new(GDK_WATCH); if (search_window.matcher_is_outdated && !summary_search_prepare_matcher()) { return FALSE; } main_window_cursor_wait(mainwindow_get_mainwindow()); gdk_window_set_cursor(gtk_widget_get_window(search_window.window), watch_cursor); statusbar_print_all(_("Searching in %s... \n"), item->path ? item->path : "(null)"); result = advsearch_search_msgs_in_folders(search_window.advsearch, &msglist, item, FALSE); statusbar_pop_all(); statusbar_progress_all(0, 0, 0); gdk_window_set_cursor(gtk_widget_get_window(search_window.window), NULL); main_window_cursor_normal(mainwindow_get_mainwindow()); if (!result) return FALSE; msgnums = procmsg_get_number_list_for_msgs(msglist); procmsg_msg_list_free(msglist); if (search_window.matched_msgnums == NULL) search_window.matched_msgnums = g_hash_table_new(g_direct_hash, NULL); g_hash_table_remove_all(search_window.matched_msgnums); for (cur = msgnums; cur != NULL; cur = cur->next) { g_hash_table_insert(search_window.matched_msgnums, cur->data, GINT_TO_POINTER(1)); } g_slist_free(msgnums); return TRUE; } static void summary_search_execute(gboolean backward, gboolean search_all) { SummaryView *summaryview = search_window.summaryview; GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); GtkCMCTreeNode *node; MsgInfo *msginfo; gboolean all_searched = FALSE; gboolean matched = FALSE; gint i = 0; if (summary_is_locked(summaryview)) { return; } summary_lock(summaryview); search_window.matcher_is_outdated = TRUE; search_window.is_searching = TRUE; main_window_cursor_wait(summaryview->mainwin); summary_show_stop_button(); if (search_window.matcher_is_outdated && !summary_search_prereduce_msg_list()) { goto exit; } if (search_all) { summary_freeze(summaryview); summary_unselect_all(summaryview); node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); backward = FALSE; } else if (!summaryview->selected) { if (backward) { node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list_end); } else { node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); } if (!node) { search_window.is_searching = FALSE; summary_hide_stop_button(); main_window_cursor_normal(summaryview->mainwin); summary_unlock(summaryview); return; } } else { if (backward) { node = gtkut_ctree_node_prev(ctree, summaryview->selected); } else { node = gtkut_ctree_node_next(ctree, summaryview->selected); } } for (; search_window.is_searching; i++) { if (!node) { gchar *str; AlertValue val; if (search_all) { break; } if (all_searched) { alertpanel_full(_("Search failed"), _("Search string not found."), GTK_STOCK_CLOSE, NULL, NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT); break; } if (backward) str = _("Beginning of list reached; continue from end?"); else str = _("End of list reached; continue from beginning?"); val = alertpanel(_("Search finished"), str, GTK_STOCK_NO, "+" GTK_STOCK_YES, NULL); if (G_ALERTALTERNATE == val) { if (backward) { node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list_end); } else { node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); } all_searched = TRUE; manage_window_focus_in(search_window.window, NULL, NULL); } else { break; } } msginfo = gtk_cmctree_node_get_row_data(ctree, node); if (msginfo) matched = summary_search_verify_match(msginfo); else matched = FALSE; if (matched) { if (search_all) { gtk_cmctree_select(ctree, node); } else { if (messageview_is_visible(summaryview->messageview)) { summary_unlock(summaryview); summary_select_node(summaryview, node, 1); summary_lock(summaryview); } else { summary_select_node(summaryview, node, 0); } break; } } if (i % (search_window.is_fast ? 1000 : 100) == 0) { GTK_EVENTS_FLUSH(); } node = backward ? gtkut_ctree_node_prev(ctree, node) : gtkut_ctree_node_next(ctree, node); } exit: search_window.is_searching = FALSE; summary_hide_stop_button(); main_window_cursor_normal(summaryview->mainwin); if (search_all) { summary_thaw_with_status(summaryview); } summary_unlock(summaryview); } static void summary_search_clear(GtkButton *button, gpointer data) { if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(search_window.adv_search_checkbtn))) { gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((search_window.adv_condition_entry)))), ""); } else { gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((search_window.from_entry)))), ""); gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((search_window.to_entry)))), ""); gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((search_window.subject_entry)))), ""); gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((search_window.body_entry)))), ""); } /* stop searching */ if (search_window.is_searching) { search_window.is_searching = FALSE; } search_window.matcher_is_outdated = TRUE; } static void summary_search_prev_clicked(GtkButton *button, gpointer data) { summary_search_execute(TRUE, FALSE); } static void summary_search_next_clicked(GtkButton *button, gpointer data) { summary_search_execute(FALSE, FALSE); } static void summary_search_all_clicked(GtkButton *button, gpointer data) { summary_search_execute(FALSE, TRUE); } static void adv_condition_btn_done(MatcherList * matchers) { gchar *str; cm_return_if_fail( mainwindow_get_mainwindow()->summaryview->quicksearch != NULL); if (matchers == NULL) { return; } str = matcherlist_to_string(matchers); search_window.matcher_is_outdated = TRUE; if (str != NULL) { gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((search_window.adv_condition_entry)))), str); g_free(str); } } static void summary_search_stop_clicked(GtkButton *button, gpointer data) { search_window.is_searching = FALSE; } static void adv_condition_btn_clicked(GtkButton *button, gpointer data) { const gchar * cond_str; MatcherList * matchers = NULL; cm_return_if_fail( search_window.window != NULL ); /* re-use the current search value if it's a condition expression, otherwise ignore it silently */ #if !GTK_CHECK_VERSION(2, 24, 0) cond_str = gtk_combo_box_get_active_text(GTK_COMBO_BOX(search_window.adv_condition_entry)); #else cond_str = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(search_window.adv_condition_entry)); #endif if (cond_str && *cond_str != '\0') { matchers = matcher_parser_get_cond((gchar*)cond_str, NULL); } prefs_matcher_open(matchers, adv_condition_btn_done); if (matchers != NULL) { matcherlist_free(matchers); } }; static void from_changed(void) { if (!search_window.from_entry_has_focus) gtk_widget_grab_focus(search_window.from_entry); search_window.matcher_is_outdated = TRUE; } static void to_changed(void) { if (!search_window.to_entry_has_focus) gtk_widget_grab_focus(search_window.to_entry); search_window.matcher_is_outdated = TRUE; } static void subject_changed(void) { if (!search_window.subject_entry_has_focus) gtk_widget_grab_focus(search_window.subject_entry); search_window.matcher_is_outdated = TRUE; } static void body_changed(void) { if (!search_window.body_entry_has_focus) gtk_widget_grab_focus(search_window.body_entry); search_window.matcher_is_outdated = TRUE; } static void adv_condition_changed(void) { if (!search_window.adv_condition_entry_has_focus) gtk_widget_grab_focus(search_window.adv_condition_entry); search_window.matcher_is_outdated = TRUE; } static gboolean from_entry_focus_evt_in(GtkWidget *widget, GdkEventFocus *event, gpointer data) { search_window.from_entry_has_focus = TRUE; return FALSE; } static gboolean from_entry_focus_evt_out(GtkWidget *widget, GdkEventFocus *event, gpointer data) { search_window.from_entry_has_focus = FALSE; return FALSE; } static gboolean to_entry_focus_evt_in(GtkWidget *widget, GdkEventFocus *event, gpointer data) { search_window.to_entry_has_focus = TRUE; return FALSE; } static gboolean to_entry_focus_evt_out(GtkWidget *widget, GdkEventFocus *event, gpointer data) { search_window.to_entry_has_focus = FALSE; return FALSE; } static gboolean subject_entry_focus_evt_in(GtkWidget *widget, GdkEventFocus *event, gpointer data) { search_window.subject_entry_has_focus = TRUE; return FALSE; } static gboolean subject_entry_focus_evt_out(GtkWidget *widget, GdkEventFocus *event, gpointer data) { search_window.subject_entry_has_focus = FALSE; return FALSE; } static gboolean body_entry_focus_evt_in(GtkWidget *widget, GdkEventFocus *event, gpointer data) { search_window.body_entry_has_focus = TRUE; return FALSE; } static gboolean body_entry_focus_evt_out(GtkWidget *widget, GdkEventFocus *event, gpointer data) { search_window.body_entry_has_focus = FALSE; return FALSE; } static gboolean adv_condition_entry_focus_evt_in(GtkWidget *widget, GdkEventFocus *event, gpointer data) { search_window.adv_condition_entry_has_focus = TRUE; return FALSE; } static gboolean adv_condition_entry_focus_evt_out(GtkWidget *widget, GdkEventFocus *event, gpointer data) { search_window.adv_condition_entry_has_focus = FALSE; return FALSE; } static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data) { if (event && (event->keyval == GDK_KEY_Escape)) { /* ESC key will: - stop a running search - close the search window if no search is running */ if (!search_window.is_searching) { gtk_widget_hide(search_window.window); } else { search_window.is_searching = FALSE; } } if (event && (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter)) { if (!search_window.is_searching) { summary_search_execute(FALSE, FALSE); } } if (event && (event->keyval == GDK_KEY_Down || event->keyval == GDK_KEY_Up)) { if (search_window.from_entry_has_focus) { combobox_set_value_from_arrow_key( GTK_COMBO_BOX(search_window.from_entry), event->keyval); return TRUE; } if (search_window.to_entry_has_focus) { combobox_set_value_from_arrow_key( GTK_COMBO_BOX(search_window.to_entry), event->keyval); return TRUE; } if (search_window.subject_entry_has_focus) { combobox_set_value_from_arrow_key( GTK_COMBO_BOX(search_window.subject_entry), event->keyval); return TRUE; } if (search_window.body_entry_has_focus) { combobox_set_value_from_arrow_key( GTK_COMBO_BOX(search_window.body_entry), event->keyval); return TRUE; } if (search_window.adv_condition_entry_has_focus) { combobox_set_value_from_arrow_key( GTK_COMBO_BOX(search_window.adv_condition_entry), event->keyval); return TRUE; } } return FALSE; }