r2867: Less frame-heavy look for info box.
[rox-filer.git] / ROX-Filer / src / infobox.c
blob9f0e8267e106b3065b35614ded3b44f18c4371de
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2003, 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 <libxml/parser.h>
32 #include <gtk/gtk.h>
34 #include "global.h"
36 #include "support.h"
37 #include "main.h"
38 #include "gui_support.h"
39 #include "diritem.h"
40 #include "type.h"
41 #include "infobox.h"
42 #include "appinfo.h"
43 #include "dnd.h" /* For xa_string */
44 #include "run.h" /* For show_item_help() */
45 #include "xml.h"
46 #include "mount.h"
47 #include "pixmaps.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 } DU;
67 typedef struct _Permissions Permissions;
69 struct _Permissions
71 gchar *path;
72 DirItem *item;
73 GtkWidget *bits[12];
76 /* Static prototypes */
77 static void refresh_info(GObject *window);
78 static GtkWidget *make_vbox(const guchar *path);
79 static GtkWidget *make_details(const guchar *path, DirItem *item);
80 static GtkWidget *make_about(const guchar *path, XMLwrapper *ai);
81 static GtkWidget *make_file_says(const guchar *path);
82 static GtkWidget *make_permissions(const gchar *path, DirItem *item);
83 static void add_file_output(FileStatus *fs,
84 gint source, GdkInputCondition condition);
85 static const gchar *pretty_type(DirItem *file, const guchar *path);
86 static void got_response(GObject *window, gint response, gpointer data);
87 static void file_info_destroyed(GtkWidget *widget, FileStatus *fs);
89 /****************************************************************
90 * EXTERNAL INTERFACE *
91 ****************************************************************/
93 /* Open each item in a new infobox. Confirms if there are a large
94 * number of items to show.
96 void infobox_show_list(GList *paths)
98 int n;
100 n = g_list_length(paths);
102 if (n >= 10)
104 gchar *message;
105 gboolean ok;
107 message = g_strdup_printf(
108 _("Are you sure you want to open %d windows?"), n);
109 ok = confirm(message, GTK_STOCK_YES, _("Show Info"));
110 g_free(message);
111 if (!ok)
112 return;
115 g_list_foreach(paths, (GFunc) infobox_new, NULL);
118 /* Create and display a new info box showing details about this item */
119 void infobox_new(const gchar *pathname)
121 GtkWidget *window, *details;
122 gchar *path;
123 GObject *owindow;
125 g_return_if_fail(pathname != NULL);
127 path = g_strdup(pathname); /* Gets attached to window & freed later */
129 window = gtk_dialog_new_with_buttons(
130 g_utf8_validate(path, -1, NULL) ? path
131 : _("(bad utf-8)"),
132 NULL, GTK_DIALOG_NO_SEPARATOR,
133 GTK_STOCK_CANCEL, GTK_RESPONSE_DELETE_EVENT,
134 GTK_STOCK_REFRESH, GTK_RESPONSE_APPLY,
135 NULL);
137 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_MOUSE);
139 details = make_vbox(path);
140 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(window)->vbox),
141 details);
143 owindow = G_OBJECT(window);
144 g_object_set_data(owindow, "details", details);
145 g_object_set_data_full(owindow, "path", path, g_free);
147 g_signal_connect(window, "response", G_CALLBACK(got_response), NULL);
149 number_of_windows++;
150 gtk_widget_show_all(window);
153 /****************************************************************
154 * INTERNAL FUNCTIONS *
155 ****************************************************************/
157 static void show_help_clicked(const gchar *path)
159 DirItem *item;
161 item = diritem_new("");
162 diritem_restat(path, item, NULL);
163 show_item_help(path, item);
164 diritem_free(item);
167 static void got_response(GObject *window, gint response, gpointer data)
169 if (response == GTK_RESPONSE_APPLY)
170 refresh_info(window);
171 else
173 gtk_widget_destroy(GTK_WIDGET(window));
174 one_less_window();
178 static void refresh_info(GObject *window)
180 GtkWidget *details, *vbox;
181 guchar *path;
183 path = g_object_get_data(window, "path");
184 details = g_object_get_data(window, "details");
185 g_return_if_fail(details != NULL);
186 g_return_if_fail(path != NULL);
188 vbox = details->parent;
189 gtk_widget_destroy(details);
191 details = make_vbox(path);
192 g_object_set_data(window, "details", details);
193 gtk_box_pack_start_defaults(GTK_BOX(vbox), details);
194 gtk_widget_show_all(details);
197 static void add_frame(GtkBox *vbox, GtkWidget *list)
199 GtkWidget *frame;
201 frame = gtk_frame_new(NULL);
202 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
203 gtk_container_add(GTK_CONTAINER(frame), list);
204 gtk_box_pack_start_defaults(vbox, frame);
207 /* Create the VBox widget that contains the details.
208 * Note that 'path' must not be freed until the vbox is destroyed.
210 static GtkWidget *make_vbox(const guchar *path)
212 DirItem *item;
213 GtkBox *vbox;
214 XMLwrapper *ai;
215 xmlNode *about = NULL;
216 gchar *help_dir;
217 GtkWidget *hbox, *name, *label;
219 g_return_val_if_fail(path[0] == '/', NULL);
221 item = diritem_new(g_basename(path));
222 diritem_restat(path, item, NULL);
224 ai = appinfo_get(path, item);
225 if (ai)
226 about = xml_get_section(ai, NULL, "About");
228 vbox = GTK_BOX(gtk_vbox_new(FALSE, 4));
229 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
231 /* Heading, with icon and name */
232 hbox = gtk_hbox_new(FALSE, 4);
233 gtk_box_pack_start(vbox, hbox, FALSE, TRUE, 0);
234 gtk_box_pack_start(GTK_BOX(hbox),
235 gtk_image_new_from_pixbuf(item->image->pixbuf),
236 FALSE, FALSE, 4);
238 if (g_utf8_validate(item->leafname, -1, NULL))
239 name = gtk_label_new(item->leafname);
240 else
242 guchar *u8;
244 u8 = to_utf8(item->leafname);
245 name = gtk_label_new(u8);
246 g_free(u8);
248 gtk_label_set_selectable(GTK_LABEL(name), TRUE);
249 gtk_box_pack_start(GTK_BOX(hbox), name, FALSE, TRUE, 4);
251 /* Make the name bolder and larger */
253 PangoAttribute *attr;
254 PangoAttrList *list;
256 list = pango_attr_list_new();
258 attr = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
259 attr->start_index = 0;
260 attr->end_index = -1;
261 pango_attr_list_insert(list, attr);
263 attr = pango_attr_scale_new(PANGO_SCALE_X_LARGE);
264 attr->start_index = 0;
265 attr->end_index = -1;
266 pango_attr_list_insert(list, attr);
268 gtk_label_set_attributes(GTK_LABEL(name), list);
271 /* List of file attributes */
272 add_frame(vbox, make_details(path, item));
274 help_dir = g_strconcat(path, "/Help", NULL);
276 if (access(help_dir, F_OK) == 0)
278 GtkWidget *button, *align;
280 align = gtk_alignment_new(0.5, 0.5, 0, 0);
282 button = button_new_mixed(GTK_STOCK_JUMP_TO,
283 _("Show _Help Files"));
284 gtk_box_pack_start(vbox, align, FALSE, TRUE, 0);
285 gtk_container_add(GTK_CONTAINER(align), button);
286 g_signal_connect_swapped(button, "clicked",
287 G_CALLBACK(show_help_clicked),
288 (gpointer) path);
290 g_free(help_dir);
292 label = gtk_label_new(NULL);
293 gtk_label_set_markup(GTK_LABEL(label), _("<b>Permissions</b>"));
294 gtk_misc_set_alignment(GTK_MISC(label), 0, 1);
295 gtk_box_pack_start(vbox, label, FALSE, TRUE, 2);
297 gtk_box_pack_start(vbox, make_permissions(path, item),
298 FALSE, TRUE, 0);
300 if (about)
301 add_frame(vbox, make_about(path, ai));
302 else if (item->base_type == TYPE_FILE)
304 label = gtk_label_new(NULL);
305 gtk_label_set_markup(GTK_LABEL(label),
306 _("<b>Contents indicate...</b>"));
307 gtk_misc_set_alignment(GTK_MISC(label), 0, 1);
308 gtk_box_pack_start(vbox, label, FALSE, TRUE, 2);
310 gtk_box_pack_start_defaults(vbox, make_file_says(path));
313 if (ai)
314 g_object_unref(ai);
316 diritem_free(item);
318 return (GtkWidget *) vbox;
321 /* The selection has changed - grab or release the primary selection */
322 static void set_selection(GtkTreeView *view, gpointer data)
324 static GtkClipboard *primary = NULL;
325 GtkTreeModel *model;
326 GtkTreePath *path = NULL;
327 GtkTreeIter iter;
328 gchar *text;
330 gtk_tree_view_get_cursor(view, &path, NULL);
331 if (!path)
332 return;
334 if (!primary)
335 primary = gtk_clipboard_get(gdk_atom_intern("PRIMARY", FALSE));
337 model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
339 gtk_tree_model_get_iter(model, &iter, path);
340 gtk_tree_path_free(path);
342 gtk_tree_model_get(model, &iter, 1, &text, -1);
344 gtk_clipboard_set_text(primary, text, -1);
346 g_free(text);
349 static gchar *add_row(GtkListStore *store, const gchar *label,
350 const gchar *data)
352 GtkTreeIter iter;
353 gchar *u8 = NULL;
354 GtkTreePath *tpath;
355 gchar *last=NULL;
357 if (!g_utf8_validate(data, -1, NULL))
358 u8 = to_utf8(data);
360 gtk_list_store_append(store, &iter);
361 gtk_list_store_set(store, &iter, 0, label, 1, u8 ? u8 : data, -1);
363 g_free(u8);
365 tpath=gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
366 if(last)
367 g_free(last);
368 last=gtk_tree_path_to_string(tpath);
369 gtk_tree_path_free(tpath);
371 return last;
374 static void add_row_and_free(GtkListStore *store,
375 const gchar *label, gchar *data)
377 add_row(store, label, data);
378 g_free(data);
381 /* Create an empty list view, ready to place some data in */
382 static void make_list(GtkListStore **list_store, GtkWidget **list_view)
384 GtkListStore *store;
385 GtkTreeView *view;
386 GtkCellRenderer *cell_renderer;
388 store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
389 view = GTK_TREE_VIEW(
390 gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)));
391 g_object_unref(G_OBJECT(store));
392 gtk_tree_view_set_headers_visible(view, FALSE);
394 cell_renderer = gtk_cell_renderer_text_new();
395 g_object_set(G_OBJECT(cell_renderer), "xalign", 1.0, NULL);
396 gtk_tree_view_insert_column_with_attributes(view,
397 0, NULL, cell_renderer, "text", 0, NULL);
399 cell_renderer = gtk_cell_renderer_text_new();
400 gtk_tree_view_insert_column_with_attributes(view,
401 1, NULL, cell_renderer, "text", 1, NULL);
403 g_signal_connect(view, "cursor_changed",
404 G_CALLBACK(set_selection), NULL);
406 *list_store = store;
407 *list_view = (GtkWidget *) view;
410 static void set_cell(GtkListStore *store, const gchar *path,
411 const gchar *ctext)
413 GtkTreeIter iter;
415 gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(store),
416 &iter, path);
417 gtk_list_store_set(store, &iter, 1, ctext, -1);
420 static void insert_size(DU *du, const char *line)
422 off_t size;
423 gchar *cell;
425 #ifdef LARGE_FILE_SUPPORT
426 size=strtoll(line, NULL, 10);
427 #else
428 size=strtol(line, NULL, 10);
429 #endif
430 size<<=10; /* Because du reports in K */
431 cell=(size >= PRETTY_SIZE_LIMIT)?
432 g_strdup_printf("%s (%" SIZE_FMT " %s)",
433 format_size(size),
434 size, _("bytes"))
435 : g_strdup(format_size(size));
437 set_cell(du->store, du->path, cell);
439 g_free(cell);
442 static gboolean read_du_output(GIOChannel *source, GIOCondition cond,
443 DU *du)
445 GString *line;
446 GIOStatus stat;
447 GError *err=NULL;
449 line=g_string_new("");
450 stat=g_io_channel_read_line_string(source, line, NULL, &err);
451 switch(stat) {
452 case G_IO_STATUS_NORMAL:
453 insert_size(du, line->str);
454 break;
455 case G_IO_STATUS_EOF:
456 break;
457 case G_IO_STATUS_AGAIN:
458 g_string_free(line, TRUE);
459 return TRUE;
460 case G_IO_STATUS_ERROR:
461 set_cell(du->store, du->path, err->message);
462 break;
464 g_string_free(line, TRUE);
466 return FALSE;
469 static void kill_du_output(GtkWidget *widget, DU *du)
471 g_io_channel_unref(du->chan);
472 g_free(du->path);
473 g_free(du);
476 /* Create the TreeView widget with the file's details */
477 static GtkWidget *make_details(const guchar *path, DirItem *item)
479 GtkListStore *store;
480 GtkWidget *view;
481 gchar *tmp, *tmp2;
483 make_list(&store, &view);
485 if (item->base_type == TYPE_ERROR)
487 add_row(store, _("Error:"), g_strerror(item->lstat_errno));
488 return view;
491 tmp = g_path_get_dirname(path);
492 tmp2 = pathdup(tmp);
493 if (strcmp(tmp, tmp2) != 0)
494 add_row_and_free(store, _("Real directory:"), tmp2);
495 g_free(tmp);
497 add_row_and_free(store, _("Owner, Group:"),
498 g_strdup_printf("%s, %s",
499 user_name(item->uid),
500 group_name(item->gid)));
502 if (item->base_type != TYPE_DIRECTORY) {
503 add_row_and_free(store, _("Size:"),
504 item->size >= PRETTY_SIZE_LIMIT
505 ? g_strdup_printf("%s (%" SIZE_FMT " %s)",
506 format_size(item->size),
507 item->size, _("bytes"))
508 : g_strdup(format_size(item->size)));
509 } else {
510 DU *du=g_new(DU, 1);
511 int out;
512 gchar *args[]={"du", "-sk", "", NULL};
514 du->store=store;
515 du->path=g_strdup(add_row(store, _("Size:"), _("Scanning")));
517 args[2]=(gchar *) path;
518 if(g_spawn_async_with_pipes(NULL, args, NULL,
519 G_SPAWN_SEARCH_PATH,
520 NULL, NULL, NULL,
521 NULL, &out, NULL,
522 NULL)) {
523 du->chan=g_io_channel_unix_new(out);
524 du->watch=g_io_add_watch(du->chan, G_IO_IN,
525 (GIOFunc) read_du_output, du);
526 g_signal_connect(G_OBJECT(view), "destroy",
527 G_CALLBACK(kill_du_output), du);
528 } else {
529 set_cell(store, du->path, _("Failed to scan"));
530 g_free(du->path);
531 g_free(du);
535 add_row_and_free(store, _("Change time:"), pretty_time(&item->ctime));
537 add_row_and_free(store, _("Modify time:"), pretty_time(&item->mtime));
539 add_row_and_free(store, _("Access time:"), pretty_time(&item->atime));
541 add_row(store, _("Type:"), pretty_type(item, path));
543 if (item->mime_type)
544 add_row(store, "", mime_type_comment(item->mime_type));
546 if (item->base_type != TYPE_DIRECTORY)
547 add_row_and_free(store, _("Run action:"),
548 describe_current_command(item->mime_type));
550 return view;
553 /* Create the TreeView widget with the application's details */
554 static GtkWidget *make_about(const guchar *path, XMLwrapper *ai)
556 GtkListStore *store;
557 GtkWidget *view;
558 xmlNode *prop;
559 xmlNode *about, *about_trans;
560 GHashTable *translate;
562 g_return_val_if_fail(ai != NULL, NULL);
564 about_trans = xml_get_section(ai, NULL, "About");
566 about = xmlDocGetRootElement(ai->doc)->xmlChildrenNode;
567 for (; about; about = about->next)
569 if (about->type != XML_ELEMENT_NODE)
570 continue;
571 if (about->ns == NULL && strcmp(about->name, "About") == 0)
572 break;
575 g_return_val_if_fail(about != NULL, NULL);
577 make_list(&store, &view);
579 /* Add each field in about to the list, but overriding each element
580 * with about_trans if a translation is supplied.
582 translate = g_hash_table_new(g_str_hash, g_str_equal);
583 if (about_trans != about)
585 xmlNode *p;
586 for (p = about_trans->xmlChildrenNode; p; p = p->next)
588 if (p->type != XML_ELEMENT_NODE)
589 continue;
590 g_hash_table_insert(translate, (char *) p->name, p);
593 for (prop = about->xmlChildrenNode; prop; prop = prop->next)
595 if (prop->type == XML_ELEMENT_NODE)
597 char *label = NULL;
598 char *value = NULL;
599 char *tmp = NULL;
600 xmlNode *trans;
602 trans = g_hash_table_lookup(translate, prop->name);
603 if (!trans)
604 trans = prop;
606 tmp = xmlGetProp(trans, "label");
607 label = g_strconcat(tmp ? tmp
608 : (char *) trans->name,
609 ":", NULL);
610 g_free(tmp);
611 value = xmlNodeListGetString(trans->doc,
612 trans->xmlChildrenNode, 1);
613 if (!value)
614 value = xmlNodeListGetString(prop->doc,
615 prop->xmlChildrenNode, 1);
616 if (!value)
617 value = g_strdup("-");
618 add_row_and_free(store, label, value);
619 g_free(label);
623 g_hash_table_destroy(translate);
625 return view;
628 static GtkWidget *make_file_says(const guchar *path)
630 GtkWidget *w_file_label;
631 GtkLabel *l_file_label;
632 int file_data[2];
633 char *argv[] = {"file", "-b", NULL, NULL};
634 FileStatus *fs = NULL;
635 guchar *tmp;
637 w_file_label = gtk_label_new(_("<nothing yet>"));
638 l_file_label = GTK_LABEL(w_file_label);
639 gtk_label_set_line_wrap(l_file_label, TRUE);
641 if (pipe(file_data))
643 tmp = g_strdup_printf("pipe(): %s", g_strerror(errno));
644 gtk_label_set_text(l_file_label, tmp);
645 g_free(tmp);
646 return w_file_label;
649 switch (fork())
651 case -1:
652 tmp = g_strdup_printf("pipe(): %s", g_strerror(errno));
653 gtk_label_set_text(l_file_label, tmp);
654 g_free(tmp);
655 close(file_data[0]);
656 close(file_data[1]);
657 break;
658 case 0:
659 /* We are the child */
660 close(file_data[0]);
661 dup2(file_data[1], STDOUT_FILENO);
662 dup2(file_data[1], STDERR_FILENO);
663 #ifdef FILE_B_FLAG
664 argv[2] = (char *) path;
665 #else
666 argv[1] = (char *) g_basename(path);
667 chdir(g_path_get_dirname(path));
668 #endif
669 if (execvp(argv[0], argv))
670 fprintf(stderr, "execvp() error: %s\n",
671 g_strerror(errno));
672 _exit(0);
673 default:
674 /* We are the parent */
675 close(file_data[1]);
676 fs = g_new(FileStatus, 1);
677 fs->label = l_file_label;
678 fs->fd = file_data[0];
679 fs->text = g_strdup("");
680 fs->input = gtk_input_add_full(fs->fd, GDK_INPUT_READ,
681 (GdkInputFunction) add_file_output,
682 NULL, fs, NULL);
683 g_signal_connect(w_file_label, "destroy",
684 G_CALLBACK(file_info_destroyed), fs);
685 break;
688 return w_file_label;
691 /* Got some data from file(1) - stick it in the window. */
692 static void add_file_output(FileStatus *fs,
693 gint source, GdkInputCondition condition)
695 char buffer[20];
696 char *str;
697 int got;
699 got = read(source, buffer, sizeof(buffer) - 1);
700 if (got <= 0)
702 int err = errno;
703 gtk_input_remove(fs->input);
704 close(source);
705 fs->fd = -1;
706 if (got < 0)
707 delayed_error(_("file(1) says... %s"),
708 g_strerror(err));
709 return;
711 buffer[got] = '\0';
713 str = g_strconcat(fs->text, buffer, NULL);
714 g_free(fs->text);
715 fs->text = str;
717 str = to_utf8(fs->text);
718 g_strstrip(str);
719 gtk_label_set_text(fs->label, str);
720 g_free(str);
723 static void file_info_destroyed(GtkWidget *widget, FileStatus *fs)
725 if (fs->fd != -1)
727 gtk_input_remove(fs->input);
728 close(fs->fd);
731 g_free(fs->text);
732 g_free(fs);
735 static void permissions_destroyed(GtkWidget *widget, Permissions *perm)
737 g_free(perm->path);
738 g_free(perm->item);
740 g_free(perm);
743 static void permissions_apply(GtkWidget *widget, Permissions *perm)
745 mode_t nmode;
746 int i;
748 nmode=0;
750 for(i=0; i<9; i++) {
751 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(perm->bits[i])))
752 nmode|=(1<<i);
754 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(perm->bits[9])))
755 nmode|=S_ISUID;
756 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(perm->bits[10])))
757 nmode|=S_ISGID;
758 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(perm->bits[11])))
759 nmode|=S_ISVTX;
761 if(chmod(perm->path, nmode)) {
762 report_error(_("Could not change permissions: %s"),
763 strerror(errno));
767 static GtkWidget *make_permissions(const gchar *path, DirItem *item)
769 Permissions *perm;
770 GtkWidget *table;
771 GtkWidget *tick, *label;
772 int i, x, y;
774 perm=g_new(Permissions, 1);
776 perm->path=g_strdup(path);
777 perm->item=diritem_new(path);
779 table=gtk_table_new(4, 5, TRUE);
781 label=gtk_label_new(_("Owner"));
782 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
783 label=gtk_label_new(_("Group"));
784 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3);
785 label=gtk_label_new(_("World"));
786 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4);
788 label=gtk_label_new(_("Read"));
789 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 0, 1);
790 label=gtk_label_new(_("Write"));
791 gtk_table_attach_defaults(GTK_TABLE(table), label, 2, 3, 0, 1);
792 label=gtk_label_new(_("Exec"));
793 gtk_table_attach_defaults(GTK_TABLE(table), label, 3, 4, 0, 1);
795 for(i=0; i<9; i++) {
796 x=1+2-i%3;
797 y=1+2-i/3;
798 perm->bits[i]=tick=gtk_check_button_new();
799 gtk_table_attach_defaults(GTK_TABLE(table), tick,
800 x, x+1, y, y+1);
801 if(item->mode & (1<<i))
802 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tick),
803 TRUE);
804 g_signal_connect(tick, "toggled",
805 G_CALLBACK(permissions_apply ), perm);
808 tick=gtk_check_button_new_with_label(_("SUID"));
809 gtk_table_attach_defaults(GTK_TABLE(table), tick, 4, 5, 1, 2);
810 if(item->mode & S_ISUID)
811 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tick), TRUE);
812 g_signal_connect(tick, "toggled",
813 G_CALLBACK(permissions_apply ), perm);
814 perm->bits[9]=tick;
816 tick=gtk_check_button_new_with_label(_("SGID"));
817 gtk_table_attach_defaults(GTK_TABLE(table), tick, 4, 5, 2, 3);
818 if(item->mode & S_ISGID)
819 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tick), TRUE);
820 g_signal_connect(tick, "toggled",
821 G_CALLBACK(permissions_apply ), perm);
822 perm->bits[10]=tick;
824 tick=gtk_check_button_new_with_label(_("Sticky"));
825 gtk_table_attach_defaults(GTK_TABLE(table), tick, 4, 5, 3, 4);
826 if(item->mode & S_ISVTX)
827 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tick), TRUE);
828 g_signal_connect(tick, "toggled",
829 G_CALLBACK(permissions_apply ), perm);
830 perm->bits[11]=tick;
832 g_signal_connect(table, "destroy",
833 G_CALLBACK(permissions_destroyed), perm);
835 gtk_widget_show_all(table);
836 return table;
839 /* Don't g_free() the result */
840 static const gchar *pretty_type(DirItem *file, const guchar *path)
842 static gchar *text = NULL;
844 null_g_free(&text);
846 if (file->flags & ITEM_FLAG_SYMLINK)
848 char *target;
850 target = readlink_dup(path);
851 if (target)
853 ensure_utf8(&target);
855 text = g_strdup_printf(_("Symbolic link to %s"),
856 target);
857 g_free(target);
858 return text;
861 return _("Symbolic link");
864 if (file->flags & ITEM_FLAG_APPDIR)
865 return _("ROX application");
867 if (file->flags & ITEM_FLAG_MOUNT_POINT)
869 MountPoint *mp;
870 const gchar *mounted;
872 mounted = mount_is_mounted(path, NULL, NULL)
873 ? _("mounted") : _("unmounted");
875 mp = g_hash_table_lookup(fstab_mounts, path);
876 if (mp)
877 text = g_strdup_printf(_("Mount point for %s (%s)"),
878 mp->name, mounted);
879 else
880 text = g_strdup_printf(_("Mount point (%s)"), mounted);
881 return text;
884 if (file->mime_type)
886 text = g_strconcat(file->mime_type->media_type, "/",
887 file->mime_type->subtype, NULL);
888 return text;
891 return "-";