4 * ROX-Filer, filer for the ROX desktop project
5 * By Thomas Leonard, <tal197@ecs.soton.ac.uk>.
8 /* action.c - code for handling the filer action windows.
9 * These routines generally fork() and talk to us via pipes.
16 #include <sys/types.h>
23 #include "gui_support.h"
27 typedef struct _GUIside GUIside
;
28 typedef void ActionChild(FilerWindow
*filer_window
);
29 typedef void ForDirCB(char *path
);
33 int from_child
; /* File descriptor */
35 int input_tag
; /* gdk_input_add() */
36 GtkWidget
*log
, *window
, *actions
;
37 int child
; /* Process ID */
40 /* These don't need to be in a structure because we fork() before
43 static int from_parent
= 0;
44 static FILE *to_parent
= NULL
;
45 static gboolean quiet
= FALSE
;
46 static GString
*message
= NULL
;
48 /* Static prototypes */
49 static gboolean
send();
50 static gboolean
send_error();
52 static void for_dir_contents(char *dir
, ForDirCB
*cb
)
56 GSList
*list
= NULL
, *next
;
65 while ((ent
= readdir(d
)))
67 if (ent
->d_name
[0] == '.' && (ent
->d_name
[1] == '\0'
68 || (ent
->d_name
[1] == '.' && ent
->d_name
[2] == '\0')))
70 list
= g_slist_append(list
, g_strdup(make_path(dir
,
82 cb((char *) next
->data
);
83 g_string_sprintf(message
, "+%s", dir
);
93 /* Read this many bytes into the buffer. TRUE on success. */
94 static gboolean
read_exact(int source
, char *buffer
, ssize_t len
)
99 got
= read(source
, buffer
, len
);
108 /* Send 'message' to our parent process. TRUE on success. */
109 static gboolean
send()
114 g_return_val_if_fail(message
->len
< 0xffff, FALSE
);
116 sprintf(len_buffer
, "%04x", message
->len
);
117 fwrite(len_buffer
, 1, 4, to_parent
);
118 len
= fwrite(message
->str
, 1, message
->len
, to_parent
);
120 return len
== message
->len
;
123 static gboolean
send_error()
125 g_string_sprintf(message
, "!ERROR: %s\n", g_strerror(errno
));
129 static void button_reply(GtkWidget
*button
, GUIside
*gui_side
)
133 text
= gtk_object_get_data(GTK_OBJECT(button
), "send-code");
134 g_return_if_fail(text
!= NULL
);
135 fputc(*text
, gui_side
->to_child
);
136 fflush(gui_side
->to_child
);
138 gtk_widget_set_sensitive(gui_side
->actions
, FALSE
);
141 /* Get one char from fd. Quit on error. */
142 static char reply(int fd
)
147 len
= read(fd
, &retval
, 1);
151 fprintf(stderr
, "read() error: %s\n", g_strerror(errno
));
152 _exit(1); /* Parent died? */
156 static void destroy_action_window(GtkWidget
*widget
, gpointer data
)
158 GUIside
*gui_side
= (GUIside
*) data
;
160 fclose(gui_side
->to_child
);
161 close(gui_side
->from_child
);
162 gdk_input_remove(gui_side
->input_tag
);
165 kill(gui_side
->child
, SIGTERM
);
169 if (--number_of_windows
< 1)
173 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
174 * (NULL on failure). The child calls func().
176 static GUIside
*start_action(FilerWindow
*filer_window
, ActionChild
*func
)
178 int filedes
[4]; /* 0 and 2 are for reading */
181 GtkWidget
*vbox
, *button
, *control
;
185 report_error("ROX-Filer", g_strerror(errno
));
189 if (pipe(filedes
+ 2))
193 report_error("ROX-Filer", g_strerror(errno
));
201 report_error("ROX-Filer", g_strerror(errno
));
204 /* We are the child */
205 message
= g_string_new(NULL
);
208 to_parent
= fdopen(filedes
[1], "wb");
209 from_parent
= filedes
[2];
214 /* We are the parent */
217 gui_side
= g_malloc(sizeof(GUIside
));
218 gui_side
->from_child
= filedes
[0];
219 gui_side
->to_child
= fdopen(filedes
[3], "wb");
220 gui_side
->log
= NULL
;
221 gui_side
->child
= child
;
223 gui_side
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
224 gtk_window_set_default_size(GTK_WINDOW(gui_side
->window
), 400, 100);
225 gtk_signal_connect(GTK_OBJECT(gui_side
->window
), "destroy",
226 GTK_SIGNAL_FUNC(destroy_action_window
), gui_side
);
228 vbox
= gtk_vbox_new(FALSE
, 4);
229 gtk_container_add(GTK_CONTAINER(gui_side
->window
), vbox
);
231 gui_side
->log
= gtk_text_new(NULL
, NULL
);
232 gtk_box_pack_start(GTK_BOX(vbox
), gui_side
->log
, TRUE
, TRUE
, 0);
234 gui_side
->actions
= gtk_hbox_new(TRUE
, 4);
235 gtk_box_pack_start(GTK_BOX(vbox
), gui_side
->actions
, FALSE
, TRUE
, 0);
237 control
= gtk_hbox_new(TRUE
, 4);
238 gtk_box_pack_start(GTK_BOX(vbox
), control
, FALSE
, TRUE
, 0);
240 button
= gtk_button_new_with_label("Suspend");
241 gtk_box_pack_start(GTK_BOX(control
), button
, TRUE
, TRUE
, 0);
243 button
= gtk_button_new_with_label("Resume");
244 gtk_widget_set_sensitive(button
, FALSE
);
245 gtk_box_pack_start(GTK_BOX(control
), button
, TRUE
, TRUE
, 0);
247 button
= gtk_button_new_with_label("Abort");
248 gtk_box_pack_start(GTK_BOX(control
), button
, TRUE
, TRUE
, 0);
249 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
250 gtk_widget_destroy
, GTK_OBJECT(gui_side
->window
));
255 /* And now, the individual actions... */
257 /* Delete one item - may need to recurse, or ask questions */
258 static void do_delete(char *path
)
264 if (lstat(path
, &info
))
270 write_prot
= access(path
, W_OK
) != 0;
271 if (quiet
== 0 || write_prot
)
273 g_string_sprintf(message
, "?Delete %s'%s'?\n",
274 write_prot
? "WRITE-PROTECTED " : " ",
277 rep
= reply(from_parent
);
283 if (S_ISDIR(info
.st_mode
))
286 safe_path
= g_strdup(path
);
287 g_string_sprintf(message
, "'Scanning in '%s'\n", safe_path
);
289 for_dir_contents(safe_path
, do_delete
);
290 if (rmdir(safe_path
))
296 g_string_assign(message
, "'Directory deleted\n");
300 else if (unlink(path
) == 0)
302 g_string_sprintf(message
, "'Deleted '%s'\n", path
);
309 /* The child executes this... */
310 static void delete_cb(FilerWindow
*filer_window
)
312 Collection
*collection
= filer_window
->collection
;
314 int left
= collection
->number_selected
;
320 if (!collection
->items
[i
].selected
)
322 item
= (FileItem
*) collection
->items
[i
].data
;
323 do_delete(make_path(filer_window
->path
, item
->leafname
)->str
);
324 g_string_sprintf(message
, "+%s", filer_window
->path
);
329 g_string_sprintf(message
, "'\nDone\n");
331 g_string_sprintf(message
, "+%s", filer_window
->path
);
336 /* Called when the child sends us a message */
337 static void got_delete_data(gpointer data
,
339 GdkInputCondition condition
)
342 GUIside
*gui_side
= (GUIside
*) data
;
343 GtkWidget
*log
= gui_side
->log
;
345 if (read_exact(source
, buf
, 4))
351 message_len
= strtol(buf
, NULL
, 16);
352 buffer
= g_malloc(message_len
+ 1);
353 if (message_len
> 0 && read_exact(source
, buffer
, message_len
))
355 buffer
[message_len
] = '\0';
357 gtk_widget_set_sensitive(gui_side
->actions
,
359 else if (*buffer
== '+')
361 refresh_dirs(buffer
+ 1);
365 gtk_text_insert(GTK_TEXT(log
),
368 buffer
+ 1, message_len
- 1);
372 g_print("Child died in the middle of a message.\n");
375 /* The child is dead */
377 gtk_widget_destroy(gui_side
->window
);
380 /* EXTERNAL INTERFACE */
382 /* Deletes all selected items in the window */
383 void action_delete(FilerWindow
*filer_window
)
386 Collection
*collection
;
389 collection
= window_with_focus
->collection
;
391 if (collection
->number_selected
< 1)
393 report_error("ROX-Filer", "You need to select some items "
398 gui_side
= start_action(filer_window
, delete_cb
);
402 gui_side
->input_tag
= gdk_input_add(gui_side
->from_child
,
404 got_delete_data
, gui_side
);
408 button
= gtk_button_new_with_label("Always");
409 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "A");
410 gtk_box_pack_start(GTK_BOX(gui_side
->actions
), button
, TRUE
, TRUE
, 0);
411 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
412 button_reply
, gui_side
);
413 button
= gtk_button_new_with_label("Yes");
414 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "Y");
415 gtk_box_pack_start(GTK_BOX(gui_side
->actions
), button
, TRUE
, TRUE
, 0);
416 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
417 button_reply
, gui_side
);
418 button
= gtk_button_new_with_label("No");
419 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "N");
420 gtk_box_pack_start(GTK_BOX(gui_side
->actions
), button
, TRUE
, TRUE
, 0);
421 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
422 button_reply
, gui_side
);
423 gtk_widget_set_sensitive(gui_side
->actions
, FALSE
);
425 gtk_widget_show_all(gui_side
->window
);
428 void action_copy(GSList
*paths
, char *dest
)
430 delayed_error("Copy", "Not implemented yet - sorry!");
433 void action_move(GSList
*paths
, char *dest
)
435 delayed_error("Move", "Not implemented yet - sorry!");
438 void action_link(GSList
*paths
, char *dest
)
440 delayed_error("Link", "Not implemented yet - sorry!");