0.8.9claws5
[claws.git] / src / prefs_actions.c
index e097335c89a12d7537a2d25cd17e3af1e1dcad93..f78545c10aa0ffecd746247e4c15756f34451e5f 100644 (file)
@@ -37,7 +37,7 @@
 #include <unistd.h>
 
 #include "intl.h"
-#include "prefs.h"
+#include "prefs_gtk.h"
 #include "inc.h"
 #include "utils.h"
 #include "gtkutils.h"
@@ -50,6 +50,8 @@
 #include "procmsg.h"
 #include "gtkstext.h"
 #include "mimeview.h"
+#include "description_window.h"
+#include "textview.h"
 
 typedef enum
 {
@@ -61,7 +63,8 @@ typedef enum
        ACTION_ASYNC    = 1 << 5,
        ACTION_OPEN_IN  = 1 << 6,
        ACTION_HIDE_IN  = 1 << 7,
-       ACTION_ERROR    = 1 << 8,
+       ACTION_INSERT   = 1 << 8,
+       ACTION_ERROR    = 1 << 9,
 } ActionType;
 
 static struct Actions
@@ -81,13 +84,12 @@ typedef struct _ChildInfo ChildInfo;
 
 struct _Children
 {
-       GtkWidget       *window;
        GtkWidget       *dialog;
        GtkWidget       *text;
        GtkWidget       *input_entry;
        GtkWidget       *input_hbox;
        GtkWidget       *abort_btn;
-       GtkWidget       *hide_btn;
+       GtkWidget       *close_btn;
        GtkWidget       *scrolledwin;
 
        gchar           *action;
@@ -114,6 +116,7 @@ struct _ChildInfo
        gint             new_out;
        GString         *output;
        GtkWidget       *text;
+       GdkFont         *msgfont;
 };
 
 /* widget creating functions */
@@ -155,34 +158,43 @@ static void update_actions_menu           (GtkItemFactory *ifactory,
                                         gchar          *branch_path,
                                         gpointer        callback,
                                         gpointer        data);
+static void compose_actions_execute_cb (Compose        *compose,
+                                        guint           action_nb,
+                                        GtkWidget      *widget);
 static void mainwin_actions_execute_cb         (MainWindow     *mainwin,
                                         guint           action_nb,
                                         GtkWidget      *widget);
-static void compose_actions_execute_cb (Compose        *compose,
+static void msgview_actions_execute_cb (MessageView    *msgview, 
                                         guint           action_nb,
-                                        GtkWidget      *widget);
+                                        GtkWidget      *widget);
+static void message_actions_execute    (MessageView    *msgview,
+                                        guint           action_nb,
+                                        GtkCTree       *ctree);
 static guint get_action_type           (gchar          *action);
 
 static gboolean execute_actions                (gchar          *action, 
-                                        GtkWidget      *window,
                                         GtkCTree       *ctree, 
                                         GtkWidget      *text,
+                                        GdkFont        *msgfont,
+                                        gint            body_pos,
                                         MimeView       *mimeview);
 
 static gchar *parse_action_cmd         (gchar          *action,
                                         MsgInfo        *msginfo,
                                         GtkCTree       *ctree,
                                         MimeView       *mimeview);
