167c9e450a7d8f759aa52bc2b70ac3f5f4f48a44
[claws.git] / src / prefs_matcher.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2012 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 3 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, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24
25 #include "defs.h"
26
27 #include <glib.h>
28 #include <glib/gi18n.h>
29 #include <gtk/gtk.h>
30 #include <gdk/gdkkeysyms.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <errno.h>
35
36 #if !GTK_CHECK_VERSION(3, 0, 0)
37 #include "gtkcmoptionmenu.h"
38 #endif
39 #include "main.h"
40 #include "prefs_gtk.h"
41 #include "prefs_matcher.h"
42 #include "prefs_common.h"
43 #include "mainwindow.h"
44 #include "foldersel.h"
45 #include "manage_window.h"
46 #include "inc.h"
47 #include "matcher.h"
48 #include "utils.h"
49 #include "gtkutils.h"
50 #include "alertpanel.h"
51 #include "folder.h"
52 #include "description_window.h"
53 #include "combobox.h"
54
55 #include "matcher_parser.h"
56 #include "colorlabel.h"
57 #include "tags.h"
58
59 #ifndef USE_NEW_ADDRBOOK
60 static void prefs_matcher_addressbook_select(void);
61 #endif
62 static void prefs_matcher_test_info(GtkWidget *widget, GtkWidget *parent);
63
64 enum {
65         PREFS_MATCHER_COND,
66         PREFS_MATCHER_COND_VALID,
67         N_PREFS_MATCHER_COLUMNS
68 };
69
70 /*!
71  *\brief        UI data for matcher dialog
72  */
73 static struct Matcher {
74         GtkWidget *window;
75
76         GtkWidget *ok_btn;
77
78         GtkWidget *match_combo;
79         GtkWidget *header_addr_combo;
80         GtkWidget *bool_op_combo;
81         GtkWidget *criteria_label2;
82         GtkWidget *criteria_combo;
83         GtkWidget *criteria_combo2;
84         GtkWidget *match_combo2;
85         GtkWidget *match_label;
86         GtkWidget *match_label2;
87         GtkWidget *headers_combo;
88         GtkWidget *upper_filler;
89         GtkWidget *lower_filler;
90
91         GtkWidget *header_entry;
92         GtkWidget *header_addr_entry;
93         GtkWidget *string_entry;
94         GtkWidget *numeric_entry;
95         GtkWidget *numeric_label;
96         GtkWidget *addressbook_folder_combo;
97         GtkWidget *case_checkbtn;
98 #ifndef G_OS_WIN32
99         GtkWidget *regexp_checkbtn;
100 #endif
101 #if !GTK_CHECK_VERSION(3, 0, 0)
102         GtkWidget *color_optmenu;
103 #endif
104
105         GtkWidget *test_btn;
106         GtkWidget *addressbook_select_btn;
107
108         GtkTreeModel *model_age;
109         GtkTreeModel *model_age_units;
110         GtkTreeModel *model_contain;
111         GtkTreeModel *model_found;
112         GtkTreeModel *model_flags;
113         GtkTreeModel *model_headers;
114         GtkTreeModel *model_partial;
115         GtkTreeModel *model_phrase;
116         GtkTreeModel *model_score;
117         GtkTreeModel *model_set;
118         GtkTreeModel *model_size;
119         GtkTreeModel *model_size_units;
120         GtkTreeModel *model_tags;
121         GtkTreeModel *model_test;
122         GtkTreeModel *model_thread;
123         
124         GtkWidget *cond_list_view;
125
126         gint selected_criteria; /*!< selected criteria in combobox */ 
127 } matcher;
128
129 /*!
130  *\brief        Conditions with a negate counterpart (like unread and ~unread)
131  *              have the same CRITERIA_XXX id). I.e. both unread and ~unread
132  *              have criteria id CRITERIA_UNREAD. This id is passed as the
133  *              first parameter to #matcherprop_new and #matcherprop_unquote_new.
134  */             
135 enum {
136         CRITERIA_ALL = 0,
137
138         CRITERIA_SUBJECT = 1,
139         CRITERIA_FROM = 2,
140         CRITERIA_TO = 3,
141         CRITERIA_CC = 4,
142         CRITERIA_TO_OR_CC = 5,
143         CRITERIA_NEWSGROUPS = 6,
144         CRITERIA_INREPLYTO = 7,
145         CRITERIA_REFERENCES = 8,
146         CRITERIA_AGE_GREATER = 9,
147         CRITERIA_AGE_LOWER = 10,
148         CRITERIA_HEADER = 11,
149         CRITERIA_HEADERS_PART = 12,
150         CRITERIA_BODY_PART = 13,
151         CRITERIA_MESSAGE = 14,
152
153         CRITERIA_UNREAD = 15,
154         CRITERIA_NEW = 16,
155         CRITERIA_MARKED = 17,
156         CRITERIA_DELETED = 18,
157         CRITERIA_REPLIED = 19,
158         CRITERIA_FORWARDED = 20,
159         CRITERIA_LOCKED = 21,
160         CRITERIA_SPAM = 22,
161         CRITERIA_COLORLABEL = 23,
162         CRITERIA_IGNORE_THREAD = 24,
163         CRITERIA_WATCH_THREAD = 25,
164
165         CRITERIA_SCORE_GREATER = 26,
166         CRITERIA_SCORE_LOWER = 27,
167         CRITERIA_SCORE_EQUAL = 28,
168
169         CRITERIA_TEST = 29,
170
171         CRITERIA_SIZE_GREATER = 30,
172         CRITERIA_SIZE_SMALLER = 31,
173         CRITERIA_SIZE_EQUAL   = 32,
174         
175         CRITERIA_PARTIAL = 33,
176
177         CRITERIA_FOUND_IN_ADDRESSBOOK = 34,
178         
179         CRITERIA_TAG = 35,
180         CRITERIA_TAGGED = 36,
181
182         CRITERIA_HAS_ATTACHMENT = 37,
183         CRITERIA_SIGNED = 38
184 };
185
186 enum {
187         MATCH_ALL       = 0,
188         MATCH_HEADER    = 1,
189         MATCH_AGE       = 2,
190         MATCH_PHRASE    = 3,
191         MATCH_FLAG      = 4,
192         MATCH_LABEL     = 5,
193         MATCH_THREAD    = 6,
194         MATCH_SCORE     = 7,
195         MATCH_SIZE      = 8,
196         MATCH_PARTIAL   = 9,
197         MATCH_ABOOK     = 10,
198         MATCH_TAGS      = 11,
199         MATCH_TEST      = 12
200 };
201
202 enum {
203         AGE_DAYS  = 0,
204         AGE_WEEKS = 1
205 };
206
207 enum {
208         SIZE_UNIT_BYTES  = 0,
209         SIZE_UNIT_KBYTES = 1,
210         SIZE_UNIT_MBYTES = 2
211 };
212
213 #define MB_SIZE 0x100000
214 #define KB_SIZE 0x000400
215
216 enum {
217         THREAD_IGNORED = 0,
218         THREAD_NOT_IGNORED = 1,
219         THREAD_WATCHED = 2,
220         THREAD_NOT_WATCHED = 3
221 };
222
223 /*!
224  *\brief        Contains predicate      
225  */
226 enum {
227         PREDICATE_CONTAINS = 0,
228         PREDICATE_DOES_NOT_CONTAIN = 1
229 };
230
231 /*!
232  *\brief        Enabled predicate
233  */
234 enum {
235         PREDICATE_FLAG_ENABLED = 0,
236         PREDICATE_FLAG_DISABLED = 1
237 };
238
239 /*!
240  *\brief        Hooks
241  */
242 static PrefsMatcherSignal *matchers_callback;
243
244 /* widget creating functions */
245 static void prefs_matcher_create        (void);
246
247 static void prefs_matcher_set_dialog    (MatcherList *matchers);
248 static void prefs_matcher_list_view_set_row     (GtkTreeIter *row, 
249                                                  MatcherProp *prop);
250
251 /* callback functions */
252
253 static void prefs_matcher_register_cb   (void);
254 static void prefs_matcher_substitute_cb (void);
255 static void prefs_matcher_delete_cb     (void);
256 static void prefs_matcher_up            (void);
257 static void prefs_matcher_down          (void);
258 static gboolean prefs_matcher_key_pressed(GtkWidget     *widget,
259                                          GdkEventKey    *event,
260                                          gpointer        data);
261 static void prefs_matcher_ok            (void);
262 static void prefs_matcher_cancel        (void);
263 static gint prefs_matcher_deleted       (GtkWidget *widget, GdkEventAny *event,
264                                          gpointer data);
265 static void prefs_matcher_criteria_select       (GtkWidget *widget,
266                                                  gpointer   user_data);
267 static void prefs_matcher_second_criteria_sel   (GtkWidget *widget,
268                                                  gpointer   user_data);
269 static void prefs_matcher_set_model             (GtkWidget *wiget,
270                                                  GtkTreeModel *model);
271 static MatcherList *prefs_matcher_get_list      (void);
272
273 static GtkListStore* prefs_matcher_create_data_store    (void);
274
275 static void prefs_matcher_list_view_insert_matcher      (GtkWidget *list_view,
276                                                          GtkTreeIter *row_iter,
277                                                          const gchar *matcher,
278                                                          gboolean is_valid);
279
280 static GtkWidget *prefs_matcher_list_view_create        (void);
281
282 static void prefs_matcher_create_list_view_columns      (GtkWidget *list_view);
283
284 static gboolean prefs_matcher_selected                  (GtkTreeSelection *selector,
285                                                          GtkTreeModel *model, 
286                                                          GtkTreePath *path,
287                                                          gboolean currently_selected,
288                                                          gpointer data);
289
290 static int header_name_to_crit(const gchar *header)
291 {
292         if (header == NULL)
293                 return CRITERIA_HEADER;
294
295         if (!strcasecmp(header, "Subject"))
296                 return CRITERIA_SUBJECT;
297         if (!strcasecmp(header, "From"))
298                 return CRITERIA_FROM;
299         if (!strcasecmp(header, "To"))
300                 return CRITERIA_TO;
301         if (!strcasecmp(header, "Cc"))
302                 return CRITERIA_CC;
303         if (!strcasecmp(header, "To or Cc"))
304                 return CRITERIA_TO_OR_CC;
305         if (!strcasecmp(header, "In-Reply-To"))
306                 return CRITERIA_INREPLYTO;
307         if (!strcasecmp(header, "Newsgroups"))
308                 return CRITERIA_NEWSGROUPS;
309         if (!strcasecmp(header, "References"))
310                 return CRITERIA_REFERENCES;
311
312         return CRITERIA_HEADER;
313 }
314
315 static void prefs_matcher_models_create(void)
316 {
317         GtkListStore *store;
318         GtkTreeIter iter;
319         
320         store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
321         COMBOBOX_ADD(store, _("more than"), CRITERIA_AGE_GREATER);
322         COMBOBOX_ADD(store, _("less than"), CRITERIA_AGE_LOWER);
323         matcher.model_age = GTK_TREE_MODEL(store);
324
325         store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
326         COMBOBOX_ADD(store, _("days"), AGE_DAYS);
327         COMBOBOX_ADD(store, _("weeks"), AGE_WEEKS);
328         matcher.model_age_units = GTK_TREE_MODEL(store);
329
330         store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
331         COMBOBOX_ADD(store, _("higher than"), CRITERIA_SCORE_GREATER);
332         COMBOBOX_ADD(store, _("lower than"), CRITERIA_SCORE_LOWER);
333         COMBOBOX_ADD(store, _("exactly"), CRITERIA_SCORE_EQUAL);
334         matcher.model_score = GTK_TREE_MODEL(store);
335         
336         store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
337         COMBOBOX_ADD(store, _("greater than"), CRITERIA_SIZE_GREATER);
338         COMBOBOX_ADD(store, _("smaller than"), CRITERIA_SIZE_SMALLER);
339         COMBOBOX_ADD(store, _("exactly"), CRITERIA_SIZE_EQUAL);
340         matcher.model_size = GTK_TREE_MODEL(store);
341
342         store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
343         COMBOBOX_ADD(store, _("bytes"), SIZE_UNIT_BYTES);
344         COMBOBOX_ADD(store, _("kilobytes"), SIZE_UNIT_KBYTES);
345         COMBOBOX_ADD(store, _("megabytes"), SIZE_UNIT_MBYTES);
346         matcher.model_size_units = GTK_TREE_MODEL(store);
347         
348         store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
349         COMBOBOX_ADD(store, _("contains"), 0);
350         COMBOBOX_ADD(store, _("doesn't contain"), 0);
351         matcher.model_contain = GTK_TREE_MODEL(store);
352         
353         store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
354         COMBOBOX_ADD(store, "Subject", CRITERIA_SUBJECT);
355         COMBOBOX_ADD(store, "From", CRITERIA_FROM);
356         COMBOBOX_ADD(store, "To", CRITERIA_TO);
357         COMBOBOX_ADD(store, "Cc", CRITERIA_CC);
358         COMBOBOX_ADD(store, "To or Cc", CRITERIA_TO_OR_CC);
359         COMBOBOX_ADD(store, "In-Reply-To", CRITERIA_INREPLYTO);
360         COMBOBOX_ADD(store, "Newsgroups", CRITERIA_NEWSGROUPS);
361         COMBOBOX_ADD(store, "References", CRITERIA_REFERENCES);
362         COMBOBOX_ADD(store, "Sender", CRITERIA_HEADER);
363         COMBOBOX_ADD(store, "X-ML-Name", CRITERIA_HEADER);
364         COMBOBOX_ADD(store, "X-List", CRITERIA_HEADER);
365         COMBOBOX_ADD(store, "X-Sequence", CRITERIA_HEADER);
366         COMBOBOX_ADD(store, "X-Mailer", CRITERIA_HEADER);
367         COMBOBOX_ADD(store, "X-BeenThere", CRITERIA_HEADER);
368         COMBOBOX_ADD(store, "List-Post", CRITERIA_HEADER);
369         COMBOBOX_ADD(store, "List-Id", CRITERIA_HEADER);
370         matcher.model_headers = GTK_TREE_MODEL(store);
371         
372         store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
373         COMBOBOX_ADD(store, _("headers part"), CRITERIA_HEADERS_PART);
374         COMBOBOX_ADD(store, _("body part"), CRITERIA_BODY_PART);
375         COMBOBOX_ADD(store, _("whole message"), CRITERIA_MESSAGE);
376         matcher.model_phrase = GTK_TREE_MODEL(store);
377         
378         store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
379         COMBOBOX_ADD(store, _("Unread"), CRITERIA_UNREAD);
380         COMBOBOX_ADD(store, _("New"), CRITERIA_NEW);
381         COMBOBOX_ADD(store, _("Marked"), CRITERIA_MARKED);
382         COMBOBOX_ADD(store, _("Deleted"), CRITERIA_DELETED);
383         COMBOBOX_ADD(store, _("Replied"), CRITERIA_REPLIED);
384         COMBOBOX_ADD(store, _("Forwarded"), CRITERIA_FORWARDED);
385         COMBOBOX_ADD(store, _("Locked"), CRITERIA_LOCKED);
386         COMBOBOX_ADD(store, _("Spam"), CRITERIA_SPAM);
387         COMBOBOX_ADD(store, _("Has attachment"), CRITERIA_HAS_ATTACHMENT);
388         COMBOBOX_ADD(store, _("Signed"), CRITERIA_SIGNED);
389         matcher.model_flags = GTK_TREE_MODEL(store);
390         
391         store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
392         COMBOBOX_ADD(store, _("set"), 0);
393         COMBOBOX_ADD(store, _("not set"), 1);
394         matcher.model_set = GTK_TREE_MODEL(store);
395
396         store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
397         COMBOBOX_ADD(store, _("yes"), CRITERIA_PARTIAL);
398         COMBOBOX_ADD(store, _("no"), CRITERIA_PARTIAL);
399         matcher.model_partial = GTK_TREE_MODEL(store);
400
401         store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
402         COMBOBOX_ADD(store, _("Any tags"), CRITERIA_TAGGED);
403         COMBOBOX_ADD(store, _("Specific tag"), CRITERIA_TAG);
404         matcher.model_tags = GTK_TREE_MODEL(store);
405
406         store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
407         COMBOBOX_ADD(store, _("ignored"), CRITERIA_IGNORE_THREAD);
408         COMBOBOX_ADD(store, _("not ignored"), CRITERIA_IGNORE_THREAD);
409         COMBOBOX_ADD(store, _("watched"), CRITERIA_WATCH_THREAD);
410         COMBOBOX_ADD(store, _("not watched"), CRITERIA_WATCH_THREAD);
411         matcher.model_thread = GTK_TREE_MODEL(store);
412         
413         store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
414         COMBOBOX_ADD(store, _("found"), 0);
415         COMBOBOX_ADD(store, _("not found"), 1);
416         matcher.model_found = GTK_TREE_MODEL(store);
417
418         store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
419         COMBOBOX_ADD(store, _("0 (Passed)"), 0);
420         COMBOBOX_ADD(store, _("non-0 (Failed)"), 1);
421         matcher.model_test = GTK_TREE_MODEL(store);
422 }
423
424 /*!
425  *\brief        Opens the matcher dialog with a list of conditions
426  *
427  *\param        matchers List of conditions
428  *\param        cb Callback
429  *
430  */
431 void prefs_matcher_open(MatcherList *matchers, PrefsMatcherSignal *cb)
432 {
433         inc_lock();
434
435         if (!matcher.window) {
436                 prefs_matcher_models_create();
437                 prefs_matcher_create();
438         } else {
439 #if !GTK_CHECK_VERSION(3, 0, 0)
440                 /* update color label menu */
441                 gtk_cmoption_menu_set_menu(GTK_CMOPTION_MENU(matcher.color_optmenu),
442                                 colorlabel_create_color_menu());
443 #endif
444         }
445
446         manage_window_set_transient(GTK_WINDOW(matcher.window));
447         gtk_widget_grab_focus(matcher.ok_btn);
448
449         matchers_callback = cb;
450
451         prefs_matcher_set_dialog(matchers);
452
453         gtk_widget_show(matcher.window);
454         gtk_window_set_modal(GTK_WINDOW(matcher.window), TRUE);
455 }
456
457 /*!
458  *\brief        Save Gtk object size to prefs dataset
459  */
460 static void prefs_matcher_size_allocate_cb(GtkWidget *widget,
461                                          GtkAllocation *allocation)
462 {
463         cm_return_if_fail(allocation != NULL);
464
465         prefs_common.matcherwin_width = allocation->width;
466         prefs_common.matcherwin_height = allocation->height;
467 }
468
469 /*!
470  *\brief        Create the matcher dialog
471  */
472 static void prefs_matcher_create(void)
473 {
474         GtkWidget *window;
475         GtkWidget *vbox;
476         GtkWidget *ok_btn;
477         GtkWidget *cancel_btn;
478         GtkWidget *confirm_area;
479
480         GtkWidget *vbox1;
481         GtkWidget *frame;
482         GtkWidget *table;
483         GtkWidget *upper_hbox;
484         GtkWidget *lower_hbox;
485         GtkWidget *match_hbox;
486         GtkWidget *criteria_combo;
487         GtkWidget *criteria_label;
488         GtkWidget *match_label;
489         GtkWidget *criteria_label2;
490         GtkWidget *headers_combo;
491         GtkWidget *match_combo2;
492         GtkWidget *match_label2;
493
494         GtkWidget *hbox;
495         GtkWidget *upper_filler;
496         GtkWidget *lower_filler;
497         
498         GtkWidget *criteria_combo2;
499         GtkWidget *header_entry;
500         GtkWidget *header_addr_combo;
501         GtkWidget *header_addr_entry;
502         GtkWidget *string_entry;
503         GtkWidget *addressbook_folder_combo;
504         GtkWidget *match_combo;
505         GtkWidget *bool_op_combo;
506         GtkWidget *bool_op_label;
507
508         GtkWidget *numeric_hbox;
509         GtkWidget *numeric_entry;
510         GtkWidget *numeric_label;
511         
512 #ifndef G_OS_WIN32
513         GtkWidget *regexp_checkbtn;
514 #endif
515         GtkWidget *case_checkbtn;
516
517         GtkWidget *reg_hbox;
518         GtkWidget *btn_hbox;
519         GtkWidget *arrow;
520         GtkWidget *reg_btn;
521         GtkWidget *subst_btn;
522         GtkWidget *del_btn;
523
524         GtkWidget *cond_hbox;
525         GtkWidget *cond_scrolledwin;
526         GtkWidget *cond_list_view;
527
528         GtkWidget *btn_vbox;
529         GtkWidget *up_btn;
530         GtkWidget *down_btn;
531
532         GtkWidget *test_btn;
533         GtkWidget *addressbook_select_btn;
534         GtkWidget *color_optmenu;
535
536         static GdkGeometry geometry;
537         GtkSizeGroup *size_group;
538         GtkListStore *store;
539         GtkTreeIter iter;
540
541         debug_print("Creating matcher configuration window...\n");
542
543         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "prefs_matcher");
544         gtk_container_set_border_width(GTK_CONTAINER(window), 4);
545         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
546
547         vbox = gtk_vbox_new(FALSE, 6);
548         gtk_container_add(GTK_CONTAINER(window), vbox);
549
550         gtkut_stock_button_set_create(&confirm_area, &cancel_btn, GTK_STOCK_CANCEL,
551                                       &ok_btn, GTK_STOCK_OK, NULL, NULL);
552         gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
553         gtk_widget_grab_default(ok_btn);
554
555         gtk_window_set_title(GTK_WINDOW(window),
556                              _("Condition configuration"));
557         g_signal_connect(G_OBJECT(window), "delete_event",
558                          G_CALLBACK(prefs_matcher_deleted), NULL);
559         g_signal_connect(G_OBJECT(window), "size_allocate",
560                          G_CALLBACK(prefs_matcher_size_allocate_cb), NULL);
561         g_signal_connect(G_OBJECT(window), "key_press_event",
562                          G_CALLBACK(prefs_matcher_key_pressed), NULL);
563         MANAGE_WINDOW_SIGNALS_CONNECT(window);
564         g_signal_connect(G_OBJECT(ok_btn), "clicked",
565                          G_CALLBACK(prefs_matcher_ok), NULL);
566         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
567                          G_CALLBACK(prefs_matcher_cancel), NULL);
568
569         vbox1 = gtk_vbox_new(FALSE, VSPACING);
570         gtk_box_pack_start(GTK_BOX(vbox), vbox1, TRUE, TRUE, 0);
571         gtk_container_set_border_width(GTK_CONTAINER (vbox1), 2);
572
573         frame = gtk_frame_new(_("Rule"));
574         gtk_frame_set_label_align(GTK_FRAME(frame), 0.01, 0.5);
575         gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0);
576         
577         table = gtk_table_new(3, 3, FALSE);
578         gtk_container_add(GTK_CONTAINER(frame), table);
579         gtk_widget_set_size_request(frame, -1, 105);
580         
581         upper_hbox = gtk_hbox_new(FALSE, HSPACING_NARROW);
582         hbox = gtk_hbox_new(FALSE, 0);
583         gtk_box_pack_start(GTK_BOX(hbox), upper_hbox, FALSE, FALSE, 0);
584         gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(""), TRUE, TRUE, 0);
585         gtk_table_attach(GTK_TABLE(table), hbox, 2, 3, 0, 1, 
586                         GTK_FILL, GTK_SHRINK, 2, 2);
587         
588         lower_hbox = gtk_hbox_new(FALSE, HSPACING_NARROW);
589         hbox = gtk_hbox_new(FALSE, 0);
590         gtk_box_pack_start(GTK_BOX(hbox), lower_hbox, FALSE, FALSE, 0);
591         gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(""), TRUE, TRUE, 0);
592         gtk_table_attach(GTK_TABLE(table), hbox,2, 3, 1, 2, 
593                          GTK_FILL, GTK_SHRINK, 2, 2);
594         
595         size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
596         gtk_size_group_add_widget(size_group, upper_hbox);
597         gtk_size_group_add_widget(size_group, lower_hbox);
598         
599         /* criteria combo box */
600         criteria_label = gtk_label_new(_("Match criteria:"));
601         gtk_misc_set_alignment(GTK_MISC(criteria_label), 1, 0.5);
602         gtk_widget_set_size_request(criteria_label, 100, -1);
603         gtk_table_attach(GTK_TABLE(table), criteria_label, 0, 1, 0, 1, 
604                          GTK_FILL, GTK_SHRINK, 2, 2);
605
606         criteria_combo = gtkut_sc_combobox_create(NULL, FALSE);
607         store = GTK_LIST_STORE(gtk_combo_box_get_model(
608                                 GTK_COMBO_BOX(criteria_combo)));
609         COMBOBOX_ADD(store, _("All messages"), 0);
610         COMBOBOX_ADD(store, _("Header"), 1);
611         COMBOBOX_ADD(store, _("Age"), 2);
612         COMBOBOX_ADD(store, _("Phrase"), 3);
613         COMBOBOX_ADD(store, _("Flags"), 4);
614         COMBOBOX_ADD(store, _("Color labels"), 5);
615         COMBOBOX_ADD(store, _("Thread"), 6);
616         COMBOBOX_ADD(store, _("Score"), 7);
617         COMBOBOX_ADD(store, _("Size"), 8);
618         COMBOBOX_ADD(store, _("Partially downloaded"), 9);
619         COMBOBOX_ADD(store, _("Address book"), 10);
620         COMBOBOX_ADD(store, _("Tags"), 11);
621         COMBOBOX_ADD(store, _("External program test"), 12);
622
623         gtk_widget_set_size_request(criteria_combo, 150, -1);
624         gtk_combo_box_set_active(GTK_COMBO_BOX(criteria_combo), MATCH_ALL);
625         gtk_table_attach(GTK_TABLE(table), criteria_combo, 1, 2, 0, 1,
626                          GTK_FILL, GTK_SHRINK, 2, 2);
627         g_signal_connect(G_OBJECT(criteria_combo), "changed",
628                          G_CALLBACK(prefs_matcher_criteria_select),
629                          NULL);
630         
631         upper_filler = gtk_label_new("");
632         gtk_box_pack_start(GTK_BOX(upper_hbox), upper_filler, TRUE, TRUE, 0); 
633         
634         lower_filler = gtk_label_new("");
635         gtk_box_pack_start(GTK_BOX(lower_hbox), lower_filler, TRUE, TRUE, 0);
636                          
637         criteria_label2 = gtk_label_new("");
638         gtk_box_pack_start(GTK_BOX(upper_hbox), criteria_label2, FALSE, FALSE, 0);
639
640         /* headers combo box entry */
641         headers_combo = gtk_combo_box_entry_new_with_model(matcher.model_headers, 0);
642         gtk_widget_set_size_request(headers_combo, 100, -1);
643         gtk_box_pack_start(GTK_BOX(upper_hbox), headers_combo, TRUE, TRUE, 0);
644         header_entry = gtk_bin_get_child(GTK_BIN((headers_combo)));
645         
646         criteria_combo2 = gtkut_sc_combobox_create(NULL, TRUE);
647         prefs_matcher_set_model(criteria_combo2, matcher.model_phrase);
648         gtk_box_pack_start(GTK_BOX(upper_hbox), criteria_combo2, TRUE, TRUE, 0);
649         g_signal_connect(G_OBJECT(criteria_combo2), "changed",
650                          G_CALLBACK(prefs_matcher_second_criteria_sel),
651                          NULL);
652
653         /* book/folder value */
654         addressbook_folder_combo = combobox_text_new(TRUE, _("Any"), NULL);
655         gtk_widget_set_size_request(addressbook_folder_combo, 150, -1);
656         gtk_box_pack_start(GTK_BOX(upper_hbox), addressbook_folder_combo, TRUE, TRUE, 0);                        
657
658         addressbook_select_btn = gtk_button_new_with_label(_("Select ..."));
659         gtk_box_pack_start(GTK_BOX(upper_hbox), addressbook_select_btn, FALSE, FALSE, 0);
660 #ifndef USE_NEW_ADDRBOOK
661         g_signal_connect(G_OBJECT (addressbook_select_btn), "clicked",
662                          G_CALLBACK(prefs_matcher_addressbook_select),
663                          NULL);
664 #else
665         gtk_widget_set_sensitive(GTK_WIDGET(addressbook_select_btn), FALSE);
666 #endif
667         match_label = gtk_label_new("");
668         gtk_misc_set_alignment(GTK_MISC(match_label), 1, 0.5);
669         gtk_table_attach(GTK_TABLE(table), match_label, 0, 1, 1, 2,
670                          GTK_FILL, GTK_SHRINK, 2, 2);
671
672         match_hbox = gtk_hbox_new(FALSE, 0);
673         gtk_table_attach(GTK_TABLE(table), match_hbox, 1, 2, 1, 2,
674                          GTK_FILL, GTK_SHRINK, 2, 2); 
675
676         match_combo = gtkut_sc_combobox_create(NULL, TRUE);
677         gtk_box_pack_start(GTK_BOX(match_hbox), match_combo, TRUE, TRUE, 0);
678         
679 #if !GTK_CHECK_VERSION(3, 0, 0)
680         /* color labels combo */
681         color_optmenu = gtk_cmoption_menu_new();
682         gtk_cmoption_menu_set_menu(GTK_CMOPTION_MENU(color_optmenu),
683                                  colorlabel_create_color_menu());
684         gtk_box_pack_start(GTK_BOX(match_hbox), color_optmenu, FALSE, FALSE, 0);
685 #endif
686         
687         /* address header name */
688         header_addr_combo = combobox_text_new(TRUE,
689                               Q_("Filtering Matcher Menu|All"), _("Any"),
690                               "From", "To", "Cc", "Reply-To", "Sender", NULL);
691         gtk_box_pack_start(GTK_BOX(match_hbox), header_addr_combo, FALSE, FALSE, 0);
692         header_addr_entry = gtk_bin_get_child(GTK_BIN((header_addr_combo)));
693         gtk_widget_set_size_request(header_addr_combo, 150, -1);
694         
695         match_label2 = gtk_label_new("");
696         gtk_box_pack_start(GTK_BOX(lower_hbox), match_label2, FALSE, FALSE, 0);
697
698         /* numeric value */
699         numeric_hbox = gtk_hbox_new(FALSE, HSPACING_NARROW);
700         gtk_box_pack_start(GTK_BOX(lower_hbox), numeric_hbox, FALSE, FALSE, 0);
701
702         numeric_entry = gtk_spin_button_new_with_range(0, 1000, 1);
703         gtk_spin_button_set_digits(GTK_SPIN_BUTTON(numeric_entry), 0);
704         gtk_box_pack_start(GTK_BOX(numeric_hbox), numeric_entry, FALSE, FALSE, 0);
705         
706         numeric_label = gtk_label_new("");
707         gtk_box_pack_start(GTK_BOX(numeric_hbox), numeric_label, FALSE, FALSE, 0);
708         gtk_box_pack_end(GTK_BOX(numeric_hbox), gtk_label_new(""), TRUE, TRUE, 0);
709
710         match_combo2 = gtkut_sc_combobox_create(NULL, TRUE);
711         gtk_box_pack_start(GTK_BOX(lower_hbox), match_combo2, TRUE, TRUE, 0);
712         
713         /* string value */
714         string_entry = gtk_entry_new();
715         gtk_box_pack_start(GTK_BOX(lower_hbox), string_entry, TRUE, TRUE, 0);
716
717         hbox = gtk_hbox_new(FALSE, HSPACING_NARROW);
718         gtk_size_group_add_widget(size_group, hbox);
719         PACK_CHECK_BUTTON(hbox, case_checkbtn, _("Case sensitive"));
720 #ifndef G_OS_WIN32
721         PACK_CHECK_BUTTON(hbox, regexp_checkbtn, _("Use regexp"));
722 #endif
723         gtk_box_pack_end(GTK_BOX(hbox), gtk_label_new(""), TRUE, TRUE, 0);
724         gtk_table_attach(GTK_TABLE(table), hbox, 2, 3, 2, 3,
725                          GTK_FILL, GTK_SHRINK, 4, 0);
726
727         /* test info button */
728         test_btn = gtk_button_new_from_stock(GTK_STOCK_INFO);
729         gtk_box_pack_start(GTK_BOX(lower_hbox), test_btn, FALSE, FALSE, 0);
730         g_signal_connect(G_OBJECT (test_btn), "clicked",
731                          G_CALLBACK(prefs_matcher_test_info),
732                          window);
733
734         /* register / substitute / delete */
735         reg_hbox = gtk_hbox_new(FALSE, HSPACING_NARROW);
736         gtk_box_pack_start(GTK_BOX(vbox1), reg_hbox, FALSE, FALSE, 0);
737
738         arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
739         gtk_box_pack_start(GTK_BOX(reg_hbox), arrow, FALSE, FALSE, 0);
740         gtk_widget_set_size_request(arrow, -1, 16);
741
742         btn_hbox = gtk_hbox_new(FALSE, HSPACING_NARROW);
743         gtk_box_pack_start(GTK_BOX(reg_hbox), btn_hbox, FALSE, FALSE, 0);
744
745         reg_btn = gtk_button_new_from_stock(GTK_STOCK_ADD);
746         gtk_box_pack_start(GTK_BOX(btn_hbox), reg_btn, FALSE, TRUE, 0);
747         g_signal_connect(G_OBJECT(reg_btn), "clicked",
748                          G_CALLBACK(prefs_matcher_register_cb), NULL);
749
750         subst_btn = gtkut_get_replace_btn(_("Replace"));
751         gtk_box_pack_start(GTK_BOX(btn_hbox), subst_btn, FALSE, TRUE, 0);
752         g_signal_connect(G_OBJECT(subst_btn), "clicked",
753                          G_CALLBACK(prefs_matcher_substitute_cb),
754                          NULL);
755
756         del_btn = gtk_button_new_from_stock(GTK_STOCK_DELETE);
757         gtk_box_pack_start(GTK_BOX(btn_hbox), del_btn, FALSE, TRUE, 0);
758         g_signal_connect(G_OBJECT(del_btn), "clicked",
759                          G_CALLBACK(prefs_matcher_delete_cb), NULL);
760
761         cond_hbox = gtk_hbox_new(FALSE, VBOX_BORDER);
762         gtk_box_pack_start(GTK_BOX(vbox1), cond_hbox, TRUE, TRUE, 0);
763
764         cond_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
765         gtk_widget_set_size_request(cond_scrolledwin, -1, 150);
766         gtk_box_pack_start(GTK_BOX(cond_hbox), cond_scrolledwin,
767                            TRUE, TRUE, 0);
768         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(cond_scrolledwin),
769                                        GTK_POLICY_AUTOMATIC,
770                                        GTK_POLICY_AUTOMATIC);
771
772         cond_list_view = prefs_matcher_list_view_create();                                     
773         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(cond_scrolledwin),
774                                             GTK_SHADOW_ETCHED_IN);
775         gtk_container_add(GTK_CONTAINER(cond_scrolledwin), cond_list_view);
776
777         btn_vbox = gtk_vbox_new(FALSE, VBOX_BORDER);
778         gtk_box_pack_start(GTK_BOX(cond_hbox), btn_vbox, FALSE, FALSE, 0);
779
780         up_btn = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
781         gtk_box_pack_start(GTK_BOX(btn_vbox), up_btn, FALSE, FALSE, 0);
782         g_signal_connect(G_OBJECT(up_btn), "clicked",
783                          G_CALLBACK(prefs_matcher_up), NULL);
784
785         down_btn = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
786         gtk_box_pack_start(GTK_BOX(btn_vbox), down_btn, FALSE, FALSE, 0);
787         g_signal_connect(G_OBJECT(down_btn), "clicked",
788                          G_CALLBACK(prefs_matcher_down), NULL);
789
790         /* boolean operation */
791         GtkWidget *hbox_bool = gtk_hbox_new(FALSE, HSPACING_NARROW);
792         gtk_box_pack_start(GTK_BOX(vbox1), hbox_bool, FALSE, FALSE, 0);
793
794         bool_op_label = gtk_label_new(_("Message must match"));
795         gtk_box_pack_start(GTK_BOX(hbox_bool), bool_op_label,
796                            FALSE, FALSE, 0);
797
798         bool_op_combo = combobox_text_new(FALSE, _("at least one"), 
799                                           _("all"), NULL);
800         gtk_box_pack_start(GTK_BOX(hbox_bool), bool_op_combo,
801                            FALSE, FALSE, 0);
802         gtk_box_pack_start(GTK_BOX(hbox_bool), gtk_label_new(_("of above rules")),
803                            FALSE, FALSE, 0);
804         gtk_box_pack_start(GTK_BOX(hbox_bool), gtk_label_new(""),
805                            TRUE, TRUE, 0);
806         
807         if (!geometry.min_height) {
808                 geometry.min_width = 550;
809                 geometry.min_height = 368;
810         }
811
812         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL, &geometry,
813                                       GDK_HINT_MIN_SIZE);
814         gtk_widget_set_size_request(window, prefs_common.matcherwin_width,
815                                     prefs_common.matcherwin_height);
816
817         gtk_widget_show_all(window);
818
819         matcher.window    = window;
820
821         matcher.ok_btn = ok_btn;
822
823         matcher.criteria_combo = criteria_combo;
824         matcher.criteria_combo2 = criteria_combo2;
825         matcher.header_entry = header_entry;
826         matcher.header_addr_combo = header_addr_combo;
827         matcher.header_addr_entry = header_addr_entry;
828         matcher.string_entry = string_entry;
829         matcher.numeric_entry = numeric_entry;
830         matcher.numeric_label = numeric_label;
831         matcher.addressbook_folder_combo = addressbook_folder_combo;
832         matcher.match_combo = match_combo;
833         matcher.case_checkbtn = case_checkbtn;
834 #ifndef G_OS_WIN32
835         matcher.regexp_checkbtn = regexp_checkbtn;
836 #endif
837         matcher.bool_op_combo = bool_op_combo;
838         matcher.test_btn = test_btn;
839 #ifndef USE_NEW_ADDRBOOK
840         matcher.addressbook_select_btn = addressbook_select_btn;
841 #endif
842         matcher.color_optmenu = color_optmenu;
843         matcher.match_label = match_label;
844         matcher.criteria_label2 = criteria_label2;
845         matcher.headers_combo = headers_combo;
846         matcher.match_combo2 = match_combo2;
847         matcher.match_label2 = match_label2;
848         matcher.upper_filler = upper_filler;
849         matcher.lower_filler = lower_filler;
850         
851         matcher.cond_list_view = cond_list_view;
852
853         matcher.selected_criteria = -1;
854         prefs_matcher_criteria_select(criteria_combo, NULL);
855 }
856
857 /*!
858  *\brief        Set the contents of a row
859  *
860  *\param        row Index of row to set
861  *\param        prop Condition to set
862  *
863  *\return       gint Row index \a prop has been added
864  */
865 static void prefs_matcher_list_view_set_row(GtkTreeIter *row, MatcherProp *prop)
866 {
867         gchar *matcher_str;
868
869         if (prop == NULL) {
870                 prefs_matcher_list_view_insert_matcher(matcher.cond_list_view,
871                                                        NULL, _("(New)"), FALSE);
872                 return;                                                
873         }
874
875         matcher_str = matcherprop_to_string(prop);
876         if (!row)
877                 prefs_matcher_list_view_insert_matcher(matcher.cond_list_view,
878                                                        NULL, matcher_str,
879                                                        TRUE);
880         else
881                 prefs_matcher_list_view_insert_matcher(matcher.cond_list_view,
882                                                        row, matcher_str, 
883                                                        TRUE);
884         g_free(matcher_str);
885 }
886
887 static gboolean match_combo2_model_set(void)
888 {
889         GtkTreeModel *model = gtk_combo_box_get_model(GTK_COMBO_BOX(matcher.match_combo2));
890         if (model == matcher.model_age_units ||
891             model == matcher.model_found ||
892             model == matcher.model_partial ||
893             model == matcher.model_phrase ||
894             model == matcher.model_set ||
895             model == matcher.model_size_units ||
896             model == matcher.model_tags ||
897             model == matcher.model_thread)
898                 return TRUE;
899         else
900                 debug_print("match_combo2 model unset.\n");
901
902         return FALSE;
903 }
904
905 static gboolean match_combo_model_set(void)
906 {
907         GtkTreeModel *model = gtk_combo_box_get_model(GTK_COMBO_BOX(matcher.match_combo));
908         if (model == matcher.model_age ||
909             model == matcher.model_contain ||
910             model == matcher.model_flags ||
911             model == matcher.model_score ||
912             model == matcher.model_size ||
913             model == matcher.model_test)
914                 return TRUE;
915         else
916                 debug_print("match_combo model unset.\n");
917
918         return FALSE;
919 }
920
921 /*!
922  *\brief        Clears a condition in the list widget
923  */
924 static void prefs_matcher_reset_condition(void)
925 {
926         gtk_combo_box_set_active(GTK_COMBO_BOX(matcher.criteria_combo), MATCH_ALL);
927         gtk_combo_box_set_active(GTK_COMBO_BOX(matcher.criteria_combo2), 0);
928         if (match_combo_model_set())
929                 gtk_combo_box_set_active(GTK_COMBO_BOX(matcher.match_combo), 0);
930         if (match_combo2_model_set())
931                 gtk_combo_box_set_active(GTK_COMBO_BOX(matcher.match_combo2), 0);
932 #if !GTK_CHECK_VERSION(3, 0, 0)
933         gtk_cmoption_menu_set_history(GTK_CMOPTION_MENU(matcher.color_optmenu), 0);
934 #endif
935         gtk_spin_button_set_value(GTK_SPIN_BUTTON(matcher.numeric_entry), 0);
936         gtk_entry_set_text(GTK_ENTRY(matcher.header_entry), "");
937         gtk_entry_set_text(GTK_ENTRY(matcher.header_addr_entry), "");
938         gtk_entry_set_text(GTK_ENTRY(matcher.string_entry), "");
939         gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((matcher.addressbook_folder_combo)))), "");
940 #ifndef G_OS_WIN32
941         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_checkbtn), FALSE);
942 #endif
943         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_checkbtn), FALSE);
944 }
945
946 /*!
947  *\brief        Initializes dialog with a set of conditions
948  *
949  *\param        matchers List of conditions
950  */
951 static void prefs_matcher_set_dialog(MatcherList *matchers)
952 {
953         GSList *cur;
954         gboolean bool_op = 1;
955         GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model
956                                 (GTK_TREE_VIEW(matcher.cond_list_view)));
957
958         gtk_list_store_clear(store);                            
959
960         prefs_matcher_list_view_set_row(NULL, NULL);
961         if (matchers != NULL) {
962                 for (cur = matchers->matchers; cur != NULL;
963                      cur = g_slist_next(cur)) {
964                         MatcherProp *prop;
965                         prop = (MatcherProp *) cur->data;
966                         prefs_matcher_list_view_set_row(NULL, prop);
967                 }
968
969                 bool_op = matchers->bool_and;
970         }
971         
972         gtk_combo_box_set_active(GTK_COMBO_BOX(matcher.bool_op_combo), bool_op);
973
974         prefs_matcher_reset_condition();
975         
976         combobox_set_sensitive(GTK_COMBO_BOX(matcher.criteria_combo), MATCH_TAGS,
977                         (tags_get_size() > 0) ? TRUE : FALSE);
978 }
979
980 /*!
981  *\brief        Converts current conditions in list box in
982  *              a matcher list used by the matcher.
983  *
984  *\return       MatcherList * List of conditions.
985  */
986 static MatcherList *prefs_matcher_get_list(void)
987 {
988         gchar *matcher_str;
989         MatcherProp *prop;
990         gboolean bool_and;
991         GSList *matcher_list;
992         MatcherList *matchers;
993         GtkTreeModel *model;
994         GtkTreeIter iter;
995
996         model = gtk_tree_view_get_model(GTK_TREE_VIEW(matcher.cond_list_view));
997         if (!gtk_tree_model_get_iter_first(model, &iter))
998                 return NULL;
999
1000         matcher_list = NULL;
1001
1002         do {
1003                 gboolean is_valid;
1004         
1005                 gtk_tree_model_get(model, &iter,
1006                                    PREFS_MATCHER_COND, &matcher_str,
1007                                    PREFS_MATCHER_COND_VALID, &is_valid,
1008                                    -1);
1009                 
1010                 if (is_valid) {
1011                         /* tmp = matcher_str; */
1012                         prop = matcher_parser_get_prop(matcher_str);
1013                         g_free(matcher_str);
1014                         if (prop == NULL)
1015                                 break;
1016                         
1017                         matcher_list = g_slist_append(matcher_list, prop);
1018                 }
1019         } while (gtk_tree_model_iter_next(model, &iter));
1020
1021         bool_and = gtk_combo_box_get_active(GTK_COMBO_BOX(matcher.bool_op_combo));
1022
1023         matchers = matcherlist_new(matcher_list, bool_and);
1024
1025         return matchers;
1026 }
1027
1028 /*!
1029  *\brief        Maps a keyword id (see #get_matchparser_tab_id) to a 
1030  *              criteria type (see first parameter of #matcherprop_new
1031  *              or #matcherprop_unquote_new)
1032  *
1033  *\param        matching_id Id returned by the matcher parser.
1034  *
1035  *\return       gint One of the CRITERIA_xxx constants.
1036  */
1037 static gint prefs_matcher_get_criteria_from_matching(gint matching_id)
1038 {
1039         switch(matching_id) {
1040         case MATCHCRITERIA_ALL:
1041                 return CRITERIA_ALL;
1042         case MATCHCRITERIA_NOT_UNREAD:
1043         case MATCHCRITERIA_UNREAD:
1044                 return CRITERIA_UNREAD;
1045         case MATCHCRITERIA_NOT_NEW:
1046         case MATCHCRITERIA_NEW:
1047                 return CRITERIA_NEW;
1048         case MATCHCRITERIA_NOT_MARKED:
1049         case MATCHCRITERIA_MARKED:
1050                 return CRITERIA_MARKED;
1051         case MATCHCRITERIA_NOT_DELETED:
1052         case MATCHCRITERIA_DELETED:
1053                 return CRITERIA_DELETED;
1054         case MATCHCRITERIA_NOT_REPLIED:
1055         case MATCHCRITERIA_REPLIED:
1056                 return CRITERIA_REPLIED;
1057         case MATCHCRITERIA_NOT_FORWARDED:
1058         case MATCHCRITERIA_FORWARDED:
1059                 return CRITERIA_FORWARDED;
1060         case MATCHCRITERIA_LOCKED:
1061         case MATCHCRITERIA_NOT_LOCKED:
1062                 return CRITERIA_LOCKED;
1063         case MATCHCRITERIA_NOT_SPAM:
1064         case MATCHCRITERIA_SPAM:
1065                 return CRITERIA_SPAM;
1066         case MATCHCRITERIA_HAS_ATTACHMENT:
1067         case MATCHCRITERIA_HAS_NO_ATTACHMENT:
1068                 return CRITERIA_HAS_ATTACHMENT;
1069         case MATCHCRITERIA_SIGNED:
1070         case MATCHCRITERIA_NOT_SIGNED:
1071                 return CRITERIA_SIGNED;
1072         case MATCHCRITERIA_PARTIAL:
1073         case MATCHCRITERIA_NOT_PARTIAL:
1074                 return CRITERIA_PARTIAL;
1075 #if !GTK_CHECK_VERSION(3, 0, 0)
1076         case MATCHCRITERIA_COLORLABEL:
1077         case MATCHCRITERIA_NOT_COLORLABEL:
1078                 return CRITERIA_COLORLABEL;
1079 #endif
1080         case MATCHCRITERIA_IGNORE_THREAD:
1081         case MATCHCRITERIA_NOT_IGNORE_THREAD:
1082                 return CRITERIA_IGNORE_THREAD;
1083         case MATCHCRITERIA_WATCH_THREAD:
1084         case MATCHCRITERIA_NOT_WATCH_THREAD:
1085                 return CRITERIA_WATCH_THREAD;
1086         case MATCHCRITERIA_NOT_SUBJECT:
1087         case MATCHCRITERIA_SUBJECT:
1088                 return CRITERIA_SUBJECT;
1089         case MATCHCRITERIA_NOT_FROM:
1090         case MATCHCRITERIA_FROM:
1091                 return CRITERIA_FROM;
1092         case MATCHCRITERIA_NOT_TO:
1093         case MATCHCRITERIA_TO:
1094                 return CRITERIA_TO;
1095         case MATCHCRITERIA_NOT_CC:
1096         case MATCHCRITERIA_CC:
1097                 return CRITERIA_CC;
1098         case MATCHCRITERIA_NOT_NEWSGROUPS:
1099         case MATCHCRITERIA_NEWSGROUPS:
1100                 return CRITERIA_NEWSGROUPS;
1101         case MATCHCRITERIA_NOT_INREPLYTO:
1102         case MATCHCRITERIA_INREPLYTO:
1103                 return CRITERIA_INREPLYTO;
1104         case MATCHCRITERIA_NOT_REFERENCES:
1105         case MATCHCRITERIA_REFERENCES:
1106                 return CRITERIA_REFERENCES;
1107         case MATCHCRITERIA_NOT_TO_AND_NOT_CC:
1108         case MATCHCRITERIA_TO_OR_CC:
1109                 return CRITERIA_TO_OR_CC;
1110         case MATCHCRITERIA_NOT_TAG:
1111         case MATCHCRITERIA_TAG:
1112                 return CRITERIA_TAG;
1113         case MATCHCRITERIA_NOT_TAGGED:
1114         case MATCHCRITERIA_TAGGED:
1115                 return CRITERIA_TAGGED;
1116         case MATCHCRITERIA_NOT_BODY_PART:
1117         case MATCHCRITERIA_BODY_PART:
1118                 return CRITERIA_BODY_PART;
1119         case MATCHCRITERIA_NOT_MESSAGE:
1120         case MATCHCRITERIA_MESSAGE:
1121                 return CRITERIA_MESSAGE;
1122         case MATCHCRITERIA_NOT_HEADERS_PART:
1123         case MATCHCRITERIA_HEADERS_PART:
1124                 return CRITERIA_HEADERS_PART;
1125         case MATCHCRITERIA_NOT_HEADER:
1126         case MATCHCRITERIA_HEADER:
1127                 return CRITERIA_HEADER;
1128         case MATCHCRITERIA_AGE_GREATER:
1129                 return CRITERIA_AGE_GREATER;
1130         case MATCHCRITERIA_AGE_LOWER:
1131                 return CRITERIA_AGE_LOWER;
1132         case MATCHCRITERIA_SCORE_GREATER:
1133                 return CRITERIA_SCORE_GREATER;
1134         case MATCHCRITERIA_SCORE_LOWER:
1135                 return CRITERIA_SCORE_LOWER;
1136         case MATCHCRITERIA_SCORE_EQUAL:
1137                 return CRITERIA_SCORE_EQUAL;
1138         case MATCHCRITERIA_NOT_TEST:
1139         case MATCHCRITERIA_TEST:
1140                 return CRITERIA_TEST;
1141         case MATCHCRITERIA_SIZE_GREATER:
1142                 return CRITERIA_SIZE_GREATER;
1143         case MATCHCRITERIA_SIZE_SMALLER:
1144                 return CRITERIA_SIZE_SMALLER;
1145         case MATCHCRITERIA_SIZE_EQUAL:
1146                 return CRITERIA_SIZE_EQUAL;
1147         case MATCHCRITERIA_FOUND_IN_ADDRESSBOOK:
1148         case MATCHCRITERIA_NOT_FOUND_IN_ADDRESSBOOK:
1149                 return CRITERIA_FOUND_IN_ADDRESSBOOK;
1150         default:
1151                 return -1;
1152         }
1153 }
1154
1155 /*!
1156  *\brief        Returns the matcher keyword id from a criteria id
1157  *
1158  *\param        criteria_id Criteria id (should not be the negate
1159  *              one)
1160  *
1161  *\return       gint A matcher keyword id. See #get_matchparser_tab_id.
1162  */
1163 static gint prefs_matcher_get_matching_from_criteria(gint criteria_id)
1164 {
1165         switch (criteria_id) {
1166         case CRITERIA_ALL:
1167                 return MATCHCRITERIA_ALL;
1168         case CRITERIA_UNREAD:
1169                 return MATCHCRITERIA_UNREAD;
1170         case CRITERIA_NEW:
1171                 return MATCHCRITERIA_NEW;
1172         case CRITERIA_MARKED:
1173                 return MATCHCRITERIA_MARKED;
1174         case CRITERIA_DELETED:
1175                 return MATCHCRITERIA_DELETED;
1176         case CRITERIA_REPLIED:
1177                 return MATCHCRITERIA_REPLIED;
1178         case CRITERIA_FORWARDED:
1179                 return MATCHCRITERIA_FORWARDED;
1180         case CRITERIA_LOCKED:
1181                 return MATCHCRITERIA_LOCKED;
1182         case CRITERIA_SPAM:
1183                 return MATCHCRITERIA_SPAM;
1184         case CRITERIA_HAS_ATTACHMENT:
1185                 return MATCHCRITERIA_HAS_ATTACHMENT;
1186         case CRITERIA_SIGNED:
1187                 return MATCHCRITERIA_SIGNED;
1188         case CRITERIA_PARTIAL:
1189                 return MATCHCRITERIA_PARTIAL;
1190 #if !GTK_CHECK_VERSION(3, 0, 0)
1191         case CRITERIA_COLORLABEL:
1192                 return MATCHCRITERIA_COLORLABEL;
1193 #endif
1194         case CRITERIA_IGNORE_THREAD:
1195                 return MATCHCRITERIA_IGNORE_THREAD;
1196         case CRITERIA_WATCH_THREAD:
1197                 return MATCHCRITERIA_WATCH_THREAD;
1198         case CRITERIA_SUBJECT:
1199                 return MATCHCRITERIA_SUBJECT;
1200         case CRITERIA_FROM:
1201                 return MATCHCRITERIA_FROM;
1202         case CRITERIA_TO:
1203                 return MATCHCRITERIA_TO;
1204         case CRITERIA_CC:
1205                 return MATCHCRITERIA_CC;
1206         case CRITERIA_TO_OR_CC:
1207                 return MATCHCRITERIA_TO_OR_CC;
1208         case CRITERIA_TAG:
1209                 return MATCHCRITERIA_TAG;
1210         case CRITERIA_TAGGED:
1211                 return MATCHCRITERIA_TAGGED;
1212         case CRITERIA_NEWSGROUPS:
1213                 return MATCHCRITERIA_NEWSGROUPS;
1214         case CRITERIA_INREPLYTO:
1215                 return MATCHCRITERIA_INREPLYTO;
1216         case CRITERIA_REFERENCES:
1217                 return MATCHCRITERIA_REFERENCES;
1218         case CRITERIA_AGE_GREATER:
1219                 return MATCHCRITERIA_AGE_GREATER;
1220         case CRITERIA_AGE_LOWER:
1221                 return MATCHCRITERIA_AGE_LOWER;
1222         case CRITERIA_SCORE_GREATER:
1223                 return MATCHCRITERIA_SCORE_GREATER;
1224         case CRITERIA_SCORE_LOWER:
1225                 return MATCHCRITERIA_SCORE_LOWER;
1226         case CRITERIA_SCORE_EQUAL:
1227                 return MATCHCRITERIA_SCORE_EQUAL;
1228         case CRITERIA_HEADER:
1229                 return MATCHCRITERIA_HEADER;
1230         case CRITERIA_HEADERS_PART:
1231                 return MATCHCRITERIA_HEADERS_PART;
1232         case CRITERIA_BODY_PART:
1233                 return MATCHCRITERIA_BODY_PART;
1234         case CRITERIA_MESSAGE:
1235                 return MATCHCRITERIA_MESSAGE;
1236         case CRITERIA_TEST:
1237                 return MATCHCRITERIA_TEST;
1238         case CRITERIA_SIZE_GREATER:
1239                 return MATCHCRITERIA_SIZE_GREATER;
1240         case CRITERIA_SIZE_SMALLER:
1241                 return MATCHCRITERIA_SIZE_SMALLER;
1242         case CRITERIA_SIZE_EQUAL:
1243                 return MATCHCRITERIA_SIZE_EQUAL;
1244         case CRITERIA_FOUND_IN_ADDRESSBOOK:
1245                 return MATCHCRITERIA_FOUND_IN_ADDRESSBOOK;
1246         default:
1247                 return -1;
1248         }
1249 }
1250
1251 /*!
1252  *\brief        Returns the negate matcher keyword id from a matcher keyword
1253  *              id.
1254  *
1255  *\param        matcher_criteria Matcher keyword id. 
1256  *
1257  *\return       gint A matcher keyword id. See #get_matchparser_tab_id.
1258  */
1259 static gint prefs_matcher_not_criteria(gint matcher_criteria)
1260 {
1261         switch(matcher_criteria) {
1262         case MATCHCRITERIA_UNREAD:
1263                 return MATCHCRITERIA_NOT_UNREAD;
1264         case MATCHCRITERIA_NEW:
1265                 return MATCHCRITERIA_NOT_NEW;
1266         case MATCHCRITERIA_MARKED:
1267                 return MATCHCRITERIA_NOT_MARKED;
1268         case MATCHCRITERIA_DELETED:
1269                 return MATCHCRITERIA_NOT_DELETED;
1270         case MATCHCRITERIA_REPLIED:
1271                 return MATCHCRITERIA_NOT_REPLIED;
1272         case MATCHCRITERIA_FORWARDED:
1273                 return MATCHCRITERIA_NOT_FORWARDED;
1274         case MATCHCRITERIA_LOCKED:
1275                 return MATCHCRITERIA_NOT_LOCKED;
1276         case MATCHCRITERIA_SPAM:
1277                 return MATCHCRITERIA_NOT_SPAM;
1278         case MATCHCRITERIA_HAS_ATTACHMENT:
1279                 return MATCHCRITERIA_HAS_NO_ATTACHMENT;
1280         case MATCHCRITERIA_SIGNED:
1281                 return MATCHCRITERIA_NOT_SIGNED;
1282         case MATCHCRITERIA_PARTIAL:
1283                 return MATCHCRITERIA_NOT_PARTIAL;
1284 #if !GTK_CHECK_VERSION(3, 0, 0)
1285         case MATCHCRITERIA_COLORLABEL:
1286                 return MATCHCRITERIA_NOT_COLORLABEL;
1287 #endif
1288         case MATCHCRITERIA_IGNORE_THREAD:
1289                 return MATCHCRITERIA_NOT_IGNORE_THREAD;
1290         case MATCHCRITERIA_WATCH_THREAD:
1291                 return MATCHCRITERIA_NOT_WATCH_THREAD;
1292         case MATCHCRITERIA_SUBJECT:
1293                 return MATCHCRITERIA_NOT_SUBJECT;
1294         case MATCHCRITERIA_FROM:
1295                 return MATCHCRITERIA_NOT_FROM;
1296         case MATCHCRITERIA_TO:
1297                 return MATCHCRITERIA_NOT_TO;
1298         case MATCHCRITERIA_CC:
1299                 return MATCHCRITERIA_NOT_CC;
1300         case MATCHCRITERIA_TO_OR_CC:
1301                 return MATCHCRITERIA_NOT_TO_AND_NOT_CC;
1302         case MATCHCRITERIA_TAG:
1303                 return MATCHCRITERIA_NOT_TAG;
1304         case MATCHCRITERIA_TAGGED:
1305                 return MATCHCRITERIA_NOT_TAGGED;
1306         case MATCHCRITERIA_NEWSGROUPS:
1307                 return MATCHCRITERIA_NOT_NEWSGROUPS;
1308         case MATCHCRITERIA_INREPLYTO:
1309                 return MATCHCRITERIA_NOT_INREPLYTO;
1310         case MATCHCRITERIA_REFERENCES:
1311                 return MATCHCRITERIA_NOT_REFERENCES;
1312         case MATCHCRITERIA_HEADER:
1313                 return MATCHCRITERIA_NOT_HEADER;
1314         case MATCHCRITERIA_HEADERS_PART:
1315                 return MATCHCRITERIA_NOT_HEADERS_PART;
1316         case MATCHCRITERIA_MESSAGE:
1317                 return MATCHCRITERIA_NOT_MESSAGE;
1318         case MATCHCRITERIA_TEST:
1319                 return MATCHCRITERIA_NOT_TEST;
1320         case MATCHCRITERIA_BODY_PART:
1321                 return MATCHCRITERIA_NOT_BODY_PART;
1322         case MATCHCRITERIA_FOUND_IN_ADDRESSBOOK:
1323                 return MATCHCRITERIA_NOT_FOUND_IN_ADDRESSBOOK;
1324         default:
1325                 return matcher_criteria;
1326         }
1327 }
1328
1329 static gint prefs_matcher_get_criteria(void)
1330 {
1331         gint match_criteria = gtk_combo_box_get_active(GTK_COMBO_BOX(
1332                                         matcher.criteria_combo));
1333         const gchar *header = NULL;
1334           
1335         switch (match_criteria) {
1336         case MATCH_ABOOK:
1337                 return CRITERIA_FOUND_IN_ADDRESSBOOK;   
1338         case MATCH_ALL:
1339                 return CRITERIA_ALL;
1340         case MATCH_AGE:
1341         case MATCH_SCORE:
1342         case MATCH_SIZE:
1343         case MATCH_FLAG:
1344                 return combobox_get_active_data(GTK_COMBO_BOX(
1345                                         matcher.match_combo));
1346         case MATCH_HEADER:
1347                 header = gtk_entry_get_text(GTK_ENTRY(matcher.header_entry));
1348                 return header_name_to_crit(header);
1349 #if !GTK_CHECK_VERSION(3, 0, 0)
1350         case MATCH_LABEL:
1351                 return CRITERIA_COLORLABEL;
1352 #endif
1353         case MATCH_PARTIAL:
1354                 return CRITERIA_PARTIAL;
1355         case MATCH_TEST:
1356                 return CRITERIA_TEST;
1357         case MATCH_PHRASE:
1358         case MATCH_TAGS:
1359         case MATCH_THREAD:
1360                 return combobox_get_active_data(GTK_COMBO_BOX(
1361                                         matcher.criteria_combo2));
1362         }
1363         
1364         return -1;
1365 }
1366
1367 static gint prefs_matcher_get_pred(const gint criteria)
1368 {
1369         switch(criteria) {
1370         case CRITERIA_SUBJECT:
1371         case CRITERIA_FROM:
1372         case CRITERIA_TO:
1373         case CRITERIA_CC:
1374         case CRITERIA_TO_OR_CC:
1375         case CRITERIA_NEWSGROUPS:
1376         case CRITERIA_INREPLYTO:
1377         case CRITERIA_REFERENCES:
1378         case CRITERIA_HEADER:
1379         case CRITERIA_HEADERS_PART:
1380         case CRITERIA_BODY_PART:
1381         case CRITERIA_MESSAGE:
1382         case CRITERIA_TAG:
1383         case CRITERIA_TAGGED:
1384         case CRITERIA_TEST:
1385                 return gtk_combo_box_get_active(GTK_COMBO_BOX(matcher.match_combo));
1386         case CRITERIA_FOUND_IN_ADDRESSBOOK:
1387         case CRITERIA_UNREAD:
1388         case CRITERIA_NEW:
1389         case CRITERIA_MARKED:
1390         case CRITERIA_DELETED:
1391         case CRITERIA_REPLIED:
1392         case CRITERIA_FORWARDED:
1393         case CRITERIA_LOCKED:
1394         case CRITERIA_SPAM:
1395         case CRITERIA_HAS_ATTACHMENT:
1396         case CRITERIA_SIGNED:
1397         case CRITERIA_COLORLABEL:
1398                 return gtk_combo_box_get_active(GTK_COMBO_BOX(matcher.match_combo2));
1399         case CRITERIA_WATCH_THREAD:
1400                 return gtk_combo_box_get_active(GTK_COMBO_BOX(matcher.criteria_combo2)) - 2;
1401         case CRITERIA_IGNORE_THREAD:
1402         case CRITERIA_PARTIAL:
1403                 return gtk_combo_box_get_active(GTK_COMBO_BOX(matcher.criteria_combo2));
1404         }
1405         
1406         return 0;
1407 }
1408
1409 /*!
1410  *\brief        Converts the text in the selected row to a 
1411  *              matcher structure
1412  *
1413  *\return       MatcherProp * Newly allocated matcher structure.
1414  */
1415 static MatcherProp *prefs_matcher_dialog_to_matcher(void)
1416 {
1417         MatcherProp *matcherprop;
1418         gint criteria;
1419         gint matchtype;
1420         gint value_pred;
1421         gint value_criteria = prefs_matcher_get_criteria();
1422         gboolean use_regexp;
1423         gboolean case_sensitive;
1424         const gchar *header;
1425         const gchar *expr;
1426         gint value, sel;
1427
1428         if (value_criteria == -1)
1429                 return NULL;
1430
1431         criteria = prefs_matcher_get_matching_from_criteria(value_criteria);
1432
1433         value_pred = prefs_matcher_get_pred(value_criteria);
1434         if(value_pred)
1435                 criteria = prefs_matcher_not_criteria(criteria);
1436
1437 #ifndef G_OS_WIN32
1438         use_regexp = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(matcher.regexp_checkbtn));
1439 #else
1440         use_regexp = FALSE;
1441 #endif
1442         case_sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(matcher.case_checkbtn));
1443
1444         if (use_regexp) {
1445                 if (case_sensitive)
1446                         matchtype = MATCHTYPE_REGEXP;
1447                 else
1448                         matchtype = MATCHTYPE_REGEXPCASE;
1449         }
1450         else {
1451                 if (case_sensitive)
1452                         matchtype = MATCHTYPE_MATCH;
1453                 else
1454                         matchtype = MATCHTYPE_MATCHCASE;
1455         }
1456
1457         header = NULL;
1458         expr = NULL;
1459         value = 0;
1460
1461         switch (value_criteria) {
1462         case CRITERIA_ALL:
1463         case CRITERIA_UNREAD:
1464         case CRITERIA_NEW:
1465         case CRITERIA_MARKED:
1466         case CRITERIA_DELETED:
1467         case CRITERIA_REPLIED:
1468         case CRITERIA_FORWARDED:
1469         case CRITERIA_LOCKED:
1470         case CRITERIA_SPAM:
1471         case CRITERIA_HAS_ATTACHMENT:
1472         case CRITERIA_SIGNED:
1473         case CRITERIA_PARTIAL:
1474         case CRITERIA_IGNORE_THREAD:
1475         case CRITERIA_WATCH_THREAD:
1476         case CRITERIA_TAGGED:
1477                 break;
1478
1479         case CRITERIA_SUBJECT:
1480         case CRITERIA_FROM:
1481         case CRITERIA_TO:
1482         case CRITERIA_CC:
1483         case CRITERIA_TO_OR_CC:
1484         case CRITERIA_TAG:
1485         case CRITERIA_NEWSGROUPS:
1486         case CRITERIA_INREPLYTO:
1487         case CRITERIA_REFERENCES:
1488         case CRITERIA_HEADERS_PART:
1489         case CRITERIA_BODY_PART:
1490         case CRITERIA_MESSAGE:
1491                 expr = gtk_entry_get_text(GTK_ENTRY(matcher.string_entry));
1492                 
1493                 if(*expr == '\0') {
1494                         alertpanel_error(_("Search pattern is not set."));
1495                         return NULL;
1496                 }
1497                 break;
1498
1499         case CRITERIA_TEST:
1500                 expr = gtk_entry_get_text(GTK_ENTRY(matcher.string_entry));
1501                 
1502                 if(*expr == '\0') {
1503                         alertpanel_error(_("Test command is not set."));
1504                         return NULL;
1505                 }
1506                 break;
1507
1508         case CRITERIA_AGE_GREATER:
1509         case CRITERIA_AGE_LOWER:
1510                 value = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(
1511                                                          matcher.numeric_entry));
1512                 sel = gtk_combo_box_get_active(GTK_COMBO_BOX(matcher.match_combo2));
1513                 if(sel == AGE_WEEKS)
1514                         value *= 7;
1515                 break;
1516                         
1517         case CRITERIA_SCORE_GREATER:
1518         case CRITERIA_SCORE_LOWER:
1519         case CRITERIA_SCORE_EQUAL:
1520                 value = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(
1521                                                          matcher.numeric_entry));
1522                 break;
1523                                                          
1524         case CRITERIA_SIZE_GREATER:
1525         case CRITERIA_SIZE_SMALLER:
1526         case CRITERIA_SIZE_EQUAL:
1527                 value = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(
1528                                                          matcher.numeric_entry));
1529                 sel = gtk_combo_box_get_active(GTK_COMBO_BOX(matcher.match_combo2));
1530                 if(sel == SIZE_UNIT_MBYTES)
1531                         value *= MB_SIZE;
1532                 if(sel == SIZE_UNIT_KBYTES)
1533                         value *= KB_SIZE;
1534                 break;
1535                 
1536 #if !GTK_CHECK_VERSION(3, 0, 0)
1537         case CRITERIA_COLORLABEL:
1538                 value = colorlabel_get_color_menu_active_item
1539                         (gtk_cmoption_menu_get_menu(GTK_CMOPTION_MENU
1540                                 (matcher.color_optmenu))); 
1541                 break;
1542 #endif
1543
1544         case CRITERIA_HEADER:
1545                 header = gtk_entry_get_text(GTK_ENTRY(matcher.header_entry));
1546                 expr = gtk_entry_get_text(GTK_ENTRY(matcher.string_entry));
1547
1548                 if (*header == '\0') {
1549                     alertpanel_error(_("Header name is not set."));
1550                     return NULL;
1551                 }
1552                 
1553                 if(*expr == '\0') {
1554                         alertpanel_error(_("Search pattern is not set."));
1555                         return NULL;
1556                 } 
1557                 break;
1558
1559         case CRITERIA_FOUND_IN_ADDRESSBOOK:
1560                 header = gtk_entry_get_text(GTK_ENTRY(matcher.header_addr_entry));
1561                 expr = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((matcher.addressbook_folder_combo)))));
1562
1563                 if (*header == '\0') {
1564                     alertpanel_error(_("Header name is not set."));
1565                     return NULL;
1566                 }
1567                 if (*expr == '\0') {
1568                         gchar *tmp;
1569
1570                         if (g_utf8_collate(header, Q_("Filtering Matcher Menu|All")) == 0)
1571                                 tmp = g_strdup(_("all addresses in all headers"));
1572                         else
1573                         if (g_utf8_collate(header, _("Any")) == 0)
1574                                 tmp = g_strdup(_("any address in any header"));
1575                         else
1576                                 tmp = g_strdup_printf(_("the address(es) in header '%s'"), header);
1577                         alertpanel_error(_("Book/folder path is not set.\n\n"
1578                                                 "If you want to match %s against the whole address book, "
1579                                                 "you have to select '%s' from the book/folder drop-down list."),
1580                                                 tmp, _("Any"));
1581                         g_free(tmp);
1582                     return NULL;
1583                 }
1584                 /* store UNtranslated "Any"/"All" in matcher expressions */
1585                 if (g_utf8_collate(header, Q_("Filtering Matcher Menu|All")) == 0)
1586                         header = "All";
1587                 else
1588                         if (g_utf8_collate(header, _("Any")) == 0)
1589                                 header = "Any";
1590                 if (g_utf8_collate(expr, _("Any")) == 0)
1591                         expr = "Any";
1592                 break;
1593         }
1594
1595         matcherprop = matcherprop_new_create(criteria, header, matchtype,
1596                                       expr, value);
1597
1598         return matcherprop;
1599 }
1600
1601 /*!
1602  *\brief        Signal handler for register button
1603  */
1604 static void prefs_matcher_register_cb(void)
1605 {
1606         MatcherProp *matcherprop;
1607         
1608         matcherprop = prefs_matcher_dialog_to_matcher();
1609         if (matcherprop == NULL)
1610                 return;
1611
1612         prefs_matcher_list_view_set_row(NULL, matcherprop);
1613
1614         matcherprop_free(matcherprop);
1615         
1616         prefs_matcher_reset_condition();
1617 }
1618
1619 /*!
1620  *\brief        Signal handler for substitute button
1621  */
1622 static void prefs_matcher_substitute_cb(void)
1623 {
1624         MatcherProp *matcherprop;
1625         GtkTreeIter row;
1626         GtkTreeSelection *selection;
1627         GtkTreeModel *model;
1628         gboolean is_valid;
1629
1630         selection = gtk_tree_view_get_selection
1631                         (GTK_TREE_VIEW(matcher.cond_list_view));
1632         
1633         if (!gtk_tree_selection_get_selected(selection, &model, &row))
1634                 return;
1635         
1636         gtk_tree_model_get(model, &row, 
1637                            PREFS_MATCHER_COND_VALID, &is_valid,
1638                            -1);
1639         if (!is_valid)
1640                 return;
1641
1642         matcherprop = prefs_matcher_dialog_to_matcher();
1643         if (matcherprop == NULL)
1644                 return;
1645
1646         prefs_matcher_list_view_set_row(&row, matcherprop);
1647
1648         matcherprop_free(matcherprop);
1649 }
1650
1651 /*!
1652  *\brief        Signal handler for delete button
1653  */
1654 static void prefs_matcher_delete_cb(void)
1655 {
1656         GtkTreeIter row;
1657         GtkTreeSelection *selection;
1658         GtkTreeModel *model;
1659         gboolean is_valid;
1660
1661         selection = gtk_tree_view_get_selection
1662                         (GTK_TREE_VIEW(matcher.cond_list_view));
1663         
1664         if (!gtk_tree_selection_get_selected(selection, &model, &row))
1665                 return;
1666                 
1667         gtk_tree_model_get(model, &row, 
1668                            PREFS_MATCHER_COND_VALID, &is_valid,
1669                            -1);
1670
1671         if (!is_valid)
1672                 return;
1673
1674         gtk_list_store_remove(GTK_LIST_STORE(model), &row);             
1675
1676         prefs_matcher_reset_condition();
1677 }
1678
1679 /*!
1680  *\brief        Signal handler for 'move up' button
1681  */
1682 static void prefs_matcher_up(void)
1683 {
1684         GtkTreePath *prev, *sel, *try;
1685         GtkTreeIter isel;
1686         GtkListStore *store = NULL;
1687         GtkTreeModel *model = NULL;
1688         GtkTreeIter iprev;
1689         
1690         if (!gtk_tree_selection_get_selected
1691                 (gtk_tree_view_get_selection
1692                         (GTK_TREE_VIEW(matcher.cond_list_view)),
1693                  &model,        
1694                  &isel))
1695                 return;
1696         store = (GtkListStore *)model;
1697         sel = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &isel);
1698         if (!sel)
1699                 return;
1700         
1701         /* no move if we're at row 0 or 1, looks phony, but other
1702          * solutions are more convoluted... */
1703         try = gtk_tree_path_copy(sel);
1704         if (!gtk_tree_path_prev(try) || !gtk_tree_path_prev(try)) {
1705                 gtk_tree_path_free(try);
1706                 gtk_tree_path_free(sel);
1707                 return;
1708         }
1709         gtk_tree_path_free(try);
1710
1711         prev = gtk_tree_path_copy(sel);         
1712         if (gtk_tree_path_prev(prev)) {
1713                 gtk_tree_model_get_iter(GTK_TREE_MODEL(store),
1714                                         &iprev, prev);
1715                 gtk_list_store_swap(store, &iprev, &isel);
1716                 /* XXX: GTK2 select row?? */
1717         }
1718
1719         gtk_tree_path_free(sel);
1720         gtk_tree_path_free(prev);
1721 }
1722
1723 /*!
1724  *\brief        Signal handler for 'move down' button
1725  */
1726 static void prefs_matcher_down(void)
1727 {
1728         GtkListStore *store = NULL;
1729         GtkTreeModel *model = NULL;
1730         GtkTreeIter next, sel;
1731         GtkTreePath *try;
1732         
1733         if (!gtk_tree_selection_get_selected
1734                 (gtk_tree_view_get_selection
1735                         (GTK_TREE_VIEW(matcher.cond_list_view)),
1736                  &model,
1737                  &sel))
1738                 return;
1739         store = (GtkListStore *)model;
1740         try = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &sel);
1741         if (!try) 
1742                 return;
1743         
1744         /* move when not at row 0 ... */
1745         if (gtk_tree_path_prev(try)) {
1746                 next = sel;
1747                 if (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &next))
1748                         gtk_list_store_swap(store, &next, &sel);
1749         }
1750                 
1751         gtk_tree_path_free(try);
1752 }
1753
1754 static void prefs_matcher_enable_widget(GtkWidget* widget, const gboolean enable)
1755 {
1756         cm_return_if_fail(widget != NULL);
1757
1758         if(enable == TRUE) {
1759                 gtk_widget_set_sensitive(widget, TRUE);
1760                 gtk_widget_show(widget);        
1761         } else {
1762                 gtk_widget_set_sensitive(widget, FALSE);
1763                 gtk_widget_hide(widget);
1764         }
1765 }
1766
1767 static void prefs_matcher_set_model(GtkWidget *widget, GtkTreeModel *model)
1768 {
1769         cm_return_if_fail(widget != NULL);
1770         cm_return_if_fail(model != NULL);
1771         
1772         gtk_combo_box_set_model(GTK_COMBO_BOX(widget), model);
1773         gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 0);
1774 }
1775
1776 static void prefs_matcher_second_criteria_sel(GtkWidget *widget,
1777                                               gpointer user_data)
1778 {
1779         gint criteria = gtk_combo_box_get_active(GTK_COMBO_BOX(
1780                                                 matcher.criteria_combo));
1781         gint criteria2 = combobox_get_active_data(GTK_COMBO_BOX(
1782                                                 matcher.criteria_combo2));
1783         
1784         if(criteria != MATCH_PHRASE && criteria != MATCH_TAGS) return;
1785         
1786         if(criteria == MATCH_PHRASE) {
1787                 switch(criteria2) {
1788                 case CRITERIA_HEADERS_PART:
1789                         gtk_label_set_text(GTK_LABEL(matcher.match_label),
1790                                         _("Headers part"));
1791                         break;
1792                 case CRITERIA_BODY_PART:
1793                         gtk_label_set_text(GTK_LABEL(matcher.match_label),
1794                                         _("Body part"));
1795                         break;  
1796                 case CRITERIA_MESSAGE:
1797                         gtk_label_set_text(GTK_LABEL(matcher.match_label),
1798                                         _("Whole message"));
1799                         break;
1800                 }
1801         }
1802         
1803         if(criteria == MATCH_TAGS) {
1804                 if(criteria2 == CRITERIA_TAGGED) {
1805                         prefs_matcher_enable_widget(matcher.upper_filler, FALSE);
1806                         prefs_matcher_enable_widget(matcher.match_label2, TRUE);
1807                         prefs_matcher_enable_widget(matcher.string_entry, FALSE);
1808                         prefs_matcher_enable_widget(matcher.case_checkbtn, FALSE);
1809 #ifndef G_OS_WIN32
1810                         prefs_matcher_enable_widget(matcher.regexp_checkbtn, FALSE);
1811 #endif
1812                 } else {
1813                         prefs_matcher_enable_widget(matcher.upper_filler, TRUE);
1814                         prefs_matcher_enable_widget(matcher.match_label2, FALSE);
1815                         prefs_matcher_enable_widget(matcher.string_entry, TRUE);
1816                         prefs_matcher_enable_widget(matcher.case_checkbtn, TRUE);
1817 #ifndef G_OS_WIN32
1818                         prefs_matcher_enable_widget(matcher.regexp_checkbtn, TRUE);
1819                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
1820                                                 matcher.regexp_checkbtn), FALSE);
1821 #endif
1822                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
1823                                                 matcher.case_checkbtn), FALSE);
1824                 }
1825         }
1826 }
1827
1828 #define MATCH_COMBO_IS_ENABLED(x) (x != MATCH_ALL && x != MATCH_ABOOK && \
1829                 x != MATCH_PARTIAL && x != MATCH_THREAD && x != MATCH_LABEL) ? TRUE : FALSE
1830 #define MATCH_CASE_REGEXP(x) (x == MATCH_HEADER || x == MATCH_PHRASE) ? TRUE : FALSE 
1831 #define MATCH_NUMERIC(x) (x == MATCH_AGE || x == MATCH_SCORE || \
1832                           x == MATCH_SIZE) ? TRUE : FALSE
1833
1834 /*!
1835  *\brief        Change widgets depending on the selected condition
1836  *
1837  *\param        criteria combo widget
1838  *\param        user_data Not used      
1839  */
1840 static void prefs_matcher_criteria_select(GtkWidget *widget,
1841                                           gpointer user_data)
1842 {
1843         gint value, old_value;
1844
1845         old_value = matcher.selected_criteria;
1846         matcher.selected_criteria = value = gtk_combo_box_get_active
1847                 (GTK_COMBO_BOX(matcher.criteria_combo));
1848
1849         if (old_value == matcher.selected_criteria)
1850                 return;
1851
1852         prefs_matcher_enable_widget(matcher.criteria_label2,
1853                                     (value == MATCH_ABOOK   ||
1854                                      value == MATCH_PHRASE  ||
1855                                      value == MATCH_HEADER  ||
1856                                      value == MATCH_PARTIAL ||
1857                                      value == MATCH_TAGS    ||
1858                                      value == MATCH_THREAD));
1859         prefs_matcher_enable_widget(matcher.headers_combo,
1860                                     (value == MATCH_HEADER));
1861         prefs_matcher_enable_widget(matcher.criteria_combo2,
1862                                     (value == MATCH_PHRASE  ||
1863                                      value == MATCH_PARTIAL ||
1864                                      value == MATCH_TAGS    ||
1865                                      value == MATCH_THREAD));
1866         prefs_matcher_enable_widget(matcher.match_combo2,
1867                                     (value == MATCH_ABOOK ||
1868                                      value == MATCH_AGE   ||
1869                                      value == MATCH_FLAG  ||
1870                                      value == MATCH_LABEL ||
1871                                      value == MATCH_SIZE));
1872         prefs_matcher_enable_widget(matcher.match_label2,
1873                                     (value == MATCH_ABOOK ||
1874                                      value == MATCH_FLAG  ||
1875                                      value == MATCH_LABEL ||
1876                                      value == MATCH_TAGS));
1877         prefs_matcher_enable_widget(matcher.header_addr_combo,
1878                                     (value == MATCH_ABOOK));
1879         prefs_matcher_enable_widget(matcher.string_entry,
1880                                     (MATCH_CASE_REGEXP(value) ||
1881                                      value == MATCH_TEST));
1882         prefs_matcher_enable_widget(matcher.numeric_entry,
1883                                     MATCH_NUMERIC(value));
1884         prefs_matcher_enable_widget(matcher.numeric_label,
1885                                     (value == MATCH_SCORE));
1886         prefs_matcher_enable_widget(matcher.addressbook_folder_combo,
1887                                     (value == MATCH_ABOOK));
1888         prefs_matcher_enable_widget(matcher.match_combo,
1889                                     MATCH_COMBO_IS_ENABLED(value));
1890         prefs_matcher_enable_widget(matcher.case_checkbtn,
1891                                     MATCH_CASE_REGEXP(value));
1892 #ifndef G_OS_WIN32
1893         prefs_matcher_enable_widget(matcher.regexp_checkbtn,
1894                                     MATCH_CASE_REGEXP(value));
1895 #endif
1896         prefs_matcher_enable_widget(matcher.test_btn,
1897                                     (value == MATCH_TEST));
1898         prefs_matcher_enable_widget(matcher.addressbook_select_btn,
1899                                     (value == MATCH_ABOOK));
1900 #if !GTK_CHECK_VERSION(3, 0, 0)
1901         prefs_matcher_enable_widget(matcher.color_optmenu,
1902                                     (value == MATCH_LABEL));
1903 #endif
1904         prefs_matcher_enable_widget(matcher.upper_filler,
1905                                     MATCH_CASE_REGEXP(value));
1906         prefs_matcher_enable_widget(matcher.lower_filler,
1907                                     (value == MATCH_ABOOK));
1908                                 
1909         gtk_label_set_text(GTK_LABEL(matcher.match_label), "");
1910         gtk_entry_set_text(GTK_ENTRY(matcher.string_entry), "");
1911
1912         switch(value) {
1913         case MATCH_ABOOK:
1914                 gtk_combo_box_set_active(GTK_COMBO_BOX(matcher.header_addr_combo), 0);  
1915                 gtk_combo_box_set_active(GTK_COMBO_BOX(matcher.addressbook_folder_combo), 0);
1916                 prefs_matcher_set_model(matcher.match_combo2, matcher.model_found);
1917                 gtk_label_set_text(GTK_LABEL(matcher.criteria_label2), _("in"));
1918                 gtk_label_set_text(GTK_LABEL(matcher.match_label), _("Header"));
1919                 gtk_label_set_text(GTK_LABEL(matcher.match_label2), _("content is"));
1920                 break;
1921         case MATCH_AGE:
1922                 prefs_matcher_set_model(matcher.match_combo, matcher.model_age);
1923                 prefs_matcher_set_model(matcher.match_combo2, matcher.model_age_units);
1924                 gtk_spin_button_set_range(GTK_SPIN_BUTTON(
1925                                   matcher.numeric_entry), 0, 1000);
1926                 gtk_spin_button_set_value(GTK_SPIN_BUTTON(matcher.numeric_entry), 0);
1927                 gtk_label_set_text(GTK_LABEL(matcher.match_label), _("Age is"));
1928                 break;
1929         case MATCH_FLAG:
1930                 prefs_matcher_set_model(matcher.match_combo, matcher.model_flags);
1931                 prefs_matcher_set_model(matcher.match_combo2, matcher.model_set);
1932                 gtk_label_set_text(GTK_LABEL(matcher.match_label), _("Flag"));
1933                 gtk_label_set_text(GTK_LABEL(matcher.match_label2), _("is"));
1934                 break;
1935         case MATCH_HEADER:
1936                 gtk_combo_box_set_active(GTK_COMBO_BOX(matcher.headers_combo), 0);
1937                 prefs_matcher_set_model(matcher.match_combo, matcher.model_contain);
1938                 gtk_label_set_text(GTK_LABEL(matcher.criteria_label2), _("Name:"));
1939                 gtk_label_set_text(GTK_LABEL(matcher.match_label), _("Header"));
1940 #ifndef G_OS_WIN32
1941                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_checkbtn), FALSE);
1942 #endif
1943                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_checkbtn), FALSE);
1944                 break;
1945 #if !GTK_CHECK_VERSION(3, 0, 0)
1946         case MATCH_LABEL:
1947                 gtk_cmoption_menu_set_history(GTK_CMOPTION_MENU(matcher.color_optmenu), 0);
1948                 prefs_matcher_set_model(matcher.match_combo2, matcher.model_set);
1949                 gtk_label_set_text(GTK_LABEL(matcher.match_label), _("Label"));
1950                 gtk_label_set_text(GTK_LABEL(matcher.match_label2), _("is"));
1951                 break;
1952 #endif
1953         case MATCH_PARTIAL:
1954                 prefs_matcher_set_model(matcher.criteria_combo2, matcher.model_partial);
1955                 gtk_label_set_text(GTK_LABEL(matcher.criteria_label2), _("Value:"));
1956                 break;
1957         case MATCH_PHRASE:
1958                 prefs_matcher_set_model(matcher.criteria_combo2, matcher.model_phrase);
1959                 prefs_matcher_set_model(matcher.match_combo, matcher.model_contain);
1960                 gtk_label_set_text(GTK_LABEL(matcher.criteria_label2), _("in"));
1961 #ifndef G_OS_WIN32
1962                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_checkbtn), FALSE);
1963 #endif
1964                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_checkbtn), FALSE);
1965                 prefs_matcher_second_criteria_sel(NULL, NULL);
1966                 break;  
1967         case MATCH_SCORE:
1968                 prefs_matcher_set_model(matcher.match_combo, matcher.model_score);
1969                 gtk_spin_button_set_range(GTK_SPIN_BUTTON(
1970                                           matcher.numeric_entry), -1000, 1000);
1971                 gtk_spin_button_set_value(GTK_SPIN_BUTTON(matcher.numeric_entry), 0);
1972                 gtk_label_set_text(GTK_LABEL(matcher.match_label), _("Score is"));
1973                 gtk_label_set_text(GTK_LABEL(matcher.numeric_label), _("points"));
1974                 break;  
1975         case MATCH_SIZE:
1976                 prefs_matcher_set_model(matcher.match_combo, matcher.model_size);
1977                 prefs_matcher_set_model(matcher.match_combo2, matcher.model_size_units);
1978                 gtk_combo_box_set_active(GTK_COMBO_BOX(matcher.match_combo2),
1979                                          SIZE_UNIT_KBYTES);
1980                 gtk_spin_button_set_range(GTK_SPIN_BUTTON(
1981                                           matcher.numeric_entry), 0, 100000);
1982                 gtk_spin_button_set_value(GTK_SPIN_BUTTON(matcher.numeric_entry), 0);
1983                 gtk_label_set_text(GTK_LABEL(matcher.match_label), _("Size is"));
1984                 break;
1985         case MATCH_TAGS:
1986                 prefs_matcher_set_model(matcher.criteria_combo2, matcher.model_tags);
1987                 prefs_matcher_set_model(matcher.match_combo, matcher.model_contain);
1988                 gtk_label_set_text(GTK_LABEL(matcher.criteria_label2), _("Scope:"));
1989                 gtk_label_set_text(GTK_LABEL(matcher.match_label), _("Message"));
1990                 gtk_label_set_text(GTK_LABEL(matcher.match_label2), _("tags"));
1991                 prefs_matcher_second_criteria_sel(NULL, NULL);
1992                 break;
1993         case MATCH_THREAD:
1994                 prefs_matcher_set_model(matcher.criteria_combo2, matcher.model_thread);
1995                 gtk_label_set_text(GTK_LABEL(matcher.criteria_label2), _("type is"));
1996                 break;
1997         case MATCH_TEST:
1998                 prefs_matcher_set_model(matcher.match_combo, matcher.model_test);
1999                 gtk_label_set_text(GTK_LABEL(matcher.match_label), _("Program returns"));
2000                 break;
2001         }       
2002 }
2003
2004 /*!
2005  *\brief        Handle key press
2006  *
2007  *\param        widget Widget receiving key press
2008  *\param        event Key event
2009  *\param        data User data
2010  */
2011 static gboolean prefs_matcher_key_pressed(GtkWidget *widget, GdkEventKey *event,
2012                                      gpointer data)
2013 {
2014         if (event && event->keyval == GDK_KEY_Escape) {
2015                 prefs_matcher_cancel();
2016                 return TRUE;            
2017         }
2018         return FALSE;
2019 }
2020
2021 /*!
2022  *\brief        Cancel matcher dialog
2023  */
2024 static void prefs_matcher_cancel(void)
2025 {
2026         gtk_widget_hide(matcher.window);
2027         gtk_window_set_modal(GTK_WINDOW(matcher.window), FALSE);
2028         inc_unlock();
2029 }
2030
2031 /*!
2032  *\brief        Accept current matchers
2033  */
2034 static void prefs_matcher_ok(void)
2035 {
2036         MatcherList *matchers;
2037         MatcherProp *matcherprop;
2038         AlertValue val;
2039         gchar *matcher_str = NULL;
2040         gchar *str = NULL;
2041         gint row = 1;
2042         GtkTreeModel *model;
2043         GtkTreeIter iter;
2044
2045         matchers = prefs_matcher_get_list();
2046
2047         if (matchers != NULL) {
2048                 matcherprop = prefs_matcher_dialog_to_matcher();
2049                 if (matcherprop != NULL) {
2050                         str = matcherprop_to_string(matcherprop);
2051                         matcherprop_free(matcherprop);
2052                         if (strcmp(str, "all") != 0) {
2053                                 model = gtk_tree_view_get_model(GTK_TREE_VIEW
2054                                                 (matcher.cond_list_view));
2055
2056                                 while (gtk_tree_model_iter_nth_child(model, &iter, NULL, row)) {
2057                                         gtk_tree_model_get(model, &iter,
2058                                                            PREFS_MATCHER_COND, &matcher_str,
2059                                                            -1);
2060                                         if (matcher_str && strcmp(matcher_str, str) == 0) 
2061                                                 break;
2062                                         row++;
2063                                         g_free(matcher_str);
2064                                         matcher_str = NULL;
2065                                 }
2066
2067                                 if (!matcher_str || strcmp(matcher_str, str) != 0) {
2068                                         val = alertpanel(_("Entry not saved"),
2069                                                  _("The entry was not saved.\nClose anyway?"),
2070                                                  GTK_STOCK_CLOSE, _("+_Continue editing"), NULL);
2071                                         if (G_ALERTDEFAULT != val) {
2072                                                 g_free(matcher_str);                                             
2073                                                 g_free(str);
2074                                                 return;
2075                                         }
2076                                 }
2077                                 g_free(matcher_str);
2078                         }
2079                 }
2080                 g_free(str);
2081                 gtk_widget_hide(matcher.window);
2082                 gtk_window_set_modal(GTK_WINDOW(matcher.window), FALSE);
2083                 if (matchers_callback != NULL)
2084                         matchers_callback(matchers);
2085                 matcherlist_free(matchers);
2086         }
2087         inc_unlock();
2088 }
2089
2090 /*!
2091  *\brief        Called when closing dialog box
2092  *
2093  *\param        widget Dialog widget
2094  *\param        event Event info
2095  *\param        data User data
2096  *
2097  *\return       gint TRUE
2098  */
2099 static gint prefs_matcher_deleted(GtkWidget *widget, GdkEventAny *event,
2100                                   gpointer data)
2101 {
2102         prefs_matcher_cancel();
2103         return TRUE;
2104 }
2105
2106 /*
2107  * Strings describing test format strings
2108  * 
2109  * When adding new lines, remember to put 2 strings for each line
2110  */
2111 static gchar *test_desc_strings[] = {
2112         "%%",   N_("literal %"),
2113         "%s",   N_("Subject"),
2114         "%f",   N_("From"),
2115         "%t",   N_("To"),
2116         "%c",   N_("Cc"),
2117         "%d",   N_("Date"),
2118         "%i",   N_("Message-ID"),
2119         "%n",   N_("Newsgroups"),
2120         "%r",   N_("References"),
2121         "%F",   N_("filename (should not be modified)"),
2122         "\\n",  N_("new line"),
2123         "\\",   N_("escape character for quotes"),
2124         "\\\"", N_("quote character"),
2125         NULL,   NULL
2126 };
2127
2128 static DescriptionWindow test_desc_win = { 
2129         NULL,
2130         NULL, 
2131         2,
2132         N_("Match Type: 'Test'"),
2133         N_("'Test' allows you to test a message or message element "
2134            "using an external program or script. The program will "
2135            "return either 0 or 1.\n\n"
2136            "The following symbols can be used:"),
2137         test_desc_strings
2138 };
2139
2140
2141
2142 /*!
2143  *\brief        Show Test action's info
2144  */
2145 static void prefs_matcher_test_info(GtkWidget *widget, GtkWidget *parent)
2146 {
2147         test_desc_win.parent = parent;
2148         description_window_create(&test_desc_win);
2149 }
2150
2151 #ifndef USE_NEW_ADDRBOOK
2152 static void prefs_matcher_addressbook_select(void)
2153 {
2154         const gchar *folderpath = NULL;
2155         gchar *new_path = NULL;
2156
2157         folderpath = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((matcher.addressbook_folder_combo)))));
2158         new_path = addressbook_folder_selection(folderpath);
2159         if (new_path) {
2160                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((matcher.addressbook_folder_combo)))), new_path);
2161                 g_free(new_path);
2162         } 
2163 }
2164 #endif
2165
2166 /*
2167  * list view
2168  */
2169
2170 static GtkListStore* prefs_matcher_create_data_store(void)
2171 {
2172         return gtk_list_store_new(N_PREFS_MATCHER_COLUMNS,
2173                                   G_TYPE_STRING,        
2174                                   G_TYPE_BOOLEAN,
2175                                   -1);
2176 }
2177
2178 static void prefs_matcher_list_view_insert_matcher(GtkWidget *list_view,
2179                                                    GtkTreeIter *row_iter,
2180                                                    const gchar *matcher,
2181                                                    gboolean is_valid) 
2182 {
2183         GtkTreeIter iter;
2184         GtkListStore *list_store = GTK_LIST_STORE(gtk_tree_view_get_model
2185                                         (GTK_TREE_VIEW(list_view)));
2186
2187         if (row_iter == NULL) {
2188                 /* append new */
2189                 gtk_list_store_append(list_store, &iter);
2190         } else {
2191                 /* change existing */
2192                 iter = *row_iter;
2193         }
2194
2195         gtk_list_store_set(list_store, &iter,
2196                            PREFS_MATCHER_COND, matcher,
2197                            PREFS_MATCHER_COND_VALID, is_valid,
2198                            -1);
2199 }
2200
2201 static GtkWidget *prefs_matcher_list_view_create(void)
2202 {
2203         GtkTreeView *list_view;
2204         GtkTreeSelection *selector;
2205         GtkTreeModel *model;
2206
2207         model = GTK_TREE_MODEL(prefs_matcher_create_data_store());
2208         list_view = GTK_TREE_VIEW(gtk_tree_view_new_with_model(model));
2209         g_object_unref(model);  
2210         
2211         gtk_tree_view_set_rules_hint(list_view, prefs_common.use_stripes_everywhere);
2212         gtk_tree_view_set_reorderable(list_view, TRUE);
2213         
2214         selector = gtk_tree_view_get_selection(list_view);
2215         gtk_tree_selection_set_mode(selector, GTK_SELECTION_BROWSE);
2216         gtk_tree_selection_set_select_function(selector, prefs_matcher_selected,
2217                                                NULL, NULL);
2218
2219         /* create the columns */
2220         prefs_matcher_create_list_view_columns(GTK_WIDGET(list_view));
2221
2222         return GTK_WIDGET(list_view);
2223 }
2224
2225 static void prefs_matcher_create_list_view_columns(GtkWidget *list_view)
2226 {
2227         GtkTreeViewColumn *column;
2228         GtkCellRenderer *renderer;
2229
2230         renderer = gtk_cell_renderer_text_new();
2231         column = gtk_tree_view_column_new_with_attributes
2232                 (_("Current condition rules"),
2233                  renderer,
2234                  "text", PREFS_MATCHER_COND,
2235                  NULL);
2236         gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), column);          
2237 }
2238
2239 static void prefs_matcher_set_criteria(const gint criteria)
2240 {
2241         gint match_criteria = 0;
2242         
2243         switch (criteria) {
2244         case CRITERIA_FOUND_IN_ADDRESSBOOK:
2245                 match_criteria = MATCH_ABOOK;
2246                 break;  
2247         case CRITERIA_ALL:
2248                 match_criteria = MATCH_ALL;
2249                 break;
2250         case CRITERIA_AGE_GREATER:
2251         case CRITERIA_AGE_LOWER:
2252                 match_criteria = MATCH_AGE;
2253                 break;
2254         case CRITERIA_SCORE_GREATER:
2255         case CRITERIA_SCORE_LOWER:
2256         case CRITERIA_SCORE_EQUAL:
2257                 match_criteria = MATCH_SCORE;
2258                 break;
2259         case CRITERIA_SIZE_GREATER:
2260         case CRITERIA_SIZE_SMALLER:
2261         case CRITERIA_SIZE_EQUAL:
2262                 match_criteria = MATCH_SIZE;
2263                 break;
2264         case CRITERIA_SUBJECT:
2265         case CRITERIA_FROM:
2266         case CRITERIA_TO:
2267         case CRITERIA_CC:
2268         case CRITERIA_TO_OR_CC:
2269         case CRITERIA_NEWSGROUPS:
2270         case CRITERIA_INREPLYTO:
2271         case CRITERIA_REFERENCES:
2272         case CRITERIA_HEADER:
2273                 match_criteria = MATCH_HEADER;
2274                 break;
2275         case CRITERIA_HEADERS_PART:
2276         case CRITERIA_BODY_PART:
2277         case CRITERIA_MESSAGE:
2278                 match_criteria = MATCH_PHRASE;
2279                 break;
2280         case CRITERIA_TEST:
2281                 match_criteria = MATCH_TEST;
2282                 break;
2283 #if !GTK_CHECK_VERSION(3, 0, 0)
2284         case CRITERIA_COLORLABEL:
2285                 match_criteria = MATCH_LABEL;
2286                 break;
2287 #endif
2288         case CRITERIA_TAG:
2289         case CRITERIA_TAGGED:
2290                 match_criteria = MATCH_TAGS;
2291                 break;
2292         case CRITERIA_UNREAD:
2293         case CRITERIA_NEW:
2294         case CRITERIA_MARKED:
2295         case CRITERIA_DELETED:
2296         case CRITERIA_REPLIED:
2297         case CRITERIA_FORWARDED:
2298         case CRITERIA_LOCKED:
2299         case CRITERIA_SPAM:
2300         case CRITERIA_HAS_ATTACHMENT:
2301         case CRITERIA_SIGNED:
2302                 match_criteria = MATCH_FLAG;
2303                 break;
2304         case CRITERIA_PARTIAL:
2305                 match_criteria = MATCH_PARTIAL;
2306                 break;
2307         case CRITERIA_IGNORE_THREAD:
2308         case CRITERIA_WATCH_THREAD:
2309                 match_criteria = MATCH_THREAD;
2310                 break;
2311         }
2312         
2313         gtk_combo_box_set_active(GTK_COMBO_BOX(matcher.criteria_combo),
2314                                  match_criteria);
2315         
2316         switch(match_criteria) {
2317         case MATCH_HEADER:
2318                 if(criteria != CRITERIA_HEADER)
2319                         combobox_select_by_data(GTK_COMBO_BOX(
2320                                                 matcher.headers_combo),
2321                                                 criteria);
2322                 break;
2323         case MATCH_AGE:
2324         case MATCH_SCORE:
2325         case MATCH_SIZE:
2326         case MATCH_FLAG:
2327                 combobox_select_by_data(GTK_COMBO_BOX(
2328                                         matcher.match_combo), criteria);
2329                 break;
2330         case MATCH_PHRASE:
2331         case MATCH_TAGS:
2332                 combobox_select_by_data(GTK_COMBO_BOX(
2333                                         matcher.criteria_combo2), criteria);
2334                 break;
2335         }
2336 }
2337
2338 static gboolean prefs_matcher_selected(GtkTreeSelection *selector,
2339                                        GtkTreeModel *model, 
2340                                        GtkTreePath *path,
2341                                        gboolean currently_selected,
2342                                        gpointer data)
2343 {
2344         gchar *matcher_str;
2345         MatcherProp *prop;
2346         gboolean negative_cond;
2347         gint criteria;
2348         GtkWidget *menu;
2349         GtkTreeIter iter;
2350         gboolean is_valid;
2351
2352         if (currently_selected)
2353                 return TRUE;
2354
2355         if (!gtk_tree_model_get_iter(model, &iter, path))
2356                 return TRUE;
2357
2358         gtk_tree_model_get(model, &iter, 
2359                            PREFS_MATCHER_COND_VALID,  &is_valid,
2360                            PREFS_MATCHER_COND, &matcher_str,
2361                            -1);
2362         
2363         if (!is_valid) {
2364                 g_free(matcher_str);
2365                 prefs_matcher_reset_condition();
2366                 return TRUE;
2367         }
2368
2369         negative_cond = FALSE;
2370
2371         prop = matcher_parser_get_prop(matcher_str);
2372         if (prop == NULL) {
2373                 g_free(matcher_str);
2374                 return TRUE;
2375         }               
2376
2377         criteria = prefs_matcher_get_criteria_from_matching(prop->criteria);
2378         prefs_matcher_set_criteria(criteria);
2379
2380         switch(prop->criteria) {
2381         case MATCHCRITERIA_NOT_UNREAD:
2382         case MATCHCRITERIA_NOT_NEW:
2383         case MATCHCRITERIA_NOT_MARKED:
2384         case MATCHCRITERIA_NOT_DELETED:
2385         case MATCHCRITERIA_NOT_REPLIED:
2386         case MATCHCRITERIA_NOT_FORWARDED:
2387         case MATCHCRITERIA_NOT_LOCKED:
2388         case MATCHCRITERIA_NOT_SPAM:
2389         case MATCHCRITERIA_HAS_NO_ATTACHMENT:
2390         case MATCHCRITERIA_NOT_SIGNED:
2391         case MATCHCRITERIA_NOT_PARTIAL:
2392         case MATCHCRITERIA_NOT_COLORLABEL:
2393         case MATCHCRITERIA_NOT_IGNORE_THREAD:
2394         case MATCHCRITERIA_NOT_WATCH_THREAD:
2395         case MATCHCRITERIA_NOT_SUBJECT:
2396         case MATCHCRITERIA_NOT_FROM:
2397         case MATCHCRITERIA_NOT_TO:
2398         case MATCHCRITERIA_NOT_CC:
2399         case MATCHCRITERIA_NOT_TO_AND_NOT_CC:
2400         case MATCHCRITERIA_NOT_TAG:
2401         case MATCHCRITERIA_NOT_TAGGED:
2402         case MATCHCRITERIA_NOT_NEWSGROUPS:
2403         case MATCHCRITERIA_NOT_INREPLYTO:
2404         case MATCHCRITERIA_NOT_REFERENCES:
2405         case MATCHCRITERIA_NOT_HEADER:
2406         case MATCHCRITERIA_NOT_HEADERS_PART:
2407         case MATCHCRITERIA_NOT_MESSAGE:
2408         case MATCHCRITERIA_NOT_BODY_PART:
2409         case MATCHCRITERIA_NOT_TEST:
2410         case MATCHCRITERIA_NOT_FOUND_IN_ADDRESSBOOK:
2411                 negative_cond = TRUE;
2412                 break;
2413         }
2414         
2415         switch(prop->criteria) {
2416         case MATCHCRITERIA_ALL:
2417                 break;
2418
2419         case MATCHCRITERIA_NOT_SUBJECT:
2420         case MATCHCRITERIA_NOT_FROM:
2421         case MATCHCRITERIA_NOT_TO:
2422         case MATCHCRITERIA_NOT_CC:
2423         case MATCHCRITERIA_NOT_TO_AND_NOT_CC:
2424         case MATCHCRITERIA_NOT_TAG:
2425         case MATCHCRITERIA_NOT_NEWSGROUPS:
2426         case MATCHCRITERIA_NOT_INREPLYTO:
2427         case MATCHCRITERIA_NOT_REFERENCES:
2428         case MATCHCRITERIA_NOT_HEADERS_PART:
2429         case MATCHCRITERIA_NOT_BODY_PART:
2430         case MATCHCRITERIA_NOT_MESSAGE:
2431         case MATCHCRITERIA_NOT_TEST:
2432         case MATCHCRITERIA_SUBJECT:
2433         case MATCHCRITERIA_FROM:
2434         case MATCHCRITERIA_TO:
2435         case MATCHCRITERIA_CC:
2436         case MATCHCRITERIA_TO_OR_CC:
2437         case MATCHCRITERIA_TAG:
2438         case MATCHCRITERIA_NEWSGROUPS:
2439         case MATCHCRITERIA_INREPLYTO:
2440         case MATCHCRITERIA_REFERENCES:
2441         case MATCHCRITERIA_HEADERS_PART:
2442         case MATCHCRITERIA_BODY_PART:
2443         case MATCHCRITERIA_MESSAGE:
2444         case MATCHCRITERIA_TEST:
2445                 gtk_entry_set_text(GTK_ENTRY(matcher.string_entry), prop->expr);
2446                 break;
2447
2448         case MATCHCRITERIA_FOUND_IN_ADDRESSBOOK:
2449         case MATCHCRITERIA_NOT_FOUND_IN_ADDRESSBOOK:
2450         {
2451                 gchar *header;
2452                 gchar *expr;
2453
2454                 /* matcher expressions contain UNtranslated "Any"/"All",
2455                   select the relevant translated combo item */
2456                 if (strcasecmp(prop->header, "All") == 0)
2457                         header = (gchar*)Q_("Filtering Matcher Menu|All");
2458                 else
2459                         if (strcasecmp(prop->header, "Any") == 0)
2460                                 header = _("Any");
2461                         else
2462                                 header = prop->header;
2463                 if (strcasecmp(prop->expr, "Any") == 0)
2464                         expr = _("Any");
2465                 else
2466                         expr = prop->expr;
2467
2468                 gtk_entry_set_text(GTK_ENTRY(matcher.header_addr_entry), header);
2469                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((matcher.addressbook_folder_combo)))), expr);
2470                 break;
2471         }
2472
2473         case MATCHCRITERIA_AGE_GREATER:
2474         case MATCHCRITERIA_AGE_LOWER:
2475                 if(prop->value >= 7 && !(prop->value % 7)) {
2476                         gtk_combo_box_set_active(GTK_COMBO_BOX(matcher.match_combo2),
2477                                                  AGE_WEEKS);
2478                         gtk_spin_button_set_value(GTK_SPIN_BUTTON(
2479                                         matcher.numeric_entry), prop->value/7);
2480                 } else {
2481                         gtk_combo_box_set_active(GTK_COMBO_BOX(matcher.match_combo2),
2482                                                  AGE_DAYS);
2483                         gtk_spin_button_set_value(GTK_SPIN_BUTTON(
2484                                         matcher.numeric_entry), prop->value);
2485                 }
2486                 break;
2487                 
2488         case MATCHCRITERIA_SCORE_GREATER:
2489         case MATCHCRITERIA_SCORE_LOWER:
2490         case MATCHCRITERIA_SCORE_EQUAL:
2491                 gtk_spin_button_set_value(GTK_SPIN_BUTTON(matcher.numeric_entry),
2492                                           prop->value);
2493                 break;
2494
2495         case MATCHCRITERIA_SIZE_GREATER:
2496         case MATCHCRITERIA_SIZE_SMALLER:
2497         case MATCHCRITERIA_SIZE_EQUAL:
2498                 if(prop->value >= MB_SIZE && !(prop->value % MB_SIZE)) {
2499                         gtk_combo_box_set_active(GTK_COMBO_BOX(matcher.match_combo2),
2500                                                  SIZE_UNIT_MBYTES);
2501                         gtk_spin_button_set_value(GTK_SPIN_BUTTON(
2502                                         matcher.numeric_entry), prop->value/MB_SIZE);
2503                 } else if(prop->value >= KB_SIZE && !(prop->value % KB_SIZE)) {
2504                         gtk_combo_box_set_active(GTK_COMBO_BOX(matcher.match_combo2),
2505                                                  SIZE_UNIT_KBYTES);
2506                         gtk_spin_button_set_value(GTK_SPIN_BUTTON(
2507                                         matcher.numeric_entry), prop->value/KB_SIZE);
2508                 } else {
2509                         gtk_combo_box_set_active(GTK_COMBO_BOX(matcher.match_combo2),
2510                                                  SIZE_UNIT_BYTES);
2511                         gtk_spin_button_set_value(GTK_SPIN_BUTTON(
2512                                         matcher.numeric_entry), prop->value);           
2513                 }
2514                 break;
2515
2516 #if !GTK_CHECK_VERSION(3, 0, 0)
2517         case MATCHCRITERIA_NOT_COLORLABEL:
2518         case MATCHCRITERIA_COLORLABEL:
2519                 gtk_cmoption_menu_set_history(GTK_CMOPTION_MENU(matcher.color_optmenu),
2520                                             prop->value + 1);
2521                 menu = gtk_cmoption_menu_get_menu(GTK_CMOPTION_MENU(matcher.color_optmenu));
2522                 g_signal_emit_by_name(G_OBJECT(menu), "selection-done", menu);
2523                 break;
2524 #endif
2525
2526         case MATCHCRITERIA_NOT_HEADER:
2527         case MATCHCRITERIA_HEADER:
2528                 gtk_entry_set_text(GTK_ENTRY(matcher.header_entry), prop->header);
2529                 gtk_entry_set_text(GTK_ENTRY(matcher.string_entry), prop->expr);
2530                 break;
2531         }
2532
2533         switch(criteria) {
2534         case CRITERIA_SUBJECT:
2535         case CRITERIA_FROM:
2536         case CRITERIA_TO:
2537         case CRITERIA_CC:
2538         case CRITERIA_TO_OR_CC:
2539         case CRITERIA_NEWSGROUPS:
2540         case CRITERIA_INREPLYTO:
2541         case CRITERIA_REFERENCES:
2542         case CRITERIA_HEADER:
2543         case CRITERIA_HEADERS_PART:
2544         case CRITERIA_BODY_PART:
2545         case CRITERIA_MESSAGE:
2546         case CRITERIA_TAG:
2547         case CRITERIA_TAGGED:
2548         case CRITERIA_TEST:
2549                 gtk_combo_box_set_active(GTK_COMBO_BOX(matcher.match_combo),
2550                                         negative_cond ? PREDICATE_DOES_NOT_CONTAIN :
2551                                                         PREDICATE_CONTAINS);
2552                 break;
2553         case CRITERIA_FOUND_IN_ADDRESSBOOK:
2554         case CRITERIA_UNREAD:
2555         case CRITERIA_NEW:
2556         case CRITERIA_MARKED:
2557         case CRITERIA_DELETED:
2558         case CRITERIA_REPLIED:
2559         case CRITERIA_FORWARDED:
2560         case CRITERIA_LOCKED:
2561         case CRITERIA_SPAM:
2562         case CRITERIA_HAS_ATTACHMENT:
2563         case CRITERIA_SIGNED:
2564         case CRITERIA_COLORLABEL:
2565                 gtk_combo_box_set_active(GTK_COMBO_BOX(matcher.match_combo2),
2566                                          negative_cond ? PREDICATE_FLAG_DISABLED :
2567                                                          PREDICATE_FLAG_ENABLED);
2568                 break;
2569         case CRITERIA_WATCH_THREAD:
2570                 gtk_combo_box_set_active(GTK_COMBO_BOX(matcher.criteria_combo2),
2571                                          negative_cond ? THREAD_NOT_WATCHED :
2572                                                          THREAD_WATCHED);
2573                 break;
2574         case CRITERIA_IGNORE_THREAD:
2575                 gtk_combo_box_set_active(GTK_COMBO_BOX(matcher.criteria_combo2),
2576                                          negative_cond ? THREAD_NOT_IGNORED :
2577                                                          THREAD_IGNORED);
2578                 break;  
2579         case CRITERIA_PARTIAL:
2580                 gtk_combo_box_set_active(GTK_COMBO_BOX(matcher.criteria_combo2),
2581                                          negative_cond ? PREDICATE_FLAG_DISABLED :
2582                                                          PREDICATE_FLAG_ENABLED);
2583                 break;
2584         }
2585
2586         switch(prop->matchtype) {
2587         case MATCHTYPE_MATCH:
2588 #ifndef G_OS_WIN32
2589                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_checkbtn), FALSE);
2590 #endif
2591                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_checkbtn), TRUE);
2592                 break;
2593
2594         case MATCHTYPE_MATCHCASE:
2595 #ifndef G_OS_WIN32
2596                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_checkbtn), FALSE);
2597 #endif
2598                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_checkbtn), FALSE);
2599                 break;
2600
2601         case MATCHTYPE_REGEXP:
2602 #ifndef G_OS_WIN32
2603                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_checkbtn), TRUE);
2604 #endif
2605                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_checkbtn), TRUE);
2606                 break;
2607
2608         case MATCHTYPE_REGEXPCASE:
2609 #ifndef G_OS_WIN32
2610                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_checkbtn), TRUE);
2611 #endif
2612                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_checkbtn), FALSE);
2613                 break;
2614         }
2615
2616         g_free(matcher_str);
2617         return TRUE;
2618 }
2619