452215494ffc960de71139990a624de66cd2e2b1
[claws.git] / src / prefs_actions.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2002 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <gtk/gtk.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <gdk/gdkx.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <sys/types.h>
35 #include <sys/wait.h>
36
37 #include "intl.h"
38 #include "prefs.h"
39 #include "inc.h"
40 #include "utils.h"
41 #include "gtkutils.h"
42 #include "manage_window.h"
43 #include "mainwindow.h"
44 #include "prefs_common.h"
45 #include "alertpanel.h"
46 #include "prefs_actions.h"
47 #include "compose.h"
48 #include "procmsg.h"
49 #include "gtkstext.h"
50
51                                                 
52 typedef enum {
53 ACTION_NONE             = 1 << 0,
54 ACTION_PIPE_IN          = 1 << 1,
55 ACTION_PIPE_OUT         = 1 << 2,
56 ACTION_SINGLE           = 1 << 3,
57 ACTION_MULTIPLE         = 1 << 4,
58 ACTION_ASYNC            = 1 << 5,
59 ACTION_OPEN_IN          = 1 << 6,
60 ACTION_HIDE_IN          = 1 << 7,
61 ACTION_ERROR            = 1 << 8,
62 } ActionsTypes;
63
64 static struct Actions {
65         GtkWidget *window;
66
67         GtkWidget *ok_btn;
68
69         GtkWidget *name_entry;
70         GtkWidget *cmd_entry;
71
72         GtkWidget *actions_clist;
73 } actions;
74
75 typedef struct _Children Children;
76 struct _Children {
77         GtkWidget       *window;
78         GtkWidget       *dialog;
79         GtkWidget       *text;
80         GtkWidget       *input_entry;
81         GtkWidget       *input_hbox;
82         GtkWidget       *abort_btn;
83         GtkWidget       *scrolledwin;
84
85         gchar           *action;
86         gint             timer;
87         GSList          *list;
88         gint             nb;
89         gint             open_in;
90         gboolean         output;
91 };
92
93 typedef struct _ChildInfo ChildInfo;
94 struct _ChildInfo {
95         gchar           *cmd;
96         guint            type;
97         pid_t            pid;
98         gint             chld_in;
99         gint             chld_out;
100         gint             chld_err;
101         gint             tag_in;
102         gint             tag_out;
103         gint             tag_err;
104         gint             new_out;
105         GString         *output;
106         GtkWidget       *text;
107 };
108
109 /* widget creating functions */
110 static void prefs_actions_create        (MainWindow *mainwin);
111 static void prefs_actions_set_dialog    (void);
112 static gint prefs_actions_clist_set_row (gint row);
113
114 /* callback functions */
115 static void prefs_actions_help_cb       (GtkWidget *w, gpointer data);
116 static void prefs_actions_register_cb   (GtkWidget *w, gpointer data);
117 static void prefs_actions_substitute_cb (GtkWidget *w, gpointer data);
118 static void prefs_actions_delete_cb     (GtkWidget *w, gpointer data);
119 static void prefs_actions_up            (GtkWidget *w, gpointer data);
120 static void prefs_actions_down          (GtkWidget *w, gpointer data);
121 static void prefs_actions_select        (GtkCList       *clist,
122                                          gint            row,
123                                          gint            column,
124                                          GdkEvent       *event);
125 static void prefs_actions_row_move      (GtkCList       *clist,
126                                          gint            source_row,
127                                          gint            dest_row);
128 static gint prefs_actions_deleted       (GtkWidget      *widget,
129                                          GdkEventAny    *event,
130                                          gpointer       *data);
131 static void prefs_actions_key_pressed   (GtkWidget      *widget,
132                                          GdkEventKey    *event,
133                                          gpointer        data);
134 static void prefs_actions_cancel        (GtkWidget *w, gpointer data);
135 static void prefs_actions_ok            (GtkWidget *w, gpointer data);
136 static void update_actions_menu         (GtkItemFactory *ifactory,
137                                          gchar *branch_path,
138                                          gpointer callback,
139                                          gpointer data);
140 static void mainwin_actions_execute_cb  (MainWindow     *mainwin,
141                                          guint           action_nb,
142                                          GtkWidget      *widget);
143 static void compose_actions_execute_cb  (Compose        *compose,
144                                          guint           action_nb,
145                                          GtkWidget      *widget);
146 static guint get_action_type            (gchar *action);
147
148 static gboolean execute_actions         (gchar *action, 
149                                          GtkWidget *window,
150                                          GtkCTree *ctree, 
151                                          GtkWidget *text);
152
153 static gchar *parse_action_cmd          (gchar *action,
154                                          MsgInfo *msginfo,
155                                          GtkCTree *ctree);
156 static GString *parse_append_filename   (GString *cmd,
157                                          MsgInfo *msginfo);
158
159 ChildInfo *fork_child                   (gchar *cmd,
160                                          gint action_type,
161                                          GtkWidget *text);
162
163 static gint wait_for_children           (gpointer data);
164
165 static void free_children               (Children *children);
166
167 static void childinfo_close_pipes       (ChildInfo *child_info);
168
169 static void create_io_dialog            (Children *children);
170
171 static void update_io_dialog            (Children *children);
172
173 static void hide_io_dialog_cb           (GtkWidget *widget,
174                                          gpointer data);
175
176 static void catch_output                (gpointer data,
177                                          gint source,
178                                          GdkInputCondition cond);
179 static void catch_input                 (gpointer data, 
180                                          gint source,
181                                          GdkInputCondition cond);
182
183 void prefs_actions_open(MainWindow *mainwin)
184 {
185         if (prefs_rc_is_readonly(ACTIONS_RC))
186                 return;
187         inc_lock();
188
189         if (!actions.window)
190                 prefs_actions_create(mainwin);
191         
192         manage_window_set_transient(GTK_WINDOW(actions.window));
193         gtk_widget_grab_focus(actions.ok_btn);
194
195         prefs_actions_set_dialog();
196
197         gtk_widget_show(actions.window);
198 }
199
200 static void prefs_actions_create(MainWindow *mainwin)
201 {
202         GtkWidget *window;
203         GtkWidget *vbox;
204         GtkWidget *ok_btn;
205         GtkWidget *cancel_btn;
206         GtkWidget *confirm_area;
207
208         GtkWidget *vbox1;
209         GtkWidget *hbox;
210         GtkWidget *name_label;
211         GtkWidget *name_entry;
212         GtkWidget *cmd_label;
213         GtkWidget *cmd_entry;
214
215         GtkWidget *reg_hbox;
216         GtkWidget *btn_hbox;
217         GtkWidget *arrow;
218         GtkWidget *reg_btn;
219         GtkWidget *subst_btn;
220         GtkWidget *del_btn;
221
222         GtkWidget *cond_hbox;
223         GtkWidget *cond_scrolledwin;
224         GtkWidget *cond_clist;
225
226         GtkWidget *help_vbox;
227         GtkWidget *help_label;
228         GtkWidget *help_toggle;
229
230         GtkWidget *btn_vbox;
231         GtkWidget *up_btn;
232         GtkWidget *down_btn;
233         
234         gchar *title[1];
235
236         debug_print(_("Creating actions setting window...\n"));
237
238         window = gtk_window_new (GTK_WINDOW_DIALOG);
239                 
240         gtk_container_set_border_width (GTK_CONTAINER (window), 8);
241         gtk_window_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
242         gtk_window_set_modal (GTK_WINDOW (window), TRUE);
243         gtk_window_set_policy (GTK_WINDOW (window), FALSE, TRUE, TRUE);
244
245         vbox = gtk_vbox_new (FALSE, 6);
246         gtk_widget_show (vbox);
247         gtk_container_add (GTK_CONTAINER (window), vbox);
248
249         gtkut_button_set_create(&confirm_area, &ok_btn, _("OK"),
250                                 &cancel_btn, _("Cancel"), NULL, NULL);
251         gtk_widget_show (confirm_area);
252         gtk_box_pack_end (GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
253         gtk_widget_grab_default (ok_btn);
254                                          
255         gtk_window_set_title (GTK_WINDOW(window),
256                               _("Actions setting"));
257         gtk_signal_connect (GTK_OBJECT(window), "delete_event",
258                             GTK_SIGNAL_FUNC(prefs_actions_deleted), NULL);
259         gtk_signal_connect (GTK_OBJECT(window), "key_press_event",
260                             GTK_SIGNAL_FUNC(prefs_actions_key_pressed), NULL);
261         gtk_signal_connect (GTK_OBJECT(window), "focus_in_event",
262                             GTK_SIGNAL_FUNC(manage_window_focus_in), NULL);
263         gtk_signal_connect (GTK_OBJECT(window), "focus_out_event",
264                             GTK_SIGNAL_FUNC(manage_window_focus_out), NULL);
265         gtk_signal_connect (GTK_OBJECT(ok_btn), "clicked",
266                             GTK_SIGNAL_FUNC(prefs_actions_ok), mainwin);
267         gtk_signal_connect (GTK_OBJECT(cancel_btn), "clicked",
268                             GTK_SIGNAL_FUNC(prefs_actions_cancel), NULL);
269
270         vbox1 = gtk_vbox_new (FALSE, VSPACING);
271         gtk_widget_show (vbox1);
272         gtk_box_pack_start (GTK_BOX (vbox), vbox1, TRUE, TRUE, 0);
273         gtk_container_set_border_width (GTK_CONTAINER (vbox1), 2);
274
275         hbox = gtk_hbox_new (FALSE, 0);
276         gtk_box_pack_start (GTK_BOX(vbox1), hbox, TRUE, TRUE, 0);
277         
278         name_label = gtk_label_new(_("Menu name: "));
279         gtk_widget_show(name_label);
280         gtk_box_pack_start (GTK_BOX (hbox), name_label, FALSE, FALSE, 0);
281
282         name_entry = gtk_entry_new();
283         gtk_widget_show(name_entry);
284         gtk_box_pack_start (GTK_BOX (hbox), name_entry, TRUE, TRUE, 0);
285
286         gtk_widget_show_all(hbox);
287
288         hbox = gtk_hbox_new (FALSE, 0);
289         gtk_box_pack_start (GTK_BOX(vbox1), hbox, TRUE, TRUE, 0);
290                  
291         cmd_label = gtk_label_new(_("Command line: "));
292         gtk_box_pack_start (GTK_BOX (hbox), cmd_label, FALSE, FALSE, 0);
293
294         cmd_entry = gtk_entry_new();
295         gtk_box_pack_start (GTK_BOX (hbox), cmd_entry, TRUE, TRUE, 0);
296
297         gtk_widget_show_all(hbox);
298
299         help_vbox = gtk_vbox_new(FALSE, 8);
300         gtk_box_pack_start (GTK_BOX (vbox1), help_vbox, TRUE, TRUE, 0);
301
302         help_label = gtk_label_new (_("Menu name:\n"
303                                       " Use '/' in menu name to make submenus.\n"
304                                       "Command line:\n"
305                                       " Begin with:\n   '|' to send message "
306                                       "body or selection "
307                                       "to command\n   '>' to send user provided"
308                                       " text to command\n   '*' to send user "
309                                       "provided hidden text to command\n"
310                                       " End with:\n   '|' to replace "
311                                       "message body or selection with command "
312                                       "output\n   '&' to"
313                                       "run command asynchronously\n Use '%f' "
314                                       "for message file name\n and '%F' for the"
315                                       " list of the file names of selected "
316                                       "messages."));
317         gtk_misc_set_alignment(GTK_MISC(help_label), 0, 0.5);
318         gtk_label_set_justify (GTK_LABEL(help_label), GTK_JUSTIFY_LEFT);
319         gtk_widget_show (help_label);
320         gtk_box_pack_start (GTK_BOX (help_vbox), help_label, TRUE, TRUE, 0);
321         gtk_widget_hide(help_vbox);
322
323                 /* register / substitute / delete */
324
325         reg_hbox = gtk_hbox_new (FALSE, 4);
326         gtk_widget_show (reg_hbox);
327         gtk_box_pack_start (GTK_BOX (vbox1), reg_hbox, FALSE, FALSE, 0);
328
329         arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
330         gtk_widget_show (arrow);
331         gtk_box_pack_start (GTK_BOX (reg_hbox), arrow, FALSE, FALSE, 0);
332         gtk_widget_set_usize (arrow, -1, 16);
333
334         btn_hbox = gtk_hbox_new (FALSE, 4);
335         gtk_widget_show (btn_hbox);
336         gtk_box_pack_start (GTK_BOX (reg_hbox), btn_hbox, FALSE, FALSE, 0);
337
338         reg_btn = gtk_button_new_with_label (_("Register"));
339         gtk_widget_show (reg_btn);
340         gtk_box_pack_start (GTK_BOX (btn_hbox), reg_btn, FALSE, TRUE, 0);
341         gtk_signal_connect (GTK_OBJECT (reg_btn), "clicked",
342                             GTK_SIGNAL_FUNC (prefs_actions_register_cb), NULL);
343
344         subst_btn = gtk_button_new_with_label (_(" Substitute "));
345         gtk_widget_show (subst_btn);
346         gtk_box_pack_start (GTK_BOX (btn_hbox), subst_btn, FALSE, TRUE, 0);
347         gtk_signal_connect (GTK_OBJECT (subst_btn), "clicked",
348                             GTK_SIGNAL_FUNC (prefs_actions_substitute_cb),
349                             NULL);
350
351         del_btn = gtk_button_new_with_label (_("Delete"));
352         gtk_widget_show (del_btn);
353         gtk_box_pack_start (GTK_BOX (btn_hbox), del_btn, FALSE, TRUE, 0);
354         gtk_signal_connect (GTK_OBJECT (del_btn), "clicked",
355                             GTK_SIGNAL_FUNC (prefs_actions_delete_cb), NULL);
356
357         help_toggle = gtk_toggle_button_new_with_label (_("Help on syntax"));
358         gtk_widget_show (help_toggle);
359         gtk_box_pack_end (GTK_BOX (btn_hbox), help_toggle, FALSE, TRUE, 0);
360         gtk_signal_connect (GTK_OBJECT (help_toggle), "toggled",
361                             GTK_SIGNAL_FUNC (prefs_actions_help_cb), help_vbox);
362
363         cond_hbox = gtk_hbox_new (FALSE, 8);
364         gtk_widget_show (cond_hbox);
365         gtk_box_pack_start (GTK_BOX (vbox1), cond_hbox, TRUE, TRUE, 0);
366
367         cond_scrolledwin = gtk_scrolled_window_new (NULL, NULL);
368         gtk_widget_show (cond_scrolledwin);
369         gtk_widget_set_usize (cond_scrolledwin, -1, 150);
370         gtk_box_pack_start (GTK_BOX (cond_hbox), cond_scrolledwin,
371                             TRUE, TRUE, 0);
372         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (cond_scrolledwin),
373                                         GTK_POLICY_AUTOMATIC,
374                                         GTK_POLICY_AUTOMATIC);
375
376         title[0] = _("Registered actions");
377         cond_clist = gtk_clist_new_with_titles(1, title);
378         gtk_widget_show (cond_clist);
379         gtk_container_add (GTK_CONTAINER (cond_scrolledwin), cond_clist);
380         gtk_clist_set_column_width (GTK_CLIST (cond_clist), 0, 80);
381         gtk_clist_set_selection_mode (GTK_CLIST (cond_clist),
382                                       GTK_SELECTION_BROWSE);
383         GTK_WIDGET_UNSET_FLAGS (GTK_CLIST (cond_clist)->column[0].button,
384                                 GTK_CAN_FOCUS);
385         gtk_signal_connect (GTK_OBJECT (cond_clist), "select_row",
386                             GTK_SIGNAL_FUNC (prefs_actions_select), NULL);
387         gtk_signal_connect_after (GTK_OBJECT (cond_clist), "row_move",
388                                   GTK_SIGNAL_FUNC (prefs_actions_row_move),
389                                   NULL);
390
391         btn_vbox = gtk_vbox_new (FALSE, 8);
392         gtk_widget_show (btn_vbox);
393         gtk_box_pack_start (GTK_BOX (cond_hbox), btn_vbox, FALSE, FALSE, 0);
394
395         up_btn = gtk_button_new_with_label (_("Up"));
396         gtk_widget_show (up_btn);
397         gtk_box_pack_start (GTK_BOX (btn_vbox), up_btn, FALSE, FALSE, 0);
398         gtk_signal_connect (GTK_OBJECT (up_btn), "clicked",
399                             GTK_SIGNAL_FUNC (prefs_actions_up), NULL);
400
401         down_btn = gtk_button_new_with_label (_("Down"));
402         gtk_widget_show (down_btn);
403         gtk_box_pack_start (GTK_BOX (btn_vbox), down_btn, FALSE, FALSE, 0);
404         gtk_signal_connect (GTK_OBJECT (down_btn), "clicked",
405                             GTK_SIGNAL_FUNC (prefs_actions_down), NULL);
406
407         gtk_widget_show(window);
408
409         actions.window = window;
410         actions.ok_btn = ok_btn;
411
412         actions.name_entry = name_entry;
413         actions.cmd_entry  = cmd_entry;
414         
415         actions.actions_clist = cond_clist;
416 }
417
418 static void prefs_actions_help_cb(GtkWidget *w, gpointer data)
419 {
420         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)))
421                 gtk_widget_show(GTK_WIDGET(data));
422         else
423                 gtk_widget_hide(GTK_WIDGET(data));
424 }
425
426 void prefs_actions_read_config()
427 {
428         gchar *rcpath;
429         FILE *fp;
430         gchar buf[PREFSBUFSIZE];
431         gchar *act;
432
433         debug_print(_("Reading actions configurations...\n"));
434
435         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ACTIONS_RC, NULL);
436         if ((fp = fopen(rcpath, "r")) == NULL) {
437                 if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen");
438                 g_free(rcpath);
439                 return;
440         }
441         g_free(rcpath);
442
443         while (prefs_common.actionslst != NULL) {
444                 act = (gchar *) prefs_common.actionslst->data;
445                 prefs_common.actionslst = g_slist_remove(
446                                                 prefs_common.actionslst,
447                                                 act);
448                 g_free(act);
449         }
450         
451         while (fgets(buf, sizeof(buf), fp) != NULL) {
452                 g_strchomp(buf);
453                 act = strstr(buf, ": ");
454                 if (act && act[2] && 
455                     get_action_type(&act[2]) != ACTION_ERROR)
456                         prefs_common.actionslst = g_slist_append(
457                                                         prefs_common.actionslst,
458                                                         g_strdup(buf));
459         }
460         fclose(fp);
461 }
462
463 void prefs_actions_write_config()
464 {
465         gchar *rcpath;
466         PrefFile *pfile;
467         GSList *cur;
468
469         debug_print(_("Writing actions configuration...\n"));
470
471         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ACTIONS_RC, NULL);
472         if ((pfile= prefs_write_open(rcpath)) == NULL) {
473                 g_warning(_("failed to write configuration to file\n"));
474                 g_free(rcpath);
475                 return;
476         }
477
478         for (cur = prefs_common.actionslst; cur != NULL; cur = cur->next) {
479                 gchar *act = (gchar *) cur->data;
480                 if (fputs(act, pfile->fp) == EOF ||
481                     fputc('\n', pfile->fp) == EOF) {
482                         FILE_OP_ERROR(rcpath, "fputs || fputc");
483                         prefs_write_close_revert(pfile);
484                         g_free(rcpath);
485                         return;
486                 }
487         }
488         
489         g_free(rcpath);
490
491         if (prefs_write_close(pfile) < 0) {
492                 g_warning(_("failed to write configuration to file\n"));
493                 return;
494         }
495 }
496
497 static guint get_action_type(gchar *action)
498 {
499         gchar *p;
500         guint action_type = ACTION_NONE;
501         
502         g_return_val_if_fail(action,  ACTION_ERROR);
503         g_return_val_if_fail(*action, ACTION_ERROR);
504
505         p = action;
506
507         if (p[0] == '|') {
508                 action_type |= ACTION_PIPE_IN;
509                 p++;
510         } else if (p[0] == '>') {
511                 action_type |= ACTION_OPEN_IN;
512                 p++;
513         } else if (p[0] == '*') {
514                 action_type |= ACTION_HIDE_IN;
515                 p++;
516         }
517
518         if (p[0] == 0x00)
519                 return ACTION_ERROR;
520
521         while (*p && action_type != ACTION_ERROR) {
522                 if (p[0] == '%') {
523                         switch (p[1]) {
524                                 case 'f': action_type |= ACTION_SINGLE;
525                                           break;
526                                 case 'F': action_type |= ACTION_MULTIPLE;
527                                           break;
528                                 default:  action_type  = ACTION_ERROR;
529                                           break;
530                         }
531                 } else if (p[0] == '|') {
532                         if (p[1] == 0x00)
533                                 action_type |= ACTION_PIPE_OUT;
534                         else
535                                 action_type  = ACTION_ERROR;
536                 } else if (p[0] == '&') {
537                         if (p[1] == 0x00)
538                                 action_type |= ACTION_ASYNC;
539                         else
540                                 action_type  = ACTION_ERROR;
541                 }
542                 p++;
543         }
544
545         return action_type;
546 }
547
548 static gchar *parse_action_cmd          (gchar *action,
549                                          MsgInfo *msginfo,
550                                          GtkCTree *ctree)
551 {
552         GString *cmd;
553         gchar *p;
554         GList *cur;
555         MsgInfo *msg;
556         
557         p = action;
558         
559         if (p[0] == '|' || p[0] == '>' || p[0] == '*')
560                 p++;
561
562         cmd = g_string_sized_new(strlen(action));
563
564         while (p[0] && p[0] != '|' && p[0] != '&') {
565                 if (p[0] == '%' && p[1]) {
566                         switch (p[1]) {
567                            case 'f': cmd = parse_append_filename(cmd, msginfo);
568                                      p++;       
569                                      break;
570                            case 'F':
571                                      for (cur =GTK_CLIST(ctree)->selection;
572                                           cur; cur = cur->next) {
573                                         msg = gtk_ctree_node_get_row_data(ctree,
574                                               GTK_CTREE_NODE(cur->data));
575                                         cmd = parse_append_filename(cmd, msg);
576                                         cmd = g_string_append_c(cmd, ' ');
577                                      }
578                                      p++;
579                                      break;
580                            default: cmd = g_string_append_c(cmd, p[0]);
581                                     cmd = g_string_append_c(cmd, p[1]);
582                                     p++;
583                         }
584                 } else {
585                         cmd = g_string_append_c(cmd, p[0]);
586                 }
587                 p++;
588         }
589         if (!cmd->len) {
590                 g_string_free(cmd, TRUE);
591                 return NULL;
592         }
593
594         p = cmd->str;
595         g_string_free(cmd, FALSE);
596         return p;
597 }
598
599 static GString *parse_append_filename(GString *cmd, MsgInfo *msginfo)
600 {
601         gchar *filename;
602
603         g_return_val_if_fail(msginfo, cmd);
604
605         filename = procmsg_get_message_file(msginfo);
606
607         cmd = g_string_append(cmd, filename);
608
609         g_free(filename);
610
611         return cmd;
612 }
613
614 static void prefs_actions_set_dialog    (void)
615 {
616         GtkCList *clist = GTK_CLIST(actions.actions_clist);
617         GSList *cur;
618         gchar *action_str[1];
619         gint row;
620
621         gtk_clist_freeze(clist);
622         gtk_clist_clear(clist);
623
624         action_str[0] = _("(New)");
625         row = gtk_clist_append(clist, action_str);
626         gtk_clist_set_row_data(clist, row, NULL);
627
628         for (cur = prefs_common.actionslst; cur != NULL; cur = cur->next) {
629                 gchar *action[1];
630                 action[0] = (gchar *) cur->data;
631
632                 row = gtk_clist_append(clist, action);
633                 gtk_clist_set_row_data(clist, row, action[0]);
634         }
635         
636         gtk_clist_thaw(clist);
637 }
638 static void prefs_actions_set_list(void)
639 {
640         gint row = 1;
641         gchar *action;
642         
643         g_slist_free(prefs_common.actionslst);
644         prefs_common.actionslst = NULL;
645
646         while ( (action = (gchar *) gtk_clist_get_row_data(GTK_CLIST(
647                                                  actions.actions_clist), row))
648                 != NULL) {
649                 prefs_common.actionslst = g_slist_append(prefs_common.actionslst,
650                                                          action);
651                 row++;
652         }
653 }
654                 
655
656 #define GET_ENTRY(entry) \
657         entry_text = gtk_entry_get_text(GTK_ENTRY(entry))
658 static gint prefs_actions_clist_set_row (gint row)
659 {
660         GtkCList *clist = GTK_CLIST(actions.actions_clist);
661         gchar *entry_text;
662         gint len;
663         gchar action[PREFSBUFSIZE];
664         gchar *buf[1];
665         
666         
667         g_return_val_if_fail(row != 0, -1);
668
669         
670
671         GET_ENTRY(actions.name_entry);
672         if (entry_text[0] == '\0') {
673                 alertpanel_error(_("Menu name is not set."));
674                 return -1;
675         }
676
677         if (strchr(entry_text, ':')) {
678                 alertpanel_error(_("Colon ':' is not allowed in the menu name."));
679                 return -1;
680         }
681         
682         strncpy(action, entry_text, PREFSBUFSIZE - 1);
683         g_strstrip(action);
684
685         /* Keep space for the ': ' delimiter */
686         len = strlen(action) + 2;
687         if (len >= PREFSBUFSIZE - 1) {
688                 alertpanel_error(_("Menu name is too long."));
689                 return -1;
690         }
691
692         strcat(action, ": ");
693
694         GET_ENTRY(actions.cmd_entry);
695
696         if (entry_text[0] == '\0') {
697                 alertpanel_error(_("Command line not set."));
698                 return -1;
699         }
700
701         if (len + strlen(entry_text) >= PREFSBUFSIZE - 1) {
702                 alertpanel_error(_("Menu name and command are too long."));
703                 return -1;
704         }
705
706         if (get_action_type(entry_text) == ACTION_ERROR) {
707                 alertpanel_error(_("The command\n%s\nhas a syntax error."), 
708                                  entry_text);
709                 return -1;
710         }
711         
712         strcat(action, entry_text);
713
714         buf[0] = action;
715         if (row < 0)
716                 row = gtk_clist_append(clist, buf);
717         else {
718                 gchar *old_action;
719                 gtk_clist_set_text(clist, row, 0, action);
720                 old_action = (gchar *) gtk_clist_get_row_data(clist, row);
721                 if (old_action)
722                         g_free(old_action);
723         }
724
725         buf[0] = g_strdup(action);
726         
727         gtk_clist_set_row_data(clist, row, buf[0]);
728
729         prefs_actions_set_list();
730
731         return 0;
732 }
733         
734 /* callback functions */
735 static void prefs_actions_register_cb   (GtkWidget *w, gpointer data)
736 {
737         prefs_actions_clist_set_row(-1);
738 }
739
740 static void prefs_actions_substitute_cb (GtkWidget *w, gpointer data)
741 {
742         GtkCList *clist = GTK_CLIST(actions.actions_clist);
743         gchar *action;
744         gint row;
745
746         if (!clist->selection) return;
747
748         row = GPOINTER_TO_INT(clist->selection->data);
749         if (row == 0) return;
750
751         action = gtk_clist_get_row_data(clist, row);
752         if (!action) return;
753         
754         prefs_actions_clist_set_row(row);
755 }
756
757 static void prefs_actions_delete_cb     (GtkWidget *w, gpointer data)
758 {
759         GtkCList *clist = GTK_CLIST(actions.actions_clist);
760         gchar *action;
761         gint row;
762
763         if (!clist->selection) return;
764         row = GPOINTER_TO_INT(clist->selection->data);
765         if (row == 0) return;
766
767         if (alertpanel(_("Delete action"),
768                        _("Do you really want to delete this action?"),
769                        _("Yes"), _("No"), NULL) == G_ALERTALTERNATE)
770                 return;
771
772         action = gtk_clist_get_row_data(clist, row);
773         g_free(action);
774         gtk_clist_remove(clist, row);
775         prefs_common.actionslst = g_slist_remove(prefs_common.actionslst, action);
776 }
777
778 static void prefs_actions_up            (GtkWidget *w, gpointer data)
779 {
780         GtkCList *clist = GTK_CLIST(actions.actions_clist);
781         gint row;
782
783         if (!clist->selection) return;
784
785         row = GPOINTER_TO_INT(clist->selection->data);
786         if (row > 1)
787                 gtk_clist_row_move(clist, row, row - 1);
788 }
789
790 static void prefs_actions_down          (GtkWidget *w, gpointer data)
791 {
792         GtkCList *clist = GTK_CLIST(actions.actions_clist);
793         gint row;
794
795         if (!clist->selection) return;
796
797         row = GPOINTER_TO_INT(clist->selection->data);
798         if (row > 0 && row < clist->rows - 1)
799                 gtk_clist_row_move(clist, row, row + 1);
800 }
801
802 #define ENTRY_SET_TEXT(entry, str) \
803         gtk_entry_set_text(GTK_ENTRY(entry), str ? str : "")
804 static void prefs_actions_select        (GtkCList       *clist,
805                                          gint            row,
806                                          gint            column,
807                                          GdkEvent       *event)
808 {
809         gchar *action;
810         gchar *cmd;
811         gchar buf[PREFSBUFSIZE];
812         action = gtk_clist_get_row_data(clist, row);
813
814         if (!action) {
815                 ENTRY_SET_TEXT(actions.name_entry, "");
816                 ENTRY_SET_TEXT(actions.cmd_entry, "");
817                 return;
818         }
819         
820         strncpy(buf, action, PREFSBUFSIZE - 1);
821         buf[PREFSBUFSIZE - 1] = 0x00;
822         cmd = strstr2(buf, ": ");
823
824         if (cmd && cmd[2])
825                 ENTRY_SET_TEXT(actions.cmd_entry, &cmd[2]);
826         else
827                 return;
828
829         *cmd = 0x00;
830         ENTRY_SET_TEXT(actions.name_entry, buf);
831 }
832         
833 static void prefs_actions_row_move      (GtkCList       *clist,
834                                          gint            source_row,
835                                          gint            dest_row)
836 {
837         prefs_actions_set_list();
838         if (gtk_clist_row_is_visible(clist, dest_row) != GTK_VISIBILITY_FULL) {
839                 gtk_clist_moveto(clist, dest_row, -1,
840                                  source_row < dest_row ? 1.0 : 0.0, 0.0);
841         }
842 }
843
844 static gint prefs_actions_deleted       (GtkWidget      *widget,
845                                          GdkEventAny    *event,
846                                          gpointer       *data)
847 {
848         prefs_actions_cancel(widget, data);
849         return TRUE;
850 }
851
852 static void prefs_actions_key_pressed   (GtkWidget      *widget,
853                                          GdkEventKey    *event,
854                                          gpointer        data)
855 {
856         if (event && event->keyval == GDK_Escape)
857                 prefs_actions_cancel(widget, data);
858 }
859
860 static void prefs_actions_cancel        (GtkWidget *w, gpointer data)
861 {
862         prefs_actions_read_config();
863         gtk_widget_hide(actions.window);
864         inc_unlock();
865 }
866
867 static void prefs_actions_ok            (GtkWidget *widget, gpointer data)
868 {
869         GtkItemFactory *ifactory;
870         MainWindow *mainwin = (MainWindow *) data;
871
872         prefs_actions_write_config();
873         ifactory = gtk_item_factory_from_widget(mainwin->menubar);
874         update_mainwin_actions_menu(ifactory, mainwin);
875         gtk_widget_hide(actions.window);
876         inc_unlock();
877 }
878
879 void update_mainwin_actions_menu(GtkItemFactory *ifactory, 
880                                  MainWindow *mainwin)
881 {
882         update_actions_menu(ifactory, "/Edit/Actions", 
883                             mainwin_actions_execute_cb, 
884                             mainwin);
885 }
886
887 void update_compose_actions_menu(GtkItemFactory *ifactory, 
888                                  gchar *branch_path,
889                                  Compose *compose)
890 {
891         update_actions_menu(ifactory, branch_path, 
892                             compose_actions_execute_cb, 
893                             compose);
894 }
895                                  
896 static void update_actions_menu(GtkItemFactory *ifactory,
897                                 gchar *branch_path,
898                                 gpointer callback,
899                                 gpointer data)
900 {
901         GtkWidget *menuitem;
902         gchar *menu_path;
903         GSList *cur;
904         gchar *action, *action_p;
905         GtkWidget *menu;
906         
907         GtkItemFactoryEntry ifentry = {
908                 branch_path, NULL, NULL, 0, "<Branch>"};
909         menuitem = gtk_item_factory_get_item(ifactory, branch_path);    
910         /* FIXME: is there a better way to remove unknown submenu items? */
911         /* Deleting and recreating the parent looses the translation */
912         menu = GTK_WIDGET(GTK_MENU_ITEM(menuitem)->submenu);
913         if (GTK_MENU_SHELL(menu)->children) {
914                 GList *amenu, *alist;
915                 for (amenu = (GTK_MENU_SHELL(menu)->children); amenu; ) {
916                         alist = amenu->next;
917                         gtk_widget_destroy(GTK_WIDGET(amenu->data));
918                         amenu = alist;
919                 }
920         }
921
922         ifentry.accelerator     = NULL;
923         ifentry.callback_action = 0;
924         ifentry.callback        = callback;
925         ifentry.item_type       = NULL;
926
927         for (cur = prefs_common.actionslst; cur; cur = cur->next) {
928                 action   = g_strdup((gchar *) cur->data);
929                 action_p = strstr(action, ": ");
930                 if (action_p && action_p[2] &&
931                     get_action_type(&action_p[2]) != ACTION_ERROR) {
932                         action_p[0] = 0x00;
933                         menu_path = g_strdup_printf("%s/%s", branch_path, 
934                                                     action);
935                         ifentry.path = menu_path;
936                         gtk_item_factory_create_item(ifactory, &ifentry, data,
937                                                      1);
938                         g_free(menu_path);
939                 }
940                 g_free(action);
941                 ifentry.callback_action++;      
942         }
943 }
944
945 static void compose_actions_execute_cb  (Compose        *compose,
946                                          guint           action_nb,
947                                          GtkWidget      *widget)
948 {
949         gchar *buf, *action;
950         guint action_type;      
951
952         g_return_if_fail(action_nb < g_slist_length(prefs_common.actionslst));
953         
954         buf = (gchar *) g_slist_nth_data(prefs_common.actionslst, action_nb);
955         
956         g_return_if_fail(buf);
957         g_return_if_fail(action = strstr(buf, ": "));
958
959         /* Point to the beginning of the command-line */
960         action++;
961         action++;
962         
963         action_type = get_action_type(action);
964         if (action_type & ACTION_MULTIPLE ||
965             action_type & ACTION_SINGLE   ) {           
966                 alertpanel_warning(_("The selected action is not a pipe "
967                                      "action.\n You can only use pipe actions "
968                                      "when composing a message."));
969                 return;
970         }
971
972         execute_actions(action, compose->window, NULL, compose->text);
973 }
974
975 static void mainwin_actions_execute_cb  (MainWindow     *mainwin,
976                                          guint           action_nb,
977                                          GtkWidget      *widget)
978 {
979         MessageView *messageview = mainwin->messageview;
980         GtkWidget   *text = NULL;
981         gchar       *buf,
982                     *action;
983         
984         g_return_if_fail (action_nb < g_slist_length(prefs_common.actionslst));
985
986         buf = (gchar *) g_slist_nth_data(prefs_common.actionslst, action_nb);
987         
988         g_return_if_fail(buf);
989         g_return_if_fail(action = strstr(buf, ": "));
990
991         /* Point to the beginning of the command-line */
992         action++;
993         action++;
994
995         switch (messageview->type) {
996                 case MVIEW_TEXT:
997                         if (messageview->textview && 
998                             messageview->textview->text)
999                                 text = messageview->textview->text;
1000                         break;
1001                 case MVIEW_MIME:
1002                         if (messageview->mimeview &&
1003                             messageview->mimeview->type == MIMEVIEW_TEXT &&
1004                             messageview->mimeview->textview &&
1005                             messageview->mimeview->textview->text)
1006                                 text = messageview->mimeview->textview->text;
1007                         break;
1008         }
1009
1010         execute_actions(action, mainwin->window,
1011                         GTK_CTREE(mainwin->summaryview->ctree), text);
1012 }
1013
1014 static gboolean execute_actions(gchar *action, 
1015                                 GtkWidget *window,
1016                                 GtkCTree *ctree, 
1017                                 GtkWidget *text)
1018 {
1019         GList *cur, *selection;
1020         GSList *children_list = NULL;
1021         gint is_ok  = TRUE;
1022         gint selection_len;
1023         Children *children;
1024         ChildInfo *child_info;
1025         gint action_type;
1026         MsgInfo *msginfo;
1027         gchar *cmd;
1028
1029         g_return_val_if_fail(action && *action, FALSE);
1030
1031         action_type = get_action_type(action);
1032
1033         if (action_type == ACTION_ERROR)
1034                 return FALSE;         /* ERR: syntax error */
1035
1036         if (action_type & (ACTION_SINGLE | ACTION_MULTIPLE) && 
1037             !(ctree && GTK_CLIST(ctree)->selection))
1038                 return FALSE;         /* ERR: file command without selection */
1039
1040         if (ctree) {
1041                 selection = GTK_CLIST(ctree)->selection;
1042                 selection_len = g_list_length(selection);
1043         }
1044         
1045         if ((action_type & (ACTION_PIPE_OUT | ACTION_PIPE_IN))) {
1046                 if (ctree && selection_len > 1)
1047                         return FALSE; /* ERR: pipe + multiple selection */
1048                 if (!text)
1049                         return FALSE; /* ERR: pipe and no displayed text */
1050         }
1051
1052         if (!(action_type & ACTION_ASYNC) && window) {
1053                 gtk_widget_set_sensitive(window, FALSE);
1054         }
1055
1056         children = g_new0(Children, 1);
1057
1058         if (action_type & ACTION_SINGLE) {
1059                 for (cur = selection; cur && is_ok == TRUE; cur = cur->next) {
1060                         msginfo = gtk_ctree_node_get_row_data(ctree,
1061                                         GTK_CTREE_NODE(cur->data));
1062                         if (!msginfo) {
1063                                 is_ok  = FALSE; /* ERR: msginfo missing */
1064                                 break;
1065                         }
1066                         cmd = parse_action_cmd(action, msginfo, ctree);
1067                         if (!cmd) {
1068                                 is_ok  = FALSE; /* ERR: command incorrect */
1069                                 break;
1070                         }
1071                         if ((child_info = fork_child(cmd, action_type, text))) {
1072                                 children_list = g_slist_append(children_list,
1073                                                                child_info);
1074                                 children->open_in = (selection_len == 1) ? 
1075                                                     (action_type & 
1076                                                      (ACTION_OPEN_IN |
1077                                                       ACTION_HIDE_IN)) : 0;
1078                         }
1079                         g_free(cmd);
1080                 }
1081         } else {
1082                 cmd = parse_action_cmd(action, NULL, ctree);
1083                 if (cmd) {
1084                         if ((child_info = fork_child(cmd, action_type, text))) {
1085                                 children_list = g_slist_append(children_list,
1086                                                                child_info);
1087                                 children->open_in = action_type &
1088                                                     (ACTION_OPEN_IN |
1089                                                      ACTION_HIDE_IN);
1090                         }
1091                         g_free(cmd);
1092                 } else
1093                         is_ok  = FALSE;         /* ERR: command incorrect */
1094         }
1095
1096         if (!children_list) {
1097                  /* If not waiting for children, return */
1098                 g_free(children);
1099                 if (!(action_type & ACTION_ASYNC) && window) {
1100                         gtk_widget_set_sensitive(window, TRUE);
1101                 }
1102         } else {
1103                 children->action  = g_strdup(action);
1104                 children->window  = window;
1105                 children->timer   = 10;
1106                 children->dialog  = NULL;
1107                 children->list    = children_list;
1108                 children->nb      = g_slist_length(children_list);
1109
1110                 gtk_timeout_add(1000, wait_for_children, children);
1111         }
1112         return is_ok ;
1113 }
1114
1115 ChildInfo *fork_child(gchar *cmd,
1116                       gint action_type,
1117                       GtkWidget *text)
1118 {
1119         gint chld_in[2], chld_out[2], chld_err[2];
1120         gchar **cmdline;
1121         gint start, end, is_selection;
1122         gchar *selection;
1123         pid_t pid;
1124         ChildInfo *child_info;
1125
1126         if (action_type & ACTION_ASYNC) {
1127                 execute_command_line(cmd, TRUE);
1128                 return NULL; /* Asynchronous command */
1129         }
1130
1131         if (pipe(chld_in) || pipe(chld_out) || pipe(chld_err)) {
1132                 alertpanel_error(_("Command could not started. Pipe creation"
1133                                    " failed.\n%s"), g_strerror(errno));
1134                 return NULL; /* Pipe error */
1135         }
1136
1137         pid = fork();
1138         if (pid == (pid_t) 0) {/* Child */
1139                 if (setpgid(0, 0))
1140                         perror("setpgid");
1141                 
1142                 if (action_type & 
1143                     (ACTION_PIPE_IN | ACTION_OPEN_IN | ACTION_HIDE_IN)) {
1144                         close(fileno(stdin));
1145                         dup  (chld_in[0]);
1146                 }
1147                 close(chld_in[0]);
1148                 close(chld_in[1]);
1149
1150                 close(fileno(stdout));
1151                 dup  (chld_out[1]);
1152                 close(chld_out[0]);
1153                 close(chld_out[1]);
1154
1155                 close(fileno(stderr));
1156                 dup  (chld_err[1]);
1157                 close(chld_err[0]);
1158                 close(chld_err[1]);
1159
1160                 close(ConnectionNumber(gdk_display));
1161
1162                 cmdline = strsplit_with_quote(cmd, " ", 1024);
1163
1164                 execvp(cmdline[0], cmdline);
1165                 g_strfreev(cmdline);
1166
1167                 _exit(1);
1168         } else if (pid < (pid_t) 0) {/* Fork error */
1169                 alertpanel_error(_("Could not fork to execute the following "
1170                                    "command:\n%s\n%s"), 
1171                                  cmd, g_strerror(errno));
1172                 return NULL; 
1173         }
1174         /* Parent */
1175
1176         close(chld_in[0]);
1177         if (!(action_type & (ACTION_PIPE_IN | ACTION_OPEN_IN | ACTION_HIDE_IN)))
1178                 close(chld_in[1]);
1179         close(chld_out[1]);
1180         close(chld_err[1]);
1181
1182         child_info = g_new0(ChildInfo, 1);
1183         
1184         child_info->pid      = pid;
1185         child_info->cmd      = g_strdup(cmd);
1186         child_info->type     = action_type;
1187         child_info->new_out  = FALSE;
1188         child_info->output   = g_string_sized_new(0);
1189         child_info->chld_in  = (action_type & (ACTION_PIPE_IN | ACTION_OPEN_IN
1190                                 | ACTION_HIDE_IN))
1191                                ? chld_in [1] : -1;
1192         child_info->chld_out = chld_out[0];
1193         child_info->chld_err = chld_err[0];
1194         child_info->tag_in   = -1;
1195         child_info->tag_out  = gdk_input_add(chld_out[0], GDK_INPUT_READ,
1196                                              catch_output, child_info);
1197         child_info->tag_err  = gdk_input_add(chld_err[0], GDK_INPUT_READ,
1198                                              catch_output, child_info);
1199
1200         if (!(action_type & (ACTION_PIPE_IN | ACTION_PIPE_OUT)))
1201                 return child_info;
1202
1203         child_info->text     = text;
1204
1205         start = 0;
1206         end   = gtk_stext_get_length(GTK_STEXT(text));
1207
1208         if (GTK_EDITABLE(text)->has_selection) {
1209                 start = GTK_EDITABLE(text)->selection_start_pos;
1210                 end   = GTK_EDITABLE(text)->selection_end_pos;
1211                 if (start > end) {
1212                         gint tmp;
1213                         tmp = start;
1214                         start = end;
1215                         end = tmp;
1216                 }
1217                 is_selection = TRUE;
1218                 if (start == end) {
1219                         start = 0;
1220                         end   = gtk_stext_get_length(GTK_STEXT(text));
1221                         is_selection = FALSE;
1222                 }
1223         }
1224
1225         selection = gtk_editable_get_chars(GTK_EDITABLE(text), start, end);
1226
1227
1228         if (action_type & ACTION_PIPE_IN) {
1229                 write(chld_in[1], selection, strlen(selection));
1230                 if (!(action_type & (ACTION_OPEN_IN | ACTION_HIDE_IN)))
1231                         close(chld_in[1]);
1232                 child_info->chld_in = -1; /* No more input */
1233         }
1234         g_free(selection);
1235
1236         gtk_stext_freeze(GTK_STEXT(text));
1237         if (action_type & ACTION_PIPE_OUT) {
1238                 gtk_stext_set_point(GTK_STEXT(text), start);
1239                 gtk_stext_forward_delete(GTK_STEXT(text), end - start);
1240         }
1241
1242         gtk_stext_thaw(GTK_STEXT(text));
1243         
1244         return child_info;
1245 }
1246         
1247 static void kill_children_cb(GtkWidget *widget, gpointer data)
1248 {
1249         GSList *cur;
1250         Children *children = (Children *) data;
1251         ChildInfo *child_info;
1252
1253         for (cur = children->list; cur; cur = cur->next) {
1254                 child_info = (ChildInfo *)(cur->data);
1255                 if (child_info->pid) 
1256                         kill(child_info->pid, SIGTERM);
1257         }
1258 }
1259
1260 static gint wait_for_children(gpointer data)
1261 {
1262         gboolean new_output;
1263         Children *children = (Children *) data;
1264         ChildInfo *child_info;
1265         GSList *cur;
1266         gint nb = 0;
1267         pid_t pid, wpid;
1268
1269                 
1270         cur = children->list;
1271         new_output = FALSE;
1272         while (cur) {
1273                 child_info = (ChildInfo *) cur->data;
1274                 pid  = child_info->pid;
1275                 if (pid) {
1276                         wpid = waitpid(pid, NULL, WNOHANG);
1277                         if (wpid == pid) {
1278                                 childinfo_close_pipes(child_info);
1279                                 child_info->pid = 0;
1280                                 children->nb--;
1281                                 nb++;
1282                         }
1283                 }
1284                 new_output |= child_info->new_out;
1285                 cur = cur->next;
1286         }
1287
1288         if (children->timer)
1289                 children->timer--;
1290
1291         if (!children->dialog && (new_output || children->timer == 0 || children->open_in))
1292                 create_io_dialog(children);
1293
1294         children->output |= new_output;
1295
1296         if (new_output || (children->dialog && nb))
1297                 update_io_dialog(children);
1298
1299         if (children->nb)
1300                 return TRUE;
1301
1302         if (!children->dialog) {
1303                 gtk_widget_set_sensitive(children->window, TRUE);
1304                 free_children(children);
1305         } else if (!children->output) {
1306                 gtk_widget_destroy(children->dialog);
1307         }
1308         return FALSE;
1309 }
1310
1311 static void send_input(GtkWidget *w, gpointer data)
1312 {
1313         Children *children = (Children *) data;
1314         ChildInfo *child_info = (ChildInfo *) children->list->data;
1315         
1316         child_info->tag_in = gdk_input_add(child_info->chld_in,
1317                                            GDK_INPUT_WRITE,
1318                                            catch_input, children);
1319         gtk_widget_set_sensitive(children->input_hbox, FALSE);
1320 }
1321
1322 static gint delete_io_dialog_cb(GtkWidget *w, GdkEvent *e, gpointer data)
1323 {
1324         hide_io_dialog_cb(w, data);
1325         return TRUE;
1326 }
1327
1328 static void hide_io_dialog_cb(GtkWidget *w, gpointer data)
1329 {
1330
1331         Children *children = (Children *) data;
1332
1333         if (children->nb)
1334                 gtk_widget_hide(children->dialog);
1335         else {
1336                 gtk_widget_set_sensitive(children->window, TRUE);
1337                 gtk_signal_disconnect_by_data(GTK_OBJECT(children->dialog),
1338                                               children);
1339                 gtk_widget_destroy(children->dialog);
1340                 free_children(children);
1341         }
1342 }
1343
1344 static void childinfo_close_pipes(ChildInfo *child_info)
1345 {
1346         if (child_info->tag_in > 0)
1347                 gdk_input_remove(child_info->tag_in);
1348         gdk_input_remove(child_info->tag_out);
1349         gdk_input_remove(child_info->tag_err);
1350
1351         if (child_info->chld_in >= 0)
1352                 close(child_info->chld_in);
1353         close(child_info->chld_out);
1354         close(child_info->chld_err);
1355 }
1356
1357 static void free_children(Children *children)
1358 {
1359         GSList *cur;
1360         ChildInfo *child_info;
1361
1362         g_free(children->action);
1363         for (cur = children->list; cur;) {
1364                 child_info = (ChildInfo *) cur->data;
1365                 g_free(child_info->cmd);
1366                 g_string_free(child_info->output, TRUE);
1367                 children->list = g_slist_remove(children->list, child_info);
1368                 g_free(child_info);
1369                 cur = children->list;
1370         }
1371 }       
1372
1373 static void update_io_dialog(Children *children)
1374 {
1375         GSList *cur;
1376
1377         if (!children->nb) {
1378                 gtk_widget_set_sensitive(children->abort_btn, FALSE);
1379                 if (children->input_hbox)
1380                         gtk_widget_set_sensitive(children->input_hbox, FALSE);
1381                         
1382         }
1383
1384         if (children->output) {
1385                 GtkWidget *text = children->text;
1386                 gchar *caption;
1387                 ChildInfo *child_info;
1388
1389                 gtk_widget_show(children->scrolledwin);
1390                 gtk_text_freeze(GTK_TEXT(text));
1391                 gtk_text_set_point(GTK_TEXT(text), 0);
1392                 gtk_text_forward_delete(GTK_TEXT(text), -1);
1393                 for (cur = children->list; cur; cur = cur->next)
1394                 {
1395                         child_info = (ChildInfo *) cur->data;
1396                         if (child_info->pid)
1397                                 caption = g_strdup_printf(_("--- Running: %s\n")
1398                                                     , child_info->cmd);
1399                         else
1400                                 caption = g_strdup_printf(_("--- Ended: %s\n")
1401                                                     , child_info->cmd);
1402                         
1403                         gtk_text_insert(GTK_TEXT(text), NULL, NULL, NULL,
1404                                          caption, -1);
1405                         gtk_text_insert(GTK_TEXT(text), NULL, NULL, NULL,
1406                                          child_info->output->str, -1);
1407                         g_free(caption);
1408                         child_info->new_out = FALSE;
1409                 }
1410                 gtk_text_thaw(GTK_TEXT(text));
1411         }
1412 }
1413         
1414         
1415 static void create_io_dialog(Children *children)
1416 {
1417         GtkWidget *dialog;
1418         GtkWidget *entry;
1419         GtkWidget *input_hbox;
1420         GtkWidget *send_button;
1421         GtkWidget *label;
1422         GtkWidget *text;
1423         GtkWidget *scrolledwin;
1424         GtkWidget *hbox;
1425         GtkWidget *abort_button;
1426         GtkWidget *hide_button;
1427         
1428         dialog = gtk_dialog_new();
1429         label = gtk_label_new(children->action);
1430         gtk_misc_set_padding(GTK_MISC(label), 8, 8);
1431         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, FALSE, 
1432                            FALSE, 0);
1433
1434         gtk_window_set_title(GTK_WINDOW(dialog), _("Actions' input/output"));
1435         gtk_signal_connect(GTK_OBJECT(dialog), "delete_event",
1436                         GTK_SIGNAL_FUNC(delete_io_dialog_cb), children);
1437         gtk_signal_connect(GTK_OBJECT(dialog), "destroy",
1438                         GTK_SIGNAL_FUNC(hide_io_dialog_cb),
1439                         children);
1440
1441         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
1442         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
1443                                        GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
1444         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), scrolledwin, TRUE,
1445                            TRUE, 0);
1446         gtk_widget_set_usize(scrolledwin, 600, 200);
1447         gtk_widget_hide(scrolledwin);
1448
1449         text = gtk_text_new(gtk_scrolled_window_get_hadjustment
1450                             (GTK_SCROLLED_WINDOW(scrolledwin)),
1451                             gtk_scrolled_window_get_vadjustment
1452                             (GTK_SCROLLED_WINDOW(scrolledwin)));
1453         gtk_text_set_editable(GTK_TEXT(text), FALSE);
1454         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
1455         gtk_widget_show(text);
1456
1457         if (children->open_in) {
1458                 input_hbox = gtk_hbox_new(FALSE, 8);
1459                 gtk_widget_show(input_hbox);
1460
1461                 entry = gtk_entry_new();
1462                 gtk_signal_connect(GTK_OBJECT(entry), "activate",
1463                                    GTK_SIGNAL_FUNC(send_input), children);
1464                 gtk_box_pack_start(GTK_BOX(input_hbox), entry, TRUE, TRUE, 8);
1465                 if (children->open_in & ACTION_HIDE_IN)
1466                         gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
1467                 gtk_widget_show(entry);
1468
1469                 send_button = gtk_button_new_with_label(_("Send"));
1470                 gtk_signal_connect(GTK_OBJECT(send_button), "clicked",
1471                                    GTK_SIGNAL_FUNC(send_input), children);
1472                 gtk_box_pack_start(GTK_BOX(input_hbox), send_button, FALSE, 
1473                                    FALSE, 8);
1474                 gtk_widget_show(send_button);
1475
1476                 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), 
1477                                 input_hbox, FALSE, FALSE, 8);
1478         }
1479
1480         hbox = gtk_hbox_new(TRUE, 0);
1481         gtk_widget_show(hbox);
1482
1483         abort_button = gtk_button_new_with_label(_("Abort actions"));
1484         gtk_signal_connect(GTK_OBJECT(abort_button), "clicked",
1485                         GTK_SIGNAL_FUNC(kill_children_cb), children);
1486         gtk_box_pack_start(GTK_BOX(hbox), abort_button, TRUE, TRUE, 8);
1487         gtk_widget_show(abort_button);
1488
1489         hide_button = gtk_button_new_with_label(_("Close window"));
1490         gtk_signal_connect(GTK_OBJECT(hide_button), "clicked",
1491                         GTK_SIGNAL_FUNC(hide_io_dialog_cb), children);
1492         gtk_box_pack_start(GTK_BOX(hbox), hide_button, TRUE, TRUE, 8);
1493         gtk_widget_show(hide_button);
1494         
1495         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), 
1496                           hbox);
1497
1498         children->dialog      = dialog;
1499         children->scrolledwin = scrolledwin;
1500         children->text        = text;
1501         children->input_hbox  = children->open_in ? input_hbox : NULL;
1502         children->input_entry = children->open_in ? entry : NULL;
1503         children->abort_btn   = abort_button;
1504
1505         gtk_widget_show(dialog);
1506 }
1507
1508 static void catch_input(gpointer data, gint source, GdkInputCondition cond)
1509 {
1510         Children *children = (Children *) data;
1511         ChildInfo *child_info = (ChildInfo *) children->list->data;
1512         gchar *input;
1513         gint c;
1514
1515         if (!(cond && GDK_INPUT_WRITE))
1516                 return;
1517         
1518         gdk_input_remove(child_info->tag_in);
1519         child_info->tag_in = -1;
1520
1521         input = gtk_editable_get_chars(GTK_EDITABLE(children->input_entry), 0,
1522                                        -1);
1523         c = write(child_info->chld_in, input, strlen(input));
1524
1525         g_free(input);
1526
1527         write(child_info->chld_in, "\n", 2);
1528
1529         gtk_entry_set_text(GTK_ENTRY(children->input_entry), "");
1530         gtk_widget_set_sensitive(children->input_hbox, TRUE);
1531 }
1532
1533 static void catch_output(gpointer data, gint source, GdkInputCondition cond)
1534 {
1535         ChildInfo *child_info = (ChildInfo *) data;
1536         gint c, i;
1537         gchar buf[PREFSBUFSIZE];
1538
1539         if (child_info->type & ACTION_PIPE_OUT
1540             && source == child_info->chld_out) {
1541                 gboolean is_selection = FALSE;
1542                 GtkWidget *text = child_info->text;
1543                 if (GTK_EDITABLE(text)->has_selection)
1544                         is_selection = TRUE;
1545                 gtk_stext_freeze(GTK_STEXT(text));
1546                 while (TRUE) {
1547                         c =read(source, buf, PREFSBUFSIZE - 1);
1548                         if (c == 0)
1549                                 break;
1550                         gtk_stext_insert(GTK_STEXT(text), NULL, NULL, NULL,
1551                                          buf, c);
1552                 }
1553                 if (is_selection) {
1554                         /* Using the select_region draws things. Should not.
1555                          * so we just change selection position and 
1556                          * defere drawing when thawing. Hack?
1557                          */
1558                         GTK_EDITABLE(text)->selection_end_pos = 
1559                                         gtk_stext_get_point(GTK_STEXT(text));
1560                 }
1561                 gtk_stext_thaw(GTK_STEXT(child_info->text));
1562         } else {
1563                 while (TRUE) {
1564                         c = read(source, buf, PREFSBUFSIZE - 1);
1565                         if (c == 0) 
1566                                 break;
1567                         if (source == child_info->chld_out) 
1568                                 printf("stdout\n");
1569                         else
1570                                 printf("stderr\n");
1571                         for (i = 0; i < c; i++)
1572                                 child_info->output = g_string_append_c(
1573                                                 child_info->output, buf[i]);
1574                         child_info->new_out = TRUE;
1575                 }
1576         }
1577 }