r223: Implemented Chris Sawer's patch which adds a field for the leafname to the
[rox-filer.git] / ROX-Filer / src / menu.c
blob39b5a669bc43122ea28ed8d0ab852f40934154c9
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);
103 static void open_vfs_rpm(gpointer data, guint action, GtkWidget *widget);
104 static void open_vfs_utar(gpointer data, guint action, GtkWidget *widget);
105 static void open_vfs_uzip(gpointer data, guint action, GtkWidget *widget);
107 static void select_all(gpointer data, guint action, GtkWidget *widget);
108 static void clear_selection(gpointer data, guint action, GtkWidget *widget);
109 static void show_options(gpointer data, guint action, GtkWidget *widget);
110 static void new_directory(gpointer data, guint action, GtkWidget *widget);
111 static void xterm_here(gpointer data, guint action, GtkWidget *widget);
113 static void open_parent_same(gpointer data, guint action, GtkWidget *widget);
114 static void open_parent(gpointer data, guint action, GtkWidget *widget);
115 static void new_window(gpointer data, guint action, GtkWidget *widget);
116 static void close_window(gpointer data, guint action, GtkWidget *widget);
117 static void enter_path(gpointer data, guint action, GtkWidget *widget);
118 static void rox_help(gpointer data, guint action, GtkWidget *widget);
120 static void open_as_dir(gpointer data, guint action, GtkWidget *widget);
121 static void close_panel(gpointer data, guint action, GtkWidget *widget);
123 static GtkWidget *create_options();
124 static void update_options();
125 static void set_options();
126 static void save_options();
128 static OptionsSection options =
130 "Menu options",
131 create_options,
132 update_options,
133 set_options,
134 save_options
138 static GtkWidget *filer_menu; /* The popup filer menu */
139 static GtkWidget *filer_file_item; /* The File '' label */
140 static GtkWidget *filer_file_menu; /* The File '' menu */
141 static GtkWidget *filer_vfs_menu; /* The Open VFS menu */
142 static GtkWidget *filer_hidden_menu; /* The Show Hidden item */
143 static GtkWidget *filer_new_window; /* The New Window item */
144 static GtkWidget *panel_menu; /* The popup panel menu */
145 static GtkWidget *panel_hidden_menu; /* The Show Hidden item */
147 /* Used for Copy, etc */
148 static GtkWidget *savebox = NULL;
149 static guchar *current_path = NULL;
150 static gboolean (*current_savebox_callback)(guchar *current, guchar *new);
152 static gint screen_width, screen_height;
154 static GtkItemFactoryEntry filer_menu_def[] = {
155 {"/Display", NULL, NULL, 0, "<Branch>"},
156 {"/Display/Large Icons", NULL, large, 0, NULL},
157 {"/Display/Small Icons", NULL, small, 0, NULL},
158 {"/Display/Full Info", NULL, full_info, 0, NULL},
159 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
160 {"/Display/Sort by Name", NULL, sort_name, 0, NULL},
161 {"/Display/Sort by Type", NULL, sort_type, 0, NULL},
162 {"/Display/Sort by Date", NULL, sort_date, 0, NULL},
163 {"/Display/Sort by Size", NULL, sort_size, 0, NULL},
164 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
165 {"/Display/Show Hidden", C_"H", hidden, 0, "<ToggleItem>"},
166 {"/Display/Refresh", C_"L", refresh, 0, NULL},
167 {"/File", NULL, NULL, 0, "<Branch>"},
168 {"/File/Copy...", NULL, copy_item, 0, NULL},
169 {"/File/Rename...", NULL, rename_item, 0, NULL},
170 {"/File/Link...", NULL, link_item, 0, NULL},
171 {"/File/Shift Open", NULL, open_file, 0, NULL},
172 {"/File/Help", "F1", help, 0, NULL},
173 {"/File/Info", "I", show_file_info, 0, NULL},
174 {"/File/Open VFS", NULL, NULL, 0, "<Branch>"},
175 {"/File/Open VFS/Unzip", NULL, open_vfs_uzip, 0, NULL},
176 {"/File/Open VFS/Untar", NULL, open_vfs_utar, 0, NULL},
177 {"/File/Open VFS/RPM", NULL, open_vfs_rpm, 0, NULL},
178 {"/File/Separator", NULL, NULL, 0, "<Separator>"},
179 {"/File/Mount", "M", mount, 0, NULL},
180 {"/File/Delete", C_"X", delete, 0, NULL},
181 {"/File/Disk Usage", "U", usage, 0, NULL},
182 {"/File/Permissions", NULL, chmod_items, 0, NULL},
183 {"/File/Touch", NULL, not_yet, 0, NULL},
184 {"/File/Find", NULL, not_yet, 0, NULL},
185 {"/Select All", C_"A", select_all, 0, NULL},
186 {"/Clear Selection", C_"Z", clear_selection, 0, NULL},
187 {"/Options...", NULL, show_options, 0, NULL},
188 {"/New Directory...", NULL, new_directory, 0, NULL},
189 {"/Xterm Here", NULL, xterm_here, 0, NULL},
190 {"/Window", NULL, NULL, 0, "<Branch>"},
191 {"/Window/Parent, New Window", NULL, open_parent, 0, NULL},
192 {"/Window/Parent, Same Window", NULL, open_parent_same, 0, NULL},
193 {"/Window/New Window", NULL, new_window, 0, NULL},
194 {"/Window/Close Window", C_"Q", close_window, 0, NULL},
195 {"/Window/Enter Path", NULL, enter_path, 0, NULL},
196 {"/Window/Separator", NULL, NULL, 0, "<Separator>"},
197 {"/Window/Show ROX-Filer help", NULL, rox_help, 0, NULL},
200 static GtkItemFactoryEntry panel_menu_def[] = {
201 {"/Display", NULL, NULL, 0, "<Branch>"},
202 {"/Display/Sort by Name", NULL, sort_name, 0, NULL},
203 {"/Display/Sort by Type", NULL, sort_type, 0, NULL},
204 {"/Display/Sort by Date", NULL, sort_date, 0, NULL},
205 {"/Display/Sort by Size", NULL, sort_size, 0, NULL},
206 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
207 {"/Display/Show Hidden", NULL, hidden, 0, "<ToggleItem>"},
208 {"/Display/Refresh", NULL, refresh, 0, NULL},
209 {"/Open Panel as Directory", NULL, open_as_dir, 0, NULL},
210 {"/Close Panel", NULL, close_panel, 0, NULL},
211 {"/Separator", NULL, NULL, 0, "<Separator>"},
212 {"/ROX-Filer Help", NULL, rox_help, 0, NULL},
213 {"/ROX-Filer Options...", NULL, show_options, 0, NULL},
214 {"/Separator", NULL, NULL, 0, "<Separator>"},
215 {"/Show Help", NULL, help, 0, NULL},
216 {"/Remove Item", NULL, remove_link, 0, NULL},
219 typedef struct _FileStatus FileStatus;
221 /* This is for the 'file(1) says...' thing */
222 struct _FileStatus
224 int fd; /* FD to read from, -1 if closed */
225 int input; /* Input watcher tag if fd valid */
226 GtkLabel *label; /* Widget to output to */
227 gboolean start; /* No output yet */
230 void menu_init()
232 GtkItemFactory *item_factory;
233 char *menurc;
234 GList *items;
236 /* This call starts returning strange values after a while, so get
237 * the result here during init.
239 gdk_window_get_size(GDK_ROOT_PARENT(), &screen_width, &screen_height);
241 filer_keys = gtk_accel_group_new();
242 item_factory = gtk_item_factory_new(GTK_TYPE_MENU,
243 "<filer>",
244 filer_keys);
245 gtk_item_factory_create_items(item_factory,
246 sizeof(filer_menu_def) / sizeof(*filer_menu_def),
247 filer_menu_def,
248 NULL);
249 filer_menu = gtk_item_factory_get_widget(item_factory, "<filer>");
250 filer_file_menu = gtk_item_factory_get_widget(item_factory,
251 "<filer>/File");
252 filer_vfs_menu = gtk_item_factory_get_widget(item_factory,
253 "<filer>/File/Open VFS");
254 filer_hidden_menu = gtk_item_factory_get_widget(item_factory,
255 "<filer>/Display/Show Hidden");
256 items = gtk_container_children(GTK_CONTAINER(filer_menu));
257 filer_file_item = GTK_BIN(g_list_nth(items, 1)->data)->child;
258 g_list_free(items);
259 filer_new_window = GTK_BIN(gtk_item_factory_get_widget(item_factory,
260 "<filer>/Window/New Window"))->child;
262 panel_keys = gtk_accel_group_new();
263 item_factory = gtk_item_factory_new(GTK_TYPE_MENU,
264 "<panel>",
265 panel_keys);
266 gtk_item_factory_create_items(item_factory,
267 sizeof(panel_menu_def) / sizeof(*panel_menu_def),
268 panel_menu_def,
269 NULL);
270 panel_menu = gtk_item_factory_get_widget(item_factory, "<panel>");
271 panel_hidden_menu = gtk_item_factory_get_widget(item_factory,
272 "<panel>/Display/Show Hidden");
274 menurc = choices_find_path_load("menus");
275 if (menurc)
276 gtk_item_factory_parse_rc(menurc);
278 gtk_accel_group_lock(panel_keys);
280 gtk_signal_connect(GTK_OBJECT(filer_menu), "unmap_event",
281 GTK_SIGNAL_FUNC(menu_closed), NULL);
282 gtk_signal_connect(GTK_OBJECT(panel_menu), "unmap_event",
283 GTK_SIGNAL_FUNC(menu_closed), NULL);
284 gtk_signal_connect(GTK_OBJECT(filer_file_menu), "unmap_event",
285 GTK_SIGNAL_FUNC(menu_closed), NULL);
287 options_sections = g_slist_prepend(options_sections, &options);
288 xterm_here_value = g_strdup("xterm");
289 option_register("xterm_here", load_xterm_here);
291 savebox = gtk_savebox_new();
292 gtk_signal_connect_object(GTK_OBJECT(savebox), "save_to_file",
293 GTK_SIGNAL_FUNC(save_to_file), NULL);
294 gtk_signal_connect_object(GTK_OBJECT(savebox), "save_done",
295 GTK_SIGNAL_FUNC(gtk_widget_hide),
296 GTK_OBJECT(savebox));
299 /* Build up some option widgets to go in the options dialog, but don't
300 * fill them in yet.
302 static GtkWidget *create_options()
304 GtkWidget *table, *label;
306 table = gtk_table_new(2, 2, FALSE);
307 gtk_container_set_border_width(GTK_CONTAINER(table), 4);
309 label = gtk_label_new("To set the keyboard short-cuts you simply open "
310 "the menu over a filer window, move the pointer over "
311 "the item you want to use and press a key. The key "
312 "will appear next to the menu item and you can just "
313 "press that key without opening the menu in future. "
314 "To save the current menu short-cuts for next time, "
315 "click the Save button at the bottom of this window.");
316 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
317 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 2, 0, 1);
319 label = gtk_label_new("'Xterm here' program:");
320 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
321 xterm_here_entry = gtk_entry_new();
322 gtk_table_attach_defaults(GTK_TABLE(table), xterm_here_entry,
323 1, 2, 1, 2);
325 return table;
328 static char *load_xterm_here(char *data)
330 g_free(xterm_here_value);
331 xterm_here_value = g_strdup(data);
332 return NULL;
335 static void update_options()
337 gtk_entry_set_text(GTK_ENTRY(xterm_here_entry), xterm_here_value);
340 static void set_options()
342 g_free(xterm_here_value);
343 xterm_here_value = g_strdup(gtk_entry_get_text(
344 GTK_ENTRY(xterm_here_entry)));
347 static void save_options()
349 char *menurc;
351 menurc = choices_find_path_save("menus", TRUE);
352 if (menurc)
353 gtk_item_factory_dump_rc(menurc, NULL, TRUE);
355 option_write("xterm_here", xterm_here_value);
359 static void items_sensitive(gboolean state)
361 int n = 7;
362 GList *items, *item;
364 items = item = gtk_container_children(GTK_CONTAINER(filer_file_menu));
365 while (item && n--)
367 gtk_widget_set_sensitive(GTK_BIN(item->data)->child, state);
368 item = item->next;
370 g_list_free(items);
372 items = item = gtk_container_children(GTK_CONTAINER(filer_vfs_menu));
373 while (item)
375 gtk_widget_set_sensitive(GTK_BIN(item->data)->child, state);
376 item = item->next;
378 g_list_free(items);
381 static void position_menu(GtkMenu *menu, gint *x, gint *y, gpointer data)
383 int *pos = (int *) data;
384 GtkRequisition requisition;
386 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
388 if (pos[0] == -1)
389 *x = screen_width - MENU_MARGIN - requisition.width;
390 else if (pos[0] == -2)
391 *x = MENU_MARGIN;
392 else
393 *x = pos[0] - (requisition.width >> 2);
395 if (pos[1] == -1)
396 *y = screen_height - MENU_MARGIN - requisition.height;
397 else if (pos[1] == -2)
398 *y = MENU_MARGIN;
399 else
400 *y = pos[1] - (requisition.height >> 2);
402 *x = CLAMP(*x, 0, screen_width - requisition.width);
403 *y = CLAMP(*y, 0, screen_height - requisition.height);
406 void show_filer_menu(FilerWindow *filer_window, GdkEventButton *event,
407 int item)
409 DirItem *file_item;
410 int pos[2];
412 updating_menu++;
414 pos[0] = event->x_root;
415 pos[1] = event->y_root;
417 window_with_focus = filer_window;
419 switch (filer_window->panel_type)
421 case PANEL_TOP:
422 pos[1] = -2;
423 break;
424 case PANEL_BOTTOM:
425 pos[1] = -1;
426 break;
427 default:
428 break;
431 if (filer_window->panel_type)
432 collection_clear_selection(filer_window->collection); /* ??? */
434 if (filer_window->collection->number_selected == 0 && item >= 0)
436 collection_select_item(filer_window->collection, item);
437 filer_window->temp_item_selected = TRUE;
439 else
440 filer_window->temp_item_selected = FALSE;
442 if (filer_window->panel_type)
444 gtk_check_menu_item_set_active(
445 GTK_CHECK_MENU_ITEM(panel_hidden_menu),
446 filer_window->show_hidden);
448 else
450 GtkWidget *file_label, *file_menu;
451 Collection *collection = filer_window->collection;
452 GString *buffer;
454 file_label = filer_file_item;
455 file_menu = filer_file_menu;
456 gtk_check_menu_item_set_active(
457 GTK_CHECK_MENU_ITEM(filer_hidden_menu),
458 filer_window->show_hidden);
459 buffer = g_string_new(NULL);
460 switch (collection->number_selected)
462 case 0:
463 g_string_assign(buffer, "Next Click");
464 items_sensitive(TRUE);
465 break;
466 case 1:
467 items_sensitive(TRUE);
468 file_item = selected_item(
469 filer_window->collection);
470 g_string_sprintf(buffer, "%s '%s'",
471 basetype_name(file_item),
472 file_item->leafname);
473 break;
474 default:
475 items_sensitive(FALSE);
476 g_string_sprintf(buffer, "%d items",
477 collection->number_selected);
478 break;
480 gtk_label_set_text(GTK_LABEL(file_label), buffer->str);
481 g_string_free(buffer, TRUE);
485 gtk_widget_set_sensitive(filer_new_window, !o_unique_filer_windows);
487 if (filer_window->panel_type)
488 popup_menu = panel_menu;
489 else
490 popup_menu = (event->state & GDK_CONTROL_MASK)
491 ? filer_file_menu
492 : filer_menu;
494 updating_menu--;
496 gtk_menu_popup(GTK_MENU(popup_menu), NULL, NULL, position_menu,
497 (gpointer) pos, event->button, event->time);
500 static void menu_closed(GtkWidget *widget)
502 if (window_with_focus == NULL || widget != popup_menu)
503 return; /* Close panel item chosen? */
505 if (window_with_focus->temp_item_selected)
507 collection_clear_selection(window_with_focus->collection);
508 window_with_focus->temp_item_selected = FALSE;
512 void target_callback(Collection *collection, gint item, gpointer real_fn)
514 g_return_if_fail(window_with_focus != NULL);
515 g_return_if_fail(window_with_focus->collection == collection);
516 g_return_if_fail(real_fn != NULL);
518 collection_wink_item(collection, item);
519 collection_clear_selection(collection);
520 collection_select_item(collection, item);
521 ((CollectionTargetFunc)real_fn)(NULL, 0, collection);
522 if (item < collection->number_of_items)
523 collection_unselect_item(collection, item);
526 /* Actions */
528 /* Fake action to warn when a menu item does nothing */
529 static void not_yet(gpointer data, guint action, GtkWidget *widget)
531 delayed_error("ROX-Filer", "Sorry, that feature isn't implemented yet");
534 static void large(gpointer data, guint action, GtkWidget *widget)
536 g_return_if_fail(window_with_focus != NULL);
538 filer_style_set(window_with_focus, LARGE_ICONS);
541 static void small(gpointer data, guint action, GtkWidget *widget)
543 g_return_if_fail(window_with_focus != NULL);
545 filer_style_set(window_with_focus, SMALL_ICONS);
548 static void full_info(gpointer data, guint action, GtkWidget *widget)
550 g_return_if_fail(window_with_focus != NULL);
552 filer_style_set(window_with_focus, FULL_INFO);
555 static void sort_name(gpointer data, guint action, GtkWidget *widget)
557 g_return_if_fail(window_with_focus != NULL);
559 filer_set_sort_fn(window_with_focus, sort_by_name);
562 static void sort_type(gpointer data, guint action, GtkWidget *widget)
564 g_return_if_fail(window_with_focus != NULL);
566 filer_set_sort_fn(window_with_focus, sort_by_type);
569 static void sort_date(gpointer data, guint action, GtkWidget *widget)
571 g_return_if_fail(window_with_focus != NULL);
573 filer_set_sort_fn(window_with_focus, sort_by_date);
576 static void sort_size(gpointer data, guint action, GtkWidget *widget)
578 g_return_if_fail(window_with_focus != NULL);
580 filer_set_sort_fn(window_with_focus, sort_by_size);
583 static void hidden(gpointer data, guint action, GtkWidget *widget)
585 if (updating_menu)
586 return;
588 g_return_if_fail(window_with_focus != NULL);
590 filer_set_hidden(window_with_focus, !window_with_focus->show_hidden);
593 static void refresh(gpointer data, guint action, GtkWidget *widget)
595 g_return_if_fail(window_with_focus != NULL);
597 full_refresh();
598 update_dir(window_with_focus, TRUE);
601 static void mount(gpointer data, guint action, GtkWidget *widget)
603 g_return_if_fail(window_with_focus != NULL);
605 if (window_with_focus->collection->number_selected == 0)
606 collection_target(window_with_focus->collection,
607 target_callback, mount);
608 else
609 action_mount(window_with_focus, NULL);
612 static void delete(gpointer data, guint action, GtkWidget *widget)
614 g_return_if_fail(window_with_focus != NULL);
616 if (window_with_focus->collection->number_selected == 0)
617 collection_target(window_with_focus->collection,
618 target_callback, delete);
619 else
620 action_delete(window_with_focus);
623 static void remove_link(gpointer data, guint action, GtkWidget *widget)
625 g_return_if_fail(window_with_focus != NULL);
627 if (window_with_focus->collection->number_selected > 1)
629 report_error("ROX-Filer", "You can only remove one link "
630 "at a time");
631 return;
633 else if (window_with_focus->collection->number_selected == 0)
634 collection_target(window_with_focus->collection,
635 target_callback, remove_link);
636 else
638 struct stat info;
639 DirItem *item;
640 guchar *path;
642 item = selected_item(window_with_focus->collection);
644 path = make_path(window_with_focus->path, item->leafname)->str;
645 if (lstat(path, &info))
646 report_error("ROX-Filer", g_strerror(errno));
647 else if (!S_ISLNK(info.st_mode))
648 report_error("ROX-Filer",
649 "You can only remove symbolic links this way - "
650 "try the 'Open Panel as Directory' item.");
651 else if (unlink(path))
652 report_error("ROX-Filer", g_strerror(errno));
653 else
654 update_dir(window_with_focus, TRUE);
658 static void usage(gpointer data, guint action, GtkWidget *widget)
660 g_return_if_fail(window_with_focus != NULL);
662 if (window_with_focus->collection->number_selected == 0)
663 collection_target(window_with_focus->collection,
664 target_callback, usage);
665 else
666 action_usage(window_with_focus);
669 static void chmod_items(gpointer data, guint action, GtkWidget *widget)
671 g_return_if_fail(window_with_focus != NULL);
673 if (window_with_focus->collection->number_selected == 0)
674 collection_target(window_with_focus->collection,
675 target_callback, chmod_items);
676 else
677 action_chmod(window_with_focus);
680 /* This pops up our savebox widget, cancelling any currently open one,
681 * and allows the user to pick a new path for it.
682 * Once the new path has been picked, the callback will be called with
683 * both the current and new paths.
685 static void savebox_show(guchar *title, guchar *path, MaskedPixmap *image,
686 gboolean (*callback)(guchar *current, guchar *new))
688 if (GTK_WIDGET_VISIBLE(savebox))
689 gtk_widget_hide(savebox);
691 if (current_path)
692 g_free(current_path);
693 current_path = g_strdup(path);
694 current_savebox_callback = callback;
696 gtk_window_set_title(GTK_WINDOW(savebox), title);
697 gtk_savebox_set_pathname(GTK_SAVEBOX(savebox), current_path);
698 gtk_savebox_set_icon(GTK_SAVEBOX(savebox), image->pixmap, image->mask);
700 gtk_widget_grab_focus(GTK_SAVEBOX(savebox)->entry);
701 gtk_widget_show(savebox);
704 static gint save_to_file(GtkSavebox *savebox, guchar *pathname)
706 g_return_val_if_fail(current_savebox_callback != NULL,
707 GTK_XDS_SAVE_ERROR);
709 return current_savebox_callback(current_path, pathname)
710 ? GTK_XDS_SAVED : GTK_XDS_SAVE_ERROR;
713 static gboolean copy_cb(guchar *current, guchar *new)
715 char *new_dir, *leaf;
716 GSList *local_paths;
718 if (new[0] != '/')
720 report_error("ROX-Filer", "New pathname is not absolute");
721 return FALSE;
724 if (new[strlen(new) - 1] == '/')
726 new_dir = g_strdup(new);
727 leaf = NULL;
729 else
731 guchar *slash;
733 slash = strrchr(new, '/');
734 new_dir = g_strndup(new, slash - new);
735 leaf = slash + 1;
738 local_paths = g_slist_append(NULL, current);
739 action_copy(local_paths, new_dir, leaf);
740 g_slist_free(local_paths);
742 g_free(new_dir);
744 return TRUE;
747 #define SHOW_SAVEBOX(title, callback) \
749 DirItem *item; \
750 guchar *path; \
751 item = selected_item(collection); \
752 path = make_path(window_with_focus->path, item->leafname)->str; \
753 savebox_show(title, path, item->image, callback); \
756 static void copy_item(gpointer data, guint action, GtkWidget *widget)
758 Collection *collection;
760 g_return_if_fail(window_with_focus != NULL);
762 collection = window_with_focus->collection;
763 if (collection->number_selected > 1)
765 report_error("ROX-Filer", "You cannot do this to more than "
766 "one item at a time");
767 return;
769 else if (collection->number_selected != 1)
770 collection_target(collection, target_callback, copy_item);
771 else
772 SHOW_SAVEBOX("Copy", copy_cb);
775 static gboolean rename_cb(guchar *current, guchar *new)
777 if (rename(current, new))
779 report_error("ROX-Filer: rename()", g_strerror(errno));
780 return FALSE;
782 return TRUE;
785 static void rename_item(gpointer data, guint action, GtkWidget *widget)
787 Collection *collection;
789 g_return_if_fail(window_with_focus != NULL);
791 collection = window_with_focus->collection;
792 if (collection->number_selected > 1)
794 report_error("ROX-Filer", "You cannot do this to more than "
795 "one item at a time");
796 return;
798 else if (collection->number_selected != 1)
799 collection_target(collection, target_callback, rename_item);
800 else
801 SHOW_SAVEBOX("Rename", rename_cb);
804 static gboolean link_cb(guchar *initial, guchar *path)
806 if (symlink(initial, path))
808 report_error("ROX-Filer: symlink()", g_strerror(errno));
809 return FALSE;
811 return TRUE;
814 static void link_item(gpointer data, guint action, GtkWidget *widget)
816 Collection *collection;
818 g_return_if_fail(window_with_focus != NULL);
820 collection = window_with_focus->collection;
821 if (collection->number_selected > 1)
823 report_error("ROX-Filer", "You cannot do this to more than "
824 "one item at a time");
825 return;
827 else if (collection->number_selected != 1)
828 collection_target(collection, target_callback, link_item);
829 else
830 SHOW_SAVEBOX("Symlink", link_cb);
833 static void open_file(gpointer data, guint action, GtkWidget *widget)
835 Collection *collection;
837 g_return_if_fail(window_with_focus != NULL);
839 collection = window_with_focus->collection;
840 if (collection->number_selected > 1)
842 report_error("ROX-Filer", "You cannot do this to more than "
843 "one item at a time");
844 return;
846 else if (collection->number_selected != 1)
847 collection_target(collection, target_callback, open_file);
848 else
849 filer_openitem(window_with_focus,
850 selected_item_number(collection),
851 OPEN_SAME_WINDOW | OPEN_SHIFT);
854 /* Got some data from file(1) - stick it in the window. */
855 static void add_file_output(FileStatus *fs,
856 gint source, GdkInputCondition condition)
858 char buffer[20];
859 char *str;
860 int got;
862 got = read(source, buffer, sizeof(buffer) - 1);
863 if (got <= 0)
865 int err = errno;
866 gtk_input_remove(fs->input);
867 close(source);
868 fs->fd = -1;
869 if (got < 0)
870 delayed_error("ROX-Filer: file(1) says...",
871 g_strerror(err));
872 return;
874 buffer[got] = '\0';
876 if (fs->start)
878 str = "";
879 fs->start = FALSE;
881 else
882 gtk_label_get(fs->label, &str);
884 str = g_strconcat(str, buffer, NULL);
885 gtk_label_set_text(fs->label, str);
886 g_free(str);
889 static void file_info_destroyed(GtkWidget *widget, FileStatus *fs)
891 if (fs->fd != -1)
893 gtk_input_remove(fs->input);
894 close(fs->fd);
897 g_free(fs);
900 /* g_free() the result */
901 guchar *pretty_type(DirItem *file, guchar *path)
903 if (file->mime_type)
904 return g_strconcat(file->mime_type->media_type, "/",
905 file->mime_type->subtype, NULL);
907 if (file->flags & ITEM_FLAG_APPDIR)
908 return g_strdup("ROX application");
910 if (file->flags & ITEM_FLAG_SYMLINK)
912 char p[MAXPATHLEN + 1];
913 int got;
914 got = readlink(path, p, MAXPATHLEN);
915 if (got > 0 && got <= MAXPATHLEN)
917 p[got] = '\0';
918 return g_strconcat("Symbolic link to ", p, NULL);
921 return g_strdup("Symbolic link");
924 if (file->flags & ITEM_FLAG_MOUNT_POINT)
926 MountPoint *mp;
927 if ((file->flags & ITEM_FLAG_MOUNTED) &&
928 (mp = g_hash_table_lookup(mtab_mounts, path)))
929 return g_strconcat("Mount point for ", mp->name, NULL);
931 return g_strdup("Mount point");
934 return g_strdup("-");
937 #define LABEL(text, row) \
938 label = gtk_label_new(text ":"); \
939 gtk_misc_set_alignment(GTK_MISC(label), 1, .5); \
940 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_RIGHT); \
941 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, row, row + 1);
943 #define VALUE(label, row) \
944 gtk_misc_set_alignment(GTK_MISC(label), 0, .5); \
945 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, row, row + 1);
947 static void show_file_info(gpointer data, guint action, GtkWidget *widget)
949 GtkWidget *window, *table, *label, *button, *frame, *value;
950 GtkWidget *file_label;
951 GString *gstring;
952 int file_data[2];
953 guchar *path, *tmp;
954 char *argv[] = {"file", "-b", NULL, NULL};
955 Collection *collection;
956 DirItem *file;
957 struct stat info;
958 FileStatus *fs = NULL;
959 guint perm;
961 g_return_if_fail(window_with_focus != NULL);
963 collection = window_with_focus->collection;
964 if (collection->number_selected > 1)
966 report_error("ROX-Filer", "You cannot do this to more than "
967 "one item at a time");
968 return;
970 else if (collection->number_selected != 1)
972 collection_target(collection, target_callback, show_file_info);
973 return;
975 file = selected_item(collection);
976 path = make_path(window_with_focus->path,
977 file->leafname)->str;
978 if (lstat(path, &info))
980 delayed_error("ROX-Filer", g_strerror(errno));
981 return;
984 gstring = g_string_new(NULL);
986 window = gtk_window_new(GTK_WINDOW_DIALOG);
987 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_MOUSE);
988 gtk_container_set_border_width(GTK_CONTAINER(window), 4);
989 gtk_window_set_title(GTK_WINDOW(window), path);
991 table = gtk_table_new(10, 2, FALSE);
992 gtk_container_add(GTK_CONTAINER(window), table);
993 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
994 gtk_table_set_col_spacings(GTK_TABLE(table), 4);
996 value = gtk_label_new(file->leafname);
997 LABEL("Name", 0);
998 VALUE(value, 0);
1000 g_string_sprintf(gstring, "%s, %s", user_name(info.st_uid),
1001 group_name(info.st_gid));
1002 value = gtk_label_new(gstring->str);
1003 LABEL("Owner, Group", 1);
1004 VALUE(value, 1);
1006 if (info.st_size >= PRETTY_SIZE_LIMIT)
1008 g_string_sprintf(gstring, "%s (%ld bytes)",
1009 format_size((unsigned long) info.st_size),
1010 (unsigned long) info.st_size);
1012 else
1014 g_string_sprintf(gstring, "%ld byte%s",
1015 (unsigned long) info.st_size,
1016 info.st_size == 1 ? "" : "s");
1018 value = gtk_label_new(gstring->str);
1019 LABEL("Size", 2);
1020 VALUE(value, 2);
1022 value = gtk_label_new(pretty_time(&info.st_ctime));
1023 LABEL("Change time", 3);
1024 VALUE(value, 3);
1026 value = gtk_label_new(pretty_time(&info.st_mtime));
1027 LABEL("Modify time", 4);
1028 VALUE(value, 4);
1030 value = gtk_label_new(pretty_time(&info.st_atime));
1031 LABEL("Access time", 5);
1032 VALUE(value, 5);
1034 value = gtk_label_new(pretty_permissions(info.st_mode));
1035 perm = applicable(info.st_uid, info.st_gid);
1036 gtk_label_set_pattern(GTK_LABEL(value),
1037 perm == 0 ? "___ " :
1038 perm == 1 ? " ___ " :
1039 " ___");
1040 gtk_widget_set_style(value, fixed_style);
1041 LABEL("Permissions", 6);
1042 VALUE(value, 6);
1044 tmp = pretty_type(file, path);
1045 value = gtk_label_new(tmp);
1046 g_free(tmp);
1047 LABEL("Type", 7);
1048 VALUE(value, 7);
1050 frame = gtk_frame_new("file(1) says...");
1051 gtk_table_attach_defaults(GTK_TABLE(table), frame, 0, 2, 8, 9);
1052 file_label = gtk_label_new("<nothing yet>");
1053 gtk_misc_set_padding(GTK_MISC(file_label), 4, 4);
1054 gtk_label_set_line_wrap(GTK_LABEL(file_label), TRUE);
1055 gtk_container_add(GTK_CONTAINER(frame), file_label);
1057 button = gtk_button_new_with_label("OK");
1058 gtk_window_set_focus(GTK_WINDOW(window), button);
1059 gtk_table_attach(GTK_TABLE(table), button, 0, 2, 10, 11,
1060 GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 40, 4);
1061 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1062 gtk_widget_destroy, GTK_OBJECT(window));
1064 gtk_widget_show_all(window);
1065 gdk_flush();
1067 if (pipe(file_data))
1069 g_string_sprintf(gstring, "pipe(): %s", g_strerror(errno));
1070 g_string_free(gstring, TRUE);
1071 return;
1073 switch (fork())
1075 case -1:
1076 g_string_sprintf(gstring, "fork(): %s",
1077 g_strerror(errno));
1078 gtk_label_set_text(GTK_LABEL(file_label), gstring->str);
1079 g_string_free(gstring, TRUE);
1080 close(file_data[0]);
1081 close(file_data[1]);
1082 break;
1083 case 0:
1084 /* We are the child */
1085 close(file_data[0]);
1086 dup2(file_data[1], STDOUT_FILENO);
1087 dup2(file_data[1], STDERR_FILENO);
1088 #ifdef FILE_B_FLAG
1089 argv[2] = path;
1090 #else
1091 argv[1] = file->leafname;
1092 chdir(window_with_focus->path);
1093 #endif
1094 if (execvp(argv[0], argv))
1095 fprintf(stderr, "execvp() error: %s\n",
1096 g_strerror(errno));
1097 _exit(0);
1098 default:
1099 /* We are the parent */
1100 close(file_data[1]);
1101 fs = g_new(FileStatus, 1);
1102 fs->label = GTK_LABEL(file_label);
1103 fs->fd = file_data[0];
1104 fs->start = TRUE;
1105 fs->input = gdk_input_add(fs->fd, GDK_INPUT_READ,
1106 (GdkInputFunction) add_file_output, fs);
1107 gtk_signal_connect(GTK_OBJECT(window), "destroy",
1108 GTK_SIGNAL_FUNC(file_info_destroyed), fs);
1109 g_string_free(gstring, TRUE);
1110 break;
1114 static void app_show_help(char *path)
1116 char *help_dir;
1117 struct stat info;
1119 help_dir = g_strconcat(path, "/Help", NULL);
1121 if (mc_stat(help_dir, &info))
1122 delayed_error("Application",
1123 "This is an application directory - you can "
1124 "run it as a program, or open it (hold down "
1125 "Shift while you open it). Most applications provide "
1126 "their own help here, but this one doesn't.");
1127 else
1128 filer_opendir(help_dir, PANEL_NO);
1131 static void help(gpointer data, guint action, GtkWidget *widget)
1133 Collection *collection;
1134 DirItem *item;
1136 g_return_if_fail(window_with_focus != NULL);
1138 collection = window_with_focus->collection;
1139 if (collection->number_selected > 1)
1141 report_error("ROX-Filer", "You cannot do this to more than "
1142 "one item at a time");
1143 return;
1145 else if (collection->number_selected != 1)
1147 collection_target(collection, target_callback, help);
1148 return;
1150 item = selected_item(collection);
1151 switch (item->base_type)
1153 case TYPE_FILE:
1154 if (item->flags & ITEM_FLAG_EXEC_FILE)
1155 delayed_error("Executable file",
1156 "This is a file with an eXecute bit "
1157 "set - it can be run as a program.");
1158 else
1159 delayed_error("File",
1160 "This is a data file. Try using the "
1161 "Info menu item to find out more...");
1162 break;
1163 case TYPE_DIRECTORY:
1164 if (item->flags & ITEM_FLAG_APPDIR)
1165 app_show_help(
1166 make_path(window_with_focus->path,
1167 item->leafname)->str);
1168 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
1169 delayed_error("Mount point",
1170 "A mount point is a directory which another "
1171 "filing system can be mounted on. Everything "
1172 "on the mounted filesystem then appears to be "
1173 "inside the directory.");
1174 else
1175 delayed_error("Directory",
1176 "This is a directory. It contains an index to "
1177 "other items - open it to see the list.");
1178 break;
1179 case TYPE_CHAR_DEVICE:
1180 case TYPE_BLOCK_DEVICE:
1181 delayed_error("Device file",
1182 "Device files allow you to read from or write "
1183 "to a device driver as though it was an "
1184 "ordinary file.");
1185 break;
1186 case TYPE_PIPE:
1187 delayed_error("Named pipe",
1188 "Pipes allow different programs to "
1189 "communicate. One program writes data to the "
1190 "pipe while another one reads it out again.");
1191 break;
1192 case TYPE_SOCKET:
1193 delayed_error("Socket",
1194 "Sockets allow processes to communicate.");
1195 break;
1196 default:
1197 delayed_error("Unknown type",
1198 "I couldn't find out what kind of file this "
1199 "is. Maybe it doesn't exist anymore or you "
1200 "don't have search permission on the directory "
1201 "it's in?");
1202 break;
1206 #define OPEN_VFS(fs) \
1207 static void open_vfs_ ## fs (gpointer data, guint action, GtkWidget *widget) \
1209 Collection *collection; \
1211 g_return_if_fail(window_with_focus != NULL); \
1213 collection = window_with_focus->collection; \
1214 if (collection->number_selected < 1) \
1215 collection_target(collection, target_callback, \
1216 open_vfs_ ## fs); \
1217 else \
1218 real_vfs_open(#fs); \
1221 static void real_vfs_open(char *fs)
1223 gchar *path;
1224 DirItem *item;
1226 if (window_with_focus->collection->number_selected != 1)
1228 report_error("ROX-Filer", "You must select a single file "
1229 "to open as a Virtual File System");
1230 return;
1233 item = selected_item(window_with_focus->collection);
1235 path = g_strconcat(window_with_focus->path,
1236 "/",
1237 item->leafname,
1238 "#", fs, NULL);
1240 filer_change_to(window_with_focus, path, NULL);
1241 g_free(path);
1244 OPEN_VFS(rpm)
1245 OPEN_VFS(utar)
1246 OPEN_VFS(uzip)
1248 static void select_all(gpointer data, guint action, GtkWidget *widget)
1250 g_return_if_fail(window_with_focus != NULL);
1252 collection_select_all(window_with_focus->collection);
1253 window_with_focus->temp_item_selected = FALSE;
1256 static void clear_selection(gpointer data, guint action, GtkWidget *widget)
1258 g_return_if_fail(window_with_focus != NULL);
1260 collection_clear_selection(window_with_focus->collection);
1261 window_with_focus->temp_item_selected = FALSE;
1264 static void show_options(gpointer data, guint action, GtkWidget *widget)
1266 g_return_if_fail(window_with_focus != NULL);
1268 options_show(window_with_focus);
1271 static gboolean new_directory_cb(guchar *initial, guchar *path)
1273 if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO))
1275 report_error("mkdir", g_strerror(errno));
1276 return FALSE;
1278 return TRUE;
1281 static void new_directory(gpointer data, guint action, GtkWidget *widget)
1283 g_return_if_fail(window_with_focus != NULL);
1285 savebox_show("New Directory",
1286 make_path(window_with_focus->path, "NewDir")->str,
1287 default_pixmap + TYPE_DIRECTORY,
1288 new_directory_cb);
1291 static void xterm_here(gpointer data, guint action, GtkWidget *widget)
1293 char *argv[] = {NULL, NULL};
1295 argv[0] = xterm_here_value;
1297 g_return_if_fail(window_with_focus != NULL);
1299 if (!spawn_full(argv, window_with_focus->path))
1300 report_error("ROX-Filer", "Failed to fork() child "
1301 "process");
1304 static void open_parent(gpointer data, guint action, GtkWidget *widget)
1306 char *copy;
1307 char *slash;
1309 g_return_if_fail(window_with_focus != NULL);
1311 if (window_with_focus->path[0] == '/'
1312 && window_with_focus->path[1] == '\0')
1313 return; /* Already in the root */
1315 copy = g_strdup(window_with_focus->path);
1316 slash = strrchr(copy, '/');
1318 if (slash)
1320 *slash = '\0';
1321 filer_opendir(*copy ? copy : "/", PANEL_NO);
1323 else
1324 g_warning("No / in directory path!\n");
1326 g_free(copy);
1329 static void open_parent_same(gpointer data, guint action, GtkWidget *widget)
1331 g_return_if_fail(window_with_focus != NULL);
1333 change_to_parent(window_with_focus);
1336 static void new_window(gpointer data, guint action, GtkWidget *widget)
1338 g_return_if_fail(window_with_focus != NULL);
1340 if (o_unique_filer_windows)
1341 report_error("ROX-Filer", "You can't open a second view onto "
1342 "this directory because the `Unique Windows' option "
1343 "is turned on in the Options window.");
1344 else
1345 filer_opendir(window_with_focus->path, PANEL_NO);
1348 static void close_window(gpointer data, guint action, GtkWidget *widget)
1350 g_return_if_fail(window_with_focus != NULL);
1352 gtk_widget_destroy(window_with_focus->window);
1355 static void enter_path(gpointer data, guint action, GtkWidget *widget)
1357 g_return_if_fail(window_with_focus != NULL);
1359 minibuffer_show(window_with_focus);
1362 static void rox_help(gpointer data, guint action, GtkWidget *widget)
1364 g_return_if_fail(window_with_focus != NULL);
1366 filer_opendir(make_path(getenv("APP_DIR"), "Help")->str, PANEL_NO);
1369 static void open_as_dir(gpointer data, guint action, GtkWidget *widget)
1371 g_return_if_fail(window_with_focus != NULL);
1373 filer_opendir(window_with_focus->path, PANEL_NO);
1376 static void close_panel(gpointer data, guint action, GtkWidget *widget)
1378 g_return_if_fail(window_with_focus != NULL);
1380 gtk_widget_destroy(window_with_focus->window);