ok & cancel buttons added - xhdr parsing fixed
[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 *ok_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_ok            (void);
165 static void prefs_matcher_cancel                (void);
166 static gint prefs_matcher_deleted(GtkWidget *widget, GdkEventAny *event,
167                                   gpointer data);
168 static void prefs_matcher_criteria_select(GtkList *list,
169                                           GtkWidget *widget,
170                                           gpointer user_data);
171 static void prefs_matcher_set_list(void);
172
173 void prefs_matcher_open(MatcherList * matchers)
174 {
175         inc_autocheck_timer_remove();
176
177         if (!matcher.window) {
178                 prefs_matcher_create();
179         }
180
181         manage_window_set_transient(GTK_WINDOW(matcher.window));
182         gtk_widget_grab_focus(matcher.ok_btn);
183
184         tmp_matchers = matchers;
185         prefs_matcher_set_dialog();
186
187         gtk_widget_show(matcher.window);
188 }
189
190 static void prefs_matcher_create(void)
191 {
192         GtkWidget *window;
193         GtkWidget *vbox;
194         GtkWidget *ok_btn;
195         GtkWidget *cancel_btn;
196         GtkWidget *confirm_area;
197
198         GtkWidget *vbox1;
199         GtkWidget *vbox2;
200         GtkWidget *vbox3;
201         GtkWidget *table1;
202
203         GtkWidget *hbox1;
204
205         GtkWidget *header_combo;
206         GtkWidget *header_entry;
207         GtkWidget *header_label;
208         GtkWidget *criteria_combo;
209         GtkWidget *criteria_list;
210         GtkWidget *criteria_label;
211         GtkWidget *value_label;
212         GtkWidget *value_entry;
213         GtkWidget *predicate_combo;
214         GtkWidget *predicate_list;
215         GtkWidget *predicate_label;
216         GtkWidget *bool_op_combo;
217         GtkWidget *bool_op_list;
218         GtkWidget *bool_op_label;
219
220         GtkWidget *regexp_chkbtn;
221         GtkWidget *case_chkbtn;
222
223         GtkWidget *reg_hbox;
224         GtkWidget *btn_hbox;
225         GtkWidget *arrow;
226         GtkWidget *reg_btn;
227         GtkWidget *subst_btn;
228         GtkWidget *del_btn;
229
230         GtkWidget *cond_hbox;
231         GtkWidget *cond_scrolledwin;
232         GtkWidget *cond_clist;
233
234         GtkWidget *btn_vbox;
235         GtkWidget *up_btn;
236         GtkWidget *down_btn;
237
238         GList *combo_items;
239         gint i;
240
241         gchar *title[] = {_("Registered rules")};
242
243         debug_print(_("Creating matcher setting window...\n"));
244
245         window = gtk_window_new (GTK_WINDOW_DIALOG);
246         gtk_container_set_border_width (GTK_CONTAINER (window), 8);
247         gtk_window_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
248         gtk_window_set_modal (GTK_WINDOW (window), TRUE);
249         gtk_window_set_policy (GTK_WINDOW (window), FALSE, TRUE, FALSE);
250
251         vbox = gtk_vbox_new (FALSE, 6);
252         gtk_widget_show (vbox);
253         gtk_container_add (GTK_CONTAINER (window), vbox);
254
255         gtkut_button_set_create(&confirm_area, &ok_btn, _("OK"),
256                                 &cancel_btn, _("Cancel"), NULL, NULL);
257         gtk_widget_show (confirm_area);
258         gtk_box_pack_end (GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
259         gtk_widget_grab_default (ok_btn);
260
261         /*
262         gtkut_button_set_create (&confirm_area, &close_btn, _("Close"),
263                                  NULL, NULL, NULL, NULL);
264         gtk_widget_show (confirm_area);
265         gtk_box_pack_end (GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
266         gtk_widget_grab_default (close_btn);*/
267
268         gtk_window_set_title (GTK_WINDOW(window),
269                               _("Condition setting"));
270         gtk_signal_connect (GTK_OBJECT(window), "delete_event",
271                             GTK_SIGNAL_FUNC(prefs_matcher_deleted), NULL);
272         gtk_signal_connect (GTK_OBJECT(window), "key_press_event",
273                             GTK_SIGNAL_FUNC(prefs_matcher_key_pressed), NULL);
274         gtk_signal_connect (GTK_OBJECT(window), "focus_in_event",
275                             GTK_SIGNAL_FUNC(manage_window_focus_in), NULL);
276         gtk_signal_connect (GTK_OBJECT(window), "focus_out_event",
277                             GTK_SIGNAL_FUNC(manage_window_focus_out), NULL);
278         gtk_signal_connect (GTK_OBJECT(ok_btn), "clicked",
279                             GTK_SIGNAL_FUNC(prefs_matcher_ok), NULL);
280         gtk_signal_connect (GTK_OBJECT(cancel_btn), "clicked",
281                             GTK_SIGNAL_FUNC(prefs_matcher_cancel), NULL);
282
283         vbox1 = gtk_vbox_new (FALSE, VSPACING);
284         gtk_widget_show (vbox1);
285         gtk_box_pack_start (GTK_BOX (vbox), vbox1, TRUE, TRUE, 0);
286         gtk_container_set_border_width (GTK_CONTAINER (vbox1), 2);
287
288         table1 = gtk_table_new (2, 3, FALSE);
289         gtk_widget_show (table1);
290
291         gtk_box_pack_start (GTK_BOX (vbox1), table1, FALSE, TRUE, 0);
292         gtk_table_set_row_spacings (GTK_TABLE (table1), 8);
293         gtk_table_set_col_spacings (GTK_TABLE (table1), 8);
294
295         /* criteria combo box */
296
297         criteria_label = gtk_label_new (_("Match type"));
298         gtk_widget_show (criteria_label);
299         gtk_misc_set_alignment (GTK_MISC (criteria_label), 0, 0.5);
300         gtk_table_attach (GTK_TABLE (table1), criteria_label, 0, 1, 0, 1,
301                           GTK_FILL, 0, 0, 0);
302
303         criteria_combo = gtk_combo_new ();
304         gtk_widget_show (criteria_combo);
305
306         combo_items = NULL;
307
308         for(i = 0 ; i < (gint) (sizeof(criteria_text) / sizeof(gchar *)) ;
309             i++) {
310                 combo_items = g_list_append(combo_items,
311                                             (gpointer) _(criteria_text[i]));
312         }
313         gtk_combo_set_popdown_strings(GTK_COMBO(criteria_combo), combo_items);
314
315         g_list_free(combo_items);
316
317         gtk_widget_set_usize (criteria_combo, 120, -1);
318         gtk_table_attach (GTK_TABLE (table1), criteria_combo, 0, 1, 1, 2,
319                           0, 0, 0, 0);
320         criteria_list = GTK_COMBO(criteria_combo)->list;
321         gtk_signal_connect (GTK_OBJECT (criteria_list), "select-child",
322                             GTK_SIGNAL_FUNC (prefs_matcher_criteria_select),
323                             NULL);
324
325         gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(criteria_combo)->entry),
326                                FALSE);
327
328         /* header name */
329
330         header_label = gtk_label_new (_("Header name"));
331         gtk_widget_show (header_label);
332         gtk_misc_set_alignment (GTK_MISC (header_label), 0, 0.5);
333         gtk_table_attach (GTK_TABLE (table1), header_label, 1, 2, 0, 1,
334                           GTK_FILL, 0, 0, 0);
335
336         header_combo = gtk_combo_new ();
337         gtk_widget_show (header_combo);
338         gtk_widget_set_usize (header_combo, 96, -1);
339         gtkut_combo_set_items (GTK_COMBO (header_combo),
340                                "Subject", "From", "To", "Cc", "Reply-To",
341                                "Sender", "X-ML-Name", "X-List", "X-Sequence",
342                                "X-Mailer",
343                                NULL);
344         gtk_table_attach (GTK_TABLE (table1), header_combo, 1, 2, 1, 2,
345                           0, 0, 0, 0);
346         header_entry = GTK_COMBO (header_combo)->entry;
347         gtk_entry_set_editable (GTK_ENTRY (header_entry), TRUE);
348
349         /* value */
350
351         value_label = gtk_label_new (_("Value"));
352         gtk_widget_show (value_label);
353         gtk_misc_set_alignment (GTK_MISC (value_label), 0, 0.5);
354         gtk_table_attach (GTK_TABLE (table1), value_label, 2, 3, 0, 1,
355                           GTK_FILL, 0, 0, 0);
356
357         value_entry = gtk_entry_new ();
358         gtk_widget_show (value_entry);
359         gtk_widget_set_usize (value_entry, 200, -1);
360         gtk_table_attach (GTK_TABLE (table1), value_entry, 2, 3, 1, 2,
361                           0, 0, 0, 0);
362
363
364         /* predicate */
365
366         vbox2 = gtk_vbox_new (FALSE, VSPACING);
367         gtk_widget_show (vbox2);
368         gtk_box_pack_start (GTK_BOX (vbox1), vbox2, TRUE, TRUE, 0);
369
370         hbox1 = gtk_hbox_new (FALSE, 8);
371         gtk_widget_show (hbox1);
372         gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, TRUE, 0);
373
374         predicate_label = gtk_label_new (_("Predicate"));
375         gtk_widget_show (predicate_label);
376         gtk_box_pack_start (GTK_BOX (hbox1), predicate_label,
377                             FALSE, FALSE, 0);
378
379         predicate_combo = gtk_combo_new ();
380         gtk_widget_show (predicate_combo);
381         gtk_widget_set_usize (predicate_combo, 120, -1);
382         predicate_list = GTK_COMBO(predicate_combo)->list;
383         gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(predicate_combo)->entry),
384                                FALSE);
385
386         combo_items = NULL;
387
388         for(i = 0 ; i < (gint) (sizeof(predicate_text) / sizeof(gchar *)) ;
389             i++) {
390                 combo_items = g_list_append(combo_items,
391                                             (gpointer) _(predicate_text[i]));
392         }
393         gtk_combo_set_popdown_strings(GTK_COMBO(predicate_combo), combo_items);
394
395         g_list_free(combo_items);
396
397         gtk_box_pack_start (GTK_BOX (hbox1), predicate_combo,
398                             FALSE, FALSE, 0);
399
400         vbox3 = gtk_vbox_new (FALSE, 0);
401         gtk_widget_show (vbox3);
402         gtk_box_pack_start (GTK_BOX (hbox1), vbox3, TRUE, TRUE, 0);
403
404         PACK_CHECK_BUTTON (vbox3, case_chkbtn, _("Case sensitive"));
405         PACK_CHECK_BUTTON (vbox3, regexp_chkbtn, _("Use regexp"));
406
407         /* register / substitute / delete */
408
409         reg_hbox = gtk_hbox_new (FALSE, 4);
410         gtk_widget_show (reg_hbox);
411         gtk_box_pack_start (GTK_BOX (vbox1), reg_hbox, FALSE, FALSE, 0);
412
413         arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
414         gtk_widget_show (arrow);
415         gtk_box_pack_start (GTK_BOX (reg_hbox), arrow, FALSE, FALSE, 0);
416         gtk_widget_set_usize (arrow, -1, 16);
417
418         btn_hbox = gtk_hbox_new (TRUE, 4);
419         gtk_widget_show (btn_hbox);
420         gtk_box_pack_start (GTK_BOX (reg_hbox), btn_hbox, FALSE, FALSE, 0);
421
422         reg_btn = gtk_button_new_with_label (_("Register"));
423         gtk_widget_show (reg_btn);
424         gtk_box_pack_start (GTK_BOX (btn_hbox), reg_btn, FALSE, TRUE, 0);
425         gtk_signal_connect (GTK_OBJECT (reg_btn), "clicked",
426                             GTK_SIGNAL_FUNC (prefs_matcher_register_cb), NULL);
427
428         subst_btn = gtk_button_new_with_label (_(" Substitute "));
429         gtk_widget_show (subst_btn);
430         gtk_box_pack_start (GTK_BOX (btn_hbox), subst_btn, FALSE, TRUE, 0);
431         gtk_signal_connect (GTK_OBJECT (subst_btn), "clicked",
432                             GTK_SIGNAL_FUNC (prefs_matcher_substitute_cb),
433                             NULL);
434
435         del_btn = gtk_button_new_with_label (_("Delete"));
436         gtk_widget_show (del_btn);
437         gtk_box_pack_start (GTK_BOX (btn_hbox), del_btn, FALSE, TRUE, 0);
438         gtk_signal_connect (GTK_OBJECT (del_btn), "clicked",
439                             GTK_SIGNAL_FUNC (prefs_matcher_delete_cb), NULL);
440
441         /* boolean operation */
442
443         bool_op_label = gtk_label_new (_("Boolean Op"));
444         gtk_misc_set_alignment (GTK_MISC (value_label), 0, 0.5);
445         gtk_widget_show (bool_op_label);
446         gtk_box_pack_start (GTK_BOX (btn_hbox), bool_op_label,
447                             FALSE, FALSE, 0);
448
449         bool_op_combo = gtk_combo_new ();
450         gtk_widget_show (bool_op_combo);
451         gtk_widget_set_usize (bool_op_combo, 50, -1);
452         bool_op_list = GTK_COMBO(bool_op_combo)->list;
453         gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(bool_op_combo)->entry),
454                                FALSE);
455
456         combo_items = NULL;
457
458         for(i = 0 ; i < (gint) (sizeof(bool_op_text) / sizeof(gchar *)) ;
459             i++) {
460                 combo_items = g_list_append(combo_items,
461                                             (gpointer) _(bool_op_text[i]));
462         }
463         gtk_combo_set_popdown_strings(GTK_COMBO(bool_op_combo), combo_items);
464
465         g_list_free(combo_items);
466
467         gtk_box_pack_start (GTK_BOX (btn_hbox), bool_op_combo,
468                             FALSE, TRUE, 0);
469
470         cond_hbox = gtk_hbox_new (FALSE, 8);
471         gtk_widget_show (cond_hbox);
472         gtk_box_pack_start (GTK_BOX (vbox1), cond_hbox, TRUE, TRUE, 0);
473
474         cond_scrolledwin = gtk_scrolled_window_new (NULL, NULL);
475         gtk_widget_show (cond_scrolledwin);
476         gtk_widget_set_usize (cond_scrolledwin, -1, 150);
477         gtk_box_pack_start (GTK_BOX (cond_hbox), cond_scrolledwin,
478                             TRUE, TRUE, 0);
479         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (cond_scrolledwin),
480                                         GTK_POLICY_AUTOMATIC,
481                                         GTK_POLICY_AUTOMATIC);
482
483         cond_clist = gtk_clist_new_with_titles(1, title);
484         gtk_widget_show (cond_clist);
485         gtk_container_add (GTK_CONTAINER (cond_scrolledwin), cond_clist);
486         gtk_clist_set_column_width (GTK_CLIST (cond_clist), 0, 80);
487         gtk_clist_set_selection_mode (GTK_CLIST (cond_clist),
488                                       GTK_SELECTION_BROWSE);
489         GTK_WIDGET_UNSET_FLAGS (GTK_CLIST (cond_clist)->column[0].button,
490                                 GTK_CAN_FOCUS);
491         gtk_signal_connect (GTK_OBJECT (cond_clist), "select_row",
492                             GTK_SIGNAL_FUNC (prefs_matcher_select), NULL);
493
494         btn_vbox = gtk_vbox_new (FALSE, 8);
495         gtk_widget_show (btn_vbox);
496         gtk_box_pack_start (GTK_BOX (cond_hbox), btn_vbox, FALSE, FALSE, 0);
497
498         up_btn = gtk_button_new_with_label (_("Up"));
499         gtk_widget_show (up_btn);
500         gtk_box_pack_start (GTK_BOX (btn_vbox), up_btn, FALSE, FALSE, 0);
501         gtk_signal_connect (GTK_OBJECT (up_btn), "clicked",
502                             GTK_SIGNAL_FUNC (prefs_matcher_up), NULL);
503
504         down_btn = gtk_button_new_with_label (_("Down"));
505         gtk_widget_show (down_btn);
506         gtk_box_pack_start (GTK_BOX (btn_vbox), down_btn, FALSE, FALSE, 0);
507         gtk_signal_connect (GTK_OBJECT (down_btn), "clicked",
508                             GTK_SIGNAL_FUNC (prefs_matcher_down), NULL);
509
510         gtk_widget_show_all(window);
511
512         matcher.window    = window;
513
514         matcher.ok_btn = ok_btn;
515
516         matcher.criteria_list = criteria_list;
517         matcher.header_combo = header_combo;
518         matcher.header_entry = header_entry;
519         matcher.header_label = header_label;
520         matcher.value_entry = value_entry;
521         matcher.value_label = value_label;
522         matcher.predicate_label = predicate_label;
523         matcher.predicate_list = predicate_list;
524         matcher.predicate_combo = predicate_combo;
525         matcher.case_chkbtn = case_chkbtn;
526         matcher.regexp_chkbtn = regexp_chkbtn;
527         matcher.bool_op_list = bool_op_list;
528
529         matcher.cond_clist   = cond_clist;
530 }
531
532 static gint prefs_matcher_clist_set_row(gint row, MatcherProp * prop)
533 {
534         GtkCList *clist = GTK_CLIST(matcher.cond_clist);
535         gchar * cond_str[1];
536         gchar * matcher_str;
537
538         if (prop == NULL) {
539                 cond_str[0] = _("(New)");
540                 return gtk_clist_append(clist, cond_str);
541         }
542
543         matcher_str = matcherprop_to_string(prop);
544         cond_str[0] = matcher_str;
545         if (row < 0)
546                 row = gtk_clist_append(clist, cond_str);
547         else
548                 gtk_clist_set_text(clist, row, 0, cond_str[0]);
549         g_free(matcher_str);
550
551         return row;
552 }
553
554 static void prefs_matcher_reset_condition(void)
555 {
556         gtk_list_select_item(GTK_LIST(matcher.criteria_list), 0);
557         gtk_list_select_item(GTK_LIST(matcher.predicate_list), 0);
558         gtk_entry_set_text(GTK_ENTRY(matcher.header_entry), "");
559         gtk_entry_set_text(GTK_ENTRY(matcher.value_entry), "");
560 }
561
562  static void prefs_matcher_set_dialog(void)
563 {
564         GtkCList *clist = GTK_CLIST(matcher.cond_clist);
565         GSList * cur;
566         gboolean bool_op = 0;
567
568         gtk_clist_freeze(clist);
569         gtk_clist_clear(clist);
570
571         prefs_matcher_clist_set_row(-1, NULL);
572         if (tmp_matchers != NULL) {
573                 for (cur = tmp_matchers->matchers ; cur != NULL ;
574                      cur = g_slist_next(cur)) {
575                         MatcherProp * prop;
576                         prop = (MatcherProp *) cur->data;
577                         prefs_matcher_clist_set_row(-1, prop);
578                         matcherprop_free(prop);
579                 }
580
581                 bool_op = tmp_matchers->bool_and;
582         }
583         gtk_clist_thaw(clist);
584
585         gtk_list_select_item(GTK_LIST(matcher.bool_op_list), bool_op);
586
587         prefs_matcher_reset_condition();
588 }
589
590 static void prefs_matcher_set_list(void)
591 {
592         gchar * matcher_str;
593         MatcherProp * prop;
594         gint row = 1;
595         GSList * l;
596         gchar * tmp;
597
598         if (tmp_matchers == NULL)
599                 return;
600
601         /* free old */
602
603         for(l = tmp_matchers->matchers ; l != NULL ; l = g_slist_next(l))
604                 matcherprop_free((MatcherProp *) l->data);
605         g_slist_free(tmp_matchers->matchers);
606         tmp_matchers->matchers = NULL;
607
608         /* set new */
609
610         while (gtk_clist_get_text(GTK_CLIST(matcher.cond_clist),
611                                   row, 0, &matcher_str)) {
612
613                 if (strcmp(matcher_str, _("(New)")) != 0) {
614                         tmp = matcher_str;
615                         prop = matcherprop_parse(&tmp);
616                         
617                         if (tmp == NULL)
618                                 break;
619                         
620                         tmp_matchers->matchers =
621                                 g_slist_append(tmp_matchers->matchers, prop);
622                 }
623                 row ++;
624         }
625
626         tmp_matchers->bool_and = get_sel_from_list(GTK_LIST(matcher.bool_op_list));
627
628 }
629
630 static gint prefs_matcher_get_matching_from_criteria(gint criteria_id)
631 {
632         switch (criteria_id) {
633         case CRITERIA_ALL:
634                 return MATCHING_ALL;
635         case CRITERIA_SUBJECT:
636                 return MATCHING_SUBJECT;
637         case CRITERIA_FROM:
638                 return MATCHING_FROM;
639         case CRITERIA_TO:
640                 return MATCHING_TO;
641         case CRITERIA_CC:
642                 return MATCHING_CC;
643         case CRITERIA_TO_OR_CC:
644                 return MATCHING_TO_OR_CC;
645         case CRITERIA_NEWSGROUPS:
646                 return MATCHING_NEWSGROUPS;
647         case CRITERIA_AGE_GREATER:
648                 return MATCHING_AGE_GREATER;
649         case CRITERIA_AGE_LOWER:
650                 return MATCHING_AGE_LOWER;
651         case CRITERIA_HEADER:
652                 return MATCHING_HEADER;
653         case CRITERIA_HEADERS_PART:
654                 return MATCHING_HEADERS_PART;
655         case CRITERIA_BODY_PART:
656                 return MATCHING_BODY_PART;
657         case CRITERIA_MESSAGE:
658                 return MATCHING_MESSAGE;
659         default:
660                 return -1;
661         }
662 }
663
664 static gint prefs_matcher_not_criteria(gint matcher_criteria)
665 {
666         switch(matcher_criteria) {
667         case MATCHING_SUBJECT:
668                 return MATCHING_NOT_SUBJECT;
669         case MATCHING_FROM:
670                 return MATCHING_NOT_FROM;
671         case MATCHING_TO:
672                 return MATCHING_NOT_TO;
673         case MATCHING_CC:
674                 return MATCHING_NOT_CC;
675         case MATCHING_TO_OR_CC:
676                 return MATCHING_NOT_TO_AND_NOT_CC;
677         case MATCHING_NEWSGROUPS:
678                 return MATCHING_NOT_NEWSGROUPS;
679         case MATCHING_HEADER:
680                 return MATCHING_NOT_HEADER;
681         case MATCHING_HEADERS_PART:
682                 return MATCHING_NOT_HEADERS_PART;
683         case MATCHING_MESSAGE:
684                 return MATCHING_NOT_MESSAGE;
685         case MATCHING_BODY_PART:
686                 return MATCHING_NOT_BODY_PART;
687         default:
688                 return matcher_criteria;
689         }
690 }
691
692 static MatcherProp * prefs_matcher_dialog_to_matcher()
693 {
694         MatcherProp * matcherprop;
695         gint criteria;
696         gint matchtype;
697         gint value_pred;
698         gint value_criteria;
699         gboolean use_regexp;
700         gboolean case_sensitive;
701         gchar * header;
702         gchar * expr;
703         gint age;
704         gchar * age_str;
705
706         value_criteria = get_sel_from_list(GTK_LIST(matcher.criteria_list));
707
708         criteria = prefs_matcher_get_matching_from_criteria(value_criteria);
709
710         value_pred = get_sel_from_list(GTK_LIST(matcher.predicate_list));
711
712         use_regexp = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(matcher.regexp_chkbtn));
713         case_sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(matcher.case_chkbtn));
714
715         if (value_pred != PREDICATE_CONTAINS)
716                 criteria = prefs_matcher_not_criteria(criteria);
717
718         if (use_regexp) {
719                 if (case_sensitive)
720                         matchtype = MATCHING_REGEXP;
721                 else
722                         matchtype = MATCHING_REGEXPCASE;
723         }
724         else {
725                 if (case_sensitive)
726                         matchtype = MATCHING_MATCH;
727                 else
728                         matchtype = MATCHING_MATCHCASE;
729         }
730
731         header = NULL;
732         expr = NULL;
733         age = 0;
734
735         switch (value_criteria) {
736         case CRITERIA_ALL:
737                 break;
738
739         case CRITERIA_SUBJECT:
740         case CRITERIA_FROM:
741         case CRITERIA_TO:
742         case CRITERIA_CC:
743         case CRITERIA_TO_OR_CC:
744         case CRITERIA_NEWSGROUPS:
745         case CRITERIA_HEADERS_PART:
746         case CRITERIA_BODY_PART:
747         case CRITERIA_MESSAGE:
748                 expr = gtk_entry_get_text(GTK_ENTRY(matcher.value_entry));
749
750                 if (*expr == '\0') {
751                     alertpanel_error(_("Match string is not set."));
752                     return NULL;
753                 }
754
755                 break;
756
757         case CRITERIA_AGE_GREATER:
758         case CRITERIA_AGE_LOWER:
759                 age_str = gtk_entry_get_text(GTK_ENTRY(matcher.value_entry));
760
761                 if (*age_str == '\0') {
762                     alertpanel_error(_("Age is not set."));
763                     return NULL;
764                 }
765
766                 age = atoi(age_str);
767
768                 break;
769
770         case CRITERIA_HEADER:
771
772                 header = gtk_entry_get_text(GTK_ENTRY(matcher.header_entry));
773                 expr = gtk_entry_get_text(GTK_ENTRY(matcher.value_entry));
774
775                 if (*header == '\0') {
776                     alertpanel_error(_("Header name is not set."));
777                     return NULL;
778                 }
779                 if (*expr == '\0') {
780                     alertpanel_error(_("Match string is not set."));
781                     return NULL;
782                 }
783
784                 break;
785         }
786
787         matcherprop =  matcherprop_new(criteria, header, matchtype, expr, age);
788         
789         return matcherprop;
790 }
791
792 static void prefs_matcher_register_cb(void)
793 {
794         MatcherProp * matcherprop;
795         
796         matcherprop = prefs_matcher_dialog_to_matcher();
797         if (matcherprop == NULL)
798                 return;
799
800         prefs_matcher_clist_set_row(-1, matcherprop);
801
802         matcherprop_free(matcherprop);
803
804         prefs_matcher_reset_condition();
805 }
806
807 static void prefs_matcher_substitute_cb(void)
808 {
809         GtkCList *clist = GTK_CLIST(matcher.cond_clist);
810         gint row;
811         MatcherProp * matcherprop;
812
813         row = GPOINTER_TO_INT(clist->selection->data);
814         if (row == 0)
815                 return;
816         
817         matcherprop = prefs_matcher_dialog_to_matcher();
818         if (matcherprop == NULL)
819                 return;
820
821         prefs_matcher_clist_set_row(row, matcherprop);
822
823         matcherprop_free(matcherprop);
824
825         prefs_matcher_reset_condition();
826 }
827
828 static void prefs_matcher_delete_cb(void)
829 {
830         GtkCList *clist = GTK_CLIST(matcher.cond_clist);
831         gint row;
832
833         if (!clist->selection) return;
834         row = GPOINTER_TO_INT(clist->selection->data);
835         if (row == 0)
836                 return;
837
838         gtk_clist_remove(clist, row);
839 }
840
841 static void prefs_matcher_up(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)
850                 gtk_clist_row_move(clist, row, row - 1);
851 }
852
853 static void prefs_matcher_down(void)
854 {
855         GtkCList *clist = GTK_CLIST(matcher.cond_clist);
856         gint row;
857
858         if (!clist->selection) return;
859
860         row = GPOINTER_TO_INT(clist->selection->data);
861         if (row >= 1 && row < clist->rows - 1)
862                 gtk_clist_row_move(clist, row, row + 1);
863 }
864
865 static void prefs_matcher_select(GtkCList *clist, gint row, gint column,
866                                  GdkEvent *event)
867 {
868         gchar * matcher_str;
869         gchar * tmp;
870         MatcherProp * prop;
871         gboolean negative_cond;
872
873         if (!gtk_clist_get_text(GTK_CLIST(matcher.cond_clist),
874                                 row, 0, &matcher_str))
875                 return;
876
877         negative_cond = FALSE;
878
879         if (row == 0) {
880                 prefs_matcher_reset_condition();
881                 return;
882         }
883
884         tmp = matcher_str;
885         prop = matcherprop_parse(&tmp);
886         if (tmp == NULL)
887                 return;
888
889         switch(prop->criteria) {
890         case MATCHING_ALL:
891                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
892                                      CRITERIA_ALL);
893                 break;
894
895         case MATCHING_NOT_SUBJECT:
896                 negative_cond = TRUE;
897         case MATCHING_SUBJECT:
898                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
899                                      CRITERIA_SUBJECT);
900                 break;
901
902         case MATCHING_NOT_FROM:
903                 negative_cond = TRUE;
904         case MATCHING_FROM:
905                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
906                                      CRITERIA_FROM);
907                 break;
908
909         case MATCHING_NOT_TO:
910                 negative_cond = TRUE;
911         case MATCHING_TO:
912                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
913                                      CRITERIA_TO);
914                 break;
915
916         case MATCHING_NOT_CC:
917                 negative_cond = TRUE;
918         case MATCHING_CC:
919                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
920                                      CRITERIA_CC);
921                 break;
922
923         case MATCHING_NOT_NEWSGROUPS:
924                 negative_cond = TRUE;
925         case MATCHING_NEWSGROUPS:
926                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
927                                      CRITERIA_NEWSGROUPS);
928                 break;
929
930         case MATCHING_NOT_TO_AND_NOT_CC:
931                 negative_cond = TRUE;
932         case MATCHING_TO_OR_CC:
933                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
934                                      CRITERIA_TO_OR_CC);
935                 break;
936
937         case MATCHING_NOT_BODY_PART:
938                 negative_cond = TRUE;
939         case MATCHING_BODY_PART:
940                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
941                                      CRITERIA_BODY_PART);
942                 break;
943
944         case MATCHING_NOT_MESSAGE:
945                 negative_cond = TRUE;
946         case MATCHING_MESSAGE:
947                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
948                                      CRITERIA_MESSAGE);
949                 break;
950
951         case MATCHING_NOT_HEADERS_PART:
952                 negative_cond = TRUE;
953         case MATCHING_HEADERS_PART:
954                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
955                                      CRITERIA_HEADERS_PART);
956                 break;
957
958         case MATCHING_NOT_HEADER:
959                 negative_cond = TRUE;
960         case MATCHING_HEADER:
961                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
962                                      CRITERIA_HEADER);
963                 break;
964
965         case MATCHING_AGE_GREATER:
966                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
967                                      CRITERIA_AGE_GREATER);
968                 break;
969
970         case MATCHING_AGE_LOWER:
971                 gtk_list_select_item(GTK_LIST(matcher.criteria_list),
972                                      CRITERIA_AGE_LOWER);
973                 break;
974         }
975         
976         switch(prop->criteria) {
977         case MATCHING_ALL:
978                 break;
979
980         case MATCHING_NOT_SUBJECT:
981         case MATCHING_NOT_FROM:
982         case MATCHING_NOT_TO:
983         case MATCHING_NOT_CC:
984         case MATCHING_NOT_TO_AND_NOT_CC:
985         case MATCHING_NOT_NEWSGROUPS:
986         case MATCHING_NOT_HEADERS_PART:
987         case MATCHING_NOT_BODY_PART:
988         case MATCHING_NOT_MESSAGE:
989         case MATCHING_SUBJECT:
990         case MATCHING_FROM:
991         case MATCHING_TO:
992         case MATCHING_CC:
993         case MATCHING_TO_OR_CC:
994         case MATCHING_NEWSGROUPS:
995         case MATCHING_HEADERS_PART:
996         case MATCHING_BODY_PART:
997         case MATCHING_MESSAGE:
998                 gtk_entry_set_text(GTK_ENTRY(matcher.value_entry), prop->expr);
999                 break;
1000
1001         case MATCHING_AGE_GREATER:
1002         case MATCHING_AGE_LOWER:
1003                 gtk_entry_set_text(GTK_ENTRY(matcher.value_entry), itos(prop->age));
1004                 break;
1005
1006         case MATCHING_NOT_HEADER:
1007         case MATCHING_HEADER:
1008                 gtk_entry_set_text(GTK_ENTRY(matcher.header_entry), prop->header);
1009                 gtk_entry_set_text(GTK_ENTRY(matcher.value_entry), prop->expr);
1010                 break;
1011         }
1012
1013         if (negative_cond)
1014                 gtk_list_select_item(GTK_LIST(matcher.predicate_list), 1);
1015         else
1016                 gtk_list_select_item(GTK_LIST(matcher.predicate_list), 0);
1017
1018         switch(prop->matchtype) {
1019         case MATCHING_MATCH:
1020                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_chkbtn), FALSE);
1021                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_chkbtn), TRUE);
1022                 break;
1023
1024         case MATCHING_MATCHCASE:
1025                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_chkbtn), FALSE);
1026                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_chkbtn), FALSE);
1027                 break;
1028
1029         case MATCHING_REGEXP:
1030                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_chkbtn), TRUE);
1031                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_chkbtn), TRUE);
1032                 break;
1033
1034         case MATCHING_REGEXPCASE:
1035                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.regexp_chkbtn), TRUE);
1036                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(matcher.case_chkbtn), FALSE);
1037                 break;
1038         }
1039 }
1040
1041 static void prefs_matcher_criteria_select(GtkList *list,
1042                                           GtkWidget *widget,
1043                                           gpointer user_data)
1044 {
1045         gint value;
1046
1047         value = get_sel_from_list(GTK_LIST(matcher.criteria_list));
1048
1049         switch (value) {
1050         case CRITERIA_ALL:
1051                 gtk_widget_set_sensitive(matcher.header_combo, FALSE);
1052                 gtk_widget_set_sensitive(matcher.header_label, FALSE);
1053                 gtk_widget_set_sensitive(matcher.value_label, FALSE);
1054                 gtk_widget_set_sensitive(matcher.value_entry, FALSE);
1055                 gtk_widget_set_sensitive(matcher.predicate_label, FALSE);
1056                 gtk_widget_set_sensitive(matcher.predicate_combo, FALSE);
1057                 gtk_widget_set_sensitive(matcher.case_chkbtn, FALSE);
1058                 gtk_widget_set_sensitive(matcher.regexp_chkbtn, FALSE);
1059                 break;
1060
1061         case CRITERIA_SUBJECT:
1062         case CRITERIA_FROM:
1063         case CRITERIA_TO:
1064         case CRITERIA_CC:
1065         case CRITERIA_TO_OR_CC:
1066         case CRITERIA_NEWSGROUPS:
1067         case CRITERIA_HEADERS_PART:
1068         case CRITERIA_BODY_PART:
1069         case CRITERIA_MESSAGE:
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, TRUE);
1075                 gtk_widget_set_sensitive(matcher.predicate_combo, TRUE);
1076                 gtk_widget_set_sensitive(matcher.case_chkbtn, TRUE);
1077                 gtk_widget_set_sensitive(matcher.regexp_chkbtn, TRUE);
1078                 break;
1079
1080         case CRITERIA_AGE_GREATER:
1081         case CRITERIA_AGE_LOWER:
1082                 gtk_widget_set_sensitive(matcher.header_combo, FALSE);
1083                 gtk_widget_set_sensitive(matcher.header_label, FALSE);
1084                 gtk_widget_set_sensitive(matcher.value_label, TRUE);
1085                 gtk_widget_set_sensitive(matcher.value_entry, TRUE);
1086                 gtk_widget_set_sensitive(matcher.predicate_label, FALSE);
1087                 gtk_widget_set_sensitive(matcher.predicate_combo, FALSE);
1088                 gtk_widget_set_sensitive(matcher.case_chkbtn, FALSE);
1089                 gtk_widget_set_sensitive(matcher.regexp_chkbtn, FALSE);
1090                 break;
1091
1092         case CRITERIA_HEADER:
1093                 gtk_widget_set_sensitive(matcher.header_combo, TRUE);
1094                 gtk_widget_set_sensitive(matcher.header_label, TRUE);
1095                 gtk_widget_set_sensitive(matcher.value_label, TRUE);
1096                 gtk_widget_set_sensitive(matcher.value_entry, TRUE);
1097                 gtk_widget_set_sensitive(matcher.predicate_label, TRUE);
1098                 gtk_widget_set_sensitive(matcher.predicate_combo, TRUE);
1099                 gtk_widget_set_sensitive(matcher.case_chkbtn, TRUE);
1100                 gtk_widget_set_sensitive(matcher.regexp_chkbtn, TRUE);
1101                 break;
1102         }
1103 }
1104
1105 static void prefs_matcher_key_pressed(GtkWidget *widget, GdkEventKey *event,
1106                                      gpointer data)
1107 {
1108         if (event && event->keyval == GDK_Escape)
1109                 prefs_matcher_cancel();
1110 }
1111
1112 static void prefs_matcher_cancel(void)
1113 {
1114         gtk_widget_hide(matcher.window);
1115 }
1116
1117 static void prefs_matcher_ok(void)
1118 {
1119         prefs_matcher_set_list();
1120         gtk_widget_hide(matcher.window);
1121 }
1122
1123 static gint prefs_matcher_deleted(GtkWidget *widget, GdkEventAny *event,
1124                                   gpointer data)
1125 {
1126         prefs_matcher_cancel();
1127         return TRUE;
1128 }