Statistics
| Revision:

root / src / prefs_filter.c @ 2080

History | View | Annotate | Download (22.3 kB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 1999-2008 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 <gtk/gtk.h>
29
#include <gdk/gdkkeysyms.h>
30
#include <stdio.h>
31
#include <stdlib.h>
32
#include <string.h>
33
#include <errno.h>
34
35
#include "main.h"
36
#include "prefs.h"
37
#include "prefs_ui.h"
38
#include "prefs_filter.h"
39
#include "prefs_filter_edit.h"
40
#include "prefs_common.h"
41
#include "mainwindow.h"
42
#include "foldersel.h"
43
#include "manage_window.h"
44
#include "stock_pixmap.h"
45
#include "inc.h"
46
#include "procheader.h"
47
#include "menu.h"
48
#include "filter.h"
49
#include "utils.h"
50
#include "gtkutils.h"
51
#include "alertpanel.h"
52
#include "xml.h"
53
54
static struct FilterRuleListWindow {
55
        GtkWidget *window;
56
57
        GtkWidget *treeview;
58
        GtkListStore *store;
59
        GtkTreeSelection *selection;
60
61
        GtkWidget *add_btn;
62
        GtkWidget *edit_btn;
63
        GtkWidget *copy_btn;
64
        GtkWidget *del_btn;
65
66
        GSList *default_hdr_list;
67
        GSList *user_hdr_list;
68
        GSList *msg_hdr_list;
69
70
        GHashTable *msg_hdr_table;
71
72
        GtkWidget *close_btn;
73
} rule_list_window;
74
75
enum {
76
        COL_ENABLED,
77
        COL_NAME,
78
        COL_FILTER_RULE,
79
        N_COLS
80
};
81
82
static void prefs_filter_create                        (void);
83
84
static void prefs_filter_set_dialog                (void);
85
static void prefs_filter_set_list_row                (GtkTreeIter        *iter,
86
                                                 FilterRule        *rule,
87
                                                 gboolean         move_view);
88
89
static void prefs_filter_set_list                (void);
90
91
/* callback functions */
92
static void prefs_filter_add_cb                (void);
93
static void prefs_filter_edit_cb        (void);
94
static void prefs_filter_copy_cb        (void);
95
static void prefs_filter_delete_cb        (void);
96
static void prefs_filter_top                (void);
97
static void prefs_filter_up                (void);
98
static void prefs_filter_down                (void);
99
static void prefs_filter_bottom                (void);
100
101
static gboolean prefs_filter_select        (GtkTreeSelection        *selection,
102
                                         GtkTreeModel                *model,
103
                                         GtkTreePath                *path,
104
                                         gboolean                 cur_selected,
105
                                         gpointer                 data);
106
static void prefs_filter_enable_toggled        (GtkCellRenderer        *cell,
107
                                         gchar                        *path,
108
                                         gpointer                 data);
109
110
static void prefs_filter_row_activated        (GtkTreeView                *treeview,
111
                                         GtkTreePath                *path,
112
                                         GtkTreeViewColumn        *column,
113
                                         gpointer                 data);
114
static void prefs_filter_row_reordered        (GtkTreeModel                *model,
115
                                         GtkTreePath                *path,
116
                                         GtkTreeIter                *iter,
117
                                         gpointer                 data,
118
                                         gpointer                 user_data);
119
120
static gint prefs_filter_deleted        (GtkWidget        *widget,
121
                                         GdkEventAny        *event,
122
                                         gpointer         data);
123
static gboolean prefs_filter_key_pressed(GtkWidget        *widget,
124
                                         GdkEventKey        *event,
125
                                         gpointer         data);
126
static void prefs_filter_close                (void);
127
128
129
void prefs_filter_open(MsgInfo *msginfo, const gchar *header, const gchar *key)
130
{
131
        inc_lock();
132
133
        if (!rule_list_window.window)
134
                prefs_filter_create();
135
136
        prefs_filter_set_header_list(msginfo);
137
138
        manage_window_set_transient(GTK_WINDOW(rule_list_window.window));
139
        gtk_widget_grab_focus(rule_list_window.close_btn);
140
141
        prefs_filter_set_dialog();
142
143
        gtk_widget_show(rule_list_window.window);
144
        manage_window_focus_in(rule_list_window.window, NULL, NULL);
145
146
        if (msginfo) {
147
                FilterRule *rule;
148
149
                rule = prefs_filter_edit_open(NULL, header, key);
150
                gtk_window_present(GTK_WINDOW(rule_list_window.window));
151
152
                if (rule) {
153
                        prefs_filter_set_list_row(NULL, rule, TRUE);
154
                        prefs_filter_set_list();
155
                }
156
        }
157
}
158
159
static void prefs_filter_create(void)
160
{
161
        GtkWidget *window;
162
        GtkWidget *vbox;
163
        GtkWidget *close_btn;
164
        GtkWidget *confirm_area;
165
166
        GtkWidget *hbox;
167
        GtkWidget *scrolledwin;
168
        GtkWidget *treeview;
169
        GtkListStore *store;
170
        GtkTreeSelection *selection;
171
        GtkTreeViewColumn *column;
172
        GtkCellRenderer *renderer;
173
174
        GtkWidget *btn_vbox;
175
        GtkWidget *spc_vbox;
176
        GtkWidget *top_btn;
177
        GtkWidget *up_btn;
178
        GtkWidget *down_btn;
179
        GtkWidget *bottom_btn;
180
181
        GtkWidget *btn_hbox;
182
        GtkWidget *add_btn;
183
        GtkWidget *edit_btn;
184
        GtkWidget *copy_btn;
185
        GtkWidget *del_btn;
186
187
        debug_print("Creating filter setting window...\n");
188
189
        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
190
        gtk_container_set_border_width(GTK_CONTAINER(window), 8);
191
        gtk_widget_set_size_request(window, 540, 360);
192
        gtk_window_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
193
        gtk_window_set_modal(GTK_WINDOW(window), TRUE);
194
        gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, FALSE);
195
196
        vbox = gtk_vbox_new(FALSE, 6);
197
        gtk_widget_show(vbox);
198
        gtk_container_add(GTK_CONTAINER(window), vbox);
199
200
        gtkut_stock_button_set_create(&confirm_area,
201
                                      &close_btn, GTK_STOCK_CLOSE,
202
                                      NULL, NULL, NULL, NULL);
203
        gtk_widget_show(confirm_area);
204
        gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
205
        gtk_widget_grab_default(close_btn);
206
207
        gtk_window_set_title(GTK_WINDOW(window),
208
                             _("Filter settings"));
209
        g_signal_connect(G_OBJECT(window), "delete_event",
210
                         G_CALLBACK(prefs_filter_deleted), NULL);
211
        g_signal_connect(G_OBJECT(window), "key_press_event",
212
                         G_CALLBACK(prefs_filter_key_pressed), NULL);
213
        MANAGE_WINDOW_SIGNALS_CONNECT (window);
214
        g_signal_connect(G_OBJECT(close_btn), "clicked",
215
                         G_CALLBACK(prefs_filter_close), NULL);
216
217
        /* Rule list */
218
219
        hbox = gtk_hbox_new(FALSE, 8);
220
        gtk_widget_show(hbox);
221
        gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
222
223
        scrolledwin = gtk_scrolled_window_new(NULL, NULL);
224
        gtk_widget_show(scrolledwin);
225
        gtk_widget_set_size_request(scrolledwin, -1, 150);
226
        gtk_box_pack_start(GTK_BOX(hbox), scrolledwin, TRUE, TRUE, 0);
227
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
228
                                       GTK_POLICY_AUTOMATIC,
229
                                       GTK_POLICY_AUTOMATIC);
