r233: Tidied up choices.c quite a lot. Also, replaced the 'guess' file with a
[rox-filer/dt.git] / ROX-Filer / src / menu.c
blob2dff356228e811abb8ea5138d4f67a00f59ddae2
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2000, Thomas Leonard, <tal197@ecs.soton.ac.uk>.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* menu.c - code for handling the popup menu */
24 #include <config.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <sys/wait.h>
30 #include <sys/param.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include <string.h>
36 #include <gdk/gdkx.h>
37 #include <gtk/gtk.h>
39 #include "run.h"
40 #include "action.h"
41 #include "filer.h"
42 #include "type.h"
43 #include "support.h"
44 #include "gui_support.h"
45 #include "options.h"
46 #include "choices.h"
47 #include "gtksavebox.h"
48 #include "mount.h"
49 #include "minibuffer.h"
51 #define C_ "<control>"
53 #define MENU_MARGIN 32
55 GtkAccelGroup *filer_keys;
56 GtkAccelGroup *panel_keys;
58 static GtkWidget *popup_menu = NULL; /* Currently open menu */
60 static gint updating_menu = 0; /* Non-zero => ignore activations */
62 /* Options */
63 static GtkWidget *xterm_here_entry;
64 static char *xterm_here_value;
66 /* Static prototypes */
68 static void position_menu(GtkMenu *menu, gint *x, gint *y, gpointer data);
69 static void menu_closed(GtkWidget *widget);
70 static void items_sensitive(gboolean state);
71 static char *load_xterm_here(char *data);
72 static void savebox_show(guchar *title, guchar *path, MaskedPixmap *image,
73 gboolean (*callback)(guchar *current, guchar *new));
74 static gint save_to_file(GtkSavebox *savebox, guchar *pathname);
76 /* Note that for these callbacks none of the arguments are used. */
77 static void not_yet(gpointer data, guint action, GtkWidget *widget);
79 static void large(gpointer data, guint action, GtkWidget *widget);
80 static void small(gpointer data, guint action, GtkWidget *widget);
81 static void full_info(gpointer data, guint action, GtkWidget *widget);
83 static void sort_name(gpointer data, guint action, GtkWidget *widget);
84 static void sort_type(gpointer data, guint action, GtkWidget *widget);
85 static void sort_size(gpointer data, guint action, GtkWidget *widget);
86 static void sort_date(gpointer data, guint action, GtkWidget *widget);
88 static void hidden(gpointer data, guint action, GtkWidget *widget);
89 static void refresh(gpointer data, guint action, GtkWidget *widget);
91 static void copy_item(gpointer data, guint action, GtkWidget *widget);
92 static void rename_item(gpointer data, guint action, GtkWidget *widget);
93 static void link_item(gpointer data, guint action, GtkWidget *widget);
94 static void open_file(gpointer data, guint action, GtkWidget *widget);
95 static void help(gpointer data, guint action, GtkWidget *widget);
96 static void show_file_info(gpointer data, guint action, GtkWidget *widget);
97 static void mount(gpointer data, guint action, GtkWidget *widget);
98 static void delete(gpointer data, guint action, GtkWidget *widget);
99 static void remove_link(gpointer data, guint action, GtkWidget *widget);
100 static void usage(gpointer data, guint action, GtkWidget *widget);
101 static void chmod_items(gpointer data, guint action, GtkWidget *widget);
102 static void find(gpointer data, guint action, GtkWidget *widget);
104 static void open_vfs_rpm(gpointer data, guint action, GtkWidget *widget);
105 static void open_vfs_utar(gpointer data, guint action, GtkWidget *widget);
106 static void open_vfs_uzip(gpointer data, guint action, GtkWidget *widget);
108 static void select_all(gpointer data, guint action, GtkWidget *widget);
109 static void clear_selection(gpointer data, guint action, GtkWidget *widget);
110 static void show_options(gpointer data, guint action, GtkWidget *widget);
111 static void new_directory(gpointer data, guint action, GtkWidget *widget);
112 static void xterm_here(gpointer data, guint action, GtkWidget *widget);
114 static void open_parent_same(gpointer data, guint action, GtkWidget *widget);
115 static void open_parent(gpointer data, guint action, GtkWidget *widget);
116 static void new_window(gpointer data, guint action, GtkWidget *widget);
117 static void close_window(gpointer data, guint action, GtkWidget *widget);
118 static void enter_path(gpointer data, guint action, GtkWidget *widget);
119 static void rox_help(gpointer data, guint action, GtkWidget *widget);
121 static void open_as_dir(gpointer data, guint action, GtkWidget *widget);
122 static void close_panel(gpointer data, guint action, GtkWidget *widget);
124 static GtkWidget *create_options();
125 static void update_options();
126 static void set_options();
127 static void save_options();
129 static OptionsSection options =
131 "Menu options",
132 create_options,
133 update_options,
134 set_options,
135 save_options
139 static GtkWidget *filer_menu; /* The popup filer menu */
140 static GtkWidget *filer_file_item; /* The File '' label */
141 static GtkWidget *filer_file_menu; /* The File '' menu */
142 static GtkWidget *filer_vfs_menu; /* The Open VFS menu */
143 static GtkWidget *filer_hidden_menu; /* The Show Hidden item */
144 static GtkWidget *filer_new_window; /* The New Window item */
145 static GtkWidget *panel_menu; /* The popup panel menu */
146 static GtkWidget *panel_hidden_menu; /* The Show Hidden item */
148 /* Used for Copy, etc */
149 static GtkWidget *savebox = NULL;
150 static guchar *current_path = NULL;
151 static gboolean (*current_savebox_callback)(guchar *current, guchar *new);
153 static gint screen_width, screen_height;
155 static GtkItemFactoryEntry filer_menu_def[] = {
156 {"/Display", NULL, NULL, 0, "<Branch>"},
157 {"/Display/Large Icons", NULL, large, 0, NULL},
158 {"/Display/Small Icons", NULL, small, 0, NULL},
159 {"/Display/Full Info", NULL, full_info, 0, NULL},
160 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
161 {"/Display/Sort by Name", NULL, sort_name, 0, NULL},
162 {"/Display/Sort by Type", NULL, sort_type, 0, NULL},
163 {"/Display/Sort by Date", NULL, sort_date, 0, NULL},
164 {"/Display/Sort by Size", NULL, sort_size, 0, NULL},
165 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
166 {"/Display/Show Hidden", C_"H", hidden, 0, "<ToggleItem>"},
167 {"/Display/Refresh", C_"L", refresh, 0, NULL},
168 {"/File", NULL, NULL, 0, "<Branch>"},
169 {"/File/Copy...", NULL, copy_item, 0, NULL},
170 {"/File/Rename...", NULL, rename_item, 0, NULL},
171 {"/File/Link...", NULL, link_item, 0, NULL},
172 {"/File/Shift Open", NULL, open_file, 0, NULL},
173 {"/File/Help", "F1", help, 0, NULL},
174 {"/File/Info", "I", show_file_info, 0, NULL},
175 {"/File/Open VFS", NULL, NULL, 0, "<Branch>"},
176 {"/File/Open VFS/Unzip", NULL, open_vfs_uzip, 0, NULL},
177 {"/File/Open VFS/Untar", NULL, open_vfs_utar, 0, NULL},
178 {"/File/Open VFS/RPM", NULL, open_vfs_rpm, 0, NULL},
179 {"/File/Separator", NULL, NULL, 0, "<Separator>"},
180 {"/File/Mount", "M", mount, 0, NULL},
181 {"/File/Delete", C_"X", delete, 0, NULL},
182 {"/File/Disk Usage", "U", usage, 0, NULL},
183 {"/File/Permissions", NULL, chmod_items, 0, NULL},
184 {"/File/Touch", NULL, not_yet, 0, NULL},
185 {"/File/Find", NULL, find, 0, NULL},
186 {"/Select All", C_"A", select_all, 0, NULL},
187 {"/Clear Selection", C_"Z", clear_selection, 0, NULL},
188 {"/Options...", NULL, show_options, 0, NULL},
189 {"/New Directory...", NULL, new_directory, 0, NULL},
190 {"/Xterm Here", NULL, xterm_here, 0, NULL},
191 {"/Window", NULL, NULL, 0, "<Branch>"},
192 {"/Window/Parent, New Window", NULL, open_parent, 0, NULL},
193 {"/Window/Parent, Same Window", NULL, open_parent_same, 0, NULL},
194 {"/Window/New Window", NULL, new_window, 0, NULL},
195 {"/Window/Close Window", C_"Q", close_window, 0, NULL},
196 {"/Window/Enter Path", NULL, enter_path, 0, NULL},
197 {"/Window/Separator", NULL, NULL, 0, "<Separator>"},
198 {"/Window/Show ROX-Filer help", NULL, rox_help, 0, NULL},
201 static GtkItemFactoryEntry panel_menu_def[] = {
202 {"/Display", NULL, NULL, 0, "<Branch>"},
203 {"/Display/Sort by Name", NULL, sort_name, 0, NULL},
204 {"/Display/Sort by Type", NULL, sort_type, 0, NULL},
205 {"/Display/Sort by Date", NULL, sort_date, 0, NULL},
206 {"/Display/Sort by Size", NULL, sort_size, 0, NULL},
207 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
208 {"/Display/Show Hidden", NULL, hidden, 0, "<ToggleItem>"},
209 {"/Display/Refresh", NULL, refresh, 0, NULL},
210 {"/Open Panel as Directory", NULL, open_as_dir, 0, NULL},
211 {"/Close Panel", NULL, close_panel, 0, NULL},
212 {"/Separator", NULL, NULL, 0, "<Separator>"},
213 {"/ROX-Filer Help", NULL, rox_help, 0, NULL},
214 {"/ROX-Filer Options...", NULL, show_options, 0, NULL},
215 {"/Separator", NULL, NULL, 0, "<Separator>"},
216 {"/Show Help", NULL, help, 0, NULL},
217 {"/Remove Item", NULL, remove_link, 0, NULL},
220 typedef struct _FileStatus FileStatus;
222 /* This is for the 'file(1) says...' thing */
223 struct _FileStatus
225 int fd; /* FD to read from, -1 if closed */
226 int input; /* Input watcher tag if fd valid */
227 GtkLabel *label; /* Widget to output to */
228 gboolean start; /* No output yet */
231 void menu_init()
233 GtkItemFactory *item_factory;
234 char *menurc;
235 GList *items;
237 /* This call starts returning strange values after a while, so get
238 * the result here during init.
240 gdk_window_get_size(GDK_ROOT_PARENT(), &screen_width, &screen_height);
242 filer_keys = gtk_accel_group_new();
243 item_factory = gtk_item_factory_new(GTK_TYPE_MENU,
244 "<filer>",
245 filer_keys);
246 gtk_item_factory_create_items(item_factory,
247 sizeof(filer_menu_def) / sizeof(*filer_menu_def),
248 filer_menu_def,
249 NULL);
250 filer_menu = gtk_item_factory_get_widget(item_factory, "<filer>");
251 filer_file_menu = gtk_item_factory_get_widget(item_factory,
252 "<filer>/File");
253 filer_vfs_menu = gtk_item_factory_get_widget(item_factory,
254 "<filer>/File/Open VFS");
255 filer_hidden_menu = gtk_item_factory_get_widget(item_factory,
256 "<filer>/Display/Show Hidden");
257 items = gtk_container_children(GTK_CONTAINER(filer_menu));
258 filer_file_item = GTK_BIN(g_list_nth(items, 1)->data)->child;
259 g_list_free(items);
260 filer_new_window = GTK_BIN(gtk_item_factory_get_widget(item_factory,
261 "<filer>/Window/New Window"))->child;
263 panel_keys = gtk_accel_group_new();
264 item_factory = gtk_item_factory_new(GTK_TYPE_MENU,
265 "<panel>",
266 panel_keys);
267 gtk_item_factory_create_items(item_factory,
268 sizeof(panel_menu_def) / sizeof(*panel_menu_def),
269 panel_menu_def,
270 NULL);
271 panel_menu = gtk_item_factory_get_widget(item_factory, "<panel>");
272 panel_hidden_menu = gtk_item_factory_get_widget(item_factory,
273 "<panel>/Display/Show Hidden");
275 menurc = choices_find_path_load("menus", "ROX-Filer");
276 if (menurc)
277 gtk_item_factory_parse_rc(menurc);
279 gtk_accel_group_lock(panel_keys);
281 gtk_signal_connect(GTK_OBJECT(filer_menu), "unmap_event",
282 GTK_SIGNAL_FUNC(menu_closed), NULL);
283 gtk_signal_connect(GTK_OBJECT(panel_menu), "unmap_event",
284 GTK_SIGNAL_FUNC(menu_closed), NULL);
285 gtk_signal_connect(GTK_OBJECT(filer_file_menu), "unmap_event",
286 GTK_SIGNAL_FUNC(menu_closed), NULL);
288 options_sections = g_slist_prepend(options_sections, &options);
289 xterm_here_value = g_strdup("xterm");
290 option_register("xterm_here", load_xterm_here);
292 savebox = gtk_savebox_new();
293 gtk_signal_connect_object(GTK_OBJECT(savebox), "save_to_file",
294 GTK_SIGNAL_FUNC(save_to_file), NULL);
295 gtk_signal_connect_object(GTK_OBJECT(savebox), "save_done",
296 GTK_SIGNAL_FUNC(gtk_widget_hide),
297 GTK_OBJECT(savebox));
300 /* Build up some option widgets to go in the options dialog, but don't
301 * fill them in yet.
303 static GtkWidget *create_options()
305 GtkWidget *table, *label;
307 table = gtk_table_new(2, 2, FALSE);
308 gtk_container_set_border_width(GTK_CONTAINER(table), 4);
310 label = gtk_label_new("To set the keyboard short-cuts you simply open "
311 "the menu over a filer window, move the pointer over "
312 "the item you want to use and press a key. The key "
313 "will appear next to the menu item and you can just "
314 "press that key without opening the menu in future. "
315 "To save the current menu short-cuts for next time, "
316 "click the Save button at the bottom of this window.");
317 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
318 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 2, 0, 1);
320 label = gtk_label_new("'Xterm here' program:");
321 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
322 xterm_here_entry = gtk_entry_new();
323 gtk_table_attach_defaults(GTK_TABLE(table), xterm_here_entry,
324 1, 2, 1, 2);
326 return table;
329 static char *load_xterm_here(char *data)
331 g_free(xterm_here_value);
332 xterm_here_value = g_strdup(data);
333 return NULL;
336 static void update_options()
338 gtk_entry_set_text(GTK_ENTRY(xterm_here_entry), xterm_here_value);
341 static void set_options()
343 g_free(xterm_here_value);
344 xterm_here_value = g_strdup(gtk_entry_get_text(
345 GTK_ENTRY(xterm_here_entry)));
348 static void save_options()
350 char *menurc;
352 menurc = choices_find_path_save("menus", "ROX-Filer", TRUE);
353 if (menurc)
354 gtk_item_factory_dump_rc(menurc, NULL, TRUE);
356 option_write("xterm_here", xterm_here_value);
360 static void items_sensitive(gboolean state)
362 int n = 7;
363 GList *items, *item;
365 items = item = gtk_container_children(GTK_CONTAINER(filer_file_menu));
366 while (item && n--)
368 gtk_widget_set_sensitive(GTK_BIN(item->data)->child, state);
369 item = item->next;
371 g_list_free(items);
373 items = item = gtk_container_children(GTK_CONTAINER(filer_vfs_menu));
374 while (item)
376 gtk_widget_set_sensitive(GTK_BIN(item->data)->child, state);
377 item = item->next;
379 g_list_free(items);
382 static void position_menu(GtkMenu *menu, gint *x, gint *y, gpointer data)
384 int *pos = (int *) data;
385 GtkRequisition requisition;
387 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
389 if (pos[0] == -1)
390 *x = screen_width - MENU_MARGIN - requisition.width;
391 else if (pos[0] == -2)
392 *x = MENU_MARGIN;
393 else
394 *x = pos[0] - (requisition.width >> 2);
396 if (pos[1] == -1)
397 *y = screen_height - MENU_MARGIN - requisition.height;
398 else if (pos[1] == -2)
399 *y = MENU_MARGIN;
400 else
401 *y = pos[1] - (requisition.height >> 2);
403 *x = CLAMP(*x, 0, screen_width - requisition.width);
404 *y = CLAMP(*y, 0, screen_height - requisition.height);
407 void show_filer_menu(FilerWindow *filer_window, GdkEventButton *event,
408 int item)
410 DirItem *file_item;
411 int pos[2];
413 updating_menu++;
415 pos[0] = event->x_root;
416 pos[1] = event->y_root;
418 window_with_focus = filer_window;
420 switch (filer_window->panel_type)
422 case PANEL_TOP:
423 pos[1] = -2;
424 break;
425 case PANEL_BOTTOM:
426 pos[1] = -1;
427 break;
428 default:
429 break;
432 if (filer_window->panel_type)
433 collection_clear_selection(filer_window->collection); /* ??? */
435 if (filer_window->collection->number_selected == 0 && item >= 0)
437 collection_select_item(filer_window->collection, item);
438 filer_window->temp_item_selected = TRUE;
440 else
441 filer_window->temp_item_selected = FALSE;
443 if (filer_window->panel_type)
445 gtk_check_menu_item_set_active(
446 GTK_CHECK_MENU_ITEM(panel_hidden_menu),
447 filer_window->show_hidden);
449 else
451 GtkWidget *file_label, *file_menu;
452 Collection *collection = filer_window->collection;
453 GString *buffer;
455 file_label = filer_file_item;
456 file_menu = filer_file_menu;
457 gtk_check_menu_item_set_active(
458 GTK_CHECK_MENU_ITEM(filer_hidden_menu),
459 filer_window->show_hidden);
460 buffer = g_string_new(NULL);
461 switch (collection->number_selected)
463 case 0:
464 g_string_assign(buffer, "Next Click");
465 items_sensitive(TRUE);
466 break;
467 case 1:
468 items_sensitive(TRUE);
469 file_item = selected_item(
470 filer_window->collection);
471 g_string_sprintf(buffer, "%s '%s'",
472 basetype_name(file_item),
473 file_item->leafname);
474 break;
475 default:
476 items_sensitive(FALSE);
477 g_string_sprintf(buffer, "%d items",
478 collection->number_selected);
479 break;
481 gtk_label_set_text(GTK_LABEL(file_label), buffer->str);
482 g_string_free(buffer, TRUE);
486 gtk_widget_set_sensitive(filer_new_window, !o_unique_filer_windows);
488 if (filer_window->panel_type)
489 popup_menu = panel_menu;
490 else
491 popup_menu = (event->state & GDK_CONTROL_MASK)
492 ? filer_file_menu
493 : filer_menu;
495 updating_menu--;
497 gtk_menu_popup(GTK_MENU(popup_menu), NULL, NULL, position_menu,
498 (gpointer) pos, event->button, event->time);
501 static void menu_closed(GtkWidget *widget)
503 if (window_with_focus == NULL || widget != popup_menu)
504 return; /* Close panel item chosen? */
506 if (window_with_focus->temp_item_selected)
508 collection_clear_selection(window_with_focus->collection);
509 window_with_focus->temp_item_selected = FALSE;
513 void target_callback(Collection *collection, gint item, gpointer real_fn)
515 g_return_if_fail(window_with_focus != NULL);
516 g_return_if_fail(window_with_focus->collection == collection);
517 g_return_if_fail(real_fn != NULL);
519 collection_wink_item(collection, item);
520 collection_clear_selection(collection);
521 collection_select_item(collection, item);
522 ((CollectionTargetFunc)real_fn)(NULL, 0, collection);
523 if (item < collection->number_of_items)
524 collection_unselect_item(collection, item);
527 /* Actions */
529 /* Fake action to warn when a menu item does nothing */
530 static void not_yet(gpointer data, guint action, GtkWidget *widget)
532 delayed_error("ROX-Filer", "Sorry, that feature isn't implemented yet");
535 static void large(gpointer data, guint action, GtkWidget *widget)
537 g_return_if_fail(window_with_focus != NULL);
539 filer_style_set(window_with_focus, LARGE_ICONS);
542 static void small(gpointer data, guint action, GtkWidget *widget)
544 g_return_if_fail(window_with_focus != NULL);
546 filer_style_set(window_with_focus, SMALL_ICONS);
549 static void full_info(gpointer data, guint action, GtkWidget *widget)
551 g_return_if_fail(window_with_focus != NULL);
553 filer_style_set(window_with_focus, FULL_INFO);
556 static void sort_name(gpointer data, guint action, GtkWidget *widget)
558 g_return_if_fail(window_with_focus != NULL);
560 filer_set_sort_fn(window_with_focus, sort_by_name);
563 static void sort_type(gpointer data, guint action, GtkWidget *widget)
565 g_return_if_fail(window_with_focus != NULL);
567 filer_set_sort_fn(window_with_focus, sort_by_type);
570 static void sort_date(gpointer data, guint action, GtkWidget *widget)
572 g_return_if_fail(window_with_focus != NULL);
574 filer_set_sort_fn(window_with_focus, sort_by_date);
577 static void sort_size(gpointer data, guint action, GtkWidget *widget)
579 g_return_if_fail(window_with_focus != NULL);
581 filer_set_sort_fn(window_with_focus, sort_by_size);
584 static void hidden(gpointer data, guint action, GtkWidget *widget)
586 if (updating_menu)
587 return;
589 g_return_if_fail(window_with_focus != NULL);
591 filer_set_hidden(window_with_focus, !window_with_focus->show_hidden);
594 static void refresh(gpointer data, guint action, GtkWidget *widget)
596 g_return_if_fail(window_with_focus != NULL);
598 full_refresh();
599 update_dir(window_with_focus, TRUE);
602 static void mount(gpointer data, guint action, GtkWidget *widget)
604 g_return_if_fail(window_with_focus != NULL);
606 if (window_with_focus->collection->number_selected == 0)
607 collection_target(window_with_focus->collection,
608 target_callback, mount);
609 else
610 action_mount(window_with_focus, NULL);
613 static void delete(gpointer data, guint action, GtkWidget *widget)
615 g_return_if_fail(window_with_focus != NULL);
617 if (window_with_focus->collection->number_selected == 0)
618 collection_target(window_with_focus->collection,
619 target_callback, delete);
620 else
621 action_delete(window_with_focus);
624 static void remove_link(gpointer data, guint action, GtkWidget *widget)
626 g_return_if_fail(window_with_focus != NULL);
628 if (window_with_focus->collection->number_selected > 1)
630 report_error("ROX-Filer", "You can only remove one link "
631 "at a time");
632 return;
634 else if (window_with_focus->collection->number_selected == 0)
635 collection_target(window_with_focus->collection,
636 target_callback, remove_link);
637 else
639 struct stat info;
640 DirItem *item;
641 guchar *path;
643 item = selected_item(window_with_focus->collection);
645 path = make_path(window_with_focus->path, item->leafname)->str;
646 if (lstat(path, &info))
647 report_error("ROX-Filer", g_strerror(errno));
648 else if (!S_ISLNK(info.st_mode))
649 report_error("ROX-Filer",
650 "You can only remove symbolic links this way - "
651 "try the 'Open Panel as Directory' item.");
652 else if (unlink(path))
653 report_error("ROX-Filer", g_strerror(errno));
654 else
655 update_dir(window_with_focus, TRUE);
659 static void usage(gpointer data, guint action, GtkWidget *widget)
661 g_return_if_fail(window_with_focus != NULL);
663 if (window_with_focus->collection->number_selected == 0)
664 collection_target(window_with_focus->collection,
665 target_callback, usage);
666 else
667 action_usage(window_with_focus);
670 static void chmod_items(gpointer data, guint action, GtkWidget *widget)
672 g_return_if_fail(window_with_focus != NULL);
674 if (window_with_focus->collection->number_selected == 0)
675 collection_target(window_with_focus->collection,
676 target_callback, chmod_items);
677 else
678 action_chmod(window_with_focus);
681 static void find(gpointer data, guint action, GtkWidget *widget)
683 g_return_if_fail(window_with_focus != NULL);
685 if (window_with_focus->collection->number_selected == 0)
686 collection_target(window_with_focus->collection,
687 target_callback, find);
688 else
689 action_find(window_with_focus);
692 /* This pops up our savebox widget, cancelling any currently open one,
693 * and allows the user to pick a new path for it.
694 * Once the new path has been picked, the callback will be called with
695 * both the current and new paths.
697 static void savebox_show(guchar *title, guchar *path, MaskedPixmap *image,
698 gboolean (*callback)(guchar *current, guchar *new))
700 if (GTK_WIDGET_VISIBLE(savebox))
701 gtk_widget_hide(savebox);
703 if (current_path)
704 g_free(current_path);
705 current_path = g_strdup(path);
706 current_savebox_callback = callback;
708 gtk_window_set_title(GTK_WINDOW(savebox), title);
709 gtk_savebox_set_pathname(GTK_SAVEBOX(savebox), current_path);
710 gtk_savebox_set_icon(GTK_SAVEBOX(savebox), image->pixmap, image->mask);
712 gtk_widget_grab_focus(GTK_SAVEBOX(savebox)->entry);
713 gtk_widget_show(savebox);
716 static gint save_to_file(GtkSavebox *savebox, guchar *pathname)
718 g_return_val_if_fail(current_savebox_callback != NULL,
719 GTK_XDS_SAVE_ERROR);
721 return current_savebox_callback(current_path, pathname)
722 ? GTK_XDS_SAVED : GTK_XDS_SAVE_ERROR;
725 static gboolean copy_cb(guchar *current, guchar *new)
727 char *new_dir, *leaf;
728 GSList *local_paths;
730 if (new[0] != '/')
732 report_error("ROX-Filer", "New pathname is not absolute");
733 return FALSE;
736 if (new[strlen(new) - 1] == '/')
738 new_dir = g_strdup(new);
739 leaf = NULL;
741 else
743 guchar *slash;
745 slash = strrchr(new, '/');
746 new_dir = g_strndup(new, slash - new);
747 leaf = slash + 1;
750 local_paths = g_slist_append(NULL, current);
751 action_copy(local_paths, new_dir, leaf);
752 g_slist_free(local_paths);
754 g_free(new_dir);
756 return TRUE;
759 #define SHOW_SAVEBOX(title, callback) \
761 DirItem *item; \
762 guchar *path; \
763 item = selected_item(collection); \
764 path = make_path(window_with_focus->path, item->leafname)->str; \
765 savebox_show(title, path, item->image, callback); \
768 static void copy_item(gpointer data, guint action, GtkWidget *widget)
770 Collection *collection;
772 g_return_if_fail(window_with_focus != NULL);
774 collection = window_with_focus->collection;
775 if (collection->number_selected > 1)
777 report_error("ROX-Filer", "You cannot do this to more than "
778 "one item at a time");
779 return;
781 else if (collection->number_selected != 1)
782 collection_target(collection, target_callback, copy_item);
783 else
784 SHOW_SAVEBOX("Copy", copy_cb);
787 static gboolean rename_cb(guchar *current, guchar *new)
789 if (rename(current, new))
791 report_error("ROX-Filer: rename()", g_strerror(errno));
792 return FALSE;
794 return TRUE;
797 static void rename_item(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, rename_item);
812 else
813 SHOW_SAVEBOX("Rename", rename_cb);
816 static gboolean link_cb(guchar *initial, guchar *path)
818 if (symlink(initial, path))
820 report_error("ROX-Filer: symlink()", g_strerror(errno));
821 return FALSE;
823 return TRUE;
826 static void link_item(gpointer data, guint action, GtkWidget *widget)
828 Collection *collection;
830 g_return_if_fail(window_with_focus != NULL);
832 collection = window_with_focus->collection;
833 if (collection->number_selected > 1)
835 report_error("ROX-Filer", "You cannot do this to more than "
836 "one item at a time");
837 return;
839 else if (collection->number_selected != 1)
840 collection_target(collection, target_callback, link_item);
841 else
842 SHOW_SAVEBOX("Symlink", link_cb);
845 static void open_file(gpointer data, guint action, GtkWidget *widget)
847 Collection *collection;
849 g_return_if_fail(window_with_focus != NULL);
851 collection = window_with_focus->collection;
852 if (collection->number_selected > 1)
854 report_error("ROX-Filer", "You cannot do this to more than "
855 "one item at a time");
856 return;
858 else if (collection->number_selected != 1)
859 collection_target(collection, target_callback, open_file);
860 else
861 filer_openitem(window_with_focus,
862 selected_item_number(collection),
863 OPEN_SAME_WINDOW | OPEN_SHIFT);
866 /* Got some data from file(1) - stick it in the window. */
867 static void add_file_output(FileStatus *fs,
868 gint source, GdkInputCondition condition)
870 char buffer[20];
871 char *str;
872 int got;
874 got = read(source, buffer, sizeof(buffer) - 1);
875 if (got <= 0)
877 int err = errno;
878 gtk_input_remove(fs->input);
879 close(source);
880 fs->fd = -1;
881 if (got < 0)
882 delayed_error("ROX-Filer: file(1) says...",
883 g_strerror(err));
884 return;
886 buffer[got] = '\0';
888 if (fs->start)
890 str = "";
891 fs->start = FALSE;
893 else
894 gtk_label_get(fs->label, &str);
896 str = g_strconcat(str, buffer, NULL);
897 gtk_label_set_text(fs->label, str);
898 g_free(str);
901 static void file_info_destroyed(GtkWidget *widget, FileStatus *fs)
903 if (fs->fd != -1)
905 gtk_input_remove(fs->input);
906 close(fs->fd);
909 g_free(fs);
912 /* g_free() the result */
913 guchar *pretty_type(DirItem *file, guchar *path)
915 if (file->mime_type)
916 return g_strconcat(file->mime_type->media_type, "/",
917 file->mime_type->subtype, NULL);
919 if (file->flags & ITEM_FLAG_APPDIR)
920 return g_strdup("ROX application");
922 if (file->flags & ITEM_FLAG_SYMLINK)
924 char p[MAXPATHLEN + 1];
925 int got;
926 got = readlink(path, p, MAXPATHLEN);
927 if (got > 0 && got <= MAXPATHLEN)
929 p[got] = '\0';
930 return g_strconcat("Symbolic link to ", p, NULL);
933 return g_strdup("Symbolic link");
936 if (file->flags & ITEM_FLAG_MOUNT_POINT)
938 MountPoint *mp;
939 if ((file->flags & ITEM_FLAG_MOUNTED) &&
940 (mp = g_hash_table_lookup(mtab_mounts, path)))
941 return g_strconcat("Mount point for ", mp->name, NULL);
943 return g_strdup("Mount point");
946 return g_strdup("-");
949 #define LABEL(text, row) \
950 label = gtk_label_new(text ":"); \
951 gtk_misc_set_alignment(GTK_MISC(label), 1, .5); \
952 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_RIGHT); \
953 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, row, row + 1);
955 #define VALUE(label, row) \
956 gtk_misc_set_alignment(GTK_MISC(label), 0, .5); \
957 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, row, row + 1);
959 static void show_file_info(gpointer data, guint action, GtkWidget *widget)
961 GtkWidget *window, *table, *label, *button, *frame, *value;
962 GtkWidget *file_label;
963 GString *gstring;
964 int file_data[2];
965 guchar *path, *tmp;
966 char *argv[] = {"file", "-b", NULL, NULL};
967 Collection *collection;
968 DirItem *file;
969 struct stat info;
970 FileStatus *fs = NULL;
971 guint perm;
973 g_return_if_fail(window_with_focus != NULL);
975 collection = window_with_focus->collection;
976 if (collection->number_selected > 1)
978 report_error("ROX-Filer", "You cannot do this to more than "
979 "one item at a time");
980 return;
982 else if (collection->number_selected != 1)
984 collection_target(collection, target_callback, show_file_info);
985 return;
987 file = selected_item(collection);
988 path = make_path(window_with_focus->path,
989 file->leafname)->str;
990 if (lstat(path, &info))
992 delayed_error("ROX-Filer", g_strerror(errno));
993 return;
996 gstring = g_string_new(NULL);
998 window = gtk_window_new(GTK_WINDOW_DIALOG);
999 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_MOUSE);
1000 gtk_container_set_border_width(GTK_CONTAINER(window), 4);
1001 gtk_window_set_title(GTK_WINDOW(window), path);
1003 table = gtk_table_new(10, 2, FALSE);
1004 gtk_container_add(GTK_CONTAINER(window), table);
1005 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
1006 gtk_table_set_col_spacings(GTK_TABLE(table), 4);
1008 value = gtk_label_new(file->leafname);
1009 LABEL("Name", 0);
1010 VALUE(value, 0);
1012 g_string_sprintf(gstring, "%s, %s", user_name(info.st_uid),
1013 group_name(info.st_gid));
1014 value = gtk_label_new(gstring->str);
1015 LABEL("Owner, Group", 1);
1016 VALUE(value, 1);
1018 if (info.st_size >= PRETTY_SIZE_LIMIT)
1020 g_string_sprintf(gstring, "%s (%ld bytes)",
1021 format_size((unsigned long) info.st_size),
1022 (unsigned long) info.st_size);
1024 else
1026 g_string_sprintf(gstring, "%ld byte%s",
1027 (unsigned long) info.st_size,
1028 info.st_size == 1 ? "" : "s");
1030 value = gtk_label_new(gstring->str);
1031 LABEL("Size", 2);
1032 VALUE(value, 2);
1034 value = gtk_label_new(pretty_time(&info.st_ctime));
1035 LABEL("Change time", 3);
1036 VALUE(value, 3);
1038 value = gtk_label_new(pretty_time(&info.st_mtime));
1039 LABEL("Modify time", 4);
1040 VALUE(value, 4);
1042 value = gtk_label_new(pretty_time(&info.st_atime));
1043 LABEL("Access time", 5);
1044 VALUE(value, 5);
1046 value = gtk_label_new(pretty_permissions(info.st_mode));
1047 perm = applicable(info.st_uid, info.st_gid);
1048 gtk_label_set_pattern(GTK_LABEL(value),
1049 perm == 0 ? "___ " :
1050 perm == 1 ? " ___ " :
1051 " ___");
1052 gtk_widget_set_style(value, fixed_style);
1053 LABEL("Permissions", 6);
1054 VALUE(value, 6);
1056 tmp = pretty_type(file, path);
1057 value = gtk_label_new(tmp);
1058 g_free(tmp);
1059 LABEL("Type", 7);
1060 VALUE(value, 7);
1062 frame = gtk_frame_new("file(1) says...");
1063 gtk_table_attach_defaults(GTK_TABLE(table), frame, 0, 2, 8, 9);
1064 file_label = gtk_label_new("<nothing yet>");
1065 gtk_misc_set_padding(GTK_MISC(file_label), 4, 4);
1066 gtk_label_set_line_wrap(GTK_LABEL(file_label), TRUE);
1067 gtk_container_add(GTK_CONTAINER(frame), file_label);
1069 button = gtk_button_new_with_label("OK");
1070 gtk_window_set_focus(GTK_WINDOW(window), button);
1071 gtk_table_attach(GTK_TABLE(table), button, 0, 2, 10, 11,
1072 GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 40, 4);
1073 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
1074 gtk_widget_destroy, GTK_OBJECT(window));
1076 gtk_widget_show_all(window);
1077 gdk_flush();
1079 if (pipe(file_data))
1081 g_string_sprintf(gstring, "pipe(): %s", g_strerror(errno));
1082 g_string_free(gstring, TRUE);
1083 return;
1085 switch (fork())
1087 case -1:
1088 g_string_sprintf(gstring, "fork(): %s",
1089 g_strerror(errno));
1090 gtk_label_set_text(GTK_LABEL(file_label), gstring->str);
1091 g_string_free(gstring, TRUE);
1092 close(file_data[0]);
1093 close(file_data[1]);
1094 break;
1095 case 0:
1096 /* We are the child */
1097 close(file_data[0]);
1098 dup2(file_data[1], STDOUT_FILENO);
1099 dup2(file_data[1], STDERR_FILENO);
1100 #ifdef FILE_B_FLAG
1101 argv[2] = path;
1102 #else
1103 argv[1] = file->leafname;
1104 chdir(window_with_focus->path);
1105 #endif
1106 if (execvp(argv[0], argv))
1107 fprintf(stderr, "execvp() error: %s\n",
1108 g_strerror(errno));
1109 _exit(0);
1110 default:
1111 /* We are the parent */
1112 close(file_data[1]);
1113 fs = g_new(FileStatus, 1);
1114 fs->label = GTK_LABEL(file_label);
1115 fs->fd = file_data[0];
1116 fs->start = TRUE;
1117 fs->input = gdk_input_add(fs->fd, GDK_INPUT_READ,
1118 (GdkInputFunction) add_file_output, fs);
1119 gtk_signal_connect(GTK_OBJECT(window), "destroy",
1120 GTK_SIGNAL_FUNC(file_info_destroyed), fs);
1121 g_string_free(gstring, TRUE);
1122 break;
1126 static void app_show_help(char *path)
1128 char *help_dir;
1129 struct stat info;
1131 help_dir = g_strconcat(path, "/Help", NULL);
1133 if (mc_stat(help_dir, &info))
1134 delayed_error("Application",
1135 "This is an application directory - you can "
1136 "run it as a program, or open it (hold down "
1137 "Shift while you open it). Most applications provide "
1138 "their own help here, but this one doesn't.");
1139 else
1140 filer_opendir(help_dir, PANEL_NO);
1143 static void help(gpointer data, guint action, GtkWidget *widget)
1145 Collection *collection;
1146 DirItem *item;
1148 g_return_if_fail(window_with_focus != NULL);
1150 collection = window_with_focus->collection;
1151 if (collection->number_selected > 1)
1153 report_error("ROX-Filer", "You cannot do this to more than "
1154 "one item at a time");
1155 return;
1157 else if (collection->number_selected != 1)
1159 collection_target(collection, target_callback, help);
1160 return;
1162 item = selected_item(collection);
1163 switch (item->base_type)
1165 case TYPE_FILE:
1166 if (item->flags & ITEM_FLAG_EXEC_FILE)
1167 delayed_error("Executable file",
1168 "This is a file with an eXecute bit "
1169 "set - it can be run as a program.");
1170 else
1171 delayed_error("File",
1172 "This is a data file. Try using the "
1173 "Info menu item to find out more...");
1174 break;
1175 case TYPE_DIRECTORY:
1176 if (item->flags & ITEM_FLAG_APPDIR)
1177 app_show_help(
1178 make_path(window_with_focus->path,
1179 item->leafname)->str);
1180 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
1181 delayed_error("Mount point",
1182 "A mount point is a directory which another "
1183 "filing system can be mounted on. Everything "
1184 "on the mounted filesystem then appears to be "
1185 "inside the directory.");
1186 else
1187 delayed_error("Directory",
1188 "This is a directory. It contains an index to "
1189 "other items - open it to see the list.");
1190 break;
1191 case TYPE_CHAR_DEVICE:
1192 case TYPE_BLOCK_DEVICE:
1193 delayed_error("Device file",
1194 "Device files allow you to read from or write "
1195 "to a device driver as though it was an "
1196 "ordinary file.");
1197 break;
1198 case TYPE_PIPE:
1199 delayed_error("Named pipe",
1200 "Pipes allow different programs to "
1201 "communicate. One program writes data to the "
1202 "pipe while another one reads it out again.");
1203 break;
1204 case TYPE_SOCKET:
1205 delayed_error("Socket",
1206 "Sockets allow processes to communicate.");
1207 break;
1208 default:
1209 delayed_error("Unknown type",
1210 "I couldn't find out what kind of file this "
1211 "is. Maybe it doesn't exist anymore or you "
1212 "don't have search permission on the directory "
1213 "it's in?");
1214 break;
1218 #define OPEN_VFS(fs) \
1219 static void open_vfs_ ## fs (gpointer data, guint action, GtkWidget *widget) \
1221 Collection *collection; \
1223 g_return_if_fail(window_with_focus != NULL); \
1225 collection = window_with_focus->collection; \
1226 if (collection->number_selected < 1) \
1227 collection_target(collection, target_callback, \
1228 open_vfs_ ## fs); \
1229 else \
1230 real_vfs_open(#fs); \
1233 static void real_vfs_open(char *fs)
1235 gchar *path;
1236 DirItem *item;
1238 if (window_with_focus->collection->number_selected != 1)
1240 report_error("ROX-Filer", "You must select a single file "
1241 "to open as a Virtual File System");
1242 return;
1245 item = selected_item(window_with_focus->collection);
1247 path = g_strconcat(window_with_focus->path,
1248 "/",
1249 item->leafname,
1250 "#", fs, NULL);
1252 filer_change_to(window_with_focus, path, NULL);
1253 g_free(path);
1256 OPEN_VFS(rpm)
1257 OPEN_VFS(utar)
1258 OPEN_VFS(uzip)
1260 static void select_all(gpointer data, guint action, GtkWidget *widget)
1262 g_return_if_fail(window_with_focus != NULL);
1264 collection_select_all(window_with_focus->collection);
1265 window_with_focus->temp_item_selected = FALSE;
1268 static void clear_selection(gpointer data, guint action, GtkWidget *widget)
1270 g_return_if_fail(window_with_focus != NULL);
1272 collection_clear_selection(window_with_focus->collection);
1273 window_with_focus->temp_item_selected = FALSE;
1276 static void show_options(gpointer data, guint action, GtkWidget *widget)
1278 g_return_if_fail(window_with_focus != NULL);
1280 options_show(window_with_focus);
1283 static gboolean new_directory_cb(guchar *initial, guchar *path)
1285 if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO))
1287 report_error("mkdir", g_strerror(errno));
1288 return FALSE;
1290 return TRUE;
1293 static void new_directory(gpointer data, guint action, GtkWidget *widget)
1295 g_return_if_fail(window_with_focus != NULL);
1297 savebox_show("New Directory",
1298 make_path(window_with_focus->path, "NewDir")->str,
1299 default_pixmap + TYPE_DIRECTORY,
1300 new_directory_cb);
1303 static void xterm_here(gpointer data, guint action, GtkWidget *widget)
1305 char *argv[] = {NULL, NULL};
1307 argv[0] = xterm_here_value;
1309 g_return_if_fail(window_with_focus != NULL);
1311 if (!spawn_full(argv, window_with_focus->path))
1312 report_error("ROX-Filer", "Failed to fork() child "
1313 "process");
1316 static void open_parent(gpointer data, guint action, GtkWidget *widget)
1318 char *copy;
1319 char *slash;
1321 g_return_if_fail(window_with_focus != NULL);
1323 if (window_with_focus->path[0] == '/'
1324 && window_with_focus->path[1] == '\0')
1325 return; /* Already in the root */
1327 copy = g_strdup(window_with_focus->path);
1328 slash = strrchr(copy, '/');
1330 if (slash)
1332 *slash = '\0';
1333 filer_opendir(*copy ? copy : "/", PANEL_NO);
1335 else
1336 g_warning("No / in directory path!\n");
1338 g_free(copy);
1341 static void open_parent_same(gpointer data, guint action, GtkWidget *widget)
1343 g_return_if_fail(window_with_focus != NULL);
1345 change_to_parent(window_with_focus);
1348 static void new_window(gpointer data, guint action, GtkWidget *widget)
1350 g_return_if_fail(window_with_focus != NULL);
1352 if (o_unique_filer_windows)
1353 report_error("ROX-Filer", "You can't open a second view onto "
1354 "this directory because the `Unique Windows' option "
1355 "is turned on in the Options window.");
1356 else
1357 filer_opendir(window_with_focus->path, PANEL_NO);
1360 static void close_window(gpointer data, guint action, GtkWidget *widget)
1362 g_return_if_fail(window_with_focus != NULL);
1364 gtk_widget_destroy(window_with_focus->window);
1367 static void enter_path(gpointer data, guint action, GtkWidget *widget)
1369 g_return_if_fail(window_with_focus != NULL);
1371 minibuffer_show(window_with_focus);
1374 static void rox_help(gpointer data, guint action, GtkWidget *widget)
1376 g_return_if_fail(window_with_focus != NULL);
1378 filer_opendir(make_path(getenv("APP_DIR"), "Help")->str, PANEL_NO);
1381 static void open_as_dir(gpointer data, guint action, GtkWidget *widget)
1383 g_return_if_fail(window_with_focus != NULL);
1385 filer_opendir(window_with_focus->path, PANEL_NO);
1388 static void close_panel(gpointer data, guint action, GtkWidget *widget)
1390 g_return_if_fail(window_with_focus != NULL);
1392 gtk_widget_destroy(window_with_focus->window);