2008-08-27 [colin] 3.5.0cvs75
[claws.git] / src / prefs_actions.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2007 Hiroyuki Yamamoto & 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 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <gtk/gtk.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 "prefs_gtk.h"
36 #include "inc.h"
37 #include "utils.h"
38 #include "gtkutils.h"
39 #include "manage_window.h"
40 #include "mainwindow.h"
41 #include "prefs_common.h"
42 #include "alertpanel.h"
43 #include "prefs_actions.h"
44 #include "action.h"
45 #include "description_window.h"
46 #include "gtkutils.h"
47 #include "manual.h"
48 #include "menu.h"
49 #include "filtering.h"
50 #include "prefs_filtering_action.h"
51 #include "matcher_parser.h"
52
53 enum {
54         PREFS_ACTIONS_STRING,   /*!< string pointer managed by list store, 
55                                  *   and never touched or retrieved by 
56                                  *   us */ 
57         PREFS_ACTIONS_DATA,     /*!< pointer to string that is not managed by 
58                                  *   the list store, and which is retrieved
59                                  *   and touched by us */
60         PREFS_ACTIONS_VALID,    /*!< contains a valid action, otherwise "(New)" */
61         N_PREFS_ACTIONS_COLUMNS
62 };
63
64 static struct Actions
65 {
66         GtkWidget *window;
67
68         GtkWidget *ok_btn;
69         GtkWidget *filter_btn;
70         GtkWidget *name_entry;
71         GtkWidget *cmd_entry;
72         GtkWidget *info_btn;
73         GtkWidget *shell_radiobtn;
74         GtkWidget *filter_radiobtn;
75         
76         GtkWidget *actions_list_view;
77 } actions;
78
79 static int modified = FALSE;
80 static int modified_list = FALSE;
81
82 /* widget creating functions */
83 static void prefs_actions_create        (MainWindow *mainwin);
84 static void prefs_actions_set_dialog    (void);
85 static gint prefs_actions_clist_set_row (gint row);
86
87 /* callback functions */
88 static void prefs_actions_info_cb       (GtkWidget      *w,
89                                          GtkWidget      *window);
90 static void prefs_actions_register_cb   (GtkWidget      *w,
91                                          gpointer        data);
92 static void prefs_actions_substitute_cb (GtkWidget      *w,
93                                          gpointer        data);
94 static void prefs_actions_delete_cb     (gpointer gtk_action, gpointer data);
95 static void prefs_actions_delete_all_cb (gpointer gtk_action, gpointer data);
96 static void prefs_actions_clear_cb      (gpointer gtk_action, gpointer data);
97 static void prefs_actions_duplicate_cb  (gpointer gtk_action, gpointer data);
98 static void prefs_actions_up            (GtkWidget      *w,
99                                          gpointer        data);
100 static void prefs_actions_down          (GtkWidget      *w,
101                                          gpointer        data);
102 static gint prefs_actions_deleted       (GtkWidget      *widget,
103                                          GdkEventAny    *event,
104                                          gpointer       *data);
105 static gboolean prefs_actions_key_pressed(GtkWidget     *widget,
106                                           GdkEventKey   *event,
107                                           gpointer       data);
108 static void prefs_actions_cancel        (GtkWidget      *w,
109                                          gpointer        data);
110 static void prefs_actions_ok            (GtkWidget      *w,
111                                          gpointer        data);
112
113 static GtkListStore* prefs_actions_create_data_store    (void);
114
115 static void prefs_actions_list_view_insert_action       (GtkWidget *list_view,
116                                                          gint row,
117                                                          gchar *action,
118                                                          gboolean is_valid);
119 static GtkWidget *prefs_actions_list_view_create        (void);
120 static void prefs_actions_create_list_view_columns      (GtkWidget *list_view);
121 static void prefs_actions_select_row(GtkTreeView *list_view, GtkTreePath *path);
122
123 static void prefs_action_filter_radiobtn_cb(GtkWidget *widget, gpointer data);
124 static void prefs_action_shell_radiobtn_cb(GtkWidget *widget, gpointer data);
125 static void prefs_action_filterbtn_cb(GtkWidget *widget, gpointer data);
126 static void prefs_action_define_filter_done(GSList * action_list);
127
128
129 void prefs_actions_open(MainWindow *mainwin)
130 {
131         inc_lock();
132
133         if (!actions.window)
134                 prefs_actions_create(mainwin);
135
136         manage_window_set_transient(GTK_WINDOW(actions.window));
137         gtk_widget_grab_focus(actions.ok_btn);
138
139         prefs_actions_set_dialog();
140
141         gtk_widget_show(actions.window);
142 }
143
144 /*!
145  *\brief        Save Gtk object size to prefs dataset
146  */
147 static void prefs_actions_size_allocate_cb(GtkWidget *widget,
148                                          GtkAllocation *allocation)
149 {
150         g_return_if_fail(allocation != NULL);
151
152         prefs_common.actionswin_width = allocation->width;
153         prefs_common.actionswin_height = allocation->height;
154 }
155
156 static void prefs_actions_create(MainWindow *mainwin)
157 {
158         GtkWidget *window;
159         GtkWidget *vbox;
160         GtkWidget *filter_hbox;
161         GtkWidget *help_btn;
162         GtkWidget *ok_btn;
163         GtkWidget *cancel_btn;
164         GtkWidget *confirm_area;
165
166         GtkWidget *vbox1;
167         GtkWidget *table;
168
169         GtkWidget *shell_radiobtn;
170         GtkWidget *filter_radiobtn;
171
172         GtkWidget *name_label;
173         GtkWidget *name_entry;
174         GtkWidget *cmd_label;
175         GtkWidget *cmd_entry;
176         GtkWidget *filter_btn;
177
178         GtkWidget *reg_hbox;
179         GtkWidget *btn_hbox;
180         GtkWidget *arrow;
181         GtkWidget *reg_btn;
182         GtkWidget *subst_btn;
183         GtkWidget *del_btn;
184         GtkWidget *clear_btn;
185
186         GtkWidget *cond_hbox;
187         GtkWidget *cond_scrolledwin;
188         GtkWidget *cond_list_view;
189
190         GtkWidget *info_btn;
191
192         GtkWidget *btn_vbox;
193         GtkWidget *up_btn;
194         GtkWidget *down_btn;
195         static GdkGeometry geometry;
196         CLAWS_TIP_DECL();
197
198         debug_print("Creating actions configuration window...\n");
199
200         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "prefs_actions");
201
202         gtk_container_set_border_width(GTK_CONTAINER (window), 8);
203         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
204         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
205         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
206
207         vbox = gtk_vbox_new(FALSE, 6);
208         gtk_widget_show(vbox);
209         gtk_container_add(GTK_CONTAINER(window), vbox);
210
211         gtkut_stock_button_set_create_with_help(&confirm_area, &help_btn,
212                         &cancel_btn, GTK_STOCK_CANCEL,
213                         &ok_btn, GTK_STOCK_OK,
214                         NULL, NULL);
215         gtk_widget_show(confirm_area);
216         gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
217         gtk_widget_grab_default(ok_btn);
218
219         gtk_window_set_title(GTK_WINDOW(window), _("Actions configuration"));
220         g_signal_connect(G_OBJECT(window), "delete_event",
221                          G_CALLBACK(prefs_actions_deleted), NULL);
222         g_signal_connect(G_OBJECT(window), "size_allocate",
223                          G_CALLBACK(prefs_actions_size_allocate_cb), NULL);
224         g_signal_connect(G_OBJECT(window), "key_press_event",
225                          G_CALLBACK(prefs_actions_key_pressed), NULL);
226         MANAGE_WINDOW_SIGNALS_CONNECT(window);
227         g_signal_connect(G_OBJECT(ok_btn), "clicked",
228                          G_CALLBACK(prefs_actions_ok), mainwin);
229         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
230                          G_CALLBACK(prefs_actions_cancel), NULL);
231         g_signal_connect(G_OBJECT(help_btn), "clicked",
232                          G_CALLBACK(manual_open_with_anchor_cb),
233                          MANUAL_ANCHOR_ACTIONS);
234
235         vbox1 = gtk_vbox_new(FALSE, VSPACING);
236         gtk_widget_show(vbox1);
237         gtk_box_pack_start(GTK_BOX(vbox), vbox1, TRUE, TRUE, 0);
238         gtk_container_set_border_width(GTK_CONTAINER(vbox1), 2);        
239
240         table = gtk_table_new(3, 2, FALSE);
241         gtk_table_set_row_spacings (GTK_TABLE (table), VSPACING_NARROW_2);
242         gtk_table_set_col_spacings (GTK_TABLE (table), 4);
243         gtk_widget_show(table);
244         gtk_box_pack_start (GTK_BOX (vbox1), table, FALSE, FALSE, 0);
245
246         name_label = gtk_label_new (_("Menu name"));
247         gtk_widget_show (name_label);
248         gtk_misc_set_alignment (GTK_MISC (name_label), 1, 0.5);
249         gtk_table_attach (GTK_TABLE (table), name_label, 0, 1, 0, 1,
250                           (GtkAttachOptions) (GTK_FILL),
251                           (GtkAttachOptions) (0), 0, 0);
252
253         name_entry = gtk_entry_new ();
254         gtk_widget_show (name_entry);
255         gtk_table_attach (GTK_TABLE (table), name_entry, 1, 2, 0, 1,
256                           (GtkAttachOptions) (GTK_FILL|GTK_EXPAND),
257                           (GtkAttachOptions) (0), 0, 0);
258
259         cmd_label = gtk_label_new (_("Command"));
260         gtk_widget_show (cmd_label);
261         gtk_misc_set_alignment (GTK_MISC (cmd_label), 1, 0.5);
262         gtk_table_attach (GTK_TABLE (table), cmd_label, 0, 1, 2, 3,
263                           (GtkAttachOptions) (GTK_FILL),
264                           (GtkAttachOptions) (0), 0, 0);
265
266         cmd_entry = gtk_entry_new ();
267         gtk_widget_show (cmd_entry);
268         gtk_table_attach (GTK_TABLE (table), cmd_entry, 1, 2, 2, 3,
269                           (GtkAttachOptions) (GTK_FILL|GTK_EXPAND),
270                           (GtkAttachOptions) (0), 0, 0);
271
272         /* radio buttons for filter actions or shell */
273         filter_hbox = gtk_hbox_new(FALSE,4);
274         gtk_table_attach(GTK_TABLE(table), filter_hbox, 1, 2, 3, 4,
275                           (GtkAttachOptions) (GTK_FILL|GTK_EXPAND),
276                           (GtkAttachOptions) (0), 0, 0);
277         gtk_widget_show(filter_hbox);
278
279         shell_radiobtn = gtk_radio_button_new_with_label(NULL, _("Shell command"));
280         gtk_box_pack_start(GTK_BOX(filter_hbox), shell_radiobtn, FALSE, FALSE, 0);
281         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(shell_radiobtn), TRUE);
282         gtk_widget_show(shell_radiobtn);
283         
284         g_signal_connect(G_OBJECT(shell_radiobtn), "clicked",
285                          G_CALLBACK(prefs_action_shell_radiobtn_cb), NULL);
286
287         filter_radiobtn =
288                 gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(shell_radiobtn), 
289                                                             _("Filter action"));
290         gtk_box_pack_start(GTK_BOX(filter_hbox), filter_radiobtn, FALSE, FALSE, 0);
291         gtk_widget_show(filter_radiobtn);
292         g_signal_connect(G_OBJECT(filter_radiobtn), "clicked",
293                          G_CALLBACK(prefs_action_filter_radiobtn_cb), NULL);
294
295         filter_btn = gtk_button_new_with_label(_("Edit filter action"));
296         gtk_box_pack_start(GTK_BOX(filter_hbox), filter_btn, FALSE, FALSE, 0);
297         gtk_widget_set_sensitive(filter_btn, FALSE);
298         g_signal_connect(G_OBJECT(filter_btn), "clicked",
299                          G_CALLBACK(prefs_action_filterbtn_cb), NULL);
300         gtk_widget_show(filter_btn);
301
302         /* register / substitute / delete */
303
304         reg_hbox = gtk_hbox_new(FALSE, 4);
305         gtk_widget_show(reg_hbox);
306         gtk_box_pack_start(GTK_BOX(vbox1), reg_hbox, FALSE, FALSE, 0);
307
308         arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
309         gtk_widget_show(arrow);
310         gtk_box_pack_start(GTK_BOX(reg_hbox), arrow, FALSE, FALSE, 0);
311         gtk_widget_set_size_request(arrow, -1, 16);
312
313         btn_hbox = gtk_hbox_new(TRUE, 4);
314         gtk_widget_show(btn_hbox);
315         gtk_box_pack_start(GTK_BOX(reg_hbox), btn_hbox, FALSE, FALSE, 0);
316
317         reg_btn = gtk_button_new_from_stock(GTK_STOCK_ADD);
318         gtk_widget_show(reg_btn);
319         gtk_box_pack_start(GTK_BOX(btn_hbox), reg_btn, FALSE, TRUE, 0);
320         g_signal_connect(G_OBJECT(reg_btn), "clicked",
321                          G_CALLBACK(prefs_actions_register_cb), NULL);
322         CLAWS_SET_TIP(reg_btn,
323                         _("Append the new action above to the list"));
324
325         subst_btn = gtkut_get_replace_btn(_("Replace"));
326         gtk_widget_show(subst_btn);
327         gtk_box_pack_start(GTK_BOX(btn_hbox), subst_btn, FALSE, TRUE, 0);
328         g_signal_connect(G_OBJECT(subst_btn), "clicked",
329                          G_CALLBACK(prefs_actions_substitute_cb), NULL);
330         CLAWS_SET_TIP(subst_btn,
331                         _("Replace the selected action in list with the action above"));
332
333         del_btn = gtk_button_new_from_stock(GTK_STOCK_DELETE);
334         gtk_widget_show(del_btn);
335         gtk_box_pack_start(GTK_BOX(btn_hbox), del_btn, FALSE, TRUE, 0);
336         g_signal_connect(G_OBJECT(del_btn), "clicked",
337                          G_CALLBACK(prefs_actions_delete_cb), NULL);
338         CLAWS_SET_TIP(del_btn,
339                         _("Delete the selected action from the list"));
340
341         clear_btn = gtk_button_new_from_stock (GTK_STOCK_CLEAR);
342         gtk_widget_show (clear_btn);
343         gtk_box_pack_start (GTK_BOX (btn_hbox), clear_btn, FALSE, TRUE, 0);
344         g_signal_connect(G_OBJECT (clear_btn), "clicked",
345                         G_CALLBACK(prefs_actions_clear_cb), NULL);
346         CLAWS_SET_TIP(clear_btn,
347                         _("Clear all the input fields in the dialog"));
348
349 #if GTK_CHECK_VERSION(2, 8, 0)
350         info_btn = gtk_button_new_from_stock(GTK_STOCK_INFO);
351 #else
352         info_btn = gtk_button_new_with_label(_("Info..."));
353 #endif
354         gtk_widget_show(info_btn);
355         gtk_box_pack_end(GTK_BOX(reg_hbox), info_btn, FALSE, FALSE, 0);
356         g_signal_connect(G_OBJECT(info_btn), "clicked",
357                          G_CALLBACK(prefs_actions_info_cb), GTK_WINDOW(window));
358         CLAWS_SET_TIP(info_btn,
359                         _("Show information on configuring actions"));
360
361         cond_hbox = gtk_hbox_new(FALSE, 8);
362         gtk_widget_show(cond_hbox);
363         gtk_box_pack_start(GTK_BOX(vbox1), cond_hbox, TRUE, TRUE, 0);
364
365         cond_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
366         gtk_widget_show(cond_scrolledwin);
367         gtk_widget_set_size_request(cond_scrolledwin, -1, 150);
368         gtk_box_pack_start(GTK_BOX(cond_hbox), cond_scrolledwin,
369                            TRUE, TRUE, 0);
370         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (cond_scrolledwin),
371                                        GTK_POLICY_AUTOMATIC,
372                                        GTK_POLICY_AUTOMATIC);
373         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(cond_scrolledwin),
374                                             GTK_SHADOW_ETCHED_IN);
375
376         cond_list_view = prefs_actions_list_view_create();                                     
377         gtk_widget_show(cond_list_view);
378         gtk_container_add(GTK_CONTAINER (cond_scrolledwin), cond_list_view);
379
380         btn_vbox = gtk_vbox_new(FALSE, 8);
381         gtk_widget_show(btn_vbox);
382         gtk_box_pack_start(GTK_BOX(cond_hbox), btn_vbox, FALSE, FALSE, 0);
383
384         up_btn = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
385         gtk_widget_show(up_btn);
386         gtk_box_pack_start(GTK_BOX(btn_vbox), up_btn, FALSE, FALSE, 0);
387         g_signal_connect(G_OBJECT(up_btn), "clicked",
388                          G_CALLBACK(prefs_actions_up), NULL);
389         CLAWS_SET_TIP(up_btn,
390                         _("Move the selected action up"));
391
392         down_btn = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
393         gtk_widget_show(down_btn);
394         gtk_box_pack_start(GTK_BOX(btn_vbox), down_btn, FALSE, FALSE, 0);
395         g_signal_connect(G_OBJECT(down_btn), "clicked",
396                          G_CALLBACK(prefs_actions_down), NULL);
397         CLAWS_SET_TIP(down_btn,
398                         _("Move selected action down"));
399
400         if (!geometry.min_height) {
401                 geometry.min_width = 486;
402                 geometry.min_height = 322;
403         }
404
405         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL, &geometry,
406                                       GDK_HINT_MIN_SIZE);
407         gtk_widget_set_size_request(window, prefs_common.actionswin_width,
408                                     prefs_common.actionswin_height);
409
410         gtk_widget_show(window);
411
412         actions.window = window;
413         actions.ok_btn = ok_btn;
414         actions.info_btn = info_btn;
415
416         actions.name_entry = name_entry;
417         actions.cmd_entry  = cmd_entry;
418         actions.filter_btn = filter_btn;
419         actions.shell_radiobtn = shell_radiobtn;
420         actions.filter_radiobtn = filter_radiobtn;
421         
422         actions.actions_list_view = cond_list_view;
423 }
424
425 static void prefs_actions_reset_dialog(void)
426 {
427         gtk_entry_set_text(GTK_ENTRY(actions.name_entry), "");
428         gtk_entry_set_text(GTK_ENTRY(actions.cmd_entry), "");
429 }
430
431 void prefs_actions_read_config(void)
432 {
433         gchar *rcpath;
434         FILE *fp;
435         gchar buf[PREFSBUFSIZE];
436         gchar *act;
437
438         debug_print("Reading actions configurations...\n");
439
440         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ACTIONS_RC, NULL);
441         if ((fp = g_fopen(rcpath, "rb")) == NULL) {
442                 if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen");
443                 g_free(rcpath);
444                 return;
445         }
446         g_free(rcpath);
447
448         while (prefs_common.actions_list != NULL) {
449                 act = (gchar *)prefs_common.actions_list->data;
450                 prefs_common.actions_list =
451                         g_slist_remove(prefs_common.actions_list, act);
452                 g_free(act);
453         }
454
455         while (fgets(buf, sizeof(buf), fp) != NULL) {
456                 const gchar *src_codeset = conv_get_locale_charset_str();
457                 const gchar *dest_codeset = CS_UTF_8;
458                 gchar *tmp;
459
460                 tmp = conv_codeset_strdup(buf, src_codeset, dest_codeset);
461                 if (!tmp) {
462                         g_warning("Failed to convert character set of action configuration\n");
463                         tmp = g_strdup(buf);
464                 }
465
466                 g_strchomp(tmp);
467                 act = strstr(tmp, ": ");
468                 if (act && act[2] && 
469                     action_get_type(&act[2]) != ACTION_ERROR)
470                         prefs_common.actions_list =
471                                 g_slist_append(prefs_common.actions_list,
472                                                tmp);
473                 else
474                         g_free(tmp);
475         }
476         fclose(fp);
477 }
478
479 void prefs_actions_write_config(void)
480 {
481         gchar *rcpath;
482         PrefFile *pfile;
483         GSList *cur;
484
485         debug_print("Writing actions configuration...\n");
486
487         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ACTIONS_RC, NULL);
488         if ((pfile= prefs_write_open(rcpath)) == NULL) {
489                 g_warning("Failed to write configuration to file\n");
490                 g_free(rcpath);
491                 return;
492         }
493
494         for (cur = prefs_common.actions_list; cur != NULL; cur = cur->next) {
495                 gchar *tmp = (gchar *)cur->data;
496                 const gchar *src_codeset = CS_UTF_8;
497                 const gchar *dest_codeset = conv_get_locale_charset_str();
498                 gchar *act;
499
500                 act = conv_codeset_strdup(tmp, src_codeset, dest_codeset);
501                 if (!act) {
502                         g_warning("Failed to convert character set of action configuration\n");
503                         act = g_strdup(act);
504                 }
505
506                 if (fputs(act, pfile->fp) == EOF ||
507                     fputc('\n', pfile->fp) == EOF) {
508                         FILE_OP_ERROR(rcpath, "fputs || fputc");
509                         prefs_file_close_revert(pfile);
510                         g_free(rcpath);
511                         return;
512                 }
513                 g_free(act);
514         }
515         
516         g_free(rcpath);
517
518         if (prefs_file_close(pfile) < 0) {
519                 g_warning("failed to write configuration to file\n");
520                 return;
521         }
522 }
523
524 static void prefs_actions_clear_list(GtkListStore *list_store)
525 {
526         gtk_list_store_clear(list_store);
527
528         prefs_actions_list_view_insert_action(actions.actions_list_view,
529                                               -1, _("(New)"), FALSE);
530 }
531
532 static void prefs_actions_set_dialog(void)
533 {
534         GtkListStore *store;
535         GSList *cur;
536         GtkTreeSelection *selection;
537         GtkTreeIter iter;
538
539         store = GTK_LIST_STORE(gtk_tree_view_get_model
540                                 (GTK_TREE_VIEW(actions.actions_list_view)));
541
542         prefs_actions_clear_list(store);        
543
544         for (cur = prefs_common.actions_list; cur != NULL; cur = cur->next) {
545                 gchar *action = (gchar *) cur->data;
546                 
547                 prefs_actions_list_view_insert_action(actions.actions_list_view,
548                                                       -1, action, TRUE);
549         }
550
551         /* select first entry */
552         selection = gtk_tree_view_get_selection
553                 (GTK_TREE_VIEW(actions.actions_list_view));
554         if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store),
555                                           &iter))
556                 gtk_tree_selection_select_iter(selection, &iter);
557 }
558
559 static void prefs_actions_set_list(void)
560 {
561         GtkTreeIter iter;
562         GtkListStore *store;
563         
564         g_slist_free(prefs_common.actions_list);
565         prefs_common.actions_list = NULL;
566
567         store = GTK_LIST_STORE(gtk_tree_view_get_model
568                                 (GTK_TREE_VIEW(actions.actions_list_view)));
569
570         if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) {
571                 do {
572                         gchar *action;
573                         gboolean is_valid;
574
575                         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
576                                            PREFS_ACTIONS_DATA, &action,
577                                            PREFS_ACTIONS_VALID, &is_valid,
578                                            -1);
579                         
580                         if (is_valid) 
581                                 prefs_common.actions_list = 
582                                         g_slist_append(prefs_common.actions_list,
583                                                        action);
584
585                 } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store),
586                                                   &iter));
587         }
588 }
589
590 #define GET_ENTRY(entry) \
591         entry_text = gtk_entry_get_text(GTK_ENTRY(entry))
592
593 static gint prefs_actions_clist_set_row(gint row)
594 {
595         const gchar *entry_text;
596         gint len;
597         gchar action[PREFSBUFSIZE];
598         gchar *new_action;
599         GtkListStore *store;
600
601         store = GTK_LIST_STORE(gtk_tree_view_get_model
602                                 (GTK_TREE_VIEW(actions.actions_list_view)));
603
604         GET_ENTRY(actions.name_entry);
605         if (entry_text[0] == '\0') {
606                 alertpanel_error(_("Menu name is not set."));
607                 return -1;
608         }
609
610         if (entry_text[0] == '/') {
611                 alertpanel_error(_("A leading '/' is not allowed in the menu name."));
612                 return -1;
613         }
614
615         if (strchr(entry_text, ':')) {
616                 alertpanel_error(_("Colon ':' is not allowed in the menu name."));
617                 return -1;
618         }
619
620         strncpy(action, entry_text, PREFSBUFSIZE - 1);
621
622         while (strstr(action, "//")) {
623                 char *to_move = strstr(action, "//")+1;
624                 char *where = strstr(action, "//");
625                 int old_len = strlen(action);
626                 memmove(where, to_move, strlen(to_move));
627                 action[old_len-1] = '\0';
628         }
629         
630         g_strstrip(action);
631
632         /* Keep space for the ': ' delimiter */
633         len = strlen(action) + 2;
634         if (len >= PREFSBUFSIZE - 1) {
635                 alertpanel_error(_("Menu name is too long."));
636                 return -1;
637         }
638
639         strcat(action, ": ");
640
641         GET_ENTRY(actions.cmd_entry);
642
643         if (entry_text[0] == '\0') {
644                 alertpanel_error(_("Command line not set."));
645                 return -1;
646         }
647
648         if (len + strlen(entry_text) >= PREFSBUFSIZE - 1) {
649                 alertpanel_error(_("Menu name and command are too long."));
650                 return -1;
651         }
652
653         if (action_get_type(entry_text) == ACTION_ERROR) {
654                 gchar *message;
655                 message = g_markup_printf_escaped(_("The command\n%s\nhas a syntax error."),
656                                                 entry_text);
657                 alertpanel_error(message);
658                 g_free(message);
659                 return -1;
660         }
661
662         strcat(action, entry_text);
663
664         new_action = g_strdup(action);  
665         prefs_actions_list_view_insert_action(actions.actions_list_view,
666                                               row, new_action, TRUE);
667                                                 
668         prefs_actions_set_list();
669
670         return 0;
671 }
672
673 /* callback functions */
674
675 static void prefs_actions_register_cb(GtkWidget *w, gpointer data)
676 {
677         prefs_actions_clist_set_row(-1);
678
679         modified = FALSE;
680         modified_list = TRUE;
681 }
682
683 static void prefs_actions_substitute_cb(GtkWidget *w, gpointer data)
684 {
685         gint row;
686
687         row = gtkut_list_view_get_selected_row(actions.actions_list_view);
688         if (row <= 0)
689                 return;
690
691         prefs_actions_clist_set_row(row);
692
693         modified = FALSE;
694         modified_list = TRUE;
695 }
696
697 static void prefs_actions_delete_cb(gpointer gtk_action, gpointer data)
698 {
699         GtkTreeIter sel;
700         GtkTreeModel *model;
701         gchar *action;
702         gint row;
703
704         row = gtkut_list_view_get_selected_row(actions.actions_list_view);
705         if (row <= 0) 
706                 return; 
707
708         if (!gtk_tree_selection_get_selected(gtk_tree_view_get_selection
709                                 (GTK_TREE_VIEW(actions.actions_list_view)),
710                                 &model, &sel))
711                 return;                         
712
713         if (alertpanel(_("Delete action"),
714                        _("Do you really want to delete this action?"),
715                        GTK_STOCK_CANCEL, GTK_STOCK_DELETE, NULL) != G_ALERTALTERNATE)
716                 return;
717
718         /* XXX: Here's the reason why we need to store the original 
719          * pointer: we search the slist for it. */
720         gtk_tree_model_get(model, &sel,
721                            PREFS_ACTIONS_DATA, &action,
722                            -1);
723         gtk_list_store_remove(GTK_LIST_STORE(model), &sel);
724
725         prefs_common.actions_list = g_slist_remove(prefs_common.actions_list,
726                                                    action);
727         modified_list = TRUE;
728 }
729
730 static void prefs_actions_delete_all_cb(gpointer gtk_action, gpointer data)
731 {
732         GtkListStore *list_store;
733
734         if (alertpanel(_("Delete all actions"),
735                           _("Do you really want to delete all the actions?"),
736                           GTK_STOCK_CANCEL, "+"GTK_STOCK_DELETE, NULL) == G_ALERTDEFAULT)
737            return;
738
739         list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(actions.actions_list_view)));
740         prefs_actions_clear_list(list_store);
741         modified = FALSE;
742
743         prefs_actions_reset_dialog();
744         modified_list = TRUE;
745 }
746
747 static void prefs_actions_clear_cb(gpointer gtk_action, gpointer data)
748 {
749         gint row;
750
751         prefs_actions_reset_dialog();
752         row = gtkut_list_view_get_selected_row(actions.actions_list_view);
753         if (row < 1)
754                 modified = FALSE;
755         else
756                 modified = TRUE;
757 }
758
759 static void prefs_actions_duplicate_cb(gpointer gtk_action, gpointer data)
760 {
761         gint row;
762         
763         row = gtkut_list_view_get_selected_row(actions.actions_list_view);
764         if (row <= 0)
765                 return;
766
767         modified_list = !prefs_actions_clist_set_row(-row-2);
768 }
769
770 static void prefs_actions_up(GtkWidget *w, gpointer data)
771 {
772         GtkTreePath *prev, *sel, *try;
773         GtkTreeIter isel;
774         GtkListStore *store = NULL;
775         GtkTreeModel *model = NULL;
776         GtkTreeIter iprev;
777         
778         if (!gtk_tree_selection_get_selected
779                 (gtk_tree_view_get_selection
780                         (GTK_TREE_VIEW(actions.actions_list_view)),
781                  &model,        
782                  &isel))
783                 return;
784         store = (GtkListStore *)model;
785         sel = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &isel);
786         if (!sel)
787                 return;
788         
789         /* no move if we're at row 0 or 1, looks phony, but other
790          * solutions are more convoluted... */
791         try = gtk_tree_path_copy(sel);
792         if (!gtk_tree_path_prev(try) || !gtk_tree_path_prev(try)) {
793                 gtk_tree_path_free(try);
794                 gtk_tree_path_free(sel);
795                 return;
796         }
797         gtk_tree_path_free(try);
798
799         prev = gtk_tree_path_copy(sel);         
800         if (!gtk_tree_path_prev(prev)) {
801                 gtk_tree_path_free(prev);
802                 gtk_tree_path_free(sel);
803                 return;
804         }
805
806         gtk_tree_model_get_iter(GTK_TREE_MODEL(store),
807                                 &iprev, prev);
808         gtk_tree_path_free(sel);
809         gtk_tree_path_free(prev);
810
811         gtk_list_store_swap(store, &iprev, &isel);
812         prefs_actions_set_list();
813         modified_list = TRUE;
814 }
815
816 static void prefs_actions_down(GtkWidget *w, gpointer data)
817 {
818         GtkListStore *store = NULL;
819         GtkTreeModel *model = NULL;
820         GtkTreeIter next, sel;
821         GtkTreePath *try;
822         
823         if (!gtk_tree_selection_get_selected
824                 (gtk_tree_view_get_selection
825                         (GTK_TREE_VIEW(actions.actions_list_view)),
826                  &model,
827                  &sel))
828                 return;
829         store = (GtkListStore *)model;
830         try = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &sel);
831         if (!try) 
832                 return;
833
834         /* no move when we're at row 0 */
835         if (!gtk_tree_path_prev(try)) {
836                 gtk_tree_path_free(try);
837                 return;
838         }
839         gtk_tree_path_free(try);
840
841         next = sel;
842         if (!gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &next)) 
843                 return;
844
845         gtk_list_store_swap(store, &next, &sel);
846         prefs_actions_set_list();
847         modified_list = TRUE;
848 }
849
850 static gint prefs_actions_deleted(GtkWidget *widget, GdkEventAny *event,
851                                   gpointer *data)
852 {
853         prefs_actions_cancel(widget, data);
854         return TRUE;
855 }
856
857 static gboolean prefs_actions_key_pressed(GtkWidget *widget, GdkEventKey *event,
858                                           gpointer data)
859 {
860         if (event && event->keyval == GDK_Escape)
861                 prefs_actions_cancel(widget, data);
862         else {
863                 GtkWidget *focused = gtkut_get_focused_child(
864                                         GTK_CONTAINER(widget));
865                 if (focused && GTK_IS_EDITABLE(focused)) {
866                         modified = TRUE;
867                 }
868         }
869         return FALSE;
870 }
871
872 static void prefs_actions_cancel(GtkWidget *w, gpointer data)
873 {
874         GtkListStore *store;
875
876         if (modified && alertpanel(_("Entry not saved"),
877                                  _("The entry was not saved. Close anyway?"),
878                                  GTK_STOCK_CLOSE, _("+_Continue editing"),
879                                  NULL) != G_ALERTDEFAULT) {
880                 return;
881         } else if (modified_list && alertpanel(_("Actions list not saved"),
882                                  _("The actions list has been modified. Close anyway?"),
883                                  GTK_STOCK_CLOSE, _("+_Continue editing"), 
884                                  NULL) != G_ALERTDEFAULT) {
885                 return;
886         }
887         modified = FALSE;
888         modified_list = FALSE;
889         store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW
890                                 (actions.actions_list_view)));
891         gtk_list_store_clear(store);
892         prefs_actions_read_config();
893         gtk_widget_hide(actions.window);
894         inc_unlock();
895 }
896
897 static void prefs_actions_ok(GtkWidget *widget, gpointer data)
898 {
899         MainWindow *mainwin = (MainWindow *) data;
900         GList *list;
901         GList *iter;
902         MessageView *msgview;
903         Compose *compose;
904         GtkListStore *store;
905
906         if (modified && alertpanel(_("Entry not saved"),
907                                  _("The entry was not saved. Close anyway?"),
908                                  GTK_STOCK_CLOSE, _("+_Continue editing"),
909                                  NULL) != G_ALERTDEFAULT) {
910                 return;
911         } 
912         modified = FALSE;
913         modified_list = FALSE;
914         prefs_actions_set_list();
915         store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW
916                                 (actions.actions_list_view)));
917         gtk_list_store_clear(store);
918         prefs_actions_write_config();
919
920         /* Update mainwindow actions menu */
921         main_window_update_actions_menu(mainwin);
922
923         /* Update separated message view actions menu */
924         list = messageview_get_msgview_list();
925         for (iter = list; iter; iter = iter->next) {
926                 msgview = (MessageView *) iter->data;
927                 messageview_update_actions_menu(msgview);
928         }
929
930         /* Update compose windows actions menu */
931         list = compose_get_compose_list();
932         for (iter = list; iter; iter = iter->next) {
933                 compose = (Compose *) iter->data;
934                 compose_update_actions_menu(compose);
935         }
936
937         gtk_widget_hide(actions.window);
938         inc_unlock();
939 }
940
941 /*
942  * Strings describing action format strings
943  * 
944  * When adding new lines, remember to put one string for each line
945  */
946 static gchar *actions_desc_strings[] = {
947         N_("<span weight=\"bold\" underline=\"single\">Menu name:</span>"), NULL,
948         N_("Use / in menu name to make submenus."), NULL,
949         "", NULL,
950         N_("<span weight=\"bold\" underline=\"single\">Command line:</span>"), NULL,
951         N_("<span weight=\"bold\">Begin with:</span>"), NULL,
952         "     |",   N_("to send message body or selection to command's standard input"),
953         "     &gt;",   N_("to send user provided text to command's standard input"),
954         "     *",   N_("to send user provided hidden text to command's standard input"),
955         N_("<span weight=\"bold\">End with:</span>"), NULL,
956         "     |",   N_("to replace message body or selection with command's standard output"),
957         "     &gt;",   N_("to insert command's standard output without replacing old text"),
958         "     &amp;",   N_("to run command asynchronously"),
959         N_("<span weight=\"bold\">Use:</span>"), NULL, 
960         "     %f",  N_("for the file of the selected message in RFC822/2822 format "),
961         "     %F",  N_("for the list of the files of the selected messages in RFC822/2822 format"),
962         "     %p",  N_("for the file of the selected decoded message MIME part"),
963         "     %u",  N_("for a user provided argument"),
964         "     %h",  N_("for a user provided hidden argument (e.g. password)"),
965         "     %s",  N_("for the text selection"),
966         "  %as{}",  N_("apply filtering actions between {} to selected messages"),
967         "     %%",  N_("for a literal %"),
968         NULL, NULL
969 };
970
971
972 static DescriptionWindow actions_desc_win = { 
973         NULL,
974         NULL,
975         2,
976         N_("Actions"),
977         N_("The Actions feature is a way for the user to launch "
978            "external commands to process a complete message file or just "
979            "one of its parts."),
980         actions_desc_strings
981 };
982
983
984 static void prefs_actions_info_cb(GtkWidget *w, GtkWidget *window)
985 {
986         actions_desc_win.parent = window;
987         description_window_create(&actions_desc_win);
988 }
989
990 static GtkListStore* prefs_actions_create_data_store(void)
991 {
992         return gtk_list_store_new(N_PREFS_ACTIONS_COLUMNS,
993                                   G_TYPE_STRING,        
994                                   G_TYPE_POINTER,
995                                   G_TYPE_BOOLEAN,
996                                   -1);
997 }
998
999 static void prefs_actions_list_view_insert_action(GtkWidget *list_view,
1000                                                   gint row,
1001                                                   gchar *action,
1002                                                   gboolean is_valid) 
1003 {
1004         GtkTreeIter iter;
1005         GtkTreeIter sibling;
1006         GtkListStore *list_store = GTK_LIST_STORE(gtk_tree_view_get_model
1007                                         (GTK_TREE_VIEW(list_view)));
1008
1009 /*      row -1 to add a new rule to store,
1010         row >=0 to change an existing row
1011         row <-1 insert a new row after (-row-2)
1012 */
1013         if (row >= 0 ) {
1014                 /* modify the existing */
1015                 if (!gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(list_store),
1016                                                    &iter, NULL, row))
1017                         row = -1;
1018         } else if (row < -1 ) {
1019                 if (!gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(list_store),
1020                                                    &sibling, NULL, -row-2))
1021                         row = -1;               
1022         }
1023
1024         if (row == -1 ) {
1025                 /* append new */
1026                 gtk_list_store_append(list_store, &iter);
1027                 gtk_list_store_set(list_store, &iter,
1028                                    PREFS_ACTIONS_STRING, action,
1029                                    PREFS_ACTIONS_DATA, action,
1030                                    PREFS_ACTIONS_VALID,  is_valid,
1031                                    -1);
1032         } else if (row < -1) {
1033                 /* duplicate */
1034                 gtk_list_store_insert_after(list_store, &iter, &sibling);
1035                 gtk_list_store_set(list_store, &iter,
1036                                    PREFS_ACTIONS_STRING, action,
1037                                    PREFS_ACTIONS_DATA, action,
1038                                    PREFS_ACTIONS_VALID,  is_valid,
1039                                    -1);
1040         } else {
1041                 /* change existing */
1042                 gchar *old_action;
1043
1044                 gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter,
1045                                    PREFS_ACTIONS_DATA, &old_action,
1046                                    -1);
1047                 g_free(old_action);                             
1048
1049                 gtk_list_store_set(list_store, &iter,
1050                                    PREFS_ACTIONS_STRING, action,
1051                                    PREFS_ACTIONS_DATA, action,
1052                                    -1);
1053         }
1054 }
1055
1056 static GtkActionGroup *prefs_actions_popup_action = NULL;
1057 static GtkWidget *prefs_actions_popup_menu = NULL;
1058
1059 static GtkActionEntry prefs_actions_popup_entries[] =
1060 {
1061         {"PrefsActionsPopup",                   NULL, "PrefsActionsPopup" },
1062         {"PrefsActionsPopup/Delete",            NULL, N_("_Delete"), NULL, NULL, G_CALLBACK(prefs_actions_delete_cb) },
1063         {"PrefsActionsPopup/DeleteAll", NULL, N_("Delete _all"), NULL, NULL, G_CALLBACK(prefs_actions_delete_all_cb) },
1064         {"PrefsActionsPopup/Duplicate", NULL, N_("D_uplicate"), NULL, NULL, G_CALLBACK(prefs_actions_duplicate_cb) },
1065 };
1066
1067 static gint prefs_actions_list_btn_pressed(GtkWidget *widget, GdkEventButton *event,
1068                                    GtkTreeView *list_view)
1069 {
1070    if (event) {
1071            /* left- or right-button click */
1072            if (event->button == 1 || event->button == 3) {
1073                    GtkTreePath *path = NULL;
1074                    if (gtk_tree_view_get_path_at_pos( list_view, event->x, event->y,
1075                                                            &path, NULL, NULL, NULL)) {
1076                            prefs_actions_select_row(list_view, path);
1077                    }
1078                    if (path)
1079                            gtk_tree_path_free(path);
1080            }
1081
1082            /* right-button click */
1083            if (event->button == 3) {
1084                    GtkTreeModel *model = gtk_tree_view_get_model(list_view);
1085                    GtkTreeIter iter;
1086                    gboolean non_empty;
1087                    gint row;
1088
1089                    if (!prefs_actions_popup_menu) {
1090                                 prefs_actions_popup_action = cm_menu_create_action_group("PrefsActionsPopup", prefs_actions_popup_entries,
1091                                         G_N_ELEMENTS(prefs_actions_popup_entries), (gpointer)list_view);
1092                                 MENUITEM_ADDUI("/Menus", "PrefsActionsPopup", "PrefsActionsPopup", GTK_UI_MANAGER_MENU)
1093                                 MENUITEM_ADDUI("/Menus/PrefsActionsPopup", "Delete", "PrefsActionsPopup/Delete", GTK_UI_MANAGER_MENUITEM)
1094                                 MENUITEM_ADDUI("/Menus/PrefsActionsPopup", "DeleteAll", "PrefsActionsPopup/DeleteAll", GTK_UI_MANAGER_MENUITEM)
1095                                 MENUITEM_ADDUI("/Menus/PrefsActionsPopup", "Duplicate", "PrefsActionsPopup/Duplicate", GTK_UI_MANAGER_MENUITEM)
1096                                 prefs_actions_popup_menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1097                                         gtk_ui_manager_get_widget(gtkut_ui_manager(), "/Menus/PrefsActionsPopup")) );
1098                    }
1099
1100                    /* grey out some popup menu items if there is no selected row */
1101                    row = gtkut_list_view_get_selected_row(GTK_WIDGET(list_view));
1102                         cm_menu_set_sensitive("PrefsActionsPopup/Delete", (row > 0));
1103                         cm_menu_set_sensitive("PrefsActionsPopup/Duplicate", (row > 0));
1104
1105                    /* grey out seom popup menu items if there is no row
1106                           (not counting the (New) one at row 0) */
1107                    non_empty = gtk_tree_model_get_iter_first(model, &iter);
1108                    if (non_empty)
1109                            non_empty = gtk_tree_model_iter_next(model, &iter);
1110                         cm_menu_set_sensitive("PrefsActionsPopup/DeleteAll", non_empty);
1111
1112                    gtk_menu_popup(GTK_MENU(prefs_actions_popup_menu), 
1113                                           NULL, NULL, NULL, NULL, 
1114                                           event->button, event->time);
1115            }
1116    }
1117    return FALSE;
1118 }
1119
1120 static gboolean prefs_actions_list_popup_menu(GtkWidget *widget, gpointer data)
1121 {
1122    GtkTreeView *list_view = (GtkTreeView *)data;
1123    GdkEventButton event;
1124    
1125    event.button = 3;
1126    event.time = gtk_get_current_event_time();
1127    
1128    prefs_actions_list_btn_pressed(NULL, &event, list_view);
1129
1130    return TRUE;
1131 }
1132
1133 static GtkWidget *prefs_actions_list_view_create(void)
1134 {
1135         GtkTreeView *list_view;
1136         GtkTreeSelection *selector;
1137         GtkTreeModel *model;
1138
1139         model = GTK_TREE_MODEL(prefs_actions_create_data_store());
1140         list_view = GTK_TREE_VIEW(gtk_tree_view_new_with_model(model));
1141         g_object_unref(model);  
1142         
1143 #ifndef MAEMO
1144         g_signal_connect(G_OBJECT(list_view), "popup-menu",
1145                          G_CALLBACK(prefs_actions_list_popup_menu), list_view);
1146 #else
1147         gtk_widget_tap_and_hold_setup(GTK_WIDGET(list_view), NULL, NULL,
1148                         GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
1149         g_signal_connect(G_OBJECT(list_view), "tap-and-hold",
1150                          G_CALLBACK(prefs_actions_list_popup_menu), list_view);
1151 #endif
1152         g_signal_connect(G_OBJECT(list_view), "button-press-event",
1153                         G_CALLBACK(prefs_actions_list_btn_pressed), list_view);
1154
1155         gtk_tree_view_set_rules_hint(list_view, prefs_common.use_stripes_everywhere);
1156         gtk_tree_view_set_reorderable(list_view, TRUE);
1157
1158         selector = gtk_tree_view_get_selection(list_view);
1159         gtk_tree_selection_set_mode(selector, GTK_SELECTION_BROWSE);
1160
1161         /* create the columns */
1162         prefs_actions_create_list_view_columns(GTK_WIDGET(list_view));
1163
1164         return GTK_WIDGET(list_view);
1165 }
1166
1167 static void prefs_actions_create_list_view_columns(GtkWidget *list_view)
1168 {
1169         GtkTreeViewColumn *column;
1170         GtkCellRenderer *renderer;
1171
1172         renderer = gtk_cell_renderer_text_new();
1173         column = gtk_tree_view_column_new_with_attributes
1174                 (_("Current actions"),
1175                  renderer,
1176                  "text", PREFS_ACTIONS_STRING,
1177                  NULL);
1178         gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), column);          
1179 }
1180
1181 #define ENTRY_SET_TEXT(entry, str) \
1182         gtk_entry_set_text(GTK_ENTRY(entry), str ? str : "")
1183
1184 static void prefs_actions_select_row(GtkTreeView *list_view, GtkTreePath *path)
1185 {
1186         GtkTreeModel *model = gtk_tree_view_get_model(list_view);
1187         GtkTreeSelection *selection;
1188         gchar *action;
1189         gchar *cmd;
1190         gchar buf[PREFSBUFSIZE];
1191         GtkTreeIter iter;
1192         gboolean is_valid;
1193
1194         if (!model || !path || !gtk_tree_model_get_iter(model, &iter, path))
1195                 return;
1196
1197         /* select row */
1198         selection = gtk_tree_view_get_selection(list_view);
1199         gtk_tree_selection_select_path(selection, path);
1200
1201         gtk_tree_model_get(model, &iter, 
1202                            PREFS_ACTIONS_VALID,  &is_valid,
1203                            PREFS_ACTIONS_DATA, &action,
1204                            -1);
1205         if (!is_valid) {
1206                 prefs_actions_reset_dialog();
1207                 return;
1208         }
1209         
1210         strncpy(buf, action, PREFSBUFSIZE - 1);
1211         buf[PREFSBUFSIZE - 1] = '\0';
1212         cmd = strstr(buf, ": ");
1213
1214         if (cmd && cmd[2])
1215                 ENTRY_SET_TEXT(actions.cmd_entry, &cmd[2]);
1216         else
1217                 return;
1218
1219         *cmd = '\0';
1220         gtk_entry_set_text(GTK_ENTRY(actions.name_entry), buf);
1221
1222         if(g_str_has_prefix(&cmd[2], "%as{") == TRUE)
1223                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
1224                                                 actions.filter_radiobtn), TRUE);
1225         else
1226                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
1227                                                 actions.shell_radiobtn), TRUE);
1228
1229         return;
1230 }
1231
1232 static void prefs_action_filter_radiobtn_cb(GtkWidget *widget, gpointer data)
1233 {
1234         if(actions.filter_btn)
1235                 gtk_widget_set_sensitive(actions.filter_btn, TRUE);
1236         if(actions.cmd_entry)
1237                 gtk_widget_set_sensitive(actions.cmd_entry,FALSE);
1238         if(actions.info_btn)
1239                 gtk_widget_set_sensitive(actions.info_btn,FALSE);
1240 }
1241
1242 static void prefs_action_shell_radiobtn_cb(GtkWidget *widget, gpointer data)
1243 {
1244         if(actions.filter_btn)
1245                 gtk_widget_set_sensitive(actions.filter_btn, FALSE);
1246         if(actions.cmd_entry)
1247                 gtk_widget_set_sensitive(actions.cmd_entry,TRUE);
1248         if(actions.info_btn)
1249                 gtk_widget_set_sensitive(actions.info_btn,TRUE);
1250 }
1251
1252 static void prefs_action_filterbtn_cb(GtkWidget *widget, gpointer data)
1253 {
1254         gchar *action_str, **tokens;
1255         GSList *action_list = NULL, *cur;
1256
1257         if(modified && alertpanel(_("Entry was modified"),
1258                         _("Opening the filter action dialog will clear current modifications "
1259                         "of the command line."),
1260                         GTK_STOCK_CANCEL, _("+_Continue editing"), NULL) == G_ALERTDEFAULT)
1261                 return;
1262
1263         action_str = gtk_editable_get_chars(GTK_EDITABLE(actions.cmd_entry), 0, -1);
1264         tokens = g_strsplit_set(action_str, "{}", 5);
1265
1266         if (tokens[0] && tokens[1] && *tokens[1] != '\0') {
1267                 action_list = matcher_parser_get_action_list(tokens[1]);
1268                 if (action_list == NULL)
1269                         alertpanel_error(_("Action string is not valid."));
1270         }
1271                 
1272         prefs_filtering_action_open(action_list, prefs_action_define_filter_done);
1273
1274         if (action_list != NULL) {
1275                 for(cur = action_list ; cur != NULL ; cur = cur->next)
1276                         filteringaction_free(cur->data);
1277         }
1278         
1279         g_free(action_str);
1280         g_strfreev(tokens);
1281 }
1282
1283 static void prefs_action_define_filter_done(GSList * action_list)
1284 {
1285         gchar *str;
1286
1287         if(action_list == NULL)
1288                 return;
1289
1290         str = filteringaction_list_to_string(action_list);
1291
1292         if (str != NULL) {
1293                 gchar *cmd;
1294                 cmd = g_strdup_printf("%%as{%s}",str);
1295                 g_free(str);
1296                 gtk_entry_set_text(GTK_ENTRY(actions.cmd_entry), cmd);
1297                 g_free(cmd);
1298                 modified = TRUE;
1299         }
1300 }