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