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