Improve synchronous action exit code (fixes long delay after action exit on RH9
[claws.git] / src / action.c
index 04a124965271fddf9ccee685ff0667b7b1d29ffb..fc5bc801a599c859b06c77b1a860857e17955b38 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2003 Hiroyuki Yamamoto
+ * Copyright (C) 1999-2003 Hiroyuki Yamamoto & The Sylpheed Claws Team
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include "mainwindow.h"
 #include "prefs_common.h"
 #include "alertpanel.h"
+#include "inputdialog.h"
 #include "action.h"
 #include "compose.h"
 #include "procmsg.h"
 #include "gtkstext.h"
-#include "mimeview.h"
 #include "textview.h"
 
 typedef struct _Children               Children;
@@ -60,22 +60,27 @@ struct _Children
        GtkWidget       *text;
        GtkWidget       *input_entry;
        GtkWidget       *input_hbox;
+       GtkWidget       *progress_bar;
        GtkWidget       *abort_btn;
        GtkWidget       *close_btn;
        GtkWidget       *scrolledwin;
 
        gchar           *action;
+       ActionType       action_type;
        GSList          *list;
        gint             nb;
+       gint             initial_nb;
        gint             open_in;
        gboolean         output;
+
+       GtkWidget       *msg_text;
+       GdkFont         *msgfont;
 };
 
 struct _ChildInfo
 {
        Children        *children;
        gchar           *cmd;
-       guint            type;
        pid_t            pid;
        gint             chld_in;
        gint             chld_out;
@@ -86,19 +91,11 @@ struct _ChildInfo
        gint             tag_err;
        gint             tag_status;
        gint             new_out;
-       GString         *output;
-       GtkWidget       *text;
-       GdkFont         *msgfont;
-};
 
-struct _UserStringDialog
-{
-       GtkWidget       *dialog;
-       GtkWidget       *entry;
-       gchar           *user_str;
+       GString         *output;
 };
 
-static void update_actions_menu                (GtkItemFactory *ifactory,
+static void action_update_menu         (GtkItemFactory *ifactory,
                                         gchar          *branch_path,
                                         gpointer        callback,
                                         gpointer        data);
@@ -113,19 +110,19 @@ static void msgview_actions_execute_cb    (MessageView    *msgview,
                                         GtkWidget      *widget);
 static void message_actions_execute    (MessageView    *msgview,
                                         guint           action_nb,
-                                        GtkCTree       *ctree);
+                                        GSList         *msg_list);
 
 static gboolean execute_actions                (gchar          *action, 
-                                        GtkCTree       *ctree
+                                        GSList         *msg_list
                                         GtkWidget      *text,
                                         GdkFont        *msgfont,
                                         gint            body_pos,
-                                        MimeView       *mimeview);
+                                        MimeInfo       *partinfo);
 
 static gchar *parse_action_cmd         (gchar          *action,
                                         MsgInfo        *msginfo,
-                                        GtkCTree       *ctree,
-                                        MimeView       *mimeview,
+                                        GSList         *msg_list,
+                                        MimeInfo       *partinfo,
                                         const gchar    *user_str,
                                         const gchar    *user_hidden_str,
                                         const gchar    *sel_str);
@@ -134,16 +131,13 @@ static gboolean parse_append_filename     (GString        *cmd,
 
 static gboolean parse_append_msgpart   (GString        *cmd,
                                         MsgInfo        *msginfo,
-                                        MimeView       *mimeview);
+                                        MimeInfo       *partinfo);
 
 static ChildInfo *fork_child           (gchar          *cmd,
-                                        gint            action_type,
-                                        GtkWidget      *text,
-                                        GdkFont        *msgfont,
-                                        gint            body_pos,
+                                        const gchar    *msg_str,
                                         Children       *children);
 
-static gint wait_for_children          (gpointer        data);
+static gint wait_for_children          (Children       *children);
 
 static void free_children              (Children       *children);
 
@@ -168,22 +162,11 @@ static void catch_status          (gpointer                data,
                                         gint                    source,
                                         GdkInputCondition       cond);
 
-static gboolean user_string_dialog_delete_cb
-                                       (GtkWidget      *widget,
-                                        GdkEvent       *event,
-                                        gpointer        data);
-static void user_string_dialog_destroy_cb
-                                       (GtkWidget      *widget,
-                                        gpointer        data);
-static void user_string_dialog_activate_cb
-                                       (GtkWidget      *widget,
-                                        gpointer        data);
-
 static gchar *get_user_string          (const gchar    *action,
                                         ActionType      type);
 
 
