r4523: Updated headers:
[rox-filer/dt.git] / ROX-Filer / src / infobox.c
blobe63d4126e45f97d0d216487f3aa626deea1b9aeb
1 /*
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)
8 * any later version.
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
13 * more details.
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 */
22 #include "config.h"
24 #include <errno.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <sys/param.h>
28 #include <signal.h>
29 #include <libxml/parser.h>
31 #include <gtk/gtk.h>
33 #include "global.h"
35 #include "support.h"
36 #include "main.h"
37 #include "gui_support.h"
38 #include "diritem.h"
39 #include "type.h"
40 #include "infobox.h"
41 #include "appinfo.h"
42 #include "dnd.h" /* For xa_string */
43 #include "run.h" /* For show_help_files() */
44 #include "xml.h"
45 #include "mount.h"
46 #include "pixmaps.h"
47 #include "xtypes.h"
49 typedef struct _FileStatus FileStatus;
51 /* This is for the 'file(1) says...' thing */
52 struct _FileStatus
54 int fd; /* FD to read from, -1 if closed */
55 int input; /* Input watcher tag if fd valid */
56 GtkLabel *label; /* Widget to output to */
57 gchar *text; /* String so far */
60 typedef struct du {
61 gchar *path;
62 GtkListStore *store;
63 guint watch;
64 GIOChannel *chan;
65 gint child;
66 } DU;
68 typedef struct _Permissions Permissions;
70 struct _Permissions
72 gchar *path;
73 DirItem *item;
74 GtkWidget *bits[12];
77 /* Static prototypes */
78 static void refresh_info(GObject *window);
79 static GtkWidget *make_vbox(const guchar *path, GObject *window);
80 static GtkWidget *make_details(const guchar *path, DirItem *item,
81 GObject *window);
82 static GtkWidget *make_about(const guchar *path, XMLwrapper *ai);
83 static GtkWidget *make_file_says(const guchar *path);
84 static GtkWidget *make_permissions(const gchar *path, DirItem *item);
85 static void add_file_output(FileStatus *fs,
86 gint source, GdkInputCondition condition);
87 static const gchar *pretty_type(DirItem *file, const guchar *path);
88 static void got_response(GObject *window, gint response, gpointer data);
89 static void file_info_destroyed(GtkWidget *widget, FileStatus *fs);
91 /****************************************************************
92 * EXTERNAL INTERFACE *
93 ****************************************************************/
95 /* Open each item in a new infobox. Confirms if there are a large
96 * number of items to show.
98 void infobox_show_list(GList *paths)
100 int n;
102 n = g_list_length(paths);
104 if (n >= 10)
106 gchar *message;
107 gboolean ok;
109 message = g_strdup_printf(
110 _("Are you sure you want to open %d windows?"), n);
111 ok = confirm(message, GTK_STOCK_YES, _("Show Info"));
112 g_free(message);
113 if (!ok)
114 return;
117 g_list_foreach(paths, (GFunc) infobox_new, NULL);
120 /* Create and display a new info box showing details about this item */
121 void infobox_new(const gchar *pathname)
123 GtkWidget *window, *details;
124 gchar *path;
125 GObject *owindow;
127 g_return_if_fail(pathname != NULL);
129 path = g_strdup(pathname); /* Gets attached to window & freed later */
131 window = gtk_dialog_new_with_buttons(
132 g_utf8_validate(path, -1, NULL) ? path
133 : _("(bad utf-8)"),
134 NULL, GTK_DIALOG_NO_SEPARATOR,
135 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
136 GTK_STOCK_REFRESH, GTK_RESPONSE_APPLY,
137 NULL);
139 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_MOUSE);
141 owindow = G_OBJECT(window);
142 details = make_vbox(path, owindow);
143 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(window)->vbox),
144 details);
146 g_object_set_data(owindow, "details", details);
147 g_object_set_data_full(owindow, "path", path, g_free);
149 g_signal_connect(window, "response", G_CALLBACK(got_response), NULL);
151 number_of_windows++;
152 gtk_widget_show_all(window);
155 /****************************************************************
156 * INTERNAL FUNCTIONS *
157 ****************************************************************/
159 static void got_response(GObject *window, gint response, gpointer data)
161 if (response == GTK_RESPONSE_APPLY)
162 refresh_info(window);
163 else
165 gtk_widget_destroy(GTK_WIDGET(window));
166 one_less_window();
170 static void refresh_info(GObject *window)
172 GtkWidget *details, *vbox;
173 guchar *path;
175 path = g_object_get_data(window, "path");
176 details = g_object_get_data(window, "details");
177 g_return_if_fail(details != NULL);
178 g_return_if_fail(path != NULL);
180 vbox = details->parent;
181 gtk_widget_destroy(details);
183 details = make_vbox(path, window);
184 g_object_set_data(window, "details", details);
185 gtk_box_pack_start_defaults(GTK_BOX(vbox), details);
186 gtk_widget_show_all(details);
189 static void add_frame(GtkBox *vbox, GtkWidget *list)
191 GtkWidget *frame;
193 frame = gtk_frame_new(NULL);
194 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
195 gtk_container_add(GTK_CONTAINER(frame), list);
196 gtk_box_pack_start_defaults(vbox, frame);
199 /* Create the VBox widget that contains the details.
200 * Note that 'path' must not be freed until the vbox is destroyed.
202 static GtkWidget *make_vbox(const guchar *path, GObject *window)
204 DirItem *item;
205 GtkBox *vbox;
206 XMLwrapper *ai;
207 xmlNode *about = NULL;
208 gchar *help_dir;
209 GtkWidget *hbox, *name, *label;
211 g_return_val_if_fail(path[0] == '/', NULL);
213 item = diritem_new(g_basename(path));
214 diritem_restat(path, item, NULL);
216 ai = appinfo_get(path, item);
217 if (ai)
218 about = xml_get_section(ai, NULL, "About");
220 vbox = GTK_BOX(gtk_vbox_new(FALSE, 4));
221 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
223 /* Heading, with icon and name */
224 hbox = gtk_hbox_new(FALSE, 4);
225 gtk_box_pack_start(vbox, hbox, FALSE, TRUE, 0);
226 gtk_box_pack_start(GTK_BOX(hbox),
227 gtk_image_new_from_pixbuf(di_image(item)->pixbuf),
228 FALSE, FALSE, 4);
230 if (g_utf8_validate(item->leafname, -1, NULL))
231 name = gtk_label_new(item->leafname);
232 else
234 guchar *u8;
236 u8 = to_utf8(item->leafname);
237 name = gtk_label_new(u8);
238 g_free(u8);
240 gtk_label_set_selectable(GTK_LABEL(name), TRUE);
241 gtk_label_set_line_wrap(GTK_LABEL(name), TRUE);
242 gtk_box_pack_start(GTK_BOX(hbox), name, FALSE, TRUE, 4);
244 make_heading(name, PANGO_SCALE_X_LARGE);
246 /* List of file attributes */
247 add_frame(vbox, make_details(path, item, window));
249 help_dir = g_strconcat(path, "/Help", NULL);
251 if (access(help_dir, F_OK) == 0)
253 GtkWidget *button, *align;
255 align = gtk_alignment_new(0.5, 0.5, 0, 0);
257 button = button_new_mixed(GTK_STOCK_JUMP_TO,
258 _("Show _Help Files"));
259 gtk_box_pack_start(vbox, align, FALSE, TRUE, 0);
260 gtk_container_add(GTK_CONTAINER(align), button);
261 g_signal_connect_swapped(button, "clicked",
262 G_CALLBACK(show_help_files),
263 (gpointer) path);
265 g_free(help_dir);
267 if (!(item->flags & ITEM_FLAG_SYMLINK))
269 label = gtk_label_new(NULL);
270 gtk_label_set_markup(GTK_LABEL(label),
271 _("<b>Permissions</b>"));
272 gtk_misc_set_alignment(GTK_MISC(label), 0, 1);
273 gtk_box_pack_start(vbox, label, FALSE, TRUE, 2);
275 gtk_box_pack_start(vbox, make_permissions(path, item),
276 FALSE, TRUE, 0);
279 if (about)
280 add_frame(vbox, make_about(path, ai));
281 else if (item->base_type == TYPE_FILE)
283 label = gtk_label_new(NULL);
284 gtk_label_set_markup(GTK_LABEL(label),
285 _("<b>Contents indicate...</b>"));
286 gtk_misc_set_alignment(GTK_MISC(label), 0, 1);
287 gtk_box_pack_start(vbox, label, FALSE, TRUE, 2);
289 gtk_box_pack_start_defaults(vbox, make_file_says(path));
292 if (ai)
293 g_object_unref(ai);
295 diritem_free(item);
297 return (GtkWidget *) vbox;
300 /* The selection has changed - grab or release the primary selection */
301 static void set_selection(GtkTreeView *view, gpointer data)
303 static GtkClipboard *primary = NULL;
304 GtkTreeModel *model;
305 GtkTreePath *path = NULL;
306 GtkTreeIter iter;
307 gchar *text;
309 gtk_tree_view_get_cursor(view, &path, NULL);
310 if (!path)
311 return;
313 if (!primary)
314 primary = gtk_clipboard_get(gdk_atom_intern("PRIMARY", FALSE));
316 model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
318 gtk_tree_model_get_iter(model, &iter, path);
319 gtk_tree_path_free(path);
321 gtk_tree_model_get(model, &iter, 1, &text, -1);
323 gtk_clipboard_set_text(primary, text, -1);
325 g_free(text);
328 /* Returns a GtkTreePath for the item */
329 static const gchar *add_row(GtkListStore *store, const gchar *label,
330 const gchar *data)
332 GtkTreeIter iter;
333 gchar *u8 = NULL;
334 GtkTreePath *tpath;
335 static gchar *last = NULL;
337 if (!g_utf8_validate(data, -1, NULL))
338 u8 = to_utf8(data);
340 gtk_list_store_append(store, &iter);
341 gtk_list_store_set(store, &iter, 0, label, 1, u8 ? u8 : data, -1);
343 g_free(u8);
345 tpath = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
346 if (last)
347 g_free(last);
348 last = gtk_tree_path_to_string(tpath);
349 gtk_tree_path_free(tpath);
351 return last;
354 static void add_row_and_free(GtkListStore *store,
355 const gchar *label, gchar *data)
357 add_row(store, label, data);
358 g_free(data);
361 /* Create an empty list view, ready to place some data in */
362 static void make_list(GtkListStore **list_store, GtkWidget **list_view,
363 GCallback cell_edited)
365 GtkListStore *store;
366 GtkTreeView *view;
367 GtkCellRenderer *cell_renderer;
369 /* Field name, value, editable */
370 store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_STRING,
371 G_TYPE_BOOLEAN);
372 view = GTK_TREE_VIEW(
373 gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)));
374 g_object_unref(G_OBJECT(store));
375 gtk_tree_view_set_headers_visible(view, FALSE);
377 cell_renderer = gtk_cell_renderer_text_new();
378 g_object_set(G_OBJECT(cell_renderer), "xalign", 1.0, NULL);
379 gtk_tree_view_insert_column_with_attributes(view,
380 0, NULL, cell_renderer, "text", 0, NULL);
382 cell_renderer = gtk_cell_renderer_text_new();
383 gtk_tree_view_insert_column_with_attributes(view,
384 1, NULL, cell_renderer, "text", 1, "editable", 2, NULL);
386 if (cell_edited) {
387 g_signal_connect(G_OBJECT(cell_renderer), "edited",
388 G_CALLBACK(cell_edited), store);
391 g_signal_connect(view, "cursor_changed",
392 G_CALLBACK(set_selection), NULL);
394 *list_store = store;
395 *list_view = (GtkWidget *) view;
398 static void set_cell(GtkListStore *store, const gchar *path,
399 const gchar *ctext)
401 GtkTreeIter iter;
403 gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(store),
404 &iter, path);
405 gtk_list_store_set(store, &iter, 1, ctext, -1);
408 static void insert_size(DU *du, const char *line)
410 off_t size;
411 gchar *cell;
413 #ifdef LARGE_FILE_SUPPORT
414 size = strtoll(line, NULL, 10);
415 #else
416 size = strtol(line, NULL, 10);
417 #endif
418 size <<= 10; /* Because du reports in K */
419 cell = (size >= PRETTY_SIZE_LIMIT)
420 ? g_strdup_printf("%s (%" SIZE_FMT " %s)",
421 format_size(size),
422 size, _("bytes"))
423 : g_strdup(format_size(size));
425 set_cell(du->store, du->path, cell);
427 g_free(cell);
430 static gboolean read_du_output(GIOChannel *source, GIOCondition cond, DU *du)
432 GString *line;
433 GIOStatus stat;
434 GError *err = NULL;
436 line = g_string_new("");
437 stat = g_io_channel_read_line_string(source, line, NULL, &err);
438 switch (stat)
440 case G_IO_STATUS_NORMAL:
441 insert_size(du, line->str);
442 break;
443 case G_IO_STATUS_EOF:
444 set_cell(du->store, du->path,
445 _("Failed to read size"));
446 break;
447 case G_IO_STATUS_AGAIN:
448 g_string_free(line, TRUE);
449 return TRUE;
450 case G_IO_STATUS_ERROR:
451 set_cell(du->store, du->path, err->message);
452 break;
454 g_string_free(line, TRUE);
456 return FALSE;
459 static void kill_du_output(GtkWidget *widget, DU *du)
461 g_source_remove(du->watch);
462 g_io_channel_shutdown(du->chan, FALSE, NULL);
463 g_io_channel_unref(du->chan);
464 kill((pid_t) du->child, SIGTERM);
465 g_object_unref(G_OBJECT(du->store));
466 g_free(du->path);
467 g_free(du);
470 static gboolean refresh_info_idle(gpointer data)
472 GObject *window = G_OBJECT(data);
474 refresh_info(window);
475 g_object_unref(window);
476 return FALSE;
479 static void cell_edited(GtkCellRendererText *cell,
480 const gchar *path_string,
481 const gchar *new_text,
482 gpointer data)
484 GtkTreeModel *model = (GtkTreeModel *) data;
485 GtkTreePath *path;
486 GtkTreeIter iter;
487 GObject *window;
488 const char *fullpath;
489 char *oldlink;
491 window = g_object_get_data(G_OBJECT(model), "rox_window");
492 g_return_if_fail(window != NULL);
494 fullpath = g_object_get_data(window, "path");
495 g_return_if_fail(fullpath != NULL);
497 path = gtk_tree_path_new_from_string(path_string);
498 gtk_tree_model_get_iter(model, &iter, path);
499 gtk_tree_path_free(path);
501 oldlink = readlink_dup(fullpath);
502 if (!oldlink) {
503 /* Must use delayed_error(), as this can be called
504 * from a focus-out event (causes a crash).
506 delayed_error(_("'%s' is no longer a symlink"), fullpath);
507 return;
509 if (strcmp(oldlink, new_text) == 0)
510 return; /* No change */
511 g_free(oldlink);
512 if (unlink(fullpath)) {
513 delayed_error(_("Failed to unlink '%s':\n%s"),
514 fullpath, g_strerror(errno));
515 return;
517 if (symlink(new_text, fullpath)) {
518 delayed_error(_("Failed to create symlink from '%s':\n%s\n"
519 "(note: old link has been deleted)"),
520 fullpath, g_strerror(errno));
521 return;
524 g_object_ref(window);
525 g_idle_add(refresh_info_idle, window);
528 /* Create the TreeView widget with the file's details */
529 static GtkWidget *make_details(const guchar *path, DirItem *item,
530 GObject *window)
532 GtkListStore *store;
533 GtkWidget *view;
534 gchar *tmp, *tmp2;
536 make_list(&store, &view, G_CALLBACK(cell_edited));
537 g_object_set_data(G_OBJECT(store), "rox_window", window);
539 /* For a symlink to an error, don't show the error */
540 if (item->base_type == TYPE_ERROR && item->lstat_errno)
542 add_row(store, _("Error:"), g_strerror(item->lstat_errno));
543 return view;
546 tmp = g_path_get_dirname(path);
547 tmp2 = pathdup(tmp);
548 if (strcmp(tmp, tmp2) != 0)
549 add_row_and_free(store, _("Real directory:"), tmp2);
550 g_free(tmp);
552 add_row_and_free(store, _("Owner, Group:"),
553 g_strdup_printf("%s, %s",
554 user_name(item->uid),
555 group_name(item->gid)));
557 if (item->base_type != TYPE_DIRECTORY)
559 add_row_and_free(store, _("Size:"),
560 item->size >= PRETTY_SIZE_LIMIT
561 ? g_strdup_printf("%s (%" SIZE_FMT " %s)",
562 format_size(item->size),
563 item->size, _("bytes"))
564 : g_strdup(format_size(item->size)));
566 else
568 gchar *stt=NULL;
570 if(item->flags & ITEM_FLAG_MOUNTED)
571 stt=mount_get_fs_size(path);
573 if(stt) {
574 add_row_and_free(store, _("Size:"), stt);
575 } else {
576 DU *du;
577 int out;
579 gchar *args[] = {"du", "-sk", "", NULL};
581 du = g_new(DU, 1);
582 du->store = store;
583 du->path = g_strdup(add_row(store, _("Size:"),
584 _("Scanning")));
586 args[2] = (gchar *) path;
587 if (g_spawn_async_with_pipes(NULL, args, NULL,
588 G_SPAWN_SEARCH_PATH,
589 NULL, NULL, &du->child,
590 NULL, &out, NULL,
591 NULL))
593 du->chan = g_io_channel_unix_new(out);
594 /* Select binary encoding so we don't get an
595 * error with non-UTF-8 filenames.
597 g_io_channel_set_encoding(du->chan, NULL, NULL);
598 du->watch = g_io_add_watch(du->chan,
599 G_IO_IN|G_IO_ERR|G_IO_HUP,
600 (GIOFunc) read_du_output, du);
601 g_object_ref(G_OBJECT(du->store));
602 g_signal_connect(G_OBJECT(view),
603 "destroy",
604 G_CALLBACK(kill_du_output),
605 du);
607 else
609 set_cell(store, du->path, _("Failed to scan"));
610 g_free(du->path);
611 g_free(du);
616 add_row_and_free(store, _("Change time:"), pretty_time(&item->ctime));
618 add_row_and_free(store, _("Modify time:"), pretty_time(&item->mtime));
620 add_row_and_free(store, _("Access time:"), pretty_time(&item->atime));
622 add_row(store, _("Type:"), pretty_type(item, path));
624 if (item->mime_type)
625 add_row(store, "", mime_type_comment(item->mime_type));
627 if (xattr_supported(NULL)) {
628 add_row(store, _("Extended attributes:"),
629 (item->flags & ITEM_FLAG_HAS_XATTR)
630 ? _("Present")
631 : xattr_supported(path) ? _("None")
632 : _("Not supported"));
635 if (item->flags & ITEM_FLAG_SYMLINK)
637 GtkTreeIter iter;
638 GtkTreeModel *model = GTK_TREE_MODEL(store);
639 char *target;
641 target = readlink_dup(path);
642 if (!target)
643 target = g_strdup(g_strerror(errno));
644 add_row_and_free(store, _("Link target:"), target);
646 /* Make cell editable */
647 gtk_tree_model_iter_nth_child(model, &iter,
648 NULL, gtk_tree_model_iter_n_children(model, NULL) - 1);
650 gtk_list_store_set(store, &iter, 2, TRUE, -1);
653 if (item->base_type != TYPE_DIRECTORY)
655 if (EXECUTABLE_FILE(item))
656 add_row(store, _("Run action:"), _("Execute file"));
657 else
659 add_row_and_free(store, _("Run action:"),
660 describe_current_command(item->mime_type));
664 return view;
667 /* Create the TreeView widget with the application's details */
668 static GtkWidget *make_about(const guchar *path, XMLwrapper *ai)
670 GtkListStore *store;
671 GtkWidget *view;
672 xmlNode *prop;
673 xmlNode *about, *about_trans;
674 GHashTable *translate;
676 g_return_val_if_fail(ai != NULL, NULL);
678 about_trans = xml_get_section(ai, NULL, "About");
680 about = xmlDocGetRootElement(ai->doc)->xmlChildrenNode;
681 for (; about; about = about->next)
683 if (about->type != XML_ELEMENT_NODE)
684 continue;
685 if (about->ns == NULL && strcmp(about->name, "About") == 0)
686 break;
689 g_return_val_if_fail(about != NULL, NULL);
691 make_list(&store, &view, NULL);
693 /* Add each field in about to the list, but overriding each element
694 * with about_trans if a translation is supplied.
696 translate = g_hash_table_new(g_str_hash, g_str_equal);
697 if (about_trans != about)
699 xmlNode *p;
700 for (p = about_trans->xmlChildrenNode; p; p = p->next)
702 if (p->type != XML_ELEMENT_NODE)
703 continue;
704 g_hash_table_insert(translate, (char *) p->name, p);
707 for (prop = about->xmlChildrenNode; prop; prop = prop->next)
709 if (prop->type == XML_ELEMENT_NODE)
711 char *label = NULL;
712 char *value = NULL;
713 char *tmp = NULL;
714 xmlNode *trans;
716 trans = g_hash_table_lookup(translate, prop->name);
717 if (!trans)
718 trans = prop;
720 tmp = xmlGetProp(trans, "label");
721 label = g_strconcat(tmp ? tmp
722 : (char *) trans->name,
723 ":", NULL);
724 g_free(tmp);
725 value = xmlNodeListGetString(trans->doc,
726 trans->xmlChildrenNode, 1);
727 if (!value)
728 value = xmlNodeListGetString(prop->doc,
729 prop->xmlChildrenNode, 1);
730 if (!value)
731 value = g_strdup("-");
732 add_row_and_free(store, label, value);
733 g_free(label);
737 g_hash_table_destroy(translate);
739 return view;
742 static GtkWidget *make_file_says(const guchar *path)
744 GtkWidget *w_file_label;
745 GtkLabel *l_file_label;
746 int file_data[2];
747 char *argv[] = {"file", "-b", NULL, NULL};
748 FileStatus *fs = NULL;
749 guchar *tmp;
751 w_file_label = gtk_label_new(_("<nothing yet>"));
752 l_file_label = GTK_LABEL(w_file_label);
753 gtk_label_set_line_wrap(l_file_label, TRUE);
755 if (pipe(file_data))
757 tmp = g_strdup_printf("pipe(): %s", g_strerror(errno));
758 gtk_label_set_text(l_file_label, tmp);
759 g_free(tmp);
760 return w_file_label;
763 switch (fork())
765 case -1:
766 tmp = g_strdup_printf("pipe(): %s", g_strerror(errno));
767 gtk_label_set_text(l_file_label, tmp);
768 g_free(tmp);
769 close(file_data[0]);
770 close(file_data[1]);
771 break;
772 case 0:
773 /* We are the child */
774 close(file_data[0]);
775 dup2(file_data[1], STDOUT_FILENO);
776 dup2(file_data[1], STDERR_FILENO);
777 #ifdef FILE_B_FLAG
778 argv[2] = (char *) path;
779 #else
780 argv[1] = (char *) g_basename(path);
781 chdir(g_path_get_dirname(path));
782 #endif
783 if (execvp(argv[0], argv))
784 fprintf(stderr, "execvp() error: %s\n",
785 g_strerror(errno));
786 _exit(0);
787 default:
788 /* We are the parent */
789 close(file_data[1]);
790 fs = g_new(FileStatus, 1);
791 fs->label = l_file_label;
792 fs->fd = file_data[0];
793 fs->text = g_strdup("");
794 fs->input = gdk_input_add_full(fs->fd, GDK_INPUT_READ,
795 (GdkInputFunction) add_file_output,
796 fs, NULL);
797 g_signal_connect(w_file_label, "destroy",
798 G_CALLBACK(file_info_destroyed), fs);
799 break;
802 return w_file_label;
805 /* Got some data from file(1) - stick it in the window. */
806 static void add_file_output(FileStatus *fs,
807 gint source, GdkInputCondition condition)
809 char buffer[20];
810 char *str;
811 int got;
813 got = read(source, buffer, sizeof(buffer) - 1);
814 if (got <= 0)
816 int err = errno;
817 g_source_remove(fs->input);
818 close(source);
819 fs->fd = -1;
820 if (got < 0)
821 delayed_error(_("file(1) says... %s"),
822 g_strerror(err));
823 return;
825 buffer[got] = '\0';
827 str = g_strconcat(fs->text, buffer, NULL);
828 g_free(fs->text);
829 fs->text = str;
831 str = to_utf8(fs->text);
832 g_strstrip(str);
833 gtk_label_set_text(fs->label, str);
834 g_free(str);
837 static void file_info_destroyed(GtkWidget *widget, FileStatus *fs)
839 if (fs->fd != -1)
841 g_source_remove(fs->input);
842 close(fs->fd);
845 g_free(fs->text);
846 g_free(fs);
849 static void permissions_destroyed(GtkWidget *widget, Permissions *perm)
851 g_free(perm->path);
852 diritem_free(perm->item);
854 g_free(perm);
857 static void permissions_apply(GtkWidget *widget, Permissions *perm)
859 mode_t nmode;
860 int i;
862 nmode=0;
864 for (i = 0; i < 9; i++)
866 GtkToggleButton *bit = GTK_TOGGLE_BUTTON(perm->bits[i]);
867 if (gtk_toggle_button_get_active(bit))
868 nmode |= 1 << i;
870 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(perm->bits[9])))
871 nmode |= S_ISUID;
872 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(perm->bits[10])))
873 nmode |= S_ISGID;
874 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(perm->bits[11])))
875 nmode |= S_ISVTX;
877 if (chmod(perm->path, nmode))
878 report_error(_("Could not change permissions: %s"),
879 g_strerror(errno));
882 static GtkWidget *make_permissions(const gchar *path, DirItem *item)
884 Permissions *perm;
885 GtkWidget *table;
886 GtkWidget *tick, *label;
887 int i, x, y;
889 perm = g_new(Permissions, 1);
891 perm->path = g_strdup(path);
892 perm->item = diritem_new(path);
894 table = gtk_table_new(4, 5, TRUE);
896 label = gtk_label_new(_("Owner"));
897 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
898 label = gtk_label_new(_("Group"));
899 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3);
900 label = gtk_label_new(_("World"));
901 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4);
903 label = gtk_label_new(_("Read"));
904 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 0, 1);
905 label = gtk_label_new(_("Write"));
906 gtk_table_attach_defaults(GTK_TABLE(table), label, 2, 3, 0, 1);
907 label = gtk_label_new(_("Exec"));
908 gtk_table_attach_defaults(GTK_TABLE(table), label, 3, 4, 0, 1);
910 for (i = 0; i < 9; i++)
912 x = 1 + 2 - i % 3;
913 y = 1 + 2 - i / 3;
914 perm->bits[i] = tick = gtk_check_button_new();
915 gtk_table_attach_defaults(GTK_TABLE(table), tick,
916 x, x + 1, y, y + 1);
917 if (item->mode & (1 << i))
918 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tick),
919 TRUE);
920 g_signal_connect(tick, "toggled",
921 G_CALLBACK(permissions_apply), perm);
924 tick = gtk_check_button_new_with_label(_("SUID"));
925 gtk_table_attach_defaults(GTK_TABLE(table), tick, 4, 5, 1, 2);
926 if (item->mode & S_ISUID)
927 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tick), TRUE);
928 g_signal_connect(tick, "toggled", G_CALLBACK(permissions_apply), perm);
929 perm->bits[9] = tick;
931 tick = gtk_check_button_new_with_label(_("SGID"));
932 gtk_table_attach_defaults(GTK_TABLE(table), tick, 4, 5, 2, 3);
933 if (item->mode & S_ISGID)
934 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tick), TRUE);
935 g_signal_connect(tick, "toggled", G_CALLBACK(permissions_apply), perm);
936 perm->bits[10] = tick;
938 tick = gtk_check_button_new_with_label(_("Sticky"));
939 gtk_table_attach_defaults(GTK_TABLE(table), tick, 4, 5, 3, 4);
940 if (item->mode & S_ISVTX)
941 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tick), TRUE);
942 g_signal_connect(tick, "toggled", G_CALLBACK(permissions_apply), perm);
943 perm->bits[11] = tick;
945 g_signal_connect(table, "destroy",
946 G_CALLBACK(permissions_destroyed), perm);
948 gtk_widget_show_all(table);
949 return table;
952 /* Don't g_free() the result */
953 static const gchar *pretty_type(DirItem *file, const guchar *path)
955 static gchar *text = NULL;
957 null_g_free(&text);
959 if (file->flags & ITEM_FLAG_SYMLINK)
960 return _("Symbolic link");
962 if (file->flags & ITEM_FLAG_APPDIR)
963 return _("ROX application");
965 if (file->flags & ITEM_FLAG_MOUNT_POINT)
967 MountPoint *mp;
968 const gchar *mounted;
970 mounted = mount_is_mounted(path, NULL, NULL)
971 ? _("mounted") : _("unmounted");
973 mp = g_hash_table_lookup(fstab_mounts, path);
974 if (mp)
975 text = g_strdup_printf(_("Mount point for %s (%s)"),
976 mp->name, mounted);
977 else
978 text = g_strdup_printf(_("Mount point (%s)"), mounted);
979 return text;
982 if (file->mime_type)
984 text = g_strconcat(file->mime_type->media_type, "/",
985 file->mime_type->subtype, NULL);
986 return text;
989 return "-";