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