230
        gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
231
                                            GTK_SHADOW_IN);
232
233
        store = gtk_list_store_new
234
                (N_COLS, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER);
235
236
        treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
237
        g_object_unref(G_OBJECT(store));
238
        gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE);
239
        gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE);
240
        gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), COL_NAME);
241
        gtk_tree_view_set_reorderable(GTK_TREE_VIEW(treeview), TRUE);
242
243
        selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
244
        gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
245
        gtk_tree_selection_set_select_function(selection, prefs_filter_select,
246
                                               NULL, NULL);
247
248
        renderer = gtk_cell_renderer_toggle_new();
249
        g_signal_connect(renderer, "toggled",
250
                         G_CALLBACK(prefs_filter_enable_toggled), NULL);
251
        column = gtk_tree_view_column_new_with_attributes
252
                (_("Enabled"), renderer, "active", COL_ENABLED, NULL);
253
        gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
254
255
        renderer = gtk_cell_renderer_text_new();
256
        column = gtk_tree_view_column_new_with_attributes
257
                (_("Name"), renderer, "text", COL_NAME, NULL);
258
        gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
259
        gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
260
261
        gtk_widget_show(treeview);
262
        gtk_container_add(GTK_CONTAINER(scrolledwin), treeview);
263
264
        g_signal_connect(G_OBJECT(treeview), "row-activated",
265
                         G_CALLBACK(prefs_filter_row_activated), NULL);
