Statistics
| Revision:

root / src / query_search.c @ 3063

History | View | Annotate | Download (34.4 kB)

1 1 hiro
/*
2 1 hiro
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 2276 hiro
 * Copyright (C) 1999-2009 Hiroyuki Yamamoto
4 1 hiro
 *
5 1 hiro
 * This program is free software; you can redistribute it and/or modify
6 1 hiro
 * it under the terms of the GNU General Public License as published by
7 1 hiro
 * the Free Software Foundation; either version 2 of the License, or
8 1 hiro
 * (at your option) any later version.
9 1 hiro
 *
10 1 hiro
 * This program is distributed in the hope that it will be useful,
11 1 hiro
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 1 hiro
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 1 hiro
 * GNU General Public License for more details.
14 1 hiro
 *
15 1 hiro
 * You should have received a copy of the GNU General Public License
16 1 hiro
 * along with this program; if not, write to the Free Software
17 1 hiro
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 1 hiro
 */
19 1 hiro
20 1 hiro
#ifdef HAVE_CONFIG_H
21 1 hiro
#  include "config.h"
22 1 hiro
#endif
23 1 hiro
24 1 hiro
#include "defs.h"
25 1 hiro
26 1 hiro
#include <glib.h>
27 92 hiro
#include <glib/gi18n.h>
28 1 hiro
#include <gdk/gdkkeysyms.h>
29 1 hiro
#include <gtk/gtkwidget.h>
30 1 hiro
#include <gtk/gtkwindow.h>
31 1 hiro
#include <gtk/gtkvbox.h>
32 1 hiro
#include <gtk/gtktable.h>
33 1 hiro
#include <gtk/gtkoptionmenu.h>
34 1 hiro
#include <gtk/gtklabel.h>
35 1 hiro
#include <gtk/gtkentry.h>
36 1 hiro
#include <gtk/gtkhbox.h>
37 1 hiro
#include <gtk/gtkcheckbutton.h>
38 1 hiro
#include <gtk/gtkhbbox.h>
39 1 hiro
#include <gtk/gtkbutton.h>
40 237 hiro
#include <gtk/gtkmenuitem.h>
41 237 hiro
#include <gtk/gtkstock.h>
42 241 hiro
#include <gtk/gtktreemodel.h>
43 836 hiro
#include <gtk/gtkliststore.h>
44 836 hiro
#include <gtk/gtktreeview.h>
45 241 hiro
#include <gtk/gtktreeselection.h>
46 836 hiro
#include <gtk/gtkcellrenderertext.h>
47 1 hiro
#include <stdio.h>
48 1 hiro
#include <stdlib.h>
49 1 hiro
#include <string.h>
50 1 hiro
51 934 hiro
#include "query_search.h"
52 1 hiro
#include "summaryview.h"
53 1 hiro
#include "messageview.h"
54 1 hiro
#include "mainwindow.h"
55 836 hiro
#include "folderview.h"
56 1 hiro
#include "menu.h"
57 1 hiro
#include "utils.h"
58 1 hiro
#include "gtkutils.h"
59 1 hiro
#include "manage_window.h"
60 1 hiro
#include "alertpanel.h"
61 836 hiro
#include "foldersel.h"
62 848 hiro
#include "statusbar.h"
63 836 hiro
#include "procmsg.h"
64 836 hiro
#include "procheader.h"
65 836 hiro
#include "folder.h"
66 836 hiro
#include "filter.h"
67 836 hiro
#include "prefs_filter.h"
68 836 hiro
#include "prefs_filter_edit.h"
69 1 hiro
70 836 hiro
enum
71 836 hiro
{
72 836 hiro
        COL_FOLDER,
73 836 hiro
        COL_SUBJECT,
74 836 hiro
        COL_FROM,
75 836 hiro
        COL_DATE,
76 836 hiro
        COL_MSGINFO,
77 836 hiro
        N_COLS
78 836 hiro
};
79 836 hiro
80 934 hiro
static struct QuerySearchWindow {
81 271 hiro
        GtkWidget *window;
82 1 hiro
83 271 hiro
        GtkWidget *bool_optmenu;
84 1 hiro
85 836 hiro
        FilterCondEdit *cond_edit;
86 271 hiro
87 836 hiro
        GtkWidget *folder_entry;
88 836 hiro
        GtkWidget *folder_btn;
89 836 hiro
90 836 hiro
        GtkWidget *subfolder_checkbtn;
91 271 hiro
        GtkWidget *case_checkbtn;
92 271 hiro
93 836 hiro
        GtkWidget *treeview;
94 836 hiro
        GtkListStore *store;
95 836 hiro
96 836 hiro
        GtkWidget *status_label;
97 836 hiro
98 271 hiro
        GtkWidget *clear_btn;
99 836 hiro
        GtkWidget *search_btn;
100 836 hiro
        GtkWidget *save_btn;
101 406 hiro
        GtkWidget *close_btn;
102 271 hiro
103 836 hiro
        FilterRule *rule;
104 836 hiro
        gboolean requires_full_headers;
105 836 hiro
106 928 hiro
        gboolean exclude_trash;
107 928 hiro
108 1967 hiro
        gint n_found;
109 1967 hiro
110 836 hiro
        gboolean on_search;
111 836 hiro
        gboolean cancelled;
112 271 hiro
} search_window;
113 271 hiro
114 836 hiro
typedef struct {
115 836 hiro
        GtkWidget *window;
116 836 hiro
117 836 hiro
        GtkWidget *folder_entry;
118 836 hiro
        GtkWidget *name_entry;
119 836 hiro
120 876 hiro
        GtkWidget *ok_btn;
121 876 hiro
        GtkWidget *cancel_btn;
122 876 hiro
123 836 hiro
        gboolean cancelled;
124 836 hiro
        gboolean finished;
125 934 hiro
} QuerySearchSaveDialog;
126 836 hiro
127 934 hiro
static void query_search_create        (void);
128 271 hiro
129 934 hiro
static FilterRule *query_search_dialog_to_rule        (const gchar        *name,
130 934 hiro
                                                 FolderItem    **item);
131 271 hiro
132 934 hiro
static void query_search_query                        (void);
133 934 hiro
static void query_search_folder                        (FolderItem        *item);
134 836 hiro
135 934 hiro
static gboolean query_search_recursive_func        (GNode                *node,
136 836 hiro
                                                 gpointer         data);
137 836 hiro
138 934 hiro
static void query_search_append_msg        (MsgInfo        *msginfo);
139 934 hiro
static void query_search_clear_list        (void);
140 836 hiro
141 934 hiro
static void query_search_hbox_added        (CondHBox        *hbox);
142 836 hiro
143 836 hiro
static void row_activated                (GtkTreeView                *treeview,
144 836 hiro
                                         GtkTreePath                *path,
145 836 hiro
                                         GtkTreeViewColumn        *column,
146 836 hiro
                                         gpointer                 data);
147 836 hiro
148 836 hiro
static gboolean row_selected                (GtkTreeSelection        *selection,
149 836 hiro
                                         GtkTreeModel                *model,
150 836 hiro
                                         GtkTreePath                *path,
151 836 hiro
                                         gboolean                 cur_selected,
152 836 hiro
                                         gpointer                 data);
153 836 hiro
154 1532 hiro
static void query_search_clear                (GtkButton        *button,
155 271 hiro
                                         gpointer         data);
156 1532 hiro
static void query_select_folder                (GtkButton        *button,
157 271 hiro
                                         gpointer         data);
158 934 hiro
static void query_search_clicked        (GtkButton        *button,
159 271 hiro
                                         gpointer         data);
160 934 hiro
static void query_search_save                (GtkButton        *button,
161 271 hiro
                                         gpointer         data);
162 934 hiro
static void query_search_close                (GtkButton        *button,
163 836 hiro
                                         gpointer         data);
164 271 hiro
165 934 hiro
static void query_search_entry_activated(GtkWidget        *widget,
166 934 hiro
                                         gpointer         data);
167 271 hiro
168 934 hiro
static gint query_search_deleted        (GtkWidget        *widget,
169 836 hiro
                                         GdkEventAny        *event,
170 836 hiro
                                         gpointer         data);
171 271 hiro
static gboolean key_pressed                (GtkWidget        *widget,
172 271 hiro
                                         GdkEventKey        *event,
173 271 hiro
                                         gpointer         data);
174 271 hiro
175 1041 hiro
static gint query_search_cmp_by_folder        (GtkTreeModel        *model,
176 1041 hiro
                                         GtkTreeIter        *a,
177 1041 hiro
                                         GtkTreeIter        *b,
178 1041 hiro
                                         gpointer         data);
179 1041 hiro
static gint query_search_cmp_by_subject        (GtkTreeModel        *model,
180 1041 hiro
                                         GtkTreeIter        *a,
181 1041 hiro
                                         GtkTreeIter        *b,
182 1041 hiro
                                         gpointer         data);
