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