r182: Changed the Tab completion in the minibuffer to the much nicer shell-style
[rox-filer.git] / ROX-Filer / src / menu.c
bloba5ea4fc51f997bb4e122fa9e3f8d2e354bf19681
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 1999, 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 <fcntl.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <time.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 /* Options */
61 static GtkWidget *xterm_here_entry;
62 static char *xterm_here_value;
64 /* Static prototypes */
66 static void position_menu(GtkMenu *menu, gint *x, gint *y, gpointer data);
67 static void menu_closed(GtkWidget *widget);
68 static void items_sensitive(GtkWidget *menu, int from, int n, gboolean state);
69 static char *load_xterm_here(char *data);
71 /* Note that for these callbacks none of the arguments are used. */
72 static void not_yet(gpointer data, guint action, GtkWidget *widget);
74 static void large(gpointer data, guint action, GtkWidget *widget);
75 static void small(gpointer data, guint action, GtkWidget *widget);
76 static void full_info(gpointer data, guint action, GtkWidget *widget);
78 static void sort_name(gpointer data, guint action, GtkWidget *widget);
79 static void sort_type(gpointer data, guint action, GtkWidget *widget);
80 static void sort_size(gpointer data, guint action, GtkWidget *widget);
81 static void sort_date(gpointer data, guint action, GtkWidget *widget);
83 static void hidden(gpointer data, guint action, GtkWidget *widget);
84 static void refresh(gpointer data, guint action, GtkWidget *widget);
86 static void copy_item(gpointer data, guint action, GtkWidget *widget);
87 static void rename_item(gpointer data, guint action, GtkWidget *widget);
88 static void link_item(gpointer data, guint action, GtkWidget *widget);
89 static void open_file(gpointer data, guint action, GtkWidget *widget);
90 static void help(gpointer data, guint action, GtkWidget *widget);
91 static void show_file_info(gpointer data, guint action, GtkWidget *widget);
92 static void mount(gpointer data, guint action, GtkWidget *widget);
93 static void delete(gpointer data, guint action, GtkWidget *widget);
94 static void usage(gpointer data, guint action, GtkWidget *widget);
96 static void select_all(gpointer data, guint action, GtkWidget *widget);
97 static void clear_selection(gpointer data, guint action, GtkWidget *widget);
98 static void show_options(gpointer data, guint action, GtkWidget *widget);
99 static void new_directory(gpointer data, guint action, GtkWidget *widget);
100 static void xterm_here(gpointer data, guint action, GtkWidget *widget);
102 static void open_parent_same(gpointer data, guint action, GtkWidget *widget);
103 static void open_parent(gpointer data, guint action, GtkWidget *widget);
104 static void new_window(gpointer data, guint action, GtkWidget *widget);
105 static void close_window(gpointer data, guint action, GtkWidget *widget);
106 static void enter_path(gpointer data, guint action, GtkWidget *widget);
107 static void rox_help(gpointer data, guint action, GtkWidget *widget);
109 static void open_as_dir(gpointer data, guint action, GtkWidget *widget);
110 static void close_panel(gpointer data, guint action, GtkWidget *widget);
112 static GtkWidget *create_options();
113 static void update_options();
114 static void set_options();
115 static void save_options();
117 static OptionsSection options =
119 "Menu options",
120 create_options,
121 update_options,
122 set_options,
123 save_options
127 static GtkWidget *filer_menu; /* The popup filer menu */
128 static GtkWidget *filer_file_item; /* The File '' label */
129 static GtkWidget *filer_file_menu; /* The File '' menu */
130 static GtkWidget *filer_hidden_menu; /* The Show Hidden item */
131 static GtkWidget *panel_menu; /* The popup panel menu */
132 static GtkWidget *panel_file_item; /* The File '' label */
133 static GtkWidget *panel_file_menu; /* The File '' menu */
134 static GtkWidget *panel_hidden_menu; /* The Show Hidden item */
136 static gint screen_width, screen_height;
138 static GtkItemFactoryEntry filer_menu_def[] = {
139 {"/Display", NULL, NULL, 0, "<Branch>"},
140 {"/Display/Large Icons", NULL, large, 0, "<RadioItem>"},
141 {"/Display/Small Icons", NULL, small, 0, "/Display/Large Icons"},
142 {"/Display/Full Info", NULL, full_info, 0, "/Display/Large Icons"},
143 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
144 {"/Display/Sort by Name", NULL, sort_name, 0, "<RadioItem>"},
145 {"/Display/Sort by Type", NULL, sort_type, 0, "/Display/Sort by Name"},
146 {"/Display/Sort by Date", NULL, sort_date, 0, "/Display/Sort by Name"},
147 {"/Display/Sort by Size", NULL, sort_size, 0, "/Display/Sort by Name"},
148 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
149 {"/Display/Show Hidden", C_"H", hidden, 0, "<ToggleItem>"},
150 {"/Display/Refresh", C_"L", refresh, 0, NULL},
151 {"/File", NULL, NULL, 0, "<Branch>"},
152 {"/File/Copy...", NULL, copy_item, 0, NULL},
153 {"/File/Rename...", NULL, rename_item, 0, NULL},
154 {"/File/Link...", NULL, link_item, 0, NULL},
155 {"/File/Shift Open", NULL, open_file, 0, NULL},
156 {"/File/Help", "F1", help, 0, NULL},
157 {"/File/Info", "I", show_file_info, 0, NULL},
158 {"/File/Separator", NULL, NULL, 0, "<Separator>"},
159 {"/File/Mount", "M", mount, 0, NULL},
160 {"/File/Delete", C_"X", delete, 0, NULL},
161 {"/File/Disk Usage", "U", usage, 0, NULL},
162 {"/File/Permissions", NULL, not_yet, 0, NULL},
163 {"/File/Touch", NULL, not_yet, 0, NULL},
164 {"/File/Find", NULL, not_yet, 0, NULL},
165 {"/Select All", C_"A", select_all, 0, NULL},
166 {"/Clear Selection", C_"Z", clear_selection, 0, NULL},
167 {"/Options...", NULL, show_options, 0, NULL},
168 {"/New Directory...", NULL, new_directory, 0, NULL},
169 {"/Xterm Here", NULL, xterm_here, 0, NULL},
170 {"/Window", NULL, NULL, 0, "<Branch>"},
171 {"/Window/Parent, New Window", NULL, open_parent, 0, NULL},
172 {"/Window/Parent, Same Window", NULL, open_parent_same, 0, NULL},
173 {"/Window/New Window", NULL, new_window, 0, NULL},
174 {"/Window/Close Window", C_"Q", close_window, 0, NULL},
175 {"/Window/Enter Path", NULL, enter_path, 0, NULL},
176 {"/Window/Separator", NULL, NULL, 0, "<Separator>"},
177 {"/Window/Show ROX-Filer help", NULL, rox_help, 0, NULL},
180 static GtkItemFactoryEntry panel_menu_def[] = {
181 {"/Display", NULL, NULL, 0, "<Branch>"},
182 {"/Display/Large Icons", NULL, large, 0, "<RadioItem>"},
183 {"/Display/Small Icons", NULL, small, 0, "/Display/Large Icons"},
184 {"/Display/Full Info", NULL, full_info, 0, "/Display/Large Icons"},
185 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
186 {"/Display/Sort by Name", NULL, sort_name, 0, "<RadioItem>"},
187 {"/Display/Sort by Type", NULL, sort_type, 0, "/Display/Sort by Name"},
188 {"/Display/Sort by Date", NULL, sort_date, 0, "/Display/Sort by Name"},
189 {"/Display/Sort by Size", NULL, sort_size, 0, "/Display/Sort by Name"},
190 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
191 {"/Display/Show Hidden", NULL, hidden, 0, "<ToggleItem>"},
192 {"/Display/Refresh", NULL, refresh, 0, NULL},
193 {"/File", NULL, NULL, 0, "<Branch>"},
194 {"/File/Help", NULL, help, 0, NULL},
195 {"/File/Info", NULL, show_file_info, 0, NULL},
196 {"/File/Delete", NULL, delete, 0, NULL},
197 {"/Open as directory", NULL, open_as_dir, 0, NULL},
198 {"/Options...", NULL, show_options, 0, NULL},
199 {"/Close panel", NULL, close_panel, 0, NULL},
200 {"/Separator", NULL, NULL, 0, "<Separator>"},
201 {"/Show ROX-Filer help", NULL, rox_help, 0, NULL},
204 typedef struct _FileStatus FileStatus;
206 /* This is for the 'file(1) says...' thing */
207 struct _FileStatus
209 int fd; /* FD to read from, -1 if closed */
210 int input; /* Input watcher tag if fd valid */
211 GtkLabel *label; /* Widget to output to */
212 gboolean start; /* No output yet */
215 void menu_init()
217 GtkItemFactory *item_factory;
218 char *menurc;
219 GList *items;
221 /* This call starts returning strange values after a while, so get
222 * the result here during init.
224 gdk_window_get_size(GDK_ROOT_PARENT(), &screen_width, &screen_height);
226 filer_keys = gtk_accel_group_new();
227 item_factory = gtk_item_factory_new(GTK_TYPE_MENU,
228 "<filer>",
229 filer_keys);
230 gtk_item_factory_create_items(item_factory,
231 sizeof(filer_menu_def) / sizeof(*filer_menu_def),
232 filer_menu_def,
233 NULL);
234 filer_menu = gtk_item_factory_get_widget(item_factory, "<filer>");
235 filer_file_menu = gtk_item_factory_get_widget(item_factory,
236 "<filer>/File");
237 filer_hidden_menu = gtk_item_factory_get_widget(item_factory,
238 "<filer>/Display/Show Hidden");
239 items = gtk_container_children(GTK_CONTAINER(filer_menu));
240 filer_file_item = GTK_BIN(g_list_nth(items, 1)->data)->child;
241 g_list_free(items);
243 panel_keys = gtk_accel_group_new();
244 item_factory = gtk_item_factory_new(GTK_TYPE_MENU,
245 "<panel>",
246 panel_keys);
247 gtk_item_factory_create_items(item_factory,
248 sizeof(panel_menu_def) / sizeof(*panel_menu_def),
249 panel_menu_def,
250 NULL);
251 panel_menu = gtk_item_factory_get_widget(item_factory, "<panel>");
252 panel_file_menu = gtk_item_factory_get_widget(item_factory,
253 "<panel>/File");
254 panel_hidden_menu = gtk_item_factory_get_widget(item_factory,
255 "<panel>/Display/Show Hidden");
256 items = gtk_container_children(GTK_CONTAINER(panel_menu));
257 panel_file_item = GTK_BIN(g_list_nth(items, 1)->data)->child;
258 g_list_free(items);
260 menurc = choices_find_path_load("menus");
261 if (menurc)
262 gtk_item_factory_parse_rc(menurc);
264 gtk_accel_group_lock(panel_keys);
266 gtk_signal_connect(GTK_OBJECT(filer_menu), "unmap_event",
267 GTK_SIGNAL_FUNC(menu_closed), NULL);
268 gtk_signal_connect(GTK_OBJECT(panel_menu), "unmap_event",
269 GTK_SIGNAL_FUNC(menu_closed), NULL);
270 gtk_signal_connect(GTK_OBJECT(filer_file_menu), "unmap_event",
271 GTK_SIGNAL_FUNC(menu_closed), NULL);
273 options_sections = g_slist_prepend(options_sections, &options);
274 xterm_here_value = g_strdup("xterm");
275 option_register("xterm_here", load_xterm_here);
278 /* Build up some option widgets to go in the options dialog, but don't
279 * fill them in yet.
281 static GtkWidget *create_options()
283 GtkWidget *table, *label;
285 table = gtk_table_new(2, 2, FALSE);
286 gtk_container_set_border_width(GTK_CONTAINER(table), 4);
288 label = gtk_label_new("To set the keyboard short-cuts you simply open "
289 "the menu over a filer window, move the pointer over "
290 "the item you want to use and press a key. The key "
291 "will appear next to the menu item and you can just "
292 "press that key without opening the menu in future. "
293 "To save the current menu short-cuts for next time, "
294 "click the Save button at the bottom of this window.");
295 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
296 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 2, 0, 1);
298 label = gtk_label_new("'Xterm here' program:");
299 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
300 xterm_here_entry = gtk_entry_new();
301 gtk_table_attach_defaults(GTK_TABLE(table), xterm_here_entry,
302 1, 2, 1, 2);
304 return table;
307 static char *load_xterm_here(char *data)
309 g_free(xterm_here_value);
310 xterm_here_value = g_strdup(data);
311 return NULL;
314 static void update_options()
316 gtk_entry_set_text(GTK_ENTRY(xterm_here_entry), xterm_here_value);
319 static void set_options()
321 g_free(xterm_here_value);
322 xterm_here_value = g_strdup(gtk_entry_get_text(
323 GTK_ENTRY(xterm_here_entry)));
326 static void save_options()
328 char *menurc;
330 menurc = choices_find_path_save("menus", TRUE);
331 if (menurc)
332 gtk_item_factory_dump_rc(menurc, NULL, TRUE);
334 option_write("xterm_here", xterm_here_value);
338 static void items_sensitive(GtkWidget *menu, int from, int n, gboolean state)
340 GList *items, *item;
342 items = gtk_container_children(GTK_CONTAINER(menu));
344 item = g_list_nth(items, from);
345 while (item && n--)
347 gtk_widget_set_sensitive(GTK_BIN(item->data)->child, state);
348 item = item->next;
351 g_list_free(items);
354 static void position_menu(GtkMenu *menu, gint *x, gint *y, gpointer data)
356 int *pos = (int *) data;
357 GtkRequisition requisition;
359 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
361 if (pos[0] == -1)
362 *x = screen_width - MENU_MARGIN - requisition.width;
363 else if (pos[0] == -2)
364 *x = MENU_MARGIN;
365 else
366 *x = pos[0] - (requisition.width >> 2);
368 if (pos[1] == -1)
369 *y = screen_height - MENU_MARGIN - requisition.height;
370 else if (pos[1] == -2)
371 *y = MENU_MARGIN;
372 else
373 *y = pos[1] - (requisition.height >> 2);
375 *x = CLAMP(*x, 0, screen_width - requisition.width);
376 *y = CLAMP(*y, 0, screen_height - requisition.height);
379 void show_filer_menu(FilerWindow *filer_window, GdkEventButton *event,
380 int item)
382 GString *buffer;
383 GtkWidget *file_label, *file_menu;
384 DirItem *file_item;
385 int pos[2];
387 pos[0] = event->x_root;
388 pos[1] = event->y_root;
390 window_with_focus = filer_window;
392 if (filer_window->panel)
394 switch (filer_window->panel_side)
396 case TOP: pos[1] = -2; break;
397 case BOTTOM: pos[1] = -1; break;
398 case LEFT: pos[0] = -2; break;
399 case RIGHT: pos[0] = -1; break;
403 if (filer_window->panel)
404 collection_clear_selection(filer_window->collection); /* ??? */
406 if (filer_window->collection->number_selected == 0 && item >= 0)
408 collection_select_item(filer_window->collection, item);
409 filer_window->temp_item_selected = TRUE;
411 else
412 filer_window->temp_item_selected = FALSE;
414 if (filer_window->panel)
416 file_label = panel_file_item;
417 file_menu = panel_file_menu;
419 gtk_check_menu_item_set_active(
420 GTK_CHECK_MENU_ITEM(panel_hidden_menu),
421 filer_window->show_hidden);
423 else
425 file_label = filer_file_item;
426 file_menu = filer_file_menu;
428 gtk_check_menu_item_set_active(
429 GTK_CHECK_MENU_ITEM(filer_hidden_menu),
430 filer_window->show_hidden);
433 buffer = g_string_new(NULL);
434 switch (filer_window->collection->number_selected)
436 case 0:
437 g_string_assign(buffer, "Next Click");
438 items_sensitive(file_menu, 0, 6, TRUE);
439 break;
440 case 1:
441 items_sensitive(file_menu, 0, 6, TRUE);
442 file_item = selected_item(filer_window->collection);
443 g_string_sprintf(buffer, "%s '%s'",
444 basetype_name(file_item),
445 file_item->leafname);
446 break;
447 default:
448 items_sensitive(file_menu, 0, 6, FALSE);
449 g_string_sprintf(buffer, "%d items",
450 filer_window->collection->number_selected);
451 break;
454 gtk_label_set_text(GTK_LABEL(file_label), buffer->str);
456 g_string_free(buffer, TRUE);
458 if (filer_window->panel)
459 popup_menu = panel_menu;
460 else
461 popup_menu = (event->state & GDK_CONTROL_MASK)
462 ? filer_file_menu
463 : filer_menu;
465 gtk_menu_popup(GTK_MENU(popup_menu), NULL, NULL, position_menu,
466 (gpointer) pos, event->button, event->time);
469 static void menu_closed(GtkWidget *widget)
471 if (window_with_focus == NULL || widget != popup_menu)
472 return; /* Close panel item chosen? */
474 if (window_with_focus->temp_item_selected)
476 collection_clear_selection(window_with_focus->collection);
477 window_with_focus->temp_item_selected = FALSE;
481 void target_callback(Collection *collection, gint item, gpointer real_fn)
483 g_return_if_fail(window_with_focus != NULL);
484 g_return_if_fail(window_with_focus->collection == collection);
485 g_return_if_fail(real_fn != NULL);
487 collection_wink_item(collection, item);
488 collection_clear_selection(collection);
489 collection_select_item(collection, item);
490 ((CollectionTargetFunc)real_fn)(NULL, 0, collection);
491 if (item < collection->number_of_items)
492 collection_unselect_item(collection, item);
495 /* Actions */
497 /* Fake action to warn when a menu item does nothing */
498 static void not_yet(gpointer data, guint action, GtkWidget *widget)
500 delayed_error("ROX-Filer", "Sorry, that feature isn't implemented yet");
503 static void large(gpointer data, guint action, GtkWidget *widget)
505 g_return_if_fail(window_with_focus != NULL);
507 filer_style_set(window_with_focus, LARGE_ICONS);
510 static void small(gpointer data, guint action, GtkWidget *widget)
512 g_return_if_fail(window_with_focus != NULL);
514 filer_style_set(window_with_focus, SMALL_ICONS);
517 static void full_info(gpointer data, guint action, GtkWidget *widget)
519 g_return_if_fail(window_with_focus != NULL);
521 filer_style_set(window_with_focus, FULL_INFO);
524 static void sort_name(gpointer data, guint action, GtkWidget *widget)
526 g_return_if_fail(window_with_focus != NULL);
528 filer_set_sort_fn(window_with_focus, sort_by_name);
531 static void sort_type(gpointer data, guint action, GtkWidget *widget)
533 g_return_if_fail(window_with_focus != NULL);
535 filer_set_sort_fn(window_with_focus, sort_by_type);
538 static void sort_date(gpointer data, guint action, GtkWidget *widget)
540 g_return_if_fail(window_with_focus != NULL);
542 filer_set_sort_fn(window_with_focus, sort_by_date);
545 static void sort_size(gpointer data, guint action, GtkWidget *widget)
547 g_return_if_fail(window_with_focus != NULL);
549 filer_set_sort_fn(window_with_focus, sort_by_size);
552 static void hidden(gpointer data, guint action, GtkWidget *widget)
554 gboolean new;
555 GtkWidget *item;
557 g_return_if_fail(window_with_focus != NULL);
559 item = window_with_focus->panel ? panel_hidden_menu : filer_hidden_menu;
560 new = GTK_CHECK_MENU_ITEM(item)->active;
562 filer_set_hidden(window_with_focus, new);
565 static void refresh(gpointer data, guint action, GtkWidget *widget)
567 g_return_if_fail(window_with_focus != NULL);
569 full_refresh();
570 update_dir(window_with_focus, TRUE);
573 static void mount(gpointer data, guint action, GtkWidget *widget)
575 g_return_if_fail(window_with_focus != NULL);
577 if (window_with_focus->collection->number_selected == 0)
578 collection_target(window_with_focus->collection,
579 target_callback, mount);
580 else
581 action_mount(window_with_focus, NULL);
584 static void delete(gpointer data, guint action, GtkWidget *widget)
586 g_return_if_fail(window_with_focus != NULL);
588 if (window_with_focus->collection->number_selected == 0)
589 collection_target(window_with_focus->collection,
590 target_callback, delete);
591 else
592 action_delete(window_with_focus);
595 static void usage(gpointer data, guint action, GtkWidget *widget)
597 g_return_if_fail(window_with_focus != NULL);
599 if (window_with_focus->collection->number_selected == 0)
600 collection_target(window_with_focus->collection,
601 target_callback, usage);
602 else
603 action_usage(window_with_focus);
606 static gboolean copy_cb(char *initial, char *path)
608 char *new_dir, *slash;
609 int len;
610 GString *command;
611 gboolean retval = TRUE;
613 slash = strrchr(path, '/');
614 if (!slash)
616 report_error("ROX-Filer", "Missing '/' in new pathname");
617 return FALSE;
620 if (access(path, F_OK) == 0)
622 report_error("ROX-Filer",
623 "An item with this name already exists");
624 return FALSE;
627 len = slash - path;
628 new_dir = g_malloc(len + 1);
629 memcpy(new_dir, path, len);
630 new_dir[len] = '\0';
632 command = g_string_new(NULL);
633 g_string_sprintf(command, "cp -a %s %s", initial, path);
634 /* XXX: Use system. In fact, use action! */
636 if (system(command->str))
638 g_string_append(command, " failed!");
639 report_error("ROX-Filer", command->str);
640 retval = FALSE;
643 g_string_free(command, TRUE);
645 refresh_dirs(new_dir);
646 return retval;
649 static void copy_item(gpointer data, guint action, GtkWidget *widget)
651 Collection *collection;
653 g_return_if_fail(window_with_focus != NULL);
655 collection = window_with_focus->collection;
656 if (collection->number_selected > 1)
658 report_error("ROX-Filer", "You cannot do this to more than "
659 "one item at a time");
660 return;
662 else if (collection->number_selected != 1)
663 collection_target(collection, target_callback, copy_item);
664 else
666 DirItem *item = selected_item(collection);
668 savebox_show(window_with_focus, "Copy",
669 window_with_focus->path,
670 item->leafname,
671 item->image, copy_cb);
675 static gboolean rename_cb(char *initial, char *path)
677 if (rename(initial, path))
679 report_error("ROX-Filer: rename()", g_strerror(errno));
680 return FALSE;
682 return TRUE;
685 static void rename_item(gpointer data, guint action, GtkWidget *widget)
687 Collection *collection;
689 g_return_if_fail(window_with_focus != NULL);
691 collection = window_with_focus->collection;
692 if (collection->number_selected > 1)
694 report_error("ROX-Filer", "You cannot do this to more than "
695 "one item at a time");
696 return;
698 else if (collection->number_selected != 1)
699 collection_target(collection, target_callback, rename_item);
700 else
702 DirItem *item = selected_item(collection);
704 savebox_show(window_with_focus, "Rename",
705 window_with_focus->path,
706 item->leafname,
707 item->image, rename_cb);
711 static gboolean link_cb(char *initial, char *path)
713 if (symlink(initial, path))
715 report_error("ROX-Filer: symlink()", g_strerror(errno));
716 return FALSE;
718 return TRUE;
721 static void link_item(gpointer data, guint action, GtkWidget *widget)
723 Collection *collection;
725 g_return_if_fail(window_with_focus != NULL);
727 collection = window_with_focus->collection;
728 if (collection->number_selected > 1)
730 report_error("ROX-Filer", "You cannot do this to more than "
731 "one item at a time");
732 return;
734 else if (collection->number_selected != 1)
735 collection_target(collection, target_callback, link_item);
736 else
738 DirItem *item = selected_item(collection);
740 savebox_show(window_with_focus, "Symlink",
741 window_with_focus->path,
742 item->leafname,
743 item->image, link_cb);
747 static void open_file(gpointer data, guint action, GtkWidget *widget)
749 Collection *collection;
751 g_return_if_fail(window_with_focus != NULL);
753 collection = window_with_focus->collection;
754 if (collection->number_selected > 1)
756 report_error("ROX-Filer", "You cannot do this to more than "
757 "one item at a time");
758 return;
760 else if (collection->number_selected != 1)
761 collection_target(collection, target_callback, open_file);
762 else
763 filer_openitem(window_with_focus,
764 selected_item_number(collection),
765 OPEN_SAME_WINDOW | OPEN_SHIFT);
768 /* Got some data from file(1) - stick it in the window. */
769 static void add_file_output(FileStatus *fs,
770 gint source, GdkInputCondition condition)
772 char buffer[20];
773 char *str;
774 int got;
776 got = read(source, buffer, sizeof(buffer) - 1);
777 if (got <= 0)
779 int err = errno;
780 gtk_input_remove(fs->input);
781 close(source);
782 fs->fd = -1;
783 if (got < 0)
784 delayed_error("ROX-Filer: file(1) says...",
785 g_strerror(err));
786 return;
788 buffer[got] = '\0';
790 if (fs->start)
792 str = "";
793 fs->start = FALSE;
795 else
796 gtk_label_get(fs->label, &str);
798 str = g_strconcat(str, buffer, NULL);
799 gtk_label_set_text(fs->label, str);
800 g_free(str);
803 static void file_info_destroyed(GtkWidget *widget, FileStatus *fs)
805 if (fs->fd != -1)
807 gtk_input_remove(fs->input);
808 close(fs->fd);
811 g_free(fs);
814 static void show_file_info(gpointer data, guint action, GtkWidget *widget)
816 GtkWidget *window, *table, *label, *button, *frame;
817 GtkWidget *file_label;
818 GString *gstring;
819 char *string;
820 int file_data[2];
821 char *path;
822 char *argv[] = {"file", "-b", NULL, NULL};
823 Collection *collection;
824 DirItem *file;
825 struct stat info;
826 FileStatus *fs = NULL;
828 g_return_if_fail(window_with_focus != NULL);
830 collection = window_with_focus->collection;
831 if (collection->number_selected > 1)
833 report_error("ROX-Filer", "You cannot do this to more than "
834 "one item at a time");
835 return;
837 else if (collection->number_selected != 1)
839 collection_target(collection, target_callback, show_file_info);
840 return;
842 file = selected_item(collection);
843 path = make_path(window_with_focus->path,
844 file->leafname)->str;
845 if (lstat(path, &info))
847 delayed_error("ROX-Filer", g_strerror(errno));
848 return;
851 gstring = g_string_new(NULL);
853 window = gtk_window_new(GTK_WINDOW_DIALOG);
854 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_MOUSE);
855 gtk_container_set_border_width(GTK_CONTAINER(window), 4);
856 gtk_window_set_title(GTK_WINDOW(window), path);
858 table = gtk_table_new(9, 2, FALSE);
859 gtk_container_add(GTK_CONTAINER(window), table);
860 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
861 gtk_table_set_col_spacings(GTK_TABLE(table), 4);
863 label = gtk_label_new("Owner, group:");
864 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
865 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_RIGHT);
866 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
867 g_string_sprintf(gstring, "%s, %s", user_name(info.st_uid),
868 group_name(info.st_gid));
869 label = gtk_label_new(gstring->str);
870 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
871 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 0, 1);
873 label = gtk_label_new("Size:");
874 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
875 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
876 if (info.st_size >=2048)
878 g_string_sprintf(gstring, "%s (%ld bytes)",
879 format_size((unsigned long) info.st_size),
880 (unsigned long) info.st_size);
882 else
884 g_string_sprintf(gstring, "%ld bytes",
885 (unsigned long) info.st_size);
887 label = gtk_label_new(gstring->str);
888 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
889 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 1, 2);
891 label = gtk_label_new("Change time:");
892 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
893 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3);
894 g_string_sprintf(gstring, "%s", ctime(&info.st_ctime));
895 g_string_truncate(gstring, gstring->len - 1);
896 label = gtk_label_new(gstring->str);
897 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
898 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 2, 3);
900 label = gtk_label_new("Modify time:");
901 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
902 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4);
903 g_string_sprintf(gstring, "%s", ctime(&info.st_mtime));
904 g_string_truncate(gstring, gstring->len - 1);
905 label = gtk_label_new(gstring->str);
906 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
907 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 3, 4);
909 label = gtk_label_new("Access time:");
910 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
911 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 4, 5);
912 g_string_sprintf(gstring, "%s", ctime(&info.st_atime));
913 g_string_truncate(gstring, gstring->len - 1);
914 label = gtk_label_new(gstring->str);
915 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
916 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 4, 5);
918 label = gtk_label_new("Permissions:");
919 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
920 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 5, 6);
921 g_string_sprintf(gstring, "%o", (unsigned int) info.st_mode);
922 label = gtk_label_new(gstring->str);
923 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
924 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 5, 6);
926 label = gtk_label_new("MIME type:");
927 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
928 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 6, 7);
929 if (file->mime_type)
931 string = g_strconcat(file->mime_type->media_type, "/",
932 file->mime_type->subtype, NULL);
933 label = gtk_label_new(string);
934 g_free(string);
936 else
937 label = gtk_label_new("-");
938 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
939 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 6, 7);
941 frame = gtk_frame_new("file(1) says...");
942 gtk_table_attach_defaults(GTK_TABLE(table), frame, 0, 2, 7, 8);
943 file_label = gtk_label_new("<nothing yet>");
944 gtk_misc_set_padding(GTK_MISC(file_label), 4, 4);
945 gtk_label_set_line_wrap(GTK_LABEL(file_label), TRUE);
946 gtk_container_add(GTK_CONTAINER(frame), file_label);
948 button = gtk_button_new_with_label("OK");
949 gtk_window_set_focus(GTK_WINDOW(window), button);
950 gtk_table_attach(GTK_TABLE(table), button, 0, 2, 8, 9,
951 GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 40, 4);
952 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
953 gtk_widget_destroy, GTK_OBJECT(window));
955 gtk_widget_show_all(window);
956 gdk_flush();
958 if (pipe(file_data))
960 g_string_sprintf(gstring, "pipe(): %s", g_strerror(errno));
961 g_string_free(gstring, TRUE);
962 return;
964 switch (fork())
966 case -1:
967 g_string_sprintf(gstring, "fork(): %s",
968 g_strerror(errno));
969 gtk_label_set_text(GTK_LABEL(file_label), gstring->str);
970 g_string_free(gstring, TRUE);
971 close(file_data[0]);
972 close(file_data[1]);
973 break;
974 case 0:
975 /* We are the child */
976 close(file_data[0]);
977 dup2(file_data[1], STDOUT_FILENO);
978 dup2(file_data[1], STDERR_FILENO);
979 #ifdef FILE_B_FLAG
980 argv[2] = path;
981 #else
982 argv[1] = path;
983 #endif
984 if (execvp(argv[0], argv))
985 fprintf(stderr, "execvp() error: %s\n",
986 g_strerror(errno));
987 _exit(0);
988 default:
989 /* We are the parent */
990 close(file_data[1]);
991 fs = g_new(FileStatus, 1);
992 fs->label = GTK_LABEL(file_label);
993 fs->fd = file_data[0];
994 fs->start = TRUE;
995 fs->input = gdk_input_add(fs->fd, GDK_INPUT_READ,
996 (GdkInputFunction) add_file_output, fs);
997 gtk_signal_connect(GTK_OBJECT(window), "destroy",
998 GTK_SIGNAL_FUNC(file_info_destroyed), fs);
999 g_string_free(gstring, TRUE);
1000 break;
1004 static void app_show_help(char *path)
1006 char *help_dir;
1007 struct stat info;
1009 help_dir = g_strconcat(path, "/Help", NULL);
1011 if (stat(help_dir, &info))
1012 delayed_error("Application",
1013 "This is an application directory - you can "
1014 "run it as a program, or open it (hold down "
1015 "Shift while you open it). Most applications provide "
1016 "their own help here, but this one doesn't.");
1017 else
1018 filer_opendir(help_dir, FALSE, BOTTOM);
1021 static void help(gpointer data, guint action, GtkWidget *widget)
1023 Collection *collection;
1024 DirItem *item;
1026 g_return_if_fail(window_with_focus != NULL);
1028 collection = window_with_focus->collection;
1029 if (collection->number_selected > 1)
1031 report_error("ROX-Filer", "You cannot do this to more than "
1032 "one item at a time");
1033 return;
1035 else if (collection->number_selected != 1)
1037 collection_target(collection, target_callback, help);
1038 return;
1040 item = selected_item(collection);
1041 switch (item->base_type)
1043 case TYPE_FILE:
1044 if (item->flags & ITEM_FLAG_EXEC_FILE)
1045 delayed_error("Executable file",
1046 "This is a file with an eXecute bit "
1047 "set - it can be run as a program.");
1048 else
1049 delayed_error("File",
1050 "This is a data file. Try using the "
1051 "Info menu item to find out more...");
1052 break;
1053 case TYPE_DIRECTORY:
1054 if (item->flags & ITEM_FLAG_APPDIR)
1055 app_show_help(
1056 make_path(window_with_focus->path,
1057 item->leafname)->str);
1058 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
1059 delayed_error("Mount point",
1060 "A mount point is a directory which another "
1061 "filing system can be mounted on. Everything "
1062 "on the mounted filesystem then appears to be "
1063 "inside the directory.");
1064 else
1065 delayed_error("Directory",
1066 "This is a directory. It contains an index to "
1067 "other items - open it to see the list.");
1068 break;
1069 case TYPE_CHAR_DEVICE:
1070 case TYPE_BLOCK_DEVICE:
1071 delayed_error("Device file",
1072 "Device files allow you to read from or write "
1073 "to a device driver as though it was an "
1074 "ordinary file.");
1075 break;
1076 case TYPE_PIPE:
1077 delayed_error("Named pipe",
1078 "Pipes allow different programs to "
1079 "communicate. One program writes data to the "
1080 "pipe while another one reads it out again.");
1081 break;
1082 case TYPE_SOCKET:
1083 delayed_error("Socket",
1084 "Sockets allow processes to communicate.");
1085 break;
1086 default:
1087 delayed_error("Unknown type",
1088 "I couldn't find out what kind of file this "
1089 "is. Maybe it doesn't exist anymore or you "
1090 "don't have search permission on the directory "
1091 "it's in?");
1092 break;
1096 static void select_all(gpointer data, guint action, GtkWidget *widget)
1098 g_return_if_fail(window_with_focus != NULL);
1100 collection_select_all(window_with_focus->collection);
1101 window_with_focus->temp_item_selected = FALSE;
1104 static void clear_selection(gpointer data, guint action, GtkWidget *widget)
1106 g_return_if_fail(window_with_focus != NULL);
1108 collection_clear_selection(window_with_focus->collection);
1109 window_with_focus->temp_item_selected = FALSE;
1112 static void show_options(gpointer data, guint action, GtkWidget *widget)
1114 g_return_if_fail(window_with_focus != NULL);
1116 options_show(window_with_focus);
1119 static gboolean new_directory_cb(char *initial, char *path)
1121 if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO))
1123 report_error("mkdir", g_strerror(errno));
1124 return FALSE;
1126 return TRUE;
1129 static void new_directory(gpointer data, guint action, GtkWidget *widget)
1131 g_return_if_fail(window_with_focus != NULL);
1133 savebox_show(window_with_focus, "Create directory",
1134 window_with_focus->path, "NewDir",
1135 default_pixmap + TYPE_DIRECTORY, new_directory_cb);
1138 static void xterm_here(gpointer data, guint action, GtkWidget *widget)
1140 char *argv[] = {NULL, NULL};
1142 argv[0] = xterm_here_value;
1144 g_return_if_fail(window_with_focus != NULL);
1146 if (!spawn_full(argv, window_with_focus->path))
1147 report_error("ROX-Filer", "Failed to fork() child "
1148 "process");
1151 static void open_parent(gpointer data, guint action, GtkWidget *widget)
1153 g_return_if_fail(window_with_focus != NULL);
1155 filer_opendir(make_path(window_with_focus->path, "/..")->str,
1156 FALSE, BOTTOM);
1159 static void open_parent_same(gpointer data, guint action, GtkWidget *widget)
1161 g_return_if_fail(window_with_focus != NULL);
1163 change_to_parent(window_with_focus);
1166 static void new_window(gpointer data, guint action, GtkWidget *widget)
1168 g_return_if_fail(window_with_focus != NULL);
1170 filer_opendir(window_with_focus->path, FALSE, BOTTOM);
1173 static void close_window(gpointer data, guint action, GtkWidget *widget)
1175 g_return_if_fail(window_with_focus != NULL);
1177 gtk_widget_destroy(window_with_focus->window);
1180 static void enter_path(gpointer data, guint action, GtkWidget *widget)
1182 g_return_if_fail(window_with_focus != NULL);
1184 minibuffer_show(window_with_focus);
1187 static void rox_help(gpointer data, guint action, GtkWidget *widget)
1189 g_return_if_fail(window_with_focus != NULL);
1191 filer_opendir(make_path(getenv("APP_DIR"), "Help")->str, FALSE, BOTTOM);
1194 static void open_as_dir(gpointer data, guint action, GtkWidget *widget)
1196 g_return_if_fail(window_with_focus != NULL);
1198 filer_opendir(window_with_focus->path, FALSE, BOTTOM);
1201 static void close_panel(gpointer data, guint action, GtkWidget *widget)
1203 g_return_if_fail(window_with_focus != NULL);
1205 gtk_widget_destroy(window_with_focus->window);