266
        g_signal_connect_after(G_OBJECT(store), "rows-reordered",
267
                               G_CALLBACK(prefs_filter_row_reordered), NULL);
268
269
        /* Up / Down */
270
271
        btn_vbox = gtk_vbox_new (FALSE, 8);
272
        gtk_widget_show(btn_vbox);
273
        gtk_box_pack_start(GTK_BOX(hbox), btn_vbox, FALSE, FALSE, 0);
274
275
        top_btn = gtk_button_new_from_stock(GTK_STOCK_GOTO_TOP);
276
        gtk_widget_show(top_btn);
277
        gtk_box_pack_start(GTK_BOX(btn_vbox), top_btn, FALSE, FALSE, 0);
278
        g_signal_connect(G_OBJECT(top_btn), "clicked",
279
                         G_CALLBACK(prefs_filter_top), NULL);
280
281
        PACK_VSPACER(btn_vbox, spc_vbox, VSPACING_NARROW_2);
282
283
        up_btn = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
284
        gtk_widget_show(up_btn);
285
        gtk_box_pack_start(GTK_BOX(btn_vbox), up_btn, FALSE, FALSE, 0);
286
        g_signal_connect(G_OBJECT(up_btn), "clicked",
287
                         G_CALLBACK(prefs_filter_up), NULL);
288
289
        down_btn = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
290
        gtk_widget_show(down_btn);
291
        gtk_box_pack_start(GTK_BOX(btn_vbox), down_btn, FALSE, FALSE, 0);
292
        g_signal_connect(G_OBJECT(down_btn), "clicked",
293
                         G_CALLBACK(prefs_filter_down), NULL);
294
295
        PACK_VSPACER(btn_vbox, spc_vbox, VSPACING_NARROW_2);
296
297
        bottom_btn = gtk_button_new_from_stock(GTK_STOCK_GOTO_BOTTOM);
298
        gtk_widget_show(bottom_btn);
299
        gtk_box_pack_start(GTK_BOX(btn_vbox), bottom_btn, FALSE, FALSE, 0);
300
        g_signal_connect(G_OBJECT(bottom_btn), "clicked",
301
                         G_CALLBACK(prefs_filter_bottom), NULL);
302
303
        /* add / edit / copy / delete */
304
305
        hbox = gtk_hbox_new(FALSE, 4);
306
        gtk_widget_show(hbox);
307
        gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
308
309
        btn_hbox = gtk_hbox_new(TRUE, 4);
310
        gtk_widget_show(btn_hbox);
311
        gtk_box_pack_start(GTK_BOX(hbox), btn_hbox, FALSE, FALSE, 0);
312
313
        add_btn = gtk_button_new_from_stock(GTK_STOCK_ADD);
314
        gtk_widget_show(add_btn);
