4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, 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.
26 /* Allow use of GtkText widget */
27 /* #define GTK_ENABLE_BROKEN */
34 #include <sys/param.h>
45 #include "gui_support.h"
50 #include "modechange.h"
56 /* Parent->Child messages are one character each:
58 * Y/N Yes/No button clicked
59 * F Force deletion of non-writeable items
61 * E Entry text changed
64 #define SENSITIVE_YESNO(gui_side, state) \
66 gtk_widget_set_sensitive((gui_side)->yes, state); \
67 gtk_widget_set_sensitive((gui_side)->no, state); \
68 if ((gui_side)->entry) \
69 gtk_widget_set_sensitive((gui_side)->entry, state);\
72 typedef struct _GUIside GUIside
;
73 typedef void ActionChild(gpointer data
);
74 typedef gboolean
ForDirCB(char *path
, char *dest_path
);
78 int from_child
; /* File descriptor */
80 int input_tag
; /* gdk_input_add() */
82 GtkWidget
*vbox
, *window
, *dir
, *log_hbox
, *flag_box
;
83 GtkWidget
*quiet
, *yes
, *no
, *quiet_flag
;
84 int child
; /* Process ID */
86 gboolean show_info
; /* For Disk Usage */
88 GtkWidget
*entry
; /* May be NULL */
89 guchar
**default_string
; /* Changed when the entry changes */
90 void (*entry_string_func
)(GtkWidget
*widget
, guchar
*string
);
92 char *next_dir
; /* NULL => no timer active */
100 /* These don't need to be in a structure because we fork() before
103 static gboolean mount_open_dir
= FALSE
;
104 static int from_parent
= 0;
105 static FILE *to_parent
= NULL
;
106 static gboolean quiet
= FALSE
;
107 static GString
*message
= NULL
;
108 static char *action_dest
= NULL
;
109 static char *action_leaf
= NULL
;
110 static gboolean (*action_do_func
)(char *source
, char *dest
);
111 static double size_tally
; /* For Disk Usage */
112 static unsigned long dir_counter
; /* For Disk Usage */
113 static unsigned long file_counter
; /* For Disk Usage */
115 static struct mode_change
*mode_change
= NULL
; /* For Permissions */
116 static FindCondition
*find_condition
= NULL
; /* For Find */
118 /* Only used by child */
119 static gboolean o_force
= FALSE
;
120 static gboolean o_brief
= FALSE
;
121 static gboolean o_recurse
= FALSE
;
123 /* Whenever the text in these boxes is changed we store a copy of the new
124 * string to be used as the default next time.
126 static guchar
*last_chmod_string
= NULL
;
127 static guchar
*last_find_string
= NULL
;
129 /* Set to one of the above before forking. This may change over a call to
130 * reply(). It is reset to NULL once the text is parsed.
132 static guchar
*new_entry_string
= NULL
;
134 /* Static prototypes */
135 static gboolean
send();
136 static gboolean
send_error();
137 static gboolean
send_dir(char *dir
);
138 static gboolean
read_exact(int source
, char *buffer
, ssize_t len
);
139 static void do_mount(guchar
*path
, gboolean mount
);
140 static GtkWidget
*add_toggle(GUIside
*gui_side
,
141 guchar
*label
, guchar
*tip
,
144 static gboolean
reply(int fd
, gboolean ignore_quiet
);
145 static gboolean
remove_pinned_ok(GList
*paths
);
149 static void preview_closed(GtkWidget
*window
, GUIside
*gui_side
)
151 gui_side
->preview
= NULL
;
154 static void select_row_callback(GtkWidget
*widget
,
155 gint row
, gint column
,
156 GdkEventButton
*event
,
161 gtk_clist_get_text(GTK_CLIST(gui_side
->results
), row
, 0, &leaf
);
162 gtk_clist_get_text(GTK_CLIST(gui_side
->results
), row
, 1, &dir
);
164 gtk_clist_unselect_row(GTK_CLIST(gui_side
->results
), row
, column
);
166 if (gui_side
->preview
)
168 if (strcmp(gui_side
->preview
->path
, dir
) == 0)
169 display_set_autoselect(gui_side
->preview
, leaf
);
171 filer_change_to(gui_side
->preview
, dir
, leaf
);
175 gui_side
->preview
= filer_opendir(dir
, NULL
);
176 if (gui_side
->preview
)
178 display_set_autoselect(gui_side
->preview
, leaf
);
179 gtk_signal_connect(GTK_OBJECT(gui_side
->preview
->window
),
181 GTK_SIGNAL_FUNC(preview_closed
), gui_side
);
186 /* This is called whenever the user edits the entry box (if any) - send the
189 static void entry_changed(GtkEditable
*entry
, GUIside
*gui_side
)
193 g_return_if_fail(gui_side
->default_string
!= NULL
);
195 text
= gtk_editable_get_chars(entry
, 0, -1);
197 if (gui_side
->entry_string_func
)
198 gui_side
->entry_string_func(GTK_WIDGET(gui_side
->entry
), text
);
200 g_free(*(gui_side
->default_string
));
201 *(gui_side
->default_string
) = text
; /* Gets text's ref */
203 if (!gui_side
->to_child
)
206 fputc('E', gui_side
->to_child
);
207 fputs(text
, gui_side
->to_child
);
208 fputc('\n', gui_side
->to_child
);
209 fflush(gui_side
->to_child
);
212 void show_condition_help(gpointer data
)
214 static GtkWidget
*help
= NULL
;
218 GtkWidget
*text
, *vbox
, *button
, *hbox
, *frame
;
220 help
= gtk_window_new(GTK_WINDOW_DIALOG
);
222 gtk_window_set_type_hint(GTK_WINDOW(help
),
223 GDK_WINDOW_TYPE_HINT_DIALOG
);
225 gtk_container_set_border_width(GTK_CONTAINER(help
), 10);
226 gtk_window_set_title(GTK_WINDOW(help
),
227 _("Find expression reference"));
229 vbox
= gtk_vbox_new(FALSE
, 0);
230 gtk_container_add(GTK_CONTAINER(help
), vbox
);
232 frame
= gtk_frame_new(_("Quick Start"));
233 text
= gtk_label_new(
234 _("Just put the name of the file you're looking for in single quotes:\n"
235 "'index.html' (to find a file called 'index.html')"));
236 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
237 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
238 gtk_container_add(GTK_CONTAINER(frame
), text
);
239 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
241 frame
= gtk_frame_new(_("Examples"));
242 text
= gtk_label_new(
243 _("'*.htm', '*.html' (finds HTML files)\n"
244 "IsDir 'lib' (finds directories called 'lib')\n"
245 "IsReg 'core' (finds a regular file called 'core')\n"
246 "! (IsDir, IsReg) (is neither a directory nor a regular file)\n"
247 "mtime after 1 day ago and size > 1Mb (big, and recently modified)\n"
248 "'CVS' prune, isreg (a regular file not in CVS)\n"
249 "IsReg system(grep -q fred \"%\") (contains the word 'fred')"));
250 gtk_widget_set_name(text
, "fixed-font");
251 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
252 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
253 gtk_container_add(GTK_CONTAINER(frame
), text
);
254 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
256 frame
= gtk_frame_new(_("Simple Tests"));
257 text
= gtk_label_new(
258 _("IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket (types)\n"
259 "IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable (permissions)"
263 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
264 "contains a slash then the match is against the full path; otherwise it is \n"
265 "against the leafname only."));
266 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
267 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
268 gtk_container_add(GTK_CONTAINER(frame
), text
);
269 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
271 frame
= gtk_frame_new(_("Comparisons"));
272 text
= gtk_label_new(
273 _("<, <=, =, !=, >, >=, After, Before (compare two values)\n"
274 "5 bytes, 1Kb, 2Mb, 3Gb (file sizes)\n"
275 "2 secs|mins|hours|days|weeks|years ago|hence (times)\n"
276 "atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks (values)"));
277 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
278 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
279 gtk_container_add(GTK_CONTAINER(frame
), text
);
280 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
282 frame
= gtk_frame_new(_("Specials"));
283 text
= gtk_label_new(
284 _("system(command) (true if 'command' returns with a zero exit status; a % \n"
285 "in 'command' is replaced with the path of the current file)\n"
286 "prune (false, and prevents searching the contents of a directory).")
288 gtk_misc_set_padding(GTK_MISC(text
), 4, 4);
289 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
290 gtk_container_add(GTK_CONTAINER(frame
), text
);
291 gtk_box_pack_start(GTK_BOX(vbox
), frame
, FALSE
, FALSE
, 4);
293 hbox
= gtk_hbox_new(FALSE
, 20);
294 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, TRUE
, TRUE
, 0);
296 text
= gtk_label_new(
297 _("See the ROX-Filer manual for full details."));
298 gtk_box_pack_start(GTK_BOX(hbox
), text
, TRUE
, TRUE
, 0);
299 button
= gtk_button_new_with_label(_("Close"));
300 gtk_box_pack_start(GTK_BOX(hbox
), button
, FALSE
, TRUE
, 0);
301 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
302 GTK_SIGNAL_FUNC(gtk_widget_hide
), GTK_OBJECT(help
));
304 gtk_signal_connect_object(GTK_OBJECT(help
), "delete_event",
305 GTK_SIGNAL_FUNC(gtk_widget_hide
), GTK_OBJECT(help
));
308 if (GTK_WIDGET_VISIBLE(help
))
309 gtk_widget_hide(help
);
310 gtk_widget_show_all(help
);
313 static void show_chmod_help(gpointer data
)
315 static GtkWidget
*help
= NULL
;
319 GtkWidget
*text
, *vbox
, *button
, *hbox
, *sep
;
321 help
= gtk_window_new(GTK_WINDOW_DIALOG
);
323 gtk_window_set_type_hint(GTK_WINDOW(help
),
324 GDK_WINDOW_TYPE_HINT_DIALOG
);
326 gtk_container_set_border_width(GTK_CONTAINER(help
), 10);
327 gtk_window_set_title(GTK_WINDOW(help
),
328 _("Permissions command reference"));
330 vbox
= gtk_vbox_new(FALSE
, 0);
331 gtk_container_add(GTK_CONTAINER(help
), vbox
);
333 text
= gtk_label_new(
334 _("Normally, you can just select a command from the menu (click \n"
335 "on the arrow beside the command box). Sometimes, you need more...\n"
337 "The format of a command is:\n"
338 "CHANGE, CHANGE, ...\n"
340 "WHO HOW PERMISSIONS\n"
341 "WHO is some combination of u, g and o which determines whether to\n"
342 "change the permissions for the User (owner), Group or Others.\n"
343 "HOW is +, - or = to add, remove or set exactly the permissions.\n"
344 "PERMISSIONS is some combination of the letters 'rwxXstugo'\n\n"
346 "Bracketed text and spaces are ignored.\n\n"
349 "u+rw (the file owner gains read and write permission)\n"
350 "g=u (the group permissions are set to be the same as the user's)\n"
351 "o=u-w (others get the same permissions as the owner, but without "
352 "write permission)\n"
353 "a+x (everyone gets execute/access permission - same as 'ugo+x')\n"
354 "a+X (directories become accessable by everyone; files which were\n"
355 "executable by anyone become executable by everyone)\n"
356 "u+rw, go+r (two commands at once!)\n"
357 "u+s (set the SetUID bit - often has no effect on script files)\n"
358 "755 (set the permissions directly)\n"
360 "\nSee the chmod(1) man page for full details."));
361 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
362 gtk_box_pack_start(GTK_BOX(vbox
), text
, TRUE
, TRUE
, 0);
364 hbox
= gtk_hbox_new(FALSE
, 20);
365 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, TRUE
, 0);
367 sep
= gtk_hseparator_new();
368 gtk_box_pack_start(GTK_BOX(hbox
), sep
, TRUE
, TRUE
, 0);
369 button
= gtk_button_new_with_label(_("Close"));
370 gtk_box_pack_start(GTK_BOX(hbox
), button
, FALSE
, TRUE
, 0);
371 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
372 GTK_SIGNAL_FUNC(gtk_widget_hide
), GTK_OBJECT(help
));
374 gtk_signal_connect_object(GTK_OBJECT(help
), "delete_event",
375 GTK_SIGNAL_FUNC(gtk_widget_hide
), GTK_OBJECT(help
));
378 if (GTK_WIDGET_VISIBLE(help
))
379 gtk_widget_hide(help
);
380 gtk_widget_show_all(help
);
383 static gboolean
display_dir(gpointer data
)
385 GUIside
*gui_side
= (GUIside
*) data
;
387 gtk_label_set_text(GTK_LABEL(gui_side
->dir
), gui_side
->next_dir
+ 1);
388 g_free(gui_side
->next_dir
);
389 gui_side
->next_dir
= NULL
;
394 static void add_to_results(GUIside
*gui_side
, gchar
*path
)
396 gchar
*row
[] = {"Leaf", "Dir"};
400 slash
= strrchr(path
, '/');
401 g_return_if_fail(slash
!= NULL
);
404 row
[1] = g_strndup(path
, MAX(len
, 1));
407 gtk_clist_append(GTK_CLIST(gui_side
->results
), row
);
412 /* Called when the child sends us a message */
413 static void message_from_child(gpointer data
,
415 GdkInputCondition condition
)
418 GUIside
*gui_side
= (GUIside
*) data
;
420 GtkTextBuffer
*text_buffer
;
423 text_buffer
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(gui_side
->log
));
426 if (read_exact(source
, buf
, 4))
432 message_len
= strtol(buf
, NULL
, 16);
433 buffer
= g_malloc(message_len
+ 1);
434 if (message_len
> 0 && read_exact(source
, buffer
, message_len
))
436 buffer
[message_len
] = '\0';
440 SENSITIVE_YESNO(gui_side
, TRUE
);
441 gtk_window_set_focus(
442 GTK_WINDOW(gui_side
->window
),
443 gui_side
->entry
? gui_side
->entry
446 else if (*buffer
== '+')
448 /* Update/rescan this item */
449 refresh_dirs(buffer
+ 1);
453 else if (*buffer
== '=')
455 /* Add to search results */
456 add_to_results(gui_side
, buffer
+ 1);
460 else if (*buffer
== '#')
462 /* Clear search results area */
463 gtk_clist_clear(GTK_CLIST(gui_side
->results
));
468 else if (*buffer
== 'm' || *buffer
== 'M')
470 /* Mount / major changes to this path */
473 filer_check_mounted(buffer
+ 1);
477 else if (*buffer
== '/')
479 /* Update the current object display */
480 if (gui_side
->next_dir
)
481 g_free(gui_side
->next_dir
);
483 gui_side
->next_timer
=
487 gui_side
->next_dir
= buffer
;
490 else if (*buffer
== 'o')
492 /* Open a filer window */
493 filer_opendir(buffer
+ 1, NULL
);
497 else if (*buffer
== '!')
501 gtk_text_buffer_get_end_iter(text_buffer
, &end
);
502 gtk_text_buffer_insert_with_tags_by_name(text_buffer
,
503 &end
, buffer
+ 1, message_len
- 1,
504 *buffer
== '!' ? "red" : NULL
, NULL
);
505 /* Causes a crash sometimes!
506 gtk_text_view_scroll_to_iter(
507 GTK_TEXT_VIEW(gui_side->log),
512 gtk_text_insert(GTK_TEXT(gui_side
->log
),
514 *buffer
== '!' ? &red
: NULL
,
516 buffer
+ 1, message_len
- 1);
521 g_printerr("Child died in the middle of a message.\n");
524 /* The child is dead */
527 fclose(gui_side
->to_child
);
528 gui_side
->to_child
= NULL
;
529 close(gui_side
->from_child
);
530 gdk_input_remove(gui_side
->input_tag
);
531 gtk_widget_set_sensitive(gui_side
->quiet
, FALSE
);
533 if (gui_side
->errors
)
537 if (gui_side
->errors
== 1)
538 report
= g_strdup(_("There was one error.\n"));
540 report
= g_strdup_printf(_("There were %d errors.\n"),
544 gtk_text_buffer_insert_at_cursor(text_buffer
, report
, -1);
546 gtk_text_insert(GTK_TEXT(gui_side
->log
), NULL
, &red
, NULL
,
552 else if (gui_side
->show_info
== FALSE
)
553 gtk_widget_destroy(gui_side
->window
);
556 /* Scans src_dir, updating dest_path whenever cb returns TRUE */
557 static void for_dir_contents(ForDirCB
*cb
, char *src_dir
, char *dest_path
)
561 GList
*list
= NULL
, *next
;
563 d
= mc_opendir(src_dir
);
566 /* Message displayed is "ERROR reading 'path': message" */
567 g_string_sprintf(message
, "!%s '%s': %s\n",
569 src_dir
, g_strerror(errno
));
576 while ((ent
= mc_readdir(d
)))
578 if (ent
->d_name
[0] == '.' && (ent
->d_name
[1] == '\0'
579 || (ent
->d_name
[1] == '.' && ent
->d_name
[2] == '\0')))
581 list
= g_list_append(list
, g_strdup(make_path(src_dir
,
593 if (cb((char *) next
->data
, dest_path
))
595 g_string_sprintf(message
, "+%s", dest_path
);
606 /* Read this many bytes into the buffer. TRUE on success. */
607 static gboolean
read_exact(int source
, char *buffer
, ssize_t len
)
612 got
= read(source
, buffer
, len
);
621 /* Send 'message' to our parent process. TRUE on success. */
622 static gboolean
send(void)
627 g_return_val_if_fail(message
->len
< 0xffff, FALSE
);
629 sprintf(len_buffer
, "%04x", message
->len
);
630 fwrite(len_buffer
, 1, 4, to_parent
);
631 len
= fwrite(message
->str
, 1, message
->len
, to_parent
);
633 return len
== message
->len
;
636 /* Set the directory indicator at the top of the window */
637 static gboolean
send_dir(char *dir
)
639 g_string_sprintf(message
, "/%s", dir
);
643 static gboolean
send_error(void)
645 g_string_sprintf(message
, "!%s: %s\n", _("ERROR"), g_strerror(errno
));
649 static void quiet_clicked(GtkWidget
*button
, GUIside
*gui_side
)
651 GtkToggleButton
*quiet_flag
= GTK_TOGGLE_BUTTON(gui_side
->quiet_flag
);
653 if (!gui_side
->to_child
)
656 gtk_widget_set_sensitive(gui_side
->quiet
, FALSE
);
658 if (gtk_toggle_button_get_active(quiet_flag
))
659 return; /* (shouldn't happen) */
661 gtk_toggle_button_set_active(quiet_flag
, TRUE
);
663 if (GTK_WIDGET_SENSITIVE(gui_side
->yes
))
664 gtk_button_clicked(GTK_BUTTON(gui_side
->yes
));
667 /* Send 'Quiet' if possible, 'Yes' otherwise */
668 static void find_return_pressed(GtkWidget
*button
, GUIside
*gui_side
)
670 if (GTK_WIDGET_SENSITIVE(gui_side
->quiet
))
671 gtk_button_clicked(GTK_BUTTON(gui_side
->quiet
));
672 else if (GTK_WIDGET_SENSITIVE(gui_side
->yes
))
673 gtk_button_clicked(GTK_BUTTON(gui_side
->yes
));
678 static void button_reply(GtkWidget
*button
, GUIside
*gui_side
)
682 if (!gui_side
->to_child
)
685 text
= gtk_object_get_data(GTK_OBJECT(button
), "send-code");
686 g_return_if_fail(text
!= NULL
);
687 fputc(*text
, gui_side
->to_child
);
688 fflush(gui_side
->to_child
);
690 if (*text
== 'Y' || *text
== 'N')
691 SENSITIVE_YESNO(gui_side
, FALSE
);
694 gtk_widget_set_sensitive(gui_side
->quiet
,
695 !gtk_toggle_button_get_active(
696 GTK_TOGGLE_BUTTON(gui_side
->quiet_flag
)));
700 static void read_new_entry_text(void)
706 new = g_string_new(NULL
);
710 len
= read(from_parent
, &c
, 1);
713 fprintf(stderr
, "read() error: %s\n",
715 _exit(1); /* Parent died? */
720 g_string_append_c(new, c
);
723 g_free(new_entry_string
);
724 new_entry_string
= new->str
;
725 g_string_free(new, FALSE
);
728 static void process_flag(char flag
)
739 o_recurse
= !o_recurse
;
745 read_new_entry_text();
748 g_string_sprintf(message
,
749 "!ERROR: Bad message '%c'\n", flag
);
755 /* If the parent has sent any flag toggles, read them */
756 static void check_flags(void)
767 FD_SET(from_parent
, &set
);
770 got
= select(from_parent
+ 1, &set
, NULL
, NULL
, &tv
);
773 g_error("select() failed: %s\n", g_strerror(errno
));
777 got
= read(from_parent
, &retval
, 1);
779 g_error("read() error: %s\n", g_strerror(errno
));
781 process_flag(retval
);
785 /* Read until the user sends a reply. If ignore_quiet is TRUE then
786 * the user MUST click Yes or No, else treat quiet on as Yes.
787 * If the user needs prompting then does send().
789 static gboolean
reply(int fd
, gboolean ignore_quiet
)
794 if (quiet
&& !ignore_quiet
)
801 len
= read(fd
, &retval
, 1);
804 fprintf(stderr
, "read() error: %s\n",
806 _exit(1); /* Parent died? */
812 g_string_sprintf(message
, "' %s\n", _("Yes"));
816 g_string_sprintf(message
, "' %s\n", _("No"));
820 process_flag(retval
);
826 static void destroy_action_window(GtkWidget
*widget
, gpointer data
)
828 GUIside
*gui_side
= (GUIside
*) data
;
832 kill(gui_side
->child
, SIGTERM
);
833 fclose(gui_side
->to_child
);
834 close(gui_side
->from_child
);
835 gdk_input_remove(gui_side
->input_tag
);
838 if (gui_side
->next_dir
)
840 gtk_timeout_remove(gui_side
->next_timer
);
841 g_free(gui_side
->next_dir
);
844 if (gui_side
->preview
)
846 gtk_signal_disconnect_by_data(
847 GTK_OBJECT(gui_side
->preview
->window
),
849 gui_side
->preview
= NULL
;
854 if (--number_of_windows
< 1)
858 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
859 * (NULL on failure). The child calls func().
861 * If autoq then automatically selects 'Quiet'.
863 static GUIside
*start_action_with_options(gpointer data
, ActionChild
*func
,
865 int force
, int brief
, int recurse
)
867 int filedes
[4]; /* 0 and 2 are for reading */
870 GtkWidget
*vbox
, *button
, *scrollbar
, *actions
, *text
;
871 struct sigaction act
;
878 report_error("pipe: %s", g_strerror(errno
));
882 if (pipe(filedes
+ 2))
886 report_error("pipe: %s", g_strerror(errno
));
898 report_error("fork: %s", g_strerror(errno
));
901 /* We are the child */
905 /* Reset the SIGCHLD handler */
906 act
.sa_handler
= SIG_DFL
;
907 sigemptyset(&act
.sa_mask
);
909 sigaction(SIGCHLD
, &act
, NULL
);
911 message
= g_string_new(NULL
);
914 to_parent
= fdopen(filedes
[1], "wb");
915 from_parent
= filedes
[2];
921 /* We are the parent */
924 gui_side
= g_malloc(sizeof(GUIside
));
925 gui_side
->from_child
= filedes
[0];
926 gui_side
->to_child
= fdopen(filedes
[3], "wb");
927 gui_side
->log
= NULL
;
928 gui_side
->child
= child
;
929 gui_side
->errors
= 0;
930 gui_side
->show_info
= FALSE
;
931 gui_side
->preview
= NULL
;
932 gui_side
->results
= NULL
;
933 gui_side
->entry
= NULL
;
934 gui_side
->default_string
= NULL
;
935 gui_side
->entry_string_func
= NULL
;
937 gui_side
->window
= gtk_window_new(GTK_WINDOW_DIALOG
);
939 gtk_window_set_type_hint(GTK_WINDOW(gui_side
->window
),
940 GDK_WINDOW_TYPE_HINT_DIALOG
);
942 gtk_container_set_border_width(GTK_CONTAINER(gui_side
->window
), 2);
943 gtk_window_set_default_size(GTK_WINDOW(gui_side
->window
), 450, 200);
944 gtk_signal_connect(GTK_OBJECT(gui_side
->window
), "destroy",
945 GTK_SIGNAL_FUNC(destroy_action_window
), gui_side
);
947 gui_side
->vbox
= vbox
= gtk_vbox_new(FALSE
, 0);
948 gtk_container_add(GTK_CONTAINER(gui_side
->window
), vbox
);
950 gui_side
->dir
= gtk_label_new(_("<dir>"));
951 gtk_widget_set_usize(gui_side
->dir
, 8, -1);
952 gui_side
->next_dir
= NULL
;
953 gtk_misc_set_alignment(GTK_MISC(gui_side
->dir
), 0.5, 0.5);
954 gtk_box_pack_start(GTK_BOX(vbox
), gui_side
->dir
, FALSE
, TRUE
, 0);
956 gui_side
->log_hbox
= gtk_hbox_new(FALSE
, 0);
957 gtk_box_pack_start(GTK_BOX(vbox
), gui_side
->log_hbox
, TRUE
, TRUE
, 4);
960 frame
= gtk_frame_new(NULL
);
961 gtk_box_pack_start(GTK_BOX(gui_side
->log_hbox
), frame
, TRUE
, TRUE
, 0);
963 text
= gtk_text_view_new();
964 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_IN
);
965 gtk_container_add(GTK_CONTAINER(frame
), text
);
967 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text
), GTK_WRAP_WORD
);
968 gui_side
->log
= text
;
969 scrollbar
= gtk_vscrollbar_new(NULL
);
970 gtk_widget_set_scroll_adjustments(text
, NULL
,
971 gtk_range_get_adjustment(GTK_RANGE(scrollbar
)));
972 gtk_text_buffer_create_tag(
973 gtk_text_view_get_buffer(GTK_TEXT_VIEW(gui_side
->log
)),
974 "red", "foreground", "red",
976 gtk_text_view_set_editable(GTK_TEXT_VIEW(text
), FALSE
);
977 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text
), FALSE
);
979 text
= gui_side
->log
= gtk_text_new(NULL
, NULL
);
980 scrollbar
= gtk_vscrollbar_new(GTK_TEXT(text
)->vadj
);
981 gtk_box_pack_start(GTK_BOX(gui_side
->log_hbox
), text
, TRUE
, TRUE
, 0);
983 gtk_widget_set_usize(text
, 400, 100);
985 gtk_box_pack_start(GTK_BOX(gui_side
->log_hbox
),
986 scrollbar
, FALSE
, TRUE
, 0);
988 actions
= gtk_hbox_new(TRUE
, 4);
989 gtk_box_pack_start(GTK_BOX(vbox
), actions
, FALSE
, TRUE
, 0);
990 gui_side
->flag_box
= gtk_hbox_new(FALSE
, 16);
991 gtk_box_pack_start(GTK_BOX(vbox
), gui_side
->flag_box
, FALSE
, TRUE
, 2);
993 gui_side
->quiet
= button
= gtk_button_new_with_label(_("Quiet"));
994 gtk_misc_set_padding(GTK_MISC(GTK_BIN(button
)->child
), 12, 0);
995 gtk_box_pack_start(GTK_BOX(gui_side
->flag_box
), button
, FALSE
, TRUE
, 0);
996 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
997 GTK_SIGNAL_FUNC(quiet_clicked
), gui_side
);
999 gui_side
->yes
= button
= gtk_button_new_with_label(_("Yes"));
1000 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "Y");
1001 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
1002 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
1003 GTK_SIGNAL_FUNC(button_reply
), gui_side
);
1004 gui_side
->no
= button
= gtk_button_new_with_label(_("No"));
1005 gtk_object_set_data(GTK_OBJECT(button
), "send-code", "N");
1006 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
1007 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
1008 GTK_SIGNAL_FUNC(button_reply
), gui_side
);
1009 SENSITIVE_YESNO(gui_side
, FALSE
);
1011 button
= gtk_button_new_with_label(_("Abort"));
1012 gtk_box_pack_start(GTK_BOX(actions
), button
, TRUE
, TRUE
, 0);
1013 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
1014 GTK_SIGNAL_FUNC(gtk_widget_destroy
),
1015 GTK_OBJECT(gui_side
->window
));
1017 gui_side
->input_tag
= gdk_input_add(gui_side
->from_child
,
1022 gui_side
->quiet_flag
= add_toggle(gui_side
,
1023 _("Quiet"), _("Don't confirm every operation"),
1025 gtk_widget_set_sensitive(gui_side
->quiet
, !autoq
);
1030 static GUIside
*start_action(gpointer data
, ActionChild
*func
, gboolean autoq
)
1032 return start_action_with_options(data
, func
, autoq
,
1033 option_get_int("action_force"),
1034 option_get_int("action_brief"),
1035 option_get_int("action_recurse"));
1038 /* ACTIONS ON ONE ITEM */
1040 /* These may call themselves recursively, or ask questions, etc.
1041 * TRUE iff the directory containing dest_path needs to be rescanned.
1044 /* Updates the global size_tally, file_counter and dir_counter */
1045 static gboolean
do_usage(char *src_path
, char *unused
)
1051 if (mc_lstat(src_path
, &info
))
1053 g_string_sprintf(message
, "'%s:\n", src_path
);
1059 if (S_ISREG(info
.st_mode
) || S_ISLNK(info
.st_mode
))
1062 size_tally
+= info
.st_size
;
1064 else if (S_ISDIR(info
.st_mode
))
1067 g_string_sprintf(message
, _("?Count contents of %s?"),
1069 if (reply(from_parent
, FALSE
))
1072 safe_path
= g_strdup(src_path
);
1073 for_dir_contents(do_usage
, safe_path
, safe_path
);
1083 /* dest_path is the dir containing src_path */
1084 static gboolean
do_delete(char *src_path
, char *dest_path
)
1087 gboolean write_prot
;
1091 if (mc_lstat(src_path
, &info
))
1097 write_prot
= S_ISLNK(info
.st_mode
) ? FALSE
1098 : access(src_path
, W_OK
) != 0;
1099 if (write_prot
|| !quiet
)
1101 g_string_sprintf(message
, _("?Delete %s'%s'?"),
1102 write_prot
? _("WRITE-PROTECTED ") : " ",
1104 if (!reply(from_parent
, write_prot
&& !o_force
))
1109 g_string_sprintf(message
, _("'Deleting '%s'\n"), src_path
);
1113 if (S_ISDIR(info
.st_mode
))
1116 safe_path
= g_strdup(src_path
);
1117 for_dir_contents(do_delete
, safe_path
, safe_path
);
1118 if (rmdir(safe_path
))
1124 g_string_sprintf(message
, _("'Directory '%s' deleted\n"),
1127 g_string_sprintf(message
, "m%s", safe_path
);
1131 else if (unlink(src_path
))
1140 /* path is the item to check. If is is a directory then we may recurse
1141 * (unless prune is used).
1143 static gboolean
do_find(char *path
, char *dummy
)
1152 g_string_sprintf(message
, _("?Check '%s'?"), path
);
1153 if (!reply(from_parent
, FALSE
))
1159 if (new_entry_string
)
1162 find_condition_free(find_condition
);
1163 find_condition
= find_compile(new_entry_string
);
1164 g_free(new_entry_string
);
1165 new_entry_string
= NULL
;
1171 g_string_assign(message
, _("!Invalid find condition - "
1172 "change it and try again\n"));
1174 g_string_sprintf(message
, _("?Check '%s'?"), path
);
1175 if (!reply(from_parent
, TRUE
))
1179 if (mc_lstat(path
, &info
.stats
))
1182 g_string_sprintf(message
, _("'(while checking '%s')\n"), path
);
1187 info
.fullpath
= path
;
1188 time(&info
.now
); /* XXX: Not for each check! */
1190 slash
= strrchr(path
, '/');
1191 info
.leaf
= slash
? slash
+ 1 : path
;
1193 if (find_test_condition(find_condition
, &info
))
1195 g_string_sprintf(message
, "=%s", path
);
1199 if (S_ISDIR(info
.stats
.st_mode
) && !info
.prune
)
1202 safe_path
= g_strdup(path
);
1203 for_dir_contents(do_find
, safe_path
, safe_path
);
1210 /* Like mode_compile(), but ignores spaces and bracketed bits */
1211 struct mode_change
*nice_mode_compile(const char *mode_string
,
1212 unsigned int masked_ops
)
1216 struct mode_change
*retval
= NULL
;
1218 new = g_string_new(NULL
);
1220 for (; *mode_string
; mode_string
++)
1222 if (*mode_string
== '(')
1224 if (*mode_string
== ')')
1232 if (brackets
== 0 && *mode_string
!= ' ')
1233 g_string_append_c(new, *mode_string
);
1237 retval
= mode_compile(new->str
, masked_ops
);
1238 g_string_free(new, TRUE
);
1242 static gboolean
do_chmod(char *path
, char *dummy
)
1249 if (mc_lstat(path
, &info
))
1254 if (S_ISLNK(info
.st_mode
))
1259 g_string_sprintf(message
,
1260 _("?Change permissions of '%s'?"), path
);
1261 if (!reply(from_parent
, FALSE
))
1266 g_string_sprintf(message
,
1267 _("'Changing permissions of '%s'\n"),
1274 if (new_entry_string
)
1277 mode_free(mode_change
);
1278 mode_change
= nice_mode_compile(new_entry_string
,
1280 g_free(new_entry_string
);
1281 new_entry_string
= NULL
;
1287 g_string_assign(message
,
1288 _("!Invalid mode command - change it and try again\n"));
1290 g_string_sprintf(message
,
1291 _("?Change permissions of '%s'?"), path
);
1292 if (!reply(from_parent
, TRUE
))
1296 if (mc_lstat(path
, &info
))
1301 if (S_ISLNK(info
.st_mode
))
1304 new_mode
= mode_adjust(info
.st_mode
, mode_change
);
1305 if (chmod(path
, new_mode
))
1311 if (o_recurse
&& S_ISDIR(info
.st_mode
))
1314 safe_path
= g_strdup(path
);
1315 for_dir_contents(do_chmod
, safe_path
, safe_path
);
1322 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1323 * is set then that is the new leafname, otherwise the leafname stays
1326 static char *make_dest_path(char *object
, char *dir
)
1334 leaf
= strrchr(object
, '/');
1336 leaf
= object
; /* Error? */
1341 return make_path(dir
, leaf
)->str
;
1344 /* If action_leaf is not NULL it specifies the new leaf name */
1345 static gboolean
do_copy2(char *path
, char *dest
)
1349 struct stat dest_info
;
1350 gboolean retval
= TRUE
;
1354 dest_path
= make_dest_path(path
, dest
);
1356 if (mc_lstat(path
, &info
))
1362 if (mc_lstat(dest_path
, &dest_info
) == 0)
1367 merge
= S_ISDIR(info
.st_mode
) && S_ISDIR(dest_info
.st_mode
);
1369 g_string_sprintf(message
, _("?'%s' already exists - %s?"),
1371 merge
? _("merge contents") : _("overwrite"));
1373 if (!reply(from_parent
, TRUE
))
1378 if (S_ISDIR(dest_info
.st_mode
))
1379 err
= rmdir(dest_path
);
1381 err
= unlink(dest_path
);
1386 if (errno
!= ENOENT
)
1388 g_string_sprintf(message
,
1389 _("'Trying copy anyway...\n"));
1396 g_string_sprintf(message
,
1397 _("?Copy %s as %s?"), path
, dest_path
);
1398 if (!reply(from_parent
, FALSE
))
1403 g_string_sprintf(message
, _("'Copying %s as %s\n"), path
,
1408 if (S_ISDIR(info
.st_mode
))
1410 mode_t mode
= info
.st_mode
;
1411 char *safe_path
, *safe_dest
;
1412 struct stat dest_info
;
1415 /* (we will do the update ourselves now, rather than
1420 safe_path
= g_strdup(path
);
1421 safe_dest
= g_strdup(dest_path
);
1423 exists
= !mc_lstat(dest_path
, &dest_info
);
1425 if (exists
&& !S_ISDIR(dest_info
.st_mode
))
1427 g_string_sprintf(message
,
1428 _("!ERROR: Destination already exists, "
1429 "but is not a directory\n"));
1431 else if (exists
== FALSE
&& mkdir(dest_path
, 0700 | mode
))
1437 /* (just been created then) */
1438 g_string_sprintf(message
, "+%s", dest
);
1443 for_dir_contents(do_copy2
, safe_path
, safe_dest
);
1444 /* Note: dest_path now invalid... */
1450 /* We may have created the directory with
1451 * more permissions than the source so that
1452 * we could write to it... change it back now.
1454 if (chmod(safe_dest
, mode
))
1457 /* Also, try to preserve the timestamps */
1458 utb
.actime
= info
.st_atime
;
1459 utb
.modtime
= info
.st_mtime
;
1461 utime(safe_dest
, &utb
);
1468 else if (S_ISLNK(info
.st_mode
))
1472 /* Not all versions of cp(1) can make symlinks,
1473 * so we special-case it.
1476 target
= readlink_dup(path
);
1479 if (symlink(target
, dest_path
))
1497 error
= copy_file(path
, dest_path
);
1501 g_string_sprintf(message
, _("!%s\nFailed to copy '%s'"),
1512 /* If action_leaf is not NULL it specifies the new leaf name */
1513 static gboolean
do_move2(char *path
, char *dest
)
1516 gboolean retval
= TRUE
;
1517 char *argv
[] = {"mv", "-f", NULL
, NULL
, NULL
};
1524 dest_path
= make_dest_path(path
, dest
);
1526 is_dir
= mc_lstat(path
, &info2
) == 0 && S_ISDIR(info2
.st_mode
);
1528 if (access(dest_path
, F_OK
) == 0)
1533 g_string_sprintf(message
,
1534 _("?'%s' already exists - overwrite?"),
1536 if (!reply(from_parent
, TRUE
))
1539 if (mc_lstat(dest_path
, &info
))
1545 if (S_ISDIR(info
.st_mode
))
1546 err
= rmdir(dest_path
);
1548 err
= unlink(dest_path
);
1553 if (errno
!= ENOENT
)
1555 g_string_sprintf(message
,
1556 _("'Trying move anyway...\n"));
1562 g_string_sprintf(message
,
1563 _("?Move %s as %s?"), path
, dest_path
);
1564 if (!reply(from_parent
, FALSE
))
1569 g_string_sprintf(message
, _("'Moving %s as %s\n"), path
,
1575 argv
[3] = dest_path
;
1577 err
= fork_exec_wait(argv
);
1580 g_string_sprintf(message
,
1581 _("!%s\nFailed to move %s as %s\n"),
1582 err
, path
, dest_path
);
1591 leaf
= strrchr(path
, '/');
1593 leaf
= path
; /* Error? */
1597 g_string_sprintf(message
, "+%s", path
);
1598 g_string_truncate(message
, leaf
- path
+ 1);
1602 g_string_sprintf(message
, "m%s", path
);
1610 /* Copy path to dest.
1611 * Check that path not copied into itself.
1613 static gboolean
do_copy(char *path
, char *dest
)
1615 if (is_sub_dir(make_dest_path(path
, dest
), path
))
1617 g_string_sprintf(message
,
1618 _("!ERROR: Can't copy object into itself\n"));
1622 return do_copy2(path
, dest
);
1625 /* Move path to dest.
1626 * Check that path not moved into itself.
1628 static gboolean
do_move(char *path
, char *dest
)
1630 if (is_sub_dir(make_dest_path(path
, dest
), path
))
1632 g_string_sprintf(message
,
1633 _("!ERROR: Can't move/rename object into itself\n"));
1637 return do_move2(path
, dest
);
1640 static gboolean
do_link(char *path
, char *dest
)
1646 dest_path
= make_dest_path(path
, dest
);
1650 g_string_sprintf(message
, _("'Linking %s as %s\n"), path
,
1656 g_string_sprintf(message
,
1657 _("?Link %s as %s?"), path
, dest_path
);
1658 if (!reply(from_parent
, FALSE
))
1662 if (symlink(path
, dest_path
))
1671 /* Mount/umount this item (depending on 'mount') */
1672 static void do_mount(guchar
*path
, gboolean mount
)
1674 char *argv
[3] = {NULL
, NULL
, NULL
};
1679 argv
[0] = mount
? "mount" : "umount";
1684 g_string_sprintf(message
,
1685 mount
? _("'Mounting %s\n")
1686 : _("'Unmounting %s\n"),
1692 g_string_sprintf(message
,
1693 mount
? _("?Mount %s?\n")
1694 : _("?Unmount %s?\n"),
1696 if (!reply(from_parent
, FALSE
))
1700 err
= fork_exec_wait(argv
);
1703 g_string_sprintf(message
, mount
?
1704 _("!%s\nMount failed\n") :
1705 _("!%s\nUnmount failed\n"), err
);
1711 g_string_sprintf(message
, "M%s", path
);
1713 if (mount
&& mount_open_dir
)
1715 g_string_sprintf(message
, "o%s", path
);
1721 /* CHILD MAIN LOOPS */
1723 /* After forking, the child calls one of these functions */
1725 /* We use a double for total size in order to count beyond 4Gb */
1726 static void usage_cb(gpointer data
)
1728 GList
*paths
= (GList
*) data
;
1729 double total_size
= 0;
1732 dir_counter
= file_counter
= 0;
1734 for (; paths
; paths
= paths
->next
)
1736 guchar
*path
= (guchar
*) paths
->data
;
1742 do_usage(path
, NULL
);
1744 g_string_sprintf(message
, "'%s: %s\n",
1746 format_double_size(size_tally
));
1748 total_size
+= size_tally
;
1751 g_string_sprintf(message
, _("'\nTotal: %s ("),
1752 format_double_size(total_size
));
1756 tmp
= g_strdup_printf("%ld %s%s",
1758 file_counter
== 1 ? _("file") : _("files"),
1759 dir_counter
? ", " : ")\n");
1760 g_string_append(message
, tmp
);
1764 if (file_counter
== 0 && dir_counter
== 0)
1765 g_string_append(message
, _("no directories)\n"));
1766 else if (dir_counter
)
1768 tmp
= g_strdup_printf("%ld %s)\n",
1770 dir_counter
== 1 ? _("directory")
1771 : _("directories"));
1772 g_string_append(message
, tmp
);
1779 #ifdef DO_MOUNT_POINTS
1780 static void mount_cb(gpointer data
)
1782 GList
*paths
= (GList
*) data
;
1783 gboolean mount_points
= FALSE
;
1785 for (; paths
; paths
= paths
->next
)
1787 guchar
*path
= (guchar
*) paths
->data
;
1789 if (mount_is_mounted(path
))
1790 do_mount(path
, FALSE
); /* Unmount */
1791 else if (g_hash_table_lookup(fstab_mounts
, path
))
1792 do_mount(path
, TRUE
); /* Mount */
1796 mount_points
= TRUE
;
1799 g_string_sprintf(message
,
1800 mount_points
? _("'\nDone\n")
1801 : _("!No mount points selected!\n"));
1806 static guchar
*dirname(guchar
*path
)
1810 slash
= strrchr(path
, '/');
1811 g_return_val_if_fail(slash
!= NULL
, g_strdup(path
));
1814 return g_strndup(path
, slash
- path
);
1815 return g_strdup("/");
1818 static void delete_cb(gpointer data
)
1820 GList
*paths
= (GList
*) data
;
1824 guchar
*path
= (guchar
*) paths
->data
;
1827 dir
= dirname(path
);
1830 if (do_delete(path
, dir
))
1832 g_string_sprintf(message
, "+%s", dir
);
1837 paths
= paths
->next
;
1840 g_string_sprintf(message
, _("'\nDone\n"));
1844 static void find_cb(gpointer data
)
1846 GList
*all_paths
= (GList
*) data
;
1851 for (paths
= all_paths
; paths
; paths
= paths
->next
)
1853 guchar
*path
= (guchar
*) paths
->data
;
1857 do_find(path
, NULL
);
1860 g_string_assign(message
, _("?Another search?"));
1861 if (!reply(from_parent
, TRUE
))
1863 g_string_assign(message
, "#");
1867 g_string_sprintf(message
, _("'\nDone\n"));
1871 static void chmod_cb(gpointer data
)
1873 GList
*paths
= (GList
*) data
;
1875 for (; paths
; paths
= paths
->next
)
1877 guchar
*path
= (guchar
*) paths
->data
;
1882 if (mc_stat(path
, &info
) != 0)
1884 else if (S_ISLNK(info
.st_mode
))
1886 g_string_sprintf(message
,
1887 _("!'%s' is a symbolic link\n"),
1891 else if (do_chmod(path
, NULL
))
1893 g_string_sprintf(message
, "+%s", path
); /* XXX */
1898 g_string_sprintf(message
, _("'\nDone\n"));
1902 static void list_cb(gpointer data
)
1904 GList
*paths
= (GList
*) data
;
1908 send_dir((char *) paths
->data
);
1910 if (action_do_func((char *) paths
->data
, action_dest
))
1912 g_string_sprintf(message
, "+%s", action_dest
);
1916 paths
= paths
->next
;
1919 g_string_sprintf(message
, _("'\nDone\n"));
1923 static GtkWidget
*add_toggle(GUIside
*gui_side
,
1924 guchar
*label
, guchar
*tip
,
1930 check
= gtk_check_button_new_with_label(label
);
1931 gtk_tooltips_set_tip(tooltips
, check
, tip
, NULL
);
1932 gtk_object_set_data(GTK_OBJECT(check
), "send-code", code
);
1933 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check
), active
);
1934 gtk_signal_connect(GTK_OBJECT(check
), "clicked",
1935 GTK_SIGNAL_FUNC(button_reply
), gui_side
);
1936 gtk_box_pack_start(GTK_BOX(gui_side
->flag_box
), check
, FALSE
, TRUE
, 0);
1942 /* EXTERNAL INTERFACE */
1944 void action_find(GList
*paths
)
1947 GtkWidget
*hbox
, *label
, *scroller
;
1950 titles
[0] = _("Name");
1951 titles
[1] = _("Directory");
1955 report_error(_("You need to select some items "
1956 "to search through"));
1960 if (!last_find_string
)
1961 last_find_string
= g_strdup("'core'");
1963 new_entry_string
= last_find_string
;
1964 gui_side
= start_action(paths
, find_cb
, FALSE
);
1968 gui_side
->show_info
= TRUE
;
1969 gui_side
->entry_string_func
= set_find_string_colour
;
1971 scroller
= gtk_scrolled_window_new(NULL
, NULL
);
1972 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller
),
1973 GTK_POLICY_NEVER
, GTK_POLICY_ALWAYS
);
1974 gtk_box_pack_start(GTK_BOX(gui_side
->vbox
), scroller
, TRUE
, TRUE
, 4);
1975 gui_side
->results
= gtk_clist_new_with_titles(
1976 sizeof(titles
) / sizeof(*titles
), titles
);
1977 gtk_clist_column_titles_passive(GTK_CLIST(gui_side
->results
));
1978 gtk_widget_set_usize(gui_side
->results
, 100, 100);
1979 gtk_clist_set_column_width(GTK_CLIST(gui_side
->results
), 0, 100);
1980 gtk_clist_set_selection_mode(GTK_CLIST(gui_side
->results
),
1981 GTK_SELECTION_SINGLE
);
1982 gtk_container_add(GTK_CONTAINER(scroller
), gui_side
->results
);
1983 gtk_box_set_child_packing(GTK_BOX(gui_side
->vbox
),
1984 gui_side
->log_hbox
, FALSE
, TRUE
, 4, GTK_PACK_START
);
1985 gtk_signal_connect(GTK_OBJECT(gui_side
->results
), "select_row",
1986 GTK_SIGNAL_FUNC(select_row_callback
), gui_side
);
1988 hbox
= gtk_hbox_new(FALSE
, 0);
1989 label
= gtk_label_new(_("Expression:"));
1990 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, TRUE
, 4);
1991 gui_side
->default_string
= &last_find_string
;
1992 gui_side
->entry
= gtk_entry_new();
1993 gtk_widget_set_name(gui_side
->entry
, "fixed-font");
1994 gtk_entry_set_text(GTK_ENTRY(gui_side
->entry
), last_find_string
);
1995 set_find_string_colour(gui_side
->entry
, last_find_string
);
1996 gtk_editable_select_region(GTK_EDITABLE(gui_side
->entry
), 0, -1);
1997 gtk_widget_set_sensitive(gui_side
->entry
, FALSE
);
1998 gtk_box_pack_start(GTK_BOX(hbox
), gui_side
->entry
, TRUE
, TRUE
, 4);
1999 gtk_signal_connect(GTK_OBJECT(gui_side
->entry
), "changed",
2000 GTK_SIGNAL_FUNC(entry_changed
), gui_side
);
2001 gtk_box_pack_start(GTK_BOX(gui_side
->vbox
), hbox
, FALSE
, TRUE
, 4);
2002 gtk_box_pack_start(GTK_BOX(hbox
),
2003 new_help_button(show_condition_help
, NULL
),
2006 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), _("Find"));
2007 gtk_window_set_focus(GTK_WINDOW(gui_side
->window
), gui_side
->entry
);
2008 gtk_signal_connect(GTK_OBJECT(gui_side
->entry
), "activate",
2009 GTK_SIGNAL_FUNC(find_return_pressed
), gui_side
);
2010 number_of_windows
++;
2011 gtk_widget_show_all(gui_side
->window
);
2014 /* Count disk space used by selected items */
2015 void action_usage(GList
*paths
)
2021 report_error(_("You need to select some items to count"));
2025 gui_side
= start_action(paths
, usage_cb
, TRUE
);
2029 gui_side
->show_info
= TRUE
;
2031 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), _("Disk Usage"));
2032 number_of_windows
++;
2033 gtk_widget_show_all(gui_side
->window
);
2036 /* Mount/unmount listed items (paths).
2037 * Free the list after this function returns.
2038 * If open_dir is TRUE and the dir is successfully mounted, open it.
2039 * quiet can be -1 for default.
2041 void action_mount(GList
*paths
, gboolean open_dir
, int quiet
)
2043 #ifdef DO_MOUNT_POINTS
2047 quiet
= option_get_int("action_mount");
2049 mount_open_dir
= open_dir
;
2050 gui_side
= start_action(paths
, mount_cb
, quiet
);
2054 gtk_window_set_title(GTK_WINDOW(gui_side
->window
),
2055 _("Mount / Unmount"));
2056 number_of_windows
++;
2057 gtk_widget_show_all(gui_side
->window
);
2060 _("ROX-Filer does not yet support mount points on your "
2062 #endif /* DO_MOUNT_POINTS */
2065 /* Deletes all selected items in the window */
2066 void action_delete(GList
*paths
)
2070 if (!remove_pinned_ok(paths
))
2073 gui_side
= start_action(paths
, delete_cb
,
2074 option_get_int("action_delete"));
2078 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), _("Delete"));
2079 add_toggle(gui_side
,
2080 _("Force"), _("Don't confirm deletion of non-writeable items"),
2081 "F", option_get_int("action_force"));
2082 add_toggle(gui_side
,
2083 _("Brief"), _("Only log directories being deleted"),
2084 "B", option_get_int("action_brief"));
2086 number_of_windows
++;
2087 gtk_widget_show_all(gui_side
->window
);
2090 /* Change the permissions of the selected items */
2091 void action_chmod(GList
*paths
)
2094 GtkWidget
*hbox
, *label
, *combo
;
2095 static GList
*presets
= NULL
;
2099 report_error(_("You need to select the items "
2100 "whose permissions you want to change"));
2106 presets
= g_list_append(presets
,
2107 _("a+x (Make executable/searchable)"));
2108 presets
= g_list_append(presets
,
2109 _("a-x (Make non-executable/non-searchable)"));
2110 presets
= g_list_append(presets
,
2111 _("u+rw (Give owner read+write)"));
2112 presets
= g_list_append(presets
,
2113 _("go-rwx (Private - owner access only)"));
2114 presets
= g_list_append(presets
,
2115 _("go=u-w (Public access, not write)"));
2118 if (!last_chmod_string
)
2119 last_chmod_string
= g_strdup((guchar
*) presets
->data
);
2120 new_entry_string
= last_chmod_string
;
2121 gui_side
= start_action(paths
, chmod_cb
, FALSE
);
2125 add_toggle(gui_side
,
2126 _("Brief"), _("Don't list processed files"),
2127 "B", option_get_int("action_brief"));
2128 add_toggle(gui_side
,
2129 _("Recurse"), _("Also change contents of subdirectories"),
2130 "R", option_get_int("action_recurse"));
2132 hbox
= gtk_hbox_new(FALSE
, 0);
2133 label
= gtk_label_new(_("Command:"));
2134 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, TRUE
, 4);
2135 gui_side
->default_string
= &last_chmod_string
;
2137 combo
= gtk_combo_new();
2138 gtk_combo_disable_activate(GTK_COMBO(combo
));
2139 gtk_combo_set_use_arrows_always(GTK_COMBO(combo
), TRUE
);
2140 gtk_combo_set_popdown_strings(GTK_COMBO(combo
), presets
);
2142 gui_side
->entry
= GTK_COMBO(combo
)->entry
;
2143 gtk_entry_set_text(GTK_ENTRY(gui_side
->entry
), last_chmod_string
);
2144 gtk_editable_select_region(GTK_EDITABLE(gui_side
->entry
), 0, -1);
2145 gtk_widget_set_sensitive(gui_side
->entry
, FALSE
);
2146 gtk_box_pack_start(GTK_BOX(hbox
), combo
, TRUE
, TRUE
, 4);
2147 gtk_signal_connect(GTK_OBJECT(gui_side
->entry
), "changed",
2148 GTK_SIGNAL_FUNC(entry_changed
), gui_side
);
2149 gtk_box_pack_start(GTK_BOX(gui_side
->vbox
), hbox
, FALSE
, TRUE
, 0);
2150 gtk_box_pack_start(GTK_BOX(hbox
),
2151 new_help_button(show_chmod_help
, NULL
),
2154 gtk_window_set_focus(GTK_WINDOW(gui_side
->window
), gui_side
->entry
);
2155 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), _("Permissions"));
2156 gtk_signal_connect_object(GTK_OBJECT(gui_side
->entry
), "activate",
2157 GTK_SIGNAL_FUNC(gtk_button_clicked
),
2158 GTK_OBJECT(gui_side
->yes
));
2160 number_of_windows
++;
2161 gtk_widget_show_all(gui_side
->window
);
2164 /* If leaf is NULL then the copy has the same name as the original.
2165 * quiet can be -1 for default.
2167 void action_copy(GList
*paths
, char *dest
, char *leaf
, int quiet
)
2172 quiet
= option_get_int("action_copy");
2176 action_do_func
= do_copy
;
2177 gui_side
= start_action(paths
, list_cb
, quiet
);
2181 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), _("Copy"));
2182 number_of_windows
++;
2183 gtk_widget_show_all(gui_side
->window
);
2186 /* If leaf is NULL then the file is not renamed.
2187 * quiet can be -1 for default.
2189 void action_move(GList
*paths
, char *dest
, char *leaf
, int quiet
)
2194 quiet
= option_get_int("action_move");
2198 action_do_func
= do_move
;
2199 gui_side
= start_action(paths
, list_cb
, quiet
);
2203 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), _("Move"));
2204 number_of_windows
++;
2205 gtk_widget_show_all(gui_side
->window
);
2208 /* If leaf is NULL then the link will have the same name */
2209 /* XXX: No quiet option here? */
2210 void action_link(GList
*paths
, char *dest
, char *leaf
)
2216 action_do_func
= do_link
;
2217 gui_side
= start_action(paths
, list_cb
, option_get_int("action_link"));
2221 gtk_window_set_title(GTK_WINDOW(gui_side
->window
), _("Link"));
2222 number_of_windows
++;
2223 gtk_widget_show_all(gui_side
->window
);
2226 void action_init(void)
2228 option_add_int("action_copy", 1, NULL
);
2229 option_add_int("action_move", 1, NULL
);
2230 option_add_int("action_link", 1, NULL
);
2231 option_add_int("action_delete", 0, NULL
);
2232 option_add_int("action_mount", 1, NULL
);
2233 option_add_int("action_force", o_force
, NULL
);
2234 option_add_int("action_brief", o_brief
, NULL
);
2235 option_add_int("action_recurse", o_recurse
, NULL
);
2240 /* Check to see if any of the selected items (or their children) are
2241 * on the pinboard or panel. If so, ask for confirmation.
2243 * TRUE if it's OK to lose them.
2245 static gboolean
remove_pinned_ok(GList
*paths
)
2247 GList
*ask
= NULL
, *next
;
2254 guchar
*path
= (guchar
*) paths
->data
;
2256 if (icons_require(path
))
2258 if (++ask_n
> MAX_ASK
)
2260 ask
= g_list_append(ask
, path
);
2263 paths
= paths
->next
;
2269 if (ask_n
> MAX_ASK
)
2271 message
= g_string_new(_("Deleting items such as "));
2274 else if (ask_n
== 1)
2275 message
= g_string_new(_("Deleting the item "));
2277 message
= g_string_new(_("Deleting the items "));
2280 for (next
= ask
; next
; next
= next
->next
)
2282 guchar
*path
= (guchar
*) next
->data
;
2285 leaf
= strrchr(path
, '/');
2291 g_string_append_c(message
, '`');
2292 g_string_append(message
, leaf
);
2293 g_string_append_c(message
, '\'');
2295 if (i
== ask_n
- 1 && i
> 0)
2296 g_string_append(message
, _(" and "));
2298 g_string_append(message
, ", ");
2304 message
= g_string_append(message
,
2305 _(" will affect some items on the pinboard "
2306 "or panel - really delete it?"));
2309 if (ask_n
> MAX_ASK
)
2310 message
= g_string_append_c(message
, ',');
2311 message
= g_string_append(message
,
2312 _(" will affect some items on the pinboard "
2313 "or panel - really delete them?"));
2316 retval
= get_choice(PROJECT
, message
->str
,
2317 2, _("Cancel"), _("OK")) == 1;
2319 g_string_free(message
, TRUE
);
2324 void set_find_string_colour(GtkWidget
*widget
, guchar
*string
)
2327 static GtkStyle
*error_style
= NULL
;
2328 FindCondition
*cond
;
2332 error_style
= gtk_style_copy(fixed_style
);
2333 error_style
->fg
[GTK_STATE_NORMAL
] = red
;
2336 cond
= find_compile(string
);
2337 gtk_widget_set_style(widget
, cond
? fixed_style
: error_style
);
2340 find_condition_free(cond
);