Statistics
| Revision:

root / src / summary_search.c @ 874

History | View | Annotate | Download (27.6 kB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 1999-2005 Hiroyuki Yamamoto
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
 */
19
20
#ifdef HAVE_CONFIG_H
21
#  include "config.h"
22
#endif
23
24
#include "defs.h"
25
26
#include <glib.h>
27
#include <glib/gi18n.h>
28
#include <gdk/gdkkeysyms.h>
29
#include <gtk/gtkwidget.h>
30
#include <gtk/gtkwindow.h>
31
#include <gtk/gtkvbox.h>
32
#include <gtk/gtktable.h>
33
#include <gtk/gtkoptionmenu.h>
34
#include <gtk/gtklabel.h>
35
#include <gtk/gtkentry.h>
36
#include <gtk/gtkhbox.h>
37
#include <gtk/gtkcheckbutton.h>
38
#include <gtk/gtkhbbox.h>
39
#include <gtk/gtkbutton.h>
40
#include <gtk/gtkmenuitem.h>
41
#include <gtk/gtkstock.h>
42
#include <gtk/gtktreemodel.h>
43
#include <gtk/gtkliststore.h>
44
#include <gtk/gtktreeview.h>
45
#include <gtk/gtktreeselection.h>
46
#include <gtk/gtkcellrenderertext.h>
47
#include <stdio.h>
48
#include <stdlib.h>
49
#include <string.h>
50
51
#include "summary_search.h"
52
#include "prefs_filter_edit.h"
53
#include "summaryview.h"
54
#include "messageview.h"
55
#include "mainwindow.h"
56
#include "folderview.h"
57
#include "menu.h"
58
#include "utils.h"
59
#include "gtkutils.h"
60
#include "manage_window.h"
61
#include "alertpanel.h"
62
#include "foldersel.h"
63
#include "statusbar.h"
64
#include "procmsg.h"
65
#include "procheader.h"
66
#include "folder.h"
67
#include "filter.h"
68
#include "prefs_filter.h"
69
#include "prefs_filter_edit.h"
70
71
enum
72
{
73
        COL_FOLDER,
74
        COL_SUBJECT,
75
        COL_FROM,
76
        COL_DATE,
77
        COL_MSGINFO,
78
        N_COLS
79
};
80
81
static struct SummarySearchWindow {
82
        GtkWidget *window;
83
84
        GtkWidget *bool_optmenu;
85
86
        FilterCondEdit *cond_edit;
87
88
        GtkWidget *folder_entry;
89
        GtkWidget *folder_btn;
90
91
        GtkWidget *subfolder_checkbtn;
92
        GtkWidget *case_checkbtn;
93
94
        GtkWidget *treeview;
95
        GtkListStore *store;
96
97
        GtkWidget *status_label;
98
99
        GtkWidget *clear_btn;
100
        GtkWidget *search_btn;
101
        GtkWidget *save_btn;
102
        GtkWidget *close_btn;
103
104
        FilterRule *rule;
105
        gboolean requires_full_headers;
106
107
        gboolean on_search;
108
        gboolean cancelled;
109
} search_window;
110
111
typedef struct {
112
        GtkWidget *window;
113
114
        GtkWidget *folder_entry;
115
        GtkWidget *name_entry;
116
117
        gboolean cancelled;
118
        gboolean finished;
119
} SummarySearchSaveDialog;
120
121
static void summary_search_create        (void);
122
123
static FilterRule *summary_search_dialog_to_rule
124
                                        (const gchar        *name,
125
                                         FolderItem    **item);
126
127
static void summary_search_query        (void);
128
static void summary_search_folder        (FolderItem        *item);
129
130
static gboolean summary_search_recursive_func        (GNode                *node,
131
                                                 gpointer         data);
132
133
static void summary_search_append_msg        (MsgInfo        *msginfo);
134
static void summary_search_clear_list        (void);
135
136
static void summary_search_hbox_added        (CondHBox        *hbox);
137
138
static void row_activated                (GtkTreeView                *treeview,
139
                                         GtkTreePath                *path,
140
                                         GtkTreeViewColumn        *column,
141
                                         gpointer                 data);
142
143
static gboolean row_selected                (GtkTreeSelection        *selection,
144
                                         GtkTreeModel                *model,
145
                                         GtkTreePath                *path,
146
                                         gboolean                 cur_selected,
147
                                         gpointer                 data);
148
149
static void summary_search_clear        (GtkButton        *button,
150
                                         gpointer         data);
151
static void summary_select_folder        (GtkButton        *button,
152
                                         gpointer         data);
153
static void summary_search_clicked        (GtkButton        *button,
154
                                         gpointer         data);
155
static void summary_search_save                (GtkButton        *button,
156
                                         gpointer         data);
157
static void summary_search_close        (GtkButton        *button,
158
                                         gpointer         data);
159
160
static void summary_search_entry_activated        (GtkWidget        *widget,
161
                                                 gpointer         data);
162
163
static gint summary_search_deleted        (GtkWidget        *widget,
164
                                         GdkEventAny        *event,
165
                                         gpointer         data);
166
static gboolean key_pressed                (GtkWidget        *widget,
167
                                         GdkEventKey        *event,
168
                                         gpointer         data);
169
170
171
void summary_search(FolderItem *item)
172
{
173
        gchar *id;
174
175
        if (!search_window.window)
176
                summary_search_create();
177
        else
178
                gtk_window_present(GTK_WINDOW(search_window.window));
179
180
        if (item && item->stype != F_VIRTUAL) {
181
                id = folder_item_get_identifier(item);
182
                gtk_entry_set_text(GTK_ENTRY(search_window.folder_entry), id);
183
                g_free(id);
184
        } else
185
                gtk_entry_set_text(GTK_ENTRY(search_window.folder_entry), "");
186
187
        gtk_widget_grab_focus(search_window.search_btn);
188
        gtk_widget_show(search_window.window);
189
}
190
191
static void summary_search_create(void)
192
{
193
        GtkWidget *window;
194
        GtkWidget *vbox1;
195
        GtkWidget *bool_hbox;
196
        GtkWidget *bool_optmenu;
197
        GtkWidget *bool_menu;
198
        GtkWidget *menuitem;
199
        GtkWidget *clear_btn;
200
        GtkWidget *search_btn;
201
202
        GtkWidget *scrolledwin;
203
        FilterCondEdit *cond_edit;
204
        CondHBox *cond_hbox;
205
206
        GtkWidget *folder_hbox;
207
        GtkWidget *folder_label;
208
        GtkWidget *folder_entry;
209
        GtkWidget *folder_btn;
210
211
        GtkWidget *checkbtn_hbox;
212
        GtkWidget *subfolder_checkbtn;
213
        GtkWidget *case_checkbtn;
214
215
        GtkWidget *treeview;
216
        GtkListStore *store;
217
        GtkTreeViewColumn *column;
218
        GtkCellRenderer *renderer;
219
        GtkTreeSelection *selection;
220
221
        GtkWidget *confirm_area;
222
223
        GtkWidget *status_label;
224
225
        GtkWidget *btn_hbox;
226
        GtkWidget *hbbox;
227
        GtkWidget *save_btn;
228
        GtkWidget *close_btn;
229
230
        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
231
        gtk_window_set_title(GTK_WINDOW (window), _("Search messages"));
232
        gtk_widget_set_size_request(window, 600, -1);
233
        gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, TRUE);
234
        gtk_container_set_border_width(GTK_CONTAINER (window), 8);
235
        g_signal_connect(G_OBJECT(window), "delete_event",
236
                         G_CALLBACK(summary_search_deleted), NULL);
237
        g_signal_connect(G_OBJECT(window), "key_press_event",
238
                         G_CALLBACK(key_pressed), NULL);
239
        MANAGE_WINDOW_SIGNALS_CONNECT(window);
240
241
        vbox1 = gtk_vbox_new (FALSE, 6);
242
        gtk_widget_show (vbox1);
243
        gtk_container_add (GTK_CONTAINER (window), vbox1);
244
245
        bool_hbox = gtk_hbox_new(FALSE, 12);
246
        gtk_widget_show(bool_hbox);
247
        gtk_box_pack_start(GTK_BOX(vbox1), bool_hbox, FALSE, FALSE, 0);
248
249
        bool_optmenu = gtk_option_menu_new();
250
        gtk_widget_show(bool_optmenu);
251
        gtk_box_pack_start(GTK_BOX(bool_hbox), bool_optmenu, FALSE, FALSE, 0);
252
253
        bool_menu = gtk_menu_new();
254
        MENUITEM_ADD(bool_menu, menuitem, _("Match any of the following"),
255
                     FLT_OR);
256
        MENUITEM_ADD(bool_menu, menuitem, _("Match all of the following"),
257
                     FLT_AND);
258
        gtk_option_menu_set_menu(GTK_OPTION_MENU(bool_optmenu), bool_menu);
259
260
        hbbox = gtk_hbutton_box_new();
261
        gtk_widget_show(hbbox);
262
        gtk_button_box_set_layout(GTK_BUTTON_BOX(hbbox), GTK_BUTTONBOX_END);
263
        gtk_box_set_spacing(GTK_BOX(hbbox), 6);
264
        gtk_box_pack_end(GTK_BOX(bool_hbox), hbbox, FALSE, FALSE, 0);
265
266
        clear_btn = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
267
        gtk_widget_show(clear_btn);
268
        gtk_box_pack_start(GTK_BOX(hbbox), clear_btn, FALSE, FALSE, 0);
269
270
        search_btn = gtk_button_new_from_stock(GTK_STOCK_FIND);
271
        GTK_WIDGET_SET_FLAGS(search_btn, GTK_CAN_DEFAULT);
272
        gtk_widget_show(search_btn);
273
        gtk_box_pack_start(GTK_BOX(hbbox), search_btn, FALSE, FALSE, 0);
274
        gtk_widget_grab_default(search_btn);
275
276
        scrolledwin = gtk_scrolled_window_new(NULL, NULL);
277
        gtk_widget_show(scrolledwin);
278
        gtk_box_pack_start(GTK_BOX(vbox1), scrolledwin, FALSE, FALSE, 0);
279
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
280
                                       GTK_POLICY_AUTOMATIC,
281
                                       GTK_POLICY_AUTOMATIC);