315
        gtk_box_pack_start(GTK_BOX(btn_hbox), add_btn, FALSE, TRUE, 0);
316
        g_signal_connect(G_OBJECT(add_btn), "clicked",
317
                         G_CALLBACK(prefs_filter_add_cb), NULL);
318
319
#ifdef GTK_STOCK_EDIT
320
        edit_btn = gtk_button_new_from_stock(GTK_STOCK_EDIT);
321
#else
322
        edit_btn = gtk_button_new_with_label(_("Edit"));
323
#endif
324
        gtk_widget_show(edit_btn);
325
        gtk_box_pack_start(GTK_BOX(btn_hbox), edit_btn, FALSE, TRUE, 0);
326
        g_signal_connect(G_OBJECT(edit_btn), "clicked",
327
                         G_CALLBACK(prefs_filter_edit_cb), NULL);
328
329
        copy_btn = gtk_button_new_from_stock(GTK_STOCK_COPY);
330
        gtk_widget_show(copy_btn);
331
        gtk_box_pack_start(GTK_BOX(btn_hbox), copy_btn, FALSE, TRUE, 0);
332
        g_signal_connect(G_OBJECT(copy_btn), "clicked",
333
                         G_CALLBACK(prefs_filter_copy_cb), NULL);
334
335
        del_btn = gtk_button_new_from_stock(GTK_STOCK_DELETE);
336
        gtk_widget_show(del_btn);
337
        gtk_box_pack_start(GTK_BOX(btn_hbox), del_btn, FALSE, TRUE, 0);
338
        g_signal_connect(G_OBJECT(del_btn), "clicked",
339
                         G_CALLBACK(prefs_filter_delete_cb), NULL);
340
341
        gtk_widget_show_all(window);
342
343
        rule_list_window.window = window;
344
        rule_list_window.close_btn = close_btn;
345
346
        rule_list_window.treeview = treeview;
347
        rule_list_window.store = store;
348
        rule_list_window.selection = selection;
349
350
        rule_list_window.add_btn = add_btn;
351
        rule_list_window.edit_btn = edit_btn;
352
        rule_list_window.copy_btn = copy_btn;
353
        rule_list_window.del_btn = del_btn;
354
355
        rule_list_window.default_hdr_list = NULL;
356
        rule_list_window.user_hdr_list = NULL;
357
        rule_list_window.msg_hdr_list = NULL;
358
        rule_list_window.msg_hdr_table = NULL;
359
}
360
361
static void prefs_filter_set_dialog(void)
362
{
363
        GSList *cur;
364
365
        gtk_list_store_clear(rule_list_window.store);
366
367
        for (cur = prefs_common.fltlist; cur != NULL; cur = cur->next) {
368
                FilterRule *rule = (FilterRule *)cur->data;
369
                prefs_filter_set_list_row(NULL, rule, FALSE);
370
        }
371
}
372
373
static void prefs_filter_set_list_row(GtkTreeIter *iter, FilterRule *rule,
374
                                      gboolean move_view)
375
{
376
        GtkListStore *store = rule_list_window.store;
377
        gchar *rule_name;
378
        GtkTreeIter iter_;
379
380
        g_return_if_fail(rule != NULL);
381
382
        if (rule->name && *rule->name)
383
                rule_name = g_strdup(rule->name);
384
        else
385
                rule_name = filter_get_str(rule);
386
387
        if (!iter) {
388
                gtk_list_store_append(store, &iter_);
389
                gtk_list_store_set(store, &iter_,
390
                                   COL_ENABLED, rule->enabled,
391
                                   COL_NAME, rule_name,
392
                                   COL_FILTER_RULE, rule, -1);
393
        } else {
394
                FilterRule *prev_rule = NULL;
395
396
                iter_ = *iter;
397
                gtk_tree_model_get(GTK_TREE_MODEL(store), &iter_,
398
                                   COL_FILTER_RULE, &prev_rule, -1);
399
                if (!prev_rule) {
400
                        g_warning("rule at the row not found\n");
401
                        gtk_list_store_append(store, &iter_);
402
                }
403
404
                gtk_list_store_set(store, &iter_,
405
                                   COL_ENABLED, rule->enabled,
406
                                   COL_NAME, rule_name,
407
                                   COL_FILTER_RULE, rule, -1);
408
409
                if (prev_rule && prev_rule != rule)
410
                        filter_rule_free(prev_rule);
411
        }
412
413
        g_free(rule_name);
414
415
        if (move_view) {
416
                GtkTreePath *path;
417
418
                path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter_);
419
                gtk_tree_view_scroll_to_cell
420
                        (GTK_TREE_VIEW(rule_list_window.treeview),
421
                         path, NULL, TRUE, 0.5, 0.0);
422
                gtk_tree_path_free(path);
423
        }
