r225: Added support for searching multiple cases and shell-style wildcards.
[rox-filer.git] / ROX-Filer / src / action.c
blob7ae2762644572789c656640455a930468870c1b7
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2000, Thomas Leonard, <tal197@ecs.soton.ac.uk>.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
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 <unistd.h>
33 #include <errno.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <signal.h>
37 #include <dirent.h>
38 #include <sys/time.h>
40 #include "action.h"
41 #include "string.h"
42 #include "support.h"
43 #include "gui_support.h"
44 #include "filer.h"
45 #include "main.h"
46 #include "options.h"
47 #include "modechange.h"
48 #include "find.h"
50 /* Options bits */
51 static GtkWidget *create_options();
52 static void update_options();
53 static void set_options();
54 static void save_options();
55 static char *action_auto_quiet(char *data);
57 static OptionsSection options =
59 "Action window options",
60 create_options,
61 update_options,
62 set_options,
63 save_options
66 static gboolean o_auto_copy = TRUE;
67 static gboolean o_auto_move = TRUE;
68 static gboolean o_auto_link = TRUE;
69 static gboolean o_auto_delete = FALSE;
70 static gboolean o_auto_mount = TRUE;
72 static GtkWidget *w_auto_copy = NULL;
73 static GtkWidget *w_auto_move = NULL;
74 static GtkWidget *w_auto_link = NULL;
75 static GtkWidget *w_auto_delete = NULL;
76 static GtkWidget *w_auto_mount = NULL;
78 static GdkColor red = {0, 0xffff, 0, 0};
80 /* Parent->Child messages are one character each:
82 * Q/Y/N Quiet/Yes/No button clicked
83 * F Force deletion of non-writeable items
86 #define SENSITIVE_YESNO(gui_side, state) \
87 do { \
88 gtk_widget_set_sensitive((gui_side)->yes, state); \
89 gtk_widget_set_sensitive((gui_side)->no, state); \
90 if ((gui_side)->entry) \
91 gtk_widget_set_sensitive((gui_side)->entry, state);\
92 } while (0)
94 #define ON(flag) ((flag) ? "on" : "off")
96 typedef struct _GUIside GUIside;
97 typedef void ActionChild(gpointer data);
98 typedef gboolean ForDirCB(char *path, char *dest_path);
100 struct _GUIside
102 int from_child; /* File descriptor */
103 FILE *to_child;
104 int input_tag; /* gdk_input_add() */
105 GtkWidget *vbox, *log, *window, *dir;
106 GtkWidget *quiet, *yes, *no;
107 int child; /* Process ID */
108 int errors;
109 gboolean show_info; /* For Disk Usage */
111 GtkWidget *entry; /* May be NULL */
112 guchar **default_string; /* Changed when the entry changes */
114 char *next_dir; /* NULL => no timer active */
115 gint next_timer;
118 /* These don't need to be in a structure because we fork() before
119 * using them again.
121 static int from_parent = 0;
122 static FILE *to_parent = NULL;
123 static gboolean quiet = FALSE;
124 static GString *message = NULL;
125 static char *action_dest = NULL;
126 static char *action_leaf = NULL;
127 static gboolean (*action_do_func)(char *source, char *dest);
128 static size_t size_tally; /* For Disk Usage */
129 static DirItem *mount_item;
131 static struct mode_change *mode_change = NULL; /* For Permissions */
132 static FindCondition *find_condition = NULL; /* For Find */
134 static gboolean o_force = FALSE;
135 static gboolean o_brief = FALSE;
136 static gboolean o_recurse = FALSE;
138 /* Whenever the text in these boxes is changed we store a copy of the new
139 * string to be used as the default next time.
141 static guchar *last_chmod_string = NULL;
142 static guchar *last_find_string = NULL;
144 /* Set to one of the above before forking. This may change over a call to
145 * reply(). It is reset to NULL once the text is parsed.
147 static guchar *new_entry_string = NULL;
149 /* Static prototypes */
150 static gboolean send();
151 static gboolean send_error();
152 static gboolean send_dir(char *dir);
153 static gboolean read_exact(int source, char *buffer, ssize_t len);
154 static void do_mount(FilerWindow *filer_window, DirItem *item);
155 static void add_toggle(GUIside *gui_side, guchar *label, guchar *code);
156 static gboolean reply(int fd, gboolean ignore_quiet);
158 /* OPTIONS */
160 /* Build up some option widgets to go in the options dialog, but don't
161 * fill them in yet.
163 static GtkWidget *create_options()
165 GtkWidget *vbox, *hbox, *label;
167 vbox = gtk_vbox_new(FALSE, 0);
168 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
170 label = gtk_label_new("Auto-start (Quiet) these actions:");
171 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
173 hbox = gtk_hbox_new(TRUE, 0);
174 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
176 w_auto_copy = gtk_check_button_new_with_label("Copy");
177 gtk_box_pack_start(GTK_BOX(hbox), w_auto_copy, FALSE, TRUE, 0);
178 w_auto_move = gtk_check_button_new_with_label("Move");
179 gtk_box_pack_start(GTK_BOX(hbox), w_auto_move, FALSE, TRUE, 0);
180 w_auto_link = gtk_check_button_new_with_label("Link");
181 gtk_box_pack_start(GTK_BOX(hbox), w_auto_link, FALSE, TRUE, 0);
182 w_auto_delete = gtk_check_button_new_with_label("Delete");
183 gtk_box_pack_start(GTK_BOX(hbox), w_auto_delete, FALSE, TRUE, 0);
184 w_auto_mount = gtk_check_button_new_with_label("Mount");
185 gtk_box_pack_start(GTK_BOX(hbox), w_auto_mount, FALSE, TRUE, 0);
187 return vbox;
190 /* Reflect current state by changing the widgets in the options box */
191 static void update_options()
193 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_copy),
194 o_auto_copy);
195 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_move),
196 o_auto_move);
197 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_link),
198 o_auto_link);
199 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_delete),
200 o_auto_delete);
201 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w_auto_mount),
202 o_auto_mount);
205 /* Set current values by reading the states of the widgets in the options box */
206 static void set_options()
208 o_auto_copy = gtk_toggle_button_get_active(
209 GTK_TOGGLE_BUTTON(w_auto_copy));
210 o_auto_move = gtk_toggle_button_get_active(
211 GTK_TOGGLE_BUTTON(w_auto_move));
212 o_auto_link = gtk_toggle_button_get_active(
213 GTK_TOGGLE_BUTTON(w_auto_link));
214 o_auto_delete = gtk_toggle_button_get_active(
215 GTK_TOGGLE_BUTTON(w_auto_delete));
216 o_auto_mount = gtk_toggle_button_get_active(
217 GTK_TOGGLE_BUTTON(w_auto_mount));
220 static void save_options()
222 guchar str[] = "cmldt";
224 if (o_auto_copy)
225 str[0] = 'C';
226 if (o_auto_move)
227 str[1] = 'M';
228 if (o_auto_link)
229 str[2] = 'L';
230 if (o_auto_delete)
231 str[3] = 'D';
232 if (o_auto_mount)
233 str[4] = 'T';
235 option_write("action_auto_quiet", str);
238 static char *action_auto_quiet(char *data)
240 while (*data)
242 char c = *data++;
243 gboolean state;
245 state = isupper(c);
247 switch (tolower(c))
249 case 'c':
250 o_auto_copy = state;
251 break;
252 case 'm':
253 o_auto_move = state;
254 break;
255 case 'l':
256 o_auto_link = state;
257 break;
258 case 'd':
259 o_auto_delete = state;
260 break;
261 case 't':
262 o_auto_mount = state;
263 break;
264 default:
265 return "Unknown flag";
269 return NULL;
272 /* SUPPORT */
274 /* This is called whenever the user edits the entry box (if any) - send the
275 * new string.
277 static void entry_changed(GtkEntry *entry, GUIside *gui_side)
279 guchar *text;
281 g_return_if_fail(gui_side->default_string != NULL);
283 text = gtk_entry_get_text(entry);
285 g_free(*(gui_side->default_string));
286 *(gui_side->default_string) = g_strdup(text);
288 if (!gui_side->to_child)
289 return;
291 fputc('E', gui_side->to_child);
292 fputs(text, gui_side->to_child);
293 fputc('\n', gui_side->to_child);
294 fflush(gui_side->to_child);
297 static void show_condition_help(gpointer data)
299 static GtkWidget *help = NULL;
301 if (!help)
303 GtkWidget *text, *vbox, *button, *hbox, *sep;
305 help = gtk_window_new(GTK_WINDOW_DIALOG);
306 gtk_container_set_border_width(GTK_CONTAINER(help), 10);
307 gtk_window_set_title(GTK_WINDOW(help),
308 "Find expression reference");
310 vbox = gtk_vbox_new(FALSE, 0);
311 gtk_container_add(GTK_CONTAINER(help), vbox);
313 text = gtk_label_new(
314 "An expression is a series of cases which are checked from left \n"
315 "to right. As soon as one matches, the expression is TRUE. If none \n"
316 "match then the expression is FALSE.\n"
317 "\n"
318 "Cases are separated by commas:\n"
319 "'*.htm', '*.html' (finds HTML files)\n"
320 "\n"
321 "Each case is a list of conditions which must all be met for the \n"
322 "case to succeed:\n"
323 "IsReg 'core' (finds a regular file called 'core')\n"
324 "Preceeding a condition by ! inverts the result of the condition:\n"
325 "IsDev ! '/dev/*' (finds device files not in /dev)\n"
326 "\n"
327 "Simple tests are: IsReg, IsLink, IsDir, IsChar, IsBlock, IsDev,\n"
328 "IsPipe, IsSUID, IsSGID, IsSticky, IsReadable, IsWriteable, IsEmpty\n"
329 "IsExecutable, IsMine\n"
330 "\n"
331 "A pattern in single quotes is a shell-style wildcard pattern to \n"
332 "match. If it contains a slash then the match is agaist the full \n"
333 "path; otherwise it is agaist the leafname only.\n"
334 "\n"
335 "Read the ROX-Filer manual for full details.");
336 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
337 gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 0);
339 hbox = gtk_hbox_new(FALSE, 20);
340 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
342 sep = gtk_hseparator_new();
343 gtk_box_pack_start(GTK_BOX(hbox), sep, TRUE, TRUE, 0);
344 button = gtk_button_new_with_label("Close");
345 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
346 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
347 gtk_widget_hide, GTK_OBJECT(help));
349 gtk_signal_connect_object(GTK_OBJECT(help), "delete_event",
350 gtk_widget_hide, GTK_OBJECT(help));
353 if (GTK_WIDGET_VISIBLE(help))
354 gtk_widget_hide(help);
355 gtk_widget_show_all(help);
358 static void show_chmod_help(gpointer data)
360 static GtkWidget *help = NULL;
362 if (!help)
364 GtkWidget *text, *vbox, *button, *hbox, *sep;
366 help = gtk_window_new(GTK_WINDOW_DIALOG);
367 gtk_container_set_border_width(GTK_CONTAINER(help), 10);
368 gtk_window_set_title(GTK_WINDOW(help),
369 "Permissions command reference");
371 vbox = gtk_vbox_new(FALSE, 0);
372 gtk_container_add(GTK_CONTAINER(help), vbox);
374 text = gtk_label_new(
375 "The format of a command is:\n"
376 "CHANGE, CHANGE, ...\n"
377 "Each CHANGE is:\n"
378 "WHO HOW PERMISSIONS\n"
379 "WHO is some combination of u, g and o which determines whether to\n"
380 "change the permissions for the User (owner), Group or Others.\n"
381 "HOW is +, - or = to add, remove or set exactly the permissions.\n"
382 "PERMISSIONS is some combination of the letters 'rwxXstugo'\n\n"
384 "Examples:\n"
385 "u+rw (the file owner gains read and write permission)\n"
386 "g=u (the group permissions are set to be the same as the user's)\n"
387 "o=u-w (others get the same permissions as the owner, but without "
388 "write permission)\n"
389 "a+x (everyone gets execute/access permission - same as 'ugo+x')\n"
390 "a+X (directories become accessable by everyone; files which were\n"
391 "executable by anyone become executable by everyone)\n"
392 "u+rw, go+r (two commands at once!)\n"
393 "u+s (set the SetUID bit - often has no effect on script files)\n"
394 "755 (set the permissions directly)\n"
396 "\nSee the chmod(1) man page for full details.");
397 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
398 gtk_box_pack_start(GTK_BOX(vbox), text, TRUE, TRUE, 0);
400 hbox = gtk_hbox_new(FALSE, 20);
401 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
403 sep = gtk_hseparator_new();
404 gtk_box_pack_start(GTK_BOX(hbox), sep, TRUE, TRUE, 0);
405 button = gtk_button_new_with_label("Close");
406 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
407 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
408 gtk_widget_hide, GTK_OBJECT(help));
410 gtk_signal_connect_object(GTK_OBJECT(help), "delete_event",
411 gtk_widget_hide, GTK_OBJECT(help));
414 if (GTK_WIDGET_VISIBLE(help))
415 gtk_widget_hide(help);
416 gtk_widget_show_all(help);
419 /* TRUE iff `sub' is (or would be) an object inside the directory `parent',
420 * (or the two are the same directory)
422 static gboolean is_sub_dir(char *sub, char *parent)
424 int parent_len;
425 guchar *real_sub, *real_parent;
426 gboolean retval;
428 real_sub = pathdup(sub);
429 real_parent = pathdup(parent);
431 parent_len = strlen(real_parent);
432 if (strncmp(real_parent, real_sub, parent_len))
433 retval = FALSE;
434 else
436 /* real_sub is at least as long as real_parent and all
437 * characters upto real_parent's length match.
440 retval = real_sub[parent_len] == '\0' ||
441 real_sub[parent_len] == '/';
444 g_free(real_sub);
445 g_free(real_parent);
447 return retval;
450 static gboolean display_dir(gpointer data)
452 GUIside *gui_side = (GUIside *) data;
454 gtk_label_set_text(GTK_LABEL(gui_side->dir), gui_side->next_dir + 1);
455 g_free(gui_side->next_dir);
456 gui_side->next_dir = NULL;
458 return FALSE;
461 /* Called when the child sends us a message */
462 static void message_from_child(gpointer data,
463 gint source,
464 GdkInputCondition condition)
466 char buf[5];
467 GUIside *gui_side = (GUIside *) data;
468 GtkWidget *log = gui_side->log;
470 if (read_exact(source, buf, 4))
472 ssize_t message_len;
473 char *buffer;
475 buf[4] = '\0';
476 message_len = strtol(buf, NULL, 16);
477 buffer = g_malloc(message_len + 1);
478 if (message_len > 0 && read_exact(source, buffer, message_len))
480 buffer[message_len] = '\0';
481 if (*buffer == '?')
482 SENSITIVE_YESNO(gui_side, TRUE);
483 else if (*buffer == '+')
485 refresh_dirs(buffer + 1);
486 g_free(buffer);
487 return;
489 else if (*buffer == 'm')
491 filer_check_mounted(buffer + 1);
492 g_free(buffer);
493 return;
495 else if (*buffer == '/')
497 if (gui_side->next_dir)
498 g_free(gui_side->next_dir);
499 else
500 gui_side->next_timer =
501 gtk_timeout_add(500,
502 display_dir,
503 gui_side);
504 gui_side->next_dir = buffer;
505 return;
507 else if (*buffer == '!')
508 gui_side->errors++;
510 gtk_text_insert(GTK_TEXT(log),
511 NULL,
512 *buffer == '!' ? &red : NULL,
513 NULL,
514 buffer + 1, message_len - 1);
515 g_free(buffer);
516 return;
518 g_print("Child died in the middle of a message.\n");
521 /* The child is dead */
522 gui_side->child = 0;
524 fclose(gui_side->to_child);
525 gui_side->to_child = NULL;
526 close(gui_side->from_child);
527 gdk_input_remove(gui_side->input_tag);
528 gtk_widget_set_sensitive(gui_side->quiet, FALSE);
530 if (gui_side->errors)
532 GString *report;
533 report = g_string_new(NULL);
534 g_string_sprintf(report, "There %s %d error%s.\n",
535 gui_side->errors == 1 ? "was" : "were",
536 gui_side->errors,
537 gui_side->errors == 1 ? "" : "s");
538 gtk_text_insert(GTK_TEXT(log), NULL, &red, NULL,
539 report->str, report->len);
541 g_string_free(report, TRUE);
543 else if (gui_side->show_info == FALSE)
544 gtk_widget_destroy(gui_side->window);
547 /* Scans src_dir, updating dest_path whenever cb returns TRUE */
548 static void for_dir_contents(ForDirCB *cb, char *src_dir, char *dest_path)
550 DIR *d;
551 struct dirent *ent;
552 GSList *list = NULL, *next;
554 d = opendir(src_dir);
555 if (!d)
557 send_error();
558 return;
561 send_dir(src_dir);
563 while ((ent = readdir(d)))
565 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0'
566 || (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
567 continue;
568 list = g_slist_append(list, g_strdup(make_path(src_dir,
569 ent->d_name)->str));
571 closedir(d);
573 if (!list)
574 return;
576 next = list;
578 while (next)
580 if (cb((char *) next->data, dest_path))
582 g_string_sprintf(message, "+%s", dest_path);
583 send();
586 g_free(next->data);
587 next = next->next;
589 g_slist_free(list);
590 return;
593 /* Read this many bytes into the buffer. TRUE on success. */
594 static gboolean read_exact(int source, char *buffer, ssize_t len)
596 while (len > 0)
598 ssize_t got;
599 got = read(source, buffer, len);
600 if (got < 1)
601 return FALSE;
602 len -= got;
603 buffer += got;
605 return TRUE;
608 /* Send 'message' to our parent process. TRUE on success. */
609 static gboolean send()
611 char len_buffer[5];
612 ssize_t len;
614 g_return_val_if_fail(message->len < 0xffff, FALSE);
616 sprintf(len_buffer, "%04x", message->len);
617 fwrite(len_buffer, 1, 4, to_parent);
618 len = fwrite(message->str, 1, message->len, to_parent);
619 fflush(to_parent);
620 return len == message->len;
623 /* Set the directory indicator at the top of the window */
624 static gboolean send_dir(char *dir)
626 g_string_sprintf(message, "/%s", dir);
627 return send();
630 static gboolean send_error()
632 g_string_sprintf(message, "!ERROR: %s\n", g_strerror(errno));
633 return send();
636 static void button_reply(GtkWidget *button, GUIside *gui_side)
638 char *text;
640 if (!gui_side->to_child)
641 return;
643 text = gtk_object_get_data(GTK_OBJECT(button), "send-code");
644 g_return_if_fail(text != NULL);
645 fputc(*text, gui_side->to_child);
646 fflush(gui_side->to_child);
648 if (*text == 'Y' || *text == 'N' || *text == 'Q')
649 SENSITIVE_YESNO(gui_side, FALSE);
652 static void process_flag(char flag)
654 switch (flag)
656 case 'Q':
657 quiet = !quiet;
658 break;
659 case 'F':
660 o_force = !o_force;
661 break;
662 case 'R':
663 o_recurse = !o_recurse;
664 break;
665 case 'B':
666 o_brief = !o_brief;
667 break;
668 default:
669 g_string_sprintf(message,
670 "!ERROR: Bad message '%c'\n", flag);
671 send();
672 break;
676 /* If the parent has sent any flag toggles, read them */
677 static void check_flags(void)
679 fd_set set;
680 int got;
681 char retval;
682 struct timeval tv;
684 FD_ZERO(&set);
686 while (1)
688 FD_SET(from_parent, &set);
689 tv.tv_sec = 0;
690 tv.tv_usec = 0;
691 got = select(from_parent + 1, &set, NULL, NULL, &tv);
693 if (got == -1)
694 g_error("select() failed: %s\n", g_strerror(errno));
695 else if (!got)
696 return;
698 got = read(from_parent, &retval, 1);
699 if (got != 1)
700 g_error("read() error: %s\n", g_strerror(errno));
702 process_flag(retval);
706 static void read_new_entry_text(void)
708 int len;
709 char c;
710 GString *new;
712 new = g_string_new(NULL);
714 for (;;)
716 len = read(from_parent, &c, 1);
717 if (len != 1)
719 fprintf(stderr, "read() error: %s\n",
720 g_strerror(errno));
721 _exit(1); /* Parent died? */
724 if (c == '\n')
725 break;
726 g_string_append_c(new, c);
729 g_free(new_entry_string);
730 new_entry_string = new->str;
731 g_string_free(new, FALSE);
734 /* Read until the user sends a reply. If ignore_quiet is TRUE then
735 * the user MUST click Yes or No, else treat quiet on as Yes.
736 * If the user needs prompting then does send().
738 static gboolean reply(int fd, gboolean ignore_quiet)
740 ssize_t len;
741 char retval;
742 gboolean asked = FALSE;
744 while (ignore_quiet || !quiet)
746 if (!asked)
748 send();
749 asked = TRUE;
752 len = read(fd, &retval, 1);
753 if (len != 1)
755 fprintf(stderr, "read() error: %s\n",
756 g_strerror(errno));
757 _exit(1); /* Parent died? */
760 switch (retval)
762 case 'Q':
763 quiet = !quiet;
764 if (ignore_quiet)
766 g_string_assign(message, "?");
767 send();
769 break;
770 case 'Y':
771 g_string_assign(message, "' Yes\n");
772 send();
773 return TRUE;
774 case 'N':
775 g_string_assign(message, "' No\n");
776 send();
777 return FALSE;
778 case 'E':
779 read_new_entry_text();
780 break;
781 default:
782 process_flag(retval);
783 break;
787 if (asked)
789 g_string_assign(message, "' Quiet\n");
790 send();
792 return TRUE;
795 static void destroy_action_window(GtkWidget *widget, gpointer data)
797 GUIside *gui_side = (GUIside *) data;
799 if (gui_side->child)
801 kill(gui_side->child, SIGTERM);
802 fclose(gui_side->to_child);
803 close(gui_side->from_child);
804 gdk_input_remove(gui_side->input_tag);
807 if (gui_side->next_dir)
809 gtk_timeout_remove(gui_side->next_timer);
810 g_free(gui_side->next_dir);
812 g_free(gui_side);
814 if (--number_of_windows < 1)
815 gtk_main_quit();
818 /* Create two pipes, fork() a child and return a pointer to a GUIside struct
819 * (NULL on failure). The child calls func().
821 * If autoq then automatically selects 'Quiet'.
823 static GUIside *start_action(gpointer data, ActionChild *func, gboolean autoq)
825 int filedes[4]; /* 0 and 2 are for reading */
826 GUIside *gui_side;
827 int child;
828 GtkWidget *vbox, *button, *hbox, *scrollbar, *actions;
829 struct sigaction act;
831 if (pipe(filedes))
833 report_error("ROX-Filer", g_strerror(errno));
834 return NULL;
837 if (pipe(filedes + 2))
839 close(filedes[0]);
840 close(filedes[1]);
841 report_error("ROX-Filer", g_strerror(errno));
842 return NULL;
845 child = fork();
846 switch (child)
848 case -1:
849 report_error("ROX-Filer", g_strerror(errno));
850 return NULL;
851 case 0:
852 /* We are the child */
854 quiet = autoq;
856 /* Reset the SIGCHLD handler */
857 act.sa_handler = SIG_DFL;
858 sigemptyset(&act.sa_mask);
859 act.sa_flags = 0;
860 sigaction(SIGCHLD, &act, NULL);
862 message = g_string_new(NULL);
863 close(filedes[0]);
864 close(filedes[3]);
865 to_parent = fdopen(filedes[1], "wb");
866 from_parent = filedes[2];
867 func(data);
868 _exit(0);
871 /* We are the parent */
872 close(filedes[1]);
873 close(filedes[2]);
874 gui_side = g_malloc(sizeof(GUIside));
875 gui_side->from_child = filedes[0];
876 gui_side->to_child = fdopen(filedes[3], "wb");
877 gui_side->log = NULL;
878 gui_side->child = child;
879 gui_side->errors = 0;
880 gui_side->show_info = FALSE;
881 gui_side->entry = NULL;
882 gui_side->default_string = NULL;
884 gui_side->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
885 gtk_container_set_border_width(GTK_CONTAINER(gui_side->window), 2);
886 gtk_window_set_default_size(GTK_WINDOW(gui_side->window), 450, 200);
887 gtk_signal_connect(GTK_OBJECT(gui_side->window), "destroy",
888 GTK_SIGNAL_FUNC(destroy_action_window), gui_side);
890 gui_side->vbox = vbox = gtk_vbox_new(FALSE, 0);
891 gtk_container_add(GTK_CONTAINER(gui_side->window), vbox);
893 gui_side->dir = gtk_label_new("<dir>");
894 gui_side->next_dir = NULL;
895 gtk_misc_set_alignment(GTK_MISC(gui_side->dir), 0.5, 0.5);
896 gtk_box_pack_start(GTK_BOX(vbox), gui_side->dir, FALSE, TRUE, 0);
898 hbox = gtk_hbox_new(FALSE, 0);
899 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 4);
901 gui_side->log = gtk_text_new(NULL, NULL);
902 gtk_widget_set_usize(gui_side->log, 400, 100);
903 gtk_box_pack_start(GTK_BOX(hbox), gui_side->log, TRUE, TRUE, 0);
904 scrollbar = gtk_vscrollbar_new(GTK_TEXT(gui_side->log)->vadj);
905 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, TRUE, 0);
907 actions = gtk_hbox_new(TRUE, 4);
908 gtk_box_pack_start(GTK_BOX(vbox), actions, FALSE, TRUE, 0);
910 gui_side->quiet = button = gtk_toggle_button_new_with_label("Quiet");
911 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), autoq);
912 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Q");
913 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
914 gtk_signal_connect(GTK_OBJECT(button), "clicked",
915 button_reply, gui_side);
916 gui_side->yes = button = gtk_button_new_with_label("Yes");
917 gtk_object_set_data(GTK_OBJECT(button), "send-code", "Y");
918 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
919 gtk_signal_connect(GTK_OBJECT(button), "clicked",
920 button_reply, gui_side);
921 gui_side->no = button = gtk_button_new_with_label("No");
922 gtk_object_set_data(GTK_OBJECT(button), "send-code", "N");
923 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
924 gtk_signal_connect(GTK_OBJECT(button), "clicked",
925 button_reply, gui_side);
926 SENSITIVE_YESNO(gui_side, FALSE);
928 button = gtk_button_new_with_label("Abort");
929 gtk_box_pack_start(GTK_BOX(actions), button, TRUE, TRUE, 0);
930 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
931 gtk_widget_destroy, GTK_OBJECT(gui_side->window));
933 gui_side->input_tag = gdk_input_add(gui_side->from_child,
934 GDK_INPUT_READ,
935 message_from_child,
936 gui_side);
938 return gui_side;
941 /* ACTIONS ON ONE ITEM */
943 /* These may call themselves recursively, or ask questions, etc.
944 * TRUE iff the directory containing dest_path needs to be rescanned.
947 /* dest_path is the dir containing src_path.
948 * Updates the global size_tally.
950 static gboolean do_usage(char *src_path, char *dest_path)
952 struct stat info;
954 check_flags();
956 if (lstat(src_path, &info))
958 g_string_sprintf(message, "'%s:\n", src_path);
959 send();
960 send_error();
961 return FALSE;
964 if (S_ISREG(info.st_mode) || S_ISLNK(info.st_mode))
965 size_tally += info.st_size;
966 else if (S_ISDIR(info.st_mode))
968 g_string_sprintf(message, "?Count contents of %s?",
969 src_path);
970 if (reply(from_parent, FALSE))
972 char *safe_path;
973 safe_path = g_strdup(src_path);
974 for_dir_contents(do_usage, safe_path, safe_path);
975 g_free(safe_path);
979 return FALSE;
982 /* dest_path is the dir containing src_path */
983 static gboolean do_delete(char *src_path, char *dest_path)
985 struct stat info;
986 gboolean write_prot;
988 check_flags();
990 if (lstat(src_path, &info))
992 send_error();
993 return FALSE;
996 write_prot = S_ISLNK(info.st_mode) ? FALSE
997 : access(src_path, W_OK) != 0;
998 if (write_prot || !quiet)
1000 g_string_sprintf(message, "?Delete %s'%s'?",
1001 write_prot ? "WRITE-PROTECTED " : " ",
1002 src_path);
1003 if (!reply(from_parent, write_prot && !o_force))
1004 return FALSE;
1006 else if (!o_brief)
1008 g_string_sprintf(message, "'Deleting '%s'\n", src_path);
1009 send();
1012 if (S_ISDIR(info.st_mode))
1014 char *safe_path;
1015 safe_path = g_strdup(src_path);
1016 for_dir_contents(do_delete, safe_path, safe_path);
1017 if (rmdir(safe_path))
1019 g_free(safe_path);
1020 send_error();
1021 return FALSE;
1023 g_string_sprintf(message, "'Directory '%s' deleted\n",
1024 safe_path);
1025 send();
1026 g_string_sprintf(message, "m%s", safe_path);
1027 send();
1028 g_free(safe_path);
1030 else if (unlink(src_path))
1032 send_error();
1033 return FALSE;
1036 return TRUE;
1039 /* path is the item to check. If is is a directory then we may recurse
1040 * (unless prune is used).
1042 static gboolean do_find(char *path, char *dummy)
1044 struct stat info;
1046 check_flags();
1048 if (!quiet)
1050 g_string_sprintf(message, "?Check '%s'?", path);
1051 if (!reply(from_parent, FALSE))
1052 return FALSE;
1055 for (;;)
1057 if (new_entry_string)
1059 if (find_condition)
1060 find_condition_free(find_condition);
1061 find_condition = find_compile(new_entry_string);
1062 g_free(new_entry_string);
1063 new_entry_string = NULL;
1066 if (find_condition)
1067 break;
1069 g_string_assign(message,
1070 "!Invalid find condition - change it and try again\n");
1071 send();
1072 g_string_sprintf(message, "?Check '%s'?", path);
1073 if (!reply(from_parent, TRUE))
1074 return FALSE;
1077 if (lstat(path, &info))
1079 send_error();
1080 g_string_sprintf(message, "'(while checking '%s')\n", path);
1081 send();
1082 return FALSE;
1085 if (find_test_condition(find_condition, path))
1087 g_string_sprintf(message, "'Found '%s'\n", path);
1088 send();
1091 if (S_ISDIR(info.st_mode))
1093 char *safe_path;
1094 safe_path = g_strdup(path);
1095 for_dir_contents(do_find, safe_path, safe_path);
1096 g_free(safe_path);
1099 return FALSE;
1102 static gboolean do_chmod(char *path, char *dummy)
1104 struct stat info;
1105 mode_t new_mode;
1107 check_flags();
1109 if (lstat(path, &info))
1111 send_error();
1112 return FALSE;
1114 if (S_ISLNK(info.st_mode))
1115 return FALSE;
1117 if (!quiet)
1119 g_string_sprintf(message, "?Change permissions of '%s'?", path);
1120 if (!reply(from_parent, FALSE))
1121 return FALSE;
1123 else if (!o_brief)
1125 g_string_sprintf(message, "'Changing permissions of '%s'\n",
1126 path);
1127 send();
1130 for (;;)
1132 if (new_entry_string)
1134 if (mode_change)
1135 mode_free(mode_change);
1136 mode_change = mode_compile(new_entry_string,
1137 MODE_MASK_ALL);
1138 g_free(new_entry_string);
1139 new_entry_string = NULL;
1142 if (mode_change)
1143 break;
1145 g_string_assign(message,
1146 "!Invalid mode command - change it and try again\n");
1147 send();
1148 g_string_sprintf(message, "?Change permissions of '%s'?", path);
1149 if (!reply(from_parent, TRUE))
1150 return FALSE;
1153 if (lstat(path, &info))
1155 send_error();
1156 return FALSE;
1158 if (S_ISLNK(info.st_mode))
1159 return FALSE;
1161 new_mode = mode_adjust(info.st_mode, mode_change);
1162 if (chmod(path, new_mode))
1164 send_error();
1165 return FALSE;
1168 if (o_recurse && S_ISDIR(info.st_mode))
1170 char *safe_path;
1171 safe_path = g_strdup(path);
1172 for_dir_contents(do_chmod, safe_path, safe_path);
1173 g_free(safe_path);
1176 return TRUE;
1179 /* We want to copy 'object' into directory 'dir'. If 'action_leaf'
1180 * is set then that is the new leafname, otherwise the leafname stays
1181 * the same.
1183 static char *make_dest_path(char *object, char *dir)
1185 char *leaf;
1187 if (action_leaf)
1188 leaf = action_leaf;
1189 else
1191 leaf = strrchr(object, '/');
1192 if (!leaf)
1193 leaf = object; /* Error? */
1194 else
1195 leaf++;
1198 return make_path(dir, leaf)->str;
1201 /* If action_leaf is not NULL it specifies the new leaf name
1203 static gboolean do_copy2(char *path, char *dest)
1205 char *dest_path;
1206 struct stat info;
1207 struct stat dest_info;
1208 gboolean retval = TRUE;
1210 check_flags();
1212 dest_path = make_dest_path(path, dest);
1214 if (lstat(path, &info))
1216 send_error();
1217 return FALSE;
1220 if (lstat(dest_path, &dest_info) == 0)
1222 int err;
1223 gboolean merge;
1225 merge = S_ISDIR(info.st_mode) && S_ISDIR(dest_info.st_mode);
1227 g_string_sprintf(message, "?'%s' already exists - %s?",
1228 dest_path,
1229 merge ? "merge contents" : "overwrite");
1231 if (!reply(from_parent, TRUE))
1232 return FALSE;
1234 if (!merge)
1236 if (S_ISDIR(dest_info.st_mode))
1237 err = rmdir(dest_path);
1238 else
1239 err = unlink(dest_path);
1241 if (err)
1243 send_error();
1244 if (errno != ENOENT)
1245 return FALSE;
1246 g_string_sprintf(message,
1247 "'Trying copy anyway...\n");
1248 send();
1252 else if (!quiet)
1254 g_string_sprintf(message, "?Copy %s as %s?", path, dest_path);
1255 if (!reply(from_parent, FALSE))
1256 return FALSE;
1258 else
1260 g_string_sprintf(message, "'Copying %s as %s\n", path,
1261 dest_path);
1262 send();
1265 if (S_ISDIR(info.st_mode))
1267 char *safe_path, *safe_dest;
1268 struct stat dest_info;
1269 gboolean exists;
1271 /* (we will do the update ourselves now, rather than
1272 * afterwards)
1274 retval = FALSE;
1276 safe_path = g_strdup(path);
1277 safe_dest = g_strdup(dest_path);
1279 exists = !lstat(dest_path, &dest_info);
1281 if (exists && !S_ISDIR(dest_info.st_mode))
1283 g_string_sprintf(message,
1284 "!ERROR: Destination already exists, "
1285 "but is not a directory\n");
1287 else if (exists == FALSE && mkdir(dest_path, info.st_mode))
1288 send_error();
1289 else
1291 if (!exists)
1293 /* (just been created then) */
1294 g_string_sprintf(message, "+%s", dest);
1295 send();
1298 action_leaf = NULL;
1299 for_dir_contents(do_copy2, safe_path, safe_dest);
1300 /* Note: dest_path now invalid... */
1303 g_free(safe_path);
1304 g_free(safe_dest);
1306 else if (S_ISLNK(info.st_mode))
1308 char target[MAXPATHLEN + 1];
1309 int count;
1311 /* Not all versions of cp(1) can make symlinks,
1312 * so we special-case it.
1315 count = readlink(path, target, sizeof(target) - 1);
1316 if (count < 0)
1318 send_error();
1319 retval = FALSE;
1321 else
1323 target[count] = '\0';
1324 if (symlink(target, dest_path))
1326 send_error();
1327 retval = FALSE;
1331 else
1333 char *argv[] = {"cp", "-pRf", NULL, NULL, NULL};
1335 argv[2] = path;
1336 argv[3] = dest_path;
1338 if (fork_exec_wait(argv))
1340 g_string_sprintf(message, "!ERROR: Copy failed\n");
1341 send();
1342 retval = FALSE;
1346 return retval;
1349 /* Copy path to dest.
1350 * Check that path not copied into itself.
1352 static gboolean do_copy(char *path, char *dest)
1354 if (is_sub_dir(make_dest_path(path, dest), path))
1356 g_string_sprintf(message,
1357 "!ERROR: Can't copy directory into itself\n");
1358 send();
1359 return FALSE;
1361 return do_copy2(path, dest);
1364 /* Move path to dest.
1365 * Check that path not moved into itself.
1367 static gboolean do_move(char *path, char *dest)
1369 char *dest_path;
1370 char *leaf;
1371 gboolean retval = TRUE;
1372 char *argv[] = {"mv", "-f", NULL, NULL, NULL};
1373 struct stat info2;
1374 gboolean is_dir;
1376 if (is_sub_dir(dest, path))
1378 g_string_sprintf(message,
1379 "!ERROR: Can't move directory into itself\n");
1380 send();
1381 return FALSE;
1384 check_flags();
1386 leaf = strrchr(path, '/');
1387 if (!leaf)
1388 leaf = path; /* Error? */
1389 else
1390 leaf++;
1392 dest_path = make_path(dest, leaf)->str;
1394 is_dir = lstat(path, &info2) == 0 && S_ISDIR(info2.st_mode);
1396 if (access(dest_path, F_OK) == 0)
1398 struct stat info;
1399 int err;
1401 g_string_sprintf(message, "?'%s' already exists - overwrite?",
1402 dest_path);
1403 if (!reply(from_parent, TRUE))
1404 return FALSE;
1406 if (lstat(dest_path, &info))
1408 send_error();
1409 return FALSE;
1412 if (S_ISDIR(info.st_mode))
1413 err = rmdir(dest_path);
1414 else
1415 err = unlink(dest_path);
1417 if (err)
1419 send_error();
1420 if (errno != ENOENT)
1421 return FALSE;
1422 g_string_sprintf(message,
1423 "'Trying move anyway...\n");
1424 send();
1427 else if (!quiet)
1429 g_string_sprintf(message, "?Move %s as %s?", path, dest_path);
1430 if (!reply(from_parent, FALSE))
1431 return FALSE;
1433 else
1435 g_string_sprintf(message, "'Moving %s as %s\n", path,
1436 dest_path);
1437 send();
1440 argv[2] = path;
1441 argv[3] = dest;
1443 if (fork_exec_wait(argv) == 0)
1445 g_string_sprintf(message, "+%s", path);
1446 g_string_truncate(message, leaf - path);
1447 send();
1448 if (is_dir) {
1449 g_string_sprintf(message, "m%s", path);
1450 send();
1453 else
1455 g_string_sprintf(message, "!ERROR: Failed to move %s as %s\n",
1456 path, dest_path);
1457 send();
1458 retval = FALSE;
1461 return retval;
1464 static gboolean do_link(char *path, char *dest)
1466 char *dest_path;
1467 char *leaf;
1469 check_flags();
1471 leaf = strrchr(path, '/');
1472 if (!leaf)
1473 leaf = path; /* Error? */
1474 else
1475 leaf++;
1477 dest_path = make_path(dest, leaf)->str;
1479 if (quiet)
1481 g_string_sprintf(message, "'Linking %s as %s\n", path,
1482 dest_path);
1483 send();
1485 else
1487 g_string_sprintf(message, "?Link %s as %s?", path, dest_path);
1488 if (!reply(from_parent, FALSE))
1489 return FALSE;
1492 if (symlink(path, dest_path))
1494 send_error();
1495 return FALSE;
1498 return TRUE;
1501 /* Mount/umount this item */
1502 static void do_mount(FilerWindow *filer_window, DirItem *item)
1504 char *argv[3] = {NULL, NULL, NULL};
1505 char *action;
1507 check_flags();
1509 argv[0] = item->flags & ITEM_FLAG_MOUNTED ? "umount" : "mount";
1510 action = item->flags & ITEM_FLAG_MOUNTED ? "Unmount" : "Mount";
1511 argv[1] = make_path(filer_window->path, item->leafname)->str;
1513 if (quiet)
1515 g_string_sprintf(message, "'%sing %s\n", action, argv[1]);
1516 send();
1518 else
1520 g_string_sprintf(message, "?%s %s?", action, argv[1]);
1521 if (!reply(from_parent, FALSE))
1522 return;
1525 if (fork_exec_wait(argv) == 0)
1527 g_string_sprintf(message, "+%s", filer_window->path);
1528 send();
1529 g_string_sprintf(message, "m%s", argv[1]);
1530 send();
1532 else
1534 g_string_sprintf(message, "!ERROR: %s failed\n", action);
1535 send();
1539 /* CHILD MAIN LOOPS */
1541 /* After forking, the child calls one of these functions */
1543 static void usage_cb(gpointer data)
1545 FilerWindow *filer_window = (FilerWindow *) data;
1546 Collection *collection = filer_window->collection;
1547 DirItem *item;
1548 int left = collection->number_selected;
1549 int i = -1;
1550 off_t total_size = 0;
1552 send_dir(filer_window->path);
1554 while (left > 0)
1556 i++;
1557 if (!collection->items[i].selected)
1558 continue;
1559 item = (DirItem *) collection->items[i].data;
1560 size_tally = 0;
1561 do_usage(make_path(filer_window->path,
1562 item->leafname)->str,
1563 filer_window->path);
1564 g_string_sprintf(message, "'%s: %s\n",
1565 item->leafname,
1566 format_size((unsigned long) size_tally));
1567 send();
1568 total_size += size_tally;
1569 left--;
1572 g_string_sprintf(message, "'\nTotal: %s\n",
1573 format_size((unsigned long) total_size));
1574 send();
1577 #ifdef DO_MOUNT_POINTS
1578 static void mount_cb(gpointer data)
1580 FilerWindow *filer_window = (FilerWindow *) data;
1581 Collection *collection = filer_window->collection;
1582 DirItem *item;
1583 int i;
1584 gboolean mount_points = FALSE;
1586 send_dir(filer_window->path);
1588 if (mount_item)
1589 do_mount(filer_window, mount_item);
1590 else
1592 for (i = 0; i < collection->number_of_items; i++)
1594 if (!collection->items[i].selected)
1595 continue;
1596 item = (DirItem *) collection->items[i].data;
1597 if (!(item->flags & ITEM_FLAG_MOUNT_POINT))
1598 continue;
1599 mount_points = TRUE;
1601 do_mount(filer_window, item);
1604 if (!mount_points)
1606 g_string_sprintf(message,
1607 "!No mount points selected!\n");
1608 send();
1612 g_string_sprintf(message, "'\nDone\n");
1613 send();
1615 #endif
1617 static void delete_cb(gpointer data)
1619 FilerWindow *filer_window = (FilerWindow *) data;
1620 Collection *collection = filer_window->collection;
1621 DirItem *item;
1622 int left = collection->number_selected;
1623 int i = -1;
1625 send_dir(filer_window->path);
1627 while (left > 0)
1629 i++;
1630 if (!collection->items[i].selected)
1631 continue;
1632 item = (DirItem *) collection->items[i].data;
1633 if (do_delete(make_path(filer_window->path,
1634 item->leafname)->str,
1635 filer_window->path))
1637 g_string_sprintf(message, "+%s", filer_window->path);
1638 send();
1640 left--;
1643 g_string_sprintf(message, "'\nDone\n");
1644 send();
1647 static void find_cb(gpointer data)
1649 FilerWindow *filer_window = (FilerWindow *) data;
1650 Collection *collection = filer_window->collection;
1651 DirItem *item;
1652 int left = collection->number_selected;
1653 int i = -1;
1655 send_dir(filer_window->path);
1657 while (left > 0)
1659 i++;
1660 if (!collection->items[i].selected)
1661 continue;
1662 item = (DirItem *) collection->items[i].data;
1663 do_find(make_path(filer_window->path,
1664 item->leafname)->str,
1665 NULL);
1666 left--;
1669 g_string_sprintf(message, "'\nDone\n");
1670 send();
1673 static void chmod_cb(gpointer data)
1675 FilerWindow *filer_window = (FilerWindow *) data;
1676 Collection *collection = filer_window->collection;
1677 DirItem *item;
1678 int left = collection->number_selected;
1679 int i = -1;
1681 send_dir(filer_window->path);
1683 while (left > 0)
1685 i++;
1686 if (!collection->items[i].selected)
1687 continue;
1688 item = (DirItem *) collection->items[i].data;
1689 if (item->flags & ITEM_FLAG_SYMLINK)
1691 g_string_sprintf(message, "!'%s' is a symbolic link\n",
1692 item->leafname);
1693 send();
1695 else if (do_chmod(make_path(filer_window->path,
1696 item->leafname)->str, NULL))
1698 g_string_sprintf(message, "+%s", filer_window->path);
1699 send();
1701 left--;
1704 g_string_sprintf(message, "'\nDone\n");
1705 send();
1708 static void list_cb(gpointer data)
1710 GSList *paths = (GSList *) data;
1712 while (paths)
1714 send_dir((char *) paths->data);
1716 if (action_do_func((char *) paths->data, action_dest))
1718 g_string_sprintf(message, "+%s", action_dest);
1719 send();
1722 paths = paths->next;
1725 g_string_sprintf(message, "'\nDone\n");
1726 send();
1729 static void add_toggle(GUIside *gui_side, guchar *label, guchar *code)
1731 GtkWidget *check;
1733 check = gtk_check_button_new_with_label(label);
1734 gtk_object_set_data(GTK_OBJECT(check), "send-code", code);
1735 gtk_signal_connect(GTK_OBJECT(check), "clicked",
1736 button_reply, gui_side);
1737 gtk_box_pack_start(GTK_BOX(gui_side->vbox), check, FALSE, TRUE, 0);
1741 /* EXTERNAL INTERFACE */
1743 void action_find(FilerWindow *filer_window)
1745 GUIside *gui_side;
1746 Collection *collection;
1747 GtkWidget *hbox, *label, *button;
1749 collection = filer_window->collection;
1751 if (collection->number_selected < 1)
1753 report_error("ROX-Filer", "You need to select some items "
1754 "to search through");
1755 return;
1758 if (!last_find_string)
1759 last_find_string = g_strdup("'core'");
1761 new_entry_string = last_find_string;
1762 gui_side = start_action(filer_window, find_cb, FALSE);
1763 if (!gui_side)
1764 return;
1766 gui_side->show_info = TRUE;
1768 hbox = gtk_hbox_new(FALSE, 0);
1769 label = gtk_label_new("Expression:");
1770 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
1771 gui_side->default_string = &last_find_string;
1772 gui_side->entry = gtk_entry_new();
1773 gtk_widget_set_style(gui_side->entry, fixed_style);
1774 gtk_entry_set_text(GTK_ENTRY(gui_side->entry), last_find_string);
1775 gtk_widget_set_sensitive(gui_side->entry, FALSE);
1776 gtk_box_pack_start(GTK_BOX(hbox), gui_side->entry, TRUE, TRUE, 4);
1777 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed",
1778 entry_changed, gui_side);
1779 gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 4);
1780 button = gtk_button_new_with_label("Show expression reference");
1781 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 4);
1782 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1783 show_condition_help, NULL);
1785 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Find");
1786 number_of_windows++;
1787 gtk_widget_show_all(gui_side->window);
1790 /* Count disk space used by selected items */
1791 void action_usage(FilerWindow *filer_window)
1793 GUIside *gui_side;
1794 Collection *collection;
1796 collection = filer_window->collection;
1798 if (collection->number_selected < 1)
1800 report_error("ROX-Filer", "You need to select some items "
1801 "to count");
1802 return;
1805 gui_side = start_action(filer_window, usage_cb, TRUE);
1806 if (!gui_side)
1807 return;
1809 gui_side->show_info = TRUE;
1811 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Disk Usage");
1812 number_of_windows++;
1813 gtk_widget_show_all(gui_side->window);
1816 /* Mount/unmount 'item', or all selected mount points if NULL. */
1817 void action_mount(FilerWindow *filer_window, DirItem *item)
1819 #ifdef DO_MOUNT_POINTS
1820 GUIside *gui_side;
1821 Collection *collection;
1823 collection = filer_window->collection;
1825 if (item == NULL && collection->number_selected < 1)
1827 report_error("ROX-Filer", "You need to select some items "
1828 "to mount or unmount");
1829 return;
1832 mount_item = item;
1833 gui_side = start_action(filer_window, mount_cb, o_auto_mount);
1834 if (!gui_side)
1835 return;
1837 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Mount / Unmount");
1838 number_of_windows++;
1839 gtk_widget_show_all(gui_side->window);
1840 #else
1841 report_error("ROX-Filer",
1842 "ROX-Filer does not yet support mount points on your "
1843 "system. Sorry.");
1844 #endif /* DO_MOUNT_POINTS */
1847 /* Deletes all selected items in the window */
1848 void action_delete(FilerWindow *filer_window)
1850 GUIside *gui_side;
1851 Collection *collection;
1853 collection = filer_window->collection;
1855 if (collection->number_selected < 1)
1857 report_error("ROX-Filer", "You need to select some items "
1858 "to delete");
1859 return;
1862 gui_side = start_action(filer_window, delete_cb, o_auto_delete);
1863 if (!gui_side)
1864 return;
1866 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Delete");
1867 add_toggle(gui_side,
1868 "Force - don't confirm deletion of non-writeable items", "F");
1869 add_toggle(gui_side,
1870 "Brief - only log directories being deleted", "B");
1872 number_of_windows++;
1873 gtk_widget_show_all(gui_side->window);
1876 /* Change the permissions of the selected items */
1877 void action_chmod(FilerWindow *filer_window)
1879 GUIside *gui_side;
1880 Collection *collection;
1881 GtkWidget *hbox, *label, *button;
1883 collection = filer_window->collection;
1885 if (collection->number_selected < 1)
1887 report_error("ROX-Filer", "You need to select the items "
1888 "whose permissions you want to change");
1889 return;
1892 if (!last_chmod_string)
1893 last_chmod_string = g_strdup("a+x");
1894 new_entry_string = last_chmod_string;
1895 gui_side = start_action(filer_window, chmod_cb, FALSE);
1896 if (!gui_side)
1897 return;
1899 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Permissions");
1900 add_toggle(gui_side,
1901 "Brief - don't list processed files", "B");
1902 add_toggle(gui_side,
1903 "Recurse - also change contents of subdirectories", "R");
1904 hbox = gtk_hbox_new(FALSE, 0);
1905 label = gtk_label_new("Command:");
1906 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
1907 gui_side->default_string = &last_chmod_string;
1908 gui_side->entry = gtk_entry_new();
1909 gtk_entry_set_text(GTK_ENTRY(gui_side->entry), last_chmod_string);
1910 gtk_widget_set_sensitive(gui_side->entry, FALSE);
1911 gtk_box_pack_start(GTK_BOX(hbox), gui_side->entry, TRUE, TRUE, 4);
1912 gtk_signal_connect(GTK_OBJECT(gui_side->entry), "changed",
1913 entry_changed, gui_side);
1914 gtk_box_pack_start(GTK_BOX(gui_side->vbox), hbox, FALSE, TRUE, 0);
1915 button = gtk_button_new_with_label("Show command reference");
1916 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 4);
1917 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1918 show_chmod_help, NULL);
1920 number_of_windows++;
1921 gtk_widget_show_all(gui_side->window);
1924 void action_copy(GSList *paths, char *dest, char *leaf)
1926 GUIside *gui_side;
1928 action_dest = dest;
1929 action_leaf = leaf;
1930 action_do_func = do_copy;
1931 gui_side = start_action(paths, list_cb, o_auto_copy);
1932 if (!gui_side)
1933 return;
1935 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Copy");
1936 number_of_windows++;
1937 gtk_widget_show_all(gui_side->window);
1940 void action_move(GSList *paths, char *dest)
1942 GUIside *gui_side;
1944 action_dest = dest;
1945 action_do_func = do_move;
1946 gui_side = start_action(paths, list_cb, o_auto_move);
1947 if (!gui_side)
1948 return;
1950 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Move");
1951 number_of_windows++;
1952 gtk_widget_show_all(gui_side->window);
1955 void action_link(GSList *paths, char *dest)
1957 GUIside *gui_side;
1959 action_dest = dest;
1960 action_do_func = do_link;
1961 gui_side = start_action(paths, list_cb, o_auto_link);
1962 if (!gui_side)
1963 return;
1965 gtk_window_set_title(GTK_WINDOW(gui_side->window), "Link");
1966 number_of_windows++;
1967 gtk_widget_show_all(gui_side->window);
1970 void action_init(void)
1972 options_sections = g_slist_prepend(options_sections, &options);
1973 option_register("action_auto_quiet", action_auto_quiet);