282
        gtk_widget_set_size_request(scrolledwin, -1, 120);
283
284
        cond_edit = prefs_filter_edit_cond_edit_create();
285
        cond_edit->add_hbox = summary_search_hbox_added;
286
        gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolledwin),
287
                                              cond_edit->cond_vbox);
288
        prefs_filter_set_header_list(NULL);
289
        prefs_filter_edit_set_header_list(cond_edit, NULL);
290
        cond_hbox = prefs_filter_edit_cond_hbox_create(cond_edit);
291
        prefs_filter_edit_set_cond_hbox_widgets(cond_hbox, PF_COND_HEADER);
292
        prefs_filter_edit_insert_cond_hbox(cond_edit, cond_hbox, -1);
293
        if (cond_edit->add_hbox)
294
                cond_edit->add_hbox(cond_hbox);
295
296
        folder_hbox = gtk_hbox_new (FALSE, 8);
297
        gtk_widget_show (folder_hbox);
298
        gtk_box_pack_start (GTK_BOX (vbox1), folder_hbox, FALSE, FALSE, 0);
299
300
        folder_label = gtk_label_new (_("Folder:"));
301
        gtk_widget_show (folder_label);
302
        gtk_box_pack_start (GTK_BOX (folder_hbox), folder_label,
303
                            FALSE, FALSE, 0);
