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