2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2003 Hiroyuki Yamamoto
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
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.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 #include <gdk/gdkkeysyms.h>
34 #include <sys/types.h>
42 #include "manage_window.h"
43 #include "mainwindow.h"
44 #include "prefs_common.h"
45 #include "alertpanel.h"
53 typedef struct _Children Children;
54 typedef struct _ChildInfo ChildInfo;
55 typedef struct _UserStringDialog UserStringDialog;
61 GtkWidget *input_entry;
62 GtkWidget *input_hbox;
65 GtkWidget *scrolledwin;
94 struct _UserStringDialog
101 static void update_actions_menu (GtkItemFactory *ifactory,
105 static void compose_actions_execute_cb (Compose *compose,
108 static void mainwin_actions_execute_cb (MainWindow *mainwin,
111 static void msgview_actions_execute_cb (MessageView *msgview,
114 static void message_actions_execute (MessageView *msgview,
118 static gboolean execute_actions (gchar *action,
125 static gchar *parse_action_cmd (gchar *action,
129 const gchar *user_str,
130 const gchar *user_hidden_str,
131 const gchar *sel_str);
132 static gboolean parse_append_filename (GString *cmd,
135 static gboolean parse_append_msgpart (GString *cmd,
139 static ChildInfo *fork_child (gchar *cmd,
146 static gint wait_for_children (gpointer data);
148 static void free_children (Children *children);
150 static void childinfo_close_pipes (ChildInfo *child_info);
152 static void create_io_dialog (Children *children);
153 static void update_io_dialog (Children *children);
155 static void hide_io_dialog_cb (GtkWidget *widget,
157 static gint io_dialog_key_pressed_cb (GtkWidget *widget,
161 static void catch_output (gpointer data,
163 GdkInputCondition cond);
164 static void catch_input (gpointer data,
166 GdkInputCondition cond);
167 static void catch_status (gpointer data,
169 GdkInputCondition cond);
171 static gboolean user_string_dialog_delete_cb
175 static void user_string_dialog_destroy_cb
178 static void user_string_dialog_activate_cb
182 static gchar *get_user_string (const gchar *action,
186 ActionType get_action_type(const gchar *action_str)
189 ActionType action_type = ACTION_NONE;
191 g_return_val_if_fail(action_str, ACTION_ERROR);
192 g_return_val_if_fail(*action_str, ACTION_ERROR);
197 action_type |= ACTION_PIPE_IN;
199 } else if (p[0] == '>') {
200 action_type |= ACTION_USER_IN;
202 } else if (p[0] == '*') {
203 action_type |= ACTION_USER_HIDDEN_IN;
210 while (*p && action_type != ACTION_ERROR) {
214 action_type |= ACTION_SINGLE;
217 action_type |= ACTION_MULTIPLE;
220 action_type |= ACTION_SINGLE;
223 action_type |= ACTION_SELECTION_STR;
226 action_type |= ACTION_USER_STR;
229 action_type |= ACTION_USER_HIDDEN_STR;
232 action_type = ACTION_ERROR;
235 } else if (p[0] == '|') {
237 action_type |= ACTION_PIPE_OUT;
238 } else if (p[0] == '>') {
240 action_type |= ACTION_INSERT;
241 } else if (p[0] == '&') {
243 action_type |= ACTION_ASYNC;
251 static gchar *parse_action_cmd(gchar *action, MsgInfo *msginfo,
252 GtkCTree *ctree, MimeView *mimeview,
253 const gchar *user_str,
254 const gchar *user_hidden_str,
255 const gchar *sel_str)
264 if (p[0] == '|' || p[0] == '>' || p[0] == '*')
267 cmd = g_string_sized_new(strlen(action));
270 !((p[0] == '|' || p[0] == '>' || p[0] == '&') && !p[1])) {
271 if (p[0] == '%' && p[1]) {
274 if (!parse_append_filename(cmd, msginfo)) {
275 g_string_free(cmd, TRUE);
281 for (cur = GTK_CLIST(ctree)->selection;
282 cur != NULL; cur = cur->next) {
283 msg = gtk_ctree_node_get_row_data(ctree,
284 GTK_CTREE_NODE(cur->data));
285 if (!parse_append_filename(cmd, msg)) {
286 g_string_free(cmd, TRUE);
290 g_string_append_c(cmd, ' ');
295 if (!parse_append_msgpart(cmd, msginfo,
297 g_string_free(cmd, TRUE);
304 g_string_append(cmd, sel_str);
309 g_string_append(cmd, user_str);
314 g_string_append(cmd, user_hidden_str);
318 g_string_append_c(cmd, p[0]);
319 g_string_append_c(cmd, p[1]);
323 g_string_append_c(cmd, p[0]);
328 g_string_free(cmd, TRUE);
333 g_string_free(cmd, FALSE);
337 static gboolean parse_append_filename(GString *cmd, MsgInfo *msginfo)
341 g_return_val_if_fail(msginfo, FALSE);
343 filename = procmsg_get_message_file(msginfo);
346 g_string_append(cmd, filename);
349 alertpanel_error(_("Could not get message file %d"),
357 static gboolean parse_append_msgpart(GString *cmd, MsgInfo *msginfo,
368 if ((fp = procmsg_open_message_decrypted(msginfo, &partinfo))
370 alertpanel_error(_("Could not get message file."));
374 if ((fp = procmsg_open_message(msginfo)) == NULL) {
375 alertpanel_error(_("Could not get message file."));
378 partinfo = procmime_scan_mime_header(fp);
382 procmime_mimeinfo_free_all(partinfo);
383 alertpanel_error(_("Could not get message part."));
386 filename = procmsg_get_message_file(msginfo);
388 if (!mimeview->opened) {
389 alertpanel_error(_("No message part selected."));
392 if (!mimeview->file) {
393 alertpanel_error(_("No message file selected."));
396 partinfo = gtk_ctree_node_get_row_data
397 (GTK_CTREE(mimeview->ctree),
399 g_return_val_if_fail(partinfo != NULL, FALSE);
400 filename = mimeview->file;
402 partname = procmime_get_tmp_file_name(partinfo);
404 ret = procmime_get_part(partname, filename, partinfo);
407 procmime_mimeinfo_free_all(partinfo);
412 alertpanel_error(_("Can't get part of multipart message"));
417 g_string_append(cmd, partname);
424 void actions_execute(gpointer data,
429 if (source == TOOLBAR_MAIN)
430 mainwin_actions_execute_cb((MainWindow*)data, action_nb, widget);
431 else if (source == TOOLBAR_COMPOSE)
432 compose_actions_execute_cb((Compose*)data, action_nb, widget);
433 else if (source == TOOLBAR_MSGVIEW)
434 msgview_actions_execute_cb((MessageView*)data, action_nb, widget);
438 void update_mainwin_actions_menu(GtkItemFactory *ifactory,
441 update_actions_menu(ifactory, "/Tools/Actions",
442 mainwin_actions_execute_cb,
446 void update_compose_actions_menu(GtkItemFactory *ifactory,
450 update_actions_menu(ifactory, branch_path,
451 compose_actions_execute_cb,
455 static void update_actions_menu(GtkItemFactory *ifactory,
463 gchar *action, *action_p;
465 GtkItemFactoryEntry ifentry = {NULL, NULL, NULL, 0, "<Branch>"};
467 ifentry.path = branch_path;
468 menuitem = gtk_item_factory_get_widget(ifactory, branch_path);
469 g_return_if_fail(menuitem != NULL);
471 amenu = GTK_MENU_SHELL(menuitem)->children;
472 while (amenu != NULL) {
473 GList *alist = amenu->next;
474 gtk_widget_destroy(GTK_WIDGET(amenu->data));
478 ifentry.accelerator = NULL;
479 ifentry.callback_action = 0;
480 ifentry.callback = callback;
481 ifentry.item_type = NULL;
483 for (cur = prefs_common.actions_list; cur; cur = cur->next) {
484 action = g_strdup((gchar *)cur->data);
485 action_p = strstr(action, ": ");
486 if (action_p && action_p[2] &&
487 get_action_type(&action_p[2]) != ACTION_ERROR) {
489 menu_path = g_strdup_printf("%s/%s", branch_path,
491 ifentry.path = menu_path;
492 gtk_item_factory_create_item(ifactory, &ifentry, data,
497 ifentry.callback_action++;
501 static void compose_actions_execute_cb(Compose *compose, guint action_nb,
507 g_return_if_fail(action_nb < g_slist_length(prefs_common.actions_list));
509 buf = (gchar *)g_slist_nth_data(prefs_common.actions_list, action_nb);
510 g_return_if_fail(buf != NULL);
511 action = strstr(buf, ": ");
512 g_return_if_fail(action != NULL);
514 /* Point to the beginning of the command-line */
517 action_type = get_action_type(action);
518 if (action_type & (ACTION_SINGLE | ACTION_MULTIPLE)) {
520 (_("The selected action cannot be used in the compose window\n"
521 "because it contains %%f, %%F or %%p."));
525 execute_actions(action, NULL, compose->text, NULL, 0, NULL);
528 static void mainwin_actions_execute_cb(MainWindow *mainwin, guint action_nb,
531 message_actions_execute(mainwin->messageview, action_nb,
532 GTK_CTREE(mainwin->summaryview->ctree));
535 static void msgview_actions_execute_cb(MessageView *msgview, guint action_nb,
538 message_actions_execute(msgview, action_nb, NULL);
541 static void message_actions_execute(MessageView *msgview, guint action_nb,
544 TextView *textview = NULL;
545 MimeView *mimeview = NULL;
548 GtkWidget *text = NULL;
549 GdkFont *msgfont = NULL;
552 g_return_if_fail(action_nb < g_slist_length(prefs_common.actions_list));
554 buf = (gchar *)g_slist_nth_data(prefs_common.actions_list, action_nb);
556 g_return_if_fail(buf);
557 g_return_if_fail(action = strstr(buf, ": "));
559 /* Point to the beginning of the command-line */
562 switch (msgview->type) {
564 if (msgview->textview && msgview->textview->text)
565 textview = msgview->textview;
568 if (msgview->mimeview) {
569 mimeview = msgview->mimeview;
570 if (msgview->mimeview->type == MIMEVIEW_TEXT &&
571 msgview->mimeview->textview &&
572 msgview->mimeview->textview->text)
573 textview = msgview->mimeview->textview;
579 text = textview->text;
580 msgfont = textview->msgfont;
581 body_pos = textview->body_pos;
584 execute_actions(action, ctree, text, msgfont, body_pos, mimeview);
587 static gboolean execute_actions(gchar *action, GtkCTree *ctree,
588 GtkWidget *text, GdkFont *msgfont,
589 gint body_pos, MimeView *mimeview)
591 GList *cur, *selection = NULL;
592 GSList *children_list = NULL;
594 gint selection_len = 0;
596 ChildInfo *child_info;
600 gchar *sel_str = NULL;
601 gchar *user_str = NULL;
602 gchar *user_hidden_str = NULL;
604 g_return_val_if_fail(action && *action, FALSE);
606 action_type = get_action_type(action);
608 if (action_type == ACTION_ERROR)
609 return FALSE; /* ERR: syntax error */
611 if (action_type & (ACTION_SINGLE | ACTION_MULTIPLE) &&
612 !(ctree && GTK_CLIST(ctree)->selection))
613 return FALSE; /* ERR: file command without selection */
616 selection = GTK_CLIST(ctree)->selection;
617 selection_len = g_list_length(selection);
620 if (action_type & (ACTION_PIPE_OUT | ACTION_PIPE_IN | ACTION_INSERT)) {
621 if (ctree && selection_len > 1)
622 return FALSE; /* ERR: pipe + multiple selection */
624 return FALSE; /* ERR: pipe and no displayed text */
627 if (action_type & ACTION_SELECTION_STR) {
629 return FALSE; /* ERR: selection string but no text */
631 guint start = 0, end = 0;
632 if (GTK_EDITABLE(text)->has_selection) {
633 start = GTK_EDITABLE(text)->selection_start_pos;
634 end = GTK_EDITABLE(text)->selection_end_pos;
642 sel_str = gtk_editable_get_chars(GTK_EDITABLE(text),
647 if (action_type & (ACTION_USER_STR))
648 if (!(user_str = get_user_string(action, ACTION_USER_STR)))
651 if (action_type & (ACTION_USER_HIDDEN_STR))
652 if (!(user_hidden_str =
653 get_user_string(action, ACTION_USER_HIDDEN_STR)))
656 children = g_new0(Children, 1);
658 if (action_type & ACTION_SINGLE) {
659 for (cur = selection; cur && is_ok == TRUE; cur = cur->next) {
660 msginfo = gtk_ctree_node_get_row_data(ctree,
661 GTK_CTREE_NODE(cur->data));
663 is_ok = FALSE; /* ERR: msginfo missing */
666 cmd = parse_action_cmd(action, msginfo, ctree,
668 user_hidden_str, sel_str);
670 debug_print("Action command error\n");
671 is_ok = FALSE; /* ERR: incorrect command */
674 if ((child_info = fork_child(cmd, action_type, text,
677 children_list = g_slist_append(children_list,
679 children->open_in = (selection_len == 1) ?
682 ACTION_USER_HIDDEN_IN)) : 0;
687 cmd = parse_action_cmd(action, NULL, ctree, mimeview, user_str,
688 user_hidden_str, sel_str);
690 if ((child_info = fork_child(cmd, action_type, text,
693 children_list = g_slist_append(children_list,
695 children->open_in = action_type &
697 ACTION_USER_HIDDEN_IN);
701 is_ok = FALSE; /* ERR: incorrect command */
705 g_free(user_hidden_str);
707 if (!children_list) {
708 /* If not waiting for children, return */
713 children->action = g_strdup(action);
714 children->dialog = NULL;
715 children->list = children_list;
716 children->nb = g_slist_length(children_list);
718 for (cur = children_list; cur; cur = cur->next) {
719 child_info = (ChildInfo *) cur->data;
720 child_info->tag_status =
721 gdk_input_add(child_info->chld_status,
723 catch_status, child_info);
726 create_io_dialog(children);
732 static ChildInfo *fork_child(gchar *cmd, gint action_type, GtkWidget *text,
733 GdkFont *msgfont, gint body_pos,
736 gint chld_in[2], chld_out[2], chld_err[2], chld_status[2];
742 ChildInfo *child_info;
745 sync = !(action_type & ACTION_ASYNC);
747 chld_in[0] = chld_in[1] = chld_out[0] = chld_out[1] = chld_err[0]
748 = chld_err[1] = chld_status[0] = chld_status[1] = -1;
751 if (pipe(chld_status) || pipe(chld_in) || pipe(chld_out) ||
753 alertpanel_error(_("Command could not be started. "
754 "Pipe creation failed.\n%s"),
756 /* Closing fd = -1 fails silently */
763 close(chld_status[0]);
764 close(chld_status[1]);
765 return NULL; /* Pipe error */
769 debug_print("Forking child and grandchild.\n");
772 if (pid == 0) { /* Child */
776 close(ConnectionNumber(gdk_display));
781 if (setpgid(0, getppid()))
787 ACTION_USER_HIDDEN_IN)) {
788 close(fileno(stdin));
794 close(fileno(stdout));
799 close(fileno(stderr));
809 execvp("/bin/sh", cmdline);
813 } else if (gch_pid < (pid_t) 0) { /* Fork error */
815 write(chld_status[1], "1\n", 2);
826 close(chld_status[0]);
829 debug_print("Child: Waiting for grandchild\n");
830 waitpid(gch_pid, NULL, 0);
831 debug_print("Child: grandchild ended\n");
832 write(chld_status[1], "0\n", 2);
833 close(chld_status[1]);
837 } else if (pid < 0) { /* Fork error */
838 alertpanel_error(_("Could not fork to execute the following "
840 cmd, g_strerror(errno));
847 waitpid(pid, NULL, 0);
852 if (!(action_type & (ACTION_PIPE_IN | ACTION_USER_IN | ACTION_USER_HIDDEN_IN)))
856 close(chld_status[1]);
858 child_info = g_new0(ChildInfo, 1);
860 child_info->children = children;
862 child_info->pid = pid;
863 child_info->cmd = g_strdup(cmd);
864 child_info->type = action_type;
865 child_info->new_out = FALSE;
866 child_info->output = g_string_new(NULL);
867 child_info->chld_in =
869 (ACTION_PIPE_IN | ACTION_USER_IN | ACTION_USER_HIDDEN_IN))
871 child_info->chld_out = chld_out[0];
872 child_info->chld_err = chld_err[0];
873 child_info->chld_status = chld_status[0];
874 child_info->tag_in = -1;
875 child_info->tag_out = gdk_input_add(chld_out[0], GDK_INPUT_READ,
876 catch_output, child_info);
877 child_info->tag_err = gdk_input_add(chld_err[0], GDK_INPUT_READ,
878 catch_output, child_info);
880 if (!(action_type & (ACTION_PIPE_IN | ACTION_PIPE_OUT | ACTION_INSERT)))
883 child_info->text = text;
884 child_info->msgfont = msgfont;
887 end = gtk_stext_get_length(GTK_STEXT(text));
889 if (GTK_EDITABLE(text)->has_selection) {
890 start = GTK_EDITABLE(text)->selection_start_pos;
891 end = GTK_EDITABLE(text)->selection_end_pos;
901 end = gtk_stext_get_length(GTK_STEXT(text));
902 is_selection = FALSE;
906 selection = gtk_editable_get_chars(GTK_EDITABLE(text), start, end);
908 if (action_type & ACTION_PIPE_IN) {
909 write(chld_in[1], selection, strlen(selection));
910 if (!(action_type & (ACTION_USER_IN | ACTION_USER_HIDDEN_IN)))
912 child_info->chld_in = -1; /* No more input */
916 gtk_stext_freeze(GTK_STEXT(text));
917 if (action_type & ACTION_PIPE_OUT) {
918 gtk_stext_set_point(GTK_STEXT(text), start);
919 gtk_stext_forward_delete(GTK_STEXT(text), end - start);
922 gtk_stext_thaw(GTK_STEXT(text));
927 static void kill_children_cb(GtkWidget *widget, gpointer data)
930 Children *children = (Children *) data;
931 ChildInfo *child_info;
933 for (cur = children->list; cur; cur = cur->next) {
934 child_info = (ChildInfo *)(cur->data);
935 debug_print("Killing child group id %d\n", child_info->pid);
936 if (child_info->pid && kill(-child_info->pid, SIGTERM) < 0)
941 static gint wait_for_children(gpointer data)
944 Children *children = (Children *)data;
945 ChildInfo *child_info;
947 gint nb = children->nb;
951 cur = children->list;
954 child_info = (ChildInfo *)cur->data;
957 new_output |= child_info->new_out;
961 children->output |= new_output;
963 if (new_output || (children->dialog && (nb != children->nb)))
964 update_io_dialog(children);
969 if (!children->dialog) {
970 free_children(children);
971 } else if (!children->output) {
972 gtk_widget_destroy(children->dialog);
978 static void send_input(GtkWidget *w, gpointer data)
980 Children *children = (Children *) data;
981 ChildInfo *child_info = (ChildInfo *) children->list->data;
983 child_info->tag_in = gdk_input_add(child_info->chld_in,
985 catch_input, children);
986 gtk_widget_set_sensitive(children->input_hbox, FALSE);
989 static gint delete_io_dialog_cb(GtkWidget *w, GdkEvent *e, gpointer data)
991 hide_io_dialog_cb(w, data);
995 static void hide_io_dialog_cb(GtkWidget *w, gpointer data)
998 Children *children = (Children *)data;
1000 if (!children->nb) {
1001 gtk_signal_disconnect_by_data(GTK_OBJECT(children->dialog),
1003 gtk_widget_destroy(children->dialog);
1004 free_children(children);
1008 static gint io_dialog_key_pressed_cb(GtkWidget *widget, GdkEventKey *event,
1011 if (event && event->keyval == GDK_Escape)
1012 hide_io_dialog_cb(widget, data);
1016 static void childinfo_close_pipes(ChildInfo *child_info)
1018 if (child_info->tag_in > 0)
1019 gdk_input_remove(child_info->tag_in);
1020 gdk_input_remove(child_info->tag_out);
1021 gdk_input_remove(child_info->tag_err);
1023 if (child_info->chld_in >= 0)
1024 close(child_info->chld_in);
1025 close(child_info->chld_out);
1026 close(child_info->chld_err);
1027 close(child_info->chld_status);
1030 static void free_children(Children *children)
1033 ChildInfo *child_info;
1035 debug_print("Freeing children data %p\n", children);
1037 g_free(children->action);
1038 for (cur = children->list; cur;) {
1039 child_info = (ChildInfo *)cur->data;
1040 g_free(child_info->cmd);
1041 g_string_free(child_info->output, TRUE);
1042 children->list = g_slist_remove(children->list, child_info);
1044 cur = children->list;
1049 static void update_io_dialog(Children *children)
1053 debug_print("Updating actions input/output dialog.\n");
1055 if (!children->nb) {
1056 gtk_widget_set_sensitive(children->abort_btn, FALSE);
1057 gtk_widget_set_sensitive(children->close_btn, TRUE);
1058 if (children->input_hbox)
1059 gtk_widget_set_sensitive(children->input_hbox, FALSE);
1060 gtk_widget_grab_focus(children->close_btn);
1061 gtk_signal_connect(GTK_OBJECT(children->dialog),
1063 GTK_SIGNAL_FUNC(io_dialog_key_pressed_cb),
1067 if (children->output) {
1068 GtkWidget *text = children->text;
1070 ChildInfo *child_info;
1072 gtk_widget_show(children->scrolledwin);
1073 gtk_text_freeze(GTK_TEXT(text));
1074 gtk_text_set_point(GTK_TEXT(text), 0);
1075 gtk_text_forward_delete(GTK_TEXT(text),
1076 gtk_text_get_length(GTK_TEXT(text)));
1077 for (cur = children->list; cur; cur = cur->next) {
1078 child_info = (ChildInfo *)cur->data;
1079 if (child_info->pid)
1080 caption = g_strdup_printf
1081 (_("--- Running: %s\n"),
1084 caption = g_strdup_printf
1085 (_("--- Ended: %s\n"),
1088 gtk_text_insert(GTK_TEXT(text), NULL, NULL, NULL,
1090 gtk_text_insert(GTK_TEXT(text), NULL, NULL, NULL,
1091 child_info->output->str, -1);
1093 child_info->new_out = FALSE;
1095 gtk_text_thaw(GTK_TEXT(text));
1099 static void create_io_dialog(Children *children)
1103 GtkWidget *entry = NULL;
1104 GtkWidget *input_hbox = NULL;
1105 GtkWidget *send_button;
1108 GtkWidget *scrolledwin;
1110 GtkWidget *abort_button;
1111 GtkWidget *close_button;
1113 debug_print("Creating action IO dialog\n");
1115 dialog = gtk_dialog_new();
1116 gtk_container_set_border_width
1117 (GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), 5);
1118 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
1119 gtk_window_set_title(GTK_WINDOW(dialog), _("Action's input/output"));
1120 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1121 manage_window_set_transient(GTK_WINDOW(dialog));
1122 gtk_signal_connect(GTK_OBJECT(dialog), "delete_event",
1123 GTK_SIGNAL_FUNC(delete_io_dialog_cb), children);
1124 gtk_signal_connect(GTK_OBJECT(dialog), "destroy",
1125 GTK_SIGNAL_FUNC(hide_io_dialog_cb),
1128 vbox = gtk_vbox_new(FALSE, 8);
1129 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), vbox);
1130 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
1131 gtk_widget_show(vbox);
1133 label = gtk_label_new(children->action);
1134 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1135 gtk_widget_show(label);
1137 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
1138 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
1139 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
1140 gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0);
1141 gtk_widget_set_usize(scrolledwin, 480, 200);
1142 gtk_widget_hide(scrolledwin);
1144 text = gtk_text_new(gtk_scrolled_window_get_hadjustment
1145 (GTK_SCROLLED_WINDOW(scrolledwin)),
1146 gtk_scrolled_window_get_vadjustment
1147 (GTK_SCROLLED_WINDOW(scrolledwin)));
1148 gtk_text_set_editable(GTK_TEXT(text), FALSE);
1149 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
1150 gtk_widget_show(text);
1152 if (children->open_in) {
1153 input_hbox = gtk_hbox_new(FALSE, 8);
1154 gtk_widget_show(input_hbox);
1156 entry = gtk_entry_new();
1157 gtk_widget_set_usize(entry, 320, -1);
1158 gtk_signal_connect(GTK_OBJECT(entry), "activate",
1159 GTK_SIGNAL_FUNC(send_input), children);
1160 gtk_box_pack_start(GTK_BOX(input_hbox), entry, TRUE, TRUE, 0);
1161 if (children->open_in & ACTION_USER_HIDDEN_IN)
1162 gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
1163 gtk_widget_show(entry);
1165 send_button = gtk_button_new_with_label(_(" Send "));
1166 gtk_signal_connect(GTK_OBJECT(send_button), "clicked",
1167 GTK_SIGNAL_FUNC(send_input), children);
1168 gtk_box_pack_start(GTK_BOX(input_hbox), send_button, FALSE,
1170 gtk_widget_show(send_button);
1172 gtk_box_pack_start(GTK_BOX(vbox), input_hbox, FALSE, FALSE, 0);
1173 gtk_widget_grab_focus(entry);
1176 gtkut_button_set_create(&hbox, &abort_button, _("Abort"),
1177 &close_button, _("Close"), NULL, NULL);
1178 gtk_signal_connect(GTK_OBJECT(abort_button), "clicked",
1179 GTK_SIGNAL_FUNC(kill_children_cb), children);
1180 gtk_signal_connect(GTK_OBJECT(close_button), "clicked",
1181 GTK_SIGNAL_FUNC(hide_io_dialog_cb), children);
1182 gtk_widget_show(hbox);
1185 gtk_widget_set_sensitive(close_button, FALSE);
1187 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), hbox);
1189 children->dialog = dialog;
1190 children->scrolledwin = scrolledwin;
1191 children->text = text;
1192 children->input_hbox = children->open_in ? input_hbox : NULL;
1193 children->input_entry = children->open_in ? entry : NULL;
1194 children->abort_btn = abort_button;
1195 children->close_btn = close_button;
1197 gtk_widget_show(dialog);
1200 static void catch_status(gpointer data, gint source, GdkInputCondition cond)
1202 ChildInfo *child_info = (ChildInfo *)data;
1206 gdk_input_remove(child_info->tag_status);
1208 c = read(source, &buf, 1);
1209 debug_print("Child returned %c\n", buf);
1211 waitpid(-child_info->pid, NULL, 0);
1212 childinfo_close_pipes(child_info);
1213 child_info->pid = 0;
1215 wait_for_children(child_info->children);
1218 static void catch_input(gpointer data, gint source, GdkInputCondition cond)
1220 Children *children = (Children *)data;
1221 ChildInfo *child_info = (ChildInfo *)children->list->data;
1225 debug_print("Sending input to grand child.\n");
1226 if (!(cond && GDK_INPUT_WRITE))
1229 gdk_input_remove(child_info->tag_in);
1230 child_info->tag_in = -1;
1232 input = gtk_editable_get_chars(GTK_EDITABLE(children->input_entry),
1234 len = strlen(input);
1238 c = write(child_info->chld_in, input + count, len - count);
1241 } while (c >= 0 && count < len);
1244 write(child_info->chld_in, "\n", 2);
1248 gtk_entry_set_text(GTK_ENTRY(children->input_entry), "");
1249 gtk_widget_set_sensitive(children->input_hbox, TRUE);
1250 close(child_info->chld_in);
1251 child_info->chld_in = -1;
1252 debug_print("Input to grand child sent.\n");
1255 static void catch_output(gpointer data, gint source, GdkInputCondition cond)
1257 ChildInfo *child_info = (ChildInfo *)data;
1259 gchar buf[BUFFSIZE];
1261 debug_print("Catching grand child's output.\n");
1262 if (child_info->type & (ACTION_PIPE_OUT | ACTION_INSERT)
1263 && source == child_info->chld_out) {
1264 gboolean is_selection = FALSE;
1265 GtkWidget *text = child_info->text;
1266 if (GTK_EDITABLE(text)->has_selection)
1267 is_selection = TRUE;
1268 gtk_stext_freeze(GTK_STEXT(text));
1270 c = read(source, buf, sizeof(buf) - 1);
1273 gtk_stext_insert(GTK_STEXT(text), child_info->msgfont,
1274 NULL, NULL, buf, c);
1277 /* Using the select_region draws things. Should not.
1278 * so we just change selection position and
1279 * defere drawing when thawing. Hack?
1281 GTK_EDITABLE(text)->selection_end_pos =
1282 gtk_stext_get_point(GTK_STEXT(text));
1284 gtk_stext_thaw(GTK_STEXT(child_info->text));
1286 c = read(source, buf, sizeof(buf) - 1);
1287 for (i = 0; i < c; i++)
1288 g_string_append_c(child_info->output, buf[i]);
1290 child_info->new_out = TRUE;
1292 wait_for_children(child_info->children);
1295 static gboolean user_string_dialog_delete_cb(GtkWidget *widget,
1296 GdkEvent *event, gpointer data)
1301 static void user_string_dialog_destroy_cb(GtkWidget *widget, gpointer data)
1306 static void user_string_dialog_activate_cb(GtkWidget *widget, gpointer data)
1308 UserStringDialog *user_dialog = (UserStringDialog *) data;
1310 g_free(user_dialog->user_str);
1311 user_dialog->user_str =
1312 gtk_editable_get_chars(GTK_EDITABLE(user_dialog->entry), 0, -1);
1313 gtk_widget_destroy(user_dialog->dialog);
1316 static gchar *get_user_string(const gchar *action, ActionType type)
1321 GtkWidget *ok_button;
1322 GtkWidget *cancel_button;
1324 UserStringDialog user_dialog;
1326 dialog = gtk_dialog_new();
1327 gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, FALSE, FALSE);
1328 gtk_container_set_border_width
1329 (GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), 8);
1330 gtk_container_set_border_width
1331 (GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), 5);
1332 gtk_window_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
1335 case ACTION_USER_HIDDEN_STR:
1336 gtk_window_set_title(GTK_WINDOW(dialog),
1337 _("Action's hidden user argument"));
1338 label_text = g_strdup_printf(_("Enter the '%%h' "
1339 "argument for the following "
1340 "action:\n%s"), action);
1342 case ACTION_USER_STR:
1343 gtk_window_set_title(GTK_WINDOW(dialog),
1344 _("Action's user argument"));
1345 label_text = g_strdup_printf(_("Enter the '%%u' "
1346 "argument for the following "
1347 "action:\n%s"), action);
1351 debug_print("Unsupported action type %d", type);
1354 label = gtk_label_new(label_text);
1356 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label,
1359 entry = gtk_entry_new();
1360 gtk_entry_set_visibility(GTK_ENTRY(entry),
1361 type != ACTION_USER_HIDDEN_STR);
1362 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), entry,
1365 ok_button = gtk_button_new_with_label(_("OK"));
1366 gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->action_area),
1367 ok_button, TRUE, TRUE, 0);
1369 cancel_button = gtk_button_new_with_label(_("Cancel"));
1370 gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->action_area),
1371 cancel_button, TRUE, TRUE, 0);
1373 user_dialog.dialog = dialog;
1374 user_dialog.user_str = NULL;
1375 user_dialog.entry = entry;
1377 gtk_signal_connect(GTK_OBJECT(dialog), "delete_event",
1378 GTK_SIGNAL_FUNC(user_string_dialog_delete_cb),
1380 gtk_signal_connect(GTK_OBJECT(dialog), "destroy",
1381 GTK_SIGNAL_FUNC(user_string_dialog_destroy_cb),
1383 gtk_signal_connect(GTK_OBJECT(entry), "activate",
1384 GTK_SIGNAL_FUNC(user_string_dialog_activate_cb),
1386 gtk_signal_connect(GTK_OBJECT(ok_button), "clicked",
1387 GTK_SIGNAL_FUNC(user_string_dialog_activate_cb),
1389 gtk_signal_connect_object(GTK_OBJECT(cancel_button), "clicked",
1390 GTK_SIGNAL_FUNC(gtk_widget_destroy),
1391 GTK_OBJECT(dialog));
1393 gtk_widget_grab_focus(entry);
1394 gtk_widget_show_all(dialog);
1397 return user_dialog.user_str;