r163: Choosing something from the selection menu when nothing is selected
[rox-filer.git] / ROX-Filer / src / menu.c
blob8d393d48fca09a1febbb7465e7f0eb107b4d2fbe
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"
50 #define C_ "<control>"
52 #define MENU_MARGIN 32
54 GtkAccelGroup *filer_keys;
55 GtkAccelGroup *panel_keys;
57 static GtkWidget *popup_menu = NULL; /* Currently open menu */
59 /* Options */
60 static GtkWidget *xterm_here_entry;
61 static char *xterm_here_value;
63 /* Static prototypes */
65 static void position_menu(GtkMenu *menu, gint *x, gint *y, gpointer data);
66 static void menu_closed(GtkWidget *widget);
67 static void items_sensitive(GtkWidget *menu, int from, int n, gboolean state);
68 static char *load_xterm_here(char *data);
70 /* Note that for these callbacks none of the arguments are used. */
71 static void not_yet(gpointer data, guint action, GtkWidget *widget);
73 static void large(gpointer data, guint action, GtkWidget *widget);
74 static void small(gpointer data, guint action, GtkWidget *widget);
75 static void full_info(gpointer data, guint action, GtkWidget *widget);
77 static void sort_name(gpointer data, guint action, GtkWidget *widget);
78 static void sort_type(gpointer data, guint action, GtkWidget *widget);
79 static void sort_size(gpointer data, guint action, GtkWidget *widget);
80 static void sort_date(gpointer data, guint action, GtkWidget *widget);
82 static void hidden(gpointer data, guint action, GtkWidget *widget);
83 static void refresh(gpointer data, guint action, GtkWidget *widget);
85 static void copy_item(gpointer data, guint action, GtkWidget *widget);
86 static void rename_item(gpointer data, guint action, GtkWidget *widget);
87 static void link_item(gpointer data, guint action, GtkWidget *widget);
88 static void open_file(gpointer data, guint action, GtkWidget *widget);
89 static void help(gpointer data, guint action, GtkWidget *widget);
90 static void show_file_info(gpointer data, guint action, GtkWidget *widget);
91 static void mount(gpointer data, guint action, GtkWidget *widget);
92 static void delete(gpointer data, guint action, GtkWidget *widget);
93 static void usage(gpointer data, guint action, GtkWidget *widget);
95 static void select_all(gpointer data, guint action, GtkWidget *widget);
96 static void clear_selection(gpointer data, guint action, GtkWidget *widget);
97 static void show_options(gpointer data, guint action, GtkWidget *widget);
98 static void new_directory(gpointer data, guint action, GtkWidget *widget);
99 static void xterm_here(gpointer data, guint action, GtkWidget *widget);
101 static void open_parent_same(gpointer data, guint action, GtkWidget *widget);
102 static void open_parent(gpointer data, guint action, GtkWidget *widget);
103 static void new_window(gpointer data, guint action, GtkWidget *widget);
104 static void close_window(gpointer data, guint action, GtkWidget *widget);
105 static void rox_help(gpointer data, guint action, GtkWidget *widget);
107 static void open_as_dir(gpointer data, guint action, GtkWidget *widget);
108 static void close_panel(gpointer data, guint action, GtkWidget *widget);
110 static GtkWidget *create_options();
111 static void update_options();
112 static void set_options();
113 static void save_options();
115 static OptionsSection options =
117 "Menu options",
118 create_options,
119 update_options,
120 set_options,
121 save_options
125 static GtkWidget *filer_menu; /* The popup filer menu */
126 static GtkWidget *filer_file_item; /* The File '' label */
127 static GtkWidget *filer_file_menu; /* The File '' menu */
128 static GtkWidget *filer_hidden_menu; /* The Show Hidden item */
129 static GtkWidget *panel_menu; /* The popup panel menu */
130 static GtkWidget *panel_file_item; /* The File '' label */
131 static GtkWidget *panel_file_menu; /* The File '' menu */
132 static GtkWidget *panel_hidden_menu; /* The Show Hidden item */
134 static gint screen_width, screen_height;
136 static GtkItemFactoryEntry filer_menu_def[] = {
137 {"/Display", NULL, NULL, 0, "<Branch>"},
138 {"/Display/Large Icons", NULL, large, 0, "<RadioItem>"},
139 {"/Display/Small Icons", NULL, small, 0, "/Display/Large Icons"},
140 {"/Display/Full Info", NULL, full_info, 0, "/Display/Large Icons"},
141 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
142 {"/Display/Sort by Name", NULL, sort_name, 0, "<RadioItem>"},
143 {"/Display/Sort by Type", NULL, sort_type, 0, "/Display/Sort by Name"},
144 {"/Display/Sort by Date", NULL, sort_date, 0, "/Display/Sort by Name"},
145 {"/Display/Sort by Size", NULL, sort_size, 0, "/Display/Sort by Name"},
146 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
147 {"/Display/Show Hidden", C_"H", hidden, 0, "<ToggleItem>"},
148 {"/Display/Refresh", C_"L", refresh, 0, NULL},
149 {"/File", NULL, NULL, 0, "<Branch>"},
150 {"/File/Copy...", NULL, copy_item, 0, NULL},
151 {"/File/Rename...", NULL, rename_item, 0, NULL},
152 {"/File/Link...", NULL, link_item, 0, NULL},
153 {"/File/Shift Open", NULL, open_file, 0, NULL},
154 {"/File/Help", "F1", help, 0, NULL},
155 {"/File/Info", "I", show_file_info, 0, NULL},
156 {"/File/Separator", NULL, NULL, 0, "<Separator>"},
157 {"/File/Mount", "M", mount, 0, NULL},
158 {"/File/Delete", C_"X", delete, 0, NULL},
159 {"/File/Disk Usage", "U", usage, 0, NULL},
160 {"/File/Permissions", NULL, not_yet, 0, NULL},
161 {"/File/Touch", NULL, not_yet, 0, NULL},
162 {"/File/Find", NULL, not_yet, 0, NULL},
163 {"/Select All", C_"A", select_all, 0, NULL},
164 {"/Clear Selection", C_"Z", clear_selection, 0, NULL},
165 {"/Options...", NULL, show_options, 0, NULL},
166 {"/New Directory...", NULL, new_directory, 0, NULL},
167 {"/Xterm Here", NULL, xterm_here, 0, NULL},
168 {"/Window", NULL, NULL, 0, "<Branch>"},
169 {"/Window/Parent, New Window", NULL, open_parent, 0, NULL},
170 {"/Window/Parent, Same Window", NULL, open_parent_same, 0, NULL},
171 {"/Window/New Window", NULL, new_window, 0, NULL},
172 {"/Window/Close Window", C_"Q", close_window, 0, NULL},
173 {"/Window/Separator", NULL, NULL, 0, "<Separator>"},
174 {"/Window/Show ROX-Filer help", NULL, rox_help, 0, NULL},
177 static GtkItemFactoryEntry panel_menu_def[] = {
178 {"/Display", NULL, NULL, 0, "<Branch>"},
179 {"/Display/Large Icons", NULL, large, 0, "<RadioItem>"},
180 {"/Display/Small Icons", NULL, small, 0, "/Display/Large Icons"},
181 {"/Display/Full Info", NULL, full_info, 0, "/Display/Large Icons"},
182 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
183 {"/Display/Sort by Name", NULL, sort_name, 0, "<RadioItem>"},
184 {"/Display/Sort by Type", NULL, sort_type, 0, "/Display/Sort by Name"},
185 {"/Display/Sort by Date", NULL, sort_date, 0, "/Display/Sort by Name"},
186 {"/Display/Sort by Size", NULL, sort_size, 0, "/Display/Sort by Name"},
187 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
188 {"/Display/Show Hidden", NULL, hidden, 0, "<ToggleItem>"},
189 {"/Display/Refresh", NULL, refresh, 0, NULL},
190 {"/File", NULL, NULL, 0, "<Branch>"},
191 {"/File/Help", NULL, help, 0, NULL},
192 {"/File/Info", NULL, show_file_info, 0, NULL},
193 {"/File/Delete", NULL, delete, 0, NULL},
194 {"/Open as directory", NULL, open_as_dir, 0, NULL},
195 {"/Close panel", NULL, close_panel, 0, NULL},
196 {"/Separator", NULL, NULL, 0, "<Separator>"},
197 {"/Show ROX-Filer help", NULL, rox_help, 0, NULL},
200 void menu_init()
202 GtkItemFactory *item_factory;
203 char *menurc;
204 GList *items;
206 /* This call starts returning strange values after a while, so get
207 * the result here during init.
209 gdk_window_get_size(GDK_ROOT_PARENT(), &screen_width, &screen_height);
211 filer_keys = gtk_accel_group_new();
212 item_factory = gtk_item_factory_new(GTK_TYPE_MENU,
213 "<filer>",
214 filer_keys);
215 gtk_item_factory_create_items(item_factory,
216 sizeof(filer_menu_def) / sizeof(*filer_menu_def),
217 filer_menu_def,
218 NULL);
219 filer_menu = gtk_item_factory_get_widget(item_factory, "<filer>");
220 filer_file_menu = gtk_item_factory_get_widget(item_factory,
221 "<filer>/File");
222 filer_hidden_menu = gtk_item_factory_get_widget(item_factory,
223 "<filer>/Display/Show Hidden");
224 items = gtk_container_children(GTK_CONTAINER(filer_menu));
225 filer_file_item = GTK_BIN(g_list_nth(items, 1)->data)->child;
226 g_list_free(items);
228 panel_keys = gtk_accel_group_new();
229 item_factory = gtk_item_factory_new(GTK_TYPE_MENU,
230 "<panel>",
231 panel_keys);
232 gtk_item_factory_create_items(item_factory,
233 sizeof(panel_menu_def) / sizeof(*panel_menu_def),
234 panel_menu_def,
235 NULL);
236 panel_menu = gtk_item_factory_get_widget(item_factory, "<panel>");
237 panel_file_menu = gtk_item_factory_get_widget(item_factory,
238 "<panel>/File");
239 panel_hidden_menu = gtk_item_factory_get_widget(item_factory,
240 "<panel>/Display/Show Hidden");
241 items = gtk_container_children(GTK_CONTAINER(panel_menu));
242 panel_file_item = GTK_BIN(g_list_nth(items, 1)->data)->child;
243 g_list_free(items);
245 menurc = choices_find_path_load("menus");
246 if (menurc)
247 gtk_item_factory_parse_rc(menurc);
249 gtk_accel_group_lock(panel_keys);
251 gtk_signal_connect(GTK_OBJECT(filer_menu), "unmap_event",
252 GTK_SIGNAL_FUNC(menu_closed), NULL);
253 gtk_signal_connect(GTK_OBJECT(panel_menu), "unmap_event",
254 GTK_SIGNAL_FUNC(menu_closed), NULL);
255 gtk_signal_connect(GTK_OBJECT(filer_file_menu), "unmap_event",
256 GTK_SIGNAL_FUNC(menu_closed), NULL);
258 options_sections = g_slist_prepend(options_sections, &options);
259 xterm_here_value = g_strdup("xterm");
260 option_register("xterm_here", load_xterm_here);
263 /* Build up some option widgets to go in the options dialog, but don't
264 * fill them in yet.
266 static GtkWidget *create_options()
268 GtkWidget *table, *label;
270 table = gtk_table_new(2, 2, FALSE);
271 gtk_container_set_border_width(GTK_CONTAINER(table), 4);
273 label = gtk_label_new("To set the keyboard short-cuts you simply open "
274 "the menu over a filer window, move the pointer over "
275 "the item you want to use and press a key. The key "
276 "will appear next to the menu item and you can just "
277 "press that key without opening the menu in future. "
278 "To save the current menu short-cuts for next time, "
279 "click the Save button at the bottom of this window.");
280 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
281 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 2, 0, 1);
283 label = gtk_label_new("'Xterm here' program:");
284 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
285 xterm_here_entry = gtk_entry_new();
286 gtk_table_attach_defaults(GTK_TABLE(table), xterm_here_entry,
287 1, 2, 1, 2);
289 return table;
292 static char *load_xterm_here(char *data)
294 g_free(xterm_here_value);
295 xterm_here_value = g_strdup(data);
296 return NULL;
299 static void update_options()
301 gtk_entry_set_text(GTK_ENTRY(xterm_here_entry), xterm_here_value);
304 static void set_options()
306 g_free(xterm_here_value);
307 xterm_here_value = g_strdup(gtk_entry_get_text(
308 GTK_ENTRY(xterm_here_entry)));
311 static void save_options()
313 char *menurc;
315 menurc = choices_find_path_save("menus", TRUE);
316 if (menurc)
317 gtk_item_factory_dump_rc(menurc, NULL, TRUE);
319 option_write("xterm_here", xterm_here_value);
323 static void items_sensitive(GtkWidget *menu, int from, int n, gboolean state)
325 GList *items, *item;
327 items = gtk_container_children(GTK_CONTAINER(menu));
329 item = g_list_nth(items, from);
330 while (item && n--)
332 gtk_widget_set_sensitive(GTK_BIN(item->data)->child, state);
333 item = item->next;
336 g_list_free(items);
339 static void position_menu(GtkMenu *menu, gint *x, gint *y, gpointer data)
341 int *pos = (int *) data;
342 GtkRequisition requisition;
344 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
346 if (pos[0] == -1)
347 *x = screen_width - MENU_MARGIN - requisition.width;
348 else if (pos[0] == -2)
349 *x = MENU_MARGIN;
350 else
351 *x = pos[0] - (requisition.width >> 2);
353 if (pos[1] == -1)
354 *y = screen_height - MENU_MARGIN - requisition.height;
355 else if (pos[1] == -2)
356 *y = MENU_MARGIN;
357 else
358 *y = pos[1] - (requisition.height >> 2);
360 *x = CLAMP(*x, 0, screen_width - requisition.width);
361 *y = CLAMP(*y, 0, screen_height - requisition.height);
364 void show_filer_menu(FilerWindow *filer_window, GdkEventButton *event,
365 int item)
367 GString *buffer;
368 GtkWidget *file_label, *file_menu;
369 DirItem *file_item;
370 int pos[2];
372 pos[0] = event->x_root;
373 pos[1] = event->y_root;
375 window_with_focus = filer_window;
377 if (filer_window->panel)
379 switch (filer_window->panel_side)
381 case TOP: pos[1] = -2; break;
382 case BOTTOM: pos[1] = -1; break;
383 case LEFT: pos[0] = -2; break;
384 case RIGHT: pos[0] = -1; break;
388 if (filer_window->panel)
389 collection_clear_selection(filer_window->collection); /* ??? */
391 if (filer_window->collection->number_selected == 0 && item >= 0)
393 collection_select_item(filer_window->collection, item);
394 filer_window->temp_item_selected = TRUE;
396 else
397 filer_window->temp_item_selected = FALSE;
399 if (filer_window->panel)
401 file_label = panel_file_item;
402 file_menu = panel_file_menu;
404 gtk_check_menu_item_set_active(
405 GTK_CHECK_MENU_ITEM(panel_hidden_menu),
406 filer_window->show_hidden);
408 else
410 file_label = filer_file_item;
411 file_menu = filer_file_menu;
413 gtk_check_menu_item_set_active(
414 GTK_CHECK_MENU_ITEM(filer_hidden_menu),
415 filer_window->show_hidden);
418 buffer = g_string_new(NULL);
419 switch (filer_window->collection->number_selected)
421 case 0:
422 g_string_assign(buffer, "Next click");
423 items_sensitive(file_menu, 0, 6, TRUE);
424 break;
425 case 1:
426 items_sensitive(file_menu, 0, 6, TRUE);
427 file_item = selected_item(filer_window->collection);
428 g_string_sprintf(buffer, "%s '%s'",
429 basetype_name(file_item),
430 file_item->leafname);
431 break;
432 default:
433 items_sensitive(file_menu, 0, 6, FALSE);
434 g_string_sprintf(buffer, "%d items",
435 filer_window->collection->number_selected);
436 break;
439 gtk_label_set_text(GTK_LABEL(file_label), buffer->str);
441 g_string_free(buffer, TRUE);
443 if (filer_window->panel)
444 popup_menu = panel_menu;
445 else
446 popup_menu = (event->state & GDK_CONTROL_MASK)
447 ? filer_file_menu
448 : filer_menu;
450 gtk_menu_popup(GTK_MENU(popup_menu), NULL, NULL, position_menu,
451 (gpointer) pos, event->button, event->time);
454 static void menu_closed(GtkWidget *widget)
456 if (window_with_focus == NULL || widget != popup_menu)
457 return; /* Close panel item chosen? */
459 if (window_with_focus->temp_item_selected)
461 collection_clear_selection(window_with_focus->collection);
462 window_with_focus->temp_item_selected = FALSE;
466 void target_callback(Collection *collection, gint item, gpointer real_fn)
468 g_return_if_fail(window_with_focus != NULL);
469 g_return_if_fail(window_with_focus->collection == collection);
470 g_return_if_fail(real_fn != NULL);
472 collection_wink_item(collection, item);
473 collection_clear_selection(collection);
474 collection_select_item(collection, item);
475 ((CollectionTargetFunc)real_fn)(NULL, 0, collection);
476 collection_unselect_item(collection, item);
479 /* Actions */
481 /* Fake action to warn when a menu item does nothing */
482 static void not_yet(gpointer data, guint action, GtkWidget *widget)
484 delayed_error("ROX-Filer", "Sorry, that feature isn't implemented yet");
487 static void large(gpointer data, guint action, GtkWidget *widget)
489 g_return_if_fail(window_with_focus != NULL);
491 filer_style_set(window_with_focus, LARGE_ICONS);
494 static void small(gpointer data, guint action, GtkWidget *widget)
496 g_return_if_fail(window_with_focus != NULL);
498 filer_style_set(window_with_focus, SMALL_ICONS);
501 static void full_info(gpointer data, guint action, GtkWidget *widget)
503 g_return_if_fail(window_with_focus != NULL);
505 filer_style_set(window_with_focus, FULL_INFO);
508 static void sort_name(gpointer data, guint action, GtkWidget *widget)
510 g_return_if_fail(window_with_focus != NULL);
512 filer_set_sort_fn(window_with_focus, sort_by_name);
515 static void sort_type(gpointer data, guint action, GtkWidget *widget)
517 g_return_if_fail(window_with_focus != NULL);
519 filer_set_sort_fn(window_with_focus, sort_by_type);
522 static void sort_date(gpointer data, guint action, GtkWidget *widget)
524 g_return_if_fail(window_with_focus != NULL);
526 filer_set_sort_fn(window_with_focus, sort_by_date);
529 static void sort_size(gpointer data, guint action, GtkWidget *widget)
531 g_return_if_fail(window_with_focus != NULL);
533 filer_set_sort_fn(window_with_focus, sort_by_size);
536 static void hidden(gpointer data, guint action, GtkWidget *widget)
538 gboolean new;
539 GtkWidget *item;
541 g_return_if_fail(window_with_focus != NULL);
543 item = window_with_focus->panel ? panel_hidden_menu : filer_hidden_menu;
544 new = GTK_CHECK_MENU_ITEM(item)->active;
546 filer_set_hidden(window_with_focus, new);
549 static void refresh(gpointer data, guint action, GtkWidget *widget)
551 g_return_if_fail(window_with_focus != NULL);
553 full_refresh();
554 update_dir(window_with_focus, TRUE);
557 static void mount(gpointer data, guint action, GtkWidget *widget)
559 g_return_if_fail(window_with_focus != NULL);
561 if (window_with_focus->collection->number_selected == 0)
562 collection_target(window_with_focus->collection,
563 target_callback, mount);
564 else
565 action_mount(window_with_focus, NULL);
568 static void delete(gpointer data, guint action, GtkWidget *widget)
570 g_return_if_fail(window_with_focus != NULL);
572 if (window_with_focus->collection->number_selected == 0)
573 collection_target(window_with_focus->collection,
574 target_callback, delete);
575 else
576 action_delete(window_with_focus);
579 static void usage(gpointer data, guint action, GtkWidget *widget)
581 g_return_if_fail(window_with_focus != NULL);
583 if (window_with_focus->collection->number_selected == 0)
584 collection_target(window_with_focus->collection,
585 target_callback, usage);
586 else
587 action_usage(window_with_focus);
590 static gboolean copy_cb(char *initial, char *path)
592 char *new_dir, *slash;
593 int len;
594 GString *command;
595 gboolean retval = TRUE;
597 slash = strrchr(path, '/');
598 if (!slash)
600 report_error("ROX-Filer", "Missing '/' in new pathname");
601 return FALSE;
604 if (access(path, F_OK) == 0)
606 report_error("ROX-Filer",
607 "An item with this name already exists");
608 return FALSE;
611 len = slash - path;
612 new_dir = g_malloc(len + 1);
613 memcpy(new_dir, path, len);
614 new_dir[len] = '\0';
616 command = g_string_new(NULL);
617 g_string_sprintf(command, "cp -a %s %s", initial, path);
619 if (system(command->str))
621 g_string_append(command, " failed!");
622 report_error("ROX-Filer", command->str);
623 retval = FALSE;
626 g_string_free(command, TRUE);
628 refresh_dirs(new_dir);
629 return retval;
632 static void copy_item(gpointer data, guint action, GtkWidget *widget)
634 Collection *collection;
636 g_return_if_fail(window_with_focus != NULL);
638 collection = window_with_focus->collection;
639 if (collection->number_selected > 1)
641 report_error("ROX-Filer", "You cannot do this to more than "
642 "one item at a time");
643 return;
645 else if (collection->number_selected != 1)
646 collection_target(collection, target_callback, copy_item);
647 else
649 DirItem *item = selected_item(collection);
651 savebox_show(window_with_focus, "Copy",
652 window_with_focus->path,
653 item->leafname,
654 item->image, copy_cb);
658 static gboolean rename_cb(char *initial, char *path)
660 if (rename(initial, path))
662 report_error("ROX-Filer: rename()", g_strerror(errno));
663 return FALSE;
665 return TRUE;
668 static void rename_item(gpointer data, guint action, GtkWidget *widget)
670 Collection *collection;
672 g_return_if_fail(window_with_focus != NULL);
674 collection = window_with_focus->collection;
675 if (collection->number_selected > 1)
677 report_error("ROX-Filer", "You cannot do this to more than "
678 "one item at a time");
679 return;
681 else if (collection->number_selected != 1)
682 collection_target(collection, target_callback, rename_item);
683 else
685 DirItem *item = selected_item(collection);
687 savebox_show(window_with_focus, "Rename",
688 window_with_focus->path,
689 item->leafname,
690 item->image, rename_cb);
694 static gboolean link_cb(char *initial, char *path)
696 if (symlink(initial, path))
698 report_error("ROX-Filer: symlink()", g_strerror(errno));
699 return FALSE;
701 return TRUE;
704 static void link_item(gpointer data, guint action, GtkWidget *widget)
706 Collection *collection;
708 g_return_if_fail(window_with_focus != NULL);
710 collection = window_with_focus->collection;
711 if (collection->number_selected > 1)
713 report_error("ROX-Filer", "You cannot do this to more than "
714 "one item at a time");
715 return;
717 else if (collection->number_selected != 1)
718 collection_target(collection, target_callback, link_item);
719 else
721 DirItem *item = selected_item(collection);
723 savebox_show(window_with_focus, "Symlink",
724 window_with_focus->path,
725 item->leafname,
726 item->image, link_cb);
730 static void open_file(gpointer data, guint action, GtkWidget *widget)
732 Collection *collection;
734 g_return_if_fail(window_with_focus != NULL);
736 collection = window_with_focus->collection;
737 if (collection->number_selected > 1)
739 report_error("ROX-Filer", "You cannot do this to more than "
740 "one item at a time");
741 return;
743 else if (collection->number_selected != 1)
744 collection_target(collection, target_callback, open_file);
745 else
746 filer_openitem(window_with_focus, selected_item(collection),
747 TRUE, FALSE);
750 static void show_file_info(gpointer data, guint action, GtkWidget *widget)
752 GtkWidget *window, *table, *label, *button, *frame;
753 GtkWidget *file_label;
754 GString *gstring;
755 char *string;
756 int file_data[2];
757 char *path;
758 char buffer[20];
759 char *argv[] = {"file", "-b", NULL, NULL};
760 int got;
761 Collection *collection;
762 DirItem *file;
763 struct stat info;
765 g_return_if_fail(window_with_focus != NULL);
767 collection = window_with_focus->collection;
768 if (collection->number_selected > 1)
770 report_error("ROX-Filer", "You cannot do this to more than "
771 "one item at a time");
772 return;
774 else if (collection->number_selected != 1)
776 collection_target(collection, target_callback, show_file_info);
777 return;
779 file = selected_item(collection);
780 path = make_path(window_with_focus->path,
781 file->leafname)->str;
782 if (lstat(path, &info))
784 delayed_error("ROX-Filer", g_strerror(errno));
785 return;
788 gstring = g_string_new(NULL);
790 window = gtk_window_new(GTK_WINDOW_DIALOG);
791 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_MOUSE);
792 gtk_container_set_border_width(GTK_CONTAINER(window), 4);
793 gtk_window_set_title(GTK_WINDOW(window), path);
795 table = gtk_table_new(9, 2, FALSE);
796 gtk_container_add(GTK_CONTAINER(window), table);
797 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
798 gtk_table_set_col_spacings(GTK_TABLE(table), 4);
800 label = gtk_label_new("Owner, group:");
801 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
802 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_RIGHT);
803 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
804 g_string_sprintf(gstring, "%s, %s", user_name(info.st_uid),
805 group_name(info.st_gid));
806 label = gtk_label_new(gstring->str);
807 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
808 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 0, 1);
810 label = gtk_label_new("Size:");
811 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
812 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
813 if (info.st_size >=2048)
815 g_string_sprintf(gstring, "%s (%ld bytes)",
816 format_size((unsigned long) info.st_size),
817 (unsigned long) info.st_size);
819 else
821 g_string_sprintf(gstring, "%ld bytes",
822 (unsigned long) info.st_size);
824 label = gtk_label_new(gstring->str);
825 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
826 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 1, 2);
828 label = gtk_label_new("Change time:");
829 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
830 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3);
831 g_string_sprintf(gstring, "%s", ctime(&info.st_ctime));
832 g_string_truncate(gstring, gstring->len - 1);
833 label = gtk_label_new(gstring->str);
834 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
835 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 2, 3);
837 label = gtk_label_new("Modify time:");
838 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
839 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4);
840 g_string_sprintf(gstring, "%s", ctime(&info.st_mtime));
841 g_string_truncate(gstring, gstring->len - 1);
842 label = gtk_label_new(gstring->str);
843 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
844 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 3, 4);
846 label = gtk_label_new("Access time:");
847 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
848 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 4, 5);
849 g_string_sprintf(gstring, "%s", ctime(&info.st_atime));
850 g_string_truncate(gstring, gstring->len - 1);
851 label = gtk_label_new(gstring->str);
852 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
853 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 4, 5);
855 label = gtk_label_new("Permissions:");
856 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
857 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 5, 6);
858 g_string_sprintf(gstring, "%o", (unsigned int) info.st_mode);
859 label = gtk_label_new(gstring->str);
860 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
861 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 5, 6);
863 label = gtk_label_new("MIME type:");
864 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
865 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 6, 7);
866 if (file->mime_type)
868 string = g_strconcat(file->mime_type->media_type, "/",
869 file->mime_type->subtype, NULL);
870 label = gtk_label_new(string);
871 g_free(string);
873 else
874 label = gtk_label_new("-");
875 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
876 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 6, 7);
878 frame = gtk_frame_new("file(1) says...");
879 gtk_table_attach_defaults(GTK_TABLE(table), frame, 0, 2, 7, 8);
880 file_label = gtk_label_new("<nothing yet>");
881 gtk_misc_set_padding(GTK_MISC(file_label), 4, 4);
882 gtk_label_set_line_wrap(GTK_LABEL(file_label), TRUE);
883 gtk_container_add(GTK_CONTAINER(frame), file_label);
885 button = gtk_button_new_with_label("OK");
886 gtk_table_attach(GTK_TABLE(table), button, 0, 2, 8, 9,
887 GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 40, 4);
888 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
889 gtk_widget_destroy, GTK_OBJECT(window));
891 gtk_widget_show_all(window);
892 gdk_flush();
894 if (pipe(file_data))
896 g_string_sprintf(gstring, "pipe(): %s", g_strerror(errno));
897 goto out;
899 switch (fork())
901 case -1:
902 close(file_data[0]);
903 close(file_data[1]);
904 g_string_sprintf(gstring, "fork(): %s",
905 g_strerror(errno));
906 goto out;
907 case 0:
908 close(file_data[0]);
909 dup2(file_data[1], STDOUT_FILENO);
910 dup2(file_data[1], STDERR_FILENO);
911 #ifdef FILE_B_FLAG
912 argv[2] = path;
913 #else
914 argv[1] = path;
915 #endif
916 if (execvp(argv[0], argv))
917 fprintf(stderr, "execvp() error: %s\n",
918 g_strerror(errno));
919 _exit(0);
921 /* We are the parent... */
922 close(file_data[1]);
923 g_string_truncate(gstring, 0);
924 while (gtk_events_pending())
925 g_main_iteration(FALSE);
926 while ((got = read(file_data[0], buffer, sizeof(buffer) - 1)) > 0)
928 buffer[got] = '\0';
929 g_string_append(gstring, buffer);
931 close(file_data[0]);
932 out:
933 gtk_label_set_text(GTK_LABEL(file_label), gstring->str);
934 g_string_free(gstring, TRUE);
937 static void app_show_help(char *path)
939 char *help_dir;
940 struct stat info;
942 help_dir = g_strconcat(path, "/Help", NULL);
944 if (stat(help_dir, &info))
945 report_error("Application",
946 "This is an application directory - you can "
947 "run it as a program, or open it (hold down "
948 "Shift while you open it). Most applications provide "
949 "their own help here, but this one doesn't.");
950 else
951 filer_opendir(help_dir, FALSE, BOTTOM);
954 static void help(gpointer data, guint action, GtkWidget *widget)
956 Collection *collection;
957 DirItem *item;
959 g_return_if_fail(window_with_focus != NULL);
961 collection = window_with_focus->collection;
962 if (collection->number_selected > 1)
964 report_error("ROX-Filer", "You cannot do this to more than "
965 "one item at a time");
966 return;
968 else if (collection->number_selected != 1)
970 collection_target(collection, target_callback, help);
971 return;
973 item = selected_item(collection);
974 switch (item->base_type)
976 case TYPE_FILE:
977 if (item->flags & ITEM_FLAG_EXEC_FILE)
978 report_error("Executable file",
979 "This is a file with an eXecute bit "
980 "set - it can be run as a program.");
981 else
982 report_error("File",
983 "This is a data file. Try using the "
984 "Info menu item to find out more...");
985 break;
986 case TYPE_DIRECTORY:
987 if (item->flags & ITEM_FLAG_APPDIR)
988 app_show_help(
989 make_path(window_with_focus->path,
990 item->leafname)->str);
991 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
992 report_error("Mount point",
993 "A mount point is a directory which another "
994 "filing system can be mounted on. Everything "
995 "on the mounted filesystem then appears to be "
996 "inside the directory.");
997 else
998 report_error("Directory",
999 "This is a directory. It contains an index to "
1000 "other items - open it to see the list.");
1001 break;
1002 case TYPE_CHAR_DEVICE:
1003 case TYPE_BLOCK_DEVICE:
1004 report_error("Device file",
1005 "Device files allow you to read from or write "
1006 "to a device driver as though it was an "
1007 "ordinary file.");
1008 break;
1009 case TYPE_PIPE:
1010 report_error("Named pipe",
1011 "Pipes allow different programs to "
1012 "communicate. One program writes data to the "
1013 "pipe while another one reads it out again.");
1014 break;
1015 case TYPE_SOCKET:
1016 report_error("Socket",
1017 "Sockets allow processes to communicate.");
1018 break;
1019 default:
1020 report_error("Unknown type",
1021 "I couldn't find out what kind of file this "
1022 "is. Maybe it doesn't exist anymore or you "
1023 "don't have search permission on the directory "
1024 "it's in?");
1025 break;
1029 static void select_all(gpointer data, guint action, GtkWidget *widget)
1031 g_return_if_fail(window_with_focus != NULL);
1033 collection_select_all(window_with_focus->collection);
1034 window_with_focus->temp_item_selected = FALSE;
1037 static void clear_selection(gpointer data, guint action, GtkWidget *widget)
1039 g_return_if_fail(window_with_focus != NULL);
1041 collection_clear_selection(window_with_focus->collection);
1042 window_with_focus->temp_item_selected = FALSE;
1045 static void show_options(gpointer data, guint action, GtkWidget *widget)
1047 g_return_if_fail(window_with_focus != NULL);
1049 options_show(window_with_focus);
1052 static gboolean new_directory_cb(char *initial, char *path)
1054 if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO))
1056 report_error("mkdir", g_strerror(errno));
1057 return FALSE;
1059 return TRUE;
1062 static void new_directory(gpointer data, guint action, GtkWidget *widget)
1064 g_return_if_fail(window_with_focus != NULL);
1066 savebox_show(window_with_focus, "Create directory",
1067 window_with_focus->path, "NewDir",
1068 default_pixmap + TYPE_DIRECTORY, new_directory_cb);
1071 static void xterm_here(gpointer data, guint action, GtkWidget *widget)
1073 char *argv[] = {NULL, NULL};
1075 argv[0] = xterm_here_value;
1077 g_return_if_fail(window_with_focus != NULL);
1079 if (!spawn_full(argv, window_with_focus->path, 0))
1080 report_error("ROX-Filer", "Failed to fork() child "
1081 "process");
1084 static void open_parent(gpointer data, guint action, GtkWidget *widget)
1086 g_return_if_fail(window_with_focus != NULL);
1088 filer_opendir(make_path(window_with_focus->path, "/..")->str,
1089 FALSE, BOTTOM);
1092 static void open_parent_same(gpointer data, guint action, GtkWidget *widget)
1094 g_return_if_fail(window_with_focus != NULL);
1096 change_to_parent(window_with_focus);
1099 static void new_window(gpointer data, guint action, GtkWidget *widget)
1101 g_return_if_fail(window_with_focus != NULL);
1103 filer_opendir(window_with_focus->path, FALSE, BOTTOM);
1106 static void close_window(gpointer data, guint action, GtkWidget *widget)
1108 g_return_if_fail(window_with_focus != NULL);
1110 gtk_widget_destroy(window_with_focus->window);
1113 static void rox_help(gpointer data, guint action, GtkWidget *widget)
1115 g_return_if_fail(window_with_focus != NULL);
1117 filer_opendir(make_path(getenv("APP_DIR"), "Help")->str, FALSE, BOTTOM);
1120 static void open_as_dir(gpointer data, guint action, GtkWidget *widget)
1122 g_return_if_fail(window_with_focus != NULL);
1124 filer_opendir(window_with_focus->path, FALSE, BOTTOM);
1127 static void close_panel(gpointer data, guint action, GtkWidget *widget)
1129 g_return_if_fail(window_with_focus != NULL);
1131 gtk_widget_destroy(window_with_focus->window);