304
305
        folder_entry = gtk_entry_new ();
306
        gtk_widget_show (folder_entry);
307
        gtk_box_pack_start (GTK_BOX (folder_hbox), folder_entry, TRUE, TRUE, 0);
308
309
        folder_btn = gtk_button_new_with_label("...");
310
        gtk_widget_show (folder_btn);
311
        gtk_box_pack_start (GTK_BOX (folder_hbox), folder_btn, FALSE, FALSE, 0);
312
313
        checkbtn_hbox = gtk_hbox_new (FALSE, 12);
314
        gtk_widget_show (checkbtn_hbox);
315
        gtk_box_pack_start (GTK_BOX (vbox1), checkbtn_hbox, FALSE, FALSE, 0);
316
317
        subfolder_checkbtn =
318
                gtk_check_button_new_with_label (_("Search subfolders"));
319
        gtk_widget_show (subfolder_checkbtn);
320
        gtk_box_pack_start (GTK_BOX (checkbtn_hbox), subfolder_checkbtn,
321
                            FALSE, FALSE, 0);
322
323
        case_checkbtn = gtk_check_button_new_with_label (_("Case sensitive"));
324
        gtk_widget_show (case_checkbtn);
325
        gtk_box_pack_start (GTK_BOX (checkbtn_hbox), case_checkbtn,
326
                            FALSE, FALSE, 0);
327
328
        scrolledwin = gtk_scrolled_window_new(NULL, NULL);
329
        gtk_box_pack_start(GTK_BOX(vbox1), scrolledwin, TRUE, TRUE, 0);
330
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
331
                                       GTK_POLICY_AUTOMATIC,
332
                                       GTK_POLICY_AUTOMATIC);
333
        gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
334
                                            GTK_SHADOW_IN);
335
        gtk_widget_set_size_request(scrolledwin, -1, 150);
336
337
        store = gtk_list_store_new(N_COLS,
338
                                   G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
339
                                   G_TYPE_STRING, G_TYPE_POINTER);
340
        treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
341
        g_object_unref(store);
342
        gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE);
343
        g_signal_connect(G_OBJECT(treeview), "row-activated",
344
                         G_CALLBACK(row_activated), NULL);
345
346
        selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
347
        gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
348
        gtk_tree_selection_set_select_function(selection, row_selected,
349
                                               NULL, NULL);
350
351
        gtk_container_add(GTK_CONTAINER(scrolledwin), treeview);
352
353
#define APPEND_COLUMN(label, col, width)                                \
354
{                                                                        \
355
        renderer = gtk_cell_renderer_text_new();                        \
356
        column = gtk_tree_view_column_new_with_attributes                \
357
                (label, renderer, "text", col, NULL);                        \
358
        gtk_tree_view_column_set_resizable(column, TRUE);                \
359
        if (width) {                                                        \
360
                gtk_tree_view_column_set_sizing                                \
361
                        (column, GTK_TREE_VIEW_COLUMN_FIXED);                \
362
                gtk_tree_view_column_set_fixed_width(column, width);        \
363
        }                                                                \
364
        gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);        \
365
}
366
367
        APPEND_COLUMN(_("Folder"), COL_FOLDER, 0);
