Statistics
| Revision:

root / src / summary_search.c @ 876

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