r277: Started adding support for setting run actions. Dialog box displays
[rox-filer/ma.git] / ROX-Filer / src / menu.c
blobc59f6a3e93d2215bb3d7ee9e58edd2703f3eb192
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"
50 #include "i18n.h"
52 #define C_ "<control>"
54 #define MENU_MARGIN 32
56 GtkAccelGroup *filer_keys;
57 GtkAccelGroup *panel_keys;
59 static GtkWidget *popup_menu = NULL; /* Currently open menu */
61 static gint updating_menu = 0; /* Non-zero => ignore activations */
63 /* Options */
64 static GtkWidget *xterm_here_entry;
65 static char *xterm_here_value;
67 /* Static prototypes */
69 static void position_menu(GtkMenu *menu, gint *x, gint *y, gpointer data);
70 static void menu_closed(GtkWidget *widget);
71 static void items_sensitive(gboolean state);
72 static char *load_xterm_here(char *data);
73 static void savebox_show(guchar *title, guchar *path, MaskedPixmap *image,
74 gboolean (*callback)(guchar *current, guchar *new));
75 static gint save_to_file(GtkSavebox *savebox, guchar *pathname);
77 /* Note that for these callbacks none of the arguments are used. */
78 static void large(gpointer data, guint action, GtkWidget *widget);
79 static void small(gpointer data, guint action, GtkWidget *widget);
80 static void full_info(gpointer data, guint action, GtkWidget *widget);
82 static void sort_name(gpointer data, guint action, GtkWidget *widget);
83 static void sort_type(gpointer data, guint action, GtkWidget *widget);
84 static void sort_size(gpointer data, guint action, GtkWidget *widget);
85 static void sort_date(gpointer data, guint action, GtkWidget *widget);
87 static void hidden(gpointer data, guint action, GtkWidget *widget);
88 static void refresh(gpointer data, guint action, GtkWidget *widget);
90 static void copy_item(gpointer data, guint action, GtkWidget *widget);
91 static void rename_item(gpointer data, guint action, GtkWidget *widget);
92 static void link_item(gpointer data, guint action, GtkWidget *widget);
93 static void run_action(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 N_("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 #undef N_
157 #define N_(x) x
159 static GtkItemFactoryEntry filer_menu_def[] = {
160 {N_("Display"), NULL, NULL, 0, "<Branch>"},
161 {">" N_("Large Icons"), NULL, large, 0, NULL},
162 {">" N_("Small Icons"), NULL, small, 0, NULL},
163 {">" N_("Full Info"), NULL, full_info, 0, NULL},
164 {">", NULL, NULL, 0, "<Separator>"},
165 {">" N_("Sort by Name"), NULL, sort_name, 0, NULL},
166 {">" N_("Sort by Type"), NULL, sort_type, 0, NULL},
167 {">" N_("Sort by Date"), NULL, sort_date, 0, NULL},
168 {">" N_("Sort by Size"), NULL, sort_size, 0, NULL},
169 {">", NULL, NULL, 0, "<Separator>"},
170 {">" N_("Show Hidden"), NULL, hidden, 0, "<ToggleItem>"},
171 {">" N_("Refresh"), NULL, refresh, 0, NULL},
172 {N_("File"), NULL, NULL, 0, "<Branch>"},
173 {">" N_("Copy..."), NULL, copy_item, 0, NULL},
174 {">" N_("Rename..."), NULL, rename_item, 0, NULL},
175 {">" N_("Link..."), NULL, link_item, 0, NULL},
176 {">" N_("Run Action..."), NULL, run_action, 0, NULL},
177 {">" N_("Shift Open"), NULL, open_file, 0, NULL},
178 {">" N_("Help"), NULL, help, 0, NULL},
179 {">" N_("Info"), NULL, show_file_info, 0, NULL},
180 {">" N_("Open VFS"), NULL, NULL, 0, "<Branch>"},
181 {">>" N_("Unzip"), NULL, open_vfs_uzip, 0, NULL},
182 {">>" N_("Untar"), NULL, open_vfs_utar, 0, NULL},
183 {">>" N_("RPM"), NULL, open_vfs_rpm, 0, NULL},
184 {">", NULL, NULL, 0, "<Separator>"},
185 {">" N_("Mount"), NULL, mount, 0, NULL},
186 {">" N_("Delete"), NULL, delete, 0, NULL},
187 {">" N_("Disk Usage"), NULL, usage, 0, NULL},
188 {">" N_("Permissions"), NULL, chmod_items, 0, NULL},
189 {">" N_("Find"), NULL, find, 0, NULL},
190 {N_("Select All"), NULL, select_all, 0, NULL},
191 {N_("Clear Selection"), NULL, clear_selection, 0, NULL},
192 {N_("Options..."), NULL, show_options, 0, NULL},
193 {N_("New Directory..."), NULL, new_directory, 0, NULL},
194 {N_("Xterm Here"), NULL, xterm_here, 0, NULL},
195 {N_("Window"), NULL, NULL, 0, "<Branch>"},
196 {">" N_("Parent, New Window"), NULL, open_parent, 0, NULL},
197 {">" N_("Parent, Same Window"), NULL, open_parent_same, 0, NULL},
198 {">" N_("New Window"), NULL, new_window, 0, NULL},
199 {">" N_("Close Window"), NULL, close_window, 0, NULL},
200 {">" N_("Enter Path"), NULL, enter_path, 0, NULL},
201 {">" N_("Shell Command"), NULL, shell_command, 0, NULL},
202 {">", NULL, NULL, 0, "<Separator>"},
203 {">" N_("Show ROX-Filer help"), NULL, rox_help, 0, NULL},
206 static GtkItemFactoryEntry panel_menu_def[] = {
207 {N_("Display"), NULL, NULL, 0, "<Branch>"},
208 {">" N_("Sort by Name"), NULL, sort_name, 0, NULL},
209 {">" N_("Sort by Type"), NULL, sort_type, 0, NULL},
210 {">" N_("Sort by Date"), NULL, sort_date, 0, NULL},
211 {">" N_("Sort by Size"), NULL, sort_size, 0, NULL},
212 {">", NULL, NULL, 0, "<Separator>"},
213 {">" N_("Show Hidden"), NULL, hidden, 0, "<ToggleItem>"},
214 {">" N_("Refresh"), NULL, refresh, 0, NULL},
215 {N_("Open Panel as Directory"), NULL, open_as_dir, 0, NULL},
216 {N_("Close Panel"), NULL, close_panel, 0, NULL},
217 {"", NULL, NULL, 0, "<Separator>"},
218 {N_("ROX-Filer Help"), NULL, rox_help, 0, NULL},
219 {N_("ROX-Filer Options..."), NULL, show_options, 0, NULL},
220 {"", NULL, NULL, 0, "<Separator>"},
221 {N_("Show Help"), NULL, help, 0, NULL},
222 {N_("Remove Item"), NULL, remove_link, 0, NULL},
225 typedef struct _FileStatus FileStatus;
227 /* This is for the 'file(1) says...' thing */
228 struct _FileStatus
230 int fd; /* FD to read from, -1 if closed */
231 int input; /* Input watcher tag if fd valid */
232 GtkLabel *label; /* Widget to output to */
233 gboolean start; /* No output yet */
236 #define GET_MENU_ITEM(var, menu) \
237 var = gtk_item_factory_get_widget(item_factory, "<" menu ">");
239 #define GET_SMENU_ITEM(var, menu, sub) \
240 do { \
241 tmp = g_strdup_printf("<" menu ">/%s", _(sub)); \
242 var = gtk_item_factory_get_widget(item_factory, tmp); \
243 g_free(tmp); \
244 } while (0)
246 #define GET_SSMENU_ITEM(var, menu, sub, subsub) \
247 do { \
248 tmp = g_strdup_printf("<" menu ">/%s/%s", _(sub), _(subsub)); \
249 var = gtk_item_factory_get_widget(item_factory, tmp); \
250 g_free(tmp); \
251 } while (0)
253 void menu_init()
255 GtkItemFactory *item_factory;
256 char *menurc;
257 GList *items;
258 guchar *tmp;
259 GtkWidget *item;
260 int n_entries;
261 GtkItemFactoryEntry *translated;
263 /* This call starts returning strange values after a while, so get
264 * the result here during init.
266 gdk_window_get_size(GDK_ROOT_PARENT(), &screen_width, &screen_height);
268 filer_keys = gtk_accel_group_new();
269 item_factory = gtk_item_factory_new(GTK_TYPE_MENU,
270 "<filer>",
271 filer_keys);
273 n_entries = sizeof(filer_menu_def) / sizeof(*filer_menu_def);
274 translated = translate_entries(filer_menu_def, n_entries);
275 gtk_item_factory_create_items (item_factory, n_entries,
276 translated, NULL);
277 free_translated_entries(translated, n_entries);
279 GET_MENU_ITEM(filer_menu, "filer");
280 GET_SMENU_ITEM(filer_file_menu, "filer", "File");
281 GET_SSMENU_ITEM(filer_vfs_menu, "filer", "File", "Open VFS");
282 GET_SSMENU_ITEM(filer_hidden_menu, "filer", "Display", "Show Hidden");
284 items = gtk_container_children(GTK_CONTAINER(filer_menu));
285 filer_file_item = GTK_BIN(g_list_nth(items, 1)->data)->child;
286 g_list_free(items);
288 GET_SSMENU_ITEM(item, "filer", "Window", "New Window");
289 filer_new_window = GTK_BIN(item)->child;
291 panel_keys = gtk_accel_group_new();
292 item_factory = gtk_item_factory_new(GTK_TYPE_MENU,
293 "<panel>",
294 panel_keys);
296 n_entries = sizeof(panel_menu_def) / sizeof(*panel_menu_def);
297 translated = translate_entries(panel_menu_def, n_entries);
298 gtk_item_factory_create_items (item_factory, n_entries,
299 translated, NULL);
300 free_translated_entries(translated, n_entries);
302 GET_MENU_ITEM(panel_menu, "panel");
303 GET_SSMENU_ITEM(panel_hidden_menu, "panel", "Display", "Show Hidden");
305 menurc = choices_find_path_load("menus", PROJECT);
306 if (menurc)
307 gtk_item_factory_parse_rc(menurc);
309 gtk_accel_group_lock(panel_keys);
311 gtk_signal_connect(GTK_OBJECT(filer_menu), "unmap_event",
312 GTK_SIGNAL_FUNC(menu_closed), NULL);
313 gtk_signal_connect(GTK_OBJECT(panel_menu), "unmap_event",
314 GTK_SIGNAL_FUNC(menu_closed), NULL);
315 gtk_signal_connect(GTK_OBJECT(filer_file_menu), "unmap_event",
316 GTK_SIGNAL_FUNC(menu_closed), NULL);
318 options_sections = g_slist_prepend(options_sections, &options);
319 xterm_here_value = g_strdup("xterm");
320 option_register("xterm_here", load_xterm_here);
322 savebox = gtk_savebox_new();
323 gtk_signal_connect_object(GTK_OBJECT(savebox), "save_to_file",
324 GTK_SIGNAL_FUNC(save_to_file), NULL);
325 gtk_signal_connect_object(GTK_OBJECT(savebox), "save_done",
326 GTK_SIGNAL_FUNC(gtk_widget_hide),
327 GTK_OBJECT(savebox));
330 /* Build up some option widgets to go in the options dialog, but don't
331 * fill them in yet.
333 static GtkWidget *create_options()
335 GtkWidget *table, *label;
337 table = gtk_table_new(2, 2, FALSE);
338 gtk_container_set_border_width(GTK_CONTAINER(table), 4);
340 label = gtk_label_new(
341 _("To set the keyboard short-cuts you simply open "
342 "the menu over a filer window, move the pointer over "
343 "the item you want to use and press a key. The key "
344 "will appear next to the menu item and you can just "
345 "press that key without opening the menu in future. "
346 "To save the current menu short-cuts for next time, "
347 "click the Save button at the bottom of this window."));
348 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
349 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 2, 0, 1);
351 label = gtk_label_new(_("'Xterm here' program:"));
352 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
353 xterm_here_entry = gtk_entry_new();
354 gtk_table_attach_defaults(GTK_TABLE(table), xterm_here_entry,
355 1, 2, 1, 2);
357 return table;
360 static char *load_xterm_here(char *data)
362 g_free(xterm_here_value);
363 xterm_here_value = g_strdup(data);
364 return NULL;
367 static void update_options()
369 gtk_entry_set_text(GTK_ENTRY(xterm_here_entry), xterm_here_value);
372 static void set_options()
374 g_free(xterm_here_value);
375 xterm_here_value = g_strdup(gtk_entry_get_text(
376 GTK_ENTRY(xterm_here_entry)));
379 static void save_options()
381 char *menurc;
383 menurc = choices_find_path_save("menus", PROJECT, TRUE);
384 if (menurc)
385 gtk_item_factory_dump_rc(menurc, NULL, TRUE);
387 option_write("xterm_here", xterm_here_value);
391 static void items_sensitive(gboolean state)
393 int n = 7;
394 GList *items, *item;
396 items = item = gtk_container_children(GTK_CONTAINER(filer_file_menu));
397 while (item && n--)
399 gtk_widget_set_sensitive(GTK_BIN(item->data)->child, state);
400 item = item->next;
402 g_list_free(items);
404 items = item = gtk_container_children(GTK_CONTAINER(filer_vfs_menu));
405 while (item)
407 gtk_widget_set_sensitive(GTK_BIN(item->data)->child, state);
408 item = item->next;
410 g_list_free(items);
413 static void position_menu(GtkMenu *menu, gint *x, gint *y, gpointer data)
415 int *pos = (int *) data;
416 GtkRequisition requisition;
418 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
420 if (pos[0] == -1)
421 *x = screen_width - MENU_MARGIN - requisition.width;
422 else if (pos[0] == -2)
423 *x = MENU_MARGIN;
424 else
425 *x = pos[0] - (requisition.width >> 2);
427 if (pos[1] == -1)
428 *y = screen_height - MENU_MARGIN - requisition.height;
429 else if (pos[1] == -2)
430 *y = MENU_MARGIN;
431 else
432 *y = pos[1] - (requisition.height >> 2);
434 *x = CLAMP(*x, 0, screen_width - requisition.width);
435 *y = CLAMP(*y, 0, screen_height - requisition.height);
438 void show_filer_menu(FilerWindow *filer_window, GdkEventButton *event,
439 int item)
441 DirItem *file_item;
442 int pos[2];
444 updating_menu++;
446 pos[0] = event->x_root;
447 pos[1] = event->y_root;
449 window_with_focus = filer_window;
451 switch (filer_window->panel_type)
453 case PANEL_TOP:
454 pos[1] = -2;
455 break;
456 case PANEL_BOTTOM:
457 pos[1] = -1;
458 break;
459 default:
460 break;
463 if (filer_window->panel_type)
464 collection_clear_selection(filer_window->collection); /* ??? */
466 if (filer_window->collection->number_selected == 0 && item >= 0)
468 collection_select_item(filer_window->collection, item);
469 filer_window->temp_item_selected = TRUE;
471 else
472 filer_window->temp_item_selected = FALSE;
474 if (filer_window->panel_type)
476 gtk_check_menu_item_set_active(
477 GTK_CHECK_MENU_ITEM(panel_hidden_menu),
478 filer_window->show_hidden);
480 else
482 GtkWidget *file_label, *file_menu;
483 Collection *collection = filer_window->collection;
484 GString *buffer;
486 file_label = filer_file_item;
487 file_menu = filer_file_menu;
488 gtk_check_menu_item_set_active(
489 GTK_CHECK_MENU_ITEM(filer_hidden_menu),
490 filer_window->show_hidden);
491 buffer = g_string_new(NULL);
492 switch (collection->number_selected)
494 case 0:
495 g_string_assign(buffer, _("Next Click"));
496 items_sensitive(TRUE);
497 break;
498 case 1:
499 items_sensitive(TRUE);
500 file_item = selected_item(
501 filer_window->collection);
502 g_string_sprintf(buffer, "%s '%s'",
503 basetype_name(file_item),
504 file_item->leafname);
505 break;
506 default:
507 items_sensitive(FALSE);
508 g_string_sprintf(buffer, _("%d items"),
509 collection->number_selected);
510 break;
512 gtk_label_set_text(GTK_LABEL(file_label), buffer->str);
513 g_string_free(buffer, TRUE);
517 gtk_widget_set_sensitive(filer_new_window, !o_unique_filer_windows);
519 if (filer_window->panel_type)
520 popup_menu = panel_menu;
521 else
522 popup_menu = (event->state & GDK_CONTROL_MASK)
523 ? filer_file_menu
524 : filer_menu;
526 updating_menu--;
528 gtk_menu_popup(GTK_MENU(popup_menu), NULL, NULL, position_menu,
529 (gpointer) pos, event->button, event->time);
532 static void menu_closed(GtkWidget *widget)
534 if (window_with_focus == NULL || widget != popup_menu)
535 return; /* Close panel item chosen? */
537 if (window_with_focus->temp_item_selected)
539 collection_clear_selection(window_with_focus->collection);
540 window_with_focus->temp_item_selected = FALSE;
544 void target_callback(Collection *collection, gint item, gpointer real_fn)
546 g_return_if_fail(window_with_focus != NULL);
547 g_return_if_fail(window_with_focus->collection == collection);
548 g_return_if_fail(real_fn != NULL);
550 collection_wink_item(collection, item);
551 collection_clear_selection(collection);
552 collection_select_item(collection, item);
553 ((CollectionTargetFunc)real_fn)(NULL, 0, collection);
554 if (item < collection->number_of_items)
555 collection_unselect_item(collection, item);
558 /* Actions */
560 static void large(gpointer data, guint action, GtkWidget *widget)
562 g_return_if_fail(window_with_focus != NULL);
564 filer_style_set(window_with_focus, LARGE_ICONS);
567 static void small(gpointer data, guint action, GtkWidget *widget)
569 g_return_if_fail(window_with_focus != NULL);
571 filer_style_set(window_with_focus, SMALL_ICONS);
574 static void full_info(gpointer data, guint action, GtkWidget *widget)
576 g_return_if_fail(window_with_focus != NULL);
578 filer_style_set(window_with_focus, FULL_INFO);
581 static void sort_name(gpointer data, guint action, GtkWidget *widget)
583 g_return_if_fail(window_with_focus != NULL);
585 filer_set_sort_fn(window_with_focus, sort_by_name);
588 static void sort_type(gpointer data, guint action, GtkWidget *widget)
590 g_return_if_fail(window_with_focus != NULL);
592 filer_set_sort_fn(window_with_focus, sort_by_type);
595 static void sort_date(gpointer data, guint action, GtkWidget *widget)
597 g_return_if_fail(window_with_focus != NULL);
599 filer_set_sort_fn(window_with_focus, sort_by_date);
602 static void sort_size(gpointer data, guint action, GtkWidget *widget)
604 g_return_if_fail(window_with_focus != NULL);
606 filer_set_sort_fn(window_with_focus, sort_by_size);
609 static void hidden(gpointer data, guint action, GtkWidget *widget)
611 if (updating_menu)
612 return;
614 g_return_if_fail(window_with_focus != NULL);
616 filer_set_hidden(window_with_focus, !window_with_focus->show_hidden);
619 static void refresh(gpointer data, guint action, GtkWidget *widget)
621 g_return_if_fail(window_with_focus != NULL);
623 full_refresh();
624 filer_update_dir(window_with_focus, TRUE);
627 static void mount(gpointer data, guint action, GtkWidget *widget)
629 g_return_if_fail(window_with_focus != NULL);
631 if (window_with_focus->collection->number_selected == 0)
632 collection_target(window_with_focus->collection,
633 target_callback, mount);
634 else
635 action_mount(window_with_focus, NULL);
638 static void delete(gpointer data, guint action, GtkWidget *widget)
640 g_return_if_fail(window_with_focus != NULL);
642 if (window_with_focus->collection->number_selected == 0)
643 collection_target(window_with_focus->collection,
644 target_callback, delete);
645 else
646 action_delete(window_with_focus);
649 static void remove_link(gpointer data, guint action, GtkWidget *widget)
651 g_return_if_fail(window_with_focus != NULL);
653 if (window_with_focus->collection->number_selected > 1)
655 report_error(PROJECT,
656 _("You can only remove one link at a time"));
657 return;
659 else if (window_with_focus->collection->number_selected == 0)
660 collection_target(window_with_focus->collection,
661 target_callback, remove_link);
662 else
664 struct stat info;
665 DirItem *item;
666 guchar *path;
668 item = selected_item(window_with_focus->collection);
670 path = make_path(window_with_focus->path, item->leafname)->str;
671 if (lstat(path, &info))
672 report_error(PROJECT, g_strerror(errno));
673 else if (!S_ISLNK(info.st_mode))
674 report_error(PROJECT,
675 _("You can only remove symbolic links this way - "
676 "try the 'Open Panel as Directory' item."));
677 else if (unlink(path))
678 report_error(PROJECT, g_strerror(errno));
679 else
680 filer_update_dir(window_with_focus, TRUE);
684 static void usage(gpointer data, guint action, GtkWidget *widget)
686 g_return_if_fail(window_with_focus != NULL);
688 if (window_with_focus->collection->number_selected == 0)
689 collection_target(window_with_focus->collection,
690 target_callback, usage);
691 else
692 action_usage(window_with_focus);
695 static void chmod_items(gpointer data, guint action, GtkWidget *widget)
697 g_return_if_fail(window_with_focus != NULL);
699 if (window_with_focus->collection->number_selected == 0)
700 collection_target(window_with_focus->collection,
701 target_callback, chmod_items);
702 else
703 action_chmod(window_with_focus);
706 static void find(gpointer data, guint action, GtkWidget *widget)
708 g_return_if_fail(window_with_focus != NULL);
710 if (window_with_focus->collection->number_selected == 0)
711 collection_target(window_with_focus->collection,
712 target_callback, find);
713 else
714 action_find(window_with_focus);
717 /* This pops up our savebox widget, cancelling any currently open one,
718 * and allows the user to pick a new path for it.
719 * Once the new path has been picked, the callback will be called with
720 * both the current and new paths.
722 static void savebox_show(guchar *title, guchar *path, MaskedPixmap *image,
723 gboolean (*callback)(guchar *current, guchar *new))
725 if (GTK_WIDGET_VISIBLE(savebox))
726 gtk_widget_hide(savebox);
728 if (current_path)
729 g_free(current_path);
730 current_path = g_strdup(path);
731 current_savebox_callback = callback;
733 gtk_window_set_title(GTK_WINDOW(savebox), title);
734 gtk_savebox_set_pathname(GTK_SAVEBOX(savebox), current_path);
735 gtk_savebox_set_icon(GTK_SAVEBOX(savebox), image->pixmap, image->mask);
737 gtk_widget_grab_focus(GTK_SAVEBOX(savebox)->entry);
738 gtk_widget_show(savebox);
741 static gint save_to_file(GtkSavebox *savebox, guchar *pathname)
743 g_return_val_if_fail(current_savebox_callback != NULL,
744 GTK_XDS_SAVE_ERROR);
746 return current_savebox_callback(current_path, pathname)
747 ? GTK_XDS_SAVED : GTK_XDS_SAVE_ERROR;
750 static gboolean copy_cb(guchar *current, guchar *new)
752 char *new_dir, *leaf;
753 GSList *local_paths;
755 if (new[0] != '/')
757 report_error(PROJECT, _("New pathname is not absolute"));
758 return FALSE;
761 if (new[strlen(new) - 1] == '/')
763 new_dir = g_strdup(new);
764 leaf = NULL;
766 else
768 guchar *slash;
770 slash = strrchr(new, '/');
771 new_dir = g_strndup(new, slash - new);
772 leaf = slash + 1;
775 local_paths = g_slist_append(NULL, current);
776 action_copy(local_paths, new_dir, leaf);
777 g_slist_free(local_paths);
779 g_free(new_dir);
781 return TRUE;
784 #define SHOW_SAVEBOX(title, callback) \
786 DirItem *item; \
787 guchar *path; \
788 item = selected_item(collection); \
789 path = make_path(window_with_focus->path, item->leafname)->str; \
790 savebox_show(title, path, item->image, callback); \
793 static void copy_item(gpointer data, guint action, GtkWidget *widget)
795 Collection *collection;
797 g_return_if_fail(window_with_focus != NULL);
799 collection = window_with_focus->collection;
800 if (collection->number_selected > 1)
802 report_error(PROJECT, _("You cannot do this to more than "
803 "one item at a time"));
804 return;
806 else if (collection->number_selected != 1)
807 collection_target(collection, target_callback, copy_item);
808 else
809 SHOW_SAVEBOX(_("Copy"), copy_cb);
812 static gboolean rename_cb(guchar *current, guchar *new)
814 if (rename(current, new))
816 report_error("ROX-Filer: rename()", g_strerror(errno));
817 return FALSE;
819 return TRUE;
822 static void rename_item(gpointer data, guint action, GtkWidget *widget)
824 Collection *collection;
826 g_return_if_fail(window_with_focus != NULL);
828 collection = window_with_focus->collection;
829 if (collection->number_selected > 1)
831 report_error(PROJECT, _("You cannot do this to more than "
832 "one item at a time"));
833 return;
835 else if (collection->number_selected != 1)
836 collection_target(collection, target_callback, rename_item);
837 else
838 SHOW_SAVEBOX(_("Rename"), rename_cb);
841 static gboolean link_cb(guchar *initial, guchar *path)
843 if (symlink(initial, path))
845 report_error("ROX-Filer: symlink()", g_strerror(errno));
846 return FALSE;
848 return TRUE;
851 static void link_item(gpointer data, guint action, GtkWidget *widget)
853 Collection *collection;
855 g_return_if_fail(window_with_focus != NULL);
857 collection = window_with_focus->collection;
858 if (collection->number_selected > 1)
860 report_error(PROJECT, _("You cannot do this to more than "
861 "one item at a time"));
862 return;
864 else if (collection->number_selected != 1)
865 collection_target(collection, target_callback, link_item);
866 else
867 SHOW_SAVEBOX(_("Symlink"), link_cb);
870 static void run_action(gpointer data, guint action, GtkWidget *widget)
872 Collection *collection;
874 g_return_if_fail(window_with_focus != NULL);
876 collection = window_with_focus->collection;
877 if (collection->number_selected > 1)
879 report_error(PROJECT, _("You cannot do this to more than "
880 "one item at a time"));
881 return;
883 else if (collection->number_selected != 1)
884 collection_target(collection, target_callback, run_action);
885 else
887 DirItem *item;
889 item = selected_item(collection);
890 g_return_if_fail(item->mime_type != NULL);
892 show_set_run_action(item->mime_type);
896 static void open_file(gpointer data, guint action, GtkWidget *widget)
898 Collection *collection;
900 g_return_if_fail(window_with_focus != NULL);
902 collection = window_with_focus->collection;
903 if (collection->number_selected > 1)
905 report_error(PROJECT, _("You cannot do this to more than "
906 "one item at a time"));
907 return;
909 else if (collection->number_selected != 1)
910 collection_target(collection, target_callback, open_file);
911 else
912 filer_openitem(window_with_focus,
913 selected_item_number(collection),
914 OPEN_SAME_WINDOW | OPEN_SHIFT);
917 /* Got some data from file(1) - stick it in the window. */
918 static void add_file_output(FileStatus *fs,
919 gint source, GdkInputCondition condition)
921 char buffer[20];
922 char *str;
923 int got;
925 got = read(source, buffer, sizeof(buffer) - 1);
926 if (got <= 0)
928 int err = errno;
929 gtk_input_remove(fs->input);
930 close(source);
931 fs->fd = -1;
932 if (got < 0)
933 delayed_error(_("ROX-Filer: file(1) says..."),
934 g_strerror(err));
935 return;
937 buffer[got] = '\0';
939 if (fs->start)
941 str = "";
942 fs->start = FALSE;
944 else
945 gtk_label_get(fs->label, &str);
947 str = g_strconcat(str, buffer, NULL);
948 gtk_label_set_text(fs->label, str);
949 g_free(str);
952 static void file_info_destroyed(GtkWidget *widget, FileStatus *fs)
954 if (fs->fd != -1)
956 gtk_input_remove(fs->input);
957 close(fs->fd);
960 g_free(fs);
963 /* g_free() the result */
964 guchar *pretty_type(DirItem *file, guchar *path)
966 if (file->mime_type)
967 return g_strconcat(file->mime_type->media_type, "/",
968 file->mime_type->subtype, NULL);
970 if (file->flags & ITEM_FLAG_APPDIR)
971 return g_strdup(_("ROX application"));
973 if (file->flags & ITEM_FLAG_SYMLINK)
975 char p[MAXPATHLEN + 1];
976 int got;
977 got = readlink(path, p, MAXPATHLEN);
978 if (got > 0 && got <= MAXPATHLEN)
980 p[got] = '\0';
981 return g_strconcat(_("Symbolic link to "), p, NULL);
984 return g_strdup(_("Symbolic link"));
987 if (file->flags & ITEM_FLAG_MOUNT_POINT)
989 MountPoint *mp;
990 if ((file->flags & ITEM_FLAG_MOUNTED) &&
991 (mp = g_hash_table_lookup(mtab_mounts, path)))
992 return g_strconcat(_("Mount point for "),
993 mp->name, NULL);
995 return g_strdup(_("Mount point"));
998 return g_strdup("-");
1001 #define LABEL(text, row) \
1002 label = gtk_label_new(text); \
1003 gtk_misc_set_alignment(GTK_MISC(label), 1, .5); \
1004 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_RIGHT); \
1005 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, row, row + 1);
1007 #define VALUE(label, row) \
1008 gtk_misc_set_alignment(GTK_MISC(label), 0, .5); \
1009 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, row, row + 1);
1011 static void show_file_info(gpointer data, guint action, GtkWidget *widget)
1013 GtkWidget *window, *table, *label, *button, *frame, *value;
1014 GtkWidget *file_label;
1015 GString *gstring;
1016 int file_data[2];
1017 guchar *path, *tmp;
1018 char *argv[] = {"file", "-b", NULL, NULL};
1019 Collection *collection;
1020 DirItem *file;
1021 struct stat info;
1022 FileStatus *fs = NULL;
1023 guint perm;
1025 g_return_if_fail(window_with_focus != NULL);
1027 collection = window_with_focus->collection;
1028 if (collection->number_selected > 1)
1030 report_error(PROJECT, _("You cannot do this to more than "
1031 "one item at a time"));
1032 return;
1034 else if (collection->number_selected != 1)
1036 collection_target(collection, target_callback, show_file_info);
1037 return;
1039 file = selected_item(collection);
1040 path = make_path(window_with_focus->path,
1041 file->leafname)->str;
1042 if (lstat(path, &info))
1044 delayed_error(PROJECT, g_strerror(errno));
1045 return;
1048 gstring = g_string_new(NULL);
1050 window = gtk_window_new(GTK_WINDOW_DIALOG);
1051 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_MOUSE);
1052 gtk_container_set_border_width(GTK_CONTAINER(window), 4);
1053 gtk_window_set_title(GTK_WINDOW(window), path);
1055 table = gtk_table_new(10, 2, FALSE);
1056 gtk_container_add(GTK_CONTAINER(window), table);
1057 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
1058 gtk_table_set_col_spacings(GTK_TABLE(table), 4);
1060 value = gtk_label_new(file->leafname);
1061 LABEL(_("Name:"), 0);
1062 VALUE(value, 0);
1064 g_string_sprintf(gstring, "%s, %s", user_name(info.st_uid),
1065 group_name(info.st_gid));
1066 value = gtk_label_new(gstring->str);
1067 LABEL(_("Owner, Group:"), 1);
1068 VALUE(value, 1);
1070 if (info.st_size >= PRETTY_SIZE_LIMIT)
1072 g_string_sprintf(gstring, "%s (%ld %s)",
1073 format_size((unsigned long) info.st_size),
1074 (unsigned long) info.st_size, _("bytes"));
1076 else
1078 g_string_assign(gstring,
1079 format_size((unsigned long) info.st_size));
1081 value = gtk_label_new(gstring->str);
1082 LABEL(_("Size:"), 2);
1083 VALUE(value, 2);
1085 value = gtk_label_new(pretty_time(&info.st_ctime));
1086 LABEL(_("Change time:"), 3);
1087 VALUE(value, 3);
1089 value = gtk_label_new(pretty_time(&info.st_mtime));
1090 LABEL(_("Modify time:"), 4);
1091 VALUE(value, 4);
1093 value = gtk_label_new(pretty_time(&info.st_atime));
1094 LABEL(_("Access time:"), 5);
1095 VALUE(value, 5);
1097 value = gtk_label_new(pretty_permissions(info.st_mode));
1098 perm = applicable(info.st_uid, info.st_gid);
1099 gtk_label_set_pattern(GTK_LABEL(value),
1100 perm == 0 ? "___ " :
1101 perm == 1 ? " ___ " :
1102 " ___");
1103 gtk_widget_set_style(value, fixed_style);
1104 LABEL(_("Permissions:"), 6);
1105 VALUE(value, 6);
1107 tmp = pretty_type(file, path);
1108 value = gtk_label_new(tmp);
1109 g_free(tmp);
1110 LABEL(_("Type:"), 7);
1111 VALUE(value, 7);
1113 frame = gtk_frame_new(_("file(1) says..."));
1114 gtk_table_attach_defaults(GTK_TABLE(table), frame, 0, 2, 8, 9);
1115 file_label = gtk_label_new(_("<nothing yet>"));
1116 gtk_misc_set_padding(GTK_MISC(file_label), 4, 4);
1117 gtk_label_set_line_wrap(GTK_LABEL(file_label), TRUE);
1118 gtk_container_add(GTK_CONTAINER(frame), file_label);
1120 button = gtk_button_new_with_label(_("OK"));
1121 gtk_window_set_focus(GTK_WINDOW(window), button);
1122 gtk_table_attach(GTK_TABLE(table), button, 0, 2, 10, 11,
1123 GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 40, 4);
1124 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1125 gtk_widget_destroy, GTK_OBJECT(window));
1127 gtk_widget_show_all(window);
1128 gdk_flush();
1130 if (pipe(file_data))
1132 g_string_sprintf(gstring, "pipe(): %s", g_strerror(errno));
1133 g_string_free(gstring, TRUE);
1134 return;
1136 switch (fork())
1138 case -1:
1139 g_string_sprintf(gstring, "fork(): %s",
1140 g_strerror(errno));
1141 gtk_label_set_text(GTK_LABEL(file_label), gstring->str);
1142 g_string_free(gstring, TRUE);
1143 close(file_data[0]);
1144 close(file_data[1]);
1145 break;
1146 case 0:
1147 /* We are the child */
1148 close(file_data[0]);
1149 dup2(file_data[1], STDOUT_FILENO);
1150 dup2(file_data[1], STDERR_FILENO);
1151 #ifdef FILE_B_FLAG
1152 argv[2] = path;
1153 #else
1154 argv[1] = file->leafname;
1155 chdir(window_with_focus->path);
1156 #endif
1157 if (execvp(argv[0], argv))
1158 fprintf(stderr, "execvp() error: %s\n",
1159 g_strerror(errno));
1160 _exit(0);
1161 default:
1162 /* We are the parent */
1163 close(file_data[1]);
1164 fs = g_new(FileStatus, 1);
1165 fs->label = GTK_LABEL(file_label);
1166 fs->fd = file_data[0];
1167 fs->start = TRUE;
1168 fs->input = gdk_input_add(fs->fd, GDK_INPUT_READ,
1169 (GdkInputFunction) add_file_output, fs);
1170 gtk_signal_connect(GTK_OBJECT(window), "destroy",
1171 GTK_SIGNAL_FUNC(file_info_destroyed), fs);
1172 g_string_free(gstring, TRUE);
1173 break;
1177 static void app_show_help(char *path)
1179 char *help_dir;
1180 struct stat info;
1182 help_dir = g_strconcat(path, "/Help", NULL);
1184 if (mc_stat(help_dir, &info))
1185 delayed_error(_("Application"),
1186 _("This is an application directory - you can "
1187 "run it as a program, or open it (hold down "
1188 "Shift while you open it). Most applications provide "
1189 "their own help here, but this one doesn't."));
1190 else
1191 filer_opendir(help_dir, PANEL_NO);
1194 static void help(gpointer data, guint action, GtkWidget *widget)
1196 Collection *collection;
1197 DirItem *item;
1199 g_return_if_fail(window_with_focus != NULL);
1201 collection = window_with_focus->collection;
1202 if (collection->number_selected > 1)
1204 report_error(PROJECT, _("You cannot do this to more than "
1205 "one item at a time"));
1206 return;
1208 else if (collection->number_selected != 1)
1210 collection_target(collection, target_callback, help);
1211 return;
1213 item = selected_item(collection);
1214 switch (item->base_type)
1216 case TYPE_FILE:
1217 if (item->flags & ITEM_FLAG_EXEC_FILE)
1218 delayed_error(_("Executable file"),
1219 _("This is a file with an eXecute bit "
1220 "set - it can be run as a program."));
1221 else
1222 delayed_error(_("File"), _(
1223 "This is a data file. Try using the "
1224 "Info menu item to find out more..."));
1225 break;
1226 case TYPE_DIRECTORY:
1227 if (item->flags & ITEM_FLAG_APPDIR)
1228 app_show_help(
1229 make_path(window_with_focus->path,
1230 item->leafname)->str);
1231 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
1232 delayed_error(_("Mount point"), _(
1233 "A mount point is a directory which another "
1234 "filing system can be mounted on. Everything "
1235 "on the mounted filesystem then appears to be "
1236 "inside the directory."));
1237 else
1238 delayed_error(_("Directory"), _(
1239 "This is a directory. It contains an index to "
1240 "other items - open it to see the list."));
1241 break;
1242 case TYPE_CHAR_DEVICE:
1243 case TYPE_BLOCK_DEVICE:
1244 delayed_error(_("Device file"), _(
1245 "Device files allow you to read from or write "
1246 "to a device driver as though it was an "
1247 "ordinary file."));
1248 break;
1249 case TYPE_PIPE:
1250 delayed_error(_("Named pipe"), _(
1251 "Pipes allow different programs to "
1252 "communicate. One program writes data to the "
1253 "pipe while another one reads it out again."));
1254 break;
1255 case TYPE_SOCKET:
1256 delayed_error(_("Socket"), _(
1257 "Sockets allow processes to communicate."));
1258 break;
1259 default:
1260 delayed_error(_("Unknown type"), _(
1261 "I couldn't find out what kind of file this "
1262 "is. Maybe it doesn't exist anymore or you "
1263 "don't have search permission on the directory "
1264 "it's in?"));
1265 break;
1269 #define OPEN_VFS(fs) \
1270 static void open_vfs_ ## fs (gpointer data, guint action, GtkWidget *widget) \
1272 Collection *collection; \
1274 g_return_if_fail(window_with_focus != NULL); \
1276 collection = window_with_focus->collection; \
1277 if (collection->number_selected < 1) \
1278 collection_target(collection, target_callback, \
1279 open_vfs_ ## fs); \
1280 else \
1281 real_vfs_open(#fs); \
1284 static void real_vfs_open(char *fs)
1286 gchar *path;
1287 DirItem *item;
1289 if (window_with_focus->collection->number_selected != 1)
1291 report_error(PROJECT, _("You must select a single file "
1292 "to open as a Virtual File System"));
1293 return;
1296 item = selected_item(window_with_focus->collection);
1298 path = g_strconcat(window_with_focus->path,
1299 "/",
1300 item->leafname,
1301 "#", fs, NULL);
1303 filer_change_to(window_with_focus, path, NULL);
1304 g_free(path);
1307 OPEN_VFS(rpm)
1308 OPEN_VFS(utar)
1309 OPEN_VFS(uzip)
1311 static void select_all(gpointer data, guint action, GtkWidget *widget)
1313 g_return_if_fail(window_with_focus != NULL);
1315 collection_select_all(window_with_focus->collection);
1316 window_with_focus->temp_item_selected = FALSE;
1319 static void clear_selection(gpointer data, guint action, GtkWidget *widget)
1321 g_return_if_fail(window_with_focus != NULL);
1323 collection_clear_selection(window_with_focus->collection);
1324 window_with_focus->temp_item_selected = FALSE;
1327 static void show_options(gpointer data, guint action, GtkWidget *widget)
1329 g_return_if_fail(window_with_focus != NULL);
1331 options_show(window_with_focus);
1334 static gboolean new_directory_cb(guchar *initial, guchar *path)
1336 if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO))
1338 report_error("mkdir", g_strerror(errno));
1339 return FALSE;
1341 return TRUE;
1344 static void new_directory(gpointer data, guint action, GtkWidget *widget)
1346 g_return_if_fail(window_with_focus != NULL);
1348 savebox_show(_("New Directory"),
1349 make_path(window_with_focus->path, _("NewDir"))->str,
1350 default_pixmap[TYPE_DIRECTORY],
1351 new_directory_cb);
1354 static void xterm_here(gpointer data, guint action, GtkWidget *widget)
1356 char *argv[] = {NULL, NULL};
1358 argv[0] = xterm_here_value;
1360 g_return_if_fail(window_with_focus != NULL);
1362 if (!spawn_full(argv, window_with_focus->path))
1363 report_error(PROJECT, "Failed to fork() child "
1364 "process");
1367 static void open_parent(gpointer data, guint action, GtkWidget *widget)
1369 g_return_if_fail(window_with_focus != NULL);
1371 filer_open_parent(window_with_focus);
1374 static void open_parent_same(gpointer data, guint action, GtkWidget *widget)
1376 g_return_if_fail(window_with_focus != NULL);
1378 change_to_parent(window_with_focus);
1381 static void new_window(gpointer data, guint action, GtkWidget *widget)
1383 g_return_if_fail(window_with_focus != NULL);
1385 if (o_unique_filer_windows)
1386 report_error(PROJECT, _("You can't open a second view onto "
1387 "this directory because the `Unique Windows' option "
1388 "is turned on in the Options window."));
1389 else
1390 filer_opendir(window_with_focus->path, PANEL_NO);
1393 static void close_window(gpointer data, guint action, GtkWidget *widget)
1395 g_return_if_fail(window_with_focus != NULL);
1397 gtk_widget_destroy(window_with_focus->window);
1400 static void enter_path(gpointer data, guint action, GtkWidget *widget)
1402 g_return_if_fail(window_with_focus != NULL);
1404 minibuffer_show(window_with_focus, MINI_PATH);
1407 static void shell_command(gpointer data, guint action, GtkWidget *widget)
1409 g_return_if_fail(window_with_focus != NULL);
1411 minibuffer_show(window_with_focus, MINI_SHELL);
1414 static void rox_help(gpointer data, guint action, GtkWidget *widget)
1416 g_return_if_fail(window_with_focus != NULL);
1418 filer_opendir(make_path(getenv("APP_DIR"), "Help")->str, PANEL_NO);
1421 static void open_as_dir(gpointer data, guint action, GtkWidget *widget)
1423 g_return_if_fail(window_with_focus != NULL);
1425 filer_opendir(window_with_focus->path, PANEL_NO);
1428 static void close_panel(gpointer data, guint action, GtkWidget *widget)
1430 g_return_if_fail(window_with_focus != NULL);
1432 gtk_widget_destroy(window_with_focus->window);