368
        APPEND_COLUMN(_("Subject"), COL_SUBJECT, 200);
369
        APPEND_COLUMN(_("From"), COL_FROM, 180);
370
        APPEND_COLUMN(_("Date"), COL_DATE, 0);
371
372
        gtk_widget_show_all(scrolledwin);
373
374
        confirm_area = gtk_hbox_new(FALSE, 12);
375
        gtk_widget_show(confirm_area);
376
        gtk_box_pack_start(GTK_BOX(vbox1), confirm_area, FALSE, FALSE, 0);
377
378
        status_label = gtk_label_new("");
379
        gtk_widget_show(status_label);
380
        gtk_box_pack_start(GTK_BOX(confirm_area), status_label,
381
                           FALSE, FALSE, 0);
382
383
        btn_hbox = gtk_hbox_new(FALSE, 6);
384
        gtk_widget_show(btn_hbox);
385
        gtk_box_pack_end(GTK_BOX(confirm_area), btn_hbox, FALSE, FALSE, 0);
386
387
        gtkut_stock_button_set_create(&hbbox, &close_btn, GTK_STOCK_CLOSE,
388
                                      NULL, NULL, NULL, NULL);
389
        gtk_widget_show(hbbox);
390
        gtk_box_pack_end(GTK_BOX(btn_hbox), hbbox, FALSE, FALSE, 0);
391
392
        save_btn = gtk_button_new_with_mnemonic(_("_Save as search folder"));
393
        gtk_widget_show(save_btn);
394
        gtk_box_pack_end(GTK_BOX(btn_hbox), save_btn, FALSE, FALSE, 0);
395
396
        g_signal_connect(G_OBJECT(clear_btn), "clicked",
397
                         G_CALLBACK(summary_search_clear), NULL);
398
        g_signal_connect(G_OBJECT(folder_btn), "clicked",
399
                         G_CALLBACK(summary_select_folder), NULL);
400
        g_signal_connect(G_OBJECT(search_btn), "clicked",
401
                         G_CALLBACK(summary_search_clicked), NULL);
402
        g_signal_connect(G_OBJECT(save_btn), "clicked",
403
                         G_CALLBACK(summary_search_save), NULL);
404
        g_signal_connect(G_OBJECT(close_btn), "clicked",
405
                         G_CALLBACK(summary_search_close), NULL);
406
407
        search_window.window = window;
408
        search_window.bool_optmenu = bool_optmenu;
409
410
        search_window.cond_edit = cond_edit;
411
412
        search_window.folder_entry = folder_entry;
413
        search_window.folder_btn = folder_btn;
414
        search_window.subfolder_checkbtn = subfolder_checkbtn;
415
        search_window.case_checkbtn = case_checkbtn;
416
417
        search_window.treeview = treeview;
418
        search_window.store = store;
419
420
        search_window.status_label = status_label;
421
422
        search_window.clear_btn = clear_btn;
423
        search_window.search_btn = search_btn;
424
        search_window.save_btn  = save_btn;
425
        search_window.close_btn = close_btn;
426
}
427
428
static FilterRule *summary_search_dialog_to_rule(const gchar *name,
429
                                                 FolderItem **item)
430
{
431
        const gchar *id;
432
        FolderItem *item_;
433
        FilterBoolOp bool_op = FLT_OR;
434
        gboolean recursive;
435
        gboolean case_sens;
436
        GSList *cond_list;
437
        FilterRule *rule;
438
439
        id = gtk_entry_get_text(GTK_ENTRY(search_window.folder_entry));
440
        item_ = folder_find_item_from_identifier(id);
441
        if (!item_)
442
                return NULL;
443
        if (item)
444
                *item = item_;
445
446
        bool_op = menu_get_option_menu_active_index
447
                (GTK_OPTION_MENU(search_window.bool_optmenu));
448
        recursive = gtk_toggle_button_get_active
449
                (GTK_TOGGLE_BUTTON(search_window.subfolder_checkbtn));
450
        case_sens = gtk_toggle_button_get_active
451
                (GTK_TOGGLE_BUTTON(search_window.case_checkbtn));
452
453
        cond_list = prefs_filter_edit_cond_edit_to_list(search_window.cond_edit,
454
                                                        case_sens);
455
        if (!cond_list)
456
                return NULL;
457
458
        rule = filter_rule_new(name, bool_op, cond_list, NULL);
459
        rule->target_folder = g_strdup(id);
460
        rule->recursive = recursive;
461
462
        return rule;
463
}
464
465
static void summary_search_query(void)
466
{
467
        FolderItem *item;
468
469
        if (search_window.on_search)
470
                return;
471
472
        search_window.on_search = TRUE;
473
474
        search_window.rule = summary_search_dialog_to_rule("Query rule", &item);
475
        if (!search_window.rule) {
476
                search_window.on_search = FALSE;
477
                return;
478
        }
479
        search_window.requires_full_headers =
480
                filter_rule_requires_full_headers(search_window.rule);
481
482
        search_window.cancelled = FALSE;
483
484
        gtk_button_set_label(GTK_BUTTON(search_window.search_btn),
485
                             GTK_STOCK_STOP);
486
        summary_search_clear_list();
487
488
        if (search_window.rule->recursive)
489
                g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
490
                                summary_search_recursive_func, NULL);
