r235: Added the Shell Command feature to the minibuffer.
[rox-filer/ma.git] / ROX-Filer / src / menu.c
blob335850d78e04733ce09509876a880075ef457dfb
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 /* menu.c - code for handling the popup menu */
24 #include <config.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <sys/wait.h>
30 #include <sys/param.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include <string.h>
36 #include <gdk/gdkx.h>
37 #include <gtk/gtk.h>
39 #include "run.h"
40 #include "action.h"
41 #include "filer.h"
42 #include "type.h"
43 #include "support.h"
44 #include "gui_support.h"
45 #include "options.h"
46 #include "choices.h"
47 #include "gtksavebox.h"
48 #include "mount.h"
49 #include "minibuffer.h"
51 #define C_ "<control>"
53 #define MENU_MARGIN 32
55 GtkAccelGroup *filer_keys;
56 GtkAccelGroup *panel_keys;
58 static GtkWidget *popup_menu = NULL; /* Currently open menu */
60 static gint updating_menu = 0; /* Non-zero => ignore activations */
62 /* Options */
63 static GtkWidget *xterm_here_entry;
64 static char *xterm_here_value;
66 /* Static prototypes */
68 static void position_menu(GtkMenu *menu, gint *x, gint *y, gpointer data);
69 static void menu_closed(GtkWidget *widget);
70 static void items_sensitive(gboolean state);
71 static char *load_xterm_here(char *data);
72 static void savebox_show(guchar *title, guchar *path, MaskedPixmap *image,
73 gboolean (*callback)(guchar *current, guchar *new));
74 static gint save_to_file(GtkSavebox *savebox, guchar *pathname);
76 /* Note that for these callbacks none of the arguments are used. */
77 static void not_yet(gpointer data, guint action, GtkWidget *widget);
79 static void large(gpointer data, guint action, GtkWidget *widget);
80 static void small(gpointer data, guint action, GtkWidget *widget);
81 static void full_info(gpointer data, guint action, GtkWidget *widget);
83 static void sort_name(gpointer data, guint action, GtkWidget *widget);
84 static void sort_type(gpointer data, guint action, GtkWidget *widget);
85 static void sort_size(gpointer data, guint action, GtkWidget *widget);
86 static void sort_date(gpointer data, guint action, GtkWidget *widget);
88 static void hidden(gpointer data, guint action, GtkWidget *widget);
89 static void refresh(gpointer data, guint action, GtkWidget *widget);
91 static void copy_item(gpointer data, guint action, GtkWidget *widget);
92 static void rename_item(gpointer data, guint action, GtkWidget *widget);
93 static void link_item(gpointer data, guint action, GtkWidget *widget);
94 static void open_file(gpointer data, guint action, GtkWidget *widget);
95 static void help(gpointer data, guint action, GtkWidget *widget);
96 static void show_file_info(gpointer data, guint action, GtkWidget *widget);
97 static void mount(gpointer data, guint action, GtkWidget *widget);
98 static void delete(gpointer data, guint action, GtkWidget *widget);
99 static void remove_link(gpointer data, guint action, GtkWidget *widget);
100 static void usage(gpointer data, guint action, GtkWidget *widget);
101 static void chmod_items(gpointer data, guint action, GtkWidget *widget);
102 static void find(gpointer data, guint action, GtkWidget *widget);
104 static void open_vfs_rpm(gpointer data, guint action, GtkWidget *widget);
105 static void open_vfs_utar(gpointer data, guint action, GtkWidget *widget);
106 static void open_vfs_uzip(gpointer data, guint action, GtkWidget *widget);
108 static void select_all(gpointer data, guint action, GtkWidget *widget);
109 static void clear_selection(gpointer data, guint action, GtkWidget *widget);
110 static void show_options(gpointer data, guint action, GtkWidget *widget);
111 static void new_directory(gpointer data, guint action, GtkWidget *widget);
112 static void xterm_here(gpointer data, guint action, GtkWidget *widget);
114 static void open_parent_same(gpointer data, guint action, GtkWidget *widget);
115 static void open_parent(gpointer data, guint action, GtkWidget *widget);
116 static void new_window(gpointer data, guint action, GtkWidget *widget);
117 static void close_window(gpointer data, guint action, GtkWidget *widget);
118 static void enter_path(gpointer data, guint action, GtkWidget *widget);
119 static void shell_command(gpointer data, guint action, GtkWidget *widget);
120 static void rox_help(gpointer data, guint action, GtkWidget *widget);
122 static void open_as_dir(gpointer data, guint action, GtkWidget *widget);
123 static void close_panel(gpointer data, guint action, GtkWidget *widget);
125 static GtkWidget *create_options();
126 static void update_options();
127 static void set_options();
128 static void save_options();
130 static OptionsSection options =
132 "Menu options",
133 create_options,
134 update_options,
135 set_options,
136 save_options
140 static GtkWidget *filer_menu; /* The popup filer menu */
141 static GtkWidget *filer_file_item; /* The File '' label */
142 static GtkWidget *filer_file_menu; /* The File '' menu */
143 static GtkWidget *filer_vfs_menu; /* The Open VFS menu */
144 static GtkWidget *filer_hidden_menu; /* The Show Hidden item */
145 static GtkWidget *filer_new_window; /* The New Window item */
146 static GtkWidget *panel_menu; /* The popup panel menu */
147 static GtkWidget *panel_hidden_menu; /* The Show Hidden item */
149 /* Used for Copy, etc */
150 static GtkWidget *savebox = NULL;
151 static guchar *current_path = NULL;
152 static gboolean (*current_savebox_callback)(guchar *current, guchar *new);
154 static gint screen_width, screen_height;
156 static GtkItemFactoryEntry filer_menu_def[] = {
157 {"/Display", NULL, NULL, 0, "<Branch>"},
158 {"/Display/Large Icons", NULL, large, 0, NULL},
159 {"/Display/Small Icons", NULL, small, 0, NULL},
160 {"/Display/Full Info", NULL, full_info, 0, NULL},
161 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
162 {"/Display/Sort by Name", NULL, sort_name, 0, NULL},
163 {"/Display/Sort by Type", NULL, sort_type, 0, NULL},
164 {"/Display/Sort by Date", NULL, sort_date, 0, NULL},
165 {"/Display/Sort by Size", NULL, sort_size, 0, NULL},
166 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
167 {"/Display/Show Hidden", C_"H", hidden, 0, "<ToggleItem>"},
168 {"/Display/Refresh", C_"L", refresh, 0, NULL},
169 {"/File", NULL, NULL, 0, "<Branch>"},
170 {"/File/Copy...", NULL, copy_item, 0, NULL},
171 {"/File/Rename...", NULL, rename_item, 0, NULL},
172 {"/File/Link...", NULL, link_item, 0, NULL},
173 {"/File/Shift Open", NULL, open_file, 0, NULL},
174 {"/File/Help", "F1", help, 0, NULL},
175 {"/File/Info", "I", show_file_info, 0, NULL},
176 {"/File/Open VFS", NULL, NULL, 0, "<Branch>"},
177 {"/File/Open VFS/Unzip", NULL, open_vfs_uzip, 0, NULL},
178 {"/File/Open VFS/Untar", NULL, open_vfs_utar, 0, NULL},
179 {"/File/Open VFS/RPM", NULL, open_vfs_rpm, 0, NULL},
180 {"/File/Separator", NULL, NULL, 0, "<Separator>"},
181 {"/File/Mount", "M", mount, 0, NULL},
182 {"/File/Delete", C_"X", delete, 0, NULL},
183 {"/File/Disk Usage", "U", usage, 0, NULL},
184 {"/File/Permissions", NULL, chmod_items, 0, NULL},
185 {"/File/Touch", NULL, not_yet, 0, NULL},
186 {"/File/Find", NULL, find, 0, NULL},
187 {"/Select All", C_"A", select_all, 0, NULL},
188 {"/Clear Selection", C_"Z", clear_selection, 0, NULL},
189 {"/Options...", NULL, show_options, 0, NULL},
190 {"/New Directory...", NULL, new_directory, 0, NULL},
191 {"/Xterm Here", NULL, xterm_here, 0, NULL},
192 {"/Window", NULL, NULL, 0, "<Branch>"},
193 {"/Window/Parent, New Window", NULL, open_parent, 0, NULL},
194 {"/Window/Parent, Same Window", NULL, open_parent_same, 0, NULL},
195 {"/Window/New Window", NULL, new_window, 0, NULL},
196 {"/Window/Close Window", C_"Q", close_window, 0, NULL},
197 {"/Window/Enter Path", NULL, enter_path, 0, NULL},
198 {"/Window/Shell Command", NULL, shell_command, 0, NULL},
199 {"/Window/Separator", NULL, NULL, 0, "<Separator>"},
200 {"/Window/Show ROX-Filer help", NULL, rox_help, 0, NULL},
203 static GtkItemFactoryEntry panel_menu_def[] = {
204 {"/Display", NULL, NULL, 0, "<Branch>"},
205 {"/Display/Sort by Name", NULL, sort_name, 0, NULL},
206 {"/Display/Sort by Type", NULL, sort_type, 0, NULL},
207 {"/Display/Sort by Date", NULL, sort_date, 0, NULL},
208 {"/Display/Sort by Size", NULL, sort_size, 0, NULL},
209 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
210 {"/Display/Show Hidden", NULL, hidden, 0, "<ToggleItem>"},
211 {"/Display/Refresh", NULL, refresh, 0, NULL},
212 {"/Open Panel as Directory", NULL, open_as_dir, 0, NULL},
213 {"/Close Panel", NULL, close_panel, 0, NULL},
214 {"/Separator", NULL, NULL, 0, "<Separator>"},
215 {"/ROX-Filer Help", NULL, rox_help, 0, NULL},
216 {"/ROX-Filer Options...", NULL, show_options, 0, NULL},
217 {"/Separator", NULL, NULL, 0, "<Separator>"},
218 {"/Show Help", NULL, help, 0, NULL},
219 {"/Remove Item", NULL, remove_link, 0, NULL},
222 typedef struct _FileStatus FileStatus;
224 /* This is for the 'file(1) says...' thing */
225 struct _FileStatus
227 int fd; /* FD to read from, -1 if closed */
228 int input; /* Input watcher tag if fd valid */
229 GtkLabel *label; /* Widget to output to */
230 gboolean start; /* No output yet */
233 void menu_init()
235 GtkItemFactory *item_factory;
236 char *menurc;
237 GList *items;
239 /* This call starts returning strange values after a while, so get
240 * the result here during init.
242 gdk_window_get_size(GDK_ROOT_PARENT(), &screen_width, &screen_height);
244 filer_keys = gtk_accel_group_new();
245 item_factory = gtk_item_factory_new(GTK_TYPE_MENU,
246 "<filer>",
247 filer_keys);
248 gtk_item_factory_create_items(item_factory,
249 sizeof(filer_menu_def) / sizeof(*filer_menu_def),
250 filer_menu_def,
251 NULL);
252 filer_menu = gtk_item_factory_get_widget(item_factory, "<filer>");
253 filer_file_menu = gtk_item_factory_get_widget(item_factory,
254 "<filer>/File");
255 filer_vfs_menu = gtk_item_factory_get_widget(item_factory,
256 "<filer>/File/Open VFS");
257 filer_hidden_menu = gtk_item_factory_get_widget(item_factory,
258 "<filer>/Display/Show Hidden");
259 items = gtk_container_children(GTK_CONTAINER(filer_menu));
260 filer_file_item = GTK_BIN(g_list_nth(items, 1)->data)->child;
261 g_list_free(items);
262 filer_new_window = GTK_BIN(gtk_item_factory_get_widget(item_factory,
263 "<filer>/Window/New Window"))->child;
265 panel_keys = gtk_accel_group_new();
266 item_factory = gtk_item_factory_new(GTK_TYPE_MENU,
267 "<panel>",
268 panel_keys);
269 gtk_item_factory_create_items(item_factory,
270 sizeof(panel_menu_def) / sizeof(*panel_menu_def),
271 panel_menu_def,
272 NULL);
273 panel_menu = gtk_item_factory_get_widget(item_factory, "<panel>");
274 panel_hidden_menu = gtk_item_factory_get_widget(item_factory,
275 "<panel>/Display/Show Hidden");
277 menurc = choices_find_path_load("menus", "ROX-Filer");
278 if (menurc)
279 gtk_item_factory_parse_rc(menurc);
281 gtk_accel_group_lock(panel_keys);
283 gtk_signal_connect(GTK_OBJECT(filer_menu), "unmap_event",
284 GTK_SIGNAL_FUNC(menu_closed), NULL);
285 gtk_signal_connect(GTK_OBJECT(panel_menu), "unmap_event",
286 GTK_SIGNAL_FUNC(menu_closed), NULL);
287 gtk_signal_connect(GTK_OBJECT(filer_file_menu), "unmap_event",
288 GTK_SIGNAL_FUNC(menu_closed), NULL);
290 options_sections = g_slist_prepend(options_sections, &options);
291 xterm_here_value = g_strdup("xterm");
292 option_register("xterm_here", load_xterm_here);
294 savebox = gtk_savebox_new();
295 gtk_signal_connect_object(GTK_OBJECT(savebox), "save_to_file",
296 GTK_SIGNAL_FUNC(save_to_file), NULL);
297 gtk_signal_connect_object(GTK_OBJECT(savebox), "save_done",
298 GTK_SIGNAL_FUNC(gtk_widget_hide),
299 GTK_OBJECT(savebox));
302 /* Build up some option widgets to go in the options dialog, but don't
303 * fill them in yet.
305 static GtkWidget *create_options()
307 GtkWidget *table, *label;
309 table = gtk_table_new(2, 2, FALSE);
310 gtk_container_set_border_width(GTK_CONTAINER(table), 4);
312 label = gtk_label_new("To set the keyboard short-cuts you simply open "
313 "the menu over a filer window, move the pointer over "
314 "the item you want to use and press a key. The key "
315 "will appear next to the menu item and you can just "
316 "press that key without opening the menu in future. "
317 "To save the current menu short-cuts for next time, "
318 "click the Save button at the bottom of this window.");
319 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
320 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 2, 0, 1);
322 label = gtk_label_new("'Xterm here' program:");
323 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
324 xterm_here_entry = gtk_entry_new();
325 gtk_table_attach_defaults(GTK_TABLE(table), xterm_here_entry,
326 1, 2, 1, 2);
328 return table;
331 static char *load_xterm_here(char *data)
333 g_free(xterm_here_value);
334 xterm_here_value = g_strdup(data);
335 return NULL;
338 static void update_options()
340 gtk_entry_set_text(GTK_ENTRY(xterm_here_entry), xterm_here_value);
343 static void set_options()
345 g_free(xterm_here_value);
346 xterm_here_value = g_strdup(gtk_entry_get_text(
347 GTK_ENTRY(xterm_here_entry)));
350 static void save_options()
352 char *menurc;
354 menurc = choices_find_path_save("menus", "ROX-Filer", TRUE);
355 if (menurc)
356 gtk_item_factory_dump_rc(menurc, NULL, TRUE);
358 option_write("xterm_here", xterm_here_value);
362 static void items_sensitive(gboolean state)
364 int n = 7;
365 GList *items, *item;
367 items = item = gtk_container_children(GTK_CONTAINER(filer_file_menu));
368 while (item && n--)
370 gtk_widget_set_sensitive(GTK_BIN(item->data)->child, state);
371 item = item->next;
373 g_list_free(items);
375 items = item = gtk_container_children(GTK_CONTAINER(filer_vfs_menu));
376 while (item)
378 gtk_widget_set_sensitive(GTK_BIN(item->data)->child, state);
379 item = item->next;
381 g_list_free(items);
384 static void position_menu(GtkMenu *menu, gint *x, gint *y, gpointer data)
386 int *pos = (int *) data;
387 GtkRequisition requisition;
389 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
391 if (pos[0] == -1)
392 *x = screen_width - MENU_MARGIN - requisition.width;
393 else if (pos[0] == -2)
394 *x = MENU_MARGIN;
395 else
396 *x = pos[0] - (requisition.width >> 2);
398 if (pos[1] == -1)
399 *y = screen_height - MENU_MARGIN - requisition.height;
400 else if (pos[1] == -2)
401 *y = MENU_MARGIN;
402 else
403 *y = pos[1] - (requisition.height >> 2);
405 *x = CLAMP(*x, 0, screen_width - requisition.width);
406 *y = CLAMP(*y, 0, screen_height - requisition.height);
409 void show_filer_menu(FilerWindow *filer_window, GdkEventButton *event,
410 int item)
412 DirItem *file_item;
413 int pos[2];
415 updating_menu++;
417 pos[0] = event->x_root;
418 pos[1] = event->y_root;
420 window_with_focus = filer_window;
422 switch (filer_window->panel_type)
424 case PANEL_TOP:
425 pos[1] = -2;
426 break;
427 case PANEL_BOTTOM:
428 pos[1] = -1;
429 break;
430 default:
431 break;
434 if (filer_window->panel_type)
435 collection_clear_selection(filer_window->collection); /* ??? */
437 if (filer_window->collection->number_selected == 0 && item >= 0)
439 collection_select_item(filer_window->collection, item);
440 filer_window->temp_item_selected = TRUE;
442 else
443 filer_window->temp_item_selected = FALSE;
445 if (filer_window->panel_type)
447 gtk_check_menu_item_set_active(
448 GTK_CHECK_MENU_ITEM(panel_hidden_menu),
449 filer_window->show_hidden);
451 else
453 GtkWidget *file_label, *file_menu;
454 Collection *collection = filer_window->collection;
455 GString *buffer;
457 file_label = filer_file_item;
458 file_menu = filer_file_menu;
459 gtk_check_menu_item_set_active(
460 GTK_CHECK_MENU_ITEM(filer_hidden_menu),
461 filer_window->show_hidden);
462 buffer = g_string_new(NULL);
463 switch (collection->number_selected)
465 case 0:
466 g_string_assign(buffer, "Next Click");
467 items_sensitive(TRUE);
468 break;
469 case 1:
470 items_sensitive(TRUE);
471 file_item = selected_item(
472 filer_window->collection);
473 g_string_sprintf(buffer, "%s '%s'",
474 basetype_name(file_item),
475 file_item->leafname);
476 break;
477 default:
478 items_sensitive(FALSE);
479 g_string_sprintf(buffer, "%d items",
480 collection->number_selected);
481 break;
483 gtk_label_set_text(GTK_LABEL(file_label), buffer->str);
484 g_string_free(buffer, TRUE);
488 gtk_widget_set_sensitive(filer_new_window, !o_unique_filer_windows);
490 if (filer_window->panel_type)
491 popup_menu = panel_menu;
492 else
493 popup_menu = (event->state & GDK_CONTROL_MASK)
494 ? filer_file_menu
495 : filer_menu;
497 updating_menu--;
499 gtk_menu_popup(GTK_MENU(popup_menu), NULL, NULL, position_menu,
500 (gpointer) pos, event->button, event->time);
503 static void menu_closed(GtkWidget *widget)
505 if (window_with_focus == NULL || widget != popup_menu)
506 return; /* Close panel item chosen? */
508 if (window_with_focus->temp_item_selected)
510 collection_clear_selection(window_with_focus->collection);
511 window_with_focus->temp_item_selected = FALSE;
515 void target_callback(Collection *collection, gint item, gpointer real_fn)
517 g_return_if_fail(window_with_focus != NULL);
518 g_return_if_fail(window_with_focus->collection == collection);
519 g_return_if_fail(real_fn != NULL);
521 collection_wink_item(collection, item);
522 collection_clear_selection(collection);
523 collection_select_item(collection, item);
524 ((CollectionTargetFunc)real_fn)(NULL, 0, collection);
525 if (item < collection->number_of_items)
526 collection_unselect_item(collection, item);
529 /* Actions */
531 /* Fake action to warn when a menu item does nothing */
532 static void not_yet(gpointer data, guint action, GtkWidget *widget)
534 delayed_error("ROX-Filer", "Sorry, that feature isn't implemented yet");
537 static void large(gpointer data, guint action, GtkWidget *widget)
539 g_return_if_fail(window_with_focus != NULL);
541 filer_style_set(window_with_focus, LARGE_ICONS);
544 static void small(gpointer data, guint action, GtkWidget *widget)
546 g_return_if_fail(window_with_focus != NULL);
548 filer_style_set(window_with_focus, SMALL_ICONS);
551 static void full_info(gpointer data, guint action, GtkWidget *widget)
553 g_return_if_fail(window_with_focus != NULL);
555 filer_style_set(window_with_focus, FULL_INFO);
558 static void sort_name(gpointer data, guint action, GtkWidget *widget)
560 g_return_if_fail(window_with_focus != NULL);
562 filer_set_sort_fn(window_with_focus, sort_by_name);
565 static void sort_type(gpointer data, guint action, GtkWidget *widget)
567 g_return_if_fail(window_with_focus != NULL);
569 filer_set_sort_fn(window_with_focus, sort_by_type);
572 static void sort_date(gpointer data, guint action, GtkWidget *widget)
574 g_return_if_fail(window_with_focus != NULL);
576 filer_set_sort_fn(window_with_focus, sort_by_date);
579 static void sort_size(gpointer data, guint action, GtkWidget *widget)
581 g_return_if_fail(window_with_focus != NULL);
583 filer_set_sort_fn(window_with_focus, sort_by_size);
586 static void hidden(gpointer data, guint action, GtkWidget *widget)
588 if (updating_menu)
589 return;
591 g_return_if_fail(window_with_focus != NULL);
593 filer_set_hidden(window_with_focus, !window_with_focus->show_hidden);
596 static void refresh(gpointer data, guint action, GtkWidget *widget)
598 g_return_if_fail(window_with_focus != NULL);
600 full_refresh();
601 update_dir(window_with_focus, TRUE);
604 static void mount(gpointer data, guint action, GtkWidget *widget)
606 g_return_if_fail(window_with_focus != NULL);
608 if (window_with_focus->collection->number_selected == 0)
609 collection_target(window_with_focus->collection,
610 target_callback, mount);
611 else
612 action_mount(window_with_focus, NULL);
615 static void delete(gpointer data, guint action, GtkWidget *widget)
617 g_return_if_fail(window_with_focus != NULL);
619 if (window_with_focus->collection->number_selected == 0)
620 collection_target(window_with_focus->collection,
621 target_callback, delete);
622 else
623 action_delete(window_with_focus);
626 static void remove_link(gpointer data, guint action, GtkWidget *widget)
628 g_return_if_fail(window_with_focus != NULL);
630 if (window_with_focus->collection->number_selected > 1)
632 report_error("ROX-Filer", "You can only remove one link "
633 "at a time");
634 return;
636 else if (window_with_focus->collection->number_selected == 0)
637 collection_target(window_with_focus->collection,
638 target_callback, remove_link);
639 else
641 struct stat info;
642 DirItem *item;
643 guchar *path;
645 item = selected_item(window_with_focus->collection);
647 path = make_path(window_with_focus->path, item->leafname)->str;
648 if (lstat(path, &info))
649 report_error("ROX-Filer", g_strerror(errno));
650 else if (!S_ISLNK(info.st_mode))
651 report_error("ROX-Filer",
652 "You can only remove symbolic links this way - "
653 "try the 'Open Panel as Directory' item.");
654 else if (unlink(path))
655 report_error("ROX-Filer", g_strerror(errno));
656 else
657 update_dir(window_with_focus, TRUE);
661 static void usage(gpointer data, guint action, GtkWidget *widget)
663 g_return_if_fail(window_with_focus != NULL);
665 if (window_with_focus->collection->number_selected == 0)
666 collection_target(window_with_focus->collection,
667 target_callback, usage);
668 else
669 action_usage(window_with_focus);
672 static void chmod_items(gpointer data, guint action, GtkWidget *widget)
674 g_return_if_fail(window_with_focus != NULL);
676 if (window_with_focus->collection->number_selected == 0)
677 collection_target(window_with_focus->collection,
678 target_callback, chmod_items);
679 else
680 action_chmod(window_with_focus);
683 static void find(gpointer data, guint action, GtkWidget *widget)
685 g_return_if_fail(window_with_focus != NULL);
687 if (window_with_focus->collection->number_selected == 0)
688 collection_target(window_with_focus->collection,
689 target_callback, find);
690 else
691 action_find(window_with_focus);
694 /* This pops up our savebox widget, cancelling any currently open one,
695 * and allows the user to pick a new path for it.
696 * Once the new path has been picked, the callback will be called with
697 * both the current and new paths.
699 static void savebox_show(guchar *title, guchar *path, MaskedPixmap *image,
700 gboolean (*callback)(guchar *current, guchar *new))
702 if (GTK_WIDGET_VISIBLE(savebox))
703 gtk_widget_hide(savebox);
705 if (current_path)
706 g_free(current_path);
707 current_path = g_strdup(path);
708 current_savebox_callback = callback;
710 gtk_window_set_title(GTK_WINDOW(savebox), title);
711 gtk_savebox_set_pathname(GTK_SAVEBOX(savebox), current_path);
712 gtk_savebox_set_icon(GTK_SAVEBOX(savebox), image->pixmap, image->mask);
714 gtk_widget_grab_focus(GTK_SAVEBOX(savebox)->entry);
715 gtk_widget_show(savebox);
718 static gint save_to_file(GtkSavebox *savebox, guchar *pathname)
720 g_return_val_if_fail(current_savebox_callback != NULL,
721 GTK_XDS_SAVE_ERROR);
723 return current_savebox_callback(current_path, pathname)
724 ? GTK_XDS_SAVED : GTK_XDS_SAVE_ERROR;
727 static gboolean copy_cb(guchar *current, guchar *new)
729 char *new_dir, *leaf;
730 GSList *local_paths;
732 if (new[0] != '/')
734 report_error("ROX-Filer", "New pathname is not absolute");
735 return FALSE;
738 if (new[strlen(new) - 1] == '/')
740 new_dir = g_strdup(new);
741 leaf = NULL;
743 else
745 guchar *slash;
747 slash = strrchr(new, '/');
748 new_dir = g_strndup(new, slash - new);
749 leaf = slash + 1;
752 local_paths = g_slist_append(NULL, current);
753 action_copy(local_paths, new_dir, leaf);
754 g_slist_free(local_paths);
756 g_free(new_dir);
758 return TRUE;
761 #define SHOW_SAVEBOX(title, callback) \
763 DirItem *item; \
764 guchar *path; \
765 item = selected_item(collection); \
766 path = make_path(window_with_focus->path, item->leafname)->str; \
767 savebox_show(title, path, item->image, callback); \
770 static void copy_item(gpointer data, guint action, GtkWidget *widget)
772 Collection *collection;
774 g_return_if_fail(window_with_focus != NULL);
776 collection = window_with_focus->collection;
777 if (collection->number_selected > 1)
779 report_error("ROX-Filer", "You cannot do this to more than "
780 "one item at a time");
781 return;
783 else if (collection->number_selected != 1)
784 collection_target(collection, target_callback, copy_item);
785 else
786 SHOW_SAVEBOX("Copy", copy_cb);
789 static gboolean rename_cb(guchar *current, guchar *new)
791 if (rename(current, new))
793 report_error("ROX-Filer: rename()", g_strerror(errno));
794 return FALSE;
796 return TRUE;
799 static void rename_item(gpointer data, guint action, GtkWidget *widget)
801 Collection *collection;
803 g_return_if_fail(window_with_focus != NULL);
805 collection = window_with_focus->collection;
806 if (collection->number_selected > 1)
808 report_error("ROX-Filer", "You cannot do this to more than "
809 "one item at a time");
810 return;
812 else if (collection->number_selected != 1)
813 collection_target(collection, target_callback, rename_item);
814 else
815 SHOW_SAVEBOX("Rename", rename_cb);
818 static gboolean link_cb(guchar *initial, guchar *path)
820 if (symlink(initial, path))
822 report_error("ROX-Filer: symlink()", g_strerror(errno));
823 return FALSE;
825 return TRUE;
828 static void link_item(gpointer data, guint action, GtkWidget *widget)
830 Collection *collection;
832 g_return_if_fail(window_with_focus != NULL);
834 collection = window_with_focus->collection;
835 if (collection->number_selected > 1)
837 report_error("ROX-Filer", "You cannot do this to more than "
838 "one item at a time");
839 return;
841 else if (collection->number_selected != 1)
842 collection_target(collection, target_callback, link_item);
843 else
844 SHOW_SAVEBOX("Symlink", link_cb);
847 static void open_file(gpointer data, guint action, GtkWidget *widget)
849 Collection *collection;
851 g_return_if_fail(window_with_focus != NULL);
853 collection = window_with_focus->collection;
854 if (collection->number_selected > 1)
856 report_error("ROX-Filer", "You cannot do this to more than "
857 "one item at a time");
858 return;
860 else if (collection->number_selected != 1)
861 collection_target(collection, target_callback, open_file);
862 else
863 filer_openitem(window_with_focus,
864 selected_item_number(collection),
865 OPEN_SAME_WINDOW | OPEN_SHIFT);
868 /* Got some data from file(1) - stick it in the window. */
869 static void add_file_output(FileStatus *fs,
870 gint source, GdkInputCondition condition)
872 char buffer[20];
873 char *str;
874 int got;
876 got = read(source, buffer, sizeof(buffer) - 1);
877 if (got <= 0)
879 int err = errno;
880 gtk_input_remove(fs->input);
881 close(source);
882 fs->fd = -1;
883 if (got < 0)
884 delayed_error("ROX-Filer: file(1) says...",
885 g_strerror(err));
886 return;
888 buffer[got] = '\0';
890 if (fs->start)
892 str = "";
893 fs->start = FALSE;
895 else
896 gtk_label_get(fs->label, &str);
898 str = g_strconcat(str, buffer, NULL);
899 gtk_label_set_text(fs->label, str);
900 g_free(str);
903 static void file_info_destroyed(GtkWidget *widget, FileStatus *fs)
905 if (fs->fd != -1)
907 gtk_input_remove(fs->input);
908 close(fs->fd);
911 g_free(fs);
914 /* g_free() the result */
915 guchar *pretty_type(DirItem *file, guchar *path)
917 if (file->mime_type)
918 return g_strconcat(file->mime_type->media_type, "/",
919 file->mime_type->subtype, NULL);
921 if (file->flags & ITEM_FLAG_APPDIR)
922 return g_strdup("ROX application");
924 if (file->flags & ITEM_FLAG_SYMLINK)
926 char p[MAXPATHLEN + 1];
927 int got;
928 got = readlink(path, p, MAXPATHLEN);
929 if (got > 0 && got <= MAXPATHLEN)
931 p[got] = '\0';
932 return g_strconcat("Symbolic link to ", p, NULL);
935 return g_strdup("Symbolic link");
938 if (file->flags & ITEM_FLAG_MOUNT_POINT)
940 MountPoint *mp;
941 if ((file->flags & ITEM_FLAG_MOUNTED) &&
942 (mp = g_hash_table_lookup(mtab_mounts, path)))
943 return g_strconcat("Mount point for ", mp->name, NULL);
945 return g_strdup("Mount point");
948 return g_strdup("-");
951 #define LABEL(text, row) \
952 label = gtk_label_new(text ":"); \
953 gtk_misc_set_alignment(GTK_MISC(label), 1, .5); \
954 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_RIGHT); \
955 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, row, row + 1);
957 #define VALUE(label, row) \
958 gtk_misc_set_alignment(GTK_MISC(label), 0, .5); \
959 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, row, row + 1);
961 static void show_file_info(gpointer data, guint action, GtkWidget *widget)
963 GtkWidget *window, *table, *label, *button, *frame, *value;
964 GtkWidget *file_label;
965 GString *gstring;
966 int file_data[2];
967 guchar *path, *tmp;
968 char *argv[] = {"file", "-b", NULL, NULL};
969 Collection *collection;
970 DirItem *file;
971 struct stat info;
972 FileStatus *fs = NULL;
973 guint perm;
975 g_return_if_fail(window_with_focus != NULL);
977 collection = window_with_focus->collection;
978 if (collection->number_selected > 1)
980 report_error("ROX-Filer", "You cannot do this to more than "
981 "one item at a time");
982 return;
984 else if (collection->number_selected != 1)
986 collection_target(collection, target_callback, show_file_info);
987 return;
989 file = selected_item(collection);
990 path = make_path(window_with_focus->path,
991 file->leafname)->str;
992 if (lstat(path, &info))
994 delayed_error("ROX-Filer", g_strerror(errno));
995 return;
998 gstring = g_string_new(NULL);
1000 window = gtk_window_new(GTK_WINDOW_DIALOG);
1001 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_MOUSE);
1002 gtk_container_set_border_width(GTK_CONTAINER(window), 4);
1003 gtk_window_set_title(GTK_WINDOW(window), path);
1005 table = gtk_table_new(10, 2, FALSE);
1006 gtk_container_add(GTK_CONTAINER(window), table);
1007 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
1008 gtk_table_set_col_spacings(GTK_TABLE(table), 4);
1010 value = gtk_label_new(file->leafname);
1011 LABEL("Name", 0);
1012 VALUE(value, 0);
1014 g_string_sprintf(gstring, "%s, %s", user_name(info.st_uid),
1015 group_name(info.st_gid));
1016 value = gtk_label_new(gstring->str);
1017 LABEL("Owner, Group", 1);
1018 VALUE(value, 1);
1020 if (info.st_size >= PRETTY_SIZE_LIMIT)
1022 g_string_sprintf(gstring, "%s (%ld bytes)",
1023 format_size((unsigned long) info.st_size),
1024 (unsigned long) info.st_size);
1026 else
1028 g_string_sprintf(gstring, "%ld byte%s",
1029 (unsigned long) info.st_size,
1030 info.st_size == 1 ? "" : "s");
1032 value = gtk_label_new(gstring->str);
1033 LABEL("Size", 2);
1034 VALUE(value, 2);
1036 value = gtk_label_new(pretty_time(&info.st_ctime));
1037 LABEL("Change time", 3);
1038 VALUE(value, 3);
1040 value = gtk_label_new(pretty_time(&info.st_mtime));
1041 LABEL("Modify time", 4);
1042 VALUE(value, 4);
1044 value = gtk_label_new(pretty_time(&info.st_atime));
1045 LABEL("Access time", 5);
1046 VALUE(value, 5);
1048 value = gtk_label_new(pretty_permissions(info.st_mode));
1049 perm = applicable(info.st_uid, info.st_gid);
1050 gtk_label_set_pattern(GTK_LABEL(value),
1051 perm == 0 ? "___ " :
1052 perm == 1 ? " ___ " :
1053 " ___");
1054 gtk_widget_set_style(value, fixed_style);
1055 LABEL("Permissions", 6);
1056 VALUE(value, 6);
1058 tmp = pretty_type(file, path);
1059 value = gtk_label_new(tmp);
1060 g_free(tmp);
1061 LABEL("Type", 7);
1062 VALUE(value, 7);
1064 frame = gtk_frame_new("file(1) says...");
1065 gtk_table_attach_defaults(GTK_TABLE(table), frame, 0, 2, 8, 9);
1066 file_label = gtk_label_new("<nothing yet>");
1067 gtk_misc_set_padding(GTK_MISC(file_label), 4, 4);
1068 gtk_label_set_line_wrap(GTK_LABEL(file_label), TRUE);
1069 gtk_container_add(GTK_CONTAINER(frame), file_label);
1071 button = gtk_button_new_with_label("OK");
1072 gtk_window_set_focus(GTK_WINDOW(window), button);
1073 gtk_table_attach(GTK_TABLE(table), button, 0, 2, 10, 11,
1074 GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 40, 4);
1075 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1076 gtk_widget_destroy, GTK_OBJECT(window));
1078 gtk_widget_show_all(window);
1079 gdk_flush();
1081 if (pipe(file_data))
1083 g_string_sprintf(gstring, "pipe(): %s", g_strerror(errno));
1084 g_string_free(gstring, TRUE);
1085 return;
1087 switch (fork())
1089 case -1:
1090 g_string_sprintf(gstring, "fork(): %s",
1091 g_strerror(errno));
1092 gtk_label_set_text(GTK_LABEL(file_label), gstring->str);
1093 g_string_free(gstring, TRUE);
1094 close(file_data[0]);
1095 close(file_data[1]);
1096 break;
1097 case 0:
1098 /* We are the child */
1099 close(file_data[0]);
1100 dup2(file_data[1], STDOUT_FILENO);
1101 dup2(file_data[1], STDERR_FILENO);
1102 #ifdef FILE_B_FLAG
1103 argv[2] = path;
1104 #else
1105 argv[1] = file->leafname;
1106 chdir(window_with_focus->path);
1107 #endif
1108 if (execvp(argv[0], argv))
1109 fprintf(stderr, "execvp() error: %s\n",
1110 g_strerror(errno));
1111 _exit(0);
1112 default:
1113 /* We are the parent */
1114 close(file_data[1]);
1115 fs = g_new(FileStatus, 1);
1116 fs->label = GTK_LABEL(file_label);
1117 fs->fd = file_data[0];
1118 fs->start = TRUE;
1119 fs->input = gdk_input_add(fs->fd, GDK_INPUT_READ,
1120 (GdkInputFunction) add_file_output, fs);
1121 gtk_signal_connect(GTK_OBJECT(window), "destroy",
1122 GTK_SIGNAL_FUNC(file_info_destroyed), fs);
1123 g_string_free(gstring, TRUE);
1124 break;
1128 static void app_show_help(char *path)
1130 char *help_dir;
1131 struct stat info;
1133 help_dir = g_strconcat(path, "/Help", NULL);
1135 if (mc_stat(help_dir, &info))
1136 delayed_error("Application",
1137 "This is an application directory - you can "
1138 "run it as a program, or open it (hold down "
1139 "Shift while you open it). Most applications provide "
1140 "their own help here, but this one doesn't.");
1141 else
1142 filer_opendir(help_dir, PANEL_NO);
1145 static void help(gpointer data, guint action, GtkWidget *widget)
1147 Collection *collection;
1148 DirItem *item;
1150 g_return_if_fail(window_with_focus != NULL);
1152 collection = window_with_focus->collection;
1153 if (collection->number_selected > 1)
1155 report_error("ROX-Filer", "You cannot do this to more than "
1156 "one item at a time");
1157 return;
1159 else if (collection->number_selected != 1)
1161 collection_target(collection, target_callback, help);
1162 return;
1164 item = selected_item(collection);
1165 switch (item->base_type)
1167 case TYPE_FILE:
1168 if (item->flags & ITEM_FLAG_EXEC_FILE)
1169 delayed_error("Executable file",
1170 "This is a file with an eXecute bit "
1171 "set - it can be run as a program.");
1172 else
1173 delayed_error("File",
1174 "This is a data file. Try using the "
1175 "Info menu item to find out more...");
1176 break;
1177 case TYPE_DIRECTORY:
1178 if (item->flags & ITEM_FLAG_APPDIR)
1179 app_show_help(
1180 make_path(window_with_focus->path,
1181 item->leafname)->str);
1182 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
1183 delayed_error("Mount point",
1184 "A mount point is a directory which another "
1185 "filing system can be mounted on. Everything "
1186 "on the mounted filesystem then appears to be "
1187 "inside the directory.");
1188 else
1189 delayed_error("Directory",
1190 "This is a directory. It contains an index to "
1191 "other items - open it to see the list.");
1192 break;
1193 case TYPE_CHAR_DEVICE:
1194 case TYPE_BLOCK_DEVICE:
1195 delayed_error("Device file",
1196 "Device files allow you to read from or write "
1197 "to a device driver as though it was an "
1198 "ordinary file.");
1199 break;
1200 case TYPE_PIPE:
1201 delayed_error("Named pipe",
1202 "Pipes allow different programs to "
1203 "communicate. One program writes data to the "
1204 "pipe while another one reads it out again.");
1205 break;
1206 case TYPE_SOCKET:
1207 delayed_error("Socket",
1208 "Sockets allow processes to communicate.");
1209 break;
1210 default:
1211 delayed_error("Unknown type",
1212 "I couldn't find out what kind of file this "
1213 "is. Maybe it doesn't exist anymore or you "
1214 "don't have search permission on the directory "
1215 "it's in?");
1216 break;
1220 #define OPEN_VFS(fs) \
1221 static void open_vfs_ ## fs (gpointer data, guint action, GtkWidget *widget) \
1223 Collection *collection; \
1225 g_return_if_fail(window_with_focus != NULL); \
1227 collection = window_with_focus->collection; \
1228 if (collection->number_selected < 1) \
1229 collection_target(collection, target_callback, \
1230 open_vfs_ ## fs); \
1231 else \
1232 real_vfs_open(#fs); \
1235 static void real_vfs_open(char *fs)
1237 gchar *path;
1238 DirItem *item;
1240 if (window_with_focus->collection->number_selected != 1)
1242 report_error("ROX-Filer", "You must select a single file "
1243 "to open as a Virtual File System");
1244 return;
1247 item = selected_item(window_with_focus->collection);
1249 path = g_strconcat(window_with_focus->path,
1250 "/",
1251 item->leafname,
1252 "#", fs, NULL);
1254 filer_change_to(window_with_focus, path, NULL);
1255 g_free(path);
1258 OPEN_VFS(rpm)
1259 OPEN_VFS(utar)
1260 OPEN_VFS(uzip)
1262 static void select_all(gpointer data, guint action, GtkWidget *widget)
1264 g_return_if_fail(window_with_focus != NULL);
1266 collection_select_all(window_with_focus->collection);
1267 window_with_focus->temp_item_selected = FALSE;
1270 static void clear_selection(gpointer data, guint action, GtkWidget *widget)
1272 g_return_if_fail(window_with_focus != NULL);
1274 collection_clear_selection(window_with_focus->collection);
1275 window_with_focus->temp_item_selected = FALSE;
1278 static void show_options(gpointer data, guint action, GtkWidget *widget)
1280 g_return_if_fail(window_with_focus != NULL);
1282 options_show(window_with_focus);
1285 static gboolean new_directory_cb(guchar *initial, guchar *path)
1287 if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO))
1289 report_error("mkdir", g_strerror(errno));
1290 return FALSE;
1292 return TRUE;
1295 static void new_directory(gpointer data, guint action, GtkWidget *widget)
1297 g_return_if_fail(window_with_focus != NULL);
1299 savebox_show("New Directory",
1300 make_path(window_with_focus->path, "NewDir")->str,
1301 default_pixmap + TYPE_DIRECTORY,
1302 new_directory_cb);
1305 static void xterm_here(gpointer data, guint action, GtkWidget *widget)
1307 char *argv[] = {NULL, NULL};
1309 argv[0] = xterm_here_value;
1311 g_return_if_fail(window_with_focus != NULL);
1313 if (!spawn_full(argv, window_with_focus->path))
1314 report_error("ROX-Filer", "Failed to fork() child "
1315 "process");
1318 static void open_parent(gpointer data, guint action, GtkWidget *widget)
1320 char *copy;
1321 char *slash;
1323 g_return_if_fail(window_with_focus != NULL);
1325 if (window_with_focus->path[0] == '/'
1326 && window_with_focus->path[1] == '\0')
1327 return; /* Already in the root */
1329 copy = g_strdup(window_with_focus->path);
1330 slash = strrchr(copy, '/');
1332 if (slash)
1334 *slash = '\0';
1335 filer_opendir(*copy ? copy : "/", PANEL_NO);
1337 else
1338 g_warning("No / in directory path!\n");
1340 g_free(copy);
1343 static void open_parent_same(gpointer data, guint action, GtkWidget *widget)
1345 g_return_if_fail(window_with_focus != NULL);
1347 change_to_parent(window_with_focus);
1350 static void new_window(gpointer data, guint action, GtkWidget *widget)
1352 g_return_if_fail(window_with_focus != NULL);
1354 if (o_unique_filer_windows)
1355 report_error("ROX-Filer", "You can't open a second view onto "
1356 "this directory because the `Unique Windows' option "
1357 "is turned on in the Options window.");
1358 else
1359 filer_opendir(window_with_focus->path, PANEL_NO);
1362 static void close_window(gpointer data, guint action, GtkWidget *widget)
1364 g_return_if_fail(window_with_focus != NULL);
1366 gtk_widget_destroy(window_with_focus->window);
1369 static void enter_path(gpointer data, guint action, GtkWidget *widget)
1371 g_return_if_fail(window_with_focus != NULL);
1373 minibuffer_show(window_with_focus, MINI_PATH);
1376 static void shell_command(gpointer data, guint action, GtkWidget *widget)
1378 g_return_if_fail(window_with_focus != NULL);
1380 minibuffer_show(window_with_focus, MINI_SHELL);
1383 static void rox_help(gpointer data, guint action, GtkWidget *widget)
1385 g_return_if_fail(window_with_focus != NULL);
1387 filer_opendir(make_path(getenv("APP_DIR"), "Help")->str, PANEL_NO);
1390 static void open_as_dir(gpointer data, guint action, GtkWidget *widget)
1392 g_return_if_fail(window_with_focus != NULL);
1394 filer_opendir(window_with_focus->path, PANEL_NO);
1397 static void close_panel(gpointer data, guint action, GtkWidget *widget)
1399 g_return_if_fail(window_with_focus != NULL);
1401 gtk_widget_destroy(window_with_focus->window);