4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2000, 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"
47 #include "modechange.h"
51 static GtkWidget
*create_options();
52 static void update_options();
53 static void set_options();
54 static void save_options();
55 static char *action_auto_quiet(char *data
);
57 static OptionsSection options
=
59 "Action window options",
66 static gboolean o_auto_copy
= TRUE
;
67 static gboolean o_auto_move
= TRUE
;
68 static gboolean o_auto_link
= TRUE
;
69 static gboolean o_auto_delete
= FALSE
;
70 static gboolean o_auto_mount
= TRUE
;
72 static GtkWidget
*w_auto_copy
= NULL
;
73 static GtkWidget
*w_auto_move
= NULL
;
74 static GtkWidget
*w_auto_link
= NULL
;
75 static GtkWidget
*w_auto_delete
= NULL
;
76 static GtkWidget
*w_auto_mount
= NULL
;
78 static GdkColor red
= {0, 0xffff, 0, 0};
80 /* Parent->Child messages are one character each:
82 * Q/Y/N Quiet/Yes/No button clicked
83 * F Force deletion of non-writeable items
86 #define SENSITIVE_YESNO(gui_side, state) \
88 gtk_widget_set_sensitive((gui_side)->yes, state); \
89 gtk_widget_set_sensitive((gui_side)->no, state); \
90 if ((gui_side)->entry) \
91 gtk_widget_set_sensitive((gui_side)->entry, state);\
94 #define ON(flag) ((flag) ? "on" : "off")
96 typedef struct _GUIside GUIside
;
97 typedef void ActionChild(gpointer data
);
98 typedef gboolean
ForDirCB(char *path
, char *dest_path
);
102 int from_child
; /* File descriptor */
104 int input_tag
; /* gdk_input_add() */
105 GtkWidget
*vbox
, *log
, *window
, *dir
, *log_hbox
;
106 GtkWidget
*quiet
, *yes
, *no
;
107 int child
; /* Process ID */
109 gboolean show_info
; /* For Disk Usage */
111 GtkWidget
*entry
; /* May be NULL */
112 guchar
**default_string
; /* Changed when the entry changes */
114 char *next_dir
; /* NULL => no timer active */
118 FilerWindow
*preview
;
122 /* These don't need to be in a structure because we fork() before
125 static int from_parent
= 0;
126 static FILE *to_parent
= NULL
;
127 static gboolean quiet
= FALSE
;
128 static GString
*message
= NULL
;
129 static char *action_dest
= NULL
;
130 static char *action_leaf
= NULL
;
131 static gboolean (*action_do_func
)(char *source
, char *dest
);
132 static size_t size_tally
; /* For Disk Usage */
133 static DirItem
*mount_item
;
135 static struct mode_change
*mode_change
= NULL
; /* For Permissions */
136 static FindCondition
*find_condition
= NULL
; /* For Find */
138 static gboolean o_force
= FALSE
;
139 static gboolean o_brief
= FALSE
;
140 static gboolean o_recurse
= FALSE
;
142 /* Whenever the text in these boxes is changed we store a copy of the new
143 * string to be used as the default next time.
145 static guchar
*last_chmod_string
= NULL
;
146 static guchar
*last_find_string
= NULL
;
148 /* Set to one of the above before forking. This may change over a call to
149 * reply(). It is reset to NULL once the text is parsed.
151 static guchar
*new_entry_string
= NULL
;
153 /* Static prototypes */
154 static gboolean
send();
155 static gboolean
send_error();
156 static gboolean
send_dir(char *dir
);
157 static gboolean
read_exact(int source
, char *buffer
, ssize_t len
);
158 static void do_mount(FilerWindow
*filer_window
, DirItem
*item
);
159 static void add_toggle(GUIside
*gui_side
, guchar
*label
, guchar
*code
);
160 static gboolean
reply(int fd
, gboolean ignore_quiet
);
164 /* Build up some option widgets to go in the options dialog, but don't
167 static GtkWidget
*create_options()
169 GtkWidget
*vbox
, *hbox
, *label
;
171 vbox
= gtk_vbox_new(FALSE
, 0);
172 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 4);
174 label
= gtk_label_new("Auto-start (Quiet) these actions:");
175 gtk_box_pack_start(GTK_BOX(vbox
), label
, FALSE
, TRUE
, 0);
177 hbox
= gtk_hbox_new(TRUE
, 0);
178 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, TRUE
, 0);
180 w_auto_copy
= gtk_check_button_new_with_label("Copy");
181 gtk_box_pack_start(GTK_BOX(hbox
), w_auto_copy
, FALSE
, TRUE
, 0);
182 w_auto_move
= gtk_check_button_new_with_label("Move");
183 gtk_box_pack_start(GTK_BOX(hbox
), w_auto_move
, FALSE
, TRUE
, 0);
184 w_auto_link
= gtk_check_button_new_with_label("Link");
185 gtk_box_pack_start(GTK_BOX(hbox
), w_auto_link
, FALSE
, TRUE
, 0);
186 w_auto_delete
= gtk_check_button_new_with_label("Delete");
187 gtk_box_pack_start(GTK_BOX(hbox
), w_auto_delete
, FALSE
, TRUE
, 0);
188 w_auto_mount
= gtk_check_button_new_with_label("Mount");
189 gtk_box_pack_start(GTK_BOX(hbox
), w_auto_mount
, FALSE
, TRUE
, 0);
194 /* Reflect current state by changing the widgets in the options box */
195 static void update_options()
197 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_copy
),
199 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_move
),
201 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_link
),
203 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_delete
),
205 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_mount
),
209 /* Set current values by reading the states of the widgets in the options box */
210 static void set_options()
212 o_auto_copy
= gtk_toggle_button_get_active(
213 GTK_TOGGLE_BUTTON(w_auto_copy
));
214 o_auto_move
= gtk_toggle_button_get_active(
215 GTK_TOGGLE_BUTTON(w_auto_move
));
216 o_auto_link
= gtk_toggle_button_get_active(
217 GTK_TOGGLE_BUTTON(w_auto_link
));
218 o_auto_delete
= gtk_toggle_button_get_active(
219 GTK_TOGGLE_BUTTON(w_auto_delete
));
220 o_auto_mount
= gtk_toggle_button_get_active(
221 GTK_TOGGLE_BUTTON(w_auto_mount
));
224 static void save_options()
226 guchar str
[] = "cmldt";
239 option_write("action_auto_quiet", str
);
242 static char *action_auto_quiet(char *data
)
263 o_auto_delete
= state
;
266 o_auto_mount
= state
;
269 return "Unknown flag";
278 static void preview_closed(GtkWidget
*window
, GUIside
*gui_side
)
280 gui_side
->preview
= NULL
;
283 static void select_row_callback(GtkWidget
*widget
,
284 gint row
, gint column
,
285 GdkEventButton
*event
,
290 gtk_clist_get_text(GTK_CLIST(gui_side
->results
), row
, 0, &leaf
);
291 gtk_clist_get_text(GTK_CLIST(gui_side
->results
), row
, 1, &dir
);
293 gtk_clist_unselect_row(GTK_CLIST(gui_side
->results
), row
, column
);
295 if (gui_side
->preview
)
297 if (strcmp(gui_side
->preview
->path
, dir
) == 0)
298 filer_set_autoselect(gui_side
->preview
, leaf
);
300 filer_change_to(gui_side
->preview
, dir
, leaf
);
304 gui_side
->preview
= filer_opendir(dir
, PANEL_NO
);
305 filer_set_autoselect(gui_side
->preview
, leaf
);
306 gtk_signal_connect(GTK_OBJECT(gui_side
->preview
->window
), "destroy",
307 GTK_SIGNAL_FUNC(preview_closed
), gui_side
);
311 /* This is called whenever the user edits the entry box (if any) - send the
314 static void entry_changed(GtkEntry
*entry
, GUIside
*gui_side
)
318 g_return_if_fail(gui_side
->default_string
!= NULL
);
320 text
= gtk_entry_get_text(entry
);
322 g_free(*(gui_side
->default_string
));
323 *(gui_side
->default_string
) = g_strdup(text
);
325 if (!gui_side
->to_child
)
328 fputc('E', gui_side
->to_child
);
329 fputs(text
, gui_side
->to_child
);
330 fputc('\n', gui_side
->to_child
);
331 fflush(gui_side
->to_child
);
334 static void show_condition_help(gpointer data
)
336 static GtkWidget
*help
= NULL
;
340 GtkWidget
*text
, *vbox
, *button
, *hbox
, *frame
;
342 help
= gtk_window_new(GTK_WINDOW_DIALOG
);
343 gtk_container_set_border_width(GTK_CONTAINER(help
), 10);
344 gtk_window_set_title(GTK_WINDOW(help
),
345 "Find expression reference");
347 vbox
= gtk_vbox_new(FALSE
, 0);
348 gtk_container_add(GTK_CONTAINER(help
), vbox
);
350 frame
= gtk_frame_new("Quick Start");
351 text
= gtk_label_new(
352 "Just put the name of the file you're looking for in single quotes:\n"
353 "'index.html' (to find a file called 'index.html')");
354 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
355 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
356 gtk_container_add(GTK_CONTAINER(frame
), text
);
357 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
359 frame
= gtk_frame_new("Examples");
360 text
= gtk_label_new(
361 "'*.htm', '*.html' (finds HTML files)\n"
362 "IsDir 'lib' (finds directories called 'lib')\n"
363 "IsReg 'core' (finds a regular file called 'core')\n"
364 "! (IsDir, IsReg) (is neither a directory nor a regualr file)\n"
365 "mtime after 1 day ago and size > 1Mb (big, and recently modified)");
366 gtk_widget_set_style(text
, fixed_style
);
367 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
368 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
369 gtk_container_add(GTK_CONTAINER(frame
), text
);
370 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
372 frame
= gtk_frame_new("Simple Tests");
373 text
= gtk_label_new(
374 "IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket (types)\n"
375 "IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable (permissions)"
379 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
380 "contains a slash then the match is agaist the full path; otherwise it is \n"
381 "agaist the leafname only.");
382 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
383 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
384 gtk_container_add(GTK_CONTAINER(frame
), text
);
385 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
387 frame
= gtk_frame_new("Comparisons");
388 text
= gtk_label_new(
389 "<, <=, =, !=, >, >=, After, Before (compare two values)\n"
390 "5 bytes, 1Kb, 2Mb, 3Gb (file sizes)\n"
391 "2 secs|mins|hours|days|weeks|years ago|hence (times)\n"
392 "atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks (values)");
393 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
394 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
395 gtk_container_add(GTK_CONTAINER(frame
), text
);
396 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
398 hbox
= gtk_hbox_new(FALSE
, 20);
399 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, TRUE
, TRUE
, 0);
401 text
= gtk_label_new(
402 "See the ROX-Filer manual for full details.");
403 gtk_box_pack_start(GTK_BOX(hbox
), text
, TRUE
, TRUE
, 0);
404 button
= gtk_button_new_with_label("Close");
405 gtk_box_pack_start(GTK_BOX(hbox
), button
, FALSE
, TRUE
, 0);
406 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
407 gtk_widget_hide
, GTK_OBJECT(help
));
409 gtk_signal_connect_object(GTK_OBJECT(help
), "delete_event",
410 gtk_widget_hide
, GTK_OBJECT(help
));
413 if (GTK_WIDGET_VISIBLE(help
))
414 gtk_widget_hide(help
);
415 gtk_widget_show_all(help
);
418 static void show_chmod_help(gpointer data
)
420 static GtkWidget
*help
= NULL
;
424 GtkWidget
*text
, *vbox
, *button
, *hbox
, *sep
;
426 help
= gtk_window_new(GTK_WINDOW_DIALOG
);
427 gtk_container_set_border_width(GTK_CONTAINER(help
), 10);
428 gtk_window_set_title(GTK_WINDOW(help
),
429 "Permissions command reference");
431 vbox
= gtk_vbox_new(FALSE
, 0);
432 gtk_container_add(GTK_CONTAINER(help
), vbox
);
434 text
= gtk_label_new(
435 "The format of a command is:\n"
436 "CHANGE, CHANGE, ...\n"
438 "WHO HOW PERMISSIONS\n"
439 "WHO is some combination of u, g and o which determines whether to\n"
440 "change the permissions for the User (owner), Group or Others.\n"
441 "HOW is +, - or = to add, remove or set exactly the permissions.\n"
442 "PERMISSIONS is some combination of the letters 'rwxXstugo'\n\n"
445 "u+rw (the file owner gains read and write permission)\n"
446 "g=u (the group permissions are set to be the same as the user's)\n"
447 "o=u-w (others get the same permissions as the owner, but without "
448 "write permission)\n"
449 "a+x (everyone gets execute/access permission - same as 'ugo+x')\n"
450 "a+X (directories become accessable by everyone; files which were\n"
451 "executable by anyone become executable by everyone)\n"
452 "u+rw, go+r (two commands at once!)\n"
453 "u+s (set the SetUID bit - often has no effect on script files)\n"
454 "755 (set the permissions directly)\n"
456 "\nSee the chmod(1) man page for full details.");
457 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
458 gtk_box_pack_start(GTK_BOX(vbox
), text
, TRUE
, TRUE
, 0);
460 hbox
= gtk_hbox_new(FALSE
, 20);
461 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, TRUE
, 0);
463 sep
= gtk_hseparator_new();
464 gtk_box_pack_start(GTK_BOX(hbox
), sep
, TRUE
, TRUE
, 0);
465 button
= gtk_button_new_with_label("Close");
466 gtk_box_pack_start(GTK_BOX(hbox
), button
, FALSE
, TRUE
, 0);
467 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
468 gtk_widget_hide
, GTK_OBJECT(help
));
470 gtk_signal_connect_object(GTK_OBJECT(help
), "delete_event",
471 gtk_widget_hide
, GTK_OBJECT(help
));
474 if (GTK_WIDGET_VISIBLE(help
))
475 gtk_widget_hide(help
);
476 gtk_widget_show_all(help
);
479 /* TRUE iff `sub' is (or would be) an object inside the directory `parent',
480 * (or the two are the same directory)
482 static gboolean
is_sub_dir(char *sub
, char *parent
)
485 guchar
*real_sub
, *real_parent
;
488 real_sub
= pathdup(sub
);
489 real_parent
= pathdup(parent
);
491 parent_len
= strlen(real_parent
);
492 if (strncmp(real_parent
, real_sub
, parent_len
))
496 /* real_sub is at least as long as real_parent and all
497 * characters upto real_parent's length match.
500 retval
= real_sub
[parent_len
] == '\0' ||
501 real_sub
[parent_len
] == '/';
510 static gboolean
display_dir(gpointer data
)
512 GUIside
*gui_side
= (GUIside
*) data
;
514 gtk_label_set_text(GTK_LABEL(gui_side
->dir
), gui_side
->next_dir
+ 1);
515 g_free(gui_side
->next_dir
);
516 gui_side
->next_dir
= NULL
;
521 static void add_to_results(GUIside
*gui_side
, gchar
*path
)
523 gchar
*row
[] = {"Leaf", "Dir"};
527 slash
= strrchr(path
, '/');
528 g_return_if_fail(slash
!= NULL
);
531 row
[1] = g_strndup(path
, MAX(len
, 1));
534 gtk_clist_append(GTK_CLIST(gui_side
->results
), row
);
539 /* Called when the child sends us a message */
540 static void message_from_child(gpointer data
,
542 GdkInputCondition condition
)
545 GUIside
*gui_side
= (GUIside
*) data
;
546 GtkWidget
*log
= gui_side
->log
;
548 if (read_exact(source
, buf
, 4))
554 message_len
= strtol(buf
, NULL
, 16);
555 buffer
= g_malloc(message_len
+ 1);
556 if (message_len
> 0 && read_exact(source
, buffer
, message_len
))
558 buffer
[message_len
] = '\0';
561 SENSITIVE_YESNO(gui_side
, TRUE
);
562 gtk_window_set_focus(
563 GTK_WINDOW(gui_side
->window
),
564 gui_side
->entry
? gui_side
->entry
567 else if (*buffer
== '+')
569 refresh_dirs(buffer
+ 1);
573 else if (*buffer
== '=')
575 add_to_results(gui_side
, buffer
+ 1);
579 else if (*buffer
== 'm')
581 filer_check_mounted(buffer
+ 1);
585 else if (*buffer
== '/')
587 if (gui_side
->next_dir
)
588 g_free(gui_side
->next_dir
);
590 gui_side
->next_timer
=
594 gui_side
->next_dir
= buffer
;
597 else if (*buffer
== '!')
600 gtk_text_insert(GTK_TEXT(log
),
602 *buffer
== '!' ? &red
: NULL
,
604 buffer
+ 1, message_len
- 1);
608 g_print("Child died in the middle of a message.\n");
611 /* The child is dead */
614 fclose(gui_side
->to_child
);
615 gui_side
->to_child
= NULL
;
616 close(gui_side
->from_child
);
617 gdk_input_remove(gui_side
->input_tag
);
618 gtk_widget_set_sensitive(gui_side
->quiet
, FALSE
);
620 if (gui_side
->errors
)
623 report
= g_string_new(NULL
);
624 g_string_sprintf(report
, "There %s %d error%s.\n",
625 gui_side
->errors
== 1 ? "was" : "were",
627 gui_side
->errors
== 1 ? "" : "s");
628 gtk_text_insert(GTK_TEXT(log
), NULL
, &red
, NULL
,
629 report
->str
, report
->len
);
631 g_string_free(report
, TRUE
);
633 else if (gui_side
->show_info
== FALSE
)
634 gtk_widget_destroy(gui_side
->window
);
637 /* Scans src_dir, updating dest_path whenever cb returns TRUE */
638 static void for_dir_contents(ForDirCB
*cb
, char *src_dir
, char *dest_path
)
642 GSList
*list
= NULL
, *next
;
644 d
= opendir(src_dir
);
653 while ((ent
= readdir(d
)))
655 if (ent
->d_name
[0] == '.' && (ent
->d_name
[1] == '\0'
656 || (ent
->d_name
[1] == '.' && ent
->d_name
[2] == '\0')))
658 list
= g_slist_append(list
, g_strdup(make_path(src_dir
,
670 if (cb((char *) next
->data
, dest_path
))
672 g_string_sprintf(message
, "+%s", dest_path
);
683 /* Read this many bytes into the buffer. TRUE on success. */
684 static gboolean
read_exact(int source
, char *buffer
, ssize_t len
)
689 got
= read(source
, buffer
, len
);
698 /* Send 'message' to our parent process. TRUE on success. */
699 static gboolean
send()
704 g_return_val_if_fail(message
->len
< 0xffff, FALSE
);
706 sprintf(len_buffer
, "%04x", message
->len
);
707 fwrite(len_buffer
, 1, 4, to_parent
);
708 len
= fwrite(message
->str
, 1, message
->len
, to_parent
);
710 return len
== message
->len
;
713 /* Set the directory indicator at the top of the window */
714 static gboolean
send_dir(char *dir
)
716 g_string_sprintf(message
, "/%s", dir
);
720 static gboolean
send_error()
722 g_string_sprintf(message
, "!ERROR: %s\n", g_strerror(errno
));
726 static void button_reply(GtkWidget
*button
, GUIside
*gui_side
)
730 if (!gui_side
->to_child
)
733 text
= gtk_object_get_data(GTK_OBJECT(button
), "send-code");
734 g_return_if_fail(text
!= NULL
);
735 fputc(*text
, gui_side
->to_child
);
736 fflush(gui_side
->to_child
);
738 if (*text
== 'Y' || *text
== 'N' || *text
== 'Q')
739 SENSITIVE_YESNO(gui_side
, FALSE
);
742 static void process_flag(char flag
)
753 o_recurse
= !o_recurse
;
759 g_string_sprintf(message
,
760 "!ERROR: Bad message '%c'\n", flag
);
766 /* If the parent has sent any flag toggles, read them */
767 static void check_flags(void)
778 FD_SET(from_parent
, &set
);
781 got
= select(from_parent
+ 1, &set
, NULL
, NULL
, &tv
);
784 g_error("select() failed: %s\n", g_strerror(errno
));
788 got
= read(from_parent
, &retval
, 1);
790 g_error("read() error: %s\n", g_strerror(errno
));
792 process_flag(retval
);
796 static void read_new_entry_text(void)
802 new = g_string_new(NULL
);
806 len
= read(from_parent
, &c
, 1);
809 fprintf(stderr
, "read() error: %s\n",
811 _exit(1); /* Parent died? */
816 g_string_append_c(new, c
);
819 g_free(new_entry_string
);
820 new_entry_string
= new->str
;
821 g_string_free(new, FALSE
);
824 /* Read until the user sends a reply. If ignore_quiet is TRUE then
825 * the user MUST click Yes or No, else treat quiet on as Yes.
826 * If the user needs prompting then does send().
828 static gboolean
reply(int fd
, gboolean ignore_quiet
)
832 gboolean asked
= FALSE
;
834 while (ignore_quiet
|| !quiet
)
842 len
= read(fd
, &retval
, 1);
845 fprintf(stderr
, "read() error: %s\n",
847 _exit(1); /* Parent died? */
856 g_string_assign(message
, "?");
861 g_string_assign(message
, "' Yes\n");
865 g_string_assign(message
, "' No\n");
869 read_new_entry_text();
872 process_flag(retval
);
879 g_string_assign(message
, "' Quiet\n");
885 static void destroy_action_window(GtkWidget
*widget
, gpointer data
)
887 GUIside
*gui_side
= (GUIside
*) data
;
891 kill(gui_side
->child
, SIGTERM
);
892 fclose(gui_side
->to_child
);
893 close(gui_side
->from_child
);
894 gdk_input_remove(gui_side
->input_tag
);
897 if (gui_side
->next_dir
)
899 gtk_timeout_remove(gui_side
->next_timer
);
900 g_free(gui_side
->next_dir
);
903 if (gui_side
->preview
)
905 gtk_signal_disconnect_by_data(
906 GTK_OBJECT(gui_side
->preview
->window
),
908 gui_side
->preview
= NULL
;
913 if (--number_of_windows
< 1)
917 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
918 * (NULL on failure). The child calls func().
920 * If autoq then automatically selects 'Quiet'.
922 static GUIside
*start_action(gpointer data
, ActionChild
*func
, gboolean autoq
)
924 int filedes
[4]; /* 0 and 2 are for reading */
927 GtkWidget
*vbox
, *button
, *scrollbar
, *actions
;
928 struct sigaction act
;
932 report_error("ROX-Filer", g_strerror(errno
));
936 if (pipe(filedes
+ 2))
940 report_error("ROX-Filer", g_strerror(errno
));
948 report_error("ROX-Filer", g_strerror(errno
));
951 /* We are the child */
955 /* Reset the SIGCHLD handler */
956 act
.sa_handler
= SIG_DFL
;
957 sigemptyset(&act
.sa_mask
);
959 sigaction(SIGCHLD
, &act
, NULL
);
961 message
= g_string_new(NULL
);
964 to_parent
= fdopen(filedes
[1], "wb");
965 from_parent
= filedes
[2];
970 /* We are the parent */
973 gui_side
= g_malloc(sizeof(GUIside
));
974 gui_side
->from_child
= filedes
[0];
975 gui_side
->to_child
= fdopen(filedes
[3], "wb");
976 gui_side
->log
= NULL
;
977 gui_side
->child
= child
;
978 gui_side
->errors
= 0;
979 gui_side
->show_info
= FALSE
;
980 gui_side
->preview
= NULL
;
981 gui_side
->results
= NULL
;
982 gui_side
->entry
= NULL
;
983 gui_side
->default_string
= NULL
;
985 gui_side
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
986 gtk_container_set_border_width(GTK_CONTAINER(gui_side
->window
), 2);
987 gtk_window_set_default_size(GTK_WINDOW(gui_side
->window
), 450, 200);
988 gtk_signal_connect(GTK_OBJECT(gui_side
->window
), "destroy",
989 GTK_SIGNAL_FUNC(destroy_action_window
), gui_side
);
991 gui_side
->vbox
= vbox
= gtk_vbox_new(FALSE
, 0);
992 gtk_container_add(GTK_CONTAINER(gui_side
->window
), vbox
);
994 gui_side
->dir
= gtk_label_new("<dir>");
995 gui_side
->next_dir
= NULL
;
996 gtk_misc_set_alignment(GTK_MISC(gui_side
->dir
), 0.5, 0.5);
997 gtk_box_pack_start(GTK_BOX(vbox
), gui_side
->dir
, FALSE
, TRUE
, 0);
999 gui_side
->log_hbox
= gtk_hbox_new(FALSE
, 0);
1000 gtk_box_pack_start(GTK_BOX(vbox
), gui_side
->log_hbox
, TRUE
, TRUE
, 4);
1002 gui_side
->log
= gtk_text_new(NULL
, NULL
);
1003 gtk_widget_set_usize(gui_side
->log
, 400, 100);
1004 gtk_box_pack_start(GTK_BOX(gui_side
->log_hbox
),
1005 gui_side
->log
, TRUE
, TRUE
, 0);
1006 scrollbar
= gtk_vscrollbar_new(GTK_TEXT(gui_side
->log
)->vadj
);
1007 gtk_box_pack_start(GTK_BOX(gui_side
->log_hbox
),
1008 scrollbar
, FALSE
, TRUE
, 0);
1010 actions
= gtk_hbox_new(TRUE
, 4);
1011 gtk_box_pack_start(GTK_BOX(vbox
), actions
, FALSE
, TRUE
, 0);
1013 gui_side
->quiet
= button
= gtk_toggle_button_new_with_label("Quiet");
1014 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button
), autoq
);
1015 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "Q");
1016 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
1017 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
1018 button_reply
, gui_side
);
1019 gui_side
->yes
= button
= gtk_button_new_with_label("Yes");
1020 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "Y");
1021 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
1022 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
1023 button_reply
, gui_side
);
1024 gui_side
->no
= button
= gtk_button_new_with_label("No");
1025 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "N");
1026 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
1027 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
1028 button_reply
, gui_side
);
1029 SENSITIVE_YESNO(gui_side
, FALSE
);
1031 button
= gtk_button_new_with_label("Abort");
1032 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
1033 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
1034 gtk_widget_destroy
, GTK_OBJECT(gui_side
->window
));
1036 gui_side
->input_tag
= gdk_input_add(gui_side
->from_child
,
1044 /* ACTIONS ON ONE ITEM */
1046 /* These may call themselves recursively, or ask questions, etc.
1047 * TRUE iff the directory containing dest_path needs to be rescanned.
1050 /* dest_path is the dir containing src_path.
1051 * Updates the global size_tally.
1053 static gboolean
do_usage(char *src_path
, char *dest_path
)
1059 if (lstat(src_path
, &info
))
1061 g_string_sprintf(message
, "'%s:\n", src_path
);
1067 if (S_ISREG(info
.st_mode
) || S_ISLNK(info
.st_mode
))
1068 size_tally
+= info
.st_size
;
1069 else if (S_ISDIR(info
.st_mode
))
1071 g_string_sprintf(message
, "?Count contents of %s?",
1073 if (reply(from_parent
, FALSE
))
1076 safe_path
= g_strdup(src_path
);
1077 for_dir_contents(do_usage
, safe_path
, safe_path
);
1085 /* dest_path is the dir containing src_path */
1086 static gboolean
do_delete(char *src_path
, char *dest_path
)
1089 gboolean write_prot
;
1093 if (lstat(src_path
, &info
))
1099 write_prot
= S_ISLNK(info
.st_mode
) ? FALSE
1100 : access(src_path
, W_OK
) != 0;
1101 if (write_prot
|| !quiet
)
1103 g_string_sprintf(message
, "?Delete %s'%s'?",
1104 write_prot
? "WRITE-PROTECTED " : " ",
1106 if (!reply(from_parent
, write_prot
&& !o_force
))
1111 g_string_sprintf(message
, "'Deleting '%s'\n", src_path
);
1115 if (S_ISDIR(info
.st_mode
))
1118 safe_path
= g_strdup(src_path
);
1119 for_dir_contents(do_delete
, safe_path
, safe_path
);
1120 if (rmdir(safe_path
))
1126 g_string_sprintf(message
, "'Directory '%s' deleted\n",
1129 g_string_sprintf(message
, "m%s", safe_path
);
1133 else if (unlink(src_path
))
1142 /* path is the item to check. If is is a directory then we may recurse
1143 * (unless prune is used).
1145 static gboolean
do_find(char *path
, char *dummy
)
1153 g_string_sprintf(message
, "?Check '%s'?", path
);
1154 if (!reply(from_parent
, FALSE
))
1160 if (new_entry_string
)
1163 find_condition_free(find_condition
);
1164 find_condition
= find_compile(new_entry_string
);
1165 g_free(new_entry_string
);
1166 new_entry_string
= NULL
;
1172 g_string_assign(message
,
1173 "!Invalid find condition - change it and try again\n");
1175 g_string_sprintf(message
, "?Check '%s'?", path
);
1176 if (!reply(from_parent
, TRUE
))
1180 if (lstat(path
, &info
))
1183 g_string_sprintf(message
, "'(while checking '%s')\n", path
);
1188 if (find_test_condition(find_condition
, path
))
1190 g_string_sprintf(message
, "=%s", path
);
1194 if (S_ISDIR(info
.st_mode
))
1197 safe_path
= g_strdup(path
);
1198 for_dir_contents(do_find
, safe_path
, safe_path
);
1205 static gboolean
do_chmod(char *path
, char *dummy
)
1212 if (lstat(path
, &info
))
1217 if (S_ISLNK(info
.st_mode
))
1222 g_string_sprintf(message
, "?Change permissions of '%s'?", path
);
1223 if (!reply(from_parent
, FALSE
))
1228 g_string_sprintf(message
, "'Changing permissions of '%s'\n",
1235 if (new_entry_string
)
1238 mode_free(mode_change
);
1239 mode_change
= mode_compile(new_entry_string
,
1241 g_free(new_entry_string
);
1242 new_entry_string
= NULL
;
1248 g_string_assign(message
,
1249 "!Invalid mode command - change it and try again\n");
1251 g_string_sprintf(message
, "?Change permissions of '%s'?", path
);
1252 if (!reply(from_parent
, TRUE
))
1256 if (lstat(path
, &info
))
1261 if (S_ISLNK(info
.st_mode
))
1264 new_mode
= mode_adjust(info
.st_mode
, mode_change
);
1265 if (chmod(path
, new_mode
))
1271 if (o_recurse
&& S_ISDIR(info
.st_mode
))
1274 safe_path
= g_strdup(path
);
1275 for_dir_contents(do_chmod
, safe_path
, safe_path
);
1282 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1283 * is set then that is the new leafname, otherwise the leafname stays
1286 static char *make_dest_path(char *object
, char *dir
)
1294 leaf
= strrchr(object
, '/');
1296 leaf
= object
; /* Error? */
1301 return make_path(dir
, leaf
)->str
;
1304 /* If action_leaf is not NULL it specifies the new leaf name
1306 static gboolean
do_copy2(char *path
, char *dest
)
1310 struct stat dest_info
;
1311 gboolean retval
= TRUE
;
1315 dest_path
= make_dest_path(path
, dest
);
1317 if (lstat(path
, &info
))
1323 if (lstat(dest_path
, &dest_info
) == 0)
1328 merge
= S_ISDIR(info
.st_mode
) && S_ISDIR(dest_info
.st_mode
);
1330 g_string_sprintf(message
, "?'%s' already exists - %s?",
1332 merge
? "merge contents" : "overwrite");
1334 if (!reply(from_parent
, TRUE
))
1339 if (S_ISDIR(dest_info
.st_mode
))
1340 err
= rmdir(dest_path
);
1342 err
= unlink(dest_path
);
1347 if (errno
!= ENOENT
)
1349 g_string_sprintf(message
,
1350 "'Trying copy anyway...\n");
1357 g_string_sprintf(message
, "?Copy %s as %s?", path
, dest_path
);
1358 if (!reply(from_parent
, FALSE
))
1363 g_string_sprintf(message
, "'Copying %s as %s\n", path
,
1368 if (S_ISDIR(info
.st_mode
))
1370 char *safe_path
, *safe_dest
;
1371 struct stat dest_info
;
1374 /* (we will do the update ourselves now, rather than
1379 safe_path
= g_strdup(path
);
1380 safe_dest
= g_strdup(dest_path
);
1382 exists
= !lstat(dest_path
, &dest_info
);
1384 if (exists
&& !S_ISDIR(dest_info
.st_mode
))
1386 g_string_sprintf(message
,
1387 "!ERROR: Destination already exists, "
1388 "but is not a directory\n");
1390 else if (exists
== FALSE
&& mkdir(dest_path
, info
.st_mode
))
1396 /* (just been created then) */
1397 g_string_sprintf(message
, "+%s", dest
);
1402 for_dir_contents(do_copy2
, safe_path
, safe_dest
);
1403 /* Note: dest_path now invalid... */
1409 else if (S_ISLNK(info
.st_mode
))
1411 char target
[MAXPATHLEN
+ 1];
1414 /* Not all versions of cp(1) can make symlinks,
1415 * so we special-case it.
1418 count
= readlink(path
, target
, sizeof(target
) - 1);
1426 target
[count
] = '\0';
1427 if (symlink(target
, dest_path
))
1436 char *argv
[] = {"cp", "-pRf", NULL
, NULL
, NULL
};
1439 argv
[3] = dest_path
;
1441 if (fork_exec_wait(argv
))
1443 g_string_sprintf(message
, "!ERROR: Copy failed\n");
1452 /* Copy path to dest.
1453 * Check that path not copied into itself.
1455 static gboolean
do_copy(char *path
, char *dest
)
1457 if (is_sub_dir(make_dest_path(path
, dest
), path
))
1459 g_string_sprintf(message
,
1460 "!ERROR: Can't copy directory into itself\n");
1464 return do_copy2(path
, dest
);
1467 /* Move path to dest.
1468 * Check that path not moved into itself.
1470 static gboolean
do_move(char *path
, char *dest
)
1474 gboolean retval
= TRUE
;
1475 char *argv
[] = {"mv", "-f", NULL
, NULL
, NULL
};
1479 if (is_sub_dir(dest
, path
))
1481 g_string_sprintf(message
,
1482 "!ERROR: Can't move directory into itself\n");
1489 leaf
= strrchr(path
, '/');
1491 leaf
= path
; /* Error? */
1495 dest_path
= make_path(dest
, leaf
)->str
;
1497 is_dir
= lstat(path
, &info2
) == 0 && S_ISDIR(info2
.st_mode
);
1499 if (access(dest_path
, F_OK
) == 0)
1504 g_string_sprintf(message
, "?'%s' already exists - overwrite?",
1506 if (!reply(from_parent
, TRUE
))
1509 if (lstat(dest_path
, &info
))
1515 if (S_ISDIR(info
.st_mode
))
1516 err
= rmdir(dest_path
);
1518 err
= unlink(dest_path
);
1523 if (errno
!= ENOENT
)
1525 g_string_sprintf(message
,
1526 "'Trying move anyway...\n");
1532 g_string_sprintf(message
, "?Move %s as %s?", path
, dest_path
);
1533 if (!reply(from_parent
, FALSE
))
1538 g_string_sprintf(message
, "'Moving %s as %s\n", path
,
1546 if (fork_exec_wait(argv
) == 0)
1548 g_string_sprintf(message
, "+%s", path
);
1549 g_string_truncate(message
, leaf
- path
);
1552 g_string_sprintf(message
, "m%s", path
);
1558 g_string_sprintf(message
, "!ERROR: Failed to move %s as %s\n",
1567 static gboolean
do_link(char *path
, char *dest
)
1574 leaf
= strrchr(path
, '/');
1576 leaf
= path
; /* Error? */
1580 dest_path
= make_path(dest
, leaf
)->str
;
1584 g_string_sprintf(message
, "'Linking %s as %s\n", path
,
1590 g_string_sprintf(message
, "?Link %s as %s?", path
, dest_path
);
1591 if (!reply(from_parent
, FALSE
))
1595 if (symlink(path
, dest_path
))
1604 /* Mount/umount this item */
1605 static void do_mount(FilerWindow
*filer_window
, DirItem
*item
)
1607 char *argv
[3] = {NULL
, NULL
, NULL
};
1612 argv
[0] = item
->flags
& ITEM_FLAG_MOUNTED
? "umount" : "mount";
1613 action
= item
->flags
& ITEM_FLAG_MOUNTED
? "Unmount" : "Mount";
1614 argv
[1] = make_path(filer_window
->path
, item
->leafname
)->str
;
1618 g_string_sprintf(message
, "'%sing %s\n", action
, argv
[1]);
1623 g_string_sprintf(message
, "?%s %s?", action
, argv
[1]);
1624 if (!reply(from_parent
, FALSE
))
1628 if (fork_exec_wait(argv
) == 0)
1630 g_string_sprintf(message
, "+%s", filer_window
->path
);
1632 g_string_sprintf(message
, "m%s", argv
[1]);
1637 g_string_sprintf(message
, "!ERROR: %s failed\n", action
);
1642 /* CHILD MAIN LOOPS */
1644 /* After forking, the child calls one of these functions */
1646 static void usage_cb(gpointer data
)
1648 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1649 Collection
*collection
= filer_window
->collection
;
1651 int left
= collection
->number_selected
;
1653 off_t total_size
= 0;
1655 send_dir(filer_window
->path
);
1660 if (!collection
->items
[i
].selected
)
1662 item
= (DirItem
*) collection
->items
[i
].data
;
1664 do_usage(make_path(filer_window
->path
,
1665 item
->leafname
)->str
,
1666 filer_window
->path
);
1667 g_string_sprintf(message
, "'%s: %s\n",
1669 format_size((unsigned long) size_tally
));
1671 total_size
+= size_tally
;
1675 g_string_sprintf(message
, "'\nTotal: %s\n",
1676 format_size((unsigned long) total_size
));
1680 #ifdef DO_MOUNT_POINTS
1681 static void mount_cb(gpointer data
)
1683 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1684 Collection
*collection
= filer_window
->collection
;
1687 gboolean mount_points
= FALSE
;
1689 send_dir(filer_window
->path
);
1692 do_mount(filer_window
, mount_item
);
1695 for (i
= 0; i
< collection
->number_of_items
; i
++)
1697 if (!collection
->items
[i
].selected
)
1699 item
= (DirItem
*) collection
->items
[i
].data
;
1700 if (!(item
->flags
& ITEM_FLAG_MOUNT_POINT
))
1702 mount_points
= TRUE
;
1704 do_mount(filer_window
, item
);
1709 g_string_sprintf(message
,
1710 "!No mount points selected!\n");
1715 g_string_sprintf(message
, "'\nDone\n");
1720 static void delete_cb(gpointer data
)
1722 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1723 Collection
*collection
= filer_window
->collection
;
1725 int left
= collection
->number_selected
;
1728 send_dir(filer_window
->path
);
1733 if (!collection
->items
[i
].selected
)
1735 item
= (DirItem
*) collection
->items
[i
].data
;
1736 if (do_delete(make_path(filer_window
->path
,
1737 item
->leafname
)->str
,
1738 filer_window
->path
))
1740 g_string_sprintf(message
, "+%s", filer_window
->path
);
1746 g_string_sprintf(message
, "'\nDone\n");
1750 static void find_cb(gpointer data
)
1752 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1753 Collection
*collection
= filer_window
->collection
;
1755 int left
= collection
->number_selected
;
1758 send_dir(filer_window
->path
);
1763 if (!collection
->items
[i
].selected
)
1765 item
= (DirItem
*) collection
->items
[i
].data
;
1766 do_find(make_path(filer_window
->path
,
1767 item
->leafname
)->str
,
1772 g_string_sprintf(message
, "'\nDone\n");
1776 static void chmod_cb(gpointer data
)
1778 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1779 Collection
*collection
= filer_window
->collection
;
1781 int left
= collection
->number_selected
;
1784 send_dir(filer_window
->path
);
1789 if (!collection
->items
[i
].selected
)
1791 item
= (DirItem
*) collection
->items
[i
].data
;
1792 if (item
->flags
& ITEM_FLAG_SYMLINK
)
1794 g_string_sprintf(message
, "!'%s' is a symbolic link\n",
1798 else if (do_chmod(make_path(filer_window
->path
,
1799 item
->leafname
)->str
, NULL
))
1801 g_string_sprintf(message
, "+%s", filer_window
->path
);
1807 g_string_sprintf(message
, "'\nDone\n");
1811 static void list_cb(gpointer data
)
1813 GSList
*paths
= (GSList
*) data
;
1817 send_dir((char *) paths
->data
);
1819 if (action_do_func((char *) paths
->data
, action_dest
))
1821 g_string_sprintf(message
, "+%s", action_dest
);
1825 paths
= paths
->next
;
1828 g_string_sprintf(message
, "'\nDone\n");
1832 static void add_toggle(GUIside
*gui_side
, guchar
*label
, guchar
*code
)
1836 check
= gtk_check_button_new_with_label(label
);
1837 gtk_object_set_data(GTK_OBJECT(check
), "send-code", code
);
1838 gtk_signal_connect(GTK_OBJECT(check
), "clicked",
1839 button_reply
, gui_side
);
1840 gtk_box_pack_start(GTK_BOX(gui_side
->vbox
), check
, FALSE
, TRUE
, 0);
1844 /* EXTERNAL INTERFACE */
1846 void action_find(FilerWindow
*filer_window
)
1849 Collection
*collection
;
1850 GtkWidget
*hbox
, *label
, *button
, *scroller
;
1851 gchar
*titles
[] = {"Name", "Directory"};
1853 collection
= filer_window
->collection
;
1855 if (collection
->number_selected
< 1)
1857 report_error("ROX-Filer", "You need to select some items "
1858 "to search through");
1862 if (!last_find_string
)
1863 last_find_string
= g_strdup("'core'");
1865 new_entry_string
= last_find_string
;
1866 gui_side
= start_action(filer_window
, find_cb
, FALSE
);
1870 gui_side
->show_info
= TRUE
;
1872 scroller
= gtk_scrolled_window_new(NULL
, NULL
);
1873 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller
),
1874 GTK_POLICY_NEVER
, GTK_POLICY_ALWAYS
);
1875 gtk_box_pack_start(GTK_BOX(gui_side
->vbox
), scroller
, TRUE
, TRUE
, 4);
1876 gui_side
->results
= gtk_clist_new_with_titles(
1877 sizeof(titles
) / sizeof(*titles
), titles
);
1878 gtk_widget_set_usize(gui_side
->results
, 100, 100);
1879 gtk_clist_set_column_width(GTK_CLIST(gui_side
->results
), 0, 100);
1880 gtk_clist_set_selection_mode(GTK_CLIST(gui_side
->results
),
1881 GTK_SELECTION_SINGLE
);
1882 gtk_container_add(GTK_CONTAINER(scroller
), gui_side
->results
);
1883 gtk_box_set_child_packing(GTK_BOX(gui_side
->vbox
),
1884 gui_side
->log_hbox
, FALSE
, TRUE
, 4, GTK_PACK_START
);
1885 gtk_signal_connect(GTK_OBJECT(gui_side
->results
), "select_row",
1886 GTK_SIGNAL_FUNC(select_row_callback
), gui_side
);
1888 hbox
= gtk_hbox_new(FALSE
, 0);
1889 label
= gtk_label_new("Expression:");
1890 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, TRUE
, 4);
1891 gui_side
->default_string
= &last_find_string
;
1892 gui_side
->entry
= gtk_entry_new();
1893 gtk_widget_set_style(gui_side
->entry
, fixed_style
);
1894 gtk_entry_set_text(GTK_ENTRY(gui_side
->entry
), last_find_string
);
1895 gtk_editable_select_region(GTK_EDITABLE(gui_side
->entry
), 0, -1);
1896 gtk_widget_set_sensitive(gui_side
->entry
, FALSE
);
1897 gtk_box_pack_start(GTK_BOX(hbox
), gui_side
->entry
, TRUE
, TRUE
, 4);
1898 gtk_signal_connect(GTK_OBJECT(gui_side
->entry
), "changed",
1899 entry_changed
, gui_side
);
1900 gtk_box_pack_start(GTK_BOX(gui_side
->vbox
), hbox
, FALSE
, TRUE
, 4);
1901 button
= gtk_button_new_with_label("Show expression reference");
1902 gtk_box_pack_start(GTK_BOX(hbox
), button
, FALSE
, TRUE
, 4);
1903 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
1904 show_condition_help
, NULL
);
1906 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Find");
1907 gtk_window_set_focus(GTK_WINDOW(gui_side
->window
), gui_side
->entry
);
1908 gtk_signal_connect_object(GTK_OBJECT(gui_side
->entry
), "activate",
1909 gtk_button_clicked
, GTK_OBJECT(gui_side
->quiet
));
1910 number_of_windows
++;
1911 gtk_widget_show_all(gui_side
->window
);
1914 /* Count disk space used by selected items */
1915 void action_usage(FilerWindow
*filer_window
)
1918 Collection
*collection
;
1920 collection
= filer_window
->collection
;
1922 if (collection
->number_selected
< 1)
1924 report_error("ROX-Filer", "You need to select some items "
1929 gui_side
= start_action(filer_window
, usage_cb
, TRUE
);
1933 gui_side
->show_info
= TRUE
;
1935 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Disk Usage");
1936 number_of_windows
++;
1937 gtk_widget_show_all(gui_side
->window
);
1940 /* Mount/unmount 'item', or all selected mount points if NULL. */
1941 void action_mount(FilerWindow
*filer_window
, DirItem
*item
)
1943 #ifdef DO_MOUNT_POINTS
1945 Collection
*collection
;
1947 collection
= filer_window
->collection
;
1949 if (item
== NULL
&& collection
->number_selected
< 1)
1951 report_error("ROX-Filer", "You need to select some items "
1952 "to mount or unmount");
1957 gui_side
= start_action(filer_window
, mount_cb
, o_auto_mount
);
1961 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Mount / Unmount");
1962 number_of_windows
++;
1963 gtk_widget_show_all(gui_side
->window
);
1965 report_error("ROX-Filer",
1966 "ROX-Filer does not yet support mount points on your "
1968 #endif /* DO_MOUNT_POINTS */
1971 /* Deletes all selected items in the window */
1972 void action_delete(FilerWindow
*filer_window
)
1975 Collection
*collection
;
1977 collection
= filer_window
->collection
;
1979 if (collection
->number_selected
< 1)
1981 report_error("ROX-Filer", "You need to select some items "
1986 gui_side
= start_action(filer_window
, delete_cb
, o_auto_delete
);
1990 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Delete");
1991 add_toggle(gui_side
,
1992 "Force - don't confirm deletion of non-writeable items", "F");
1993 add_toggle(gui_side
,
1994 "Brief - only log directories being deleted", "B");
1996 number_of_windows
++;
1997 gtk_widget_show_all(gui_side
->window
);
2000 /* Change the permissions of the selected items */
2001 void action_chmod(FilerWindow
*filer_window
)
2004 Collection
*collection
;
2005 GtkWidget
*hbox
, *label
, *button
;
2007 collection
= filer_window
->collection
;
2009 if (collection
->number_selected
< 1)
2011 report_error("ROX-Filer", "You need to select the items "
2012 "whose permissions you want to change");
2016 if (!last_chmod_string
)
2017 last_chmod_string
= g_strdup("a+x");
2018 new_entry_string
= last_chmod_string
;
2019 gui_side
= start_action(filer_window
, chmod_cb
, FALSE
);
2023 add_toggle(gui_side
,
2024 "Brief - don't list processed files", "B");
2025 add_toggle(gui_side
,
2026 "Recurse - also change contents of subdirectories", "R");
2027 hbox
= gtk_hbox_new(FALSE
, 0);
2028 label
= gtk_label_new("Command:");
2029 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, TRUE
, 4);
2030 gui_side
->default_string
= &last_chmod_string
;
2031 gui_side
->entry
= gtk_entry_new();
2032 gtk_entry_set_text(GTK_ENTRY(gui_side
->entry
), last_chmod_string
);
2033 gtk_editable_select_region(GTK_EDITABLE(gui_side
->entry
), 0, -1);
2034 gtk_widget_set_sensitive(gui_side
->entry
, FALSE
);
2035 gtk_box_pack_start(GTK_BOX(hbox
), gui_side
->entry
, TRUE
, TRUE
, 4);
2036 gtk_signal_connect(GTK_OBJECT(gui_side
->entry
), "changed",
2037 entry_changed
, gui_side
);
2038 gtk_box_pack_start(GTK_BOX(gui_side
->vbox
), hbox
, FALSE
, TRUE
, 0);
2039 button
= gtk_button_new_with_label("Show command reference");
2040 gtk_box_pack_start(GTK_BOX(hbox
), button
, FALSE
, TRUE
, 4);
2041 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
2042 show_chmod_help
, NULL
);
2044 gtk_window_set_focus(GTK_WINDOW(gui_side
->window
), gui_side
->entry
);
2045 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Permissions");
2046 gtk_signal_connect_object(GTK_OBJECT(gui_side
->entry
), "activate",
2047 gtk_button_clicked
, GTK_OBJECT(gui_side
->yes
));
2049 number_of_windows
++;
2050 gtk_widget_show_all(gui_side
->window
);
2053 void action_copy(GSList
*paths
, char *dest
, char *leaf
)
2059 action_do_func
= do_copy
;
2060 gui_side
= start_action(paths
, list_cb
, o_auto_copy
);
2064 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Copy");
2065 number_of_windows
++;
2066 gtk_widget_show_all(gui_side
->window
);
2069 void action_move(GSList
*paths
, char *dest
)
2074 action_do_func
= do_move
;
2075 gui_side
= start_action(paths
, list_cb
, o_auto_move
);
2079 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Move");
2080 number_of_windows
++;
2081 gtk_widget_show_all(gui_side
->window
);
2084 void action_link(GSList
*paths
, char *dest
)
2089 action_do_func
= do_link
;
2090 gui_side
= start_action(paths
, list_cb
, o_auto_link
);
2094 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), "Link");
2095 number_of_windows
++;
2096 gtk_widget_show_all(gui_side
->window
);
2099 void action_init(void)
2101 options_sections
= g_slist_prepend(options_sections
, &options
);
2102 option_register("action_auto_quiet", action_auto_quiet
);