r127: The Show Hidden menu item now shows the true state for each window/panel.
[rox-filer.git] / ROX-Filer / src / menu.c
blob21d1876c00dc6101f7f108f531c89e392ee6b318
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 hidden(gpointer data, guint action, GtkWidget *widget);
70 static void refresh(gpointer data, guint action, GtkWidget *widget);
72 static void copy_item(gpointer data, guint action, GtkWidget *widget);
73 static void rename_item(gpointer data, guint action, GtkWidget *widget);
74 static void link_item(gpointer data, guint action, GtkWidget *widget);
75 static void help(gpointer data, guint action, GtkWidget *widget);
76 static void show_file_info(gpointer data, guint action, GtkWidget *widget);
77 static void mount(gpointer data, guint action, GtkWidget *widget);
78 static void delete(gpointer data, guint action, GtkWidget *widget);
79 static void usage(gpointer data, guint action, GtkWidget *widget);
81 static void select_all(gpointer data, guint action, GtkWidget *widget);
82 static void clear_selection(gpointer data, guint action, GtkWidget *widget);
83 static void show_options(gpointer data, guint action, GtkWidget *widget);
84 static void new_directory(gpointer data, guint action, GtkWidget *widget);
85 static void xterm_here(gpointer data, guint action, GtkWidget *widget);
87 static void open_parent_same(gpointer data, guint action, GtkWidget *widget);
88 static void open_parent(gpointer data, guint action, GtkWidget *widget);
89 static void new_window(gpointer data, guint action, GtkWidget *widget);
90 static void close_window(gpointer data, guint action, GtkWidget *widget);
92 static void open_as_dir(gpointer data, guint action, GtkWidget *widget);
93 static void close_panel(gpointer data, guint action, GtkWidget *widget);
95 static GtkWidget *create_options();
96 static void update_options();
97 static void set_options();
98 static void save_options();
100 static OptionsSection options =
102 "Menu options",
103 create_options,
104 update_options,
105 set_options,
106 save_options
110 static GtkWidget *filer_menu; /* The popup filer menu */
111 static GtkWidget *filer_file_item; /* The File '' label */
112 static GtkWidget *filer_file_menu; /* The File '' menu */
113 static GtkWidget *filer_hidden_menu; /* The Show Hidden item */
114 static GtkWidget *panel_menu; /* The popup panel menu */
115 static GtkWidget *panel_file_item; /* The File '' label */
116 static GtkWidget *panel_file_menu; /* The File '' menu */
117 static GtkWidget *panel_hidden_menu; /* The Show Hidden item */
119 static gint screen_width, screen_height;
121 static GtkItemFactoryEntry filer_menu_def[] = {
122 {"/Display", NULL, NULL, 0, "<Branch>"},
123 {"/Display/Large Icons", NULL, not_yet, 0, "<RadioItem>"},
124 {"/Display/Small Icons", NULL, not_yet, 0, "/Display/Large Icons"},
125 {"/Display/Full Info", NULL, not_yet, 0, "/Display/Large Icons"},
126 {"/Display/Separator", NULL, not_yet, 0, "<Separator>"},
127 {"/Display/Sort by Name", NULL, not_yet, 0, "<RadioItem>"},
128 {"/Display/Sort by Type", NULL, not_yet, 0, "/Display/Sort by Name"},
129 {"/Display/Sort by Date", NULL, not_yet, 0, "/Display/Sort by Name"},
130 {"/Display/Sort by Size", NULL, not_yet, 0, "/Display/Sort by Name"},
131 {"/Display/Sort by Owner", NULL, not_yet, 0, "/Display/Sort by Name"},
132 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
133 {"/Display/Show Hidden", C_"H", hidden, 0, "<ToggleItem>"},
134 {"/Display/Refresh", C_"L", refresh, 0, NULL},
135 {"/File", NULL, NULL, 0, "<Branch>"},
136 {"/File/Copy...", NULL, copy_item, 0, NULL},
137 {"/File/Rename...", NULL, rename_item, 0, NULL},
138 {"/File/Link...", NULL, link_item, 0, NULL},
139 {"/File/Help", "F1", help, 0, NULL},
140 {"/File/Info", "I", show_file_info, 0, NULL},
141 {"/File/Separator", NULL, NULL, 0, "<Separator>"},
142 {"/File/Mount", "M", mount, 0, NULL},
143 {"/File/Delete", C_"X", delete, 0, NULL},
144 {"/File/Disk Usage", "U", usage, 0, NULL},
145 {"/File/Permissions", NULL, not_yet, 0, NULL},
146 {"/File/Touch", NULL, not_yet, 0, NULL},
147 {"/File/Find", NULL, not_yet, 0, NULL},
148 {"/Select All", C_"A", select_all, 0, NULL},
149 {"/Clear Selection", C_"Z", clear_selection, 0, NULL},
150 {"/Options...", NULL, show_options, 0, NULL},
151 {"/New directory", NULL, new_directory, 0, NULL},
152 {"/Xterm here", NULL, xterm_here, 0, NULL},
153 {"/Window", NULL, NULL, 0, "<Branch>"},
154 {"/Window/Parent, new window", NULL, open_parent, 0, NULL},
155 {"/Window/Parent, same window", NULL, open_parent_same, 0, NULL},
156 {"/Window/New window", NULL, new_window, 0, NULL},
157 {"/Window/Close window", C_"Q", close_window, 0, NULL},
160 static GtkItemFactoryEntry panel_menu_def[] = {
161 {"/Display", NULL, NULL, 0, "<Branch>"},
162 {"/Display/Large Icons", NULL, not_yet, 0, "<RadioItem>"},
163 {"/Display/Small Icons", NULL, not_yet, 0, "/Display/Large Icons"},
164 {"/Display/Full Info", NULL, not_yet, 0, "/Display/Large Icons"},
165 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
166 {"/Display/Sort by Name", NULL, not_yet, 0, "<RadioItem>"},
167 {"/Display/Sort by Type", NULL, not_yet, 0, "/Display/Sort by Name"},
168 {"/Display/Sort by Date", NULL, not_yet, 0, "/Display/Sort by Name"},
169 {"/Display/Sort by Size", NULL, not_yet, 0, "/Display/Sort by Name"},
170 {"/Display/Sort by Owner", NULL, not_yet, 0, "/Display/Sort by Name"},
171 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
172 {"/Display/Show Hidden", NULL, hidden, 0, "<ToggleItem>"},
173 {"/Display/Refresh", NULL, refresh, 0, NULL},
174 {"/File", NULL, NULL, 0, "<Branch>"},
175 {"/File/Help", NULL, help, 0, NULL},
176 {"/File/Info", NULL, show_file_info, 0, NULL},
177 {"/File/Delete", NULL, delete, 0, NULL},
178 {"/Open as directory", NULL, open_as_dir, 0, NULL},
179 {"/Close panel", NULL, close_panel, 0, NULL},
182 void menu_init()
184 GtkItemFactory *item_factory;
185 char *menurc;
186 GList *items;
188 /* This call starts returning strange values after a while, so get
189 * the result here during init.
191 gdk_window_get_size(GDK_ROOT_PARENT(), &screen_width, &screen_height);
193 filer_keys = gtk_accel_group_new();
194 item_factory = gtk_item_factory_new(GTK_TYPE_MENU,
195 "<filer>",
196 filer_keys);
197 gtk_item_factory_create_items(item_factory,
198 sizeof(filer_menu_def) / sizeof(*filer_menu_def),
199 filer_menu_def,
200 NULL);
201 filer_menu = gtk_item_factory_get_widget(item_factory, "<filer>");
202 filer_file_menu = gtk_item_factory_get_widget(item_factory,
203 "<filer>/File");
204 filer_hidden_menu = gtk_item_factory_get_widget(item_factory,
205 "<filer>/Display/Show Hidden");
206 items = gtk_container_children(GTK_CONTAINER(filer_menu));
207 filer_file_item = GTK_BIN(g_list_nth(items, 1)->data)->child;
208 g_list_free(items);
210 panel_keys = gtk_accel_group_new();
211 item_factory = gtk_item_factory_new(GTK_TYPE_MENU,
212 "<panel>",
213 panel_keys);
214 gtk_item_factory_create_items(item_factory,
215 sizeof(panel_menu_def) / sizeof(*panel_menu_def),
216 panel_menu_def,
217 NULL);
218 panel_menu = gtk_item_factory_get_widget(item_factory, "<panel>");
219 panel_file_menu = gtk_item_factory_get_widget(item_factory,
220 "<panel>/File");
221 panel_hidden_menu = gtk_item_factory_get_widget(item_factory,
222 "<panel>/Display/Show Hidden");
223 items = gtk_container_children(GTK_CONTAINER(panel_menu));
224 panel_file_item = GTK_BIN(g_list_nth(items, 1)->data)->child;
225 g_list_free(items);
227 menurc = choices_find_path_load("menus");
228 if (menurc)
229 gtk_item_factory_parse_rc(menurc);
231 gtk_signal_connect(GTK_OBJECT(panel_menu), "unmap_event",
232 GTK_SIGNAL_FUNC(menu_closed), NULL);
233 gtk_signal_connect(GTK_OBJECT(filer_menu), "unmap_event",
234 GTK_SIGNAL_FUNC(menu_closed), NULL);
236 gtk_accel_group_lock(panel_keys);
238 options_sections = g_slist_prepend(options_sections, &options);
239 xterm_here_value = g_strdup("xterm");
240 option_register("xterm_here", load_xterm_here);
243 /* Build up some option widgets to go in the options dialog, but don't
244 * fill them in yet.
246 static GtkWidget *create_options()
248 GtkWidget *table, *label;
250 table = gtk_table_new(2, 2, FALSE);
251 gtk_container_set_border_width(GTK_CONTAINER(table), 4);
253 label = gtk_label_new("To set the keyboard short-cuts you simply open "
254 "the menu over a filer window, move the pointer over "
255 "the item you want to use and press a key. The key "
256 "will appear next to the menu item and you can just "
257 "press that key without opening the menu in future. "
258 "To save the current menu short-cuts for next time, "
259 "click the Save button at the bottom of this window.");
260 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
261 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 2, 0, 1);
263 label = gtk_label_new("'Xterm here' program:");
264 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
265 xterm_here_entry = gtk_entry_new();
266 gtk_table_attach_defaults(GTK_TABLE(table), xterm_here_entry,
267 1, 2, 1, 2);
269 return table;
272 static char *load_xterm_here(char *data)
274 g_free(xterm_here_value);
275 xterm_here_value = g_strdup(data);
276 return NULL;
279 static void update_options()
281 gtk_entry_set_text(GTK_ENTRY(xterm_here_entry), xterm_here_value);
284 static void set_options()
286 g_free(xterm_here_value);
287 xterm_here_value = g_strdup(gtk_entry_get_text(
288 GTK_ENTRY(xterm_here_entry)));
291 static void save_options()
293 char *menurc;
295 menurc = choices_find_path_save("menus");
296 if (menurc)
297 gtk_item_factory_dump_rc(menurc, NULL, TRUE);
299 option_write("xterm_here", xterm_here_value);
303 static void items_sensitive(GtkWidget *menu, int from, int n, gboolean state)
305 GList *items, *item;
307 items = gtk_container_children(GTK_CONTAINER(menu));
309 item = g_list_nth(items, from);
310 while (item && n--)
312 gtk_widget_set_sensitive(GTK_BIN(item->data)->child, state);
313 item = item->next;
316 g_list_free(items);
319 static void position_menu(GtkMenu *menu, gint *x, gint *y, gpointer data)
321 int *pos = (int *) data;
322 GtkRequisition requisition;
324 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
326 if (pos[0] == -1)
327 *x = screen_width - MENU_MARGIN - requisition.width;
328 else if (pos[0] == -2)
329 *x = MENU_MARGIN;
330 else
331 *x = pos[0] - (requisition.width >> 2);
333 if (pos[1] == -1)
334 *y = screen_height - MENU_MARGIN - requisition.height;
335 else if (pos[1] == -2)
336 *y = MENU_MARGIN;
337 else
338 *y = pos[1] - (requisition.height >> 2);
340 *x = CLAMP(*x, 0, screen_width - requisition.width);
341 *y = CLAMP(*y, 0, screen_height - requisition.height);
344 void show_filer_menu(FilerWindow *filer_window, GdkEventButton *event,
345 int item)
347 GString *buffer;
348 GtkWidget *file_label, *file_menu;
349 FileItem *file_item;
350 int pos[2];
352 pos[0] = event->x_root;
353 pos[1] = event->y_root;
355 window_with_focus = filer_window;
357 if (filer_window->panel)
359 switch (filer_window->panel_side)
361 case TOP: pos[1] = -2; break;
362 case BOTTOM: pos[1] = -1; break;
363 case LEFT: pos[0] = -2; break;
364 case RIGHT: pos[0] = -1; break;
368 if (filer_window->panel)
369 collection_clear_selection(filer_window->collection); /* ??? */
371 if (filer_window->collection->number_selected == 0 && item >= 0)
373 collection_select_item(filer_window->collection, item);
374 filer_window->temp_item_selected = TRUE;
376 else
377 filer_window->temp_item_selected = FALSE;
379 if (filer_window->panel)
381 file_label = panel_file_item;
382 file_menu = panel_file_menu;
384 gtk_check_menu_item_set_active(
385 GTK_CHECK_MENU_ITEM(panel_hidden_menu),
386 filer_window->show_hidden);
388 else
390 file_label = filer_file_item;
391 file_menu = filer_file_menu;
393 gtk_check_menu_item_set_active(
394 GTK_CHECK_MENU_ITEM(filer_hidden_menu),
395 filer_window->show_hidden);
398 buffer = g_string_new(NULL);
399 switch (filer_window->collection->number_selected)
401 case 0:
402 g_string_assign(buffer, "<nothing selected>");
403 items_sensitive(file_menu, 0, 5, FALSE);
404 items_sensitive(file_menu, 6, -1, FALSE);
405 gtk_widget_set_sensitive(file_label, FALSE);
406 break;
407 case 1:
408 items_sensitive(file_menu, 0, 5, TRUE);
409 items_sensitive(file_menu, 6, -1, TRUE);
410 gtk_widget_set_sensitive(file_label, TRUE);
411 file_item = selected_item(filer_window->collection);
412 g_string_sprintf(buffer, "%s '%s'",
413 basetype_name(file_item),
414 file_item->leafname);
415 break;
416 default:
417 items_sensitive(file_menu, 0, 5, FALSE);
418 items_sensitive(file_menu, 6, -1, TRUE);
419 gtk_widget_set_sensitive(file_label, TRUE);
420 g_string_sprintf(buffer, "%d items",
421 filer_window->collection->number_selected);
422 break;
425 gtk_label_set_text(GTK_LABEL(file_label), buffer->str);
427 g_string_free(buffer, TRUE);
429 gtk_menu_popup(filer_window->panel ? GTK_MENU(panel_menu)
430 : GTK_MENU(filer_menu),
431 NULL, NULL, position_menu,
432 (gpointer) pos, event->button, event->time);
435 static void menu_closed(GtkWidget *widget)
437 if (window_with_focus == NULL)
438 return; /* Close panel item chosen? */
440 if (window_with_focus->temp_item_selected)
442 collection_clear_selection(window_with_focus->collection);
443 window_with_focus->temp_item_selected = FALSE;
447 /* Actions */
449 /* Fake action to warn when a menu item does nothing */
450 static void not_yet(gpointer data, guint action, GtkWidget *widget)
452 delayed_error("ROX-Filer", "Sorry, that feature isn't implemented yet");
455 static void hidden(gpointer data, guint action, GtkWidget *widget)
457 gboolean new;
458 GtkWidget *item;
460 g_return_if_fail(window_with_focus != NULL);
462 item = window_with_focus->panel ? panel_hidden_menu : filer_hidden_menu;
463 new = GTK_CHECK_MENU_ITEM(item)->active;
465 if (window_with_focus->show_hidden == new)
466 return;
468 window_with_focus->show_hidden = new;
469 update_dir(window_with_focus);
472 static void refresh(gpointer data, guint action, GtkWidget *widget)
474 g_return_if_fail(window_with_focus != NULL);
476 update_dir(window_with_focus);
479 static void delete(gpointer data, guint action, GtkWidget *widget)
481 g_return_if_fail(window_with_focus != NULL);
483 action_delete(window_with_focus);
486 static void usage(gpointer data, guint action, GtkWidget *widget)
488 g_return_if_fail(window_with_focus != NULL);
490 action_usage(window_with_focus);
493 static gboolean copy_cb(char *initial, char *path)
495 char *new_dir, *slash;
496 int len;
497 GString *command;
498 gboolean retval = TRUE;
500 slash = strrchr(path, '/');
501 if (!slash)
503 report_error("ROX-Filer", "Missing '/' in new pathname");
504 return FALSE;
507 if (access(path, F_OK) == 0)
509 report_error("ROX-Filer",
510 "An item with this name already exists");
511 return FALSE;
514 len = slash - path;
515 new_dir = g_malloc(len + 1);
516 memcpy(new_dir, path, len);
517 new_dir[len] = '\0';
519 command = g_string_new(NULL);
520 g_string_sprintf(command, "cp -a %s %s", initial, path);
522 if (system(command->str))
524 g_string_append(command, " failed!");
525 report_error("ROX-Filer", command->str);
526 retval = FALSE;
529 g_string_free(command, TRUE);
531 refresh_dirs(new_dir);
532 return retval;
535 static void copy_item(gpointer data, guint action, GtkWidget *widget)
537 Collection *collection;
539 g_return_if_fail(window_with_focus != NULL);
541 collection = window_with_focus->collection;
542 if (collection->number_selected != 1)
543 report_error("ROX-Filer", "You must select a single "
544 "item to copy");
545 else
547 FileItem *item = selected_item(collection);
549 savebox_show(window_with_focus, "Copy",
550 window_with_focus->path, item->leafname,
551 item->image, copy_cb);
555 static gboolean rename_cb(char *initial, char *path)
557 if (rename(initial, path))
559 report_error("ROX-Filer: rename()", g_strerror(errno));
560 return FALSE;
562 return TRUE;
565 static void rename_item(gpointer data, guint action, GtkWidget *widget)
567 Collection *collection;
569 g_return_if_fail(window_with_focus != NULL);
571 collection = window_with_focus->collection;
572 if (collection->number_selected != 1)
573 report_error("ROX-Filer", "You must select a single "
574 "item to rename");
575 else
577 FileItem *item = selected_item(collection);
579 savebox_show(window_with_focus, "Rename",
580 window_with_focus->path, item->leafname,
581 item->image, rename_cb);
585 static gboolean link_cb(char *initial, char *path)
587 if (symlink(initial, path))
589 report_error("ROX-Filer: symlink()", g_strerror(errno));
590 return FALSE;
592 return TRUE;
595 static void link_item(gpointer data, guint action, GtkWidget *widget)
597 Collection *collection;
599 g_return_if_fail(window_with_focus != NULL);
601 collection = window_with_focus->collection;
602 if (collection->number_selected != 1)
603 report_error("ROX-Filer", "You must select a single "
604 "item to link");
605 else
607 FileItem *item = selected_item(collection);
609 savebox_show(window_with_focus, "Symlink",
610 window_with_focus->path, item->leafname,
611 item->image, link_cb);
615 static void show_file_info(gpointer data, guint action, GtkWidget *widget)
617 GtkWidget *window, *table, *label, *button, *frame;
618 GtkWidget *file_label;
619 GString *gstring;
620 char *string;
621 int file_data[2];
622 char *path;
623 char buffer[20];
624 char *argv[] = {"file", "-b", NULL, NULL};
625 int got;
626 Collection *collection;
627 FileItem *file;
628 struct stat info;
630 g_return_if_fail(window_with_focus != NULL);
632 collection = window_with_focus->collection;
633 if (collection->number_selected != 1)
635 report_error("ROX-Filer", "You must select a single "
636 "item before using Info");
637 return;
639 file = selected_item(collection);
640 path = make_path(window_with_focus->path, file->leafname)->str;
641 if (lstat(path, &info))
643 delayed_error("ROX-Filer", g_strerror(errno));
644 return;
647 gstring = g_string_new(NULL);
649 window = gtk_window_new(GTK_WINDOW_DIALOG);
650 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_MOUSE);
651 gtk_container_set_border_width(GTK_CONTAINER(window), 4);
652 gtk_window_set_title(GTK_WINDOW(window), path);
654 table = gtk_table_new(9, 2, FALSE);
655 gtk_container_add(GTK_CONTAINER(window), table);
656 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
657 gtk_table_set_col_spacings(GTK_TABLE(table), 4);
659 label = gtk_label_new("Owner, group:");
660 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
661 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_RIGHT);
662 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
663 g_string_sprintf(gstring, "%s, %s", user_name(info.st_uid),
664 group_name(info.st_gid));
665 label = gtk_label_new(gstring->str);
666 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
667 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 0, 1);
669 label = gtk_label_new("Size:");
670 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
671 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
672 if (info.st_size >=2048)
674 g_string_sprintf(gstring, "%s (%ld bytes)",
675 format_size((unsigned long) info.st_size),
676 (unsigned long) info.st_size);
678 else
680 g_string_sprintf(gstring, "%ld bytes",
681 (unsigned long) info.st_size);
683 label = gtk_label_new(gstring->str);
684 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
685 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 1, 2);
687 label = gtk_label_new("Change time:");
688 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
689 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3);
690 g_string_sprintf(gstring, "%s", ctime(&info.st_ctime));
691 g_string_truncate(gstring, gstring->len - 1);
692 label = gtk_label_new(gstring->str);
693 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
694 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 2, 3);
696 label = gtk_label_new("Modify time:");
697 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
698 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4);
699 g_string_sprintf(gstring, "%s", ctime(&info.st_mtime));
700 g_string_truncate(gstring, gstring->len - 1);
701 label = gtk_label_new(gstring->str);
702 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
703 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 3, 4);
705 label = gtk_label_new("Access time:");
706 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
707 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 4, 5);
708 g_string_sprintf(gstring, "%s", ctime(&info.st_atime));
709 g_string_truncate(gstring, gstring->len - 1);
710 label = gtk_label_new(gstring->str);
711 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
712 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 4, 5);
714 label = gtk_label_new("Permissions:");
715 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
716 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 5, 6);
717 g_string_sprintf(gstring, "%o", (unsigned int) info.st_mode);
718 label = gtk_label_new(gstring->str);
719 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
720 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 5, 6);
722 label = gtk_label_new("MIME type:");
723 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
724 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 6, 7);
725 if (file->mime_type)
727 string = g_strconcat(file->mime_type->media_type, "/",
728 file->mime_type->subtype, NULL);
729 label = gtk_label_new(string);
730 g_free(string);
732 else
733 label = gtk_label_new("-");
734 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
735 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 6, 7);
737 frame = gtk_frame_new("file(1) says...");
738 gtk_table_attach_defaults(GTK_TABLE(table), frame, 0, 2, 7, 8);
739 file_label = gtk_label_new("<nothing yet>");
740 gtk_misc_set_padding(GTK_MISC(file_label), 4, 4);
741 gtk_label_set_line_wrap(GTK_LABEL(file_label), TRUE);
742 gtk_container_add(GTK_CONTAINER(frame), file_label);
744 button = gtk_button_new_with_label("OK");
745 gtk_table_attach(GTK_TABLE(table), button, 0, 2, 8, 9,
746 GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 40, 4);
747 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
748 gtk_widget_destroy, GTK_OBJECT(window));
750 gtk_widget_show_all(window);
751 gdk_flush();
753 pipe(file_data);
754 switch (fork())
756 case -1:
757 close(file_data[0]);
758 close(file_data[1]);
759 gtk_label_set_text(GTK_LABEL(file_label),
760 "fork() error");
761 g_string_free(gstring, TRUE);
762 return;
763 case 0:
764 close(file_data[0]);
765 dup2(file_data[1], STDOUT_FILENO);
766 dup2(file_data[1], STDERR_FILENO);
767 #ifdef FILE_B_FLAG
768 argv[2] = path;
769 #else
770 argv[1] = path;
771 #endif
772 if (execvp(argv[0], argv))
773 fprintf(stderr, "execvp() error: %s\n",
774 g_strerror(errno));
775 _exit(0);
777 /* We are the parent... */
778 close(file_data[1]);
779 g_string_truncate(gstring, 0);
780 while (gtk_events_pending())
781 g_main_iteration(FALSE);
782 while ((got = read(file_data[0], buffer, sizeof(buffer) - 1)))
784 buffer[got] = '\0';
785 g_string_append(gstring, buffer);
787 close(file_data[0]);
788 gtk_label_set_text(GTK_LABEL(file_label), gstring->str);
789 g_string_free(gstring, TRUE);
792 static void help(gpointer data, guint action, GtkWidget *widget)
794 Collection *collection;
795 FileItem *item;
797 g_return_if_fail(window_with_focus != NULL);
799 collection = window_with_focus->collection;
800 if (collection->number_selected != 1)
802 report_error("ROX-Filer", "You must select a single "
803 "item to get help on");
804 return;
806 item = selected_item(collection);
807 switch (item->base_type)
809 case TYPE_FILE:
810 if (item->flags & ITEM_FLAG_EXEC_FILE)
811 report_error("Executable file",
812 "This is a file with an eXecute bit "
813 "set - it can be run as a program.");
814 else
815 report_error("File",
816 "This is a data file. Try using the "
817 "Info menu item to find out more...");
818 break;
819 case TYPE_DIRECTORY:
820 if (item->flags & ITEM_FLAG_APPDIR)
821 app_show_help(
822 make_path(window_with_focus->path,
823 item->leafname)->str);
824 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
825 report_error("Mount point",
826 "A mount point is a directory which another "
827 "filing system can be mounted on. Everything "
828 "on the mounted filesystem then appears to be "
829 "inside the directory.");
830 else
831 report_error("Directory",
832 "This is a directory. It contains an index to "
833 "other items - open it to see the list.");
834 break;
835 case TYPE_CHAR_DEVICE:
836 case TYPE_BLOCK_DEVICE:
837 report_error("Device file",
838 "Device files allow you to read from or write "
839 "to a device driver as though it was an "
840 "ordinary file.");
841 break;
842 case TYPE_PIPE:
843 report_error("Named pipe",
844 "Pipes allow different programs to "
845 "communicate. One program writes data to the "
846 "pipe while another one reads it out again.");
847 break;
848 case TYPE_SOCKET:
849 report_error("Socket",
850 "Sockets allow processes to communicate.");
851 break;
852 default:
853 report_error("Unknown type",
854 "I couldn't find out what kind of file this "
855 "is. Maybe it doesn't exist anymore or you "
856 "don't have search permission on the directory "
857 "it's in?");
858 break;
862 static void mount(gpointer data, guint action, GtkWidget *widget)
864 #ifdef DO_MOUNT_POINTS
865 FileItem *item;
866 int i;
867 Collection *collection;
868 char *error = NULL;
869 int count = 0;
871 g_return_if_fail(window_with_focus != NULL);
873 collection = window_with_focus->collection;
875 for (i = 0; i < collection->number_of_items; i++)
876 if (collection->items[i].selected)
878 item = (FileItem *) collection->items[i].data;
879 if (item->flags & ITEM_FLAG_MOUNT_POINT)
881 char *argv[] = {"mount", NULL, NULL};
882 int child;
884 count++;
885 if (item->flags & ITEM_FLAG_MOUNTED)
886 argv[0] = "umount";
887 argv[1] = make_path(window_with_focus->path,
888 item->leafname)->str;
889 child = spawn(argv);
890 if (child)
891 waitpid(child, NULL, 0);
892 else
893 error = "Failed to run mount/umount";
896 if (count)
897 update_dir(window_with_focus);
898 else if (!error)
899 error = "You must select some mount points first!";
901 if (error)
902 report_error("ROX-Filer", error);
903 #else
904 report_error("ROX-Filer",
905 "ROX-Filer does not yet support mount points on your "
906 "system. Sorry.");
907 #endif /* DO_MOUNT_POINTS */
910 static void select_all(gpointer data, guint action, GtkWidget *widget)
912 g_return_if_fail(window_with_focus != NULL);
914 collection_select_all(window_with_focus->collection);
915 window_with_focus->temp_item_selected = FALSE;
918 static void clear_selection(gpointer data, guint action, GtkWidget *widget)
920 g_return_if_fail(window_with_focus != NULL);
922 collection_clear_selection(window_with_focus->collection);
923 window_with_focus->temp_item_selected = FALSE;
926 static void show_options(gpointer data, guint action, GtkWidget *widget)
928 g_return_if_fail(window_with_focus != NULL);
930 options_show(window_with_focus);
933 static gboolean new_directory_cb(char *initial, char *path)
935 if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO))
937 report_error("mkdir", g_strerror(errno));
938 return FALSE;
940 return TRUE;
943 static void new_directory(gpointer data, guint action, GtkWidget *widget)
945 g_return_if_fail(window_with_focus != NULL);
947 savebox_show(window_with_focus, "Create directory",
948 window_with_focus->path, "NewDir",
949 default_pixmap + TYPE_DIRECTORY, new_directory_cb);
952 static void xterm_here(gpointer data, guint action, GtkWidget *widget)
954 char *argv[] = {NULL, NULL};
956 argv[0] = xterm_here_value;
958 g_return_if_fail(window_with_focus != NULL);
960 if (!spawn_full(argv, window_with_focus->path, 0))
961 report_error("ROX-Filer", "Failed to fork() child "
962 "process");
965 static void open_parent(gpointer data, guint action, GtkWidget *widget)
967 g_return_if_fail(window_with_focus != NULL);
969 filer_opendir(make_path(window_with_focus->path, "/..")->str,
970 FALSE, BOTTOM);
973 static void open_parent_same(gpointer data, guint action, GtkWidget *widget)
975 g_return_if_fail(window_with_focus != NULL);
977 change_to_parent(window_with_focus);
980 static void new_window(gpointer data, guint action, GtkWidget *widget)
982 g_return_if_fail(window_with_focus != NULL);
984 filer_opendir(window_with_focus->path, FALSE, BOTTOM);
987 static void close_window(gpointer data, guint action, GtkWidget *widget)
989 g_return_if_fail(window_with_focus != NULL);
991 gtk_widget_destroy(window_with_focus->window);
994 static void open_as_dir(gpointer data, guint action, GtkWidget *widget)
996 g_return_if_fail(window_with_focus != NULL);
998 filer_opendir(window_with_focus->path, FALSE, BOTTOM);
1001 static void close_panel(gpointer data, guint action, GtkWidget *widget)
1003 g_return_if_fail(window_with_focus != NULL);
1005 gtk_widget_destroy(window_with_focus->window);