424
}
425
426
#define APPEND_HDR_LIST(hdr_list)                                          \
427
        for (cur = hdr_list; cur != NULL; cur = cur->next) {                  \
428
                header = (Header *)cur->data;                                  \
429
                                                                          \
430
                if (!g_hash_table_lookup(table, header->name)) {          \
431
                        g_hash_table_insert(table, header->name, header); \
432
                        list = procheader_add_header_list                  \
433
                                (list, header->name, header->body);          \
434
                }                                                          \
435
        }
436
437
GSList *prefs_filter_get_header_list(void)
438
{
439
        GSList *list = NULL;
440
        GSList *cur;
441
        GHashTable *table;
442
        Header *header;
443
444
        table = g_hash_table_new(str_case_hash, str_case_equal);
445
446
        APPEND_HDR_LIST(rule_list_window.default_hdr_list)
447
        APPEND_HDR_LIST(rule_list_window.user_hdr_list);
448
        APPEND_HDR_LIST(rule_list_window.msg_hdr_list);
449
450
        g_hash_table_destroy(table);
451
452
        return list;
453
}
454
455
#undef APPEND_HDR_LIST
456
457
GSList *prefs_filter_get_user_header_list(void)
458
{
459
        return rule_list_window.user_hdr_list;
460
}
461
462
gchar *prefs_filter_get_msg_header_field(const gchar *header_name)
463
{
464
        if (!rule_list_window.msg_hdr_table)
465
                return NULL;
466
467
        return (gchar *)g_hash_table_lookup
468
                (rule_list_window.msg_hdr_table, header_name);
469
}
470
471
void prefs_filter_set_user_header_list(GSList *list)
472
{
473
        procheader_header_list_destroy(rule_list_window.user_hdr_list);
474
        rule_list_window.user_hdr_list = list;
475
}
476
477
void prefs_filter_set_msg_header_list(MsgInfo *msginfo)
478
{
479
        gchar *file;
480
        GSList *cur;
481
        GSList *next;
482
        Header *header;
483
484
        if (rule_list_window.msg_hdr_table) {
485
                g_hash_table_destroy(rule_list_window.msg_hdr_table);
486
                rule_list_window.msg_hdr_table = NULL;
487
        }
488
        if (rule_list_window.msg_hdr_list) {
489
                procheader_header_list_destroy(rule_list_window.msg_hdr_list);
490
                rule_list_window.msg_hdr_list = NULL;
491
        }
492
493
        if (!msginfo)
494
                return;
495
496
        file = procmsg_get_message_file(msginfo);
497
        g_return_if_fail(file != NULL);
498
499
        rule_list_window.msg_hdr_list =
500
                procheader_get_header_list_from_file(file);
501
502
        g_free(file);
503
504
        rule_list_window.msg_hdr_table =
505
                g_hash_table_new(str_case_hash, str_case_equal);
506
507
        for (cur = rule_list_window.msg_hdr_list; cur != NULL;
508
             cur = next) {
509
                next = cur->next;
510
                header = (Header *)cur->data;
511
                if (!g_ascii_strcasecmp(header->name, "Received") ||
512
                    !g_ascii_strcasecmp(header->name, "Mime-Version") ||
513
                    !g_ascii_strcasecmp(header->name, "X-UIDL")) {
514
                        procheader_header_free(header);
515
                        rule_list_window.msg_hdr_list =
516
                                g_slist_remove(rule_list_window.msg_hdr_list,
517
                                               header);
518
                        continue;
519
                }
520
                if (!g_hash_table_lookup(rule_list_window.msg_hdr_table,
521
                                         header->name)) {
522
                        g_hash_table_insert(rule_list_window.msg_hdr_table,
523
                                            header->name, header->body);
524
                }
525
        }
526
}
527
528
void prefs_filter_set_header_list(MsgInfo *msginfo)
529
{
530
        GSList *list = NULL;
531
        gchar *path;
532
        FILE *fp;
533
534
        list = procheader_add_header_list(list, "From", NULL);
535
        list = procheader_add_header_list(list, "To", NULL);
536
        list = procheader_add_header_list(list, "Cc", NULL);
537
        list = procheader_add_header_list(list, "Subject", NULL);
538
        list = procheader_add_header_list(list, "Reply-To", NULL);
539
        list = procheader_add_header_list(list, "List-Id", NULL);
540
        list = procheader_add_header_list(list, "X-ML-Name", NULL);
541
542
        procheader_header_list_destroy(rule_list_window.default_hdr_list);
543
        rule_list_window.default_hdr_list = list;
544
545
        list = NULL;
546
        path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, FILTER_HEADER_RC,
547
                           NULL);