183 1041 hiro
static gint query_search_cmp_by_from        (GtkTreeModel        *model,
184 1041 hiro
                                         GtkTreeIter        *a,
185 1041 hiro
                                         GtkTreeIter        *b,
186 1041 hiro
                                         gpointer         data);
187 1041 hiro
static gint query_search_cmp_by_date        (GtkTreeModel        *model,
188 1041 hiro
                                         GtkTreeIter        *a,
189 1041 hiro
                                         GtkTreeIter        *b,
190 1041 hiro
                                         gpointer         data);
191 271 hiro
192 1041 hiro
193 934 hiro
void query_search(FolderItem *item)
194 1 hiro
{
195 836 hiro
        gchar *id;
196 836 hiro
197 271 hiro
        if (!search_window.window)
198 934 hiro
                query_search_create();
199 1 hiro
        else
200 869 hiro
                gtk_window_present(GTK_WINDOW(search_window.window));
201 1 hiro
202 848 hiro
        if (item && item->stype != F_VIRTUAL) {
203 836 hiro
                id = folder_item_get_identifier(item);
204 836 hiro
                gtk_entry_set_text(GTK_ENTRY(search_window.folder_entry), id);
205 836 hiro
                g_free(id);
206 836 hiro
        } else
207 836 hiro
                gtk_entry_set_text(GTK_ENTRY(search_window.folder_entry), "");
208 836 hiro
209 836 hiro
        gtk_widget_grab_focus(search_window.search_btn);
210 271 hiro
        gtk_widget_show(search_window.window);
211 1 hiro
}
212 1 hiro
213 934 hiro
static void query_search_create(void)
214 1 hiro
{
215 271 hiro
        GtkWidget *window;
216 1 hiro
        GtkWidget *vbox1;
217 1 hiro
        GtkWidget *bool_hbox;
218 271 hiro
        GtkWidget *bool_optmenu;
219 1 hiro
        GtkWidget *bool_menu;
220 1 hiro
        GtkWidget *menuitem;
221 271 hiro
        GtkWidget *clear_btn;
222 836 hiro
        GtkWidget *search_btn;
223 271 hiro
224 836 hiro
        GtkWidget *scrolledwin;
225 836 hiro
        FilterCondEdit *cond_edit;
226 836 hiro
        CondHBox *cond_hbox;
227 271 hiro
228 836 hiro
        GtkWidget *folder_hbox;
229 836 hiro
        GtkWidget *folder_label;
230 836 hiro
        GtkWidget *folder_entry;
231 836 hiro
        GtkWidget *folder_btn;
232 836 hiro
233 1 hiro
        GtkWidget *checkbtn_hbox;
234 836 hiro
        GtkWidget *subfolder_checkbtn;
235 271 hiro
        GtkWidget *case_checkbtn;
236 271 hiro
237 836 hiro
        GtkWidget *treeview;
238 836 hiro
        GtkListStore *store;
239 836 hiro
        GtkTreeViewColumn *column;
240 836 hiro
        GtkCellRenderer *renderer;
241 836 hiro
        GtkTreeSelection *selection;
242 836 hiro
243 1 hiro
        GtkWidget *confirm_area;
244 836 hiro
245 836 hiro
        GtkWidget *status_label;
246 836 hiro
247 837 hiro
        GtkWidget *btn_hbox;
248 836 hiro
        GtkWidget *hbbox;
249 836 hiro
        GtkWidget *save_btn;
250 406 hiro
        GtkWidget *close_btn;
251 1 hiro
252 1 hiro
        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
253 1 hiro
        gtk_window_set_title(GTK_WINDOW (window), _("Search messages"));
254 836 hiro
        gtk_widget_set_size_request(window, 600, -1);
255 1 hiro
        gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, TRUE);
256 1 hiro
        gtk_container_set_border_width(GTK_CONTAINER (window), 8);
257 1 hiro
        g_signal_connect(G_OBJECT(window), "delete_event",
258 934 hiro
                         G_CALLBACK(query_search_deleted), NULL);
259 1 hiro
        g_signal_connect(G_OBJECT(window), "key_press_event",
260 1 hiro
                         G_CALLBACK(key_pressed), NULL);
261 1 hiro
        MANAGE_WINDOW_SIGNALS_CONNECT(window);
262 1 hiro
263 836 hiro
        vbox1 = gtk_vbox_new (FALSE, 6);
264 1 hiro
        gtk_widget_show (vbox1);
265 1 hiro
        gtk_container_add (GTK_CONTAINER (window), vbox1);
266 1 hiro
267 836 hiro
        bool_hbox = gtk_hbox_new(FALSE, 12);
268 1 hiro
        gtk_widget_show(bool_hbox);
269 1 hiro
        gtk_box_pack_start(GTK_BOX(vbox1), bool_hbox, FALSE, FALSE, 0);
270 1 hiro
271 1 hiro
        bool_optmenu = gtk_option_menu_new();
272 1 hiro
        gtk_widget_show(bool_optmenu);
273 1 hiro
        gtk_box_pack_start(GTK_BOX(bool_hbox), bool_optmenu, FALSE, FALSE, 0);
274 1 hiro
275 1 hiro
        bool_menu = gtk_menu_new();
276 836 hiro
        MENUITEM_ADD(bool_menu, menuitem, _("Match any of the following"),
277 836 hiro
                     FLT_OR);
278 836 hiro
        MENUITEM_ADD(bool_menu, menuitem, _("Match all of the following"),
279 836 hiro
                     FLT_AND);
280 1 hiro
        gtk_option_menu_set_menu(GTK_OPTION_MENU(bool_optmenu), bool_menu);
281 2776 hiro
        gtk_option_menu_set_history(GTK_OPTION_MENU(bool_optmenu), FLT_AND);
282 1 hiro
283 836 hiro
        hbbox = gtk_hbutton_box_new();
284 836 hiro
        gtk_widget_show(hbbox);
285 836 hiro
        gtk_button_box_set_layout(GTK_BUTTON_BOX(hbbox), GTK_BUTTONBOX_END);
286 836 hiro
        gtk_box_set_spacing(GTK_BOX(hbbox), 6);
287 836 hiro
        gtk_box_pack_end(GTK_BOX(bool_hbox), hbbox, FALSE, FALSE, 0);
288 836 hiro
289 271 hiro
        clear_btn = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
290 271 hiro
        gtk_widget_show(clear_btn);
291 836 hiro
        gtk_box_pack_start(GTK_BOX(hbbox), clear_btn, FALSE, FALSE, 0);
292 271 hiro
293 836 hiro
        search_btn = gtk_button_new_from_stock(GTK_STOCK_FIND);
294 836 hiro
        GTK_WIDGET_SET_FLAGS(search_btn, GTK_CAN_DEFAULT);
295 836 hiro
        gtk_widget_show(search_btn);
296 836 hiro
        gtk_box_pack_start(GTK_BOX(hbbox), search_btn, FALSE, FALSE, 0);
297 836 hiro
        gtk_widget_grab_default(search_btn);
298 1 hiro
299 836 hiro
        scrolledwin = gtk_scrolled_window_new(NULL, NULL);
300 836 hiro
        gtk_widget_show(scrolledwin);
301 836 hiro
        gtk_box_pack_start(GTK_BOX(vbox1), scrolledwin, FALSE, FALSE, 0);
302 836 hiro
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
303 836 hiro
                                       GTK_POLICY_AUTOMATIC,
304 836 hiro
                                       GTK_POLICY_AUTOMATIC);
305 836 hiro
        gtk_widget_set_size_request(scrolledwin, -1, 120);
306 1 hiro
307 836 hiro
        cond_edit = prefs_filter_edit_cond_edit_create();
308 934 hiro
        cond_edit->add_hbox = query_search_hbox_added;
309 836 hiro
        gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolledwin),
310 836 hiro
                                              cond_edit->cond_vbox);
311 836 hiro
        prefs_filter_set_header_list(NULL);
312 836 hiro
        prefs_filter_edit_set_header_list(cond_edit, NULL);
313 836 hiro
        cond_hbox = prefs_filter_edit_cond_hbox_create(cond_edit);
314 836 hiro
        prefs_filter_edit_set_cond_hbox_widgets(cond_hbox, PF_COND_HEADER);
315 836 hiro
        prefs_filter_edit_insert_cond_hbox(cond_edit, cond_hbox, -1);
316 836 hiro
        if (cond_edit->add_hbox)
317 836 hiro
                cond_edit->add_hbox(cond_hbox);
318 1 hiro
319 836 hiro
        folder_hbox = gtk_hbox_new (FALSE, 8);
320 836 hiro
        gtk_widget_show (folder_hbox);
321 836 hiro
        gtk_box_pack_start (GTK_BOX (vbox1), folder_hbox, FALSE, FALSE, 0);
