sync with sylpheed 0.7.2cvs21
[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         MANAGE_WINDOW_SIGNALS_CONNECT (window);
224         gtk_signal_connect (GTK_OBJECT(ok_btn), "clicked",
225                             GTK_SIGNAL_FUNC(prefs_filter_ok), NULL);
226         gtk_signal_connect (GTK_OBJECT(cancel_btn), "clicked",
227                             GTK_SIGNAL_FUNC(prefs_filter_cancel), NULL);
228
229         vbox1 = gtk_vbox_new (FALSE, VSPACING);
230         gtk_widget_show (vbox1);
231         gtk_box_pack_start (GTK_BOX (vbox), vbox1, TRUE, TRUE, 0);
232         gtk_container_set_border_width (GTK_CONTAINER (vbox1), 2);
233
234         table1 = gtk_table_new (3, 4, FALSE);
235         gtk_widget_show (table1);
236         gtk_box_pack_start (GTK_BOX (vbox1), table1, FALSE, FALSE, 0);
237         gtk_table_set_row_spacings (GTK_TABLE (table1), 8);
238         gtk_table_set_col_spacings (GTK_TABLE (table1), 8);
239
240         cond_label = gtk_label_new (_("Condition"));
241         gtk_widget_show (cond_label);
242         gtk_table_attach (GTK_TABLE (table1), cond_label, 0, 1, 0, 1,
243                           GTK_FILL, 0, 0, 0);
244         gtk_misc_set_alignment (GTK_MISC (cond_label), 0, 0.5);
245
246         cond_combo = gtk_combo_new ();
247         gtk_widget_show (cond_combo);
248         gtk_table_attach (GTK_TABLE (table1), cond_combo, 0, 1, 2, 3,
249                           0, 0, 0, 0);
250         gtk_widget_set_usize (cond_combo, 52, -1);
251         gtkut_combo_set_items (GTK_COMBO (cond_combo), "and", "or", NULL);
252
253         cond_entry = GTK_COMBO (cond_combo)->entry;
254         gtk_entry_set_editable (GTK_ENTRY (cond_entry), FALSE);
255
256         hdr_label = gtk_label_new (_("Header"));
257         gtk_widget_show (hdr_label);
258         gtk_table_attach (GTK_TABLE (table1), hdr_label, 1, 2, 0, 1,
259                           GTK_FILL, 0, 0, 0);
260         gtk_misc_set_alignment (GTK_MISC (hdr_label), 0, 0.5);
261
262         hdr_combo1 = gtk_combo_new ();
263         gtk_widget_show (hdr_combo1);
264         gtk_table_attach (GTK_TABLE (table1), hdr_combo1, 1, 2, 1, 2,
265                           0, 0, 0, 0);
266         gtk_widget_set_usize (hdr_combo1, 96, -1);
267         gtkut_combo_set_items (GTK_COMBO (hdr_combo1),
268                                "Subject", "From", "To", "Cc", "Reply-To",
269                                "Sender", "List-Id",
270                                "X-ML-Name", "X-List", "X-Sequence", "X-Mailer",
271                                NULL);
272
273         hdr_combo2 = gtk_combo_new ();
274         gtk_widget_show (hdr_combo2);
275         gtk_table_attach (GTK_TABLE (table1), hdr_combo2, 1, 2, 2, 3,
276                           0, 0, 0, 0);
277         gtk_widget_set_usize (hdr_combo2, 96, -1);
278         gtkut_combo_set_items (GTK_COMBO (hdr_combo2), _("(none)"),
279                                "Subject", "From", "To", "Cc", "Reply-To",
280                                "Sender", "List-Id",
281                                "X-ML-Name", "X-List", "X-Sequence", "X-Mailer",
282                                NULL);
283
284         key_label = gtk_label_new (_("Keyword"));
285         gtk_widget_show (key_label);
286         gtk_table_attach (GTK_TABLE (table1), key_label, 2, 3, 0, 1,
287                           GTK_EXPAND | GTK_SHRINK | GTK_FILL,
288                           0, 0, 0);
289         gtk_misc_set_alignment (GTK_MISC (key_label), 0, 0.5);
290
291         key_entry1 = gtk_entry_new ();
292         gtk_widget_show (key_entry1);
293         gtk_widget_set_usize (key_entry1, 128, -1);
294         gtk_table_attach (GTK_TABLE (table1), key_entry1, 2, 3, 1, 2,
295                           GTK_EXPAND | GTK_SHRINK | GTK_FILL,
296                           0, 0, 0);
297
298         key_entry2 = gtk_entry_new ();
299         gtk_widget_show (key_entry2);
300         gtk_widget_set_usize (key_entry2, 128, -1);
301         gtk_table_attach (GTK_TABLE (table1), key_entry2, 2, 3, 2, 3,
302                           GTK_EXPAND | GTK_SHRINK | GTK_FILL,
303                           0, 0, 0);
304
305         pred_label = gtk_label_new (_("Predicate"));
306         gtk_widget_show (pred_label);
307         gtk_table_attach (GTK_TABLE (table1), pred_label, 3, 4, 0, 1,
308                           GTK_FILL, 0, 0, 0);
309         gtk_misc_set_alignment (GTK_MISC (pred_label), 0, 0.5);
310
311         pred_combo1 = gtk_combo_new ();
312         gtk_widget_show (pred_combo1);
313         gtk_table_attach (GTK_TABLE (table1), pred_combo1, 3, 4, 1, 2,
314                           0, 0, 0, 0);
315         gtk_widget_set_usize (pred_combo1, 92, -1);
316         gtkut_combo_set_items (GTK_COMBO (pred_combo1),
317                                _("contains"), _("not contain"), NULL);
318
319         pred_entry1 = GTK_COMBO (pred_combo1)->entry;
320         gtk_entry_set_editable (GTK_ENTRY (pred_entry1), FALSE);
321
322         pred_combo2 = gtk_combo_new ();
323         gtk_widget_show (pred_combo2);
324         gtk_table_attach (GTK_TABLE (table1), pred_combo2, 3, 4, 2, 3,
325                           0, 0, 0, 0);
326         gtk_widget_set_usize (pred_combo2, 92, -1);
327         gtkut_combo_set_items (GTK_COMBO (pred_combo2),
328                                _("contains"), _("not contain"), NULL);
329
330         pred_entry2 = GTK_COMBO (pred_combo2)->entry;
331         gtk_entry_set_editable (GTK_ENTRY (pred_entry2), FALSE);
332
333         /* destination */
334
335         vbox2 = gtk_vbox_new (FALSE, VSPACING_NARROW);
336         gtk_widget_show (vbox2);
337         gtk_box_pack_start (GTK_BOX (vbox1), vbox2, FALSE, TRUE, 0);
338
339         dest_hbox = gtk_hbox_new (FALSE, 8);
340         gtk_widget_show (dest_hbox);
341         gtk_box_pack_start (GTK_BOX (vbox2), dest_hbox, FALSE, TRUE, 0);
342
343         dest_radiobtn =
344                 gtk_radio_button_new_with_label (recv_group, _("Destination"));
345         recv_group = gtk_radio_button_group (GTK_RADIO_BUTTON (dest_radiobtn));
346         gtk_widget_show (dest_radiobtn);
347         gtk_box_pack_start (GTK_BOX (dest_hbox), dest_radiobtn,
348                             FALSE, FALSE, 0);
349         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dest_radiobtn), TRUE);
350         gtk_signal_connect
351                 (GTK_OBJECT (dest_radiobtn), "toggled",
352                  GTK_SIGNAL_FUNC (prefs_filter_dest_radio_button_toggled),
353                  NULL);
354
355         dest_entry = gtk_entry_new ();
356         gtk_widget_show (dest_entry);
357         gtk_widget_set_usize (dest_entry, DEFAULT_ENTRY_WIDTH, -1);
358         gtk_box_pack_start (GTK_BOX (dest_hbox), dest_entry, TRUE, TRUE, 0);
359         gtk_entry_set_editable (GTK_ENTRY (dest_entry), FALSE);
360
361         destsel_btn = gtk_button_new_with_label (_(" Select... "));
362         gtk_widget_show (destsel_btn);
363         gtk_box_pack_start (GTK_BOX (dest_hbox), destsel_btn, FALSE, FALSE, 0);
364         gtk_signal_connect (GTK_OBJECT (destsel_btn), "clicked",
365                             GTK_SIGNAL_FUNC (prefs_filter_select_dest_cb),
366                             NULL);
367
368         PACK_CHECK_BUTTON (dest_hbox, regex_chkbtn, _("Use regex"));
369         gtk_signal_connect
370                 (GTK_OBJECT (regex_chkbtn), "toggled",
371                  GTK_SIGNAL_FUNC (prefs_filter_regex_check_button_toggled),
372                  NULL);
373
374         notrecv_radiobtn = gtk_radio_button_new_with_label
375                 (recv_group, _("Don't receive"));
376         recv_group = gtk_radio_button_group
377                 (GTK_RADIO_BUTTON (notrecv_radiobtn));
378         gtk_widget_show (notrecv_radiobtn);
379         gtk_box_pack_start (GTK_BOX (vbox2), notrecv_radiobtn, FALSE, FALSE, 0);
380         gtk_signal_connect
381                 (GTK_OBJECT (notrecv_radiobtn), "toggled",
382                  GTK_SIGNAL_FUNC (prefs_filter_notrecv_radio_button_toggled),
383                  NULL);
384
385         /* register / substitute / delete */
386
387         reg_hbox = gtk_hbox_new (FALSE, 4);
388         gtk_widget_show (reg_hbox);
389         gtk_box_pack_start (GTK_BOX (vbox1), reg_hbox, FALSE, FALSE, 0);
390
391         arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
392         gtk_widget_show (arrow);
393         gtk_box_pack_start (GTK_BOX (reg_hbox), arrow, FALSE, FALSE, 0);
394         gtk_widget_set_usize (arrow, -1, 16);
395
396         btn_hbox = gtk_hbox_new (TRUE, 4);
397         gtk_widget_show (btn_hbox);
398         gtk_box_pack_start (GTK_BOX (reg_hbox), btn_hbox, FALSE, FALSE, 0);
399
400         reg_btn = gtk_button_new_with_label (_("Register"));
401         gtk_widget_show (reg_btn);
402         gtk_box_pack_start (GTK_BOX (btn_hbox), reg_btn, FALSE, TRUE, 0);
403         gtk_signal_connect (GTK_OBJECT (reg_btn), "clicked",
404                             GTK_SIGNAL_FUNC (prefs_filter_register_cb), NULL);
405
406         subst_btn = gtk_button_new_with_label (_(" Substitute "));
407         gtk_widget_show (subst_btn);
408         gtk_box_pack_start (GTK_BOX (btn_hbox), subst_btn, FALSE, TRUE, 0);
409         gtk_signal_connect (GTK_OBJECT (subst_btn), "clicked",
410                             GTK_SIGNAL_FUNC (prefs_filter_substitute_cb),
411                             NULL);
412
413         del_btn = gtk_button_new_with_label (_("Delete"));
414         gtk_widget_show (del_btn);
415         gtk_box_pack_start (GTK_BOX (btn_hbox), del_btn, FALSE, TRUE, 0);
416         gtk_signal_connect (GTK_OBJECT (del_btn), "clicked",
417                             GTK_SIGNAL_FUNC (prefs_filter_delete_cb), NULL);
418
419         cond_hbox = gtk_hbox_new (FALSE, 8);
420         gtk_widget_show (cond_hbox);
421         gtk_box_pack_start (GTK_BOX (vbox1), cond_hbox, TRUE, TRUE, 0);
422
423         cond_scrolledwin = gtk_scrolled_window_new (NULL, NULL);
424         gtk_widget_show (cond_scrolledwin);
425         gtk_widget_set_usize (cond_scrolledwin, -1, 150);
426         gtk_box_pack_start (GTK_BOX (cond_hbox), cond_scrolledwin,
427                             TRUE, TRUE, 0);
428         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (cond_scrolledwin),
429                                         GTK_POLICY_AUTOMATIC,
430                                         GTK_POLICY_AUTOMATIC);
431
432         title[0] = _("Registered rules");
433         cond_clist = gtk_clist_new_with_titles(1, title);
434         gtk_widget_show (cond_clist);
435         gtk_container_add (GTK_CONTAINER (cond_scrolledwin), cond_clist);
436         gtk_clist_set_column_width (GTK_CLIST (cond_clist), 0, 80);
437         gtk_clist_set_selection_mode (GTK_CLIST (cond_clist),
438                                       GTK_SELECTION_BROWSE);
439         GTK_WIDGET_UNSET_FLAGS (GTK_CLIST (cond_clist)->column[0].button,
440                                 GTK_CAN_FOCUS);
441         gtk_signal_connect (GTK_OBJECT (cond_clist), "select_row",
442                             GTK_SIGNAL_FUNC (prefs_filter_select), NULL);
443         gtk_signal_connect_after (GTK_OBJECT (cond_clist), "row_move",
444                                   GTK_SIGNAL_FUNC (prefs_filter_row_move),
445                                   NULL);
446
447         btn_vbox = gtk_vbox_new (FALSE, 8);
448         gtk_widget_show (btn_vbox);
449         gtk_box_pack_start (GTK_BOX (cond_hbox), btn_vbox, FALSE, FALSE, 0);
450
451         up_btn = gtk_button_new_with_label (_("Up"));
452         gtk_widget_show (up_btn);
453         gtk_box_pack_start (GTK_BOX (btn_vbox), up_btn, FALSE, FALSE, 0);
454         gtk_signal_connect (GTK_OBJECT (up_btn), "clicked",
455                             GTK_SIGNAL_FUNC (prefs_filter_up), NULL);
456
457         down_btn = gtk_button_new_with_label (_("Down"));
458         gtk_widget_show (down_btn);
459         gtk_box_pack_start (GTK_BOX (btn_vbox), down_btn, FALSE, FALSE, 0);
460         gtk_signal_connect (GTK_OBJECT (down_btn), "clicked",
461                             GTK_SIGNAL_FUNC (prefs_filter_down), NULL);
462
463         gtk_widget_show_all(window);
464
465         filter.window    = window;
466         filter.ok_btn = ok_btn;
467
468         filter.hdr_combo1  = hdr_combo1;
469         filter.hdr_combo2  = hdr_combo2;
470         filter.hdr_entry1  = GTK_COMBO (hdr_combo1)->entry;
471         filter.hdr_entry2  = GTK_COMBO (hdr_combo2)->entry;
472         filter.key_entry1  = key_entry1;
473         filter.key_entry2  = key_entry2;
474         filter.pred_combo1 = pred_combo1;
475         filter.pred_combo2 = pred_combo2;
476         filter.pred_entry1 = pred_entry1;
477         filter.pred_entry2 = pred_entry2;
478         filter.cond_combo  = cond_combo;
479         filter.cond_entry  = cond_entry;
480
481         filter.dest_entry       = dest_entry;
482         filter.destsel_btn      = destsel_btn;
483         filter.dest_radiobtn    = dest_radiobtn;
484         filter.notrecv_radiobtn = notrecv_radiobtn;
485         filter.regex_chkbtn     = regex_chkbtn;
486
487         filter.cond_clist   = cond_clist;
488 }
489
490 void prefs_filter_read_config(void)
491 {
492         gchar *rcpath;
493         FILE *fp;
494         gchar buf[PREFSBUFSIZE];
495         Filter *flt;
496
497         debug_print(_("Reading filter configuration...\n"));
498
499         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, FILTER_RC, NULL);
500         if ((fp = fopen(rcpath, "r")) == NULL) {
501                 if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen");
502                 g_free(rcpath);
503                 return;
504         }
505         g_free(rcpath);
506
507         /* remove all previous filter list */
508         while (prefs_common.fltlist != NULL) {
509                 flt = (Filter *)prefs_common.fltlist->data;
510                 filter_free(flt);
511                 prefs_common.fltlist = g_slist_remove(prefs_common.fltlist,
512                                                       flt);
513         }
514
515         while (fgets(buf, sizeof(buf), fp) != NULL) {
516                 g_strchomp(buf);
517                 flt = filter_read_str(buf);
518                 if (flt) {
519                         prefs_common.fltlist =
520                                 g_slist_append(prefs_common.fltlist, flt);
521                 }
522         }
523
524         fclose(fp);
525 }
526
527 void prefs_filter_write_config(void)
528 {
529         gchar *rcpath;
530         PrefFile *pfile;
531         GSList *cur;
532
533         debug_print(_("Writing filter configuration...\n"));
534
535         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, FILTER_RC, NULL);
536         if ((pfile = prefs_write_open(rcpath)) == NULL) {
537                 g_warning(_("failed to write configuration to file\n"));
538                 g_free(rcpath);
539                 return;
540         }
541
542         for (cur = prefs_common.fltlist; cur != NULL; cur = cur->next) {
543                 Filter *flt = (Filter *)cur->data;
544                 gchar *fstr;
545
546                 fstr = filter_get_str(flt);
547                 if (fputs(fstr, pfile->fp) == EOF ||
548                     fputc('\n', pfile->fp) == EOF) {
549                         FILE_OP_ERROR(rcpath, "fputs || fputc");
550                         prefs_write_close_revert(pfile);
551                         g_free(rcpath);
552                         g_free(fstr);
553                         return;
554                 }
555                 g_free(fstr);
556         }
557
558         g_free(rcpath);
559
560         if (prefs_write_close(pfile) < 0) {
561                 g_warning(_("failed to write configuration to file\n"));
562                 return;
563         }
564 }
565
566 void prefs_filter_rename_path(const gchar *old_path, const gchar *new_path)
567 {
568         GSList *cur;
569         gchar *base;
570         gchar *dest_path;
571         gint oldpathlen;
572
573         g_return_if_fail(old_path != NULL);
574         g_return_if_fail(new_path != NULL);
575
576         for (cur = prefs_common.fltlist; cur != NULL; cur = cur->next) {
577                 Filter *flt = (Filter *)cur->data;
578
579                 oldpathlen = strlen(old_path);
580                 if (flt->dest && !strncmp(old_path, flt->dest, oldpathlen)) {
581                         base = flt->dest + oldpathlen;
582                         while (*base == G_DIR_SEPARATOR) base++;
583                         if (*base == '\0')
584                                 dest_path = g_strdup(new_path);
585                         else
586                                 dest_path = g_strconcat(new_path,
587                                                         G_DIR_SEPARATOR_S,
588                                                         base, NULL);
589                         g_free(flt->dest);
590                         flt->dest = dest_path;
591                 }
592         }
593
594         prefs_filter_write_config();
595 }
596
597 void prefs_filter_delete_path(const gchar *path)
598 {
599         GSList *cur;
600         GSList *next;
601
602         g_return_if_fail(path != NULL);
603
604         for (cur = prefs_common.fltlist; cur != NULL; cur = next) {
605                 Filter *flt = (Filter *)cur->data;
606                 next = cur->next;
607
608                 if (flt->dest && !strncmp(path, flt->dest, strlen(path))) {
609                         filter_free(flt);
610                         prefs_common.fltlist =
611                                 g_slist_remove(prefs_common.fltlist, flt);
612                 }
613         }
614
615         prefs_filter_write_config();
616 }
617
618 static void prefs_filter_set_dialog(const gchar *header, const gchar *key)
619 {
620         GtkCList *clist = GTK_CLIST(filter.cond_clist);
621         GSList *cur;
622         gchar *cond_str[1];
623         gint row;
624
625         gtk_clist_freeze(clist);
626         gtk_clist_clear(clist);
627
628         cond_str[0] = _("(New)");
629         row = gtk_clist_append(clist, cond_str);
630         gtk_clist_set_row_data(clist, row, NULL);
631
632         for (cur = prefs_common.fltlist; cur != NULL; cur = cur->next) {
633                 Filter *flt = (Filter *)cur->data;
634
635                 cond_str[0] = filter_get_str(flt);
636                 subst_char(cond_str[0], '\t', ':');
637                 row = gtk_clist_append(clist, cond_str);
638                 gtk_clist_set_row_data(clist, row, flt);
639
640                 g_free(cond_str[0]);
641         }
642
643         gtk_clist_thaw(clist);
644
645         if (header && key) {
646                 gtk_entry_set_text(GTK_ENTRY(filter.hdr_entry1), header);
647                 gtk_entry_set_text(GTK_ENTRY(filter.key_entry1), key);
648         }
649 }
650
651 static void prefs_filter_set_list(void)
652 {
653         gint row = 1;
654         Filter *flt;
655
656         g_slist_free(prefs_common.fltlist);
657         prefs_common.fltlist = NULL;
658
659         while ((flt = gtk_clist_get_row_data(GTK_CLIST(filter.cond_clist),
660                 row)) != NULL) {
661                 prefs_common.fltlist = g_slist_append(prefs_common.fltlist,
662                                                       flt);
663                 row++;
664         }
665 }
666
667 #define GET_ENTRY(entry) \
668         entry_text = gtk_entry_get_text(GTK_ENTRY(entry))
669
670 static gint prefs_filter_clist_set_row(gint row)
671 {
672         GtkCList *clist = GTK_CLIST(filter.cond_clist);
673         Filter *flt;
674         gchar *entry_text;
675         gchar *cond_str[1];
676
677         g_return_val_if_fail(row != 0, -1);
678
679         if (GTK_WIDGET_IS_SENSITIVE(filter.dest_entry))
680                 GET_ENTRY(filter.dest_entry);
681         else
682                 entry_text = FILTER_NOT_RECEIVE;
683         if (entry_text[0] == '\0') {
684                 alertpanel_error(_("Destination is not set."));
685                 return -1;
686         }
687         GET_ENTRY(filter.hdr_entry1);
688         if (entry_text[0] == '\0') {
689                 alertpanel_error(_("Header name is not set."));
690                 return -1;
691         }
692
693         flt = g_new0(Filter, 1);
694
695         flt->name1 = g_strdup(entry_text);
696
697         GET_ENTRY(filter.key_entry1);
698         if (entry_text[0] != '\0')
699                 flt->body1 = g_strdup(entry_text);
700
701         GET_ENTRY(filter.hdr_entry2);
702         if (entry_text[0] != '\0' && strcmp(entry_text, _("(none)")) != 0) {
703                 flt->name2 = g_strdup(entry_text);
704
705                 GET_ENTRY(filter.key_entry2);
706                 if (entry_text[0] != '\0')
707                         flt->body2 = g_strdup(entry_text);
708         }
709
710         if (gtk_toggle_button_get_active
711                 (GTK_TOGGLE_BUTTON(filter.regex_chkbtn)))
712                 flt->flag1 = flt->flag2 = FLT_REGEX;
713
714         GET_ENTRY(filter.pred_entry1);
715         if (!strcmp(entry_text, _("contains")))
716                 flt->flag1 |= FLT_CONTAIN;
717         GET_ENTRY(filter.pred_entry2);
718         if (!strcmp(entry_text, _("contains")))
719                 flt->flag2 |= FLT_CONTAIN;
720
721         GET_ENTRY(filter.cond_entry);
722         if (!strcmp(entry_text, "and"))
723                 flt->cond = FLT_AND;
724         else
725                 flt->cond = FLT_OR;
726
727         if (GTK_WIDGET_IS_SENSITIVE(filter.dest_entry)) {
728                 entry_text = gtk_entry_get_text(GTK_ENTRY(filter.dest_entry));
729                 flt->dest = g_strdup(entry_text);
730                 flt->action = FLT_MOVE;
731         } else
732                 flt->action = FLT_NOTRECV;
733
734         cond_str[0] = filter_get_str(flt);
735         subst_char(cond_str[0], '\t', ':');
736
737         if (row < 0)
738                 row = gtk_clist_append(clist, cond_str);
739         else {
740                 Filter *tmpflt;
741
742                 gtk_clist_set_text(clist, row, 0, cond_str[0]);
743                 tmpflt = gtk_clist_get_row_data(clist, row);
744                 if (tmpflt)
745                         filter_free(tmpflt);
746         }
747
748         gtk_clist_set_row_data(clist, row, flt);
749
750         g_free(cond_str[0]);
751
752         prefs_filter_set_list();
753
754         return row;
755 }
756
757 static void prefs_filter_select_dest_cb(void)
758 {
759         FolderItem *dest;
760
761         dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL);
762         if (!dest) return;
763
764         gtk_entry_set_text(GTK_ENTRY(filter.dest_entry), dest->path);
765 }
766
767 static void prefs_filter_register_cb(void)
768 {
769         prefs_filter_clist_set_row(-1);
770 }
771
772 static void prefs_filter_substitute_cb(void)
773 {
774         GtkCList *clist = GTK_CLIST(filter.cond_clist);
775         Filter *flt;
776         gint row;
777
778         if (!clist->selection) return;
779
780         row = GPOINTER_TO_INT(clist->selection->data);
781         if (row == 0) return;
782
783         flt = gtk_clist_get_row_data(clist, row);
784         if (!flt) return;
785
786         prefs_filter_clist_set_row(row);
787 }
788
789 static void prefs_filter_delete_cb(void)
790 {
791         GtkCList *clist = GTK_CLIST(filter.cond_clist);
792         Filter *flt;
793         gint row;
794
795         if (!clist->selection) return;
796         row = GPOINTER_TO_INT(clist->selection->data);
797         if (row == 0) return;
798
799         if (alertpanel(_("Delete rule"),
800                        _("Do you really want to delete this rule?"),
801                        _("Yes"), _("No"), NULL) == G_ALERTALTERNATE)
802                 return;
803
804         flt = gtk_clist_get_row_data(clist, row);
805         filter_free(flt);
806         gtk_clist_remove(clist, row);
807         prefs_common.fltlist = g_slist_remove(prefs_common.fltlist, flt);
808 }
809
810 static void prefs_filter_up(void)
811 {
812         GtkCList *clist = GTK_CLIST(filter.cond_clist);
813         gint row;
814
815         if (!clist->selection) return;
816
817         row = GPOINTER_TO_INT(clist->selection->data);
818         if (row > 1)
819                 gtk_clist_row_move(clist, row, row - 1);
820 }
821
822 static void prefs_filter_down(void)
823 {
824         GtkCList *clist = GTK_CLIST(filter.cond_clist);
825         gint row;
826
827         if (!clist->selection) return;
828
829         row = GPOINTER_TO_INT(clist->selection->data);
830         if (row > 0 && row < clist->rows - 1)
831                 gtk_clist_row_move(clist, row, row + 1);
832 }
833
834 #define ENTRY_SET_TEXT(entry, str) \
835         gtk_entry_set_text(GTK_ENTRY(entry), str ? str : "")
836
837 static void prefs_filter_select(GtkCList *clist, gint row, gint column,
838                                 GdkEvent *event)
839 {
840         Filter *flt;
841         Filter default_flt = {"Subject", NULL, NULL, NULL,
842                               FLT_CONTAIN, FLT_CONTAIN, FLT_AND,
843                               NULL, FLT_MOVE};
844         gboolean is_regex;
845
846         default_flt.name2 = _("(none)");
847
848         flt = gtk_clist_get_row_data(clist, row);
849         if (!flt)
850                 flt = &default_flt;
851
852         ENTRY_SET_TEXT(filter.dest_entry, flt->dest);
853         ENTRY_SET_TEXT(filter.hdr_entry1, flt->name1);
854         ENTRY_SET_TEXT(filter.key_entry1, flt->body1);
855         ENTRY_SET_TEXT(filter.hdr_entry2,
856                        flt->name2 ? flt->name2 : _("(none)"));
857         ENTRY_SET_TEXT(filter.key_entry2, flt->body2);
858
859         ENTRY_SET_TEXT(filter.pred_entry1,
860                        FLT_IS_CONTAIN(flt->flag1)
861                        ? _("contains") : _("not contain"));
862         ENTRY_SET_TEXT(filter.pred_entry2,
863                        FLT_IS_CONTAIN(flt->flag2)
864                        ? _("contains") : _("not contain"));
865
866         is_regex = FLT_IS_REGEX(flt->flag1);
867         gtk_widget_set_sensitive(filter.pred_combo1, !is_regex);
868         gtk_widget_set_sensitive(filter.pred_combo2, !is_regex);
869         gtk_toggle_button_set_active
870                 (GTK_TOGGLE_BUTTON(filter.regex_chkbtn), is_regex);
871
872         gtk_entry_set_text(GTK_ENTRY(filter.cond_entry),
873                            flt->cond == FLT_OR ? "or" : "and");
874         if (flt->action == FLT_NOTRECV)
875                 gtk_toggle_button_set_active
876                         (GTK_TOGGLE_BUTTON(filter.notrecv_radiobtn), TRUE);
877         else
878                 gtk_toggle_button_set_active
879                         (GTK_TOGGLE_BUTTON(filter.dest_radiobtn), TRUE);
880 }
881
882 static void prefs_filter_row_move(GtkCList *clist, gint source_row,
883                                   gint dest_row)
884 {
885         prefs_filter_set_list();
886         if (gtk_clist_row_is_visible(clist, dest_row) != GTK_VISIBILITY_FULL) {
887                 gtk_clist_moveto(clist, dest_row, -1,
888                                  source_row < dest_row ? 1.0 : 0.0, 0.0);
889         }
890 }
891
892 static void prefs_filter_dest_radio_button_toggled(void)
893 {
894         gtk_widget_set_sensitive(filter.dest_entry, TRUE);
895         gtk_widget_set_sensitive(filter.destsel_btn, TRUE);
896 }
897
898 static void prefs_filter_notrecv_radio_button_toggled(void)
899 {
900         gtk_widget_set_sensitive(filter.dest_entry, FALSE);
901         gtk_widget_set_sensitive(filter.destsel_btn, FALSE);
902 }
903
904 static void prefs_filter_regex_check_button_toggled(void)
905 {
906         gboolean is_regex;
907
908         is_regex = gtk_toggle_button_get_active
909                 (GTK_TOGGLE_BUTTON(filter.regex_chkbtn));
910         gtk_widget_set_sensitive(filter.pred_combo1, !is_regex);
911         gtk_widget_set_sensitive(filter.pred_combo2, !is_regex);
912 }
913
914 static gint prefs_filter_deleted(GtkWidget *widget, GdkEventAny *event,
915                                  gpointer data)
916 {
917         prefs_filter_cancel();
918         return TRUE;
919 }
920
921 static void prefs_filter_key_pressed(GtkWidget *widget, GdkEventKey *event,
922                                      gpointer data)
923 {
924         if (event && event->keyval == GDK_Escape)
925                 prefs_filter_cancel();
926 }
927
928 static void prefs_filter_ok(void)
929 {
930         prefs_filter_write_config();
931         gtk_widget_hide(filter.window);
932         inc_unlock();
933 }
934
935 static void prefs_filter_cancel(void)
936 {
937         prefs_filter_read_config();
938         gtk_widget_hide(filter.window);
939         inc_unlock();
940 }