4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 1999, Thomas Leonard, <tal197@ecs.soton.ac.uk>.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* action.c - code for handling the filer action windows.
23 * These routines generally fork() and talk to us via pipes.
31 #include <sys/param.h>
34 #include <sys/types.h>
43 #include "gui_support.h"
49 static GtkWidget
*create_options();
50 static void update_options();
51 static void set_options();
52 static void save_options();
53 static char *action_auto_quiet(char *data
);
55 static OptionsSection options
=
57 "Action window options",
64 static gboolean o_auto_copy
= TRUE
;
65 static gboolean o_auto_move
= TRUE
;
66 static gboolean o_auto_link
= TRUE
;
67 static gboolean o_auto_delete
= FALSE
;
68 static gboolean o_auto_mount
= TRUE
;
70 static GtkWidget
*w_auto_copy
= NULL
;
71 static GtkWidget
*w_auto_move
= NULL
;
72 static GtkWidget
*w_auto_link
= NULL
;
73 static GtkWidget
*w_auto_delete
= NULL
;
74 static GtkWidget
*w_auto_mount
= NULL
;
76 static GdkColor red
= {0, 0xffff, 0, 0};
78 /* Parent->Child messages are one character each:
80 * Q/Y/N Quiet/Yes/No button clicked
81 * F Force deletion of non-writeable items
84 #define SENSITIVE_YESNO(gui_side, state) \
86 gtk_widget_set_sensitive((gui_side)->yes, state); \
87 gtk_widget_set_sensitive((gui_side)->no, state); \
90 #define ON(flag) ((flag) ? "on" : "off")
92 typedef struct _GUIside GUIside
;
93 typedef void ActionChild(gpointer data
);
94 typedef gboolean
ForDirCB(char *path
, char *dest_path
);
98 int from_child
; /* File descriptor */
100 int input_tag
; /* gdk_input_add() */
101 GtkWidget
*vbox
, *log
, *window
, *dir
;
102 GtkWidget
*quiet
, *yes
, *no
;
103 int child
; /* Process ID */
105 gboolean show_info
; /* For Disk Usage */
107 char *next_dir
; /* NULL => no timer active */
111 /* These don't need to be in a structure because we fork() before
114 static int from_parent
= 0;
115 static FILE *to_parent
= NULL
;
116 static gboolean quiet
= FALSE
;
117 static GString
*message
= NULL
;
118 static char *action_dest
= NULL
;
119 static gboolean (*action_do_func
)(char *source
, char *dest
);
120 static size_t size_tally
; /* For Disk Usage */
121 static DirItem
*mount_item
;
123 static gboolean o_force
= FALSE
;
125 /* Static prototypes */
126 static gboolean
send();
127 static gboolean
send_error();
128 static gboolean
send_dir(char *dir
);
129 static gboolean
read_exact(int source
, char *buffer
, ssize_t len
);
130 static void do_mount(FilerWindow
*filer_window
, DirItem
*item
);
131 static void add_toggle(GUIside
*gui_side
, guchar
*label
, guchar
*code
);
132 static gboolean
reply(int fd
, gboolean ignore_quiet
);
136 /* Build up some option widgets to go in the options dialog, but don't
139 static GtkWidget
*create_options()
141 GtkWidget
*vbox
, *hbox
, *label
;
143 vbox
= gtk_vbox_new(FALSE
, 0);
144 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 4);
146 label
= gtk_label_new("Auto-start (Quiet) these actions:");
147 gtk_box_pack_start(GTK_BOX(vbox
), label
, FALSE
, TRUE
, 0);
149 hbox
= gtk_hbox_new(TRUE
, 0);
150 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, TRUE
, 0);
152 w_auto_copy
= gtk_check_button_new_with_label("Copy");
153 gtk_box_pack_start(GTK_BOX(hbox
), w_auto_copy
, FALSE
, TRUE
, 0);
154 w_auto_move
= gtk_check_button_new_with_label("Move");
155 gtk_box_pack_start(GTK_BOX(hbox
), w_auto_move
, FALSE
, TRUE
, 0);
156 w_auto_link
= gtk_check_button_new_with_label("Link");
157 gtk_box_pack_start(GTK_BOX(hbox
), w_auto_link
, FALSE
, TRUE
, 0);
158 w_auto_delete
= gtk_check_button_new_with_label("Delete");
159 gtk_box_pack_start(GTK_BOX(hbox
), w_auto_delete
, FALSE
, TRUE
, 0);
160 w_auto_mount
= gtk_check_button_new_with_label("Mount");
161 gtk_box_pack_start(GTK_BOX(hbox
), w_auto_mount
, FALSE
, TRUE
, 0);
166 /* Reflect current state by changing the widgets in the options box */
167 static void update_options()
169 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_copy
),
171 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_move
),
173 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_link
),
175 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_delete
),
177 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_mount
),
181 /* Set current values by reading the states of the widgets in the options box */
182 static void set_options()
184 o_auto_copy
= gtk_toggle_button_get_active(
185 GTK_TOGGLE_BUTTON(w_auto_copy
));
186 o_auto_move
= gtk_toggle_button_get_active(
187 GTK_TOGGLE_BUTTON(w_auto_move
));
188 o_auto_link
= gtk_toggle_button_get_active(
189 GTK_TOGGLE_BUTTON(w_auto_link
));
190 o_auto_delete
= gtk_toggle_button_get_active(
191 GTK_TOGGLE_BUTTON(w_auto_delete
));
192 o_auto_mount
= gtk_toggle_button_get_active(
193 GTK_TOGGLE_BUTTON(w_auto_mount
));
196 static void save_options()
198 guchar str
[] = "cmldt";
211 option_write("action_auto_quiet", str
);
214 static char *action_auto_quiet(char *data
)
235 o_auto_delete
= state
;
238 o_auto_mount
= state
;
241 return "Unknown flag";
250 /* TRUE iff `sub' is (or would be) an object inside the directory `parent',
251 * (or the two are the same directory)
253 static gboolean
is_sub_dir(char *sub
, char *parent
)
256 guchar
*real_sub
, *real_parent
;
259 real_sub
= pathdup(sub
);
260 real_parent
= pathdup(parent
);
262 parent_len
= strlen(real_parent
);
263 if (strncmp(real_parent
, real_sub
, parent_len
))
267 /* real_sub is at least as long as real_parent and all
268 * characters upto real_parent's length match.
271 retval
= real_sub
[parent_len
] == 0
272 || real_sub
[parent_len
] == '/';
281 static gboolean
display_dir(gpointer data
)
283 GUIside
*gui_side
= (GUIside
*) data
;
285 gtk_label_set_text(GTK_LABEL(gui_side
->dir
), gui_side
->next_dir
+ 1);
286 g_free(gui_side
->next_dir
);
287 gui_side
->next_dir
= NULL
;
292 /* Called when the child sends us a message */
293 static void message_from_child(gpointer data
,
295 GdkInputCondition condition
)
298 GUIside
*gui_side
= (GUIside
*) data
;
299 GtkWidget
*log
= gui_side
->log
;
301 if (read_exact(source
, buf
, 4))
307 message_len
= strtol(buf
, NULL
, 16);
308 buffer
= g_malloc(message_len
+ 1);
309 if (message_len
> 0 && read_exact(source
, buffer
, message_len
))
311 buffer
[message_len
] = '\0';
313 SENSITIVE_YESNO(gui_side
, TRUE
);
314 else if (*buffer
== '+')
316 refresh_dirs(buffer
+ 1);
320 else if (*buffer
== 'm')
322 filer_check_mounted(buffer
+ 1);
326 else if (*buffer
== '/')
328 if (gui_side
->next_dir
)
329 g_free(gui_side
->next_dir
);
331 gui_side
->next_timer
=
335 gui_side
->next_dir
= buffer
;
338 else if (*buffer
== '!')
341 gtk_text_insert(GTK_TEXT(log
),
343 *buffer
== '!' ? &red
: NULL
,
345 buffer
+ 1, message_len
- 1);
349 g_print("Child died in the middle of a message.\n");
352 /* The child is dead */
355 fclose(gui_side
->to_child
);
356 gui_side
->to_child
= NULL
;
357 close(gui_side
->from_child
);
358 gdk_input_remove(gui_side
->input_tag
);
359 gtk_widget_set_sensitive(gui_side
->quiet
, FALSE
);
361 if (gui_side
->errors
)
364 report
= g_string_new(NULL
);
365 g_string_sprintf(report
, "There %s %d error%s.\n",
366 gui_side
->errors
== 1 ? "was" : "were",
368 gui_side
->errors
== 1 ? "" : "s");
369 gtk_text_insert(GTK_TEXT(log
), NULL
, &red
, NULL
,
370 report
->str
, report
->len
);
372 g_string_free(report
, TRUE
);
374 else if (gui_side
->show_info
== FALSE
)
375 gtk_widget_destroy(gui_side
->window
);
378 /* Scans src_dir, updating dest_path whenever cb returns TRUE */
379 static void for_dir_contents(ForDirCB
*cb
, char *src_dir
, char *dest_path
)
383 GSList
*list
= NULL
, *next
;
385 d
= opendir(src_dir
);
394 while ((ent
= readdir(d
)))
396 if (ent
->d_name
[0] == '.' && (ent
->d_name
[1] == '\0'
397 || (ent
->d_name
[1] == '.' && ent
->d_name
[2] == '\0')))
399 list
= g_slist_append(list
, g_strdup(make_path(src_dir
,
411 if (cb((char *) next
->data
, dest_path
))
413 g_string_sprintf(message
, "+%s", dest_path
);
424 /* Read this many bytes into the buffer. TRUE on success. */
425 static gboolean
read_exact(int source
, char *buffer
, ssize_t len
)
430 got
= read(source
, buffer
, len
);
439 /* Send 'message' to our parent process. TRUE on success. */
440 static gboolean
send()
445 g_return_val_if_fail(message
->len
< 0xffff, FALSE
);
447 sprintf(len_buffer
, "%04x", message
->len
);
448 fwrite(len_buffer
, 1, 4, to_parent
);
449 len
= fwrite(message
->str
, 1, message
->len
, to_parent
);
451 return len
== message
->len
;
454 /* Set the directory indicator at the top of the window */
455 static gboolean
send_dir(char *dir
)
457 g_string_sprintf(message
, "/%s", dir
);
461 static gboolean
send_error()
463 g_string_sprintf(message
, "!ERROR: %s\n", g_strerror(errno
));
467 static void button_reply(GtkWidget
*button
, GUIside
*gui_side
)
471 if (!gui_side
->to_child
)
474 text
= gtk_object_get_data(GTK_OBJECT(button
), "send-code");
475 g_return_if_fail(text
!= NULL
);
476 fputc(*text
, gui_side
->to_child
);
477 fflush(gui_side
->to_child
);
479 if (*text
== 'Y' || *text
== 'N' || *text
== 'Q')
480 SENSITIVE_YESNO(gui_side
, FALSE
);
483 static void process_flag(char flag
)
494 g_string_sprintf(message
,
495 "!ERROR: Bad message '%c'\n", flag
);
501 /* If the parent has sent any flag toggles, read them */
502 static void check_flags(void)
513 FD_SET(from_parent
, &set
);
516 got
= select(from_parent
+ 1, &set
, NULL
, NULL
, &tv
);
519 g_error("select() failed: %s\n", g_strerror(errno
));
523 got
= read(from_parent
, &retval
, 1);
525 g_error("read() error: %s\n", g_strerror(errno
));
527 process_flag(retval
);
531 /* Read until the user sends a reply. If ignore_quiet is TRUE then
532 * the user MUST click Yes or No, else treat quiet on as Yes.
533 * If the user needs prompting then does send().
535 static gboolean
reply(int fd
, gboolean ignore_quiet
)
539 gboolean asked
= FALSE
;
541 while (ignore_quiet
|| !quiet
)
549 len
= read(fd
, &retval
, 1);
552 fprintf(stderr
, "read() error: %s\n",
554 _exit(1); /* Parent died? */
563 g_string_assign(message
, "?");
568 g_string_assign(message
, "' Yes\n");
572 g_string_assign(message
, "' No\n");
576 process_flag(retval
);
583 g_string_assign(message
, "' Quiet\n");
589 static void destroy_action_window(GtkWidget
*widget
, gpointer data
)
591 GUIside
*gui_side
= (GUIside
*) data
;
595 kill(gui_side
->child
, SIGTERM
);
596 fclose(gui_side
->to_child
);
597 close(gui_side
->from_child
);
598 gdk_input_remove(gui_side
->input_tag
);
601 if (gui_side
->next_dir
)
603 gtk_timeout_remove(gui_side
->next_timer
);
604 g_free(gui_side
->next_dir
);
608 if (--number_of_windows
< 1)
612 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
613 * (NULL on failure). The child calls func().
615 * If autoq then automatically selects 'Quiet'.
617 static GUIside
*start_action(gpointer data
, ActionChild
*func
, gboolean autoq
)
619 int filedes
[4]; /* 0 and 2 are for reading */
622 GtkWidget
*vbox
, *button
, *hbox
, *scrollbar
, *actions
;
623 struct sigaction act
;
627 report_error("ROX-Filer", g_strerror(errno
));
631 if (pipe(filedes
+ 2))
635 report_error("ROX-Filer", g_strerror(errno
));
643 report_error("ROX-Filer", g_strerror(errno
));
646 /* We are the child */
650 /* Reset the SIGCHLD handler */
651 act
.sa_handler
= SIG_DFL
;
652 sigemptyset(&act
.sa_mask
);
654 sigaction(SIGCHLD
, &act
, NULL
);
656 message
= g_string_new(NULL
);
659 to_parent
= fdopen(filedes
[1], "wb");
660 from_parent
= filedes
[2];
665 /* We are the parent */
668 gui_side
= g_malloc(sizeof(GUIside
));
669 gui_side
->from_child
= filedes
[0];
670 gui_side
->to_child
= fdopen(filedes
[3], "wb");
671 gui_side
->log
= NULL
;
672 gui_side
->child
= child
;
673 gui_side
->errors
= 0;
674 gui_side
->show_info
= FALSE
;
676 gui_side
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
677 gtk_container_set_border_width(GTK_CONTAINER(gui_side
->window
), 2);
678 gtk_window_set_default_size(GTK_WINDOW(gui_side
->window
), 450, 200);
679 gtk_signal_connect(GTK_OBJECT(gui_side
->window
), "destroy",
680 GTK_SIGNAL_FUNC(destroy_action_window
), gui_side
);
682 gui_side
->vbox
= vbox
= gtk_vbox_new(FALSE
, 4);
683 gtk_container_add(GTK_CONTAINER(gui_side
->window
), vbox
);
685 gui_side
->dir
= gtk_label_new("<dir>");
686 gui_side
->next_dir
= NULL
;
687 gtk_misc_set_alignment(GTK_MISC(gui_side
->dir
), 0.5, 0.5);
688 gtk_box_pack_start(GTK_BOX(vbox
), gui_side
->dir
, FALSE
, TRUE
, 0);
690 hbox
= gtk_hbox_new(FALSE
, 0);
691 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, TRUE
, TRUE
, 0);
693 gui_side
->log
= gtk_text_new(NULL
, NULL
);
694 gtk_widget_set_usize(gui_side
->log
, 400, 100);
695 gtk_box_pack_start(GTK_BOX(hbox
), gui_side
->log
, TRUE
, TRUE
, 0);
696 scrollbar
= gtk_vscrollbar_new(GTK_TEXT(gui_side
->log
)->vadj
);
697 gtk_box_pack_start(GTK_BOX(hbox
), scrollbar
, FALSE
, TRUE
, 0);
699 actions
= gtk_hbox_new(TRUE
, 4);
700 gtk_box_pack_start(GTK_BOX(vbox
), actions
, FALSE
, TRUE
, 0);
702 gui_side
->quiet
= button
= gtk_toggle_button_new_with_label("Quiet");
703 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button
), autoq
);
704 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "Q");
705 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
706 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
707 button_reply
, gui_side
);
708 gui_side
->yes
= button
= gtk_button_new_with_label("Yes");
709 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "Y");
710 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
711 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
712 button_reply
, gui_side
);
713 gui_side
->no
= button
= gtk_button_new_with_label("No");
714 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "N");
715 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
716 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
717 button_reply
, gui_side
);
718 SENSITIVE_YESNO(gui_side
, FALSE
);
720 button
= gtk_button_new_with_label("Abort");
721 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
722 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
723 gtk_widget_destroy
, GTK_OBJECT(gui_side
->window
));
725 gui_side
->input_tag
= gdk_input_add(gui_side
->from_child
,
733 /* ACTIONS ON ONE ITEM */
735 /* These may call themselves recursively, or ask questions, etc.
736 * TRUE iff the directory containing dest_path needs to be rescanned.
739 /* dest_path is the dir containing src_path.
740 * Updates the global size_tally.
742 static gboolean
do_usage(char *src_path
, char *dest_path
)
748 if (lstat(src_path
, &info
))
750 g_string_sprintf(message
, "'%s:\n", src_path
);
756 if (S_ISREG(info
.st_mode
) || S_ISLNK(info
.st_mode
))
757 size_tally
+= info
.st_size
;
758 else if (S_ISDIR(info
.st_mode
))
760 g_string_sprintf(message
, "?Count contents of %s?",
762 if (reply(from_parent
, FALSE
))
765 safe_path
= g_strdup(src_path
);
766 for_dir_contents(do_usage
, safe_path
, safe_path
);
774 /* dest_path is the dir containing src_path */
775 static gboolean
do_delete(char *src_path
, char *dest_path
)
782 if (lstat(src_path
, &info
))
788 write_prot
= S_ISLNK(info
.st_mode
) ? FALSE
789 : access(src_path
, W_OK
) != 0;
790 if (write_prot
|| !quiet
)
792 g_string_sprintf(message
, "?Delete %s'%s'?",
793 write_prot
? "WRITE-PROTECTED " : " ",
795 if (!reply(from_parent
, write_prot
&& !o_force
))
800 g_string_sprintf(message
, "'Removing '%s'\n", src_path
);
804 if (S_ISDIR(info
.st_mode
))
807 safe_path
= g_strdup(src_path
);
808 for_dir_contents(do_delete
, safe_path
, safe_path
);
809 if (rmdir(safe_path
))
815 g_string_assign(message
, "'Directory deleted\n");
819 else if (unlink(src_path
))
828 static gboolean
do_copy2(char *path
, char *dest
)
833 struct stat dest_info
;
834 gboolean retval
= TRUE
;
838 leaf
= strrchr(path
, '/');
840 leaf
= path
; /* Error? */
844 dest_path
= make_path(dest
, leaf
)->str
;
846 if (lstat(path
, &info
))
852 if (lstat(dest_path
, &dest_info
) == 0)
857 merge
= S_ISDIR(info
.st_mode
) && S_ISDIR(dest_info
.st_mode
);
859 g_string_sprintf(message
, "?'%s' already exists - %s?",
861 merge
? "merge contents" : "overwrite");
863 if (!reply(from_parent
, TRUE
))
868 if (S_ISDIR(dest_info
.st_mode
))
869 err
= rmdir(dest_path
);
871 err
= unlink(dest_path
);
878 g_string_sprintf(message
,
879 "'Trying copy anyway...\n");
886 g_string_sprintf(message
, "?Copy %s as %s?", path
, dest_path
);
887 if (!reply(from_parent
, FALSE
))
892 g_string_sprintf(message
, "'Copying %s as %s\n", path
,
897 if (S_ISDIR(info
.st_mode
))
899 char *safe_path
, *safe_dest
;
900 struct stat dest_info
;
903 /* (we will do the update ourselves now, rather than
908 safe_path
= g_strdup(path
);
909 safe_dest
= g_strdup(dest_path
);
911 exists
= !lstat(dest_path
, &dest_info
);
913 if (exists
&& !S_ISDIR(dest_info
.st_mode
))
915 g_string_sprintf(message
,
916 "!ERROR: Destination already exists, "
917 "but is not a directory\n");
919 else if (exists
== FALSE
&& mkdir(dest_path
, info
.st_mode
))
925 /* (just been created then) */
926 g_string_sprintf(message
, "+%s", dest
);
930 for_dir_contents(do_copy2
, safe_path
, safe_dest
);
931 /* Note: dest_path now invalid... */
937 else if (S_ISLNK(info
.st_mode
))
939 char target
[MAXPATHLEN
+ 1];
942 /* Not all versions of cp(1) can make symlinks,
943 * so we special-case it.
946 count
= readlink(path
, target
, sizeof(target
) - 1);
954 target
[count
] = '\0';
955 if (symlink(target
, dest_path
))
964 char *argv
[] = {"cp", "-pRf", NULL
, NULL
, NULL
};
969 if (fork_exec_wait(argv
))
971 g_string_sprintf(message
, "!ERROR: Copy failed\n");
980 /* Copy path to dest.
981 * Check that path not copied into itself.
983 static gboolean
do_copy(char *path
, char *dest
)
985 if (is_sub_dir(dest
, path
))
987 g_string_sprintf(message
,
988 "!ERROR: Can't copy directory into itself\n");
992 return do_copy2(path
, dest
);
995 /* Move path to dest.
996 * Check that path not moved into itself.
998 static gboolean
do_move(char *path
, char *dest
)
1002 gboolean retval
= TRUE
;
1003 char *argv
[] = {"mv", "-f", NULL
, NULL
, NULL
};
1005 if (is_sub_dir(dest
, path
))
1007 g_string_sprintf(message
,
1008 "!ERROR: Can't move directory into itself\n");
1015 leaf
= strrchr(path
, '/');
1017 leaf
= path
; /* Error? */
1021 dest_path
= make_path(dest
, leaf
)->str
;
1023 if (access(dest_path
, F_OK
) == 0)
1028 g_string_sprintf(message
, "?'%s' already exists - overwrite?",
1030 if (!reply(from_parent
, TRUE
))
1033 if (lstat(dest_path
, &info
))
1039 if (S_ISDIR(info
.st_mode
))
1040 err
= rmdir(dest_path
);
1042 err
= unlink(dest_path
);
1047 if (errno
!= ENOENT
)
1049 g_string_sprintf(message
,
1050 "'Trying move anyway...\n");
1056 g_string_sprintf(message
, "?Move %s as %s?", path
, dest_path
);
1057 if (!reply(from_parent
, FALSE
))
1062 g_string_sprintf(message
, "'Moving %s as %s\n", path
,
1070 if (fork_exec_wait(argv
) == 0)
1072 g_string_sprintf(message
, "+%s", path
);
1073 g_string_truncate(message
, leaf
- path
);
1078 g_string_sprintf(message
, "!ERROR: Failed to move %s as %s\n",
1087 static gboolean
do_link(char *path
, char *dest
)
1094 leaf
= strrchr(path
, '/');
1096 leaf
= path
; /* Error? */
1100 dest_path
= make_path(dest
, leaf
)->str
;
1104 g_string_sprintf(message
, "'Linking %s as %s\n", path
,
1110 g_string_sprintf(message
, "?Link %s as %s?", path
, dest_path
);
1111 if (!reply(from_parent
, FALSE
))
1115 if (symlink(path
, dest_path
))
1124 /* Mount/umount this item */
1125 static void do_mount(FilerWindow
*filer_window
, DirItem
*item
)
1127 char *argv
[3] = {NULL
, NULL
, NULL
};
1131 argv
[0] = item
->flags
& ITEM_FLAG_MOUNTED
? "umount" : "mount";
1132 argv
[1] = make_path(filer_window
->path
, item
->leafname
)->str
;
1136 g_string_sprintf(message
, "'%sing %s\n", argv
[0], argv
[1]);
1141 g_string_sprintf(message
, "?%s %s?", argv
[0], argv
[1]);
1142 if (!reply(from_parent
, FALSE
))
1146 if (fork_exec_wait(argv
) == 0)
1148 g_string_sprintf(message
, "+%s", filer_window
->path
);
1150 g_string_sprintf(message
, "m%s", argv
[1]);
1155 g_string_sprintf(message
, "!ERROR: %s failed\n", argv
[0]);
1160 /* CHILD MAIN LOOPS */
1162 /* After forking, the child calls one of these functions */
1164 static void usage_cb(gpointer data
)
1166 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1167 Collection
*collection
= filer_window
->collection
;
1169 int left
= collection
->number_selected
;
1171 off_t total_size
= 0;
1173 send_dir(filer_window
->path
);
1178 if (!collection
->items
[i
].selected
)
1180 item
= (DirItem
*) collection
->items
[i
].data
;
1182 do_usage(make_path(filer_window
->path
,
1183 item
->leafname
)->str
,
1184 filer_window
->path
);
1185 g_string_sprintf(message
, "'%s: %s\n",
1187 format_size((unsigned long) size_tally
));
1189 total_size
+= size_tally
;
1193 g_string_sprintf(message
, "'\nTotal: %s\n",
1194 format_size((unsigned long) total_size
));
1198 #ifdef DO_MOUNT_POINTS
1199 static void mount_cb(gpointer data
)
1201 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1202 Collection
*collection
= filer_window
->collection
;
1205 gboolean mount_points
= FALSE
;
1207 send_dir(filer_window
->path
);
1210 do_mount(filer_window
, mount_item
);
1213 for (i
= 0; i
< collection
->number_of_items
; i
++)
1215 if (!collection
->items
[i
].selected
)
1217 item
= (DirItem
*) collection
->items
[i
].data
;
1218 if (!(item
->flags
& ITEM_FLAG_MOUNT_POINT
))
1220 mount_points
= TRUE
;
1222 do_mount(filer_window
, item
);
1227 g_string_sprintf(message
,
1228 "!No mount points selected!\n");
1233 g_string_sprintf(message
, "'\nDone\n");
1238 static void delete_cb(gpointer data
)
1240 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1241 Collection
*collection
= filer_window
->collection
;
1243 int left
= collection
->number_selected
;
1246 send_dir(filer_window
->path
);
1251 if (!collection
->items
[i
].selected
)
1253 item
= (DirItem
*) collection
->items
[i
].data
;
1254 if (do_delete(make_path(filer_window
->path
,
1255 item
->leafname
)->str
,
1256 filer_window
->path
))
1258 g_string_sprintf(message
, "+%s", filer_window
->path
);
1264 g_string_sprintf(message
, "'\nDone\n");
1268 static void list_cb(gpointer data
)
1270 GSList
*paths
= (GSList
*) data
;
1274 send_dir((char *) paths
->data
);
1276 if (action_do_func((char *) paths
->data
, action_dest
))
1278 g_string_sprintf(message
, "+%s", action_dest
);
1282 paths
= paths
->next
;
1285 g_string_sprintf(message
, "'\nDone\n");
1289 static void add_toggle(GUIside
*gui_side
, guchar
*label
, guchar
*code
)
1293 check
= gtk_check_button_new_with_label(label
);
1294 gtk_object_set_data(GTK_OBJECT(check
), "send-code", code
);
1295 gtk_signal_connect(GTK_OBJECT(check
), "clicked",
1296 button_reply
, gui_side
);
1297 gtk_box_pack_start(GTK_BOX(gui_side
->vbox
), check
, FALSE
, TRUE
, 0);
1301 /* EXTERNAL INTERFACE */
1303 /* Count disk space used by selected items */
1304 void action_usage(FilerWindow
*filer_window
)
1307 Collection
*collection
;
1309 collection
= filer_window
->collection
;
1311 if (collection
->number_selected
< 1)
1313 report_error("ROX-Filer", "You need to select some items "
1318 gui_side
= start_action(filer_window
, usage_cb
, TRUE
);
1322 gui_side
->show_info
= TRUE
;
1324 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Disk Usage");
1325 number_of_windows
++;
1326 gtk_widget_show_all(gui_side
->window
);
1329 /* Mount/unmount 'item', or all selected mount points if NULL. */
1330 void action_mount(FilerWindow
*filer_window
, DirItem
*item
)
1332 #ifdef DO_MOUNT_POINTS
1334 Collection
*collection
;
1336 collection
= filer_window
->collection
;
1338 if (item
== NULL
&& collection
->number_selected
< 1)
1340 report_error("ROX-Filer", "You need to select some items "
1341 "to mount or unmount");
1346 gui_side
= start_action(filer_window
, mount_cb
, o_auto_mount
);
1350 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Mount / Unmount");
1351 number_of_windows
++;
1352 gtk_widget_show_all(gui_side
->window
);
1354 report_error("ROX-Filer",
1355 "ROX-Filer does not yet support mount points on your "
1357 #endif /* DO_MOUNT_POINTS */
1360 /* Deletes all selected items in the window */
1361 void action_delete(FilerWindow
*filer_window
)
1364 Collection
*collection
;
1366 collection
= filer_window
->collection
;
1368 if (collection
->number_selected
< 1)
1370 report_error("ROX-Filer", "You need to select some items "
1375 gui_side
= start_action(filer_window
, delete_cb
, o_auto_delete
);
1379 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Delete");
1380 add_toggle(gui_side
,
1381 "Don't confirm deletion of non-writeable items", "F");
1383 number_of_windows
++;
1384 gtk_widget_show_all(gui_side
->window
);
1387 void action_copy(GSList
*paths
, char *dest
)
1392 action_do_func
= do_copy
;
1393 gui_side
= start_action(paths
, list_cb
, o_auto_copy
);
1397 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Copy");
1398 number_of_windows
++;
1399 gtk_widget_show_all(gui_side
->window
);
1402 void action_move(GSList
*paths
, char *dest
)
1407 action_do_func
= do_move
;
1408 gui_side
= start_action(paths
, list_cb
, o_auto_move
);
1412 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Move");
1413 number_of_windows
++;
1414 gtk_widget_show_all(gui_side
->window
);
1417 void action_link(GSList
*paths
, char *dest
)
1422 action_do_func
= do_link
;
1423 gui_side
= start_action(paths
, list_cb
, o_auto_link
);
1427 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Link");
1428 number_of_windows
++;
1429 gtk_widget_show_all(gui_side
->window
);
1432 void action_init(void)
1434 options_sections
= g_slist_prepend(options_sections
, &options
);
1435 option_register("action_auto_quiet", action_auto_quiet
);