491
        else
492
                summary_search_folder(item);
493
494
        filter_rule_free(search_window.rule);
495
        search_window.rule = NULL;
496
        search_window.requires_full_headers = FALSE;
497
498
        gtk_button_set_label(GTK_BUTTON(search_window.search_btn),
499
                             GTK_STOCK_FIND);
500
        gtk_label_set_text(GTK_LABEL(search_window.status_label), _("Done."));
501
        statusbar_pop_all();
502
503
        if (search_window.cancelled)
504
                debug_print("* query search cancelled.\n");
505
        debug_print("query search finished.\n");
506
507
        search_window.on_search = FALSE;
508
        search_window.cancelled = FALSE;
509
}
510
511
static void summary_search_folder(FolderItem *item)
512
{
513
        gchar *folder_name, *str;
514
        GSList *mlist;
515
        FilterInfo fltinfo;
516
        GSList *cur;
517
        gint count = 1, total;
518
        GTimeVal tv_prev, tv_cur;
519
520
        if (!item->path || item->stype == F_VIRTUAL)
521
                return;
522
523
        folder_name = g_path_get_basename(item->path);
524
        str = g_strdup_printf(_("Searching %s ..."), folder_name);
525
        gtk_label_set_text(GTK_LABEL(search_window.status_label), str);
526
        g_free(str);
527
        g_get_current_time(&tv_prev);
528
        ui_update();
529
530
        if (search_window.cancelled) {
531
                g_free(folder_name);
532
                return;
533
        }
534
535
        mlist = folder_item_get_msg_list(item, TRUE);
536
        total = g_slist_length(mlist);
537
538
        memset(&fltinfo, 0, sizeof(FilterInfo));
539
540
        debug_print("requires_full_headers: %d\n",
541
                    search_window.requires_full_headers);
542
        debug_print("start query search: %s\n", item->path ? item->path : "");
543
544
        for (cur = mlist; cur != NULL; cur = cur->next) {
545
                MsgInfo *msginfo = (MsgInfo *)cur->data;
546
                GSList *hlist;
547
548
                g_get_current_time(&tv_cur);
549
                if (tv_cur.tv_sec > tv_prev.tv_sec ||
550
                    tv_cur.tv_usec - tv_prev.tv_usec >
551
                    PROGRESS_UPDATE_INTERVAL * 1000) {
552
                        str = g_strdup_printf(_("Searching %s (%d / %d)..."),
553
                                              folder_name, count, total);
554
                        gtk_label_set_text
555
                                (GTK_LABEL(search_window.status_label), str);
556
                        g_free(str);
557
                        ui_update();
558
                        tv_prev = tv_cur;
559
                }
560
                ++count;
561
562
                if (search_window.cancelled)
563
                        break;
564
565
                fltinfo.flags = msginfo->flags;
566
                if (search_window.requires_full_headers) {
567
                        gchar *file;
568
569
                        file = procmsg_get_message_file(msginfo);
570
                        hlist = procheader_get_header_list_from_file(file);
571
                        g_free(file);
572
                } else
573
                        hlist = procheader_get_header_list_from_msginfo
574
                                (msginfo);
575
                if (!hlist)
576
                        continue;
577
578
                if (filter_match_rule(search_window.rule, msginfo, hlist,
579
                                      &fltinfo)) {
580
                        summary_search_append_msg(msginfo);
581
                        cur->data = NULL;
582
                }
583
584
                procheader_header_list_destroy(hlist);
585
        }
586
587
        procmsg_msg_list_free(mlist);
588
        g_free(folder_name);
589
}
590
591
static gboolean summary_search_recursive_func(GNode *node, gpointer data)
592
{
593
        FolderItem *item;
594
595
        g_return_val_if_fail(node->data != NULL, FALSE);
596
597
        item = FOLDER_ITEM(node->data);
598
599
        if (!item->path)
600
                return FALSE;
601
602
        summary_search_folder(item);
603
604
        if (search_window.cancelled)
605
                return TRUE;
606
607
        return FALSE;
608
}
609
610
static void summary_search_append_msg(MsgInfo *msginfo)
611
{
612
        GtkListStore *store = search_window.store;
613
        GtkTreeIter iter;
614
        gchar *folder;
615
        gchar date_buf[80];
616
        const gchar *subject, *from, *date;
617
        gchar *id;
618
619
        id = folder_item_get_identifier(msginfo->folder);
620
        folder = g_path_get_basename(id);
621
        g_free(id);
622
        subject = msginfo->subject ? msginfo->subject : _("(No Subject)");
623
        from = msginfo->from ? msginfo->from : _("(No From)");
624
        if (msginfo->date_t) {
625
                procheader_date_get_localtime(date_buf, sizeof(date_buf),
626
                                              msginfo->date_t);
627
                date = date_buf;
628
        } else if (msginfo->date)
629
                date = msginfo->date;
630
        else
631
                date = _("(No Date)");
632
633
        gtk_list_store_append(store, &iter);
634
        gtk_list_store_set(store, &iter,
635
                           COL_FOLDER, folder,
636
                           COL_SUBJECT, subject,
637
                           COL_FROM, from,
638
                           COL_DATE, date,
639
                           COL_MSGINFO, msginfo,
640
                           -1);
641
642
        g_free(folder);
643
}
644
645
static void summary_search_clear_list(void)
646
{
647
        GtkTreeIter iter;
648
        GtkTreeModel *model = GTK_TREE_MODEL(search_window.store);
649
        MsgInfo *msginfo;
650
651
        if (!gtk_tree_model_get_iter_first(model, &iter))
652
                return;
653
654
        do {
655
                gtk_tree_model_get(model, &iter, COL_MSGINFO, &msginfo, -1);
656
                procmsg_msginfo_free(msginfo);
657
        } while (gtk_tree_model_iter_next(model, &iter));
658
659
        gtk_list_store_clear(search_window.store);
660
}
661
662
static void summary_search_hbox_added(CondHBox *hbox)
663
{
664
        g_signal_connect(hbox->key_entry, "activate",
665
                         G_CALLBACK(summary_search_entry_activated), NULL);
666
}
667
668
static void row_activated(GtkTreeView *treeview, GtkTreePath *path,
669
                          GtkTreeViewColumn *column, gpointer data)
