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 static gboolean
is_sub_dir(char *sub
, char *parent
)
255 parent_len
= strlen(parent
);
256 if (strncmp(parent
, sub
, parent_len
))
259 /* sub is at least as long as parent and all characters upto
260 * parent's length match.
263 return sub
[parent_len
] == 0 || sub
[parent_len
] == '/';
266 static gboolean
display_dir(gpointer data
)
268 GUIside
*gui_side
= (GUIside
*) data
;
270 gtk_label_set_text(GTK_LABEL(gui_side
->dir
), gui_side
->next_dir
+ 1);
271 g_free(gui_side
->next_dir
);
272 gui_side
->next_dir
= NULL
;
277 /* Called when the child sends us a message */
278 static void message_from_child(gpointer data
,
280 GdkInputCondition condition
)
283 GUIside
*gui_side
= (GUIside
*) data
;
284 GtkWidget
*log
= gui_side
->log
;
286 if (read_exact(source
, buf
, 4))
292 message_len
= strtol(buf
, NULL
, 16);
293 buffer
= g_malloc(message_len
+ 1);
294 if (message_len
> 0 && read_exact(source
, buffer
, message_len
))
296 buffer
[message_len
] = '\0';
298 SENSITIVE_YESNO(gui_side
, TRUE
);
299 else if (*buffer
== '+')
301 refresh_dirs(buffer
+ 1);
305 else if (*buffer
== 'm')
307 filer_check_mounted(buffer
+ 1);
311 else if (*buffer
== '/')
313 if (gui_side
->next_dir
)
314 g_free(gui_side
->next_dir
);
316 gui_side
->next_timer
=
320 gui_side
->next_dir
= buffer
;
323 else if (*buffer
== '!')
326 gtk_text_insert(GTK_TEXT(log
),
328 *buffer
== '!' ? &red
: NULL
,
330 buffer
+ 1, message_len
- 1);
334 g_print("Child died in the middle of a message.\n");
337 /* The child is dead */
340 fclose(gui_side
->to_child
);
341 gui_side
->to_child
= NULL
;
342 close(gui_side
->from_child
);
343 gdk_input_remove(gui_side
->input_tag
);
344 gtk_widget_set_sensitive(gui_side
->quiet
, FALSE
);
346 if (gui_side
->errors
)
349 report
= g_string_new(NULL
);
350 g_string_sprintf(report
, "There %s %d error%s.\n",
351 gui_side
->errors
== 1 ? "was" : "were",
353 gui_side
->errors
== 1 ? "" : "s");
354 gtk_text_insert(GTK_TEXT(log
), NULL
, &red
, NULL
,
355 report
->str
, report
->len
);
357 g_string_free(report
, TRUE
);
359 else if (gui_side
->show_info
== FALSE
)
360 gtk_widget_destroy(gui_side
->window
);
363 /* Scans src_dir, updating dest_path whenever cb returns TRUE */
364 static void for_dir_contents(ForDirCB
*cb
, char *src_dir
, char *dest_path
)
368 GSList
*list
= NULL
, *next
;
370 d
= opendir(src_dir
);
379 while ((ent
= readdir(d
)))
381 if (ent
->d_name
[0] == '.' && (ent
->d_name
[1] == '\0'
382 || (ent
->d_name
[1] == '.' && ent
->d_name
[2] == '\0')))
384 list
= g_slist_append(list
, g_strdup(make_path(src_dir
,
396 if (cb((char *) next
->data
, dest_path
))
398 g_string_sprintf(message
, "+%s", dest_path
);
409 /* Read this many bytes into the buffer. TRUE on success. */
410 static gboolean
read_exact(int source
, char *buffer
, ssize_t len
)
415 got
= read(source
, buffer
, len
);
424 /* Send 'message' to our parent process. TRUE on success. */
425 static gboolean
send()
430 g_return_val_if_fail(message
->len
< 0xffff, FALSE
);
432 sprintf(len_buffer
, "%04x", message
->len
);
433 fwrite(len_buffer
, 1, 4, to_parent
);
434 len
= fwrite(message
->str
, 1, message
->len
, to_parent
);
436 return len
== message
->len
;
439 /* Set the directory indicator at the top of the window */
440 static gboolean
send_dir(char *dir
)
442 g_string_sprintf(message
, "/%s", dir
);
446 static gboolean
send_error()
448 g_string_sprintf(message
, "!ERROR: %s\n", g_strerror(errno
));
452 static void button_reply(GtkWidget
*button
, GUIside
*gui_side
)
456 if (!gui_side
->to_child
)
459 text
= gtk_object_get_data(GTK_OBJECT(button
), "send-code");
460 g_return_if_fail(text
!= NULL
);
461 fputc(*text
, gui_side
->to_child
);
462 fflush(gui_side
->to_child
);
464 if (*text
== 'Y' || *text
== 'N' || *text
== 'Q')
465 SENSITIVE_YESNO(gui_side
, FALSE
);
468 static void process_flag(char flag
)
479 g_string_sprintf(message
,
480 "!ERROR: Bad message '%c'\n", flag
);
486 /* If the parent has sent any flag toggles, read them */
487 static void check_flags(void)
498 FD_SET(from_parent
, &set
);
501 got
= select(from_parent
+ 1, &set
, NULL
, NULL
, &tv
);
504 g_error("select() failed: %s\n", g_strerror(errno
));
508 got
= read(from_parent
, &retval
, 1);
510 g_error("read() error: %s\n", g_strerror(errno
));
512 process_flag(retval
);
516 /* Read until the user sends a reply. If ignore_quiet is TRUE then
517 * the user MUST click Yes or No, else treat quiet on as Yes.
518 * If the user needs prompting then does send().
520 static gboolean
reply(int fd
, gboolean ignore_quiet
)
524 gboolean asked
= FALSE
;
526 while (ignore_quiet
|| !quiet
)
534 len
= read(fd
, &retval
, 1);
537 fprintf(stderr
, "read() error: %s\n",
539 _exit(1); /* Parent died? */
548 g_string_assign(message
, "?");
553 g_string_assign(message
, "' Yes\n");
557 g_string_assign(message
, "' No\n");
561 process_flag(retval
);
568 g_string_assign(message
, "' Quiet\n");
574 static void destroy_action_window(GtkWidget
*widget
, gpointer data
)
576 GUIside
*gui_side
= (GUIside
*) data
;
580 kill(gui_side
->child
, SIGTERM
);
581 fclose(gui_side
->to_child
);
582 close(gui_side
->from_child
);
583 gdk_input_remove(gui_side
->input_tag
);
586 if (gui_side
->next_dir
)
588 gtk_timeout_remove(gui_side
->next_timer
);
589 g_free(gui_side
->next_dir
);
593 if (--number_of_windows
< 1)
597 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
598 * (NULL on failure). The child calls func().
600 * If autoq then automatically selects 'Quiet'.
602 static GUIside
*start_action(gpointer data
, ActionChild
*func
, gboolean autoq
)
604 int filedes
[4]; /* 0 and 2 are for reading */
607 GtkWidget
*vbox
, *button
, *hbox
, *scrollbar
, *actions
;
608 struct sigaction act
;
612 report_error("ROX-Filer", g_strerror(errno
));
616 if (pipe(filedes
+ 2))
620 report_error("ROX-Filer", g_strerror(errno
));
628 report_error("ROX-Filer", g_strerror(errno
));
631 /* We are the child */
635 /* Reset the SIGCHLD handler */
636 act
.sa_handler
= SIG_DFL
;
637 sigemptyset(&act
.sa_mask
);
639 sigaction(SIGCHLD
, &act
, NULL
);
641 message
= g_string_new(NULL
);
644 to_parent
= fdopen(filedes
[1], "wb");
645 from_parent
= filedes
[2];
650 /* We are the parent */
653 gui_side
= g_malloc(sizeof(GUIside
));
654 gui_side
->from_child
= filedes
[0];
655 gui_side
->to_child
= fdopen(filedes
[3], "wb");
656 gui_side
->log
= NULL
;
657 gui_side
->child
= child
;
658 gui_side
->errors
= 0;
659 gui_side
->show_info
= FALSE
;
661 gui_side
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
662 gtk_container_set_border_width(GTK_CONTAINER(gui_side
->window
), 2);
663 gtk_window_set_default_size(GTK_WINDOW(gui_side
->window
), 450, 200);
664 gtk_signal_connect(GTK_OBJECT(gui_side
->window
), "destroy",
665 GTK_SIGNAL_FUNC(destroy_action_window
), gui_side
);
667 gui_side
->vbox
= vbox
= gtk_vbox_new(FALSE
, 4);
668 gtk_container_add(GTK_CONTAINER(gui_side
->window
), vbox
);
670 gui_side
->dir
= gtk_label_new("<dir>");
671 gui_side
->next_dir
= NULL
;
672 gtk_misc_set_alignment(GTK_MISC(gui_side
->dir
), 0.5, 0.5);
673 gtk_box_pack_start(GTK_BOX(vbox
), gui_side
->dir
, FALSE
, TRUE
, 0);
675 hbox
= gtk_hbox_new(FALSE
, 0);
676 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, TRUE
, TRUE
, 0);
678 gui_side
->log
= gtk_text_new(NULL
, NULL
);
679 gtk_widget_set_usize(gui_side
->log
, 400, 100);
680 gtk_box_pack_start(GTK_BOX(hbox
), gui_side
->log
, TRUE
, TRUE
, 0);
681 scrollbar
= gtk_vscrollbar_new(GTK_TEXT(gui_side
->log
)->vadj
);
682 gtk_box_pack_start(GTK_BOX(hbox
), scrollbar
, FALSE
, TRUE
, 0);
684 actions
= gtk_hbox_new(TRUE
, 4);
685 gtk_box_pack_start(GTK_BOX(vbox
), actions
, FALSE
, TRUE
, 0);
687 gui_side
->quiet
= button
= gtk_toggle_button_new_with_label("Quiet");
688 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button
), autoq
);
689 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "Q");
690 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
691 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
692 button_reply
, gui_side
);
693 gui_side
->yes
= button
= gtk_button_new_with_label("Yes");
694 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "Y");
695 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
696 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
697 button_reply
, gui_side
);
698 gui_side
->no
= button
= gtk_button_new_with_label("No");
699 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "N");
700 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
701 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
702 button_reply
, gui_side
);
703 SENSITIVE_YESNO(gui_side
, FALSE
);
705 button
= gtk_button_new_with_label("Abort");
706 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
707 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
708 gtk_widget_destroy
, GTK_OBJECT(gui_side
->window
));
710 gui_side
->input_tag
= gdk_input_add(gui_side
->from_child
,
718 /* ACTIONS ON ONE ITEM */
720 /* These may call themselves recursively, or ask questions, etc.
721 * TRUE iff the directory containing dest_path needs to be rescanned.
724 /* dest_path is the dir containing src_path.
725 * Updates the global size_tally.
727 static gboolean
do_usage(char *src_path
, char *dest_path
)
733 if (lstat(src_path
, &info
))
735 g_string_sprintf(message
, "'%s:\n", src_path
);
741 if (S_ISREG(info
.st_mode
) || S_ISLNK(info
.st_mode
))
742 size_tally
+= info
.st_size
;
743 else if (S_ISDIR(info
.st_mode
))
745 g_string_sprintf(message
, "?Count contents of %s?",
747 if (reply(from_parent
, FALSE
))
750 safe_path
= g_strdup(src_path
);
751 for_dir_contents(do_usage
, safe_path
, safe_path
);
759 /* dest_path is the dir containing src_path */
760 static gboolean
do_delete(char *src_path
, char *dest_path
)
767 if (lstat(src_path
, &info
))
773 write_prot
= S_ISLNK(info
.st_mode
) ? FALSE
774 : access(src_path
, W_OK
) != 0;
775 if (write_prot
|| !quiet
)
777 g_string_sprintf(message
, "?Delete %s'%s'?",
778 write_prot
? "WRITE-PROTECTED " : " ",
780 if (!reply(from_parent
, write_prot
&& !o_force
))
785 g_string_sprintf(message
, "'Removing '%s'\n", src_path
);
789 if (S_ISDIR(info
.st_mode
))
792 safe_path
= g_strdup(src_path
);
793 for_dir_contents(do_delete
, safe_path
, safe_path
);
794 if (rmdir(safe_path
))
800 g_string_assign(message
, "'Directory deleted\n");
804 else if (unlink(src_path
))
813 static gboolean
do_copy2(char *path
, char *dest
)
818 struct stat dest_info
;
819 gboolean retval
= TRUE
;
823 leaf
= strrchr(path
, '/');
825 leaf
= path
; /* Error? */
829 dest_path
= make_path(dest
, leaf
)->str
;
831 if (lstat(path
, &info
))
837 if (lstat(dest_path
, &dest_info
) == 0)
842 merge
= S_ISDIR(info
.st_mode
) && S_ISDIR(dest_info
.st_mode
);
844 g_string_sprintf(message
, "?'%s' already exists - %s?",
846 merge
? "merge contents" : "overwrite");
848 if (!reply(from_parent
, TRUE
))
853 if (S_ISDIR(dest_info
.st_mode
))
854 err
= rmdir(dest_path
);
856 err
= unlink(dest_path
);
863 g_string_sprintf(message
,
864 "'Trying copy anyway...\n");
871 g_string_sprintf(message
, "?Copy %s as %s?", path
, dest_path
);
872 if (!reply(from_parent
, FALSE
))
877 g_string_sprintf(message
, "'Copying %s as %s\n", path
,
882 if (S_ISDIR(info
.st_mode
))
884 char *safe_path
, *safe_dest
;
885 struct stat dest_info
;
888 /* (we will do the update ourselves now, rather than
893 safe_path
= g_strdup(path
);
894 safe_dest
= g_strdup(dest_path
);
896 exists
= !lstat(dest_path
, &dest_info
);
898 if (exists
&& !S_ISDIR(dest_info
.st_mode
))
900 g_string_sprintf(message
,
901 "!ERROR: Destination already exists, "
902 "but is not a directory\n");
904 else if (exists
== FALSE
&& mkdir(dest_path
, info
.st_mode
))
910 /* (just been created then) */
911 g_string_sprintf(message
, "+%s", dest
);
915 for_dir_contents(do_copy2
, safe_path
, safe_dest
);
916 /* Note: dest_path now invalid... */
922 else if (S_ISLNK(info
.st_mode
))
924 char target
[MAXPATHLEN
+ 1];
927 /* Not all versions of cp(1) can make symlinks,
928 * so we special-case it.
931 count
= readlink(path
, target
, sizeof(target
) - 1);
939 target
[count
] = '\0';
940 if (symlink(target
, dest_path
))
949 char *argv
[] = {"cp", "-pRf", NULL
, NULL
, NULL
};
954 if (fork_exec_wait(argv
))
956 g_string_sprintf(message
, "!ERROR: Copy failed\n");
965 /* Copy path to dest.
966 * Check that path not copied into itself.
967 * path and dest are real paths (coming from the filer)
969 static gboolean
do_copy(char *path
, char *dest
)
971 if (is_sub_dir(dest
, path
))
973 g_string_sprintf(message
,
974 "!ERROR: Can't copy directory into itself\n");
978 return do_copy2(path
, dest
);
981 /* Move path to dest.
982 * Check that path not moved into itself.
983 * path and dest are real paths (coming from the filer)
985 static gboolean
do_move(char *path
, char *dest
)
989 gboolean retval
= TRUE
;
990 char *argv
[] = {"mv", "-f", NULL
, NULL
, NULL
};
992 if (is_sub_dir(dest
, path
))
994 g_string_sprintf(message
,
995 "!ERROR: Can't move directory into itself\n");
1002 leaf
= strrchr(path
, '/');
1004 leaf
= path
; /* Error? */
1008 dest_path
= make_path(dest
, leaf
)->str
;
1010 if (access(dest_path
, F_OK
) == 0)
1015 g_string_sprintf(message
, "?'%s' already exists - overwrite?",
1017 if (!reply(from_parent
, TRUE
))
1020 if (lstat(dest_path
, &info
))
1026 if (S_ISDIR(info
.st_mode
))
1027 err
= rmdir(dest_path
);
1029 err
= unlink(dest_path
);
1034 if (errno
!= ENOENT
)
1036 g_string_sprintf(message
,
1037 "'Trying move anyway...\n");
1043 g_string_sprintf(message
, "?Move %s as %s?", path
, dest_path
);
1044 if (!reply(from_parent
, FALSE
))
1049 g_string_sprintf(message
, "'Moving %s as %s\n", path
,
1057 if (fork_exec_wait(argv
) == 0)
1059 g_string_sprintf(message
, "+%s", path
);
1060 g_string_truncate(message
, leaf
- path
);
1065 g_string_sprintf(message
, "!ERROR: Failed to move %s as %s\n",
1074 static gboolean
do_link(char *path
, char *dest
)
1081 leaf
= strrchr(path
, '/');
1083 leaf
= path
; /* Error? */
1087 dest_path
= make_path(dest
, leaf
)->str
;
1091 g_string_sprintf(message
, "'Linking %s as %s\n", path
,
1097 g_string_sprintf(message
, "?Link %s as %s?", path
, dest_path
);
1098 if (!reply(from_parent
, FALSE
))
1102 if (symlink(path
, dest_path
))
1111 /* Mount/umount this item */
1112 static void do_mount(FilerWindow
*filer_window
, DirItem
*item
)
1114 char *argv
[3] = {NULL
, NULL
, NULL
};
1118 argv
[0] = item
->flags
& ITEM_FLAG_MOUNTED
? "umount" : "mount";
1119 argv
[1] = make_path(filer_window
->path
, item
->leafname
)->str
;
1123 g_string_sprintf(message
, "'%sing %s\n", argv
[0], argv
[1]);
1128 g_string_sprintf(message
, "?%s %s?", argv
[0], argv
[1]);
1129 if (!reply(from_parent
, FALSE
))
1133 if (fork_exec_wait(argv
) == 0)
1135 g_string_sprintf(message
, "+%s", filer_window
->path
);
1137 g_string_sprintf(message
, "m%s", argv
[1]);
1142 g_string_sprintf(message
, "!ERROR: %s failed\n", argv
[0]);
1147 /* CHILD MAIN LOOPS */
1149 /* After forking, the child calls one of these functions */
1151 static void usage_cb(gpointer data
)
1153 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1154 Collection
*collection
= filer_window
->collection
;
1156 int left
= collection
->number_selected
;
1158 off_t total_size
= 0;
1160 send_dir(filer_window
->path
);
1165 if (!collection
->items
[i
].selected
)
1167 item
= (DirItem
*) collection
->items
[i
].data
;
1169 do_usage(make_path(filer_window
->path
,
1170 item
->leafname
)->str
,
1171 filer_window
->path
);
1172 g_string_sprintf(message
, "'%s: %s\n",
1174 format_size((unsigned long) size_tally
));
1176 total_size
+= size_tally
;
1180 g_string_sprintf(message
, "'\nTotal: %s\n",
1181 format_size((unsigned long) total_size
));
1185 #ifdef DO_MOUNT_POINTS
1186 static void mount_cb(gpointer data
)
1188 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1189 Collection
*collection
= filer_window
->collection
;
1192 gboolean mount_points
= FALSE
;
1194 send_dir(filer_window
->path
);
1197 do_mount(filer_window
, mount_item
);
1200 for (i
= 0; i
< collection
->number_of_items
; i
++)
1202 if (!collection
->items
[i
].selected
)
1204 item
= (DirItem
*) collection
->items
[i
].data
;
1205 if (!(item
->flags
& ITEM_FLAG_MOUNT_POINT
))
1207 mount_points
= TRUE
;
1209 do_mount(filer_window
, item
);
1214 g_string_sprintf(message
,
1215 "!No mount points selected!\n");
1220 g_string_sprintf(message
, "'\nDone\n");
1225 static void delete_cb(gpointer data
)
1227 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1228 Collection
*collection
= filer_window
->collection
;
1230 int left
= collection
->number_selected
;
1233 send_dir(filer_window
->path
);
1238 if (!collection
->items
[i
].selected
)
1240 item
= (DirItem
*) collection
->items
[i
].data
;
1241 if (do_delete(make_path(filer_window
->path
,
1242 item
->leafname
)->str
,
1243 filer_window
->path
))
1245 g_string_sprintf(message
, "+%s", filer_window
->path
);
1251 g_string_sprintf(message
, "'\nDone\n");
1255 static void list_cb(gpointer data
)
1257 GSList
*paths
= (GSList
*) data
;
1261 send_dir((char *) paths
->data
);
1263 if (action_do_func((char *) paths
->data
, action_dest
))
1265 g_string_sprintf(message
, "+%s", action_dest
);
1269 paths
= paths
->next
;
1272 g_string_sprintf(message
, "'\nDone\n");
1276 static void add_toggle(GUIside
*gui_side
, guchar
*label
, guchar
*code
)
1280 check
= gtk_check_button_new_with_label(label
);
1281 gtk_object_set_data(GTK_OBJECT(check
), "send-code", code
);
1282 gtk_signal_connect(GTK_OBJECT(check
), "clicked",
1283 button_reply
, gui_side
);
1284 gtk_box_pack_start(GTK_BOX(gui_side
->vbox
), check
, FALSE
, TRUE
, 0);
1288 /* EXTERNAL INTERFACE */
1290 /* Count disk space used by selected items */
1291 void action_usage(FilerWindow
*filer_window
)
1294 Collection
*collection
;
1296 collection
= filer_window
->collection
;
1298 if (collection
->number_selected
< 1)
1300 report_error("ROX-Filer", "You need to select some items "
1305 gui_side
= start_action(filer_window
, usage_cb
, TRUE
);
1309 gui_side
->show_info
= TRUE
;
1311 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Disk Usage");
1312 number_of_windows
++;
1313 gtk_widget_show_all(gui_side
->window
);
1316 /* Mount/unmount 'item', or all selected mount points if NULL. */
1317 void action_mount(FilerWindow
*filer_window
, DirItem
*item
)
1319 #ifdef DO_MOUNT_POINTS
1321 Collection
*collection
;
1323 collection
= filer_window
->collection
;
1325 if (item
== NULL
&& collection
->number_selected
< 1)
1327 report_error("ROX-Filer", "You need to select some items "
1328 "to mount or unmount");
1333 gui_side
= start_action(filer_window
, mount_cb
, o_auto_mount
);
1337 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Mount / Unmount");
1338 number_of_windows
++;
1339 gtk_widget_show_all(gui_side
->window
);
1341 report_error("ROX-Filer",
1342 "ROX-Filer does not yet support mount points on your "
1344 #endif /* DO_MOUNT_POINTS */
1347 /* Deletes all selected items in the window */
1348 void action_delete(FilerWindow
*filer_window
)
1351 Collection
*collection
;
1353 collection
= filer_window
->collection
;
1355 if (collection
->number_selected
< 1)
1357 report_error("ROX-Filer", "You need to select some items "
1362 gui_side
= start_action(filer_window
, delete_cb
, o_auto_delete
);
1366 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Delete");
1367 add_toggle(gui_side
,
1368 "Don't confirm deletion of non-writeable items", "F");
1370 number_of_windows
++;
1371 gtk_widget_show_all(gui_side
->window
);
1374 void action_copy(GSList
*paths
, char *dest
)
1379 action_do_func
= do_copy
;
1380 gui_side
= start_action(paths
, list_cb
, o_auto_copy
);
1384 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Copy");
1385 number_of_windows
++;
1386 gtk_widget_show_all(gui_side
->window
);
1389 void action_move(GSList
*paths
, char *dest
)
1394 action_do_func
= do_move
;
1395 gui_side
= start_action(paths
, list_cb
, o_auto_move
);
1399 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Move");
1400 number_of_windows
++;
1401 gtk_widget_show_all(gui_side
->window
);
1404 void action_link(GSList
*paths
, char *dest
)
1409 action_do_func
= do_link
;
1410 gui_side
= start_action(paths
, list_cb
, o_auto_link
);
1414 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Link");
1415 number_of_windows
++;
1416 gtk_widget_show_all(gui_side
->window
);
1419 void action_init(void)
1421 options_sections
= g_slist_prepend(options_sections
, &options
);
1422 option_register("action_auto_quiet", action_auto_quiet
);