322 1 hiro
323 836 hiro
        folder_label = gtk_label_new (_("Folder:"));
324 836 hiro
        gtk_widget_show (folder_label);
325 836 hiro
        gtk_box_pack_start (GTK_BOX (folder_hbox), folder_label,
326 836 hiro
                            FALSE, FALSE, 0);
327 1 hiro
328 836 hiro
        folder_entry = gtk_entry_new ();
329 836 hiro
        gtk_widget_show (folder_entry);
330 836 hiro
        gtk_box_pack_start (GTK_BOX (folder_hbox), folder_entry, TRUE, TRUE, 0);
331 1 hiro
332 836 hiro
        folder_btn = gtk_button_new_with_label("...");
333 836 hiro
        gtk_widget_show (folder_btn);
334 836 hiro
        gtk_box_pack_start (GTK_BOX (folder_hbox), folder_btn, FALSE, FALSE, 0);
335 1 hiro
336 836 hiro
        checkbtn_hbox = gtk_hbox_new (FALSE, 12);
337 836 hiro
        gtk_widget_show (checkbtn_hbox);
338 836 hiro
        gtk_box_pack_start (GTK_BOX (vbox1), checkbtn_hbox, FALSE, FALSE, 0);
339 1 hiro
340 836 hiro
        subfolder_checkbtn =
341 836 hiro
                gtk_check_button_new_with_label (_("Search subfolders"));
342 836 hiro
        gtk_widget_show (subfolder_checkbtn);
343 836 hiro
        gtk_box_pack_start (GTK_BOX (checkbtn_hbox), subfolder_checkbtn,
344 836 hiro
                            FALSE, FALSE, 0);
345 1 hiro
346 1 hiro
        case_checkbtn = gtk_check_button_new_with_label (_("Case sensitive"));
347 1 hiro
        gtk_widget_show (case_checkbtn);
348 1 hiro
        gtk_box_pack_start (GTK_BOX (checkbtn_hbox), case_checkbtn,
349 1 hiro
                            FALSE, FALSE, 0);
350 1 hiro
351 836 hiro
        scrolledwin = gtk_scrolled_window_new(NULL, NULL);
352 836 hiro
        gtk_box_pack_start(GTK_BOX(vbox1), scrolledwin, TRUE, TRUE, 0);
353 836 hiro
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
354 836 hiro
                                       GTK_POLICY_AUTOMATIC,
355 836 hiro
                                       GTK_POLICY_AUTOMATIC);
356 836 hiro
        gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
357 836 hiro
                                            GTK_SHADOW_IN);
358 836 hiro
        gtk_widget_set_size_request(scrolledwin, -1, 150);
359 1 hiro
360 836 hiro
        store = gtk_list_store_new(N_COLS,
361 836 hiro
                                   G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
362 836 hiro
                                   G_TYPE_STRING, G_TYPE_POINTER);
363 836 hiro
        treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
364 836 hiro
        g_object_unref(store);
365 836 hiro
        gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE);
366 836 hiro
        g_signal_connect(G_OBJECT(treeview), "row-activated",
367 836 hiro
                         G_CALLBACK(row_activated), NULL);
368 406 hiro
369 1041 hiro
        gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), COL_FOLDER,
370 1041 hiro
                                        query_search_cmp_by_folder, NULL, NULL);
371 1041 hiro
        gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), COL_SUBJECT,
372 1041 hiro
                                        query_search_cmp_by_subject,
373 1041 hiro
                                        NULL, NULL);
374 1041 hiro
        gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), COL_FROM,
375 1041 hiro
                                        query_search_cmp_by_from, NULL, NULL);
376 1041 hiro
        gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), COL_DATE,
377 1041 hiro
                                        query_search_cmp_by_date, NULL, NULL);
378 1041 hiro
379 836 hiro
        selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
380 836 hiro
        gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
381 836 hiro
        gtk_tree_selection_set_select_function(selection, row_selected,
382 836 hiro
                                               NULL, NULL);
383 1 hiro
384 836 hiro
        gtk_container_add(GTK_CONTAINER(scrolledwin), treeview);
385 271 hiro
386 836 hiro
#define APPEND_COLUMN(label, col, width)                                \
387 836 hiro
{                                                                        \
388 836 hiro
        renderer = gtk_cell_renderer_text_new();                        \
389 836 hiro
        column = gtk_tree_view_column_new_with_attributes                \
390 836 hiro
                (label, renderer, "text", col, NULL);                        \
391 836 hiro
        gtk_tree_view_column_set_resizable(column, TRUE);                \
392 836 hiro
        if (width) {                                                        \
393 836 hiro
                gtk_tree_view_column_set_sizing                                \
394 836 hiro
                        (column, GTK_TREE_VIEW_COLUMN_FIXED);                \
395 836 hiro
                gtk_tree_view_column_set_fixed_width(column, width);        \
396 836 hiro
        }                                                                \
397 1041 hiro
        gtk_tree_view_column_set_sort_column_id(column, col);                \
398 836 hiro
        gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);        \
399 836 hiro
}
400 271 hiro
401 836 hiro
        APPEND_COLUMN(_("Folder"), COL_FOLDER, 0);
402 836 hiro
        APPEND_COLUMN(_("Subject"), COL_SUBJECT, 200);
403 836 hiro
        APPEND_COLUMN(_("From"), COL_FROM, 180);
404 836 hiro
        APPEND_COLUMN(_("Date"), COL_DATE, 0);
405 1 hiro
406 836 hiro
        gtk_widget_show_all(scrolledwin);
407 836 hiro
408 836 hiro
        confirm_area = gtk_hbox_new(FALSE, 12);
409 836 hiro
        gtk_widget_show(confirm_area);
410 836 hiro
        gtk_box_pack_start(GTK_BOX(vbox1), confirm_area, FALSE, FALSE, 0);
411 836 hiro
412 836 hiro
        status_label = gtk_label_new("");
413 836 hiro
        gtk_widget_show(status_label);
414 836 hiro
        gtk_box_pack_start(GTK_BOX(confirm_area), status_label,
415 836 hiro
                           FALSE, FALSE, 0);
416 836 hiro
417 837 hiro
        btn_hbox = gtk_hbox_new(FALSE, 6);
418 837 hiro
        gtk_widget_show(btn_hbox);
419 837 hiro
        gtk_box_pack_end(GTK_BOX(confirm_area), btn_hbox, FALSE, FALSE, 0);
420 837 hiro
421 837 hiro
        gtkut_stock_button_set_create(&hbbox, &close_btn, GTK_STOCK_CLOSE,
422 837 hiro
                                      NULL, NULL, NULL, NULL);
423 836 hiro
        gtk_widget_show(hbbox);
424 837 hiro
        gtk_box_pack_end(GTK_BOX(btn_hbox), hbbox, FALSE, FALSE, 0);
425 836 hiro
426 836 hiro
        save_btn = gtk_button_new_with_mnemonic(_("_Save as search folder"));
427 836 hiro
        gtk_widget_show(save_btn);
428 837 hiro
        gtk_box_pack_end(GTK_BOX(btn_hbox), save_btn, FALSE, FALSE, 0);
429 836 hiro
430 1 hiro
        g_signal_connect(G_OBJECT(clear_btn), "clicked",
431 934 hiro
                         G_CALLBACK(query_search_clear), NULL);
432 836 hiro
        g_signal_connect(G_OBJECT(folder_btn), "clicked",
433 934 hiro
                         G_CALLBACK(query_select_folder), NULL);
434 836 hiro
        g_signal_connect(G_OBJECT(search_btn), "clicked",
435 934 hiro
                         G_CALLBACK(query_search_clicked), NULL);
436 836 hiro
        g_signal_connect(G_OBJECT(save_btn), "clicked",
437 934 hiro
                         G_CALLBACK(query_search_save), NULL);
438 836 hiro
        g_signal_connect(G_OBJECT(close_btn), "clicked",
439 934 hiro
                         G_CALLBACK(query_search_close), NULL);
440 271 hiro
441 271 hiro
        search_window.window = window;
442 271 hiro
        search_window.bool_optmenu = bool_optmenu;
443 836 hiro
444 836 hiro
        search_window.cond_edit = cond_edit;
445 836 hiro
446 836 hiro
        search_window.folder_entry = folder_entry;
447 836 hiro
        search_window.folder_btn = folder_btn;
448 836 hiro
        search_window.subfolder_checkbtn = subfolder_checkbtn;
449 271 hiro
        search_window.case_checkbtn = case_checkbtn;
450 836 hiro
451 836 hiro
        search_window.treeview = treeview;
452 836 hiro
        search_window.store = store;
453 836 hiro
454 836 hiro
        search_window.status_label = status_label;
455 836 hiro
456 271 hiro
        search_window.clear_btn = clear_btn;