670
{
671
        GtkTreeIter iter;
672
        GtkTreeModel *model = GTK_TREE_MODEL(search_window.store);
673
        MsgInfo *msginfo;
674
        MessageView *msgview;
675
676
        if (!gtk_tree_model_get_iter(model, &iter, path))
677
                return;
678
679
        gtk_tree_model_get(model, &iter, COL_MSGINFO, &msginfo, -1);
680
        if (!summary_select_by_msginfo(main_window_get()->summaryview,
681
                                       msginfo)) {
682
                msgview = messageview_create_with_new_window();
683
                messageview_show(msgview, msginfo, FALSE);
684
        }
685
}
686
687
static gboolean row_selected(GtkTreeSelection *selection,
688
                             GtkTreeModel *model, GtkTreePath *path,
689
                             gboolean cur_selected, gpointer data)
690
{
691
        return TRUE;
692
}
693
694
static void summary_search_clear(GtkButton *button, gpointer data)
695
{
696
        CondHBox *cond_hbox;
697
698
        prefs_filter_edit_clear_cond_edit(search_window.cond_edit);
699
        prefs_filter_set_header_list(NULL);
700
        prefs_filter_edit_set_header_list(search_window.cond_edit, NULL);
701
        cond_hbox = prefs_filter_edit_cond_hbox_create(search_window.cond_edit);
702
        prefs_filter_edit_set_cond_hbox_widgets(cond_hbox, PF_COND_HEADER);
703
        prefs_filter_edit_insert_cond_hbox
704
                (search_window.cond_edit, cond_hbox, -1);
705
        if (search_window.cond_edit->add_hbox)
706
                search_window.cond_edit->add_hbox(cond_hbox);
707
708
        gtk_label_set_text(GTK_LABEL(search_window.status_label), "");
709
710
        summary_search_clear_list();
711
}
712
713
static void summary_select_folder(GtkButton *button, gpointer data)
714
{
715
        FolderItem *item;
716
        gchar *id;
717
718
        item = foldersel_folder_sel(NULL, FOLDER_SEL_ALL, NULL);
719
        if (!item || item->stype == F_VIRTUAL)
720
                return;
721
722
        id = folder_item_get_identifier(item);
723
        if (id) {
724
                gtk_entry_set_text(GTK_ENTRY(search_window.folder_entry), id);
725
                g_free(id);
726
        }
727
}
728
729
static void summary_search_clicked(GtkButton *button, gpointer data)
730
{
731
        if (search_window.on_search)
732
                search_window.cancelled = TRUE;
733
        else
734
                summary_search_query();
735
}
736
737
static gint summary_search_save_dialog_deleted(GtkWidget *widget,
738
                                               GdkEventAny *event,
739
                                               gpointer data)
740
{
741
        SummarySearchSaveDialog *dialog = (SummarySearchSaveDialog *)data;
742
743
        dialog->cancelled = TRUE;
744
        dialog->finished = TRUE;
745
        return TRUE;
746
}
747
748
static gint summary_search_save_dialog_key_pressed(GtkWidget *widget,
749
                                                   GdkEventKey *event,
750
                                                   gpointer data)
751
{
752
        SummarySearchSaveDialog *dialog = (SummarySearchSaveDialog *)data;
753
754
        if (event && event->keyval == GDK_Escape) {
755
                dialog->cancelled = TRUE;
756
                dialog->finished = TRUE;
757
        }
758
        return FALSE;
759
}
760
761
static void summary_search_save_dialog_select_folder(GtkButton *button,
762
                                                     gpointer data)
