10d68a92e6b622fb9e38c033f0baf23f4c2bc55d
[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_signal_connect
372                 (GTK_OBJECT (regex_chkbtn), "toggled",
373                  GTK_SIGNAL_FUNC (prefs_filter_regex_check_button_toggled),
374                  NULL);
375
376         notrecv_radiobtn = gtk_radio_button_new_with_label
377                 (recv_group, _("Don't receive"));
378         recv_group = gtk_radio_button_group
379                 (GTK_RADIO_BUTTON (notrecv_radiobtn));
380         gtk_widget_show (notrecv_radiobtn);
381         gtk_box_pack_start (GTK_BOX (vbox2), notrecv_radiobtn, FALSE, FALSE, 0);
382         gtk_signal_connect
383                 (GTK_OBJECT (notrecv_radiobtn), "toggled",
384                  GTK_SIGNAL_FUNC (prefs_filter_notrecv_radio_button_toggled),
385                  NULL);
386
387         /* register / substitute / delete */
388
389         reg_hbox = gtk_hbox_new (FALSE, 4);
390         gtk_widget_show (reg_hbox);
391         gtk_box_pack_start (GTK_BOX (vbox1), reg_hbox, FALSE, FALSE, 0);
392
393         arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
394         gtk_widget_show (arrow);
395         gtk_box_pack_start (GTK_BOX (reg_hbox), arrow, FALSE, FALSE, 0);
396         gtk_widget_set_usize (arrow, -1, 16);
397
398         btn_hbox = gtk_hbox_new (TRUE, 4);
399         gtk_widget_show (btn_hbox);
400         gtk_box_pack_start (GTK_BOX (reg_hbox), btn_hbox, FALSE, FALSE, 0);
401
402         reg_btn = gtk_button_new_with_label (_("Register"));
403         gtk_widget_show (reg_btn);
404         gtk_box_pack_start (GTK_BOX (btn_hbox), reg_btn, FALSE, TRUE, 0);
405         gtk_signal_connect (GTK_OBJECT (reg_btn), "clicked",
406                             GTK_SIGNAL_FUNC (prefs_filter_register_cb), NULL);
407
408         subst_btn = gtk_button_new_with_label (_(" Substitute "));
409         gtk_widget_show (subst_btn);
410         gtk_box_pack_start (GTK_BOX (btn_hbox), subst_btn, FALSE, TRUE, 0);
411         gtk_signal_connect (GTK_OBJECT (subst_btn), "clicked",
412                             GTK_SIGNAL_FUNC (prefs_filter_substitute_cb),
413                             NULL);
414
415         del_btn = gtk_button_new_with_label (_("Delete"));
416         gtk_widget_show (del_btn);
417         gtk_box_pack_start (GTK_BOX (btn_hbox), del_btn, FALSE, TRUE, 0);
418         gtk_signal_connect (GTK_OBJECT (del_btn), "clicked",
419                             GTK_SIGNAL_FUNC (prefs_filter_delete_cb), NULL);
420
421         cond_hbox = gtk_hbox_new (FALSE, 8);
422         gtk_widget_show (cond_hbox);
423         gtk_box_pack_start (GTK_BOX (vbox1), cond_hbox, TRUE, TRUE, 0);
424
425         cond_scrolledwin = gtk_scrolled_window_new (NULL, NULL);
426         gtk_widget_show (cond_scrolledwin);
427         gtk_widget_set_usize (cond_scrolledwin, -1, 150);
428         gtk_box_pack_start (GTK_BOX (cond_hbox), cond_scrolledwin,
429                             TRUE, TRUE, 0);
430         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (cond_scrolledwin),
431                                         GTK_POLICY_AUTOMATIC,
432                                         GTK_POLICY_AUTOMATIC);
433
434         cond_clist = gtk_clist_new_with_titles(1, title);
435         gtk_widget_show (cond_clist);
436         gtk_container_add (GTK_CONTAINER (cond_scrolledwin), cond_clist);
437         gtk_clist_set_column_width (GTK_CLIST (cond_clist), 0, 80);
438         gtk_clist_set_selection_mode (GTK_CLIST (cond_clist),
439                                       GTK_SELECTION_BROWSE);
440         GTK_WIDGET_UNSET_FLAGS (GTK_CLIST (cond_clist)->column[0].button,
441                                 GTK_CAN_FOCUS);
442         gtk_signal_connect (GTK_OBJECT (cond_clist), "select_row",
443                             GTK_SIGNAL_FUNC (prefs_filter_select), NULL);
444         gtk_signal_connect (GTK_OBJECT (cond_clist), "row_move",
445                             GTK_SIGNAL_FUNC (prefs_filter_row_move), NULL);
446
447         btn_vbox = gtk_vbox_new (FALSE, 8);
448         gtk_widget_show (btn_vbox);
449         gtk_box_pack_start (GTK_BOX (cond_hbox), btn_vbox, FALSE, FALSE, 0);
450
451         up_btn = gtk_button_new_with_label (_("Up"));
452         gtk_widget_show (up_btn);
453         gtk_box_pack_start (GTK_BOX (btn_vbox), up_btn, FALSE, FALSE, 0);
454         gtk_signal_connect (GTK_OBJECT (up_btn), "clicked",
455                             GTK_SIGNAL_FUNC (prefs_filter_up), NULL);
456
457         down_btn = gtk_button_new_with_label (_("Down"));
458         gtk_widget_show (down_btn);
459         gtk_box_pack_start (GTK_BOX (btn_vbox), down_btn, FALSE, FALSE, 0);
460         gtk_signal_connect (GTK_OBJECT (down_btn), "clicked",
461                             GTK_SIGNAL_FUNC (prefs_filter_down), NULL);
462
463         gtk_widget_show_all(window);
464
465         filter.window    = window;
466         filter.ok_btn = ok_btn;
467
468         filter.hdr_combo1  = hdr_combo1;
469         filter.hdr_combo2  = hdr_combo2;
470         filter.hdr_entry1  = GTK_COMBO (hdr_combo1)->entry;
471         filter.hdr_entry2  = GTK_COMBO (hdr_combo2)->entry;
472         filter.key_entry1  = key_entry1;
473         filter.key_entry2  = key_entry2;
474         filter.pred_combo1 = pred_combo1;
475         filter.pred_combo2 = pred_combo2;
476         filter.pred_entry1 = pred_entry1;
477         filter.pred_entry2 = pred_entry2;
478         filter.op_combo    = op_combo;
479         filter.op_entry    = op_entry;
480
481         filter.dest_entry       = dest_entry;
482         filter.destsel_btn      = destsel_btn;
483         filter.dest_radiobtn    = dest_radiobtn;
484         filter.notrecv_radiobtn = notrecv_radiobtn;
485         filter.regex_chkbtn     = regex_chkbtn;
486
487         filter.cond_clist   = cond_clist;
488 }
489
490 void prefs_filter_read_config(void)
491 {
492         gchar *rcpath;
493         FILE *fp;
494         gchar buf[PREFSBUFSIZE];
495         Filter *flt;
496
497         debug_print(_("Reading filter configuration...\n"));
498
499         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, FILTER_RC, NULL);
500         if ((fp = fopen(rcpath, "r")) == NULL) {
501                 if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen");
502                 g_free(rcpath);
503                 return;
504         }
505         g_free(rcpath);
506
507         /* remove all previous filter list */
508         while (prefs_common.fltlist != NULL) {
509                 flt = (Filter *)prefs_common.fltlist->data;
510                 filter_free(flt);
511                 prefs_common.fltlist = g_slist_remove(prefs_common.fltlist,
512                                                       flt);
513         }
514
515         while (fgets(buf, sizeof(buf), fp) != NULL) {
516                 g_strchomp(buf);
517                 flt = filter_read_str(buf);
518                 if (flt) {
519                         prefs_common.fltlist =
520                                 g_slist_append(prefs_common.fltlist, flt);
521                 }
522         }
523
524         fclose(fp);
525 }
526
527 void prefs_filter_write_config(void)
528 {
529         gchar *rcpath;
530         PrefFile *pfile;
531         GSList *cur;
532
533         debug_print(_("Writing filter configuration...\n"));
534
535         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, FILTER_RC, NULL);
536         if ((pfile = prefs_write_open(rcpath)) == NULL) {
537                 g_warning(_("failed to write configuration to file\n"));
538                 g_free(rcpath);
539                 return;
540         }
541
542         for (cur = prefs_common.fltlist; cur != NULL; cur = cur->next) {
543                 Filter *flt = (Filter *)cur->data;
544                 gchar *fstr;
545
546                 fstr = filter_get_str(flt);
547                 if (fputs(fstr, pfile->fp) == EOF ||
548                     fputc('\n', pfile->fp) == EOF) {
549                         FILE_OP_ERROR(rcpath, "fputs || fputc");
550                         prefs_write_close_revert(pfile);
551                         g_free(rcpath);
552                         g_free(fstr);
553                         return;
554                 }
555                 g_free(fstr);
556         }
557
558         g_free(rcpath);
559
560         if (prefs_write_close(pfile) < 0) {
561                 g_warning(_("failed to write configuration to file\n"));
562                 return;
563         }
564 }
565
566 static void prefs_filter_set_dialog(void)
567 {
568         GtkCList *clist = GTK_CLIST(filter.cond_clist);
569         GSList *cur;
570         gchar *cond_str[1];
571         gint row;
572
573         gtk_clist_freeze(clist);
574         gtk_clist_clear(clist);
575
576         cond_str[0] = _("(New)");
577         row = gtk_clist_append(clist, cond_str);
578         gtk_clist_set_row_data(clist, row, NULL);
579
580         for (cur = prefs_common.fltlist; cur != NULL; cur = cur->next) {
581                 Filter *flt = (Filter *)cur->data;
582
583                 cond_str[0] = filter_get_str(flt);
584                 subst_char(cond_str[0], '\t', ':');
585                 row = gtk_clist_append(clist, cond_str);
586                 gtk_clist_set_row_data(clist, row, flt);
587
588                 g_free(cond_str[0]);
589         }
590
591         gtk_clist_thaw(clist);
592 }
593
594 static void prefs_filter_set_list(void)
595 {
596         gint row = 1;
597         Filter *flt;
598
599         g_slist_free(prefs_common.fltlist);
600         prefs_common.fltlist = NULL;
601
602         while ((flt = gtk_clist_get_row_data(GTK_CLIST(filter.cond_clist),
603                 row)) != NULL) {
604                 prefs_common.fltlist = g_slist_append(prefs_common.fltlist,
605                                                       flt);
606                 row++;
607         }
608 }
609
610 #define GET_ENTRY(entry) \
611         entry_text = gtk_entry_get_text(GTK_ENTRY(entry))
612
613 static gint prefs_filter_clist_set_row(gint row)
614 {
615         GtkCList *clist = GTK_CLIST(filter.cond_clist);
616         Filter *flt;
617         gchar *entry_text;
618         gchar *cond_str[1];
619
620         g_return_val_if_fail(row != 0, -1);
621
622         if (GTK_WIDGET_IS_SENSITIVE(filter.dest_entry))
623                 GET_ENTRY(filter.dest_entry);
624         else
625                 entry_text = FILTER_NOT_RECEIVE;
626         if (entry_text[0] == '\0') {
627                 alertpanel_error(_("Destination is not set."));
628                 return -1;
629         }
630         GET_ENTRY(filter.hdr_entry1);
631         if (entry_text[0] == '\0') {
632                 alertpanel_error(_("Header name is not set."));
633                 return -1;
634         }
635
636         flt = g_new0(Filter, 1);
637
638         flt->name1 = g_strdup(entry_text);
639
640         GET_ENTRY(filter.key_entry1);
641         if (entry_text[0] != '\0')
642                 flt->body1 = g_strdup(entry_text);
643
644         GET_ENTRY(filter.hdr_entry2);
645         if (entry_text[0] != '\0' && strcmp(entry_text, _("(none)")) != 0) {
646                 flt->name2 = g_strdup(entry_text);
647
648                 GET_ENTRY(filter.key_entry2);
649                 if (entry_text[0] != '\0')
650                         flt->body2 = g_strdup(entry_text);
651         }
652
653         if (gtk_toggle_button_get_active
654                 (GTK_TOGGLE_BUTTON(filter.regex_chkbtn)))
655                 flt->flag1 = flt->flag2 = FLT_REGEX;
656
657         GET_ENTRY(filter.pred_entry1);
658         if (!strcmp(entry_text, _("contains")))
659                 flt->flag1 |= FLT_CONTAIN;
660         GET_ENTRY(filter.pred_entry2);
661         if (!strcmp(entry_text, _("contains")))
662                 flt->flag2 |= FLT_CONTAIN;
663
664         GET_ENTRY(filter.op_entry);
665         if (!strcmp(entry_text, "and"))
666                 flt->cond = FLT_AND;
667         else
668                 flt->cond = FLT_OR;
669
670         if (GTK_WIDGET_IS_SENSITIVE(filter.dest_entry)) {
671                 entry_text = gtk_entry_get_text(GTK_ENTRY(filter.dest_entry));
672                 flt->dest = g_strdup(entry_text);
673                 flt->action = FLT_MOVE;
674         } else
675                 flt->action = FLT_NOTRECV;
676
677         cond_str[0] = filter_get_str(flt);
678         subst_char(cond_str[0], '\t', ':');
679
680         if (row < 0)
681                 row = gtk_clist_append(clist, cond_str);
682         else {
683                 Filter *tmpflt;
684
685                 gtk_clist_set_text(clist, row, 0, cond_str[0]);
686                 tmpflt = gtk_clist_get_row_data(clist, row);
687                 if (tmpflt)
688                         filter_free(tmpflt);
689         }
690
691         gtk_clist_set_row_data(clist, row, flt);
692
693         g_free(cond_str[0]);
694
695         prefs_filter_set_list();
696
697         return row;
698 }
699
700 static void prefs_filter_select_dest_cb(void)
701 {
702         FolderItem *dest;
703
704         dest = foldersel_folder_sel(NULL, NULL);
705         if (!dest) return;
706
707         gtk_entry_set_text(GTK_ENTRY(filter.dest_entry), dest->path);
708 }
709
710 static void prefs_filter_register_cb(void)
711 {
712         prefs_filter_clist_set_row(-1);
713 }
714
715 static void prefs_filter_substitute_cb(void)
716 {
717         GtkCList *clist = GTK_CLIST(filter.cond_clist);
718         Filter *flt;
719         gint row;
720
721         if (!clist->selection) return;
722
723         row = GPOINTER_TO_INT(clist->selection->data);
724         if (row == 0) return;
725
726         flt = gtk_clist_get_row_data(clist, row);
727         if (!flt) return;
728
729         prefs_filter_clist_set_row(row);
730 }
731
732 static void prefs_filter_delete_cb(void)
733 {
734         GtkCList *clist = GTK_CLIST(filter.cond_clist);
735         Filter *flt;
736         gint row;
737
738         if (!clist->selection) return;
739         row = GPOINTER_TO_INT(clist->selection->data);
740         if (row == 0) return;
741
742         if (alertpanel(_("Delete rule"),
743                        _("Do you really want to delete this rule?"),
744                        _("Yes"), _("No"), NULL) == G_ALERTALTERNATE)
745                 return;
746
747         flt = gtk_clist_get_row_data(clist, row);
748         filter_free(flt);
749         gtk_clist_remove(clist, row);
750         prefs_common.fltlist = g_slist_remove(prefs_common.fltlist, flt);
751 }
752
753 static void prefs_filter_up(void)
754 {
755         GtkCList *clist = GTK_CLIST(filter.cond_clist);
756         gint row;
757
758         if (!clist->selection) return;
759
760         row = GPOINTER_TO_INT(clist->selection->data);
761         if (row > 1)
762                 gtk_clist_row_move(clist, row, row - 1);
763 }
764
765 static void prefs_filter_down(void)
766 {
767         GtkCList *clist = GTK_CLIST(filter.cond_clist);
768         gint row;
769
770         if (!clist->selection) return;
771
772         row = GPOINTER_TO_INT(clist->selection->data);
773         if (row > 0 && row < clist->rows - 1)
774                 gtk_clist_row_move(clist, row, row + 1);
775 }
776
777 #define ENTRY_SET_TEXT(entry, str) \
778         gtk_entry_set_text(GTK_ENTRY(entry), str ? str : "")
779
780 static void prefs_filter_select(GtkCList *clist, gint row, gint column,
781                                 GdkEvent *event)
782 {
783         Filter *flt;
784         Filter default_flt = {"Subject", NULL, _("(none)"), NULL,
785                               FLT_CONTAIN, FLT_CONTAIN, FLT_AND,
786                               NULL, FLT_MOVE};
787         gboolean is_regex;
788
789         flt = gtk_clist_get_row_data(clist, row);
790         if (!flt)
791                 flt = &default_flt;
792
793         ENTRY_SET_TEXT(filter.dest_entry, flt->dest);
794         ENTRY_SET_TEXT(filter.hdr_entry1, flt->name1);
795         ENTRY_SET_TEXT(filter.key_entry1, flt->body1);
796         ENTRY_SET_TEXT(filter.hdr_entry2,
797                        flt->name2 ? flt->name2 : _("(none)"));
798         ENTRY_SET_TEXT(filter.key_entry2, flt->body2);
799
800         ENTRY_SET_TEXT(filter.pred_entry1,
801                        FLT_IS_CONTAIN(flt->flag1)
802                        ? _("contains") : _("not contain"));
803         ENTRY_SET_TEXT(filter.pred_entry2,
804                        FLT_IS_CONTAIN(flt->flag2)
805                        ? _("contains") : _("not contain"));
806
807         is_regex = FLT_IS_REGEX(flt->flag1);
808         gtk_widget_set_sensitive(filter.pred_combo1, !is_regex);
809         gtk_widget_set_sensitive(filter.pred_combo2, !is_regex);
810         gtk_toggle_button_set_active
811                 (GTK_TOGGLE_BUTTON(filter.regex_chkbtn), is_regex);
812
813         gtk_entry_set_text(GTK_ENTRY(filter.op_entry),
814                            flt->cond == FLT_OR ? "or" : "and");
815         if (flt->action == FLT_NOTRECV)
816                 gtk_toggle_button_set_active
817                         (GTK_TOGGLE_BUTTON(filter.notrecv_radiobtn), TRUE);
818         else
819                 gtk_toggle_button_set_active
820                         (GTK_TOGGLE_BUTTON(filter.dest_radiobtn), TRUE);
821 }
822
823 static void prefs_filter_row_move(GtkCList *clist, gint source_row,
824                                   gint dest_row)
825 {
826         prefs_filter_set_list();
827         if (gtk_clist_row_is_visible(clist, dest_row) != GTK_VISIBILITY_FULL) {
828                 gtk_clist_moveto(clist, dest_row, -1,
829                                  source_row < dest_row ? 1.0 : 0.0, 0.0);
830         }
831 }
832
833 static void prefs_filter_dest_radio_button_toggled(void)
834 {
835         gtk_widget_set_sensitive(filter.dest_entry, TRUE);
836         gtk_widget_set_sensitive(filter.destsel_btn, TRUE);
837 }
838
839 static void prefs_filter_notrecv_radio_button_toggled(void)
840 {
841         gtk_widget_set_sensitive(filter.dest_entry, FALSE);
842         gtk_widget_set_sensitive(filter.destsel_btn, FALSE);
843 }
844
845 static void prefs_filter_regex_check_button_toggled(void)
846 {
847         gboolean is_regex;
848
849         is_regex = gtk_toggle_button_get_active
850                 (GTK_TOGGLE_BUTTON(filter.regex_chkbtn));
851         gtk_widget_set_sensitive(filter.pred_combo1, !is_regex);
852         gtk_widget_set_sensitive(filter.pred_combo2, !is_regex);
853 }
854
855 static gint prefs_filter_deleted(GtkWidget *widget, GdkEventAny *event,
856                                  gpointer data)
857 {
858         prefs_filter_cancel();
859         return TRUE;
860 }
861
862 static void prefs_filter_key_pressed(GtkWidget *widget, GdkEventKey *event,
863                                      gpointer data)
864 {
865         if (event && event->keyval == GDK_Escape)
866                 prefs_filter_cancel();
867 }
868
869 static void prefs_filter_ok(void)
870 {
871         prefs_filter_write_config();
872         gtk_widget_hide(filter.window);
873         inc_autocheck_timer_set();      
874 }
875
876 static void prefs_filter_cancel(void)
877 {
878         prefs_filter_read_config();
879         gtk_widget_hide(filter.window);
880         inc_autocheck_timer_set();      
881 }