457 836 hiro
        search_window.search_btn = search_btn;
458 836 hiro
        search_window.save_btn  = save_btn;
459 406 hiro
        search_window.close_btn = close_btn;
460 1 hiro
}
461 1 hiro
462 934 hiro
static FilterRule *query_search_dialog_to_rule(const gchar *name,
463 836 hiro
                                                 FolderItem **item)
464 1 hiro
{
465 836 hiro
        const gchar *id;
466 836 hiro
        FolderItem *item_;
467 2776 hiro
        FilterBoolOp bool_op;
468 836 hiro
        gboolean recursive;
469 1 hiro
        gboolean case_sens;
470 874 hiro
        GSList *cond_list;
471 836 hiro
        FilterRule *rule;
472 1 hiro
473 836 hiro
        id = gtk_entry_get_text(GTK_ENTRY(search_window.folder_entry));
474 836 hiro
        item_ = folder_find_item_from_identifier(id);
475 836 hiro
        if (!item_)
476 836 hiro
                return NULL;
477 836 hiro
        if (item)
478 836 hiro
                *item = item_;
479 1 hiro
480 836 hiro
        bool_op = menu_get_option_menu_active_index
481 271 hiro
                (GTK_OPTION_MENU(search_window.bool_optmenu));
482 836 hiro
        recursive = gtk_toggle_button_get_active
483 836 hiro
                (GTK_TOGGLE_BUTTON(search_window.subfolder_checkbtn));
484 1 hiro
        case_sens = gtk_toggle_button_get_active
485 271 hiro
                (GTK_TOGGLE_BUTTON(search_window.case_checkbtn));
486 1 hiro
487 874 hiro
        cond_list = prefs_filter_edit_cond_edit_to_list(search_window.cond_edit,
488 874 hiro
                                                        case_sens);
489 836 hiro
        if (!cond_list)
490 836 hiro
                return NULL;
491 836 hiro
492 836 hiro
        rule = filter_rule_new(name, bool_op, cond_list, NULL);
493 836 hiro
        rule->target_folder = g_strdup(id);
494 836 hiro
        rule->recursive = recursive;
495 836 hiro
496 836 hiro
        return rule;
497 836 hiro
}
498 836 hiro
499 934 hiro
static void query_search_query(void)
500 836 hiro
{
501 836 hiro
        FolderItem *item;
502 1967 hiro
        gchar *msg;
503 836 hiro
504 836 hiro
        if (search_window.on_search)
505 836 hiro
                return;
506 836 hiro
507 836 hiro
        search_window.on_search = TRUE;
508 836 hiro
509 934 hiro
        search_window.rule = query_search_dialog_to_rule("Query rule", &item);
510 836 hiro
        if (!search_window.rule) {
511 836 hiro
                search_window.on_search = FALSE;
512 836 hiro
                return;
513 1 hiro
        }
514 836 hiro
        search_window.requires_full_headers =
515 836 hiro
                filter_rule_requires_full_headers(search_window.rule);
516 1 hiro
517 928 hiro
        if (search_window.rule->recursive) {
518 928 hiro
                if (item->stype == F_TRASH)
519 928 hiro
                        search_window.exclude_trash = FALSE;
520 928 hiro
                else
521 928 hiro
                        search_window.exclude_trash = TRUE;
522 928 hiro
        } else
523 928 hiro
                search_window.exclude_trash = FALSE;
524 928 hiro
525 1967 hiro
        search_window.n_found = 0;
526 836 hiro
        search_window.cancelled = FALSE;
527 1 hiro
528 1041 hiro
        gtk_widget_set_sensitive(search_window.clear_btn, FALSE);
529 836 hiro
        gtk_button_set_label(GTK_BUTTON(search_window.search_btn),
530 836 hiro
                             GTK_STOCK_STOP);
531 934 hiro
        query_search_clear_list();
532 1 hiro
533 836 hiro
        if (search_window.rule->recursive)
534 836 hiro
                g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
535 934 hiro
                                query_search_recursive_func, NULL);
536 836 hiro
        else
537 934 hiro
                query_search_folder(item);
538 1 hiro
539 836 hiro
        filter_rule_free(search_window.rule);
540 836 hiro
        search_window.rule = NULL;
541 836 hiro
        search_window.requires_full_headers = FALSE;
542 928 hiro
        search_window.exclude_trash = FALSE;
543 1 hiro
544 1041 hiro
        gtk_widget_set_sensitive(search_window.clear_btn, TRUE);
545 836 hiro
        gtk_button_set_label(GTK_BUTTON(search_window.search_btn),
546 836 hiro
                             GTK_STOCK_FIND);
547 1967 hiro
        if (search_window.n_found == 0)
548 1967 hiro
                msg = g_strdup_printf(_("Message not found."));
549 1967 hiro
        else if (search_window.n_found == 1)
550 1967 hiro
                msg = g_strdup_printf(_("1 message found."));
551 1967 hiro
        else
552 1967 hiro
                msg = g_strdup_printf(_("%d messages found."),
553 1967 hiro
                                      search_window.n_found);
554 1967 hiro
        gtk_label_set_text(GTK_LABEL(search_window.status_label), msg);
555 1967 hiro
        g_free(msg);
556 848 hiro
        statusbar_pop_all();
557 1 hiro
558 836 hiro
        if (search_window.cancelled)
559 836 hiro
                debug_print("* query search cancelled.\n");
560 836 hiro
        debug_print("query search finished.\n");
561 1 hiro
562 1967 hiro
        search_window.n_found = 0;
563 836 hiro
        search_window.on_search = FALSE;
564 836 hiro
        search_window.cancelled = FALSE;
565 836 hiro
}
566 1 hiro
567 2276 hiro
typedef struct _QueryData
568 836 hiro
{
569 2276 hiro
        FolderItem *item;
570 2276 hiro
        gchar *folder_name;
571 2276 hiro
        gint count;
572 2276 hiro
        gint total;
573 2276 hiro
        gint flag;
574 2276 hiro
        GTimeVal tv_prev;
575 2330 hiro
        GSList *mlist;
576 2276 hiro
#if USE_THREADS
577 2276 hiro
        GAsyncQueue *queue;
578 2284 hiro
        guint timer_tag;
579 2276 hiro
#endif
580 2276 hiro
} QueryData;
581 836 hiro
582 2284 hiro
static void query_search_folder_show_progress(const gchar *name, gint count,
583 2284 hiro
                                              gint total)
