4 * ROX-Filer, filer for the ROX desktop project
5 * By Thomas Leonard, <tal197@ecs.soton.ac.uk>.
8 /* menu.c - code for handling the popup menu */
13 #include <sys/types.h>
28 #include "gui_support.h"
33 #define C_ "<control>"
35 #define MENU_MARGIN 32
37 GtkAccelGroup
*filer_keys
;
38 GtkAccelGroup
*panel_keys
;
41 static GtkWidget
*xterm_here_entry
;
42 static char *xterm_here_value
;
44 /* Static prototypes */
45 static void position_menu(GtkMenu
*menu
, gint
*x
, gint
*y
, gpointer data
);
46 static void menu_closed(GtkWidget
*widget
);
47 static void items_sensitive(GtkWidget
*menu
, int from
, int n
, gboolean state
);
48 static char *load_xterm_here(char *data
);
50 static void hidden(gpointer data
, guint action
, GtkWidget
*widget
);
51 static void refresh(gpointer data
, guint action
, GtkWidget
*widget
);
53 static void copy_item(gpointer data
, guint action
, GtkWidget
*widget
);
54 static void rename_item(gpointer data
, guint action
, GtkWidget
*widget
);
55 static void link_item(gpointer data
, guint action
, GtkWidget
*widget
);
56 static void help(gpointer data
, guint action
, GtkWidget
*widget
);
57 static void mount(gpointer data
, guint action
, GtkWidget
*widget
);
58 static void delete(gpointer data
, guint action
, GtkWidget
*widget
);
60 static void select_all(gpointer data
, guint action
, GtkWidget
*widget
);
61 static void clear_selection(gpointer data
, guint action
, GtkWidget
*widget
);
62 static void show_options(gpointer data
, guint action
, GtkWidget
*widget
);
63 static void new_directory(gpointer data
, guint action
, GtkWidget
*widget
);
64 static void xterm_here(gpointer data
, guint action
, GtkWidget
*widget
);
65 static void open_parent(gpointer data
, guint action
, GtkWidget
*widget
);
67 static void open_as_dir(gpointer data
, guint action
, GtkWidget
*widget
);
68 static void close_panel(gpointer data
, guint action
, GtkWidget
*widget
);
70 static GtkWidget
*filer_menu
; /* The popup filer menu */
71 static GtkWidget
*filer_file_item
; /* The File '' label */
72 static GtkWidget
*filer_file_menu
; /* The File '' menu */
73 static GtkWidget
*panel_menu
; /* The popup panel menu */
74 static GtkWidget
*panel_file_item
; /* The File '' label */
75 static GtkWidget
*panel_file_menu
; /* The File '' menu */
77 static GtkItemFactoryEntry filer_menu_def
[] = {
78 {"/Display", NULL
, NULL
, 0, "<Branch>"},
79 {"/Display/Large Icons", NULL
, NULL
, 0, "<RadioItem>"},
80 {"/Display/Small Icons", NULL
, NULL
, 0, "/Display/Large Icons"},
81 {"/Display/Full Info", NULL
, NULL
, 0, "/Display/Large Icons"},
82 {"/Display/Separator", NULL
, NULL
, 0, "<Separator>"},
83 {"/Display/Sort by Name", NULL
, NULL
, 0, "<RadioItem>"},
84 {"/Display/Sort by Type", NULL
, NULL
, 0, "/Display/Sort by Name"},
85 {"/Display/Sort by Date", NULL
, NULL
, 0, "/Display/Sort by Name"},
86 {"/Display/Sort by Size", NULL
, NULL
, 0, "/Display/Sort by Name"},
87 {"/Display/Sort by Owner", NULL
, NULL
, 0, "/Display/Sort by Name"},
88 {"/Display/Separator", NULL
, NULL
, 0, "<Separator>"},
89 {"/Display/Show Hidden", C_
"H", hidden
, 0, "<ToggleItem>"},
90 {"/Display/Refresh", C_
"L", refresh
, 0, NULL
},
91 {"/File", NULL
, NULL
, 0, "<Branch>"},
92 {"/File/Copy...", NULL
, copy_item
, 0, NULL
},
93 {"/File/Rename...", NULL
, rename_item
, 0, NULL
},
94 {"/File/Link...", NULL
, link_item
, 0, NULL
},
95 {"/File/Help", "F1", help
, 0, NULL
},
96 {"/File/Info", NULL
, NULL
, 0, NULL
},
97 {"/File/Separator", NULL
, NULL
, 0, "<Separator>"},
98 {"/File/Mount", C_
"M", mount
, 0, NULL
},
99 {"/File/Delete", C_
"X", delete, 0, NULL
},
100 {"/File/Disk Usage", C_
"U", NULL
, 0, NULL
},
101 {"/File/Permissions", NULL
, NULL
, 0, NULL
},
102 {"/File/Touch", NULL
, NULL
, 0, NULL
},
103 {"/File/Find", NULL
, NULL
, 0, NULL
},
104 {"/Select All", C_
"A", select_all
, 0, NULL
},
105 {"/Clear Selection", C_
"Z", clear_selection
, 0, NULL
},
106 {"/Options...", NULL
, show_options
, 0, NULL
},
107 {"/New directory", NULL
, new_directory
, 0, NULL
},
108 {"/Xterm here", NULL
, xterm_here
, 0, NULL
},
109 {"/Open parent", NULL
, open_parent
, 0, NULL
},
112 static GtkItemFactoryEntry panel_menu_def
[] = {
113 {"/Display", NULL
, NULL
, 0, "<Branch>"},
114 {"/Display/Large Icons", NULL
, NULL
, 0, "<RadioItem>"},
115 {"/Display/Small Icons", NULL
, NULL
, 0, "/Display/Large Icons"},
116 {"/Display/Full Info", NULL
, NULL
, 0, "/Display/Large Icons"},
117 {"/Display/Separator", NULL
, NULL
, 0, "<Separator>"},
118 {"/Display/Sort by Name", NULL
, NULL
, 0, "<RadioItem>"},
119 {"/Display/Sort by Type", NULL
, NULL
, 0, "/Display/Sort by Name"},
120 {"/Display/Sort by Date", NULL
, NULL
, 0, "/Display/Sort by Name"},
121 {"/Display/Sort by Size", NULL
, NULL
, 0, "/Display/Sort by Name"},
122 {"/Display/Sort by Owner", NULL
, NULL
, 0, "/Display/Sort by Name"},
123 {"/Display/Separator", NULL
, NULL
, 0, "<Separator>"},
124 {"/Display/Show Hidden", NULL
, hidden
, 0, "<ToggleItem>"},
125 {"/Display/Refresh", NULL
, refresh
, 0, NULL
},
126 {"/File", NULL
, NULL
, 0, "<Branch>"},
127 {"/File/Help", NULL
, help
, 0, NULL
},
128 {"/File/Delete", NULL
, delete, 0, NULL
},
129 {"/Open as directory", NULL
, open_as_dir
, 0, NULL
},
130 {"/Close panel", NULL
, close_panel
, 0, NULL
},
135 GtkItemFactory
*item_factory
;
139 filer_keys
= gtk_accel_group_new();
140 item_factory
= gtk_item_factory_new(GTK_TYPE_MENU
,
143 gtk_item_factory_create_items(item_factory
,
144 sizeof(filer_menu_def
) / sizeof(*filer_menu_def
),
147 filer_menu
= gtk_item_factory_get_widget(item_factory
, "<filer>");
148 filer_file_menu
= gtk_item_factory_get_widget(item_factory
,
150 items
= gtk_container_children(GTK_CONTAINER(filer_menu
));
151 filer_file_item
= GTK_BIN(g_list_nth(items
, 1)->data
)->child
;
154 panel_keys
= gtk_accel_group_new();
155 item_factory
= gtk_item_factory_new(GTK_TYPE_MENU
,
158 gtk_item_factory_create_items(item_factory
,
159 sizeof(panel_menu_def
) / sizeof(*panel_menu_def
),
162 panel_menu
= gtk_item_factory_get_widget(item_factory
, "<panel>");
163 panel_file_menu
= gtk_item_factory_get_widget(item_factory
,
165 items
= gtk_container_children(GTK_CONTAINER(panel_menu
));
166 panel_file_item
= GTK_BIN(g_list_nth(items
, 1)->data
)->child
;
169 menurc
= choices_find_path_load("menus");
171 gtk_item_factory_parse_rc(menurc
);
173 gtk_signal_connect(GTK_OBJECT(panel_menu
), "unmap_event",
174 GTK_SIGNAL_FUNC(menu_closed
), NULL
);
175 gtk_signal_connect(GTK_OBJECT(filer_menu
), "unmap_event",
176 GTK_SIGNAL_FUNC(menu_closed
), NULL
);
178 gtk_accel_group_lock(panel_keys
);
180 xterm_here_value
= g_strdup("xterm");
181 option_register("xterm_here", load_xterm_here
);
184 /* Build up some option widgets to go in the options dialog, but don't
187 GtkWidget
*create_menu_options()
189 GtkWidget
*table
, *label
;
191 table
= gtk_table_new(2, 2, FALSE
);
192 gtk_container_set_border_width(GTK_CONTAINER(table
), 4);
194 label
= gtk_label_new("To set the keyboard short-cuts you simply open "
195 "the menu over a filer window, move the pointer over "
196 "the item you want to use and press a key. The key "
197 "will appear next to the menu item and you can just "
198 "press that key without opening the menu in future. "
199 "To save the current menu short-cuts for next time, "
200 "click the Save button at the bottom of this window.");
201 gtk_label_set_line_wrap(GTK_LABEL(label
), TRUE
);
202 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 0, 2, 0, 1);
204 label
= gtk_label_new("'Xterm here' program:");
205 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 0, 1, 1, 2);
206 xterm_here_entry
= gtk_entry_new();
207 gtk_table_attach_defaults(GTK_TABLE(table
), xterm_here_entry
,
213 static char *load_xterm_here(char *data
)
215 g_free(xterm_here_value
);
216 xterm_here_value
= g_strdup(data
);
220 void menu_update_options()
222 gtk_entry_set_text(GTK_ENTRY(xterm_here_entry
), xterm_here_value
);
225 void menu_set_options()
227 g_free(xterm_here_value
);
228 xterm_here_value
= g_strdup(gtk_entry_get_text(
229 GTK_ENTRY(xterm_here_entry
)));
232 void menu_save_options()
236 menurc
= choices_find_path_save("menus");
238 gtk_item_factory_dump_rc(menurc
, NULL
, TRUE
);
240 option_write("xterm_here", xterm_here_value
);
244 static void items_sensitive(GtkWidget
*menu
, int from
, int n
, gboolean state
)
248 items
= gtk_container_children(GTK_CONTAINER(menu
));
250 item
= g_list_nth(items
, from
);
253 gtk_widget_set_sensitive(GTK_BIN(item
->data
)->child
, state
);
260 static void position_menu(GtkMenu
*menu
, gint
*x
, gint
*y
, gpointer data
)
262 int *pos
= (int *) data
;
264 GtkRequisition requisition
;
266 gdk_window_get_size(GDK_ROOT_PARENT(), &swidth
, &sheight
);
267 /* XXX: GDK sometimes seems to get the height wrong... */
270 g_print("ROX-Filer: Root window height reported as %d\n",
275 gtk_widget_size_request(GTK_WIDGET(menu
), &requisition
);
278 *x
= swidth
- MENU_MARGIN
- requisition
.width
;
279 else if (pos
[0] == -2)
282 *x
= pos
[0] - (requisition
.width
>> 2);
285 *y
= sheight
- MENU_MARGIN
- requisition
.height
;
286 else if (pos
[1] == -2)
289 *y
= pos
[1] - (requisition
.height
>> 2);
291 *x
= CLAMP(*x
, 0, swidth
- requisition
.width
);
292 *y
= CLAMP(*y
, 0, sheight
- requisition
.height
);
295 void show_filer_menu(FilerWindow
*filer_window
, GdkEventButton
*event
,
299 GtkWidget
*file_label
, *file_menu
;
301 int pos
[] = {event
->x_root
, event
->y_root
};
303 window_with_focus
= filer_window
;
305 if (filer_window
->panel
)
307 switch (filer_window
->panel_side
)
309 case TOP
: pos
[1] = -2; break;
310 case BOTTOM
: pos
[1] = -1; break;
311 case LEFT
: pos
[0] = -2; break;
312 case RIGHT
: pos
[0] = -1; break;
316 if (filer_window
->panel
)
318 collection_clear_selection(filer_window
->collection
);
319 panel_set_timeout(NULL
, 0);
322 if (filer_window
->collection
->number_selected
== 0 && item
>= 0)
324 collection_select_item(filer_window
->collection
, item
);
325 filer_window
->temp_item_selected
= TRUE
;
328 filer_window
->temp_item_selected
= FALSE
;
330 if (filer_window
->panel
)
332 file_label
= panel_file_item
;
333 file_menu
= panel_file_menu
;
337 file_label
= filer_file_item
;
338 file_menu
= filer_file_menu
;
341 buffer
= g_string_new(NULL
);
342 switch (filer_window
->collection
->number_selected
)
345 g_string_assign(buffer
, "<nothing selected>");
346 items_sensitive(file_menu
, 0, 5, FALSE
);
347 items_sensitive(file_menu
, 6, -1, FALSE
);
348 gtk_widget_set_sensitive(file_label
, FALSE
);
351 items_sensitive(file_menu
, 0, 5, TRUE
);
352 items_sensitive(file_menu
, 6, -1, TRUE
);
353 gtk_widget_set_sensitive(file_label
, TRUE
);
354 file_item
= selected_item(filer_window
->collection
);
355 g_string_sprintf(buffer
, "%s '%s'",
356 basetype_name(file_item
),
357 file_item
->leafname
);
360 items_sensitive(file_menu
, 0, 5, FALSE
);
361 items_sensitive(file_menu
, 6, -1, TRUE
);
362 gtk_widget_set_sensitive(file_label
, TRUE
);
363 g_string_sprintf(buffer
, "%d items",
364 filer_window
->collection
->number_selected
);
368 gtk_label_set_text(GTK_LABEL(file_label
), buffer
->str
);
370 g_string_free(buffer
, TRUE
);
372 gtk_menu_popup(filer_window
->panel
? GTK_MENU(panel_menu
)
373 : GTK_MENU(filer_menu
),
374 NULL
, NULL
, position_menu
,
375 (gpointer
) pos
, event
->button
, event
->time
);
378 static void menu_closed(GtkWidget
*widget
)
380 if (window_with_focus
== NULL
)
381 return; /* Close panel item chosen? */
383 if (window_with_focus
->temp_item_selected
)
385 collection_clear_selection(window_with_focus
->collection
);
386 window_with_focus
->temp_item_selected
= FALSE
;
392 static void hidden(gpointer data
, guint action
, GtkWidget
*widget
)
394 g_return_if_fail(window_with_focus
!= NULL
);
396 window_with_focus
->show_hidden
= !window_with_focus
->show_hidden
;
397 scan_dir(window_with_focus
);
400 static void refresh(gpointer data
, guint action
, GtkWidget
*widget
)
402 g_return_if_fail(window_with_focus
!= NULL
);
404 scan_dir(window_with_focus
);
407 static void delete(gpointer data
, guint action
, GtkWidget
*widget
)
409 g_return_if_fail(window_with_focus
!= NULL
);
411 action_delete(window_with_focus
);
414 static gboolean
copy_cb(char *initial
, char *path
)
416 char *new_dir
, *slash
;
419 gboolean retval
= TRUE
;
421 slash
= strrchr(path
, '/');
424 report_error("ROX-Filer", "Missing '/' in new pathname");
428 if (access(path
, F_OK
) == 0)
430 report_error("ROX-Filer",
431 "An item with this name already exists");
436 new_dir
= g_malloc(len
+ 1);
437 memcpy(new_dir
, path
, len
);
440 command
= g_string_new(NULL
);
441 g_string_sprintf(command
, "cp -a %s %s", initial
, path
);
443 if (system(command
->str
))
445 g_string_append(command
, " failed!");
446 report_error("ROX-Filer", command
->str
);
450 g_string_free(command
, TRUE
);
452 refresh_dirs(new_dir
);
456 static void copy_item(gpointer data
, guint action
, GtkWidget
*widget
)
458 Collection
*collection
;
460 g_return_if_fail(window_with_focus
!= NULL
);
462 collection
= window_with_focus
->collection
;
463 if (collection
->number_selected
!= 1)
464 report_error("ROX-Filer", "You must select a single "
468 FileItem
*item
= selected_item(collection
);
470 savebox_show(window_with_focus
, "Copy",
471 window_with_focus
->path
, item
->leafname
,
472 item
->image
, copy_cb
);
476 static gboolean
rename_cb(char *initial
, char *path
)
478 if (rename(initial
, path
))
480 report_error("ROX-Filer: rename()", g_strerror(errno
));
486 static void rename_item(gpointer data
, guint action
, GtkWidget
*widget
)
488 Collection
*collection
;
490 g_return_if_fail(window_with_focus
!= NULL
);
492 collection
= window_with_focus
->collection
;
493 if (collection
->number_selected
!= 1)
494 report_error("ROX-Filer", "You must select a single "
498 FileItem
*item
= selected_item(collection
);
500 savebox_show(window_with_focus
, "Rename",
501 window_with_focus
->path
, item
->leafname
,
502 item
->image
, rename_cb
);
506 static gboolean
link_cb(char *initial
, char *path
)
508 if (symlink(initial
, path
))
510 report_error("ROX-Filer: symlink()", g_strerror(errno
));
516 static void link_item(gpointer data
, guint action
, GtkWidget
*widget
)
518 Collection
*collection
;
520 g_return_if_fail(window_with_focus
!= NULL
);
522 collection
= window_with_focus
->collection
;
523 if (collection
->number_selected
!= 1)
524 report_error("ROX-Filer", "You must select a single "
528 FileItem
*item
= selected_item(collection
);
530 savebox_show(window_with_focus
, "Symlink",
531 window_with_focus
->path
, item
->leafname
,
532 item
->image
, link_cb
);
536 static void help(gpointer data
, guint action
, GtkWidget
*widget
)
538 Collection
*collection
;
541 g_return_if_fail(window_with_focus
!= NULL
);
543 collection
= window_with_focus
->collection
;
544 if (collection
->number_selected
!= 1)
546 report_error("ROX-Filer", "You must select a single "
547 "item to get help on");
550 item
= selected_item(collection
);
551 switch (item
->base_type
)
554 if (item
->flags
& ITEM_FLAG_EXEC_FILE
)
555 report_error("Executable file",
556 "This is a file with an eXecute bit "
557 "set - it can be run as a program.");
560 "This is a file. It contains stored data. Try "
561 "running file(1) on it to find out what kind "
562 "of data it contains.");
565 if (item
->flags
& ITEM_FLAG_APPDIR
)
567 make_path(window_with_focus
->path
,
568 item
->leafname
)->str
);
569 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
570 report_error("Mount point",
571 "A mount point is a directory which another "
572 "filing system can be mounted on. Everything "
573 "on the mounted filesystem then appears to be "
574 "inside the directory.");
576 report_error("Directory",
577 "This is a directory. It contains an index to "
578 "other items - open it to see the list.");
580 case TYPE_CHAR_DEVICE
:
581 case TYPE_BLOCK_DEVICE
:
582 report_error("Device file",
583 "Device files allow you to read from or write "
584 "to a device driver as though it was an "
588 report_error("Named pipe",
589 "Pipes allow different programs to "
590 "communicate. One program writes data to the "
591 "pipe while another one reads it out again.");
594 report_error("Socket",
595 "Sockets allow processes to communicate.");
598 report_error("Unknown type",
599 "I couldn't find out what kind of file this "
600 "is. Maybe it doesn't exist anymore or you "
601 "don't have search permission on the directory "
608 static void mount(gpointer data
, guint action
, GtkWidget
*widget
)
612 Collection
*collection
;
616 g_return_if_fail(window_with_focus
!= NULL
);
618 collection
= window_with_focus
->collection
;
620 for (i
= 0; i
< collection
->number_of_items
; i
++)
621 if (collection
->items
[i
].selected
)
623 item
= (FileItem
*) collection
->items
[i
].data
;
624 if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
626 char *argv
[] = {"mount", NULL
, NULL
};
630 if (item
->flags
& ITEM_FLAG_MOUNTED
)
632 argv
[1] = make_path(window_with_focus
->path
,
633 item
->leafname
)->str
;
636 waitpid(child
, NULL
, 0);
638 error
= "Failed to run mount/umount";
642 scan_dir(window_with_focus
);
644 error
= "You must select some mount points first!";
647 report_error("ROX-Filer", error
);
650 static void select_all(gpointer data
, guint action
, GtkWidget
*widget
)
652 g_return_if_fail(window_with_focus
!= NULL
);
654 collection_select_all(window_with_focus
->collection
);
655 window_with_focus
->temp_item_selected
= FALSE
;
658 static void clear_selection(gpointer data
, guint action
, GtkWidget
*widget
)
660 g_return_if_fail(window_with_focus
!= NULL
);
662 collection_clear_selection(window_with_focus
->collection
);
663 window_with_focus
->temp_item_selected
= FALSE
;
666 static void show_options(gpointer data
, guint action
, GtkWidget
*widget
)
668 g_return_if_fail(window_with_focus
!= NULL
);
670 options_show(window_with_focus
);
673 static gboolean
new_directory_cb(char *initial
, char *path
)
675 if (mkdir(path
, S_IRWXU
| S_IRWXG
| S_IRWXO
))
677 report_error("mkdir", g_strerror(errno
));
683 static void new_directory(gpointer data
, guint action
, GtkWidget
*widget
)
685 g_return_if_fail(window_with_focus
!= NULL
);
687 savebox_show(window_with_focus
, "Create directory",
688 window_with_focus
->path
, "NewDir",
689 default_pixmap
+ TYPE_DIRECTORY
, new_directory_cb
);
692 static void xterm_here(gpointer data
, guint action
, GtkWidget
*widget
)
694 char *argv
[] = {xterm_here_value
, NULL
};
696 g_return_if_fail(window_with_focus
!= NULL
);
698 if (!spawn_full(argv
, window_with_focus
->path
, 0))
699 report_error("ROX-Filer", "Failed to fork() child "
703 static void open_parent(gpointer data
, guint action
, GtkWidget
*widget
)
705 g_return_if_fail(window_with_focus
!= NULL
);
707 filer_opendir(make_path(window_with_focus
->path
, "/..")->str
,
711 static void open_as_dir(gpointer data
, guint action
, GtkWidget
*widget
)
713 g_return_if_fail(window_with_focus
!= NULL
);
715 filer_opendir(window_with_focus
->path
, FALSE
, BOTTOM
);
718 static void close_panel(gpointer data
, guint action
, GtkWidget
*widget
)
720 g_return_if_fail(window_with_focus
!= NULL
);
722 gtk_widget_destroy(window_with_focus
->window
);