2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA
20 /* infobox.c - code for showing a file's attributes */
27 #include <sys/param.h>
29 #include <libxml/parser.h>
37 #include "gui_support.h"
42 #include "dnd.h" /* For xa_string */
43 #include "run.h" /* For show_help_files() */
50 typedef struct _FileStatus FileStatus
;
52 /* This is for the 'file(1) says...' thing */
55 int fd
; /* FD to read from, -1 if closed */
56 int input
; /* Input watcher tag if fd valid */
57 GtkLabel
*label
; /* Widget to output to */
58 gchar
*text
; /* String so far */
69 typedef struct _Permissions Permissions
;
78 /* Static prototypes */
79 static void refresh_info(GObject
*window
);
80 static GtkWidget
*make_vbox(const guchar
*path
, GObject
*window
);
81 static GtkWidget
*make_details(const guchar
*path
, DirItem
*item
,
83 static GtkWidget
*make_about(const guchar
*path
, XMLwrapper
*ai
);
84 static GtkWidget
*make_about_desktop(const gchar
*path
);
85 static GtkWidget
*make_file_says(const guchar
*path
);
86 static GtkWidget
*make_permissions(const gchar
*path
, DirItem
*item
);
87 static GtkWidget
*make_unmount_options(const gchar
*path
);
88 static void add_file_output(FileStatus
*fs
,
89 gint source
, GdkInputCondition condition
);
90 static const gchar
*pretty_type(DirItem
*file
, const guchar
*path
);
91 static void got_response(GObject
*window
, gint response
, gpointer data
);
92 static void file_info_destroyed(GtkWidget
*widget
, FileStatus
*fs
);
94 /****************************************************************
95 * EXTERNAL INTERFACE *
96 ****************************************************************/
98 /* Open each item in a new infobox. Confirms if there are a large
99 * number of items to show.
101 void infobox_show_list(GList
*paths
)
105 n
= g_list_length(paths
);
112 message
= g_strdup_printf(
113 _("Are you sure you want to open %d windows?"), n
);
114 ok
= confirm(message
, GTK_STOCK_YES
, _("Show Info"));
120 g_list_foreach(paths
, (GFunc
) infobox_new
, NULL
);
123 /* Create and display a new info box showing details about this item */
124 void infobox_new(const gchar
*pathname
)
126 GtkWidget
*window
, *details
;
130 g_return_if_fail(pathname
!= NULL
);
132 path
= g_strdup(pathname
); /* Gets attached to window & freed later */
134 window
= gtk_dialog_new_with_buttons(
135 g_utf8_validate(path
, -1, NULL
) ? path
137 NULL
, GTK_DIALOG_NO_SEPARATOR
,
138 GTK_STOCK_CLOSE
, GTK_RESPONSE_CANCEL
,
139 GTK_STOCK_REFRESH
, GTK_RESPONSE_APPLY
,
142 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_MOUSE
);
144 owindow
= G_OBJECT(window
);
145 details
= make_vbox(path
, owindow
);
146 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(window
)->vbox
),
149 g_object_set_data(owindow
, "details", details
);
150 g_object_set_data_full(owindow
, "path", path
, g_free
);
152 g_signal_connect(window
, "response", G_CALLBACK(got_response
), NULL
);
155 gtk_widget_show_all(window
);
158 /****************************************************************
159 * INTERNAL FUNCTIONS *
160 ****************************************************************/
162 static void got_response(GObject
*window
, gint response
, gpointer data
)
164 if (response
== GTK_RESPONSE_APPLY
)
165 refresh_info(window
);
168 gtk_widget_destroy(GTK_WIDGET(window
));
173 static void refresh_info(GObject
*window
)
175 GtkWidget
*details
, *vbox
;
178 path
= g_object_get_data(window
, "path");
179 details
= g_object_get_data(window
, "details");
180 g_return_if_fail(details
!= NULL
);
181 g_return_if_fail(path
!= NULL
);
183 vbox
= details
->parent
;
184 gtk_widget_destroy(details
);
186 details
= make_vbox(path
, window
);
187 g_object_set_data(window
, "details", details
);
188 gtk_box_pack_start_defaults(GTK_BOX(vbox
), details
);
189 gtk_widget_show_all(details
);
192 static void add_frame(GtkBox
*vbox
, GtkWidget
*list
)
196 frame
= gtk_frame_new(NULL
);
197 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_IN
);
198 gtk_container_add(GTK_CONTAINER(frame
), list
);
199 gtk_box_pack_start_defaults(vbox
, frame
);
202 /* Create the VBox widget that contains the details.
203 * Note that 'path' must not be freed until the vbox is destroyed.
205 static GtkWidget
*make_vbox(const guchar
*path
, GObject
*window
)
210 xmlNode
*about
= NULL
;
212 GtkWidget
*hbox
, *name
, *label
;
215 g_return_val_if_fail(path
[0] == '/', NULL
);
217 item
= diritem_new(g_basename(path
));
218 diritem_restat(path
, item
, NULL
);
220 ai
= appinfo_get(path
, item
);
222 about
= xml_get_section(ai
, NULL
, "About");
224 vbox
= GTK_BOX(gtk_vbox_new(FALSE
, 4));
225 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 4);
227 /* Heading, with icon and name */
228 hbox
= gtk_hbox_new(FALSE
, 4);
229 gtk_box_pack_start(vbox
, hbox
, FALSE
, TRUE
, 0);
230 gtk_box_pack_start(GTK_BOX(hbox
),
231 gtk_image_new_from_pixbuf(di_image(item
)->pixbuf
),
234 if (g_utf8_validate(item
->leafname
, -1, NULL
))
235 name
= gtk_label_new(item
->leafname
);
240 u8
= to_utf8(item
->leafname
);
241 name
= gtk_label_new(u8
);
244 gtk_label_set_selectable(GTK_LABEL(name
), TRUE
);
245 gtk_label_set_line_wrap(GTK_LABEL(name
), TRUE
);
246 gtk_box_pack_start(GTK_BOX(hbox
), name
, FALSE
, TRUE
, 4);
248 make_heading(name
, PANGO_SCALE_X_LARGE
);
250 thumb
=pixmap_try_thumb(path
, FALSE
);
253 gtk_box_pack_start(GTK_BOX(hbox
),
254 gtk_image_new_from_pixbuf(thumb
->src_pixbuf
),
256 g_object_unref(thumb
);
259 /* List of file attributes */
260 add_frame(vbox
, make_details(path
, item
, window
));
262 help_dir
= g_strconcat(path
, "/Help", NULL
);
264 if (access(help_dir
, F_OK
) == 0)
266 GtkWidget
*button
, *align
;
268 align
= gtk_alignment_new(0.5, 0.5, 0, 0);
270 button
= button_new_mixed(GTK_STOCK_JUMP_TO
,
271 _("Show _Help Files"));
272 gtk_box_pack_start(vbox
, align
, FALSE
, TRUE
, 0);
273 gtk_container_add(GTK_CONTAINER(align
), button
);
274 g_signal_connect_swapped(button
, "clicked",
275 G_CALLBACK(show_help_files
),
280 if (!(item
->flags
& ITEM_FLAG_SYMLINK
))
282 label
= gtk_label_new(NULL
);
283 gtk_label_set_markup(GTK_LABEL(label
),
284 _("<b>Permissions</b>"));
285 gtk_misc_set_alignment(GTK_MISC(label
), 0, 1);
286 gtk_box_pack_start(vbox
, label
, FALSE
, TRUE
, 2);
288 gtk_box_pack_start(vbox
, make_permissions(path
, item
),
293 add_frame(vbox
, make_about(path
, ai
));
294 else if (item
->mime_type
== application_x_desktop
)
296 add_frame(vbox
, make_about_desktop(path
));
298 else if (item
->base_type
== TYPE_FILE
)
300 label
= gtk_label_new(NULL
);
301 gtk_label_set_markup(GTK_LABEL(label
),
302 _("<b>Contents indicate...</b>"));
303 gtk_misc_set_alignment(GTK_MISC(label
), 0, 1);
304 gtk_box_pack_start(vbox
, label
, FALSE
, TRUE
, 2);
306 gtk_box_pack_start_defaults(vbox
, make_file_says(path
));
308 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
310 label
= gtk_label_new(NULL
);
311 gtk_label_set_markup(GTK_LABEL(label
),
312 _("<b>When all directories are closed</b>"));
313 gtk_misc_set_alignment(GTK_MISC(label
), 0, 1);
314 gtk_box_pack_start(vbox
, label
, FALSE
, TRUE
, 2);
315 gtk_box_pack_start(vbox
, make_unmount_options(path
), FALSE
, TRUE
, 0);
323 return (GtkWidget
*) vbox
;
326 /* The selection has changed - grab or release the primary selection */
327 static void set_selection(GtkTreeView
*view
, gpointer data
)
329 static GtkClipboard
*primary
= NULL
;
331 GtkTreePath
*path
= NULL
;
335 gtk_tree_view_get_cursor(view
, &path
, NULL
);
340 primary
= gtk_clipboard_get(gdk_atom_intern("PRIMARY", FALSE
));
342 model
= gtk_tree_view_get_model(GTK_TREE_VIEW(view
));
344 gtk_tree_model_get_iter(model
, &iter
, path
);
345 gtk_tree_path_free(path
);
347 gtk_tree_model_get(model
, &iter
, 1, &text
, -1);
349 gtk_clipboard_set_text(primary
, text
, -1);
354 /* Returns a GtkTreePath for the item */
355 static const gchar
*add_row(GtkListStore
*store
, const gchar
*label
,
361 static gchar
*last
= NULL
;
363 if (!g_utf8_validate(data
, -1, NULL
))
366 gtk_list_store_append(store
, &iter
);
367 gtk_list_store_set(store
, &iter
, 0, label
, 1, u8
? u8
: data
, -1);
371 tpath
= gtk_tree_model_get_path(GTK_TREE_MODEL(store
), &iter
);
374 last
= gtk_tree_path_to_string(tpath
);
375 gtk_tree_path_free(tpath
);
380 static void add_row_and_free(GtkListStore
*store
,
381 const gchar
*label
, gchar
*data
)
383 add_row(store
, label
, data
);
387 /* Create an empty list view, ready to place some data in */
388 static void make_list(GtkListStore
**list_store
, GtkWidget
**list_view
,
389 GCallback cell_edited
)
393 GtkCellRenderer
*cell_renderer
;
395 /* Field name, value, editable */
396 store
= gtk_list_store_new(3, G_TYPE_STRING
, G_TYPE_STRING
,
398 view
= GTK_TREE_VIEW(
399 gtk_tree_view_new_with_model(GTK_TREE_MODEL(store
)));
400 g_object_unref(G_OBJECT(store
));
401 gtk_tree_view_set_headers_visible(view
, FALSE
);
403 cell_renderer
= gtk_cell_renderer_text_new();
404 g_object_set(G_OBJECT(cell_renderer
), "xalign", 1.0, NULL
);
405 gtk_tree_view_insert_column_with_attributes(view
,
406 0, NULL
, cell_renderer
, "text", 0, NULL
);
408 cell_renderer
= gtk_cell_renderer_text_new();
409 gtk_tree_view_insert_column_with_attributes(view
,
410 1, NULL
, cell_renderer
, "text", 1, "editable", 2, NULL
);
413 g_signal_connect(G_OBJECT(cell_renderer
), "edited",
414 G_CALLBACK(cell_edited
), store
);
417 g_signal_connect(view
, "cursor_changed",
418 G_CALLBACK(set_selection
), NULL
);
421 *list_view
= (GtkWidget
*) view
;
424 static void set_cell(GtkListStore
*store
, const gchar
*path
,
429 gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(store
),
431 gtk_list_store_set(store
, &iter
, 1, ctext
, -1);
434 static void insert_size(DU
*du
, const char *line
)
439 #ifdef LARGE_FILE_SUPPORT
440 size
= strtoll(line
, NULL
, 10);
442 size
= strtol(line
, NULL
, 10);
444 size
<<= 10; /* Because du reports in K */
445 cell
= (size
>= PRETTY_SIZE_LIMIT
)
446 ? g_strdup_printf("%s (%" SIZE_FMT
" %s)",
449 : g_strdup(format_size(size
));
451 set_cell(du
->store
, du
->path
, cell
);
456 static gboolean
read_du_output(GIOChannel
*source
, GIOCondition cond
, DU
*du
)
462 line
= g_string_new("");
463 stat
= g_io_channel_read_line_string(source
, line
, NULL
, &err
);
466 case G_IO_STATUS_NORMAL
:
467 insert_size(du
, line
->str
);
469 case G_IO_STATUS_EOF
:
470 set_cell(du
->store
, du
->path
,
471 _("Failed to read size"));
473 case G_IO_STATUS_AGAIN
:
474 g_string_free(line
, TRUE
);
476 case G_IO_STATUS_ERROR
:
477 set_cell(du
->store
, du
->path
, err
->message
);
480 g_string_free(line
, TRUE
);
485 static void kill_du_output(GtkWidget
*widget
, DU
*du
)
487 g_source_remove(du
->watch
);
488 g_io_channel_shutdown(du
->chan
, FALSE
, NULL
);
489 g_io_channel_unref(du
->chan
);
490 kill((pid_t
) du
->child
, SIGTERM
);
491 g_object_unref(G_OBJECT(du
->store
));
496 static gboolean
refresh_info_idle(gpointer data
)
498 GObject
*window
= G_OBJECT(data
);
500 refresh_info(window
);
501 g_object_unref(window
);
505 static void cell_edited(GtkCellRendererText
*cell
,
506 const gchar
*path_string
,
507 const gchar
*new_text
,
510 GtkTreeModel
*model
= (GtkTreeModel
*) data
;
514 const char *fullpath
;
517 window
= g_object_get_data(G_OBJECT(model
), "rox_window");
518 g_return_if_fail(window
!= NULL
);
520 fullpath
= g_object_get_data(window
, "path");
521 g_return_if_fail(fullpath
!= NULL
);
523 path
= gtk_tree_path_new_from_string(path_string
);
524 gtk_tree_model_get_iter(model
, &iter
, path
);
525 gtk_tree_path_free(path
);
527 oldlink
= readlink_dup(fullpath
);
529 /* Must use delayed_error(), as this can be called
530 * from a focus-out event (causes a crash).
532 delayed_error(_("'%s' is no longer a symlink"), fullpath
);
535 if (strcmp(oldlink
, new_text
) == 0)
536 return; /* No change */
538 if (unlink(fullpath
)) {
539 delayed_error(_("Failed to unlink '%s':\n%s"),
540 fullpath
, g_strerror(errno
));
543 if (symlink(new_text
, fullpath
)) {
544 delayed_error(_("Failed to create symlink from '%s':\n%s\n"
545 "(note: old link has been deleted)"),
546 fullpath
, g_strerror(errno
));
550 g_object_ref(window
);
551 g_idle_add(refresh_info_idle
, window
);
554 /* Create the TreeView widget with the file's details */
555 static GtkWidget
*make_details(const guchar
*path
, DirItem
*item
,
562 make_list(&store
, &view
, G_CALLBACK(cell_edited
));
563 g_object_set_data(G_OBJECT(store
), "rox_window", window
);
565 /* For a symlink to an error, don't show the error */
566 if (item
->base_type
== TYPE_ERROR
&& item
->lstat_errno
)
568 add_row(store
, _("Error:"), g_strerror(item
->lstat_errno
));
572 tmp
= g_path_get_dirname(path
);
574 if (strcmp(tmp
, tmp2
) != 0)
575 add_row_and_free(store
, _("Real directory:"), tmp2
);
578 add_row_and_free(store
, _("Owner, Group:"),
579 g_strdup_printf("%s, %s",
580 user_name(item
->uid
),
581 group_name(item
->gid
)));
583 if (item
->base_type
!= TYPE_DIRECTORY
)
585 add_row_and_free(store
, _("Size:"),
586 item
->size
>= PRETTY_SIZE_LIMIT
587 ? g_strdup_printf("%s (%" SIZE_FMT
" %s)",
588 format_size(item
->size
),
589 item
->size
, _("bytes"))
590 : g_strdup(format_size(item
->size
)));
596 if(item
->flags
& ITEM_FLAG_MOUNTED
)
597 stt
=mount_get_fs_size(path
);
600 add_row_and_free(store
, _("Size:"), stt
);
605 gchar
*args
[] = {"du", "-sk", "", NULL
};
609 du
->path
= g_strdup(add_row(store
, _("Size:"),
612 args
[2] = (gchar
*) path
;
613 if (g_spawn_async_with_pipes(NULL
, args
, NULL
,
615 NULL
, NULL
, &du
->child
,
619 du
->chan
= g_io_channel_unix_new(out
);
620 /* Select binary encoding so we don't get an
621 * error with non-UTF-8 filenames.
623 g_io_channel_set_encoding(du
->chan
, NULL
, NULL
);
624 du
->watch
= g_io_add_watch(du
->chan
,
625 G_IO_IN
|G_IO_ERR
|G_IO_HUP
,
626 (GIOFunc
) read_du_output
, du
);
627 g_object_ref(G_OBJECT(du
->store
));
628 g_signal_connect(G_OBJECT(view
),
630 G_CALLBACK(kill_du_output
),
635 set_cell(store
, du
->path
, _("Failed to scan"));
642 add_row_and_free(store
, _("Change time:"), pretty_time(&item
->ctime
));
644 add_row_and_free(store
, _("Modify time:"), pretty_time(&item
->mtime
));
646 add_row_and_free(store
, _("Access time:"), pretty_time(&item
->atime
));
648 add_row(store
, _("Type:"), pretty_type(item
, path
));
651 add_row(store
, "", mime_type_comment(item
->mime_type
));
653 if (xattr_supported(NULL
)) {
654 add_row(store
, _("Extended attributes:"),
655 (item
->flags
& ITEM_FLAG_HAS_XATTR
)
657 : xattr_supported(path
) ? _("None")
658 : _("Not supported"));
661 if (item
->flags
& ITEM_FLAG_SYMLINK
)
664 GtkTreeModel
*model
= GTK_TREE_MODEL(store
);
667 target
= readlink_dup(path
);
669 target
= g_strdup(g_strerror(errno
));
670 add_row_and_free(store
, _("Link target:"), target
);
672 /* Make cell editable */
673 gtk_tree_model_iter_nth_child(model
, &iter
,
674 NULL
, gtk_tree_model_iter_n_children(model
, NULL
) - 1);
676 gtk_list_store_set(store
, &iter
, 2, TRUE
, -1);
679 if (item
->base_type
!= TYPE_DIRECTORY
)
681 if (EXECUTABLE_FILE(item
))
682 add_row(store
, _("Run action:"), _("Execute file"));
685 add_row_and_free(store
, _("Run action:"),
686 describe_current_command(item
->mime_type
));
693 /* Create the TreeView widget with the application's details */
694 static GtkWidget
*make_about(const guchar
*path
, XMLwrapper
*ai
)
699 xmlNode
*about
, *about_trans
;
700 GHashTable
*translate
;
702 g_return_val_if_fail(ai
!= NULL
, NULL
);
704 about_trans
= xml_get_section(ai
, NULL
, "About");
706 about
= xmlDocGetRootElement(ai
->doc
)->xmlChildrenNode
;
707 for (; about
; about
= about
->next
)
709 if (about
->type
!= XML_ELEMENT_NODE
)
711 if (about
->ns
== NULL
&& strcmp(about
->name
, "About") == 0)
715 g_return_val_if_fail(about
!= NULL
, NULL
);
717 make_list(&store
, &view
, NULL
);
719 /* Add each field in about to the list, but overriding each element
720 * with about_trans if a translation is supplied.
722 translate
= g_hash_table_new(g_str_hash
, g_str_equal
);
723 if (about_trans
!= about
)
726 for (p
= about_trans
->xmlChildrenNode
; p
; p
= p
->next
)
728 if (p
->type
!= XML_ELEMENT_NODE
)
730 g_hash_table_insert(translate
, (char *) p
->name
, p
);
733 for (prop
= about
->xmlChildrenNode
; prop
; prop
= prop
->next
)
735 if (prop
->type
== XML_ELEMENT_NODE
)
742 trans
= g_hash_table_lookup(translate
, prop
->name
);
746 tmp
= xmlGetProp(trans
, "label");
747 label
= g_strconcat(tmp
? tmp
748 : (char *) trans
->name
,
751 value
= xmlNodeListGetString(trans
->doc
,
752 trans
->xmlChildrenNode
, 1);
754 value
= xmlNodeListGetString(prop
->doc
,
755 prop
->xmlChildrenNode
, 1);
757 value
= g_strdup("-");
758 add_row_and_free(store
, label
, value
);
763 g_hash_table_destroy(translate
);
768 /* Create the TreeView widget with the desktop entry's details */
769 static GtkWidget
*make_about_desktop(const gchar
*path
)
774 gchar
*name
=NULL
, *comment
=NULL
, *exec
=NULL
;
776 make_list(&store
, &view
, NULL
);
778 if(!get_values_from_desktop_file(path
, &error
,
779 "Desktop Entry", "Name", &name
,
780 "Desktop Entry", "Comment", &comment
,
781 "Desktop Entry", "Exec", &exec
,
785 delayed_error("%s", error
->message
);
792 add_row_and_free(store
, _("Name"), name
);
794 add_row_and_free(store
, _("Comment"), comment
);
796 add_row_and_free(store
, _("Execute"), exec
);
801 static GtkWidget
*make_file_says(const guchar
*path
)
803 GtkWidget
*w_file_label
;
804 GtkLabel
*l_file_label
;
806 char *argv
[] = {"file", "-b", NULL
, NULL
};
807 FileStatus
*fs
= NULL
;
810 w_file_label
= gtk_label_new(_("<nothing yet>"));
811 l_file_label
= GTK_LABEL(w_file_label
);
812 gtk_label_set_line_wrap(l_file_label
, TRUE
);
813 gtk_label_set_selectable(l_file_label
, TRUE
);
817 tmp
= g_strdup_printf("pipe(): %s", g_strerror(errno
));
818 gtk_label_set_text(l_file_label
, tmp
);
826 tmp
= g_strdup_printf("pipe(): %s", g_strerror(errno
));
827 gtk_label_set_text(l_file_label
, tmp
);
833 /* We are the child */
835 dup2(file_data
[1], STDOUT_FILENO
);
836 dup2(file_data
[1], STDERR_FILENO
);
838 argv
[2] = (char *) path
;
840 argv
[1] = (char *) g_basename(path
);
841 chdir(g_path_get_dirname(path
));
843 if (execvp(argv
[0], argv
))
844 fprintf(stderr
, "execvp() error: %s\n",
848 /* We are the parent */
850 fs
= g_new(FileStatus
, 1);
851 fs
->label
= l_file_label
;
852 fs
->fd
= file_data
[0];
853 fs
->text
= g_strdup("");
854 fs
->input
= gdk_input_add_full(fs
->fd
, GDK_INPUT_READ
,
855 (GdkInputFunction
) add_file_output
,
857 g_signal_connect(w_file_label
, "destroy",
858 G_CALLBACK(file_info_destroyed
), fs
);
865 /* Got some data from file(1) - stick it in the window. */
866 static void add_file_output(FileStatus
*fs
,
867 gint source
, GdkInputCondition condition
)
873 got
= read(source
, buffer
, sizeof(buffer
) - 1);
877 g_source_remove(fs
->input
);
881 delayed_error(_("file(1) says... %s"),
887 str
= g_strconcat(fs
->text
, buffer
, NULL
);
891 str
= to_utf8(fs
->text
);
893 gtk_label_set_text(fs
->label
, str
);
897 static void file_info_destroyed(GtkWidget
*widget
, FileStatus
*fs
)
901 g_source_remove(fs
->input
);
909 static void permissions_destroyed(GtkWidget
*widget
, Permissions
*perm
)
912 diritem_free(perm
->item
);
917 static void permissions_apply(GtkWidget
*widget
, Permissions
*perm
)
924 for (i
= 0; i
< 9; i
++)
926 GtkToggleButton
*bit
= GTK_TOGGLE_BUTTON(perm
->bits
[i
]);
927 if (gtk_toggle_button_get_active(bit
))
930 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(perm
->bits
[9])))
932 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(perm
->bits
[10])))
934 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(perm
->bits
[11])))
937 if (chmod(perm
->path
, nmode
))
938 report_error(_("Could not change permissions: %s"),
942 static GtkWidget
*make_permissions(const gchar
*path
, DirItem
*item
)
946 GtkWidget
*tick
, *label
;
949 perm
= g_new(Permissions
, 1);
951 perm
->path
= g_strdup(path
);
952 perm
->item
= diritem_new(path
);
954 table
= gtk_table_new(4, 5, TRUE
);
956 label
= gtk_label_new(_("Owner"));
957 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 0, 1, 1, 2);
958 label
= gtk_label_new(_("Group"));
959 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 0, 1, 2, 3);
960 label
= gtk_label_new(_("World"));
961 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 0, 1, 3, 4);
963 label
= gtk_label_new(_("Read"));
964 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
965 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 1, 2, 0, 1);
966 label
= gtk_label_new(_("Write"));
967 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
968 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 2, 3, 0, 1);
969 label
= gtk_label_new(_("Exec"));
970 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
971 gtk_table_attach_defaults(GTK_TABLE(table
), label
, 3, 4, 0, 1);
973 for (i
= 0; i
< 9; i
++)
977 perm
->bits
[i
] = tick
= gtk_check_button_new();
978 gtk_table_attach_defaults(GTK_TABLE(table
), tick
,
980 if (item
->mode
& (1 << i
))
981 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tick
),
983 g_signal_connect(tick
, "toggled",
984 G_CALLBACK(permissions_apply
), perm
);
987 tick
= gtk_check_button_new_with_label(_("SUID"));
988 gtk_table_attach_defaults(GTK_TABLE(table
), tick
, 4, 5, 1, 2);
989 if (item
->mode
& S_ISUID
)
990 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tick
), TRUE
);
991 g_signal_connect(tick
, "toggled", G_CALLBACK(permissions_apply
), perm
);
992 perm
->bits
[9] = tick
;
994 tick
= gtk_check_button_new_with_label(_("SGID"));
995 gtk_table_attach_defaults(GTK_TABLE(table
), tick
, 4, 5, 2, 3);
996 if (item
->mode
& S_ISGID
)
997 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tick
), TRUE
);
998 g_signal_connect(tick
, "toggled", G_CALLBACK(permissions_apply
), perm
);
999 perm
->bits
[10] = tick
;
1001 tick
= gtk_check_button_new_with_label(_("Sticky"));
1002 gtk_table_attach_defaults(GTK_TABLE(table
), tick
, 4, 5, 3, 4);
1003 if (item
->mode
& S_ISVTX
)
1004 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tick
), TRUE
);
1005 g_signal_connect(tick
, "toggled", G_CALLBACK(permissions_apply
), perm
);
1006 perm
->bits
[11] = tick
;
1008 g_signal_connect(table
, "destroy",
1009 G_CALLBACK(permissions_destroyed
), perm
);
1011 gtk_widget_show_all(table
);
1015 static void unmount_option_toggled(GtkToggleButton
*toggle
, const char *path
)
1017 if (gtk_toggle_button_get_active(toggle
))
1019 filer_set_unmount_action(path
,
1020 GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toggle
),
1021 "unmount_action")));
1025 static GtkWidget
*pack_unmount_radio(const char *path
,
1026 UnmountPrompt path_value
, const char *label
,
1027 UnmountPrompt btn_value
, GtkWidget
*group_owner
, GtkWidget
*hbox
)
1033 radio
= gtk_radio_button_new_with_label_from_widget(
1034 GTK_RADIO_BUTTON(group_owner
), label
);
1038 radio
= gtk_radio_button_new_with_label(NULL
, label
);
1040 if (path_value
== btn_value
)
1041 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio
), TRUE
);
1042 g_object_set_data(G_OBJECT(radio
), "unmount_action",
1043 GINT_TO_POINTER(btn_value
));
1044 g_signal_connect(radio
, "toggled", G_CALLBACK(unmount_option_toggled
),
1046 gtk_box_pack_start(GTK_BOX(hbox
), radio
, FALSE
, FALSE
, 0);
1050 static GtkWidget
*make_unmount_options(const char *path
)
1052 GtkWidget
*hbox
, *radio
;
1053 UnmountPrompt upval
= filer_get_unmount_action(path
);
1055 hbox
= gtk_hbox_new(TRUE
, 4);
1056 radio
= pack_unmount_radio(path
, upval
,
1057 _("Do nothing"), UNMOUNT_PROMPT_NO_CHANGE
, NULL
, hbox
);
1058 radio
= pack_unmount_radio(path
, upval
,
1059 _("Unmount"), UNMOUNT_PROMPT_UNMOUNT
, radio
, hbox
);
1060 radio
= pack_unmount_radio(path
, upval
,
1061 _("Eject"), UNMOUNT_PROMPT_EJECT
, radio
, hbox
);
1062 pack_unmount_radio(path
, upval
,
1063 _("Ask"), UNMOUNT_PROMPT_ASK
, radio
, hbox
);
1067 /* Don't g_free() the result */
1068 static const gchar
*pretty_type(DirItem
*file
, const guchar
*path
)
1070 static gchar
*text
= NULL
;
1074 if (file
->flags
& ITEM_FLAG_SYMLINK
)
1075 return _("Symbolic link");
1077 if (file
->flags
& ITEM_FLAG_APPDIR
)
1078 return _("ROX application");
1080 if (file
->flags
& ITEM_FLAG_MOUNT_POINT
)
1083 const gchar
*mounted
;
1085 mounted
= mount_is_mounted(path
, NULL
, NULL
)
1086 ? _("mounted") : _("unmounted");
1088 mp
= g_hash_table_lookup(fstab_mounts
, path
);
1090 text
= g_strdup_printf(_("Mount point for %s (%s)"),
1093 text
= g_strdup_printf(_("Mount point (%s)"), mounted
);
1097 if (file
->mime_type
)
1099 text
= g_strconcat(file
->mime_type
->media_type
, "/",
1100 file
->mime_type
->subtype
, NULL
);