2005-11-10 [wwp] 1.9.100cvs6
[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, 60, -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         matcher.selected_criteria = -1;
709         prefs_matcher_criteria_select(GTK_LIST(criteria_list), NULL, NULL);
710 }
711
712 /*!
713  *\brief        Set the contents of a row
714  *
715  *\param        row Index of row to set
716  *\param        prop Condition to set
717  *
718  *\return       gint Row index \a prop has been added
719  */
720 static void prefs_matcher_list_view_set_row(GtkTreeIter *row, MatcherProp *prop)
721 {
722         gchar *matcher_str;
723
724         if (prop == NULL) {
725                 prefs_matcher_list_view_insert_matcher(matcher.cond_list_view,
726                                                        NULL, _("New"), FALSE);
727                 return;                                                
728         }
729
730         matcher_str = matcherprop_to_string(prop);
731         if (!row)
732                 prefs_matcher_list_view_insert_matcher(matcher.cond_list_view,
733                                                        NULL, matcher_str,
734                                                        TRUE);
735         else
736                 prefs_matcher_list_view_insert_matcher(matcher.cond_list_view,
737                                                        row, matcher_str, 
738                                                        TRUE);
739         g_free(matcher_str);
740 }
741
742 /*!
743  *\brief        Clears a condition in the list widget
744  */
745 static void prefs_matcher_reset_condition(void)
746 {
747         gtk_list_select_item(GTK_LIST(matcher.criteria_list), 0);
748         gtk_list_select_item(GTK_LIST(matcher.predicate_list), 0);
749         gtk_entry_set_text(GTK_ENTRY(matcher.header_entry), "");
750         gtk_entry_set_text(GTK_ENTRY(matcher.value_entry), "");
751 }
752
753 /*!
754  *\brief        Initializes dialog with a set of conditions
755  *
756  *\param        matchers List of conditions
757  */
758 static void prefs_matcher_set_dialog(MatcherList *matchers)
759 {
760         GSList *cur;
761         gboolean bool_op = 1;
762         GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model
763                                 (GTK_TREE_VIEW(matcher.cond_list_view)));
764
765         gtk_list_store_clear(store);                            
766
767         prefs_matcher_list_view_set_row(NULL, NULL);
768         if (matchers != NULL) {
769                 for (cur = matchers->matchers; cur != NULL;
770                      cur = g_slist_next(cur)) {
771                         MatcherProp *prop;
772                         prop = (MatcherProp *) cur->data;
773                         prefs_matcher_list_view_set_row(NULL, prop);
774                 }
775
776                 bool_op = matchers->bool_and;
777         }
778         
779         gtk_list_select_item(GTK_LIST(matcher.bool_op_list), bool_op);
780
781         prefs_matcher_reset_condition();
782 }
783
784 /*!
785  *\brief        Converts current conditions in list box in
786  *              a matcher list used by the matcher.
787  *
788  *\return       MatcherList * List of conditions.
789  */
790 static MatcherList *prefs_matcher_get_list(void)
791 {
792         gchar *matcher_str;
793         MatcherProp *prop;
794         gboolean bool_and;
795         GSList *matcher_list;
796         MatcherList *matchers;
797         GtkTreeModel *model;
798         GtkTreeIter iter;
799
800         model = gtk_tree_view_get_model(GTK_TREE_VIEW(matcher.cond_list_view));
801         if (!gtk_tree_model_get_iter_first(model, &iter))
802                 return NULL;
803
804         matcher_list = NULL;
805
806         do {
807                 gboolean is_valid;
808         
809                 gtk_tree_model_get(model, &iter,
810                                    PREFS_MATCHER_COND, &matcher_str,
811                                    PREFS_MATCHER_COND_VALID, &is_valid,
812                                    -1);
813                 
814                 if (is_valid) {
815                         /* tmp = matcher_str; */
816                         prop = matcher_parser_get_prop(matcher_str);
817                         g_free(matcher_str);
818                         if (prop == NULL)
819                                 break;
820                         
821                         matcher_list = g_slist_append(matcher_list, prop);
822                 }
823         } while (gtk_tree_model_iter_next(model, &iter));
824
825         bool_and = get_sel_from_list(GTK_LIST(matcher.bool_op_list));
826
827         matchers = matcherlist_new(matcher_list, bool_and);
828
829         return matchers;
830 }
831
832 /*!
833  *\brief        Maps a keyword id (see #get_matchparser_tab_id) to a 
834  *              criteria type (see first parameter of #matcherprop_new
835  *              or #matcherprop_unquote_new)
836  *
837  *\param        matching_id Id returned by the matcher parser.
838  *
839  *\return       gint One of the CRITERIA_xxx constants.
840  */
841 static gint prefs_matcher_get_criteria_from_matching(gint matching_id)
842 {
843         switch(matching_id) {
844         case MATCHCRITERIA_ALL:
845                 return CRITERIA_ALL;
846         case MATCHCRITERIA_NOT_UNREAD:
847         case MATCHCRITERIA_UNREAD:
848                 return CRITERIA_UNREAD;
849         case MATCHCRITERIA_NOT_NEW:
850         case MATCHCRITERIA_NEW:
851                 return CRITERIA_NEW;
852         case MATCHCRITERIA_NOT_MARKED:
853         case MATCHCRITERIA_MARKED:
854                 return CRITERIA_MARKED;
855         case MATCHCRITERIA_NOT_DELETED:
856         case MATCHCRITERIA_DELETED:
857                 return CRITERIA_DELETED;
858         case MATCHCRITERIA_NOT_REPLIED:
859         case MATCHCRITERIA_REPLIED:
860                 return CRITERIA_REPLIED;
861         case MATCHCRITERIA_NOT_FORWARDED:
862         case MATCHCRITERIA_FORWARDED:
863                 return CRITERIA_FORWARDED;
864         case MATCHCRITERIA_LOCKED:
865         case MATCHCRITERIA_NOT_LOCKED:
866                 return CRITERIA_LOCKED;
867         case MATCHCRITERIA_PARTIAL:
868         case MATCHCRITERIA_NOT_PARTIAL:
869                 return CRITERIA_PARTIAL;
870         case MATCHCRITERIA_COLORLABEL:
871         case MATCHCRITERIA_NOT_COLORLABEL:
872                 return CRITERIA_COLORLABEL;
873         case MATCHCRITERIA_IGNORE_THREAD:
874         case MATCHCRITERIA_NOT_IGNORE_THREAD:
875                 return CRITERIA_IGNORE_THREAD;
876         case MATCHCRITERIA_NOT_SUBJECT:
877         case MATCHCRITERIA_SUBJECT:
878                 return CRITERIA_SUBJECT;
879         case MATCHCRITERIA_NOT_FROM:
880         case MATCHCRITERIA_FROM:
881                 return CRITERIA_FROM;
882         case MATCHCRITERIA_NOT_TO:
883         case MATCHCRITERIA_TO:
884                 return CRITERIA_TO;
885         case MATCHCRITERIA_NOT_CC:
886         case MATCHCRITERIA_CC:
887                 return CRITERIA_CC;
888         case MATCHCRITERIA_NOT_NEWSGROUPS:
889         case MATCHCRITERIA_NEWSGROUPS:
890                 return CRITERIA_NEWSGROUPS;
891         case MATCHCRITERIA_NOT_INREPLYTO:
892         case MATCHCRITERIA_INREPLYTO:
893                 return CRITERIA_INREPLYTO;
894         case MATCHCRITERIA_NOT_REFERENCES:
895         case MATCHCRITERIA_REFERENCES:
896                 return CRITERIA_REFERENCES;
897         case MATCHCRITERIA_NOT_TO_AND_NOT_CC:
898         case MATCHCRITERIA_TO_OR_CC:
899                 return CRITERIA_TO_OR_CC;
900         case MATCHCRITERIA_NOT_BODY_PART:
901         case MATCHCRITERIA_BODY_PART:
902                 return CRITERIA_BODY_PART;
903         case MATCHCRITERIA_NOT_MESSAGE:
904         case MATCHCRITERIA_MESSAGE:
905                 return CRITERIA_MESSAGE;
906                 break;
907         case MATCHCRITERIA_NOT_HEADERS_PART:
908         case MATCHCRITERIA_HEADERS_PART:
909                 return CRITERIA_HEADERS_PART;
910         case MATCHCRITERIA_NOT_HEADER:
911         case MATCHCRITERIA_HEADER:
912                 return CRITERIA_HEADER;
913         case MATCHCRITERIA_AGE_GREATER:
914                 return CRITERIA_AGE_GREATER;
915         case MATCHCRITERIA_AGE_LOWER:
916                 return CRITERIA_AGE_LOWER;
917         case MATCHCRITERIA_SCORE_GREATER:
918                 return CRITERIA_SCORE_GREATER;
919         case MATCHCRITERIA_SCORE_LOWER:
920                 return CRITERIA_SCORE_LOWER;
921         case MATCHCRITERIA_SCORE_EQUAL:
922                 return CRITERIA_SCORE_EQUAL;
923         case MATCHCRITERIA_NOT_TEST:
924         case MATCHCRITERIA_TEST:
925                 return CRITERIA_TEST;
926         case MATCHCRITERIA_SIZE_GREATER:
927                 return CRITERIA_SIZE_GREATER;
928         case MATCHCRITERIA_SIZE_SMALLER:
929                 return CRITERIA_SIZE_SMALLER;
930         case MATCHCRITERIA_SIZE_EQUAL:
931                 return CRITERIA_SIZE_EQUAL;
932         default:
933                 return -1;
934         }
935 }
936
937 /*!
938  *\brief        Returns the matcher keyword id from a criteria id
939  *
940  *\param        criteria_id Criteria id (should not be the negate
941  *              one)
942  *
943  *\return       gint A matcher keyword id. See #get_matchparser_tab_id.
944  */
945 static gint prefs_matcher_get_matching_from_criteria(gint criteria_id)
946 {
947         switch (criteria_id) {
948         case CRITERIA_ALL:
949                 return MATCHCRITERIA_ALL;
950         case CRITERIA_UNREAD:
951                 return MATCHCRITERIA_UNREAD;
952         case CRITERIA_NEW:
953                 return MATCHCRITERIA_NEW;
954         case CRITERIA_MARKED:
955                 return MATCHCRITERIA_MARKED;
956         case CRITERIA_DELETED:
957                 return MATCHCRITERIA_DELETED;
958         case CRITERIA_REPLIED:
959                 return MATCHCRITERIA_REPLIED;
960         case CRITERIA_FORWARDED:
961                 return MATCHCRITERIA_FORWARDED;
962         case CRITERIA_LOCKED:
963                 return MATCHCRITERIA_LOCKED;
964         case CRITERIA_PARTIAL:
965                 return MATCHCRITERIA_PARTIAL;
966         case CRITERIA_COLORLABEL:
967                 return MATCHCRITERIA_COLORLABEL;
968         case CRITERIA_IGNORE_THREAD:
969                 return MATCHCRITERIA_IGNORE_THREAD;
970         case CRITERIA_SUBJECT:
971                 return MATCHCRITERIA_SUBJECT;
972         case CRITERIA_FROM:
973                 return MATCHCRITERIA_FROM;
974         case CRITERIA_TO:
975                 return MATCHCRITERIA_TO;
976         case CRITERIA_CC:
977                 return MATCHCRITERIA_CC;
978         case CRITERIA_TO_OR_CC:
979                 return MATCHCRITERIA_TO_OR_CC;
980         case CRITERIA_NEWSGROUPS:
981                 return MATCHCRITERIA_NEWSGROUPS;
982         case CRITERIA_INREPLYTO:
983                 return MATCHCRITERIA_INREPLYTO;
984         case CRITERIA_REFERENCES:
985                 return MATCHCRITERIA_REFERENCES;
986         case CRITERIA_AGE_GREATER:
987                 return MATCHCRITERIA_AGE_GREATER;
988         case CRITERIA_AGE_LOWER:
989                 return MATCHCRITERIA_AGE_LOWER;
990         case CRITERIA_SCORE_GREATER:
991                 return MATCHCRITERIA_SCORE_GREATER;
992         case CRITERIA_SCORE_LOWER:
993                 return MATCHCRITERIA_SCORE_LOWER;
994         case CRITERIA_SCORE_EQUAL:
995                 return MATCHCRITERIA_SCORE_EQUAL;
996         case CRITERIA_HEADER:
997                 return MATCHCRITERIA_HEADER;
998         case CRITERIA_HEADERS_PART:
999                 return MATCHCRITERIA_HEADERS_PART;
1000         case CRITERIA_BODY_PART:
1001                 return MATCHCRITERIA_BODY_PART;
1002         case CRITERIA_MESSAGE:
1003                 return MATCHCRITERIA_MESSAGE;
1004         case CRITERIA_TEST:
1005                 return MATCHCRITERIA_TEST;
1006         case CRITERIA_SIZE_GREATER:
1007                 return MATCHCRITERIA_SIZE_GREATER;
1008         case CRITERIA_SIZE_SMALLER:
1009                 return MATCHCRITERIA_SIZE_SMALLER;
1010         case CRITERIA_SIZE_EQUAL:
1011                 return MATCHCRITERIA_SIZE_EQUAL;
1012         default:
1013                 return -1;
1014         }
1015 }
1016
1017 /*!
1018  *\brief        Returns the negate matcher keyword id from a matcher keyword
1019  *              id.
1020  *
1021  *\param        matcher_criteria Matcher keyword id. 
1022  *
1023  *\return       gint A matcher keyword id. See #get_matchparser_tab_id.
1024  */
1025 static gint prefs_matcher_not_criteria(gint matcher_criteria)
1026 {
1027         switch(matcher_criteria) {
1028         case MATCHCRITERIA_UNREAD:
1029                 return MATCHCRITERIA_NOT_UNREAD;
1030         case MATCHCRITERIA_NEW:
1031                 return MATCHCRITERIA_NOT_NEW;
1032         case MATCHCRITERIA_MARKED:
1033                 return MATCHCRITERIA_NOT_MARKED;
1034         case MATCHCRITERIA_DELETED:
1035                 return MATCHCRITERIA_NOT_DELETED;
1036         case MATCHCRITERIA_REPLIED:
1037                 return MATCHCRITERIA_NOT_REPLIED;
1038         case MATCHCRITERIA_FORWARDED:
1039                 return MATCHCRITERIA_NOT_FORWARDED;
1040         case MATCHCRITERIA_LOCKED:
1041                 return MATCHCRITERIA_NOT_LOCKED;
1042         case MATCHCRITERIA_PARTIAL:
1043                 return MATCHCRITERIA_NOT_PARTIAL;
1044         case MATCHCRITERIA_COLORLABEL:
1045                 return MATCHCRITERIA_NOT_COLORLABEL;
1046         case MATCHCRITERIA_IGNORE_THREAD:
1047                 return MATCHCRITERIA_NOT_IGNORE_THREAD;
1048         case MATCHCRITERIA_SUBJECT:
1049                 return MATCHCRITERIA_NOT_SUBJECT;
1050         case MATCHCRITERIA_FROM:
1051                 return MATCHCRITERIA_NOT_FROM;
1052         case MATCHCRITERIA_TO:
1053                 return MATCHCRITERIA_NOT_TO;
1054         case MATCHCRITERIA_CC:
1055                 return MATCHCRITERIA_NOT_CC;
1056         case MATCHCRITERIA_TO_OR_CC:
1057                 return MATCHCRITERIA_NOT_TO_AND_NOT_CC;
1058         case MATCHCRITERIA_NEWSGROUPS:
1059                 return MATCHCRITERIA_NOT_NEWSGROUPS;
1060         case MATCHCRITERIA_INREPLYTO:
1061                 return MATCHCRITERIA_NOT_INREPLYTO;
1062         case MATCHCRITERIA_REFERENCES:
1063                 return MATCHCRITERIA_NOT_REFERENCES;
1064         case MATCHCRITERIA_HEADER:
1065                 return MATCHCRITERIA_NOT_HEADER;
1066         case MATCHCRITERIA_HEADERS_PART:
1067                 return MATCHCRITERIA_NOT_HEADERS_PART;
1068         case MATCHCRITERIA_MESSAGE:
1069                 return MATCHCRITERIA_NOT_MESSAGE;
1070         case MATCHCRITERIA_TEST:
1071                 return MATCHCRITERIA_NOT_TEST;
1072         case MATCHCRITERIA_BODY_PART:
1073                 return MATCHCRITERIA_NOT_BODY_PART;
1074         default:
1075                 return matcher_criteria;
1076         }
1077 }
1078
1079 /*!
1080  *\brief        Converts the text in the selected row to a 
1081  *              matcher structure
1082  *
1083  *\return       MatcherProp * Newly allocated matcher structure.
1084  */
1085 static MatcherProp *prefs_matcher_dialog_to_matcher(void)
1086 {
1087         MatcherProp *matcherprop;
1088         gint criteria;
1089         gint matchtype;
1090         gint value_pred;
1091         gint value_pred_flag;
1092         gint value_criteria;
1093         gboolean use_regexp;
1094         gboolean case_sensitive;
1095         const gchar *header;
1096         const gchar *expr;
1097         gint value;
1098         const gchar *value_str;
1099
1100         value_criteria = get_sel_from_list(GTK_LIST(matcher.criteria_list));
1101
1102         criteria = prefs_matcher_get_matching_from_criteria(value_criteria);
1103
1104         value_pred = get_sel_from_list(GTK_LIST(matcher.predicate_list));
1105         value_pred_flag = get_sel_from_list(GTK_LIST(matcher.predicate_flag_list));
1106
1107         use_regexp = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(matcher.regexp_chkbtn));
1108         case_sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(matcher.case_chkbtn));
1109
1110         switch (value_criteria) {
1111         case CRITERIA_UNREAD:
1112         case CRITERIA_NEW:
1113         case CRITERIA_MARKED:
1114         case CRITERIA_DELETED:
1115         case CRITERIA_REPLIED:
1116         case CRITERIA_FORWARDED:
1117         case CRITERIA_LOCKED:
1118         case CRITERIA_PARTIAL:
1119         case CRITERIA_TEST:
1120         case CRITERIA_COLORLABEL:
1121         case CRITERIA_IGNORE_THREAD:
1122                 if (value_pred_flag == PREDICATE_FLAG_DISABLED)
1123                         criteria = prefs_matcher_not_criteria(criteria);
1124                 break;
1125         case CRITERIA_SUBJECT:
1126         case CRITERIA_FROM:
1127         case CRITERIA_TO:
1128         case CRITERIA_CC:
1129         case CRITERIA_TO_OR_CC:
1130         case CRITERIA_NEWSGROUPS:
1131         case CRITERIA_INREPLYTO:
1132         case CRITERIA_REFERENCES:
1133         case CRITERIA_HEADERS_PART:
1134         case CRITERIA_BODY_PART:
1135         case CRITERIA_MESSAGE:
1136         case CRITERIA_AGE_GREATER:
1137         case CRITERIA_AGE_LOWER:
1138         case CRITERIA_HEADER:
1139                 if (value_pred == PREDICATE_DOES_NOT_CONTAIN)
1140                         criteria = prefs_matcher_not_criteria(criteria);
1141                 break;
1142         }
1143
1144         if (use_regexp) {
1145                 if (case_sensitive)
1146                         matchtype = MATCHTYPE_REGEXP;
1147                 else
1148                         matchtype = MATCHTYPE_REGEXPCASE;
1149         }
1150         else {
1151                 if (case_sensitive)
1152                         matchtype = MATCHTYPE_MATCH;
1153                 else
1154                         matchtype = MATCHTYPE_MATCHCASE;
1155         }
1156
1157         header = NULL;
1158         expr = NULL;
1159         value = 0;
1160
1161         switch (value_criteria) {
1162         case CRITERIA_ALL:
1163         case CRITERIA_UNREAD:
1164         case CRITERIA_NEW:
1165         case CRITERIA_MARKED:
1166         case CRITERIA_DELETED:
1167         case CRITERIA_REPLIED:
1168         case CRITERIA_FORWARDED:
1169         case CRITERIA_LOCKED:
1170         case CRITERIA_PARTIAL:
1171         case CRITERIA_IGNORE_THREAD:
1172                 break;
1173
1174         case CRITERIA_SUBJECT:
1175         case CRITERIA_FROM:
1176         case CRITERIA_TO:
1177         case CRITERIA_CC:
1178         case CRITERIA_TO_OR_CC:
1179         case CRITERIA_NEWSGROUPS:
1180         case CRITERIA_INREPLYTO:
1181         case CRITERIA_REFERENCES:
1182         case CRITERIA_HEADERS_PART:
1183         case CRITERIA_BODY_PART:
1184         case CRITERIA_MESSAGE:
1185         case CRITERIA_TEST:
1186                 expr = gtk_entry_get_text(GTK_ENTRY(matcher.value_entry));
1187                 break;
1188
1189         case CRITERIA_AGE_GREATER:
1190         case CRITERIA_AGE_LOWER:
1191         case CRITERIA_SCORE_GREATER:
1192         case CRITERIA_SCORE_LOWER:
1193         case CRITERIA_SCORE_EQUAL:
1194         case CRITERIA_SIZE_GREATER:
1195         case CRITERIA_SIZE_SMALLER:
1196         case CRITERIA_SIZE_EQUAL:
1197                 value_str = gtk_entry_get_text(GTK_ENTRY(matcher.value_entry));
1198
1199                 if (*value_str == '\0') {
1200                     alertpanel_error(_("Value is not set."));
1201                     return NULL;
1202                 }
1203
1204                 value = atoi(value_str);
1205
1206                 break;
1207                 
1208         case CRITERIA_COLORLABEL:
1209                 value = colorlabel_get_color_menu_active_item
1210                         (gtk_option_menu_get_menu(GTK_OPTION_MENU
1211                                 (matcher.color_optmenu))); 
1212                 break;
1213
1214         case CRITERIA_HEADER:
1215
1216                 header = gtk_entry_get_text(GTK_ENTRY(matcher.header_entry));
1217                 expr = gtk_entry_get_text(GTK_ENTRY(matcher.value_entry));
1218
1219                 if (*header == '\0') {
1220                     alertpanel_error(_("Header name is not set."));
1221                     return NULL;
1222                 }
1223                 break;
1224         }
1225
1226         matcherprop = matcherprop_new(criteria, header, matchtype,
1227                                       expr, value);
1228
1229         return matcherprop;
1230 }
1231
1232 /*!
1233  *\brief        Signal handler for register button
1234  */
1235 static void prefs_matcher_register_cb(void)
1236 {
1237         MatcherProp *matcherprop;
1238         
1239         matcherprop = prefs_matcher_dialog_to_matcher();
1240         if (matcherprop == NULL)
1241                 return;
1242
1243         prefs_matcher_list_view_set_row(NULL, matcherprop);
1244
1245         matcherprop_free(matcherprop);
1246
1247         prefs_matcher_reset_condition();
1248 }
1249
1250 /*!
1251  *\brief        Signal handler for substitute button
1252  */
1253 static void prefs_matcher_substitute_cb(void)
1254 {
1255         MatcherProp *matcherprop;
1256         GtkTreeIter row;
1257         GtkTreeSelection *selection;
1258         GtkTreeModel *model;
1259         gboolean is_valid;
1260
1261         selection = gtk_tree_view_get_selection
1262                         (GTK_TREE_VIEW(matcher.cond_list_view));
1263         
1264         if (!gtk_tree_selection_get_selected(selection, &model, &row))
1265                 return;
1266         
1267         gtk_tree_model_get(model, &row, 
1268                            PREFS_MATCHER_COND_VALID, &is_valid,
1269                            -1);
1270         if (!is_valid)
1271                 return;
1272
1273         matcherprop = prefs_matcher_dialog_to_matcher();
1274         if (matcherprop == NULL)
1275                 return;
1276
1277         prefs_matcher_list_view_set_row(&row, matcherprop);
1278
1279         matcherprop_free(matcherprop);
1280
1281         prefs_matcher_reset_condition();
1282 }
1283
1284 /*!
1285  *\brief        Signal handler for delete button
1286  */
1287 static void prefs_matcher_delete_cb(void)
1288 {
1289         GtkTreeIter row;
1290         GtkTreeSelection *selection;
1291         GtkTreeModel *model;
1292         gboolean is_valid;
1293
1294         selection = gtk_tree_view_get_selection
1295                         (GTK_TREE_VIEW(matcher.cond_list_view));
1296         
1297         if (!gtk_tree_selection_get_selected(selection, &model, &row))
1298                 return;
1299                 
1300         gtk_tree_model_get(model, &row, 
1301                            PREFS_MATCHER_COND_VALID, &is_valid,
1302                            -1);
1303
1304         if (!is_valid)
1305                 return;
1306
1307         gtk_list_store_remove(GTK_LIST_STORE(model), &row);             
1308
1309         prefs_matcher_reset_condition();
1310 }
1311
1312 /*!
1313  *\brief        Signal handler for 'move up' button
1314  */
1315 static void prefs_matcher_up(void)
1316 {
1317         GtkTreePath *prev, *sel, *try;
1318         GtkTreeIter isel;
1319         GtkListStore *store = NULL;
1320         GtkTreeModel *model = NULL;
1321         GtkTreeIter iprev;
1322         
1323         if (!gtk_tree_selection_get_selected
1324                 (gtk_tree_view_get_selection
1325                         (GTK_TREE_VIEW(matcher.cond_list_view)),
1326                  &model,        
1327                  &isel))
1328                 return;
1329         store = (GtkListStore *)model;
1330         sel = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &isel);
1331         if (!sel)
1332                 return;
1333         
1334         /* no move if we're at row 0 or 1, looks phony, but other
1335          * solutions are more convoluted... */
1336         try = gtk_tree_path_copy(sel);
1337         if (!gtk_tree_path_prev(try) || !gtk_tree_path_prev(try)) {
1338                 gtk_tree_path_free(try);
1339                 gtk_tree_path_free(sel);
1340                 return;
1341         }
1342         gtk_tree_path_free(try);
1343
1344         prev = gtk_tree_path_copy(sel);         
1345         if (gtk_tree_path_prev(prev)) {
1346                 gtk_tree_model_get_iter(GTK_TREE_MODEL(store),
1347                                         &iprev, prev);
1348                 gtk_list_store_swap(store, &iprev, &isel);
1349                 /* XXX: GTK2 select row?? */
1350         }
1351
1352         gtk_tree_path_free(sel);
1353         gtk_tree_path_free(prev);
1354 }
1355
1356 /*!
1357  *\brief        Signal handler for 'move down' button
1358  */
1359 static void prefs_matcher_down(void)
1360 {
1361         GtkListStore *store = NULL;
1362         GtkTreeModel *model = NULL;
1363         GtkTreeIter next, sel;
1364         GtkTreePath *try;
1365         
1366         if (!gtk_tree_selection_get_selected
1367                 (gtk_tree_view_get_selection
1368                         (GTK_TREE_VIEW(matcher.cond_list_view)),
1369                  &model,
1370                  &sel))
1371                 return;
1372         store = (GtkListStore *)model;
1373         try = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &sel);
1374         if (!try) 
1375                 return;
1376         
1377         /* move when not at row 0 ... */
1378         if (gtk_tree_path_prev(try)) {
1379                 next = sel;
1380                 if (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &next))
1381                         gtk_list_store_swap(store, &next, &sel);
1382         }
1383                 
1384         gtk_tree_path_free(try);
1385 }
1386
1387 /*!
1388  *\brief        Helper function that allows us to replace the 'Value' entry box
1389  *              by another widget.
1390  *
1391  *\param        old_widget Widget that needs to be removed.
1392  *\param        new_widget Replacement widget
1393  */
1394 static void prefs_matcher_set_value_widget(GtkWidget *old_widget, 
1395                                            GtkWidget *new_widget)
1396 {
1397         /* TODO: find out why the following spews harmless "parent errors" */
1398
1399         /* NOTE: we first need to bump up the refcount of the old_widget,
1400          * because the gtkut_container_remove() will otherwise destroy it */
1401         gtk_widget_ref(old_widget);
1402         gtkut_container_remove(GTK_CONTAINER(matcher.criteria_table), old_widget);
1403         gtk_widget_show(new_widget);
1404         gtk_widget_set_size_request(new_widget, 200, -1);
1405         gtk_table_attach(GTK_TABLE(matcher.criteria_table), new_widget, 
1406                          2, 3, 1, 2, 
1407                          GTK_FILL | GTK_SHRINK | GTK_EXPAND, 
1408                          0, 0, 0);
1409 }
1410
1411 /*!
1412  *\brief        Change widgets depending on the selected condition
1413  *
1414  *\param        list List widget
1415  *\param        widget Not used
1416  *\param        user_data Not used      
1417  */
1418 static void prefs_matcher_criteria_select(GtkList *list,
1419                                           GtkWidget *widget,
1420                                           gpointer user_data)
1421 {
1422         gint value, old_value;
1423
1424         old_value = matcher.selected_criteria;
1425         matcher.selected_criteria = value = get_sel_from_list
1426                 (GTK_LIST(matcher.criteria_list));
1427
1428         if (old_value == matcher.selected_criteria)
1429                 return;
1430
1431         /* CLAWS: the value widget is currently either the color label combo box,
1432          * or a GtkEntry, so kiss for now */
1433         if (matcher.selected_criteria == CRITERIA_COLORLABEL) { 
1434                 prefs_matcher_set_value_widget(matcher.value_entry, 
1435                                                matcher.color_optmenu);
1436         } else if (old_value == CRITERIA_COLORLABEL) {
1437                 prefs_matcher_set_value_widget(matcher.color_optmenu,
1438                                                matcher.value_entry);
1439         }                                              
1440
1441         switch (value) {
1442         case CRITERIA_ALL:
1443                 gtk_widget_set_sensitive(matcher.header_combo, FALSE);
1444                 gtk_widget_set_sensitive(matcher.header_label, FALSE);
1445                 gtk_widget_set_sensitive(matcher.value_label, FALSE);
1446                 gtk_widget_set_sensitive(matcher.value_entry, FALSE);
1447                 gtk_widget_set_sensitive(matcher.predicate_label, FALSE);
1448                 gtk_widget_set_sensitive(matcher.predicate_combo, FALSE);
1449                 gtk_widget_set_sensitive(matcher.predicate_flag_combo, FALSE);
1450                 gtk_widget_hide(matcher.predicate_combo);
1451                 gtk_widget_show(matcher.predicate_flag_combo);
1452                 gtk_widget_set_sensitive(matcher.case_chkbtn, FALSE);
1453                 gtk_widget_set_sensitive(matcher.regexp_chkbtn, FALSE);
1454                 gtk_widget_set_sensitive(matcher.test_btn, FALSE);
1455                 break;
1456
1457         case CRITERIA_UNREAD:
1458         case CRITERIA_NEW:
1459         case CRITERIA_MARKED:
1460         case CRITERIA_DELETED:
1461         case CRITERIA_REPLIED:
1462         case CRITERIA_FORWARDED:
1463         case CRITERIA_LOCKED:
1464         case CRITERIA_PARTIAL:
1465         case CRITERIA_IGNORE_THREAD:
1466                 gtk_widget_set_sensitive(matcher.header_combo, FALSE);
1467                 gtk_widget_set_sensitive(matcher.header_label, FALSE);
1468                 gtk_widget_set_sensitive(matcher.value_label, FALSE);
1469                 gtk_widget_set_sensitive(matcher.value_entry, FALSE);
1470                 gtk_widget_set_sensitive(matcher.predicate_label, TRUE);
1471                 gtk_widget_set_sensitive(matcher.predicate_combo, FALSE);
1472                 gtk_widget_set_sensitive(matcher.predicate_flag_combo, TRUE);
1473                 gtk_widget_hide(matcher.predicate_combo);
1474                 gtk_widget_show(matcher.predicate_flag_combo);
1475                 gtk_widget_set_sensitive(matcher.case_chkbtn, FALSE);
1476                 gtk_widget_set_sensitive(matcher.regexp_chkbtn, FALSE);
1477                 gtk_widget_set_sensitive(matcher.test_btn, FALSE);
1478                 break;
1479                 
1480         case CRITERIA_COLORLABEL:
1481                 gtk_widget_set_sensitive(matcher.header_combo, FALSE);
1482                 gtk_widget_set_sensitive(matcher.header_label, FALSE);
1483                 gtk_widget_set_sensitive(matcher.value_label, TRUE);
1484                 gtk_widget_set_sensitive(matcher.predicate_label, TRUE);
1485                 gtk_widget_set_sensitive(matcher.predicate_combo, FALSE);
1486                 gtk_widget_set_sensitive(matcher.predicate_flag_combo, TRUE);
1487                 gtk_widget_hide(matcher.predicate_combo);
1488                 gtk_widget_show(matcher.predicate_flag_combo);
1489                 gtk_widget_set_sensitive(matcher.case_chkbtn, FALSE);
1490                 gtk_widget_set_sensitive(matcher.regexp_chkbtn, FALSE);
1491                 gtk_widget_set_sensitive(matcher.test_btn, FALSE);
1492                 break;
1493
1494         case CRITERIA_SUBJECT:
1495         case CRITERIA_FROM:
1496         case CRITERIA_TO:
1497         case CRITERIA_CC:
1498         case CRITERIA_TO_OR_CC:
1499         case CRITERIA_NEWSGROUPS:
1500         case CRITERIA_INREPLYTO:
1501         case CRITERIA_REFERENCES:
1502         case CRITERIA_HEADERS_PART:
1503         case CRITERIA_BODY_PART:
1504         case CRITERIA_MESSAGE:
1505                 gtk_widget_set_sensitive(matcher.header_combo, FALSE);
1506                 gtk_widget_set_sensitive(matcher.header_label, FALSE);
1507                 gtk_widget_set_sensitive(matcher.value_label, TRUE);
1508                 gtk_widget_set_sensitive(matcher.value_entry, TRUE);
1509                 gtk_widget_set_sensitive(matcher.predicate_label, TRUE);
1510                 gtk_widget_set_sensitive(matcher.predicate_combo, TRUE);
1511                 gtk_widget_set_sensitive(matcher.predicate_flag_combo, FALSE);
1512                 gtk_widget_show(matcher.predicate_combo);
1513                 gtk_widget_hide(matcher.predicate_flag_combo);
1514                 gtk_widget_set_sensitive(matcher.case_chkbtn, TRUE);
1515                 gtk_widget_set_sensitive(matcher.regexp_chkbtn, TRUE);
1516                 gtk_widget_set_sensitive(matcher.test_btn, FALSE);
1517                 break;
1518
1519         case CRITERIA_TEST:
1520                 gtk_widget_set_sensitive(matcher.header_combo, FALSE);
1521                 gtk_widget_set_sensitive(matcher.header_label, FALSE);
1522                 gtk_widget_set_sensitive(matcher.value_label, TRUE);
1523                 gtk_widget_set_sensitive(matcher.value_entry, TRUE);
1524                 gtk_widget_set_sensitive(matcher.predicate_label, TRUE);
1525                 gtk_widget_set_sensitive(matcher.predicate_combo, FALSE);
1526                 gtk_widget_set_sensitive(matcher.predicate_flag_combo, TRUE);
1527                 gtk_widget_hide(matcher.predicate_combo);
1528                 gtk_widget_show(matcher.predicate_flag_combo);
1529                 gtk_widget_set_sensitive(matcher.case_chkbtn, FALSE);
1530                 gtk_widget_set_sensitive(matcher.regexp_chkbtn, FALSE);
1531                 gtk_widget_set_sensitive(matcher.test_btn, TRUE);
1532                 break;
1533
1534         case CRITERIA_AGE_GREATER:
1535         case CRITERIA_AGE_LOWER:
1536         case CRITERIA_SCORE_GREATER:
1537         case CRITERIA_SCORE_LOWER:
1538         case CRITERIA_SCORE_EQUAL:
1539         case CRITERIA_SIZE_GREATER:
1540         case CRITERIA_SIZE_SMALLER:
1541         case CRITERIA_SIZE_EQUAL:
1542                 gtk_widget_set_sensitive(matcher.header_combo, FALSE);
1543                 gtk_widget_set_sensitive(matcher.header_label, FALSE);
1544                 gtk_widget_set_sensitive(matcher.value_label, TRUE);
1545                 gtk_widget_set_sensitive(matcher.value_entry, TRUE);
1546                 gtk_widget_set_sensitive(matcher.predicate_label, FALSE);
1547                 gtk_widget_set_sensitive(matcher.predicate_combo, FALSE);
1548                 gtk_widget_set_sensitive(matcher.predicate_flag_combo, FALSE);
1549                 gtk_widget_show(matcher.predicate_combo);
1550                 gtk_widget_hide(matcher.predicate_flag_combo);
1551                 gtk_widget_set_sensitive(matcher.case_chkbtn, FALSE);
1552                 gtk_widget_set_sensitive(matcher.regexp_chkbtn, FALSE);
1553                 gtk_widget_set_sensitive(matcher.test_btn, FALSE);
1554                 break;
1555
1556         case CRITERIA_HEADER:
1557                 gtk_widget_set_sensitive(matcher.header_combo, TRUE);
1558                 gtk_widget_set_sensitive(matcher.header_label, TRUE);
1559                 gtk_widget_set_sensitive(matcher.value_label, TRUE);
1560                 gtk_widget_set_sensitive(matcher.value_entry, TRUE);
1561                 gtk_widget_set_sensitive(matcher.predicate_label, TRUE);
1562                 gtk_widget_set_sensitive(matcher.predicate_combo, TRUE);
1563                 gtk_widget_set_sensitive(matcher.predicate_flag_combo, FALSE);
1564                 gtk_widget_show(matcher.predicate_combo);
1565                 gtk_widget_hide(matcher.predicate_flag_combo);
1566                 gtk_widget_set_sensitive(matcher.case_chkbtn, TRUE);
1567                 gtk_widget_set_sensitive(matcher.regexp_chkbtn, TRUE);
1568                 gtk_widget_set_sensitive(matcher.test_btn, FALSE);
1569                 break;
1570         }
1571 }
1572
1573 /*!
1574  *\brief        Handle key press
1575  *
1576  *\param        widget Widget receiving key press
1577  *\param        event Key event
1578  *\param        data User data
1579  */
1580 static gboolean prefs_matcher_key_pressed(GtkWidget *widget, GdkEventKey *event,
1581                                      gpointer data)
1582 {
1583         if (event && event->keyval == GDK_Escape) {
1584                 prefs_matcher_cancel();
1585                 return TRUE;            
1586         }
1587         return FALSE;
1588 }
1589
1590 /*!
1591  *\brief        Cancel matcher dialog
1592  */
1593 static void prefs_matcher_cancel(void)
1594 {
1595         gtk_widget_hide(matcher.window);
1596         inc_unlock();
1597 }
1598
1599 /*!
1600  *\brief        Accept current matchers
1601  */
1602 static void prefs_matcher_ok(void)
1603 {
1604         MatcherList *matchers;
1605         MatcherProp *matcherprop;
1606         AlertValue val;
1607         gchar *matcher_str = NULL;
1608         gchar *str = NULL;
1609         gint row = 1;
1610         GtkTreeModel *model;
1611         GtkTreeIter iter;
1612
1613         matchers = prefs_matcher_get_list();
1614
1615         if (matchers != NULL) {
1616                 matcherprop = prefs_matcher_dialog_to_matcher();
1617                 if (matcherprop != NULL) {
1618                         str = matcherprop_to_string(matcherprop);
1619                         matcherprop_free(matcherprop);
1620                         if (strcmp(str, "all") != 0) {
1621                                 model = gtk_tree_view_get_model(GTK_TREE_VIEW
1622                                                 (matcher.cond_list_view));
1623
1624                                 while (gtk_tree_model_iter_nth_child(model, &iter, NULL, row)) {
1625                                         gtk_tree_model_get(model, &iter,
1626                                                            PREFS_MATCHER_COND, &matcher_str,
1627                                                            -1);
1628                                         if (matcher_str && strcmp(matcher_str, str) == 0) 
1629                                                 break;
1630                                         row++;
1631                                         g_free(matcher_str);
1632                                         matcher_str = NULL;
1633                                 }
1634
1635                                 if (!matcher_str || strcmp(matcher_str, str) != 0) {
1636                                         val = alertpanel(_("Entry not saved"),
1637                                                  _("The entry was not saved.\nHave you really finished?"),
1638                                                  GTK_STOCK_YES, GTK_STOCK_NO, NULL);
1639                                         if (G_ALERTDEFAULT != val) {
1640                                                 g_free(matcher_str);                                             
1641                                                 g_free(str);
1642                                                 return;
1643                                         }
1644                                 }
1645                                 g_free(matcher_str);
1646                         }
1647                 }
1648                 g_free(str);
1649                 gtk_widget_hide(matcher.window);
1650                 inc_unlock();
1651                 if (matchers_callback != NULL)
1652                         matchers_callback(matchers);
1653                 matcherlist_free(matchers);
1654         }
1655 }
1656
1657 /*!
1658  *\brief        Called when closing dialog box
1659  *
1660  *\param        widget Dialog widget
1661  *\param        event Event info
1662  *\param        data User data
1663  *
1664  *\return       gint TRUE
1665  */
1666 static gint prefs_matcher_deleted(GtkWidget *widget, GdkEventAny *event,
1667                                   gpointer data)
1668 {
1669         prefs_matcher_cancel();
1670         return TRUE;
1671 }
1672
1673 /*
1674  * Strings describing test format strings
1675  * 
1676  * When adding new lines, remember to put 2 strings for each line
1677  */
1678 static gchar *test_desc_strings[] = {
1679         N_("'Test' allows you to test a message or message element"), NULL,
1680         N_("using an external program or script. The program will"), NULL,
1681         N_("return either 0 or 1"), NULL,
1682         N_("The following symbols can be used:"), NULL,
1683         "%%",   "%",
1684         "%s",   N_("Subject"),
1685         "%f",   N_("From"),
1686         "%t",   N_("To"),
1687         "%c",   N_("Cc"),
1688         "%d",   N_("Date"),
1689         "%i",   N_("Message-ID"),
1690         "%n",   N_("Newsgroups"),
1691         "%r",   N_("References"),
1692         "%F",   N_("Filename - should not be modified"),
1693         "\\n",  N_("new line"),
1694         "\\",   N_("escape character for quotes"),
1695         "\\\"", N_("quote character"),
1696         NULL,   NULL
1697 };
1698
1699 static DescriptionWindow test_desc_win = { 
1700         NULL,
1701         NULL, 
1702         2,
1703         N_("Match Type: 'Test'"),
1704         test_desc_strings
1705 };
1706
1707
1708
1709 /*!
1710  *\brief        Show Test action's info
1711  */
1712 void prefs_matcher_test_info(void)
1713 {
1714         description_window_create(&test_desc_win);
1715 }
1716
1717 /*
1718  * list view
1719  */
1720
1721 static GtkListStore* prefs_matcher_create_data_store(void)
1722 {
1723         return gtk_list_store_new(N_PREFS_MATCHER_COLUMNS,
1724                                   G_TYPE_STRING,        
1725                                   G_TYPE_BOOLEAN,
1726                                   -1);
1727 }
1728
1729 static void prefs_matcher_list_view_insert_matcher(GtkWidget *list_view,
1730                                                    GtkTreeIter *row_iter,
1731                                                    const gchar *matcher,
1732                                                    gboolean is_valid) 
1733 {
1734         GtkTreeIter iter;
1735         GtkListStore *list_store = GTK_LIST_STORE(gtk_tree_view_get_model
1736                                         (GTK_TREE_VIEW(list_view)));
1737
1738         if (row_iter == NULL) {
1739                 /* append new */
1740                 gtk_list_store_append(list_store, &iter);
1741         } else {
1742                 /* change existing */
1743                 iter = *row_iter;
1744         }
1745
1746         gtk_list_store_set(list_store, &iter,
1747                            PREFS_MATCHER_COND, matcher,
1748                            PREFS_MATCHER_COND_VALID, is_valid,
1749                            -1);
1750 }
1751
1752 static GtkWidget *prefs_matcher_list_view_create(void)
1753 {
1754         GtkTreeView *list_view;
1755         GtkTreeSelection *selector;
1756         GtkTreeModel *model;
1757
1758         model = GTK_TREE_MODEL(prefs_matcher_create_data_store());
1759         list_view = GTK_TREE_VIEW(gtk_tree_view_new_with_model(model));
1760         g_object_unref(model);  
1761         
1762         gtk_tree_view_set_rules_hint(list_view, prefs_common.enable_rules_hint);
1763         
1764         selector = gtk_tree_view_get_selection(list_view);
1765         gtk_tree_selection_set_mode(selector, GTK_SELECTION_BROWSE);
1766         gtk_tree_selection_set_select_function(selector, prefs_matcher_selected,
1767                                                NULL, NULL);
1768
1769         /* create the columns */
1770         prefs_matcher_create_list_view_columns(GTK_WIDGET(list_view));
1771
1772         return GTK_WIDGET(list_view);
1773 }
1774
1775 static void prefs_matcher_create_list_view_columns(GtkWidget *list_view)
1776 {
1777         GtkTreeViewColumn *column;
1778         GtkCellRenderer *renderer;
1779
1780         renderer = gtk_cell_renderer_text_new();
1781         column = gtk_tree_view_column_new_with_attributes
1782                 (_("Current condition rules"),
1783                  renderer,
1784                  "text", PREFS_MATCHER_COND,
1785                  NULL);
1786         gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), column);          
1787 }
1788
1789 static gboolean prefs_matcher_selected(GtkTreeSelection *selector,
1790                                        GtkTreeModel *model, 
1791                                        GtkTreePath *path,
1792                                        gboolean currently_selected,
1793                                        gpointer data)
1794 {
1795         gchar *matcher_str;
1796         MatcherProp *prop;
1797         gboolean negative_cond;
1798         gint criteria;
1799         GtkTreeIter iter;
1800         gboolean is_valid;
1801
1802         if (currently_selected)
1803                 return TRUE;
1804
1805         if (!gtk_tree_model_get_iter(model, &iter, path))
1806                 return TRUE;
1807
1808         gtk_tree_model_get(model, &iter, 
1809                            PREFS_MATCHER_COND_VALID,  &is_valid,
1810                            PREFS_MATCHER_COND, &matcher_str,
1811                            -1);
1812         
1813         if (!is_valid) {
1814                 g_free(matcher_str);
1815                 prefs_matcher_reset_condition();
1816                 return TRUE;
1817         }
1818
1819         negative_cond = FALSE;
1820
1821         prop = matcher_parser_get_prop(matcher_str);
1822         if (prop == NULL) {
1823                 g_free(matcher_str);
1824                 return TRUE;
1825         }               
1826
1827         criteria = prefs_matcher_get_criteria_from_matching(prop->criteria);
1828         if (criteria != -1)
1829                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
1830                                      criteria);
1831
1832         switch(prop->criteria) {
1833         case MATCHCRITERIA_NOT_UNREAD:
1834         case MATCHCRITERIA_NOT_NEW:
1835         case MATCHCRITERIA_NOT_MARKED:
1836         case MATCHCRITERIA_NOT_DELETED:
1837         case MATCHCRITERIA_NOT_REPLIED:
1838         case MATCHCRITERIA_NOT_FORWARDED:
1839         case MATCHCRITERIA_NOT_LOCKED:
1840         case MATCHCRITERIA_NOT_PARTIAL:
1841         case MATCHCRITERIA_NOT_COLORLABEL:
1842         case MATCHCRITERIA_NOT_IGNORE_THREAD:
1843         case MATCHCRITERIA_NOT_SUBJECT:
1844         case MATCHCRITERIA_NOT_FROM:
1845         case MATCHCRITERIA_NOT_TO:
1846         case MATCHCRITERIA_NOT_CC:
1847         case MATCHCRITERIA_NOT_NEWSGROUPS:
1848         case MATCHCRITERIA_NOT_INREPLYTO:
1849         case MATCHCRITERIA_NOT_REFERENCES:
1850         case MATCHCRITERIA_NOT_TO_AND_NOT_CC:
1851         case MATCHCRITERIA_NOT_BODY_PART:
1852         case MATCHCRITERIA_NOT_MESSAGE:
1853         case MATCHCRITERIA_NOT_HEADERS_PART:
1854         case MATCHCRITERIA_NOT_HEADER:
1855                 negative_cond = TRUE;
1856                 break;
1857         }
1858         
1859         switch(prop->criteria) {
1860         case MATCHCRITERIA_ALL:
1861                 break;
1862
1863         case MATCHCRITERIA_NOT_SUBJECT:
1864         case MATCHCRITERIA_NOT_FROM:
1865         case MATCHCRITERIA_NOT_TO:
1866         case MATCHCRITERIA_NOT_CC:
1867         case MATCHCRITERIA_NOT_TO_AND_NOT_CC:
1868         case MATCHCRITERIA_NOT_NEWSGROUPS:
1869         case MATCHCRITERIA_NOT_INREPLYTO:
1870         case MATCHCRITERIA_NOT_REFERENCES:
1871         case MATCHCRITERIA_NOT_HEADERS_PART:
1872         case MATCHCRITERIA_NOT_BODY_PART:
1873         case MATCHCRITERIA_NOT_MESSAGE:
1874         case MATCHCRITERIA_SUBJECT:
1875         case MATCHCRITERIA_FROM:
1876         case MATCHCRITERIA_TO:
1877         case MATCHCRITERIA_CC:
1878         case MATCHCRITERIA_TO_OR_CC:
1879         case MATCHCRITERIA_NEWSGROUPS:
1880         case MATCHCRITERIA_INREPLYTO:
1881         case MATCHCRITERIA_REFERENCES:
1882         case MATCHCRITERIA_HEADERS_PART:
1883         case MATCHCRITERIA_BODY_PART:
1884         case MATCHCRITERIA_MESSAGE:
1885         case MATCHCRITERIA_TEST:
1886                 gtk_entry_set_text(GTK_ENTRY(matcher.value_entry), prop->expr);
1887                 break;
1888
1889         case MATCHCRITERIA_AGE_GREATER:
1890         case MATCHCRITERIA_AGE_LOWER:
1891         case MATCHCRITERIA_SCORE_GREATER:
1892         case MATCHCRITERIA_SCORE_LOWER:
1893         case MATCHCRITERIA_SCORE_EQUAL:
1894         case MATCHCRITERIA_SIZE_GREATER:
1895         case MATCHCRITERIA_SIZE_SMALLER:
1896         case MATCHCRITERIA_SIZE_EQUAL:
1897                 gtk_entry_set_text(GTK_ENTRY(matcher.value_entry), itos(prop->value));
1898                 break;
1899
1900         case MATCHCRITERIA_NOT_COLORLABEL:
1901         case MATCHCRITERIA_COLORLABEL:
1902                 gtk_option_menu_set_history(GTK_OPTION_MENU(matcher.color_optmenu),
1903                                             prop->value);
1904                 break;
1905
1906         case MATCHCRITERIA_NOT_HEADER:
1907         case MATCHCRITERIA_HEADER:
1908                 gtk_entry_set_text(GTK_ENTRY(matcher.header_entry), prop->header);
1909                 gtk_entry_set_text(GTK_ENTRY(matcher.value_entry), prop->expr);
1910                 break;
1911         }
1912
1913         if (negative_cond)
1914                 gtk_list_select_item(GTK_LIST(matcher.predicate_list), 1);
1915         else
1916                 gtk_list_select_item(GTK_LIST(matcher.predicate_list), 0);
1917
1918         switch(prop->matchtype) {
1919         case MATCHTYPE_MATCH:
1920                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_chkbtn), FALSE);
1921                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_chkbtn), TRUE);
1922                 break;
1923
1924         case MATCHTYPE_MATCHCASE:
1925                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_chkbtn), FALSE);
1926                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_chkbtn), FALSE);
1927                 break;
1928
1929         case MATCHTYPE_REGEXP:
1930                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_chkbtn), TRUE);
1931                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_chkbtn), TRUE);
1932                 break;
1933
1934         case MATCHTYPE_REGEXPCASE:
1935                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_chkbtn), TRUE);
1936                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_chkbtn), FALSE);
1937                 break;
1938         }
1939
1940         g_free(matcher_str);
1941         return TRUE;
1942 }
1943