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