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