2005-02-18 [colin] 1.0.1cvs13
[claws.git] / src / prefs_matcher.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_gtk.h"
37 #include "prefs_matcher.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 "matcher.h"
44 #include "utils.h"
45 #include "gtkutils.h"
46 #include "alertpanel.h"
47 #include "folder.h"
48 #include "description_window.h"
49
50 #include "matcher_parser.h"
51 #include "colorlabel.h"
52
53 /*!
54  *\brief        UI data for matcher dialog
55  */
56 static struct Matcher {
57         GtkWidget *window;
58
59         GtkWidget *ok_btn;
60
61         GtkWidget *predicate_combo;
62         GtkWidget *predicate_flag_combo;
63         GtkWidget *header_combo;
64
65         GtkWidget *criteria_list;
66
67         GtkWidget *predicate_list;
68         GtkWidget *predicate_label;
69         GtkWidget *predicate_flag_list;
70
71         GtkWidget *bool_op_list;
72
73         GtkWidget *header_entry;
74         GtkWidget *header_label;
75         GtkWidget *value_entry;
76         GtkWidget *value_label;
77         GtkWidget *case_chkbtn;
78         GtkWidget *regexp_chkbtn;
79         GtkWidget *color_optmenu;
80
81         GtkWidget *test_btn;
82
83         GtkWidget *cond_clist;
84
85         GtkWidget *criteria_table;
86
87         gint selected_criteria; /*!< selected criteria in combobox */ 
88 } matcher;
89
90 /*!
91  *\brief        Conditions with a negate counterpart (like unread and ~unread)
92  *              have the same CRITERIA_XXX id). I.e. both unread and ~unread
93  *              have criteria id CRITERIA_UNREAD. This id is passed as the
94  *              first parameter to #matcherprop_new and #matcherprop_unquote_new.
95  *
96  *\warning      Currently the enum constants should have the same order as the 
97  *              #criteria_text  
98  */             
99 enum {
100         CRITERIA_ALL = 0,
101
102         CRITERIA_SUBJECT = 1,
103         CRITERIA_FROM = 2,
104         CRITERIA_TO = 3,
105         CRITERIA_CC = 4,
106         CRITERIA_TO_OR_CC = 5,
107         CRITERIA_NEWSGROUPS = 6,
108         CRITERIA_INREPLYTO = 7,
109         CRITERIA_REFERENCES = 8,
110         CRITERIA_AGE_GREATER = 9,
111         CRITERIA_AGE_LOWER = 10,
112         CRITERIA_HEADER = 11,
113         CRITERIA_HEADERS_PART = 12,
114         CRITERIA_BODY_PART = 13,
115         CRITERIA_MESSAGE = 14,
116
117         CRITERIA_UNREAD = 15,
118         CRITERIA_NEW = 16,
119         CRITERIA_MARKED = 17,
120         CRITERIA_DELETED = 18,
121         CRITERIA_REPLIED = 19,
122         CRITERIA_FORWARDED = 20,
123         CRITERIA_LOCKED = 21,
124         CRITERIA_COLORLABEL = 22,
125         CRITERIA_IGNORE_THREAD = 23,
126
127         CRITERIA_SCORE_GREATER = 24,
128         CRITERIA_SCORE_LOWER = 25,
129         CRITERIA_SCORE_EQUAL = 26,
130
131         CRITERIA_TEST = 27,
132
133         CRITERIA_SIZE_GREATER = 28,
134         CRITERIA_SIZE_SMALLER = 29,
135         CRITERIA_SIZE_EQUAL   = 30,
136         
137         CRITERIA_PARTIAL = 31
138 };
139
140 /*!
141  *\brief        Descriptive text for conditions
142  */
143 static const gchar *criteria_text [] = {
144         N_("All messages"), N_("Subject"),
145         N_("From"), N_("To"), N_("Cc"), N_("To or Cc"),
146         N_("Newsgroups"), N_("In reply to"), N_("References"),
147         N_("Age greater than"), N_("Age lower than"),
148         N_("Header"), N_("Headers part"),
149         N_("Body part"), N_("Whole message"),
150         N_("Unread flag"), N_("New flag"),
151         N_("Marked flag"), N_("Deleted flag"),
152         N_("Replied flag"), N_("Forwarded flag"),
153         N_("Locked flag"),
154         N_("Color label"),
155         N_("Ignore thread"),
156         N_("Score greater than"), N_("Score lower than"),
157         N_("Score equal to"),
158         N_("Test"),
159         N_("Size greater than"), 
160         N_("Size smaller than"),
161         N_("Size exactly"),
162         N_("Partially downloaded")
163 };
164
165 /*!
166  *\brief        Boolean / predicate constants
167  *
168  *\warning      Same order as #bool_op_text!
169  */
170 enum {
171         BOOL_OP_OR = 0,
172         BOOL_OP_AND = 1
173 };
174
175 /*!
176  *\brief        Descriptive text in UI
177  */
178 static const gchar *bool_op_text [] = {
179         N_("or"), N_("and")
180 };
181
182 /*!
183  *\brief        Contains predicate      
184  *
185  *\warning      Same order as in #predicate_text
186  */
187 enum {
188         PREDICATE_CONTAINS = 0,
189         PREDICATE_DOES_NOT_CONTAIN = 1
190 };
191
192 /*!
193  *\brief        Descriptive text in UI for predicate
194  */
195 static const gchar *predicate_text [] = {
196         N_("contains"), N_("does not contain")
197 };
198
199 /*!
200  *\brief        Enabled predicate
201  *
202  *\warning      Same order as in #predicate_flag_text
203  */
204 enum {
205         PREDICATE_FLAG_ENABLED = 0,
206         PREDICATE_FLAG_DISABLED = 1
207 };
208
209 /*!
210  *\brief        Descriptive text in UI for enabled flag
211  */
212 static const gchar *predicate_flag_text [] = {
213         N_("yes"), N_("no")
214 };
215
216 /*!
217  *\brief        Hooks
218  */
219 static PrefsMatcherSignal *matchers_callback;
220
221 /* widget creating functions */
222 static void prefs_matcher_create        (void);
223
224 static void prefs_matcher_set_dialog    (MatcherList *matchers);
225 static gint prefs_matcher_clist_set_row (gint row, MatcherProp *prop);
226
227 /* callback functions */
228
229 static void prefs_matcher_register_cb   (void);
230 static void prefs_matcher_substitute_cb (void);
231 static void prefs_matcher_delete_cb     (void);
232 static void prefs_matcher_up            (void);
233 static void prefs_matcher_down          (void);
234 static void prefs_matcher_select        (GtkCList       *clist,
235                                          gint            row,
236                                          gint            column,
237                                          GdkEvent       *event);
238 static gboolean prefs_matcher_key_pressed(GtkWidget     *widget,
239                                          GdkEventKey    *event,
240                                          gpointer        data);
241 static void prefs_matcher_ok            (void);
242 static void prefs_matcher_cancel        (void);
243 static gint prefs_matcher_deleted       (GtkWidget *widget, GdkEventAny *event,
244                                          gpointer data);
245 static void prefs_matcher_criteria_select       (GtkList   *list,
246                                                  GtkWidget *widget,
247                                                  gpointer   user_data);
248 static MatcherList *prefs_matcher_get_list      (void);
249
250
251 /*!
252  *\brief        Find index of list selection 
253  *
254  *\param        list GTK list widget
255  *
256  *\return       gint Selection index
257  */
258 static gint get_sel_from_list(GtkList *list)
259 {
260         gint row = 0;
261         void * sel;
262         GList * child;
263
264         sel = list->selection->data;
265         for (child = list->children; child != NULL; child = g_list_next(child)) {
266                 if (child->data == sel)
267                         return row;
268                 row ++;
269         }
270         
271         return row;
272 }
273
274 /*!
275  *\brief        Opens the matcher dialog with a list of conditions
276  *
277  *\param        matchers List of conditions
278  *\param        cb Callback
279  *
280  */
281 void prefs_matcher_open(MatcherList *matchers, PrefsMatcherSignal *cb)
282 {
283         inc_lock();
284
285         if (!matcher.window) {
286                 prefs_matcher_create();
287         }
288
289         manage_window_set_transient(GTK_WINDOW(matcher.window));
290         gtk_widget_grab_focus(matcher.ok_btn);
291
292         matchers_callback = cb;
293
294         prefs_matcher_set_dialog(matchers);
295
296         gtk_widget_show(matcher.window);
297 }
298
299 /*!
300  *\brief        Create the matcher dialog
301  */
302 static void prefs_matcher_create(void)
303 {
304         GtkWidget *window;
305         GtkWidget *vbox;
306         GtkWidget *ok_btn;
307         GtkWidget *cancel_btn;
308         GtkWidget *confirm_area;
309
310         GtkWidget *vbox1;
311         GtkWidget *vbox2;
312         GtkWidget *vbox3;
313         GtkWidget *criteria_table;
314
315         GtkWidget *hbox1;
316
317         GtkWidget *header_combo;
318         GtkWidget *header_entry;
319         GtkWidget *header_label;
320         GtkWidget *criteria_combo;
321         GtkWidget *criteria_list;
322         GtkWidget *criteria_label;
323         GtkWidget *value_label;
324         GtkWidget *value_entry;
325         GtkWidget *predicate_combo;
326         GtkWidget *predicate_list;
327         GtkWidget *predicate_flag_combo;
328         GtkWidget *predicate_flag_list;
329         GtkWidget *predicate_label;
330         GtkWidget *bool_op_combo;
331         GtkWidget *bool_op_list;
332         GtkWidget *bool_op_label;
333
334         GtkWidget *regexp_chkbtn;
335         GtkWidget *case_chkbtn;
336
337         GtkWidget *reg_hbox;
338         GtkWidget *btn_hbox;
339         GtkWidget *arrow;
340         GtkWidget *reg_btn;
341         GtkWidget *subst_btn;
342         GtkWidget *del_btn;
343
344         GtkWidget *cond_hbox;
345         GtkWidget *cond_scrolledwin;
346         GtkWidget *cond_clist;
347
348         GtkWidget *btn_vbox;
349         GtkWidget *up_btn;
350         GtkWidget *down_btn;
351
352         GtkWidget *test_btn;
353
354         GtkWidget *color_optmenu;
355
356         GList *combo_items;
357         gint i;
358
359         gchar *title[1];
360
361         debug_print("Creating matcher configuration window...\n");
362
363         window = gtk_window_new(GTK_WINDOW_DIALOG);
364         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
365         gtk_window_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
366         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
367         gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, FALSE);
368
369         vbox = gtk_vbox_new(FALSE, 6);
370         gtk_widget_show(vbox);
371         gtk_container_add(GTK_CONTAINER(window), vbox);
372
373         gtkut_button_set_create(&confirm_area, &ok_btn, _("OK"),
374                                 &cancel_btn, _("Cancel"), NULL, NULL);
375         gtk_widget_show(confirm_area);
376         gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
377         gtk_widget_grab_default(ok_btn);
378
379         gtk_window_set_title(GTK_WINDOW(window),
380                              _("Condition configuration"));
381         gtk_signal_connect(GTK_OBJECT(window), "delete_event",
382                            GTK_SIGNAL_FUNC(prefs_matcher_deleted), NULL);
383         gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
384                            GTK_SIGNAL_FUNC(prefs_matcher_key_pressed), NULL);
385         MANAGE_WINDOW_SIGNALS_CONNECT(window);
386         gtk_signal_connect(GTK_OBJECT(ok_btn), "clicked",
387                            GTK_SIGNAL_FUNC(prefs_matcher_ok), NULL);
388         gtk_signal_connect(GTK_OBJECT(cancel_btn), "clicked",
389                            GTK_SIGNAL_FUNC(prefs_matcher_cancel), NULL);
390
391         vbox1 = gtk_vbox_new(FALSE, VSPACING);
392         gtk_widget_show(vbox1);
393         gtk_box_pack_start(GTK_BOX(vbox), vbox1, TRUE, TRUE, 0);
394         gtk_container_set_border_width(GTK_CONTAINER (vbox1), 2);
395
396         criteria_table = gtk_table_new(2, 4, FALSE);
397         gtk_widget_show(criteria_table);
398
399         gtk_box_pack_start(GTK_BOX(vbox1), criteria_table, FALSE, FALSE, 0);
400         gtk_table_set_row_spacings(GTK_TABLE(criteria_table), 8);
401         gtk_table_set_col_spacings(GTK_TABLE(criteria_table), 8);
402
403         /* criteria combo box */
404
405         criteria_label = gtk_label_new(_("Match type"));
406         gtk_widget_show(criteria_label);
407         gtk_misc_set_alignment(GTK_MISC(criteria_label), 0, 0.5);
408         gtk_table_attach(GTK_TABLE(criteria_table), criteria_label, 0, 1, 0, 1,
409                          GTK_FILL, 0, 0, 0);
410
411         criteria_combo = gtk_combo_new();
412         gtk_widget_show(criteria_combo);
413
414         combo_items = NULL;
415
416         for (i = 0; i < (gint) (sizeof(criteria_text) / sizeof(gchar *)); i++) {
417                 combo_items = g_list_append(combo_items,
418                                             (gpointer) _(criteria_text[i]));
419         }
420         gtk_combo_set_popdown_strings(GTK_COMBO(criteria_combo), combo_items);
421
422         g_list_free(combo_items);
423
424         gtk_widget_set_usize(criteria_combo, 120, -1);
425         gtk_table_attach(GTK_TABLE(criteria_table), criteria_combo, 0, 1, 1, 2,
426                           0, 0, 0, 0);
427         criteria_list = GTK_COMBO(criteria_combo)->list;
428         gtk_signal_connect(GTK_OBJECT(criteria_list), "select-child",
429                            GTK_SIGNAL_FUNC(prefs_matcher_criteria_select),
430                            NULL);
431
432         gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(criteria_combo)->entry),
433                                FALSE);
434
435         /* header name */
436
437         header_label = gtk_label_new(_("Header name"));
438         gtk_widget_show(header_label);
439         gtk_misc_set_alignment(GTK_MISC(header_label), 0, 0.5);
440         gtk_table_attach(GTK_TABLE(criteria_table), header_label, 1, 2, 0, 1,
441                          GTK_FILL, 0, 0, 0);
442
443         header_combo = gtk_combo_new();
444         gtk_widget_show(header_combo);
445         gtk_widget_set_usize(header_combo, 96, -1);
446         gtkut_combo_set_items(GTK_COMBO (header_combo),
447                               "Subject", "From", "To", "Cc", "Reply-To",
448                               "Sender", "X-ML-Name", "X-List", "X-Sequence",
449                               "X-Mailer","X-BeenThere",
450                               NULL);
451         gtk_table_attach(GTK_TABLE(criteria_table), header_combo, 1, 2, 1, 2,
452                          0, 0, 0, 0);
453         header_entry = GTK_COMBO(header_combo)->entry;
454         gtk_entry_set_editable(GTK_ENTRY(header_entry), TRUE);
455
456         /* value */
457
458         value_label = gtk_label_new(_("Value"));
459         gtk_widget_show(value_label);
460         gtk_misc_set_alignment(GTK_MISC (value_label), 0, 0.5);
461         gtk_table_attach(GTK_TABLE(criteria_table), value_label, 2, 3, 0, 1,
462                          GTK_FILL | GTK_SHRINK | GTK_EXPAND, 0, 0, 0);
463
464         value_entry = gtk_entry_new();
465         gtk_widget_show(value_entry);
466         gtk_widget_set_usize(value_entry, 200, -1);
467         gtk_table_attach(GTK_TABLE(criteria_table), value_entry, 2, 3, 1, 2,
468                          GTK_FILL | GTK_SHRINK | GTK_EXPAND, 0, 0, 0);
469
470         test_btn = gtk_button_new_with_label(_("Info ..."));
471         gtk_widget_show(test_btn);
472         gtk_table_attach(GTK_TABLE (criteria_table), test_btn, 3, 4, 1, 2,
473                          GTK_FILL | GTK_SHRINK | GTK_EXPAND, 0, 0, 0);
474         gtk_signal_connect(GTK_OBJECT (test_btn), "clicked",
475                            GTK_SIGNAL_FUNC (prefs_matcher_test_info),
476                            NULL);
477
478         color_optmenu = gtk_option_menu_new();
479         gtk_option_menu_set_menu(GTK_OPTION_MENU(color_optmenu),
480                                  colorlabel_create_color_menu());
481
482         /* predicate */
483
484         vbox2 = gtk_vbox_new(FALSE, VSPACING);
485         gtk_widget_show(vbox2);
486         gtk_box_pack_start(GTK_BOX(vbox1), vbox2, FALSE, FALSE, 0);
487
488         hbox1 = gtk_hbox_new(FALSE, 8);
489         gtk_widget_show(hbox1);
490         gtk_box_pack_start(GTK_BOX(vbox2), hbox1, FALSE, FALSE, 0);
491
492         predicate_label = gtk_label_new(_("Predicate"));
493         gtk_widget_show(predicate_label);
494         gtk_box_pack_start(GTK_BOX(hbox1), predicate_label,
495                            FALSE, FALSE, 0);
496
497         predicate_combo = gtk_combo_new();
498         gtk_widget_show(predicate_combo);
499         gtk_widget_set_usize(predicate_combo, 120, -1);
500         predicate_list = GTK_COMBO(predicate_combo)->list;
501         gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(predicate_combo)->entry),
502                                FALSE);
503
504         combo_items = NULL;
505
506         for (i = 0; i < (gint) (sizeof(predicate_text) / sizeof(gchar *)); i++) {
507                 combo_items = g_list_append(combo_items,
508                                             (gpointer) _(predicate_text[i]));
509         }
510         gtk_combo_set_popdown_strings(GTK_COMBO(predicate_combo), combo_items);
511
512         g_list_free(combo_items);
513
514         gtk_box_pack_start(GTK_BOX(hbox1), predicate_combo,
515                            FALSE, FALSE, 0);
516
517         /* predicate flag */
518
519         predicate_flag_combo = gtk_combo_new();
520         gtk_widget_hide(predicate_flag_combo);
521         gtk_widget_set_usize(predicate_flag_combo, 120, -1);
522         predicate_flag_list = GTK_COMBO(predicate_flag_combo)->list;
523         gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(predicate_flag_combo)->entry), FALSE);
524
525         combo_items = NULL;
526
527         for (i = 0; i < (gint) (sizeof(predicate_text) / sizeof(gchar *)); i++) {
528                 combo_items = g_list_append(combo_items, (gpointer) _(predicate_flag_text[i]));
529         }
530         gtk_combo_set_popdown_strings(GTK_COMBO(predicate_flag_combo),
531                                       combo_items);
532
533         g_list_free(combo_items);
534
535         gtk_box_pack_start(GTK_BOX(hbox1), predicate_flag_combo,
536                            FALSE, FALSE, 0);
537
538         vbox3 = gtk_vbox_new(FALSE, 0);
539         gtk_widget_show (vbox3);
540         gtk_box_pack_start(GTK_BOX(hbox1), vbox3, FALSE, FALSE, 0);
541
542         PACK_CHECK_BUTTON(vbox3, case_chkbtn, _("Case sensitive"));
543         PACK_CHECK_BUTTON(vbox3, regexp_chkbtn, _("Use regexp"));
544
545         /* register / substitute / delete */
546
547         reg_hbox = gtk_hbox_new(FALSE, 4);
548         gtk_widget_show(reg_hbox);
549         gtk_box_pack_start(GTK_BOX(vbox1), reg_hbox, FALSE, FALSE, 0);
550
551         arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
552         gtk_widget_show(arrow);
553         gtk_box_pack_start(GTK_BOX(reg_hbox), arrow, FALSE, FALSE, 0);
554         gtk_widget_set_usize(arrow, -1, 16);
555
556         btn_hbox = gtk_hbox_new(TRUE, 4);
557         gtk_widget_show(btn_hbox);
558         gtk_box_pack_start(GTK_BOX(reg_hbox), btn_hbox, FALSE, FALSE, 0);
559
560         reg_btn = gtk_button_new_with_label(_("Add"));
561         gtk_widget_show(reg_btn);
562         gtk_box_pack_start(GTK_BOX(btn_hbox), reg_btn, FALSE, TRUE, 0);
563         gtk_signal_connect(GTK_OBJECT(reg_btn), "clicked",
564                            GTK_SIGNAL_FUNC(prefs_matcher_register_cb), NULL);
565
566         subst_btn = gtk_button_new_with_label(_("  Replace  "));
567         gtk_widget_show(subst_btn);
568         gtk_box_pack_start(GTK_BOX(btn_hbox), subst_btn, FALSE, TRUE, 0);
569         gtk_signal_connect(GTK_OBJECT(subst_btn), "clicked",
570                            GTK_SIGNAL_FUNC(prefs_matcher_substitute_cb),
571                            NULL);
572
573         del_btn = gtk_button_new_with_label(_("Delete"));
574         gtk_widget_show(del_btn);
575         gtk_box_pack_start(GTK_BOX(btn_hbox), del_btn, FALSE, TRUE, 0);
576         gtk_signal_connect(GTK_OBJECT(del_btn), "clicked",
577                            GTK_SIGNAL_FUNC(prefs_matcher_delete_cb), NULL);
578
579         /* boolean operation */
580
581         bool_op_label = gtk_label_new(_("Boolean Op"));
582         gtk_misc_set_alignment(GTK_MISC(value_label), 0, 0.5);
583         gtk_widget_show(bool_op_label);
584         gtk_box_pack_start(GTK_BOX(btn_hbox), bool_op_label,
585                            FALSE, FALSE, 0);
586
587         bool_op_combo = gtk_combo_new();
588         gtk_widget_show(bool_op_combo);
589         gtk_widget_set_usize(bool_op_combo, 50, -1);
590         bool_op_list = GTK_COMBO(bool_op_combo)->list;
591         gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(bool_op_combo)->entry),
592                                FALSE);
593
594         combo_items = NULL;
595
596         for (i = 0; i < (gint) (sizeof(bool_op_text) / sizeof(gchar *)); i++) {
597                 combo_items = g_list_append(combo_items,
598                                             (gpointer) _(bool_op_text[i]));
599         }
600         gtk_combo_set_popdown_strings(GTK_COMBO(bool_op_combo), combo_items);
601
602         g_list_free(combo_items);
603
604         gtk_box_pack_start(GTK_BOX(btn_hbox), bool_op_combo,
605                            FALSE, FALSE, 0);
606
607         cond_hbox = gtk_hbox_new(FALSE, 8);
608         gtk_widget_show(cond_hbox);
609         gtk_box_pack_start(GTK_BOX(vbox1), cond_hbox, TRUE, TRUE, 0);
610
611         cond_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
612         gtk_widget_show(cond_scrolledwin);
613         gtk_widget_set_usize(cond_scrolledwin, -1, 150);
614         gtk_box_pack_start(GTK_BOX(cond_hbox), cond_scrolledwin,
615                            TRUE, TRUE, 0);
616         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(cond_scrolledwin),
617                                        GTK_POLICY_AUTOMATIC,
618                                        GTK_POLICY_AUTOMATIC);
619
620         title[0] = _("Current condition rules");
621         cond_clist = gtk_clist_new_with_titles(1, title);
622         gtk_widget_show(cond_clist);
623         gtk_container_add(GTK_CONTAINER(cond_scrolledwin), cond_clist);
624         gtk_clist_set_column_width(GTK_CLIST(cond_clist), 0, 80);
625         gtk_clist_set_selection_mode(GTK_CLIST(cond_clist),
626                                      GTK_SELECTION_BROWSE);
627         GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(cond_clist)->column[0].button,
628                                GTK_CAN_FOCUS);
629         gtk_signal_connect(GTK_OBJECT(cond_clist), "select_row",
630                            GTK_SIGNAL_FUNC(prefs_matcher_select), NULL);
631
632         btn_vbox = gtk_vbox_new(FALSE, 8);
633         gtk_widget_show(btn_vbox);
634         gtk_box_pack_start(GTK_BOX(cond_hbox), btn_vbox, FALSE, FALSE, 0);
635
636         up_btn = gtk_button_new_with_label(_("Up"));
637         gtk_widget_show(up_btn);
638         gtk_box_pack_start(GTK_BOX(btn_vbox), up_btn, FALSE, FALSE, 0);
639         gtk_signal_connect(GTK_OBJECT(up_btn), "clicked",
640                            GTK_SIGNAL_FUNC(prefs_matcher_up), NULL);
641
642         down_btn = gtk_button_new_with_label(_("Down"));
643         gtk_widget_show(down_btn);
644         gtk_box_pack_start(GTK_BOX(btn_vbox), down_btn, FALSE, FALSE, 0);
645         gtk_signal_connect(GTK_OBJECT(down_btn), "clicked",
646                            GTK_SIGNAL_FUNC(prefs_matcher_down), NULL);
647
648         gtk_widget_show_all(window);
649
650         matcher.window    = window;
651
652         matcher.ok_btn = ok_btn;
653
654         matcher.criteria_list = criteria_list;
655         matcher.header_combo = header_combo;
656         matcher.header_entry = header_entry;
657         matcher.header_label = header_label;
658         matcher.value_entry = value_entry;
659         matcher.value_label = value_label;
660         matcher.predicate_label = predicate_label;
661         matcher.predicate_list = predicate_list;
662         matcher.predicate_combo = predicate_combo;
663         matcher.predicate_flag_list = predicate_flag_list;
664         matcher.predicate_flag_combo = predicate_flag_combo;
665         matcher.case_chkbtn = case_chkbtn;
666         matcher.regexp_chkbtn = regexp_chkbtn;
667         matcher.bool_op_list = bool_op_list;
668         matcher.test_btn = test_btn;
669         matcher.color_optmenu = color_optmenu;
670         matcher.criteria_table = criteria_table;
671
672         matcher.cond_clist = cond_clist;
673 }
674
675 /*!
676  *\brief        Set the contents of a row
677  *
678  *\param        row Index of row to set
679  *\param        prop Condition to set
680  *
681  *\return       gint Row index \a prop has been added
682  */
683 static gint prefs_matcher_clist_set_row(gint row, MatcherProp *prop)
684 {
685         GtkCList *clist = GTK_CLIST(matcher.cond_clist);
686         gchar *cond_str[1];
687         gchar *matcher_str;
688
689         if (prop == NULL) {
690                 cond_str[0] = _("(New)");
691                 return gtk_clist_append(clist, cond_str);
692         }
693
694         matcher_str = matcherprop_to_string(prop);
695         cond_str[0] = matcher_str;
696         if (row < 0)
697                 row = gtk_clist_append(clist, cond_str);
698         else
699                 gtk_clist_set_text(clist, row, 0, cond_str[0]);
700         g_free(matcher_str);
701
702         return row;
703 }
704
705 /*!
706  *\brief        Clears a condition in the list widget
707  */
708 static void prefs_matcher_reset_condition(void)
709 {
710         gtk_list_select_item(GTK_LIST(matcher.criteria_list), 0);
711         gtk_list_select_item(GTK_LIST(matcher.predicate_list), 0);
712         gtk_entry_set_text(GTK_ENTRY(matcher.header_entry), "");
713         gtk_entry_set_text(GTK_ENTRY(matcher.value_entry), "");
714 }
715
716 /*!
717  *\brief        Update scrollbar
718  */
719 static void prefs_matcher_update_hscrollbar(void)
720 {
721         gint optwidth = gtk_clist_optimal_column_width(GTK_CLIST(matcher.cond_clist), 0);
722         gtk_clist_set_column_width(GTK_CLIST(matcher.cond_clist), 0, optwidth);
723 }
724
725 /*!
726  *\brief        Initializes dialog with a set of conditions
727  *
728  *\param        matchers List of conditions
729  */
730 static void prefs_matcher_set_dialog(MatcherList *matchers)
731 {
732         GtkCList *clist = GTK_CLIST(matcher.cond_clist);
733         GSList *cur;
734         gboolean bool_op = 1;
735
736         gtk_clist_freeze(clist);
737         gtk_clist_clear(clist);
738
739         prefs_matcher_clist_set_row(-1, NULL);
740         if (matchers != NULL) {
741                 for (cur = matchers->matchers; cur != NULL;
742                      cur = g_slist_next(cur)) {
743                         MatcherProp *prop;
744                         prop = (MatcherProp *) cur->data;
745                         prefs_matcher_clist_set_row(-1, prop);
746                 }
747
748                 bool_op = matchers->bool_and;
749         }
750         
751         prefs_matcher_update_hscrollbar();
752
753         gtk_clist_thaw(clist);
754
755         gtk_list_select_item(GTK_LIST(matcher.bool_op_list), bool_op);
756
757         prefs_matcher_reset_condition();
758 }
759
760 /*!
761  *\brief        Converts current conditions in list box in
762  *              a matcher list used by the matcher.
763  *
764  *\return       MatcherList * List of conditions.
765  */
766 static MatcherList *prefs_matcher_get_list(void)
767 {
768         gchar *matcher_str;
769         MatcherProp *prop;
770         gint row = 1;
771         gboolean bool_and;
772         GSList *matcher_list;
773         MatcherList *matchers;
774
775         matcher_list = NULL;
776
777         while (gtk_clist_get_text(GTK_CLIST(matcher.cond_clist),
778                                   row, 0, &matcher_str)) {
779
780                 if (strcmp(matcher_str, _("(New)")) != 0) {
781                         /* tmp = matcher_str; */
782                         prop = matcher_parser_get_prop(matcher_str);
783                         
784                         if (prop == NULL)
785                                 break;
786                         
787                         matcher_list = g_slist_append(matcher_list, prop);
788                 }
789                 row ++;
790         }
791
792         bool_and = get_sel_from_list(GTK_LIST(matcher.bool_op_list));
793
794         matchers = matcherlist_new(matcher_list, bool_and);
795
796         return matchers;
797 }
798
799 /*!
800  *\brief        Maps a keyword id (see #get_matchparser_tab_id) to a 
801  *              criteria type (see first parameter of #matcherprop_new
802  *              or #matcherprop_unquote_new)
803  *
804  *\param        matching_id Id returned by the matcher parser.
805  *
806  *\return       gint One of the CRITERIA_xxx constants.
807  */
808 static gint prefs_matcher_get_criteria_from_matching(gint matching_id)
809 {
810         switch(matching_id) {
811         case MATCHCRITERIA_ALL:
812                 return CRITERIA_ALL;
813         case MATCHCRITERIA_NOT_UNREAD:
814         case MATCHCRITERIA_UNREAD:
815                 return CRITERIA_UNREAD;
816         case MATCHCRITERIA_NOT_NEW:
817         case MATCHCRITERIA_NEW:
818                 return CRITERIA_NEW;
819         case MATCHCRITERIA_NOT_MARKED:
820         case MATCHCRITERIA_MARKED:
821                 return CRITERIA_MARKED;
822         case MATCHCRITERIA_NOT_DELETED:
823         case MATCHCRITERIA_DELETED:
824                 return CRITERIA_DELETED;
825         case MATCHCRITERIA_NOT_REPLIED:
826         case MATCHCRITERIA_REPLIED:
827                 return CRITERIA_REPLIED;
828         case MATCHCRITERIA_NOT_FORWARDED:
829         case MATCHCRITERIA_FORWARDED:
830                 return CRITERIA_FORWARDED;
831         case MATCHCRITERIA_LOCKED:
832         case MATCHCRITERIA_NOT_LOCKED:
833                 return CRITERIA_LOCKED;
834         case MATCHCRITERIA_PARTIAL:
835         case MATCHCRITERIA_NOT_PARTIAL:
836                 return CRITERIA_PARTIAL;
837         case MATCHCRITERIA_COLORLABEL:
838         case MATCHCRITERIA_NOT_COLORLABEL:
839                 return CRITERIA_COLORLABEL;
840         case MATCHCRITERIA_IGNORE_THREAD:
841         case MATCHCRITERIA_NOT_IGNORE_THREAD:
842                 return CRITERIA_IGNORE_THREAD;
843         case MATCHCRITERIA_NOT_SUBJECT:
844         case MATCHCRITERIA_SUBJECT:
845                 return CRITERIA_SUBJECT;
846         case MATCHCRITERIA_NOT_FROM:
847         case MATCHCRITERIA_FROM:
848                 return CRITERIA_FROM;
849         case MATCHCRITERIA_NOT_TO:
850         case MATCHCRITERIA_TO:
851                 return CRITERIA_TO;
852         case MATCHCRITERIA_NOT_CC:
853         case MATCHCRITERIA_CC:
854                 return CRITERIA_CC;
855         case MATCHCRITERIA_NOT_NEWSGROUPS:
856         case MATCHCRITERIA_NEWSGROUPS:
857                 return CRITERIA_NEWSGROUPS;
858         case MATCHCRITERIA_NOT_INREPLYTO:
859         case MATCHCRITERIA_INREPLYTO:
860                 return CRITERIA_INREPLYTO;
861         case MATCHCRITERIA_NOT_REFERENCES:
862         case MATCHCRITERIA_REFERENCES:
863                 return CRITERIA_REFERENCES;
864         case MATCHCRITERIA_NOT_TO_AND_NOT_CC:
865         case MATCHCRITERIA_TO_OR_CC:
866                 return CRITERIA_TO_OR_CC;
867         case MATCHCRITERIA_NOT_BODY_PART:
868         case MATCHCRITERIA_BODY_PART:
869                 return CRITERIA_BODY_PART;
870         case MATCHCRITERIA_NOT_MESSAGE:
871         case MATCHCRITERIA_MESSAGE:
872                 return CRITERIA_MESSAGE;
873                 break;
874         case MATCHCRITERIA_NOT_HEADERS_PART:
875         case MATCHCRITERIA_HEADERS_PART:
876                 return CRITERIA_HEADERS_PART;
877         case MATCHCRITERIA_NOT_HEADER:
878         case MATCHCRITERIA_HEADER:
879                 return CRITERIA_HEADER;
880         case MATCHCRITERIA_AGE_GREATER:
881                 return CRITERIA_AGE_GREATER;
882         case MATCHCRITERIA_AGE_LOWER:
883                 return CRITERIA_AGE_LOWER;
884         case MATCHCRITERIA_SCORE_GREATER:
885                 return CRITERIA_SCORE_GREATER;
886         case MATCHCRITERIA_SCORE_LOWER:
887                 return CRITERIA_SCORE_LOWER;
888         case MATCHCRITERIA_SCORE_EQUAL:
889                 return CRITERIA_SCORE_EQUAL;
890         case MATCHCRITERIA_NOT_TEST:
891         case MATCHCRITERIA_TEST:
892                 return CRITERIA_TEST;
893         case MATCHCRITERIA_SIZE_GREATER:
894                 return CRITERIA_SIZE_GREATER;
895         case MATCHCRITERIA_SIZE_SMALLER:
896                 return CRITERIA_SIZE_SMALLER;
897         case MATCHCRITERIA_SIZE_EQUAL:
898                 return CRITERIA_SIZE_EQUAL;
899         default:
900                 return -1;
901         }
902 }
903
904 /*!
905  *\brief        Returns the matcher keyword id from a criteria id
906  *
907  *\param        criteria_id Criteria id (should not be the negate
908  *              one)
909  *
910  *\return       gint A matcher keyword id. See #get_matchparser_tab_id.
911  */
912 static gint prefs_matcher_get_matching_from_criteria(gint criteria_id)
913 {
914         switch (criteria_id) {
915         case CRITERIA_ALL:
916                 return MATCHCRITERIA_ALL;
917         case CRITERIA_UNREAD:
918                 return MATCHCRITERIA_UNREAD;
919         case CRITERIA_NEW:
920                 return MATCHCRITERIA_NEW;
921         case CRITERIA_MARKED:
922                 return MATCHCRITERIA_MARKED;
923         case CRITERIA_DELETED:
924                 return MATCHCRITERIA_DELETED;
925         case CRITERIA_REPLIED:
926                 return MATCHCRITERIA_REPLIED;
927         case CRITERIA_FORWARDED:
928                 return MATCHCRITERIA_FORWARDED;
929         case CRITERIA_LOCKED:
930                 return MATCHCRITERIA_LOCKED;
931         case CRITERIA_PARTIAL:
932                 return MATCHCRITERIA_PARTIAL;
933         case CRITERIA_COLORLABEL:
934                 return MATCHCRITERIA_COLORLABEL;
935         case CRITERIA_IGNORE_THREAD:
936                 return MATCHCRITERIA_IGNORE_THREAD;
937         case CRITERIA_SUBJECT:
938                 return MATCHCRITERIA_SUBJECT;
939         case CRITERIA_FROM:
940                 return MATCHCRITERIA_FROM;
941         case CRITERIA_TO:
942                 return MATCHCRITERIA_TO;
943         case CRITERIA_CC:
944                 return MATCHCRITERIA_CC;
945         case CRITERIA_TO_OR_CC:
946                 return MATCHCRITERIA_TO_OR_CC;
947         case CRITERIA_NEWSGROUPS:
948                 return MATCHCRITERIA_NEWSGROUPS;
949         case CRITERIA_INREPLYTO:
950                 return MATCHCRITERIA_INREPLYTO;
951         case CRITERIA_REFERENCES:
952                 return MATCHCRITERIA_REFERENCES;
953         case CRITERIA_AGE_GREATER:
954                 return MATCHCRITERIA_AGE_GREATER;
955         case CRITERIA_AGE_LOWER:
956                 return MATCHCRITERIA_AGE_LOWER;
957         case CRITERIA_SCORE_GREATER:
958                 return MATCHCRITERIA_SCORE_GREATER;
959         case CRITERIA_SCORE_LOWER:
960                 return MATCHCRITERIA_SCORE_LOWER;
961         case CRITERIA_SCORE_EQUAL:
962                 return MATCHCRITERIA_SCORE_EQUAL;
963         case CRITERIA_HEADER:
964                 return MATCHCRITERIA_HEADER;
965         case CRITERIA_HEADERS_PART:
966                 return MATCHCRITERIA_HEADERS_PART;
967         case CRITERIA_BODY_PART:
968                 return MATCHCRITERIA_BODY_PART;
969         case CRITERIA_MESSAGE:
970                 return MATCHCRITERIA_MESSAGE;
971         case CRITERIA_TEST:
972                 return MATCHCRITERIA_TEST;
973         case CRITERIA_SIZE_GREATER:
974                 return MATCHCRITERIA_SIZE_GREATER;
975         case CRITERIA_SIZE_SMALLER:
976                 return MATCHCRITERIA_SIZE_SMALLER;
977         case CRITERIA_SIZE_EQUAL:
978                 return MATCHCRITERIA_SIZE_EQUAL;
979         default:
980                 return -1;
981         }
982 }
983
984 /*!
985  *\brief        Returns the negate matcher keyword id from a matcher keyword
986  *              id.
987  *
988  *\param        matcher_criteria Matcher keyword id. 
989  *
990  *\return       gint A matcher keyword id. See #get_matchparser_tab_id.
991  */
992 static gint prefs_matcher_not_criteria(gint matcher_criteria)
993 {
994         switch(matcher_criteria) {
995         case MATCHCRITERIA_UNREAD:
996                 return MATCHCRITERIA_NOT_UNREAD;
997         case MATCHCRITERIA_NEW:
998                 return MATCHCRITERIA_NOT_NEW;
999         case MATCHCRITERIA_MARKED:
1000                 return MATCHCRITERIA_NOT_MARKED;
1001         case MATCHCRITERIA_DELETED:
1002                 return MATCHCRITERIA_NOT_DELETED;
1003         case MATCHCRITERIA_REPLIED:
1004                 return MATCHCRITERIA_NOT_REPLIED;
1005         case MATCHCRITERIA_FORWARDED:
1006                 return MATCHCRITERIA_NOT_FORWARDED;
1007         case MATCHCRITERIA_LOCKED:
1008                 return MATCHCRITERIA_NOT_LOCKED;
1009         case MATCHCRITERIA_PARTIAL:
1010                 return MATCHCRITERIA_NOT_PARTIAL;
1011         case MATCHCRITERIA_COLORLABEL:
1012                 return MATCHCRITERIA_NOT_COLORLABEL;
1013         case MATCHCRITERIA_IGNORE_THREAD:
1014                 return MATCHCRITERIA_NOT_IGNORE_THREAD;
1015         case MATCHCRITERIA_SUBJECT:
1016                 return MATCHCRITERIA_NOT_SUBJECT;
1017         case MATCHCRITERIA_FROM:
1018                 return MATCHCRITERIA_NOT_FROM;
1019         case MATCHCRITERIA_TO:
1020                 return MATCHCRITERIA_NOT_TO;
1021         case MATCHCRITERIA_CC:
1022                 return MATCHCRITERIA_NOT_CC;
1023         case MATCHCRITERIA_TO_OR_CC:
1024                 return MATCHCRITERIA_NOT_TO_AND_NOT_CC;
1025         case MATCHCRITERIA_NEWSGROUPS:
1026                 return MATCHCRITERIA_NOT_NEWSGROUPS;
1027         case MATCHCRITERIA_INREPLYTO:
1028                 return MATCHCRITERIA_NOT_INREPLYTO;
1029         case MATCHCRITERIA_REFERENCES:
1030                 return MATCHCRITERIA_NOT_REFERENCES;
1031         case MATCHCRITERIA_HEADER:
1032                 return MATCHCRITERIA_NOT_HEADER;
1033         case MATCHCRITERIA_HEADERS_PART:
1034                 return MATCHCRITERIA_NOT_HEADERS_PART;
1035         case MATCHCRITERIA_MESSAGE:
1036                 return MATCHCRITERIA_NOT_MESSAGE;
1037         case MATCHCRITERIA_TEST:
1038                 return MATCHCRITERIA_NOT_TEST;
1039         case MATCHCRITERIA_BODY_PART:
1040                 return MATCHCRITERIA_NOT_BODY_PART;
1041         default:
1042                 return matcher_criteria;
1043         }
1044 }
1045
1046 /*!
1047  *\brief        Converts the text in the selected row to a 
1048  *              matcher structure
1049  *
1050  *\return       MatcherProp * Newly allocated matcher structure.
1051  */
1052 static MatcherProp *prefs_matcher_dialog_to_matcher(void)
1053 {
1054         MatcherProp *matcherprop;
1055         gint criteria;
1056         gint matchtype;
1057         gint value_pred;
1058         gint value_pred_flag;
1059         gint value_criteria;
1060         gboolean use_regexp;
1061         gboolean case_sensitive;
1062         gchar *header;
1063         gchar *expr;
1064         gint value;
1065         gchar *value_str;
1066
1067         value_criteria = get_sel_from_list(GTK_LIST(matcher.criteria_list));
1068
1069         criteria = prefs_matcher_get_matching_from_criteria(value_criteria);
1070
1071         value_pred = get_sel_from_list(GTK_LIST(matcher.predicate_list));
1072         value_pred_flag = get_sel_from_list(GTK_LIST(matcher.predicate_flag_list));
1073
1074         use_regexp = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(matcher.regexp_chkbtn));
1075         case_sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(matcher.case_chkbtn));
1076
1077         switch (value_criteria) {
1078         case CRITERIA_UNREAD:
1079         case CRITERIA_NEW:
1080         case CRITERIA_MARKED:
1081         case CRITERIA_DELETED:
1082         case CRITERIA_REPLIED:
1083         case CRITERIA_FORWARDED:
1084         case CRITERIA_LOCKED:
1085         case CRITERIA_PARTIAL:
1086         case CRITERIA_TEST:
1087         case CRITERIA_COLORLABEL:
1088         case CRITERIA_IGNORE_THREAD:
1089                 if (value_pred_flag == PREDICATE_FLAG_DISABLED)
1090                         criteria = prefs_matcher_not_criteria(criteria);
1091                 break;
1092         case CRITERIA_SUBJECT:
1093         case CRITERIA_FROM:
1094         case CRITERIA_TO:
1095         case CRITERIA_CC:
1096         case CRITERIA_TO_OR_CC:
1097         case CRITERIA_NEWSGROUPS:
1098         case CRITERIA_INREPLYTO:
1099         case CRITERIA_REFERENCES:
1100         case CRITERIA_HEADERS_PART:
1101         case CRITERIA_BODY_PART:
1102         case CRITERIA_MESSAGE:
1103         case CRITERIA_AGE_GREATER:
1104         case CRITERIA_AGE_LOWER:
1105         case CRITERIA_HEADER:
1106                 if (value_pred == PREDICATE_DOES_NOT_CONTAIN)
1107                         criteria = prefs_matcher_not_criteria(criteria);
1108                 break;
1109         }
1110
1111         if (use_regexp) {
1112                 if (case_sensitive)
1113                         matchtype = MATCHTYPE_REGEXP;
1114                 else
1115                         matchtype = MATCHTYPE_REGEXPCASE;
1116         }
1117         else {
1118                 if (case_sensitive)
1119                         matchtype = MATCHTYPE_MATCH;
1120                 else
1121                         matchtype = MATCHTYPE_MATCHCASE;
1122         }
1123
1124         header = NULL;
1125         expr = NULL;
1126         value = 0;
1127
1128         switch (value_criteria) {
1129         case CRITERIA_ALL:
1130         case CRITERIA_UNREAD:
1131         case CRITERIA_NEW:
1132         case CRITERIA_MARKED:
1133         case CRITERIA_DELETED:
1134         case CRITERIA_REPLIED:
1135         case CRITERIA_FORWARDED:
1136         case CRITERIA_LOCKED:
1137         case CRITERIA_PARTIAL:
1138         case CRITERIA_IGNORE_THREAD:
1139                 break;
1140
1141         case CRITERIA_SUBJECT:
1142         case CRITERIA_FROM:
1143         case CRITERIA_TO:
1144         case CRITERIA_CC:
1145         case CRITERIA_TO_OR_CC:
1146         case CRITERIA_NEWSGROUPS:
1147         case CRITERIA_INREPLYTO:
1148         case CRITERIA_REFERENCES:
1149         case CRITERIA_HEADERS_PART:
1150         case CRITERIA_BODY_PART:
1151         case CRITERIA_MESSAGE:
1152         case CRITERIA_TEST:
1153                 expr = gtk_entry_get_text(GTK_ENTRY(matcher.value_entry));
1154                 break;
1155
1156         case CRITERIA_AGE_GREATER:
1157         case CRITERIA_AGE_LOWER:
1158         case CRITERIA_SCORE_GREATER:
1159         case CRITERIA_SCORE_LOWER:
1160         case CRITERIA_SCORE_EQUAL:
1161         case CRITERIA_SIZE_GREATER:
1162         case CRITERIA_SIZE_SMALLER:
1163         case CRITERIA_SIZE_EQUAL:
1164                 value_str = gtk_entry_get_text(GTK_ENTRY(matcher.value_entry));
1165
1166                 if (*value_str == '\0') {
1167                     alertpanel_error(_("Value is not set."));
1168                     return NULL;
1169                 }
1170
1171                 value = atoi(value_str);
1172
1173                 break;
1174                 
1175         case CRITERIA_COLORLABEL:
1176                 value = colorlabel_get_color_menu_active_item
1177                         (gtk_option_menu_get_menu(GTK_OPTION_MENU
1178                                 (matcher.color_optmenu))); 
1179                 break;
1180
1181         case CRITERIA_HEADER:
1182
1183                 header = gtk_entry_get_text(GTK_ENTRY(matcher.header_entry));
1184                 expr = gtk_entry_get_text(GTK_ENTRY(matcher.value_entry));
1185
1186                 if (*header == '\0') {
1187                     alertpanel_error(_("Header name is not set."));
1188                     return NULL;
1189                 }
1190                 break;
1191         }
1192
1193         matcherprop = matcherprop_new(criteria, header, matchtype,
1194                                       expr, value);
1195
1196         return matcherprop;
1197 }
1198
1199 /*!
1200  *\brief        Signal handler for register button
1201  */
1202 static void prefs_matcher_register_cb(void)
1203 {
1204         MatcherProp *matcherprop;
1205         
1206         matcherprop = prefs_matcher_dialog_to_matcher();
1207         if (matcherprop == NULL)
1208                 return;
1209
1210         prefs_matcher_clist_set_row(-1, matcherprop);
1211
1212         matcherprop_free(matcherprop);
1213
1214         prefs_matcher_reset_condition();
1215         prefs_matcher_update_hscrollbar();
1216 }
1217
1218 /*!
1219  *\brief        Signal handler for substitute button
1220  */
1221 static void prefs_matcher_substitute_cb(void)
1222 {
1223         GtkCList *clist = GTK_CLIST(matcher.cond_clist);
1224         gint row;
1225         MatcherProp *matcherprop;
1226
1227         if (!clist->selection) return;
1228         row = GPOINTER_TO_INT(clist->selection->data);
1229         if (row == 0)
1230                 return;
1231         
1232         matcherprop = prefs_matcher_dialog_to_matcher();
1233         if (matcherprop == NULL)
1234                 return;
1235
1236         prefs_matcher_clist_set_row(row, matcherprop);
1237
1238         matcherprop_free(matcherprop);
1239
1240         prefs_matcher_reset_condition();
1241         
1242         prefs_matcher_update_hscrollbar();
1243 }
1244
1245 /*!
1246  *\brief        Signal handler for delete button
1247  */
1248 static void prefs_matcher_delete_cb(void)
1249 {
1250         GtkCList *clist = GTK_CLIST(matcher.cond_clist);
1251         gint row;
1252
1253         if (!clist->selection) return;
1254         row = GPOINTER_TO_INT(clist->selection->data);
1255         if (row == 0)
1256                 return;
1257
1258         gtk_clist_remove(clist, row);
1259
1260         prefs_matcher_reset_condition();
1261
1262         prefs_matcher_update_hscrollbar();
1263 }
1264
1265 /*!
1266  *\brief        Signal handler for 'move up' button
1267  */
1268 static void prefs_matcher_up(void)
1269 {
1270         GtkCList *clist = GTK_CLIST(matcher.cond_clist);
1271         gint row;
1272
1273         if (!clist->selection) return;
1274
1275         row = GPOINTER_TO_INT(clist->selection->data);
1276         if (row > 1) {
1277                 gtk_clist_row_move(clist, row, row - 1);
1278                 if (gtk_clist_row_is_visible(clist, row - 1) != GTK_VISIBILITY_FULL)
1279                         gtk_clist_moveto(clist, row - 1, 0, 0, 0);
1280         }
1281 }
1282
1283 /*!
1284  *\brief        Signal handler for 'move down' button
1285  */
1286 static void prefs_matcher_down(void)
1287 {
1288         GtkCList *clist = GTK_CLIST(matcher.cond_clist);
1289         gint row;
1290
1291         if (!clist->selection) return;
1292
1293         row = GPOINTER_TO_INT(clist->selection->data);
1294         if (row >= 1 && row < clist->rows - 1) {
1295                 gtk_clist_row_move(clist, row, row + 1);
1296                 if (gtk_clist_row_is_visible(clist, row + 1) != GTK_VISIBILITY_FULL)
1297                         gtk_clist_moveto(clist, row + 1, 0, 1, 0);
1298         }
1299 }
1300
1301 /*!
1302  *\brief        Signal handler for select row.
1303  *
1304  *\param        clist List widget
1305  *\param        row Selected row
1306  *\param        column Selected column
1307  *\param        event Event information
1308  */
1309 static void prefs_matcher_select(GtkCList *clist, gint row, gint column,
1310                                  GdkEvent *event)
1311 {
1312         gchar *matcher_str;
1313         MatcherProp *prop;
1314         gboolean negative_cond;
1315         gint criteria;
1316
1317         if (!gtk_clist_get_text(GTK_CLIST(matcher.cond_clist),
1318                                 row, 0, &matcher_str))
1319                 return;
1320
1321         negative_cond = FALSE;
1322
1323         if (row == 0) {
1324                 prefs_matcher_reset_condition();
1325                 return;
1326         }
1327
1328         prop = matcher_parser_get_prop(matcher_str);
1329         if (prop == NULL)
1330                 return;
1331
1332         criteria = prefs_matcher_get_criteria_from_matching(prop->criteria);
1333         if (criteria != -1)
1334                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
1335                                      criteria);
1336
1337         switch(prop->criteria) {
1338         case MATCHCRITERIA_NOT_UNREAD:
1339         case MATCHCRITERIA_NOT_NEW:
1340         case MATCHCRITERIA_NOT_MARKED:
1341         case MATCHCRITERIA_NOT_DELETED:
1342         case MATCHCRITERIA_NOT_REPLIED:
1343         case MATCHCRITERIA_NOT_FORWARDED:
1344         case MATCHCRITERIA_NOT_LOCKED:
1345         case MATCHCRITERIA_NOT_PARTIAL:
1346         case MATCHCRITERIA_NOT_COLORLABEL:
1347         case MATCHCRITERIA_NOT_IGNORE_THREAD:
1348         case MATCHCRITERIA_NOT_SUBJECT:
1349         case MATCHCRITERIA_NOT_FROM:
1350         case MATCHCRITERIA_NOT_TO:
1351         case MATCHCRITERIA_NOT_CC:
1352         case MATCHCRITERIA_NOT_NEWSGROUPS:
1353         case MATCHCRITERIA_NOT_INREPLYTO:
1354         case MATCHCRITERIA_NOT_REFERENCES:
1355         case MATCHCRITERIA_NOT_TO_AND_NOT_CC:
1356         case MATCHCRITERIA_NOT_BODY_PART:
1357         case MATCHCRITERIA_NOT_MESSAGE:
1358         case MATCHCRITERIA_NOT_HEADERS_PART:
1359         case MATCHCRITERIA_NOT_HEADER:
1360                 negative_cond = TRUE;
1361                 break;
1362         }
1363         
1364         switch(prop->criteria) {
1365         case MATCHCRITERIA_ALL:
1366                 break;
1367
1368         case MATCHCRITERIA_NOT_SUBJECT:
1369         case MATCHCRITERIA_NOT_FROM:
1370         case MATCHCRITERIA_NOT_TO:
1371         case MATCHCRITERIA_NOT_CC:
1372         case MATCHCRITERIA_NOT_TO_AND_NOT_CC:
1373         case MATCHCRITERIA_NOT_NEWSGROUPS:
1374         case MATCHCRITERIA_NOT_INREPLYTO:
1375         case MATCHCRITERIA_NOT_REFERENCES:
1376         case MATCHCRITERIA_NOT_HEADERS_PART:
1377         case MATCHCRITERIA_NOT_BODY_PART:
1378         case MATCHCRITERIA_NOT_MESSAGE:
1379         case MATCHCRITERIA_SUBJECT:
1380         case MATCHCRITERIA_FROM:
1381         case MATCHCRITERIA_TO:
1382         case MATCHCRITERIA_CC:
1383         case MATCHCRITERIA_TO_OR_CC:
1384         case MATCHCRITERIA_NEWSGROUPS:
1385         case MATCHCRITERIA_INREPLYTO:
1386         case MATCHCRITERIA_REFERENCES:
1387         case MATCHCRITERIA_HEADERS_PART:
1388         case MATCHCRITERIA_BODY_PART:
1389         case MATCHCRITERIA_MESSAGE:
1390         case MATCHCRITERIA_TEST:
1391                 gtk_entry_set_text(GTK_ENTRY(matcher.value_entry), prop->expr);
1392                 break;
1393
1394         case MATCHCRITERIA_AGE_GREATER:
1395         case MATCHCRITERIA_AGE_LOWER:
1396         case MATCHCRITERIA_SCORE_GREATER:
1397         case MATCHCRITERIA_SCORE_LOWER:
1398         case MATCHCRITERIA_SCORE_EQUAL:
1399         case MATCHCRITERIA_SIZE_GREATER:
1400         case MATCHCRITERIA_SIZE_SMALLER:
1401         case MATCHCRITERIA_SIZE_EQUAL:
1402                 gtk_entry_set_text(GTK_ENTRY(matcher.value_entry), itos(prop->value));
1403                 break;
1404
1405         case MATCHCRITERIA_NOT_COLORLABEL:
1406         case MATCHCRITERIA_COLORLABEL:
1407                 gtk_option_menu_set_history(GTK_OPTION_MENU(matcher.color_optmenu),
1408                                             prop->value);
1409                 break;
1410
1411         case MATCHCRITERIA_NOT_HEADER:
1412         case MATCHCRITERIA_HEADER:
1413                 gtk_entry_set_text(GTK_ENTRY(matcher.header_entry), prop->header);
1414                 gtk_entry_set_text(GTK_ENTRY(matcher.value_entry), prop->expr);
1415                 break;
1416         }
1417
1418         if (negative_cond)
1419                 gtk_list_select_item(GTK_LIST(matcher.predicate_list), 1);
1420         else
1421                 gtk_list_select_item(GTK_LIST(matcher.predicate_list), 0);
1422
1423         switch(prop->matchtype) {
1424         case MATCHTYPE_MATCH:
1425                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_chkbtn), FALSE);
1426                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_chkbtn), TRUE);
1427                 break;
1428
1429         case MATCHTYPE_MATCHCASE:
1430                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_chkbtn), FALSE);
1431                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_chkbtn), FALSE);
1432                 break;
1433
1434         case MATCHTYPE_REGEXP:
1435                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_chkbtn), TRUE);
1436                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_chkbtn), TRUE);
1437                 break;
1438
1439         case MATCHTYPE_REGEXPCASE:
1440                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_chkbtn), TRUE);
1441                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_chkbtn), FALSE);
1442                 break;
1443         }
1444 }
1445
1446 /*!
1447  *\brief        Helper function that allows us to replace the 'Value' entry box
1448  *              by another widget.
1449  *
1450  *\param        old_widget Widget that needs to be removed.
1451  *\param        new_widget Replacement widget
1452  */
1453 static void prefs_matcher_set_value_widget(GtkWidget *old_widget, 
1454                                            GtkWidget *new_widget)
1455 {
1456         /* TODO: find out why the following spews harmless "parent errors" */
1457
1458         /* NOTE: we first need to bump up the refcount of the old_widget,
1459          * because the gtkut_container_remove() will otherwise destroy it */
1460         gtk_widget_ref(old_widget);
1461         gtkut_container_remove(GTK_CONTAINER(matcher.criteria_table), old_widget);
1462         gtk_widget_show(new_widget);
1463         gtk_widget_set_usize(new_widget, 200, -1);
1464         gtk_table_attach(GTK_TABLE(matcher.criteria_table), new_widget, 
1465                          2, 3, 1, 2, 
1466                          GTK_FILL | GTK_SHRINK | GTK_EXPAND, 
1467                          0, 0, 0);
1468 }
1469
1470 /*!
1471  *\brief        Change widgets depending on the selected condition
1472  *
1473  *\param        list List widget
1474  *\param        widget Not used
1475  *\param        user_data Not used      
1476  */
1477 static void prefs_matcher_criteria_select(GtkList *list,
1478                                           GtkWidget *widget,
1479                                           gpointer user_data)
1480 {
1481         gint value, old_value;
1482
1483         old_value = matcher.selected_criteria;
1484         matcher.selected_criteria = value = get_sel_from_list
1485                 (GTK_LIST(matcher.criteria_list));
1486         
1487         /* CLAWS: the value widget is currently either the color label combo box,
1488          * or a GtkEntry, so kiss for now */
1489         if (matcher.selected_criteria == CRITERIA_COLORLABEL) { 
1490                 prefs_matcher_set_value_widget(matcher.value_entry, 
1491                                                matcher.color_optmenu);
1492         } else if (old_value == CRITERIA_COLORLABEL) {
1493                 prefs_matcher_set_value_widget(matcher.color_optmenu,
1494                                                matcher.value_entry);
1495         }                                              
1496
1497         switch (value) {
1498         case CRITERIA_ALL:
1499                 gtk_widget_set_sensitive(matcher.header_combo, FALSE);
1500                 gtk_widget_set_sensitive(matcher.header_label, FALSE);
1501                 gtk_widget_set_sensitive(matcher.value_label, FALSE);
1502                 gtk_widget_set_sensitive(matcher.value_entry, FALSE);
1503                 gtk_widget_set_sensitive(matcher.predicate_label, FALSE);
1504                 gtk_widget_set_sensitive(matcher.predicate_combo, FALSE);
1505                 gtk_widget_set_sensitive(matcher.predicate_flag_combo, FALSE);
1506                 gtk_widget_hide(matcher.predicate_combo);
1507                 gtk_widget_show(matcher.predicate_flag_combo);
1508                 gtk_widget_set_sensitive(matcher.case_chkbtn, FALSE);
1509                 gtk_widget_set_sensitive(matcher.regexp_chkbtn, FALSE);
1510                 gtk_widget_set_sensitive(matcher.test_btn, FALSE);
1511                 break;
1512
1513         case CRITERIA_UNREAD:
1514         case CRITERIA_NEW:
1515         case CRITERIA_MARKED:
1516         case CRITERIA_DELETED:
1517         case CRITERIA_REPLIED:
1518         case CRITERIA_FORWARDED:
1519         case CRITERIA_LOCKED:
1520         case CRITERIA_PARTIAL:
1521         case CRITERIA_IGNORE_THREAD:
1522                 gtk_widget_set_sensitive(matcher.header_combo, FALSE);
1523                 gtk_widget_set_sensitive(matcher.header_label, FALSE);
1524                 gtk_widget_set_sensitive(matcher.value_label, FALSE);
1525                 gtk_widget_set_sensitive(matcher.value_entry, FALSE);
1526                 gtk_widget_set_sensitive(matcher.predicate_label, TRUE);
1527                 gtk_widget_set_sensitive(matcher.predicate_combo, FALSE);
1528                 gtk_widget_set_sensitive(matcher.predicate_flag_combo, TRUE);
1529                 gtk_widget_hide(matcher.predicate_combo);
1530                 gtk_widget_show(matcher.predicate_flag_combo);
1531                 gtk_widget_set_sensitive(matcher.case_chkbtn, FALSE);
1532                 gtk_widget_set_sensitive(matcher.regexp_chkbtn, FALSE);
1533                 gtk_widget_set_sensitive(matcher.test_btn, FALSE);
1534                 break;
1535                 
1536         case CRITERIA_COLORLABEL:
1537                 gtk_widget_set_sensitive(matcher.header_combo, FALSE);
1538                 gtk_widget_set_sensitive(matcher.header_label, FALSE);
1539                 gtk_widget_set_sensitive(matcher.value_label, TRUE);
1540                 gtk_widget_set_sensitive(matcher.predicate_label, TRUE);
1541                 gtk_widget_set_sensitive(matcher.predicate_combo, FALSE);
1542                 gtk_widget_set_sensitive(matcher.predicate_flag_combo, TRUE);
1543                 gtk_widget_hide(matcher.predicate_combo);
1544                 gtk_widget_show(matcher.predicate_flag_combo);
1545                 gtk_widget_set_sensitive(matcher.case_chkbtn, FALSE);
1546                 gtk_widget_set_sensitive(matcher.regexp_chkbtn, FALSE);
1547                 gtk_widget_set_sensitive(matcher.test_btn, FALSE);
1548                 break;
1549
1550         case CRITERIA_SUBJECT:
1551         case CRITERIA_FROM:
1552         case CRITERIA_TO:
1553         case CRITERIA_CC:
1554         case CRITERIA_TO_OR_CC:
1555         case CRITERIA_NEWSGROUPS:
1556         case CRITERIA_INREPLYTO:
1557         case CRITERIA_REFERENCES:
1558         case CRITERIA_HEADERS_PART:
1559         case CRITERIA_BODY_PART:
1560         case CRITERIA_MESSAGE:
1561                 gtk_widget_set_sensitive(matcher.header_combo, FALSE);
1562                 gtk_widget_set_sensitive(matcher.header_label, FALSE);
1563                 gtk_widget_set_sensitive(matcher.value_label, TRUE);
1564                 gtk_widget_set_sensitive(matcher.value_entry, TRUE);
1565                 gtk_widget_set_sensitive(matcher.predicate_label, TRUE);
1566                 gtk_widget_set_sensitive(matcher.predicate_combo, TRUE);
1567                 gtk_widget_set_sensitive(matcher.predicate_flag_combo, FALSE);
1568                 gtk_widget_show(matcher.predicate_combo);
1569                 gtk_widget_hide(matcher.predicate_flag_combo);
1570                 gtk_widget_set_sensitive(matcher.case_chkbtn, TRUE);
1571                 gtk_widget_set_sensitive(matcher.regexp_chkbtn, TRUE);
1572                 gtk_widget_set_sensitive(matcher.test_btn, FALSE);
1573                 break;
1574
1575         case CRITERIA_TEST:
1576                 gtk_widget_set_sensitive(matcher.header_combo, FALSE);
1577                 gtk_widget_set_sensitive(matcher.header_label, FALSE);
1578                 gtk_widget_set_sensitive(matcher.value_label, TRUE);
1579                 gtk_widget_set_sensitive(matcher.value_entry, TRUE);
1580                 gtk_widget_set_sensitive(matcher.predicate_label, TRUE);
1581                 gtk_widget_set_sensitive(matcher.predicate_combo, FALSE);
1582                 gtk_widget_set_sensitive(matcher.predicate_flag_combo, TRUE);
1583                 gtk_widget_hide(matcher.predicate_combo);
1584                 gtk_widget_show(matcher.predicate_flag_combo);
1585                 gtk_widget_set_sensitive(matcher.case_chkbtn, FALSE);
1586                 gtk_widget_set_sensitive(matcher.regexp_chkbtn, FALSE);
1587                 gtk_widget_set_sensitive(matcher.test_btn, TRUE);
1588                 break;
1589
1590         case CRITERIA_AGE_GREATER:
1591         case CRITERIA_AGE_LOWER:
1592         case CRITERIA_SCORE_GREATER:
1593         case CRITERIA_SCORE_LOWER:
1594         case CRITERIA_SCORE_EQUAL:
1595         case CRITERIA_SIZE_GREATER:
1596         case CRITERIA_SIZE_SMALLER:
1597         case CRITERIA_SIZE_EQUAL:
1598                 gtk_widget_set_sensitive(matcher.header_combo, FALSE);
1599                 gtk_widget_set_sensitive(matcher.header_label, FALSE);
1600                 gtk_widget_set_sensitive(matcher.value_label, TRUE);
1601                 gtk_widget_set_sensitive(matcher.value_entry, TRUE);
1602                 gtk_widget_set_sensitive(matcher.predicate_label, FALSE);
1603                 gtk_widget_set_sensitive(matcher.predicate_combo, FALSE);
1604                 gtk_widget_set_sensitive(matcher.predicate_flag_combo, FALSE);
1605                 gtk_widget_show(matcher.predicate_combo);
1606                 gtk_widget_hide(matcher.predicate_flag_combo);
1607                 gtk_widget_set_sensitive(matcher.case_chkbtn, FALSE);
1608                 gtk_widget_set_sensitive(matcher.regexp_chkbtn, FALSE);
1609                 gtk_widget_set_sensitive(matcher.test_btn, FALSE);
1610                 break;
1611
1612         case CRITERIA_HEADER:
1613                 gtk_widget_set_sensitive(matcher.header_combo, TRUE);
1614                 gtk_widget_set_sensitive(matcher.header_label, TRUE);
1615                 gtk_widget_set_sensitive(matcher.value_label, TRUE);
1616                 gtk_widget_set_sensitive(matcher.value_entry, TRUE);
1617                 gtk_widget_set_sensitive(matcher.predicate_label, TRUE);
1618                 gtk_widget_set_sensitive(matcher.predicate_combo, TRUE);
1619                 gtk_widget_set_sensitive(matcher.predicate_flag_combo, FALSE);
1620                 gtk_widget_show(matcher.predicate_combo);
1621                 gtk_widget_hide(matcher.predicate_flag_combo);
1622                 gtk_widget_set_sensitive(matcher.case_chkbtn, TRUE);
1623                 gtk_widget_set_sensitive(matcher.regexp_chkbtn, TRUE);
1624                 gtk_widget_set_sensitive(matcher.test_btn, FALSE);
1625                 break;
1626         }
1627 }
1628
1629 /*!
1630  *\brief        Handle key press
1631  *
1632  *\param        widget Widget receiving key press
1633  *\param        event Key event
1634  *\param        data User data
1635  */
1636 static gboolean prefs_matcher_key_pressed(GtkWidget *widget, GdkEventKey *event,
1637                                      gpointer data)
1638 {
1639         if (event && event->keyval == GDK_Escape)
1640                 prefs_matcher_cancel();
1641         return TRUE;            
1642 }
1643
1644 /*!
1645  *\brief        Cancel matcher dialog
1646  */
1647 static void prefs_matcher_cancel(void)
1648 {
1649         gtk_widget_hide(matcher.window);
1650         inc_unlock();
1651 }
1652
1653 /*!
1654  *\brief        Accept current matchers
1655  */
1656 static void prefs_matcher_ok(void)
1657 {
1658         MatcherList *matchers;
1659         MatcherProp *matcherprop;
1660         AlertValue val;
1661         gchar *matcher_str;
1662         gchar *str = NULL;
1663         gint row = 1;
1664
1665         matchers = prefs_matcher_get_list();
1666
1667         if (matchers != NULL) {
1668                 matcherprop = prefs_matcher_dialog_to_matcher();
1669                 if (matcherprop != NULL) {
1670                         str = matcherprop_to_string(matcherprop);
1671                         matcherprop_free(matcherprop);
1672                         if(strcmp(str, "all") != 0) {
1673                                 while (gtk_clist_get_text(GTK_CLIST(matcher.cond_clist),
1674                                                   row, 0, &matcher_str)) {
1675                                         if (strcmp(matcher_str, str) == 0) break;
1676                                         row++;
1677                                 }
1678
1679                                 if (strcmp(matcher_str, str) != 0) {
1680                                         val = alertpanel(_("Entry not saved"),
1681                                                  _("The entry was not saved\nHave you really finished?"),
1682                                                  _("Yes"), _("No"), NULL);
1683                                         if (G_ALERTDEFAULT != val) {
1684                                                 g_free(str);
1685                                                 return;
1686                                         }
1687                                 }
1688                         }
1689                 }
1690                 g_free(str);
1691                 gtk_widget_hide(matcher.window);
1692                 inc_unlock();
1693                 if (matchers_callback != NULL)
1694                         matchers_callback(matchers);
1695                 matcherlist_free(matchers);
1696         }
1697 }
1698
1699 /*!
1700  *\brief        Called when closing dialog box
1701  *
1702  *\param        widget Dialog widget
1703  *\param        event Event info
1704  *\param        data User data
1705  *
1706  *\return       gint TRUE
1707  */
1708 static gint prefs_matcher_deleted(GtkWidget *widget, GdkEventAny *event,
1709                                   gpointer data)
1710 {
1711         prefs_matcher_cancel();
1712         return TRUE;
1713 }
1714
1715 /*
1716  * Strings describing test format strings
1717  * 
1718  * When adding new lines, remember to put 2 strings for each line
1719  */
1720 static gchar *test_desc_strings[] = {
1721         N_("'Test' allows you to test a message or message element"), NULL,
1722         N_("using an external program or script. The program will"), NULL,
1723         N_("return either 0 or 1"), NULL,
1724         N_("The following symbols can be used:"), NULL,
1725         "%%",   "%",
1726         "%s",   N_("Subject"),
1727         "%f",   N_("From"),
1728         "%t",   N_("To"),
1729         "%c",   N_("Cc"),
1730         "%d",   N_("Date"),
1731         "%i",   N_("Message-ID"),
1732         "%n",   N_("Newsgroups"),
1733         "%r",   N_("References"),
1734         "%F",   N_("Filename - should not be modified"),
1735         "\\n",  N_("new line"),
1736         "\\",   N_("escape character for quotes"),
1737         "\\\"", N_("quote character"),
1738         NULL,   NULL
1739 };
1740
1741 static DescriptionWindow test_desc_win = { 
1742         NULL, 
1743         2,
1744         N_("Match Type: 'Test'"),
1745         test_desc_strings
1746 };
1747
1748
1749
1750 /*!
1751  *\brief        Show Test action's info
1752  */
1753 void prefs_matcher_test_info(void)
1754 {
1755         description_window_create(&test_desc_win);
1756 }
1757