r496: Many internal changes to the Options system.
[rox-filer.git] / ROX-Filer / src / action.c
blobd8df3f651d6ac74f5460ea51ce3979de16ce1140
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2000, Thomas Leonard, <tal197@users.sourceforge.net>.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
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
15 * more details.
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 #include "config.h"
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <ctype.h>
31 #include <sys/param.h>
32 #include <errno.h>
33 #include <signal.h>
34 #include <sys/time.h>
36 #include "global.h"
38 #include "action.h"
39 #include "string.h"
40 #include "support.h"
41 #include "gui_support.h"
42 #include "filer.h"
43 #include "main.h"
44 #include "options.h"
45 #include "modechange.h"
46 #include "find.h"
47 #include "dir.h"
48 #include "icon.h"
50 /* Parent->Child messages are one character each:
52 * Q/Y/N Quiet/Yes/No button clicked
53 * F Force deletion of non-writeable items
56 #define SENSITIVE_YESNO(gui_side, state) \
57 do { \
58 gtk_widget_set_sensitive((gui_side)->yes, state); \
59 gtk_widget_set_sensitive((gui_side)->no, state); \
60 if ((gui_side)->entry) \
61 gtk_widget_set_sensitive((gui_side)->entry, state);\
62 } while (0)
64 typedef struct _GUIside GUIside;
65 typedef void ActionChild(gpointer data);
66 typedef gboolean ForDirCB(char *path, char *dest_path);
68 struct _GUIside
70 int from_child; /* File descriptor */
71 FILE *to_child;
72 int input_tag; /* gdk_input_add() */
73 GtkWidget *vbox, *log, *window, *dir, *log_hbox;
74 GtkWidget *quiet, *yes, *no;
75 int child; /* Process ID */
76 int errors;
77 gboolean show_info; /* For Disk Usage */
79 GtkWidget *entry; /* May be NULL */
80 guchar **default_string; /* Changed when the entry changes */
82 char *next_dir; /* NULL => no timer active */
83 gint next_timer;
85 /* Used by Find */
86 FilerWindow *preview;
87 GtkWidget *results;
90 /* These don't need to be in a structure because we fork() before
91 * using them again.
93 static int from_parent = 0;
94 static FILE *to_parent = NULL;
95 static gboolean quiet = FALSE;
96 static GString *message = NULL;
97 static char *action_dest = NULL;
98 static char *action_leaf = NULL;
99 static gboolean (*action_do_func)(char *source, char *dest);
100 static size_t size_tally; /* For Disk Usage */
102 static struct mode_change *mode_change = NULL; /* For Permissions */
103 static FindCondition *find_condition = NULL; /* For Find */
105 static gboolean o_force = FALSE;
106 static gboolean o_brief = FALSE;
107 static gboolean o_recurse = FALSE;
109 /* Whenever the text in these boxes is changed we store a copy of the new
110 * string to be used as the default next time.
112 static guchar *last_chmod_string = NULL;
113 static guchar *last_find_string = NULL;
115 /* Set to one of the above before forking. This may change over a call to
116 * reply(). It is reset to NULL once the text is parsed.
118 static guchar *new_entry_string = NULL;
120 /* Static prototypes */
121 static gboolean send();
122 static gboolean send_error();
123 static gboolean send_dir(char *dir);
124 static gboolean read_exact(int source, char *buffer, ssize_t len);
125 static void do_mount(guchar *path, gboolean mount);
126 static void add_toggle(GUIside *gui_side, guchar *label, guchar *code);
127 static gboolean reply(int fd, gboolean ignore_quiet);
128 static gboolean remove_pinned_ok(FilerWindow *filer_window);
130 /* SUPPORT */
132 static void preview_closed(GtkWidget *window, GUIside *gui_side)
134 gui_side->preview = NULL;
137 static void select_row_callback(GtkWidget *widget,
138 gint row, gint column,
139 GdkEventButton *event,
140 GUIside *gui_side)
142 char *leaf, *dir;
144 gtk_clist_get_text(GTK_CLIST(gui_side->results), row, 0, &leaf);
145 gtk_clist_get_text(GTK_CLIST(gui_side->results), row, 1, &dir);
147 gtk_clist_unselect_row(GTK_CLIST(gui_side->results), row, column);
149 if (gui_side->preview)
151 if (strcmp(gui_side->preview->path, dir) == 0)
152 display_set_autoselect(gui_side->preview, leaf);
153 else
154 filer_change_to(gui_side->preview, dir, leaf);
155 return;
158 gui_side->preview = filer_opendir(dir);
159 if (gui_side->preview)
161 display_set_autoselect(gui_side->preview, leaf);
162 gtk_signal_connect(GTK_OBJECT(gui_side->preview->window),
163 "destroy",
164 GTK_SIGNAL_FUNC(preview_closed), gui_side);
169 /* This is called whenever the user edits the entry box (if any) - send the
170 * new string.
172 static void entry_changed(GtkEntry *entry, GUIside *gui_side)
174 guchar *text;
176 g_return_if_fail(gui_side->default_string != NULL);
178 text = gtk_entry_get_text(entry);
180 g_free(*(gui_side->default_string));
181 *(gui_side->default_string) = g_strdup(text);
183 if (!gui_side->to_child)
184 return;
186 fputc('E', gui_side->to_child);
187 fputs(text, gui_side->to_child);
188 fputc('\n', gui_side->to_child);
189 fflush(gui_side->to_child);
192 void show_condition_help(gpointer data)
194 static GtkWidget *help = NULL;
196 if (!help)
198 GtkWidget *text, *vbox, *button, *hbox, *frame;
200 help = gtk_window_new(GTK_WINDOW_DIALOG);
201 gtk_container_set_border_width(GTK_CONTAINER(help), 10);
202 gtk_window_set_title(GTK_WINDOW(help),
203 _("Find expression reference"));
205 vbox = gtk_vbox_new(FALSE, 0);
206 gtk_container_add(GTK_CONTAINER(help), vbox);
208 frame = gtk_frame_new(_("Quick Start"));
209 text = gtk_label_new(
210 _("Just put the name of the file you're looking for in single quotes:\n"
211 "'index.html' (to find a file called 'index.html')"));
212 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
213 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
214 gtk_container_add(GTK_CONTAINER(frame), text);
215 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
217 frame = gtk_frame_new(_("Examples"));
218 text = gtk_label_new(
219 _("'*.htm', '*.html' (finds HTML files)\n"
220 "IsDir 'lib' (finds directories called 'lib')\n"
221 "IsReg 'core' (finds a regular file called 'core')\n"
222 "! (IsDir, IsReg) (is neither a directory nor a regular file)\n"
223 "mtime after 1 day ago and size > 1Mb (big, and recently modified)\n"
224 "'CVS' prune, isreg (a regular file not in CVS)\n"
225 "IsReg system(grep -q fred \"%\") (contains the word 'fred')"));
226 gtk_widget_set_style(text, fixed_style);
227 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
228 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
229 gtk_container_add(GTK_CONTAINER(frame), text);
230 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
232 frame = gtk_frame_new(_("Simple Tests"));
233 text = gtk_label_new(
234 _("IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev, IsPipe, IsSocket (types)\n"
235 "IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsExecutable (permissions)"
236 "\n"
237 "IsEmpty, IsMine\n"
238 "\n"
239 "A pattern in single quotes is a shell-style wildcard pattern to match. If it\n"
240 "contains a slash then the match is against the full path; otherwise it is \n"
241 "against the leafname only."));
242 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
243 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
244 gtk_container_add(GTK_CONTAINER(frame), text);
245 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
247 frame = gtk_frame_new(_("Comparisons"));
248 text = gtk_label_new(
249 _("<, <=, =, !=, >, >=, After, Before (compare two values)\n"
250 "5 bytes, 1Kb, 2Mb, 3Gb (file sizes)\n"
251 "2 secs|mins|hours|days|weeks|years ago|hence (times)\n"
252 "atime, ctime, mtime, now, size, inode, nlinks, uid, gid, blocks (values)"));
253 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
254 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
255 gtk_container_add(GTK_CONTAINER(frame), text);
256 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
258 frame = gtk_frame_new(_("Specials"));
259 text = gtk_label_new(
260 _("system(command) (true if 'command' returns with a zero exit status; a % \n"
261 "in 'command' is replaced with the path of the current file)\n"
262 "prune (false, and prevents searching the contents of a directory).")
264 gtk_misc_set_padding(GTK_MISC(text), 4, 4);
265 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
266 gtk_container_add(GTK_CONTAINER(frame), text);
267 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 4);
269 hbox = gtk_hbox_new(FALSE, 20);
270 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
272 text = gtk_label_new(
273 _("See the ROX-Filer manual for full details."));
274 gtk_box_pack_start(GTK_BOX(hbox), text, TRUE, TRUE, 0);
275 button = gtk_button_new_with_label(_("Close"));
276 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
277 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
278 gtk_widget_hide, GTK_OBJECT(help));
280 gtk_signal_connect_object(GTK_OBJECT(help), "delete_event",
281 gtk_widget_hide, GTK_OBJECT(help));
284 if (GTK_WIDGET_VISIBLE(help))
285 gtk_widget_hide(help);
286 gtk_widget_show_all(help);
289 static void show_chmod_help(gpointer data)
291 static GtkWidget *help = NULL;
293 if (!help)
295 GtkWidget *text, *vbox, *button, *hbox, *sep;
297 help = gtk_window_new(GTK_WINDOW_DIALOG);
298 gtk_container_set_border_width(GTK_CONTAINER(help), 10);
299 gtk_window_set_title(GTK_WINDOW(help),
300 _("Permissions command reference"));
302 vbox = gtk_vbox_new(FALSE, 0);
303 gtk_container_add(GTK_CONTAINER(help), vbox);
305 text = gtk_label_new(
306 _("Normally, you can just select a command from the menu (click \n"
307 "on the arrow beside the command box). Sometimes, you need more...\n"
308 "\n"
309 "The format of a command is:\n"
310 "CHANGE, CHANGE, ...\n"
311 "Each CHANGE is:\n"
312 "WHO HOW PERMISSIONS\n"
313 "WHO is some combination of u, g and o which determines whether to\n"
314 "change the permissions for the User (owner), Group or Others.\n"
315 "HOW is +, - or = to add, remove or set exactly the permissions.\n"
316 "PERMISSIONS is some combination of the letters 'rwxXstugo'\n\n"
318 "Bracketed text and spaces are ignored.\n\n"
320 "Examples:\n"
321 "u+rw (the file owner gains read and write permission)\n"
322 "g=u (the group permissions are set to be the same as the user's)\n"
323 "o=u-w (others get the same permissions as the owner, but without "
324 "write permission)\n"
325 "a+x (everyone gets execute/access permission - same as 'ugo+x')\n"
326 "a+X (directories become accessable by everyone; files which were\n"
327 "executable by anyone become executable by everyone)\n"
328 "u+rw, go+r (two commands at once!)\n"
329 "u+s (set the SetUID bit - often has no effect on script files)\n"
330 "755 (set the permissions directly)\n"
332 "\nSee the chmod(1) man page for full details."));
333 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
334 gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 0);
336 hbox = gtk_hbox_new(FALSE, 20);
337 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
339 sep = gtk_hseparator_new();
340 gtk_box_pack_start(GTK_BOX(hbox), sep, TRUE, TRUE, 0);
341 button = gtk_button_new_with_label(_("Close"));
342 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
343 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
344 gtk_widget_hide, GTK_OBJECT(help));
346 gtk_signal_connect_object(GTK_OBJECT(help), "delete_event",
347 gtk_widget_hide, GTK_OBJECT(help));
350 if (GTK_WIDGET_VISIBLE(help))
351 gtk_widget_hide(help);
352 gtk_widget_show_all(help);
355 static gboolean display_dir(gpointer data)
357 GUIside *gui_side = (GUIside *) data;
359 gtk_label_set_text(GTK_LABEL(gui_side->dir), gui_side->next_dir + 1);
360 g_free(gui_side->next_dir);
361 gui_side->next_dir = NULL;
363 return FALSE;
366 static void add_to_results(GUIside *gui_side, gchar *path)
368 gchar *row[] = {"Leaf", "Dir"};
369 gchar *slash;
370 int len;
372 slash = strrchr(path, '/');
373 g_return_if_fail(slash != NULL);
375 len = slash - path;
376 row[1] = g_strndup(path, MAX(len, 1));
377 row[0] = slash + 1;
379 gtk_clist_append(GTK_CLIST(gui_side->results), row);
381 g_free(row[1]);
384 /* Called when the child sends us a message */
385 static void message_from_child(gpointer data,
386 gint source,
387 GdkInputCondition condition)
389 char buf[5];
390 GUIside *gui_side = (GUIside *) data;
391 GtkWidget *log = gui_side->log;
393 if (read_exact(source, buf, 4))
395 ssize_t message_len;
396 char *buffer;
398 buf[4] = '\0';
399 message_len = strtol(buf, NULL, 16);
400 buffer = g_malloc(message_len + 1);
401 if (message_len > 0 && read_exact(source, buffer, message_len))
403 buffer[message_len] = '\0';
404 if (*buffer == '?')
406 SENSITIVE_YESNO(gui_side, TRUE);
407 gtk_window_set_focus(
408 GTK_WINDOW(gui_side->window),
409 gui_side->entry ? gui_side->entry
410 : gui_side->yes);
412 else if (*buffer == '+')
414 refresh_dirs(buffer + 1);
415 g_free(buffer);
416 return;
418 else if (*buffer == '=')
420 add_to_results(gui_side, buffer + 1);
421 g_free(buffer);
422 return;
424 else if (*buffer == '#')
426 gtk_clist_clear(GTK_CLIST(gui_side->results));
427 g_free(buffer);
428 return;
431 else if (*buffer == 'm' || *buffer == 'M')
433 if (*buffer == 'M')
434 mount_update(TRUE);
435 filer_check_mounted(buffer + 1);
436 g_free(buffer);
437 return;
439 else if (*buffer == '/')
441 if (gui_side->next_dir)
442 g_free(gui_side->next_dir);
443 else
444 gui_side->next_timer =
445 gtk_timeout_add(500,
446 display_dir,
447 gui_side);
448 gui_side->next_dir = buffer;
449 return;
451 else if (*buffer == '!')
452 gui_side->errors++;
454 gtk_text_insert(GTK_TEXT(log),
455 NULL,
456 *buffer == '!' ? &red : NULL,
457 NULL,
458 buffer + 1, message_len - 1);
459 g_free(buffer);
460 return;
462 g_printerr("Child died in the middle of a message.\n");
465 /* The child is dead */
466 gui_side->child = 0;
468 fclose(gui_side->to_child);
469 gui_side->to_child = NULL;
470 close(gui_side->from_child);
471 gdk_input_remove(gui_side->input_tag);
472 gtk_widget_set_sensitive(gui_side->quiet, FALSE);
474 if (gui_side->errors)
476 guchar *report;
478 if (gui_side->errors == 1)
479 report = g_strdup(_("There was one error.\n"));
480 else
481 report = g_strdup_printf(_("There were %d errors.\n"),
482 gui_side->errors);
484 gtk_text_insert(GTK_TEXT(log), NULL, &red, NULL,
485 report, -1);
487 g_free(report);
489 else if (gui_side->show_info == FALSE)
490 gtk_widget_destroy(gui_side->window);
493 /* Scans src_dir, updating dest_path whenever cb returns TRUE */
494 static void for_dir_contents(ForDirCB *cb, char *src_dir, char *dest_path)
496 DIR *d;
497 struct dirent *ent;
498 GSList *list = NULL, *next;
500 d = mc_opendir(src_dir);
501 if (!d)
503 /* Message displayed is "ERROR reading 'path': message" */
504 g_string_sprintf(message, "!%s '%s': %s\n",
505 _("ERROR reading"),
506 src_dir, g_strerror(errno));
507 send();
508 return;
511 send_dir(src_dir);
513 while ((ent = mc_readdir(d)))
515 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
516 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
517 continue;
518 list = g_slist_append(list, g_strdup(make_path(src_dir,
519 ent->d_name)->str));
521 mc_closedir(d);
523 if (!list)
524 return;
526 next = list;
528 while (next)
530 if (cb((char *) next->data, dest_path))
532 g_string_sprintf(message, "+%s", dest_path);
533 send();
536 g_free(next->data);
537 next = next->next;
539 g_slist_free(list);
540 return;
543 /* Read this many bytes into the buffer. TRUE on success. */
544 static gboolean read_exact(int source, char *buffer, ssize_t len)
546 while (len > 0)
548 ssize_t got;
549 got = read(source, buffer, len);
550 if (got < 1)
551 return FALSE;
552 len -= got;
553 buffer += got;
555 return TRUE;
558 /* Send 'message' to our parent process. TRUE on success. */
559 static gboolean send(void)
561 char len_buffer[5];
562 ssize_t len;
564 g_return_val_if_fail(message->len < 0xffff, FALSE);
566 sprintf(len_buffer, "%04x", message->len);
567 fwrite(len_buffer, 1, 4, to_parent);
568 len = fwrite(message->str, 1, message->len, to_parent);
569 fflush(to_parent);
570 return len == message->len;
573 /* Set the directory indicator at the top of the window */
574 static gboolean send_dir(char *dir)
576 g_string_sprintf(message, "/%s", dir);
577 return send();
580 static gboolean send_error(void)
582 g_string_sprintf(message, "!%s: %s\n", _("ERROR"), g_strerror(errno));
583 return send();
586 static void button_reply(GtkWidget *button, GUIside *gui_side)
588 char *text;
590 if (!gui_side->to_child)
591 return;
593 text = gtk_object_get_data(GTK_OBJECT(button), "send-code");
594 g_return_if_fail(text != NULL);
595 fputc(*text, gui_side->to_child);
596 fflush(gui_side->to_child);
598 if (*text == 'Y' || *text == 'N' || *text == 'Q')
599 SENSITIVE_YESNO(gui_side, FALSE);
602 static void process_flag(char flag)
604 switch (flag)
606 case 'Q':
607 quiet = !quiet;
608 break;
609 case 'F':
610 o_force = !o_force;
611 break;
612 case 'R':
613 o_recurse = !o_recurse;
614 break;
615 case 'B':
616 o_brief = !o_brief;
617 break;
618 default:
619 g_string_sprintf(message,
620 "!ERROR: Bad message '%c'\n", flag);
621 send();
622 break;
626 /* If the parent has sent any flag toggles, read them */
627 static void check_flags(void)
629 fd_set set;
630 int got;
631 char retval;
632 struct timeval tv;
634 FD_ZERO(&set);
636 while (1)
638 FD_SET(from_parent, &set);
639 tv.tv_sec = 0;
640 tv.tv_usec = 0;
641 got = select(from_parent + 1, &set, NULL, NULL, &tv);
643 if (got == -1)
644 g_error("select() failed: %s\n", g_strerror(errno));
645 else if (!got)
646 return;
648 got = read(from_parent, &retval, 1);
649 if (got != 1)
650 g_error("read() error: %s\n", g_strerror(errno));
652 process_flag(retval);
656 static void read_new_entry_text(void)
658 int len;
659 char c;
660 GString *new;
662 new = g_string_new(NULL);
664 for (;;)
666 len = read(from_parent, &c, 1);
667 if (len != 1)
669 fprintf(stderr, "read() error: %s\n",
670 g_strerror(errno));
671 _exit(1); /* Parent died? */
674 if (c == '\n')
675 break;
676 g_string_append_c(new, c);
679 g_free(new_entry_string);
680 new_entry_string = new->str;
681 g_string_free(new, FALSE);
684 /* Read until the user sends a reply. If ignore_quiet is TRUE then
685 * the user MUST click Yes or No, else treat quiet on as Yes.
686 * If the user needs prompting then does send().
688 static gboolean reply(int fd, gboolean ignore_quiet)
690 ssize_t len;
691 char retval;
692 gboolean asked = FALSE;
694 while (ignore_quiet || !quiet)
696 if (!asked)
698 send();
699 asked = TRUE;
702 len = read(fd, &retval, 1);
703 if (len != 1)
705 fprintf(stderr, "read() error: %s\n",
706 g_strerror(errno));
707 _exit(1); /* Parent died? */
710 switch (retval)
712 case 'Q':
713 quiet = !quiet;
714 if (ignore_quiet)
716 g_string_assign(message, "?");
717 send();
719 break;
720 case 'Y':
721 g_string_sprintf(message, "' %s\n", _("Yes"));
722 send();
723 return TRUE;
724 case 'N':
725 g_string_sprintf(message, "' %s\n", _("No"));
726 send();
727 return FALSE;
728 case 'E':
729 read_new_entry_text();
730 break;
731 default:
732 process_flag(retval);
733 break;
737 if (asked)
739 g_string_sprintf(message, "' %s\n", _("Quiet"));
740 send();
742 return TRUE;
745 static void destroy_action_window(GtkWidget *widget, gpointer data)
747 GUIside *gui_side = (GUIside *) data;
749 if (gui_side->child)
751 kill(gui_side->child, SIGTERM);
752 fclose(gui_side->to_child);
753 close(gui_side->from_child);
754 gdk_input_remove(gui_side->input_tag);
757 if (gui_side->next_dir)
759 gtk_timeout_remove(gui_side->next_timer);
760 g_free(gui_side->next_dir);
763 if (gui_side->preview)
765 gtk_signal_disconnect_by_data(
766 GTK_OBJECT(gui_side->preview->window),
767 gui_side);
768 gui_side->preview = NULL;
771 g_free(gui_side);
773 if (--number_of_windows < 1)
774 gtk_main_quit();
777 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
778 * (NULL on failure). The child calls func().
780 * If autoq then automatically selects 'Quiet'.
782 static GUIside *start_action(gpointer data, ActionChild *func, gboolean autoq)
784 int filedes[4]; /* 0 and 2 are for reading */
785 GUIside *gui_side;
786 int child;
787 GtkWidget *vbox, *button, *scrollbar, *actions;
788 struct sigaction act;
790 if (pipe(filedes))
792 report_error(PROJECT, g_strerror(errno));
793 return NULL;
796 if (pipe(filedes + 2))
798 close(filedes[0]);
799 close(filedes[1]);
800 report_error(PROJECT, g_strerror(errno));
801 return NULL;
804 child = fork();
805 switch (child)
807 case -1:
808 report_error(PROJECT, g_strerror(errno));
809 return NULL;
810 case 0:
811 /* We are the child */
813 quiet = autoq;
815 /* Reset the SIGCHLD handler */
816 act.sa_handler = SIG_DFL;
817 sigemptyset(&act.sa_mask);
818 act.sa_flags = 0;
819 sigaction(SIGCHLD, &act, NULL);
821 message = g_string_new(NULL);
822 close(filedes[0]);
823 close(filedes[3]);
824 to_parent = fdopen(filedes[1], "wb");
825 from_parent = filedes[2];
826 func(data);
827 send_dir("");
828 _exit(0);
831 /* We are the parent */
832 close(filedes[1]);
833 close(filedes[2]);
834 gui_side = g_malloc(sizeof(GUIside));
835 gui_side->from_child = filedes[0];
836 gui_side->to_child = fdopen(filedes[3], "wb");
837 gui_side->log = NULL;
838 gui_side->child = child;
839 gui_side->errors = 0;
840 gui_side->show_info = FALSE;
841 gui_side->preview = NULL;
842 gui_side->results = NULL;
843 gui_side->entry = NULL;
844 gui_side->default_string = NULL;
846 gui_side->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
847 gtk_container_set_border_width(GTK_CONTAINER(gui_side->window), 2);
848 gtk_window_set_default_size(GTK_WINDOW(gui_side->window), 450, 200);
849 gtk_signal_connect(GTK_OBJECT(gui_side->window), "destroy",
850 GTK_SIGNAL_FUNC(destroy_action_window), gui_side);
852 gui_side->vbox = vbox = gtk_vbox_new(FALSE, 0);
853 gtk_container_add(GTK_CONTAINER(gui_side->window), vbox);
855 gui_side->dir = gtk_label_new(_("<dir>"));
856 gui_side->next_dir = NULL;
857 gtk_misc_set_alignment(GTK_MISC(gui_side->dir), 0.5, 0.5);
858 gtk_box_pack_start(GTK_BOX(vbox), gui_side->dir, FALSE, TRUE, 0);
860 gui_side->log_hbox = gtk_hbox_new(FALSE, 0);
861 gtk_box_pack_start(GTK_BOX(vbox), gui_side->log_hbox, TRUE, TRUE, 4);
863 gui_side->log = gtk_text_new(NULL, NULL);
864 gtk_widget_set_usize(gui_side->log, 400, 100);
865 gtk_box_pack_start(GTK_BOX(gui_side->log_hbox),
866 gui_side->log, TRUE, TRUE, 0);
867 scrollbar = gtk_vscrollbar_new(GTK_TEXT(gui_side->log)->vadj);
868 gtk_box_pack_start(GTK_BOX(gui_side->log_hbox),
869 scrollbar, FALSE, TRUE, 0);
871 actions = gtk_hbox_new(TRUE, 4);
872 gtk_box_pack_start(GTK_BOX(vbox), actions, FALSE, TRUE, 0);
874 gui_side->quiet = button = gtk_toggle_button_new_with_label(_("Quiet"));
875 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), autoq);
876 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Q");
877 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
878 gtk_signal_connect(GTK_OBJECT(button), "clicked",
879 button_reply, gui_side);
880 gui_side->yes = button = gtk_button_new_with_label(_("Yes"));
881 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Y");
882 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
883 gtk_signal_connect(GTK_OBJECT(button), "clicked",
884 button_reply, gui_side);
885 gui_side->no = button = gtk_button_new_with_label(_("No"));
886 gtk_object_set_data(GTK_OBJECT(button), "send-code", "N");
887 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
888 gtk_signal_connect(GTK_OBJECT(button), "clicked",
889 button_reply, gui_side);
890 SENSITIVE_YESNO(gui_side, FALSE);
892 button = gtk_button_new_with_label(_("Abort"));
893 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
894 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
895 gtk_widget_destroy, GTK_OBJECT(gui_side->window));
897 gui_side->input_tag = gdk_input_add(gui_side->from_child,
898 GDK_INPUT_READ,
899 message_from_child,
900 gui_side);
902 return gui_side;
905 /* ACTIONS ON ONE ITEM */
907 /* These may call themselves recursively, or ask questions, etc.
908 * TRUE iff the directory containing dest_path needs to be rescanned.
911 /* dest_path is the dir containing src_path.
912 * Updates the global size_tally.
914 static gboolean do_usage(char *src_path, char *dest_path)
916 struct stat info;
918 check_flags();
920 if (mc_lstat(src_path, &info))
922 g_string_sprintf(message, "'%s:\n", src_path);
923 send();
924 send_error();
925 return FALSE;
928 if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
929 size_tally += info.st_size;
930 else if (S_ISDIR(info.st_mode))
932 g_string_sprintf(message, _("?Count contents of %s?"),
933 src_path);
934 if (reply(from_parent, FALSE))
936 char *safe_path;
937 safe_path = g_strdup(src_path);
938 for_dir_contents(do_usage, safe_path, safe_path);
939 g_free(safe_path);
943 return FALSE;
946 /* dest_path is the dir containing src_path */
947 static gboolean do_delete(char *src_path, char *dest_path)
949 struct stat info;
950 gboolean write_prot;
952 check_flags();
954 if (mc_lstat(src_path, &info))
956 send_error();
957 return FALSE;
960 write_prot = S_ISLNK(info.st_mode) ? FALSE
961 : access(src_path, W_OK) != 0;
962 if (write_prot || !quiet)
964 g_string_sprintf(message, _("?Delete %s'%s'?"),
965 write_prot ? _("WRITE-PROTECTED ") : " ",
966 src_path);
967 if (!reply(from_parent, write_prot && !o_force))
968 return FALSE;
970 else if (!o_brief)
972 g_string_sprintf(message, _("'Deleting '%s'\n"), src_path);
973 send();
976 if (S_ISDIR(info.st_mode))
978 char *safe_path;
979 safe_path = g_strdup(src_path);
980 for_dir_contents(do_delete, safe_path, safe_path);
981 if (rmdir(safe_path))
983 g_free(safe_path);
984 send_error();
985 return FALSE;
987 g_string_sprintf(message, _("'Directory '%s' deleted\n"),
988 safe_path);
989 send();
990 g_string_sprintf(message, "m%s", safe_path);
991 send();
992 g_free(safe_path);
994 else if (unlink(src_path))
996 send_error();
997 return FALSE;
1000 return TRUE;
1003 /* path is the item to check. If is is a directory then we may recurse
1004 * (unless prune is used).
1006 static gboolean do_find(char *path, char *dummy)
1008 FindInfo info;
1009 char *slash;
1011 check_flags();
1013 if (!quiet)
1015 g_string_sprintf(message, _("?Check '%s'?"), path);
1016 if (!reply(from_parent, FALSE))
1017 return FALSE;
1020 for (;;)
1022 if (new_entry_string)
1024 if (find_condition)
1025 find_condition_free(find_condition);
1026 find_condition = find_compile(new_entry_string);
1027 g_free(new_entry_string);
1028 new_entry_string = NULL;
1031 if (find_condition)
1032 break;
1034 g_string_assign(message, _("!Invalid find condition - "
1035 "change it and try again\n"));
1036 send();
1037 g_string_sprintf(message, _("?Check '%s'?"), path);
1038 if (!reply(from_parent, TRUE))
1039 return FALSE;
1042 if (mc_lstat(path, &info.stats))
1044 send_error();
1045 g_string_sprintf(message, _("'(while checking '%s')\n"), path);
1046 send();
1047 return FALSE;
1050 info.fullpath = path;
1051 time(&info.now); /* XXX: Not for each check! */
1053 slash = strrchr(path, '/');
1054 info.leaf = slash ? slash + 1 : path;
1055 info.prune = FALSE;
1056 if (find_test_condition(find_condition, &info))
1058 g_string_sprintf(message, "=%s", path);
1059 send();
1062 if (S_ISDIR(info.stats.st_mode) && !info.prune)
1064 char *safe_path;
1065 safe_path = g_strdup(path);
1066 for_dir_contents(do_find, safe_path, safe_path);
1067 g_free(safe_path);
1070 return FALSE;
1073 /* Like mode_compile(), but ignores spaces and bracketed bits */
1074 struct mode_change *nice_mode_compile(const char *mode_string,
1075 unsigned int masked_ops)
1077 GString *new;
1078 int brackets = 0;
1079 struct mode_change *retval = NULL;
1081 new = g_string_new(NULL);
1083 for (; *mode_string; mode_string++)
1085 if (*mode_string == '(')
1086 brackets++;
1087 if (*mode_string == ')')
1089 brackets--;
1090 if (brackets < 0)
1091 break;
1092 continue;
1095 if (brackets == 0 && *mode_string != ' ')
1096 g_string_append_c(new, *mode_string);
1099 if (brackets == 0)
1100 retval = mode_compile(new->str, masked_ops);
1101 g_string_free(new, TRUE);
1102 return retval;
1105 static gboolean do_chmod(char *path, char *dummy)
1107 struct stat info;
1108 mode_t new_mode;
1110 check_flags();
1112 if (mc_lstat(path, &info))
1114 send_error();
1115 return FALSE;
1117 if (S_ISLNK(info.st_mode))
1118 return FALSE;
1120 if (!quiet)
1122 g_string_sprintf(message,
1123 _("?Change permissions of '%s'?"), path);
1124 if (!reply(from_parent, FALSE))
1125 return FALSE;
1127 else if (!o_brief)
1129 g_string_sprintf(message,
1130 _("'Changing permissions of '%s'\n"),
1131 path);
1132 send();
1135 for (;;)
1137 if (new_entry_string)
1139 if (mode_change)
1140 mode_free(mode_change);
1141 mode_change = nice_mode_compile(new_entry_string,
1142 MODE_MASK_ALL);
1143 g_free(new_entry_string);
1144 new_entry_string = NULL;
1147 if (mode_change)
1148 break;
1150 g_string_assign(message,
1151 _("!Invalid mode command - change it and try again\n"));
1152 send();
1153 g_string_sprintf(message,
1154 _("?Change permissions of '%s'?"), path);
1155 if (!reply(from_parent, TRUE))
1156 return FALSE;
1159 if (mc_lstat(path, &info))
1161 send_error();
1162 return FALSE;
1164 if (S_ISLNK(info.st_mode))
1165 return FALSE;
1167 new_mode = mode_adjust(info.st_mode, mode_change);
1168 if (chmod(path, new_mode))
1170 send_error();
1171 return FALSE;
1174 if (o_recurse && S_ISDIR(info.st_mode))
1176 guchar *safe_path;
1177 safe_path = g_strdup(path);
1178 for_dir_contents(do_chmod, safe_path, safe_path);
1179 g_free(safe_path);
1182 return TRUE;
1185 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1186 * is set then that is the new leafname, otherwise the leafname stays
1187 * the same.
1189 static char *make_dest_path(char *object, char *dir)
1191 char *leaf;
1193 if (action_leaf)
1194 leaf = action_leaf;
1195 else
1197 leaf = strrchr(object, '/');
1198 if (!leaf)
1199 leaf = object; /* Error? */
1200 else
1201 leaf++;
1204 return make_path(dir, leaf)->str;
1207 /* If action_leaf is not NULL it specifies the new leaf name */
1208 static gboolean do_copy2(char *path, char *dest)
1210 char *dest_path;
1211 struct stat info;
1212 struct stat dest_info;
1213 gboolean retval = TRUE;
1215 check_flags();
1217 dest_path = make_dest_path(path, dest);
1219 if (mc_lstat(path, &info))
1221 send_error();
1222 return FALSE;
1225 if (mc_lstat(dest_path, &dest_info) == 0)
1227 int err;
1228 gboolean merge;
1230 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1232 g_string_sprintf(message, _("?'%s' already exists - %s?"),
1233 dest_path,
1234 merge ? _("merge contents") : _("overwrite"));
1236 if (!reply(from_parent, TRUE))
1237 return FALSE;
1239 if (!merge)
1241 if (S_ISDIR(dest_info.st_mode))
1242 err = rmdir(dest_path);
1243 else
1244 err = unlink(dest_path);
1246 if (err)
1248 send_error();
1249 if (errno != ENOENT)
1250 return FALSE;
1251 g_string_sprintf(message,
1252 _("'Trying copy anyway...\n"));
1253 send();
1257 else if (!quiet)
1259 g_string_sprintf(message,
1260 _("?Copy %s as %s?"), path, dest_path);
1261 if (!reply(from_parent, FALSE))
1262 return FALSE;
1264 else
1266 g_string_sprintf(message, _("'Copying %s as %s\n"), path,
1267 dest_path);
1268 send();
1271 if (S_ISDIR(info.st_mode))
1273 char *safe_path, *safe_dest;
1274 struct stat dest_info;
1275 gboolean exists;
1277 /* (we will do the update ourselves now, rather than
1278 * afterwards)
1280 retval = FALSE;
1282 safe_path = g_strdup(path);
1283 safe_dest = g_strdup(dest_path);
1285 exists = !mc_lstat(dest_path, &dest_info);
1287 if (exists && !S_ISDIR(dest_info.st_mode))
1289 g_string_sprintf(message,
1290 _("!ERROR: Destination already exists, "
1291 "but is not a directory\n"));
1293 else if (exists == FALSE && mkdir(dest_path, info.st_mode))
1294 send_error();
1295 else
1297 if (!exists)
1299 /* (just been created then) */
1300 g_string_sprintf(message, "+%s", dest);
1301 send();
1304 action_leaf = NULL;
1305 for_dir_contents(do_copy2, safe_path, safe_dest);
1306 /* Note: dest_path now invalid... */
1309 g_free(safe_path);
1310 g_free(safe_dest);
1312 else if (S_ISLNK(info.st_mode))
1314 char target[MAXPATHLEN + 1];
1315 int count;
1317 /* Not all versions of cp(1) can make symlinks,
1318 * so we special-case it.
1321 count = readlink(path, target, sizeof(target) - 1);
1322 if (count < 0)
1324 send_error();
1325 retval = FALSE;
1327 else
1329 target[count] = '\0';
1330 if (symlink(target, dest_path))
1332 send_error();
1333 retval = FALSE;
1337 else
1339 guchar *error;
1341 error = copy_file(path, dest_path);
1343 if (error)
1345 g_string_sprintf(message, _("!ERROR: %s\n"), error);
1346 send();
1347 retval = FALSE;
1351 return retval;
1354 /* If action_leaf is not NULL it specifies the new leaf name */
1355 static gboolean do_move2(char *path, char *dest)
1357 char *dest_path;
1358 gboolean retval = TRUE;
1359 char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1360 struct stat info2;
1361 gboolean is_dir;
1363 check_flags();
1365 dest_path = make_dest_path(path, dest);
1367 is_dir = mc_lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1369 if (access(dest_path, F_OK) == 0)
1371 struct stat info;
1372 int err;
1374 g_string_sprintf(message,
1375 _("?'%s' already exists - overwrite?"),
1376 dest_path);
1377 if (!reply(from_parent, TRUE))
1378 return FALSE;
1380 if (mc_lstat(dest_path, &info))
1382 send_error();
1383 return FALSE;
1386 if (S_ISDIR(info.st_mode))
1387 err = rmdir(dest_path);
1388 else
1389 err = unlink(dest_path);
1391 if (err)
1393 send_error();
1394 if (errno != ENOENT)
1395 return FALSE;
1396 g_string_sprintf(message,
1397 _("'Trying move anyway...\n"));
1398 send();
1401 else if (!quiet)
1403 g_string_sprintf(message,
1404 _("?Move %s as %s?"), path, dest_path);
1405 if (!reply(from_parent, FALSE))
1406 return FALSE;
1408 else
1410 g_string_sprintf(message, _("'Moving %s as %s\n"), path,
1411 dest_path);
1412 send();
1415 argv[2] = path;
1416 argv[3] = dest_path;
1418 if (fork_exec_wait(argv) == 0)
1420 char *leaf;
1422 leaf = strrchr(path, '/');
1423 if (!leaf)
1424 leaf = path; /* Error? */
1425 else
1426 leaf++;
1428 g_string_sprintf(message, "+%s", path);
1429 g_string_truncate(message, leaf - path + 1);
1430 send();
1431 if (is_dir) {
1432 g_string_sprintf(message, "m%s", path);
1433 send();
1436 else
1438 g_string_sprintf(message,
1439 _("!ERROR: Failed to move %s as %s\n"),
1440 path, dest_path);
1441 send();
1442 retval = FALSE;
1445 return retval;
1448 /* Copy path to dest.
1449 * Check that path not copied into itself.
1451 static gboolean do_copy(char *path, char *dest)
1453 if (is_sub_dir(make_dest_path(path, dest), path))
1455 g_string_sprintf(message,
1456 _("!ERROR: Can't copy object into itself\n"));
1457 send();
1458 return FALSE;
1460 return do_copy2(path, dest);
1463 /* Move path to dest.
1464 * Check that path not moved into itself.
1466 static gboolean do_move(char *path, char *dest)
1468 if (is_sub_dir(make_dest_path(path, dest), path))
1470 g_string_sprintf(message,
1471 _("!ERROR: Can't move/rename object into itself\n"));
1472 send();
1473 return FALSE;
1475 return do_move2(path, dest);
1478 static gboolean do_link(char *path, char *dest)
1480 char *dest_path;
1481 char *leaf;
1483 check_flags();
1485 leaf = strrchr(path, '/');
1486 if (!leaf)
1487 leaf = path; /* Error? */
1488 else
1489 leaf++;
1491 dest_path = make_path(dest, leaf)->str;
1493 if (quiet)
1495 g_string_sprintf(message, _("'Linking %s as %s\n"), path,
1496 dest_path);
1497 send();
1499 else
1501 g_string_sprintf(message,
1502 _("?Link %s as %s?"), path, dest_path);
1503 if (!reply(from_parent, FALSE))
1504 return FALSE;
1507 if (symlink(path, dest_path))
1509 send_error();
1510 return FALSE;
1513 return TRUE;
1516 /* Mount/umount this item (depending on 'mount') */
1517 static void do_mount(guchar *path, gboolean mount)
1519 char *argv[3] = {NULL, NULL, NULL};
1521 check_flags();
1523 argv[0] = mount ? "mount" : "umount";
1524 argv[1] = path;
1526 if (quiet)
1528 g_string_sprintf(message,
1529 mount ? _("'Mounting %s\n")
1530 : _("'Unmounting %s\n"),
1531 path);
1532 send();
1534 else
1536 g_string_sprintf(message,
1537 mount ? _("?Mount %s?\n")
1538 : _("?Unmount %s?\n"),
1539 path);
1540 if (!reply(from_parent, FALSE))
1541 return;
1544 if (fork_exec_wait(argv) == 0)
1546 g_string_sprintf(message, "M%s", path);
1547 send();
1549 else
1551 g_string_sprintf(message, _("!ERROR: Mount failed\n"));
1552 send();
1556 /* CHILD MAIN LOOPS */
1558 /* After forking, the child calls one of these functions */
1560 static void usage_cb(gpointer data)
1562 FilerWindow *filer_window = (FilerWindow *) data;
1563 Collection *collection = filer_window->collection;
1564 DirItem *item;
1565 int left = collection->number_selected;
1566 int i = -1;
1567 off_t total_size = 0;
1569 send_dir(filer_window->path);
1571 while (left > 0)
1573 i++;
1574 if (!collection->items[i].selected)
1575 continue;
1576 item = (DirItem *) collection->items[i].data;
1577 size_tally = 0;
1578 do_usage(make_path(filer_window->path,
1579 item->leafname)->str,
1580 filer_window->path);
1581 g_string_sprintf(message, "'%s: %s\n",
1582 item->leafname,
1583 format_size((unsigned long) size_tally));
1584 send();
1585 total_size += size_tally;
1586 left--;
1589 g_string_sprintf(message, _("'\nTotal: %s\n"),
1590 format_size((unsigned long) total_size));
1591 send();
1594 #ifdef DO_MOUNT_POINTS
1595 static void mount_cb(gpointer data)
1597 GList *paths = (GList *) data;
1598 gboolean mount_points = FALSE;
1600 for (; paths; paths = paths->next)
1602 guchar *path = (guchar *) paths->data;
1604 if (mount_is_mounted(path))
1605 do_mount(path, FALSE); /* Unmount */
1606 else if (g_hash_table_lookup(fstab_mounts, path))
1607 do_mount(path, TRUE); /* Mount */
1608 else
1609 continue;
1611 mount_points = TRUE;
1614 g_string_sprintf(message,
1615 mount_points ? _("'\nDone\n")
1616 : _("!No mount points selected!\n"));
1617 send();
1619 #endif
1621 static void delete_cb(gpointer data)
1623 FilerWindow *filer_window = (FilerWindow *) data;
1624 Collection *collection = filer_window->collection;
1625 DirItem *item;
1626 int left = collection->number_selected;
1627 int i = -1;
1629 send_dir(filer_window->path);
1631 while (left > 0)
1633 i++;
1634 if (!collection->items[i].selected)
1635 continue;
1636 item = (DirItem *) collection->items[i].data;
1637 if (do_delete(make_path(filer_window->path,
1638 item->leafname)->str,
1639 filer_window->path))
1641 g_string_sprintf(message, "+%s", filer_window->path);
1642 send();
1644 left--;
1647 g_string_sprintf(message, _("'\nDone\n"));
1648 send();
1651 static void find_cb(gpointer data)
1653 FilerWindow *filer_window = (FilerWindow *) data;
1654 Collection *collection = filer_window->collection;
1655 DirItem *item;
1656 int left;
1657 int i;
1659 send_dir(filer_window->path);
1661 while (1)
1663 left = collection->number_selected;
1664 i = -1;
1666 while (left > 0)
1668 i++;
1669 if (!collection->items[i].selected)
1670 continue;
1671 item = (DirItem *) collection->items[i].data;
1672 do_find(make_path(filer_window->path,
1673 item->leafname)->str,
1674 NULL);
1675 left--;
1678 g_string_assign(message, _("?Another search?"));
1679 if (!reply(from_parent, TRUE))
1680 break;
1681 g_string_assign(message, "#");
1682 send();
1685 g_string_sprintf(message, _("'\nDone\n"));
1686 send();
1689 static void chmod_cb(gpointer data)
1691 FilerWindow *filer_window = (FilerWindow *) data;
1692 Collection *collection = filer_window->collection;
1693 DirItem *item;
1694 int left = collection->number_selected;
1695 int i = -1;
1697 send_dir(filer_window->path);
1699 while (left > 0)
1701 i++;
1702 if (!collection->items[i].selected)
1703 continue;
1704 item = (DirItem *) collection->items[i].data;
1705 if (item->flags & ITEM_FLAG_SYMLINK)
1707 g_string_sprintf(message,
1708 _("!'%s' is a symbolic link\n"),
1709 item->leafname);
1710 send();
1712 else if (do_chmod(make_path(filer_window->path,
1713 item->leafname)->str, NULL))
1715 g_string_sprintf(message, "+%s", filer_window->path);
1716 send();
1718 left--;
1721 g_string_sprintf(message, _("'\nDone\n"));
1722 send();
1725 static void list_cb(gpointer data)
1727 GSList *paths = (GSList *) data;
1729 while (paths)
1731 send_dir((char *) paths->data);
1733 if (action_do_func((char *) paths->data, action_dest))
1735 g_string_sprintf(message, "+%s", action_dest);
1736 send();
1739 paths = paths->next;
1742 g_string_sprintf(message, _("'\nDone\n"));
1743 send();
1746 static void add_toggle(GUIside *gui_side, guchar *label, guchar *code)
1748 GtkWidget *check;
1750 check = gtk_check_button_new_with_label(label);
1751 gtk_object_set_data(GTK_OBJECT(check), "send-code", code);
1752 gtk_signal_connect(GTK_OBJECT(check), "clicked",
1753 button_reply, gui_side);
1754 gtk_box_pack_start(GTK_BOX(gui_side->vbox), check, FALSE, TRUE, 0);
1758 /* EXTERNAL INTERFACE */
1760 void action_find(FilerWindow *filer_window)
1762 GUIside *gui_side;
1763 Collection *collection;
1764 GtkWidget *hbox, *label, *scroller;
1765 gchar *titles[2];
1767 titles[0] = _("Name");
1768 titles[1] = _("Directory");
1770 collection = filer_window->collection;
1772 if (collection->number_selected < 1)
1774 report_error(PROJECT, _("You need to select some items "
1775 "to search through"));
1776 return;
1779 if (!last_find_string)
1780 last_find_string = g_strdup("'core'");
1782 new_entry_string = last_find_string;
1783 gui_side = start_action(filer_window, find_cb, FALSE);
1784 if (!gui_side)
1785 return;
1787 gui_side->show_info = TRUE;
1789 scroller = gtk_scrolled_window_new(NULL, NULL);
1790 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller),
1791 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
1792 gtk_box_pack_start(GTK_BOX(gui_side->vbox), scroller, TRUE, TRUE, 4);
1793 gui_side->results = gtk_clist_new_with_titles(
1794 sizeof(titles) / sizeof(*titles), titles);
1795 gtk_clist_column_titles_passive(GTK_CLIST(gui_side->results));
1796 gtk_widget_set_usize(gui_side->results, 100, 100);
1797 gtk_clist_set_column_width(GTK_CLIST(gui_side->results), 0, 100);
1798 gtk_clist_set_selection_mode(GTK_CLIST(gui_side->results),
1799 GTK_SELECTION_SINGLE);
1800 gtk_container_add(GTK_CONTAINER(scroller), gui_side->results);
1801 gtk_box_set_child_packing(GTK_BOX(gui_side->vbox),
1802 gui_side->log_hbox, FALSE, TRUE, 4, GTK_PACK_START);
1803 gtk_signal_connect(GTK_OBJECT(gui_side->results), "select_row",
1804 GTK_SIGNAL_FUNC(select_row_callback), gui_side);
1806 hbox = gtk_hbox_new(FALSE, 0);
1807 label = gtk_label_new(_("Expression:"));
1808 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
1809 gui_side->default_string = &last_find_string;
1810 gui_side->entry = gtk_entry_new();
1811 gtk_widget_set_style(gui_side->entry, fixed_style);
1812 gtk_entry_set_text(GTK_ENTRY(gui_side->entry), last_find_string);
1813 gtk_editable_select_region(GTK_EDITABLE(gui_side->entry), 0, -1);
1814 gtk_widget_set_sensitive(gui_side->entry, FALSE);
1815 gtk_box_pack_start(GTK_BOX(hbox), gui_side->entry, TRUE, TRUE, 4);
1816 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed",
1817 entry_changed, gui_side);
1818 gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 4);
1819 gtk_box_pack_start(GTK_BOX(hbox),
1820 new_help_button(show_condition_help, NULL),
1821 FALSE, TRUE, 4);
1823 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Find"));
1824 gtk_window_set_focus(GTK_WINDOW(gui_side->window), gui_side->entry);
1825 gtk_signal_connect_object(GTK_OBJECT(gui_side->entry), "activate",
1826 gtk_button_clicked, GTK_OBJECT(gui_side->quiet));
1827 number_of_windows++;
1828 gtk_widget_show_all(gui_side->window);
1831 /* Count disk space used by selected items */
1832 void action_usage(FilerWindow *filer_window)
1834 GUIside *gui_side;
1835 Collection *collection;
1837 collection = filer_window->collection;
1839 if (collection->number_selected < 1)
1841 report_error(PROJECT,
1842 _("You need to select some items to count"));
1843 return;
1846 gui_side = start_action(filer_window, usage_cb, TRUE);
1847 if (!gui_side)
1848 return;
1850 gui_side->show_info = TRUE;
1852 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Disk Usage"));
1853 number_of_windows++;
1854 gtk_widget_show_all(gui_side->window);
1857 /* Mount/unmount listed items (paths).
1858 * Free the list after this function returns.
1860 void action_mount(GList *paths)
1862 #ifdef DO_MOUNT_POINTS
1863 GUIside *gui_side;
1865 gui_side = start_action(paths, mount_cb,
1866 option_get_int("action_mount"));
1867 if (!gui_side)
1868 return;
1870 gtk_window_set_title(GTK_WINDOW(gui_side->window),
1871 _("Mount / Unmount"));
1872 number_of_windows++;
1873 gtk_widget_show_all(gui_side->window);
1874 #else
1875 report_error(PROJECT,
1876 _("ROX-Filer does not yet support mount points on your "
1877 "system. Sorry."));
1878 #endif /* DO_MOUNT_POINTS */
1881 /* Deletes all selected items in the window */
1882 void action_delete(FilerWindow *filer_window)
1884 GUIside *gui_side;
1885 Collection *collection;
1887 collection = filer_window->collection;
1889 if (collection->number_selected < 1)
1891 report_error(PROJECT,
1892 _("You need to select some items to delete"));
1893 return;
1896 if (!remove_pinned_ok(filer_window))
1897 return;
1899 gui_side = start_action(filer_window, delete_cb,
1900 option_get_int("action_delete"));
1901 if (!gui_side)
1902 return;
1904 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Delete"));
1905 add_toggle(gui_side,
1906 _("Force - don't confirm deletion of non-writeable items"),
1907 "F");
1908 add_toggle(gui_side,
1909 _("Brief - only log directories being deleted"),
1910 "B");
1912 number_of_windows++;
1913 gtk_widget_show_all(gui_side->window);
1916 /* Change the permissions of the selected items */
1917 void action_chmod(FilerWindow *filer_window)
1919 GUIside *gui_side;
1920 Collection *collection;
1921 GtkWidget *hbox, *label, *combo;
1922 static GList *presets = NULL;
1924 collection = filer_window->collection;
1926 if (collection->number_selected < 1)
1928 report_error(PROJECT,
1929 _("You need to select the items "
1930 "whose permissions you want to change"));
1931 return;
1934 if (!presets)
1936 presets = g_list_append(presets,
1937 _("a+x (Make executable/searchable)"));
1938 presets = g_list_append(presets,
1939 _("a-x (Make non-executable/non-searchable)"));
1940 presets = g_list_append(presets,
1941 _("u+rw (Give owner read+write)"));
1942 presets = g_list_append(presets,
1943 _("go-rwx (Private - owner access only)"));
1944 presets = g_list_append(presets,
1945 _("go=u-w (Public access, not write)"));
1948 if (!last_chmod_string)
1949 last_chmod_string = g_strdup((guchar *) presets->data);
1950 new_entry_string = last_chmod_string;
1951 gui_side = start_action(filer_window, chmod_cb, FALSE);
1952 if (!gui_side)
1953 return;
1955 add_toggle(gui_side,
1956 _("Brief - don't list processed files"), "B");
1957 add_toggle(gui_side,
1958 _("Recurse - also change contents of subdirectories"), "R");
1960 hbox = gtk_hbox_new(FALSE, 0);
1961 label = gtk_label_new(_("Command:"));
1962 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
1963 gui_side->default_string = &last_chmod_string;
1965 combo = gtk_combo_new();
1966 gtk_combo_disable_activate(GTK_COMBO(combo));
1967 gtk_combo_set_use_arrows_always(GTK_COMBO(combo), TRUE);
1968 gtk_combo_set_popdown_strings(GTK_COMBO(combo), presets);
1970 gui_side->entry = GTK_COMBO(combo)->entry;
1971 gtk_entry_set_text(GTK_ENTRY(gui_side->entry), last_chmod_string);
1972 gtk_editable_select_region(GTK_EDITABLE(gui_side->entry), 0, -1);
1973 gtk_widget_set_sensitive(gui_side->entry, FALSE);
1974 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 4);
1975 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed",
1976 entry_changed, gui_side);
1977 gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 0);
1978 gtk_box_pack_start(GTK_BOX(hbox),
1979 new_help_button(show_chmod_help, NULL),
1980 FALSE, TRUE, 4);
1982 gtk_window_set_focus(GTK_WINDOW(gui_side->window), gui_side->entry);
1983 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Permissions"));
1984 gtk_signal_connect_object(GTK_OBJECT(gui_side->entry), "activate",
1985 gtk_button_clicked, GTK_OBJECT(gui_side->yes));
1987 number_of_windows++;
1988 gtk_widget_show_all(gui_side->window);
1991 /* If leaf is NULL then the copy has the same name as the original */
1992 void action_copy(GSList *paths, char *dest, char *leaf)
1994 GUIside *gui_side;
1996 action_dest = dest;
1997 action_leaf = leaf;
1998 action_do_func = do_copy;
1999 gui_side = start_action(paths, list_cb, option_get_int("action_copy"));
2000 if (!gui_side)
2001 return;
2003 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Copy"));
2004 number_of_windows++;
2005 gtk_widget_show_all(gui_side->window);
2008 /* If leaf is NULL then the file is not renamed */
2009 void action_move(GSList *paths, char *dest, char *leaf)
2011 GUIside *gui_side;
2013 action_dest = dest;
2014 action_leaf = leaf;
2015 action_do_func = do_move;
2016 gui_side = start_action(paths, list_cb, option_get_int("action_move"));
2017 if (!gui_side)
2018 return;
2020 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Move"));
2021 number_of_windows++;
2022 gtk_widget_show_all(gui_side->window);
2025 void action_link(GSList *paths, char *dest)
2027 GUIside *gui_side;
2029 action_dest = dest;
2030 action_do_func = do_link;
2031 gui_side = start_action(paths, list_cb, option_get_int("action_link"));
2032 if (!gui_side)
2033 return;
2035 gtk_window_set_title(GTK_WINDOW(gui_side->window), _("Link"));
2036 number_of_windows++;
2037 gtk_widget_show_all(gui_side->window);
2040 void action_init(void)
2042 option_add_int("action_copy", 1, NULL);
2043 option_add_int("action_move", 1, NULL);
2044 option_add_int("action_link", 1, NULL);
2045 option_add_int("action_delete", 0, NULL);
2046 option_add_int("action_mount", 1, NULL);
2049 #define MAX_ASK 4
2051 /* Check to see if any of the selected items (or their children) are
2052 * on the pinboard or panel. If so, ask for confirmation.
2054 * TRUE if it's OK to lose them.
2056 static gboolean remove_pinned_ok(FilerWindow *filer_window)
2058 Collection *collection = filer_window->collection;
2059 int i;
2060 GList *ask = NULL, *next;
2061 GString *message;
2062 int ask_n = 0;
2063 gboolean retval;
2065 for (i = 0; i < collection->number_of_items; i++)
2067 DirItem *item;
2068 guchar *path;
2070 if (!collection->items[i].selected)
2071 continue;
2073 item = (DirItem *) collection->items[i].data;
2074 path = make_path(filer_window->path, item->leafname)->str;
2076 if (icons_require(path))
2078 if (++ask_n > MAX_ASK)
2079 break;
2080 ask = g_list_append(ask, item->leafname);
2084 if (!ask)
2085 return TRUE;
2087 if (ask_n > MAX_ASK)
2089 message = g_string_new(_("Deleting items such as "));
2090 ask_n--;
2092 else if (ask_n == 1)
2093 message = g_string_new(_("Deleting the item "));
2094 else
2095 message = g_string_new(_("Deleting the items "));
2097 i = 0;
2098 for (next = ask; next; next = next->next)
2100 guchar *leaf = (guchar *) next->data;
2102 g_string_append_c(message, '`');
2103 g_string_append(message, leaf);
2104 g_string_append_c(message, '\'');
2105 i++;
2106 if (i == ask_n - 1 && i > 0)
2107 g_string_append(message, " and ");
2108 else if (i < ask_n)
2109 g_string_append(message, ", ");
2112 g_list_free(ask);
2114 if (ask_n == 1)
2115 message = g_string_append(message,
2116 _(" will affect some items on the pinboard "
2117 "or panel - really delete it?"));
2118 else
2120 if (ask_n > MAX_ASK)
2121 message = g_string_append_c(message, ',');
2122 message = g_string_append(message,
2123 _(" will affect some items on the pinboard "
2124 "or panel - really delete them?"));
2127 retval = get_choice(PROJECT, message->str,
2128 2, _("OK"), _("Cancel")) == 0;
2130 g_string_free(message, TRUE);
2132 return retval;