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