a4597560b63285d6a23fdb335c5ff34ceb2b7ee6
[claws.git] / src / prefs_filter.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2002 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 <gtk/gtk.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33
34 #include "intl.h"
35 #include "main.h"
36 #include "prefs.h"
37 #include "prefs_filter.h"
38 #include "prefs_common.h"
39 #include "mainwindow.h"
40 #include "foldersel.h"
41 #include "manage_window.h"
42 #include "inc.h"
43 #include "filter.h"
44 #include "utils.h"
45 #include "gtkutils.h"
46 #include "alertpanel.h"
47 #include "folder.h"
48
49 static struct Filter {
50         GtkWidget *window;
51
52         GtkWidget *ok_btn;
53
54         GtkWidget *hdr_combo1;
55         GtkWidget *hdr_combo2;
56         GtkWidget *hdr_entry1;
57         GtkWidget *hdr_entry2;
58         GtkWidget *key_entry1;
59         GtkWidget *key_entry2;
60         GtkWidget *pred_combo1;
61         GtkWidget *pred_combo2;
62         GtkWidget *pred_entry1;
63         GtkWidget *pred_entry2;
64         GtkWidget *cond_combo;
65         GtkWidget *cond_entry;
66
67         GtkWidget *dest_entry;
68         GtkWidget *regex_chkbtn;
69
70         GtkWidget *destsel_btn;
71         GtkWidget *dest_radiobtn;
72         GtkWidget *notrecv_radiobtn;
73
74         GtkWidget *cond_clist;
75 } filter;
76
77 /*
78    parameter name, default value, pointer to the prefs variable, data type,
79    pointer to the widget pointer,
80    pointer to the function for data setting,
81    pointer to the function for widget setting
82  */
83
84 /* widget creating functions */
85 static void prefs_filter_create         (void);
86
87 static void prefs_filter_set_dialog     (const gchar    *header,
88                                          const gchar    *key);
89 static void prefs_filter_set_list       (void);
90 static gint prefs_filter_clist_set_row  (gint    row);
91
92 /* callback functions */
93 static void prefs_filter_select_dest_cb (void);
94 static void prefs_filter_register_cb    (void);
95 static void prefs_filter_substitute_cb  (void);
96 static void prefs_filter_delete_cb      (void);
97 static void prefs_filter_up             (void);
98 static void prefs_filter_down           (void);
99 static void prefs_filter_select         (GtkCList       *clist,
100                                          gint            row,
101                                          gint            column,
102                                          GdkEvent       *event);
103 static void prefs_filter_row_move       (GtkCList       *clist,
104                                          gint            source_row,
105                                          gint            dest_row);
106
107 static void prefs_filter_dest_radio_button_toggled      (void);
108 static void prefs_filter_notrecv_radio_button_toggled   (void);
109 static void prefs_filter_regex_check_button_toggled     (void);
110
111 static gint prefs_filter_deleted        (GtkWidget      *widget,
112                                          GdkEventAny    *event,
113                                          gpointer        data);
114 static void prefs_filter_key_pressed    (GtkWidget      *widget,
115                                          GdkEventKey    *event,
116                                          gpointer        data);
117 static void prefs_filter_cancel         (void);
118 static void prefs_filter_ok             (void);
119
120 void prefs_filter_open(const gchar *header, const gchar *key)
121 {
122         if (prefs_rc_is_readonly(FILTER_RC))
123                 return;
124
125         inc_lock();
126
127         if (!filter.window) {
128                 prefs_filter_create();
129         }
130
131         manage_window_set_transient(GTK_WINDOW(filter.window));
132         gtk_widget_grab_focus(filter.ok_btn);
133
134         prefs_filter_set_dialog(header, key);
135
136         gtk_widget_show(filter.window);
137 }
138
139 static void prefs_filter_create(void)
140 {
141         GtkWidget *window;
142         GtkWidget *vbox;
143         GtkWidget *ok_btn;
144         GtkWidget *cancel_btn;
145         GtkWidget *confirm_area;
146
147         GtkWidget *vbox1;
148         GtkWidget *table1;
149         GtkWidget *cond_label;
150         GtkWidget *cond_combo;
151         GtkWidget *cond_entry;
152         GtkWidget *hdr_label;
153         GtkWidget *hdr_combo1;
154         GtkWidget *hdr_combo2;
155         GtkWidget *key_label;
156         GtkWidget *key_entry1;
157         GtkWidget *key_entry2;
158         GtkWidget *pred_label;
159         GtkWidget *pred_combo1;
160         GtkWidget *pred_entry1;
161         GtkWidget *pred_combo2;
162         GtkWidget *pred_entry2;
163
164         GtkWidget *vbox2;
165         GtkWidget *dest_hbox;
166         GtkWidget *dest_entry;
167         GtkWidget *destsel_btn;
168         GtkWidget *dest_radiobtn;
169         GtkWidget *notrecv_radiobtn;
170         GSList *recv_group = NULL;
171
172         GtkWidget *regex_chkbtn;
173
174         GtkWidget *reg_hbox;
175         GtkWidget *btn_hbox;
176         GtkWidget *arrow;
177         GtkWidget *reg_btn;
178         GtkWidget *subst_btn;
179         GtkWidget *del_btn;
180
181         GtkWidget *cond_hbox;
182         GtkWidget *cond_scrolledwin;
183         GtkWidget *cond_clist;
184
185         GtkWidget *btn_vbox;
186         GtkWidget *up_btn;
187         GtkWidget *down_btn;
188
189         gchar *title[1];
190
191         debug_print(_("Creating filter setting window...\n"));
192
193         window = gtk_window_new (GTK_WINDOW_DIALOG);
194         gtk_container_set_border_width (GTK_CONTAINER (window), 8);
195         gtk_window_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
196         gtk_window_set_modal (GTK_WINDOW (window), TRUE);
197         gtk_window_set_policy (GTK_WINDOW (window), FALSE, TRUE, FALSE);
198
199         vbox = gtk_vbox_new (FALSE, 6);
200         gtk_widget_show (vbox);
201         gtk_container_add (GTK_CONTAINER (window), vbox);
202
203         gtkut_button_set_create(&confirm_area, &ok_btn, _("OK"),
204                                 &cancel_btn, _("Cancel"), NULL, NULL);
205         gtk_widget_show (confirm_area);
206         gtk_box_pack_end (GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
207         gtk_widget_grab_default (ok_btn);
208
209         /*
210         gtkut_button_set_create (&confirm_area, &close_btn, _("Close"),
211                                  NULL, NULL, NULL, NULL);
212         gtk_widget_show (confirm_area);
213         gtk_box_pack_end (GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
214         gtk_widget_grab_default (close_btn);
215         */
216
217         gtk_window_set_title (GTK_WINDOW(window),
218                               _("Filter setting"));
219         gtk_signal_connect (GTK_OBJECT(window), "delete_event",
220                             GTK_SIGNAL_FUNC(prefs_filter_deleted), NULL);
221         gtk_signal_connect (GTK_OBJECT(window), "key_press_event",
222                             GTK_SIGNAL_FUNC(prefs_filter_key_pressed), NULL);
223         gtk_signal_connect (GTK_OBJECT(window), "focus_in_event",
224                             GTK_SIGNAL_FUNC(manage_window_focus_in), NULL);
225         gtk_signal_connect (GTK_OBJECT(window), "focus_out_event",
226                             GTK_SIGNAL_FUNC(manage_window_focus_out), NULL);
227         gtk_signal_connect (GTK_OBJECT(ok_btn), "clicked",
228                             GTK_SIGNAL_FUNC(prefs_filter_ok), NULL);
229         gtk_signal_connect (GTK_OBJECT(cancel_btn), "clicked",
230                             GTK_SIGNAL_FUNC(prefs_filter_cancel), NULL);
231
232         vbox1 = gtk_vbox_new (FALSE, VSPACING);
233         gtk_widget_show (vbox1);
234         gtk_box_pack_start (GTK_BOX (vbox), vbox1, TRUE, TRUE, 0);
235         gtk_container_set_border_width (GTK_CONTAINER (vbox1), 2);
236
237         table1 = gtk_table_new (3, 4, FALSE);
238         gtk_widget_show (table1);
239         gtk_box_pack_start (GTK_BOX (vbox1), table1, FALSE, FALSE, 0);
240         gtk_table_set_row_spacings (GTK_TABLE (table1), 8);
241         gtk_table_set_col_spacings (GTK_TABLE (table1), 8);
242
243         cond_label = gtk_label_new (_("Condition"));
244         gtk_widget_show (cond_label);
245         gtk_table_attach (GTK_TABLE (table1), cond_label, 0, 1, 0, 1,
246                           GTK_FILL, 0, 0, 0);
247         gtk_misc_set_alignment (GTK_MISC (cond_label), 0, 0.5);
248
249         cond_combo = gtk_combo_new ();
250         gtk_widget_show (cond_combo);
251         gtk_table_attach (GTK_TABLE (table1), cond_combo, 0, 1, 2, 3,
252                           0, 0, 0, 0);
253         gtk_widget_set_usize (cond_combo, 52, -1);
254         gtkut_combo_set_items (GTK_COMBO (cond_combo), "and", "or", NULL);
255
256         cond_entry = GTK_COMBO (cond_combo)->entry;
257         gtk_entry_set_editable (GTK_ENTRY (cond_entry), FALSE);
258
259         hdr_label = gtk_label_new (_("Header"));
260         gtk_widget_show (hdr_label);
261         gtk_table_attach (GTK_TABLE (table1), hdr_label, 1, 2, 0, 1,
262                           GTK_FILL, 0, 0, 0);
263         gtk_misc_set_alignment (GTK_MISC (hdr_label), 0, 0.5);
264
265         hdr_combo1 = gtk_combo_new ();
266         gtk_widget_show (hdr_combo1);
267         gtk_table_attach (GTK_TABLE (table1), hdr_combo1, 1, 2, 1, 2,
268                           0, 0, 0, 0);
269         gtk_widget_set_usize (hdr_combo1, 96, -1);
270         gtkut_combo_set_items (GTK_COMBO (hdr_combo1),
271                                "Subject", "From", "To", "Cc", "Reply-To",
272                                "Sender", "List-Id",
273                                "X-ML-Name", "X-List", "X-Sequence", "X-Mailer",
274                                NULL);
275
276         hdr_combo2 = gtk_combo_new ();
277         gtk_widget_show (hdr_combo2);
278         gtk_table_attach (GTK_TABLE (table1), hdr_combo2, 1, 2, 2, 3,
279                           0, 0, 0, 0);
280         gtk_widget_set_usize (hdr_combo2, 96, -1);
281         gtkut_combo_set_items (GTK_COMBO (hdr_combo2), _("(none)"),
282                                "Subject", "From", "To", "Cc", "Reply-To",
283                                "Sender", "List-Id",
284                                "X-ML-Name", "X-List", "X-Sequence", "X-Mailer",
285                                NULL);
286
287         key_label = gtk_label_new (_("Keyword"));
288         gtk_widget_show (key_label);
289         gtk_table_attach (GTK_TABLE (table1), key_label, 2, 3, 0, 1,
290                           GTK_EXPAND | GTK_SHRINK | GTK_FILL,
291                           0, 0, 0);
292         gtk_misc_set_alignment (GTK_MISC (key_label), 0, 0.5);
293
294         key_entry1 = gtk_entry_new ();
295         gtk_widget_show (key_entry1);
296         gtk_widget_set_usize (key_entry1, 128, -1);
297         gtk_table_attach (GTK_TABLE (table1), key_entry1, 2, 3, 1, 2,
298                           GTK_EXPAND | GTK_SHRINK | GTK_FILL,
299                           0, 0, 0);
300
301         key_entry2 = gtk_entry_new ();
302         gtk_widget_show (key_entry2);
303         gtk_widget_set_usize (key_entry2, 128, -1);
304         gtk_table_attach (GTK_TABLE (table1), key_entry2, 2, 3, 2, 3,
305                           GTK_EXPAND | GTK_SHRINK | GTK_FILL,
306                           0, 0, 0);
307
308         pred_label = gtk_label_new (_("Predicate"));
309         gtk_widget_show (pred_label);
310         gtk_table_attach (GTK_TABLE (table1), pred_label, 3, 4, 0, 1,
311                           GTK_FILL, 0, 0, 0);
312         gtk_misc_set_alignment (GTK_MISC (pred_label), 0, 0.5);
313
314         pred_combo1 = gtk_combo_new ();
315         gtk_widget_show (pred_combo1);
316         gtk_table_attach (GTK_TABLE (table1), pred_combo1, 3, 4, 1, 2,
317                           0, 0, 0, 0);
318         gtk_widget_set_usize (pred_combo1, 92, -1);
319         gtkut_combo_set_items (GTK_COMBO (pred_combo1),
320                                _("contains"), _("not contain"), NULL);
321
322         pred_entry1 = GTK_COMBO (pred_combo1)->entry;
323         gtk_entry_set_editable (GTK_ENTRY (pred_entry1), FALSE);
324
325         pred_combo2 = gtk_combo_new ();
326         gtk_widget_show (pred_combo2);
327         gtk_table_attach (GTK_TABLE (table1), pred_combo2, 3, 4, 2, 3,
328                           0, 0, 0, 0);
329         gtk_widget_set_usize (pred_combo2, 92, -1);
330         gtkut_combo_set_items (GTK_COMBO (pred_combo2),
331                                _("contains"), _("not contain"), NULL);
332
333         pred_entry2 = GTK_COMBO (pred_combo2)->entry;
334         gtk_entry_set_editable (GTK_ENTRY (pred_entry2), FALSE);
335
336         /* destination */
337
338         vbox2 = gtk_vbox_new (FALSE, VSPACING_NARROW);
339         gtk_widget_show (vbox2);
340         gtk_box_pack_start (GTK_BOX (vbox1), vbox2, FALSE, TRUE, 0);
341
342         dest_hbox = gtk_hbox_new (FALSE, 8);
343         gtk_widget_show (dest_hbox);
344         gtk_box_pack_start (GTK_BOX (vbox2), dest_hbox, FALSE, TRUE, 0);
345
346         dest_radiobtn =
347                 gtk_radio_button_new_with_label (recv_group, _("Destination"));
348         recv_group = gtk_radio_button_group (GTK_RADIO_BUTTON (dest_radiobtn));
349         gtk_widget_show (dest_radiobtn);
350         gtk_box_pack_start (GTK_BOX (dest_hbox), dest_radiobtn,
351                             FALSE, FALSE, 0);
352         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dest_radiobtn), TRUE);
353         gtk_signal_connect
354                 (GTK_OBJECT (dest_radiobtn), "toggled",
355                  GTK_SIGNAL_FUNC (prefs_filter_dest_radio_button_toggled),
356                  NULL);
357
358         dest_entry = gtk_entry_new ();
359         gtk_widget_show (dest_entry);
360         gtk_widget_set_usize (dest_entry, DEFAULT_ENTRY_WIDTH, -1);
361         gtk_box_pack_start (GTK_BOX (dest_hbox), dest_entry, TRUE, TRUE, 0);
362         gtk_entry_set_editable (GTK_ENTRY (dest_entry), FALSE);
363
364         destsel_btn = gtk_button_new_with_label (_(" Select... "));
365         gtk_widget_show (destsel_btn);
366         gtk_box_pack_start (GTK_BOX (dest_hbox), destsel_btn, FALSE, FALSE, 0);
367         gtk_signal_connect (GTK_OBJECT (destsel_btn), "clicked",
368                             GTK_SIGNAL_FUNC (prefs_filter_select_dest_cb),
369                             NULL);
370
371         PACK_CHECK_BUTTON (dest_hbox, regex_chkbtn, _("Use regex"));
372         gtk_signal_connect
373                 (GTK_OBJECT (regex_chkbtn), "toggled",
374                  GTK_SIGNAL_FUNC (prefs_filter_regex_check_button_toggled),
375                  NULL);
376
377         notrecv_radiobtn = gtk_radio_button_new_with_label
378                 (recv_group, _("Don't receive"));
379         recv_group = gtk_radio_button_group
380                 (GTK_RADIO_BUTTON (notrecv_radiobtn));
381         gtk_widget_show (notrecv_radiobtn);
382         gtk_box_pack_start (GTK_BOX (vbox2), notrecv_radiobtn, FALSE, FALSE, 0);
383         gtk_signal_connect
384                 (GTK_OBJECT (notrecv_radiobtn), "toggled",
385                  GTK_SIGNAL_FUNC (prefs_filter_notrecv_radio_button_toggled),
386                  NULL);
387
388         /* register / substitute / delete */
389
390         reg_hbox = gtk_hbox_new (FALSE, 4);
391         gtk_widget_show (reg_hbox);
392         gtk_box_pack_start (GTK_BOX (vbox1), reg_hbox, FALSE, FALSE, 0);
393
394         arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
395         gtk_widget_show (arrow);
396         gtk_box_pack_start (GTK_BOX (reg_hbox), arrow, FALSE, FALSE, 0);
397         gtk_widget_set_usize (arrow, -1, 16);
398
399         btn_hbox = gtk_hbox_new (TRUE, 4);
400         gtk_widget_show (btn_hbox);
401         gtk_box_pack_start (GTK_BOX (reg_hbox), btn_hbox, FALSE, FALSE, 0);
402
403         reg_btn = gtk_button_new_with_label (_("Register"));
404         gtk_widget_show (reg_btn);
405         gtk_box_pack_start (GTK_BOX (btn_hbox), reg_btn, FALSE, TRUE, 0);
406         gtk_signal_connect (GTK_OBJECT (reg_btn), "clicked",
407                             GTK_SIGNAL_FUNC (prefs_filter_register_cb), NULL);
408
409         subst_btn = gtk_button_new_with_label (_(" Substitute "));
410         gtk_widget_show (subst_btn);
411         gtk_box_pack_start (GTK_BOX (btn_hbox), subst_btn, FALSE, TRUE, 0);
412         gtk_signal_connect (GTK_OBJECT (subst_btn), "clicked",
413                             GTK_SIGNAL_FUNC (prefs_filter_substitute_cb),
414                             NULL);
415
416         del_btn = gtk_button_new_with_label (_("Delete"));
417         gtk_widget_show (del_btn);
418         gtk_box_pack_start (GTK_BOX (btn_hbox), del_btn, FALSE, TRUE, 0);
419         gtk_signal_connect (GTK_OBJECT (del_btn), "clicked",
420                             GTK_SIGNAL_FUNC (prefs_filter_delete_cb), NULL);
421
422         cond_hbox = gtk_hbox_new (FALSE, 8);
423         gtk_widget_show (cond_hbox);
424         gtk_box_pack_start (GTK_BOX (vbox1), cond_hbox, TRUE, TRUE, 0);
425
426         cond_scrolledwin = gtk_scrolled_window_new (NULL, NULL);
427         gtk_widget_show (cond_scrolledwin);
428         gtk_widget_set_usize (cond_scrolledwin, -1, 150);
429         gtk_box_pack_start (GTK_BOX (cond_hbox), cond_scrolledwin,
430                             TRUE, TRUE, 0);
431         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (cond_scrolledwin),
432                                         GTK_POLICY_AUTOMATIC,
433                                         GTK_POLICY_AUTOMATIC);
434
435         title[0] = _("Registered rules");
436         cond_clist = gtk_clist_new_with_titles(1, title);
437         gtk_widget_show (cond_clist);
438         gtk_container_add (GTK_CONTAINER (cond_scrolledwin), cond_clist);
439         gtk_clist_set_column_width (GTK_CLIST (cond_clist), 0, 80);
440         gtk_clist_set_selection_mode (GTK_CLIST (cond_clist),
441                                       GTK_SELECTION_BROWSE);
442         GTK_WIDGET_UNSET_FLAGS (GTK_CLIST (cond_clist)->column[0].button,
443                                 GTK_CAN_FOCUS);
444         gtk_signal_connect (GTK_OBJECT (cond_clist), "select_row",
445                             GTK_SIGNAL_FUNC (prefs_filter_select), NULL);
446         gtk_signal_connect_after (GTK_OBJECT (cond_clist), "row_move",
447                                   GTK_SIGNAL_FUNC (prefs_filter_row_move),
448                                   NULL);
449
450         btn_vbox = gtk_vbox_new (FALSE, 8);
451         gtk_widget_show (btn_vbox);
452         gtk_box_pack_start (GTK_BOX (cond_hbox), btn_vbox, FALSE, FALSE, 0);
453
454         up_btn = gtk_button_new_with_label (_("Up"));
455         gtk_widget_show (up_btn);
456         gtk_box_pack_start (GTK_BOX (btn_vbox), up_btn, FALSE, FALSE, 0);
457         gtk_signal_connect (GTK_OBJECT (up_btn), "clicked",
458                             GTK_SIGNAL_FUNC (prefs_filter_up), NULL);
459
460         down_btn = gtk_button_new_with_label (_("Down"));
461         gtk_widget_show (down_btn);
462         gtk_box_pack_start (GTK_BOX (btn_vbox), down_btn, FALSE, FALSE, 0);
463         gtk_signal_connect (GTK_OBJECT (down_btn), "clicked",
464                             GTK_SIGNAL_FUNC (prefs_filter_down), NULL);
465
466         gtk_widget_show_all(window);
467
468         filter.window    = window;
469         filter.ok_btn = ok_btn;
470
471         filter.hdr_combo1  = hdr_combo1;
472         filter.hdr_combo2  = hdr_combo2;
473         filter.hdr_entry1  = GTK_COMBO (hdr_combo1)->entry;
474         filter.hdr_entry2  = GTK_COMBO (hdr_combo2)->entry;
475         filter.key_entry1  = key_entry1;
476         filter.key_entry2  = key_entry2;
477         filter.pred_combo1 = pred_combo1;
478         filter.pred_combo2 = pred_combo2;
479         filter.pred_entry1 = pred_entry1;
480         filter.pred_entry2 = pred_entry2;
481         filter.cond_combo  = cond_combo;
482         filter.cond_entry  = cond_entry;
483
484         filter.dest_entry       = dest_entry;
485         filter.destsel_btn      = destsel_btn;
486         filter.dest_radiobtn    = dest_radiobtn;
487         filter.notrecv_radiobtn = notrecv_radiobtn;
488         filter.regex_chkbtn     = regex_chkbtn;
489
490         filter.cond_clist   = cond_clist;
491 }
492
493 void prefs_filter_read_config(void)
494 {
495         gchar *rcpath;
496         FILE *fp;
497         gchar buf[PREFSBUFSIZE];
498         Filter *flt;
499
500         debug_print(_("Reading filter configuration...\n"));
501
502         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, FILTER_RC, NULL);
503         if ((fp = fopen(rcpath, "r")) == NULL) {
504                 if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen");
505                 g_free(rcpath);
506                 return;
507         }
508         g_free(rcpath);
509
510         /* remove all previous filter list */
511         while (prefs_common.fltlist != NULL) {
512                 flt = (Filter *)prefs_common.fltlist->data;
513                 filter_free(flt);
514                 prefs_common.fltlist = g_slist_remove(prefs_common.fltlist,
515                                                       flt);
516         }
517
518         while (fgets(buf, sizeof(buf), fp) != NULL) {
519                 g_strchomp(buf);
520                 flt = filter_read_str(buf);
521                 if (flt) {
522                         prefs_common.fltlist =
523                                 g_slist_append(prefs_common.fltlist, flt);
524                 }
525         }
526
527         fclose(fp);
528 }
529
530 void prefs_filter_write_config(void)
531 {
532         gchar *rcpath;
533         PrefFile *pfile;
534         GSList *cur;
535
536         debug_print(_("Writing filter configuration...\n"));
537
538         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, FILTER_RC, NULL);
539         if ((pfile = prefs_write_open(rcpath)) == NULL) {
540                 g_warning(_("failed to write configuration to file\n"));
541                 g_free(rcpath);
542                 return;
543         }
544
545         for (cur = prefs_common.fltlist; cur != NULL; cur = cur->next) {
546                 Filter *flt = (Filter *)cur->data;
547                 gchar *fstr;
548
549                 fstr = filter_get_str(flt);
550                 if (fputs(fstr, pfile->fp) == EOF ||
551                     fputc('\n', pfile->fp) == EOF) {
552                         FILE_OP_ERROR(rcpath, "fputs || fputc");
553                         prefs_write_close_revert(pfile);
554                         g_free(rcpath);
555                         g_free(fstr);
556                         return;
557                 }
558                 g_free(fstr);
559         }
560
561         g_free(rcpath);
562
563         if (prefs_write_close(pfile) < 0) {
564                 g_warning(_("failed to write configuration to file\n"));
565                 return;
566         }
567 }
568
569 void prefs_filter_rename_path(const gchar *old_path, const gchar *new_path)
570 {
571         GSList *cur;
572         gchar *base;
573         gchar *dest_path;
574         gint oldpathlen;
575
576         for (cur = prefs_common.fltlist; cur != NULL; cur = cur->next) {
577                 Filter *flt = (Filter *)cur->data;
578
579                 oldpathlen = strlen(old_path);
580                 if (!strncmp(old_path, (gchar *)flt->dest, oldpathlen)) {
581                         base = (gchar *)flt->dest + oldpathlen;
582                         while (*base == G_DIR_SEPARATOR) base++;
583                         if (*base == '\0')
584                                 dest_path = g_strdup(new_path);
585                         else
586                                 dest_path = g_strconcat(new_path,
587                                                         G_DIR_SEPARATOR_S,
588                                                         base, NULL);
589                         g_free(flt->dest);
590                         flt->dest = dest_path;
591                 }
592         }
593
594         prefs_filter_write_config();
595 }
596
597 void prefs_filter_delete_path(const gchar *path)
598 {
599         GSList *cur;
600         GSList *next;
601         gint len;
602
603         for (cur = prefs_common.fltlist; cur != NULL; cur = next) {
604                 Filter *flt = (Filter *)cur->data;
605                 next = cur->next;
606
607                 len = strlen(path);
608                 if (!strncmp(path, flt->dest, len)) {
609                         filter_free(flt);
610                         prefs_common.fltlist =
611                                 g_slist_remove(prefs_common.fltlist, flt);
612                 }
613         }
614
615         prefs_filter_write_config();
616 }
617
618 static void prefs_filter_set_dialog(const gchar *header, const gchar *key)
619 {
620         GtkCList *clist = GTK_CLIST(filter.cond_clist);
621         GSList *cur;
622         gchar *cond_str[1];
623         gint row;
624
625         gtk_clist_freeze(clist);
626         gtk_clist_clear(clist);
627
628         cond_str[0] = _("(New)");
629         row = gtk_clist_append(clist, cond_str);
630         gtk_clist_set_row_data(clist, row, NULL);
631
632         for (cur = prefs_common.fltlist; cur != NULL; cur = cur->next) {
633                 Filter *flt = (Filter *)cur->data;
634
635                 cond_str[0] = filter_get_str(flt);
636                 subst_char(cond_str[0], '\t', ':');
637                 row = gtk_clist_append(clist, cond_str);
638                 gtk_clist_set_row_data(clist, row, flt);
639
640                 g_free(cond_str[0]);
641         }
642
643         gtk_clist_thaw(clist);
644
645         if (header && key) {
646                 gtk_entry_set_text(GTK_ENTRY(filter.hdr_entry1), header);
647                 gtk_entry_set_text(GTK_ENTRY(filter.key_entry1), key);
648         }
649 }
650
651 static void prefs_filter_set_list(void)
652 {
653         gint row = 1;
654         Filter *flt;
655
656         g_slist_free(prefs_common.fltlist);
657         prefs_common.fltlist = NULL;
658
659         while ((flt = gtk_clist_get_row_data(GTK_CLIST(filter.cond_clist),
660                 row)) != NULL) {
661                 prefs_common.fltlist = g_slist_append(prefs_common.fltlist,
662                                                       flt);
663                 row++;
664         }
665 }
666
667 #define GET_ENTRY(entry) \
668         entry_text = gtk_entry_get_text(GTK_ENTRY(entry))
669
670 static gint prefs_filter_clist_set_row(gint row)
671 {
672         GtkCList *clist = GTK_CLIST(filter.cond_clist);
673         Filter *flt;
674         gchar *entry_text;
675         gchar *cond_str[1];
676
677         g_return_val_if_fail(row != 0, -1);
678
679         if (GTK_WIDGET_IS_SENSITIVE(filter.dest_entry))
680                 GET_ENTRY(filter.dest_entry);
681         else
682                 entry_text = FILTER_NOT_RECEIVE;
683         if (entry_text[0] == '\0') {
684                 alertpanel_error(_("Destination is not set."));
685                 return -1;
686         }
687         GET_ENTRY(filter.hdr_entry1);
688         if (entry_text[0] == '\0') {
689                 alertpanel_error(_("Header name is not set."));
690                 return -1;
691         }
692
693         flt = g_new0(Filter, 1);
694
695         flt->name1 = g_strdup(entry_text);
696
697         GET_ENTRY(filter.key_entry1);
698         if (entry_text[0] != '\0')
699                 flt->body1 = g_strdup(entry_text);
700
701         GET_ENTRY(filter.hdr_entry2);
702         if (entry_text[0] != '\0' && strcmp(entry_text, _("(none)")) != 0) {
703                 flt->name2 = g_strdup(entry_text);
704
705                 GET_ENTRY(filter.key_entry2);
706                 if (entry_text[0] != '\0')
707                         flt->body2 = g_strdup(entry_text);
708         }
709
710         if (gtk_toggle_button_get_active
711                 (GTK_TOGGLE_BUTTON(filter.regex_chkbtn)))
712                 flt->flag1 = flt->flag2 = FLT_REGEX;
713
714         GET_ENTRY(filter.pred_entry1);
715         if (!strcmp(entry_text, _("contains")))
716                 flt->flag1 |= FLT_CONTAIN;
717         GET_ENTRY(filter.pred_entry2);
718         if (!strcmp(entry_text, _("contains")))
719                 flt->flag2 |= FLT_CONTAIN;
720
721         GET_ENTRY(filter.cond_entry);
722         if (!strcmp(entry_text, "and"))
723                 flt->cond = FLT_AND;
724         else
725                 flt->cond = FLT_OR;
726
727         if (GTK_WIDGET_IS_SENSITIVE(filter.dest_entry)) {
728                 entry_text = gtk_entry_get_text(GTK_ENTRY(filter.dest_entry));
729                 flt->dest = g_strdup(entry_text);
730                 flt->action = FLT_MOVE;
731         } else
732                 flt->action = FLT_NOTRECV;
733
734         cond_str[0] = filter_get_str(flt);
735         subst_char(cond_str[0], '\t', ':');
736
737         if (row < 0)
738                 row = gtk_clist_append(clist, cond_str);
739         else {
740                 Filter *tmpflt;
741
742                 gtk_clist_set_text(clist, row, 0, cond_str[0]);
743                 tmpflt = gtk_clist_get_row_data(clist, row);
744                 if (tmpflt)
745                         filter_free(tmpflt);
746         }
747
748         gtk_clist_set_row_data(clist, row, flt);
749
750         g_free(cond_str[0]);
751
752         prefs_filter_set_list();
753
754         return row;
755 }
756
757 static void prefs_filter_select_dest_cb(void)
758 {
759         FolderItem *dest;
760
761         dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL);
762         if (!dest) return;
763
764         gtk_entry_set_text(GTK_ENTRY(filter.dest_entry), dest->path);
765 }
766
767 static void prefs_filter_register_cb(void)
768 {
769         prefs_filter_clist_set_row(-1);
770 }
771
772 static void prefs_filter_substitute_cb(void)
773 {
774         GtkCList *clist = GTK_CLIST(filter.cond_clist);
775         Filter *flt;
776         gint row;
777
778         if (!clist->selection) return;
779
780         row = GPOINTER_TO_INT(clist->selection->data);
781         if (row == 0) return;
782
783         flt = gtk_clist_get_row_data(clist, row);
784         if (!flt) return;
785
786         prefs_filter_clist_set_row(row);
787 }
788
789 static void prefs_filter_delete_cb(void)
790 {
791         GtkCList *clist = GTK_CLIST(filter.cond_clist);
792         Filter *flt;
793         gint row;
794
795         if (!clist->selection) return;
796         row = GPOINTER_TO_INT(clist->selection->data);
797         if (row == 0) return;
798
799         if (alertpanel(_("Delete rule"),
800                        _("Do you really want to delete this rule?"),
801                        _("Yes"), _("No"), NULL) == G_ALERTALTERNATE)
802                 return;
803
804         flt = gtk_clist_get_row_data(clist, row);
805         filter_free(flt);
806         gtk_clist_remove(clist, row);
807         prefs_common.fltlist = g_slist_remove(prefs_common.fltlist, flt);
808 }
809
810 static void prefs_filter_up(void)
811 {
812         GtkCList *clist = GTK_CLIST(filter.cond_clist);
813         gint row;
814
815         if (!clist->selection) return;
816
817         row = GPOINTER_TO_INT(clist->selection->data);
818         if (row > 1)
819                 gtk_clist_row_move(clist, row, row - 1);
820 }
821
822 static void prefs_filter_down(void)
823 {
824         GtkCList *clist = GTK_CLIST(filter.cond_clist);
825         gint row;
826
827         if (!clist->selection) return;
828
829         row = GPOINTER_TO_INT(clist->selection->data);
830         if (row > 0 && row < clist->rows - 1)
831                 gtk_clist_row_move(clist, row, row + 1);
832 }
833
834 #define ENTRY_SET_TEXT(entry, str) \
835         gtk_entry_set_text(GTK_ENTRY(entry), str ? str : "")
836
837 static void prefs_filter_select(GtkCList *clist, gint row, gint column,
838                                 GdkEvent *event)
839 {
840         Filter *flt;
841         Filter default_flt = {"Subject", NULL, NULL, NULL,
842                               FLT_CONTAIN, FLT_CONTAIN, FLT_AND,
843                               NULL, FLT_MOVE};
844         gboolean is_regex;
845
846         default_flt.name2 = _("(none)");
847
848         flt = gtk_clist_get_row_data(clist, row);
849         if (!flt)
850                 flt = &default_flt;
851
852         ENTRY_SET_TEXT(filter.dest_entry, flt->dest);
853         ENTRY_SET_TEXT(filter.hdr_entry1, flt->name1);
854         ENTRY_SET_TEXT(filter.key_entry1, flt->body1);
855         ENTRY_SET_TEXT(filter.hdr_entry2,
856                        flt->name2 ? flt->name2 : _("(none)"));
857         ENTRY_SET_TEXT(filter.key_entry2, flt->body2);
858
859         ENTRY_SET_TEXT(filter.pred_entry1,
860                        FLT_IS_CONTAIN(flt->flag1)
861                        ? _("contains") : _("not contain"));
862         ENTRY_SET_TEXT(filter.pred_entry2,
863                        FLT_IS_CONTAIN(flt->flag2)
864                        ? _("contains") : _("not contain"));
865
866         is_regex = FLT_IS_REGEX(flt->flag1);
867         gtk_widget_set_sensitive(filter.pred_combo1, !is_regex);
868         gtk_widget_set_sensitive(filter.pred_combo2, !is_regex);
869         gtk_toggle_button_set_active
870                 (GTK_TOGGLE_BUTTON(filter.regex_chkbtn), is_regex);
871
872         gtk_entry_set_text(GTK_ENTRY(filter.cond_entry),
873                            flt->cond == FLT_OR ? "or" : "and");
874         if (flt->action == FLT_NOTRECV)
875                 gtk_toggle_button_set_active
876                         (GTK_TOGGLE_BUTTON(filter.notrecv_radiobtn), TRUE);
877         else
878                 gtk_toggle_button_set_active
879                         (GTK_TOGGLE_BUTTON(filter.dest_radiobtn), TRUE);
880 }
881
882 static void prefs_filter_row_move(GtkCList *clist, gint source_row,
883                                   gint dest_row)
884 {
885         prefs_filter_set_list();
886         if (gtk_clist_row_is_visible(clist, dest_row) != GTK_VISIBILITY_FULL) {
887                 gtk_clist_moveto(clist, dest_row, -1,
888                                  source_row < dest_row ? 1.0 : 0.0, 0.0);
889         }
890 }
891
892 static void prefs_filter_dest_radio_button_toggled(void)
893 {
894         gtk_widget_set_sensitive(filter.dest_entry, TRUE);
895         gtk_widget_set_sensitive(filter.destsel_btn, TRUE);
896 }
897
898 static void prefs_filter_notrecv_radio_button_toggled(void)
899 {
900         gtk_widget_set_sensitive(filter.dest_entry, FALSE);
901         gtk_widget_set_sensitive(filter.destsel_btn, FALSE);
902 }
903
904 static void prefs_filter_regex_check_button_toggled(void)
905 {
906         gboolean is_regex;
907
908         is_regex = gtk_toggle_button_get_active
909                 (GTK_TOGGLE_BUTTON(filter.regex_chkbtn));
910         gtk_widget_set_sensitive(filter.pred_combo1, !is_regex);
911         gtk_widget_set_sensitive(filter.pred_combo2, !is_regex);
912 }
913
914 static gint prefs_filter_deleted(GtkWidget *widget, GdkEventAny *event,
915                                  gpointer data)
916 {
917         prefs_filter_cancel();
918         return TRUE;
919 }
920
921 static void prefs_filter_key_pressed(GtkWidget *widget, GdkEventKey *event,
922                                      gpointer data)
923 {
924         if (event && event->keyval == GDK_Escape)
925                 prefs_filter_cancel();
926 }
927
928 static void prefs_filter_ok(void)
929 {
930         prefs_filter_write_config();
931         gtk_widget_hide(filter.window);
932         inc_unlock();
933 }
934
935 static void prefs_filter_cancel(void)
936 {
937         prefs_filter_read_config();
938         gtk_widget_hide(filter.window);
939         inc_unlock();
940 }