763
{
764
        SummarySearchSaveDialog *dialog = (SummarySearchSaveDialog *)data;
765
        FolderItem *item;
766
        gchar *id;
767
768
        item = foldersel_folder_sel(NULL, FOLDER_SEL_ALL, NULL);
769
        if (!item || item->no_sub || item->stype == F_VIRTUAL)
770
                return;
771
772
        id = folder_item_get_identifier(item);
773
        if (id) {
774
                gtk_entry_set_text(GTK_ENTRY(dialog->folder_entry), id);
775
                g_free(id);
776
        }
777
}
778
779
static void summary_search_save_ok(GtkButton *button, gpointer data)
780
{
781
        SummarySearchSaveDialog *dialog = (SummarySearchSaveDialog *)data;
782
783
        dialog->finished = TRUE;
784
}
785
786
static void summary_search_save_cancel(GtkButton *button, gpointer data)
787
{
788
        SummarySearchSaveDialog *dialog = (SummarySearchSaveDialog *)data;
789
790
        dialog->cancelled = TRUE;
791
        dialog->finished = TRUE;
792
}
793
794
static SummarySearchSaveDialog *summary_search_save_dialog_create(void)
795
{
796
        SummarySearchSaveDialog *dialog;
797
        GtkWidget *window;
798
        GtkWidget *vbox;
799
        GtkWidget *hbox;
800
        GtkWidget *label;
801
        GtkWidget *folder_entry;
802
        GtkWidget *folder_btn;
803
        GtkWidget *name_entry;
804
805
        GtkWidget *confirm_area;
806
        GtkWidget *hbbox;
807
        GtkWidget *cancel_btn;
808
        GtkWidget *ok_btn;
809
810
        dialog = g_new0(SummarySearchSaveDialog, 1);
811
812
        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
813
        gtk_window_set_title(GTK_WINDOW(window), _("Save as search folder"));
814
        gtk_widget_set_size_request(window, 400, -1);
815
        gtk_container_set_border_width(GTK_CONTAINER(window), 8);
816
        gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
817
        gtk_window_set_modal(GTK_WINDOW(window), TRUE);
818
        gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, FALSE);
819
        g_signal_connect(G_OBJECT(window), "delete_event",
820
                         G_CALLBACK(summary_search_save_dialog_deleted),
821
                         dialog);
822
        g_signal_connect(G_OBJECT(window), "key_press_event",
823
                         G_CALLBACK(summary_search_save_dialog_key_pressed),
824
                         dialog);
825
        MANAGE_WINDOW_SIGNALS_CONNECT(window);
826
        manage_window_set_transient(GTK_WINDOW(window));
827
828
        vbox = gtk_vbox_new(FALSE, 8);
829
        gtk_container_add(GTK_CONTAINER(window), vbox);
830
831
        hbox = gtk_hbox_new(FALSE, 8);
832
        gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
833
834
        label = gtk_label_new(_("Location:"));
835
        gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
836
837
        folder_entry = gtk_entry_new();
838
        gtk_box_pack_start(GTK_BOX(hbox), folder_entry, TRUE, TRUE, 0);
839
840
        folder_btn = gtk_button_new_with_label("...");
841
        gtk_box_pack_start(GTK_BOX(hbox), folder_btn, FALSE, FALSE, 0);
842
        g_signal_connect(G_OBJECT(folder_btn), "clicked",
843
                         G_CALLBACK(summary_search_save_dialog_select_folder),
844
                         dialog);
845
846
        hbox = gtk_hbox_new(FALSE, 8);
847
        gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
848
849
        label = gtk_label_new(_("Folder name:"));
850
        gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
851
852
        name_entry = gtk_entry_new();
853
        gtk_box_pack_start(GTK_BOX(hbox), name_entry, TRUE, TRUE, 0);
854
855
        confirm_area = gtk_hbox_new(FALSE, 12);
856
        gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
857
858
        gtkut_stock_button_set_create(&hbbox,
859
                                      &ok_btn, GTK_STOCK_OK,
860
                                      &cancel_btn, GTK_STOCK_CANCEL,
861
                                      NULL, NULL);
862
        gtk_box_pack_end(GTK_BOX(confirm_area), hbbox, FALSE, FALSE, 0);
863
        GTK_WIDGET_SET_FLAGS(ok_btn, GTK_CAN_DEFAULT);
864
        gtk_widget_grab_default(ok_btn);
865
        g_signal_connect(G_OBJECT(ok_btn), "clicked",
866
                         G_CALLBACK(summary_search_save_ok), dialog);
867
        g_signal_connect(G_OBJECT(cancel_btn), "clicked",
868
                         G_CALLBACK(summary_search_save_cancel), dialog);
869
870
        gtk_widget_grab_focus(name_entry);
871
872
        gtk_widget_show_all(window);
873
874
        dialog->window = window;