584 2276 hiro
{
585 2276 hiro
        gchar *str;
586 836 hiro
587 2276 hiro
        str = g_strdup_printf(_("Searching %s (%d / %d)..."),
588 2284 hiro
                              name, count, total);
589 836 hiro
        gtk_label_set_text(GTK_LABEL(search_window.status_label), str);
590 836 hiro
        g_free(str);
591 2276 hiro
#ifndef USE_THREADS
592 836 hiro
        ui_update();
593 2276 hiro
#endif
594 2276 hiro
}
595 836 hiro
596 2284 hiro
#if USE_THREADS
597 2284 hiro
static gboolean query_search_progress_func(gpointer data)
598 2284 hiro
{
599 2284 hiro
        QueryData *qdata = (QueryData *)data;
600 2284 hiro
        MsgInfo *msginfo;
601 2284 hiro
602 2284 hiro
        gdk_threads_enter();
603 2284 hiro
        query_search_folder_show_progress(qdata->folder_name,
604 2284 hiro
                                          g_atomic_int_get(&qdata->count),
605 2284 hiro
                                          qdata->total);
606 2284 hiro
        while ((msginfo = g_async_queue_try_pop(qdata->queue)))
607 2284 hiro
                query_search_append_msg(msginfo);
608 2284 hiro
        gdk_threads_leave();
609 2284 hiro
610 2284 hiro
        return TRUE;
611 2284 hiro
}
612 2284 hiro
#endif
613 2284 hiro
614 2276 hiro
static gpointer query_search_folder_func(gpointer data)
615 2276 hiro
{
616 2276 hiro
        QueryData *qdata = (QueryData *)data;
617 2276 hiro
        GSList *mlist, *cur;
618 2276 hiro
        FilterInfo fltinfo;
619 2276 hiro
        GTimeVal tv_cur;
620 836 hiro
621 2276 hiro
        debug_print("query_search_folder_func start\n");
622 1624 hiro
623 2280 hiro
#if USE_THREADS
624 2279 hiro
        g_async_queue_ref(qdata->queue);
625 2280 hiro
#endif
626 2279 hiro
627 2330 hiro
        mlist = qdata->mlist;
628 836 hiro
629 836 hiro
        memset(&fltinfo, 0, sizeof(FilterInfo));
630 836 hiro
631 836 hiro
        debug_print("requires_full_headers: %d\n",
632 836 hiro
                    search_window.requires_full_headers);
633 2276 hiro
        debug_print("start query search: %s\n",
634 2276 hiro
                    qdata->item->path ? qdata->item->path : "");
635 836 hiro
636 836 hiro
        for (cur = mlist; cur != NULL; cur = cur->next) {
637 836 hiro
                MsgInfo *msginfo = (MsgInfo *)cur->data;
638 836 hiro
                GSList *hlist;
639 836 hiro
640 2284 hiro
                g_atomic_int_add(&qdata->count, 1);
641 2276 hiro
642 836 hiro
                g_get_current_time(&tv_cur);
643 2276 hiro
                if ((tv_cur.tv_sec - qdata->tv_prev.tv_sec) * G_USEC_PER_SEC +
644 2276 hiro
                    tv_cur.tv_usec - qdata->tv_prev.tv_usec >
645 836 hiro
                    PROGRESS_UPDATE_INTERVAL * 1000) {
646 2284 hiro
#ifndef USE_THREADS
647 2284 hiro
                        query_search_folder_show_progress(qdata->folder_name,
648 2284 hiro
                                                          qdata->count,
649 2284 hiro
                                                          qdata->total);
650 2276 hiro
#endif
651 2276 hiro
                        qdata->tv_prev = tv_cur;
652 1 hiro
                }
653 1 hiro
654 836 hiro
                if (search_window.cancelled)
655 836 hiro
                        break;
656 836 hiro
657 836 hiro
                fltinfo.flags = msginfo->flags;
658 836 hiro
                if (search_window.requires_full_headers) {
659 836 hiro
                        gchar *file;
660 836 hiro
661 836 hiro
                        file = procmsg_get_message_file(msginfo);
662 836 hiro
                        hlist = procheader_get_header_list_from_file(file);
663 836 hiro
                        g_free(file);
664 836 hiro
                } else
665 836 hiro
                        hlist = procheader_get_header_list_from_msginfo
666 836 hiro
                                (msginfo);
667 836 hiro
                if (!hlist)
668 836 hiro
                        continue;
669 836 hiro
670 836 hiro
                if (filter_match_rule(search_window.rule, msginfo, hlist,
671 836 hiro
                                      &fltinfo)) {
672 2276 hiro
#if USE_THREADS
673 2276 hiro
                        g_async_queue_push(qdata->queue, msginfo);
674 2276 hiro
#else
675 934 hiro
                        query_search_append_msg(msginfo);
676 2276 hiro
#endif
677 836 hiro
                        cur->data = NULL;
678 1967 hiro
                        search_window.n_found++;
679 1 hiro
                }
680 1 hiro
681 836 hiro
                procheader_header_list_destroy(hlist);
682 1 hiro
        }
683 1 hiro
684 2280 hiro
#if USE_THREADS
685 2279 hiro
        g_async_queue_unref(qdata->queue);
686 2280 hiro
#endif
687 2276 hiro
688 2282 hiro
        g_atomic_int_set(&qdata->flag, 1);
689 2276 hiro
        g_main_context_wakeup(NULL);
690 2276 hiro
691 2276 hiro
        debug_print("query_search_folder_func end\n");
692 2276 hiro
693 2276 hiro
        return GINT_TO_POINTER(0);
694 2276 hiro
}
695 2276 hiro
696 2276 hiro
static void query_search_folder(FolderItem *item)
697 2276 hiro
{
698 2276 hiro
        gchar *str;
699 2276 hiro
        QueryData data = {item};
700 2277 hiro
#if USE_THREADS
701 2277 hiro
        GThread *thread;
702 2276 hiro
        MsgInfo *msginfo;
703 2277 hiro
#endif
704 2276 hiro
705 2276 hiro
        if (!item->path || item->stype == F_VIRTUAL)
706 2276 hiro
                return;
707 2276 hiro
708 2276 hiro
        data.folder_name = g_path_get_basename(item->path);
709 2276 hiro
        str = g_strdup_printf(_("Searching %s ..."), data.folder_name);
710 2276 hiro
        gtk_label_set_text(GTK_LABEL(search_window.status_label), str);
711 2276 hiro
        g_free(str);
712 2276 hiro
        g_get_current_time(&data.tv_prev);
713 2276 hiro
#ifndef USE_THREADS
714 2276 hiro
        ui_update();
715 2276 hiro
#endif
716 2276 hiro
717 2276 hiro
        if (search_window.cancelled) {
718 2276 hiro
                g_free(data.folder_name);
719 2276 hiro
                return;
720 2276 hiro
        }
721 2276 hiro
722 2276 hiro
        if (item->opened)
723 2276 hiro
                summary_write_cache(main_window_get()->summaryview);
724 2276 hiro
725 2276 hiro
        procmsg_set_auto_decrypt_message(FALSE);
726 2276 hiro
727 2330 hiro
        data.mlist = folder_item_get_msg_list(item, TRUE);
728 2330 hiro
        data.total = g_slist_length(data.mlist);
729 2330 hiro
730 2276 hiro
#if USE_THREADS
731 2276 hiro
        data.queue = g_async_queue_new();
732 2284 hiro
        data.timer_tag = g_timeout_add(PROGRESS_UPDATE_INTERVAL,
733 2284 hiro
                                       query_search_progress_func, &data);
734 2276 hiro
        thread = g_thread_create(query_search_folder_func, &data, TRUE, NULL);
735 2276 hiro
736 2276 hiro
        debug_print("query_search_folder: thread started\n");
737 2284 hiro
        while (g_atomic_int_get(&data.flag) == 0)
738 2276 hiro
                gtk_main_iteration();
739 2330 hiro
        log_window_flush();
740 2276 hiro
741 2276 hiro
        while ((msginfo = g_async_queue_try_pop(data.queue)))
742 2276 hiro
                query_search_append_msg(msginfo);
743 2276 hiro
744 2284 hiro
        g_source_remove(data.timer_tag);
745 2276 hiro
        g_thread_join(thread);
746 2276 hiro
        debug_print("query_search_folder: thread exited\n");
747 2277 hiro
748 2279 hiro
        g_async_queue_unref(data.queue);
749 2277 hiro
#else /* !USE_THREADS */
750 2276 hiro
        query_search_folder_func(&data);
751 2276 hiro
#endif
752 2276 hiro
753 2330 hiro
        procmsg_msg_list_free(data.mlist);
754 1896 hiro
        procmsg_set_auto_decrypt_message(TRUE);
755 2276 hiro
        g_free(data.folder_name);
756 836 hiro
}
757 1 hiro
758 934 hiro
static gboolean query_search_recursive_func(GNode *node, gpointer data)
759 836 hiro
{
760 836 hiro
        FolderItem *item;
761 836 hiro
762 836 hiro
        g_return_val_if_fail(node->data != NULL, FALSE);
763 836 hiro
764 836 hiro
        item = FOLDER_ITEM(node->data);
765 836 hiro
766 836 hiro
        if (!item->path)
767 836 hiro
                return FALSE;
768 928 hiro
        if (search_window.exclude_trash && item->stype == F_TRASH)
769 928 hiro
                return FALSE;
770 836 hiro
771 934 hiro
        query_search_folder(item);
772 836 hiro
773 836 hiro
        if (search_window.cancelled)
774 836 hiro
                return TRUE;
775 836 hiro
776 836 hiro
        return FALSE;
777 1 hiro
}
778 1 hiro
779 934 hiro
static void query_search_append_msg(MsgInfo *msginfo)
780 836 hiro
{
781 836 hiro
        GtkListStore *store = search_window.store;
782 836 hiro
        GtkTreeIter iter;
783 836 hiro
        gchar *folder;
784 836 hiro
        gchar date_buf[80];
785 836 hiro
        const gchar *subject, *from, *date;
786 836 hiro
        gchar *id;
787 836 hiro
788 836 hiro
        id = folder_item_get_identifier(msginfo->folder);
789 836 hiro
        folder = g_path_get_basename(id);
790 836 hiro
        g_free(id);
791 836 hiro
        subject = msginfo->subject ? msginfo->subject : _("(No Subject)");
792 836 hiro
        from = msginfo->from ? msginfo->from : _("(No From)");
793 836 hiro
        if (msginfo->date_t) {
794 836 hiro
                procheader_date_get_localtime(date_buf, sizeof(date_buf),
795 836 hiro
                                              msginfo->date_t);
796 836 hiro
                date = date_buf;
797 836 hiro
        } else if (msginfo->date)
798 836 hiro
                date = msginfo->date;
799 836 hiro
        else
800 836 hiro
                date = _("(No Date)");
801 836 hiro
802 836 hiro
        gtk_list_store_append(store, &iter);
803 836 hiro
        gtk_list_store_set(store, &iter,
804 836 hiro
                           COL_FOLDER, folder,
805 836 hiro
                           COL_SUBJECT, subject,
806 836 hiro
                           COL_FROM, from,
807 836 hiro
                           COL_DATE, date,
808 836 hiro
                           COL_MSGINFO, msginfo,
809 836 hiro
                           -1);
810 836 hiro
811 836 hiro
        g_free(folder);
812 836 hiro
}
813 836 hiro
814 934 hiro
static void query_search_clear_list(void)
815 836 hiro
{
816 836 hiro
        GtkTreeIter iter;
817 836 hiro
        GtkTreeModel *model = GTK_TREE_MODEL(search_window.store);
818 836 hiro
        MsgInfo *msginfo;
819 836 hiro
820 1041 hiro
        gtkut_tree_sortable_unset_sort_column_id
821 1041 hiro
                (GTK_TREE_SORTABLE(search_window.store));
822 1041 hiro
823 836 hiro
        if (!gtk_tree_model_get_iter_first(model, &iter))
824 836 hiro
                return;
825 836 hiro
826 836 hiro
        do {
827 836 hiro
                gtk_tree_model_get(model, &iter, COL_MSGINFO, &msginfo, -1);
828 836 hiro
                procmsg_msginfo_free(msginfo);
829 836 hiro
        } while (gtk_tree_model_iter_next(model, &iter));
830 836 hiro
831 836 hiro
        gtk_list_store_clear(search_window.store);
832 836 hiro
}
833 836 hiro
834 934 hiro
static void query_search_hbox_added(CondHBox *hbox)
835 836 hiro
{
836 836 hiro
        g_signal_connect(hbox->key_entry, "activate",
837 934 hiro
                         G_CALLBACK(query_search_entry_activated), NULL);
838 836 hiro
}
839 836 hiro
840 836 hiro
static void row_activated(GtkTreeView *treeview, GtkTreePath *path,
841 836 hiro
                          GtkTreeViewColumn *column, gpointer data)
