4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, 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)
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
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 */
29 #include <sys/param.h>
30 #include <libxml/parser.h>
38 #include "gui_support.h"
43 #include "dnd.h" /* For xa_string */
46 typedef struct _FileStatus FileStatus
;
48 /* This is for the 'file(1) says...' thing */
51 int fd
; /* FD to read from, -1 if closed */
52 int input
; /* Input watcher tag if fd valid */
53 GtkLabel
*label
; /* Widget to output to */
54 gchar
*text
; /* String so far */
57 /* Static prototypes */
58 static void refresh_info(GObject
*window
);
59 static GtkWidget
*make_vbox(guchar
*path
);
60 static GtkWidget
*make_details(guchar
*path
, DirItem
*item
, xmlNode
*about
);
61 static GtkWidget
*make_file_says(guchar
*path
);
62 static void add_file_output(FileStatus
*fs
,
63 gint source
, GdkInputCondition condition
);
64 static guchar
*pretty_type(DirItem
*file
, guchar
*path
);
65 static GtkWidget
*make_vbox(guchar
*path
);
66 static void got_response(GObject
*window
, gint response
, gpointer data
);
67 static void file_info_destroyed(GtkWidget
*widget
, FileStatus
*fs
);
69 /****************************************************************
70 * EXTERNAL INTERFACE *
71 ****************************************************************/
73 /* Create and display a new info box showing details about this item */
74 void infobox_new(const gchar
*pathname
)
76 GtkWidget
*window
, *details
;
79 g_return_if_fail(pathname
!= NULL
);
81 path
= g_strdup(pathname
); /* Gets attached to window & freed later */
83 window
= gtk_dialog_new_with_buttons(path
,
84 NULL
, GTK_DIALOG_NO_SEPARATOR
,
85 GTK_STOCK_CANCEL
, GTK_RESPONSE_DELETE_EVENT
,
86 GTK_STOCK_REFRESH
, GTK_RESPONSE_APPLY
,
89 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_MOUSE
);
91 details
= make_vbox(path
);
92 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window
)->vbox
),
93 details
, TRUE
, TRUE
, 0);
95 g_object_set_data(G_OBJECT(window
), "details", details
);
96 g_object_set_data_full(G_OBJECT(window
), "path", path
, g_free
);
98 g_signal_connect(window
, "response", G_CALLBACK(got_response
), NULL
);
101 gtk_widget_show_all(window
);
104 /****************************************************************
105 * INTERNAL FUNCTIONS *
106 ****************************************************************/
108 static void got_response(GObject
*window
, gint response
, gpointer data
)
110 if (response
== GTK_RESPONSE_APPLY
)
111 refresh_info(window
);
114 gtk_widget_destroy(GTK_WIDGET(window
));
115 if (--number_of_windows
< 1)
120 static void refresh_info(GObject
*window
)
122 GtkWidget
*details
, *vbox
;
125 path
= g_object_get_data(window
, "path");
126 details
= g_object_get_data(window
, "details");
127 g_return_if_fail(details
!= NULL
);
128 g_return_if_fail(path
!= NULL
);
130 vbox
= details
->parent
;
131 gtk_widget_destroy(details
);
133 details
= make_vbox(path
);
134 g_object_set_data(window
, "details", details
);
135 gtk_box_pack_start(GTK_BOX(vbox
), details
, TRUE
, TRUE
, 0);
136 gtk_widget_show_all(details
);
139 /* Create the VBox widget that contains the details */
140 static GtkWidget
*make_vbox(guchar
*path
)
143 GtkWidget
*vbox
, *list
, *file
, *frame
;
145 xmlNode
*about
= NULL
;
147 g_return_val_if_fail(path
[0] == '/', NULL
);
149 item
= diritem_new(g_basename(path
));
150 diritem_restat(path
, item
, NULL
);
152 vbox
= gtk_vbox_new(FALSE
, 4);
154 ai
= appinfo_get(path
, item
);
156 about
= xml_get_section(ai
, NULL
, "About");
158 frame
= gtk_frame_new(NULL
);
159 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_IN
);
160 list
= make_details(path
, item
, about
);
161 gtk_container_add(GTK_CONTAINER(frame
), list
);
162 gtk_box_pack_start(GTK_BOX(vbox
), frame
, TRUE
, TRUE
, 0);
166 file
= make_file_says(path
);
167 gtk_box_pack_start(GTK_BOX(vbox
), file
, TRUE
, TRUE
, 0);
178 /* The selection has changed - grab or release the primary selection */
179 static void set_selection(GtkTreeView
*view
, gpointer data
)
181 static GtkClipboard
*primary
= NULL
;
183 GtkTreePath
*path
= NULL
;
187 gtk_tree_view_get_cursor(view
, &path
, NULL
);
192 primary
= gtk_clipboard_get(gdk_atom_intern("PRIMARY", FALSE
));
194 model
= gtk_tree_view_get_model(GTK_TREE_VIEW(view
));
196 gtk_tree_model_get_iter(model
, &iter
, path
);
197 gtk_tree_path_free(path
);
199 gtk_tree_model_get(model
, &iter
, 1, &text
, -1);
201 gtk_clipboard_set_text(primary
, text
, -1);
206 static void add_row(GtkListStore
*store
, const gchar
*label
, const gchar
*data
)
210 gtk_list_store_append(store
, &iter
);
211 gtk_list_store_set(store
, &iter
, 0, label
, 1, data
, -1);
214 /* Create the TreeView widget with the file's details */
215 static GtkWidget
*make_details(guchar
*path
, DirItem
*item
, xmlNode
*about
)
219 GtkCellRenderer
*cell_renderer
;
225 store
= gtk_list_store_new(2, G_TYPE_STRING
, G_TYPE_STRING
);
226 view
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(store
));
227 g_object_unref(G_OBJECT(store
));
228 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
230 cell_renderer
= gtk_cell_renderer_text_new();
231 g_object_set(G_OBJECT(cell_renderer
), "xalign", 1.0, NULL
);
232 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
),
233 0, NULL
, cell_renderer
, "text", 0, NULL
);
235 cell_renderer
= gtk_cell_renderer_text_new();
236 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view
),
237 1, NULL
, cell_renderer
, "text", 1, NULL
);
239 g_signal_connect(view
, "cursor_changed",
240 G_CALLBACK(set_selection
), NULL
);
242 add_row(store
, _("Name:"), item
->leafname
);
244 if (lstat(path
, &info
))
246 add_row(store
, _("Error:"), g_strerror(errno
));
250 gstring
= g_string_new(NULL
);
252 g_string_sprintf(gstring
, "%s, %s", user_name(info
.st_uid
),
253 group_name(info
.st_gid
));
254 add_row(store
, _("Owner, Group:"), gstring
->str
);
256 if (info
.st_size
>= PRETTY_SIZE_LIMIT
)
258 g_string_sprintf(gstring
, "%s (%" SIZE_FMT
" %s)",
259 format_size(info
.st_size
),
260 info
.st_size
, _("bytes"));
264 g_string_assign(gstring
,
265 format_size(info
.st_size
));
267 add_row(store
, _("Size:"), gstring
->str
);
269 tmp
= pretty_time(&info
.st_ctime
);
270 add_row(store
, _("Change time:"), tmp
);
273 tmp
= pretty_time(&info
.st_mtime
);
274 add_row(store
, _("Modify time:"), tmp
);
277 tmp
= pretty_time(&info
.st_atime
);
278 add_row(store
, _("Access time:"), tmp
);
281 g_string_free(gstring
, TRUE
);
283 add_row(store
, _("Permissions:"), pretty_permissions(info
.st_mode
));
285 tmp
= pretty_type(item
, path
);
286 add_row(store
, _("Type:"), tmp
);
289 if (item
->base_type
!= TYPE_DIRECTORY
)
291 tmp
= describe_current_command(item
->mime_type
);
292 add_row(store
, _("Run action:"), tmp
);
298 add_row(store
, "", "");
299 //gtk_clist_set_selectable(table, table->rows - 1, FALSE);
300 for (prop
= about
->xmlChildrenNode
; prop
; prop
= prop
->next
)
302 if (prop
->type
== XML_ELEMENT_NODE
)
306 l
= g_strconcat((char *) prop
->name
, ":", NULL
);
307 tmp
= xmlNodeListGetString(prop
->doc
,
308 prop
->xmlChildrenNode
, 1);
311 add_row(store
, l
, tmp
);
321 static GtkWidget
*make_file_says(guchar
*path
)
324 GtkWidget
*file_label
;
326 char *argv
[] = {"file", "-b", NULL
, NULL
};
327 FileStatus
*fs
= NULL
;
330 frame
= gtk_frame_new(_("file(1) says..."));
331 file_label
= gtk_label_new(_("<nothing yet>"));
332 gtk_misc_set_padding(GTK_MISC(file_label
), 4, 4);
333 gtk_label_set_line_wrap(GTK_LABEL(file_label
), TRUE
);
334 gtk_container_add(GTK_CONTAINER(frame
), file_label
);
338 tmp
= g_strdup_printf("pipe(): %s", g_strerror(errno
));
339 gtk_label_set_text(GTK_LABEL(file_label
), tmp
);
347 tmp
= g_strdup_printf("pipe(): %s", g_strerror(errno
));
348 gtk_label_set_text(GTK_LABEL(file_label
), tmp
);
354 /* We are the child */
356 dup2(file_data
[1], STDOUT_FILENO
);
357 dup2(file_data
[1], STDERR_FILENO
);
361 tmp
= strrchr(path
, '/');
364 chdir(g_strndup(path
, tmp
- path
));
368 if (execvp(argv
[0], argv
))
369 fprintf(stderr
, "execvp() error: %s\n",
373 /* We are the parent */
375 fs
= g_new(FileStatus
, 1);
376 fs
->label
= GTK_LABEL(file_label
);
377 fs
->fd
= file_data
[0];
378 fs
->text
= g_strdup("");
379 fs
->input
= gtk_input_add_full(fs
->fd
, GDK_INPUT_READ
,
380 (GdkInputFunction
) add_file_output
,
382 g_signal_connect(frame
, "destroy",
383 G_CALLBACK(file_info_destroyed
), fs
);
390 /* Got some data from file(1) - stick it in the window. */
391 static void add_file_output(FileStatus
*fs
,
392 gint source
, GdkInputCondition condition
)
398 got
= read(source
, buffer
, sizeof(buffer
) - 1);
402 gtk_input_remove(fs
->input
);
406 delayed_error(_("file(1) says... %s"),
412 str
= g_strconcat(fs
->text
, buffer
, NULL
);
416 str
= g_strdup(fs
->text
);
418 gtk_label_set_text(fs
->label
, str
);
422 static void file_info_destroyed(GtkWidget
*widget
, FileStatus
*fs
)
426 gtk_input_remove(fs
->input
);
434 /* g_free() the result */
435 static guchar
*pretty_type(DirItem
*file
, guchar
*path
)
437 if (file
->flags
& ITEM_FLAG_SYMLINK
)
441 target
= readlink_dup(path
);
446 retval
= g_strdup_printf(_("Symbolic link to %s"),
452 return g_strdup(_("Symbolic link"));
455 if (file
->flags
& ITEM_FLAG_APPDIR
)
456 return g_strdup(_("ROX application"));
458 if (file
->flags
& ITEM_FLAG_MOUNT_POINT
)
459 return g_strdup(_("Mount point"));
462 return g_strconcat(file
->mime_type
->media_type
, "/",
463 file
->mime_type
->subtype
, NULL
);
465 return g_strdup("-");