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