548
        if ((fp = g_fopen(path, "rb")) != NULL) {
549
                gchar buf[PREFSBUFSIZE];
550
551
                while (fgets(buf, sizeof(buf), fp) != NULL) {
552
                        g_strstrip(buf);
553
                        if (buf[0] == '\0') continue;
554
                        list = procheader_add_header_list(list, buf, NULL);
555
                }
556
557
                fclose(fp);
558
        } else
559
                if (ENOENT != errno) FILE_OP_ERROR(path, "fopen");
560
        g_free(path);
561
562
        prefs_filter_set_user_header_list(list);
563
        prefs_filter_set_msg_header_list(msginfo);
564
}
565
566
void prefs_filter_write_user_header_list(void)
567
{
568
        gchar *path;
569
        PrefFile *pfile;
570
        GSList *cur;
571
572
        path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, FILTER_HEADER_RC,
573
                           NULL);
574
575
        if ((pfile = prefs_file_open(path)) == NULL) {
576
                g_warning("failed to write filter user header list\n");
577
                g_free(path);
578
                return;
579
        }
580
        g_free(path);
581
582
        for (cur = rule_list_window.user_hdr_list; cur != NULL;
583
             cur = cur->next) {
584
                Header *header = (Header *)cur->data;
585
                fputs(header->name, pfile->fp);
586
                fputc('\n', pfile->fp);
587
        }
588
589
        if (prefs_file_close(pfile) < 0)
590
                g_warning("failed to write filter user header list\n");