842 836 hiro
{
843 836 hiro
        GtkTreeIter iter;
844 836 hiro
        GtkTreeModel *model = GTK_TREE_MODEL(search_window.store);
845 836 hiro
        MsgInfo *msginfo;
846 836 hiro
        MessageView *msgview;
847 836 hiro
848 836 hiro
        if (!gtk_tree_model_get_iter(model, &iter, path))
849 836 hiro
                return;
850 836 hiro
851 836 hiro
        gtk_tree_model_get(model, &iter, COL_MSGINFO, &msginfo, -1);
852 852 hiro
        if (!summary_select_by_msginfo(main_window_get()->summaryview,
853 852 hiro
                                       msginfo)) {
854 852 hiro
                msgview = messageview_create_with_new_window();
855 852 hiro
                messageview_show(msgview, msginfo, FALSE);
856 2477 hiro
                statusbar_pop_all();
857 852 hiro
        }
858 836 hiro
}
859 836 hiro
860 836 hiro
static gboolean row_selected(GtkTreeSelection *selection,
861 836 hiro
                             GtkTreeModel *model, GtkTreePath *path,
862 836 hiro
                             gboolean cur_selected, gpointer data)
863 836 hiro
{
864 836 hiro
        return TRUE;
865 836 hiro
}
866 836 hiro
867 934 hiro
static void query_search_clear(GtkButton *button, gpointer data)
868 1 hiro
{
869 836 hiro
        CondHBox *cond_hbox;
870 836 hiro
871 1041 hiro
        if (search_window.on_search)
872 1041 hiro
                return;
873 1041 hiro
874 836 hiro
        prefs_filter_edit_clear_cond_edit(search_window.cond_edit);
875 836 hiro
        prefs_filter_set_header_list(NULL);
876 836 hiro
        prefs_filter_edit_set_header_list(search_window.cond_edit, NULL);
877 836 hiro
        cond_hbox = prefs_filter_edit_cond_hbox_create(search_window.cond_edit);
878 836 hiro
        prefs_filter_edit_set_cond_hbox_widgets(cond_hbox, PF_COND_HEADER);
879 836 hiro
        prefs_filter_edit_insert_cond_hbox
880 836 hiro
                (search_window.cond_edit, cond_hbox, -1);
881 836 hiro
        if (search_window.cond_edit->add_hbox)
882 836 hiro
                search_window.cond_edit->add_hbox(cond_hbox);
883 836 hiro
884 836 hiro
        gtk_label_set_text(GTK_LABEL(search_window.status_label), "");
885 836 hiro
886 934 hiro
        query_search_clear_list();
887 1 hiro
}
888 1 hiro
889 934 hiro
static void query_select_folder(GtkButton *button, gpointer data)
890 271 hiro
{
891 836 hiro
        FolderItem *item;
892 836 hiro
        gchar *id;
893 836 hiro
894 836 hiro
        item = foldersel_folder_sel(NULL, FOLDER_SEL_ALL, NULL);
895 848 hiro
        if (!item || item->stype == F_VIRTUAL)
896 836 hiro
                return;
897 836 hiro
898 836 hiro
        id = folder_item_get_identifier(item);
899 836 hiro
        if (id) {
900 836 hiro
                gtk_entry_set_text(GTK_ENTRY(search_window.folder_entry), id);
901 836 hiro
                g_free(id);
902 836 hiro
        }
903 271 hiro
}
904 271 hiro
905 934 hiro
static void query_search_clicked(GtkButton *button, gpointer data)
906 271 hiro
{
907 836 hiro
        if (search_window.on_search)
908 836 hiro
                search_window.cancelled = TRUE;
909 836 hiro
        else
910 934 hiro
                query_search_query();
911 271 hiro
}
912 271 hiro
913 934 hiro
static gint query_search_save_dialog_deleted(GtkWidget *widget,
914 934 hiro
                                             GdkEventAny *event, gpointer data)
915 271 hiro
{
916 934 hiro
        QuerySearchSaveDialog *dialog = (QuerySearchSaveDialog *)data;
917 836 hiro
918 836 hiro
        dialog->cancelled = TRUE;
919 836 hiro
        dialog->finished = TRUE;
920 836 hiro
        return TRUE;
921 271 hiro
}
922 271 hiro
923 934 hiro
static gint query_search_save_dialog_key_pressed(GtkWidget *widget,
924 836 hiro
                                                   GdkEventKey *event,
925 836 hiro
                                                   gpointer data)
926 1 hiro
{
927 934 hiro
        QuerySearchSaveDialog *dialog = (QuerySearchSaveDialog *)data;
928 836 hiro
929 836 hiro
        if (event && event->keyval == GDK_Escape) {
930 836 hiro
                dialog->cancelled = TRUE;
931 836 hiro
                dialog->finished = TRUE;
932 836 hiro
        }
933 836 hiro
        return FALSE;
934 1 hiro
}
935 1 hiro
936 934 hiro
static void query_search_save_dialog_select_folder(GtkButton *button,
937 934 hiro
                                                   gpointer data)
938 1 hiro
{
939 934 hiro
        QuerySearchSaveDialog *dialog = (QuerySearchSaveDialog *)data;
940 836 hiro
        FolderItem *item;
941 836 hiro
        gchar *id;
942 836 hiro
943 836 hiro
        item = foldersel_folder_sel(NULL, FOLDER_SEL_ALL, NULL);
944 848 hiro
        if (!item || item->no_sub || item->stype == F_VIRTUAL)
945 836 hiro
                return;
946 836 hiro
947 836 hiro
        id = folder_item_get_identifier(item);
948 836 hiro
        if (id) {
949 836 hiro
                gtk_entry_set_text(GTK_ENTRY(dialog->folder_entry), id);
950 836 hiro
                g_free(id);
951 836 hiro
        }
952 1 hiro
}
953 1 hiro
954 934 hiro
static void query_search_save_activated(GtkEditable *editable, gpointer data)
955 876 hiro
{
956 934 hiro
        QuerySearchSaveDialog *dialog = (QuerySearchSaveDialog *)data;
957 876 hiro
958 876 hiro
        gtk_button_clicked(GTK_BUTTON(dialog->ok_btn));
959 876 hiro
}
960 876 hiro
961 934 hiro
static void query_search_save_ok(GtkButton *button, gpointer data)
962 1 hiro
{
963 934 hiro
        QuerySearchSaveDialog *dialog = (QuerySearchSaveDialog *)data;
964 836 hiro
965 836 hiro
        dialog->finished = TRUE;
966 1 hiro
}
967 1 hiro
968 934 hiro
static void query_search_save_cancel(GtkButton *button, gpointer data)
969 1 hiro
{
970 934 hiro
        QuerySearchSaveDialog *dialog = (QuerySearchSaveDialog *)data;
971 836 hiro
972 836 hiro
        dialog->cancelled = TRUE;
973 836 hiro
        dialog->finished = TRUE;
974 1 hiro
}
975 1 hiro
976 934 hiro
static QuerySearchSaveDialog *query_search_save_dialog_create(void)
977 836 hiro
{
978 934 hiro
        QuerySearchSaveDialog *dialog;
979 836 hiro
        GtkWidget *window;
980 836 hiro
        GtkWidget *vbox;
981 836 hiro
        GtkWidget *hbox;
982 836 hiro
        GtkWidget *label;
983 836 hiro
        GtkWidget *folder_entry;
984 836 hiro
        GtkWidget *folder_btn;
985 836 hiro
        GtkWidget *name_entry;
986 836 hiro
987 836 hiro
        GtkWidget *confirm_area;
988 836 hiro
        GtkWidget *hbbox;
989 836 hiro
        GtkWidget *cancel_btn;
990 836 hiro
        GtkWidget *ok_btn;
991 836 hiro
992 934 hiro
        dialog = g_new0(QuerySearchSaveDialog, 1);
993 836 hiro
994 836 hiro
        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
995 836 hiro
        gtk_window_set_title(GTK_WINDOW(window), _("Save as search folder"));
996 836 hiro
        gtk_widget_set_size_request(window, 400, -1);
997 836 hiro
        gtk_container_set_border_width(GTK_CONTAINER(window), 8);
998 836 hiro
        gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
999 836 hiro
        gtk_window_set_modal(GTK_WINDOW(window), TRUE);
1000 836 hiro
        gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, FALSE);