-static GString *parse_append_filename  (GString        *cmd,
+static gboolean parse_append_filename  (GString        **cmd,
                                         MsgInfo        *msginfo);
 
-static GString *parse_append_msgpart   (GString        *cmd,
+static gboolean parse_append_msgpart   (GString        **cmd,
                                         MsgInfo        *msginfo,
                                         MimeView       *mimeview);
 
 ChildInfo *fork_child                  (gchar          *cmd,
                                         gint            action_type,
                                         GtkWidget      *text,
+                                        GdkFont        *msgfont,
+                                        gint            body_pos,
                                         Children       *children);
 
 static gint wait_for_children          (gpointer        data);
@@ -192,12 +204,13 @@ static void free_children         (Children       *children);
 static void childinfo_close_pipes      (ChildInfo      *child_info);
 
 static void create_io_dialog           (Children       *children);
-
 static void update_io_dialog           (Children       *children);
 
 static void hide_io_dialog_cb          (GtkWidget      *widget,
                                         gpointer        data);
-
+static gint io_dialog_key_pressed_cb   (GtkWidget      *widget,
+                                        GdkEventKey    *event,
+                                        gpointer        data);
 static void catch_output               (gpointer                data,
                                         gint                    source,
                                         GdkInputCondition       cond);
@@ -208,6 +221,7 @@ static void catch_status            (gpointer                data,
                                         gint                    source,
                                         GdkInputCondition       cond);
 
+
 void prefs_actions_open(MainWindow *mainwin)
 {
 #if 0
@@ -255,9 +269,7 @@ static void prefs_actions_create(MainWindow *mainwin)
        GtkWidget *cond_scrolledwin;
        GtkWidget *cond_clist;
 
-       GtkWidget *help_vbox;
-       GtkWidget *help_label;
-       GtkWidget *help_toggle;
+       GtkWidget *help_button;
 
        GtkWidget *btn_vbox;
        GtkWidget *up_btn;
@@ -265,7 +277,7 @@ static void prefs_actions_create(MainWindow *mainwin)
 
        gchar *title[1];
 
-       debug_print(_("Creating actions setting window...\n"));
+       debug_print("Creating actions configuration window...\n");
 
        window = gtk_window_new (GTK_WINDOW_DIALOG);
 
@@ -285,7 +297,7 @@ static void prefs_actions_create(MainWindow *mainwin)
        gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
        gtk_widget_grab_default(ok_btn);
 
-       gtk_window_set_title(GTK_WINDOW(window), _("Actions setting"));
+       gtk_window_set_title(GTK_WINDOW(window), _("Actions configuration"));
        gtk_signal_connect(GTK_OBJECT(window), "delete_event",
                           GTK_SIGNAL_FUNC(prefs_actions_deleted), NULL);
        gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
@@ -324,29 +336,6 @@ static void prefs_actions_create(MainWindow *mainwin)
 
        gtk_widget_show_all(entry_vbox);
 
-       help_vbox = gtk_vbox_new(FALSE, 8);
-       gtk_box_pack_start(GTK_BOX(vbox1), help_vbox, FALSE, FALSE, 0);
-
-       help_label = gtk_label_new
-               (_("Menu name:\n"
-                  " Use / in menu name to make submenus.\n"
-                  "Command line:\n"
-                  " Begin with:\n"
-                  "   | to send message body or selection to command\n"
-                  "   > to send user provided text to command\n"
-                  "   * to send user provided hidden text to command\n"
-                  " End with:\n"
-                  "   | to replace message body or selection with command output\n"
-                  "   & to run command asynchronously\n"
-                  " Use %f for message file name\n"
-                  "   %F for the list of the file names of selected messages\n"
-                  "   %p for the selected message part."));
-       gtk_misc_set_alignment(GTK_MISC(help_label), 0, 0.5);
-       gtk_label_set_justify(GTK_LABEL(help_label), GTK_JUSTIFY_LEFT);
-       gtk_widget_show(help_label);
-       gtk_box_pack_start(GTK_BOX(help_vbox), help_label, FALSE, FALSE, 0);
-       gtk_widget_hide(help_vbox);
-
        /* register / substitute / delete */
 
        reg_hbox = gtk_hbox_new(FALSE, 4);
@@ -362,13 +351,13 @@ static void prefs_actions_create(MainWindow *mainwin)
        gtk_widget_show(btn_hbox);
        gtk_box_pack_start(GTK_BOX(reg_hbox), btn_hbox, FALSE, FALSE, 0);
 
-       reg_btn = gtk_button_new_with_label(_("Register"));
+       reg_btn = gtk_button_new_with_label(_("Add"));
        gtk_widget_show(reg_btn);
        gtk_box_pack_start(GTK_BOX(btn_hbox), reg_btn, FALSE, TRUE, 0);
        gtk_signal_connect(GTK_OBJECT(reg_btn), "clicked",
                           GTK_SIGNAL_FUNC(prefs_actions_register_cb), NULL);
 
-       subst_btn = gtk_button_new_with_label(_(" Substitute "));
+       subst_btn = gtk_button_new_with_label(_("  Replace  "));
        gtk_widget_show(subst_btn);
        gtk_box_pack_start(GTK_BOX(btn_hbox), subst_btn, FALSE, TRUE, 0);
        gtk_signal_connect(GTK_OBJECT(subst_btn), "clicked",
@@ -381,11 +370,11 @@ static void prefs_actions_create(MainWindow *mainwin)
        gtk_signal_connect(GTK_OBJECT(del_btn), "clicked",
                           GTK_SIGNAL_FUNC(prefs_actions_delete_cb), NULL);
 
-       help_toggle = gtk_toggle_button_new_with_label(_(" Syntax help "));
-       gtk_widget_show(help_toggle);
-       gtk_box_pack_end(GTK_BOX(reg_hbox), help_toggle, FALSE, FALSE, 0);
-       gtk_signal_connect(GTK_OBJECT(help_toggle), "toggled",
-                          GTK_SIGNAL_FUNC(prefs_actions_help_cb), help_vbox);
+       help_button = gtk_button_new_with_label(_(" Syntax help "));
+       gtk_widget_show(help_button);
+       gtk_box_pack_end(GTK_BOX(reg_hbox), help_button, FALSE, FALSE, 0);
+       gtk_signal_connect(GTK_OBJECT(help_button), "clicked",
+                          GTK_SIGNAL_FUNC(prefs_actions_help_cb), NULL);
 
        cond_hbox = gtk_hbox_new(FALSE, 8);
        gtk_widget_show(cond_hbox);
@@ -400,7 +389,7 @@ static void prefs_actions_create(MainWindow *mainwin)
                                       GTK_POLICY_AUTOMATIC,
                                       GTK_POLICY_AUTOMATIC);
 
-       title[0] = _("Registered actions");
+       title[0] = _("Current actions");
        cond_clist = gtk_clist_new_with_titles(1, title);
        gtk_widget_show(cond_clist);
        gtk_container_add(GTK_CONTAINER (cond_scrolledwin), cond_clist);
@@ -442,13 +431,6 @@ static void prefs_actions_create(MainWindow *mainwin)
        actions.actions_clist = cond_clist;
 }
 
-static void prefs_actions_help_cb(GtkWidget *w, gpointer data)
-{
-       if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)))
-               gtk_widget_show(GTK_WIDGET(data));
-       else
-               gtk_widget_hide(GTK_WIDGET(data));
-}
 
 void prefs_actions_read_config(void)
 {
@@ -457,7 +439,7 @@ void prefs_actions_read_config(void)
        gchar buf[PREFSBUFSIZE];
        gchar *act;
 
-       debug_print(_("Reading actions configurations...\n"));
+       debug_print("Reading actions configurations...\n");
 
        rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ACTIONS_RC, NULL);
        if ((fp = fopen(rcpath, "rb")) == NULL) {
@@ -492,11 +474,11 @@ void prefs_actions_write_config(void)
        PrefFile *pfile;
        GSList *cur;
 
-       debug_print(_("Writing actions configuration...\n"));
+       debug_print("Writing actions configuration...\n");
 
        rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ACTIONS_RC, NULL);
        if ((pfile= prefs_write_open(rcpath)) == NULL) {
-               g_warning(_("failed to write configuration to file\n"));
+               g_warning("failed to write configuration to file\n");
                g_free(rcpath);
                return;
        }
