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