875
        dialog->folder_entry = folder_entry;
876
        dialog->name_entry = name_entry;
877
        dialog->cancelled = FALSE;
878
        dialog->finished = FALSE;
879
880
        return dialog;
881
}
882
883
static void summary_search_save_dialog_destroy(SummarySearchSaveDialog *dialog)
884
{
885
        gtk_widget_destroy(dialog->window);
886
        g_free(dialog);
887
}
888
889
static FolderItem *summary_search_create_vfolder(FolderItem *parent,
890
                                                 const gchar *name)
891
{
892
        gchar *path;
893
        gchar *fs_name;
894
        gchar *fullpath;
895
        FolderItem *item;
896
897
        g_return_val_if_fail(parent != NULL, NULL);
898
        g_return_val_if_fail(name != NULL, NULL);
899
900
        path = folder_item_get_path(parent);
901
        fs_name = g_filename_from_utf8(name, -1, NULL, NULL, NULL);
902
        fullpath = g_strconcat(path, G_DIR_SEPARATOR_S,
903
                               fs_name ? fs_name : name, NULL);
904
        g_free(fs_name);
905
        g_free(path);
906
907
        if (make_dir_hier(fullpath) < 0) {
908
                g_free(fullpath);
909
                return NULL;
910
        }
911
912
        if (parent->path)
913
                path = g_strconcat(parent->path, G_DIR_SEPARATOR_S, name, NULL);
914
        else
915
                path = g_strdup(name);
916
917
        item = folder_item_new(name, path);
918
        item->stype = F_VIRTUAL;
919
        item->no_sub = TRUE;
920
        folder_item_append(parent, item);
921
922
        g_free(path);
923
924
        return item;
925
}
926
927
static void summary_search_vfolder_update_rule(FolderItem *item)
928
{
929
        GSList list;
930
        FilterRule *rule;
931
        gchar *file;
932
        gchar *path;
933
934
        rule = summary_search_dialog_to_rule(item->name, NULL);
935
        list.data = rule;
936
        list.next = NULL;
937
938
        path = folder_item_get_path(item);
939
        file = g_strconcat(path, G_DIR_SEPARATOR_S, FILTER_LIST, NULL);
940
        filter_write_file(&list, file);
941
        g_free(file);
942
        g_free(path);
943
944
        filter_rule_free(rule);
945
}
946
947
static void summary_search_save(GtkButton *button, gpointer data)
948
{
949
        SummarySearchSaveDialog *dialog;
950
        const gchar *id, *name;
951
        FolderItem *parent, *item;
952
953
        dialog = summary_search_save_dialog_create();
954
        id = gtk_entry_get_text(GTK_ENTRY(search_window.folder_entry));
955
        if (id && *id)
956
                gtk_entry_set_text(GTK_ENTRY(dialog->folder_entry), id);
957
958
        while (!dialog->finished)
959
                gtk_main_iteration();
960
961
        if (dialog->cancelled) {
962
                summary_search_save_dialog_destroy(dialog);
963
                return;
964
        }
965
966
        id = gtk_entry_get_text(GTK_ENTRY(dialog->folder_entry));
967
        parent = folder_find_item_from_identifier(id);
968
        name = gtk_entry_get_text(GTK_ENTRY(dialog->name_entry));
969
        if (parent && name && *name) {
970
                if (folder_find_child_item_by_name(parent, name)) {
971
                        alertpanel_error(_("The folder `%s' already exists."),
972
                                         name);
973
                } else {
974
                        item = summary_search_create_vfolder(parent, name);
975
                        if (item) {
976
                                summary_search_vfolder_update_rule(item);
977
                                folderview_append_item(folderview_get(),
978
                                                       NULL, item, TRUE);
979
                                folder_write_list();
980
                        }
981
                }
982
        }
983
984
        summary_search_save_dialog_destroy(dialog);
985
}
986
987
static void summary_search_close(GtkButton *button, gpointer data)
988
{
989
        if (search_window.on_search)
990
                search_window.cancelled = TRUE;
991
        gtk_widget_hide(search_window.window);
992
}
993
994
static void summary_search_entry_activated(GtkWidget *widget, gpointer data)
995
{
996
        gtk_button_clicked(GTK_BUTTON(search_window.search_btn));
997
}
998
999
static gint summary_search_deleted(GtkWidget *widget, GdkEventAny *event,
1000
                                   gpointer data)
1001
{
1002
        gtk_button_clicked(GTK_BUTTON(search_window.close_btn));
1003
        return TRUE;
1004
}
1005
1006
static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event,
1007
                            gpointer data)
1008
{
1009
        if (event && event->keyval == GDK_Escape) {
1010
                if (search_window.on_search)
1011
                        gtk_button_clicked
1012
                                (GTK_BUTTON(search_window.search_btn));
1013
                else
1014
                        gtk_button_clicked(GTK_BUTTON(search_window.close_btn));
1015
                return TRUE;
1016
        }
1017
        return FALSE;
1018
}