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