r156: Fixed some compiler warnings for systems with signed char types.
[rox-filer/ma.git] / ROX-Filer / src / menu.c
blobefe88f0e623d1cc37668ca0b5acff6b216e12de2
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 sort_name(gpointer data, guint action, GtkWidget *widget);
74 static void sort_type(gpointer data, guint action, GtkWidget *widget);
75 static void sort_size(gpointer data, guint action, GtkWidget *widget);
76 static void sort_date(gpointer data, guint action, GtkWidget *widget);
78 static void hidden(gpointer data, guint action, GtkWidget *widget);
79 static void refresh(gpointer data, guint action, GtkWidget *widget);
81 static void copy_item(gpointer data, guint action, GtkWidget *widget);
82 static void rename_item(gpointer data, guint action, GtkWidget *widget);
83 static void link_item(gpointer data, guint action, GtkWidget *widget);
84 static void help(gpointer data, guint action, GtkWidget *widget);
85 static void show_file_info(gpointer data, guint action, GtkWidget *widget);
86 static void mount(gpointer data, guint action, GtkWidget *widget);
87 static void delete(gpointer data, guint action, GtkWidget *widget);
88 static void usage(gpointer data, guint action, GtkWidget *widget);
90 static void select_all(gpointer data, guint action, GtkWidget *widget);
91 static void clear_selection(gpointer data, guint action, GtkWidget *widget);
92 static void show_options(gpointer data, guint action, GtkWidget *widget);
93 static void new_directory(gpointer data, guint action, GtkWidget *widget);
94 static void xterm_here(gpointer data, guint action, GtkWidget *widget);
96 static void open_parent_same(gpointer data, guint action, GtkWidget *widget);
97 static void open_parent(gpointer data, guint action, GtkWidget *widget);
98 static void new_window(gpointer data, guint action, GtkWidget *widget);
99 static void close_window(gpointer data, guint action, GtkWidget *widget);
101 static void open_as_dir(gpointer data, guint action, GtkWidget *widget);
102 static void close_panel(gpointer data, guint action, GtkWidget *widget);
104 static GtkWidget *create_options();
105 static void update_options();
106 static void set_options();
107 static void save_options();
109 static OptionsSection options =
111 "Menu options",
112 create_options,
113 update_options,
114 set_options,
115 save_options
119 static GtkWidget *filer_menu; /* The popup filer menu */
120 static GtkWidget *filer_file_item; /* The File '' label */
121 static GtkWidget *filer_file_menu; /* The File '' menu */
122 static GtkWidget *filer_hidden_menu; /* The Show Hidden item */
123 static GtkWidget *panel_menu; /* The popup panel menu */
124 static GtkWidget *panel_file_item; /* The File '' label */
125 static GtkWidget *panel_file_menu; /* The File '' menu */
126 static GtkWidget *panel_hidden_menu; /* The Show Hidden item */
128 static gint screen_width, screen_height;
130 static GtkItemFactoryEntry filer_menu_def[] = {
131 {"/Display", NULL, NULL, 0, "<Branch>"},
132 {"/Display/Large Icons", NULL, large, 0, "<RadioItem>"},
133 {"/Display/Small Icons", NULL, small, 0, "/Display/Large Icons"},
134 {"/Display/Full Info", NULL, full_info, 0, "/Display/Large Icons"},
135 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
136 {"/Display/Sort by Name", NULL, sort_name, 0, "<RadioItem>"},
137 {"/Display/Sort by Type", NULL, sort_type, 0, "/Display/Sort by Name"},
138 {"/Display/Sort by Date", NULL, sort_date, 0, "/Display/Sort by Name"},
139 {"/Display/Sort by Size", NULL, sort_size, 0, "/Display/Sort by Name"},
140 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
141 {"/Display/Show Hidden", C_"H", hidden, 0, "<ToggleItem>"},
142 {"/Display/Refresh", C_"L", refresh, 0, NULL},
143 {"/File", NULL, NULL, 0, "<Branch>"},
144 {"/File/Copy...", NULL, copy_item, 0, NULL},
145 {"/File/Rename...", NULL, rename_item, 0, NULL},
146 {"/File/Link...", NULL, link_item, 0, NULL},
147 {"/File/Help", "F1", help, 0, NULL},
148 {"/File/Info", "I", show_file_info, 0, NULL},
149 {"/File/Separator", NULL, NULL, 0, "<Separator>"},
150 {"/File/Mount", "M", mount, 0, NULL},
151 {"/File/Delete", C_"X", delete, 0, NULL},
152 {"/File/Disk Usage", "U", usage, 0, NULL},
153 {"/File/Permissions", NULL, not_yet, 0, NULL},
154 {"/File/Touch", NULL, not_yet, 0, NULL},
155 {"/File/Find", NULL, not_yet, 0, NULL},
156 {"/Select All", C_"A", select_all, 0, NULL},
157 {"/Clear Selection", C_"Z", clear_selection, 0, NULL},
158 {"/Options...", NULL, show_options, 0, NULL},
159 {"/New directory", NULL, new_directory, 0, NULL},
160 {"/Xterm here", NULL, xterm_here, 0, NULL},
161 {"/Window", NULL, NULL, 0, "<Branch>"},
162 {"/Window/Parent, new window", NULL, open_parent, 0, NULL},
163 {"/Window/Parent, same window", NULL, open_parent_same, 0, NULL},
164 {"/Window/New window", NULL, new_window, 0, NULL},
165 {"/Window/Close window", C_"Q", close_window, 0, NULL},
168 static GtkItemFactoryEntry panel_menu_def[] = {
169 {"/Display", NULL, NULL, 0, "<Branch>"},
170 {"/Display/Large Icons", NULL, large, 0, "<RadioItem>"},
171 {"/Display/Small Icons", NULL, small, 0, "/Display/Large Icons"},
172 {"/Display/Full Info", NULL, full_info, 0, "/Display/Large Icons"},
173 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
174 {"/Display/Sort by Name", NULL, sort_name, 0, "<RadioItem>"},
175 {"/Display/Sort by Type", NULL, sort_type, 0, "/Display/Sort by Name"},
176 {"/Display/Sort by Date", NULL, sort_date, 0, "/Display/Sort by Name"},
177 {"/Display/Sort by Size", NULL, sort_size, 0, "/Display/Sort by Name"},
178 {"/Display/Separator", NULL, NULL, 0, "<Separator>"},
179 {"/Display/Show Hidden", NULL, hidden, 0, "<ToggleItem>"},
180 {"/Display/Refresh", NULL, refresh, 0, NULL},
181 {"/File", NULL, NULL, 0, "<Branch>"},
182 {"/File/Help", NULL, help, 0, NULL},
183 {"/File/Info", NULL, show_file_info, 0, NULL},
184 {"/File/Delete", NULL, delete, 0, NULL},
185 {"/Open as directory", NULL, open_as_dir, 0, NULL},
186 {"/Close panel", NULL, close_panel, 0, NULL},
189 void menu_init()
191 GtkItemFactory *item_factory;
192 char *menurc;
193 GList *items;
195 /* This call starts returning strange values after a while, so get
196 * the result here during init.
198 gdk_window_get_size(GDK_ROOT_PARENT(), &screen_width, &screen_height);
200 filer_keys = gtk_accel_group_new();
201 item_factory = gtk_item_factory_new(GTK_TYPE_MENU,
202 "<filer>",
203 filer_keys);
204 gtk_item_factory_create_items(item_factory,
205 sizeof(filer_menu_def) / sizeof(*filer_menu_def),
206 filer_menu_def,
207 NULL);
208 filer_menu = gtk_item_factory_get_widget(item_factory, "<filer>");
209 filer_file_menu = gtk_item_factory_get_widget(item_factory,
210 "<filer>/File");
211 filer_hidden_menu = gtk_item_factory_get_widget(item_factory,
212 "<filer>/Display/Show Hidden");
213 items = gtk_container_children(GTK_CONTAINER(filer_menu));
214 filer_file_item = GTK_BIN(g_list_nth(items, 1)->data)->child;
215 g_list_free(items);
217 panel_keys = gtk_accel_group_new();
218 item_factory = gtk_item_factory_new(GTK_TYPE_MENU,
219 "<panel>",
220 panel_keys);
221 gtk_item_factory_create_items(item_factory,
222 sizeof(panel_menu_def) / sizeof(*panel_menu_def),
223 panel_menu_def,
224 NULL);
225 panel_menu = gtk_item_factory_get_widget(item_factory, "<panel>");
226 panel_file_menu = gtk_item_factory_get_widget(item_factory,
227 "<panel>/File");
228 panel_hidden_menu = gtk_item_factory_get_widget(item_factory,
229 "<panel>/Display/Show Hidden");
230 items = gtk_container_children(GTK_CONTAINER(panel_menu));
231 panel_file_item = GTK_BIN(g_list_nth(items, 1)->data)->child;
232 g_list_free(items);
234 menurc = choices_find_path_load("menus");
235 if (menurc)
236 gtk_item_factory_parse_rc(menurc);
238 gtk_signal_connect(GTK_OBJECT(panel_menu), "unmap_event",
239 GTK_SIGNAL_FUNC(menu_closed), NULL);
240 gtk_signal_connect(GTK_OBJECT(filer_menu), "unmap_event",
241 GTK_SIGNAL_FUNC(menu_closed), NULL);
243 gtk_accel_group_lock(panel_keys);
245 options_sections = g_slist_prepend(options_sections, &options);
246 xterm_here_value = g_strdup("xterm");
247 option_register("xterm_here", load_xterm_here);
250 /* Build up some option widgets to go in the options dialog, but don't
251 * fill them in yet.
253 static GtkWidget *create_options()
255 GtkWidget *table, *label;
257 table = gtk_table_new(2, 2, FALSE);
258 gtk_container_set_border_width(GTK_CONTAINER(table), 4);
260 label = gtk_label_new("To set the keyboard short-cuts you simply open "
261 "the menu over a filer window, move the pointer over "
262 "the item you want to use and press a key. The key "
263 "will appear next to the menu item and you can just "
264 "press that key without opening the menu in future. "
265 "To save the current menu short-cuts for next time, "
266 "click the Save button at the bottom of this window.");
267 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
268 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 2, 0, 1);
270 label = gtk_label_new("'Xterm here' program:");
271 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
272 xterm_here_entry = gtk_entry_new();
273 gtk_table_attach_defaults(GTK_TABLE(table), xterm_here_entry,
274 1, 2, 1, 2);
276 return table;
279 static char *load_xterm_here(char *data)
281 g_free(xterm_here_value);
282 xterm_here_value = g_strdup(data);
283 return NULL;
286 static void update_options()
288 gtk_entry_set_text(GTK_ENTRY(xterm_here_entry), xterm_here_value);
291 static void set_options()
293 g_free(xterm_here_value);
294 xterm_here_value = g_strdup(gtk_entry_get_text(
295 GTK_ENTRY(xterm_here_entry)));
298 static void save_options()
300 char *menurc;
302 menurc = choices_find_path_save("menus");
303 if (menurc)
304 gtk_item_factory_dump_rc(menurc, NULL, TRUE);
306 option_write("xterm_here", xterm_here_value);
310 static void items_sensitive(GtkWidget *menu, int from, int n, gboolean state)
312 GList *items, *item;
314 items = gtk_container_children(GTK_CONTAINER(menu));
316 item = g_list_nth(items, from);
317 while (item && n--)
319 gtk_widget_set_sensitive(GTK_BIN(item->data)->child, state);
320 item = item->next;
323 g_list_free(items);
326 static void position_menu(GtkMenu *menu, gint *x, gint *y, gpointer data)
328 int *pos = (int *) data;
329 GtkRequisition requisition;
331 gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
333 if (pos[0] == -1)
334 *x = screen_width - MENU_MARGIN - requisition.width;
335 else if (pos[0] == -2)
336 *x = MENU_MARGIN;
337 else
338 *x = pos[0] - (requisition.width >> 2);
340 if (pos[1] == -1)
341 *y = screen_height - MENU_MARGIN - requisition.height;
342 else if (pos[1] == -2)
343 *y = MENU_MARGIN;
344 else
345 *y = pos[1] - (requisition.height >> 2);
347 *x = CLAMP(*x, 0, screen_width - requisition.width);
348 *y = CLAMP(*y, 0, screen_height - requisition.height);
351 void show_filer_menu(FilerWindow *filer_window, GdkEventButton *event,
352 int item)
354 GString *buffer;
355 GtkWidget *file_label, *file_menu;
356 DirItem *file_item;
357 int pos[2];
359 pos[0] = event->x_root;
360 pos[1] = event->y_root;
362 window_with_focus = filer_window;
364 if (filer_window->panel)
366 switch (filer_window->panel_side)
368 case TOP: pos[1] = -2; break;
369 case BOTTOM: pos[1] = -1; break;
370 case LEFT: pos[0] = -2; break;
371 case RIGHT: pos[0] = -1; break;
375 if (filer_window->panel)
376 collection_clear_selection(filer_window->collection); /* ??? */
378 if (filer_window->collection->number_selected == 0 && item >= 0)
380 collection_select_item(filer_window->collection, item);
381 filer_window->temp_item_selected = TRUE;
383 else
384 filer_window->temp_item_selected = FALSE;
386 if (filer_window->panel)
388 file_label = panel_file_item;
389 file_menu = panel_file_menu;
391 gtk_check_menu_item_set_active(
392 GTK_CHECK_MENU_ITEM(panel_hidden_menu),
393 filer_window->show_hidden);
395 else
397 file_label = filer_file_item;
398 file_menu = filer_file_menu;
400 gtk_check_menu_item_set_active(
401 GTK_CHECK_MENU_ITEM(filer_hidden_menu),
402 filer_window->show_hidden);
405 buffer = g_string_new(NULL);
406 switch (filer_window->collection->number_selected)
408 case 0:
409 g_string_assign(buffer, "<nothing selected>");
410 items_sensitive(file_menu, 0, 5, FALSE);
411 items_sensitive(file_menu, 6, -1, FALSE);
412 gtk_widget_set_sensitive(file_label, FALSE);
413 break;
414 case 1:
415 items_sensitive(file_menu, 0, 5, TRUE);
416 items_sensitive(file_menu, 6, -1, TRUE);
417 gtk_widget_set_sensitive(file_label, TRUE);
418 file_item = selected_item(filer_window->collection);
419 g_string_sprintf(buffer, "%s '%s'",
420 basetype_name(file_item),
421 file_item->leafname);
422 break;
423 default:
424 items_sensitive(file_menu, 0, 5, FALSE);
425 items_sensitive(file_menu, 6, -1, TRUE);
426 gtk_widget_set_sensitive(file_label, TRUE);
427 g_string_sprintf(buffer, "%d items",
428 filer_window->collection->number_selected);
429 break;
432 gtk_label_set_text(GTK_LABEL(file_label), buffer->str);
434 g_string_free(buffer, TRUE);
436 gtk_menu_popup(filer_window->panel ? GTK_MENU(panel_menu)
437 : GTK_MENU(filer_menu),
438 NULL, NULL, position_menu,
439 (gpointer) pos, event->button, event->time);
442 static void menu_closed(GtkWidget *widget)
444 if (window_with_focus == NULL)
445 return; /* Close panel item chosen? */
447 if (window_with_focus->temp_item_selected)
449 collection_clear_selection(window_with_focus->collection);
450 window_with_focus->temp_item_selected = FALSE;
454 /* Actions */
456 /* Fake action to warn when a menu item does nothing */
457 static void not_yet(gpointer data, guint action, GtkWidget *widget)
459 delayed_error("ROX-Filer", "Sorry, that feature isn't implemented yet");
462 static void large(gpointer data, guint action, GtkWidget *widget)
464 g_return_if_fail(window_with_focus != NULL);
466 filer_style_set(window_with_focus, LARGE_ICONS);
469 static void small(gpointer data, guint action, GtkWidget *widget)
471 g_return_if_fail(window_with_focus != NULL);
473 filer_style_set(window_with_focus, SMALL_ICONS);
476 static void full_info(gpointer data, guint action, GtkWidget *widget)
478 g_return_if_fail(window_with_focus != NULL);
480 filer_style_set(window_with_focus, FULL_INFO);
483 static void sort_name(gpointer data, guint action, GtkWidget *widget)
485 g_return_if_fail(window_with_focus != NULL);
487 filer_set_sort_fn(window_with_focus, sort_by_name);
490 static void sort_type(gpointer data, guint action, GtkWidget *widget)
492 g_return_if_fail(window_with_focus != NULL);
494 filer_set_sort_fn(window_with_focus, sort_by_type);
497 static void sort_date(gpointer data, guint action, GtkWidget *widget)
499 g_return_if_fail(window_with_focus != NULL);
501 filer_set_sort_fn(window_with_focus, sort_by_date);
504 static void sort_size(gpointer data, guint action, GtkWidget *widget)
506 g_return_if_fail(window_with_focus != NULL);
508 filer_set_sort_fn(window_with_focus, sort_by_size);
511 static void hidden(gpointer data, guint action, GtkWidget *widget)
513 gboolean new;
514 GtkWidget *item;
516 g_return_if_fail(window_with_focus != NULL);
518 item = window_with_focus->panel ? panel_hidden_menu : filer_hidden_menu;
519 new = GTK_CHECK_MENU_ITEM(item)->active;
521 filer_set_hidden(window_with_focus, new);
524 static void refresh(gpointer data, guint action, GtkWidget *widget)
526 g_return_if_fail(window_with_focus != NULL);
528 full_refresh();
529 update_dir(window_with_focus);
532 static void mount(gpointer data, guint action, GtkWidget *widget)
534 g_return_if_fail(window_with_focus != NULL);
536 action_mount(window_with_focus);
539 static void delete(gpointer data, guint action, GtkWidget *widget)
541 g_return_if_fail(window_with_focus != NULL);
543 action_delete(window_with_focus);
546 static void usage(gpointer data, guint action, GtkWidget *widget)
548 g_return_if_fail(window_with_focus != NULL);
550 action_usage(window_with_focus);
553 static gboolean copy_cb(char *initial, char *path)
555 char *new_dir, *slash;
556 int len;
557 GString *command;
558 gboolean retval = TRUE;
560 slash = strrchr(path, '/');
561 if (!slash)
563 report_error("ROX-Filer", "Missing '/' in new pathname");
564 return FALSE;
567 if (access(path, F_OK) == 0)
569 report_error("ROX-Filer",
570 "An item with this name already exists");
571 return FALSE;
574 len = slash - path;
575 new_dir = g_malloc(len + 1);
576 memcpy(new_dir, path, len);
577 new_dir[len] = '\0';
579 command = g_string_new(NULL);
580 g_string_sprintf(command, "cp -a %s %s", initial, path);
582 if (system(command->str))
584 g_string_append(command, " failed!");
585 report_error("ROX-Filer", command->str);
586 retval = FALSE;
589 g_string_free(command, TRUE);
591 refresh_dirs(new_dir);
592 return retval;
595 static void copy_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 copy");
605 else
607 DirItem *item = selected_item(collection);
609 savebox_show(window_with_focus, "Copy",
610 window_with_focus->path,
611 item->leafname,
612 item->image, copy_cb);
616 static gboolean rename_cb(char *initial, char *path)
618 if (rename(initial, path))
620 report_error("ROX-Filer: rename()", g_strerror(errno));
621 return FALSE;
623 return TRUE;
626 static void rename_item(gpointer data, guint action, GtkWidget *widget)
628 Collection *collection;
630 g_return_if_fail(window_with_focus != NULL);
632 collection = window_with_focus->collection;
633 if (collection->number_selected != 1)
634 report_error("ROX-Filer", "You must select a single "
635 "item to rename");
636 else
638 DirItem *item = selected_item(collection);
640 savebox_show(window_with_focus, "Rename",
641 window_with_focus->path,
642 item->leafname,
643 item->image, rename_cb);
647 static gboolean link_cb(char *initial, char *path)
649 if (symlink(initial, path))
651 report_error("ROX-Filer: symlink()", g_strerror(errno));
652 return FALSE;
654 return TRUE;
657 static void link_item(gpointer data, guint action, GtkWidget *widget)
659 Collection *collection;
661 g_return_if_fail(window_with_focus != NULL);
663 collection = window_with_focus->collection;
664 if (collection->number_selected != 1)
665 report_error("ROX-Filer", "You must select a single "
666 "item to link");
667 else
669 DirItem *item = selected_item(collection);
671 savebox_show(window_with_focus, "Symlink",
672 window_with_focus->path,
673 item->leafname,
674 item->image, link_cb);
678 static void show_file_info(gpointer data, guint action, GtkWidget *widget)
680 GtkWidget *window, *table, *label, *button, *frame;
681 GtkWidget *file_label;
682 GString *gstring;
683 char *string;
684 int file_data[2];
685 char *path;
686 char buffer[20];
687 char *argv[] = {"file", "-b", NULL, NULL};
688 int got;
689 Collection *collection;
690 DirItem *file;
691 struct stat info;
693 g_return_if_fail(window_with_focus != NULL);
695 collection = window_with_focus->collection;
696 if (collection->number_selected != 1)
698 report_error("ROX-Filer", "You must select a single "
699 "item before using Info");
700 return;
702 file = selected_item(collection);
703 path = make_path(window_with_focus->path,
704 file->leafname)->str;
705 if (lstat(path, &info))
707 delayed_error("ROX-Filer", g_strerror(errno));
708 return;
711 gstring = g_string_new(NULL);
713 window = gtk_window_new(GTK_WINDOW_DIALOG);
714 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_MOUSE);
715 gtk_container_set_border_width(GTK_CONTAINER(window), 4);
716 gtk_window_set_title(GTK_WINDOW(window), path);
718 table = gtk_table_new(9, 2, FALSE);
719 gtk_container_add(GTK_CONTAINER(window), table);
720 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
721 gtk_table_set_col_spacings(GTK_TABLE(table), 4);
723 label = gtk_label_new("Owner, group:");
724 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
725 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_RIGHT);
726 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
727 g_string_sprintf(gstring, "%s, %s", user_name(info.st_uid),
728 group_name(info.st_gid));
729 label = gtk_label_new(gstring->str);
730 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
731 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 0, 1);
733 label = gtk_label_new("Size:");
734 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
735 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
736 if (info.st_size >=2048)
738 g_string_sprintf(gstring, "%s (%ld bytes)",
739 format_size((unsigned long) info.st_size),
740 (unsigned long) info.st_size);
742 else
744 g_string_sprintf(gstring, "%ld bytes",
745 (unsigned long) info.st_size);
747 label = gtk_label_new(gstring->str);
748 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
749 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 1, 2);
751 label = gtk_label_new("Change time:");
752 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
753 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3);
754 g_string_sprintf(gstring, "%s", ctime(&info.st_ctime));
755 g_string_truncate(gstring, gstring->len - 1);
756 label = gtk_label_new(gstring->str);
757 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
758 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 2, 3);
760 label = gtk_label_new("Modify time:");
761 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
762 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4);
763 g_string_sprintf(gstring, "%s", ctime(&info.st_mtime));
764 g_string_truncate(gstring, gstring->len - 1);
765 label = gtk_label_new(gstring->str);
766 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
767 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 3, 4);
769 label = gtk_label_new("Access time:");
770 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
771 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 4, 5);
772 g_string_sprintf(gstring, "%s", ctime(&info.st_atime));
773 g_string_truncate(gstring, gstring->len - 1);
774 label = gtk_label_new(gstring->str);
775 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
776 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 4, 5);
778 label = gtk_label_new("Permissions:");
779 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
780 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 5, 6);
781 g_string_sprintf(gstring, "%o", (unsigned int) info.st_mode);
782 label = gtk_label_new(gstring->str);
783 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
784 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 5, 6);
786 label = gtk_label_new("MIME type:");
787 gtk_misc_set_alignment(GTK_MISC(label), 1, .5);
788 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 6, 7);
789 if (file->mime_type)
791 string = g_strconcat(file->mime_type->media_type, "/",
792 file->mime_type->subtype, NULL);
793 label = gtk_label_new(string);
794 g_free(string);
796 else
797 label = gtk_label_new("-");
798 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
799 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 6, 7);
801 frame = gtk_frame_new("file(1) says...");
802 gtk_table_attach_defaults(GTK_TABLE(table), frame, 0, 2, 7, 8);
803 file_label = gtk_label_new("<nothing yet>");
804 gtk_misc_set_padding(GTK_MISC(file_label), 4, 4);
805 gtk_label_set_line_wrap(GTK_LABEL(file_label), TRUE);
806 gtk_container_add(GTK_CONTAINER(frame), file_label);
808 button = gtk_button_new_with_label("OK");
809 gtk_table_attach(GTK_TABLE(table), button, 0, 2, 8, 9,
810 GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 40, 4);
811 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
812 gtk_widget_destroy, GTK_OBJECT(window));
814 gtk_widget_show_all(window);
815 gdk_flush();
817 if (pipe(file_data))
819 g_string_sprintf(gstring, "pipe(): %s", g_strerror(errno));
820 goto out;
822 switch (fork())
824 case -1:
825 close(file_data[0]);
826 close(file_data[1]);
827 g_string_sprintf(gstring, "fork(): %s",
828 g_strerror(errno));
829 goto out;
830 case 0:
831 close(file_data[0]);
832 dup2(file_data[1], STDOUT_FILENO);
833 dup2(file_data[1], STDERR_FILENO);
834 #ifdef FILE_B_FLAG
835 argv[2] = path;
836 #else
837 argv[1] = path;
838 #endif
839 if (execvp(argv[0], argv))
840 fprintf(stderr, "execvp() error: %s\n",
841 g_strerror(errno));
842 _exit(0);
844 /* We are the parent... */
845 close(file_data[1]);
846 g_string_truncate(gstring, 0);
847 while (gtk_events_pending())
848 g_main_iteration(FALSE);
849 while ((got = read(file_data[0], buffer, sizeof(buffer) - 1)) > 0)
851 buffer[got] = '\0';
852 g_string_append(gstring, buffer);
854 close(file_data[0]);
855 out:
856 gtk_label_set_text(GTK_LABEL(file_label), gstring->str);
857 g_string_free(gstring, TRUE);
860 static void help(gpointer data, guint action, GtkWidget *widget)
862 Collection *collection;
863 DirItem *item;
865 g_return_if_fail(window_with_focus != NULL);
867 collection = window_with_focus->collection;
868 if (collection->number_selected != 1)
870 report_error("ROX-Filer", "You must select a single "
871 "item to get help on");
872 return;
874 item = selected_item(collection);
875 switch (item->base_type)
877 case TYPE_FILE:
878 if (item->flags & ITEM_FLAG_EXEC_FILE)
879 report_error("Executable file",
880 "This is a file with an eXecute bit "
881 "set - it can be run as a program.");
882 else
883 report_error("File",
884 "This is a data file. Try using the "
885 "Info menu item to find out more...");
886 break;
887 case TYPE_DIRECTORY:
888 if (item->flags & ITEM_FLAG_APPDIR)
889 app_show_help(
890 make_path(window_with_focus->path,
891 item->leafname)->str);
892 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
893 report_error("Mount point",
894 "A mount point is a directory which another "
895 "filing system can be mounted on. Everything "
896 "on the mounted filesystem then appears to be "
897 "inside the directory.");
898 else
899 report_error("Directory",
900 "This is a directory. It contains an index to "
901 "other items - open it to see the list.");
902 break;
903 case TYPE_CHAR_DEVICE:
904 case TYPE_BLOCK_DEVICE:
905 report_error("Device file",
906 "Device files allow you to read from or write "
907 "to a device driver as though it was an "
908 "ordinary file.");
909 break;
910 case TYPE_PIPE:
911 report_error("Named pipe",
912 "Pipes allow different programs to "
913 "communicate. One program writes data to the "
914 "pipe while another one reads it out again.");
915 break;
916 case TYPE_SOCKET:
917 report_error("Socket",
918 "Sockets allow processes to communicate.");
919 break;
920 default:
921 report_error("Unknown type",
922 "I couldn't find out what kind of file this "
923 "is. Maybe it doesn't exist anymore or you "
924 "don't have search permission on the directory "
925 "it's in?");
926 break;
930 static void select_all(gpointer data, guint action, GtkWidget *widget)
932 g_return_if_fail(window_with_focus != NULL);
934 collection_select_all(window_with_focus->collection);
935 window_with_focus->temp_item_selected = FALSE;
938 static void clear_selection(gpointer data, guint action, GtkWidget *widget)
940 g_return_if_fail(window_with_focus != NULL);
942 collection_clear_selection(window_with_focus->collection);
943 window_with_focus->temp_item_selected = FALSE;
946 static void show_options(gpointer data, guint action, GtkWidget *widget)
948 g_return_if_fail(window_with_focus != NULL);
950 options_show(window_with_focus);
953 static gboolean new_directory_cb(char *initial, char *path)
955 if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO))
957 report_error("mkdir", g_strerror(errno));
958 return FALSE;
960 return TRUE;
963 static void new_directory(gpointer data, guint action, GtkWidget *widget)
965 g_return_if_fail(window_with_focus != NULL);
967 savebox_show(window_with_focus, "Create directory",
968 window_with_focus->path, "NewDir",
969 default_pixmap + TYPE_DIRECTORY, new_directory_cb);
972 static void xterm_here(gpointer data, guint action, GtkWidget *widget)
974 char *argv[] = {NULL, NULL};
976 argv[0] = xterm_here_value;
978 g_return_if_fail(window_with_focus != NULL);
980 if (!spawn_full(argv, window_with_focus->path, 0))
981 report_error("ROX-Filer", "Failed to fork() child "
982 "process");
985 static void open_parent(gpointer data, guint action, GtkWidget *widget)
987 g_return_if_fail(window_with_focus != NULL);
989 filer_opendir(make_path(window_with_focus->path, "/..")->str,
990 FALSE, BOTTOM);
993 static void open_parent_same(gpointer data, guint action, GtkWidget *widget)
995 g_return_if_fail(window_with_focus != NULL);
997 change_to_parent(window_with_focus);
1000 static void new_window(gpointer data, guint action, GtkWidget *widget)
1002 g_return_if_fail(window_with_focus != NULL);
1004 filer_opendir(window_with_focus->path, FALSE, BOTTOM);
1007 static void close_window(gpointer data, guint action, GtkWidget *widget)
1009 g_return_if_fail(window_with_focus != NULL);
1011 gtk_widget_destroy(window_with_focus->window);
1014 static void open_as_dir(gpointer data, guint action, GtkWidget *widget)
1016 g_return_if_fail(window_with_focus != NULL);
1018 filer_opendir(window_with_focus->path, FALSE, BOTTOM);
1021 static void close_panel(gpointer data, guint action, GtkWidget *widget)
1023 g_return_if_fail(window_with_focus != NULL);
1025 gtk_widget_destroy(window_with_focus->window);