sync with sylpheed 0.7.0cvs12
[claws.git] / src / prefs_filter.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2002 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <gtk/gtk.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33
34 #include "intl.h"
35 #include "main.h"
36 #include "prefs.h"
37 #include "prefs_filter.h"
38 #include "prefs_common.h"
39 #include "mainwindow.h"
40 #include "foldersel.h"
41 #include "manage_window.h"
42 #include "inc.h"
43 #include "filter.h"
44 #include "utils.h"
45 #include "gtkutils.h"
46 #include "alertpanel.h"
47 #include "folder.h"
48
49 static struct Filter {
50         GtkWidget *window;
51
52         GtkWidget *ok_btn;
53
54         GtkWidget *hdr_combo1;
55         GtkWidget *hdr_combo2;
56         GtkWidget *hdr_entry1;
57         GtkWidget *hdr_entry2;
58         GtkWidget *key_entry1;
59         GtkWidget *key_entry2;
60         GtkWidget *pred_combo1;
61         GtkWidget *pred_combo2;
62         GtkWidget *pred_entry1;
63         GtkWidget *pred_entry2;
64         GtkWidget *cond_combo;
65         GtkWidget *cond_entry;
66
67         GtkWidget *dest_entry;
68         GtkWidget *regex_chkbtn;
69
70         GtkWidget *destsel_btn;
71         GtkWidget *dest_radiobtn;
72         GtkWidget *notrecv_radiobtn;
73
74         GtkWidget *cond_clist;
75 } filter;
76
77 /*
78    parameter name, default value, pointer to the prefs variable, data type,
79    pointer to the widget pointer,
80    pointer to the function for data setting,
81    pointer to the function for widget setting
82  */
83
84 /* widget creating functions */
85 static void prefs_filter_create         (void);
86
87 static void prefs_filter_set_dialog     (const gchar    *header,
88                                          const gchar    *key);
89 static void prefs_filter_set_list       (void);
90 static gint prefs_filter_clist_set_row  (gint    row);
91
92 /* callback functions */
93 static void prefs_filter_select_dest_cb (void);
94 static void prefs_filter_register_cb    (void);
95 static void prefs_filter_substitute_cb  (void);
96 static void prefs_filter_delete_cb      (void);
97 static void prefs_filter_up             (void);
98 static void prefs_filter_down           (void);
99 static void prefs_filter_select         (GtkCList       *clist,
100                                          gint            row,
101                                          gint            column,
102                                          GdkEvent       *event);
103 static void prefs_filter_row_move       (GtkCList       *clist,
104                                          gint            source_row,
105                                          gint            dest_row);
106
107 static void prefs_filter_dest_radio_button_toggled      (void);
108 static void prefs_filter_notrecv_radio_button_toggled   (void);
109 static void prefs_filter_regex_check_button_toggled     (void);
110
111 static gint prefs_filter_deleted        (GtkWidget      *widget,
112                                          GdkEventAny    *event,
113                                          gpointer        data);
114 static void prefs_filter_key_pressed    (GtkWidget      *widget,
115                                          GdkEventKey    *event,
116                                          gpointer        data);
117 static void prefs_filter_cancel         (void);
118 static void prefs_filter_ok             (void);
119
120 void prefs_filter_open(const gchar *header, const gchar *key)
121 {
122         if (prefs_rc_is_readonly(FILTER_RC))
123                 return;
124
125         inc_lock();
126
127         if (!filter.window) {
128                 prefs_filter_create();
129         }
130
131         manage_window_set_transient(GTK_WINDOW(filter.window));
132         gtk_widget_grab_focus(filter.ok_btn);
133
134         prefs_filter_set_dialog(header, key);
135
136         gtk_widget_show(filter.window);
137 }
138
139 static void prefs_filter_create(void)
140 {
141         GtkWidget *window;
142         GtkWidget *vbox;
143         GtkWidget *ok_btn;
144         GtkWidget *cancel_btn;
145         GtkWidget *confirm_area;
146
147         GtkWidget *vbox1;
148         GtkWidget *table1;
149         GtkWidget *cond_label;
150         GtkWidget *cond_combo;
151         GtkWidget *cond_entry;
152         GtkWidget *hdr_label;
153         GtkWidget *hdr_combo1;
154         GtkWidget *hdr_combo2;
155         GtkWidget *key_label;
156         GtkWidget *key_entry1;
157         GtkWidget *key_entry2;
158         GtkWidget *pred_label;
159         GtkWidget *pred_combo1;
160         GtkWidget *pred_entry1;
161         GtkWidget *pred_combo2;
162         GtkWidget *pred_entry2;
163
164         GtkWidget *vbox2;
165         GtkWidget *dest_hbox;
166         GtkWidget *dest_entry;
167         GtkWidget *destsel_btn;
168         GtkWidget *dest_radiobtn;
169         GtkWidget *notrecv_radiobtn;
170         GSList *recv_group = NULL;
171
172         GtkWidget *regex_chkbtn;
173
174         GtkWidget *reg_hbox;
175         GtkWidget *btn_hbox;
176         GtkWidget *arrow;
177         GtkWidget *reg_btn;
178         GtkWidget *subst_btn;
179         GtkWidget *del_btn;
180
181         GtkWidget *cond_hbox;
182         GtkWidget *cond_scrolledwin;
183         GtkWidget *cond_clist;
184
185         GtkWidget *btn_vbox;
186         GtkWidget *up_btn;
187         GtkWidget *down_btn;
188
189         gchar *title[] = {_("Registered rules")};
190
191         debug_print(_("Creating filter setting window...\n"));
192
193         window = gtk_window_new (GTK_WINDOW_DIALOG);
194         gtk_container_set_border_width (GTK_CONTAINER (window), 8);
195         gtk_window_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
196         gtk_window_set_modal (GTK_WINDOW (window), TRUE);
197         gtk_window_set_policy (GTK_WINDOW (window), FALSE, TRUE, FALSE);
198
199         vbox = gtk_vbox_new (FALSE, 6);
200         gtk_widget_show (vbox);
201         gtk_container_add (GTK_CONTAINER (window), vbox);
202
203         gtkut_button_set_create(&confirm_area, &ok_btn, _("OK"),
204                                 &cancel_btn, _("Cancel"), NULL, NULL);
205         gtk_widget_show (confirm_area);
206         gtk_box_pack_end (GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
207         gtk_widget_grab_default (ok_btn);
208
209         /*
210         gtkut_button_set_create (&confirm_area, &close_btn, _("Close"),
211                                  NULL, NULL, NULL, NULL);
212         gtk_widget_show (confirm_area);
213         gtk_box_pack_end (GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
214         gtk_widget_grab_default (close_btn);
215         */
216
217         gtk_window_set_title (GTK_WINDOW(window),
218                               _("Filter setting"));
219         gtk_signal_connect (GTK_OBJECT(window), "delete_event",
220                             GTK_SIGNAL_FUNC(prefs_filter_deleted), NULL);
221         gtk_signal_connect (GTK_OBJECT(window), "key_press_event",
222                             GTK_SIGNAL_FUNC(prefs_filter_key_pressed), NULL);
223         gtk_signal_connect (GTK_OBJECT(window), "focus_in_event",
224                             GTK_SIGNAL_FUNC(manage_window_focus_in), NULL);
225         gtk_signal_connect (GTK_OBJECT(window), "focus_out_event",
226                             GTK_SIGNAL_FUNC(manage_window_focus_out), NULL);
227         gtk_signal_connect (GTK_OBJECT(ok_btn), "clicked",
228                             GTK_SIGNAL_FUNC(prefs_filter_ok), NULL);
229         gtk_signal_connect (GTK_OBJECT(cancel_btn), "clicked",
230                             GTK_SIGNAL_FUNC(prefs_filter_cancel), NULL);
231
232         vbox1 = gtk_vbox_new (FALSE, VSPACING);
233         gtk_widget_show (vbox1);
234         gtk_box_pack_start (GTK_BOX (vbox), vbox1, TRUE, TRUE, 0);
235         gtk_container_set_border_width (GTK_CONTAINER (vbox1), 2);
236
237         table1 = gtk_table_new (3, 4, FALSE);
238         gtk_widget_show (table1);
239         gtk_box_pack_start (GTK_BOX (vbox1), table1, FALSE, FALSE, 0);
240         gtk_table_set_row_spacings (GTK_TABLE (table1), 8);
241         gtk_table_set_col_spacings (GTK_TABLE (table1), 8);
242
243         cond_label = gtk_label_new (_("Condition"));
244         gtk_widget_show (cond_label);
245         gtk_table_attach (GTK_TABLE (table1), cond_label, 0, 1, 0, 1,
246                           GTK_FILL, 0, 0, 0);
247         gtk_misc_set_alignment (GTK_MISC (cond_label), 0, 0.5);
248
249         cond_combo = gtk_combo_new ();
250         gtk_widget_show (cond_combo);
251         gtk_table_attach (GTK_TABLE (table1), cond_combo, 0, 1, 2, 3,
252                           0, 0, 0, 0);
253         gtk_widget_set_usize (cond_combo, 52, -1);
254         gtkut_combo_set_items (GTK_COMBO (cond_combo), "and", "or", NULL);
255
256         cond_entry = GTK_COMBO (cond_combo)->entry;
257         gtk_entry_set_editable (GTK_ENTRY (cond_entry), FALSE);
258
259         hdr_label = gtk_label_new (_("Header"));
260         gtk_widget_show (hdr_label);
261         gtk_table_attach (GTK_TABLE (table1), hdr_label, 1, 2, 0, 1,
262                           GTK_FILL, 0, 0, 0);
263         gtk_misc_set_alignment (GTK_MISC (hdr_label), 0, 0.5);
264
265         hdr_combo1 = gtk_combo_new ();
266         gtk_widget_show (hdr_combo1);
267         gtk_table_attach (GTK_TABLE (table1), hdr_combo1, 1, 2, 1, 2,
268                           0, 0, 0, 0);
269         gtk_widget_set_usize (hdr_combo1, 96, -1);
270         gtkut_combo_set_items (GTK_COMBO (hdr_combo1),
271                                "Subject", "From", "To", "Cc", "Reply-To",
272                                "Sender", "List-Id",
273                                "X-ML-Name", "X-List", "X-Sequence", "X-Mailer",
274                                NULL);
275
276         hdr_combo2 = gtk_combo_new ();
277         gtk_widget_show (hdr_combo2);
278         gtk_table_attach (GTK_TABLE (table1), hdr_combo2, 1, 2, 2, 3,
279                           0, 0, 0, 0);
280         gtk_widget_set_usize (hdr_combo2, 96, -1);
281         gtkut_combo_set_items (GTK_COMBO (hdr_combo2), _("(none)"),
282                                "Subject", "From", "To", "Cc", "Reply-To",
283                                "Sender", "List-Id",
284                                "X-ML-Name", "X-List", "X-Sequence", "X-Mailer",
285                                NULL);
286
287         key_label = gtk_label_new (_("Keyword"));
288         gtk_widget_show (key_label);
289         gtk_table_attach (GTK_TABLE (table1), key_label, 2, 3, 0, 1,
290                           GTK_EXPAND | GTK_SHRINK | GTK_FILL,
291                           0, 0, 0);
292         gtk_misc_set_alignment (GTK_MISC (key_label), 0, 0.5);
293
294         key_entry1 = gtk_entry_new ();
295         gtk_widget_show (key_entry1);
296         gtk_widget_set_usize (key_entry1, 128, -1);
297         gtk_table_attach (GTK_TABLE (table1), key_entry1, 2, 3, 1, 2,
298                           GTK_EXPAND | GTK_SHRINK | GTK_FILL,
299                           0, 0, 0);
300
301         key_entry2 = gtk_entry_new ();
302         gtk_widget_show (key_entry2);
303         gtk_widget_set_usize (key_entry2, 128, -1);
304         gtk_table_attach (GTK_TABLE (table1), key_entry2, 2, 3, 2, 3,
305                           GTK_EXPAND | GTK_SHRINK | GTK_FILL,
306                           0, 0, 0);
307
308         pred_label = gtk_label_new (_("Predicate"));
309         gtk_widget_show (pred_label);
310         gtk_table_attach (GTK_TABLE (table1), pred_label, 3, 4, 0, 1,
311                           GTK_FILL, 0, 0, 0);
312         gtk_misc_set_alignment (GTK_MISC (pred_label), 0, 0.5);
313
314         pred_combo1 = gtk_combo_new ();
315         gtk_widget_show (pred_combo1);
316         gtk_table_attach (GTK_TABLE (table1), pred_combo1, 3, 4, 1, 2,
317                           0, 0, 0, 0);
318         gtk_widget_set_usize (pred_combo1, 92, -1);
319         gtkut_combo_set_items (GTK_COMBO (pred_combo1),
320                                _("contains"), _("not contain"), NULL);
321
322         pred_entry1 = GTK_COMBO (pred_combo1)->entry;
323         gtk_entry_set_editable (GTK_ENTRY (pred_entry1), FALSE);
324
325         pred_combo2 = gtk_combo_new ();
326         gtk_widget_show (pred_combo2);
327         gtk_table_attach (GTK_TABLE (table1), pred_combo2, 3, 4, 2, 3,
328                           0, 0, 0, 0);
329         gtk_widget_set_usize (pred_combo2, 92, -1);
330         gtkut_combo_set_items (GTK_COMBO (pred_combo2),
331                                _("contains"), _("not contain"), NULL);
332
333         pred_entry2 = GTK_COMBO (pred_combo2)->entry;
334         gtk_entry_set_editable (GTK_ENTRY (pred_entry2), FALSE);
335
336         /* destination */
337
338         vbox2 = gtk_vbox_new (FALSE, VSPACING_NARROW);
339         gtk_widget_show (vbox2);
340         gtk_box_pack_start (GTK_BOX (vbox1), vbox2, FALSE, TRUE, 0);
341
342         dest_hbox = gtk_hbox_new (FALSE, 8);
343         gtk_widget_show (dest_hbox);
344         gtk_box_pack_start (GTK_BOX (vbox2), dest_hbox, FALSE, TRUE, 0);
345
346         dest_radiobtn =
347                 gtk_radio_button_new_with_label (recv_group, _("Destination"));
348         recv_group = gtk_radio_button_group (GTK_RADIO_BUTTON (dest_radiobtn));
349         gtk_widget_show (dest_radiobtn);
350         gtk_box_pack_start (GTK_BOX (dest_hbox), dest_radiobtn,
351                             FALSE, FALSE, 0);
352         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dest_radiobtn), TRUE);
353         gtk_signal_connect
354                 (GTK_OBJECT (dest_radiobtn), "toggled",
355                  GTK_SIGNAL_FUNC (prefs_filter_dest_radio_button_toggled),
356                  NULL);
357
358         dest_entry = gtk_entry_new ();
359         gtk_widget_show (dest_entry);
360         gtk_widget_set_usize (dest_entry, DEFAULT_ENTRY_WIDTH, -1);
361         gtk_box_pack_start (GTK_BOX (dest_hbox), dest_entry, TRUE, TRUE, 0);
362         gtk_entry_set_editable (GTK_ENTRY (dest_entry), FALSE);
363
364         destsel_btn = gtk_button_new_with_label (_(" Select... "));
365         gtk_widget_show (destsel_btn);
366         gtk_box_pack_start (GTK_BOX (dest_hbox), destsel_btn, FALSE, FALSE, 0);
367         gtk_signal_connect (GTK_OBJECT (destsel_btn), "clicked",
368                             GTK_SIGNAL_FUNC (prefs_filter_select_dest_cb),
369                             NULL);
370
371         PACK_CHECK_BUTTON (dest_hbox, regex_chkbtn, _("Use regex"));
372         gtk_signal_connect
373                 (GTK_OBJECT (regex_chkbtn), "toggled",
374                  GTK_SIGNAL_FUNC (prefs_filter_regex_check_button_toggled),
375                  NULL);
376
377         notrecv_radiobtn = gtk_radio_button_new_with_label
378                 (recv_group, _("Don't receive"));
379         recv_group = gtk_radio_button_group
380                 (GTK_RADIO_BUTTON (notrecv_radiobtn));
381         gtk_widget_show (notrecv_radiobtn);
382         gtk_box_pack_start (GTK_BOX (vbox2), notrecv_radiobtn, FALSE, FALSE, 0);
383         gtk_signal_connect
384                 (GTK_OBJECT (notrecv_radiobtn), "toggled",
385                  GTK_SIGNAL_FUNC (prefs_filter_notrecv_radio_button_toggled),
386                  NULL);
387
388         /* register / substitute / delete */
389
390         reg_hbox = gtk_hbox_new (FALSE, 4);
391         gtk_widget_show (reg_hbox);
392         gtk_box_pack_start (GTK_BOX (vbox1), reg_hbox, FALSE, FALSE, 0);
393
394         arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
395         gtk_widget_show (arrow);
396         gtk_box_pack_start (GTK_BOX (reg_hbox), arrow, FALSE, FALSE, 0);
397         gtk_widget_set_usize (arrow, -1, 16);
398
399         btn_hbox = gtk_hbox_new (TRUE, 4);
400         gtk_widget_show (btn_hbox);
401         gtk_box_pack_start (GTK_BOX (reg_hbox), btn_hbox, FALSE, FALSE, 0);
402
403         reg_btn = gtk_button_new_with_label (_("Register"));
404         gtk_widget_show (reg_btn);
405         gtk_box_pack_start (GTK_BOX (btn_hbox), reg_btn, FALSE, TRUE, 0);
406         gtk_signal_connect (GTK_OBJECT (reg_btn), "clicked",
407                             GTK_SIGNAL_FUNC (prefs_filter_register_cb), NULL);
408
409         subst_btn = gtk_button_new_with_label (_(" Substitute "));
410         gtk_widget_show (subst_btn);
411         gtk_box_pack_start (GTK_BOX (btn_hbox), subst_btn, FALSE, TRUE, 0);
412         gtk_signal_connect (GTK_OBJECT (subst_btn), "clicked",
413                             GTK_SIGNAL_FUNC (prefs_filter_substitute_cb),
414                             NULL);
415
416         del_btn = gtk_button_new_with_label (_("Delete"));
417         gtk_widget_show (del_btn);
418         gtk_box_pack_start (GTK_BOX (btn_hbox), del_btn, FALSE, TRUE, 0);
419         gtk_signal_connect (GTK_OBJECT (del_btn), "clicked",
420                             GTK_SIGNAL_FUNC (prefs_filter_delete_cb), NULL);
421
422         cond_hbox = gtk_hbox_new (FALSE, 8);
423         gtk_widget_show (cond_hbox);
424         gtk_box_pack_start (GTK_BOX (vbox1), cond_hbox, TRUE, TRUE, 0);
425
426         cond_scrolledwin = gtk_scrolled_window_new (NULL, NULL);
427         gtk_widget_show (cond_scrolledwin);
428         gtk_widget_set_usize (cond_scrolledwin, -1, 150);
429         gtk_box_pack_start (GTK_BOX (cond_hbox), cond_scrolledwin,
430                             TRUE, TRUE, 0);
431         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (cond_scrolledwin),
432                                         GTK_POLICY_AUTOMATIC,
433                                         GTK_POLICY_AUTOMATIC);
434
435         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.cond_combo  = cond_combo;
480         filter.cond_entry  = cond_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(const gchar *header, const gchar *key)
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         if (header && key) {
595                 gtk_entry_set_text(GTK_ENTRY(filter.hdr_entry1), header);
596                 gtk_entry_set_text(GTK_ENTRY(filter.key_entry1), key);
597         }
598 }
599
600 static void prefs_filter_set_list(void)
601 {
602         gint row = 1;
603         Filter *flt;
604
605         g_slist_free(prefs_common.fltlist);
606         prefs_common.fltlist = NULL;
607
608         while ((flt = gtk_clist_get_row_data(GTK_CLIST(filter.cond_clist),
609                 row)) != NULL) {
610                 prefs_common.fltlist = g_slist_append(prefs_common.fltlist,
611                                                       flt);
612                 row++;
613         }
614 }
615
616 #define GET_ENTRY(entry) \
617         entry_text = gtk_entry_get_text(GTK_ENTRY(entry))
618
619 static gint prefs_filter_clist_set_row(gint row)
620 {
621         GtkCList *clist = GTK_CLIST(filter.cond_clist);
622         Filter *flt;
623         gchar *entry_text;
624         gchar *cond_str[1];
625
626         g_return_val_if_fail(row != 0, -1);
627
628         if (GTK_WIDGET_IS_SENSITIVE(filter.dest_entry))
629                 GET_ENTRY(filter.dest_entry);
630         else
631                 entry_text = FILTER_NOT_RECEIVE;
632         if (entry_text[0] == '\0') {
633                 alertpanel_error(_("Destination is not set."));
634                 return -1;
635         }
636         GET_ENTRY(filter.hdr_entry1);
637         if (entry_text[0] == '\0') {
638                 alertpanel_error(_("Header name is not set."));
639                 return -1;
640         }
641
642         flt = g_new0(Filter, 1);
643
644         flt->name1 = g_strdup(entry_text);
645
646         GET_ENTRY(filter.key_entry1);
647         if (entry_text[0] != '\0')
648                 flt->body1 = g_strdup(entry_text);
649
650         GET_ENTRY(filter.hdr_entry2);
651         if (entry_text[0] != '\0' && strcmp(entry_text, _("(none)")) != 0) {
652                 flt->name2 = g_strdup(entry_text);
653
654                 GET_ENTRY(filter.key_entry2);
655                 if (entry_text[0] != '\0')
656                         flt->body2 = g_strdup(entry_text);
657         }
658
659         if (gtk_toggle_button_get_active
660                 (GTK_TOGGLE_BUTTON(filter.regex_chkbtn)))
661                 flt->flag1 = flt->flag2 = FLT_REGEX;
662
663         GET_ENTRY(filter.pred_entry1);
664         if (!strcmp(entry_text, _("contains")))
665                 flt->flag1 |= FLT_CONTAIN;
666         GET_ENTRY(filter.pred_entry2);
667         if (!strcmp(entry_text, _("contains")))
668                 flt->flag2 |= FLT_CONTAIN;
669
670         GET_ENTRY(filter.cond_entry);
671         if (!strcmp(entry_text, "and"))
672                 flt->cond = FLT_AND;
673         else
674                 flt->cond = FLT_OR;
675
676         if (GTK_WIDGET_IS_SENSITIVE(filter.dest_entry)) {
677                 entry_text = gtk_entry_get_text(GTK_ENTRY(filter.dest_entry));
678                 flt->dest = g_strdup(entry_text);
679                 flt->action = FLT_MOVE;
680         } else
681                 flt->action = FLT_NOTRECV;
682
683         cond_str[0] = filter_get_str(flt);
684         subst_char(cond_str[0], '\t', ':');
685
686         if (row < 0)
687                 row = gtk_clist_append(clist, cond_str);
688         else {
689                 Filter *tmpflt;
690
691                 gtk_clist_set_text(clist, row, 0, cond_str[0]);
692                 tmpflt = gtk_clist_get_row_data(clist, row);
693                 if (tmpflt)
694                         filter_free(tmpflt);
695         }
696
697         gtk_clist_set_row_data(clist, row, flt);
698
699         g_free(cond_str[0]);
700
701         prefs_filter_set_list();
702
703         return row;
704 }
705
706 static void prefs_filter_select_dest_cb(void)
707 {
708         FolderItem *dest;
709
710         dest = foldersel_folder_sel(NULL, NULL);
711         if (!dest) return;
712
713         gtk_entry_set_text(GTK_ENTRY(filter.dest_entry), dest->path);
714 }
715
716 static void prefs_filter_register_cb(void)
717 {
718         prefs_filter_clist_set_row(-1);
719 }
720
721 static void prefs_filter_substitute_cb(void)
722 {
723         GtkCList *clist = GTK_CLIST(filter.cond_clist);
724         Filter *flt;
725         gint row;
726
727         if (!clist->selection) return;
728
729         row = GPOINTER_TO_INT(clist->selection->data);
730         if (row == 0) return;
731
732         flt = gtk_clist_get_row_data(clist, row);
733         if (!flt) return;
734
735         prefs_filter_clist_set_row(row);
736 }
737
738 static void prefs_filter_delete_cb(void)
739 {
740         GtkCList *clist = GTK_CLIST(filter.cond_clist);
741         Filter *flt;
742         gint row;
743
744         if (!clist->selection) return;
745         row = GPOINTER_TO_INT(clist->selection->data);
746         if (row == 0) return;
747
748         if (alertpanel(_("Delete rule"),
749                        _("Do you really want to delete this rule?"),
750                        _("Yes"), _("No"), NULL) == G_ALERTALTERNATE)
751                 return;
752
753         flt = gtk_clist_get_row_data(clist, row);
754         filter_free(flt);
755         gtk_clist_remove(clist, row);
756         prefs_common.fltlist = g_slist_remove(prefs_common.fltlist, flt);
757 }
758
759 static void prefs_filter_up(void)
760 {
761         GtkCList *clist = GTK_CLIST(filter.cond_clist);
762         gint row;
763
764         if (!clist->selection) return;
765
766         row = GPOINTER_TO_INT(clist->selection->data);
767         if (row > 1)
768                 gtk_clist_row_move(clist, row, row - 1);
769 }
770
771 static void prefs_filter_down(void)
772 {
773         GtkCList *clist = GTK_CLIST(filter.cond_clist);
774         gint row;
775
776         if (!clist->selection) return;
777
778         row = GPOINTER_TO_INT(clist->selection->data);
779         if (row > 0 && row < clist->rows - 1)
780                 gtk_clist_row_move(clist, row, row + 1);
781 }
782
783 #define ENTRY_SET_TEXT(entry, str) \
784         gtk_entry_set_text(GTK_ENTRY(entry), str ? str : "")
785
786 static void prefs_filter_select(GtkCList *clist, gint row, gint column,
787                                 GdkEvent *event)
788 {
789         Filter *flt;
790         Filter default_flt = {"Subject", NULL, _("(none)"), NULL,
791                               FLT_CONTAIN, FLT_CONTAIN, FLT_AND,
792                               NULL, FLT_MOVE};
793         gboolean is_regex;
794
795         flt = gtk_clist_get_row_data(clist, row);
796         if (!flt)
797                 flt = &default_flt;
798
799         ENTRY_SET_TEXT(filter.dest_entry, flt->dest);
800         ENTRY_SET_TEXT(filter.hdr_entry1, flt->name1);
801         ENTRY_SET_TEXT(filter.key_entry1, flt->body1);
802         ENTRY_SET_TEXT(filter.hdr_entry2,
803                        flt->name2 ? flt->name2 : _("(none)"));
804         ENTRY_SET_TEXT(filter.key_entry2, flt->body2);
805
806         ENTRY_SET_TEXT(filter.pred_entry1,
807                        FLT_IS_CONTAIN(flt->flag1)
808                        ? _("contains") : _("not contain"));
809         ENTRY_SET_TEXT(filter.pred_entry2,
810                        FLT_IS_CONTAIN(flt->flag2)
811                        ? _("contains") : _("not contain"));
812
813         is_regex = FLT_IS_REGEX(flt->flag1);
814         gtk_widget_set_sensitive(filter.pred_combo1, !is_regex);
815         gtk_widget_set_sensitive(filter.pred_combo2, !is_regex);
816         gtk_toggle_button_set_active
817                 (GTK_TOGGLE_BUTTON(filter.regex_chkbtn), is_regex);
818
819         gtk_entry_set_text(GTK_ENTRY(filter.cond_entry),
820                            flt->cond == FLT_OR ? "or" : "and");
821         if (flt->action == FLT_NOTRECV)
822                 gtk_toggle_button_set_active
823                         (GTK_TOGGLE_BUTTON(filter.notrecv_radiobtn), TRUE);
824         else
825                 gtk_toggle_button_set_active
826                         (GTK_TOGGLE_BUTTON(filter.dest_radiobtn), TRUE);
827 }
828
829 static void prefs_filter_row_move(GtkCList *clist, gint source_row,
830                                   gint dest_row)
831 {
832         prefs_filter_set_list();
833         if (gtk_clist_row_is_visible(clist, dest_row) != GTK_VISIBILITY_FULL) {
834                 gtk_clist_moveto(clist, dest_row, -1,
835                                  source_row < dest_row ? 1.0 : 0.0, 0.0);
836         }
837 }
838
839 static void prefs_filter_dest_radio_button_toggled(void)
840 {
841         gtk_widget_set_sensitive(filter.dest_entry, TRUE);
842         gtk_widget_set_sensitive(filter.destsel_btn, TRUE);
843 }
844
845 static void prefs_filter_notrecv_radio_button_toggled(void)
846 {
847         gtk_widget_set_sensitive(filter.dest_entry, FALSE);
848         gtk_widget_set_sensitive(filter.destsel_btn, FALSE);
849 }
850
851 static void prefs_filter_regex_check_button_toggled(void)
852 {
853         gboolean is_regex;
854
855         is_regex = gtk_toggle_button_get_active
856                 (GTK_TOGGLE_BUTTON(filter.regex_chkbtn));
857         gtk_widget_set_sensitive(filter.pred_combo1, !is_regex);
858         gtk_widget_set_sensitive(filter.pred_combo2, !is_regex);
859 }
860
861 static gint prefs_filter_deleted(GtkWidget *widget, GdkEventAny *event,
862                                  gpointer data)
863 {
864         prefs_filter_cancel();
865         return TRUE;
866 }
867
868 static void prefs_filter_key_pressed(GtkWidget *widget, GdkEventKey *event,
869                                      gpointer data)
870 {
871         if (event && event->keyval == GDK_Escape)
872                 prefs_filter_cancel();
873 }
874
875 static void prefs_filter_ok(void)
876 {
877         prefs_filter_write_config();
878         gtk_widget_hide(filter.window);
879         inc_unlock();
880 }
881
882 static void prefs_filter_cancel(void)
883 {
884         prefs_filter_read_config();
885         gtk_widget_hide(filter.window);
886         inc_unlock();
887 }