-ActionType get_action_type(const gchar *action_str)
+ActionType action_get_type(const gchar *action_str)
 {
        const gchar *p;
        ActionType action_type = ACTION_NONE;
@@ -249,15 +232,14 @@ ActionType get_action_type(const gchar *action_str)
 }
 
 static gchar *parse_action_cmd(gchar *action, MsgInfo *msginfo,
-                              GtkCTree *ctree, MimeView *mimeview,
+                              GSList *msg_list, MimeInfo *partinfo,
                               const gchar *user_str,
                               const gchar *user_hidden_str,
                               const gchar *sel_str)
 {
        GString *cmd;
        gchar *p;
-       GList *cur;
-       MsgInfo *msg;
+       GSList *cur;
        
        p = action;
        
@@ -278,10 +260,10 @@ static gchar *parse_action_cmd(gchar *action, MsgInfo *msginfo,
                                p++;
                                break;
                        case 'F':
-                               for (cur = GTK_CLIST(ctree)->selection;
-                                    cur != NULL; cur = cur->next) {
-                                       msg = gtk_ctree_node_get_row_data(ctree,
-                                             GTK_CTREE_NODE(cur->data));
+                               for (cur = msg_list; cur != NULL;
+                                    cur = cur->next) {
+                                       MsgInfo *msg = (MsgInfo *)cur->data;
+
                                        if (!parse_append_filename(cmd, msg)) {
                                                g_string_free(cmd, TRUE);
                                                return NULL;
@@ -293,7 +275,7 @@ static gchar *parse_action_cmd(gchar *action, MsgInfo *msginfo,
                                break;
                        case 'p':
                                if (!parse_append_msgpart(cmd, msginfo,
-                                                         mimeview)) {
+                                                         partinfo)) {
                                        g_string_free(cmd, TRUE);
                                        return NULL;
                                }
@@ -355,68 +337,41 @@ static gboolean parse_append_filename(GString *cmd, MsgInfo *msginfo)
 }
 
 static gboolean parse_append_msgpart(GString *cmd, MsgInfo *msginfo,
-                                    MimeView *mimeview)
+                                    MimeInfo *partinfo)
 {
-       gchar    *filename;
-       gchar    *partname;
-       MimeInfo *partinfo;
-       gint      ret;
-       FILE     *fp;
-
-       if (!mimeview) {
-#if USE_GPGME
-               if ((fp = procmsg_open_message_decrypted(msginfo, &partinfo))
-                   == NULL) {
-                       alertpanel_error(_("Could not get message file."));
-                       return FALSE;
-               }
-#else
-               if ((fp = procmsg_open_message(msginfo)) == NULL) {
-                       alertpanel_error(_("Could not get message file."));
-                       return FALSE;
-               }
-               partinfo = procmime_scan_mime_header(fp);
-#endif
-               fclose(fp);
+       gboolean single_part = FALSE;
+       gchar *filename;
+       gchar *part_filename;
+       gint ret;
+
+       if (!partinfo) {
+               partinfo = procmime_scan_message(msginfo);
                if (!partinfo) {
-                       procmime_mimeinfo_free_all(partinfo);
                        alertpanel_error(_("Could not get message part."));
                        return FALSE;
                }
-               filename = procmsg_get_message_file(msginfo);
-       } else {
-               if (!mimeview->opened) {
-                       alertpanel_error(_("No message part selected."));
-                       return FALSE;
-               }
-               if (!mimeview->file) {
-                       alertpanel_error(_("No message file selected."));
-                       return FALSE;
-               }
-               partinfo = gtk_ctree_node_get_row_data
-                               (GTK_CTREE(mimeview->ctree),
-                                mimeview->opened);
-               g_return_val_if_fail(partinfo != NULL, FALSE);
-               filename = mimeview->file;
+
+               single_part = TRUE;
        }
-       partname = procmime_get_tmp_file_name(partinfo);
 
-       ret = procmime_get_part(partname, filename, partinfo); 
+       filename = procmsg_get_message_file_path(msginfo);
+       part_filename = procmime_get_tmp_file_name(partinfo);
 
-       if (!mimeview) {
+       ret = procmime_get_part(part_filename, filename, partinfo); 
+
+       if (single_part)
                procmime_mimeinfo_free_all(partinfo);
-               g_free(filename);
-       }
+       g_free(filename);
 
        if (ret < 0) {
                alertpanel_error(_("Can't get part of multipart message"));
-               g_free(partname);
+               g_free(part_filename);
                return FALSE;
        }
 
-       g_string_append(cmd, partname);
+       g_string_append(cmd, part_filename);
 
-       g_free(partname);
+       g_free(part_filename);
 
        return TRUE;
 }
@@ -434,28 +389,26 @@ void actions_execute(gpointer data,
                msgview_actions_execute_cb((MessageView*)data, action_nb, widget);      
 }
 
+void action_update_mainwin_menu(GtkItemFactory *ifactory, MainWindow *mainwin)
+{
+       action_update_menu(ifactory, "/Tools/Actions",
+                          mainwin_actions_execute_cb, mainwin);
+}
 
-void update_mainwin_actions_menu(GtkItemFactory *ifactory,
-                                MainWindow *mainwin)
+void action_update_msgview_menu(GtkItemFactory *ifactory, MessageView *msgview)
 {
-       update_actions_menu(ifactory, "/Tools/Actions",
-                           mainwin_actions_execute_cb,
-                           mainwin);
+       action_update_menu(ifactory, "/Tools/Actions",
+                          msgview_actions_execute_cb, msgview);
 }
 
-void update_compose_actions_menu(GtkItemFactory *ifactory,
-                                gchar *branch_path,
-                                Compose *compose)
+void action_update_compose_menu(GtkItemFactory *ifactory, Compose *compose)
 {
-       update_actions_menu(ifactory, branch_path,
-                           compose_actions_execute_cb,
-                           compose);
+       action_update_menu(ifactory, "/Tools/Actions",
+                          compose_actions_execute_cb, compose);
 }
 
-static void update_actions_menu(GtkItemFactory *ifactory,
-                               gchar *branch_path,
-                               gpointer callback,
-                               gpointer data)
+static void action_update_menu(GtkItemFactory *ifactory, gchar *branch_path,
+                              gpointer callback, gpointer data)
 {
        GtkWidget *menuitem;
        gchar *menu_path;
@@ -484,7 +437,7 @@ static void update_actions_menu(GtkItemFactory *ifactory,
                action   = g_strdup((gchar *)cur->data);
                action_p = strstr(action, ": ");
                if (action_p && action_p[2] &&
-                   get_action_type(&action_p[2]) != ACTION_ERROR) {
+                   action_get_type(&action_p[2]) != ACTION_ERROR) {
                        action_p[0] = 0x00;
                        menu_path = g_strdup_printf("%s/%s", branch_path,
                                                    action);
@@ -502,7 +455,7 @@ static void compose_actions_execute_cb(Compose *compose, guint action_nb,
                                       GtkWidget *widget)
 {
        gchar *buf, *action;
-       guint action_type;
+       ActionType action_type;
 
        g_return_if_fail(action_nb < g_slist_length(prefs_common.actions_list));
 
@@ -514,7 +467,7 @@ static void compose_actions_execute_cb(Compose *compose, guint action_nb,
        /* Point to the beginning of the command-line */
        action += 2;
 
-       action_type = get_action_type(action);
+       action_type = action_get_type(action);
        if (action_type & (ACTION_SINGLE | ACTION_MULTIPLE)) {
                alertpanel_warning
                        (_("The selected action cannot be used in the compose window\n"
@@ -528,27 +481,36 @@ static void compose_actions_execute_cb(Compose *compose, guint action_nb,
 static void mainwin_actions_execute_cb(MainWindow *mainwin, guint action_nb,
                                       GtkWidget *widget)
 {
-       message_actions_execute(mainwin->messageview, action_nb,
-                               GTK_CTREE(mainwin->summaryview->ctree));
+       GSList *msg_list;
+
+       msg_list = summary_get_selected_msg_list(mainwin->summaryview);
+       message_actions_execute(mainwin->messageview, action_nb, msg_list);
+       g_slist_free(msg_list);
 }
 
 static void msgview_actions_execute_cb(MessageView *msgview, guint action_nb,
                                       GtkWidget *widget)
 {
-       message_actions_execute(msgview, action_nb, NULL);
+       GSList *msg_list = NULL;
+
+       if (msgview->msginfo)
+               msg_list = g_slist_append(msg_list, msgview->msginfo);
+       message_actions_execute(msgview, action_nb, msg_list);
+       g_slist_free(msg_list);
 }
 
 static void message_actions_execute(MessageView *msgview, guint action_nb,
-                                   GtkCTree *ctree)
+                                   GSList *msg_list)
 {
-       TextView *textview = NULL;
-       MimeView *mimeview = NULL;
+       TextView *textview;
+       MimeInfo *partinfo;
        gchar *buf;
        gchar *action;
        GtkWidget *text = NULL;
        GdkFont *msgfont = NULL;
        guint body_pos = 0;
-
+       ActionType action_type;
+       
        g_return_if_fail(action_nb < g_slist_length(prefs_common.actions_list));
 
        buf = (gchar *)g_slist_nth_data(prefs_common.actions_list, action_nb);
@@ -559,66 +521,54 @@ static void message_actions_execute(MessageView *msgview, guint action_nb,
        /* Point to the beginning of the command-line */
        action += 2;
 
-       switch (msgview->type) {
-       case MVIEW_TEXT:
-               if (msgview->textview && msgview->textview->text)
-                       textview = msgview->textview;
-               break;
-       case MVIEW_MIME:
-               if (msgview->mimeview) {
-                       mimeview = msgview->mimeview;
-                       if (msgview->mimeview->type == MIMEVIEW_TEXT &&
-                           msgview->mimeview->textview &&
-                           msgview->mimeview->textview->text)
-                               textview = msgview->mimeview->textview;
-               }
-               break;
-       }
-
+       textview = messageview_get_current_textview(msgview);
        if (textview) {
                text     = textview->text;
                msgfont  = textview->msgfont;
                body_pos = textview->body_pos;
        }
+       partinfo = messageview_get_selected_mime_part(msgview);
+
+       /* this command will alter the message text */
+       action_type = action_get_type(action);
+       if (action_type & (ACTION_PIPE_OUT | ACTION_INSERT))
+               msgview->filtered = TRUE;
 
-       execute_actions(action, ctree, text, msgfont, body_pos, mimeview);
+       execute_actions(action, msg_list, text, msgfont, body_pos, partinfo);
 }
 
-static gboolean execute_actions(gchar *action, GtkCTree *ctree,
+static gboolean execute_actions(gchar *action, GSList *msg_list,
                                GtkWidget *text, GdkFont *msgfont,
-                               gint body_pos, MimeView *mimeview)
+                               gint body_pos, MimeInfo *partinfo)
 {
-       GList *cur, *selection = NULL;
        GSList *children_list = NULL;
        gint is_ok  = TRUE;
-       gint selection_len = 0;
+       gint msg_list_len;
        Children *children;
        ChildInfo *child_info;
-       gint action_type;
+       ActionType action_type;
        MsgInfo *msginfo;
        gchar *cmd;
+       guint start = 0, end = 0;
        gchar *sel_str = NULL;
+       gchar *msg_str = NULL;
        gchar *user_str = NULL;
        gchar *user_hidden_str = NULL;
 
        g_return_val_if_fail(action && *action, FALSE);
 
-       action_type = get_action_type(action);
+       action_type = action_get_type(action);
 
        if (action_type == ACTION_ERROR)
                return FALSE;         /* ERR: syntax error */
 
-       if (action_type & (ACTION_SINGLE | ACTION_MULTIPLE) && 
-           !(ctree && GTK_CLIST(ctree)->selection))
+       if (action_type & (ACTION_SINGLE | ACTION_MULTIPLE) && !msg_list)
                return FALSE;         /* ERR: file command without selection */
 
-       if (ctree) {
-               selection = GTK_CLIST(ctree)->selection;
-               selection_len = g_list_length(selection);
-       }
+       msg_list_len = g_slist_length(msg_list);
 
        if (action_type & (ACTION_PIPE_OUT | ACTION_PIPE_IN | ACTION_INSERT)) {
-               if (ctree && selection_len > 1)
+               if (msg_list_len > 1)
                        return FALSE; /* ERR: pipe + multiple selection */
                if (!text)
                        return FALSE; /* ERR: pipe and no displayed text */
@@ -627,93 +577,123 @@ static gboolean execute_actions(gchar *action, GtkCTree *ctree,
        if (action_type & ACTION_SELECTION_STR) {
                if (!text)
                        return FALSE; /* ERR: selection string but no text */
-               else {
-                       guint start = 0, end = 0;
-                       if (GTK_EDITABLE(text)->has_selection) {
-                               start = GTK_EDITABLE(text)->selection_start_pos;
-                               end   = GTK_EDITABLE(text)->selection_end_pos;
-                               if (start > end) {
-                                       guint tmp;
-                                       tmp = start;
-                                       start = end;
-                                       end = tmp;
-                               }
+       }
+
+       if (text) {
+               if (GTK_EDITABLE(text)->has_selection) {
+                       start = GTK_EDITABLE(text)->selection_start_pos;
+                       end   = GTK_EDITABLE(text)->selection_end_pos;
+                       if (start > end) {
+                               guint tmp;
+                               tmp = start;
+                               start = end;
+                               end = tmp;
                        }
-                       sel_str = gtk_editable_get_chars(GTK_EDITABLE(text),
+
+                       if (start == end) {
+                               start = body_pos;
+                               end = gtk_stext_get_length(GTK_STEXT(text));
+                               msg_str = gtk_editable_get_chars
+                                       (GTK_EDITABLE(text), start, end);
+                       } else {
+                               sel_str = gtk_editable_get_chars
+                                       (GTK_EDITABLE(text), start, end);
+                               msg_str = g_strdup(sel_str);
+                       }
+               } else {
+                       start = body_pos;
+                       end = gtk_stext_get_length(GTK_STEXT(text));
+                       msg_str = gtk_editable_get_chars(GTK_EDITABLE(text),
                                                         start, end);
                }
        }
 
-       if (action_type & (ACTION_USER_STR))
-               if (!(user_str = get_user_string(action, ACTION_USER_STR)))
+       if (action_type & ACTION_USER_STR) {
+               if (!(user_str = get_user_string(action, ACTION_USER_STR))) {
+                       g_free(msg_str);
+                       g_free(sel_str);
                        return FALSE;
+               }
+       }
 
-       if (action_type & (ACTION_USER_HIDDEN_STR))
+       if (action_type & ACTION_USER_HIDDEN_STR) {
                if (!(user_hidden_str =
-                       get_user_string(action, ACTION_USER_HIDDEN_STR)))
+                       get_user_string(action, ACTION_USER_HIDDEN_STR))) {
+                       g_free(msg_str);
+                       g_free(sel_str);
+                       g_free(user_str);
                        return FALSE;
+               }
+       }
+
+       if (action_type & ACTION_PIPE_OUT) {
+               gtk_stext_freeze(GTK_STEXT(text));
+               gtk_stext_set_point(GTK_STEXT(text), start);
+               gtk_stext_forward_delete(GTK_STEXT(text), end - start);
+               gtk_stext_thaw(GTK_STEXT(text));
+       }
 
        children = g_new0(Children, 1);
 
+       children->action      = g_strdup(action);
+       children->action_type = action_type;
+       children->msg_text    = text;
+       children->msgfont     = msgfont;
+
+       if ((action_type & (ACTION_USER_IN | ACTION_USER_HIDDEN_IN)) &&
+           ((action_type & ACTION_SINGLE) == 0 || msg_list_len == 1))
+               children->open_in = 1;
+
        if (action_type & ACTION_SINGLE) {
-               for (cur = selection; cur && is_ok == TRUE; cur = cur->next) {
-                       msginfo = gtk_ctree_node_get_row_data(ctree,
-                                       GTK_CTREE_NODE(cur->data));
+               GSList *cur;
+
+               for (cur = msg_list; cur && is_ok == TRUE; cur = cur->next) {
+                       msginfo = (MsgInfo *)cur->data;
                        if (!msginfo) {
                                is_ok  = FALSE; /* ERR: msginfo missing */
                                break;
                        }
-                       cmd = parse_action_cmd(action, msginfo, ctree,
-                                              mimeview, user_str,
+                       cmd = parse_action_cmd(action, msginfo, msg_list,
+                                              partinfo, user_str,
                                               user_hidden_str, sel_str);
                        if (!cmd) {
                                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))) {
+                       if ((child_info = fork_child(cmd, msg_str, children))) {
                                children_list = g_slist_append(children_list,
                                                               child_info);
-                               children->open_in = (selection_len == 1) ?
-                                                   (action_type &
-                                                    (ACTION_USER_IN |
-                                                     ACTION_USER_HIDDEN_IN)) : 0;
                        }
                        g_free(cmd);
                }
        } else {
-               cmd = parse_action_cmd(action, NULL, ctree, mimeview, user_str,
-                                      user_hidden_str, sel_str);
+               cmd = parse_action_cmd(action, NULL, msg_list, partinfo,
+                                      user_str, user_hidden_str, sel_str);
                if (cmd) {
-                       if ((child_info = fork_child(cmd, action_type, text,
-                                                    msgfont, body_pos,
-                                                    children))) {
+                       if ((child_info = fork_child(cmd, msg_str, children))) {
                                children_list = g_slist_append(children_list,
                                                               child_info);
-                               children->open_in = action_type &
-                                                   (ACTION_USER_IN |
-                                                    ACTION_USER_HIDDEN_IN);
                        }
                        g_free(cmd);
                } else
                        is_ok  = FALSE;         /* ERR: incorrect command */
        }
 
+       g_free(msg_str);
+       g_free(sel_str);
        g_free(user_str);
        g_free(user_hidden_str);
 
        if (!children_list) {
                 /* If not waiting for children, return */
-               g_free(children);
+               free_children(children);
        } else {
                GSList *cur;
 
-               children->action  = g_strdup(action);
-               children->dialog  = NULL;
-               children->list    = children_list;
-               children->nb      = g_slist_length(children_list);
+               children->list        = children_list;
+               children->nb          = g_slist_length(children_list);
+               children->initial_nb  = children->nb;
 
                for (cur = children_list; cur; cur = cur->next) {
                        child_info = (ChildInfo *) cur->data;
@@ -729,20 +709,16 @@ static gboolean execute_actions(gchar *action, GtkCTree *ctree,
        return is_ok;
 }
 
-static ChildInfo *fork_child(gchar *cmd, gint action_type, GtkWidget *text,
-                            GdkFont *msgfont, gint body_pos,
+static ChildInfo *fork_child(gchar *cmd, const gchar *msg_str,
                             Children *children)
 {
        gint chld_in[2], chld_out[2], chld_err[2], chld_status[2];
        gchar *cmdline[4];
-       guint start, end;
-       gint is_selection;
-       gchar *selection;
        pid_t pid, gch_pid;
        ChildInfo *child_info;
        gint sync;
 
-       sync = !(action_type & ACTION_ASYNC);
+       sync = !(children->action_type & ACTION_ASYNC);
 
        chld_in[0] = chld_in[1] = chld_out[0] = chld_out[1] = chld_err[0]
                = chld_err[1] = chld_status[0] = chld_status[1] = -1;
@@ -780,8 +756,9 @@ static ChildInfo *fork_child(gchar *cmd, gint action_type, GtkWidget *text,
                if (gch_pid == 0) {
                        if (setpgid(0, getppid()))
                                perror("setpgid");
+
                        if (sync) {
-                               if (action_type &
+                               if (children->action_type &
                                    (ACTION_PIPE_IN |
                                     ACTION_USER_IN |
                                     ACTION_USER_HIDDEN_IN)) {
@@ -824,8 +801,7 @@ static ChildInfo *fork_child(gchar *cmd, gint action_type, GtkWidget *text,
                                close(chld_err[0]);
                                close(chld_err[1]);
                                close(chld_status[0]);
-                       }
-                       if (sync) {
+
                                debug_print("Child: Waiting for grandchild\n");
                                waitpid(gch_pid, NULL, 0);
                                debug_print("Child: grandchild ended\n");
@@ -849,7 +825,8 @@ static ChildInfo *fork_child(gchar *cmd, gint action_type, GtkWidget *text,
        }
 
        close(chld_in[0]);
-       if (!(action_type & (ACTION_PIPE_IN | ACTION_USER_IN | ACTION_USER_HIDDEN_IN)))
+       if (!(children->action_type &
+             (ACTION_PIPE_IN | ACTION_USER_IN | ACTION_USER_HIDDEN_IN)))
                close(chld_in[1]);
        close(chld_out[1]);
        close(chld_err[1]);
@@ -861,11 +838,10 @@ static ChildInfo *fork_child(gchar *cmd, gint action_type, GtkWidget *text,
 
        child_info->pid         = pid;
        child_info->cmd         = g_strdup(cmd);
-       child_info->type        = action_type;
        child_info->new_out     = FALSE;
        child_info->output      = g_string_new(NULL);
        child_info->chld_in     =
-               (action_type &
+               (children->action_type &
                 (ACTION_PIPE_IN | ACTION_USER_IN | ACTION_USER_HIDDEN_IN))
                        ? chld_in [1] : -1;
        child_info->chld_out    = chld_out[0];
@@ -877,49 +853,17 @@ static 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 | ACTION_INSERT)))
+       if (!(children->action_type &
+             (ACTION_PIPE_IN | ACTION_PIPE_OUT | ACTION_INSERT)))
                return child_info;
 
-       child_info->text        = text;
-       child_info->msgfont     = msgfont;
-
-       start = body_pos;
-       end   = gtk_stext_get_length(GTK_STEXT(text));
-
-       if (GTK_EDITABLE(text)->has_selection) {
-               start = GTK_EDITABLE(text)->selection_start_pos;
-               end   = GTK_EDITABLE(text)->selection_end_pos;
-               if (start > end) {
-                       guint tmp;
-                       tmp = start;
-                       start = end;
-                       end = tmp;
-               }
-               is_selection = TRUE;
-               if (start == end) {
-                       start = 0;
-                       end = gtk_stext_get_length(GTK_STEXT(text));
-                       is_selection = FALSE;
-               }
-       }
-
-       selection = gtk_editable_get_chars(GTK_EDITABLE(text), start, end);
-
-       if (action_type & ACTION_PIPE_IN) {
-               write(chld_in[1], selection, strlen(selection));
-               if (!(action_type & (ACTION_USER_IN | ACTION_USER_HIDDEN_IN)))
+       if ((children->action_type & ACTION_PIPE_IN) && msg_str) {
+               write(chld_in[1], msg_str, strlen(msg_str));
+               if (!(children->action_type &
+                     (ACTION_USER_IN | ACTION_USER_HIDDEN_IN)))
                        close(chld_in[1]);
                child_info->chld_in = -1; /* No more input */
        }
-       g_free(selection);
-
-       gtk_stext_freeze(GTK_STEXT(text));
-       if (action_type & ACTION_PIPE_OUT) {
-               gtk_stext_set_point(GTK_STEXT(text), start);
-               gtk_stext_forward_delete(GTK_STEXT(text), end - start);
-       }
-
-       gtk_stext_thaw(GTK_STEXT(text));
 
        return child_info;
 }
@@ -938,10 +882,9 @@ static void kill_children_cb(GtkWidget *widget, gpointer data)
        }
 }
 
-static gint wait_for_children(gpointer data)
+static gint wait_for_children(Children *children)
 {
        gboolean new_output;
-       Children *children = (Children *)data;
        ChildInfo *child_info;
        GSList *cur;
        gint nb = children->nb;
@@ -1015,33 +958,41 @@ static gint io_dialog_key_pressed_cb(GtkWidget *widget, GdkEventKey *event,
 
 static void childinfo_close_pipes(ChildInfo *child_info)
 {
+       /* stdout and stderr pipes are guaranteed to be removed by
+        * their handler, but in case where we receive child exit notification
+        * before grand-child's pipes closing signals, we check them and close
+        * them if necessary
+        */
        if (child_info->tag_in > 0)
                gdk_input_remove(child_info->tag_in);
-       gdk_input_remove(child_info->tag_out);
-       gdk_input_remove(child_info->tag_err);
+       if (child_info->tag_out > 0)
+               gdk_input_remove(child_info->tag_out);
+       if (child_info->tag_err > 0)
+               gdk_input_remove(child_info->tag_err);
 
        if (child_info->chld_in >= 0)
                close(child_info->chld_in);
-       close(child_info->chld_out);
-       close(child_info->chld_err);
+       if (child_info->chld_out >= 0)
+               close(child_info->chld_out);
+       if (child_info->chld_err >= 0)
+               close(child_info->chld_err);
+
        close(child_info->chld_status);
 }
 
 static void free_children(Children *children)
 {
-       GSList *cur;
        ChildInfo *child_info;
 
        debug_print("Freeing children data %p\n", children);
 
        g_free(children->action);
-       for (cur = children->list; cur;) {
-               child_info = (ChildInfo *)cur->data;
+       while (children->list != NULL) {
+               child_info = (ChildInfo *)children->list->data;
                g_free(child_info->cmd);
                g_string_free(child_info->output, TRUE);
                children->list = g_slist_remove(children->list, child_info);
                g_free(child_info);
-               cur = children->list;
        }
        g_free(children);
 }
@@ -1052,6 +1003,12 @@ static void update_io_dialog(Children *children)
 
        debug_print("Updating actions input/output dialog.\n");
 
+       if (children->progress_bar) {
+               gtk_progress_configure(GTK_PROGRESS(children->progress_bar),
+                               children->initial_nb -children->nb,
+                               0.0, children->initial_nb);
+       }
+
        if (!children->nb) {
                gtk_widget_set_sensitive(children->abort_btn, FALSE);
                gtk_widget_set_sensitive(children->close_btn, TRUE);
@@ -1107,6 +1064,7 @@ static void create_io_dialog(Children *children)
        GtkWidget *text;
        GtkWidget *scrolledwin;
        GtkWidget *hbox;
+       GtkWidget *progress_bar = NULL;
        GtkWidget *abort_button;
        GtkWidget *close_button;
 
@@ -1158,7 +1116,7 @@ static void create_io_dialog(Children *children)
                gtk_signal_connect(GTK_OBJECT(entry), "activate",
                                   GTK_SIGNAL_FUNC(send_input), children);
                gtk_box_pack_start(GTK_BOX(input_hbox), entry, TRUE, TRUE, 0);
-               if (children->open_in & ACTION_USER_HIDDEN_IN)
+               if (children->action_type & ACTION_USER_HIDDEN_IN)
                        gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
                gtk_widget_show(entry);
 
@@ -1173,6 +1131,25 @@ static void create_io_dialog(Children *children)
                gtk_widget_grab_focus(entry);
        }
 
+       if (children->initial_nb > 1) {
+               progress_bar = gtk_progress_bar_new();
+               gtk_progress_bar_set_bar_style(GTK_PROGRESS_BAR(progress_bar),
+                               GTK_PROGRESS_CONTINUOUS);
+               gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(progress_bar),
+                               GTK_PROGRESS_LEFT_TO_RIGHT);
+               gtk_progress_set_activity_mode(GTK_PROGRESS(progress_bar), 
+                               FALSE);
+               gtk_progress_set_format_string(GTK_PROGRESS(progress_bar),
+                               _("Completed %v/%u"));
+               gtk_progress_set_show_text(GTK_PROGRESS(progress_bar), TRUE);
+               gtk_progress_configure(GTK_PROGRESS(progress_bar),
+                               children->initial_nb -children->nb,
+                               0.0, children->initial_nb);
+
+               gtk_box_pack_start(GTK_BOX(vbox), progress_bar, TRUE, TRUE, 0);
+               gtk_widget_show(progress_bar);
+       }
+
        gtkut_button_set_create(&hbox, &abort_button, _("Abort"),
                                &close_button, _("Close"), NULL, NULL);
        gtk_signal_connect(GTK_OBJECT(abort_button), "clicked",
@@ -1186,13 +1163,14 @@ static void create_io_dialog(Children *children)
 
        gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), hbox);
 
-       children->dialog      = dialog;
-       children->scrolledwin = scrolledwin;
-       children->text        = text;
-       children->input_hbox  = children->open_in ? input_hbox : NULL;
-       children->input_entry = children->open_in ? entry : NULL;
-       children->abort_btn   = abort_button;
-       children->close_btn   = close_button;
+       children->dialog       = dialog;
+       children->scrolledwin  = scrolledwin;
+       children->text         = text;
+       children->input_hbox   = children->open_in ? input_hbox : NULL;
+       children->input_entry  = children->open_in ? entry : NULL;
+       children->progress_bar = progress_bar;
+       children->abort_btn    = abort_button;
+       children->close_btn    = close_button;
 
        gtk_widget_show(dialog);
 }
@@ -1259,10 +1237,12 @@ static void catch_output(gpointer data, gint source, GdkInputCondition cond)
        gchar buf[BUFFSIZE];
 
        debug_print("Catching grand child's output.\n");
-       if (child_info->type & (ACTION_PIPE_OUT | ACTION_INSERT)
+       if (child_info->children->action_type &
+           (ACTION_PIPE_OUT | ACTION_INSERT)
            && source == child_info->chld_out) {
                gboolean is_selection = FALSE;
-               GtkWidget *text = child_info->text;
+               GtkWidget *text = child_info->children->msg_text;
+
                if (GTK_EDITABLE(text)->has_selection)
                        is_selection = TRUE;
                gtk_stext_freeze(GTK_STEXT(text));
@@ -1270,7 +1250,8 @@ static void catch_output(gpointer data, gint source, GdkInputCondition cond)
                        c = read(source, buf, sizeof(buf) - 1);
                        if (c == 0)
                                break;
-                       gtk_stext_insert(GTK_STEXT(text), child_info->msgfont,
+                       gtk_stext_insert(GTK_STEXT(text),
+                                        child_info->children->msgfont,
                                         NULL, NULL, buf, c);
                }
                if (is_selection) {
@@ -1281,7 +1262,7 @@ static void catch_output(gpointer data, gint source, GdkInputCondition cond)
                        GTK_EDITABLE(text)->selection_end_pos =
                                        gtk_stext_get_point(GTK_STEXT(text));
                }
-               gtk_stext_thaw(GTK_STEXT(child_info->text));
+               gtk_stext_thaw(GTK_STEXT(text));
        } else {
                c = read(source, buf, sizeof(buf) - 1);
                for (i = 0; i < c; i++)
@@ -1289,110 +1270,50 @@ static void catch_output(gpointer data, gint source, GdkInputCondition cond)
                if (c > 0)
                        child_info->new_out = TRUE;
        }
+       if (c == 0) {
+               if (source == child_info->chld_out) {
+                       gdk_input_remove(child_info->tag_out);
+                       child_info->tag_out = -1;
+                       close(child_info->chld_out);
+                       child_info->chld_out = -1;
+               } else {
+                       gdk_input_remove(child_info->tag_err);
+                       child_info->tag_err = -1;
+                       close(child_info->chld_err);
+                       child_info->chld_err = -1;
+               }
+       }
+       
        wait_for_children(child_info->children);
 }
 
-static gboolean user_string_dialog_delete_cb(GtkWidget *widget,
-                                            GdkEvent *event, gpointer data)
-{
-       return FALSE;
-}
-
-static void user_string_dialog_destroy_cb(GtkWidget *widget, gpointer data)
-{
-       gtk_main_quit();
-}
-
-static void user_string_dialog_activate_cb(GtkWidget *widget, gpointer data)
-{
-       UserStringDialog *user_dialog = (UserStringDialog *) data;
-
-       g_free(user_dialog->user_str);
-       user_dialog->user_str =
-               gtk_editable_get_chars(GTK_EDITABLE(user_dialog->entry), 0, -1);
-       gtk_widget_destroy(user_dialog->dialog);
-}
-
 static gchar *get_user_string(const gchar *action, ActionType type)
 {
-       GtkWidget *dialog;
-       GtkWidget *label;
-       GtkWidget *entry;
-       GtkWidget *ok_button;
-       GtkWidget *cancel_button;
-       gchar *label_text;
-       UserStringDialog user_dialog;
-
-       dialog = gtk_dialog_new();
-       gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, FALSE, FALSE);
-       gtk_container_set_border_width
-               (GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), 8);
-       gtk_container_set_border_width
-               (GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), 5);
-       gtk_window_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
+       gchar *message;
+       gchar *user_str = NULL;
 
        switch (type) {
        case ACTION_USER_HIDDEN_STR:
-               gtk_window_set_title(GTK_WINDOW(dialog),
-                                    _("Action's hidden user argument"));
-               label_text = g_strdup_printf(_("Enter the '%%h' "
-                                            "argument for the following "
-                                            "action:\n%s"), action);
+               message = g_strdup_printf
+                       (_("Enter the argument for the following action:\n"
+                          "(`%%h' will be replaced with the argument)\n"
+                          "  %s"),
+                        action);
+               user_str = input_dialog_with_invisible
+                       (_("Action's hidden user argument"), message, NULL);
                break;
        case ACTION_USER_STR:
-               gtk_window_set_title(GTK_WINDOW(dialog),
-                                    _("Action's user argument"));
-               label_text = g_strdup_printf(_("Enter the '%%u' "
-                                            "argument for the following "
-                                            "action:\n%s"), action);
+               message = g_strdup_printf
+                       (_("Enter the argument for the following action:\n"
+                          "(`%%u' will be replaced with the argument)\n"
+                          "  %s"),
+                        action);
+               user_str = input_dialog
+                       (_("Action's user argument"), message, NULL);
                break;
        default:
-               label_text = NULL;
-               debug_print("Unsupported action type %d", type);
+               g_warning("Unsupported action type %d", type);
        }
 
-       label = gtk_label_new(label_text);
-       g_free(label_text);
-       gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label,
-                          TRUE, TRUE, 0);
-
-       entry = gtk_entry_new();
-       gtk_entry_set_visibility(GTK_ENTRY(entry),
-                                type != ACTION_USER_HIDDEN_STR);
-       gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), entry,
-                          TRUE, TRUE, 0);
-
-       ok_button = gtk_button_new_with_label(_("OK"));
-       gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->action_area),
-                        ok_button, TRUE, TRUE, 0);
-
-       cancel_button = gtk_button_new_with_label(_("Cancel"));
-       gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->action_area),
-                        cancel_button, TRUE, TRUE, 0);
-
-       user_dialog.dialog   = dialog;
-       user_dialog.user_str = NULL;
-       user_dialog.entry    = entry;
-
-       gtk_signal_connect(GTK_OBJECT(dialog), "delete_event",
-                          GTK_SIGNAL_FUNC(user_string_dialog_delete_cb),
-                          &user_dialog);
-       gtk_signal_connect(GTK_OBJECT(dialog), "destroy",
-                          GTK_SIGNAL_FUNC(user_string_dialog_destroy_cb),
-                          &user_dialog);
-       gtk_signal_connect(GTK_OBJECT(entry), "activate",
-                          GTK_SIGNAL_FUNC(user_string_dialog_activate_cb),
-                          &user_dialog);
-       gtk_signal_connect(GTK_OBJECT(ok_button), "clicked",
-                          GTK_SIGNAL_FUNC(user_string_dialog_activate_cb),
-                          &user_dialog);
-       gtk_signal_connect_object(GTK_OBJECT(cancel_button), "clicked",
-                                 GTK_SIGNAL_FUNC(gtk_widget_destroy),
-                                 GTK_OBJECT(dialog));
-
-       gtk_widget_grab_focus(entry);
-       gtk_widget_show_all(dialog);
-       gtk_main();
-
-       return user_dialog.user_str;
+       return user_str;
 }