finished matching UI
[claws.git] / src / prefs_matcher.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2001 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <gtk/gtk.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33
34 #include "intl.h"
35 #include "main.h"
36 #include "prefs.h"
37 #include "prefs_matcher.h"
38 #include "prefs_common.h"
39 #include "mainwindow.h"
40 #include "foldersel.h"
41 #include "manage_window.h"
42 #include "inc.h"
43 #include "matcher.h"
44 #include "utils.h"
45 #include "gtkutils.h"
46 #include "alertpanel.h"
47 #include "folder.h"
48
49 static struct Matcher {
50         GtkWidget *window;
51
52         GtkWidget *close_btn;
53
54         GtkWidget *predicate_combo;
55         GtkWidget *header_combo;
56
57         GtkWidget *criteria_list;
58         GtkWidget *predicate_list;
59         GtkWidget *bool_op_list;
60
61         GtkWidget *header_entry;
62         GtkWidget *header_label;
63         GtkWidget *value_entry;
64         GtkWidget *value_label;
65         GtkWidget *predicate_label;
66         GtkWidget *case_chkbtn;
67         GtkWidget *regexp_chkbtn;
68
69         GtkWidget *cond_clist;
70 } matcher;
71
72 /* choice in the list */
73
74 enum {
75         CRITERIA_ALL = 0,
76         CRITERIA_SUBJECT = 1,
77         CRITERIA_FROM = 2,
78         CRITERIA_TO = 3,
79         CRITERIA_CC = 4,
80         CRITERIA_TO_OR_CC = 5,
81         CRITERIA_NEWSGROUPS = 6,
82         CRITERIA_AGE_GREATER = 7,
83         CRITERIA_AGE_LOWER = 8,
84         CRITERIA_HEADER = 9,
85         CRITERIA_HEADERS_PART = 10,
86         CRITERIA_BODY_PART = 11,
87         CRITERIA_MESSAGE = 12
88 };
89
90 gchar * bool_op_text [] = {
91         "or", "and"
92 };
93
94 gchar * predicate_text [] = {
95         "contains", "does not contain"
96 };
97
98 gchar * criteria_text [] = {
99         "All messages", "Subject",
100         "From", "To", "Cc", "To or Cc",
101         "Newsgroups",
102         "Age greater than", "Age lower than",
103         "Header", "Headers part",
104         "Body part", "Whole message"
105 };
106
107 gint get_sel_from_list(GtkList * list)
108 {
109         gint row = 0;
110         void * sel;
111         GList * child;
112
113         sel = list->selection->data;
114         for(child = list->children ; child != NULL ;
115             child = g_list_next(child)) {
116                 if (child->data == sel)
117                         return row;
118                 row ++;
119         }
120         
121         return row;
122 }
123
124 enum {
125         PREDICATE_CONTAINS = 0,
126         PREDICATE_DOES_NOT_CONTAIN = 1
127 };
128
129 static MatcherList * tmp_matchers;
130
131 #define VSPACING                12
132 #define VSPACING_NARROW         4
133 #define DEFAULT_ENTRY_WIDTH     80
134 #define PREFSBUFSIZE            1024
135
136 /* widget creating functions */
137 static void prefs_matcher_create        (void);
138
139 static void prefs_matcher_set_dialog    (void);
140
141 /*
142 static void prefs_matcher_set_list      (void);
143 static gint prefs_matcher_clist_set_row (gint    row);
144 */
145
146 /* callback functions */
147
148 /*
149 static void prefs_matcher_select_dest_cb        (void);
150 */
151 static void prefs_matcher_register_cb   (void);
152 static void prefs_matcher_substitute_cb (void);
153 static void prefs_matcher_delete_cb     (void);
154 static void prefs_matcher_up            (void);
155 static void prefs_matcher_down          (void);
156 static void prefs_matcher_select        (GtkCList       *clist,
157                                          gint            row,
158                                          gint            column,
159                                          GdkEvent       *event);
160
161 static void prefs_matcher_key_pressed   (GtkWidget      *widget,
162                                          GdkEventKey    *event,
163                                          gpointer        data);
164 static void prefs_matcher_close         (void);
165 static gint prefs_matcher_deleted(GtkWidget *widget, GdkEventAny *event,
166                                   gpointer data);
167 static void prefs_matcher_criteria_select(GtkList *list,
168                                           GtkWidget *widget,
169                                           gpointer user_data);
170 static void prefs_matcher_set_list(void);
171
172 void prefs_matcher_open(MatcherList * matchers)
173 {
174         inc_autocheck_timer_remove();
175
176         if (!matcher.window) {
177                 prefs_matcher_create();
178         }
179
180         manage_window_set_transient(GTK_WINDOW(matcher.window));
181         gtk_widget_grab_focus(matcher.close_btn);
182
183         tmp_matchers = matchers;
184         prefs_matcher_set_dialog();
185
186         gtk_widget_show(matcher.window);
187 }
188
189 static void prefs_matcher_create(void)
190 {
191         GtkWidget *window;
192         GtkWidget *vbox;
193         GtkWidget *close_btn;
194         GtkWidget *confirm_area;
195
196         GtkWidget *vbox1;
197         GtkWidget *vbox2;
198         GtkWidget *vbox3;
199         GtkWidget *table1;
200
201         GtkWidget *hbox1;
202
203         GtkWidget *header_combo;
204         GtkWidget *header_entry;
205         GtkWidget *header_label;
206         GtkWidget *criteria_combo;
207         GtkWidget *criteria_list;
208         GtkWidget *criteria_label;
209         GtkWidget *value_label;
210         GtkWidget *value_entry;
211         GtkWidget *predicate_combo;
212         GtkWidget *predicate_list;
213         GtkWidget *predicate_label;
214         GtkWidget *bool_op_combo;
215         GtkWidget *bool_op_list;
216         GtkWidget *bool_op_label;
217
218         GtkWidget *regexp_chkbtn;
219         GtkWidget *case_chkbtn;
220
221         GtkWidget *reg_hbox;
222         GtkWidget *btn_hbox;
223         GtkWidget *arrow;
224         GtkWidget *reg_btn;
225         GtkWidget *subst_btn;
226         GtkWidget *del_btn;
227
228         GtkWidget *cond_hbox;
229         GtkWidget *cond_scrolledwin;
230         GtkWidget *cond_clist;
231
232         GtkWidget *btn_vbox;
233         GtkWidget *up_btn;
234         GtkWidget *down_btn;
235
236         GList *combo_items;
237         gint i;
238
239         gchar *title[] = {_("Registered rules")};
240
241         debug_print(_("Creating matcher setting window...\n"));
242
243         window = gtk_window_new (GTK_WINDOW_DIALOG);
244         gtk_container_set_border_width (GTK_CONTAINER (window), 8);
245         gtk_window_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
246         gtk_window_set_modal (GTK_WINDOW (window), TRUE);
247         gtk_window_set_policy (GTK_WINDOW (window), FALSE, TRUE, FALSE);
248
249         vbox = gtk_vbox_new (FALSE, 6);
250         gtk_widget_show (vbox);
251         gtk_container_add (GTK_CONTAINER (window), vbox);
252
253         gtkut_button_set_create (&confirm_area, &close_btn, _("Close"),
254                                  NULL, NULL, NULL, NULL);
255         gtk_widget_show (confirm_area);
256         gtk_box_pack_end (GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
257         gtk_widget_grab_default (close_btn);
258
259         gtk_window_set_title (GTK_WINDOW(window),
260                               _("Condition setting"));
261         gtk_signal_connect (GTK_OBJECT(window), "delete_event",
262                             GTK_SIGNAL_FUNC(prefs_matcher_deleted), NULL);
263         gtk_signal_connect (GTK_OBJECT(window), "key_press_event",
264                             GTK_SIGNAL_FUNC(prefs_matcher_key_pressed), NULL);
265         gtk_signal_connect (GTK_OBJECT(window), "focus_in_event",
266                             GTK_SIGNAL_FUNC(manage_window_focus_in), NULL);
267         gtk_signal_connect (GTK_OBJECT(window), "focus_out_event",
268                             GTK_SIGNAL_FUNC(manage_window_focus_out), NULL);
269         gtk_signal_connect (GTK_OBJECT(close_btn), "clicked",
270                             GTK_SIGNAL_FUNC(prefs_matcher_close), NULL);
271
272         vbox1 = gtk_vbox_new (FALSE, VSPACING);
273         gtk_widget_show (vbox1);
274         gtk_box_pack_start (GTK_BOX (vbox), vbox1, TRUE, TRUE, 0);
275         gtk_container_set_border_width (GTK_CONTAINER (vbox1), 2);
276
277         table1 = gtk_table_new (2, 3, FALSE);
278         gtk_widget_show (table1);
279
280         gtk_box_pack_start (GTK_BOX (vbox1), table1, FALSE, TRUE, 0);
281         gtk_table_set_row_spacings (GTK_TABLE (table1), 8);
282         gtk_table_set_col_spacings (GTK_TABLE (table1), 8);
283
284         /* criteria combo box */
285
286         criteria_label = gtk_label_new (_("Match type"));
287         gtk_widget_show (criteria_label);
288         gtk_misc_set_alignment (GTK_MISC (criteria_label), 0, 0.5);
289         gtk_table_attach (GTK_TABLE (table1), criteria_label, 0, 1, 0, 1,
290                           GTK_FILL, 0, 0, 0);
291
292         criteria_combo = gtk_combo_new ();
293         gtk_widget_show (criteria_combo);
294
295         combo_items = NULL;
296
297         for(i = 0 ; i < (gint) (sizeof(criteria_text) / sizeof(gchar *)) ;
298             i++) {
299                 combo_items = g_list_append(combo_items,
300                                             (gpointer) _(criteria_text[i]));
301         }
302         gtk_combo_set_popdown_strings(GTK_COMBO(criteria_combo), combo_items);
303
304         g_list_free(combo_items);
305
306         gtk_widget_set_usize (criteria_combo, 120, -1);
307         gtk_table_attach (GTK_TABLE (table1), criteria_combo, 0, 1, 1, 2,
308                           0, 0, 0, 0);
309         criteria_list = GTK_COMBO(criteria_combo)->list;
310         gtk_signal_connect (GTK_OBJECT (criteria_list), "select-child",
311                             GTK_SIGNAL_FUNC (prefs_matcher_criteria_select),
312                             NULL);
313
314         gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(criteria_combo)->entry),
315                                FALSE);
316
317         /* header name */
318
319         header_label = gtk_label_new (_("Header name"));
320         gtk_widget_show (header_label);
321         gtk_misc_set_alignment (GTK_MISC (header_label), 0, 0.5);
322         gtk_table_attach (GTK_TABLE (table1), header_label, 1, 2, 0, 1,
323                           GTK_FILL, 0, 0, 0);
324
325         header_combo = gtk_combo_new ();
326         gtk_widget_show (header_combo);
327         gtk_widget_set_usize (header_combo, 96, -1);
328         gtkut_combo_set_items (GTK_COMBO (header_combo),
329                                "Subject", "From", "To", "Cc", "Reply-To",
330                                "Sender", "X-ML-Name", "X-List", "X-Sequence",
331                                "X-Mailer",
332                                NULL);
333         gtk_table_attach (GTK_TABLE (table1), header_combo, 1, 2, 1, 2,
334                           0, 0, 0, 0);
335         header_entry = GTK_COMBO (header_combo)->entry;
336         gtk_entry_set_editable (GTK_ENTRY (header_entry), TRUE);
337
338         /* value */
339
340         value_label = gtk_label_new (_("Value"));
341         gtk_widget_show (value_label);
342         gtk_misc_set_alignment (GTK_MISC (value_label), 0, 0.5);
343         gtk_table_attach (GTK_TABLE (table1), value_label, 2, 3, 0, 1,
344                           GTK_FILL, 0, 0, 0);
345
346         value_entry = gtk_entry_new ();
347         gtk_widget_show (value_entry);
348         gtk_widget_set_usize (value_entry, 200, -1);
349         gtk_table_attach (GTK_TABLE (table1), value_entry, 2, 3, 1, 2,
350                           0, 0, 0, 0);
351
352
353         /* predicate */
354
355         vbox2 = gtk_vbox_new (FALSE, VSPACING);
356         gtk_widget_show (vbox2);
357         gtk_box_pack_start (GTK_BOX (vbox1), vbox2, TRUE, TRUE, 0);
358
359         hbox1 = gtk_hbox_new (FALSE, 8);
360         gtk_widget_show (hbox1);
361         gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, TRUE, 0);
362
363         predicate_label = gtk_label_new (_("Predicate"));
364         gtk_widget_show (predicate_label);
365         gtk_box_pack_start (GTK_BOX (hbox1), predicate_label,
366                             FALSE, FALSE, 0);
367
368         predicate_combo = gtk_combo_new ();
369         gtk_widget_show (predicate_combo);
370         gtk_widget_set_usize (predicate_combo, 120, -1);
371         predicate_list = GTK_COMBO(predicate_combo)->list;
372         gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(predicate_combo)->entry),
373                                FALSE);
374
375         combo_items = NULL;
376
377         for(i = 0 ; i < (gint) (sizeof(predicate_text) / sizeof(gchar *)) ;
378             i++) {
379                 combo_items = g_list_append(combo_items,
380                                             (gpointer) _(predicate_text[i]));
381         }
382         gtk_combo_set_popdown_strings(GTK_COMBO(predicate_combo), combo_items);
383
384         g_list_free(combo_items);
385
386         gtk_box_pack_start (GTK_BOX (hbox1), predicate_combo,
387                             FALSE, FALSE, 0);
388
389         vbox3 = gtk_vbox_new (FALSE, 0);
390         gtk_widget_show (vbox3);
391         gtk_box_pack_start (GTK_BOX (hbox1), vbox3, TRUE, TRUE, 0);
392
393         PACK_CHECK_BUTTON (vbox3, case_chkbtn, _("Case sensitive"));
394         PACK_CHECK_BUTTON (vbox3, regexp_chkbtn, _("Use regexp"));
395
396         /* register / substitute / delete */
397
398         reg_hbox = gtk_hbox_new (FALSE, 4);
399         gtk_widget_show (reg_hbox);
400         gtk_box_pack_start (GTK_BOX (vbox1), reg_hbox, FALSE, FALSE, 0);
401
402         arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
403         gtk_widget_show (arrow);
404         gtk_box_pack_start (GTK_BOX (reg_hbox), arrow, FALSE, FALSE, 0);
405         gtk_widget_set_usize (arrow, -1, 16);
406
407         btn_hbox = gtk_hbox_new (TRUE, 4);
408         gtk_widget_show (btn_hbox);
409         gtk_box_pack_start (GTK_BOX (reg_hbox), btn_hbox, FALSE, FALSE, 0);
410
411         reg_btn = gtk_button_new_with_label (_("Register"));
412         gtk_widget_show (reg_btn);
413         gtk_box_pack_start (GTK_BOX (btn_hbox), reg_btn, FALSE, TRUE, 0);
414         gtk_signal_connect (GTK_OBJECT (reg_btn), "clicked",
415                             GTK_SIGNAL_FUNC (prefs_matcher_register_cb), NULL);
416
417         subst_btn = gtk_button_new_with_label (_(" Substitute "));
418         gtk_widget_show (subst_btn);
419         gtk_box_pack_start (GTK_BOX (btn_hbox), subst_btn, FALSE, TRUE, 0);
420         gtk_signal_connect (GTK_OBJECT (subst_btn), "clicked",
421                             GTK_SIGNAL_FUNC (prefs_matcher_substitute_cb),
422                             NULL);
423
424         del_btn = gtk_button_new_with_label (_("Delete"));
425         gtk_widget_show (del_btn);
426         gtk_box_pack_start (GTK_BOX (btn_hbox), del_btn, FALSE, TRUE, 0);
427         gtk_signal_connect (GTK_OBJECT (del_btn), "clicked",
428                             GTK_SIGNAL_FUNC (prefs_matcher_delete_cb), NULL);
429
430         /* boolean operation */
431
432         bool_op_label = gtk_label_new (_("Boolean Op"));
433         gtk_misc_set_alignment (GTK_MISC (value_label), 0, 0.5);
434         gtk_widget_show (bool_op_label);
435         gtk_box_pack_start (GTK_BOX (btn_hbox), bool_op_label,
436                             FALSE, FALSE, 0);
437
438         bool_op_combo = gtk_combo_new ();
439         gtk_widget_show (bool_op_combo);
440         gtk_widget_set_usize (bool_op_combo, 50, -1);
441         bool_op_list = GTK_COMBO(bool_op_combo)->list;
442         gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(bool_op_combo)->entry),
443                                FALSE);
444
445         combo_items = NULL;
446
447         for(i = 0 ; i < (gint) (sizeof(bool_op_text) / sizeof(gchar *)) ;
448             i++) {
449                 combo_items = g_list_append(combo_items,
450                                             (gpointer) _(bool_op_text[i]));
451         }
452         gtk_combo_set_popdown_strings(GTK_COMBO(bool_op_combo), combo_items);
453
454         g_list_free(combo_items);
455
456         gtk_box_pack_start (GTK_BOX (btn_hbox), bool_op_combo,
457                             FALSE, TRUE, 0);
458
459         cond_hbox = gtk_hbox_new (FALSE, 8);
460         gtk_widget_show (cond_hbox);
461         gtk_box_pack_start (GTK_BOX (vbox1), cond_hbox, TRUE, TRUE, 0);
462
463         cond_scrolledwin = gtk_scrolled_window_new (NULL, NULL);
464         gtk_widget_show (cond_scrolledwin);
465         gtk_widget_set_usize (cond_scrolledwin, -1, 150);
466         gtk_box_pack_start (GTK_BOX (cond_hbox), cond_scrolledwin,
467                             TRUE, TRUE, 0);
468         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (cond_scrolledwin),
469                                         GTK_POLICY_AUTOMATIC,
470                                         GTK_POLICY_AUTOMATIC);
471
472         cond_clist = gtk_clist_new_with_titles(1, title);
473         gtk_widget_show (cond_clist);
474         gtk_container_add (GTK_CONTAINER (cond_scrolledwin), cond_clist);
475         gtk_clist_set_column_width (GTK_CLIST (cond_clist), 0, 80);
476         gtk_clist_set_selection_mode (GTK_CLIST (cond_clist),
477                                       GTK_SELECTION_BROWSE);
478         GTK_WIDGET_UNSET_FLAGS (GTK_CLIST (cond_clist)->column[0].button,
479                                 GTK_CAN_FOCUS);
480         gtk_signal_connect (GTK_OBJECT (cond_clist), "select_row",
481                             GTK_SIGNAL_FUNC (prefs_matcher_select), NULL);
482
483         btn_vbox = gtk_vbox_new (FALSE, 8);
484         gtk_widget_show (btn_vbox);
485         gtk_box_pack_start (GTK_BOX (cond_hbox), btn_vbox, FALSE, FALSE, 0);
486
487         up_btn = gtk_button_new_with_label (_("Up"));
488         gtk_widget_show (up_btn);
489         gtk_box_pack_start (GTK_BOX (btn_vbox), up_btn, FALSE, FALSE, 0);
490         gtk_signal_connect (GTK_OBJECT (up_btn), "clicked",
491                             GTK_SIGNAL_FUNC (prefs_matcher_up), NULL);
492
493         down_btn = gtk_button_new_with_label (_("Down"));
494         gtk_widget_show (down_btn);
495         gtk_box_pack_start (GTK_BOX (btn_vbox), down_btn, FALSE, FALSE, 0);
496         gtk_signal_connect (GTK_OBJECT (down_btn), "clicked",
497                             GTK_SIGNAL_FUNC (prefs_matcher_down), NULL);
498
499         gtk_widget_show_all(window);
500
501         matcher.window    = window;
502         matcher.close_btn = close_btn;
503
504         matcher.criteria_list = criteria_list;
505         matcher.header_combo = header_combo;
506         matcher.header_entry = header_entry;
507         matcher.header_label = header_label;
508         matcher.value_entry = value_entry;
509         matcher.value_label = value_label;
510         matcher.predicate_label = predicate_label;
511         matcher.predicate_list = predicate_list;
512         matcher.predicate_combo = predicate_combo;
513         matcher.case_chkbtn = case_chkbtn;
514         matcher.regexp_chkbtn = regexp_chkbtn;
515         matcher.bool_op_list = bool_op_list;
516
517         matcher.cond_clist   = cond_clist;
518 }
519
520 static gint prefs_matcher_clist_set_row(gint row, MatcherProp * prop)
521 {
522         GtkCList *clist = GTK_CLIST(matcher.cond_clist);
523         gchar * cond_str[1];
524         gchar * matcher_str;
525
526         if (prop == NULL) {
527                 cond_str[0] = _("(New)");
528                 return gtk_clist_append(clist, cond_str);
529         }
530
531         matcher_str = matcherprop_to_string(prop);
532         cond_str[0] = matcher_str;
533         if (row < 0)
534                 row = gtk_clist_append(clist, cond_str);
535         else
536                 gtk_clist_set_text(clist, row, 0, cond_str[0]);
537         g_free(matcher_str);
538
539         return row;
540 }
541
542 static void prefs_matcher_reset_condition(void)
543 {
544         gtk_list_select_item(GTK_LIST(matcher.criteria_list), 0);
545         gtk_list_select_item(GTK_LIST(matcher.predicate_list), 0);
546         gtk_entry_set_text(GTK_ENTRY(matcher.header_entry), "");
547         gtk_entry_set_text(GTK_ENTRY(matcher.value_entry), "");
548 }
549
550  static void prefs_matcher_set_dialog(void)
551 {
552         GtkCList *clist = GTK_CLIST(matcher.cond_clist);
553         GSList * cur;
554         gboolean bool_op = 0;
555
556         gtk_clist_freeze(clist);
557         gtk_clist_clear(clist);
558
559         prefs_matcher_clist_set_row(-1, NULL);
560         if (tmp_matchers != NULL) {
561                 for (cur = tmp_matchers->matchers ; cur != NULL ;
562                      cur = g_slist_next(cur)) {
563                         MatcherProp * prop;
564                         prop = (MatcherProp *) cur->data;
565                         prefs_matcher_clist_set_row(-1, prop);
566                         matcherprop_free(prop);
567                 }
568
569                 bool_op = tmp_matchers->bool_and;
570         }
571         gtk_clist_thaw(clist);
572
573         gtk_list_select_item(GTK_LIST(matcher.bool_op_list), bool_op);
574
575         prefs_matcher_reset_condition();
576 }
577
578 static void prefs_matcher_set_list(void)
579 {
580         gchar * matcher_str;
581         MatcherProp * prop;
582         gint row = 1;
583         GSList * l;
584         gchar * tmp;
585
586         if (tmp_matchers == NULL)
587                 return;
588
589         /* free old */
590
591         for(l = tmp_matchers->matchers ; l != NULL ; l = g_slist_next(l))
592                 matcherprop_free((MatcherProp *) l->data);
593         g_slist_free(tmp_matchers->matchers);
594         tmp_matchers->matchers = NULL;
595
596         /* set new */
597
598         while (gtk_clist_get_text(GTK_CLIST(matcher.cond_clist),
599                                   row, 0, &matcher_str)) {
600
601                 if (strcmp(matcher_str, _("(New)")) != 0) {
602                         tmp = matcher_str;
603                         prop = matcherprop_parse(&tmp);
604                         
605                         if (tmp == NULL)
606                                 break;
607                         
608                         tmp_matchers->matchers =
609                                 g_slist_append(tmp_matchers->matchers, prop);
610                 }
611                 row ++;
612         }
613
614         tmp_matchers->bool_and = get_sel_from_list(GTK_LIST(matcher.bool_op_list));
615
616 }
617
618 static gint prefs_matcher_get_matching_from_criteria(gint criteria_id)
619 {
620         switch (criteria_id) {
621         case CRITERIA_ALL:
622                 return MATCHING_ALL;
623         case CRITERIA_SUBJECT:
624                 return MATCHING_SUBJECT;
625         case CRITERIA_FROM:
626                 return MATCHING_FROM;
627         case CRITERIA_TO:
628                 return MATCHING_TO;
629         case CRITERIA_CC:
630                 return MATCHING_CC;
631         case CRITERIA_TO_OR_CC:
632                 return MATCHING_TO_OR_CC;
633         case CRITERIA_NEWSGROUPS:
634                 return MATCHING_NEWSGROUPS;
635         case CRITERIA_AGE_GREATER:
636                 return MATCHING_AGE_GREATER;
637         case CRITERIA_AGE_LOWER:
638                 return MATCHING_AGE_LOWER;
639         case CRITERIA_HEADER:
640                 return MATCHING_HEADER;
641         case CRITERIA_HEADERS_PART:
642                 return MATCHING_HEADERS_PART;
643         case CRITERIA_BODY_PART:
644                 return MATCHING_BODY_PART;
645         case CRITERIA_MESSAGE:
646                 return MATCHING_MESSAGE;
647         default:
648                 return -1;
649         }
650 }
651
652 static gint prefs_matcher_not_criteria(gint matcher_criteria)
653 {
654         switch(matcher_criteria) {
655         case MATCHING_SUBJECT:
656                 return MATCHING_NOT_SUBJECT;
657         case MATCHING_FROM:
658                 return MATCHING_NOT_FROM;
659         case MATCHING_TO:
660                 return MATCHING_NOT_TO;
661         case MATCHING_CC:
662                 return MATCHING_NOT_CC;
663         case MATCHING_TO_OR_CC:
664                 return MATCHING_NOT_TO_AND_NOT_CC;
665         case MATCHING_NEWSGROUPS:
666                 return MATCHING_NOT_NEWSGROUPS;
667         case MATCHING_HEADER:
668                 return MATCHING_NOT_HEADER;
669         case MATCHING_HEADERS_PART:
670                 return MATCHING_NOT_HEADERS_PART;
671         case MATCHING_MESSAGE:
672                 return MATCHING_NOT_MESSAGE;
673         case MATCHING_BODY_PART:
674                 return MATCHING_NOT_BODY_PART;
675         default:
676                 return matcher_criteria;
677         }
678 }
679
680 static MatcherProp * prefs_matcher_dialog_to_matcher()
681 {
682         MatcherProp * matcherprop;
683         gint criteria;
684         gint matchtype;
685         gint value_pred;
686         gint value_criteria;
687         gboolean use_regexp;
688         gboolean case_sensitive;
689         gchar * header;
690         gchar * expr;
691         gint age;
692         gchar * age_str;
693
694         value_criteria = get_sel_from_list(GTK_LIST(matcher.criteria_list));
695
696         criteria = prefs_matcher_get_matching_from_criteria(value_criteria);
697
698         value_pred = get_sel_from_list(GTK_LIST(matcher.predicate_list));
699
700         use_regexp = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(matcher.regexp_chkbtn));
701         case_sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(matcher.case_chkbtn));
702
703         if (value_pred != PREDICATE_CONTAINS)
704                 criteria = prefs_matcher_not_criteria(criteria);
705
706         if (use_regexp) {
707                 if (case_sensitive)
708                         matchtype = MATCHING_REGEXP;
709                 else
710                         matchtype = MATCHING_REGEXPCASE;
711         }
712         else {
713                 if (case_sensitive)
714                         matchtype = MATCHING_MATCH;
715                 else
716                         matchtype = MATCHING_MATCHCASE;
717         }
718
719         header = NULL;
720         expr = NULL;
721         age = 0;
722
723         switch (value_criteria) {
724         case CRITERIA_ALL:
725                 break;
726
727         case CRITERIA_SUBJECT:
728         case CRITERIA_FROM:
729         case CRITERIA_TO:
730         case CRITERIA_CC:
731         case CRITERIA_TO_OR_CC:
732         case CRITERIA_NEWSGROUPS:
733         case CRITERIA_HEADERS_PART:
734         case CRITERIA_BODY_PART:
735         case CRITERIA_MESSAGE:
736                 expr = gtk_entry_get_text(GTK_ENTRY(matcher.value_entry));
737
738                 if (*expr == '\0') {
739                     alertpanel_error(_("Match string is not set."));
740                     return NULL;
741                 }
742
743                 break;
744
745         case CRITERIA_AGE_GREATER:
746         case CRITERIA_AGE_LOWER:
747                 age_str = gtk_entry_get_text(GTK_ENTRY(matcher.value_entry));
748
749                 if (*age_str == '\0') {
750                     alertpanel_error(_("Age is not set."));
751                     return NULL;
752                 }
753
754                 age = atoi(age_str);
755
756                 break;
757
758         case CRITERIA_HEADER:
759
760                 header = gtk_entry_get_text(GTK_ENTRY(matcher.header_entry));
761                 expr = gtk_entry_get_text(GTK_ENTRY(matcher.value_entry));
762
763                 if (*header == '\0') {
764                     alertpanel_error(_("Header name is not set."));
765                     return NULL;
766                 }
767                 if (*expr == '\0') {
768                     alertpanel_error(_("Match string is not set."));
769                     return NULL;
770                 }
771
772                 break;
773         }
774
775         matcherprop =  matcherprop_new(criteria, header, matchtype, expr, age);
776         
777         return matcherprop;
778 }
779
780 static void prefs_matcher_register_cb(void)
781 {
782         MatcherProp * matcherprop;
783         
784         matcherprop = prefs_matcher_dialog_to_matcher();
785         if (matcherprop == NULL)
786                 return;
787
788         prefs_matcher_clist_set_row(-1, matcherprop);
789
790         matcherprop_free(matcherprop);
791
792         prefs_matcher_reset_condition();
793 }
794
795 static void prefs_matcher_substitute_cb(void)
796 {
797         GtkCList *clist = GTK_CLIST(matcher.cond_clist);
798         gint row;
799         MatcherProp * matcherprop;
800
801         row = GPOINTER_TO_INT(clist->selection->data);
802         if (row == 0)
803                 return;
804         
805         matcherprop = prefs_matcher_dialog_to_matcher();
806         if (matcherprop == NULL)
807                 return;
808
809         prefs_matcher_clist_set_row(row, matcherprop);
810
811         matcherprop_free(matcherprop);
812
813         prefs_matcher_reset_condition();
814 }
815
816 static void prefs_matcher_delete_cb(void)
817 {
818         GtkCList *clist = GTK_CLIST(matcher.cond_clist);
819         gint row;
820
821         if (!clist->selection) return;
822         row = GPOINTER_TO_INT(clist->selection->data);
823         if (row == 0)
824                 return;
825
826         gtk_clist_remove(clist, row);
827 }
828
829 static void prefs_matcher_up(void)
830 {
831         GtkCList *clist = GTK_CLIST(matcher.cond_clist);
832         gint row;
833
834         if (!clist->selection) return;
835
836         row = GPOINTER_TO_INT(clist->selection->data);
837         if (row > 1)
838                 gtk_clist_row_move(clist, row, row - 1);
839 }
840
841 static void prefs_matcher_down(void)
842 {
843         GtkCList *clist = GTK_CLIST(matcher.cond_clist);
844         gint row;
845
846         if (!clist->selection) return;
847
848         row = GPOINTER_TO_INT(clist->selection->data);
849         if (row >= 1 && row < clist->rows - 1)
850                 gtk_clist_row_move(clist, row, row + 1);
851 }
852
853 static void prefs_matcher_select(GtkCList *clist, gint row, gint column,
854                                  GdkEvent *event)
855 {
856         gchar * matcher_str;
857         gchar * tmp;
858         MatcherProp * prop;
859         gboolean negative_cond;
860
861         if (!gtk_clist_get_text(GTK_CLIST(matcher.cond_clist),
862                                 row, 0, &matcher_str))
863                 return;
864
865         negative_cond = FALSE;
866
867         if (row == 0) {
868                 prefs_matcher_reset_condition();
869                 return;
870         }
871
872         tmp = matcher_str;
873         prop = matcherprop_parse(&tmp);
874         if (tmp == NULL)
875                 return;
876
877         switch(prop->criteria) {
878         case MATCHING_ALL:
879                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
880                                      CRITERIA_ALL);
881                 break;
882
883         case MATCHING_NOT_SUBJECT:
884                 negative_cond = TRUE;
885         case MATCHING_SUBJECT:
886                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
887                                      CRITERIA_SUBJECT);
888                 break;
889
890         case MATCHING_NOT_FROM:
891                 negative_cond = TRUE;
892         case MATCHING_FROM:
893                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
894                                      CRITERIA_FROM);
895                 break;
896
897         case MATCHING_NOT_TO:
898                 negative_cond = TRUE;
899         case MATCHING_TO:
900                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
901                                      CRITERIA_TO);
902                 break;
903
904         case MATCHING_NOT_CC:
905                 negative_cond = TRUE;
906         case MATCHING_CC:
907                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
908                                      CRITERIA_CC);
909                 break;
910
911         case MATCHING_NOT_NEWSGROUPS:
912                 negative_cond = TRUE;
913         case MATCHING_NEWSGROUPS:
914                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
915                                      CRITERIA_NEWSGROUPS);
916                 break;
917
918         case MATCHING_NOT_TO_AND_NOT_CC:
919                 negative_cond = TRUE;
920         case MATCHING_TO_OR_CC:
921                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
922                                      CRITERIA_TO_OR_CC);
923                 break;
924
925         case MATCHING_NOT_BODY_PART:
926                 negative_cond = TRUE;
927         case MATCHING_BODY_PART:
928                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
929                                      CRITERIA_BODY_PART);
930                 break;
931
932         case MATCHING_NOT_MESSAGE:
933                 negative_cond = TRUE;
934         case MATCHING_MESSAGE:
935                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
936                                      CRITERIA_MESSAGE);
937                 break;
938
939         case MATCHING_NOT_HEADERS_PART:
940                 negative_cond = TRUE;
941         case MATCHING_HEADERS_PART:
942                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
943                                      CRITERIA_HEADERS_PART);
944                 break;
945
946         case MATCHING_NOT_HEADER:
947                 negative_cond = TRUE;
948         case MATCHING_HEADER:
949                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
950                                      CRITERIA_HEADER);
951                 break;
952
953         case MATCHING_AGE_GREATER:
954                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
955                                      CRITERIA_AGE_GREATER);
956                 break;
957
958         case MATCHING_AGE_LOWER:
959                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
960                                      CRITERIA_AGE_LOWER);
961                 break;
962         }
963         
964         switch(prop->criteria) {
965         case MATCHING_ALL:
966                 break;
967
968         case MATCHING_NOT_SUBJECT:
969         case MATCHING_NOT_FROM:
970         case MATCHING_NOT_TO:
971         case MATCHING_NOT_CC:
972         case MATCHING_NOT_TO_AND_NOT_CC:
973         case MATCHING_NOT_NEWSGROUPS:
974         case MATCHING_NOT_HEADERS_PART:
975         case MATCHING_NOT_BODY_PART:
976         case MATCHING_NOT_MESSAGE:
977         case MATCHING_SUBJECT:
978         case MATCHING_FROM:
979         case MATCHING_TO:
980         case MATCHING_CC:
981         case MATCHING_TO_OR_CC:
982         case MATCHING_NEWSGROUPS:
983         case MATCHING_HEADERS_PART:
984         case MATCHING_BODY_PART:
985         case MATCHING_MESSAGE:
986                 gtk_entry_set_text(GTK_ENTRY(matcher.value_entry), prop->expr);
987                 break;
988
989         case MATCHING_AGE_GREATER:
990         case MATCHING_AGE_LOWER:
991                 gtk_entry_set_text(GTK_ENTRY(matcher.value_entry), itos(prop->age));
992                 break;
993
994         case MATCHING_NOT_HEADER:
995         case MATCHING_HEADER:
996                 gtk_entry_set_text(GTK_ENTRY(matcher.header_entry), prop->header);
997                 gtk_entry_set_text(GTK_ENTRY(matcher.value_entry), prop->expr);
998                 break;
999         }
1000
1001         if (negative_cond)
1002                 gtk_list_select_item(GTK_LIST(matcher.predicate_list), 1);
1003         else
1004                 gtk_list_select_item(GTK_LIST(matcher.predicate_list), 0);
1005
1006         switch(prop->matchtype) {
1007         case MATCHING_MATCH:
1008                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_chkbtn), FALSE);
1009                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_chkbtn), TRUE);
1010                 break;
1011
1012         case MATCHING_MATCHCASE:
1013                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_chkbtn), FALSE);
1014                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_chkbtn), FALSE);
1015                 break;
1016
1017         case MATCHING_REGEXP:
1018                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_chkbtn), TRUE);
1019                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_chkbtn), TRUE);
1020                 break;
1021
1022         case MATCHING_REGEXPCASE:
1023                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_chkbtn), TRUE);
1024                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_chkbtn), FALSE);
1025                 break;
1026         }
1027 }
1028
1029 static void prefs_matcher_criteria_select(GtkList *list,
1030                                           GtkWidget *widget,
1031                                           gpointer user_data)
1032 {
1033         gint value;
1034
1035         value = get_sel_from_list(GTK_LIST(matcher.criteria_list));
1036
1037         switch (value) {
1038         case CRITERIA_ALL:
1039                 gtk_widget_set_sensitive(matcher.header_combo, FALSE);
1040                 gtk_widget_set_sensitive(matcher.header_label, FALSE);
1041                 gtk_widget_set_sensitive(matcher.value_label, FALSE);
1042                 gtk_widget_set_sensitive(matcher.value_entry, FALSE);
1043                 gtk_widget_set_sensitive(matcher.predicate_label, FALSE);
1044                 gtk_widget_set_sensitive(matcher.predicate_combo, FALSE);
1045                 gtk_widget_set_sensitive(matcher.case_chkbtn, FALSE);
1046                 gtk_widget_set_sensitive(matcher.regexp_chkbtn, FALSE);
1047                 break;
1048
1049         case CRITERIA_SUBJECT:
1050         case CRITERIA_FROM:
1051         case CRITERIA_TO:
1052         case CRITERIA_CC:
1053         case CRITERIA_TO_OR_CC:
1054         case CRITERIA_NEWSGROUPS:
1055         case CRITERIA_HEADERS_PART:
1056         case CRITERIA_BODY_PART:
1057         case CRITERIA_MESSAGE:
1058                 gtk_widget_set_sensitive(matcher.header_combo, FALSE);
1059                 gtk_widget_set_sensitive(matcher.header_label, FALSE);
1060                 gtk_widget_set_sensitive(matcher.value_label, TRUE);
1061                 gtk_widget_set_sensitive(matcher.value_entry, TRUE);
1062                 gtk_widget_set_sensitive(matcher.predicate_label, TRUE);
1063                 gtk_widget_set_sensitive(matcher.predicate_combo, TRUE);
1064                 gtk_widget_set_sensitive(matcher.case_chkbtn, TRUE);
1065                 gtk_widget_set_sensitive(matcher.regexp_chkbtn, TRUE);
1066                 break;
1067
1068         case CRITERIA_AGE_GREATER:
1069         case CRITERIA_AGE_LOWER:
1070                 gtk_widget_set_sensitive(matcher.header_combo, FALSE);
1071                 gtk_widget_set_sensitive(matcher.header_label, FALSE);
1072                 gtk_widget_set_sensitive(matcher.value_label, TRUE);
1073                 gtk_widget_set_sensitive(matcher.value_entry, TRUE);
1074                 gtk_widget_set_sensitive(matcher.predicate_label, FALSE);
1075                 gtk_widget_set_sensitive(matcher.predicate_combo, FALSE);
1076                 gtk_widget_set_sensitive(matcher.case_chkbtn, FALSE);
1077                 gtk_widget_set_sensitive(matcher.regexp_chkbtn, FALSE);
1078                 break;
1079
1080         case CRITERIA_HEADER:
1081                 gtk_widget_set_sensitive(matcher.header_combo, TRUE);
1082                 gtk_widget_set_sensitive(matcher.header_label, TRUE);
1083                 gtk_widget_set_sensitive(matcher.value_label, TRUE);
1084                 gtk_widget_set_sensitive(matcher.value_entry, TRUE);
1085                 gtk_widget_set_sensitive(matcher.predicate_label, TRUE);
1086                 gtk_widget_set_sensitive(matcher.predicate_combo, TRUE);
1087                 gtk_widget_set_sensitive(matcher.case_chkbtn, TRUE);
1088                 gtk_widget_set_sensitive(matcher.regexp_chkbtn, TRUE);
1089                 break;
1090         }
1091 }
1092
1093 static void prefs_matcher_key_pressed(GtkWidget *widget, GdkEventKey *event,
1094                                      gpointer data)
1095 {
1096         if (event && event->keyval == GDK_Escape)
1097                 prefs_matcher_close();
1098 }
1099
1100 static void prefs_matcher_close(void)
1101 {
1102         prefs_matcher_set_list();
1103         gtk_widget_hide(matcher.window);
1104 }
1105
1106 static gint prefs_matcher_deleted(GtkWidget *widget, GdkEventAny *event,
1107                                   gpointer data)
1108 {
1109         prefs_matcher_close();
1110         return TRUE;
1111 }