4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2000, 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>
30 #include <sys/param.h>
44 #include "gui_support.h"
49 #include "minibuffer.h"
51 #define C_ "<control>"
53 #define MENU_MARGIN 32
55 GtkAccelGroup
*filer_keys
;
56 GtkAccelGroup
*panel_keys
;
58 static GtkWidget
*popup_menu
= NULL
; /* Currently open menu */
60 static gint updating_menu
= 0; /* Non-zero => ignore activations */
63 static GtkWidget
*xterm_here_entry
;
64 static char *xterm_here_value
;
66 /* Static prototypes */
68 static void position_menu(GtkMenu
*menu
, gint
*x
, gint
*y
, gpointer data
);
69 static void menu_closed(GtkWidget
*widget
);
70 static void items_sensitive(gboolean state
);
71 static char *load_xterm_here(char *data
);
73 /* Note that for these callbacks none of the arguments are used. */
74 static void not_yet(gpointer data
, guint action
, GtkWidget
*widget
);
76 static void large(gpointer data
, guint action
, GtkWidget
*widget
);
77 static void small(gpointer data
, guint action
, GtkWidget
*widget
);
78 static void full_info(gpointer data
, guint action
, GtkWidget
*widget
);
80 static void sort_name(gpointer data
, guint action
, GtkWidget
*widget
);
81 static void sort_type(gpointer data
, guint action
, GtkWidget
*widget
);
82 static void sort_size(gpointer data
, guint action
, GtkWidget
*widget
);
83 static void sort_date(gpointer data
, guint action
, GtkWidget
*widget
);
85 static void hidden(gpointer data
, guint action
, GtkWidget
*widget
);
86 static void refresh(gpointer data
, guint action
, GtkWidget
*widget
);
88 static void copy_item(gpointer data
, guint action
, GtkWidget
*widget
);
89 static void rename_item(gpointer data
, guint action
, GtkWidget
*widget
);
90 static void link_item(gpointer data
, guint action
, GtkWidget
*widget
);
91 static void open_file(gpointer data
, guint action
, GtkWidget
*widget
);
92 static void help(gpointer data
, guint action
, GtkWidget
*widget
);
93 static void show_file_info(gpointer data
, guint action
, GtkWidget
*widget
);
94 static void mount(gpointer data
, guint action
, GtkWidget
*widget
);
95 static void delete(gpointer data
, guint action
, GtkWidget
*widget
);
96 static void remove_link(gpointer data
, guint action
, GtkWidget
*widget
);
97 static void usage(gpointer data
, guint action
, GtkWidget
*widget
);
98 static void chmod_items(gpointer data
, guint action
, GtkWidget
*widget
);
100 static void open_vfs_rpm(gpointer data
, guint action
, GtkWidget
*widget
);
101 static void open_vfs_utar(gpointer data
, guint action
, GtkWidget
*widget
);
102 static void open_vfs_uzip(gpointer data
, guint action
, GtkWidget
*widget
);
104 static void select_all(gpointer data
, guint action
, GtkWidget
*widget
);
105 static void clear_selection(gpointer data
, guint action
, GtkWidget
*widget
);
106 static void show_options(gpointer data
, guint action
, GtkWidget
*widget
);
107 static void new_directory(gpointer data
, guint action
, GtkWidget
*widget
);
108 static void xterm_here(gpointer data
, guint action
, GtkWidget
*widget
);
110 static void open_parent_same(gpointer data
, guint action
, GtkWidget
*widget
);
111 static void open_parent(gpointer data
, guint action
, GtkWidget
*widget
);
112 static void new_window(gpointer data
, guint action
, GtkWidget
*widget
);
113 static void close_window(gpointer data
, guint action
, GtkWidget
*widget
);
114 static void enter_path(gpointer data
, guint action
, GtkWidget
*widget
);
115 static void rox_help(gpointer data
, guint action
, GtkWidget
*widget
);
117 static void open_as_dir(gpointer data
, guint action
, GtkWidget
*widget
);
118 static void close_panel(gpointer data
, guint action
, GtkWidget
*widget
);
120 static GtkWidget
*create_options();
121 static void update_options();
122 static void set_options();
123 static void save_options();
125 static OptionsSection options
=
135 static GtkWidget
*filer_menu
; /* The popup filer menu */
136 static GtkWidget
*filer_file_item
; /* The File '' label */
137 static GtkWidget
*filer_file_menu
; /* The File '' menu */
138 static GtkWidget
*filer_vfs_menu
; /* The Open VFS menu */
139 static GtkWidget
*filer_hidden_menu
; /* The Show Hidden item */
140 static GtkWidget
*filer_new_window
; /* The New Window item */
141 static GtkWidget
*panel_menu
; /* The popup panel menu */
142 static GtkWidget
*panel_hidden_menu
; /* The Show Hidden item */
144 static gint screen_width
, screen_height
;
146 static GtkItemFactoryEntry filer_menu_def
[] = {
147 {"/Display", NULL
, NULL
, 0, "<Branch>"},
148 {"/Display/Large Icons", NULL
, large
, 0, NULL
},
149 {"/Display/Small Icons", NULL
, small
, 0, NULL
},
150 {"/Display/Full Info", NULL
, full_info
, 0, NULL
},
151 {"/Display/Separator", NULL
, NULL
, 0, "<Separator>"},
152 {"/Display/Sort by Name", NULL
, sort_name
, 0, NULL
},
153 {"/Display/Sort by Type", NULL
, sort_type
, 0, NULL
},
154 {"/Display/Sort by Date", NULL
, sort_date
, 0, NULL
},
155 {"/Display/Sort by Size", NULL
, sort_size
, 0, NULL
},
156 {"/Display/Separator", NULL
, NULL
, 0, "<Separator>"},
157 {"/Display/Show Hidden", C_
"H", hidden
, 0, "<ToggleItem>"},
158 {"/Display/Refresh", C_
"L", refresh
, 0, NULL
},
159 {"/File", NULL
, NULL
, 0, "<Branch>"},
160 {"/File/Copy...", NULL
, copy_item
, 0, NULL
},
161 {"/File/Rename...", NULL
, rename_item
, 0, NULL
},
162 {"/File/Link...", NULL
, link_item
, 0, NULL
},
163 {"/File/Shift Open", NULL
, open_file
, 0, NULL
},
164 {"/File/Help", "F1", help
, 0, NULL
},
165 {"/File/Info", "I", show_file_info
, 0, NULL
},
166 {"/File/Open VFS", NULL
, NULL
, 0, "<Branch>"},
167 {"/File/Open VFS/Unzip", NULL
, open_vfs_uzip
, 0, NULL
},
168 {"/File/Open VFS/Untar", NULL
, open_vfs_utar
, 0, NULL
},
169 {"/File/Open VFS/RPM", NULL
, open_vfs_rpm
, 0, NULL
},
170 {"/File/Separator", NULL
, NULL
, 0, "<Separator>"},
171 {"/File/Mount", "M", mount
, 0, NULL
},
172 {"/File/Delete", C_
"X", delete, 0, NULL
},
173 {"/File/Disk Usage", "U", usage
, 0, NULL
},
174 {"/File/Permissions", NULL
, chmod_items
, 0, NULL
},
175 {"/File/Touch", NULL
, not_yet
, 0, NULL
},
176 {"/File/Find", NULL
, not_yet
, 0, NULL
},
177 {"/Select All", C_
"A", select_all
, 0, NULL
},
178 {"/Clear Selection", C_
"Z", clear_selection
, 0, NULL
},
179 {"/Options...", NULL
, show_options
, 0, NULL
},
180 {"/New Directory...", NULL
, new_directory
, 0, NULL
},
181 {"/Xterm Here", NULL
, xterm_here
, 0, NULL
},
182 {"/Window", NULL
, NULL
, 0, "<Branch>"},
183 {"/Window/Parent, New Window", NULL
, open_parent
, 0, NULL
},
184 {"/Window/Parent, Same Window", NULL
, open_parent_same
, 0, NULL
},
185 {"/Window/New Window", NULL
, new_window
, 0, NULL
},
186 {"/Window/Close Window", C_
"Q", close_window
, 0, NULL
},
187 {"/Window/Enter Path", NULL
, enter_path
, 0, NULL
},
188 {"/Window/Separator", NULL
, NULL
, 0, "<Separator>"},
189 {"/Window/Show ROX-Filer help", NULL
, rox_help
, 0, NULL
},
192 static GtkItemFactoryEntry panel_menu_def
[] = {
193 {"/Display", NULL
, NULL
, 0, "<Branch>"},
194 {"/Display/Sort by Name", NULL
, sort_name
, 0, NULL
},
195 {"/Display/Sort by Type", NULL
, sort_type
, 0, NULL
},
196 {"/Display/Sort by Date", NULL
, sort_date
, 0, NULL
},
197 {"/Display/Sort by Size", NULL
, sort_size
, 0, NULL
},
198 {"/Display/Separator", NULL
, NULL
, 0, "<Separator>"},
199 {"/Display/Show Hidden", NULL
, hidden
, 0, "<ToggleItem>"},
200 {"/Display/Refresh", NULL
, refresh
, 0, NULL
},
201 {"/Open Panel as Directory", NULL
, open_as_dir
, 0, NULL
},
202 {"/Close Panel", NULL
, close_panel
, 0, NULL
},
203 {"/Separator", NULL
, NULL
, 0, "<Separator>"},
204 {"/ROX-Filer Help", NULL
, rox_help
, 0, NULL
},
205 {"/ROX-Filer Options...", NULL
, show_options
, 0, NULL
},
206 {"/Separator", NULL
, NULL
, 0, "<Separator>"},
207 {"/Show Help", NULL
, help
, 0, NULL
},
208 {"/Remove Item", NULL
, remove_link
, 0, NULL
},
211 typedef struct _FileStatus FileStatus
;
213 /* This is for the 'file(1) says...' thing */
216 int fd
; /* FD to read from, -1 if closed */
217 int input
; /* Input watcher tag if fd valid */
218 GtkLabel
*label
; /* Widget to output to */
219 gboolean start
; /* No output yet */
224 GtkItemFactory
*item_factory
;
228 /* This call starts returning strange values after a while, so get
229 * the result here during init.
231 gdk_window_get_size(GDK_ROOT_PARENT(), &screen_width
, &screen_height
);
233 filer_keys
= gtk_accel_group_new();
234 item_factory
= gtk_item_factory_new(GTK_TYPE_MENU
,
237 gtk_item_factory_create_items(item_factory
,
238 sizeof(filer_menu_def
) / sizeof(*filer_menu_def
),
241 filer_menu
= gtk_item_factory_get_widget(item_factory
, "<filer>");
242 filer_file_menu
= gtk_item_factory_get_widget(item_factory
,
244 filer_vfs_menu
= gtk_item_factory_get_widget(item_factory
,
245 "<filer>/File/Open VFS");
246 filer_hidden_menu
= gtk_item_factory_get_widget(item_factory
,
247 "<filer>/Display/Show Hidden");
248 items
= gtk_container_children(GTK_CONTAINER(filer_menu
));
249 filer_file_item
= GTK_BIN(g_list_nth(items
, 1)->data
)->child
;
251 filer_new_window
= GTK_BIN(gtk_item_factory_get_widget(item_factory
,
252 "<filer>/Window/New Window"))->child
;
254 panel_keys
= gtk_accel_group_new();
255 item_factory
= gtk_item_factory_new(GTK_TYPE_MENU
,
258 gtk_item_factory_create_items(item_factory
,
259 sizeof(panel_menu_def
) / sizeof(*panel_menu_def
),
262 panel_menu
= gtk_item_factory_get_widget(item_factory
, "<panel>");
263 panel_hidden_menu
= gtk_item_factory_get_widget(item_factory
,
264 "<panel>/Display/Show Hidden");
266 menurc
= choices_find_path_load("menus");
268 gtk_item_factory_parse_rc(menurc
);
270 gtk_accel_group_lock(panel_keys
);
272 gtk_signal_connect(GTK_OBJECT(filer_menu
), "unmap_event",
273 GTK_SIGNAL_FUNC(menu_closed
), NULL
);
274 gtk_signal_connect(GTK_OBJECT(panel_menu
), "unmap_event",
275 GTK_SIGNAL_FUNC(menu_closed
), NULL
);
276 gtk_signal_connect(GTK_OBJECT(filer_file_menu
), "unmap_event",
277 GTK_SIGNAL_FUNC(menu_closed
), NULL
);
279 options_sections
= g_slist_prepend(options_sections
, &options
);
280 xterm_here_value
= g_strdup("xterm");
281 option_register("xterm_here", load_xterm_here
);
284 /* Build up some option widgets to go in the options dialog, but don't
287 static GtkWidget
*create_options()
289 GtkWidget
*table
, *label
;
291 table
= gtk_table_new(2, 2, FALSE
);
292 gtk_container_set_border_width(GTK_CONTAINER(table
), 4);
294 label
= gtk_label_new("To set the keyboard short-cuts you simply open "
295 "the menu over a filer window, move the pointer over "
296 "the item you want to use and press a key. The key "
297 "will appear next to the menu item and you can just "
298 "press that key without opening the menu in future. "
299 "To save the current menu short-cuts for next time, "
300 "click the Save button at the bottom of this window.");
301 gtk_label_set_line_wrap(GTK_LABEL(label
), TRUE
);
302 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 0, 2, 0, 1);
304 label
= gtk_label_new("'Xterm here' program:");
305 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 0, 1, 1, 2);
306 xterm_here_entry
= gtk_entry_new();
307 gtk_table_attach_defaults(GTK_TABLE(table
), xterm_here_entry
,
313 static char *load_xterm_here(char *data
)
315 g_free(xterm_here_value
);
316 xterm_here_value
= g_strdup(data
);
320 static void update_options()
322 gtk_entry_set_text(GTK_ENTRY(xterm_here_entry
), xterm_here_value
);
325 static void set_options()
327 g_free(xterm_here_value
);
328 xterm_here_value
= g_strdup(gtk_entry_get_text(
329 GTK_ENTRY(xterm_here_entry
)));
332 static void save_options()
336 menurc
= choices_find_path_save("menus", TRUE
);
338 gtk_item_factory_dump_rc(menurc
, NULL
, TRUE
);
340 option_write("xterm_here", xterm_here_value
);
344 static void items_sensitive(gboolean state
)
349 items
= item
= gtk_container_children(GTK_CONTAINER(filer_file_menu
));
352 gtk_widget_set_sensitive(GTK_BIN(item
->data
)->child
, state
);
357 items
= item
= gtk_container_children(GTK_CONTAINER(filer_vfs_menu
));
360 gtk_widget_set_sensitive(GTK_BIN(item
->data
)->child
, state
);
366 static void position_menu(GtkMenu
*menu
, gint
*x
, gint
*y
, gpointer data
)
368 int *pos
= (int *) data
;
369 GtkRequisition requisition
;
371 gtk_widget_size_request(GTK_WIDGET(menu
), &requisition
);
374 *x
= screen_width
- MENU_MARGIN
- requisition
.width
;
375 else if (pos
[0] == -2)
378 *x
= pos
[0] - (requisition
.width
>> 2);
381 *y
= screen_height
- MENU_MARGIN
- requisition
.height
;
382 else if (pos
[1] == -2)
385 *y
= pos
[1] - (requisition
.height
>> 2);
387 *x
= CLAMP(*x
, 0, screen_width
- requisition
.width
);
388 *y
= CLAMP(*y
, 0, screen_height
- requisition
.height
);
391 void show_filer_menu(FilerWindow
*filer_window
, GdkEventButton
*event
,
399 pos
[0] = event
->x_root
;
400 pos
[1] = event
->y_root
;
402 window_with_focus
= filer_window
;
404 switch (filer_window
->panel_type
)
416 if (filer_window
->panel_type
)
417 collection_clear_selection(filer_window
->collection
); /* ??? */
419 if (filer_window
->collection
->number_selected
== 0 && item
>= 0)
421 collection_select_item(filer_window
->collection
, item
);
422 filer_window
->temp_item_selected
= TRUE
;
425 filer_window
->temp_item_selected
= FALSE
;
427 if (filer_window
->panel_type
)
429 gtk_check_menu_item_set_active(
430 GTK_CHECK_MENU_ITEM(panel_hidden_menu
),
431 filer_window
->show_hidden
);
435 GtkWidget
*file_label
, *file_menu
;
436 Collection
*collection
= filer_window
->collection
;
439 file_label
= filer_file_item
;
440 file_menu
= filer_file_menu
;
441 gtk_check_menu_item_set_active(
442 GTK_CHECK_MENU_ITEM(filer_hidden_menu
),
443 filer_window
->show_hidden
);
444 buffer
= g_string_new(NULL
);
445 switch (collection
->number_selected
)
448 g_string_assign(buffer
, "Next Click");
449 items_sensitive(TRUE
);
452 items_sensitive(TRUE
);
453 file_item
= selected_item(
454 filer_window
->collection
);
455 g_string_sprintf(buffer
, "%s '%s'",
456 basetype_name(file_item
),
457 file_item
->leafname
);
460 items_sensitive(FALSE
);
461 g_string_sprintf(buffer
, "%d items",
462 collection
->number_selected
);
465 gtk_label_set_text(GTK_LABEL(file_label
), buffer
->str
);
466 g_string_free(buffer
, TRUE
);
470 gtk_widget_set_sensitive(filer_new_window
, !o_unique_filer_windows
);
472 if (filer_window
->panel_type
)
473 popup_menu
= panel_menu
;
475 popup_menu
= (event
->state
& GDK_CONTROL_MASK
)
481 gtk_menu_popup(GTK_MENU(popup_menu
), NULL
, NULL
, position_menu
,
482 (gpointer
) pos
, event
->button
, event
->time
);
485 static void menu_closed(GtkWidget
*widget
)
487 if (window_with_focus
== NULL
|| widget
!= popup_menu
)
488 return; /* Close panel item chosen? */
490 if (window_with_focus
->temp_item_selected
)
492 collection_clear_selection(window_with_focus
->collection
);
493 window_with_focus
->temp_item_selected
= FALSE
;
497 void target_callback(Collection
*collection
, gint item
, gpointer real_fn
)
499 g_return_if_fail(window_with_focus
!= NULL
);
500 g_return_if_fail(window_with_focus
->collection
== collection
);
501 g_return_if_fail(real_fn
!= NULL
);
503 collection_wink_item(collection
, item
);
504 collection_clear_selection(collection
);
505 collection_select_item(collection
, item
);
506 ((CollectionTargetFunc
)real_fn
)(NULL
, 0, collection
);
507 if (item
< collection
->number_of_items
)
508 collection_unselect_item(collection
, item
);
513 /* Fake action to warn when a menu item does nothing */
514 static void not_yet(gpointer data
, guint action
, GtkWidget
*widget
)
516 delayed_error("ROX-Filer", "Sorry, that feature isn't implemented yet");
519 static void large(gpointer data
, guint action
, GtkWidget
*widget
)
521 g_return_if_fail(window_with_focus
!= NULL
);
523 filer_style_set(window_with_focus
, LARGE_ICONS
);
526 static void small(gpointer data
, guint action
, GtkWidget
*widget
)
528 g_return_if_fail(window_with_focus
!= NULL
);
530 filer_style_set(window_with_focus
, SMALL_ICONS
);
533 static void full_info(gpointer data
, guint action
, GtkWidget
*widget
)
535 g_return_if_fail(window_with_focus
!= NULL
);
537 filer_style_set(window_with_focus
, FULL_INFO
);
540 static void sort_name(gpointer data
, guint action
, GtkWidget
*widget
)
542 g_return_if_fail(window_with_focus
!= NULL
);
544 filer_set_sort_fn(window_with_focus
, sort_by_name
);
547 static void sort_type(gpointer data
, guint action
, GtkWidget
*widget
)
549 g_return_if_fail(window_with_focus
!= NULL
);
551 filer_set_sort_fn(window_with_focus
, sort_by_type
);
554 static void sort_date(gpointer data
, guint action
, GtkWidget
*widget
)
556 g_return_if_fail(window_with_focus
!= NULL
);
558 filer_set_sort_fn(window_with_focus
, sort_by_date
);
561 static void sort_size(gpointer data
, guint action
, GtkWidget
*widget
)
563 g_return_if_fail(window_with_focus
!= NULL
);
565 filer_set_sort_fn(window_with_focus
, sort_by_size
);
568 static void hidden(gpointer data
, guint action
, GtkWidget
*widget
)
573 g_return_if_fail(window_with_focus
!= NULL
);
575 filer_set_hidden(window_with_focus
, !window_with_focus
->show_hidden
);
578 static void refresh(gpointer data
, guint action
, GtkWidget
*widget
)
580 g_return_if_fail(window_with_focus
!= NULL
);
583 update_dir(window_with_focus
, TRUE
);
586 static void mount(gpointer data
, guint action
, GtkWidget
*widget
)
588 g_return_if_fail(window_with_focus
!= NULL
);
590 if (window_with_focus
->collection
->number_selected
== 0)
591 collection_target(window_with_focus
->collection
,
592 target_callback
, mount
);
594 action_mount(window_with_focus
, NULL
);
597 static void delete(gpointer data
, guint action
, GtkWidget
*widget
)
599 g_return_if_fail(window_with_focus
!= NULL
);
601 if (window_with_focus
->collection
->number_selected
== 0)
602 collection_target(window_with_focus
->collection
,
603 target_callback
, delete);
605 action_delete(window_with_focus
);
608 static void remove_link(gpointer data
, guint action
, GtkWidget
*widget
)
610 g_return_if_fail(window_with_focus
!= NULL
);
612 if (window_with_focus
->collection
->number_selected
> 1)
614 report_error("ROX-Filer", "You can only remove one link "
618 else if (window_with_focus
->collection
->number_selected
== 0)
619 collection_target(window_with_focus
->collection
,
620 target_callback
, remove_link
);
627 item
= selected_item(window_with_focus
->collection
);
629 path
= make_path(window_with_focus
->path
, item
->leafname
)->str
;
630 if (lstat(path
, &info
))
631 report_error("ROX-Filer", g_strerror(errno
));
632 else if (!S_ISLNK(info
.st_mode
))
633 report_error("ROX-Filer",
634 "You can only remove symbolic links this way - "
635 "try the 'Open Panel as Directory' item.");
636 else if (unlink(path
))
637 report_error("ROX-Filer", g_strerror(errno
));
639 update_dir(window_with_focus
, TRUE
);
643 static void usage(gpointer data
, guint action
, GtkWidget
*widget
)
645 g_return_if_fail(window_with_focus
!= NULL
);
647 if (window_with_focus
->collection
->number_selected
== 0)
648 collection_target(window_with_focus
->collection
,
649 target_callback
, usage
);
651 action_usage(window_with_focus
);
654 static void chmod_items(gpointer data
, guint action
, GtkWidget
*widget
)
656 g_return_if_fail(window_with_focus
!= NULL
);
658 if (window_with_focus
->collection
->number_selected
== 0)
659 collection_target(window_with_focus
->collection
,
660 target_callback
, chmod_items
);
662 action_chmod(window_with_focus
);
665 static gboolean
copy_cb(char *initial
, char *path
)
667 char *new_dir
, *leaf
;
672 report_error("ROX-Filer", "New pathname is not absolute");
676 if (path
[strlen(path
) - 1] == '/')
678 new_dir
= g_strdup(path
);
685 slash
= strrchr(path
, '/');
686 new_dir
= g_strndup(path
, slash
- path
);
690 local_paths
= g_slist_append(NULL
, initial
);
691 action_copy(local_paths
, new_dir
, leaf
);
692 g_slist_free(local_paths
);
699 static void copy_item(gpointer data
, guint action
, GtkWidget
*widget
)
701 Collection
*collection
;
703 g_return_if_fail(window_with_focus
!= NULL
);
705 collection
= window_with_focus
->collection
;
706 if (collection
->number_selected
> 1)
708 report_error("ROX-Filer", "You cannot do this to more than "
709 "one item at a time");
712 else if (collection
->number_selected
!= 1)
713 collection_target(collection
, target_callback
, copy_item
);
716 DirItem
*item
= selected_item(collection
);
718 savebox_show(window_with_focus
, "Copy",
719 window_with_focus
->path
,
721 item
->image
, copy_cb
);
725 static gboolean
rename_cb(char *initial
, char *path
)
727 if (rename(initial
, path
))
729 report_error("ROX-Filer: rename()", g_strerror(errno
));
735 static void rename_item(gpointer data
, guint action
, GtkWidget
*widget
)
737 Collection
*collection
;
739 g_return_if_fail(window_with_focus
!= NULL
);
741 collection
= window_with_focus
->collection
;
742 if (collection
->number_selected
> 1)
744 report_error("ROX-Filer", "You cannot do this to more than "
745 "one item at a time");
748 else if (collection
->number_selected
!= 1)
749 collection_target(collection
, target_callback
, rename_item
);
752 DirItem
*item
= selected_item(collection
);
754 savebox_show(window_with_focus
, "Rename",
755 window_with_focus
->path
,
757 item
->image
, rename_cb
);
761 static gboolean
link_cb(char *initial
, char *path
)
763 if (symlink(initial
, path
))
765 report_error("ROX-Filer: symlink()", g_strerror(errno
));
771 static void link_item(gpointer data
, guint action
, GtkWidget
*widget
)
773 Collection
*collection
;
775 g_return_if_fail(window_with_focus
!= NULL
);
777 collection
= window_with_focus
->collection
;
778 if (collection
->number_selected
> 1)
780 report_error("ROX-Filer", "You cannot do this to more than "
781 "one item at a time");
784 else if (collection
->number_selected
!= 1)
785 collection_target(collection
, target_callback
, link_item
);
788 DirItem
*item
= selected_item(collection
);
790 savebox_show(window_with_focus
, "Symlink",
791 window_with_focus
->path
,
793 item
->image
, link_cb
);
797 static void open_file(gpointer data
, guint action
, GtkWidget
*widget
)
799 Collection
*collection
;
801 g_return_if_fail(window_with_focus
!= NULL
);
803 collection
= window_with_focus
->collection
;
804 if (collection
->number_selected
> 1)
806 report_error("ROX-Filer", "You cannot do this to more than "
807 "one item at a time");
810 else if (collection
->number_selected
!= 1)
811 collection_target(collection
, target_callback
, open_file
);
813 filer_openitem(window_with_focus
,
814 selected_item_number(collection
),
815 OPEN_SAME_WINDOW
| OPEN_SHIFT
);
818 /* Got some data from file(1) - stick it in the window. */
819 static void add_file_output(FileStatus
*fs
,
820 gint source
, GdkInputCondition condition
)
826 got
= read(source
, buffer
, sizeof(buffer
) - 1);
830 gtk_input_remove(fs
->input
);
834 delayed_error("ROX-Filer: file(1) says...",
846 gtk_label_get(fs
->label
, &str
);
848 str
= g_strconcat(str
, buffer
, NULL
);
849 gtk_label_set_text(fs
->label
, str
);
853 static void file_info_destroyed(GtkWidget
*widget
, FileStatus
*fs
)
857 gtk_input_remove(fs
->input
);
864 static void show_file_info(gpointer data
, guint action
, GtkWidget
*widget
)
866 GtkWidget
*window
, *table
, *label
, *button
, *frame
;
867 GtkWidget
*file_label
;
872 char *argv
[] = {"file", "-b", NULL
, NULL
};
873 Collection
*collection
;
876 FileStatus
*fs
= NULL
;
879 g_return_if_fail(window_with_focus
!= NULL
);
881 collection
= window_with_focus
->collection
;
882 if (collection
->number_selected
> 1)
884 report_error("ROX-Filer", "You cannot do this to more than "
885 "one item at a time");
888 else if (collection
->number_selected
!= 1)
890 collection_target(collection
, target_callback
, show_file_info
);
893 file
= selected_item(collection
);
894 path
= make_path(window_with_focus
->path
,
895 file
->leafname
)->str
;
896 if (lstat(path
, &info
))
898 delayed_error("ROX-Filer", g_strerror(errno
));
902 gstring
= g_string_new(NULL
);
904 window
= gtk_window_new(GTK_WINDOW_DIALOG
);
905 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_MOUSE
);
906 gtk_container_set_border_width(GTK_CONTAINER(window
), 4);
907 gtk_window_set_title(GTK_WINDOW(window
), path
);
909 table
= gtk_table_new(9, 2, FALSE
);
910 gtk_container_add(GTK_CONTAINER(window
), table
);
911 gtk_table_set_row_spacings(GTK_TABLE(table
), 8);
912 gtk_table_set_col_spacings(GTK_TABLE(table
), 4);
914 label
= gtk_label_new("Owner, group:");
915 gtk_misc_set_alignment(GTK_MISC(label
), 1, .5);
916 gtk_label_set_justify(GTK_LABEL(label
), GTK_JUSTIFY_RIGHT
);
917 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 0, 1, 0, 1);
918 g_string_sprintf(gstring
, "%s, %s", user_name(info
.st_uid
),
919 group_name(info
.st_gid
));
920 label
= gtk_label_new(gstring
->str
);
921 gtk_misc_set_alignment(GTK_MISC(label
), 0, .5);
922 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 1, 2, 0, 1);
924 label
= gtk_label_new("Size:");
925 gtk_misc_set_alignment(GTK_MISC(label
), 1, .5);
926 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 0, 1, 1, 2);
927 if (info
.st_size
>= PRETTY_SIZE_LIMIT
)
929 g_string_sprintf(gstring
, "%s (%ld bytes)",
930 format_size((unsigned long) info
.st_size
),
931 (unsigned long) info
.st_size
);
935 g_string_sprintf(gstring
, "%ld byte%s",
936 (unsigned long) info
.st_size
,
937 info
.st_size
== 1 ? "" : "s");
939 label
= gtk_label_new(gstring
->str
);
940 gtk_misc_set_alignment(GTK_MISC(label
), 0, .5);
941 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 1, 2, 1, 2);
943 label
= gtk_label_new("Change time:");
944 gtk_misc_set_alignment(GTK_MISC(label
), 1, .5);
945 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 0, 1, 2, 3);
946 label
= gtk_label_new(pretty_time(&info
.st_ctime
));
947 gtk_misc_set_alignment(GTK_MISC(label
), 0, .5);
948 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 1, 2, 2, 3);
950 label
= gtk_label_new("Modify time:");
951 gtk_misc_set_alignment(GTK_MISC(label
), 1, .5);
952 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 0, 1, 3, 4);
953 label
= gtk_label_new(pretty_time(&info
.st_mtime
));
954 gtk_misc_set_alignment(GTK_MISC(label
), 0, .5);
955 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 1, 2, 3, 4);
957 label
= gtk_label_new("Access time:");
958 gtk_misc_set_alignment(GTK_MISC(label
), 1, .5);
959 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 0, 1, 4, 5);
960 label
= gtk_label_new(pretty_time(&info
.st_atime
));
961 gtk_misc_set_alignment(GTK_MISC(label
), 0, .5);
962 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 1, 2, 4, 5);
964 label
= gtk_label_new("Permissions:");
965 gtk_misc_set_alignment(GTK_MISC(label
), 1, .5);
966 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 0, 1, 5, 6);
967 label
= gtk_label_new(pretty_permissions(info
.st_mode
));
968 perm
= applicable(info
.st_uid
, info
.st_gid
);
969 gtk_label_set_pattern(GTK_LABEL(label
),
971 perm
== 1 ? " ___ " :
973 gtk_widget_set_style(label
, fixed_style
);
974 gtk_misc_set_alignment(GTK_MISC(label
), 0, .5);
975 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 1, 2, 5, 6);
977 label
= gtk_label_new("Type:");
978 gtk_misc_set_alignment(GTK_MISC(label
), 1, .5);
979 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 0, 1, 6, 7);
982 string
= g_strconcat(file
->mime_type
->media_type
, "/",
983 file
->mime_type
->subtype
, NULL
);
985 else if (file
->flags
& ITEM_FLAG_APPDIR
)
986 string
= g_strdup("ROX application");
987 else if (file
->flags
& ITEM_FLAG_SYMLINK
)
989 char p
[MAXPATHLEN
+ 1];
991 got
= readlink(path
, p
, MAXPATHLEN
);
992 if (got
> 0 && got
<= MAXPATHLEN
)
995 string
= g_strconcat("Symbolic link to ",
999 string
= g_strdup("Symbolic link");
1001 else if (file
->flags
& ITEM_FLAG_MOUNT_POINT
)
1004 if ((file
->flags
& ITEM_FLAG_MOUNTED
) &&
1005 (mp
= g_hash_table_lookup(mtab_mounts
, path
)))
1006 string
= g_strconcat("Mount point for ",
1009 string
= g_strdup("Mount point");
1012 string
= g_strdup("-");
1013 label
= gtk_label_new(string
);
1015 gtk_misc_set_alignment(GTK_MISC(label
), 0, .5);
1016 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 1, 2, 6, 7);
1018 frame
= gtk_frame_new("file(1) says...");
1019 gtk_table_attach_defaults(GTK_TABLE(table
), frame
, 0, 2, 7, 8);
1020 file_label
= gtk_label_new("<nothing yet>");
1021 gtk_misc_set_padding(GTK_MISC(file_label
), 4, 4);
1022 gtk_label_set_line_wrap(GTK_LABEL(file_label
), TRUE
);
1023 gtk_container_add(GTK_CONTAINER(frame
), file_label
);
1025 button
= gtk_button_new_with_label("OK");
1026 gtk_window_set_focus(GTK_WINDOW(window
), button
);
1027 gtk_table_attach(GTK_TABLE(table
), button
, 0, 2, 9, 10,
1028 GTK_EXPAND
| GTK_FILL
| GTK_SHRINK
, 0, 40, 4);
1029 gtk_signal_connect_object(GTK_OBJECT(button
), "clicked",
1030 gtk_widget_destroy
, GTK_OBJECT(window
));
1032 gtk_widget_show_all(window
);
1035 if (pipe(file_data
))
1037 g_string_sprintf(gstring
, "pipe(): %s", g_strerror(errno
));
1038 g_string_free(gstring
, TRUE
);
1044 g_string_sprintf(gstring
, "fork(): %s",
1046 gtk_label_set_text(GTK_LABEL(file_label
), gstring
->str
);
1047 g_string_free(gstring
, TRUE
);
1048 close(file_data
[0]);
1049 close(file_data
[1]);
1052 /* We are the child */
1053 close(file_data
[0]);
1054 dup2(file_data
[1], STDOUT_FILENO
);
1055 dup2(file_data
[1], STDERR_FILENO
);
1059 argv
[1] = file
->leafname
;
1060 chdir(window_with_focus
->path
);
1062 if (execvp(argv
[0], argv
))
1063 fprintf(stderr
, "execvp() error: %s\n",
1067 /* We are the parent */
1068 close(file_data
[1]);
1069 fs
= g_new(FileStatus
, 1);
1070 fs
->label
= GTK_LABEL(file_label
);
1071 fs
->fd
= file_data
[0];
1073 fs
->input
= gdk_input_add(fs
->fd
, GDK_INPUT_READ
,
1074 (GdkInputFunction
) add_file_output
, fs
);
1075 gtk_signal_connect(GTK_OBJECT(window
), "destroy",
1076 GTK_SIGNAL_FUNC(file_info_destroyed
), fs
);
1077 g_string_free(gstring
, TRUE
);
1082 static void app_show_help(char *path
)
1087 help_dir
= g_strconcat(path
, "/Help", NULL
);
1089 if (mc_stat(help_dir
, &info
))
1090 delayed_error("Application",
1091 "This is an application directory - you can "
1092 "run it as a program, or open it (hold down "
1093 "Shift while you open it). Most applications provide "
1094 "their own help here, but this one doesn't.");
1096 filer_opendir(help_dir
, PANEL_NO
);
1099 static void help(gpointer data
, guint action
, GtkWidget
*widget
)
1101 Collection
*collection
;
1104 g_return_if_fail(window_with_focus
!= NULL
);
1106 collection
= window_with_focus
->collection
;
1107 if (collection
->number_selected
> 1)
1109 report_error("ROX-Filer", "You cannot do this to more than "
1110 "one item at a time");
1113 else if (collection
->number_selected
!= 1)
1115 collection_target(collection
, target_callback
, help
);
1118 item
= selected_item(collection
);
1119 switch (item
->base_type
)
1122 if (item
->flags
& ITEM_FLAG_EXEC_FILE
)
1123 delayed_error("Executable file",
1124 "This is a file with an eXecute bit "
1125 "set - it can be run as a program.");
1127 delayed_error("File",
1128 "This is a data file. Try using the "
1129 "Info menu item to find out more...");
1131 case TYPE_DIRECTORY
:
1132 if (item
->flags
& ITEM_FLAG_APPDIR
)
1134 make_path(window_with_focus
->path
,
1135 item
->leafname
)->str
);
1136 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
1137 delayed_error("Mount point",
1138 "A mount point is a directory which another "
1139 "filing system can be mounted on. Everything "
1140 "on the mounted filesystem then appears to be "
1141 "inside the directory.");
1143 delayed_error("Directory",
1144 "This is a directory. It contains an index to "
1145 "other items - open it to see the list.");
1147 case TYPE_CHAR_DEVICE
:
1148 case TYPE_BLOCK_DEVICE
:
1149 delayed_error("Device file",
1150 "Device files allow you to read from or write "
1151 "to a device driver as though it was an "
1155 delayed_error("Named pipe",
1156 "Pipes allow different programs to "
1157 "communicate. One program writes data to the "
1158 "pipe while another one reads it out again.");
1161 delayed_error("Socket",
1162 "Sockets allow processes to communicate.");
1165 delayed_error("Unknown type",
1166 "I couldn't find out what kind of file this "
1167 "is. Maybe it doesn't exist anymore or you "
1168 "don't have search permission on the directory "
1174 #define OPEN_VFS(fs) \
1175 static void open_vfs_ ## fs (gpointer data, guint action, GtkWidget *widget) \
1177 Collection *collection; \
1179 g_return_if_fail(window_with_focus != NULL); \
1181 collection = window_with_focus->collection; \
1182 if (collection->number_selected < 1) \
1183 collection_target(collection, target_callback, \
1186 real_vfs_open(#fs); \
1189 static void real_vfs_open(char *fs
)
1194 if (window_with_focus
->collection
->number_selected
!= 1)
1196 report_error("ROX-Filer", "You must select a single file "
1197 "to open as a Virtual File System");
1201 item
= selected_item(window_with_focus
->collection
);
1203 path
= g_strconcat(window_with_focus
->path
,
1208 filer_change_to(window_with_focus
, path
, NULL
);
1216 static void select_all(gpointer data
, guint action
, GtkWidget
*widget
)
1218 g_return_if_fail(window_with_focus
!= NULL
);
1220 collection_select_all(window_with_focus
->collection
);
1221 window_with_focus
->temp_item_selected
= FALSE
;
1224 static void clear_selection(gpointer data
, guint action
, GtkWidget
*widget
)
1226 g_return_if_fail(window_with_focus
!= NULL
);
1228 collection_clear_selection(window_with_focus
->collection
);
1229 window_with_focus
->temp_item_selected
= FALSE
;
1232 static void show_options(gpointer data
, guint action
, GtkWidget
*widget
)
1234 g_return_if_fail(window_with_focus
!= NULL
);
1236 options_show(window_with_focus
);
1239 static gboolean
new_directory_cb(char *initial
, char *path
)
1241 if (mkdir(path
, S_IRWXU
| S_IRWXG
| S_IRWXO
))
1243 report_error("mkdir", g_strerror(errno
));
1249 static void new_directory(gpointer data
, guint action
, GtkWidget
*widget
)
1251 g_return_if_fail(window_with_focus
!= NULL
);
1253 savebox_show(window_with_focus
, "Create directory",
1254 window_with_focus
->path
, "NewDir",
1255 default_pixmap
+ TYPE_DIRECTORY
, new_directory_cb
);
1258 static void xterm_here(gpointer data
, guint action
, GtkWidget
*widget
)
1260 char *argv
[] = {NULL
, NULL
};
1262 argv
[0] = xterm_here_value
;
1264 g_return_if_fail(window_with_focus
!= NULL
);
1266 if (!spawn_full(argv
, window_with_focus
->path
))
1267 report_error("ROX-Filer", "Failed to fork() child "
1271 static void open_parent(gpointer data
, guint action
, GtkWidget
*widget
)
1276 g_return_if_fail(window_with_focus
!= NULL
);
1278 if (window_with_focus
->path
[0] == '/'
1279 && window_with_focus
->path
[1] == '\0')
1280 return; /* Already in the root */
1282 copy
= g_strdup(window_with_focus
->path
);
1283 slash
= strrchr(copy
, '/');
1288 filer_opendir(*copy
? copy
: "/", PANEL_NO
);
1291 g_warning("No / in directory path!\n");
1296 static void open_parent_same(gpointer data
, guint action
, GtkWidget
*widget
)
1298 g_return_if_fail(window_with_focus
!= NULL
);
1300 change_to_parent(window_with_focus
);
1303 static void new_window(gpointer data
, guint action
, GtkWidget
*widget
)
1305 g_return_if_fail(window_with_focus
!= NULL
);
1307 if (o_unique_filer_windows
)
1308 report_error("ROX-Filer", "You can't open a second view onto "
1309 "this directory because the `Unique Windows' option "
1310 "is turned on in the Options window.");
1312 filer_opendir(window_with_focus
->path
, PANEL_NO
);
1315 static void close_window(gpointer data
, guint action
, GtkWidget
*widget
)
1317 g_return_if_fail(window_with_focus
!= NULL
);
1319 gtk_widget_destroy(window_with_focus
->window
);
1322 static void enter_path(gpointer data
, guint action
, GtkWidget
*widget
)
1324 g_return_if_fail(window_with_focus
!= NULL
);
1326 minibuffer_show(window_with_focus
);
1329 static void rox_help(gpointer data
, guint action
, GtkWidget
*widget
)
1331 g_return_if_fail(window_with_focus
!= NULL
);
1333 filer_opendir(make_path(getenv("APP_DIR"), "Help")->str
, PANEL_NO
);
1336 static void open_as_dir(gpointer data
, guint action
, GtkWidget
*widget
)
1338 g_return_if_fail(window_with_focus
!= NULL
);
1340 filer_opendir(window_with_focus
->path
, PANEL_NO
);
1343 static void close_panel(gpointer data
, guint action
, GtkWidget
*widget
)
1345 g_return_if_fail(window_with_focus
!= NULL
);
1347 gtk_widget_destroy(window_with_focus
->window
);