r137: Added Small Icons style. Show Hidden and mount points in / work again.
[rox-filer.git] / ROX-Filer / src / menu.c
blob1446c9f2d4f33f16b85e00ee752ab22eb9c48ad9
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 "apps.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 /* Options */
58 static GtkWidget *xterm_here_entry;
59 static char *xterm_here_value;
61 /* Static prototypes */
62 static void position_menu(GtkMenu *menu, gint *x, gint *y, gpointer data);
63 static void menu_closed(GtkWidget *widget);
64 static void items_sensitive(GtkWidget *menu, int from, int n, gboolean state);
65 static char *load_xterm_here(char *data);
67 static void not_yet(gpointer data, guint action, GtkWidget *widget);
69 static void large(gpointer data, guint action, GtkWidget *widget);
70 static void small(gpointer data, guint action, GtkWidget *widget);
71 static void full_info(gpointer data, guint action, GtkWidget *widget);
73 static void hidden(gpointer data, guint action, GtkWidget *widget);
74 static void refresh(gpointer data, guint action, GtkWidget *widget);
76 static void copy_item(gpointer data, guint action, GtkWidget *widget);
77 static void rename_item(gpointer data, guint action, GtkWidget *widget);
78 static void link_item(gpointer data, guint action, GtkWidget *widget);
79 static void help(gpointer data, guint action, GtkWidget *widget);
80 static void show_file_info(gpointer data, guint action, GtkWidget *widget);
81 static void mount(gpointer data, guint action, GtkWidget *widget);
82 static void delete(gpointer data, guint action, GtkWidget *widget);
83 static void usage(gpointer data, guint action, GtkWidget *widget);
85 static void select_all(gpointer data, guint action, GtkWidget *widget);
86 static void clear_selection(gpointer data, guint action, GtkWidget *widget);
87 static void show_options(gpointer data, guint action, GtkWidget *widget);
88 static void new_directory(gpointer data, guint action, GtkWidget *widget);
89 static void xterm_here(gpointer data, guint action, GtkWidget *widget);
91 static void open_parent_same(gpointer data, guint action, GtkWidget *widget);
92 static void open_parent(gpointer data, guint action, GtkWidget *widget);
93 static void new_window(gpointer data, guint action, GtkWidget *widget);
94 static void close_window(gpointer data, guint action, GtkWidget *widget);
96 static void open_as_dir(gpointer data, guint action, GtkWidget *widget);
97 static void close_panel(gpointer data, guint action, GtkWidget *widget);
99 static GtkWidget *create_options();
100 static void update_options();
101 static void set_options();
102 static void save_options();
104 static OptionsSection options =
106 "Menu options",
107 create_options,
108 update_options,
109 set_options,
110 save_options
114 static GtkWidget *filer_menu; /* The popup filer menu */
115 static GtkWidget *filer_file_item; /* The File '' label */
116 static GtkWidget *filer_file_menu; /* The File '' menu */
117 static GtkWidget *filer_hidden_menu; /* The Show Hidden item */
118 static GtkWidget *panel_menu; /* The popup panel menu */
119 static GtkWidget *panel_file_item; /* The File '' label */
120 static GtkWidget *panel_file_menu; /* The File '' menu */
121 static GtkWidget *panel_hidden_menu; /* The Show Hidden item */
123 static gint screen_width, screen_height;
125 static GtkItemFactoryEntry filer_menu_def[] = {
126 {"/Display", NULL, NULL, 0, "<Branch>"},
127 {"/Display/Large Icons", NULL, large, 0, "<RadioItem>"},
128 {"/Display/Small Icons", NULL, small, 0, "/Display/Large Icons"},
129 {"/Display/Full Info", NULL, full_info, 0, "/Display/Large Icons"},
130 {"/Display/Separator", NULL, not_yet, 0, "<Separator>"},
131 {"/Display/Sort by Name", NULL, not_yet, 0, "<RadioItem>"},
132 {"/Display/Sort by Type", NULL, not_yet, 0, "/Display/Sort by Name"},
133 {"/Display/Sort by Date", NULL, not_yet, 0, "/Display/Sort by Name"},
134 {"/Display/Sort by Size", NULL, not_yet, 0, "/Display/Sort by Name"},
135 {"/Display/Sort by Owner", NULL, not_yet, 0, "/Display/Sort by Name"},
136 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
137 {"/Display/Show Hidden", C_"H", hidden, 0, "<ToggleItem>"},
138 {"/Display/Refresh", C_"L", refresh, 0, NULL},
139 {"/File", NULL, NULL, 0, "<Branch>"},
140 {"/File/Copy...", NULL, copy_item, 0, NULL},
141 {"/File/Rename...", NULL, rename_item, 0, NULL},
142 {"/File/Link...", NULL, link_item, 0, NULL},
143 {"/File/Help", "F1", help, 0, NULL},
144 {"/File/Info", "I", show_file_info, 0, NULL},
145 {"/File/Separator", NULL, NULL, 0, "<Separator>"},
146 {"/File/Mount", "M", mount, 0, NULL},
147 {"/File/Delete", C_"X", delete, 0, NULL},
148 {"/File/Disk Usage", "U", usage, 0, NULL},
149 {"/File/Permissions", NULL, not_yet, 0, NULL},
150 {"/File/Touch", NULL, not_yet, 0, NULL},
151 {"/File/Find", NULL, not_yet, 0, NULL},
152 {"/Select All", C_"A", select_all, 0, NULL},
153 {"/Clear Selection", C_"Z", clear_selection, 0, NULL},
154 {"/Options...", NULL, show_options, 0, NULL},
155 {"/New directory", NULL, new_directory, 0, NULL},
156 {"/Xterm here", NULL, xterm_here, 0, NULL},
157 {"/Window", NULL, NULL, 0, "<Branch>"},
158 {"/Window/Parent, new window", NULL, open_parent, 0, NULL},
159 {"/Window/Parent, same window", NULL, open_parent_same, 0, NULL},
160 {"/Window/New window", NULL, new_window, 0, NULL},
161 {"/Window/Close window", C_"Q", close_window, 0, NULL},
164 static GtkItemFactoryEntry panel_menu_def[] = {
165 {"/Display", NULL, NULL, 0, "<Branch>"},
166 {"/Display/Large Icons", NULL, not_yet, 0, "<RadioItem>"},
167 {"/Display/Small Icons", NULL, not_yet, 0, "/Display/Large Icons"},
168 {"/Display/Full Info", NULL, not_yet, 0, "/Display/Large Icons"},
169 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
170 {"/Display/Sort by Name", NULL, not_yet, 0, "<RadioItem>"},
171 {"/Display/Sort by Type", NULL, not_yet, 0, "/Display/Sort by Name"},
172 {"/Display/Sort by Date", NULL, not_yet, 0, "/Display/Sort by Name"},
173 {"/Display/Sort by Size", NULL, not_yet, 0, "/Display/Sort by Name"},
174 {"/Display/Sort by Owner", NULL, not_yet, 0, "/Display/Sort by Name"},
175 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
176 {"/Display/Show Hidden", NULL, hidden, 0, "<ToggleItem>"},
177 {"/Display/Refresh", NULL, refresh, 0, NULL},
178 {"/File", NULL, NULL, 0, "<Branch>"},
179 {"/File/Help", NULL, help, 0, NULL},
180 {"/File/Info", NULL, show_file_info, 0, NULL},
181 {"/File/Delete", NULL, delete, 0, NULL},
182 {"/Open as directory", NULL, open_as_dir, 0, NULL},
183 {"/Close panel", NULL, close_panel, 0, NULL},
186 void menu_init()
188 GtkItemFactory *item_factory;
189 char *menurc;
190 GList *items;
192 /* This call starts returning strange values after a while, so get
193 * the result here during init.
195 gdk_window_get_size(GDK_ROOT_PARENT(), &screen_width, &screen_height);
197 filer_keys = gtk_accel_group_new();
198 item_factory = gtk_item_factory_new(GTK_TYPE_MENU,
199 "<filer>",
200 filer_keys);
201 gtk_item_factory_create_items(item_factory,
202 sizeof(filer_menu_def) / sizeof(*filer_menu_def),
203 filer_menu_def,
204 NULL);
205 filer_menu = gtk_item_factory_get_widget(item_factory, "<filer>");
206 filer_file_menu = gtk_item_factory_get_widget(item_factory,
207 "<filer>/File");
208 filer_hidden_menu = gtk_item_factory_get_widget(item_factory,
209 "<filer>/Display/Show Hidden");
210 items = gtk_container_children(GTK_CONTAINER(filer_menu));
211 filer_file_item = GTK_BIN(g_list_nth(items, 1)->data)->child;
212 g_list_free(items);
214 panel_keys = gtk_accel_group_new();
215 item_factory = gtk_item_factory_new(GTK_TYPE_MENU,
216 "<panel>",
217 panel_keys);
218 gtk_item_factory_create_items(item_factory,
219 sizeof(panel_menu_def) / sizeof(*panel_menu_def),
220 panel_menu_def,
221 NULL);
222 panel_menu = gtk_item_factory_get_widget(item_factory, "<panel>");
223 panel_file_menu = gtk_item_factory_get_widget(item_factory,
224 "<panel>/File");
225 panel_hidden_menu = gtk_item_factory_get_widget(item_factory,
226 "<panel>/Display/Show Hidden");
227 items = gtk_container_children(GTK_CONTAINER(panel_menu));
228 panel_file_item = GTK_BIN(g_list_nth(items, 1)->data)->child;
229 g_list_free(items);
231 menurc = choices_find_path_load("menus");
232 if (menurc)
233 gtk_item_factory_parse_rc(menurc);
235 gtk_signal_connect(GTK_OBJECT(panel_menu), "unmap_event",
236 GTK_SIGNAL_FUNC(menu_closed), NULL);
237 gtk_signal_connect(GTK_OBJECT(filer_menu), "unmap_event",
238 GTK_SIGNAL_FUNC(menu_closed), NULL);
240 gtk_accel_group_lock(panel_keys);
242 options_sections = g_slist_prepend(options_sections, &options);
243 xterm_here_value = g_strdup("xterm");
244 option_register("xterm_here", load_xterm_here);
247 /* Build up some option widgets to go in the options dialog, but don't
248 * fill them in yet.
250 static GtkWidget *create_options()
252 GtkWidget *table, *label;
254 table = gtk_table_new(2, 2, FALSE);
255 gtk_container_set_border_width(GTK_CONTAINER(table), 4);
257 label = gtk_label_new("To set the keyboard short-cuts you simply open "
258 "the menu over a filer window, move the pointer over "
259 "the item you want to use and press a key. The key "
260 "will appear next to the menu item and you can just "
261 "press that key without opening the menu in future. "
262 "To save the current menu short-cuts for next time, "
263 "click the Save button at the bottom of this window.");
264 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
265 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 2, 0, 1);
267 label = gtk_label_new("'Xterm here' program:");
268 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
269 xterm_here_entry = gtk_entry_new();
270 gtk_table_attach_defaults(GTK_TABLE(table), xterm_here_entry,
271 1, 2, 1, 2);
273 return table;
276 static char *load_xterm_here(char *data)
278 g_free(xterm_here_value);
279 xterm_here_value = g_strdup(data);
280 return NULL;
283 static void update_options()
285 gtk_entry_set_text(GTK_ENTRY(xterm_here_entry), xterm_here_value);
288 static void set_options()
290 g_free(xterm_here_value);
291 xterm_here_value = g_strdup(gtk_entry_get_text(
292 GTK_ENTRY(xterm_here_entry)));
295 static void save_options()
297 char *menurc;
299 menurc = choices_find_path_save("menus");
300 if (menurc)
301 gtk_item_factory_dump_rc(menurc, NULL, TRUE);
303 option_write("xterm_here", xterm_here_value);
307 static void items_sensitive(GtkWidget *menu, int from, int n, gboolean state)
309 GList *items, *item;
311 items = gtk_container_children(GTK_CONTAINER(menu));
313 item = g_list_nth(items, from);
314 while (item && n--)
316 gtk_widget_set_sensitive(GTK_BIN(item->data)->child, state);
317 item = item->next;
320 g_list_free(items);
323 static void position_menu(GtkMenu *menu, gint *x, gint *y, gpointer data)
325 int *pos = (int *) data;
326 GtkRequisition requisition;
328 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
330 if (pos[0] == -1)
331 *x = screen_width - MENU_MARGIN - requisition.width;
332 else if (pos[0] == -2)
333 *x = MENU_MARGIN;
334 else
335 *x = pos[0] - (requisition.width >> 2);
337 if (pos[1] == -1)
338 *y = screen_height - MENU_MARGIN - requisition.height;
339 else if (pos[1] == -2)
340 *y = MENU_MARGIN;
341 else
342 *y = pos[1] - (requisition.height >> 2);
344 *x = CLAMP(*x, 0, screen_width - requisition.width);
345 *y = CLAMP(*y, 0, screen_height - requisition.height);
348 void show_filer_menu(FilerWindow *filer_window, GdkEventButton *event,
349 int item)
351 GString *buffer;
352 GtkWidget *file_label, *file_menu;
353 DirItem *file_item;
354 int pos[2];
356 pos[0] = event->x_root;
357 pos[1] = event->y_root;
359 window_with_focus = filer_window;
361 if (filer_window->panel)
363 switch (filer_window->panel_side)
365 case TOP: pos[1] = -2; break;
366 case BOTTOM: pos[1] = -1; break;
367 case LEFT: pos[0] = -2; break;
368 case RIGHT: pos[0] = -1; break;
372 if (filer_window->panel)
373 collection_clear_selection(filer_window->collection); /* ??? */
375 if (filer_window->collection->number_selected == 0 && item >= 0)
377 collection_select_item(filer_window->collection, item);
378 filer_window->temp_item_selected = TRUE;
380 else
381 filer_window->temp_item_selected = FALSE;
383 if (filer_window->panel)
385 file_label = panel_file_item;
386 file_menu = panel_file_menu;
388 gtk_check_menu_item_set_active(
389 GTK_CHECK_MENU_ITEM(panel_hidden_menu),
390 filer_window->show_hidden);
392 else
394 file_label = filer_file_item;
395 file_menu = filer_file_menu;
397 gtk_check_menu_item_set_active(
398 GTK_CHECK_MENU_ITEM(filer_hidden_menu),
399 filer_window->show_hidden);
402 buffer = g_string_new(NULL);
403 switch (filer_window->collection->number_selected)
405 case 0:
406 g_string_assign(buffer, "<nothing selected>");
407 items_sensitive(file_menu, 0, 5, FALSE);
408 items_sensitive(file_menu, 6, -1, FALSE);
409 gtk_widget_set_sensitive(file_label, FALSE);
410 break;
411 case 1:
412 items_sensitive(file_menu, 0, 5, TRUE);
413 items_sensitive(file_menu, 6, -1, TRUE);
414 gtk_widget_set_sensitive(file_label, TRUE);
415 file_item = selected_item(filer_window->collection);
416 g_string_sprintf(buffer, "%s '%s'",
417 basetype_name(file_item),
418 file_item->leafname);
419 break;
420 default:
421 items_sensitive(file_menu, 0, 5, FALSE);
422 items_sensitive(file_menu, 6, -1, TRUE);
423 gtk_widget_set_sensitive(file_label, TRUE);
424 g_string_sprintf(buffer, "%d items",
425 filer_window->collection->number_selected);
426 break;
429 gtk_label_set_text(GTK_LABEL(file_label), buffer->str);
431 g_string_free(buffer, TRUE);
433 gtk_menu_popup(filer_window->panel ? GTK_MENU(panel_menu)
434 : GTK_MENU(filer_menu),
435 NULL, NULL, position_menu,
436 (gpointer) pos, event->button, event->time);
439 static void menu_closed(GtkWidget *widget)
441 if (window_with_focus == NULL)
442 return; /* Close panel item chosen? */
444 if (window_with_focus->temp_item_selected)
446 collection_clear_selection(window_with_focus->collection);
447 window_with_focus->temp_item_selected = FALSE;
451 /* Actions */
453 /* Fake action to warn when a menu item does nothing */
454 static void not_yet(gpointer data, guint action, GtkWidget *widget)
456 delayed_error("ROX-Filer", "Sorry, that feature isn't implemented yet");
459 static void large(gpointer data, guint action, GtkWidget *widget)
461 g_return_if_fail(window_with_focus != NULL);
463 filer_style_set(window_with_focus, LARGE_ICONS);
466 static void small(gpointer data, guint action, GtkWidget *widget)
468 g_return_if_fail(window_with_focus != NULL);
470 filer_style_set(window_with_focus, SMALL_ICONS);
473 static void full_info(gpointer data, guint action, GtkWidget *widget)
475 g_return_if_fail(window_with_focus != NULL);
477 filer_style_set(window_with_focus, FULL_INFO);
480 static void hidden(gpointer data, guint action, GtkWidget *widget)
482 gboolean new;
483 GtkWidget *item;
485 g_return_if_fail(window_with_focus != NULL);
487 item = window_with_focus->panel ? panel_hidden_menu : filer_hidden_menu;
488 new = GTK_CHECK_MENU_ITEM(item)->active;
490 filer_set_hidden(window_with_focus, new);
493 static void refresh(gpointer data, guint action, GtkWidget *widget)
495 g_return_if_fail(window_with_focus != NULL);
497 update_dir(window_with_focus);
500 static void delete(gpointer data, guint action, GtkWidget *widget)
502 g_return_if_fail(window_with_focus != NULL);
504 action_delete(window_with_focus);
507 static void usage(gpointer data, guint action, GtkWidget *widget)
509 g_return_if_fail(window_with_focus != NULL);
511 action_usage(window_with_focus);
514 static gboolean copy_cb(char *initial, char *path)
516 char *new_dir, *slash;
517 int len;
518 GString *command;
519 gboolean retval = TRUE;
521 slash = strrchr(path, '/');
522 if (!slash)
524 report_error("ROX-Filer", "Missing '/' in new pathname");
525 return FALSE;
528 if (access(path, F_OK) == 0)
530 report_error("ROX-Filer",
531 "An item with this name already exists");
532 return FALSE;
535 len = slash - path;
536 new_dir = g_malloc(len + 1);
537 memcpy(new_dir, path, len);
538 new_dir[len] = '\0';
540 command = g_string_new(NULL);
541 g_string_sprintf(command, "cp -a %s %s", initial, path);
543 if (system(command->str))
545 g_string_append(command, " failed!");
546 report_error("ROX-Filer", command->str);
547 retval = FALSE;
550 g_string_free(command, TRUE);
552 refresh_dirs(new_dir);
553 return retval;
556 static void copy_item(gpointer data, guint action, GtkWidget *widget)
558 Collection *collection;
560 g_return_if_fail(window_with_focus != NULL);
562 collection = window_with_focus->collection;
563 if (collection->number_selected != 1)
564 report_error("ROX-Filer", "You must select a single "
565 "item to copy");
566 else
568 DirItem *item = selected_item(collection);
570 savebox_show(window_with_focus, "Copy",
571 window_with_focus->path,
572 item->leafname,
573 item->image, copy_cb);
577 static gboolean rename_cb(char *initial, char *path)
579 if (rename(initial, path))
581 report_error("ROX-Filer: rename()", g_strerror(errno));
582 return FALSE;
584 return TRUE;
587 static void rename_item(gpointer data, guint action, GtkWidget *widget)
589 Collection *collection;
591 g_return_if_fail(window_with_focus != NULL);
593 collection = window_with_focus->collection;
594 if (collection->number_selected != 1)
595 report_error("ROX-Filer", "You must select a single "
596 "item to rename");
597 else
599 DirItem *item = selected_item(collection);
601 savebox_show(window_with_focus, "Rename",
602 window_with_focus->path,
603 item->leafname,
604 item->image, rename_cb);
608 static gboolean link_cb(char *initial, char *path)
610 if (symlink(initial, path))
612 report_error("ROX-Filer: symlink()", g_strerror(errno));
613 return FALSE;
615 return TRUE;
618 static void link_item(gpointer data, guint action, GtkWidget *widget)
620 Collection *collection;
622 g_return_if_fail(window_with_focus != NULL);
624 collection = window_with_focus->collection;
625 if (collection->number_selected != 1)
626 report_error("ROX-Filer", "You must select a single "
627 "item to link");
628 else
630 DirItem *item = selected_item(collection);
632 savebox_show(window_with_focus, "Symlink",
633 window_with_focus->path,
634 item->leafname,
635 item->image, link_cb);
639 static void show_file_info(gpointer data, guint action, GtkWidget *widget)
641 GtkWidget *window, *table, *label, *button, *frame;
642 GtkWidget *file_label;
643 GString *gstring;
644 char *string;
645 int file_data[2];
646 char *path;
647 char buffer[20];
648 char *argv[] = {"file", "-b", NULL, NULL};
649 int got;
650 Collection *collection;
651 DirItem *file;
652 struct stat info;
654 g_return_if_fail(window_with_focus != NULL);
656 collection = window_with_focus->collection;
657 if (collection->number_selected != 1)
659 report_error("ROX-Filer", "You must select a single "
660 "item before using Info");
661 return;
663 file = selected_item(collection);
664 path = make_path(window_with_focus->path,
665 file->leafname)->str;
666 if (lstat(path, &info))
668 delayed_error("ROX-Filer", g_strerror(errno));
669 return;
672 gstring = g_string_new(NULL);
674 window = gtk_window_new(GTK_WINDOW_DIALOG);
675 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_MOUSE);
676 gtk_container_set_border_width(GTK_CONTAINER(window), 4);
677 gtk_window_set_title(GTK_WINDOW(window), path);
679 table = gtk_table_new(9, 2, FALSE);
680 gtk_container_add(GTK_CONTAINER(window), table);
681 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
682 gtk_table_set_col_spacings(GTK_TABLE(table), 4);
684 label = gtk_label_new("Owner, group:");
685 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
686 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_RIGHT);
687 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
688 g_string_sprintf(gstring, "%s, %s", user_name(info.st_uid),
689 group_name(info.st_gid));
690 label = gtk_label_new(gstring->str);
691 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
692 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 0, 1);
694 label = gtk_label_new("Size:");
695 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
696 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
697 if (info.st_size >=2048)
699 g_string_sprintf(gstring, "%s (%ld bytes)",
700 format_size((unsigned long) info.st_size),
701 (unsigned long) info.st_size);
703 else
705 g_string_sprintf(gstring, "%ld bytes",
706 (unsigned long) info.st_size);
708 label = gtk_label_new(gstring->str);
709 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
710 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 1, 2);
712 label = gtk_label_new("Change time:");
713 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
714 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3);
715 g_string_sprintf(gstring, "%s", ctime(&info.st_ctime));
716 g_string_truncate(gstring, gstring->len - 1);
717 label = gtk_label_new(gstring->str);
718 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
719 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 2, 3);
721 label = gtk_label_new("Modify time:");
722 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
723 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4);
724 g_string_sprintf(gstring, "%s", ctime(&info.st_mtime));
725 g_string_truncate(gstring, gstring->len - 1);
726 label = gtk_label_new(gstring->str);
727 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
728 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 3, 4);
730 label = gtk_label_new("Access time:");
731 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
732 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 4, 5);
733 g_string_sprintf(gstring, "%s", ctime(&info.st_atime));
734 g_string_truncate(gstring, gstring->len - 1);
735 label = gtk_label_new(gstring->str);
736 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
737 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 4, 5);
739 label = gtk_label_new("Permissions:");
740 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
741 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 5, 6);
742 g_string_sprintf(gstring, "%o", (unsigned int) info.st_mode);
743 label = gtk_label_new(gstring->str);
744 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
745 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 5, 6);
747 label = gtk_label_new("MIME type:");
748 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
749 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 6, 7);
750 if (file->mime_type)
752 string = g_strconcat(file->mime_type->media_type, "/",
753 file->mime_type->subtype, NULL);
754 label = gtk_label_new(string);
755 g_free(string);
757 else
758 label = gtk_label_new("-");
759 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
760 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 6, 7);
762 frame = gtk_frame_new("file(1) says...");
763 gtk_table_attach_defaults(GTK_TABLE(table), frame, 0, 2, 7, 8);
764 file_label = gtk_label_new("<nothing yet>");
765 gtk_misc_set_padding(GTK_MISC(file_label), 4, 4);
766 gtk_label_set_line_wrap(GTK_LABEL(file_label), TRUE);
767 gtk_container_add(GTK_CONTAINER(frame), file_label);
769 button = gtk_button_new_with_label("OK");
770 gtk_table_attach(GTK_TABLE(table), button, 0, 2, 8, 9,
771 GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 40, 4);
772 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
773 gtk_widget_destroy, GTK_OBJECT(window));
775 gtk_widget_show_all(window);
776 gdk_flush();
778 pipe(file_data);
779 switch (fork())
781 case -1:
782 close(file_data[0]);
783 close(file_data[1]);
784 gtk_label_set_text(GTK_LABEL(file_label),
785 "fork() error");
786 g_string_free(gstring, TRUE);
787 return;
788 case 0:
789 close(file_data[0]);
790 dup2(file_data[1], STDOUT_FILENO);
791 dup2(file_data[1], STDERR_FILENO);
792 #ifdef FILE_B_FLAG
793 argv[2] = path;
794 #else
795 argv[1] = path;
796 #endif
797 if (execvp(argv[0], argv))
798 fprintf(stderr, "execvp() error: %s\n",
799 g_strerror(errno));
800 _exit(0);
802 /* We are the parent... */
803 close(file_data[1]);
804 g_string_truncate(gstring, 0);
805 while (gtk_events_pending())
806 g_main_iteration(FALSE);
807 while ((got = read(file_data[0], buffer, sizeof(buffer) - 1)))
809 buffer[got] = '\0';
810 g_string_append(gstring, buffer);
812 close(file_data[0]);
813 gtk_label_set_text(GTK_LABEL(file_label), gstring->str);
814 g_string_free(gstring, TRUE);
817 static void help(gpointer data, guint action, GtkWidget *widget)
819 Collection *collection;
820 DirItem *item;
822 g_return_if_fail(window_with_focus != NULL);
824 collection = window_with_focus->collection;
825 if (collection->number_selected != 1)
827 report_error("ROX-Filer", "You must select a single "
828 "item to get help on");
829 return;
831 item = selected_item(collection);
832 switch (item->base_type)
834 case TYPE_FILE:
835 if (item->flags & ITEM_FLAG_EXEC_FILE)
836 report_error("Executable file",
837 "This is a file with an eXecute bit "
838 "set - it can be run as a program.");
839 else
840 report_error("File",
841 "This is a data file. Try using the "
842 "Info menu item to find out more...");
843 break;
844 case TYPE_DIRECTORY:
845 if (item->flags & ITEM_FLAG_APPDIR)
846 app_show_help(
847 make_path(window_with_focus->path,
848 item->leafname)->str);
849 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
850 report_error("Mount point",
851 "A mount point is a directory which another "
852 "filing system can be mounted on. Everything "
853 "on the mounted filesystem then appears to be "
854 "inside the directory.");
855 else
856 report_error("Directory",
857 "This is a directory. It contains an index to "
858 "other items - open it to see the list.");
859 break;
860 case TYPE_CHAR_DEVICE:
861 case TYPE_BLOCK_DEVICE:
862 report_error("Device file",
863 "Device files allow you to read from or write "
864 "to a device driver as though it was an "
865 "ordinary file.");
866 break;
867 case TYPE_PIPE:
868 report_error("Named pipe",
869 "Pipes allow different programs to "
870 "communicate. One program writes data to the "
871 "pipe while another one reads it out again.");
872 break;
873 case TYPE_SOCKET:
874 report_error("Socket",
875 "Sockets allow processes to communicate.");
876 break;
877 default:
878 report_error("Unknown type",
879 "I couldn't find out what kind of file this "
880 "is. Maybe it doesn't exist anymore or you "
881 "don't have search permission on the directory "
882 "it's in?");
883 break;
887 static void mount(gpointer data, guint action, GtkWidget *widget)
889 #ifdef DO_MOUNT_POINTS
890 DirItem *item;
891 int i;
892 Collection *collection;
893 char *error = NULL;
894 int count = 0;
896 g_return_if_fail(window_with_focus != NULL);
898 collection = window_with_focus->collection;
900 for (i = 0; i < collection->number_of_items; i++)
901 if (collection->items[i].selected)
903 item = (DirItem *) collection->items[i].data;
904 if (item->flags & ITEM_FLAG_MOUNT_POINT)
906 char *argv[] = {"mount", NULL, NULL};
907 int child;
909 count++;
910 if (item->flags & ITEM_FLAG_MOUNTED)
911 argv[0] = "umount";
912 argv[1] = make_path(window_with_focus->path,
913 item->leafname)->str;
914 child = spawn(argv);
915 if (child)
916 waitpid(child, NULL, 0);
917 else
918 error = "Failed to run mount/umount";
921 if (count)
922 update_dir(window_with_focus);
923 else if (!error)
924 error = "You must select some mount points first!";
926 if (error)
927 report_error("ROX-Filer", error);
928 #else
929 report_error("ROX-Filer",
930 "ROX-Filer does not yet support mount points on your "
931 "system. Sorry.");
932 #endif /* DO_MOUNT_POINTS */
935 static void select_all(gpointer data, guint action, GtkWidget *widget)
937 g_return_if_fail(window_with_focus != NULL);
939 collection_select_all(window_with_focus->collection);
940 window_with_focus->temp_item_selected = FALSE;
943 static void clear_selection(gpointer data, guint action, GtkWidget *widget)
945 g_return_if_fail(window_with_focus != NULL);
947 collection_clear_selection(window_with_focus->collection);
948 window_with_focus->temp_item_selected = FALSE;
951 static void show_options(gpointer data, guint action, GtkWidget *widget)
953 g_return_if_fail(window_with_focus != NULL);
955 options_show(window_with_focus);
958 static gboolean new_directory_cb(char *initial, char *path)
960 if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO))
962 report_error("mkdir", g_strerror(errno));
963 return FALSE;
965 return TRUE;
968 static void new_directory(gpointer data, guint action, GtkWidget *widget)
970 g_return_if_fail(window_with_focus != NULL);
972 savebox_show(window_with_focus, "Create directory",
973 window_with_focus->path, "NewDir",
974 default_pixmap + TYPE_DIRECTORY, new_directory_cb);
977 static void xterm_here(gpointer data, guint action, GtkWidget *widget)
979 char *argv[] = {NULL, NULL};
981 argv[0] = xterm_here_value;
983 g_return_if_fail(window_with_focus != NULL);
985 if (!spawn_full(argv, window_with_focus->path, 0))
986 report_error("ROX-Filer", "Failed to fork() child "
987 "process");
990 static void open_parent(gpointer data, guint action, GtkWidget *widget)
992 g_return_if_fail(window_with_focus != NULL);
994 filer_opendir(make_path(window_with_focus->path, "/..")->str,
995 FALSE, BOTTOM);
998 static void open_parent_same(gpointer data, guint action, GtkWidget *widget)
1000 g_return_if_fail(window_with_focus != NULL);
1002 change_to_parent(window_with_focus);
1005 static void new_window(gpointer data, guint action, GtkWidget *widget)
1007 g_return_if_fail(window_with_focus != NULL);
1009 filer_opendir(window_with_focus->path, FALSE, BOTTOM);
1012 static void close_window(gpointer data, guint action, GtkWidget *widget)
1014 g_return_if_fail(window_with_focus != NULL);
1016 gtk_widget_destroy(window_with_focus->window);
1019 static void open_as_dir(gpointer data, guint action, GtkWidget *widget)
1021 g_return_if_fail(window_with_focus != NULL);
1023 filer_opendir(window_with_focus->path, FALSE, BOTTOM);
1026 static void close_panel(gpointer data, guint action, GtkWidget *widget)
1028 g_return_if_fail(window_with_focus != NULL);
1030 gtk_widget_destroy(window_with_focus->window);