@@ -506,7 +488,7 @@ void prefs_actions_write_config(void)
                if (fputs(act, pfile->fp) == EOF ||
                    fputc('\n', pfile->fp) == EOF) {
                        FILE_OP_ERROR(rcpath, "fputs || fputc");
-                       prefs_write_close_revert(pfile);
+                       prefs_file_close_revert(pfile);
                        g_free(rcpath);
                        return;
                }
@@ -514,8 +496,8 @@ void prefs_actions_write_config(void)
        
        g_free(rcpath);
 
-       if (prefs_write_close(pfile) < 0) {
-               g_warning(_("failed to write configuration to file\n"));
+       if (prefs_file_close(pfile) < 0) {
+               g_warning("failed to write configuration to file\n");
                return;
        }
 }
@@ -563,11 +545,12 @@ static guint get_action_type(gchar *action)
                } else if (p[0] == '|') {
                        if (p[1] == 0x00)
                                action_type |= ACTION_PIPE_OUT;
+               } else if (p[0] == '>') {
+                       if (p[1] == 0x00)
+                               action_type |= ACTION_INSERT;
                } else if (p[0] == '&') {
                        if (p[1] == 0x00)
                                action_type |= ACTION_ASYNC;
-                       else
-                               action_type = ACTION_ERROR;
                }
                p++;
        }
