4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2001, the ROX-Filer team.
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>
42 #include "gui_support.h"
46 #include "modechange.h"
51 /* Parent->Child messages are one character each:
53 * Q/Y/N Quiet/Yes/No button clicked
54 * F Force deletion of non-writeable items
57 #define SENSITIVE_YESNO(gui_side, state) \
59 gtk_widget_set_sensitive((gui_side)->yes, state); \
60 gtk_widget_set_sensitive((gui_side)->no, state); \
61 if ((gui_side)->entry) \
62 gtk_widget_set_sensitive((gui_side)->entry, state);\
65 typedef struct _GUIside GUIside
;
66 typedef void ActionChild(gpointer data
);
67 typedef gboolean
ForDirCB(char *path
, char *dest_path
);
71 int from_child
; /* File descriptor */
73 int input_tag
; /* gdk_input_add() */
74 GtkWidget
*vbox
, *log
, *window
, *dir
, *log_hbox
;
75 GtkWidget
*quiet
, *yes
, *no
;
76 int child
; /* Process ID */
78 gboolean show_info
; /* For Disk Usage */
80 GtkWidget
*entry
; /* May be NULL */
81 guchar
**default_string
; /* Changed when the entry changes */
82 void (*entry_string_func
)(GtkWidget
*widget
, guchar
*string
);
84 char *next_dir
; /* NULL => no timer active */
92 /* These don't need to be in a structure because we fork() before
95 static gboolean mount_open_dir
= FALSE
;
96 static int from_parent
= 0;
97 static FILE *to_parent
= NULL
;
98 static gboolean quiet
= FALSE
;
99 static GString
*message
= NULL
;
100 static char *action_dest
= NULL
;
101 static char *action_leaf
= NULL
;
102 static gboolean (*action_do_func
)(char *source
, char *dest
);
103 static size_t size_tally
; /* For Disk Usage */
105 static struct mode_change
*mode_change
= NULL
; /* For Permissions */
106 static FindCondition
*find_condition
= NULL
; /* For Find */
108 static gboolean o_force
= FALSE
;
109 static gboolean o_brief
= FALSE
;
110 static gboolean o_recurse
= FALSE
;
112 /* Whenever the text in these boxes is changed we store a copy of the new
113 * string to be used as the default next time.
115 static guchar
*last_chmod_string
= NULL
;
116 static guchar
*last_find_string
= NULL
;
118 /* Set to one of the above before forking. This may change over a call to
119 * reply(). It is reset to NULL once the text is parsed.
121 static guchar
*new_entry_string
= NULL
;
123 /* Static prototypes */
124 static gboolean
send();
125 static gboolean
send_error();
126 static gboolean
send_dir(char *dir
);
127 static gboolean
read_exact(int source
, char *buffer
, ssize_t len
);
128 static void do_mount(guchar
*path
, gboolean mount
);
129 static void add_toggle(GUIside
*gui_side
, guchar
*label
, guchar
*code
);
130 static gboolean
reply(int fd
, gboolean ignore_quiet
);
131 static gboolean
remove_pinned_ok(GList
*paths
);
135 static void preview_closed(GtkWidget
*window
, GUIside
*gui_side
)
137 gui_side
->preview
= NULL
;
140 static void select_row_callback(GtkWidget
*widget
,
141 gint row
, gint column
,
142 GdkEventButton
*event
,
147 gtk_clist_get_text(GTK_CLIST(gui_side
->results
), row
, 0, &leaf
);
148 gtk_clist_get_text(GTK_CLIST(gui_side
->results
), row
, 1, &dir
);
150 gtk_clist_unselect_row(GTK_CLIST(gui_side
->results
), row
, column
);
152 if (gui_side
->preview
)
154 if (strcmp(gui_side
->preview
->path
, dir
) == 0)
155 display_set_autoselect(gui_side
->preview
, leaf
);
157 filer_change_to(gui_side
->preview
, dir
, leaf
);
161 gui_side
->preview
= filer_opendir(dir
);
162 if (gui_side
->preview
)
164 display_set_autoselect(gui_side
->preview
, leaf
);
165 gtk_signal_connect(GTK_OBJECT(gui_side
->preview
->window
),
167 GTK_SIGNAL_FUNC(preview_closed
), gui_side
);
172 /* This is called whenever the user edits the entry box (if any) - send the
175 static void entry_changed(GtkEntry
*entry
, GUIside
*gui_side
)
179 g_return_if_fail(gui_side
->default_string
!= NULL
);
181 text
= gtk_entry_get_text(entry
);
183 if (gui_side
->entry_string_func
)
184 gui_side
->entry_string_func(GTK_WIDGET(gui_side
->entry
), text
);
186 g_free(*(gui_side
->default_string
));
187 *(gui_side
->default_string
) = g_strdup(text
);
189 if (!gui_side
->to_child
)
192 fputc('E', gui_side
->to_child
);
193 fputs(text
, gui_side
->to_child
);
194 fputc('\n', gui_side
->to_child
);
195 fflush(gui_side
->to_child
);
198 void show_condition_help(gpointer data
)
200 static GtkWidget
*help
= NULL
;
204 GtkWidget
*text
, *vbox
, *button
, *hbox
, *frame
;
206 help
= gtk_window_new(GTK_WINDOW_DIALOG
);
207 gtk_container_set_border_width(GTK_CONTAINER(help
), 10);
208 gtk_window_set_title(GTK_WINDOW(help
),
209 _("Find expression reference"));
211 vbox
= gtk_vbox_new(FALSE
, 0);
212 gtk_container_add(GTK_CONTAINER(help
), vbox
);
214 frame
= gtk_frame_new(_("Quick Start"));
215 text
= gtk_label_new(
216 _("Just put the name of the file you're looking for in single quotes:\n"
217 "'index.html' (to find a file called 'index.html')"));
218 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
219 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
220 gtk_container_add(GTK_CONTAINER(frame
), text
);
221 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
223 frame
= gtk_frame_new(_("Examples"));
224 text
= gtk_label_new(
225 _("'*.htm', '*.html' (finds HTML files)\n"
226 "IsDir 'lib' (finds directories called 'lib')\n"
227 "IsReg 'core' (finds a regular file called 'core')\n"
228 "! (IsDir, IsReg) (is neither a directory nor a regular file)\n"
229 "mtime after 1 day ago and size > 1Mb (big, and recently modified)\n"
230 "'CVS' prune, isreg (a regular file not in CVS)\n"
231 "IsReg system(grep -q fred \"%\") (contains the word 'fred')"));
232 gtk_widget_set_style(text
, fixed_style
);
233 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
234 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
235 gtk_container_add(GTK_CONTAINER(frame
), text
);
236 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
238 frame
= gtk_frame_new(_("Simple Tests"));
239 text
= gtk_label_new(
240 _("IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket (types)\n"
241 "IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable (permissions)"
245 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
246 "contains a slash then the match is against the full path; otherwise it is \n"
247 "against the leafname only."));
248 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
249 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
250 gtk_container_add(GTK_CONTAINER(frame
), text
);
251 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
253 frame
= gtk_frame_new(_("Comparisons"));
254 text
= gtk_label_new(
255 _("<, <=, =, !=, >, >=, After, Before (compare two values)\n"
256 "5 bytes, 1Kb, 2Mb, 3Gb (file sizes)\n"
257 "2 secs|mins|hours|days|weeks|years ago|hence (times)\n"
258 "atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks (values)"));
259 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
260 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
261 gtk_container_add(GTK_CONTAINER(frame
), text
);
262 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
264 frame
= gtk_frame_new(_("Specials"));
265 text
= gtk_label_new(
266 _("system(command) (true if 'command' returns with a zero exit status; a % \n"
267 "in 'command' is replaced with the path of the current file)\n"
268 "prune (false, and prevents searching the contents of a directory).")
270 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
271 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
272 gtk_container_add(GTK_CONTAINER(frame
), text
);
273 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
275 hbox
= gtk_hbox_new(FALSE
, 20);
276 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, TRUE
, TRUE
, 0);
278 text
= gtk_label_new(
279 _("See the ROX-Filer manual for full details."));
280 gtk_box_pack_start(GTK_BOX(hbox
), text
, TRUE
, TRUE
, 0);
281 button
= gtk_button_new_with_label(_("Close"));
282 gtk_box_pack_start(GTK_BOX(hbox
), button
, FALSE
, TRUE
, 0);
283 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
284 gtk_widget_hide
, GTK_OBJECT(help
));
286 gtk_signal_connect_object(GTK_OBJECT(help
), "delete_event",
287 gtk_widget_hide
, GTK_OBJECT(help
));
290 if (GTK_WIDGET_VISIBLE(help
))
291 gtk_widget_hide(help
);
292 gtk_widget_show_all(help
);
295 static void show_chmod_help(gpointer data
)
297 static GtkWidget
*help
= NULL
;
301 GtkWidget
*text
, *vbox
, *button
, *hbox
, *sep
;
303 help
= gtk_window_new(GTK_WINDOW_DIALOG
);
304 gtk_container_set_border_width(GTK_CONTAINER(help
), 10);
305 gtk_window_set_title(GTK_WINDOW(help
),
306 _("Permissions command reference"));
308 vbox
= gtk_vbox_new(FALSE
, 0);
309 gtk_container_add(GTK_CONTAINER(help
), vbox
);
311 text
= gtk_label_new(
312 _("Normally, you can just select a command from the menu (click \n"
313 "on the arrow beside the command box). Sometimes, you need more...\n"
315 "The format of a command is:\n"
316 "CHANGE, CHANGE, ...\n"
318 "WHO HOW PERMISSIONS\n"
319 "WHO is some combination of u, g and o which determines whether to\n"
320 "change the permissions for the User (owner), Group or Others.\n"
321 "HOW is +, - or = to add, remove or set exactly the permissions.\n"
322 "PERMISSIONS is some combination of the letters 'rwxXstugo'\n\n"
324 "Bracketed text and spaces are ignored.\n\n"
327 "u+rw (the file owner gains read and write permission)\n"
328 "g=u (the group permissions are set to be the same as the user's)\n"
329 "o=u-w (others get the same permissions as the owner, but without "
330 "write permission)\n"
331 "a+x (everyone gets execute/access permission - same as 'ugo+x')\n"
332 "a+X (directories become accessable by everyone; files which were\n"
333 "executable by anyone become executable by everyone)\n"
334 "u+rw, go+r (two commands at once!)\n"
335 "u+s (set the SetUID bit - often has no effect on script files)\n"
336 "755 (set the permissions directly)\n"
338 "\nSee the chmod(1) man page for full details."));
339 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
340 gtk_box_pack_start(GTK_BOX(vbox
), text
, TRUE
, TRUE
, 0);
342 hbox
= gtk_hbox_new(FALSE
, 20);
343 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, TRUE
, 0);
345 sep
= gtk_hseparator_new();
346 gtk_box_pack_start(GTK_BOX(hbox
), sep
, TRUE
, TRUE
, 0);
347 button
= gtk_button_new_with_label(_("Close"));
348 gtk_box_pack_start(GTK_BOX(hbox
), button
, FALSE
, TRUE
, 0);
349 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
350 gtk_widget_hide
, GTK_OBJECT(help
));
352 gtk_signal_connect_object(GTK_OBJECT(help
), "delete_event",
353 gtk_widget_hide
, GTK_OBJECT(help
));
356 if (GTK_WIDGET_VISIBLE(help
))
357 gtk_widget_hide(help
);
358 gtk_widget_show_all(help
);
361 static gboolean
display_dir(gpointer data
)
363 GUIside
*gui_side
= (GUIside
*) data
;
365 gtk_label_set_text(GTK_LABEL(gui_side
->dir
), gui_side
->next_dir
+ 1);
366 g_free(gui_side
->next_dir
);
367 gui_side
->next_dir
= NULL
;
372 static void add_to_results(GUIside
*gui_side
, gchar
*path
)
374 gchar
*row
[] = {"Leaf", "Dir"};
378 slash
= strrchr(path
, '/');
379 g_return_if_fail(slash
!= NULL
);
382 row
[1] = g_strndup(path
, MAX(len
, 1));
385 gtk_clist_append(GTK_CLIST(gui_side
->results
), row
);
390 /* Called when the child sends us a message */
391 static void message_from_child(gpointer data
,
393 GdkInputCondition condition
)
396 GUIside
*gui_side
= (GUIside
*) data
;
397 GtkWidget
*log
= gui_side
->log
;
399 if (read_exact(source
, buf
, 4))
405 message_len
= strtol(buf
, NULL
, 16);
406 buffer
= g_malloc(message_len
+ 1);
407 if (message_len
> 0 && read_exact(source
, buffer
, message_len
))
409 buffer
[message_len
] = '\0';
413 SENSITIVE_YESNO(gui_side
, TRUE
);
414 gtk_window_set_focus(
415 GTK_WINDOW(gui_side
->window
),
416 gui_side
->entry
? gui_side
->entry
419 else if (*buffer
== '+')
421 /* Update/rescan this item */
422 refresh_dirs(buffer
+ 1);
426 else if (*buffer
== '=')
428 /* Add to search results */
429 add_to_results(gui_side
, buffer
+ 1);
433 else if (*buffer
== '#')
435 /* Clear search results area */
436 gtk_clist_clear(GTK_CLIST(gui_side
->results
));
441 else if (*buffer
== 'm' || *buffer
== 'M')
443 /* Mount / major changes to this path */
446 filer_check_mounted(buffer
+ 1);
450 else if (*buffer
== '/')
452 /* Update the current object display */
453 if (gui_side
->next_dir
)
454 g_free(gui_side
->next_dir
);
456 gui_side
->next_timer
=
460 gui_side
->next_dir
= buffer
;
463 else if (*buffer
== 'o')
465 /* Open a filer window */
466 filer_opendir(buffer
+ 1);
470 else if (*buffer
== '!')
473 gtk_text_insert(GTK_TEXT(log
),
475 *buffer
== '!' ? &red
: NULL
,
477 buffer
+ 1, message_len
- 1);
481 g_printerr("Child died in the middle of a message.\n");
484 /* The child is dead */
487 fclose(gui_side
->to_child
);
488 gui_side
->to_child
= NULL
;
489 close(gui_side
->from_child
);
490 gdk_input_remove(gui_side
->input_tag
);
491 gtk_widget_set_sensitive(gui_side
->quiet
, FALSE
);
493 if (gui_side
->errors
)
497 if (gui_side
->errors
== 1)
498 report
= g_strdup(_("There was one error.\n"));
500 report
= g_strdup_printf(_("There were %d errors.\n"),
503 gtk_text_insert(GTK_TEXT(log
), NULL
, &red
, NULL
,
508 else if (gui_side
->show_info
== FALSE
)
509 gtk_widget_destroy(gui_side
->window
);
512 /* Scans src_dir, updating dest_path whenever cb returns TRUE */
513 static void for_dir_contents(ForDirCB
*cb
, char *src_dir
, char *dest_path
)
517 GSList
*list
= NULL
, *next
;
519 d
= mc_opendir(src_dir
);
522 /* Message displayed is "ERROR reading 'path': message" */
523 g_string_sprintf(message
, "!%s '%s': %s\n",
525 src_dir
, g_strerror(errno
));
532 while ((ent
= mc_readdir(d
)))
534 if (ent
->d_name
[0] == '.' && (ent
->d_name
[1] == '\0'
535 || (ent
->d_name
[1] == '.' && ent
->d_name
[2] == '\0')))
537 list
= g_slist_append(list
, g_strdup(make_path(src_dir
,
549 if (cb((char *) next
->data
, dest_path
))
551 g_string_sprintf(message
, "+%s", dest_path
);
562 /* Read this many bytes into the buffer. TRUE on success. */
563 static gboolean
read_exact(int source
, char *buffer
, ssize_t len
)
568 got
= read(source
, buffer
, len
);
577 /* Send 'message' to our parent process. TRUE on success. */
578 static gboolean
send(void)
583 g_return_val_if_fail(message
->len
< 0xffff, FALSE
);
585 sprintf(len_buffer
, "%04x", message
->len
);
586 fwrite(len_buffer
, 1, 4, to_parent
);
587 len
= fwrite(message
->str
, 1, message
->len
, to_parent
);
589 return len
== message
->len
;
592 /* Set the directory indicator at the top of the window */
593 static gboolean
send_dir(char *dir
)
595 g_string_sprintf(message
, "/%s", dir
);
599 static gboolean
send_error(void)
601 g_string_sprintf(message
, "!%s: %s\n", _("ERROR"), g_strerror(errno
));
605 static void button_reply(GtkWidget
*button
, GUIside
*gui_side
)
609 if (!gui_side
->to_child
)
612 text
= gtk_object_get_data(GTK_OBJECT(button
), "send-code");
613 g_return_if_fail(text
!= NULL
);
614 fputc(*text
, gui_side
->to_child
);
615 fflush(gui_side
->to_child
);
617 if (*text
== 'Y' || *text
== 'N' || *text
== 'Q')
618 SENSITIVE_YESNO(gui_side
, FALSE
);
621 static void process_flag(char flag
)
632 o_recurse
= !o_recurse
;
638 g_string_sprintf(message
,
639 "!ERROR: Bad message '%c'\n", flag
);
645 /* If the parent has sent any flag toggles, read them */
646 static void check_flags(void)
657 FD_SET(from_parent
, &set
);
660 got
= select(from_parent
+ 1, &set
, NULL
, NULL
, &tv
);
663 g_error("select() failed: %s\n", g_strerror(errno
));
667 got
= read(from_parent
, &retval
, 1);
669 g_error("read() error: %s\n", g_strerror(errno
));
671 process_flag(retval
);
675 static void read_new_entry_text(void)
681 new = g_string_new(NULL
);
685 len
= read(from_parent
, &c
, 1);
688 fprintf(stderr
, "read() error: %s\n",
690 _exit(1); /* Parent died? */
695 g_string_append_c(new, c
);
698 g_free(new_entry_string
);
699 new_entry_string
= new->str
;
700 g_string_free(new, FALSE
);
703 /* Read until the user sends a reply. If ignore_quiet is TRUE then
704 * the user MUST click Yes or No, else treat quiet on as Yes.
705 * If the user needs prompting then does send().
707 static gboolean
reply(int fd
, gboolean ignore_quiet
)
711 gboolean asked
= FALSE
;
713 while (ignore_quiet
|| !quiet
)
721 len
= read(fd
, &retval
, 1);
724 fprintf(stderr
, "read() error: %s\n",
726 _exit(1); /* Parent died? */
735 g_string_assign(message
, "?");
740 g_string_sprintf(message
, "' %s\n", _("Yes"));
744 g_string_sprintf(message
, "' %s\n", _("No"));
748 read_new_entry_text();
751 process_flag(retval
);
758 g_string_sprintf(message
, "' %s\n", _("Quiet"));
764 static void destroy_action_window(GtkWidget
*widget
, gpointer data
)
766 GUIside
*gui_side
= (GUIside
*) data
;
770 kill(gui_side
->child
, SIGTERM
);
771 fclose(gui_side
->to_child
);
772 close(gui_side
->from_child
);
773 gdk_input_remove(gui_side
->input_tag
);
776 if (gui_side
->next_dir
)
778 gtk_timeout_remove(gui_side
->next_timer
);
779 g_free(gui_side
->next_dir
);
782 if (gui_side
->preview
)
784 gtk_signal_disconnect_by_data(
785 GTK_OBJECT(gui_side
->preview
->window
),
787 gui_side
->preview
= NULL
;
792 if (--number_of_windows
< 1)
796 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
797 * (NULL on failure). The child calls func().
799 * If autoq then automatically selects 'Quiet'.
801 static GUIside
*start_action(gpointer data
, ActionChild
*func
, gboolean autoq
)
803 int filedes
[4]; /* 0 and 2 are for reading */
806 GtkWidget
*vbox
, *button
, *scrollbar
, *actions
;
807 struct sigaction act
;
811 report_error(PROJECT
, g_strerror(errno
));
815 if (pipe(filedes
+ 2))
819 report_error(PROJECT
, g_strerror(errno
));
827 report_error(PROJECT
, g_strerror(errno
));
830 /* We are the child */
834 /* Reset the SIGCHLD handler */
835 act
.sa_handler
= SIG_DFL
;
836 sigemptyset(&act
.sa_mask
);
838 sigaction(SIGCHLD
, &act
, NULL
);
840 message
= g_string_new(NULL
);
843 to_parent
= fdopen(filedes
[1], "wb");
844 from_parent
= filedes
[2];
850 /* We are the parent */
853 gui_side
= g_malloc(sizeof(GUIside
));
854 gui_side
->from_child
= filedes
[0];
855 gui_side
->to_child
= fdopen(filedes
[3], "wb");
856 gui_side
->log
= NULL
;
857 gui_side
->child
= child
;
858 gui_side
->errors
= 0;
859 gui_side
->show_info
= FALSE
;
860 gui_side
->preview
= NULL
;
861 gui_side
->results
= NULL
;
862 gui_side
->entry
= NULL
;
863 gui_side
->default_string
= NULL
;
864 gui_side
->entry_string_func
= NULL
;
866 gui_side
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
867 gtk_container_set_border_width(GTK_CONTAINER(gui_side
->window
), 2);
868 gtk_window_set_default_size(GTK_WINDOW(gui_side
->window
), 450, 200);
869 gtk_signal_connect(GTK_OBJECT(gui_side
->window
), "destroy",
870 GTK_SIGNAL_FUNC(destroy_action_window
), gui_side
);
872 gui_side
->vbox
= vbox
= gtk_vbox_new(FALSE
, 0);
873 gtk_container_add(GTK_CONTAINER(gui_side
->window
), vbox
);
875 gui_side
->dir
= gtk_label_new(_("<dir>"));
876 gui_side
->next_dir
= NULL
;
877 gtk_misc_set_alignment(GTK_MISC(gui_side
->dir
), 0.5, 0.5);
878 gtk_box_pack_start(GTK_BOX(vbox
), gui_side
->dir
, FALSE
, TRUE
, 0);
880 gui_side
->log_hbox
= gtk_hbox_new(FALSE
, 0);
881 gtk_box_pack_start(GTK_BOX(vbox
), gui_side
->log_hbox
, TRUE
, TRUE
, 4);
883 gui_side
->log
= gtk_text_new(NULL
, NULL
);
884 gtk_widget_set_usize(gui_side
->log
, 400, 100);
885 gtk_box_pack_start(GTK_BOX(gui_side
->log_hbox
),
886 gui_side
->log
, TRUE
, TRUE
, 0);
887 scrollbar
= gtk_vscrollbar_new(GTK_TEXT(gui_side
->log
)->vadj
);
888 gtk_box_pack_start(GTK_BOX(gui_side
->log_hbox
),
889 scrollbar
, FALSE
, TRUE
, 0);
891 actions
= gtk_hbox_new(TRUE
, 4);
892 gtk_box_pack_start(GTK_BOX(vbox
), actions
, FALSE
, TRUE
, 0);
894 gui_side
->quiet
= button
= gtk_toggle_button_new_with_label(_("Quiet"));
895 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button
), autoq
);
896 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "Q");
897 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
898 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
899 button_reply
, gui_side
);
900 gui_side
->yes
= button
= gtk_button_new_with_label(_("Yes"));
901 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "Y");
902 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
903 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
904 button_reply
, gui_side
);
905 gui_side
->no
= button
= gtk_button_new_with_label(_("No"));
906 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "N");
907 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
908 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
909 button_reply
, gui_side
);
910 SENSITIVE_YESNO(gui_side
, FALSE
);
912 button
= gtk_button_new_with_label(_("Abort"));
913 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
914 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
915 gtk_widget_destroy
, GTK_OBJECT(gui_side
->window
));
917 gui_side
->input_tag
= gdk_input_add(gui_side
->from_child
,
925 /* ACTIONS ON ONE ITEM */
927 /* These may call themselves recursively, or ask questions, etc.
928 * TRUE iff the directory containing dest_path needs to be rescanned.
931 /* dest_path is the dir containing src_path.
932 * Updates the global size_tally.
934 static gboolean
do_usage(char *src_path
, char *dest_path
)
940 if (mc_lstat(src_path
, &info
))
942 g_string_sprintf(message
, "'%s:\n", src_path
);
948 if (S_ISREG(info
.st_mode
) || S_ISLNK(info
.st_mode
))
949 size_tally
+= info
.st_size
;
950 else if (S_ISDIR(info
.st_mode
))
952 g_string_sprintf(message
, _("?Count contents of %s?"),
954 if (reply(from_parent
, FALSE
))
957 safe_path
= g_strdup(src_path
);
958 for_dir_contents(do_usage
, safe_path
, safe_path
);
966 /* dest_path is the dir containing src_path */
967 static gboolean
do_delete(char *src_path
, char *dest_path
)
974 if (mc_lstat(src_path
, &info
))
980 write_prot
= S_ISLNK(info
.st_mode
) ? FALSE
981 : access(src_path
, W_OK
) != 0;
982 if (write_prot
|| !quiet
)
984 g_string_sprintf(message
, _("?Delete %s'%s'?"),
985 write_prot
? _("WRITE-PROTECTED ") : " ",
987 if (!reply(from_parent
, write_prot
&& !o_force
))
992 g_string_sprintf(message
, _("'Deleting '%s'\n"), src_path
);
996 if (S_ISDIR(info
.st_mode
))
999 safe_path
= g_strdup(src_path
);
1000 for_dir_contents(do_delete
, safe_path
, safe_path
);
1001 if (rmdir(safe_path
))
1007 g_string_sprintf(message
, _("'Directory '%s' deleted\n"),
1010 g_string_sprintf(message
, "m%s", safe_path
);
1014 else if (unlink(src_path
))
1023 /* path is the item to check. If is is a directory then we may recurse
1024 * (unless prune is used).
1026 static gboolean
do_find(char *path
, char *dummy
)
1035 g_string_sprintf(message
, _("?Check '%s'?"), path
);
1036 if (!reply(from_parent
, FALSE
))
1042 if (new_entry_string
)
1045 find_condition_free(find_condition
);
1046 find_condition
= find_compile(new_entry_string
);
1047 g_free(new_entry_string
);
1048 new_entry_string
= NULL
;
1054 g_string_assign(message
, _("!Invalid find condition - "
1055 "change it and try again\n"));
1057 g_string_sprintf(message
, _("?Check '%s'?"), path
);
1058 if (!reply(from_parent
, TRUE
))
1062 if (mc_lstat(path
, &info
.stats
))
1065 g_string_sprintf(message
, _("'(while checking '%s')\n"), path
);
1070 info
.fullpath
= path
;
1071 time(&info
.now
); /* XXX: Not for each check! */
1073 slash
= strrchr(path
, '/');
1074 info
.leaf
= slash
? slash
+ 1 : path
;
1076 if (find_test_condition(find_condition
, &info
))
1078 g_string_sprintf(message
, "=%s", path
);
1082 if (S_ISDIR(info
.stats
.st_mode
) && !info
.prune
)
1085 safe_path
= g_strdup(path
);
1086 for_dir_contents(do_find
, safe_path
, safe_path
);
1093 /* Like mode_compile(), but ignores spaces and bracketed bits */
1094 struct mode_change
*nice_mode_compile(const char *mode_string
,
1095 unsigned int masked_ops
)
1099 struct mode_change
*retval
= NULL
;
1101 new = g_string_new(NULL
);
1103 for (; *mode_string
; mode_string
++)
1105 if (*mode_string
== '(')
1107 if (*mode_string
== ')')
1115 if (brackets
== 0 && *mode_string
!= ' ')
1116 g_string_append_c(new, *mode_string
);
1120 retval
= mode_compile(new->str
, masked_ops
);
1121 g_string_free(new, TRUE
);
1125 static gboolean
do_chmod(char *path
, char *dummy
)
1132 if (mc_lstat(path
, &info
))
1137 if (S_ISLNK(info
.st_mode
))
1142 g_string_sprintf(message
,
1143 _("?Change permissions of '%s'?"), path
);
1144 if (!reply(from_parent
, FALSE
))
1149 g_string_sprintf(message
,
1150 _("'Changing permissions of '%s'\n"),
1157 if (new_entry_string
)
1160 mode_free(mode_change
);
1161 mode_change
= nice_mode_compile(new_entry_string
,
1163 g_free(new_entry_string
);
1164 new_entry_string
= NULL
;
1170 g_string_assign(message
,
1171 _("!Invalid mode command - change it and try again\n"));
1173 g_string_sprintf(message
,
1174 _("?Change permissions of '%s'?"), path
);
1175 if (!reply(from_parent
, TRUE
))
1179 if (mc_lstat(path
, &info
))
1184 if (S_ISLNK(info
.st_mode
))
1187 new_mode
= mode_adjust(info
.st_mode
, mode_change
);
1188 if (chmod(path
, new_mode
))
1194 if (o_recurse
&& S_ISDIR(info
.st_mode
))
1197 safe_path
= g_strdup(path
);
1198 for_dir_contents(do_chmod
, safe_path
, safe_path
);
1205 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1206 * is set then that is the new leafname, otherwise the leafname stays
1209 static char *make_dest_path(char *object
, char *dir
)
1217 leaf
= strrchr(object
, '/');
1219 leaf
= object
; /* Error? */
1224 return make_path(dir
, leaf
)->str
;
1227 /* If action_leaf is not NULL it specifies the new leaf name */
1228 static gboolean
do_copy2(char *path
, char *dest
)
1232 struct stat dest_info
;
1233 gboolean retval
= TRUE
;
1237 dest_path
= make_dest_path(path
, dest
);
1239 if (mc_lstat(path
, &info
))
1245 if (mc_lstat(dest_path
, &dest_info
) == 0)
1250 merge
= S_ISDIR(info
.st_mode
) && S_ISDIR(dest_info
.st_mode
);
1252 g_string_sprintf(message
, _("?'%s' already exists - %s?"),
1254 merge
? _("merge contents") : _("overwrite"));
1256 if (!reply(from_parent
, TRUE
))
1261 if (S_ISDIR(dest_info
.st_mode
))
1262 err
= rmdir(dest_path
);
1264 err
= unlink(dest_path
);
1269 if (errno
!= ENOENT
)
1271 g_string_sprintf(message
,
1272 _("'Trying copy anyway...\n"));
1279 g_string_sprintf(message
,
1280 _("?Copy %s as %s?"), path
, dest_path
);
1281 if (!reply(from_parent
, FALSE
))
1286 g_string_sprintf(message
, _("'Copying %s as %s\n"), path
,
1291 if (S_ISDIR(info
.st_mode
))
1293 mode_t mode
= info
.st_mode
;
1294 char *safe_path
, *safe_dest
;
1295 struct stat dest_info
;
1298 /* (we will do the update ourselves now, rather than
1303 safe_path
= g_strdup(path
);
1304 safe_dest
= g_strdup(dest_path
);
1306 exists
= !mc_lstat(dest_path
, &dest_info
);
1308 if (exists
&& !S_ISDIR(dest_info
.st_mode
))
1310 g_string_sprintf(message
,
1311 _("!ERROR: Destination already exists, "
1312 "but is not a directory\n"));
1314 else if (exists
== FALSE
&& mkdir(dest_path
, 0700 | mode
))
1320 /* (just been created then) */
1321 g_string_sprintf(message
, "+%s", dest
);
1326 for_dir_contents(do_copy2
, safe_path
, safe_dest
);
1327 /* Note: dest_path now invalid... */
1333 /* We may have created the directory with
1334 * more permissions than the source so that
1335 * we could write to it... change it back now.
1337 if (chmod(safe_dest
, mode
))
1340 /* Also, try to preserve the timestamps */
1341 utb
.actime
= info
.st_atime
;
1342 utb
.modtime
= info
.st_mtime
;
1344 utime(safe_dest
, &utb
);
1351 else if (S_ISLNK(info
.st_mode
))
1353 char target
[MAXPATHLEN
+ 1];
1356 /* Not all versions of cp(1) can make symlinks,
1357 * so we special-case it.
1360 count
= readlink(path
, target
, sizeof(target
) - 1);
1368 target
[count
] = '\0';
1369 if (symlink(target
, dest_path
))
1380 error
= copy_file(path
, dest_path
);
1384 g_string_sprintf(message
, _("!ERROR: %s\n"), error
);
1393 /* If action_leaf is not NULL it specifies the new leaf name */
1394 static gboolean
do_move2(char *path
, char *dest
)
1397 gboolean retval
= TRUE
;
1398 char *argv
[] = {"mv", "-f", NULL
, NULL
, NULL
};
1404 dest_path
= make_dest_path(path
, dest
);
1406 is_dir
= mc_lstat(path
, &info2
) == 0 && S_ISDIR(info2
.st_mode
);
1408 if (access(dest_path
, F_OK
) == 0)
1413 g_string_sprintf(message
,
1414 _("?'%s' already exists - overwrite?"),
1416 if (!reply(from_parent
, TRUE
))
1419 if (mc_lstat(dest_path
, &info
))
1425 if (S_ISDIR(info
.st_mode
))
1426 err
= rmdir(dest_path
);
1428 err
= unlink(dest_path
);
1433 if (errno
!= ENOENT
)
1435 g_string_sprintf(message
,
1436 _("'Trying move anyway...\n"));
1442 g_string_sprintf(message
,
1443 _("?Move %s as %s?"), path
, dest_path
);
1444 if (!reply(from_parent
, FALSE
))
1449 g_string_sprintf(message
, _("'Moving %s as %s\n"), path
,
1455 argv
[3] = dest_path
;
1457 if (fork_exec_wait(argv
) == 0)
1461 leaf
= strrchr(path
, '/');
1463 leaf
= path
; /* Error? */
1467 g_string_sprintf(message
, "+%s", path
);
1468 g_string_truncate(message
, leaf
- path
+ 1);
1471 g_string_sprintf(message
, "m%s", path
);
1477 g_string_sprintf(message
,
1478 _("!ERROR: Failed to move %s as %s\n"),
1487 /* Copy path to dest.
1488 * Check that path not copied into itself.
1490 static gboolean
do_copy(char *path
, char *dest
)
1492 if (is_sub_dir(make_dest_path(path
, dest
), path
))
1494 g_string_sprintf(message
,
1495 _("!ERROR: Can't copy object into itself\n"));
1499 return do_copy2(path
, dest
);
1502 /* Move path to dest.
1503 * Check that path not moved into itself.
1505 static gboolean
do_move(char *path
, char *dest
)
1507 if (is_sub_dir(make_dest_path(path
, dest
), path
))
1509 g_string_sprintf(message
,
1510 _("!ERROR: Can't move/rename object into itself\n"));
1514 return do_move2(path
, dest
);
1517 static gboolean
do_link(char *path
, char *dest
)
1524 leaf
= strrchr(path
, '/');
1526 leaf
= path
; /* Error? */
1530 dest_path
= make_path(dest
, leaf
)->str
;
1534 g_string_sprintf(message
, _("'Linking %s as %s\n"), path
,
1540 g_string_sprintf(message
,
1541 _("?Link %s as %s?"), path
, dest_path
);
1542 if (!reply(from_parent
, FALSE
))
1546 if (symlink(path
, dest_path
))
1555 /* Mount/umount this item (depending on 'mount') */
1556 static void do_mount(guchar
*path
, gboolean mount
)
1558 char *argv
[3] = {NULL
, NULL
, NULL
};
1562 argv
[0] = mount
? "mount" : "umount";
1567 g_string_sprintf(message
,
1568 mount
? _("'Mounting %s\n")
1569 : _("'Unmounting %s\n"),
1575 g_string_sprintf(message
,
1576 mount
? _("?Mount %s?\n")
1577 : _("?Unmount %s?\n"),
1579 if (!reply(from_parent
, FALSE
))
1583 if (fork_exec_wait(argv
) == 0)
1585 g_string_sprintf(message
, "M%s", path
);
1587 if (mount
&& mount_open_dir
)
1589 g_string_sprintf(message
, "o%s", path
);
1595 g_string_sprintf(message
, mount
?
1596 _("!ERROR: Mount failed\n") :
1597 _("!ERROR: Unmount failed\n"));
1602 /* CHILD MAIN LOOPS */
1604 /* After forking, the child calls one of these functions */
1606 static void usage_cb(gpointer data
)
1608 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1609 Collection
*collection
= filer_window
->collection
;
1611 int left
= collection
->number_selected
;
1613 off_t total_size
= 0;
1615 send_dir(filer_window
->path
);
1620 if (!collection
->items
[i
].selected
)
1622 item
= (DirItem
*) collection
->items
[i
].data
;
1624 do_usage(make_path(filer_window
->path
,
1625 item
->leafname
)->str
,
1626 filer_window
->path
);
1627 g_string_sprintf(message
, "'%s: %s\n",
1629 format_size((unsigned long) size_tally
));
1631 total_size
+= size_tally
;
1635 g_string_sprintf(message
, _("'\nTotal: %s\n"),
1636 format_size((unsigned long) total_size
));
1640 #ifdef DO_MOUNT_POINTS
1641 static void mount_cb(gpointer data
)
1643 GList
*paths
= (GList
*) data
;
1644 gboolean mount_points
= FALSE
;
1646 for (; paths
; paths
= paths
->next
)
1648 guchar
*path
= (guchar
*) paths
->data
;
1650 if (mount_is_mounted(path
))
1651 do_mount(path
, FALSE
); /* Unmount */
1652 else if (g_hash_table_lookup(fstab_mounts
, path
))
1653 do_mount(path
, TRUE
); /* Mount */
1657 mount_points
= TRUE
;
1660 g_string_sprintf(message
,
1661 mount_points
? _("'\nDone\n")
1662 : _("!No mount points selected!\n"));
1667 static guchar
*dirname(guchar
*path
)
1671 slash
= strrchr(path
, '/');
1672 g_return_val_if_fail(slash
!= NULL
, g_strdup(path
));
1675 return g_strndup(path
, slash
- path
);
1676 return g_strdup("/");
1679 static void delete_cb(gpointer data
)
1681 GList
*paths
= (GList
*) data
;
1685 guchar
*path
= (guchar
*) paths
->data
;
1688 dir
= dirname(path
);
1691 if (do_delete(path
, dir
))
1693 g_string_sprintf(message
, "+%s", dir
);
1698 paths
= paths
->next
;
1701 g_string_sprintf(message
, _("'\nDone\n"));
1705 static void find_cb(gpointer data
)
1707 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1708 Collection
*collection
= filer_window
->collection
;
1713 send_dir(filer_window
->path
);
1717 left
= collection
->number_selected
;
1723 if (!collection
->items
[i
].selected
)
1725 item
= (DirItem
*) collection
->items
[i
].data
;
1726 do_find(make_path(filer_window
->path
,
1727 item
->leafname
)->str
,
1732 g_string_assign(message
, _("?Another search?"));
1733 if (!reply(from_parent
, TRUE
))
1735 g_string_assign(message
, "#");
1739 g_string_sprintf(message
, _("'\nDone\n"));
1743 static void chmod_cb(gpointer data
)
1745 FilerWindow
*filer_window
= (FilerWindow
*) data
;
1746 Collection
*collection
= filer_window
->collection
;
1748 int left
= collection
->number_selected
;
1751 send_dir(filer_window
->path
);
1756 if (!collection
->items
[i
].selected
)
1758 item
= (DirItem
*) collection
->items
[i
].data
;
1759 if (item
->flags
& ITEM_FLAG_SYMLINK
)
1761 g_string_sprintf(message
,
1762 _("!'%s' is a symbolic link\n"),
1766 else if (do_chmod(make_path(filer_window
->path
,
1767 item
->leafname
)->str
, NULL
))
1769 g_string_sprintf(message
, "+%s", filer_window
->path
);
1775 g_string_sprintf(message
, _("'\nDone\n"));
1779 static void list_cb(gpointer data
)
1781 GSList
*paths
= (GSList
*) data
;
1785 send_dir((char *) paths
->data
);
1787 if (action_do_func((char *) paths
->data
, action_dest
))
1789 g_string_sprintf(message
, "+%s", action_dest
);
1793 paths
= paths
->next
;
1796 g_string_sprintf(message
, _("'\nDone\n"));
1800 static void add_toggle(GUIside
*gui_side
, guchar
*label
, guchar
*code
)
1804 check
= gtk_check_button_new_with_label(label
);
1805 gtk_object_set_data(GTK_OBJECT(check
), "send-code", code
);
1806 gtk_signal_connect(GTK_OBJECT(check
), "clicked",
1807 button_reply
, gui_side
);
1808 gtk_box_pack_start(GTK_BOX(gui_side
->vbox
), check
, FALSE
, TRUE
, 0);
1812 /* EXTERNAL INTERFACE */
1814 void action_find(FilerWindow
*filer_window
)
1817 Collection
*collection
;
1818 GtkWidget
*hbox
, *label
, *scroller
;
1821 titles
[0] = _("Name");
1822 titles
[1] = _("Directory");
1824 collection
= filer_window
->collection
;
1826 if (collection
->number_selected
< 1)
1828 report_error(PROJECT
, _("You need to select some items "
1829 "to search through"));
1833 if (!last_find_string
)
1834 last_find_string
= g_strdup("'core'");
1836 new_entry_string
= last_find_string
;
1837 gui_side
= start_action(filer_window
, find_cb
, FALSE
);
1841 gui_side
->show_info
= TRUE
;
1842 gui_side
->entry_string_func
= set_find_string_colour
;
1844 scroller
= gtk_scrolled_window_new(NULL
, NULL
);
1845 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller
),
1846 GTK_POLICY_NEVER
, GTK_POLICY_ALWAYS
);
1847 gtk_box_pack_start(GTK_BOX(gui_side
->vbox
), scroller
, TRUE
, TRUE
, 4);
1848 gui_side
->results
= gtk_clist_new_with_titles(
1849 sizeof(titles
) / sizeof(*titles
), titles
);
1850 gtk_clist_column_titles_passive(GTK_CLIST(gui_side
->results
));
1851 gtk_widget_set_usize(gui_side
->results
, 100, 100);
1852 gtk_clist_set_column_width(GTK_CLIST(gui_side
->results
), 0, 100);
1853 gtk_clist_set_selection_mode(GTK_CLIST(gui_side
->results
),
1854 GTK_SELECTION_SINGLE
);
1855 gtk_container_add(GTK_CONTAINER(scroller
), gui_side
->results
);
1856 gtk_box_set_child_packing(GTK_BOX(gui_side
->vbox
),
1857 gui_side
->log_hbox
, FALSE
, TRUE
, 4, GTK_PACK_START
);
1858 gtk_signal_connect(GTK_OBJECT(gui_side
->results
), "select_row",
1859 GTK_SIGNAL_FUNC(select_row_callback
), gui_side
);
1861 hbox
= gtk_hbox_new(FALSE
, 0);
1862 label
= gtk_label_new(_("Expression:"));
1863 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, TRUE
, 4);
1864 gui_side
->default_string
= &last_find_string
;
1865 gui_side
->entry
= gtk_entry_new();
1866 gtk_widget_set_style(gui_side
->entry
, fixed_style
);
1867 gtk_entry_set_text(GTK_ENTRY(gui_side
->entry
), last_find_string
);
1868 set_find_string_colour(gui_side
->entry
, last_find_string
);
1869 gtk_editable_select_region(GTK_EDITABLE(gui_side
->entry
), 0, -1);
1870 gtk_widget_set_sensitive(gui_side
->entry
, FALSE
);
1871 gtk_box_pack_start(GTK_BOX(hbox
), gui_side
->entry
, TRUE
, TRUE
, 4);
1872 gtk_signal_connect(GTK_OBJECT(gui_side
->entry
), "changed",
1873 entry_changed
, gui_side
);
1874 gtk_box_pack_start(GTK_BOX(gui_side
->vbox
), hbox
, FALSE
, TRUE
, 4);
1875 gtk_box_pack_start(GTK_BOX(hbox
),
1876 new_help_button(show_condition_help
, NULL
),
1879 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), _("Find"));
1880 gtk_window_set_focus(GTK_WINDOW(gui_side
->window
), gui_side
->entry
);
1881 gtk_signal_connect_object(GTK_OBJECT(gui_side
->entry
), "activate",
1882 gtk_button_clicked
, GTK_OBJECT(gui_side
->quiet
));
1883 number_of_windows
++;
1884 gtk_widget_show_all(gui_side
->window
);
1887 /* Count disk space used by selected items */
1888 void action_usage(FilerWindow
*filer_window
)
1891 Collection
*collection
;
1893 collection
= filer_window
->collection
;
1895 if (collection
->number_selected
< 1)
1897 report_error(PROJECT
,
1898 _("You need to select some items to count"));
1902 gui_side
= start_action(filer_window
, usage_cb
, TRUE
);
1906 gui_side
->show_info
= TRUE
;
1908 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), _("Disk Usage"));
1909 number_of_windows
++;
1910 gtk_widget_show_all(gui_side
->window
);
1913 /* Mount/unmount listed items (paths).
1914 * Free the list after this function returns.
1915 * If open_dir is TRUE and the dir is successfully mounted, open it.
1917 void action_mount(GList
*paths
, gboolean open_dir
)
1919 #ifdef DO_MOUNT_POINTS
1922 mount_open_dir
= open_dir
;
1923 gui_side
= start_action(paths
, mount_cb
,
1924 option_get_int("action_mount"));
1928 gtk_window_set_title(GTK_WINDOW(gui_side
->window
),
1929 _("Mount / Unmount"));
1930 number_of_windows
++;
1931 gtk_widget_show_all(gui_side
->window
);
1933 report_error(PROJECT
,
1934 _("ROX-Filer does not yet support mount points on your "
1936 #endif /* DO_MOUNT_POINTS */
1939 /* Deletes all selected items in the window */
1940 void action_delete(FilerWindow
*filer_window
)
1945 paths
= filer_selected_items(filer_window
);
1947 if (!remove_pinned_ok(paths
))
1950 gui_side
= start_action(paths
, delete_cb
,
1951 option_get_int("action_delete"));
1955 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), _("Delete"));
1956 add_toggle(gui_side
,
1957 _("Force - don't confirm deletion of non-writeable items"),
1959 add_toggle(gui_side
,
1960 _("Brief - only log directories being deleted"),
1963 number_of_windows
++;
1964 gtk_widget_show_all(gui_side
->window
);
1967 g_list_foreach(paths
, (GFunc
) g_free
, NULL
);
1971 /* Change the permissions of the selected items */
1972 void action_chmod(FilerWindow
*filer_window
)
1975 Collection
*collection
;
1976 GtkWidget
*hbox
, *label
, *combo
;
1977 static GList
*presets
= NULL
;
1979 collection
= filer_window
->collection
;
1981 if (collection
->number_selected
< 1)
1983 report_error(PROJECT
,
1984 _("You need to select the items "
1985 "whose permissions you want to change"));
1991 presets
= g_list_append(presets
,
1992 _("a+x (Make executable/searchable)"));
1993 presets
= g_list_append(presets
,
1994 _("a-x (Make non-executable/non-searchable)"));
1995 presets
= g_list_append(presets
,
1996 _("u+rw (Give owner read+write)"));
1997 presets
= g_list_append(presets
,
1998 _("go-rwx (Private - owner access only)"));
1999 presets
= g_list_append(presets
,
2000 _("go=u-w (Public access, not write)"));
2003 if (!last_chmod_string
)
2004 last_chmod_string
= g_strdup((guchar
*) presets
->data
);
2005 new_entry_string
= last_chmod_string
;
2006 gui_side
= start_action(filer_window
, chmod_cb
, FALSE
);
2010 add_toggle(gui_side
,
2011 _("Brief - don't list processed files"), "B");
2012 add_toggle(gui_side
,
2013 _("Recurse - also change contents of subdirectories"), "R");
2015 hbox
= gtk_hbox_new(FALSE
, 0);
2016 label
= gtk_label_new(_("Command:"));
2017 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, TRUE
, 4);
2018 gui_side
->default_string
= &last_chmod_string
;
2020 combo
= gtk_combo_new();
2021 gtk_combo_disable_activate(GTK_COMBO(combo
));
2022 gtk_combo_set_use_arrows_always(GTK_COMBO(combo
), TRUE
);
2023 gtk_combo_set_popdown_strings(GTK_COMBO(combo
), presets
);
2025 gui_side
->entry
= GTK_COMBO(combo
)->entry
;
2026 gtk_entry_set_text(GTK_ENTRY(gui_side
->entry
), last_chmod_string
);
2027 gtk_editable_select_region(GTK_EDITABLE(gui_side
->entry
), 0, -1);
2028 gtk_widget_set_sensitive(gui_side
->entry
, FALSE
);
2029 gtk_box_pack_start(GTK_BOX(hbox
), combo
, TRUE
, TRUE
, 4);
2030 gtk_signal_connect(GTK_OBJECT(gui_side
->entry
), "changed",
2031 entry_changed
, gui_side
);
2032 gtk_box_pack_start(GTK_BOX(gui_side
->vbox
), hbox
, FALSE
, TRUE
, 0);
2033 gtk_box_pack_start(GTK_BOX(hbox
),
2034 new_help_button(show_chmod_help
, NULL
),
2037 gtk_window_set_focus(GTK_WINDOW(gui_side
->window
), gui_side
->entry
);
2038 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), _("Permissions"));
2039 gtk_signal_connect_object(GTK_OBJECT(gui_side
->entry
), "activate",
2040 gtk_button_clicked
, GTK_OBJECT(gui_side
->yes
));
2042 number_of_windows
++;
2043 gtk_widget_show_all(gui_side
->window
);
2046 /* If leaf is NULL then the copy has the same name as the original */
2047 void action_copy(GSList
*paths
, char *dest
, char *leaf
)
2053 action_do_func
= do_copy
;
2054 gui_side
= start_action(paths
, list_cb
, option_get_int("action_copy"));
2058 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), _("Copy"));
2059 number_of_windows
++;
2060 gtk_widget_show_all(gui_side
->window
);
2063 /* If leaf is NULL then the file is not renamed */
2064 void action_move(GSList
*paths
, char *dest
, char *leaf
)
2070 action_do_func
= do_move
;
2071 gui_side
= start_action(paths
, list_cb
, option_get_int("action_move"));
2075 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), _("Move"));
2076 number_of_windows
++;
2077 gtk_widget_show_all(gui_side
->window
);
2080 void action_link(GSList
*paths
, char *dest
)
2085 action_do_func
= do_link
;
2086 gui_side
= start_action(paths
, list_cb
, option_get_int("action_link"));
2090 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), _("Link"));
2091 number_of_windows
++;
2092 gtk_widget_show_all(gui_side
->window
);
2095 void action_init(void)
2097 option_add_int("action_copy", 1, NULL
);
2098 option_add_int("action_move", 1, NULL
);
2099 option_add_int("action_link", 1, NULL
);
2100 option_add_int("action_delete", 0, NULL
);
2101 option_add_int("action_mount", 1, NULL
);
2106 /* Check to see if any of the selected items (or their children) are
2107 * on the pinboard or panel. If so, ask for confirmation.
2109 * TRUE if it's OK to lose them.
2111 static gboolean
remove_pinned_ok(GList
*paths
)
2113 GList
*ask
= NULL
, *next
;
2120 guchar
*path
= (guchar
*) paths
->data
;
2122 if (icons_require(path
))
2124 if (++ask_n
> MAX_ASK
)
2126 ask
= g_list_append(ask
, path
);
2129 paths
= paths
->next
;
2135 if (ask_n
> MAX_ASK
)
2137 message
= g_string_new(_("Deleting items such as "));
2140 else if (ask_n
== 1)
2141 message
= g_string_new(_("Deleting the item "));
2143 message
= g_string_new(_("Deleting the items "));
2146 for (next
= ask
; next
; next
= next
->next
)
2148 guchar
*path
= (guchar
*) next
->data
;
2151 leaf
= strrchr(path
, '/');
2157 g_string_append_c(message
, '`');
2158 g_string_append(message
, leaf
);
2159 g_string_append_c(message
, '\'');
2161 if (i
== ask_n
- 1 && i
> 0)
2162 g_string_append(message
, _(" and "));
2164 g_string_append(message
, ", ");
2170 message
= g_string_append(message
,
2171 _(" will affect some items on the pinboard "
2172 "or panel - really delete it?"));
2175 if (ask_n
> MAX_ASK
)
2176 message
= g_string_append_c(message
, ',');
2177 message
= g_string_append(message
,
2178 _(" will affect some items on the pinboard "
2179 "or panel - really delete them?"));
2182 retval
= get_choice(PROJECT
, message
->str
,
2183 2, _("OK"), _("Cancel")) == 0;
2185 g_string_free(message
, TRUE
);
2190 void set_find_string_colour(GtkWidget
*widget
, guchar
*string
)
2192 static GtkStyle
*error_style
= NULL
;
2193 FindCondition
*cond
;
2197 error_style
= gtk_style_copy(fixed_style
);
2198 error_style
->fg
[GTK_STATE_NORMAL
] = red
;
2201 cond
= find_compile(string
);
2202 gtk_widget_set_style(widget
, cond
? fixed_style
: error_style
);
2205 find_condition_free(cond
);