3ffcfa45e6ea167f774d6f68454d85f6f90f7975
[claws.git] / src / prefs_filtering.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2013 Hiroyuki Yamamoto and the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24
25 #include "defs.h"
26
27 #include <glib.h>
28 #include <glib/gi18n.h>
29 #include <gtk/gtk.h>
30 #include <gdk/gdkkeysyms.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <errno.h>
35
36 #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 "folder_item_prefs.h"
50 #include "filtering.h"
51 #include "addr_compl.h"
52 #include "manual.h"
53 #include "combobox.h"
54 #include "menu.h"
55 #include "account.h"
56
57 #include "matcher_parser.h"
58 #include "matcher.h"
59 #include "prefs_filtering_action.h"
60
61 enum {
62         PREFS_FILTERING_ENABLED,
63         PREFS_FILTERING_NAME,
64         PREFS_FILTERING_ACCOUNT_ID,
65         PREFS_FILTERING_ACCOUNT_NAME,
66         PREFS_FILTERING_RULE,
67         PREFS_FILTERING_PROP,
68         N_PREFS_FILTERING_COLUMNS
69 };
70
71 struct _Filtering {
72         GtkWidget *window;
73
74         GtkWidget *help_btn;
75         GtkWidget *ok_btn;
76         GtkWidget *name_entry;
77         GtkWidget *account_label;
78         GtkWidget *account_combobox;
79         GtkListStore *account_combobox_list;
80         GtkWidget *cond_entry;
81         GtkWidget *action_entry;
82
83         GtkWidget *cond_list_view;
84
85         GtkTreeViewColumn *account_name_column;
86 };
87
88 typedef struct _Filtering Filtering;
89
90 static Filtering  filtering;
91
92 static GSList ** p_processing_list = NULL;
93
94 /* widget creating functions */
95 static void prefs_filtering_create              (void);
96
97 static void prefs_filtering_set_dialog  (const gchar *header,
98                                          const gchar *key);
99 static void prefs_filtering_set_list    (void);
100
101 /* callback functions */
102 static gboolean prefs_filtering_search_func_cb (GtkTreeModel *model, gint column, 
103                                                 const gchar *key, GtkTreeIter *iter, 
104                                                 gpointer search_data);
105 static void prefs_filtering_register_cb (gpointer action, gpointer data);
106 static void prefs_filtering_substitute_cb       (gpointer action, gpointer data);
107 static void prefs_filtering_delete_cb   (gpointer action, gpointer data);
108 static void prefs_filtering_delete_all_cb(gpointer action, gpointer data);
109 static void prefs_filtering_clear_cb(gpointer action, gpointer data);
110 static void prefs_filtering_duplicate_cb(gpointer action, gpointer data);
111 static void prefs_filtering_top         (gpointer action, gpointer data);
112 static void prefs_filtering_page_up     (gpointer action, gpointer data);
113 static void prefs_filtering_up          (gpointer action, gpointer data);
114 static void prefs_filtering_down        (gpointer action, gpointer data);
115 static void prefs_filtering_page_down   (gpointer action, gpointer data);
116 static void prefs_filtering_bottom      (gpointer action, gpointer data);
117 static gint prefs_filtering_deleted     (GtkWidget      *widget,
118                                          GdkEventAny    *event,
119                                          gpointer        data);
120 static void prefs_filtering_row_selected(GtkTreeSelection *selection,
121                                          GtkTreeView *list_view);
122 static gboolean prefs_filtering_key_pressed(GtkWidget   *widget,
123                                          GdkEventKey    *event,
124                                          gpointer        data);
125 static void prefs_filtering_cancel      (gpointer action, gpointer data);
126 static void prefs_filtering_ok          (gpointer action, gpointer data);
127
128 static void prefs_filtering_condition_define    (gpointer action, gpointer data);
129 static void prefs_filtering_action_define       (gpointer action, gpointer data);
130 static gint prefs_filtering_list_view_set_row   (gint row, FilteringProp * prop);
131                                           
132 static void prefs_filtering_reset_dialog        (void);
133 static gboolean prefs_filtering_rename_tag_func(GNode *node, gpointer data);
134 static gboolean prefs_filtering_rename_path_func(GNode *node, gpointer data);
135 static gboolean prefs_filtering_delete_path_func(GNode *node, gpointer data);
136
137 static void delete_path(GSList ** p_filters, const gchar * path);
138
139
140 static GtkListStore* prefs_filtering_create_data_store  (void);
141 static gint prefs_filtering_list_view_insert_rule       (GtkListStore *list_store,
142                                                          gint row,
143                                                          gboolean enabled,
144                                                          const gchar *name,
145                                                          gint account_id,
146                                                          const gchar *account_name,
147                                                          const gchar *rule, 
148                                                          gboolean prop);
149 static gchar *prefs_filtering_list_view_get_rule        (GtkWidget *list, 
150                                                          gint row);
151 static void prefs_filtering_list_view_get_rule_info     (GtkWidget *list, 
152                                                          gint row,
153                                                          gboolean *enabled,
154                                                          gchar **name,
155                                                          gint *account_id);
156
157 static GtkWidget *prefs_filtering_list_view_create      (void);
158 static void prefs_filtering_create_list_view_columns    (GtkWidget *list_view);
159
160 static void prefs_filtering_select_row(GtkTreeView *list_view, GtkTreePath *path);
161
162 static void prefs_filtering_account_option_menu_populate(void);
163
164 static gulong signal_id = 0; /* filtering.help_btn clicked signal */
165
166 static int modified = FALSE;
167
168 void prefs_filtering_open(GSList ** p_processing,
169                           const gchar *title,
170                           const gchar *help_url_anchor,
171                           const gchar *header,
172                           const gchar *key,
173                           gboolean per_account_filtering)
174 {
175         if (prefs_rc_is_readonly(FILTERING_RC))
176                 return;
177
178         inc_lock();
179
180         if (!filtering.window) {
181                 prefs_filtering_create();
182         } else {
183                 gtk_list_store_clear(filtering.account_combobox_list);
184                 prefs_filtering_account_option_menu_populate();
185         }
186
187         gtk_tree_view_column_set_visible(filtering.account_name_column,
188                                                                          per_account_filtering);
189
190         manage_window_set_transient(GTK_WINDOW(filtering.window));
191         gtk_widget_grab_focus(filtering.ok_btn);
192         
193         if (title != NULL)
194                 gtk_window_set_title(GTK_WINDOW(filtering.window), title);
195         else
196                 gtk_window_set_title (GTK_WINDOW(filtering.window),
197                                       _("Filtering/Processing configuration"));
198
199         if (help_url_anchor != NULL) {
200                 if (signal_id != 0) {
201                         g_signal_handler_disconnect(
202                                         G_OBJECT(filtering.help_btn),
203                                         signal_id);
204                 }
205
206                 signal_id = g_signal_connect(G_OBJECT(filtering.help_btn),
207                                 "clicked",
208                                 G_CALLBACK(manual_open_with_anchor_cb),
209                                 (gchar*)help_url_anchor);
210         }
211         else {
212                 gtk_widget_set_sensitive(filtering.help_btn, FALSE);
213         }
214
215         p_processing_list = p_processing;
216         
217         prefs_filtering_set_dialog(header, key);
218         if (per_account_filtering) {
219                 gtk_widget_show(filtering.account_label);
220                 gtk_widget_show(filtering.account_combobox);
221         } else {
222                 gtk_widget_hide(filtering.account_label);
223                 gtk_widget_hide(filtering.account_combobox);
224                 combobox_select_by_data(GTK_COMBO_BOX(filtering.account_combobox), 0);
225         }
226
227         gtk_widget_show(filtering.window);
228         gtk_window_set_modal(GTK_WINDOW(filtering.window), TRUE);
229
230         start_address_completion(NULL);
231 }
232
233 static void prefs_filtering_size_allocate_cb(GtkWidget *widget,
234                                          GtkAllocation *allocation)
235 {
236         cm_return_if_fail(allocation != NULL);
237
238         prefs_common.filteringwin_width = allocation->width;
239         prefs_common.filteringwin_height = allocation->height;
240 }
241
242 /* prefs_filtering_close() - just to have one common exit point */
243 static void prefs_filtering_close(void)
244 {
245         GtkListStore *store;
246
247         end_address_completion();
248
249         store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW
250                                 (filtering.cond_list_view)));
251         gtk_list_store_clear(store);
252         gtk_widget_hide(filtering.window);
253         gtk_window_set_modal(GTK_WINDOW(filtering.window), FALSE);
254         inc_unlock();
255 }
256
257 static void prefs_filtering_account_option_menu_populate(void)
258 {
259         GList *accounts = NULL;
260         GtkTreeIter iter;
261
262         accounts = account_get_list();
263
264         cm_return_if_fail(accounts != NULL);
265
266         COMBOBOX_ADD(filtering.account_combobox_list, C_("Filtering Account Menu", "All"), 0);
267         COMBOBOX_ADD(filtering.account_combobox_list, NULL, 0);
268         for (; accounts != NULL; accounts = accounts->next) {
269                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
270
271                 COMBOBOX_ADD_ESCAPED(filtering.account_combobox_list, ac->account_name, ac->account_id);
272         }
273 }
274
275 static GtkWidget *prefs_filtering_account_option_menu(Filtering *filtering)
276 {
277         GtkWidget *optmenu = NULL;
278         GtkWidget *optmenubox = NULL;
279         GtkListStore *menu = NULL;
280         
281         optmenubox = gtk_event_box_new();
282         optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
283         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
284
285         filtering->account_combobox = optmenu;
286         filtering->account_combobox_list = menu;
287
288         prefs_filtering_account_option_menu_populate();
289
290         return optmenubox;
291 }
292
293 static void prefs_filtering_create(void)
294 {
295         GtkWidget *window;
296         GtkWidget *vbox;
297         GtkWidget *help_btn;
298         GtkWidget *cancel_btn;
299         GtkWidget *ok_btn;
300         GtkWidget *confirm_area;
301
302         GtkWidget *vbox1;
303         GtkWidget *reg_hbox;
304         GtkWidget *arrow;
305         GtkWidget *btn_hbox;
306
307         GtkWidget *name_label;
308         GtkWidget *name_entry;
309         GtkWidget *account_label;
310         GtkWidget *account_opt_menu;
311         GtkWidget *cond_label;
312         GtkWidget *cond_entry;
313         GtkWidget *cond_btn;
314         GtkWidget *action_label;
315         GtkWidget *action_entry;
316         GtkWidget *action_btn;
317
318         GtkWidget *reg_btn;
319         GtkWidget *subst_btn;
320         GtkWidget *del_btn;
321         GtkWidget *clear_btn;
322
323         GtkWidget *cond_hbox;
324         GtkWidget *cond_scrolledwin;
325         GtkWidget *cond_list_view;
326
327         GtkWidget *btn_vbox;
328         GtkWidget *top_btn;
329         GtkWidget *up_btn;
330         GtkWidget *down_btn;
331 #ifndef GENERIC_UMPC
332         GtkWidget *page_up_btn;
333         GtkWidget *page_down_btn;
334 #endif
335         GtkWidget *bottom_btn;
336         GtkWidget *table;
337         static GdkGeometry geometry;
338
339         debug_print("Creating filtering configuration window...\n");
340
341         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "prefs_filtering");
342         gtk_container_set_border_width (GTK_CONTAINER (window), 8);
343         gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
344         gtk_window_set_resizable(GTK_WINDOW (window), TRUE);
345
346         vbox = gtk_vbox_new (FALSE, 6);
347         gtk_widget_show (vbox);
348         gtk_container_add (GTK_CONTAINER (window), vbox);
349
350         gtkut_stock_button_set_create_with_help(&confirm_area, &help_btn,
351                         &cancel_btn, GTK_STOCK_CANCEL,
352                         &ok_btn, GTK_STOCK_OK,
353                         NULL, NULL);
354         gtk_widget_show (confirm_area);
355         gtk_box_pack_end (GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
356         gtk_widget_grab_default (ok_btn);
357
358         gtk_window_set_title (GTK_WINDOW(window),
359                                     _("Filtering/Processing configuration"));
360
361         g_signal_connect(G_OBJECT(window), "delete_event",
362                          G_CALLBACK(prefs_filtering_deleted), NULL);
363         g_signal_connect(G_OBJECT(window), "size_allocate",
364                          G_CALLBACK(prefs_filtering_size_allocate_cb), NULL);
365         g_signal_connect(G_OBJECT(window), "key_press_event",
366                          G_CALLBACK(prefs_filtering_key_pressed), NULL);
367         MANAGE_WINDOW_SIGNALS_CONNECT (window);
368         g_signal_connect(G_OBJECT(ok_btn), "clicked",
369                          G_CALLBACK(prefs_filtering_ok), NULL);
370         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
371                          G_CALLBACK(prefs_filtering_cancel), NULL);
372
373         vbox1 = gtk_vbox_new (FALSE, VSPACING);
374         gtk_widget_show (vbox1);
375         gtk_box_pack_start (GTK_BOX (vbox), vbox1, FALSE, TRUE, 0);
376         gtk_container_set_border_width (GTK_CONTAINER (vbox1), 2);
377
378         table = gtk_table_new(4, 3, FALSE);
379         gtk_table_set_row_spacings (GTK_TABLE (table), VSPACING_NARROW_2);
380         gtk_table_set_col_spacings (GTK_TABLE (table), 4);
381         gtk_widget_show(table);
382         gtk_box_pack_start (GTK_BOX (vbox1), table, TRUE, TRUE, 0);
383
384         name_label = gtk_label_new (_("Name"));
385         gtk_widget_show (name_label);
386         gtk_misc_set_alignment (GTK_MISC (name_label), 1, 0.5);
387         gtk_table_attach (GTK_TABLE (table), name_label, 0, 1, 0, 1,
388                           (GtkAttachOptions) (GTK_FILL),
389                           (GtkAttachOptions) (0), 0, 0);
390
391         name_entry = gtk_entry_new ();
392         gtk_widget_show (name_entry);
393         gtk_table_attach (GTK_TABLE (table), name_entry, 1, 2, 0, 1,
394                           (GtkAttachOptions) (GTK_FILL|GTK_EXPAND),
395                           (GtkAttachOptions) (0), 0, 0);
396
397         account_label = gtk_label_new (_("Account"));
398         gtk_widget_show (account_label);
399         gtk_misc_set_alignment (GTK_MISC (account_label), 1, 0.5);
400         gtk_table_attach (GTK_TABLE (table), account_label, 0, 1, 1, 2,
401                           (GtkAttachOptions) (GTK_FILL),
402                           (GtkAttachOptions) (0), 0, 0);
403
404         account_opt_menu = prefs_filtering_account_option_menu(&filtering);
405         gtk_widget_show (account_opt_menu);
406         gtk_table_attach (GTK_TABLE (table), account_opt_menu, 1, 2, 1, 2,
407                           (GtkAttachOptions) (GTK_FILL|GTK_EXPAND),
408                           (GtkAttachOptions) (0), 0, 0);
409         combobox_select_by_data(GTK_COMBO_BOX(filtering.account_combobox), 0);
410
411         cond_label = gtk_label_new (_("Condition"));
412         gtk_widget_show (cond_label);
413         gtk_misc_set_alignment (GTK_MISC (cond_label), 1, 0.5);
414         gtk_table_attach (GTK_TABLE (table), cond_label, 0, 1, 2, 3,
415                           (GtkAttachOptions) (GTK_FILL),
416                           (GtkAttachOptions) (0), 0, 0);
417
418         cond_entry = gtk_entry_new ();
419         gtk_widget_show (cond_entry);
420         gtk_table_attach (GTK_TABLE (table), cond_entry, 1, 2, 2, 3,
421                           (GtkAttachOptions) (GTK_FILL|GTK_EXPAND),
422                           (GtkAttachOptions) (0), 0, 0);
423
424         cond_btn =  gtk_button_new_with_mnemonic (_(" D_efine... "));
425         gtk_widget_show (cond_btn);
426         gtk_table_attach (GTK_TABLE (table), cond_btn, 2, 3, 2, 3,
427                           (GtkAttachOptions) (GTK_FILL),
428                           (GtkAttachOptions) (0), 2, 2);
429         g_signal_connect(G_OBJECT (cond_btn), "clicked",
430                          G_CALLBACK(prefs_filtering_condition_define),
431                          NULL);
432
433         action_label = gtk_label_new (_("Action"));
434         gtk_widget_show (action_label);
435         gtk_misc_set_alignment (GTK_MISC (action_label), 1, 0.5);
436         gtk_table_attach (GTK_TABLE (table), action_label, 0, 1, 3, 4,
437                           (GtkAttachOptions) (GTK_FILL),
438                           (GtkAttachOptions) (0), 0, 0);
439
440         action_entry = gtk_entry_new ();
441         gtk_widget_show (action_entry);
442         gtk_table_attach (GTK_TABLE (table), action_entry, 1, 2, 3, 4,
443                           (GtkAttachOptions) (GTK_FILL|GTK_EXPAND),
444                           (GtkAttachOptions) (0), 0, 0);
445
446         action_btn =  gtk_button_new_with_mnemonic (_(" De_fine... "));
447         gtk_widget_show (action_btn);
448         gtk_table_attach (GTK_TABLE (table), action_btn, 2, 3, 3, 4,
449                           (GtkAttachOptions) (GTK_FILL),
450                           (GtkAttachOptions) (0), 2, 2);
451         g_signal_connect(G_OBJECT (action_btn), "clicked",
452                          G_CALLBACK(prefs_filtering_action_define),
453                          NULL);
454                          
455         /* register / substitute / delete */
456         reg_hbox = gtk_hbox_new (FALSE, 4);
457         gtk_widget_show (reg_hbox);
458         gtk_box_pack_start (GTK_BOX (vbox1), reg_hbox, FALSE, FALSE, 0);
459
460         arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
461         gtk_widget_show (arrow);
462         gtk_box_pack_start (GTK_BOX (reg_hbox), arrow, FALSE, FALSE, 0);
463         gtk_widget_set_size_request (arrow, -1, 16);
464
465         btn_hbox = gtk_hbox_new (TRUE, 4);
466         gtk_widget_show (btn_hbox);
467         gtk_box_pack_start (GTK_BOX (reg_hbox), btn_hbox, FALSE, FALSE, 0);
468
469         reg_btn = gtk_button_new_from_stock (GTK_STOCK_ADD);
470         gtk_widget_show (reg_btn);
471         gtk_box_pack_start (GTK_BOX (btn_hbox), reg_btn, FALSE, TRUE, 0);
472         g_signal_connect(G_OBJECT (reg_btn), "clicked",
473                          G_CALLBACK(prefs_filtering_register_cb), NULL);
474         CLAWS_SET_TIP(reg_btn,
475                         _("Append the new rule above to the list"));
476
477         subst_btn = gtkut_get_replace_btn (_("_Replace"));
478         gtk_widget_show (subst_btn);
479         gtk_box_pack_start (GTK_BOX (btn_hbox), subst_btn, FALSE, TRUE, 0);
480         g_signal_connect(G_OBJECT (subst_btn), "clicked",
481                          G_CALLBACK(prefs_filtering_substitute_cb),
482                          NULL);
483         CLAWS_SET_TIP(subst_btn,
484                         _("Replace the selected rule in list with the rule above"));
485
486         del_btn = gtk_button_new_with_mnemonic (_("Re_move"));
487         gtk_button_set_image(GTK_BUTTON(del_btn),
488                         gtk_image_new_from_stock(GTK_STOCK_REMOVE,GTK_ICON_SIZE_BUTTON));
489         gtk_box_pack_start (GTK_BOX (btn_hbox), del_btn, FALSE, TRUE, 0);
490         g_signal_connect(G_OBJECT (del_btn), "clicked",
491                         G_CALLBACK(prefs_filtering_delete_cb), NULL);
492         CLAWS_SET_TIP(del_btn,
493                         _("Delete the selected rule from the list"));
494
495         clear_btn = gtk_button_new_with_mnemonic (_("C_lear"));
496         gtk_button_set_image(GTK_BUTTON(clear_btn),
497                         gtk_image_new_from_stock(GTK_STOCK_CLEAR,GTK_ICON_SIZE_BUTTON));
498         gtk_widget_show (clear_btn);
499         gtk_box_pack_start (GTK_BOX (btn_hbox), clear_btn, FALSE, TRUE, 0);
500         g_signal_connect(G_OBJECT (clear_btn), "clicked",
501                         G_CALLBACK(prefs_filtering_clear_cb), NULL);
502         CLAWS_SET_TIP(clear_btn,
503                         _("Clear all the input fields in the dialog"));
504
505         cond_hbox = gtk_hbox_new (FALSE, 8);
506         gtk_widget_show (cond_hbox);
507         gtk_box_pack_start (GTK_BOX (vbox), cond_hbox, TRUE, TRUE, 0);
508
509         cond_scrolledwin = gtk_scrolled_window_new (NULL, NULL);
510         gtk_widget_show (cond_scrolledwin);
511         gtk_widget_set_size_request (cond_scrolledwin, -1, 150);
512         gtk_box_pack_start (GTK_BOX (cond_hbox), cond_scrolledwin,
513                             TRUE, TRUE, 0);
514         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (cond_scrolledwin),
515                                         GTK_POLICY_AUTOMATIC,
516                                         GTK_POLICY_AUTOMATIC);
517
518         cond_list_view = prefs_filtering_list_view_create();    
519         gtk_widget_show (cond_list_view);
520         gtk_container_add (GTK_CONTAINER (cond_scrolledwin), cond_list_view);
521
522         btn_vbox = gtk_vbox_new (FALSE, 8);
523         gtk_widget_show (btn_vbox);
524         gtk_box_pack_start (GTK_BOX (cond_hbox), btn_vbox, FALSE, FALSE, 0);
525
526         top_btn = gtk_button_new_from_stock (GTK_STOCK_GOTO_TOP);
527         gtk_widget_show (top_btn);
528         gtk_box_pack_start (GTK_BOX (btn_vbox), top_btn, FALSE, FALSE, 0);
529         g_signal_connect(G_OBJECT (top_btn), "clicked",
530                          G_CALLBACK(prefs_filtering_top), NULL);
531         CLAWS_SET_TIP(top_btn,
532                         _("Move the selected rule to the top"));
533
534 #ifndef GENERIC_UMPC
535         page_up_btn = gtk_button_new_with_mnemonic (_("Page u_p"));
536         gtk_button_set_image(GTK_BUTTON(page_up_btn),
537                         gtk_image_new_from_stock(GTK_STOCK_GO_UP,GTK_ICON_SIZE_BUTTON));
538         gtk_widget_show (page_up_btn);
539         gtk_box_pack_start (GTK_BOX (btn_vbox), page_up_btn, FALSE, FALSE, 0);
540         g_signal_connect(G_OBJECT (page_up_btn), "clicked",
541                          G_CALLBACK(prefs_filtering_page_up), NULL);
542         CLAWS_SET_TIP(page_up_btn,
543                         _("Move the selected rule one page up"));
544 #endif
545
546         up_btn = gtk_button_new_from_stock (GTK_STOCK_GO_UP);
547         gtk_widget_show (up_btn);
548         gtk_box_pack_start (GTK_BOX (btn_vbox), up_btn, FALSE, FALSE, 0);
549         g_signal_connect(G_OBJECT (up_btn), "clicked",
550                          G_CALLBACK(prefs_filtering_up), NULL);
551         CLAWS_SET_TIP(up_btn,
552                         _("Move the selected rule up"));
553
554         down_btn = gtk_button_new_from_stock (GTK_STOCK_GO_DOWN);
555         gtk_widget_show (down_btn);
556         gtk_box_pack_start (GTK_BOX (btn_vbox), down_btn, FALSE, FALSE, 0);
557         g_signal_connect(G_OBJECT (down_btn), "clicked",
558                          G_CALLBACK(prefs_filtering_down), NULL);
559         CLAWS_SET_TIP(down_btn,
560                         _("Move the selected rule down"));
561
562 #ifndef GENERIC_UMPC
563         page_down_btn = gtk_button_new_with_mnemonic (_("Page dow_n"));
564         gtk_button_set_image(GTK_BUTTON(page_down_btn),
565                         gtk_image_new_from_stock(GTK_STOCK_GO_DOWN,GTK_ICON_SIZE_BUTTON));
566         gtk_widget_show (page_down_btn);
567         gtk_box_pack_start (GTK_BOX (btn_vbox), page_down_btn, FALSE, FALSE, 0);
568         g_signal_connect(G_OBJECT (page_down_btn), "clicked",
569                          G_CALLBACK(prefs_filtering_page_down), NULL);
570         CLAWS_SET_TIP(page_down_btn,
571                         _("Move the selected rule one page down"));
572 #endif
573
574         bottom_btn = gtk_button_new_from_stock (GTK_STOCK_GOTO_BOTTOM);
575         gtk_widget_show (bottom_btn);
576         gtk_box_pack_start (GTK_BOX (btn_vbox), bottom_btn, FALSE, FALSE, 0);
577         g_signal_connect(G_OBJECT (bottom_btn), "clicked",
578                          G_CALLBACK(prefs_filtering_bottom), NULL);
579         CLAWS_SET_TIP(bottom_btn,
580                         _("Move the selected rule to the bottom"));
581
582         if (!geometry.min_height) {
583                 geometry.min_width = 500;
584                 geometry.min_height = 460;
585         }
586
587         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL, &geometry,
588                                       GDK_HINT_MIN_SIZE);
589         gtk_widget_set_size_request(window, prefs_common.filteringwin_width,
590                                     prefs_common.filteringwin_height);
591
592         gtk_widget_show_all(window);
593
594         filtering.window    = window;
595         filtering.help_btn = help_btn;
596         filtering.ok_btn = ok_btn;
597
598         filtering.name_entry     = name_entry;
599         filtering.cond_entry     = cond_entry;
600         filtering.action_entry   = action_entry;
601         filtering.cond_list_view = cond_list_view;
602         filtering.account_label  = account_label;
603 }
604
605 static void rename_tag(GSList * filters,
606                         const gchar * old_tag, const gchar * new_tag);
607
608 void prefs_filtering_rename_tag(const gchar *old_tag, const gchar *new_tag)
609 {
610         GList * cur;
611         const gchar *tags[2] = {NULL, NULL};
612         tags[0] = old_tag;
613         tags[1] = new_tag;
614         for (cur = folder_get_list() ; cur != NULL ; cur = g_list_next(cur)) {
615                 Folder *folder;
616                 folder = (Folder *) cur->data;
617                 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
618                                 prefs_filtering_rename_tag_func, tags);
619         }
620         
621         rename_tag(pre_global_processing, old_tag, new_tag);
622         rename_tag(post_global_processing, old_tag, new_tag);
623         rename_tag(filtering_rules, old_tag, new_tag);
624         
625         prefs_matcher_write_config();
626 }
627
628
629 static void rename_path(GSList * filters,
630                         const gchar * old_path, const gchar * new_path);
631
632 void prefs_filtering_rename_path(const gchar *old_path, const gchar *new_path)
633 {
634         GList * cur;
635         const gchar *paths[2] = {NULL, NULL};
636         paths[0] = old_path;
637         paths[1] = new_path;
638         for (cur = folder_get_list() ; cur != NULL ; cur = g_list_next(cur)) {
639                 Folder *folder;
640                 folder = (Folder *) cur->data;
641                 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
642                                 prefs_filtering_rename_path_func, paths);
643         }
644         
645         rename_path(pre_global_processing, old_path, new_path);
646         rename_path(post_global_processing, old_path, new_path);
647         rename_path(filtering_rules, old_path, new_path);
648         
649         prefs_matcher_write_config();
650 }
651
652 static void rename_path(GSList * filters,
653                         const gchar * old_path, const gchar * new_path)
654 {
655         GSList * cur;
656
657         for (cur = filters; cur != NULL; cur = cur->next) {
658                 FilteringProp   *filtering = (FilteringProp *)cur->data;
659                 filtering_action_list_rename_path(filtering->action_list,
660                                                   old_path, new_path);
661         }
662 }
663
664 static gboolean prefs_filtering_rename_path_func(GNode *node, gpointer data)
665 {
666         GSList *filters;
667         const gchar * old_path;
668         const gchar * new_path;
669         const gchar ** paths;
670         FolderItem *item;
671         
672         paths = data;
673         old_path = paths[0];
674         new_path = paths[1];
675
676         cm_return_val_if_fail(old_path != NULL, FALSE);
677         cm_return_val_if_fail(new_path != NULL, FALSE);
678         cm_return_val_if_fail(node != NULL, FALSE);
679
680         item = node->data;
681         if (!item || !item->prefs)
682                 return FALSE;
683         filters = item->prefs->processing;
684
685         rename_path(filters, old_path, new_path);
686
687         return FALSE;
688 }
689
690 static void rename_tag(GSList * filters,
691                         const gchar * old_tag, const gchar * new_tag)
692 {
693         GSList * action_cur;
694         GSList * cur;
695
696         for (cur = filters; cur != NULL; cur = cur->next) {
697                 FilteringProp   *filtering = (FilteringProp *)cur->data;
698                 
699                 for(action_cur = filtering->action_list ; action_cur != NULL ;
700                     action_cur = action_cur->next) {
701
702                         FilteringAction *action = action_cur->data;
703                         
704                         if (action->type != MATCHACTION_SET_TAG &&
705                             action->type != MATCHACTION_UNSET_TAG)
706                                 continue;
707                         if (!action->destination)
708                                 continue;
709                         if (!strcmp(action->destination, old_tag)) {
710                                 g_free(action->destination);
711                                 action->destination = g_strdup(new_tag);
712                         }
713                 }
714         }
715 }
716
717 static gboolean prefs_filtering_rename_tag_func(GNode *node, gpointer data)
718 {
719         GSList *filters;
720         const gchar * old_tag;
721         const gchar * new_tag;
722         const gchar ** tags;
723         FolderItem *item;
724         
725         tags = data;
726         old_tag = tags[0];
727         new_tag = tags[1];
728
729         cm_return_val_if_fail(old_tag != NULL, FALSE);
730         cm_return_val_if_fail(new_tag != NULL, FALSE);
731         cm_return_val_if_fail(node != NULL, FALSE);
732
733         item = node->data;
734         if (!item || !item->prefs)
735                 return FALSE;
736         filters = item->prefs->processing;
737
738         rename_tag(filters, old_tag, new_tag);
739
740         return FALSE;
741 }
742
743 void prefs_filtering_delete_path(const gchar *path)
744 {
745         GList * cur;
746         for (cur = folder_get_list() ; cur != NULL ; cur = g_list_next(cur)) {
747                 Folder *folder;
748                 folder = (Folder *) cur->data;
749                 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
750                                 prefs_filtering_delete_path_func, (gchar *)path);
751         }
752         delete_path(&pre_global_processing, path);
753         delete_path(&post_global_processing, path);
754         delete_path(&filtering_rules, path);
755         
756         prefs_matcher_write_config();
757 }
758
759 static void delete_path(GSList ** p_filters, const gchar * path)
760 {
761         GSList * filters;
762         GSList * duplist;
763         gchar *suffix;
764         gint destlen;
765         gint prefixlen;
766         gint pathlen;
767         GSList * action_cur;
768         GSList * cur;
769         
770         filters = *p_filters;
771         pathlen = strlen(path);
772         duplist = g_slist_copy(filters);
773         for (cur = duplist ; cur != NULL; cur = g_slist_next(cur)) {
774                 FilteringProp *filtering = (FilteringProp *) cur->data;
775                 
776                 for(action_cur = filtering->action_list ; action_cur != NULL ;
777                     action_cur = action_cur->next) {
778                 
779                         FilteringAction *action;
780                         
781                         action = action_cur->data;
782                         
783                         if (action->type == MATCHACTION_SET_TAG ||
784                             action->type == MATCHACTION_UNSET_TAG)
785                                 continue;
786                         if (!action->destination) 
787                                 continue;
788                         
789                         destlen = strlen(action->destination);
790                         
791                         if (destlen > pathlen) {
792                                 prefixlen = destlen - pathlen;
793                                 suffix = action->destination + prefixlen;
794                                 
795                                 if (suffix && !strncmp(path, suffix, pathlen)) {
796                                         filteringprop_free(filtering);
797                                         filters = g_slist_remove(filters, filtering);
798                                 }
799                         } else if (strcmp(action->destination, path) == 0) {
800                                 filteringprop_free(filtering);
801                                 filters = g_slist_remove(filters, filtering);
802                         }
803                 }
804         }                
805         g_slist_free(duplist);
806         
807         * p_filters = filters;
808 }
809
810 static gboolean prefs_filtering_delete_path_func(GNode *node, gpointer data)
811 {
812         const gchar *path = data;
813         FolderItem *item;
814         GSList ** p_filters;
815         
816         cm_return_val_if_fail(path != NULL, FALSE);
817         cm_return_val_if_fail(node != NULL, FALSE);
818
819         item = node->data;
820         if (!item || !item->prefs)
821                 return FALSE;
822         p_filters = &item->prefs->processing;
823         
824         delete_path(p_filters, path);
825
826         return FALSE;
827 }
828
829 static void prefs_filtering_clear_list(GtkListStore *list_store)
830 {
831         gtk_list_store_clear(list_store);
832
833         /* add the place holder (New) at row 0 */
834         prefs_filtering_list_view_insert_rule(list_store, -1, 
835                                               FALSE,
836                                               _("(New)"),
837                                                   0,
838                                               _("(New)"),
839                                               _("(New)"),
840                                               FALSE);
841 }
842
843 static void prefs_filtering_set_dialog(const gchar *header, const gchar *key)
844 {
845         GtkTreeView *list_view = GTK_TREE_VIEW(filtering.cond_list_view);
846         GSList *cur;
847         GSList * prefs_filtering;
848         gchar *cond_str;
849         GtkListStore *list_store;
850         
851         list_store = GTK_LIST_STORE(gtk_tree_view_get_model(list_view));
852         prefs_filtering_clear_list(list_store);
853
854         prefs_filtering = *p_processing_list;
855
856         for(cur = prefs_filtering ; cur != NULL ; cur = g_slist_next(cur)) {
857                 FilteringProp * prop = (FilteringProp *) cur->data;
858                 gchar *account_name = NULL;
859
860                 if (prop->account_id > 0) {
861                         PrefsAccount *ac_prefs = account_find_from_id(prop->account_id);
862
863                         if (ac_prefs)
864                                 account_name = ac_prefs->account_name;
865                 }
866                 if (account_name == NULL)
867                         account_name = (gchar *)C_("Filtering Account Menu", "All");
868
869                 cond_str = filteringprop_to_string(prop);
870                 subst_char(cond_str, '\t', ':');
871
872                 prefs_filtering_list_view_insert_rule(list_store, -1, 
873                                                       prop->enabled,
874                                                       prop->name,
875                                                           prop->account_id,
876                                                           account_name,
877                                                       cond_str, TRUE);
878                 
879                 g_free(cond_str);
880         }
881
882         prefs_filtering_reset_dialog();
883
884         if (header && key) {
885                 gchar * quoted_key;
886                 gchar *match_str;
887
888                 quoted_key = matcher_quote_str(key);
889                 
890                 match_str = g_strconcat(header, " ", get_matchparser_tab_str(MATCHTYPE_MATCHCASE),
891                                         " \"", quoted_key, "\"", NULL);
892                 g_free(quoted_key);
893                 
894                 gtk_entry_set_text(GTK_ENTRY(filtering.cond_entry), match_str);
895                 g_free(match_str);
896         }
897 }
898
899 static void prefs_filtering_reset_dialog(void)
900 {
901         gtk_entry_set_text(GTK_ENTRY(filtering.name_entry), "");
902         combobox_select_by_data(GTK_COMBO_BOX(filtering.account_combobox), 0);
903         gtk_entry_set_text(GTK_ENTRY(filtering.cond_entry), "");
904         gtk_entry_set_text(GTK_ENTRY(filtering.action_entry), "");
905 }
906
907 static gboolean prefs_filtering_search_func_cb (GtkTreeModel *model, gint column, const gchar *key, 
908                                                 GtkTreeIter *iter, gpointer search_data) 
909 {
910         gchar *store_string;
911         gboolean retval;
912         GtkTreePath *path;
913
914         gtk_tree_model_get (model, iter, column, &store_string, -1);
915
916         if (!store_string || !key) return FALSE;
917
918
919         retval = (g_ascii_strncasecmp (key, store_string, strlen(key)) != 0);
920
921         g_free(store_string);
922         debug_print("selecting row\n");
923         path = gtk_tree_model_get_path(model, iter);
924         prefs_filtering_select_row(GTK_TREE_VIEW(filtering.cond_list_view), path);
925         gtk_tree_path_free(path);
926
927         return retval;
928 }
929
930 static void prefs_filtering_set_list(void)
931 {
932         gint row = 1;
933         FilteringProp *prop;
934         GSList * cur;
935         gchar * filtering_str;
936         GSList * prefs_filtering;
937
938         prefs_filtering = *p_processing_list;
939         for (cur = prefs_filtering ; cur != NULL ; cur = g_slist_next(cur))
940                 filteringprop_free((FilteringProp *) cur->data);
941         g_slist_free(prefs_filtering);
942         prefs_filtering = NULL;
943         
944
945         while (NULL != (filtering_str = prefs_filtering_list_view_get_rule
946                                                 (filtering.cond_list_view, row))) {
947                 /* FIXME: this strcmp() is bogus: "(New)" should never
948                  * be inserted in the storage */
949                 if (strcmp(filtering_str, _("(New)")) != 0) {
950                         gboolean enabled;
951                         gchar *name;
952                         gint account_id = 0;
953
954                         prefs_filtering_list_view_get_rule_info(
955                                         filtering.cond_list_view, row,
956                                         &enabled, &name, &account_id);
957                         prop = matcher_parser_get_filtering(filtering_str);
958                         g_free(filtering_str);
959                         if (prop) {
960                                 prop->enabled = enabled;
961                                 prop->name = name;
962                                 prop->account_id = account_id;
963                                 prefs_filtering = 
964                                         g_slist_append(prefs_filtering, prop);
965                         }
966                 }
967                 
968                 row++;
969         }                               
970         
971         *p_processing_list = prefs_filtering;
972 }
973
974 static gint prefs_filtering_list_view_set_row(gint row, FilteringProp * prop)
975 {
976         GtkTreeView *list_view = GTK_TREE_VIEW(filtering.cond_list_view);
977         gchar *str = NULL;
978         GtkListStore *list_store;
979         gchar *name = NULL;
980         gint account_id = 0;
981         gchar *account_name = (gchar *)C_("Filtering Account Menu", "All");
982         gboolean enabled = TRUE;
983
984         if (prop)
985                 str = filteringprop_to_string(prop);
986         if (str == NULL)
987                 return -1;
988
989         if (prop) {
990                 if (prop->name)
991                         name = prop->name;
992                 account_id = prop->account_id;
993                 if (account_id > 0)
994                         account_name = account_find_from_id(account_id)->account_name;
995                 enabled = prop->enabled;
996         }
997
998         list_store = GTK_LIST_STORE(gtk_tree_view_get_model(list_view));
999
1000         row = prefs_filtering_list_view_insert_rule(list_store, row,
1001                                                     enabled,
1002                                                     name,
1003                                                     account_id,
1004                                                         account_name,
1005                                                     str,
1006                                                     prop != NULL);
1007
1008         g_free(str);
1009
1010         return row;
1011 }
1012
1013 static void prefs_filtering_condition_define_done(MatcherList * matchers)
1014 {
1015         gchar * str;
1016
1017         if (matchers == NULL)
1018                 return;
1019
1020         str = matcherlist_to_string(matchers);
1021
1022         if (str != NULL) {
1023                 gtk_entry_set_text(GTK_ENTRY(filtering.cond_entry), str);
1024                 g_free(str);
1025         }
1026 }
1027
1028 static void prefs_filtering_condition_define(gpointer action, gpointer data)
1029 {
1030         gchar * cond_str;
1031         MatcherList * matchers = NULL;
1032
1033         cond_str = gtk_editable_get_chars(GTK_EDITABLE(filtering.cond_entry), 0, -1);
1034
1035         if (*cond_str != '\0') {
1036                 matchers = matcher_parser_get_cond(cond_str, NULL);
1037                 if (matchers == NULL)
1038                         alertpanel_error(_("Condition string is not valid."));
1039         }
1040         
1041         g_free(cond_str);
1042
1043         prefs_matcher_open(matchers, prefs_filtering_condition_define_done);
1044
1045         if (matchers != NULL)
1046                 matcherlist_free(matchers);
1047 }
1048
1049 static void prefs_filtering_action_define_done(GSList * action_list)
1050 {
1051         gchar * str;
1052
1053         if (action_list == NULL)
1054                 return;
1055
1056         str = filteringaction_list_to_string(action_list);
1057
1058         if (str != NULL) {
1059                 gtk_entry_set_text(GTK_ENTRY(filtering.action_entry), str);
1060                 g_free(str);
1061         }
1062 }
1063
1064 static void prefs_filtering_action_define(gpointer action, gpointer data)
1065 {
1066         gchar * action_str;
1067         GSList * action_list = NULL;
1068
1069         action_str = gtk_editable_get_chars(GTK_EDITABLE(filtering.action_entry), 0, -1);
1070
1071         if (*action_str != '\0') {
1072                 action_list = matcher_parser_get_action_list(action_str);
1073                 if (action_list == NULL)
1074                         alertpanel_error(_("Action string is not valid."));
1075         }
1076         
1077         g_free(action_str);
1078
1079         prefs_filtering_action_open(action_list,
1080             prefs_filtering_action_define_done);
1081
1082         if (action_list != NULL) {
1083                 GSList * cur;
1084                 for(cur = action_list ; cur != NULL ; cur = cur->next) {
1085                         filteringaction_free(cur->data);
1086                 }
1087         }
1088 }
1089
1090
1091 /* register / substitute delete buttons */
1092
1093
1094 static FilteringProp * prefs_filtering_dialog_to_filtering(gboolean alert)
1095 {
1096         MatcherList * cond;
1097         gboolean enabled = TRUE;
1098         gchar * name = NULL;
1099         gint account_id = 0;
1100         gchar * cond_str = NULL;
1101         gchar * action_str = NULL;
1102         FilteringProp * prop = NULL;
1103         GSList * action_list;
1104
1105         name = gtk_editable_get_chars(GTK_EDITABLE(filtering.name_entry), 0, -1);
1106         
1107         account_id = combobox_get_active_data(GTK_COMBO_BOX(filtering.account_combobox));
1108
1109         cond_str = gtk_editable_get_chars(GTK_EDITABLE(filtering.cond_entry), 0, -1);
1110         if (*cond_str == '\0') {
1111                 if(alert == TRUE) alertpanel_error(_("Condition string is empty."));
1112                 goto fail;
1113         }
1114         
1115         action_str = gtk_editable_get_chars(GTK_EDITABLE(filtering.action_entry), 0, -1);
1116         if (*action_str == '\0') {
1117                 if(alert == TRUE) alertpanel_error(_("Action string is empty."));
1118                 goto fail;
1119         }
1120
1121         cond = matcher_parser_get_cond(cond_str, NULL);
1122
1123         if (cond == NULL) {
1124                 if(alert == TRUE) alertpanel_error(_("Condition string is not valid."));
1125                 goto fail;
1126         }
1127         
1128         action_list = matcher_parser_get_action_list(action_str);
1129         
1130
1131         if (action_list == NULL) {
1132                 if(alert == TRUE) alertpanel_error(_("Action string is not valid."));
1133                 goto fail;
1134         }
1135
1136         prop = filteringprop_new(enabled, name, account_id, cond, action_list);
1137
1138 fail:
1139         g_free(name);
1140         g_free(cond_str);
1141         g_free(action_str);
1142         return prop;
1143 }
1144
1145 static void prefs_filtering_register_cb(gpointer action, gpointer data)
1146 {
1147         FilteringProp *prop;
1148         
1149         prop = prefs_filtering_dialog_to_filtering(TRUE);
1150         if (prop == NULL)
1151                 return;
1152         prefs_filtering_list_view_set_row(-1, prop);
1153         
1154         filteringprop_free(prop);
1155
1156         prefs_filtering_reset_dialog();
1157         modified = TRUE;
1158 }
1159
1160 static void prefs_filtering_substitute_cb(gpointer action, gpointer data)
1161 {
1162         gint selected_row = gtkut_list_view_get_selected_row
1163                 (filtering.cond_list_view);
1164         FilteringProp *prop;
1165         gboolean enabled;
1166         gchar *name;
1167         gint account_id;
1168         
1169         if (selected_row <= 0)
1170                 return;
1171
1172         prop = prefs_filtering_dialog_to_filtering(TRUE);
1173
1174         if (prop == NULL) 
1175                 return;
1176
1177         /* prop->emabled is always TRUE here, re-use the value from the selected row 
1178            as we don't substitute this value from dialog */
1179         prefs_filtering_list_view_get_rule_info(
1180                         filtering.cond_list_view, selected_row,
1181                         &enabled, &name, &account_id);
1182         prop->enabled = enabled;
1183
1184         prefs_filtering_list_view_set_row(selected_row, prop);
1185
1186         filteringprop_free(prop);
1187
1188         prefs_filtering_row_selected(gtk_tree_view_get_selection(
1189                                 GTK_TREE_VIEW(filtering.cond_list_view)),
1190                                 GTK_TREE_VIEW(filtering.cond_list_view));
1191         modified = TRUE;
1192 }
1193
1194 static void prefs_filtering_delete_cb(gpointer action, gpointer data)
1195 {
1196         GtkTreeView *list_view = GTK_TREE_VIEW(filtering.cond_list_view);
1197         GtkTreeModel *model;
1198         GtkTreeIter iter;
1199         gint selected_row;
1200         
1201         selected_row = gtkut_list_view_get_selected_row(filtering.cond_list_view);
1202         if (selected_row <= 0) 
1203                 return; 
1204
1205         if (alertpanel(_("Delete rule"),
1206                        _("Do you really want to delete this rule?"),
1207                        GTK_STOCK_CANCEL, "+"GTK_STOCK_DELETE, NULL) == G_ALERTDEFAULT)
1208                 return;
1209
1210         model = gtk_tree_view_get_model(list_view);     
1211         if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, selected_row))
1212                 return;
1213
1214         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
1215
1216         prefs_filtering_reset_dialog();
1217         modified = TRUE;
1218 }
1219
1220 static void prefs_filtering_delete_all_cb(gpointer action, gpointer data)
1221 {
1222         GtkListStore *list_store;
1223         
1224         if (alertpanel(_("Delete all rules"),
1225                        _("Do you really want to delete all the rules?"),
1226                        GTK_STOCK_CANCEL, "+"GTK_STOCK_DELETE, NULL) == G_ALERTDEFAULT)
1227                 return;
1228
1229         list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(filtering.cond_list_view)));
1230         prefs_filtering_clear_list(list_store);
1231
1232         prefs_filtering_reset_dialog();
1233         modified = TRUE;
1234 }
1235
1236 static void prefs_filtering_clear_cb(gpointer action, gpointer data)
1237 {
1238         prefs_filtering_reset_dialog();
1239 }
1240
1241 static void prefs_filtering_duplicate_cb(gpointer action, gpointer data)
1242 {
1243         gint selected_row = gtkut_list_view_get_selected_row
1244                 (filtering.cond_list_view);     
1245         FilteringProp *prop;
1246         gboolean enabled;
1247         gchar *name;
1248         gint account_id;
1249
1250         if (selected_row <= 0) 
1251                 return; 
1252         
1253         prop = prefs_filtering_dialog_to_filtering(TRUE);
1254         if (prop == NULL)
1255                 return;
1256
1257         /* prop->emabled is always TRUE here, re-use the value from the selected row 
1258            as we don't substitute this value from dialog */
1259         prefs_filtering_list_view_get_rule_info(
1260                         filtering.cond_list_view, selected_row,
1261                         &enabled, &name, &account_id);
1262         prop->enabled = enabled;
1263
1264         prefs_filtering_list_view_set_row(-selected_row-2, prop);
1265         
1266         filteringprop_free(prop);
1267
1268         prefs_filtering_reset_dialog();
1269         modified = TRUE;
1270 }
1271
1272 static void prefs_filtering_top(gpointer action, gpointer data)
1273 {
1274         gint row;
1275         GtkTreeIter top, sel;
1276         GtkTreeModel *model;
1277
1278         row = gtkut_list_view_get_selected_row(filtering.cond_list_view);
1279         if (row <= 1) 
1280                 return;
1281
1282         model = gtk_tree_view_get_model(GTK_TREE_VIEW(filtering.cond_list_view));               
1283         
1284         if (!gtk_tree_model_iter_nth_child(model, &top, NULL, 0)
1285         ||  !gtk_tree_model_iter_nth_child(model, &sel, NULL, row))
1286                 return;
1287
1288         gtk_list_store_move_after(GTK_LIST_STORE(model), &sel, &top);
1289         gtkut_list_view_select_row(filtering.cond_list_view, 1);
1290         modified = TRUE;
1291 }
1292
1293 static void prefs_filtering_page_up(gpointer action, gpointer data)
1294 {
1295         gint row, target_row;
1296         GtkTreeIter selected, target;
1297         GtkTreeModel *model;
1298         GtkTreePath *path;
1299         GdkRectangle cell_rect, view_rect;
1300
1301         model = gtk_tree_view_get_model(GTK_TREE_VIEW(filtering.cond_list_view));       
1302         row = gtkut_list_view_get_selected_row(filtering.cond_list_view);
1303         if (row <= 1)
1304                 return;
1305
1306         if (!gtk_tree_model_iter_nth_child(model, &selected, NULL, row))
1307                 return;
1308
1309         /* compute number of rows per page (approximation) */
1310         path = gtk_tree_model_get_path(model, &selected);
1311         gtk_tree_view_get_cell_area(GTK_TREE_VIEW(filtering.cond_list_view), path, NULL, &cell_rect);
1312         gtk_tree_view_get_visible_rect(GTK_TREE_VIEW(filtering.cond_list_view), &view_rect);
1313         gtk_tree_path_free(path);
1314         target_row = row - (view_rect.height/cell_rect.height);
1315         if (target_row < 1)
1316                 target_row = 1;
1317
1318         if (!gtk_tree_model_iter_nth_child(model, &target, NULL, target_row))
1319                 return;
1320         gtk_list_store_move_before(GTK_LIST_STORE(model), &selected, &target);
1321         gtkut_list_view_select_row(filtering.cond_list_view, target_row);
1322         modified = TRUE;
1323 }
1324 static void prefs_filtering_up(gpointer action, gpointer data)
1325 {
1326         gint row;
1327         GtkTreeIter top, sel;
1328         GtkTreeModel *model;
1329
1330         row = gtkut_list_view_get_selected_row(filtering.cond_list_view);
1331         if (row <= 1) 
1332                 return;
1333                 
1334         model = gtk_tree_view_get_model(GTK_TREE_VIEW(filtering.cond_list_view));       
1335
1336         if (!gtk_tree_model_iter_nth_child(model, &top, NULL, row - 1)
1337         ||  !gtk_tree_model_iter_nth_child(model, &sel, NULL, row))
1338                 return;
1339
1340         gtk_list_store_swap(GTK_LIST_STORE(model), &top, &sel);
1341         gtkut_list_view_select_row(filtering.cond_list_view, row - 1);
1342         modified = TRUE;
1343 }
1344
1345 static void prefs_filtering_down(gpointer action, gpointer data)
1346 {
1347         gint row, n_rows;
1348         GtkTreeIter top, sel;
1349         GtkTreeModel *model;
1350
1351         model = gtk_tree_view_get_model(GTK_TREE_VIEW(filtering.cond_list_view));       
1352         n_rows = gtk_tree_model_iter_n_children(model, NULL);
1353         row = gtkut_list_view_get_selected_row(filtering.cond_list_view);
1354         if (row < 1 || row >= n_rows - 1)
1355                 return;
1356
1357         if (!gtk_tree_model_iter_nth_child(model, &top, NULL, row)
1358         ||  !gtk_tree_model_iter_nth_child(model, &sel, NULL, row + 1))
1359                 return;
1360                         
1361         gtk_list_store_swap(GTK_LIST_STORE(model), &top, &sel);
1362         gtkut_list_view_select_row(filtering.cond_list_view, row + 1);
1363         modified = TRUE;
1364 }
1365
1366 static void prefs_filtering_page_down(gpointer action, gpointer data)
1367 {
1368         gint row, target_row, n_rows;
1369         GtkTreeIter selected, target;
1370         GtkTreeModel *model;
1371         GtkTreePath *path;
1372         GdkRectangle cell_rect, view_rect;
1373
1374         model = gtk_tree_view_get_model(GTK_TREE_VIEW(filtering.cond_list_view));       
1375         n_rows = gtk_tree_model_iter_n_children(model, NULL);
1376         row = gtkut_list_view_get_selected_row(filtering.cond_list_view);
1377         if (row < 1 || row >= n_rows -1)
1378                 return;
1379
1380         if (!gtk_tree_model_iter_nth_child(model, &selected, NULL, row))
1381                 return;
1382
1383         /* compute number of rows per page (approximation) */
1384         path = gtk_tree_model_get_path(model, &selected);
1385         gtk_tree_view_get_cell_area(GTK_TREE_VIEW(filtering.cond_list_view), path, NULL, &cell_rect);
1386         gtk_tree_view_get_visible_rect(GTK_TREE_VIEW(filtering.cond_list_view), &view_rect);
1387         gtk_tree_path_free(path);
1388         target_row = row + (view_rect.height/cell_rect.height);
1389         if (target_row > n_rows-1)
1390                 target_row = n_rows-1;
1391
1392         if (!gtk_tree_model_iter_nth_child(model, &target, NULL, target_row))
1393                 return;
1394         gtk_list_store_move_after(GTK_LIST_STORE(model), &selected, &target);
1395         gtkut_list_view_select_row(filtering.cond_list_view, target_row);
1396         modified = TRUE;
1397 }
1398
1399 static void prefs_filtering_bottom(gpointer action, gpointer data)
1400 {
1401         gint row, n_rows;
1402         GtkTreeIter top, sel;
1403         GtkTreeModel *model;
1404
1405         model = gtk_tree_view_get_model(GTK_TREE_VIEW(filtering.cond_list_view));       
1406         n_rows = gtk_tree_model_iter_n_children(model, NULL);
1407         row = gtkut_list_view_get_selected_row(filtering.cond_list_view);
1408         if (row < 1 || row >= n_rows - 1)
1409                 return;
1410
1411         if (!gtk_tree_model_iter_nth_child(model, &top, NULL, row)
1412         ||  !gtk_tree_model_iter_nth_child(model, &sel, NULL, n_rows - 1))
1413                 return;
1414
1415         gtk_list_store_move_after(GTK_LIST_STORE(model), &top, &sel);           
1416         gtkut_list_view_select_row(filtering.cond_list_view, n_rows - 1);
1417         modified = TRUE;
1418 }
1419
1420 static void prefs_filtering_select_set(FilteringProp *prop)
1421 {
1422         gchar *matcher_str;
1423         gchar *action_str;
1424
1425         prefs_filtering_reset_dialog();
1426
1427         matcher_str = matcherlist_to_string(prop->matchers);
1428         if (matcher_str == NULL) {
1429                 return;
1430         }
1431         
1432         if (prop->name != NULL)
1433                 gtk_entry_set_text(GTK_ENTRY(filtering.name_entry), prop->name);
1434
1435         combobox_select_by_data(GTK_COMBO_BOX(filtering.account_combobox), prop->account_id);
1436                 
1437         gtk_entry_set_text(GTK_ENTRY(filtering.cond_entry), matcher_str);
1438
1439         action_str = filteringaction_list_to_string(prop->action_list);
1440         if (action_str != NULL)
1441                 gtk_entry_set_text(GTK_ENTRY(filtering.action_entry), action_str);
1442
1443         g_free(action_str);
1444         g_free(matcher_str);
1445 }
1446
1447 static gint prefs_filtering_deleted(GtkWidget *widget, GdkEventAny *event,
1448                                  gpointer data)
1449 {
1450         prefs_filtering_cancel(NULL, NULL);
1451         return TRUE;
1452 }
1453
1454 static gboolean prefs_filtering_key_pressed(GtkWidget *widget, GdkEventKey *event,
1455                                      gpointer data)
1456 {
1457         if (event && event->keyval == GDK_KEY_Escape) {
1458                 prefs_filtering_cancel(NULL, NULL);
1459                 return TRUE;                    
1460         }
1461         return FALSE;
1462 }
1463
1464 static gboolean prefs_filtering_check_mod(gboolean check_changed_list)
1465 {
1466         FilteringProp * prop;
1467         gchar * str;
1468         gchar * filtering_str;
1469         gint row = 1;
1470         AlertValue val;
1471         
1472         prop = prefs_filtering_dialog_to_filtering(FALSE);
1473         
1474         if (check_changed_list) {
1475                 if (modified && alertpanel(_("Filtering rules not saved"),
1476                                          _("The list of filtering rules have been modified. Close anyway?"),
1477                                          GTK_STOCK_CLOSE, g_strconcat("+", _("_Continue editing"), NULL),
1478                                          NULL) != G_ALERTDEFAULT) {
1479                         return TRUE;
1480                 }
1481         }
1482         
1483         /* check if a rule is being edited */
1484         if (prop != NULL) {
1485                 str = filteringprop_to_string(prop);
1486
1487                 while (NULL != (filtering_str = (prefs_filtering_list_view_get_rule
1488                                                         (filtering.cond_list_view,
1489                                                          row)))) {
1490                         if (strcmp(filtering_str, str) == 0)
1491                                 break;
1492                         row++;
1493                         g_free(filtering_str);
1494                 }       
1495
1496                 if (!filtering_str) {
1497                         val = alertpanel(_("Entry not saved"),
1498                                  _("The entry was not saved. Close anyway?"),
1499                                  GTK_STOCK_CLOSE, g_strconcat("+", _("_Continue editing"),NULL), NULL);
1500                         if (G_ALERTDEFAULT != val) {
1501                                 g_free(filtering_str);
1502                                 g_free(str); /* fixed two leaks: huzzah! */
1503                                 filteringprop_free(prop);
1504                                 return TRUE;
1505                         }
1506                 }               
1507
1508                 g_free(filtering_str);
1509                 g_free(str);
1510                 filteringprop_free(prop); /* fixed a leak: huzzah! */
1511         } else {
1512                 gchar *name, *condition, *action;
1513                 name = gtk_editable_get_chars(GTK_EDITABLE(filtering.name_entry), 0, -1);
1514                 condition = gtk_editable_get_chars(GTK_EDITABLE(filtering.cond_entry), 0, -1);
1515                 action = gtk_editable_get_chars(GTK_EDITABLE(filtering.action_entry), 0, -1);
1516                 if (strlen(name) || 
1517                     strlen(condition) || 
1518                     strlen(action)) {
1519                         val = alertpanel(_("Entry not saved"),
1520                                  _("The entry was not saved. Close anyway?"),
1521                                  GTK_STOCK_CLOSE, g_strconcat("+", _("_Continue editing"), NULL), NULL);
1522                         if (G_ALERTDEFAULT != val) {
1523                                 g_free(name);
1524                                 g_free(condition);
1525                                 g_free(action);
1526                                 return TRUE;
1527                         }
1528                 }
1529                 g_free(name);
1530                 g_free(condition);
1531                 g_free(action);
1532         }
1533         return FALSE;
1534 }
1535
1536 static void prefs_filtering_ok(gpointer action, gpointer data)
1537 {
1538         if (prefs_filtering_check_mod(FALSE))
1539                 return;
1540         modified = FALSE;
1541         prefs_filtering_set_list();
1542         prefs_matcher_write_config();
1543         prefs_filtering_close();
1544 }
1545
1546 static void prefs_filtering_cancel(gpointer action, gpointer data)
1547 {
1548         if (prefs_filtering_check_mod(TRUE))
1549                 return;
1550         modified = FALSE;
1551         prefs_matcher_read_config();
1552         prefs_filtering_close();
1553 }
1554
1555 static GtkListStore* prefs_filtering_create_data_store(void)
1556 {
1557         return gtk_list_store_new(N_PREFS_FILTERING_COLUMNS,
1558                                   G_TYPE_BOOLEAN,
1559                                   G_TYPE_STRING,
1560                                   G_TYPE_INT,
1561                                   G_TYPE_STRING,
1562                                   G_TYPE_STRING,
1563                                   G_TYPE_BOOLEAN,
1564                                  -1);
1565 }
1566
1567 /*!
1568  *\brief        Insert filtering rule into store. Note that we access the
1569  *              tree view / store by index, which is a bit suboptimal, but
1570  *              at least it made GTK 2 porting easier.
1571  *
1572  *\param        list_store Store to operate on
1573  *\param        row -1 to add a new rule to store,
1574                         row >=0 to change an existing row
1575                         row <-1 insert a new row after (-row-2)
1576  *\param        enabled TRUE if rule is enabled
1577  *\param        name The Name of rule
1578  *\param        account_id The account ID
1579  *\param        account_name The account name or All or (New)
1580  *\param        rule String representation of rule
1581  *\param        prop TRUE if valid filtering rule; if FALSE it's the first
1582  *              entry in the store ("(New)").
1583  *
1584  *\return       int Row of inserted / changed rule.
1585  */
1586 static gint prefs_filtering_list_view_insert_rule(GtkListStore *list_store,
1587                                                   gint row,
1588                                                   gboolean enabled,
1589                                                   const gchar *name,
1590                                                   gint account_id,
1591                                                   const gchar *account_name,
1592                                                   const gchar *rule,
1593                                                   gboolean prop) 
1594 {
1595         GtkTreeIter iter;
1596         GtkTreeIter sibling;
1597
1598         /* check if valid row at all */
1599         if (row >= 0) {
1600                 if (!gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(list_store),
1601                                                    &iter, NULL, row))
1602                         row = -1;
1603         } else if (row < -1) {
1604                 if (!gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(list_store),
1605                                                    &sibling, NULL, -row-2))
1606                         row = -1;                                                  
1607         }
1608
1609         if (row == -1 ) {
1610                 /* append new */
1611                 gtk_list_store_append(list_store, &iter);
1612                 gtk_list_store_set(list_store, &iter, 
1613                                    PREFS_FILTERING_ENABLED, enabled,
1614                                    PREFS_FILTERING_NAME, name,
1615                                    PREFS_FILTERING_ACCOUNT_ID, account_id,
1616                                    PREFS_FILTERING_ACCOUNT_NAME, account_name,
1617                                    PREFS_FILTERING_RULE, rule,
1618                                    PREFS_FILTERING_PROP, prop,
1619                                    -1);
1620                 return gtk_tree_model_iter_n_children(GTK_TREE_MODEL(list_store),
1621                                                       NULL) - 1;
1622         } else if (row < -1) {
1623                 /* duplicate */
1624                 gtk_list_store_insert_after(list_store, &iter, &sibling);
1625                 gtk_list_store_set(list_store, &iter, 
1626                                    PREFS_FILTERING_ENABLED, enabled,
1627                                    PREFS_FILTERING_NAME, name,
1628                                    PREFS_FILTERING_ACCOUNT_ID, account_id,
1629                                    PREFS_FILTERING_ACCOUNT_NAME, account_name,
1630                                    PREFS_FILTERING_RULE, rule,
1631                                    PREFS_FILTERING_PROP, prop,
1632                                    -1);
1633                 return gtk_tree_model_iter_n_children(GTK_TREE_MODEL(list_store),
1634                                                       NULL) - 1;
1635         } else {
1636                 /* change existing */
1637                 gtk_list_store_set(list_store, &iter, 
1638                                    PREFS_FILTERING_ENABLED, enabled,
1639                                    PREFS_FILTERING_NAME, name,
1640                                    PREFS_FILTERING_ACCOUNT_ID, account_id,
1641                                    PREFS_FILTERING_ACCOUNT_NAME, account_name,
1642                                    PREFS_FILTERING_RULE, rule,
1643                                    -1);
1644                 return row;                                
1645         }
1646 }
1647
1648 /*!
1649  *\return       gchar * Rule at specified row - should be freed.
1650  */
1651 static gchar *prefs_filtering_list_view_get_rule(GtkWidget *list, gint row)
1652 {       
1653         GtkTreeView *list_view = GTK_TREE_VIEW(list);
1654         GtkTreeModel *model = gtk_tree_view_get_model(list_view);
1655         GtkTreeIter iter;
1656         gchar *result;
1657
1658         if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, row))
1659                 return NULL;
1660         
1661         gtk_tree_model_get(model, &iter, 
1662                            PREFS_FILTERING_RULE, &result,
1663                            -1);
1664         
1665         return result;
1666 }
1667
1668 static void prefs_filtering_list_view_get_rule_info(GtkWidget *list, gint row,
1669                                 gboolean *enabled, gchar **name, gint *account_id)
1670 {       
1671         GtkTreeView *list_view = GTK_TREE_VIEW(list);
1672         GtkTreeModel *model = gtk_tree_view_get_model(list_view);
1673         GtkTreeIter iter;
1674
1675         *enabled = TRUE;
1676         *name = NULL;
1677
1678         if (gtk_tree_model_iter_nth_child(model, &iter, NULL, row)) {
1679                 gtk_tree_model_get(model, &iter, 
1680                                    PREFS_FILTERING_ENABLED, enabled,
1681                                    PREFS_FILTERING_NAME, name,
1682                                    PREFS_FILTERING_ACCOUNT_ID, account_id,
1683                                    -1);
1684         }
1685 }
1686
1687 static GtkActionGroup *prefs_filtering_popup_action = NULL;
1688 static GtkWidget *prefs_filtering_popup_menu = NULL;
1689
1690 static GtkActionEntry prefs_filtering_popup_entries[] =
1691 {
1692         {"PrefsFilteringPopup",                 NULL, "PrefsFilteringPopup" },
1693         {"PrefsFilteringPopup/Delete",          NULL, N_("_Delete"), NULL, NULL, G_CALLBACK(prefs_filtering_delete_cb) },
1694         {"PrefsFilteringPopup/DeleteAll",       NULL, N_("Delete _all"), NULL, NULL, G_CALLBACK(prefs_filtering_delete_all_cb) },
1695         {"PrefsFilteringPopup/Duplicate",       NULL, N_("D_uplicate"), NULL, NULL, G_CALLBACK(prefs_filtering_duplicate_cb) },
1696 #ifdef GENERIC_UMPC
1697         {"PrefsFilteringPopup/---",             NULL, "---", NULL, NULL, NULL },
1698         {"PrefsFilteringPopup/PageUp",          NULL, N_("Move one page up"), NULL, NULL, G_CALLBACK(prefs_filtering_page_up) },
1699         {"PrefsFilteringPopup/PageDown",        NULL, N_("Move one page down"), NULL, NULL, G_CALLBACK(prefs_filtering_page_down) },
1700 #endif
1701 };
1702
1703 static void prefs_filtering_row_selected(GtkTreeSelection *selection,
1704                                          GtkTreeView *list_view)
1705 {
1706         GtkTreePath *path;
1707         GtkTreeIter iter;
1708         GtkTreeModel *model;
1709         
1710         if (!gtk_tree_selection_get_selected(selection, &model, &iter))
1711                 return;
1712         
1713         path = gtk_tree_model_get_path(model, &iter);
1714         prefs_filtering_select_row(list_view, path);
1715         gtk_tree_path_free(path);
1716 }
1717
1718 static gint prefs_filtering_list_btn_pressed(GtkWidget *widget, GdkEventButton *event,
1719                                     GtkTreeView *list_view)
1720 {
1721         if (event) {
1722                 /* left- or right-button click */
1723                 if (event->button == 1 || event->button == 3) {
1724                         GtkTreePath *path = NULL;
1725
1726                         if (gtk_tree_view_get_path_at_pos( list_view, event->x, event->y,
1727                                                                 &path, NULL, NULL, NULL)) {
1728                                 prefs_filtering_select_row(list_view, path);
1729                         }
1730                         if (path)
1731                                 gtk_tree_path_free(path);
1732                 }
1733
1734                 /* right-button click */
1735                 if (event->button == 3) {
1736                         GtkTreeModel *model = gtk_tree_view_get_model(list_view);
1737                         GtkTreeIter iter;
1738                         gboolean non_empty;
1739                         gint row;
1740
1741                         if (!prefs_filtering_popup_menu) {
1742                                 prefs_filtering_popup_action = cm_menu_create_action_group("PrefsFilteringPopup", prefs_filtering_popup_entries,
1743                                         G_N_ELEMENTS(prefs_filtering_popup_entries), (gpointer)list_view);
1744                                 MENUITEM_ADDUI("/Menus", "PrefsFilteringPopup", "PrefsFilteringPopup", GTK_UI_MANAGER_MENU)
1745                                 MENUITEM_ADDUI("/Menus/PrefsFilteringPopup", "Delete", "PrefsFilteringPopup/Delete", GTK_UI_MANAGER_MENUITEM)
1746                                 MENUITEM_ADDUI("/Menus/PrefsFilteringPopup", "DeleteAll", "PrefsFilteringPopup/DeleteAll", GTK_UI_MANAGER_MENUITEM)
1747                                 MENUITEM_ADDUI("/Menus/PrefsFilteringPopup", "Duplicate", "PrefsFilteringPopup/Duplicate", GTK_UI_MANAGER_MENUITEM)
1748 #ifdef GENERIC_UMPC
1749                                 MENUITEM_ADDUI("/Menus/PrefsFilteringPopup", "Separator1", "PrefsFilteringPopup/---", GTK_UI_MANAGER_SEPARATOR)
1750                                 MENUITEM_ADDUI("/Menus/PrefsFilteringPopup", "PageUp", "PrefsFilteringPopup/PageUp", GTK_UI_MANAGER_MENUITEM)
1751                                 MENUITEM_ADDUI("/Menus/PrefsFilteringPopup", "PageDown", "PrefsFilteringPopup/PageDown", GTK_UI_MANAGER_MENUITEM)
1752 #endif
1753                                 prefs_filtering_popup_menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1754                                         gtk_ui_manager_get_widget(gtkut_ui_manager(), "/Menus/PrefsFilteringPopup")) );
1755                         }
1756
1757                         /* grey out some popup menu items if there is no selected row */
1758                         row = gtkut_list_view_get_selected_row(GTK_WIDGET(list_view));
1759                         cm_menu_set_sensitive("PrefsFilteringPopup/Delete", (row > 0));
1760                         cm_menu_set_sensitive("PrefsFilteringPopup/Duplicate", (row > 0));
1761
1762                         /* grey out seom popup menu items if there is no row
1763                            (not counting the (New) one at row 0) */
1764                         non_empty = gtk_tree_model_get_iter_first(model, &iter);
1765                         if (non_empty)
1766                                 non_empty = gtk_tree_model_iter_next(model, &iter);
1767                         cm_menu_set_sensitive("PrefsFilteringPopup/DeleteAll", non_empty);
1768
1769                         gtk_menu_popup(GTK_MENU(prefs_filtering_popup_menu), 
1770                                    NULL, NULL, NULL, NULL, 
1771                                    event->button, event->time);
1772                 }
1773         }
1774         return FALSE;
1775 }
1776
1777 static gboolean prefs_filtering_list_popup_menu(GtkWidget *widget, gpointer data)
1778 {
1779         GtkTreeView *list_view = (GtkTreeView *)data;
1780         GdkEventButton event;
1781         
1782         event.button = 3;
1783         event.time = gtk_get_current_event_time();
1784         
1785         prefs_filtering_list_btn_pressed(NULL, &event, list_view);
1786
1787         return TRUE;
1788 }
1789
1790 /*!
1791  *\brief        Create list view for filtering
1792  */
1793 static GtkWidget *prefs_filtering_list_view_create(void)
1794 {
1795         GtkTreeView *list_view;
1796         GtkTreeSelection *selector;
1797
1798         list_view = GTK_TREE_VIEW(gtk_tree_view_new_with_model(GTK_TREE_MODEL
1799                 (prefs_filtering_create_data_store())));
1800 #ifdef GENERIC_UMPC
1801         g_object_set(list_view, "allow-checkbox-mode", FALSE, NULL);
1802 #endif
1803
1804         g_signal_connect(G_OBJECT(list_view), "popup-menu",
1805                          G_CALLBACK(prefs_filtering_list_popup_menu), list_view);
1806         g_signal_connect(G_OBJECT(list_view), "button-press-event",
1807                         G_CALLBACK(prefs_filtering_list_btn_pressed), list_view);
1808         
1809         gtk_tree_view_set_rules_hint(list_view, prefs_common.use_stripes_everywhere);
1810         gtk_tree_view_set_reorderable(list_view, TRUE);
1811         
1812         selector = gtk_tree_view_get_selection(list_view);
1813         gtk_tree_selection_set_mode(selector, GTK_SELECTION_BROWSE);
1814         g_signal_connect(G_OBJECT(selector), "changed",
1815                          G_CALLBACK(prefs_filtering_row_selected), list_view);
1816
1817         /* create the columns */
1818         prefs_filtering_create_list_view_columns(GTK_WIDGET(list_view));
1819
1820         return GTK_WIDGET(list_view);
1821 }
1822
1823 static void prefs_filtering_enable_toggled(GtkCellRendererToggle *widget,
1824                 gchar *path,
1825                 GtkWidget *list_view)
1826 {
1827         GtkTreeIter iter;
1828         GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list_view));
1829         gboolean enabled = TRUE;
1830
1831         if (!gtk_tree_model_get_iter_from_string(model, &iter, path))
1832                 return;
1833
1834         gtk_tree_model_get(model, &iter,
1835                            PREFS_FILTERING_ENABLED, &enabled,
1836                            -1);
1837
1838         gtk_list_store_set(GTK_LIST_STORE(model), &iter,
1839                            PREFS_FILTERING_ENABLED, !enabled,
1840                            -1);
1841 }
1842
1843 static void prefs_filtering_create_list_view_columns(GtkWidget *list_view)
1844 {
1845         GtkTreeViewColumn *column;
1846         GtkCellRenderer *renderer;
1847
1848         renderer = gtk_cell_renderer_toggle_new();
1849         g_object_set(renderer,
1850                      "radio", FALSE,
1851                      "activatable", TRUE,
1852                      NULL);
1853         column = gtk_tree_view_column_new_with_attributes
1854                 (_("Enable"), /* FIXME : Enable, Enabled, or 'E' ? */
1855                  renderer,
1856                  "active", PREFS_FILTERING_ENABLED,
1857                  NULL);
1858         gtk_tree_view_column_set_alignment (column, 0.5);
1859         gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), column);
1860         g_signal_connect(G_OBJECT(renderer), "toggled",
1861                          G_CALLBACK(prefs_filtering_enable_toggled),
1862                          list_view);
1863
1864         renderer = gtk_cell_renderer_text_new();
1865         column = gtk_tree_view_column_new_with_attributes
1866                 (_("Name"),
1867                  renderer,
1868                  "text", PREFS_FILTERING_NAME,
1869                  NULL);
1870         gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), column);
1871         gtk_tree_view_column_set_resizable(column, TRUE);
1872
1873         renderer = gtk_cell_renderer_text_new();
1874         column = gtk_tree_view_column_new_with_attributes
1875                 (_("Account"),
1876                  renderer,
1877                  "text", PREFS_FILTERING_ACCOUNT_NAME,
1878                  NULL);
1879         gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), column);
1880         gtk_tree_view_column_set_resizable(column, TRUE);
1881
1882         filtering.account_name_column = column;
1883                 
1884         renderer = gtk_cell_renderer_text_new();
1885         column = gtk_tree_view_column_new_with_attributes
1886                 (_("Rule"),
1887                  renderer,
1888                  "text", PREFS_FILTERING_RULE,
1889                  NULL);
1890
1891         gtk_tree_view_set_search_column(GTK_TREE_VIEW(list_view), PREFS_FILTERING_NAME);
1892         gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(list_view), prefs_filtering_search_func_cb , NULL, NULL);
1893         
1894         gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), column);          
1895 }
1896
1897 /*!
1898  *\brief        Triggered when a row has to be selected
1899  */
1900 static void prefs_filtering_select_row(GtkTreeView *list_view, GtkTreePath *path)
1901 {
1902         GtkTreeModel *model = gtk_tree_view_get_model(list_view);
1903
1904         if (path && model) {
1905                 GtkTreeSelection *selection;
1906                 gboolean has_prop = FALSE;
1907                 GtkTreeIter iter;
1908
1909                 /* select row */
1910                 selection = gtk_tree_view_get_selection(list_view);
1911                 gtk_tree_selection_select_path(selection, path);
1912
1913                 /* update dialog from selection */
1914                 gtk_tree_model_get_iter(model, &iter, path);
1915                 gtk_tree_model_get(model, &iter,
1916                                    PREFS_FILTERING_PROP, &has_prop,
1917                                    -1);
1918
1919                 if (has_prop) {
1920                         FilteringProp *prop;
1921                         gchar *filtering_str = NULL;
1922                         gchar *name = NULL;
1923                         gint account_id = 0;
1924
1925                         gtk_tree_model_get(model, &iter,
1926                                            PREFS_FILTERING_RULE, &filtering_str,
1927                                            -1);
1928                         gtk_tree_model_get(model, &iter,
1929                                            PREFS_FILTERING_NAME, &name,
1930                                            -1);
1931                         gtk_tree_model_get(model, &iter,
1932                                            PREFS_FILTERING_ACCOUNT_ID, &account_id,
1933                                            -1);
1934
1935                         prop = matcher_parser_get_filtering(filtering_str);
1936                         if (prop) {
1937                                 prop->name = g_strdup(name);
1938                                 prop->account_id = account_id;
1939                                 prefs_filtering_select_set(prop);
1940                                 filteringprop_free(prop);
1941                         }                               
1942                         g_free(name);
1943                         g_free(filtering_str);
1944                 } else
1945                         prefs_filtering_reset_dialog();
1946         }
1947 }