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