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