1001 836 hiro
        g_signal_connect(G_OBJECT(window), "delete_event",
1002 934 hiro
                         G_CALLBACK(query_search_save_dialog_deleted),
1003 836 hiro
                         dialog);
1004 836 hiro
        g_signal_connect(G_OBJECT(window), "key_press_event",
1005 934 hiro
                         G_CALLBACK(query_search_save_dialog_key_pressed),
1006 836 hiro
                         dialog);
1007 836 hiro
        MANAGE_WINDOW_SIGNALS_CONNECT(window);
1008 836 hiro
        manage_window_set_transient(GTK_WINDOW(window));
1009 836 hiro
1010 836 hiro
        vbox = gtk_vbox_new(FALSE, 8);
1011 836 hiro
        gtk_container_add(GTK_CONTAINER(window), vbox);
1012 836 hiro
1013 836 hiro
        hbox = gtk_hbox_new(FALSE, 8);
1014 836 hiro
        gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1015 836 hiro
1016 836 hiro
        label = gtk_label_new(_("Location:"));
1017 836 hiro
        gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1018 836 hiro
1019 836 hiro
        folder_entry = gtk_entry_new();
1020 836 hiro
        gtk_box_pack_start(GTK_BOX(hbox), folder_entry, TRUE, TRUE, 0);
1021 836 hiro
1022 836 hiro
        folder_btn = gtk_button_new_with_label("...");
1023 836 hiro
        gtk_box_pack_start(GTK_BOX(hbox), folder_btn, FALSE, FALSE, 0);
1024 836 hiro
        g_signal_connect(G_OBJECT(folder_btn), "clicked",
1025 934 hiro
                         G_CALLBACK(query_search_save_dialog_select_folder),
1026 836 hiro
                         dialog);
1027 836 hiro
1028 836 hiro
        hbox = gtk_hbox_new(FALSE, 8);
1029 836 hiro
        gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1030 836 hiro
1031 836 hiro
        label = gtk_label_new(_("Folder name:"));
1032 836 hiro
        gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1033 836 hiro
1034 836 hiro
        name_entry = gtk_entry_new();
1035 836 hiro
        gtk_box_pack_start(GTK_BOX(hbox), name_entry, TRUE, TRUE, 0);
1036 876 hiro
        g_signal_connect(G_OBJECT(name_entry), "activate",
1037 934 hiro
                         G_CALLBACK(query_search_save_activated), dialog);
1038 836 hiro
1039 836 hiro
        confirm_area = gtk_hbox_new(FALSE, 12);
1040 836 hiro
        gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
1041 836 hiro
1042 836 hiro
        gtkut_stock_button_set_create(&hbbox,
1043 836 hiro
                                      &ok_btn, GTK_STOCK_OK,
1044 836 hiro
                                      &cancel_btn, GTK_STOCK_CANCEL,
1045 836 hiro
                                      NULL, NULL);
1046 836 hiro
        gtk_box_pack_end(GTK_BOX(confirm_area), hbbox, FALSE, FALSE, 0);
1047 837 hiro
        GTK_WIDGET_SET_FLAGS(ok_btn, GTK_CAN_DEFAULT);
1048 837 hiro
        gtk_widget_grab_default(ok_btn);
1049 836 hiro
        g_signal_connect(G_OBJECT(ok_btn), "clicked",
1050 934 hiro
                         G_CALLBACK(query_search_save_ok), dialog);
1051 836 hiro
        g_signal_connect(G_OBJECT(cancel_btn), "clicked",
1052 934 hiro
                         G_CALLBACK(query_search_save_cancel), dialog);
1053 836 hiro
1054 837 hiro
        gtk_widget_grab_focus(name_entry);
1055 837 hiro
1056 836 hiro
        gtk_widget_show_all(window);
1057 836 hiro
1058 836 hiro
        dialog->window = window;
1059 836 hiro
        dialog->folder_entry = folder_entry;
1060 836 hiro
        dialog->name_entry = name_entry;
1061 876 hiro
        dialog->ok_btn = ok_btn;
1062 876 hiro
        dialog->cancel_btn = cancel_btn;
1063 836 hiro
        dialog->cancelled = FALSE;
1064 836 hiro
        dialog->finished = FALSE;
1065 836 hiro
1066 836 hiro
        return dialog;
1067 836 hiro
}
1068 836 hiro
1069 934 hiro
static void query_search_save_dialog_destroy(QuerySearchSaveDialog *dialog)
1070 836 hiro
{
1071 836 hiro
        gtk_widget_destroy(dialog->window);
1072 836 hiro
        g_free(dialog);
1073 836 hiro
}
1074 836 hiro
1075 934 hiro
static FolderItem *query_search_create_vfolder(FolderItem *parent,
1076 934 hiro
                                               const gchar *name)
