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