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