1077 836 hiro
{
1078 836 hiro
        gchar *path;
1079 836 hiro
        gchar *fs_name;
1080 836 hiro
        gchar *fullpath;
1081 836 hiro
        FolderItem *item;
1082 836 hiro
1083 836 hiro
        g_return_val_if_fail(parent != NULL, NULL);
1084 836 hiro
        g_return_val_if_fail(name != NULL, NULL);
1085 836 hiro
1086 836 hiro
        path = folder_item_get_path(parent);
1087 836 hiro
        fs_name = g_filename_from_utf8(name, -1, NULL, NULL, NULL);
1088 836 hiro
        fullpath = g_strconcat(path, G_DIR_SEPARATOR_S,
1089 836 hiro
                               fs_name ? fs_name : name, NULL);
1090 836 hiro
        g_free(fs_name);
1091 836 hiro
        g_free(path);
1092 836 hiro
1093 836 hiro
        if (make_dir_hier(fullpath) < 0) {
1094 836 hiro
                g_free(fullpath);
1095 836 hiro
                return NULL;
1096 836 hiro
        }
1097 836 hiro
1098 836 hiro
        if (parent->path)
1099 1678 Hiro
                path = g_strconcat(parent->path, "/", name, NULL);
1100 836 hiro
        else
1101 836 hiro
                path = g_strdup(name);
1102 836 hiro
1103 836 hiro
        item = folder_item_new(name, path);
1104 836 hiro
        item->stype = F_VIRTUAL;
1105 848 hiro
        item->no_sub = TRUE;
1106 836 hiro
        folder_item_append(parent, item);
1107 836 hiro
1108 836 hiro
        g_free(path);
1109 836 hiro
1110 836 hiro
        return item;
1111 836 hiro
}
1112 836 hiro
1113 934 hiro
static void query_search_vfolder_update_rule(FolderItem *item)
1114 836 hiro
{
1115 836 hiro
        GSList list;
1116 836 hiro
        FilterRule *rule;
1117 836 hiro
        gchar *file;
1118 836 hiro
        gchar *path;
1119 836 hiro
1120 934 hiro
        rule = query_search_dialog_to_rule(item->name, NULL);
1121 836 hiro
        list.data = rule;
1122 836 hiro
        list.next = NULL;
1123 836 hiro
1124 836 hiro
        path = folder_item_get_path(item);
1125 836 hiro
        file = g_strconcat(path, G_DIR_SEPARATOR_S, FILTER_LIST, NULL);
1126 836 hiro
        filter_write_file(&list, file);
1127 836 hiro
        g_free(file);
1128 836 hiro
        g_free(path);
1129 836 hiro
1130 836 hiro
        filter_rule_free(rule);
1131 836 hiro
}
1132 836 hiro
1133 934 hiro
static void query_search_save(GtkButton *button, gpointer data)
1134 836 hiro
{
1135 934 hiro
        QuerySearchSaveDialog *dialog;
1136 837 hiro
        const gchar *id, *name;
1137 837 hiro
        FolderItem *parent, *item;
1138 836 hiro
1139 934 hiro
        dialog = query_search_save_dialog_create();
1140 837 hiro
        id = gtk_entry_get_text(GTK_ENTRY(search_window.folder_entry));
1141 837 hiro
        if (id && *id)
1142 837 hiro
                gtk_entry_set_text(GTK_ENTRY(dialog->folder_entry), id);
1143 836 hiro
1144 836 hiro
        while (!dialog->finished)
1145 836 hiro
                gtk_main_iteration();
1146 836 hiro
1147 837 hiro
        if (dialog->cancelled) {
1148 934 hiro
                query_search_save_dialog_destroy(dialog);
1149 837 hiro
                return;
1150 837 hiro
        }
1151 836 hiro
1152 837 hiro
        id = gtk_entry_get_text(GTK_ENTRY(dialog->folder_entry));
1153 837 hiro
        parent = folder_find_item_from_identifier(id);
1154 837 hiro
        name = gtk_entry_get_text(GTK_ENTRY(dialog->name_entry));
1155 837 hiro
        if (parent && name && *name) {
1156 837 hiro
                if (folder_find_child_item_by_name(parent, name)) {
1157 837 hiro
                        alertpanel_error(_("The folder `%s' already exists."),
1158 836 hiro
                                         name);
1159 837 hiro
                } else {
1160 934 hiro
                        item = query_search_create_vfolder(parent, name);
1161 837 hiro
                        if (item) {
1162 934 hiro
                                query_search_vfolder_update_rule(item);
1163 837 hiro
                                folderview_append_item(folderview_get(),
1164 837 hiro
                                                       NULL, item, TRUE);
1165 837 hiro
                                folder_write_list();
1166 836 hiro
                        }
1167 836 hiro
                }
1168 836 hiro
        }
1169 836 hiro
1170 934 hiro
        query_search_save_dialog_destroy(dialog);
1171 836 hiro
}
1172 836 hiro
1173 934 hiro
static void query_search_close(GtkButton *button, gpointer data)
1174 836 hiro
{
1175 836 hiro
        if (search_window.on_search)
1176 836 hiro
                search_window.cancelled = TRUE;
1177 836 hiro
        gtk_widget_hide(search_window.window);
1178 836 hiro
}
1179 836 hiro
1180 934 hiro
static void query_search_entry_activated(GtkWidget *widget, gpointer data)
1181 836 hiro
{
1182 836 hiro
        gtk_button_clicked(GTK_BUTTON(search_window.search_btn));
1183 836 hiro
}
1184 836 hiro
1185 934 hiro
static gint query_search_deleted(GtkWidget *widget, GdkEventAny *event,
1186 934 hiro
                                 gpointer data)
1187 836 hiro
{
1188 836 hiro
        gtk_button_clicked(GTK_BUTTON(search_window.close_btn));
1189 836 hiro
        return TRUE;
1190 836 hiro
}
1191 836 hiro
1192 1 hiro
static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event,
1193 1 hiro
                            gpointer data)
1194 1 hiro
{
1195 836 hiro
        if (event && event->keyval == GDK_Escape) {
1196 836 hiro
                if (search_window.on_search)
1197 836 hiro
                        gtk_button_clicked
1198 836 hiro
                                (GTK_BUTTON(search_window.search_btn));
1199 836 hiro
                else
1200 836 hiro
                        gtk_button_clicked(GTK_BUTTON(search_window.close_btn));
1201 836 hiro
                return TRUE;
1202 836 hiro
        }
1203 1 hiro
        return FALSE;
1204 1 hiro
}
1205 1041 hiro
1206 1041 hiro
static gint query_search_cmp_by_folder(GtkTreeModel *model,
1207 1041 hiro
                                       GtkTreeIter *a, GtkTreeIter *b,
1208 1041 hiro
                                       gpointer data)
1209 1041 hiro
{
1210 1041 hiro
        gchar *folder_a = NULL, *folder_b = NULL;
1211 1041 hiro
        MsgInfo *msginfo_a = NULL, *msginfo_b = NULL;
1212 1041 hiro
        gint ret;
1213 1041 hiro
1214 1041 hiro
        gtk_tree_model_get(model, a, COL_FOLDER, &folder_a, COL_MSGINFO,
1215 1041 hiro
                           &msginfo_a, -1);
1216 1041 hiro
        gtk_tree_model_get(model, b, COL_FOLDER, &folder_b, COL_MSGINFO,
1217 1041 hiro
                           &msginfo_b, -1);
1218 1041 hiro
1219 1041 hiro
        if (!folder_a || !folder_b || !msginfo_a || !msginfo_b)
1220 1041 hiro
                return 0;
1221 1041 hiro
1222 1041 hiro
        ret = g_ascii_strcasecmp(folder_a, folder_b);
1223 1041 hiro
        return (ret != 0) ? ret : (msginfo_a->date_t - msginfo_b->date_t);
1224 1041 hiro
}
1225 1041 hiro
1226 1041 hiro
static gint query_search_cmp_by_subject(GtkTreeModel *model,
1227 1041 hiro
                                        GtkTreeIter *a, GtkTreeIter *b,
1228 1041 hiro
                                        gpointer data)
1229 1041 hiro
{
1230 1041 hiro
        MsgInfo *msginfo_a = NULL, *msginfo_b = NULL;
1231 1041 hiro
        gint ret;
1232 1041 hiro
1233 1041 hiro
        gtk_tree_model_get(model, a, COL_MSGINFO, &msginfo_a, -1);
1234 1041 hiro
        gtk_tree_model_get(model, b, COL_MSGINFO, &msginfo_b, -1);
1235 1041 hiro
1236 1041 hiro
        if (!msginfo_a || !msginfo_b)
1237 1041 hiro
                return 0;
1238 1041 hiro
1239 1041 hiro
        if (!msginfo_a->subject)
1240 1041 hiro
                return -(msginfo_b->subject != NULL);
1241 1041 hiro
        if (!msginfo_b->subject)
1242 1041 hiro
                return (msginfo_a->subject != NULL);
1243 1041 hiro
1244 1041 hiro
        ret = subject_compare_for_sort(msginfo_a->subject, msginfo_b->subject);
1245 1041 hiro
        return (ret != 0) ? ret : (msginfo_a->date_t - msginfo_b->date_t);
1246 1041 hiro
}
1247 1041 hiro
1248 1041 hiro
static gint query_search_cmp_by_from(GtkTreeModel *model,
1249 1041 hiro
                                     GtkTreeIter *a, GtkTreeIter *b,
1250 1041 hiro
                                     gpointer data)
1251 1041 hiro
{
1252 1041 hiro
        MsgInfo *msginfo_a = NULL, *msginfo_b = NULL;
1253 1041 hiro
        gint ret;
1254 1041 hiro
1255 1041 hiro
        gtk_tree_model_get(model, a, COL_MSGINFO, &msginfo_a, -1);
1256 1041 hiro
        gtk_tree_model_get(model, b, COL_MSGINFO, &msginfo_b, -1);
1257 1041 hiro
1258 1041 hiro
        if (!msginfo_a || !msginfo_b)
1259 1041 hiro
                return 0;
1260 1041 hiro
1261 1041 hiro
        if (!msginfo_a->fromname)
1262 1041 hiro
                return -(msginfo_b->fromname != NULL);
1263 1041 hiro
        if (!msginfo_b->fromname)
1264 1041 hiro
                return (msginfo_a->fromname != NULL);
1265 1041 hiro
1266 1041 hiro
        ret = g_ascii_strcasecmp(msginfo_a->fromname, msginfo_b->fromname);
1267 1041 hiro
        return (ret != 0) ? ret : (msginfo_a->date_t - msginfo_b->date_t);
1268 1041 hiro
}
1269 1041 hiro
1270 1041 hiro
static gint query_search_cmp_by_date(GtkTreeModel *model,
1271 1041 hiro
                                     GtkTreeIter *a, GtkTreeIter *b,
1272 1041 hiro
                                     gpointer data)
1273 1041 hiro
{
1274 1041 hiro
        MsgInfo *msginfo_a = NULL, *msginfo_b = NULL;
1275 1041 hiro
1276 1041 hiro
        gtk_tree_model_get(model, a, COL_MSGINFO, &msginfo_a, -1);
1277 1041 hiro
        gtk_tree_model_get(model, b, COL_MSGINFO, &msginfo_b, -1);
1278 1041 hiro
1279 1041 hiro
        if (!msginfo_a || !msginfo_b)
1280 1041 hiro
                return 0;
1281 1041 hiro
1282 1041 hiro
        return msginfo_a->date_t - msginfo_b->date_t;
1283 1041 hiro
}