591
}
592
593
static void prefs_filter_set_list(void)
594
{
595
        FilterRule *rule;
596
        GtkTreeIter iter;
597
        GtkTreeModel *model = GTK_TREE_MODEL(rule_list_window.store);
598
599
        g_slist_free(prefs_common.fltlist);
600
        prefs_common.fltlist = NULL;
601
602
        if (!gtk_tree_model_get_iter_first(model, &iter))
603
                return;
604
605
        do {
606
                gtk_tree_model_get(model, &iter, COL_FILTER_RULE, &rule, -1);
607
                if (rule)
608
                        prefs_common.fltlist =
609
                                g_slist_append(prefs_common.fltlist, rule);
610
        } while (gtk_tree_model_iter_next(model, &iter));
611
}
612
613
static void prefs_filter_add_cb(void)
614
{
615
        FilterRule *rule;
616
617
        rule = prefs_filter_edit_open(NULL, NULL, NULL);
618
        gtk_window_present(GTK_WINDOW(rule_list_window.window));
619
620
        if (rule) {
621
                prefs_filter_set_list_row(NULL, rule, TRUE);
622
                prefs_filter_set_list();
623
        }
624
}
625
626
static void prefs_filter_edit_cb(void)
627
{
628
        GtkTreeIter iter;
629
        FilterRule *rule, *new_rule;
630
631
        if (!gtk_tree_selection_get_selected(rule_list_window.selection,
632
                                             NULL, &iter))
633
                return;
634
635
        gtk_tree_model_get(GTK_TREE_MODEL(rule_list_window.store), &iter,
636
                           COL_FILTER_RULE, &rule, -1);
637
        g_return_if_fail(rule != NULL);
638
639
        new_rule = prefs_filter_edit_open(rule, NULL, NULL);
640
        gtk_window_present(GTK_WINDOW(rule_list_window.window));
641
642
        if (new_rule) {
643
                prefs_filter_set_list_row(&iter, new_rule, TRUE);
644
                prefs_filter_set_list();
645
        }
646
}
647
648
static void prefs_filter_copy_cb(void)
649
{
650
        GtkTreeIter iter;
651
        FilterRule *rule, *new_rule;
652
653
        if (!gtk_tree_selection_get_selected(rule_list_window.selection,
654
                                             NULL, &iter))
655
                return;
656
657
        gtk_tree_model_get(GTK_TREE_MODEL(rule_list_window.store), &iter,
658
                           COL_FILTER_RULE, &rule, -1);
659
        g_return_if_fail(rule != NULL);
660
661
        new_rule = prefs_filter_edit_open(rule, NULL, NULL);
662
        gtk_window_present(GTK_WINDOW(rule_list_window.window));
663
664
        if (new_rule) {
665
                prefs_filter_set_list_row(NULL, new_rule, TRUE);
666
                prefs_filter_set_list();
667
        }
668
}
669
670
static void prefs_filter_delete_cb(void)
671
{
672
        GtkTreeIter iter;
673
        FilterRule *rule;
674
        gchar buf[BUFFSIZE];
675
        gboolean valid;
676
677
        if (!gtk_tree_selection_get_selected(rule_list_window.selection,
678
                                             NULL, &iter))
679
                return;
680
681
        gtk_tree_model_get(GTK_TREE_MODEL(rule_list_window.store), &iter,
682
                           COL_FILTER_RULE, &rule, -1);
683
        g_return_if_fail(rule != NULL);
684
685
        g_snprintf(buf, sizeof(buf),
686
                   _("Do you really want to delete the rule '%s'?"),
687
                   rule->name ? rule->name : _("(Untitled)"));
688
        if (alertpanel(_("Delete rule"), buf,
689
                       GTK_STOCK_YES, GTK_STOCK_NO, NULL) != G_ALERTDEFAULT)
690
                return;
691
692
        valid = gtk_list_store_remove(rule_list_window.store, &iter);
693
        if (valid)
694
                gtk_tree_selection_select_iter(rule_list_window.selection,
695
                                               &iter);
696
697
        prefs_common.fltlist = g_slist_remove(prefs_common.fltlist, rule);
698
        filter_rule_free(rule);
699
}
700
701
static void prefs_filter_top(void)
702
{
703
        GtkTreeIter iter;
704
705
        if (!gtk_tree_selection_get_selected(rule_list_window.selection,
706
                                             NULL, &iter))
707
                return;
708
709
        gtk_list_store_move_after(rule_list_window.store, &iter, NULL);
710
}
711
712
static void prefs_filter_up(void)
713
{
714
        GtkTreeModel *model = GTK_TREE_MODEL(rule_list_window.store);
715
        GtkTreeIter iter, prev;
716
        GtkTreePath *path;
717
718
        if (!gtk_tree_selection_get_selected(rule_list_window.selection,
719
                                             NULL, &iter))
720
                return;
721
722
        path = gtk_tree_model_get_path(model, &iter);
723
        if (gtk_tree_path_prev(path)) {
724
                gtk_tree_model_get_iter(model, &prev, path);
725
                gtk_list_store_swap(rule_list_window.store, &iter, &prev);
726
        }
727
        gtk_tree_path_free(path);
728
}
729
730
static void prefs_filter_down(void)
731
{
732
        GtkTreeIter iter, next;
733
734
        if (!gtk_tree_selection_get_selected(rule_list_window.selection,
735
                                             NULL, &iter))
736
                return;
737
738
        next = iter;
739
        if (gtk_tree_model_iter_next(GTK_TREE_MODEL(rule_list_window.store),
740
                                     &next))
741
                gtk_list_store_swap(rule_list_window.store, &iter, &next);
742
}
743
744
static void prefs_filter_bottom(void)
745
{
746
        GtkTreeIter iter;
747
748
        if (!gtk_tree_selection_get_selected(rule_list_window.selection,
749
                                             NULL, &iter))
750
                return;
751
752
        gtk_list_store_move_before(rule_list_window.store, &iter, NULL);
753
}
754
755
static gboolean prefs_filter_select(GtkTreeSelection *selection,
756
                                    GtkTreeModel *model, GtkTreePath *path,
757
                                    gboolean cur_selected, gpointer data)
