r217: Merged the MIME-Type and Special fields in the info window into a single
[rox-filer.git] / ROX-Filer / src / menu.c
blob4b0485a4377fb6705193e13f98684c5dc0479d98
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 "savebox.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);
73 /* Note that for these callbacks none of the arguments are used. */
74 static void not_yet(gpointer data, guint action, GtkWidget *widget);
76 static void large(gpointer data, guint action, GtkWidget *widget);
77 static void small(gpointer data, guint action, GtkWidget *widget);
78 static void full_info(gpointer data, guint action, GtkWidget *widget);
80 static void sort_name(gpointer data, guint action, GtkWidget *widget);
81 static void sort_type(gpointer data, guint action, GtkWidget *widget);
82 static void sort_size(gpointer data, guint action, GtkWidget *widget);
83 static void sort_date(gpointer data, guint action, GtkWidget *widget);
85 static void hidden(gpointer data, guint action, GtkWidget *widget);
86 static void refresh(gpointer data, guint action, GtkWidget *widget);
88 static void copy_item(gpointer data, guint action, GtkWidget *widget);
89 static void rename_item(gpointer data, guint action, GtkWidget *widget);
90 static void link_item(gpointer data, guint action, GtkWidget *widget);
91 static void open_file(gpointer data, guint action, GtkWidget *widget);
92 static void help(gpointer data, guint action, GtkWidget *widget);
93 static void show_file_info(gpointer data, guint action, GtkWidget *widget);
94 static void mount(gpointer data, guint action, GtkWidget *widget);
95 static void delete(gpointer data, guint action, GtkWidget *widget);
96 static void remove_link(gpointer data, guint action, GtkWidget *widget);
97 static void usage(gpointer data, guint action, GtkWidget *widget);
98 static void chmod_items(gpointer data, guint action, GtkWidget *widget);
100 static void open_vfs_rpm(gpointer data, guint action, GtkWidget *widget);
101 static void open_vfs_utar(gpointer data, guint action, GtkWidget *widget);
102 static void open_vfs_uzip(gpointer data, guint action, GtkWidget *widget);
104 static void select_all(gpointer data, guint action, GtkWidget *widget);
105 static void clear_selection(gpointer data, guint action, GtkWidget *widget);
106 static void show_options(gpointer data, guint action, GtkWidget *widget);
107 static void new_directory(gpointer data, guint action, GtkWidget *widget);
108 static void xterm_here(gpointer data, guint action, GtkWidget *widget);
110 static void open_parent_same(gpointer data, guint action, GtkWidget *widget);
111 static void open_parent(gpointer data, guint action, GtkWidget *widget);
112 static void new_window(gpointer data, guint action, GtkWidget *widget);
113 static void close_window(gpointer data, guint action, GtkWidget *widget);
114 static void enter_path(gpointer data, guint action, GtkWidget *widget);
115 static void rox_help(gpointer data, guint action, GtkWidget *widget);
117 static void open_as_dir(gpointer data, guint action, GtkWidget *widget);
118 static void close_panel(gpointer data, guint action, GtkWidget *widget);
120 static GtkWidget *create_options();
121 static void update_options();
122 static void set_options();
123 static void save_options();
125 static OptionsSection options =
127 "Menu options",
128 create_options,
129 update_options,
130 set_options,
131 save_options
135 static GtkWidget *filer_menu; /* The popup filer menu */
136 static GtkWidget *filer_file_item; /* The File '' label */
137 static GtkWidget *filer_file_menu; /* The File '' menu */
138 static GtkWidget *filer_vfs_menu; /* The Open VFS menu */
139 static GtkWidget *filer_hidden_menu; /* The Show Hidden item */
140 static GtkWidget *filer_new_window; /* The New Window item */
141 static GtkWidget *panel_menu; /* The popup panel menu */
142 static GtkWidget *panel_hidden_menu; /* The Show Hidden item */
144 static gint screen_width, screen_height;
146 static GtkItemFactoryEntry filer_menu_def[] = {
147 {"/Display", NULL, NULL, 0, "<Branch>"},
148 {"/Display/Large Icons", NULL, large, 0, NULL},
149 {"/Display/Small Icons", NULL, small, 0, NULL},
150 {"/Display/Full Info", NULL, full_info, 0, NULL},
151 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
152 {"/Display/Sort by Name", NULL, sort_name, 0, NULL},
153 {"/Display/Sort by Type", NULL, sort_type, 0, NULL},
154 {"/Display/Sort by Date", NULL, sort_date, 0, NULL},
155 {"/Display/Sort by Size", NULL, sort_size, 0, NULL},
156 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
157 {"/Display/Show Hidden", C_"H", hidden, 0, "<ToggleItem>"},
158 {"/Display/Refresh", C_"L", refresh, 0, NULL},
159 {"/File", NULL, NULL, 0, "<Branch>"},
160 {"/File/Copy...", NULL, copy_item, 0, NULL},
161 {"/File/Rename...", NULL, rename_item, 0, NULL},
162 {"/File/Link...", NULL, link_item, 0, NULL},
163 {"/File/Shift Open", NULL, open_file, 0, NULL},
164 {"/File/Help", "F1", help, 0, NULL},
165 {"/File/Info", "I", show_file_info, 0, NULL},
166 {"/File/Open VFS", NULL, NULL, 0, "<Branch>"},
167 {"/File/Open VFS/Unzip", NULL, open_vfs_uzip, 0, NULL},
168 {"/File/Open VFS/Untar", NULL, open_vfs_utar, 0, NULL},
169 {"/File/Open VFS/RPM", NULL, open_vfs_rpm, 0, NULL},
170 {"/File/Separator", NULL, NULL, 0, "<Separator>"},
171 {"/File/Mount", "M", mount, 0, NULL},
172 {"/File/Delete", C_"X", delete, 0, NULL},
173 {"/File/Disk Usage", "U", usage, 0, NULL},
174 {"/File/Permissions", NULL, chmod_items, 0, NULL},
175 {"/File/Touch", NULL, not_yet, 0, NULL},
176 {"/File/Find", NULL, not_yet, 0, NULL},
177 {"/Select All", C_"A", select_all, 0, NULL},
178 {"/Clear Selection", C_"Z", clear_selection, 0, NULL},
179 {"/Options...", NULL, show_options, 0, NULL},
180 {"/New Directory...", NULL, new_directory, 0, NULL},
181 {"/Xterm Here", NULL, xterm_here, 0, NULL},
182 {"/Window", NULL, NULL, 0, "<Branch>"},
183 {"/Window/Parent, New Window", NULL, open_parent, 0, NULL},
184 {"/Window/Parent, Same Window", NULL, open_parent_same, 0, NULL},
185 {"/Window/New Window", NULL, new_window, 0, NULL},
186 {"/Window/Close Window", C_"Q", close_window, 0, NULL},
187 {"/Window/Enter Path", NULL, enter_path, 0, NULL},
188 {"/Window/Separator", NULL, NULL, 0, "<Separator>"},
189 {"/Window/Show ROX-Filer help", NULL, rox_help, 0, NULL},
192 static GtkItemFactoryEntry panel_menu_def[] = {
193 {"/Display", NULL, NULL, 0, "<Branch>"},
194 {"/Display/Sort by Name", NULL, sort_name, 0, NULL},
195 {"/Display/Sort by Type", NULL, sort_type, 0, NULL},
196 {"/Display/Sort by Date", NULL, sort_date, 0, NULL},
197 {"/Display/Sort by Size", NULL, sort_size, 0, NULL},
198 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
199 {"/Display/Show Hidden", NULL, hidden, 0, "<ToggleItem>"},
200 {"/Display/Refresh", NULL, refresh, 0, NULL},
201 {"/Open Panel as Directory", NULL, open_as_dir, 0, NULL},
202 {"/Close Panel", NULL, close_panel, 0, NULL},
203 {"/Separator", NULL, NULL, 0, "<Separator>"},
204 {"/ROX-Filer Help", NULL, rox_help, 0, NULL},
205 {"/ROX-Filer Options...", NULL, show_options, 0, NULL},
206 {"/Separator", NULL, NULL, 0, "<Separator>"},
207 {"/Show Help", NULL, help, 0, NULL},
208 {"/Remove Item", NULL, remove_link, 0, NULL},
211 typedef struct _FileStatus FileStatus;
213 /* This is for the 'file(1) says...' thing */
214 struct _FileStatus
216 int fd; /* FD to read from, -1 if closed */
217 int input; /* Input watcher tag if fd valid */
218 GtkLabel *label; /* Widget to output to */
219 gboolean start; /* No output yet */
222 void menu_init()
224 GtkItemFactory *item_factory;
225 char *menurc;
226 GList *items;
228 /* This call starts returning strange values after a while, so get
229 * the result here during init.
231 gdk_window_get_size(GDK_ROOT_PARENT(), &screen_width, &screen_height);
233 filer_keys = gtk_accel_group_new();
234 item_factory = gtk_item_factory_new(GTK_TYPE_MENU,
235 "<filer>",
236 filer_keys);
237 gtk_item_factory_create_items(item_factory,
238 sizeof(filer_menu_def) / sizeof(*filer_menu_def),
239 filer_menu_def,
240 NULL);
241 filer_menu = gtk_item_factory_get_widget(item_factory, "<filer>");
242 filer_file_menu = gtk_item_factory_get_widget(item_factory,
243 "<filer>/File");
244 filer_vfs_menu = gtk_item_factory_get_widget(item_factory,
245 "<filer>/File/Open VFS");
246 filer_hidden_menu = gtk_item_factory_get_widget(item_factory,
247 "<filer>/Display/Show Hidden");
248 items = gtk_container_children(GTK_CONTAINER(filer_menu));
249 filer_file_item = GTK_BIN(g_list_nth(items, 1)->data)->child;
250 g_list_free(items);
251 filer_new_window = GTK_BIN(gtk_item_factory_get_widget(item_factory,
252 "<filer>/Window/New Window"))->child;
254 panel_keys = gtk_accel_group_new();
255 item_factory = gtk_item_factory_new(GTK_TYPE_MENU,
256 "<panel>",
257 panel_keys);
258 gtk_item_factory_create_items(item_factory,
259 sizeof(panel_menu_def) / sizeof(*panel_menu_def),
260 panel_menu_def,
261 NULL);
262 panel_menu = gtk_item_factory_get_widget(item_factory, "<panel>");
263 panel_hidden_menu = gtk_item_factory_get_widget(item_factory,
264 "<panel>/Display/Show Hidden");
266 menurc = choices_find_path_load("menus");
267 if (menurc)
268 gtk_item_factory_parse_rc(menurc);
270 gtk_accel_group_lock(panel_keys);
272 gtk_signal_connect(GTK_OBJECT(filer_menu), "unmap_event",
273 GTK_SIGNAL_FUNC(menu_closed), NULL);
274 gtk_signal_connect(GTK_OBJECT(panel_menu), "unmap_event",
275 GTK_SIGNAL_FUNC(menu_closed), NULL);
276 gtk_signal_connect(GTK_OBJECT(filer_file_menu), "unmap_event",
277 GTK_SIGNAL_FUNC(menu_closed), NULL);
279 options_sections = g_slist_prepend(options_sections, &options);
280 xterm_here_value = g_strdup("xterm");
281 option_register("xterm_here", load_xterm_here);
284 /* Build up some option widgets to go in the options dialog, but don't
285 * fill them in yet.
287 static GtkWidget *create_options()
289 GtkWidget *table, *label;
291 table = gtk_table_new(2, 2, FALSE);
292 gtk_container_set_border_width(GTK_CONTAINER(table), 4);
294 label = gtk_label_new("To set the keyboard short-cuts you simply open "
295 "the menu over a filer window, move the pointer over "
296 "the item you want to use and press a key. The key "
297 "will appear next to the menu item and you can just "
298 "press that key without opening the menu in future. "
299 "To save the current menu short-cuts for next time, "
300 "click the Save button at the bottom of this window.");
301 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
302 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 2, 0, 1);
304 label = gtk_label_new("'Xterm here' program:");
305 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
306 xterm_here_entry = gtk_entry_new();
307 gtk_table_attach_defaults(GTK_TABLE(table), xterm_here_entry,
308 1, 2, 1, 2);
310 return table;
313 static char *load_xterm_here(char *data)
315 g_free(xterm_here_value);
316 xterm_here_value = g_strdup(data);
317 return NULL;
320 static void update_options()
322 gtk_entry_set_text(GTK_ENTRY(xterm_here_entry), xterm_here_value);
325 static void set_options()
327 g_free(xterm_here_value);
328 xterm_here_value = g_strdup(gtk_entry_get_text(
329 GTK_ENTRY(xterm_here_entry)));
332 static void save_options()
334 char *menurc;
336 menurc = choices_find_path_save("menus", TRUE);
337 if (menurc)
338 gtk_item_factory_dump_rc(menurc, NULL, TRUE);
340 option_write("xterm_here", xterm_here_value);
344 static void items_sensitive(gboolean state)
346 int n = 7;
347 GList *items, *item;
349 items = item = gtk_container_children(GTK_CONTAINER(filer_file_menu));
350 while (item && n--)
352 gtk_widget_set_sensitive(GTK_BIN(item->data)->child, state);
353 item = item->next;
355 g_list_free(items);
357 items = item = gtk_container_children(GTK_CONTAINER(filer_vfs_menu));
358 while (item)
360 gtk_widget_set_sensitive(GTK_BIN(item->data)->child, state);
361 item = item->next;
363 g_list_free(items);
366 static void position_menu(GtkMenu *menu, gint *x, gint *y, gpointer data)
368 int *pos = (int *) data;
369 GtkRequisition requisition;
371 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
373 if (pos[0] == -1)
374 *x = screen_width - MENU_MARGIN - requisition.width;
375 else if (pos[0] == -2)
376 *x = MENU_MARGIN;
377 else
378 *x = pos[0] - (requisition.width >> 2);
380 if (pos[1] == -1)
381 *y = screen_height - MENU_MARGIN - requisition.height;
382 else if (pos[1] == -2)
383 *y = MENU_MARGIN;
384 else
385 *y = pos[1] - (requisition.height >> 2);
387 *x = CLAMP(*x, 0, screen_width - requisition.width);
388 *y = CLAMP(*y, 0, screen_height - requisition.height);
391 void show_filer_menu(FilerWindow *filer_window, GdkEventButton *event,
392 int item)
394 DirItem *file_item;
395 int pos[2];
397 updating_menu++;
399 pos[0] = event->x_root;
400 pos[1] = event->y_root;
402 window_with_focus = filer_window;
404 switch (filer_window->panel_type)
406 case PANEL_TOP:
407 pos[1] = -2;
408 break;
409 case PANEL_BOTTOM:
410 pos[1] = -1;
411 break;
412 default:
413 break;
416 if (filer_window->panel_type)
417 collection_clear_selection(filer_window->collection); /* ??? */
419 if (filer_window->collection->number_selected == 0 && item >= 0)
421 collection_select_item(filer_window->collection, item);
422 filer_window->temp_item_selected = TRUE;
424 else
425 filer_window->temp_item_selected = FALSE;
427 if (filer_window->panel_type)
429 gtk_check_menu_item_set_active(
430 GTK_CHECK_MENU_ITEM(panel_hidden_menu),
431 filer_window->show_hidden);
433 else
435 GtkWidget *file_label, *file_menu;
436 Collection *collection = filer_window->collection;
437 GString *buffer;
439 file_label = filer_file_item;
440 file_menu = filer_file_menu;
441 gtk_check_menu_item_set_active(
442 GTK_CHECK_MENU_ITEM(filer_hidden_menu),
443 filer_window->show_hidden);
444 buffer = g_string_new(NULL);
445 switch (collection->number_selected)
447 case 0:
448 g_string_assign(buffer, "Next Click");
449 items_sensitive(TRUE);
450 break;
451 case 1:
452 items_sensitive(TRUE);
453 file_item = selected_item(
454 filer_window->collection);
455 g_string_sprintf(buffer, "%s '%s'",
456 basetype_name(file_item),
457 file_item->leafname);
458 break;
459 default:
460 items_sensitive(FALSE);
461 g_string_sprintf(buffer, "%d items",
462 collection->number_selected);
463 break;
465 gtk_label_set_text(GTK_LABEL(file_label), buffer->str);
466 g_string_free(buffer, TRUE);
470 gtk_widget_set_sensitive(filer_new_window, !o_unique_filer_windows);
472 if (filer_window->panel_type)
473 popup_menu = panel_menu;
474 else
475 popup_menu = (event->state & GDK_CONTROL_MASK)
476 ? filer_file_menu
477 : filer_menu;
479 updating_menu--;
481 gtk_menu_popup(GTK_MENU(popup_menu), NULL, NULL, position_menu,
482 (gpointer) pos, event->button, event->time);
485 static void menu_closed(GtkWidget *widget)
487 if (window_with_focus == NULL || widget != popup_menu)
488 return; /* Close panel item chosen? */
490 if (window_with_focus->temp_item_selected)
492 collection_clear_selection(window_with_focus->collection);
493 window_with_focus->temp_item_selected = FALSE;
497 void target_callback(Collection *collection, gint item, gpointer real_fn)
499 g_return_if_fail(window_with_focus != NULL);
500 g_return_if_fail(window_with_focus->collection == collection);
501 g_return_if_fail(real_fn != NULL);
503 collection_wink_item(collection, item);
504 collection_clear_selection(collection);
505 collection_select_item(collection, item);
506 ((CollectionTargetFunc)real_fn)(NULL, 0, collection);
507 if (item < collection->number_of_items)
508 collection_unselect_item(collection, item);
511 /* Actions */
513 /* Fake action to warn when a menu item does nothing */
514 static void not_yet(gpointer data, guint action, GtkWidget *widget)
516 delayed_error("ROX-Filer", "Sorry, that feature isn't implemented yet");
519 static void large(gpointer data, guint action, GtkWidget *widget)
521 g_return_if_fail(window_with_focus != NULL);
523 filer_style_set(window_with_focus, LARGE_ICONS);
526 static void small(gpointer data, guint action, GtkWidget *widget)
528 g_return_if_fail(window_with_focus != NULL);
530 filer_style_set(window_with_focus, SMALL_ICONS);
533 static void full_info(gpointer data, guint action, GtkWidget *widget)
535 g_return_if_fail(window_with_focus != NULL);
537 filer_style_set(window_with_focus, FULL_INFO);
540 static void sort_name(gpointer data, guint action, GtkWidget *widget)
542 g_return_if_fail(window_with_focus != NULL);
544 filer_set_sort_fn(window_with_focus, sort_by_name);
547 static void sort_type(gpointer data, guint action, GtkWidget *widget)
549 g_return_if_fail(window_with_focus != NULL);
551 filer_set_sort_fn(window_with_focus, sort_by_type);
554 static void sort_date(gpointer data, guint action, GtkWidget *widget)
556 g_return_if_fail(window_with_focus != NULL);
558 filer_set_sort_fn(window_with_focus, sort_by_date);
561 static void sort_size(gpointer data, guint action, GtkWidget *widget)
563 g_return_if_fail(window_with_focus != NULL);
565 filer_set_sort_fn(window_with_focus, sort_by_size);
568 static void hidden(gpointer data, guint action, GtkWidget *widget)
570 if (updating_menu)
571 return;
573 g_return_if_fail(window_with_focus != NULL);
575 filer_set_hidden(window_with_focus, !window_with_focus->show_hidden);
578 static void refresh(gpointer data, guint action, GtkWidget *widget)
580 g_return_if_fail(window_with_focus != NULL);
582 full_refresh();
583 update_dir(window_with_focus, TRUE);
586 static void mount(gpointer data, guint action, GtkWidget *widget)
588 g_return_if_fail(window_with_focus != NULL);
590 if (window_with_focus->collection->number_selected == 0)
591 collection_target(window_with_focus->collection,
592 target_callback, mount);
593 else
594 action_mount(window_with_focus, NULL);
597 static void delete(gpointer data, guint action, GtkWidget *widget)
599 g_return_if_fail(window_with_focus != NULL);
601 if (window_with_focus->collection->number_selected == 0)
602 collection_target(window_with_focus->collection,
603 target_callback, delete);
604 else
605 action_delete(window_with_focus);
608 static void remove_link(gpointer data, guint action, GtkWidget *widget)
610 g_return_if_fail(window_with_focus != NULL);
612 if (window_with_focus->collection->number_selected > 1)
614 report_error("ROX-Filer", "You can only remove one link "
615 "at a time");
616 return;
618 else if (window_with_focus->collection->number_selected == 0)
619 collection_target(window_with_focus->collection,
620 target_callback, remove_link);
621 else
623 struct stat info;
624 DirItem *item;
625 guchar *path;
627 item = selected_item(window_with_focus->collection);
629 path = make_path(window_with_focus->path, item->leafname)->str;
630 if (lstat(path, &info))
631 report_error("ROX-Filer", g_strerror(errno));
632 else if (!S_ISLNK(info.st_mode))
633 report_error("ROX-Filer",
634 "You can only remove symbolic links this way - "
635 "try the 'Open Panel as Directory' item.");
636 else if (unlink(path))
637 report_error("ROX-Filer", g_strerror(errno));
638 else
639 update_dir(window_with_focus, TRUE);
643 static void usage(gpointer data, guint action, GtkWidget *widget)
645 g_return_if_fail(window_with_focus != NULL);
647 if (window_with_focus->collection->number_selected == 0)
648 collection_target(window_with_focus->collection,
649 target_callback, usage);
650 else
651 action_usage(window_with_focus);
654 static void chmod_items(gpointer data, guint action, GtkWidget *widget)
656 g_return_if_fail(window_with_focus != NULL);
658 if (window_with_focus->collection->number_selected == 0)
659 collection_target(window_with_focus->collection,
660 target_callback, chmod_items);
661 else
662 action_chmod(window_with_focus);
665 static gboolean copy_cb(char *initial, char *path)
667 char *new_dir, *leaf;
668 GSList *local_paths;
670 if (path[0] != '/')
672 report_error("ROX-Filer", "New pathname is not absolute");
673 return FALSE;
676 if (path[strlen(path) - 1] == '/')
678 new_dir = g_strdup(path);
679 leaf = NULL;
681 else
683 char *slash;
685 slash = strrchr(path, '/');
686 new_dir = g_strndup(path, slash - path);
687 leaf = slash + 1;
690 local_paths = g_slist_append(NULL, initial);
691 action_copy(local_paths, new_dir, leaf);
692 g_slist_free(local_paths);
694 g_free(new_dir);
696 return TRUE;
699 static void copy_item(gpointer data, guint action, GtkWidget *widget)
701 Collection *collection;
703 g_return_if_fail(window_with_focus != NULL);
705 collection = window_with_focus->collection;
706 if (collection->number_selected > 1)
708 report_error("ROX-Filer", "You cannot do this to more than "
709 "one item at a time");
710 return;
712 else if (collection->number_selected != 1)
713 collection_target(collection, target_callback, copy_item);
714 else
716 DirItem *item = selected_item(collection);
718 savebox_show(window_with_focus, "Copy",
719 window_with_focus->path,
720 item->leafname,
721 item->image, copy_cb);
725 static gboolean rename_cb(char *initial, char *path)
727 if (rename(initial, path))
729 report_error("ROX-Filer: rename()", g_strerror(errno));
730 return FALSE;
732 return TRUE;
735 static void rename_item(gpointer data, guint action, GtkWidget *widget)
737 Collection *collection;
739 g_return_if_fail(window_with_focus != NULL);
741 collection = window_with_focus->collection;
742 if (collection->number_selected > 1)
744 report_error("ROX-Filer", "You cannot do this to more than "
745 "one item at a time");
746 return;
748 else if (collection->number_selected != 1)
749 collection_target(collection, target_callback, rename_item);
750 else
752 DirItem *item = selected_item(collection);
754 savebox_show(window_with_focus, "Rename",
755 window_with_focus->path,
756 item->leafname,
757 item->image, rename_cb);
761 static gboolean link_cb(char *initial, char *path)
763 if (symlink(initial, path))
765 report_error("ROX-Filer: symlink()", g_strerror(errno));
766 return FALSE;
768 return TRUE;
771 static void link_item(gpointer data, guint action, GtkWidget *widget)
773 Collection *collection;
775 g_return_if_fail(window_with_focus != NULL);
777 collection = window_with_focus->collection;
778 if (collection->number_selected > 1)
780 report_error("ROX-Filer", "You cannot do this to more than "
781 "one item at a time");
782 return;
784 else if (collection->number_selected != 1)
785 collection_target(collection, target_callback, link_item);
786 else
788 DirItem *item = selected_item(collection);
790 savebox_show(window_with_focus, "Symlink",
791 window_with_focus->path,
792 item->leafname,
793 item->image, link_cb);
797 static void open_file(gpointer data, guint action, GtkWidget *widget)
799 Collection *collection;
801 g_return_if_fail(window_with_focus != NULL);
803 collection = window_with_focus->collection;
804 if (collection->number_selected > 1)
806 report_error("ROX-Filer", "You cannot do this to more than "
807 "one item at a time");
808 return;
810 else if (collection->number_selected != 1)
811 collection_target(collection, target_callback, open_file);
812 else
813 filer_openitem(window_with_focus,
814 selected_item_number(collection),
815 OPEN_SAME_WINDOW | OPEN_SHIFT);
818 /* Got some data from file(1) - stick it in the window. */
819 static void add_file_output(FileStatus *fs,
820 gint source, GdkInputCondition condition)
822 char buffer[20];
823 char *str;
824 int got;
826 got = read(source, buffer, sizeof(buffer) - 1);
827 if (got <= 0)
829 int err = errno;
830 gtk_input_remove(fs->input);
831 close(source);
832 fs->fd = -1;
833 if (got < 0)
834 delayed_error("ROX-Filer: file(1) says...",
835 g_strerror(err));
836 return;
838 buffer[got] = '\0';
840 if (fs->start)
842 str = "";
843 fs->start = FALSE;
845 else
846 gtk_label_get(fs->label, &str);
848 str = g_strconcat(str, buffer, NULL);
849 gtk_label_set_text(fs->label, str);
850 g_free(str);
853 static void file_info_destroyed(GtkWidget *widget, FileStatus *fs)
855 if (fs->fd != -1)
857 gtk_input_remove(fs->input);
858 close(fs->fd);
861 g_free(fs);
864 static void show_file_info(gpointer data, guint action, GtkWidget *widget)
866 GtkWidget *window, *table, *label, *button, *frame;
867 GtkWidget *file_label;
868 GString *gstring;
869 char *string;
870 int file_data[2];
871 char *path;
872 char *argv[] = {"file", "-b", NULL, NULL};
873 Collection *collection;
874 DirItem *file;
875 struct stat info;
876 FileStatus *fs = NULL;
877 guint perm;
879 g_return_if_fail(window_with_focus != NULL);
881 collection = window_with_focus->collection;
882 if (collection->number_selected > 1)
884 report_error("ROX-Filer", "You cannot do this to more than "
885 "one item at a time");
886 return;
888 else if (collection->number_selected != 1)
890 collection_target(collection, target_callback, show_file_info);
891 return;
893 file = selected_item(collection);
894 path = make_path(window_with_focus->path,
895 file->leafname)->str;
896 if (lstat(path, &info))
898 delayed_error("ROX-Filer", g_strerror(errno));
899 return;
902 gstring = g_string_new(NULL);
904 window = gtk_window_new(GTK_WINDOW_DIALOG);
905 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_MOUSE);
906 gtk_container_set_border_width(GTK_CONTAINER(window), 4);
907 gtk_window_set_title(GTK_WINDOW(window), path);
909 table = gtk_table_new(9, 2, FALSE);
910 gtk_container_add(GTK_CONTAINER(window), table);
911 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
912 gtk_table_set_col_spacings(GTK_TABLE(table), 4);
914 label = gtk_label_new("Owner, group:");
915 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
916 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_RIGHT);
917 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
918 g_string_sprintf(gstring, "%s, %s", user_name(info.st_uid),
919 group_name(info.st_gid));
920 label = gtk_label_new(gstring->str);
921 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
922 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 0, 1);
924 label = gtk_label_new("Size:");
925 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
926 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
927 if (info.st_size >= PRETTY_SIZE_LIMIT)
929 g_string_sprintf(gstring, "%s (%ld bytes)",
930 format_size((unsigned long) info.st_size),
931 (unsigned long) info.st_size);
933 else
935 g_string_sprintf(gstring, "%ld byte%s",
936 (unsigned long) info.st_size,
937 info.st_size == 1 ? "" : "s");
939 label = gtk_label_new(gstring->str);
940 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
941 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 1, 2);
943 label = gtk_label_new("Change time:");
944 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
945 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3);
946 label = gtk_label_new(pretty_time(&info.st_ctime));
947 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
948 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 2, 3);
950 label = gtk_label_new("Modify time:");
951 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
952 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4);
953 label = gtk_label_new(pretty_time(&info.st_mtime));
954 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
955 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 3, 4);
957 label = gtk_label_new("Access time:");
958 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
959 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 4, 5);
960 label = gtk_label_new(pretty_time(&info.st_atime));
961 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
962 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 4, 5);
964 label = gtk_label_new("Permissions:");
965 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
966 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 5, 6);
967 label = gtk_label_new(pretty_permissions(info.st_mode));
968 perm = applicable(info.st_uid, info.st_gid);
969 gtk_label_set_pattern(GTK_LABEL(label),
970 perm == 0 ? "___ " :
971 perm == 1 ? " ___ " :
972 " ___");
973 gtk_widget_set_style(label, fixed_style);
974 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
975 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 5, 6);
977 label = gtk_label_new("Type:");
978 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
979 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 6, 7);
980 if (file->mime_type)
982 string = g_strconcat(file->mime_type->media_type, "/",
983 file->mime_type->subtype, NULL);
985 else if (file->flags & ITEM_FLAG_APPDIR)
986 string = g_strdup("ROX application");
987 else if (file->flags & ITEM_FLAG_SYMLINK)
989 char p[MAXPATHLEN + 1];
990 int got;
991 got = readlink(path, p, MAXPATHLEN);
992 if (got > 0 && got <= MAXPATHLEN)
994 p[got] = '\0';
995 string = g_strconcat("Symbolic link to ",
996 p, NULL);
998 else
999 string = g_strdup("Symbolic link");
1001 else if (file->flags & ITEM_FLAG_MOUNT_POINT)
1003 MountPoint *mp;
1004 if ((file->flags & ITEM_FLAG_MOUNTED) &&
1005 (mp = g_hash_table_lookup(mtab_mounts, path)))
1006 string = g_strconcat("Mount point for ",
1007 mp->name, NULL);
1008 else
1009 string = g_strdup("Mount point");
1011 else
1012 string = g_strdup("-");
1013 label = gtk_label_new(string);
1014 g_free(string);
1015 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
1016 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 6, 7);
1018 frame = gtk_frame_new("file(1) says...");
1019 gtk_table_attach_defaults(GTK_TABLE(table), frame, 0, 2, 7, 8);
1020 file_label = gtk_label_new("<nothing yet>");
1021 gtk_misc_set_padding(GTK_MISC(file_label), 4, 4);
1022 gtk_label_set_line_wrap(GTK_LABEL(file_label), TRUE);
1023 gtk_container_add(GTK_CONTAINER(frame), file_label);
1025 button = gtk_button_new_with_label("OK");
1026 gtk_window_set_focus(GTK_WINDOW(window), button);
1027 gtk_table_attach(GTK_TABLE(table), button, 0, 2, 9, 10,
1028 GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 40, 4);
1029 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1030 gtk_widget_destroy, GTK_OBJECT(window));
1032 gtk_widget_show_all(window);
1033 gdk_flush();
1035 if (pipe(file_data))
1037 g_string_sprintf(gstring, "pipe(): %s", g_strerror(errno));
1038 g_string_free(gstring, TRUE);
1039 return;
1041 switch (fork())
1043 case -1:
1044 g_string_sprintf(gstring, "fork(): %s",
1045 g_strerror(errno));
1046 gtk_label_set_text(GTK_LABEL(file_label), gstring->str);
1047 g_string_free(gstring, TRUE);
1048 close(file_data[0]);
1049 close(file_data[1]);
1050 break;
1051 case 0:
1052 /* We are the child */
1053 close(file_data[0]);
1054 dup2(file_data[1], STDOUT_FILENO);
1055 dup2(file_data[1], STDERR_FILENO);
1056 #ifdef FILE_B_FLAG
1057 argv[2] = path;
1058 #else
1059 argv[1] = file->leafname;
1060 chdir(window_with_focus->path);
1061 #endif
1062 if (execvp(argv[0], argv))
1063 fprintf(stderr, "execvp() error: %s\n",
1064 g_strerror(errno));
1065 _exit(0);
1066 default:
1067 /* We are the parent */
1068 close(file_data[1]);
1069 fs = g_new(FileStatus, 1);
1070 fs->label = GTK_LABEL(file_label);
1071 fs->fd = file_data[0];
1072 fs->start = TRUE;
1073 fs->input = gdk_input_add(fs->fd, GDK_INPUT_READ,
1074 (GdkInputFunction) add_file_output, fs);
1075 gtk_signal_connect(GTK_OBJECT(window), "destroy",
1076 GTK_SIGNAL_FUNC(file_info_destroyed), fs);
1077 g_string_free(gstring, TRUE);
1078 break;
1082 static void app_show_help(char *path)
1084 char *help_dir;
1085 struct stat info;
1087 help_dir = g_strconcat(path, "/Help", NULL);
1089 if (mc_stat(help_dir, &info))
1090 delayed_error("Application",
1091 "This is an application directory - you can "
1092 "run it as a program, or open it (hold down "
1093 "Shift while you open it). Most applications provide "
1094 "their own help here, but this one doesn't.");
1095 else
1096 filer_opendir(help_dir, PANEL_NO);
1099 static void help(gpointer data, guint action, GtkWidget *widget)
1101 Collection *collection;
1102 DirItem *item;
1104 g_return_if_fail(window_with_focus != NULL);
1106 collection = window_with_focus->collection;
1107 if (collection->number_selected > 1)
1109 report_error("ROX-Filer", "You cannot do this to more than "
1110 "one item at a time");
1111 return;
1113 else if (collection->number_selected != 1)
1115 collection_target(collection, target_callback, help);
1116 return;
1118 item = selected_item(collection);
1119 switch (item->base_type)
1121 case TYPE_FILE:
1122 if (item->flags & ITEM_FLAG_EXEC_FILE)
1123 delayed_error("Executable file",
1124 "This is a file with an eXecute bit "
1125 "set - it can be run as a program.");
1126 else
1127 delayed_error("File",
1128 "This is a data file. Try using the "
1129 "Info menu item to find out more...");
1130 break;
1131 case TYPE_DIRECTORY:
1132 if (item->flags & ITEM_FLAG_APPDIR)
1133 app_show_help(
1134 make_path(window_with_focus->path,
1135 item->leafname)->str);
1136 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
1137 delayed_error("Mount point",
1138 "A mount point is a directory which another "
1139 "filing system can be mounted on. Everything "
1140 "on the mounted filesystem then appears to be "
1141 "inside the directory.");
1142 else
1143 delayed_error("Directory",
1144 "This is a directory. It contains an index to "
1145 "other items - open it to see the list.");
1146 break;
1147 case TYPE_CHAR_DEVICE:
1148 case TYPE_BLOCK_DEVICE:
1149 delayed_error("Device file",
1150 "Device files allow you to read from or write "
1151 "to a device driver as though it was an "
1152 "ordinary file.");
1153 break;
1154 case TYPE_PIPE:
1155 delayed_error("Named pipe",
1156 "Pipes allow different programs to "
1157 "communicate. One program writes data to the "
1158 "pipe while another one reads it out again.");
1159 break;
1160 case TYPE_SOCKET:
1161 delayed_error("Socket",
1162 "Sockets allow processes to communicate.");
1163 break;
1164 default:
1165 delayed_error("Unknown type",
1166 "I couldn't find out what kind of file this "
1167 "is. Maybe it doesn't exist anymore or you "
1168 "don't have search permission on the directory "
1169 "it's in?");
1170 break;
1174 #define OPEN_VFS(fs) \
1175 static void open_vfs_ ## fs (gpointer data, guint action, GtkWidget *widget) \
1177 Collection *collection; \
1179 g_return_if_fail(window_with_focus != NULL); \
1181 collection = window_with_focus->collection; \
1182 if (collection->number_selected < 1) \
1183 collection_target(collection, target_callback, \
1184 open_vfs_ ## fs); \
1185 else \
1186 real_vfs_open(#fs); \
1189 static void real_vfs_open(char *fs)
1191 gchar *path;
1192 DirItem *item;
1194 if (window_with_focus->collection->number_selected != 1)
1196 report_error("ROX-Filer", "You must select a single file "
1197 "to open as a Virtual File System");
1198 return;
1201 item = selected_item(window_with_focus->collection);
1203 path = g_strconcat(window_with_focus->path,
1204 "/",
1205 item->leafname,
1206 "#", fs, NULL);
1208 filer_change_to(window_with_focus, path, NULL);
1209 g_free(path);
1212 OPEN_VFS(rpm)
1213 OPEN_VFS(utar)
1214 OPEN_VFS(uzip)
1216 static void select_all(gpointer data, guint action, GtkWidget *widget)
1218 g_return_if_fail(window_with_focus != NULL);
1220 collection_select_all(window_with_focus->collection);
1221 window_with_focus->temp_item_selected = FALSE;
1224 static void clear_selection(gpointer data, guint action, GtkWidget *widget)
1226 g_return_if_fail(window_with_focus != NULL);
1228 collection_clear_selection(window_with_focus->collection);
1229 window_with_focus->temp_item_selected = FALSE;
1232 static void show_options(gpointer data, guint action, GtkWidget *widget)
1234 g_return_if_fail(window_with_focus != NULL);
1236 options_show(window_with_focus);
1239 static gboolean new_directory_cb(char *initial, char *path)
1241 if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO))
1243 report_error("mkdir", g_strerror(errno));
1244 return FALSE;
1246 return TRUE;
1249 static void new_directory(gpointer data, guint action, GtkWidget *widget)
1251 g_return_if_fail(window_with_focus != NULL);
1253 savebox_show(window_with_focus, "Create directory",
1254 window_with_focus->path, "NewDir",
1255 default_pixmap + TYPE_DIRECTORY, new_directory_cb);
1258 static void xterm_here(gpointer data, guint action, GtkWidget *widget)
1260 char *argv[] = {NULL, NULL};
1262 argv[0] = xterm_here_value;
1264 g_return_if_fail(window_with_focus != NULL);
1266 if (!spawn_full(argv, window_with_focus->path))
1267 report_error("ROX-Filer", "Failed to fork() child "
1268 "process");
1271 static void open_parent(gpointer data, guint action, GtkWidget *widget)
1273 char *copy;
1274 char *slash;
1276 g_return_if_fail(window_with_focus != NULL);
1278 if (window_with_focus->path[0] == '/'
1279 && window_with_focus->path[1] == '\0')
1280 return; /* Already in the root */
1282 copy = g_strdup(window_with_focus->path);
1283 slash = strrchr(copy, '/');
1285 if (slash)
1287 *slash = '\0';
1288 filer_opendir(*copy ? copy : "/", PANEL_NO);
1290 else
1291 g_warning("No / in directory path!\n");
1293 g_free(copy);
1296 static void open_parent_same(gpointer data, guint action, GtkWidget *widget)
1298 g_return_if_fail(window_with_focus != NULL);
1300 change_to_parent(window_with_focus);
1303 static void new_window(gpointer data, guint action, GtkWidget *widget)
1305 g_return_if_fail(window_with_focus != NULL);
1307 if (o_unique_filer_windows)
1308 report_error("ROX-Filer", "You can't open a second view onto "
1309 "this directory because the `Unique Windows' option "
1310 "is turned on in the Options window.");
1311 else
1312 filer_opendir(window_with_focus->path, PANEL_NO);
1315 static void close_window(gpointer data, guint action, GtkWidget *widget)
1317 g_return_if_fail(window_with_focus != NULL);
1319 gtk_widget_destroy(window_with_focus->window);
1322 static void enter_path(gpointer data, guint action, GtkWidget *widget)
1324 g_return_if_fail(window_with_focus != NULL);
1326 minibuffer_show(window_with_focus);
1329 static void rox_help(gpointer data, guint action, GtkWidget *widget)
1331 g_return_if_fail(window_with_focus != NULL);
1333 filer_opendir(make_path(getenv("APP_DIR"), "Help")->str, PANEL_NO);
1336 static void open_as_dir(gpointer data, guint action, GtkWidget *widget)
1338 g_return_if_fail(window_with_focus != NULL);
1340 filer_opendir(window_with_focus->path, PANEL_NO);
1343 static void close_panel(gpointer data, guint action, GtkWidget *widget)
1345 g_return_if_fail(window_with_focus != NULL);
1347 gtk_widget_destroy(window_with_focus->window);