0.9.4claws36
[claws.git] / src / prefs_filtering.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2003 Hiroyuki Yamamoto and the Sylpheed-Claws team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 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 <gtk/gtkoptionmenu.h>
29 #include <gdk/gdkkeysyms.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <errno.h>
34
35 #include "intl.h"
36 #include "main.h"
37 #include "prefs_gtk.h"
38 #include "prefs_matcher.h"
39 #include "prefs_filtering.h"
40 #include "prefs_common.h"
41 #include "mainwindow.h"
42 #include "foldersel.h"
43 #include "manage_window.h"
44 #include "inc.h"
45 #include "utils.h"
46 #include "gtkutils.h"
47 #include "alertpanel.h"
48 #include "folder.h"
49 #include "filtering.h"
50 #include "addr_compl.h"
51 #include "colorlabel.h"
52
53 #include "matcher_parser.h"
54 #include "matcher.h"
55 #include "prefs_filtering_action.h"
56
57 static struct Filtering {
58         GtkWidget *window;
59
60         GtkWidget *ok_btn;
61         GtkWidget *cond_entry;
62         GtkWidget *action_entry;
63
64         GtkWidget *cond_clist;
65 } filtering;
66
67 /* widget creating functions */
68 static void prefs_filtering_create              (void);
69
70 static void prefs_filtering_set_dialog  (const gchar *header,
71                                          const gchar *key);
72 static void prefs_filtering_set_list    (void);
73
74 /* callback functions */
75 static void prefs_filtering_register_cb (void);
76 static void prefs_filtering_substitute_cb       (void);
77 static void prefs_filtering_delete_cb   (void);
78 static void prefs_filtering_up          (void);
79 static void prefs_filtering_down        (void);
80 static void prefs_filtering_select      (GtkCList       *clist,
81                                          gint            row,
82                                          gint            column,
83                                          GdkEvent       *event);
84
85 static gint prefs_filtering_deleted     (GtkWidget      *widget,
86                                          GdkEventAny    *event,
87                                          gpointer        data);
88 static void prefs_filtering_key_pressed (GtkWidget      *widget,
89                                          GdkEventKey    *event,
90                                          gpointer        data);
91 static void prefs_filtering_cancel      (void);
92 static void prefs_filtering_ok          (void);
93
94 static void prefs_filtering_condition_define    (void);
95 static void prefs_filtering_action_define(void);
96 static gint prefs_filtering_clist_set_row       (gint row, FilteringProp * prop);
97                                           
98 static void prefs_filtering_reset_dialog        (void);
99 static gboolean prefs_filtering_rename_path_func(GNode *node, gpointer data);
100 static gboolean prefs_filtering_delete_path_func(GNode *node, gpointer data);
101
102 static FolderItem * cur_item = NULL; /* folder (if dialog opened for processing) */
103
104 typedef enum Action_ {
105         ACTION_MOVE,
106         ACTION_COPY,
107         ACTION_DELETE,
108         ACTION_MARK,
109         ACTION_UNMARK,
110         ACTION_LOCK,
111         ACTION_UNLOCK,
112         ACTION_MARK_AS_READ,
113         ACTION_MARK_AS_UNREAD,
114         ACTION_FORWARD,
115         ACTION_FORWARD_AS_ATTACHMENT,
116         ACTION_REDIRECT,
117         ACTION_EXECUTE,
118         ACTION_COLOR,
119         /* add other action constants */
120 } Action;
121
122 void prefs_filtering_open(FolderItem * item,
123                           const gchar *header,
124                           const gchar *key)
125 {
126         gchar *esckey;
127
128         if (prefs_rc_is_readonly(FILTERING_RC))
129                 return;
130
131         inc_lock();
132
133         if (!filtering.window) {
134                 prefs_filtering_create();
135         }
136
137         manage_window_set_transient(GTK_WINDOW(filtering.window));
138         gtk_widget_grab_focus(filtering.ok_btn);
139
140         cur_item = item;
141
142         esckey = matcher_escape_str(key);
143         prefs_filtering_set_dialog(header, esckey);
144         g_free(esckey);
145
146         gtk_widget_show(filtering.window);
147
148         start_address_completion();
149 }
150
151 /* prefs_filtering_close() - just to have one common exit point */
152 static void prefs_filtering_close(void)
153 {
154         end_address_completion();
155         
156         gtk_widget_hide(filtering.window);
157         inc_unlock();
158 }
159
160 static void prefs_filtering_create(void)
161 {
162         GtkWidget *window;
163         GtkWidget *vbox;
164         GtkWidget *ok_btn;
165         GtkWidget *cancel_btn;
166         GtkWidget *confirm_area;
167
168         GtkWidget *vbox1;
169         GtkWidget *hbox1;
170         GtkWidget *reg_hbox;
171         GtkWidget *arrow;
172         GtkWidget *btn_hbox;
173
174         GtkWidget *cond_label;
175         GtkWidget *cond_entry;
176         GtkWidget *cond_btn;
177         GtkWidget *action_label;
178         GtkWidget *action_entry;
179         GtkWidget *action_btn;
180
181         GtkWidget *reg_btn;
182         GtkWidget *subst_btn;
183         GtkWidget *del_btn;
184
185         GtkWidget *cond_hbox;
186         GtkWidget *cond_scrolledwin;
187         GtkWidget *cond_clist;
188
189         GtkWidget *btn_vbox;
190         GtkWidget *up_btn;
191         GtkWidget *down_btn;
192
193         gchar *title[1];
194
195         debug_print("Creating filtering configuration window...\n");
196
197         window = gtk_window_new (GTK_WINDOW_DIALOG);
198         gtk_container_set_border_width (GTK_CONTAINER (window), 8);
199         gtk_window_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
200         gtk_window_set_modal (GTK_WINDOW (window), TRUE);
201         gtk_window_set_policy (GTK_WINDOW (window), FALSE, TRUE, FALSE);
202
203         vbox = gtk_vbox_new (FALSE, 6);
204         gtk_widget_show (vbox);
205         gtk_container_add (GTK_CONTAINER (window), vbox);
206
207         gtkut_button_set_create(&confirm_area, &ok_btn, _("OK"),
208                                 &cancel_btn, _("Cancel"), NULL, NULL);
209         gtk_widget_show (confirm_area);
210         gtk_box_pack_end (GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
211         gtk_widget_grab_default (ok_btn);
212
213         gtk_window_set_title (GTK_WINDOW(window),
214                                     _("Filtering/Processing configuration"));
215
216         gtk_signal_connect (GTK_OBJECT(window), "delete_event",
217                             GTK_SIGNAL_FUNC(prefs_filtering_deleted), NULL);
218         gtk_signal_connect (GTK_OBJECT(window), "key_press_event",
219                             GTK_SIGNAL_FUNC(prefs_filtering_key_pressed), NULL);
220         MANAGE_WINDOW_SIGNALS_CONNECT (window);
221         gtk_signal_connect (GTK_OBJECT(ok_btn), "clicked",
222                             GTK_SIGNAL_FUNC(prefs_filtering_ok), NULL);
223         gtk_signal_connect (GTK_OBJECT(cancel_btn), "clicked",
224                             GTK_SIGNAL_FUNC(prefs_filtering_cancel), NULL);
225
226         vbox1 = gtk_vbox_new (FALSE, VSPACING);
227         gtk_widget_show (vbox1);
228         gtk_box_pack_start (GTK_BOX (vbox), vbox1, TRUE, TRUE, 0);
229         gtk_container_set_border_width (GTK_CONTAINER (vbox1), 2);
230
231         cond_label = gtk_label_new (_("Condition"));
232         gtk_widget_show (cond_label);
233         gtk_misc_set_alignment (GTK_MISC (cond_label), 0, 0.5);
234         gtk_box_pack_start (GTK_BOX (vbox1), cond_label, FALSE, FALSE, 0);
235
236         hbox1 = gtk_hbox_new (FALSE, VSPACING);
237         gtk_widget_show (hbox1);
238         gtk_box_pack_start (GTK_BOX (vbox1), hbox1, FALSE, FALSE, 0);
239         gtk_container_set_border_width (GTK_CONTAINER (vbox1), 2);
240
241         cond_entry = gtk_entry_new ();
242         gtk_widget_show (cond_entry);
243         gtk_box_pack_start (GTK_BOX (hbox1), cond_entry, TRUE, TRUE, 0);
244
245         cond_btn = gtk_button_new_with_label (_("Define ..."));
246         gtk_widget_show (cond_btn);
247         gtk_box_pack_start (GTK_BOX (hbox1), cond_btn, FALSE, FALSE, 0);
248         gtk_signal_connect (GTK_OBJECT (cond_btn), "clicked",
249                             GTK_SIGNAL_FUNC (prefs_filtering_condition_define),
250                             NULL);
251
252         action_label = gtk_label_new (_("Action"));
253         gtk_widget_show (action_label);
254         gtk_misc_set_alignment (GTK_MISC (action_label), 0, 0.5);
255         gtk_box_pack_start (GTK_BOX (vbox1), action_label, FALSE, FALSE, 0);
256
257         hbox1 = gtk_hbox_new (FALSE, VSPACING);
258         gtk_widget_show (hbox1);
259         gtk_box_pack_start (GTK_BOX (vbox1), hbox1, FALSE, FALSE, 0);
260         gtk_container_set_border_width (GTK_CONTAINER (vbox1), 2);
261
262         action_entry = gtk_entry_new ();
263         gtk_widget_show (action_entry);
264         gtk_box_pack_start (GTK_BOX (hbox1), action_entry, TRUE, TRUE, 0);
265
266         action_btn = gtk_button_new_with_label (_("Define ..."));
267         gtk_widget_show (action_btn);
268         gtk_box_pack_start (GTK_BOX (hbox1), action_btn, FALSE, FALSE, 0);
269         gtk_signal_connect (GTK_OBJECT (action_btn), "clicked",
270                             GTK_SIGNAL_FUNC (prefs_filtering_action_define),
271                             NULL);
272
273         /* register / substitute / delete */
274
275         reg_hbox = gtk_hbox_new (FALSE, 4);
276         gtk_widget_show (reg_hbox);
277         gtk_box_pack_start (GTK_BOX (vbox1), reg_hbox, FALSE, FALSE, 0);
278
279         arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
280         gtk_widget_show (arrow);
281         gtk_box_pack_start (GTK_BOX (reg_hbox), arrow, FALSE, FALSE, 0);
282         gtk_widget_set_usize (arrow, -1, 16);
283
284         btn_hbox = gtk_hbox_new (TRUE, 4);
285         gtk_widget_show (btn_hbox);
286         gtk_box_pack_start (GTK_BOX (reg_hbox), btn_hbox, FALSE, FALSE, 0);
287
288         reg_btn = gtk_button_new_with_label (_("Add"));
289         gtk_widget_show (reg_btn);
290         gtk_box_pack_start (GTK_BOX (btn_hbox), reg_btn, FALSE, TRUE, 0);
291         gtk_signal_connect (GTK_OBJECT (reg_btn), "clicked",
292                             GTK_SIGNAL_FUNC (prefs_filtering_register_cb), NULL);
293
294         subst_btn = gtk_button_new_with_label (_("  Replace  "));
295         gtk_widget_show (subst_btn);
296         gtk_box_pack_start (GTK_BOX (btn_hbox), subst_btn, FALSE, TRUE, 0);
297         gtk_signal_connect (GTK_OBJECT (subst_btn), "clicked",
298                             GTK_SIGNAL_FUNC (prefs_filtering_substitute_cb),
299                             NULL);
300
301         del_btn = gtk_button_new_with_label (_("Delete"));
302         gtk_widget_show (del_btn);
303         gtk_box_pack_start (GTK_BOX (btn_hbox), del_btn, FALSE, TRUE, 0);
304         gtk_signal_connect (GTK_OBJECT (del_btn), "clicked",
305                             GTK_SIGNAL_FUNC (prefs_filtering_delete_cb), NULL);
306
307         cond_hbox = gtk_hbox_new (FALSE, 8);
308         gtk_widget_show (cond_hbox);
309         gtk_box_pack_start (GTK_BOX (vbox1), cond_hbox, TRUE, TRUE, 0);
310
311         cond_scrolledwin = gtk_scrolled_window_new (NULL, NULL);
312         gtk_widget_show (cond_scrolledwin);
313         gtk_widget_set_usize (cond_scrolledwin, -1, 150);
314         gtk_box_pack_start (GTK_BOX (cond_hbox), cond_scrolledwin,
315                             TRUE, TRUE, 0);
316         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (cond_scrolledwin),
317                                         GTK_POLICY_AUTOMATIC,
318                                         GTK_POLICY_AUTOMATIC);
319
320         title[0] = _("Current filtering/processing rules");
321         cond_clist = gtk_clist_new_with_titles(1, title);
322         gtk_widget_show (cond_clist);
323         gtk_container_add (GTK_CONTAINER (cond_scrolledwin), cond_clist);
324         gtk_clist_set_column_width (GTK_CLIST (cond_clist), 0, 80);
325         gtk_clist_set_selection_mode (GTK_CLIST (cond_clist),
326                                       GTK_SELECTION_BROWSE);
327         GTK_WIDGET_UNSET_FLAGS (GTK_CLIST (cond_clist)->column[0].button,
328                                 GTK_CAN_FOCUS);
329         gtk_signal_connect (GTK_OBJECT (cond_clist), "select_row",
330                             GTK_SIGNAL_FUNC (prefs_filtering_select), NULL);
331
332         btn_vbox = gtk_vbox_new (FALSE, 8);
333         gtk_widget_show (btn_vbox);
334         gtk_box_pack_start (GTK_BOX (cond_hbox), btn_vbox, FALSE, FALSE, 0);
335
336         up_btn = gtk_button_new_with_label (_("Up"));
337         gtk_widget_show (up_btn);
338         gtk_box_pack_start (GTK_BOX (btn_vbox), up_btn, FALSE, FALSE, 0);
339         gtk_signal_connect (GTK_OBJECT (up_btn), "clicked",
340                             GTK_SIGNAL_FUNC (prefs_filtering_up), NULL);
341
342         down_btn = gtk_button_new_with_label (_("Down"));
343         gtk_widget_show (down_btn);
344         gtk_box_pack_start (GTK_BOX (btn_vbox), down_btn, FALSE, FALSE, 0);
345         gtk_signal_connect (GTK_OBJECT (down_btn), "clicked",
346                             GTK_SIGNAL_FUNC (prefs_filtering_down), NULL);
347
348         gtk_widget_set_usize(window, 500, -1);
349
350         gtk_widget_show_all(window);
351
352         filtering.window    = window;
353         filtering.ok_btn = ok_btn;
354
355         filtering.cond_entry = cond_entry;
356         filtering.action_entry = action_entry;
357         filtering.cond_clist   = cond_clist;
358 }
359
360 static void prefs_filtering_update_hscrollbar(void)
361 {
362         gint optwidth = gtk_clist_optimal_column_width(GTK_CLIST(filtering.cond_clist), 0);
363         gtk_clist_set_column_width(GTK_CLIST(filtering.cond_clist), 0, optwidth);
364 }
365
366 void prefs_filtering_rename_path(const gchar *old_path, const gchar *new_path)
367 {
368         GList * cur;
369         gchar *paths[2] = {NULL, NULL};
370         paths[0] = (gchar*)old_path;
371         paths[1] = (gchar*)new_path;
372         for (cur = folder_get_list() ; cur != NULL ; cur = g_list_next(cur)) {
373                 Folder *folder;
374                 folder = (Folder *) cur->data;
375                 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
376                                 prefs_filtering_rename_path_func, paths);
377         }
378         prefs_filtering_rename_path_func(NULL, paths);
379 }
380
381 static gboolean prefs_filtering_rename_path_func(GNode *node, gpointer data)
382 {
383         GSList *cur;
384         gchar *old_path, *new_path;
385         gchar *base;
386         gchar *prefix;
387         gchar *suffix;
388         gchar *dest_path;
389         gchar *old_path_with_sep;
390         gint destlen;
391         gint prefixlen;
392         gint oldpathlen;
393         FolderItem *item;
394         GSList * action_cur;
395
396         old_path = ((gchar **)data)[0];
397         new_path = ((gchar **)data)[1];
398
399         g_return_val_if_fail(old_path != NULL, FALSE);
400         g_return_val_if_fail(new_path != NULL, FALSE);
401
402         oldpathlen = strlen(old_path);
403         old_path_with_sep = g_strconcat(old_path,G_DIR_SEPARATOR_S,NULL);
404         if (node == NULL)
405                 cur = global_processing;
406         else {
407                 item = node->data;
408                 if (!item || !item->prefs) 
409                         return FALSE;
410                 cur = item->prefs->processing;
411         }
412
413
414         for (; cur != NULL; cur = cur->next) {
415                 FilteringProp   *filtering = (FilteringProp *)cur->data;
416                 
417                 for(action_cur = filtering->action_list ; action_cur != NULL ;
418                     action_cur = action_cur->next) {
419
420                         FilteringAction *action = action_cur->data;
421                         
422                         if (!action->destination) continue;
423                         
424                         destlen = strlen(action->destination);
425                         
426                         if (destlen > oldpathlen) {
427                                 prefixlen = destlen - oldpathlen;
428                                 suffix = action->destination + prefixlen;
429                                 
430                                 if (!strncmp(old_path, suffix, oldpathlen)) {
431                                         prefix = g_malloc0(prefixlen + 1);
432                                         strncpy2(prefix, action->destination, prefixlen);
433                                         
434                                         base = suffix + oldpathlen;
435                                         while (*base == G_DIR_SEPARATOR) base++;
436                                         if (*base == '\0')
437                                                 dest_path = g_strconcat(prefix,
438                                                     G_DIR_SEPARATOR_S,
439                                                     new_path, NULL);
440                                         else
441                                                 dest_path = g_strconcat(prefix,
442                                                     G_DIR_SEPARATOR_S,
443                                                     new_path,
444                                                     G_DIR_SEPARATOR_S,
445                                                     base, NULL);
446                                         
447                                         g_free(prefix);
448                                         g_free(action->destination);
449                                         action->destination = dest_path;
450                                 } else { /* for non-leaf folders */
451                                         /* compare with trailing slash */
452                                         if (!strncmp(old_path_with_sep, action->destination, oldpathlen+1)) {
453                                                 
454                                                 suffix = action->destination + oldpathlen + 1;
455                                                 dest_path = g_strconcat(new_path,
456                                                     G_DIR_SEPARATOR_S,
457                                                     suffix, NULL);
458                                                 g_free(action->destination);
459                                                 action->destination = dest_path;
460                                         }
461                                 }
462                         } else {
463                                 /* folder-moving a leaf */
464                                 if (!strcmp(old_path, action->destination)) {           
465                                         dest_path = g_strdup(new_path);
466                                         g_free(action->destination);
467                                         action->destination = dest_path;
468                                 }
469                         }
470                 }
471         }
472         
473         g_free(old_path_with_sep);
474         prefs_matcher_write_config();
475
476         return FALSE;
477 }
478
479 void prefs_filtering_delete_path(const gchar *path)
480 {
481         GList * cur;
482         for (cur = folder_get_list() ; cur != NULL ; cur = g_list_next(cur)) {
483                 Folder *folder;
484                 folder = (Folder *) cur->data;
485                 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
486                                 prefs_filtering_delete_path_func, (gchar *)path);
487         }
488         prefs_filtering_delete_path_func(NULL, (gchar *)path);
489 }
490
491 static gboolean prefs_filtering_delete_path_func(GNode *node, gpointer data)
492 {
493         GSList *cur, *filters, *duplist;
494         gchar *path = (gchar *) data;
495         gchar *suffix;
496         gint destlen;
497         gint prefixlen;
498         gint pathlen;
499         FolderItem *item;
500         GSList * action_cur;
501         
502         g_return_val_if_fail(path != NULL, FALSE);
503
504         pathlen = strlen(path);
505         if (node == NULL)
506                 filters = global_processing;
507         else {
508                 item = node->data;
509                 if (!item || !item->prefs)
510                         return FALSE;
511                 filters = item->prefs->processing;
512         }
513
514         duplist = g_slist_copy(filters);
515         for (cur = duplist ; cur != NULL; cur = g_slist_next(cur)) {
516                 FilteringProp *filtering = (FilteringProp *) cur->data;
517                 
518                 for(action_cur = filtering->action_list ; action_cur != NULL ;
519                     action_cur = action_cur->next) {
520                 
521                         FilteringAction *action;
522                         
523                         action = action_cur->data;
524                         
525                         if (!action->destination) continue;
526                         
527                         destlen = strlen(action->destination);
528                         
529                         if (destlen > pathlen) {
530                                 prefixlen = destlen - pathlen;
531                                 suffix = action->destination + prefixlen;
532                                 
533                                 if (suffix && !strncmp(path, suffix, pathlen)) {
534                                         filteringprop_free(filtering);
535                                         filters = g_slist_remove(filters, filtering);
536                                 }
537                         } else if (strcmp(action->destination, path) == 0) {
538                                 filteringprop_free(filtering);
539                                 filters = g_slist_remove(filters, filtering);
540                         }
541                 }
542         }                
543         g_slist_free(duplist);
544
545         if (node == NULL)
546                 global_processing = filters;
547         else {
548                 item = node->data;
549                 if (!item || !item->prefs)
550                         return FALSE;
551                 item->prefs->processing = filters;
552         }
553         
554         prefs_matcher_write_config();
555
556         return FALSE;
557 }
558
559 static void prefs_filtering_set_dialog(const gchar *header, const gchar *key)
560 {
561         GtkCList *clist = GTK_CLIST(filtering.cond_clist);
562         GSList *cur;
563         GSList * prefs_filtering;
564         gchar *cond_str[1];
565         gint row;
566         
567         gtk_clist_freeze(clist);
568         gtk_clist_clear(clist);
569
570         cond_str[0] = _("(New)");
571         row = gtk_clist_append(clist, cond_str);
572         gtk_clist_set_row_data(clist, row, NULL);
573
574         if (cur_item == NULL)
575                 prefs_filtering = global_processing;
576         else
577                 prefs_filtering = cur_item->prefs->processing;
578
579         for(cur = prefs_filtering ; cur != NULL ; cur = g_slist_next(cur)) {
580                 FilteringProp * prop = (FilteringProp *) cur->data;
581
582                 cond_str[0] = filteringprop_to_string(prop);
583                 subst_char(cond_str[0], '\t', ':');
584                 row = gtk_clist_append(clist, cond_str);
585                 gtk_clist_set_row_data(clist, row, prop);
586
587                 g_free(cond_str[0]);
588         }
589
590         prefs_filtering_update_hscrollbar();
591         gtk_clist_thaw(clist);
592
593         prefs_filtering_reset_dialog();
594
595         if (header && key) {
596                 gchar *match_str = g_strconcat(header, " ",
597                         get_matchparser_tab_str(MATCHTYPE_MATCHCASE),
598                         " \"", key, "\"", NULL);
599                 gtk_entry_set_text(GTK_ENTRY(filtering.cond_entry), match_str);
600                 g_free(match_str);
601         }
602 }
603
604 static void prefs_filtering_reset_dialog(void)
605 {
606         gtk_entry_set_text(GTK_ENTRY(filtering.cond_entry), "");
607         gtk_entry_set_text(GTK_ENTRY(filtering.action_entry), "");
608 }
609
610 static void prefs_filtering_set_list(void)
611 {
612         gint row = 1;
613         FilteringProp *prop;
614         GSList * cur;
615         gchar * filtering_str;
616         GSList * prefs_filtering;
617
618         if (cur_item == NULL)
619                 prefs_filtering = global_processing;
620         else
621                 prefs_filtering = cur_item->prefs->processing;
622
623         for(cur = prefs_filtering ; cur != NULL ; cur = g_slist_next(cur))
624                 filteringprop_free((FilteringProp *) cur->data);
625         g_slist_free(prefs_filtering);
626         prefs_filtering = NULL;
627
628         while (gtk_clist_get_text(GTK_CLIST(filtering.cond_clist),
629                                   row, 0, &filtering_str)) {
630                 if (strcmp(filtering_str, _("(New)")) != 0) {
631                         prop = matcher_parser_get_filtering(filtering_str);
632                         if (prop != NULL)
633                                 prefs_filtering =
634                                         g_slist_append(prefs_filtering, prop);
635                 }
636                 row++;
637         }
638
639         if (cur_item == NULL)
640                 global_processing = prefs_filtering;
641         else
642                 cur_item->prefs->processing = prefs_filtering;
643 }
644
645 static gint prefs_filtering_clist_set_row(gint row, FilteringProp * prop)
646 {
647         GtkCList *clist = GTK_CLIST(filtering.cond_clist);
648         gchar * str;
649         gchar *cond_str[1];
650
651         if (prop == NULL) {
652                 cond_str[0] = _("(New)");
653                 return gtk_clist_append(clist, cond_str);
654         }
655
656         str = filteringprop_to_string(prop);
657         if (str == NULL) {
658                 return -1;
659         }
660         cond_str[0] = str;
661
662         if (row < 0)
663                 row = gtk_clist_append(clist, cond_str);
664         else
665                 gtk_clist_set_text(clist, row, 0, cond_str[0]);
666         g_free(str);
667
668         return row;
669 }
670
671 static void prefs_filtering_condition_define_done(MatcherList * matchers)
672 {
673         gchar * str;
674
675         if (matchers == NULL)
676                 return;
677
678         str = matcherlist_to_string(matchers);
679
680         if (str != NULL) {
681                 gtk_entry_set_text(GTK_ENTRY(filtering.cond_entry), str);
682                 g_free(str);
683         }
684 }
685
686 static void prefs_filtering_condition_define(void)
687 {
688         gchar * cond_str;
689         MatcherList * matchers = NULL;
690
691         cond_str = gtk_entry_get_text(GTK_ENTRY(filtering.cond_entry));
692
693         if (*cond_str != '\0') {
694                 matchers = matcher_parser_get_cond(cond_str);
695                 if (matchers == NULL)
696                         alertpanel_error(_("Condition string is not valid."));
697         }
698
699         prefs_matcher_open(matchers, prefs_filtering_condition_define_done);
700
701         if (matchers != NULL)
702                 matcherlist_free(matchers);
703 }
704
705 static void prefs_filtering_action_define_done(GSList * action_list)
706 {
707         gchar * str;
708
709         if (action_list == NULL)
710                 return;
711
712         str = filteringaction_list_to_string(action_list);
713
714         if (str != NULL) {
715                 gtk_entry_set_text(GTK_ENTRY(filtering.action_entry), str);
716                 g_free(str);
717         }
718 }
719
720 static void prefs_filtering_action_define(void)
721 {
722         gchar * action_str;
723         GSList * action_list = NULL;
724
725         action_str = gtk_entry_get_text(GTK_ENTRY(filtering.action_entry));
726
727         if (*action_str != '\0') {
728                 action_list = matcher_parser_get_action_list(action_str);
729                 if (action_list == NULL)
730                         alertpanel_error(_("Action string is not valid."));
731         }
732
733         prefs_filtering_action_open(action_list,
734             prefs_filtering_action_define_done);
735
736         if (action_list != NULL) {
737                 GSList * cur;
738                 for(cur = action_list ; cur != NULL ; cur = cur->next) {
739                         filteringaction_free(cur->data);
740                 }
741         }
742 }
743
744
745 /* register / substitute delete buttons */
746
747
748 static FilteringProp * prefs_filtering_dialog_to_filtering(gboolean alert)
749 {
750         MatcherList * cond;
751         gchar * cond_str;
752         gchar * action_str;
753         FilteringProp * prop;
754         GSList * action_list;
755
756         cond_str = gtk_entry_get_text(GTK_ENTRY(filtering.cond_entry));
757         if (*cond_str == '\0') {
758                 if(alert == TRUE) alertpanel_error(_("Condition string is empty."));
759                 return NULL;
760         }
761
762         action_str = gtk_entry_get_text(GTK_ENTRY(filtering.action_entry));
763         if (*action_str == '\0') {
764                 if(alert == TRUE) alertpanel_error(_("Action string is empty."));
765                 return NULL;
766         }
767
768         cond = matcher_parser_get_cond(cond_str);
769
770         if (cond == NULL) {
771                 if(alert == TRUE) alertpanel_error(_("Condition string is not valid."));
772                 return NULL;
773         }
774         
775         action_list = matcher_parser_get_action_list(action_str);
776
777         if (action_list == NULL) {
778                 if(alert == TRUE) alertpanel_error(_("Action string is not valid."));
779                 return NULL;
780         }
781
782         prop = filteringprop_new(cond, action_list);
783
784         return prop;
785 }
786
787 static void prefs_filtering_register_cb(void)
788 {
789         FilteringProp * prop;
790         
791         prop = prefs_filtering_dialog_to_filtering(TRUE);
792         if (prop == NULL)
793                 return;
794         prefs_filtering_clist_set_row(-1, prop);
795
796         filteringprop_free(prop);
797         
798         prefs_filtering_update_hscrollbar();
799 }
800
801 static void prefs_filtering_substitute_cb(void)
802 {
803         GtkCList *clist = GTK_CLIST(filtering.cond_clist);
804         gint row;
805         FilteringProp * prop;
806         
807         if (!clist->selection) return;
808
809         row = GPOINTER_TO_INT(clist->selection->data);
810         if (row == 0) return;
811
812         prop = prefs_filtering_dialog_to_filtering(TRUE);
813         if (prop == NULL)
814                 return;
815         prefs_filtering_clist_set_row(row, prop);
816
817         filteringprop_free(prop);
818         
819         prefs_filtering_update_hscrollbar();
820 }
821
822 static void prefs_filtering_delete_cb(void)
823 {
824         GtkCList *clist = GTK_CLIST(filtering.cond_clist);
825         gint row;
826
827         if (!clist->selection) return;
828         row = GPOINTER_TO_INT(clist->selection->data);
829         if (row == 0) return;
830
831         if (alertpanel(_("Delete rule"),
832                        _("Do you really want to delete this rule?"),
833                        _("Yes"), _("No"), NULL) == G_ALERTALTERNATE)
834                 return;
835
836         gtk_clist_remove(clist, row);
837
838         prefs_filtering_reset_dialog();
839
840         prefs_filtering_update_hscrollbar();
841 }
842
843 static void prefs_filtering_up(void)
844 {
845         GtkCList *clist = GTK_CLIST(filtering.cond_clist);
846         gint row;
847
848         if (!clist->selection) return;
849
850         row = GPOINTER_TO_INT(clist->selection->data);
851         if (row > 1) {
852                 gtk_clist_row_move(clist, row, row - 1);
853                 if(gtk_clist_row_is_visible(clist, row - 1) != GTK_VISIBILITY_FULL) {
854                         gtk_clist_moveto(clist, row - 1, 0, 0, 0);
855                 } 
856         }
857 }
858
859 static void prefs_filtering_down(void)
860 {
861         GtkCList *clist = GTK_CLIST(filtering.cond_clist);
862         gint row;
863
864         if (!clist->selection) return;
865
866         row = GPOINTER_TO_INT(clist->selection->data);
867         if (row > 0 && row < clist->rows - 1) {
868                 gtk_clist_row_move(clist, row, row + 1);
869                 if(gtk_clist_row_is_visible(clist, row + 1) != GTK_VISIBILITY_FULL) {
870                         gtk_clist_moveto(clist, row + 1, 0, 1, 0);
871                 } 
872         }
873 }
874
875 static void prefs_filtering_select_set(FilteringProp *prop)
876 {
877         gchar *matcher_str;
878         gchar *action_str;
879
880         prefs_filtering_reset_dialog();
881
882         matcher_str = matcherlist_to_string(prop->matchers);
883         if (matcher_str == NULL) {
884                 return;
885         }
886
887         gtk_entry_set_text(GTK_ENTRY(filtering.cond_entry), matcher_str);
888
889         action_str = filteringaction_list_to_string(prop->action_list);
890         if (matcher_str == NULL) {
891                 return;
892         }
893         gtk_entry_set_text(GTK_ENTRY(filtering.action_entry), action_str);
894
895         g_free(action_str);
896         g_free(matcher_str);
897 }
898
899 static void prefs_filtering_select(GtkCList *clist, gint row, gint column,
900                                 GdkEvent *event)
901 {
902         FilteringProp * prop;
903         gchar * filtering_str;
904
905         if (row == 0) {
906                 prefs_filtering_reset_dialog();
907                 return;
908         }
909
910         if (!gtk_clist_get_text(GTK_CLIST(filtering.cond_clist),
911                                 row, 0, &filtering_str))
912                 return;
913         
914         prop = matcher_parser_get_filtering(filtering_str);
915         if (prop == NULL)
916                 return;
917
918         prefs_filtering_select_set(prop);
919
920         filteringprop_free(prop);
921 }
922
923
924 static gint prefs_filtering_deleted(GtkWidget *widget, GdkEventAny *event,
925                                  gpointer data)
926 {
927         prefs_filtering_cancel();
928         return TRUE;
929 }
930
931 static void prefs_filtering_key_pressed(GtkWidget *widget, GdkEventKey *event,
932                                      gpointer data)
933 {
934         if (event && event->keyval == GDK_Escape)
935                 prefs_filtering_cancel();
936 }
937
938 static void prefs_filtering_ok(void)
939 {
940         FilteringProp * prop;
941         gchar * str;
942         gchar * filtering_str;
943         gint row = 1;
944         AlertValue val;
945         
946         prop = prefs_filtering_dialog_to_filtering(FALSE);
947         if (prop != NULL) {
948                 str = filteringprop_to_string(prop);
949
950                 while (gtk_clist_get_text(GTK_CLIST(filtering.cond_clist),
951                                           row, 0, &filtering_str)) {
952                         if (strcmp(filtering_str, str) == 0) break;
953                         row++;
954                 }
955                 if (strcmp(filtering_str, str) != 0) {
956                         val = alertpanel(_("Entry not saved"),
957                                  _("The entry was not saved. Close anyway?"),
958                                  _("Yes"), _("No"), NULL);
959                         if (G_ALERTDEFAULT != val) {
960                                 g_free(str);
961                                 return;
962                         }
963                 }
964                 g_free(str);
965         }
966         prefs_filtering_set_list();
967         prefs_matcher_write_config();
968         prefs_filtering_close();
969 }
970
971 static void prefs_filtering_cancel(void)
972 {
973         prefs_matcher_read_config();
974         prefs_filtering_close();
975 }