4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2000, Thomas Leonard, <tal197@users.sourceforge.net>.
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>
41 #include "gui_support.h"
45 #include "modechange.h"
50 /* Parent->Child messages are one character each:
52 * Q/Y/N Quiet/Yes/No button clicked
53 * F Force deletion of non-writeable items
56 #define SENSITIVE_YESNO(gui_side, state) \
58 gtk_widget_set_sensitive((gui_side)->yes, state); \
59 gtk_widget_set_sensitive((gui_side)->no, state); \
60 if ((gui_side)->entry) \
61 gtk_widget_set_sensitive((gui_side)->entry, state);\
64 typedef struct _GUIside GUIside
;
65 typedef void ActionChild(gpointer data
);
66 typedef gboolean
ForDirCB(char *path
, char *dest_path
);
70 int from_child
; /* File descriptor */
72 int input_tag
; /* gdk_input_add() */
73 GtkWidget
*vbox
, *log
, *window
, *dir
, *log_hbox
;
74 GtkWidget
*quiet
, *yes
, *no
;
75 int child
; /* Process ID */
77 gboolean show_info
; /* For Disk Usage */
79 GtkWidget
*entry
; /* May be NULL */
80 guchar
**default_string
; /* Changed when the entry changes */
82 char *next_dir
; /* NULL => no timer active */
90 /* These don't need to be in a structure because we fork() before
93 static int from_parent
= 0;
94 static FILE *to_parent
= NULL
;
95 static gboolean quiet
= FALSE
;
96 static GString
*message
= NULL
;
97 static char *action_dest
= NULL
;
98 static char *action_leaf
= NULL
;
99 static gboolean (*action_do_func
)(char *source
, char *dest
);
100 static size_t size_tally
; /* For Disk Usage */
102 static struct mode_change
*mode_change
= NULL
; /* For Permissions */
103 static FindCondition
*find_condition
= NULL
; /* For Find */
105 static gboolean o_force
= FALSE
;
106 static gboolean o_brief
= FALSE
;
107 static gboolean o_recurse
= FALSE
;
109 /* Whenever the text in these boxes is changed we store a copy of the new
110 * string to be used as the default next time.
112 static guchar
*last_chmod_string
= NULL
;
113 static guchar
*last_find_string
= NULL
;
115 /* Set to one of the above before forking. This may change over a call to
116 * reply(). It is reset to NULL once the text is parsed.
118 static guchar
*new_entry_string
= NULL
;
120 /* Static prototypes */
121 static gboolean
send();
122 static gboolean
send_error();
123 static gboolean
send_dir(char *dir
);
124 static gboolean
read_exact(int source
, char *buffer
, ssize_t len
);
125 static void do_mount(guchar
*path
, gboolean mount
);
126 static void add_toggle(GUIside
*gui_side
, guchar
*label
, guchar
*code
);
127 static gboolean
reply(int fd
, gboolean ignore_quiet
);
128 static gboolean
remove_pinned_ok(FilerWindow
*filer_window
);
132 static void preview_closed(GtkWidget
*window
, GUIside
*gui_side
)
134 gui_side
->preview
= NULL
;
137 static void select_row_callback(GtkWidget
*widget
,
138 gint row
, gint column
,
139 GdkEventButton
*event
,
144 gtk_clist_get_text(GTK_CLIST(gui_side
->results
), row
, 0, &leaf
);
145 gtk_clist_get_text(GTK_CLIST(gui_side
->results
), row
, 1, &dir
);
147 gtk_clist_unselect_row(GTK_CLIST(gui_side
->results
), row
, column
);
149 if (gui_side
->preview
)
151 if (strcmp(gui_side
->preview
->path
, dir
) == 0)
152 display_set_autoselect(gui_side
->preview
, leaf
);
154 filer_change_to(gui_side
->preview
, dir
, leaf
);
158 gui_side
->preview
= filer_opendir(dir
);
159 if (gui_side
->preview
)
161 display_set_autoselect(gui_side
->preview
, leaf
);
162 gtk_signal_connect(GTK_OBJECT(gui_side
->preview
->window
),
164 GTK_SIGNAL_FUNC(preview_closed
), gui_side
);
169 /* This is called whenever the user edits the entry box (if any) - send the
172 static void entry_changed(GtkEntry
*entry
, GUIside
*gui_side
)
176 g_return_if_fail(gui_side
->default_string
!= NULL
);
178 text
= gtk_entry_get_text(entry
);
180 g_free(*(gui_side
->default_string
));
181 *(gui_side
->default_string
) = g_strdup(text
);
183 if (!gui_side
->to_child
)
186 fputc('E', gui_side
->to_child
);
187 fputs(text
, gui_side
->to_child
);
188 fputc('\n', gui_side
->to_child
);
189 fflush(gui_side
->to_child
);
192 void show_condition_help(gpointer data
)
194 static GtkWidget
*help
= NULL
;
198 GtkWidget
*text
, *vbox
, *button
, *hbox
, *frame
;
200 help
= gtk_window_new(GTK_WINDOW_DIALOG
);
201 gtk_container_set_border_width(GTK_CONTAINER(help
), 10);
202 gtk_window_set_title(GTK_WINDOW(help
),
203 _("Find expression reference"));
205 vbox
= gtk_vbox_new(FALSE
, 0);
206 gtk_container_add(GTK_CONTAINER(help
), vbox
);
208 frame
= gtk_frame_new(_("Quick Start"));
209 text
= gtk_label_new(
210 _("Just put the name of the file you're looking for in single quotes:\n"
211 "'index.html' (to find a file called 'index.html')"));
212 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
213 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
214 gtk_container_add(GTK_CONTAINER(frame
), text
);
215 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
217 frame
= gtk_frame_new(_("Examples"));
218 text
= gtk_label_new(
219 _("'*.htm', '*.html' (finds HTML files)\n"
220 "IsDir 'lib' (finds directories called 'lib')\n"
221 "IsReg 'core' (finds a regular file called 'core')\n"
222 "! (IsDir, IsReg) (is neither a directory nor a regular file)\n"
223 "mtime after 1 day ago and size > 1Mb (big, and recently modified)\n"
224 "'CVS' prune, isreg (a regular file not in CVS)\n"
225 "IsReg system(grep -q fred \"%\") (contains the word 'fred')"));
226 gtk_widget_set_style(text
, fixed_style
);
227 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
228 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
229 gtk_container_add(GTK_CONTAINER(frame
), text
);
230 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
232 frame
= gtk_frame_new(_("Simple Tests"));
233 text
= gtk_label_new(
234 _("IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket (types)\n"
235 "IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable (permissions)"
239 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
240 "contains a slash then the match is against the full path; otherwise it is \n"
241 "against the leafname only."));
242 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
243 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
244 gtk_container_add(GTK_CONTAINER(frame
), text
);
245 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
247 frame
= gtk_frame_new(_("Comparisons"));
248 text
= gtk_label_new(
249 _("<, <=, =, !=, >, >=, After, Before (compare two values)\n"
250 "5 bytes, 1Kb, 2Mb, 3Gb (file sizes)\n"
251 "2 secs|mins|hours|days|weeks|years ago|hence (times)\n"
252 "atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks (values)"));
253 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
254 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
255 gtk_container_add(GTK_CONTAINER(frame
), text
);
256 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
258 frame
= gtk_frame_new(_("Specials"));
259 text
= gtk_label_new(
260 _("system(command) (true if 'command' returns with a zero exit status; a % \n"
261 "in 'command' is replaced with the path of the current file)\n"
262 "prune (false, and prevents searching the contents of a directory).")
264 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
265 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
266 gtk_container_add(GTK_CONTAINER(frame
), text
);
267 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
269 hbox
= gtk_hbox_new(FALSE
, 20);
270 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, TRUE
, TRUE
, 0);
272 text
= gtk_label_new(
273 _("See the ROX-Filer manual for full details."));
274 gtk_box_pack_start(GTK_BOX(hbox
), text
, TRUE
, TRUE
, 0);
275 button
= gtk_button_new_with_label(_("Close"));
276 gtk_box_pack_start(GTK_BOX(hbox
), button
, FALSE
, TRUE
, 0);
277 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
278 gtk_widget_hide
, GTK_OBJECT(help
));
280 gtk_signal_connect_object(GTK_OBJECT(help
), "delete_event",
281 gtk_widget_hide
, GTK_OBJECT(help
));
284 if (GTK_WIDGET_VISIBLE(help
))
285 gtk_widget_hide(help
);
286 gtk_widget_show_all(help
);
289 static void show_chmod_help(gpointer data
)
291 static GtkWidget
*help
= NULL
;
295 GtkWidget
*text
, *vbox
, *button
, *hbox
, *sep
;
297 help
= gtk_window_new(GTK_WINDOW_DIALOG
);
298 gtk_container_set_border_width(GTK_CONTAINER(help
), 10);
299 gtk_window_set_title(GTK_WINDOW(help
),
300 _("Permissions command reference"));
302 vbox
= gtk_vbox_new(FALSE
, 0);
303 gtk_container_add(GTK_CONTAINER(help
), vbox
);
305 text
= gtk_label_new(
306 _("Normally, you can just select a command from the menu (click \n"
307 "on the arrow beside the command box). Sometimes, you need more...\n"
309 "The format of a command is:\n"
310 "CHANGE, CHANGE, ...\n"
312 "WHO HOW PERMISSIONS\n"
313 "WHO is some combination of u, g and o which determines whether to\n"
314 "change the permissions for the User (owner), Group or Others.\n"
315 "HOW is +, - or = to add, remove or set exactly the permissions.\n"
316 "PERMISSIONS is some combination of the letters 'rwxXstugo'\n\n"
318 "Bracketed text and spaces are ignored.\n\n"
321 "u+rw (the file owner gains read and write permission)\n"
322 "g=u (the group permissions are set to be the same as the user's)\n"
323 "o=u-w (others get the same permissions as the owner, but without "
324 "write permission)\n"
325 "a+x (everyone gets execute/access permission - same as 'ugo+x')\n"
326 "a+X (directories become accessable by everyone; files which were\n"
327 "executable by anyone become executable by everyone)\n"
328 "u+rw, go+r (two commands at once!)\n"
329 "u+s (set the SetUID bit - often has no effect on script files)\n"
330 "755 (set the permissions directly)\n"
332 "\nSee the chmod(1) man page for full details."));
333 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
334 gtk_box_pack_start(GTK_BOX(vbox
), text
, TRUE
, TRUE
, 0);
336 hbox
= gtk_hbox_new(FALSE
, 20);
337 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, TRUE
, 0);
339 sep
= gtk_hseparator_new();
340 gtk_box_pack_start(GTK_BOX(hbox
), sep
, TRUE
, TRUE
, 0);
341 button
= gtk_button_new_with_label(_("Close"));
342 gtk_box_pack_start(GTK_BOX(hbox
), button
, FALSE
, TRUE
, 0);
343 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
344 gtk_widget_hide
, GTK_OBJECT(help
));
346 gtk_signal_connect_object(GTK_OBJECT(help
), "delete_event",
347 gtk_widget_hide
, GTK_OBJECT(help
));
350 if (GTK_WIDGET_VISIBLE(help
))
351 gtk_widget_hide(help
);
352 gtk_widget_show_all(help
);
355 static gboolean
display_dir(gpointer data
)
357 GUIside
*gui_side
= (GUIside
*) data
;
359 gtk_label_set_text(GTK_LABEL(gui_side
->dir
), gui_side
->next_dir
+ 1);
360 g_free(gui_side
->next_dir
);
361 gui_side
->next_dir
= NULL
;
366 static void add_to_results(GUIside
*gui_side
, gchar
*path
)
368 gchar
*row
[] = {"Leaf", "Dir"};
372 slash
= strrchr(path
, '/');
373 g_return_if_fail(slash
!= NULL
);
376 row
[1] = g_strndup(path
, MAX(len
, 1));
379 gtk_clist_append(GTK_CLIST(gui_side
->results
), row
);
384 /* Called when the child sends us a message */
385 static void message_from_child(gpointer data
,
387 GdkInputCondition condition
)
390 GUIside
*gui_side
= (GUIside
*) data
;
391 GtkWidget
*log
= gui_side
->log
;
393 if (read_exact(source
, buf
, 4))
399 message_len
= strtol(buf
, NULL
, 16);
400 buffer
= g_malloc(message_len
+ 1);
401 if (message_len
> 0 && read_exact(source
, buffer
, message_len
))
403 buffer
[message_len
] = '\0';
406 SENSITIVE_YESNO(gui_side
, TRUE
);
407 gtk_window_set_focus(
408 GTK_WINDOW(gui_side
->window
),
409 gui_side
->entry
? gui_side
->entry
412 else if (*buffer
== '+')
414 refresh_dirs(buffer
+ 1);
418 else if (*buffer
== '=')
420 add_to_results(gui_side
, buffer
+ 1);
424 else if (*buffer
== '#')
426 gtk_clist_clear(GTK_CLIST(gui_side
->results
));
431 else if (*buffer
== 'm' || *buffer
== 'M')
435 filer_check_mounted(buffer
+ 1);
439 else if (*buffer
== '/')
441 if (gui_side
->next_dir
)
442 g_free(gui_side
->next_dir
);
444 gui_side
->next_timer
=
448 gui_side
->next_dir
= buffer
;
451 else if (*buffer
== '!')
454 gtk_text_insert(GTK_TEXT(log
),
456 *buffer
== '!' ? &red
: NULL
,
458 buffer
+ 1, message_len
- 1);
462 g_printerr("Child died in the middle of a message.\n");
465 /* The child is dead */
468 fclose(gui_side
->to_child
);
469 gui_side
->to_child
= NULL
;
470 close(gui_side
->from_child
);
471 gdk_input_remove(gui_side
->input_tag
);
472 gtk_widget_set_sensitive(gui_side
->quiet
, FALSE
);
474 if (gui_side
->errors
)
478 if (gui_side
->errors
== 1)
479 report
= g_strdup(_("There was one error.\n"));
481 report
= g_strdup_printf(_("There were %d errors.\n"),
484 gtk_text_insert(GTK_TEXT(log
), NULL
, &red
, NULL
,
489 else if (gui_side
->show_info
== FALSE
)
490 gtk_widget_destroy(gui_side
->window
);
493 /* Scans src_dir, updating dest_path whenever cb returns TRUE */
494 static void for_dir_contents(ForDirCB
*cb
, char *src_dir
, char *dest_path
)
498 GSList
*list
= NULL
, *next
;
500 d
= mc_opendir(src_dir
);
503 /* Message displayed is "ERROR reading 'path': message" */
504 g_string_sprintf(message
, "!%s '%s': %s\n",
506 src_dir
, g_strerror(errno
));
513 while ((ent
= mc_readdir(d
)))
515 if (ent
->d_name
[0] == '.' && (ent
->d_name
[1] == '\0'
516 || (ent
->d_name
[1] == '.' && ent
->d_name
[2] == '\0')))
518 list
= g_slist_append(list
, g_strdup(make_path(src_dir
,
530 if (cb((char *) next
->data
, dest_path
))
532 g_string_sprintf(message
, "+%s", dest_path
);
543 /* Read this many bytes into the buffer. TRUE on success. */
544 static gboolean
read_exact(int source
, char *buffer
, ssize_t len
)
549 got
= read(source
, buffer
, len
);
558 /* Send 'message' to our parent process. TRUE on success. */
559 static gboolean
send(void)
564 g_return_val_if_fail(message
->len
< 0xffff, FALSE
);
566 sprintf(len_buffer
, "%04x", message
->len
);
567 fwrite(len_buffer
, 1, 4, to_parent
);
568 len
= fwrite(message
->str
, 1, message
->len
, to_parent
);
570 return len
== message
->len
;
573 /* Set the directory indicator at the top of the window */
574 static gboolean
send_dir(char *dir
)
576 g_string_sprintf(message
, "/%s", dir
);
580 static gboolean
send_error(void)
582 g_string_sprintf(message
, "!%s: %s\n", _("ERROR"), g_strerror(errno
));
586 static void button_reply(GtkWidget
*button
, GUIside
*gui_side
)
590 if (!gui_side
->to_child
)
593 text
= gtk_object_get_data(GTK_OBJECT(button
), "send-code");
594 g_return_if_fail(text
!= NULL
);
595 fputc(*text
, gui_side
->to_child
);
596 fflush(gui_side
->to_child
);
598 if (*text
== 'Y' || *text
== 'N' || *text
== 'Q')
599 SENSITIVE_YESNO(gui_side
, FALSE
);
602 static void process_flag(char flag
)
613 o_recurse
= !o_recurse
;
619 g_string_sprintf(message
,
620 "!ERROR: Bad message '%c'\n", flag
);
626 /* If the parent has sent any flag toggles, read them */
627 static void check_flags(void)
638 FD_SET(from_parent
, &set
);
641 got
= select(from_parent
+ 1, &set
, NULL
, NULL
, &tv
);
644 g_error("select() failed: %s\n", g_strerror(errno
));
648 got
= read(from_parent
, &retval
, 1);
650 g_error("read() error: %s\n", g_strerror(errno
));
652 process_flag(retval
);
656 static void read_new_entry_text(void)
662 new = g_string_new(NULL
);
666 len
= read(from_parent
, &c
, 1);
669 fprintf(stderr
, "read() error: %s\n",
671 _exit(1); /* Parent died? */
676 g_string_append_c(new, c
);
679 g_free(new_entry_string
);
680 new_entry_string
= new->str
;
681 g_string_free(new, FALSE
);
684 /* Read until the user sends a reply. If ignore_quiet is TRUE then
685 * the user MUST click Yes or No, else treat quiet on as Yes.
686 * If the user needs prompting then does send().
688 static gboolean
reply(int fd
, gboolean ignore_quiet
)
692 gboolean asked
= FALSE
;
694 while (ignore_quiet
|| !quiet
)
702 len
= read(fd
, &retval
, 1);
705 fprintf(stderr
, "read() error: %s\n",
707 _exit(1); /* Parent died? */
716 g_string_assign(message
, "?");
721 g_string_sprintf(message
, "' %s\n", _("Yes"));
725 g_string_sprintf(message
, "' %s\n", _("No"));
729 read_new_entry_text();
732 process_flag(retval
);
739 g_string_sprintf(message
, "' %s\n", _("Quiet"));
745 static void destroy_action_window(GtkWidget
*widget
, gpointer data
)
747 GUIside
*gui_side
= (GUIside
*) data
;
751 kill(gui_side
->child
, SIGTERM
);
752 fclose(gui_side
->to_child
);
753 close(gui_side
->from_child
);
754 gdk_input_remove(gui_side
->input_tag
);
757 if (gui_side
->next_dir
)
759 gtk_timeout_remove(gui_side
->next_timer
);
760 g_free(gui_side
->next_dir
);
763 if (gui_side
->preview
)
765 gtk_signal_disconnect_by_data(
766 GTK_OBJECT(gui_side
->preview
->window
),
768 gui_side
->preview
= NULL
;
773 if (--number_of_windows
< 1)
777 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
778 * (NULL on failure). The child calls func().
780 * If autoq then automatically selects 'Quiet'.
782 static GUIside
*start_action(gpointer data
, ActionChild
*func
, gboolean autoq
)
784 int filedes
[4]; /* 0 and 2 are for reading */
787 GtkWidget
*vbox
, *button
, *scrollbar
, *actions
;
788 struct sigaction act
;
792 report_error(PROJECT
, g_strerror(errno
));
796 if (pipe(filedes
+ 2))
800 report_error(PROJECT
, g_strerror(errno
));
808 report_error(PROJECT
, g_strerror(errno
));
811 /* We are the child */
815 /* Reset the SIGCHLD handler */
816 act
.sa_handler
= SIG_DFL
;
817 sigemptyset(&act
.sa_mask
);
819 sigaction(SIGCHLD
, &act
, NULL
);
821 message
= g_string_new(NULL
);
824 to_parent
= fdopen(filedes
[1], "wb");
825 from_parent
= filedes
[2];
831 /* We are the parent */
834 gui_side
= g_malloc(sizeof(GUIside
));
835 gui_side
->from_child
= filedes
[0];
836 gui_side
->to_child
= fdopen(filedes
[3], "wb");
837 gui_side
->log
= NULL
;
838 gui_side
->child
= child
;
839 gui_side
->errors
= 0;
840 gui_side
->show_info
= FALSE
;
841 gui_side
->preview
= NULL
;
842 gui_side
->results
= NULL
;
843 gui_side
->entry
= NULL
;
844 gui_side
->default_string
= NULL
;
846 gui_side
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
847 gtk_container_set_border_width(GTK_CONTAINER(gui_side
->window
), 2);
848 gtk_window_set_default_size(GTK_WINDOW(gui_side
->window
), 450, 200);
849 gtk_signal_connect(GTK_OBJECT(gui_side
->window
), "destroy",
850 GTK_SIGNAL_FUNC(destroy_action_window
), gui_side
);
852 gui_side
->vbox
= vbox
= gtk_vbox_new(FALSE
, 0);
853 gtk_container_add(GTK_CONTAINER(gui_side
->window
), vbox
);
855 gui_side
->dir
= gtk_label_new(_("<dir>"));
856 gui_side
->next_dir
= NULL
;
857 gtk_misc_set_alignment(GTK_MISC(gui_side
->dir
), 0.5, 0.5);
858 gtk_box_pack_start(GTK_BOX(vbox
), gui_side
->dir
, FALSE
, TRUE
, 0);
860 gui_side
->log_hbox
= gtk_hbox_new(FALSE
, 0);
861 gtk_box_pack_start(GTK_BOX(vbox
), gui_side
->log_hbox
, TRUE
, TRUE
, 4);
863 gui_side
->log
= gtk_text_new(NULL
, NULL
);
864 gtk_widget_set_usize(gui_side
->log
, 400, 100);
865 gtk_box_pack_start(GTK_BOX(gui_side
->log_hbox
),
866 gui_side
->log
, TRUE
, TRUE
, 0);
867 scrollbar
= gtk_vscrollbar_new(GTK_TEXT(gui_side
->log
)->vadj
);
868 gtk_box_pack_start(GTK_BOX(gui_side
->log_hbox
),
869 scrollbar
, FALSE
, TRUE
, 0);
871 actions
= gtk_hbox_new(TRUE
, 4);
872 gtk_box_pack_start(GTK_BOX(vbox
), actions
, FALSE
, TRUE
, 0);
874 gui_side
->quiet
= button
= gtk_toggle_button_new_with_label(_("Quiet"));
875 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button
), autoq
);
876 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "Q");
877 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
878 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
879 button_reply
, gui_side
);
880 gui_side
->yes
= button
= gtk_button_new_with_label(_("Yes"));
881 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "Y");
882 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
883 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
884 button_reply
, gui_side
);
885 gui_side
->no
= button
= gtk_button_new_with_label(_("No"));
886 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "N");
887 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
888 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
889 button_reply
, gui_side
);
890 SENSITIVE_YESNO(gui_side
, FALSE
);
892 button
= gtk_button_new_with_label(_("Abort"));
893 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
894 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
895 gtk_widget_destroy
, GTK_OBJECT(gui_side
->window
));
897 gui_side
->input_tag
= gdk_input_add(gui_side
->from_child
,
905 /* ACTIONS ON ONE ITEM */
907 /* These may call themselves recursively, or ask questions, etc.
908 * TRUE iff the directory containing dest_path needs to be rescanned.
911 /* dest_path is the dir containing src_path.
912 * Updates the global size_tally.
914 static gboolean
do_usage(char *src_path
, char *dest_path
)
920 if (mc_lstat(src_path
, &info
))
922 g_string_sprintf(message
, "'%s:\n", src_path
);
928 if (S_ISREG(info
.st_mode
) || S_ISLNK(info
.st_mode
))
929 size_tally
+= info
.st_size
;
930 else if (S_ISDIR(info
.st_mode
))
932 g_string_sprintf(message
, _("?Count contents of %s?"),
934 if (reply(from_parent
, FALSE
))
937 safe_path
= g_strdup(src_path
);
938 for_dir_contents(do_usage
, safe_path
, safe_path
);
946 /* dest_path is the dir containing src_path */
947 static gboolean
do_delete(char *src_path
, char *dest_path
)
954 if (mc_lstat(src_path
, &info
))
960 write_prot
= S_ISLNK(info
.st_mode
) ? FALSE
961 : access(src_path
, W_OK
) != 0;
962 if (write_prot
|| !quiet
)
964 g_string_sprintf(message
, _("?Delete %s'%s'?"),
965 write_prot
? _("WRITE-PROTECTED ") : " ",
967 if (!reply(from_parent
, write_prot
&& !o_force
))
972 g_string_sprintf(message
, _("'Deleting '%s'\n"), src_path
);
976 if (S_ISDIR(info
.st_mode
))
979 safe_path
= g_strdup(src_path
);
980 for_dir_contents(do_delete
, safe_path
, safe_path
);
981 if (rmdir(safe_path
))
987 g_string_sprintf(message
, _("'Directory '%s' deleted\n"),
990 g_string_sprintf(message
, "m%s", safe_path
);
994 else if (unlink(src_path
))
1003 /* path is the item to check. If is is a directory then we may recurse
1004 * (unless prune is used).
1006 static gboolean
do_find(char *path
, char *dummy
)
1015 g_string_sprintf(message
, _("?Check '%s'?"), path
);
1016 if (!reply(from_parent
, FALSE
))
1022 if (new_entry_string
)
1025 find_condition_free(find_condition
);
1026 find_condition
= find_compile(new_entry_string
);
1027 g_free(new_entry_string
);
1028 new_entry_string
= NULL
;
1034 g_string_assign(message
, _("!Invalid find condition - "
1035 "change it and try again\n"));
1037 g_string_sprintf(message
, _("?Check '%s'?"), path
);
1038 if (!reply(from_parent
, TRUE
))
1042 if (mc_lstat(path
, &info
.stats
))
1045 g_string_sprintf(message
, _("'(while checking '%s')\n"), path
);
1050 info
.fullpath
= path
;
1051 time(&info
.now
); /* XXX: Not for each check! */
1053 slash
= strrchr(path
, '/');
1054 info
.leaf
= slash
? slash
+ 1 : path
;
1056 if (find_test_condition(find_condition
, &info
))
1058 g_string_sprintf(message
, "=%s", path
);
1062 if (S_ISDIR(info
.stats
.st_mode
) && !info
.prune
)
1065 safe_path
= g_strdup(path
);
1066 for_dir_contents(do_find
, safe_path
, safe_path
);
1073 /* Like mode_compile(), but ignores spaces and bracketed bits */
1074 struct mode_change
*nice_mode_compile(const char *mode_string
,
1075 unsigned int masked_ops
)
1079 struct mode_change
*retval
= NULL
;
1081 new = g_string_new(NULL
);
1083 for (; *mode_string
; mode_string
++)
1085 if (*mode_string
== '(')
1087 if (*mode_string
== ')')
1095 if (brackets
== 0 && *mode_string
!= ' ')
1096 g_string_append_c(new, *mode_string
);
1100 retval
= mode_compile(new->str
, masked_ops
);
1101 g_string_free(new, TRUE
);
1105 static gboolean
do_chmod(char *path
, char *dummy
)
1112 if (mc_lstat(path
, &info
))
1117 if (S_ISLNK(info
.st_mode
))
1122 g_string_sprintf(message
,
1123 _("?Change permissions of '%s'?"), path
);
1124 if (!reply(from_parent
, FALSE
))
1129 g_string_sprintf(message
,
1130 _("'Changing permissions of '%s'\n"),
1137 if (new_entry_string
)
1140 mode_free(mode_change
);
1141 mode_change
= nice_mode_compile(new_entry_string
,
1143 g_free(new_entry_string
);
1144 new_entry_string
= NULL
;
1150 g_string_assign(message
,
1151 _("!Invalid mode command - change it and try again\n"));
1153 g_string_sprintf(message
,
1154 _("?Change permissions of '%s'?"), path
);
1155 if (!reply(from_parent
, TRUE
))
1159 if (mc_lstat(path
, &info
))
1164 if (S_ISLNK(info
.st_mode
))
1167 new_mode
= mode_adjust(info
.st_mode
, mode_change
);
1168 if (chmod(path
, new_mode
))
1174 if (o_recurse
&& S_ISDIR(info
.st_mode
))
1177 safe_path
= g_strdup(path
);
1178 for_dir_contents(do_chmod
, safe_path
, safe_path
);
1185 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1186 * is set then that is the new leafname, otherwise the leafname stays
1189 static char *make_dest_path(char *object
, char *dir
)
1197 leaf
= strrchr(object
, '/');
1199 leaf
= object
; /* Error? */
1204 return make_path(dir
, leaf
)->str
;
1207 /* If action_leaf is not NULL it specifies the new leaf name */
1208 static gboolean
do_copy2(char *path
, char *dest
)
1212 struct stat dest_info
;
1213 gboolean retval
= TRUE
;
1217 dest_path
= make_dest_path(path
, dest
);
1219 if (mc_lstat(path
, &info
))
1225 if (mc_lstat(dest_path
, &dest_info
) == 0)
1230 merge
= S_ISDIR(info
.st_mode
) && S_ISDIR(dest_info
.st_mode
);
1232 g_string_sprintf(message
, _("?'%s' already exists - %s?"),
1234 merge
? _("merge contents") : _("overwrite"));
1236 if (!reply(from_parent
, TRUE
))
1241 if (S_ISDIR(dest_info
.st_mode
))
1242 err
= rmdir(dest_path
);
1244 err
= unlink(dest_path
);
1249 if (errno
!= ENOENT
)
1251 g_string_sprintf(message
,
1252 _("'Trying copy anyway...\n"));
1259 g_string_sprintf(message
,
1260 _("?Copy %s as %s?"), path
, dest_path
);
1261 if (!reply(from_parent
, FALSE
))
1266 g_string_sprintf(message
, _("'Copying %s as %s\n"), path
,
1271 if (S_ISDIR(info
.st_mode
))
1273 char *safe_path
, *safe_dest
;
1274 struct stat dest_info
;
1277 /* (we will do the update ourselves now, rather than
1282 safe_path
= g_strdup(path
);
1283 safe_dest
= g_strdup(dest_path
);
1285 exists
= !mc_lstat(dest_path
, &dest_info
);
1287 if (exists
&& !S_ISDIR(dest_info
.st_mode
))
1289 g_string_sprintf(message
,
1290 _("!ERROR: Destination already exists, "
1291 "but is not a directory\n"));
1293 else if (exists
== FALSE
&& mkdir(dest_path
, info
.st_mode
))
1299 /* (just been created then) */
1300 g_string_sprintf(message
, "+%s", dest
);
1305 for_dir_contents(do_copy2
, safe_path
, safe_dest
);
1306 /* Note: dest_path now invalid... */
1312 else if (S_ISLNK(info
.st_mode
))
1314 char target
[MAXPATHLEN
+ 1];
1317 /* Not all versions of cp(1) can make symlinks,
1318 * so we special-case it.
1321 count
= readlink(path
, target
, sizeof(target
) - 1);
1329 target
[count
] = '\0';
1330 if (symlink(target
, dest_path
))
1341 error
= copy_file(path
, dest_path
);
1345 g_string_sprintf(message
, _("!ERROR: %s\n"), error
);
1354 /* If action_leaf is not NULL it specifies the new leaf name */
1355 static gboolean
do_move2(char *path
, char *dest
)
1358 gboolean retval
= TRUE
;
1359 char *argv
[] = {"mv", "-f", NULL
, NULL
, NULL
};
1365 dest_path
= make_dest_path(path
, dest
);
1367 is_dir
= mc_lstat(path
, &info2
) == 0 && S_ISDIR(info2
.st_mode
);
1369 if (access(dest_path
, F_OK
) == 0)
1374 g_string_sprintf(message
,
1375 _("?'%s' already exists - overwrite?"),
1377 if (!reply(from_parent
, TRUE
))
1380 if (mc_lstat(dest_path
, &info
))
1386 if (S_ISDIR(info
.st_mode
))
1387 err
= rmdir(dest_path
);
1389 err
= unlink(dest_path
);
1394 if (errno
!= ENOENT
)
1396 g_string_sprintf(message
,
1397 _("'Trying move anyway...\n"));
1403 g_string_sprintf(message
,
1404 _("?Move %s as %s?"), path
, dest_path
);
1405 if (!reply(from_parent
, FALSE
))
1410 g_string_sprintf(message
, _("'Moving %s as %s\n"), path
,
1416 argv
[3] = dest_path
;
1418 if (fork_exec_wait(argv
) == 0)
1422 leaf
= strrchr(path
, '/');
1424 leaf
= path
; /* Error? */
1428 g_string_sprintf(message
, "+%s", path
);
1429 g_string_truncate(message
, leaf
- path
+ 1);
1432 g_string_sprintf(message
, "m%s", path
);
1438 g_string_sprintf(message
,
1439 _("!ERROR: Failed to move %s as %s\n"),
1448 /* Copy path to dest.
1449 * Check that path not copied into itself.
1451 static gboolean
do_copy(char *path
, char *dest
)
1453 if (is_sub_dir(make_dest_path(path
, dest
), path
))
1455 g_string_sprintf(message
,
1456 _("!ERROR: Can't copy object into itself\n"));
1460 return do_copy2(path
, dest
);
1463 /* Move path to dest.
1464 * Check that path not moved into itself.
1466 static gboolean
do_move(char *path
, char *dest
)
1468 if (is_sub_dir(make_dest_path(path
, dest
), path
))
1470 g_string_sprintf(message
,
1471 _("!ERROR: Can't move/rename object into itself\n"));
1475 return do_move2(path
, dest
);
1478 static gboolean
do_link(char *path
, char *dest
)
1485 leaf
= strrchr(path
, '/');
1487 leaf
= path
; /* Error? */
1491 dest_path
= make_path(dest
, leaf
)->str
;
1495 g_string_sprintf(message
, _("'Linking %s as %s\n"), path
,
1501 g_string_sprintf(message
,
1502 _("?Link %s as %s?"), path
, dest_path
);
1503 if (!reply(from_parent
, FALSE
))
1507 if (symlink(path
, dest_path
))
1516 /* Mount/umount this item (depending on 'mount') */
1517 static void do_mount(guchar
*path
, gboolean mount
)
1519 char *argv
[3] = {NULL
, NULL
, NULL
};
1523 argv
[0] = mount
? "mount" : "umount";
1528 g_string_sprintf(message
,
1529 mount
? _("'Mounting %s\n")
1530 : _("'Unmounting %s\n"),
1536 g_string_sprintf(message
,
1537 mount
? _("?Mount %s?\n")
1538 : _("?Unmount %s?\n"),
1540 if (!reply(from_parent
, FALSE
))
1544 if (fork_exec_wait(argv
) == 0)
1546 g_string_sprintf(message
, "M%s", path
);
1551 g_string_sprintf(message
, _("!ERROR: Mount failed\n"));
1556 /* CHILD MAIN LOOPS */
1558 /* After forking, the child calls one of these functions */
1560 static void usage_cb(gpointer data
)
1562 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1563 Collection
*collection
= filer_window
->collection
;
1565 int left
= collection
->number_selected
;
1567 off_t total_size
= 0;
1569 send_dir(filer_window
->path
);
1574 if (!collection
->items
[i
].selected
)
1576 item
= (DirItem
*) collection
->items
[i
].data
;
1578 do_usage(make_path(filer_window
->path
,
1579 item
->leafname
)->str
,
1580 filer_window
->path
);
1581 g_string_sprintf(message
, "'%s: %s\n",
1583 format_size((unsigned long) size_tally
));
1585 total_size
+= size_tally
;
1589 g_string_sprintf(message
, _("'\nTotal: %s\n"),
1590 format_size((unsigned long) total_size
));
1594 #ifdef DO_MOUNT_POINTS
1595 static void mount_cb(gpointer data
)
1597 GList
*paths
= (GList
*) data
;
1598 gboolean mount_points
= FALSE
;
1600 for (; paths
; paths
= paths
->next
)
1602 guchar
*path
= (guchar
*) paths
->data
;
1604 if (mount_is_mounted(path
))
1605 do_mount(path
, FALSE
); /* Unmount */
1606 else if (g_hash_table_lookup(fstab_mounts
, path
))
1607 do_mount(path
, TRUE
); /* Mount */
1611 mount_points
= TRUE
;
1614 g_string_sprintf(message
,
1615 mount_points
? _("'\nDone\n")
1616 : _("!No mount points selected!\n"));
1621 static void delete_cb(gpointer data
)
1623 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1624 Collection
*collection
= filer_window
->collection
;
1626 int left
= collection
->number_selected
;
1629 send_dir(filer_window
->path
);
1634 if (!collection
->items
[i
].selected
)
1636 item
= (DirItem
*) collection
->items
[i
].data
;
1637 if (do_delete(make_path(filer_window
->path
,
1638 item
->leafname
)->str
,
1639 filer_window
->path
))
1641 g_string_sprintf(message
, "+%s", filer_window
->path
);
1647 g_string_sprintf(message
, _("'\nDone\n"));
1651 static void find_cb(gpointer data
)
1653 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1654 Collection
*collection
= filer_window
->collection
;
1659 send_dir(filer_window
->path
);
1663 left
= collection
->number_selected
;
1669 if (!collection
->items
[i
].selected
)
1671 item
= (DirItem
*) collection
->items
[i
].data
;
1672 do_find(make_path(filer_window
->path
,
1673 item
->leafname
)->str
,
1678 g_string_assign(message
, _("?Another search?"));
1679 if (!reply(from_parent
, TRUE
))
1681 g_string_assign(message
, "#");
1685 g_string_sprintf(message
, _("'\nDone\n"));
1689 static void chmod_cb(gpointer data
)
1691 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1692 Collection
*collection
= filer_window
->collection
;
1694 int left
= collection
->number_selected
;
1697 send_dir(filer_window
->path
);
1702 if (!collection
->items
[i
].selected
)
1704 item
= (DirItem
*) collection
->items
[i
].data
;
1705 if (item
->flags
& ITEM_FLAG_SYMLINK
)
1707 g_string_sprintf(message
,
1708 _("!'%s' is a symbolic link\n"),
1712 else if (do_chmod(make_path(filer_window
->path
,
1713 item
->leafname
)->str
, NULL
))
1715 g_string_sprintf(message
, "+%s", filer_window
->path
);
1721 g_string_sprintf(message
, _("'\nDone\n"));
1725 static void list_cb(gpointer data
)
1727 GSList
*paths
= (GSList
*) data
;
1731 send_dir((char *) paths
->data
);
1733 if (action_do_func((char *) paths
->data
, action_dest
))
1735 g_string_sprintf(message
, "+%s", action_dest
);
1739 paths
= paths
->next
;
1742 g_string_sprintf(message
, _("'\nDone\n"));
1746 static void add_toggle(GUIside
*gui_side
, guchar
*label
, guchar
*code
)
1750 check
= gtk_check_button_new_with_label(label
);
1751 gtk_object_set_data(GTK_OBJECT(check
), "send-code", code
);
1752 gtk_signal_connect(GTK_OBJECT(check
), "clicked",
1753 button_reply
, gui_side
);
1754 gtk_box_pack_start(GTK_BOX(gui_side
->vbox
), check
, FALSE
, TRUE
, 0);
1758 /* EXTERNAL INTERFACE */
1760 void action_find(FilerWindow
*filer_window
)
1763 Collection
*collection
;
1764 GtkWidget
*hbox
, *label
, *scroller
;
1767 titles
[0] = _("Name");
1768 titles
[1] = _("Directory");
1770 collection
= filer_window
->collection
;
1772 if (collection
->number_selected
< 1)
1774 report_error(PROJECT
, _("You need to select some items "
1775 "to search through"));
1779 if (!last_find_string
)
1780 last_find_string
= g_strdup("'core'");
1782 new_entry_string
= last_find_string
;
1783 gui_side
= start_action(filer_window
, find_cb
, FALSE
);
1787 gui_side
->show_info
= TRUE
;
1789 scroller
= gtk_scrolled_window_new(NULL
, NULL
);
1790 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller
),
1791 GTK_POLICY_NEVER
, GTK_POLICY_ALWAYS
);
1792 gtk_box_pack_start(GTK_BOX(gui_side
->vbox
), scroller
, TRUE
, TRUE
, 4);
1793 gui_side
->results
= gtk_clist_new_with_titles(
1794 sizeof(titles
) / sizeof(*titles
), titles
);
1795 gtk_clist_column_titles_passive(GTK_CLIST(gui_side
->results
));
1796 gtk_widget_set_usize(gui_side
->results
, 100, 100);
1797 gtk_clist_set_column_width(GTK_CLIST(gui_side
->results
), 0, 100);
1798 gtk_clist_set_selection_mode(GTK_CLIST(gui_side
->results
),
1799 GTK_SELECTION_SINGLE
);
1800 gtk_container_add(GTK_CONTAINER(scroller
), gui_side
->results
);
1801 gtk_box_set_child_packing(GTK_BOX(gui_side
->vbox
),
1802 gui_side
->log_hbox
, FALSE
, TRUE
, 4, GTK_PACK_START
);
1803 gtk_signal_connect(GTK_OBJECT(gui_side
->results
), "select_row",
1804 GTK_SIGNAL_FUNC(select_row_callback
), gui_side
);
1806 hbox
= gtk_hbox_new(FALSE
, 0);
1807 label
= gtk_label_new(_("Expression:"));
1808 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, TRUE
, 4);
1809 gui_side
->default_string
= &last_find_string
;
1810 gui_side
->entry
= gtk_entry_new();
1811 gtk_widget_set_style(gui_side
->entry
, fixed_style
);
1812 gtk_entry_set_text(GTK_ENTRY(gui_side
->entry
), last_find_string
);
1813 gtk_editable_select_region(GTK_EDITABLE(gui_side
->entry
), 0, -1);
1814 gtk_widget_set_sensitive(gui_side
->entry
, FALSE
);
1815 gtk_box_pack_start(GTK_BOX(hbox
), gui_side
->entry
, TRUE
, TRUE
, 4);
1816 gtk_signal_connect(GTK_OBJECT(gui_side
->entry
), "changed",
1817 entry_changed
, gui_side
);
1818 gtk_box_pack_start(GTK_BOX(gui_side
->vbox
), hbox
, FALSE
, TRUE
, 4);
1819 gtk_box_pack_start(GTK_BOX(hbox
),
1820 new_help_button(show_condition_help
, NULL
),
1823 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), _("Find"));
1824 gtk_window_set_focus(GTK_WINDOW(gui_side
->window
), gui_side
->entry
);
1825 gtk_signal_connect_object(GTK_OBJECT(gui_side
->entry
), "activate",
1826 gtk_button_clicked
, GTK_OBJECT(gui_side
->quiet
));
1827 number_of_windows
++;
1828 gtk_widget_show_all(gui_side
->window
);
1831 /* Count disk space used by selected items */
1832 void action_usage(FilerWindow
*filer_window
)
1835 Collection
*collection
;
1837 collection
= filer_window
->collection
;
1839 if (collection
->number_selected
< 1)
1841 report_error(PROJECT
,
1842 _("You need to select some items to count"));
1846 gui_side
= start_action(filer_window
, usage_cb
, TRUE
);
1850 gui_side
->show_info
= TRUE
;
1852 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), _("Disk Usage"));
1853 number_of_windows
++;
1854 gtk_widget_show_all(gui_side
->window
);
1857 /* Mount/unmount listed items (paths).
1858 * Free the list after this function returns.
1860 void action_mount(GList
*paths
)
1862 #ifdef DO_MOUNT_POINTS
1865 gui_side
= start_action(paths
, mount_cb
,
1866 option_get_int("action_mount"));
1870 gtk_window_set_title(GTK_WINDOW(gui_side
->window
),
1871 _("Mount / Unmount"));
1872 number_of_windows
++;
1873 gtk_widget_show_all(gui_side
->window
);
1875 report_error(PROJECT
,
1876 _("ROX-Filer does not yet support mount points on your "
1878 #endif /* DO_MOUNT_POINTS */
1881 /* Deletes all selected items in the window */
1882 void action_delete(FilerWindow
*filer_window
)
1885 Collection
*collection
;
1887 collection
= filer_window
->collection
;
1889 if (collection
->number_selected
< 1)
1891 report_error(PROJECT
,
1892 _("You need to select some items to delete"));
1896 if (!remove_pinned_ok(filer_window
))
1899 gui_side
= start_action(filer_window
, delete_cb
,
1900 option_get_int("action_delete"));
1904 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), _("Delete"));
1905 add_toggle(gui_side
,
1906 _("Force - don't confirm deletion of non-writeable items"),
1908 add_toggle(gui_side
,
1909 _("Brief - only log directories being deleted"),
1912 number_of_windows
++;
1913 gtk_widget_show_all(gui_side
->window
);
1916 /* Change the permissions of the selected items */
1917 void action_chmod(FilerWindow
*filer_window
)
1920 Collection
*collection
;
1921 GtkWidget
*hbox
, *label
, *combo
;
1922 static GList
*presets
= NULL
;
1924 collection
= filer_window
->collection
;
1926 if (collection
->number_selected
< 1)
1928 report_error(PROJECT
,
1929 _("You need to select the items "
1930 "whose permissions you want to change"));
1936 presets
= g_list_append(presets
,
1937 _("a+x (Make executable/searchable)"));
1938 presets
= g_list_append(presets
,
1939 _("a-x (Make non-executable/non-searchable)"));
1940 presets
= g_list_append(presets
,
1941 _("u+rw (Give owner read+write)"));
1942 presets
= g_list_append(presets
,
1943 _("go-rwx (Private - owner access only)"));
1944 presets
= g_list_append(presets
,
1945 _("go=u-w (Public access, not write)"));
1948 if (!last_chmod_string
)
1949 last_chmod_string
= g_strdup((guchar
*) presets
->data
);
1950 new_entry_string
= last_chmod_string
;
1951 gui_side
= start_action(filer_window
, chmod_cb
, FALSE
);
1955 add_toggle(gui_side
,
1956 _("Brief - don't list processed files"), "B");
1957 add_toggle(gui_side
,
1958 _("Recurse - also change contents of subdirectories"), "R");
1960 hbox
= gtk_hbox_new(FALSE
, 0);
1961 label
= gtk_label_new(_("Command:"));
1962 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, TRUE
, 4);
1963 gui_side
->default_string
= &last_chmod_string
;
1965 combo
= gtk_combo_new();
1966 gtk_combo_disable_activate(GTK_COMBO(combo
));
1967 gtk_combo_set_use_arrows_always(GTK_COMBO(combo
), TRUE
);
1968 gtk_combo_set_popdown_strings(GTK_COMBO(combo
), presets
);
1970 gui_side
->entry
= GTK_COMBO(combo
)->entry
;
1971 gtk_entry_set_text(GTK_ENTRY(gui_side
->entry
), last_chmod_string
);
1972 gtk_editable_select_region(GTK_EDITABLE(gui_side
->entry
), 0, -1);
1973 gtk_widget_set_sensitive(gui_side
->entry
, FALSE
);
1974 gtk_box_pack_start(GTK_BOX(hbox
), combo
, TRUE
, TRUE
, 4);
1975 gtk_signal_connect(GTK_OBJECT(gui_side
->entry
), "changed",
1976 entry_changed
, gui_side
);
1977 gtk_box_pack_start(GTK_BOX(gui_side
->vbox
), hbox
, FALSE
, TRUE
, 0);
1978 gtk_box_pack_start(GTK_BOX(hbox
),
1979 new_help_button(show_chmod_help
, NULL
),
1982 gtk_window_set_focus(GTK_WINDOW(gui_side
->window
), gui_side
->entry
);
1983 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), _("Permissions"));
1984 gtk_signal_connect_object(GTK_OBJECT(gui_side
->entry
), "activate",
1985 gtk_button_clicked
, GTK_OBJECT(gui_side
->yes
));
1987 number_of_windows
++;
1988 gtk_widget_show_all(gui_side
->window
);
1991 /* If leaf is NULL then the copy has the same name as the original */
1992 void action_copy(GSList
*paths
, char *dest
, char *leaf
)
1998 action_do_func
= do_copy
;
1999 gui_side
= start_action(paths
, list_cb
, option_get_int("action_copy"));
2003 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), _("Copy"));
2004 number_of_windows
++;
2005 gtk_widget_show_all(gui_side
->window
);
2008 /* If leaf is NULL then the file is not renamed */
2009 void action_move(GSList
*paths
, char *dest
, char *leaf
)
2015 action_do_func
= do_move
;
2016 gui_side
= start_action(paths
, list_cb
, option_get_int("action_move"));
2020 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), _("Move"));
2021 number_of_windows
++;
2022 gtk_widget_show_all(gui_side
->window
);
2025 void action_link(GSList
*paths
, char *dest
)
2030 action_do_func
= do_link
;
2031 gui_side
= start_action(paths
, list_cb
, option_get_int("action_link"));
2035 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), _("Link"));
2036 number_of_windows
++;
2037 gtk_widget_show_all(gui_side
->window
);
2040 void action_init(void)
2042 option_add_int("action_copy", 1, NULL
);
2043 option_add_int("action_move", 1, NULL
);
2044 option_add_int("action_link", 1, NULL
);
2045 option_add_int("action_delete", 0, NULL
);
2046 option_add_int("action_mount", 1, NULL
);
2051 /* Check to see if any of the selected items (or their children) are
2052 * on the pinboard or panel. If so, ask for confirmation.
2054 * TRUE if it's OK to lose them.
2056 static gboolean
remove_pinned_ok(FilerWindow
*filer_window
)
2058 Collection
*collection
= filer_window
->collection
;
2060 GList
*ask
= NULL
, *next
;
2065 for (i
= 0; i
< collection
->number_of_items
; i
++)
2070 if (!collection
->items
[i
].selected
)
2073 item
= (DirItem
*) collection
->items
[i
].data
;
2074 path
= make_path(filer_window
->path
, item
->leafname
)->str
;
2076 if (icons_require(path
))
2078 if (++ask_n
> MAX_ASK
)
2080 ask
= g_list_append(ask
, item
->leafname
);
2087 if (ask_n
> MAX_ASK
)
2089 message
= g_string_new(_("Deleting items such as "));
2092 else if (ask_n
== 1)
2093 message
= g_string_new(_("Deleting the item "));
2095 message
= g_string_new(_("Deleting the items "));
2098 for (next
= ask
; next
; next
= next
->next
)
2100 guchar
*leaf
= (guchar
*) next
->data
;
2102 g_string_append_c(message
, '`');
2103 g_string_append(message
, leaf
);
2104 g_string_append_c(message
, '\'');
2106 if (i
== ask_n
- 1 && i
> 0)
2107 g_string_append(message
, " and ");
2109 g_string_append(message
, ", ");
2115 message
= g_string_append(message
,
2116 _(" will affect some items on the pinboard "
2117 "or panel - really delete it?"));
2120 if (ask_n
> MAX_ASK
)
2121 message
= g_string_append_c(message
, ',');
2122 message
= g_string_append(message
,
2123 _(" will affect some items on the pinboard "
2124 "or panel - really delete them?"));
2127 retval
= get_choice(PROJECT
, message
->str
,
2128 2, _("OK"), _("Cancel")) == 0;
2130 g_string_free(message
, TRUE
);