758
{
759
        return TRUE;
760
}
761
762
static void prefs_filter_enable_toggled(GtkCellRenderer *cell, gchar *path_str,
763
                                        gpointer data)
764
{
765
        FilterRule *rule;
766
        GtkTreeIter iter;
767
        GtkTreePath *path;
768
769
        path = gtk_tree_path_new_from_string(path_str);
770
        gtk_tree_model_get_iter(GTK_TREE_MODEL(rule_list_window.store),
771
                                &iter, path);
772
        gtk_tree_path_free(path);
773
        gtk_tree_model_get(GTK_TREE_MODEL(rule_list_window.store), &iter,
774
                           COL_FILTER_RULE, &rule, -1);
775
776
        rule->enabled ^= TRUE;
777
778
        gtk_list_store_set(rule_list_window.store, &iter,
779
                           COL_ENABLED, rule->enabled, -1);
780
}
781
782
static void prefs_filter_row_activated(GtkTreeView *treeview, GtkTreePath *path,
783
                                       GtkTreeViewColumn *column,
784
                                       gpointer data)
785
{
786
        gtk_button_clicked(GTK_BUTTON(rule_list_window.edit_btn));
787
}
788
789
static void prefs_filter_row_reordered(GtkTreeModel *model,
790
                                       GtkTreePath *path, GtkTreeIter *iter,
791
                                       gpointer data, gpointer user_data)
792
{
793
        GtkTreeIter iter_;
794
        GtkTreePath *path_;
795
796
        if (!gtk_tree_selection_get_selected(rule_list_window.selection,
797
                                             NULL, &iter_))
798
                return;
799
        path_ = gtk_tree_model_get_path
800
                (GTK_TREE_MODEL(rule_list_window.store), &iter_);
801
        gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(rule_list_window.treeview),
802
                                     path_, NULL, FALSE, 0.0, 0.0);
803
        gtk_tree_path_free(path_);
804
}
805
806
static gint prefs_filter_deleted(GtkWidget *widget, GdkEventAny *event,
807
                                 gpointer data)
808
{
809
        prefs_filter_close();
810
        return TRUE;
811
}
812
813
static gboolean prefs_filter_key_pressed(GtkWidget *widget, GdkEventKey *event,
814
                                         gpointer data)
815
{
816
        if (event && event->keyval == GDK_Escape)
817
                prefs_filter_close();
818
        return FALSE;
819
}
820
821
static void prefs_filter_close(void)
822
{
823
        prefs_filter_set_msg_header_list(NULL);
824
        prefs_filter_set_list();
825
        filter_write_config();
826
        gtk_widget_hide(rule_list_window.window);
827
        gtk_list_store_clear(rule_list_window.store);
828
        main_window_popup(main_window_get());
829
        inc_unlock();
830
}