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