r4475: When reading the output from 'du' for the Properties box, set the channel's
[rox-filer.git] / ROX-Filer / src / infobox.c
blob10d25bb3123e1fc8a21bdba664f46a9598185c94
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2005, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* infobox.c - code for showing a file's attributes */
24 #include "config.h"
26 #include <errno.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <sys/param.h>
30 #include <signal.h>
31 #include <libxml/parser.h>
33 #include <gtk/gtk.h>
35 #include "global.h"
37 #include "support.h"
38 #include "main.h"
39 #include "gui_support.h"
40 #include "diritem.h"
41 #include "type.h"
42 #include "infobox.h"
43 #include "appinfo.h"
44 #include "dnd.h" /* For xa_string */
45 #include "run.h" /* For show_help_files() */
46 #include "xml.h"
47 #include "mount.h"
48 #include "pixmaps.h"
49 #include "xtypes.h"
51 typedef struct _FileStatus FileStatus;
53 /* This is for the 'file(1) says...' thing */
54 struct _FileStatus
56 int fd; /* FD to read from, -1 if closed */
57 int input; /* Input watcher tag if fd valid */
58 GtkLabel *label; /* Widget to output to */
59 gchar *text; /* String so far */
62 typedef struct du {
63 gchar *path;
64 GtkListStore *store;
65 guint watch;
66 GIOChannel *chan;
67 gint child;
68 } DU;
70 typedef struct _Permissions Permissions;
72 struct _Permissions
74 gchar *path;
75 DirItem *item;
76 GtkWidget *bits[12];
79 /* Static prototypes */
80 static void refresh_info(GObject *window);
81 static GtkWidget *make_vbox(const guchar *path, GObject *window);
82 static GtkWidget *make_details(const guchar *path, DirItem *item,
83 GObject *window);
84 static GtkWidget *make_about(const guchar *path, XMLwrapper *ai);
85 static GtkWidget *make_file_says(const guchar *path);
86 static GtkWidget *make_permissions(const gchar *path, DirItem *item);
87 static void add_file_output(FileStatus *fs,
88 gint source, GdkInputCondition condition);
89 static const gchar *pretty_type(DirItem *file, const guchar *path);
90 static void got_response(GObject *window, gint response, gpointer data);
91 static void file_info_destroyed(GtkWidget *widget, FileStatus *fs);
93 /****************************************************************
94 * EXTERNAL INTERFACE *
95 ****************************************************************/
97 /* Open each item in a new infobox. Confirms if there are a large
98 * number of items to show.
100 void infobox_show_list(GList *paths)
102 int n;
104 n = g_list_length(paths);
106 if (n >= 10)
108 gchar *message;
109 gboolean ok;
111 message = g_strdup_printf(
112 _("Are you sure you want to open %d windows?"), n);
113 ok = confirm(message, GTK_STOCK_YES, _("Show Info"));
114 g_free(message);
115 if (!ok)
116 return;
119 g_list_foreach(paths, (GFunc) infobox_new, NULL);
122 /* Create and display a new info box showing details about this item */
123 void infobox_new(const gchar *pathname)
125 GtkWidget *window, *details;
126 gchar *path;
127 GObject *owindow;
129 g_return_if_fail(pathname != NULL);
131 path = g_strdup(pathname); /* Gets attached to window & freed later */
133 window = gtk_dialog_new_with_buttons(
134 g_utf8_validate(path, -1, NULL) ? path
135 : _("(bad utf-8)"),
136 NULL, GTK_DIALOG_NO_SEPARATOR,
137 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
138 GTK_STOCK_REFRESH, GTK_RESPONSE_APPLY,
139 NULL);
141 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_MOUSE);
143 owindow = G_OBJECT(window);
144 details = make_vbox(path, owindow);
145 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(window)->vbox),
146 details);
148 g_object_set_data(owindow, "details", details);
149 g_object_set_data_full(owindow, "path", path, g_free);
151 g_signal_connect(window, "response", G_CALLBACK(got_response), NULL);
153 number_of_windows++;
154 gtk_widget_show_all(window);
157 /****************************************************************
158 * INTERNAL FUNCTIONS *
159 ****************************************************************/
161 static void got_response(GObject *window, gint response, gpointer data)
163 if (response == GTK_RESPONSE_APPLY)
164 refresh_info(window);
165 else
167 gtk_widget_destroy(GTK_WIDGET(window));
168 one_less_window();
172 static void refresh_info(GObject *window)
174 GtkWidget *details, *vbox;
175 guchar *path;
177 path = g_object_get_data(window, "path");
178 details = g_object_get_data(window, "details");
179 g_return_if_fail(details != NULL);
180 g_return_if_fail(path != NULL);
182 vbox = details->parent;
183 gtk_widget_destroy(details);
185 details = make_vbox(path, window);
186 g_object_set_data(window, "details", details);
187 gtk_box_pack_start_defaults(GTK_BOX(vbox), details);
188 gtk_widget_show_all(details);
191 static void add_frame(GtkBox *vbox, GtkWidget *list)
193 GtkWidget *frame;
195 frame = gtk_frame_new(NULL);
196 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
197 gtk_container_add(GTK_CONTAINER(frame), list);
198 gtk_box_pack_start_defaults(vbox, frame);
201 /* Create the VBox widget that contains the details.
202 * Note that 'path' must not be freed until the vbox is destroyed.
204 static GtkWidget *make_vbox(const guchar *path, GObject *window)
206 DirItem *item;
207 GtkBox *vbox;
208 XMLwrapper *ai;
209 xmlNode *about = NULL;
210 gchar *help_dir;
211 GtkWidget *hbox, *name, *label;
213 g_return_val_if_fail(path[0] == '/', NULL);
215 item = diritem_new(g_basename(path));
216 diritem_restat(path, item, NULL);
218 ai = appinfo_get(path, item);
219 if (ai)
220 about = xml_get_section(ai, NULL, "About");
222 vbox = GTK_BOX(gtk_vbox_new(FALSE, 4));
223 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
225 /* Heading, with icon and name */
226 hbox = gtk_hbox_new(FALSE, 4);
227 gtk_box_pack_start(vbox, hbox, FALSE, TRUE, 0);
228 gtk_box_pack_start(GTK_BOX(hbox),
229 gtk_image_new_from_pixbuf(di_image(item)->pixbuf),
230 FALSE, FALSE, 4);
232 if (g_utf8_validate(item->leafname, -1, NULL))
233 name = gtk_label_new(item->leafname);
234 else
236 guchar *u8;
238 u8 = to_utf8(item->leafname);
239 name = gtk_label_new(u8);
240 g_free(u8);
242 gtk_label_set_selectable(GTK_LABEL(name), TRUE);
243 gtk_label_set_line_wrap(GTK_LABEL(name), TRUE);
244 gtk_box_pack_start(GTK_BOX(hbox), name, FALSE, TRUE, 4);
246 make_heading(name, PANGO_SCALE_X_LARGE);
248 /* List of file attributes */
249 add_frame(vbox, make_details(path, item, window));
251 help_dir = g_strconcat(path, "/Help", NULL);
253 if (access(help_dir, F_OK) == 0)
255 GtkWidget *button, *align;
257 align = gtk_alignment_new(0.5, 0.5, 0, 0);
259 button = button_new_mixed(GTK_STOCK_JUMP_TO,
260 _("Show _Help Files"));
261 gtk_box_pack_start(vbox, align, FALSE, TRUE, 0);
262 gtk_container_add(GTK_CONTAINER(align), button);
263 g_signal_connect_swapped(button, "clicked",
264 G_CALLBACK(show_help_files),
265 (gpointer) path);
267 g_free(help_dir);
269 if (!(item->flags & ITEM_FLAG_SYMLINK))
271 label = gtk_label_new(NULL);
272 gtk_label_set_markup(GTK_LABEL(label),
273 _("<b>Permissions</b>"));
274 gtk_misc_set_alignment(GTK_MISC(label), 0, 1);
275 gtk_box_pack_start(vbox, label, FALSE, TRUE, 2);
277 gtk_box_pack_start(vbox, make_permissions(path, item),
278 FALSE, TRUE, 0);
281 if (about)
282 add_frame(vbox, make_about(path, ai));
283 else if (item->base_type == TYPE_FILE)
285 label = gtk_label_new(NULL);
286 gtk_label_set_markup(GTK_LABEL(label),
287 _("<b>Contents indicate...</b>"));
288 gtk_misc_set_alignment(GTK_MISC(label), 0, 1);
289 gtk_box_pack_start(vbox, label, FALSE, TRUE, 2);
291 gtk_box_pack_start_defaults(vbox, make_file_says(path));
294 if (ai)
295 g_object_unref(ai);
297 diritem_free(item);
299 return (GtkWidget *) vbox;
302 /* The selection has changed - grab or release the primary selection */
303 static void set_selection(GtkTreeView *view, gpointer data)
305 static GtkClipboard *primary = NULL;
306 GtkTreeModel *model;
307 GtkTreePath *path = NULL;
308 GtkTreeIter iter;
309 gchar *text;
311 gtk_tree_view_get_cursor(view, &path, NULL);
312 if (!path)
313 return;
315 if (!primary)
316 primary = gtk_clipboard_get(gdk_atom_intern("PRIMARY", FALSE));
318 model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
320 gtk_tree_model_get_iter(model, &iter, path);
321 gtk_tree_path_free(path);
323 gtk_tree_model_get(model, &iter, 1, &text, -1);
325 gtk_clipboard_set_text(primary, text, -1);
327 g_free(text);
330 /* Returns a GtkTreePath for the item */
331 static const gchar *add_row(GtkListStore *store, const gchar *label,
332 const gchar *data)
334 GtkTreeIter iter;
335 gchar *u8 = NULL;
336 GtkTreePath *tpath;
337 static gchar *last = NULL;
339 if (!g_utf8_validate(data, -1, NULL))
340 u8 = to_utf8(data);
342 gtk_list_store_append(store, &iter);
343 gtk_list_store_set(store, &iter, 0, label, 1, u8 ? u8 : data, -1);
345 g_free(u8);
347 tpath = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
348 if (last)
349 g_free(last);
350 last = gtk_tree_path_to_string(tpath);
351 gtk_tree_path_free(tpath);
353 return last;
356 static void add_row_and_free(GtkListStore *store,
357 const gchar *label, gchar *data)
359 add_row(store, label, data);
360 g_free(data);
363 /* Create an empty list view, ready to place some data in */
364 static void make_list(GtkListStore **list_store, GtkWidget **list_view,
365 GCallback cell_edited)
367 GtkListStore *store;
368 GtkTreeView *view;
369 GtkCellRenderer *cell_renderer;
371 /* Field name, value, editable */
372 store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_STRING,
373 G_TYPE_BOOLEAN);
374 view = GTK_TREE_VIEW(
375 gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)));
376 g_object_unref(G_OBJECT(store));
377 gtk_tree_view_set_headers_visible(view, FALSE);
379 cell_renderer = gtk_cell_renderer_text_new();
380 g_object_set(G_OBJECT(cell_renderer), "xalign", 1.0, NULL);
381 gtk_tree_view_insert_column_with_attributes(view,
382 0, NULL, cell_renderer, "text", 0, NULL);
384 cell_renderer = gtk_cell_renderer_text_new();
385 gtk_tree_view_insert_column_with_attributes(view,
386 1, NULL, cell_renderer, "text", 1, "editable", 2, NULL);
388 if (cell_edited) {
389 g_signal_connect(G_OBJECT(cell_renderer), "edited",
390 G_CALLBACK(cell_edited), store);
393 g_signal_connect(view, "cursor_changed",
394 G_CALLBACK(set_selection), NULL);
396 *list_store = store;
397 *list_view = (GtkWidget *) view;
400 static void set_cell(GtkListStore *store, const gchar *path,
401 const gchar *ctext)
403 GtkTreeIter iter;
405 gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(store),
406 &iter, path);
407 gtk_list_store_set(store, &iter, 1, ctext, -1);
410 static void insert_size(DU *du, const char *line)
412 off_t size;
413 gchar *cell;
415 #ifdef LARGE_FILE_SUPPORT
416 size = strtoll(line, NULL, 10);
417 #else
418 size = strtol(line, NULL, 10);
419 #endif
420 size <<= 10; /* Because du reports in K */
421 cell = (size >= PRETTY_SIZE_LIMIT)
422 ? g_strdup_printf("%s (%" SIZE_FMT " %s)",
423 format_size(size),
424 size, _("bytes"))
425 : g_strdup(format_size(size));
427 set_cell(du->store, du->path, cell);
429 g_free(cell);
432 static gboolean read_du_output(GIOChannel *source, GIOCondition cond, DU *du)
434 GString *line;
435 GIOStatus stat;
436 GError *err = NULL;
438 line = g_string_new("");
439 stat = g_io_channel_read_line_string(source, line, NULL, &err);
440 switch (stat)
442 case G_IO_STATUS_NORMAL:
443 insert_size(du, line->str);
444 break;
445 case G_IO_STATUS_EOF:
446 set_cell(du->store, du->path,
447 _("Failed to read size"));
448 break;
449 case G_IO_STATUS_AGAIN:
450 g_string_free(line, TRUE);
451 return TRUE;
452 case G_IO_STATUS_ERROR:
453 set_cell(du->store, du->path, err->message);
454 break;
456 g_string_free(line, TRUE);
458 return FALSE;
461 static void kill_du_output(GtkWidget *widget, DU *du)
463 g_source_remove(du->watch);
464 g_io_channel_shutdown(du->chan, FALSE, NULL);
465 g_io_channel_unref(du->chan);
466 kill((pid_t) du->child, SIGTERM);
467 g_object_unref(G_OBJECT(du->store));
468 g_free(du->path);
469 g_free(du);
472 static gboolean refresh_info_idle(gpointer data)
474 GObject *window = G_OBJECT(data);
476 refresh_info(window);
477 g_object_unref(window);
478 return FALSE;
481 static void cell_edited(GtkCellRendererText *cell,
482 const gchar *path_string,
483 const gchar *new_text,
484 gpointer data)
486 GtkTreeModel *model = (GtkTreeModel *) data;
487 GtkTreePath *path;
488 GtkTreeIter iter;
489 GObject *window;
490 const char *fullpath;
491 char *oldlink;
493 window = g_object_get_data(G_OBJECT(model), "rox_window");
494 g_return_if_fail(window != NULL);
496 fullpath = g_object_get_data(window, "path");
497 g_return_if_fail(fullpath != NULL);
499 path = gtk_tree_path_new_from_string(path_string);
500 gtk_tree_model_get_iter(model, &iter, path);
501 gtk_tree_path_free(path);
503 oldlink = readlink_dup(fullpath);
504 if (!oldlink) {
505 /* Must use delayed_error(), as this can be called
506 * from a focus-out event (causes a crash).
508 delayed_error(_("'%s' is no longer a symlink"), fullpath);
509 return;
511 if (strcmp(oldlink, new_text) == 0)
512 return; /* No change */
513 g_free(oldlink);
514 if (unlink(fullpath)) {
515 delayed_error(_("Failed to unlink '%s':\n%s"),
516 fullpath, g_strerror(errno));
517 return;
519 if (symlink(new_text, fullpath)) {
520 delayed_error(_("Failed to create symlink from '%s':\n%s\n"
521 "(note: old link has been deleted)"),
522 fullpath, g_strerror(errno));
523 return;
526 g_object_ref(window);
527 g_idle_add(refresh_info_idle, window);
530 /* Create the TreeView widget with the file's details */
531 static GtkWidget *make_details(const guchar *path, DirItem *item,
532 GObject *window)
534 GtkListStore *store;
535 GtkWidget *view;
536 gchar *tmp, *tmp2;
538 make_list(&store, &view, G_CALLBACK(cell_edited));
539 g_object_set_data(G_OBJECT(store), "rox_window", window);
541 /* For a symlink to an error, don't show the error */
542 if (item->base_type == TYPE_ERROR && item->lstat_errno)
544 add_row(store, _("Error:"), g_strerror(item->lstat_errno));
545 return view;
548 tmp = g_path_get_dirname(path);
549 tmp2 = pathdup(tmp);
550 if (strcmp(tmp, tmp2) != 0)
551 add_row_and_free(store, _("Real directory:"), tmp2);
552 g_free(tmp);
554 add_row_and_free(store, _("Owner, Group:"),
555 g_strdup_printf("%s, %s",
556 user_name(item->uid),
557 group_name(item->gid)));
559 if (item->base_type != TYPE_DIRECTORY)
561 add_row_and_free(store, _("Size:"),
562 item->size >= PRETTY_SIZE_LIMIT
563 ? g_strdup_printf("%s (%" SIZE_FMT " %s)",
564 format_size(item->size),
565 item->size, _("bytes"))
566 : g_strdup(format_size(item->size)));
568 else
570 gchar *stt=NULL;
572 if(item->flags & ITEM_FLAG_MOUNTED)
573 stt=mount_get_fs_size(path);
575 if(stt) {
576 add_row_and_free(store, _("Size:"), stt);
577 } else {
578 DU *du;
579 int out;
581 gchar *args[] = {"du", "-sk", "", NULL};
583 du = g_new(DU, 1);
584 du->store = store;
585 du->path = g_strdup(add_row(store, _("Size:"),
586 _("Scanning")));
588 args[2] = (gchar *) path;
589 if (g_spawn_async_with_pipes(NULL, args, NULL,
590 G_SPAWN_SEARCH_PATH,
591 NULL, NULL, &du->child,
592 NULL, &out, NULL,
593 NULL))
595 du->chan = g_io_channel_unix_new(out);
596 /* Select binary encoding so we don't get an
597 * error with non-UTF-8 filenames.
599 g_io_channel_set_encoding(du->chan, NULL, NULL);
600 du->watch = g_io_add_watch(du->chan,
601 G_IO_IN|G_IO_ERR|G_IO_HUP,
602 (GIOFunc) read_du_output, du);
603 g_object_ref(G_OBJECT(du->store));
604 g_signal_connect(G_OBJECT(view),
605 "destroy",
606 G_CALLBACK(kill_du_output),
607 du);
609 else
611 set_cell(store, du->path, _("Failed to scan"));
612 g_free(du->path);
613 g_free(du);
618 add_row_and_free(store, _("Change time:"), pretty_time(&item->ctime));
620 add_row_and_free(store, _("Modify time:"), pretty_time(&item->mtime));
622 add_row_and_free(store, _("Access time:"), pretty_time(&item->atime));
624 add_row(store, _("Type:"), pretty_type(item, path));
626 if (item->mime_type)
627 add_row(store, "", mime_type_comment(item->mime_type));
629 if (xattr_supported(NULL)) {
630 add_row(store, _("Extended attributes:"),
631 (item->flags & ITEM_FLAG_HAS_XATTR)
632 ? _("Present")
633 : xattr_supported(path) ? _("None")
634 : _("Not supported"));
637 if (item->flags & ITEM_FLAG_SYMLINK)
639 GtkTreeIter iter;
640 GtkTreeModel *model = GTK_TREE_MODEL(store);
641 char *target;
643 target = readlink_dup(path);
644 if (!target)
645 target = g_strdup(g_strerror(errno));
646 add_row_and_free(store, _("Link target:"), target);
648 /* Make cell editable */
649 gtk_tree_model_iter_nth_child(model, &iter,
650 NULL, gtk_tree_model_iter_n_children(model, NULL) - 1);
652 gtk_list_store_set(store, &iter, 2, TRUE, -1);
655 if (item->base_type != TYPE_DIRECTORY)
657 if (EXECUTABLE_FILE(item))
658 add_row(store, _("Run action:"), _("Execute file"));
659 else
661 add_row_and_free(store, _("Run action:"),
662 describe_current_command(item->mime_type));
666 return view;
669 /* Create the TreeView widget with the application's details */
670 static GtkWidget *make_about(const guchar *path, XMLwrapper *ai)
672 GtkListStore *store;
673 GtkWidget *view;
674 xmlNode *prop;
675 xmlNode *about, *about_trans;
676 GHashTable *translate;
678 g_return_val_if_fail(ai != NULL, NULL);
680 about_trans = xml_get_section(ai, NULL, "About");
682 about = xmlDocGetRootElement(ai->doc)->xmlChildrenNode;
683 for (; about; about = about->next)
685 if (about->type != XML_ELEMENT_NODE)
686 continue;
687 if (about->ns == NULL && strcmp(about->name, "About") == 0)
688 break;
691 g_return_val_if_fail(about != NULL, NULL);
693 make_list(&store, &view, NULL);
695 /* Add each field in about to the list, but overriding each element
696 * with about_trans if a translation is supplied.
698 translate = g_hash_table_new(g_str_hash, g_str_equal);
699 if (about_trans != about)
701 xmlNode *p;
702 for (p = about_trans->xmlChildrenNode; p; p = p->next)
704 if (p->type != XML_ELEMENT_NODE)
705 continue;
706 g_hash_table_insert(translate, (char *) p->name, p);
709 for (prop = about->xmlChildrenNode; prop; prop = prop->next)
711 if (prop->type == XML_ELEMENT_NODE)
713 char *label = NULL;
714 char *value = NULL;
715 char *tmp = NULL;
716 xmlNode *trans;
718 trans = g_hash_table_lookup(translate, prop->name);
719 if (!trans)
720 trans = prop;
722 tmp = xmlGetProp(trans, "label");
723 label = g_strconcat(tmp ? tmp
724 : (char *) trans->name,
725 ":", NULL);
726 g_free(tmp);
727 value = xmlNodeListGetString(trans->doc,
728 trans->xmlChildrenNode, 1);
729 if (!value)
730 value = xmlNodeListGetString(prop->doc,
731 prop->xmlChildrenNode, 1);
732 if (!value)
733 value = g_strdup("-");
734 add_row_and_free(store, label, value);
735 g_free(label);
739 g_hash_table_destroy(translate);
741 return view;
744 static GtkWidget *make_file_says(const guchar *path)
746 GtkWidget *w_file_label;
747 GtkLabel *l_file_label;
748 int file_data[2];
749 char *argv[] = {"file", "-b", NULL, NULL};
750 FileStatus *fs = NULL;
751 guchar *tmp;
753 w_file_label = gtk_label_new(_("<nothing yet>"));
754 l_file_label = GTK_LABEL(w_file_label);
755 gtk_label_set_line_wrap(l_file_label, TRUE);
757 if (pipe(file_data))
759 tmp = g_strdup_printf("pipe(): %s", g_strerror(errno));
760 gtk_label_set_text(l_file_label, tmp);
761 g_free(tmp);
762 return w_file_label;
765 switch (fork())
767 case -1:
768 tmp = g_strdup_printf("pipe(): %s", g_strerror(errno));
769 gtk_label_set_text(l_file_label, tmp);
770 g_free(tmp);
771 close(file_data[0]);
772 close(file_data[1]);
773 break;
774 case 0:
775 /* We are the child */
776 close(file_data[0]);
777 dup2(file_data[1], STDOUT_FILENO);
778 dup2(file_data[1], STDERR_FILENO);
779 #ifdef FILE_B_FLAG
780 argv[2] = (char *) path;
781 #else
782 argv[1] = (char *) g_basename(path);
783 chdir(g_path_get_dirname(path));
784 #endif
785 if (execvp(argv[0], argv))
786 fprintf(stderr, "execvp() error: %s\n",
787 g_strerror(errno));
788 _exit(0);
789 default:
790 /* We are the parent */
791 close(file_data[1]);
792 fs = g_new(FileStatus, 1);
793 fs->label = l_file_label;
794 fs->fd = file_data[0];
795 fs->text = g_strdup("");
796 fs->input = gdk_input_add_full(fs->fd, GDK_INPUT_READ,
797 (GdkInputFunction) add_file_output,
798 fs, NULL);
799 g_signal_connect(w_file_label, "destroy",
800 G_CALLBACK(file_info_destroyed), fs);
801 break;
804 return w_file_label;
807 /* Got some data from file(1) - stick it in the window. */
808 static void add_file_output(FileStatus *fs,
809 gint source, GdkInputCondition condition)
811 char buffer[20];
812 char *str;
813 int got;
815 got = read(source, buffer, sizeof(buffer) - 1);
816 if (got <= 0)
818 int err = errno;
819 g_source_remove(fs->input);
820 close(source);
821 fs->fd = -1;
822 if (got < 0)
823 delayed_error(_("file(1) says... %s"),
824 g_strerror(err));
825 return;
827 buffer[got] = '\0';
829 str = g_strconcat(fs->text, buffer, NULL);
830 g_free(fs->text);
831 fs->text = str;
833 str = to_utf8(fs->text);
834 g_strstrip(str);
835 gtk_label_set_text(fs->label, str);
836 g_free(str);
839 static void file_info_destroyed(GtkWidget *widget, FileStatus *fs)
841 if (fs->fd != -1)
843 g_source_remove(fs->input);
844 close(fs->fd);
847 g_free(fs->text);
848 g_free(fs);
851 static void permissions_destroyed(GtkWidget *widget, Permissions *perm)
853 g_free(perm->path);
854 diritem_free(perm->item);
856 g_free(perm);
859 static void permissions_apply(GtkWidget *widget, Permissions *perm)
861 mode_t nmode;
862 int i;
864 nmode=0;
866 for (i = 0; i < 9; i++)
868 GtkToggleButton *bit = GTK_TOGGLE_BUTTON(perm->bits[i]);
869 if (gtk_toggle_button_get_active(bit))
870 nmode |= 1 << i;
872 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(perm->bits[9])))
873 nmode |= S_ISUID;
874 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(perm->bits[10])))
875 nmode |= S_ISGID;
876 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(perm->bits[11])))
877 nmode |= S_ISVTX;
879 if (chmod(perm->path, nmode))
880 report_error(_("Could not change permissions: %s"),
881 g_strerror(errno));
884 static GtkWidget *make_permissions(const gchar *path, DirItem *item)
886 Permissions *perm;
887 GtkWidget *table;
888 GtkWidget *tick, *label;
889 int i, x, y;
891 perm = g_new(Permissions, 1);
893 perm->path = g_strdup(path);
894 perm->item = diritem_new(path);
896 table = gtk_table_new(4, 5, TRUE);
898 label = gtk_label_new(_("Owner"));
899 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
900 label = gtk_label_new(_("Group"));
901 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3);
902 label = gtk_label_new(_("World"));
903 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4);
905 label = gtk_label_new(_("Read"));
906 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 0, 1);
907 label = gtk_label_new(_("Write"));
908 gtk_table_attach_defaults(GTK_TABLE(table), label, 2, 3, 0, 1);
909 label = gtk_label_new(_("Exec"));
910 gtk_table_attach_defaults(GTK_TABLE(table), label, 3, 4, 0, 1);
912 for (i = 0; i < 9; i++)
914 x = 1 + 2 - i % 3;
915 y = 1 + 2 - i / 3;
916 perm->bits[i] = tick = gtk_check_button_new();
917 gtk_table_attach_defaults(GTK_TABLE(table), tick,
918 x, x + 1, y, y + 1);
919 if (item->mode & (1 << i))
920 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tick),
921 TRUE);
922 g_signal_connect(tick, "toggled",
923 G_CALLBACK(permissions_apply), perm);
926 tick = gtk_check_button_new_with_label(_("SUID"));
927 gtk_table_attach_defaults(GTK_TABLE(table), tick, 4, 5, 1, 2);
928 if (item->mode & S_ISUID)
929 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tick), TRUE);
930 g_signal_connect(tick, "toggled", G_CALLBACK(permissions_apply), perm);
931 perm->bits[9] = tick;
933 tick = gtk_check_button_new_with_label(_("SGID"));
934 gtk_table_attach_defaults(GTK_TABLE(table), tick, 4, 5, 2, 3);
935 if (item->mode & S_ISGID)
936 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tick), TRUE);
937 g_signal_connect(tick, "toggled", G_CALLBACK(permissions_apply), perm);
938 perm->bits[10] = tick;
940 tick = gtk_check_button_new_with_label(_("Sticky"));
941 gtk_table_attach_defaults(GTK_TABLE(table), tick, 4, 5, 3, 4);
942 if (item->mode & S_ISVTX)
943 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tick), TRUE);
944 g_signal_connect(tick, "toggled", G_CALLBACK(permissions_apply), perm);
945 perm->bits[11] = tick;
947 g_signal_connect(table, "destroy",
948 G_CALLBACK(permissions_destroyed), perm);
950 gtk_widget_show_all(table);
951 return table;
954 /* Don't g_free() the result */
955 static const gchar *pretty_type(DirItem *file, const guchar *path)
957 static gchar *text = NULL;
959 null_g_free(&text);
961 if (file->flags & ITEM_FLAG_SYMLINK)
962 return _("Symbolic link");
964 if (file->flags & ITEM_FLAG_APPDIR)
965 return _("ROX application");
967 if (file->flags & ITEM_FLAG_MOUNT_POINT)
969 MountPoint *mp;
970 const gchar *mounted;
972 mounted = mount_is_mounted(path, NULL, NULL)
973 ? _("mounted") : _("unmounted");
975 mp = g_hash_table_lookup(fstab_mounts, path);
976 if (mp)
977 text = g_strdup_printf(_("Mount point for %s (%s)"),
978 mp->name, mounted);
979 else
980 text = g_strdup_printf(_("Mount point (%s)"), mounted);
981 return text;
984 if (file->mime_type)
986 text = g_strconcat(file->mime_type->media_type, "/",
987 file->mime_type->subtype, NULL);
988 return text;
991 return "-";