@@ -578,7 +561,7 @@ static guint get_action_type(gchar *action)
 static gchar *parse_action_cmd(gchar *action, MsgInfo *msginfo,
                               GtkCTree *ctree, MimeView *mimeview)
 {
-       GString *cmd, *tmpcmd;
+       GString *cmd;
        gchar *p;
        GList *cur;
        MsgInfo *msg;
@@ -590,11 +573,15 @@ static gchar *parse_action_cmd(gchar *action, MsgInfo *msginfo,
 
        cmd = g_string_sized_new(strlen(action));
 
-       while (p[0] && !(p[0] == '|' && !p[1]) && p[0] != '&') {
+       while (p[0] &&
+              !((p[0] == '|' || p[0] == '>' || p[0] == '&') && !p[1])) {
                if (p[0] == '%' && p[1]) {
                        switch (p[1]) {
                        case 'f':
-                               cmd = parse_append_filename(cmd, msginfo);
+                               if (!parse_append_filename(&cmd, msginfo)) {
+                                       g_string_free(cmd, TRUE);
+                                       return NULL;
+                               }
                                p++;
                                break;
                        case 'F':
@@ -602,18 +589,18 @@ static gchar *parse_action_cmd(gchar *action, MsgInfo *msginfo,
                                     cur != NULL; cur = cur->next) {
                                        msg = gtk_ctree_node_get_row_data(ctree,
                                              GTK_CTREE_NODE(cur->data));
-                                       cmd = parse_append_filename(cmd, msg);
+                                       if (!parse_append_filename(&cmd, msg)) {
+                                               g_string_free(cmd, TRUE);
+                                               return NULL;
+                                       }
                                        if (cur->next)
                                                cmd = g_string_append_c(cmd, ' ');
                                }
                                p++;
                                break;
                        case 'p':
-                               tmpcmd = parse_append_msgpart
-                                       (cmd, msginfo, mimeview);
-                               if (tmpcmd)
-                                       cmd = tmpcmd;
-                               else {
+                               if (!parse_append_msgpart(&cmd, msginfo,
+                                                         mimeview)) {
                                        g_string_free(cmd, TRUE);
                                        return NULL;
                                }
@@ -639,23 +626,27 @@ static gchar *parse_action_cmd(gchar *action, MsgInfo *msginfo,
        return p;
 }
 
-static GString *parse_append_filename(GString *cmd, MsgInfo *msginfo)
+static gboolean parse_append_filename(GString **cmd, MsgInfo *msginfo)
 {
        gchar *filename;
 
-       g_return_val_if_fail(msginfo, cmd);
+       g_return_val_if_fail(msginfo, FALSE);
 
        filename = procmsg_get_message_file(msginfo);
 
        if (filename) {
-               cmd = g_string_append(cmd, filename);
+               *cmd = g_string_append(*cmd, filename);
                g_free(filename);
-       } 
+       } else {
+               alertpanel_error(_("Could not get message file %d"),
+                               msginfo->msgnum);
+               return FALSE;
+       }
 
-       return cmd;
+       return TRUE;
 }
 
-static GString *parse_append_msgpart(GString *cmd, MsgInfo *msginfo,
+static gboolean parse_append_msgpart(GString **cmd, MsgInfo *msginfo,
                                     MimeView *mimeview)
 {
        gchar    *filename;
@@ -669,35 +660,35 @@ static GString *parse_append_msgpart(GString *cmd, MsgInfo *msginfo,
                if ((fp = procmsg_open_message_decrypted(msginfo, &partinfo))
                    == NULL) {
                        alertpanel_error(_("Could not get message file."));
-                       return NULL;
+                       return FALSE;
                }
 #else
                if ((fp = procmsg_open_message(msginfo)) == NULL) {
                        alertpanel_error(_("Could not get message file."));
-                       return NULL;
+                       return FALSE;
                }
                partinfo = procmime_scan_mime_header(fp);
 #endif
                fclose(fp);
                if (!partinfo) {
-                       procmime_mimeinfo_free(partinfo);
+                       procmime_mimeinfo_free_all(partinfo);
                        alertpanel_error(_("Could not get message part."));
-                       return NULL;
+                       return FALSE;
                }
                filename = procmsg_get_message_file(msginfo);
        } else {
                if (!mimeview->opened) {
                        alertpanel_error(_("No message part selected."));
-                       return NULL;
+                       return FALSE;
                }
                if (!mimeview->file) {
                        alertpanel_error(_("No message file selected."));
-                       return NULL;
+                       return FALSE;
                }
                partinfo = gtk_ctree_node_get_row_data
                                (GTK_CTREE(mimeview->ctree),
                                 mimeview->opened);
-               g_return_val_if_fail(partinfo != NULL, cmd);
+               g_return_val_if_fail(partinfo != NULL, FALSE);
                filename = mimeview->file;
        }
        partname = procmime_get_tmp_file_name(partinfo);
@@ -705,21 +696,21 @@ static GString *parse_append_msgpart(GString *cmd, MsgInfo *msginfo,
        ret = procmime_get_part(partname, filename, partinfo); 
 
        if (!mimeview) {
-               procmime_mimeinfo_free(partinfo);
+               procmime_mimeinfo_free_all(partinfo);
                g_free(filename);
        }
 
        if (ret < 0) {
                alertpanel_error(_("Can't get part of multipart message"));
                g_free(partname);
-               return NULL;
+               return FALSE;
        }
 
-       cmd = g_string_append(cmd,partname);
+       *cmd = g_string_append(*cmd, partname);
 
        g_free(partname);
 
-       return cmd;
+       return TRUE;
 }
 
 static void prefs_actions_set_dialog(void)
@@ -999,6 +990,21 @@ void update_compose_actions_menu(GtkItemFactory *ifactory,
                            compose);
 }
 
+
+void actions_execute(gpointer data, 
+                    guint action_nb,
+                    GtkWidget *widget,
+                    gint source)
+{
+       if (source == TOOLBAR_MAIN) 
+               mainwin_actions_execute_cb((MainWindow*)data, action_nb, widget);
+       else if (source == TOOLBAR_COMPOSE)
+               compose_actions_execute_cb((Compose*)data, action_nb, widget);
+       else if (source == TOOLBAR_MSGVIEW)
+               msgview_actions_execute_cb((MessageView*)data, action_nb, widget);      
+}
+
+
 static void update_actions_menu(GtkItemFactory *ifactory,
                                gchar *branch_path,
                                gpointer callback,
@@ -1069,17 +1075,33 @@ static void compose_actions_execute_cb(Compose *compose, guint action_nb,
                return;
        }
 
-       execute_actions(action, compose->window, NULL, compose->text, NULL);
+       execute_actions(action, NULL, compose->text, NULL, 0, NULL);
 }
 
 static void mainwin_actions_execute_cb(MainWindow *mainwin, guint action_nb,
                                       GtkWidget *widget)
 {
-       MessageView *messageview = mainwin->messageview;
-       GtkWidget   *text = NULL;
+       message_actions_execute(mainwin->messageview, action_nb,
+                               GTK_CTREE(mainwin->summaryview->ctree));
+}
+
+static void msgview_actions_execute_cb(MessageView *msgview, guint action_nb,
+                                      GtkWidget *widget)
+{
+       message_actions_execute(msgview, action_nb, NULL);
+       
+}
+
+static void message_actions_execute(MessageView *msgview, guint action_nb,
+                                   GtkCTree *ctree)
+{
+       TextView    *textview = NULL;
        gchar       *buf,
                    *action;
        MimeView    *mimeview = NULL;
+       GdkFont     *msgfont  = NULL;
+       guint        body_pos = 0;
+       GtkWidget   *text     = NULL;
 
        g_return_if_fail(action_nb < g_slist_length(prefs_common.actions_list));
 
@@ -1091,29 +1113,37 @@ static void mainwin_actions_execute_cb(MainWindow *mainwin, guint action_nb,
        /* Point to the beginning of the command-line */
        action += 2;
 
-       switch (messageview->type) {
+       switch (msgview->type) {
        case MVIEW_TEXT:
-               if (messageview->textview && messageview->textview->text)
-                       text = messageview->textview->text;
+               if (msgview->textview && msgview->textview->text)
+                       textview = msgview->textview;
                break;
        case MVIEW_MIME:
-               if (messageview->mimeview &&
-                   messageview->mimeview->type == MIMEVIEW_TEXT &&
-                   messageview->mimeview->textview &&
-                   messageview->mimeview->textview->text) {
-                       text = messageview->mimeview->textview->text;
-                       mimeview = messageview->mimeview;
-               }
+               if (msgview->mimeview) {
+                       mimeview = msgview->mimeview;
+                       if (msgview->mimeview->type == MIMEVIEW_TEXT &&
+                           msgview->mimeview->textview &&
+                           msgview->mimeview->textview->text)
+                               textview = msgview->mimeview->textview;
+               } 
                break;
        }
 
-       execute_actions(action, mainwin->window,
-                       GTK_CTREE(mainwin->summaryview->ctree), text, mimeview);
+       if (textview) {
+               text     = textview->text;
+               msgfont  = textview->msgfont;
+               body_pos = textview->body_pos;
+       }
+       
+       execute_actions(action, ctree, text, msgfont, body_pos, mimeview);
 }
 
-static gboolean execute_actions(gchar *action, GtkWidget *window,
-                               GtkCTree *ctree, GtkWidget *text,
-                               MimeView *mimeview)
+static gboolean execute_actions(gchar    *action, 
+                               GtkCTree  *ctree,
+                               GtkWidget *text, 
+                               GdkFont   *msgfont,
+                               gint       body_pos,
+                               MimeView  *mimeview)
 {
        GList *cur, *selection = NULL;
        GSList *children_list = NULL;
@@ -1141,17 +1171,13 @@ static gboolean execute_actions(gchar *action, GtkWidget *window,
                selection_len = g_list_length(selection);
        }
 
-       if (action_type & (ACTION_PIPE_OUT | ACTION_PIPE_IN)) {
+       if (action_type & (ACTION_PIPE_OUT | ACTION_PIPE_IN | ACTION_INSERT)) {
                if (ctree && selection_len > 1)
                        return FALSE; /* ERR: pipe + multiple selection */
                if (!text)
                        return FALSE; /* ERR: pipe and no displayed text */
        }
 
-       if (!(action_type & ACTION_ASYNC) && window) {
-               gtk_widget_set_sensitive(window, FALSE);
-       }
-
        children = g_new0(Children, 1);
 
        if (action_type & ACTION_SINGLE) {
@@ -1165,11 +1191,12 @@ static gboolean execute_actions(gchar *action, GtkWidget *window,
                        cmd = parse_action_cmd(action, msginfo, ctree,
                                               mimeview);
                        if (!cmd) {
-                               debug_print(_("Action command error\n"));
+                               debug_print("Action command error\n");
                                is_ok  = FALSE; /* ERR: incorrect command */
                                break;
                        }
                        if ((child_info = fork_child(cmd, action_type, text,
+                                                    msgfont, body_pos,
                                                     children))) {
                                children_list = g_slist_append(children_list,
                                                               child_info);
@@ -1184,6 +1211,7 @@ static gboolean execute_actions(gchar *action, GtkWidget *window,
                cmd = parse_action_cmd(action, NULL, ctree, mimeview);
                if (cmd) {
                        if ((child_info = fork_child(cmd, action_type, text,
+                                                    msgfont, body_pos,
                                                     children))) {
                                children_list = g_slist_append(children_list,
                                                               child_info);
@@ -1199,14 +1227,10 @@ static gboolean execute_actions(gchar *action, GtkWidget *window,
        if (!children_list) {
                 /* If not waiting for children, return */
                g_free(children);
-               if (!(action_type & ACTION_ASYNC) && window) {
-                       gtk_widget_set_sensitive(window, TRUE);
-               }
        } else {
                GSList *cur;
 
                children->action  = g_strdup(action);
-               children->window  = window;
                children->dialog  = NULL;
                children->list    = children_list;
                children->nb      = g_slist_length(children_list);
@@ -1218,14 +1242,15 @@ static gboolean execute_actions(gchar *action, GtkWidget *window,
                                              GDK_INPUT_READ,
                                              catch_status, child_info);
                }
+
+               create_io_dialog(children);
        }
-       create_io_dialog(children);
 
        return is_ok;
 }
 
 ChildInfo *fork_child(gchar *cmd, gint action_type, GtkWidget *text,
-                     Children *children)
+                     GdkFont *msgfont, gint body_pos, Children *children)
 {
        gint chld_in[2], chld_out[2], chld_err[2], chld_status[2];
        gchar *cmdline[4];
@@ -1259,7 +1284,7 @@ ChildInfo *fork_child(gchar *cmd, gint action_type, GtkWidget *text,
                }
        }
 
-       debug_print(_("Forking child and grandchild.\n"));
+       debug_print("Forking child and grandchild.\n");
 
        pid = fork();
        if (pid == 0) { /* Child */
@@ -1318,11 +1343,10 @@ ChildInfo *fork_child(gchar *cmd, gint action_type, GtkWidget *text,
                                close(chld_err[1]);
                                close(chld_status[0]);
                        }
-
-                       debug_print(_("Child: Waiting for grandchild\n"));
-                       waitpid(gch_pid, NULL, 0);
-                       debug_print(_("Child: grandchild ended\n"));
                        if (sync) {
+                               debug_print("Child: Waiting for grandchild\n");
+                               waitpid(gch_pid, NULL, 0);
+                               debug_print("Child: grandchild ended\n");
                                write(chld_status[1], "0\n", 2);
                                close(chld_status[1]);
                        }
@@ -1337,8 +1361,10 @@ ChildInfo *fork_child(gchar *cmd, gint action_type, GtkWidget *text,
 
        /* Parent */
 
-       if (!sync)
+       if (!sync) {
+               waitpid(pid, NULL, 0);
                return NULL;
+       }
 
        close(chld_in[0]);
        if (!(action_type & (ACTION_PIPE_IN | ACTION_OPEN_IN | ACTION_HIDE_IN)))
@@ -1369,12 +1395,13 @@ ChildInfo *fork_child(gchar *cmd, gint action_type, GtkWidget *text,
        child_info->tag_err     = gdk_input_add(chld_err[0], GDK_INPUT_READ,
                                                catch_output, child_info);
 
-       if (!(action_type & (ACTION_PIPE_IN | ACTION_PIPE_OUT)))
+       if (!(action_type & (ACTION_PIPE_IN | ACTION_PIPE_OUT | ACTION_INSERT)))
                return child_info;
 
        child_info->text        = text;
+       child_info->msgfont     = msgfont;
 
-       start = 0;
+       start = body_pos;
        end   = gtk_stext_get_length(GTK_STEXT(text));
 
        if (GTK_EDITABLE(text)->has_selection) {
@@ -1423,7 +1450,7 @@ static void kill_children_cb(GtkWidget *widget, gpointer data)
 
        for (cur = children->list; cur; cur = cur->next) {
                child_info = (ChildInfo *)(cur->data);
-               debug_print(_("Killing child group id %d\n"), child_info->pid);
+               debug_print("Killing child group id %d\n", child_info->pid);
                if (child_info->pid && kill(-child_info->pid, SIGTERM) < 0)
                        perror("kill");
        }
@@ -1458,11 +1485,11 @@ static gint wait_for_children(gpointer data)
                return FALSE;
 
        if (!children->dialog) {
-               gtk_widget_set_sensitive(children->window, TRUE);
                free_children(children);
        } else if (!children->output) {
                gtk_widget_destroy(children->dialog);
        }
+
        return FALSE;
 }
 
@@ -1489,7 +1516,6 @@ static void hide_io_dialog_cb(GtkWidget *w, gpointer data)
        Children *children = (Children *)data;
 
        if (!children->nb) {
-               gtk_widget_set_sensitive(children->window, TRUE);
                gtk_signal_disconnect_by_data(GTK_OBJECT(children->dialog),
                                              children);
                gtk_widget_destroy(children->dialog);
@@ -1497,6 +1523,15 @@ static void hide_io_dialog_cb(GtkWidget *w, gpointer data)
        }
 }
 
+static gint io_dialog_key_pressed_cb(GtkWidget *widget,
+                                    GdkEventKey        *event,
+                                    gpointer    data)
+{
+       if (event && event->keyval == GDK_Escape)
+               hide_io_dialog_cb(widget, data);
+       return TRUE;    
+}
+
 static void childinfo_close_pipes(ChildInfo *child_info)
 {
        if (child_info->tag_in > 0)
@@ -1516,7 +1551,7 @@ static void free_children(Children *children)
        GSList *cur;
        ChildInfo *child_info;
 
-       debug_print(_("Freeing children data %p\n"), children);
+       debug_print("Freeing children data %p\n", children);
 
        g_free(children->action);
        for (cur = children->list; cur;) {
@@ -1534,14 +1569,18 @@ static void update_io_dialog(Children *children)
 {
        GSList *cur;
 
-       debug_print(_("Updating actions input/output dialog.\n"));
+       debug_print("Updating actions input/output dialog.\n");
 
        if (!children->nb) {
                gtk_widget_set_sensitive(children->abort_btn, FALSE);
-               gtk_widget_set_sensitive(children->hide_btn, TRUE);
-               gtk_widget_set_sensitive(children->window, TRUE);
+               gtk_widget_set_sensitive(children->close_btn, TRUE);
                if (children->input_hbox)
                        gtk_widget_set_sensitive(children->input_hbox, FALSE);
+               gtk_widget_grab_focus(children->close_btn);
+               gtk_signal_connect(GTK_OBJECT(children->dialog), 
+                                  "key_press_event",
+                                  GTK_SIGNAL_FUNC(io_dialog_key_pressed_cb),
+                                  children);
        }
 
        if (children->output) {
@@ -1579,6 +1618,7 @@ static void update_io_dialog(Children *children)
 static void create_io_dialog(Children *children)
 {
        GtkWidget *dialog;
+       GtkWidget *vbox;
        GtkWidget *entry = NULL;
        GtkWidget *input_hbox = NULL;
        GtkWidget *send_button;
@@ -1587,29 +1627,37 @@ static void create_io_dialog(Children *children)
        GtkWidget *scrolledwin;
        GtkWidget *hbox;
        GtkWidget *abort_button;
-       GtkWidget *hide_button;
+       GtkWidget *close_button;
 
-       debug_print(_("Creating actions dialog\n"));
+       debug_print("Creating action IO dialog\n");
 
        dialog = gtk_dialog_new();
-       label = gtk_label_new(children->action);
-       gtk_misc_set_padding(GTK_MISC(label), 8, 8);
-       gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, FALSE,
-                          FALSE, 0);
-
-       gtk_window_set_title(GTK_WINDOW(dialog), _("Actions' input/output"));
+       gtk_container_set_border_width
+               (GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), 5);
+       gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
+       gtk_window_set_title(GTK_WINDOW(dialog), _("Action's input/output"));
+       gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
+       manage_window_set_transient(GTK_WINDOW(dialog));
        gtk_signal_connect(GTK_OBJECT(dialog), "delete_event",
                        GTK_SIGNAL_FUNC(delete_io_dialog_cb), children);
        gtk_signal_connect(GTK_OBJECT(dialog), "destroy",
                        GTK_SIGNAL_FUNC(hide_io_dialog_cb),
                        children);
 
+       vbox = gtk_vbox_new(FALSE, 8);
+       gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), vbox);
+       gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
+       gtk_widget_show(vbox);
+
+       label = gtk_label_new(children->action);
+       gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+       gtk_widget_show(label);
+
        scrolledwin = gtk_scrolled_window_new(NULL, NULL);
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
                                       GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
-       gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), scrolledwin, TRUE,
-                          TRUE, 0);
-       gtk_widget_set_usize(scrolledwin, 600, 200);
+       gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0);
+       gtk_widget_set_usize(scrolledwin, 480, 200);
        gtk_widget_hide(scrolledwin);
 
        text = gtk_text_new(gtk_scrolled_window_get_hadjustment
@@ -1625,42 +1673,35 @@ static void create_io_dialog(Children *children)
                gtk_widget_show(input_hbox);
 
                entry = gtk_entry_new();
+               gtk_widget_set_usize(entry, 320, -1);
                gtk_signal_connect(GTK_OBJECT(entry), "activate",
                                   GTK_SIGNAL_FUNC(send_input), children);
-               gtk_box_pack_start(GTK_BOX(input_hbox), entry, TRUE, TRUE, 8);
+               gtk_box_pack_start(GTK_BOX(input_hbox), entry, TRUE, TRUE, 0);
                if (children->open_in & ACTION_HIDE_IN)
                        gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
                gtk_widget_show(entry);
 
-               send_button = gtk_button_new_with_label(_("Send"));
+               send_button = gtk_button_new_with_label(_(" Send "));
                gtk_signal_connect(GTK_OBJECT(send_button), "clicked",
                                   GTK_SIGNAL_FUNC(send_input), children);
                gtk_box_pack_start(GTK_BOX(input_hbox), send_button, FALSE,
-                                  FALSE, 8);
+                                  FALSE, 0);
                gtk_widget_show(send_button);
 
-               gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
-                               input_hbox, FALSE, FALSE, 8);
+               gtk_box_pack_start(GTK_BOX(vbox), input_hbox, FALSE, FALSE, 0);
                gtk_widget_grab_focus(entry);
        }
 
-       hbox = gtk_hbox_new(TRUE, 0);
-       gtk_widget_show(hbox);
-
-       abort_button = gtk_button_new_with_label(_("Abort actions"));
+       gtkut_button_set_create(&hbox, &abort_button, _("Abort"),
+                               &close_button, _("Close"), NULL, NULL);
        gtk_signal_connect(GTK_OBJECT(abort_button), "clicked",
                        GTK_SIGNAL_FUNC(kill_children_cb), children);
-       gtk_box_pack_start(GTK_BOX(hbox), abort_button, TRUE, TRUE, 8);
-       gtk_widget_show(abort_button);
-
-       hide_button = gtk_button_new_with_label(_("Close window"));
-       gtk_signal_connect(GTK_OBJECT(hide_button), "clicked",
+       gtk_signal_connect(GTK_OBJECT(close_button), "clicked",
                        GTK_SIGNAL_FUNC(hide_io_dialog_cb), children);
-       gtk_box_pack_start(GTK_BOX(hbox), hide_button, TRUE, TRUE, 8);
-       gtk_widget_show(hide_button);
+       gtk_widget_show(hbox);
 
        if (children->nb)
-               gtk_widget_set_sensitive(hide_button, FALSE);
+               gtk_widget_set_sensitive(close_button, FALSE);
 
        gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), hbox);
 
@@ -1670,7 +1711,7 @@ static void create_io_dialog(Children *children)
        children->input_hbox  = children->open_in ? input_hbox : NULL;
        children->input_entry = children->open_in ? entry : NULL;
        children->abort_btn   = abort_button;
-       children->hide_btn    = hide_button;
+       children->close_btn   = close_button;
 
        gtk_widget_show(dialog);
 }
@@ -1684,7 +1725,7 @@ static void catch_status(gpointer data, gint source, GdkInputCondition cond)
        gdk_input_remove(child_info->tag_status);
 
        c = read(source, &buf, 1);
-       debug_print(_("Child returned %c\n"), buf);
+       debug_print("Child returned %c\n", buf);
 
        waitpid(-child_info->pid, NULL, 0);
        childinfo_close_pipes(child_info);
@@ -1700,7 +1741,7 @@ static void catch_input(gpointer data, gint source, GdkInputCondition cond)
        gchar *input;
        gint c;
 
-       debug_print(_("Sending input to grand child.\n"));
+       debug_print("Sending input to grand child.\n");
        if (!(cond && GDK_INPUT_WRITE))
                return;
 
@@ -1717,7 +1758,7 @@ static void catch_input(gpointer data, gint source, GdkInputCondition cond)
 
        gtk_entry_set_text(GTK_ENTRY(children->input_entry), "");
        gtk_widget_set_sensitive(children->input_hbox, TRUE);
-       debug_print(_("Input to grand child sent.\n"));
+       debug_print("Input to grand child sent.\n");
 }
 
 static void catch_output(gpointer data, gint source, GdkInputCondition cond)
@@ -1726,8 +1767,8 @@ static void catch_output(gpointer data, gint source, GdkInputCondition cond)
        gint c, i;
        gchar buf[PREFSBUFSIZE];
 
-       debug_print(_("Catching grand child's output.\n"));
-       if (child_info->type & ACTION_PIPE_OUT
+       debug_print("Catching grand child's output.\n");
+       if (child_info->type & (ACTION_PIPE_OUT | ACTION_INSERT)
            && source == child_info->chld_out) {
                gboolean is_selection = FALSE;
                GtkWidget *text = child_info->text;
@@ -1738,8 +1779,8 @@ static void catch_output(gpointer data, gint source, GdkInputCondition cond)
                        c = read(source, buf, PREFSBUFSIZE - 1);
                        if (c == 0)
                                break;
-                       gtk_stext_insert(GTK_STEXT(text), NULL, NULL, NULL,
-                                        buf, c);
+                       gtk_stext_insert(GTK_STEXT(text), child_info->msgfont,
+                                        NULL, NULL, buf, c);
                }
                if (is_selection) {
                        /* Using the select_region draws things. Should not.
@@ -1758,4 +1799,44 @@ static void catch_output(gpointer data, gint source, GdkInputCondition cond)
                if (c > 0)
                        child_info->new_out = TRUE;
        }
+       wait_for_children(child_info->children);
+}
+
+/*
+ * Strings describing action format strings
+ * 
+ * When adding new lines, remember to put one string for each line
+ */
+static gchar *actions_desc_strings[] = {
+       N_("Menu name:"), NULL,
+       "      ",   N_("Use / in menu name to make submenus."),
+       "", NULL,
+       N_("Command line:"), NULL,
+       N_("* Begin with:"), NULL,
+       "     |",   N_("to send message body or selection to command"),
+       "     >",   N_("to send user provided text to command"),
+       "     *",   N_("to send user provided hidden text to command"),
+       N_("* End with:"), NULL, 
+       "     |",   N_("to replace message body or selection with command output"),
+       "     >",   N_("to insert command's output without replacing old text"),
+       "     &",   N_("to run command asynchronously"),
+       N_("* Use:"), NULL, 
+       "     %f",  N_("for message file name"),
+       "     %F",  N_("for the list of the file names of selected messages"),
+       "     %p",  N_("for the selected message MIME part."),
+       NULL
+};
+
+
+static DescriptionWindow actions_desc_win = { 
+        NULL, 
+        2,
+        N_("Description of symbols"),
+        actions_desc_strings
+};
+
+
+static void prefs_actions_help_cb(GtkWidget *w, gpointer data)
+{
+       description_window_create(&actions_desc_win);
 }