1 /*
2  * ROX-Filer, filer for the ROX desktop project
3  * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
4  *
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.
9  *
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.
14  *
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
18  */
19 
20 /* display.c - code for arranging and displaying file items */
21 
22 #include "config.h"
23 
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <math.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <sys/param.h>
30 #include <unistd.h>
31 #include <time.h>
32 #include <ctype.h>
33 
34 #include <gtk/gtk.h>
35 #include <gdk/gdkx.h>
36 #include <gdk/gdkkeysyms.h>
37 
38 #include "global.h"
39 
40 #include "main.h"
41 #include "filer.h"
42 #include "display.h"
43 #include "support.h"
44 #include "gui_support.h"
45 #include "pixmaps.h"
46 #include "menu.h"
47 #include "dnd.h"
48 #include "run.h"
49 #include "mount.h"
50 #include "type.h"
51 #include "options.h"
52 #include "action.h"
53 #include "minibuffer.h"
54 #include "dir.h"
55 #include "diritem.h"
56 #include "fscache.h"
57 #include "view_iface.h"
58 #include "xtypes.h"
59 
60 #define HUGE_WRAP (1.5 * o_large_width.int_value)
61 
62 /* Options bits */
63 static Option o_display_caps_first;
64 static Option o_display_dirs_first;
65 Option o_display_size;
66 Option o_display_details;
67 Option o_display_sort_by;
68 static Option o_large_width;
69 Option o_small_width;
70 Option o_display_show_hidden;
71 Option o_display_show_thumbs;
72 Option o_display_show_headers;
73 Option o_display_show_full_type;
74 Option o_display_inherit_options;
75 static Option o_filer_change_size_num;
76 Option o_vertical_order_small, o_vertical_order_large;
77 Option o_xattr_show;
78 
79 /* Static prototypes */
80 static void display_details_set(FilerWindow *filer_window, DetailsType details);
81 static void display_style_set(FilerWindow *filer_window, DisplayStyle style);
82 static void options_changed(void);
83 static char *details(FilerWindow *filer_window, DirItem *item);
84 static void display_set_actual_size_real(FilerWindow *filer_window);
85 
86 /****************************************************************
87  *			EXTERNAL INTERFACE			*
88  ****************************************************************/
89 
display_init()90 void display_init()
91 {
92 	option_add_int(&o_display_caps_first, "display_caps_first", FALSE);
93 	option_add_int(&o_display_dirs_first, "display_dirs_first", FALSE);
94 	option_add_int(&o_display_size, "display_icon_size", AUTO_SIZE_ICONS);
95 	option_add_int(&o_display_details, "display_details", DETAILS_NONE);
96 	option_add_int(&o_display_sort_by, "display_sort_by", SORT_NAME);
97 	option_add_int(&o_large_width, "display_large_width", 155);
98 	option_add_int(&o_small_width, "display_small_width", 250);
99 	option_add_int(&o_display_show_hidden, "display_show_hidden", FALSE);
100 	option_add_int(&o_display_show_thumbs, "display_show_thumbs", FALSE);
101 	option_add_int(&o_display_show_headers, "display_show_headers", TRUE);
102 	option_add_int(&o_display_show_full_type, "display_show_full_type", FALSE);
103 	option_add_int(&o_display_inherit_options,
104 		       "display_inherit_options", FALSE);
105 	option_add_int(&o_filer_change_size_num, "filer_change_size_num", 30);
106 	option_add_int(&o_vertical_order_small, "vertical_order_small", FALSE);
107 	option_add_int(&o_vertical_order_large, "vertical_order_large", FALSE);
108 	option_add_int(&o_xattr_show, "xattr_show", TRUE);
109 
110 	option_add_notify(options_changed);
111 }
112 
draw_emblem_on_icon(GdkWindow * window,GtkStyle * style,const char * stock_id,int * x,int y)113 void draw_emblem_on_icon(GdkWindow *window, GtkStyle   *style,
114 				const char *stock_id,
115 				int *x, int y)
116 {
117 	GtkIconSet *icon_set;
118 	GdkPixbuf  *pixbuf;
119 
120 	icon_set = gtk_style_lookup_icon_set(style,
121 					     stock_id);
122 	if (icon_set)
123 	{
124 		pixbuf = gtk_icon_set_render_icon(icon_set,
125 						  style,
126 						  GTK_TEXT_DIR_LTR,
127 						  GTK_STATE_NORMAL,
128 						  mount_icon_size,
129 						  NULL,
130 						  NULL);
131 	}
132 	else
133 	{
134 		pixbuf=im_unknown->pixbuf;
135 		g_object_ref(pixbuf);
136 	}
137 
138 	gdk_pixbuf_render_to_drawable_alpha(pixbuf,
139 				window,
140 				0, 0, 				/* src */
141 				*x, y,		                /* dest */
142 				-1, -1,
143 				GDK_PIXBUF_ALPHA_FULL, 128,	/* (unused) */
144 				GDK_RGB_DITHER_NORMAL, 0, 0);
145 
146 	*x+=gdk_pixbuf_get_width(pixbuf)+1;
147 	g_object_unref(pixbuf);
148 }
149 
150 /* Draw this icon (including any symlink or mount symbol) inside the
151  * given rectangle.
152  */
draw_huge_icon(GdkWindow * window,GtkStyle * style,GdkRectangle * area,DirItem * item,MaskedPixmap * image,gboolean selected,GdkColor * color)153 void draw_huge_icon(GdkWindow *window, GtkStyle *style, GdkRectangle *area,
154 		   DirItem  *item, MaskedPixmap *image, gboolean selected,
155 		   GdkColor *color)
156 {
157 	int		width, height;
158 	int		image_x;
159 	int		image_y;
160 	GdkPixbuf	*pixbuf;
161 
162 	if (!image)
163 		return;
164 
165 	width = image->huge_width;
166 	height = image->huge_height;
167 	image_x = area->x + ((area->width - width) >> 1);
168 	image_y = MAX(0, area->height - height - 6);
169 
170 	pixbuf = selected
171 		? create_spotlight_pixbuf(image->huge_pixbuf, color)
172 		: image->huge_pixbuf;
173 
174 	gdk_pixbuf_render_to_drawable_alpha(
175 			pixbuf,
176 			window,
177 			0, 0, 				/* src */
178 			image_x, area->y + image_y,	/* dest */
179 			width, height,
180 			GDK_PIXBUF_ALPHA_FULL, 128,	/* (unused) */
181 			GDK_RGB_DITHER_NORMAL, 0, 0);
182 
183 	if (selected)
184 		g_object_unref(pixbuf);
185 
186 	if (item->flags & ITEM_FLAG_MOUNT_POINT)
187 	{
188 		const char *mp = item->flags & ITEM_FLAG_MOUNTED
189 					? ROX_STOCK_MOUNTED
190 					: ROX_STOCK_MOUNT;
191 		draw_emblem_on_icon(window, style, mp, &image_x, area->y + 2);
192 	}
193 	if (item->flags & ITEM_FLAG_SYMLINK)
194 	{
195 		draw_emblem_on_icon(window, style, ROX_STOCK_SYMLINK,
196 				    &image_x, area->y + 2);
197 	}
198 	if ((item->flags & ITEM_FLAG_HAS_XATTR) && o_xattr_show.int_value)
199 	{
200 		draw_emblem_on_icon(window, style, ROX_STOCK_XATTR,
201 				    &image_x, area->y + 2);
202 	}
203 }
204 
205 /* Draw this icon (including any symlink or mount symbol) inside the
206  * given rectangle.
207  */
draw_large_icon(GdkWindow * window,GtkStyle * style,GdkRectangle * area,DirItem * item,MaskedPixmap * image,gboolean selected,GdkColor * color)208 void draw_large_icon(GdkWindow *window,
209 		     GtkStyle *style,
210 		     GdkRectangle *area,
211 		     DirItem  *item,
212 		     MaskedPixmap *image,
213 		     gboolean selected,
214 		     GdkColor *color)
215 {
216 	int	width;
217 	int	height;
218 	int	image_x;
219 	int	image_y;
220 	GdkPixbuf *pixbuf;
221 
222 	if (!image)
223 		return;
224 
225 	width = MIN(image->width, ICON_WIDTH);
226 	height = MIN(image->height, ICON_HEIGHT);
227 	image_x = area->x + ((area->width - width) >> 1);
228 	image_y = MAX(0, area->height - height - 6);
229 
230 	pixbuf = selected
231 		? create_spotlight_pixbuf(image->pixbuf, color)
232 		: image->pixbuf;
233 
234 	gdk_pixbuf_render_to_drawable_alpha(
235 			pixbuf,
236 			window,
237 			0, 0, 				/* src */
238 			image_x, area->y + image_y,	/* dest */
239 			width, height,
240 			GDK_PIXBUF_ALPHA_FULL, 128,	/* (unused) */
241 			GDK_RGB_DITHER_NORMAL, 0, 0);
242 
243 	if (selected)
244 		g_object_unref(pixbuf);
245 
246 	if (item->flags & ITEM_FLAG_MOUNT_POINT)
247 	{
248 		const char *mp = item->flags & ITEM_FLAG_MOUNTED
249 					? ROX_STOCK_MOUNTED
250 					: ROX_STOCK_MOUNT;
251 		draw_emblem_on_icon(window, style, mp, &image_x, area->y + 2);
252 	}
253 	if (item->flags & ITEM_FLAG_SYMLINK)
254 	{
255 		draw_emblem_on_icon(window, style, ROX_STOCK_SYMLINK,
256 				    &image_x, area->y + 2);
257 	}
258 	if ((item->flags & ITEM_FLAG_HAS_XATTR) && o_xattr_show.int_value)
259 	{
260 		draw_emblem_on_icon(window, style, ROX_STOCK_XATTR,
261 				    &image_x, area->y + 2);
262 	}
263 }
264 
draw_small_icon(GdkWindow * window,GtkStyle * style,GdkRectangle * area,DirItem * item,MaskedPixmap * image,gboolean selected,GdkColor * color)265 void draw_small_icon(GdkWindow *window, GtkStyle *style, GdkRectangle *area,
266 		     DirItem  *item, MaskedPixmap *image, gboolean selected,
267 		     GdkColor *color)
268 {
269 	int		width, height, image_x, image_y;
270 	GdkPixbuf	*pixbuf;
271 
272 	if (!image)
273 		return;
274 
275 	if (!image->sm_pixbuf)
276 		pixmap_make_small(image);
277 
278 	width = MIN(image->sm_width, SMALL_WIDTH);
279 	height = MIN(image->sm_height, SMALL_HEIGHT);
280 	image_x = area->x + ((area->width - width) >> 1);
281 	image_y = MAX(0, SMALL_HEIGHT - image->sm_height);
282 
283 	pixbuf = selected
284 		? create_spotlight_pixbuf(image->sm_pixbuf, color)
285 		: image->sm_pixbuf;
286 
287 	gdk_pixbuf_render_to_drawable_alpha(
288 			pixbuf,
289 			window,
290 			0, 0, 				/* src */
291 			image_x, area->y + image_y,	/* dest */
292 			width, height,
293 			GDK_PIXBUF_ALPHA_FULL, 128,	/* (unused) */
294 			GDK_RGB_DITHER_NORMAL, 0, 0);
295 
296 	if (selected)
297 		g_object_unref(pixbuf);
298 
299 	if (item->flags & ITEM_FLAG_MOUNT_POINT)
300 	{
301 		const char *mp = item->flags & ITEM_FLAG_MOUNTED
302 					? ROX_STOCK_MOUNTED
303 					: ROX_STOCK_MOUNT;
304 		draw_emblem_on_icon(window, style, mp, &image_x, area->y + 2);
305 	}
306 	if (item->flags & ITEM_FLAG_SYMLINK)
307 	{
308 		draw_emblem_on_icon(window, style, ROX_STOCK_SYMLINK,
309 				    &image_x, area->y + 8);
310 	}
311 	if ((item->flags & ITEM_FLAG_HAS_XATTR) && o_xattr_show.int_value)
312 	{
313 		draw_emblem_on_icon(window, style, ROX_STOCK_XATTR,
314 				    &image_x, area->y + 8);
315 	}
316 }
317 
318 /* The sort functions aren't called from outside, but they are
319  * passed as arguments to display_set_sort_fn().
320  */
321 
322 #define IS_A_DIR(item) (item->base_type == TYPE_DIRECTORY && \
323 			!(item->flags & ITEM_FLAG_APPDIR))
324 
325 #define SORT_DIRS	\
326 	if (o_display_dirs_first.int_value) {	\
327 		gboolean id1 = IS_A_DIR(i1);	\
328 		gboolean id2 = IS_A_DIR(i2);	\
329 		if (id1 && !id2) return -1;				\
330 		if (id2 && !id1) return 1;				\
331 	}
332 
sort_by_name(const void * item1,const void * item2)333 int sort_by_name(const void *item1, const void *item2)
334 {
335 	const DirItem *i1 = (DirItem *) item1;
336 	const DirItem *i2 = (DirItem *) item2;
337 	CollateKey *n1 = i1->leafname_collate;
338 	CollateKey *n2 = i2->leafname_collate;
339 	int retval;
340 
341 	SORT_DIRS;
342 
343 	retval = collate_key_cmp(n1, n2, o_display_caps_first.int_value);
344 
345 	return retval ? retval : strcmp(i1->leafname, i2->leafname);
346 }
347 
sort_by_type(const void * item1,const void * item2)348 int sort_by_type(const void *item1, const void *item2)
349 {
350 	const DirItem *i1 = (DirItem *) item1;
351 	const DirItem *i2 = (DirItem *) item2;
352 	MIME_type *m1, *m2;
353 
354 	int	 diff = i1->base_type - i2->base_type;
355 
356 	if (!diff)
357 		diff = (i1->flags & ITEM_FLAG_APPDIR)
358 		     - (i2->flags & ITEM_FLAG_APPDIR);
359 	if (diff)
360 		return diff > 0 ? 1 : -1;
361 
362 	m1 = i1->mime_type;
363 	m2 = i2->mime_type;
364 
365 	if (m1 && m2)
366 	{
367 		diff = strcmp(m1->media_type, m2->media_type);
368 		if (!diff)
369 			diff = strcmp(m1->subtype, m2->subtype);
370 	}
371 	else if (m1 || m2)
372 		diff = m1 ? 1 : -1;
373 	else
374 		diff = 0;
375 
376 	if (diff)
377 		return diff > 0 ? 1 : -1;
378 
379 	return sort_by_name(item1, item2);
380 }
381 
sort_by_owner(const void * item1,const void * item2)382 int sort_by_owner(const void *item1, const void *item2)
383 {
384 	const DirItem *i1 = (DirItem *) item1;
385 	const DirItem *i2 = (DirItem *) item2;
386 	const gchar *name1;
387 	const gchar *name2;
388 
389 	if(i1->uid==i2->uid)
390 		return sort_by_name(item1, item2);
391 
392 	name1=user_name(i1->uid);
393 	name2=user_name(i2->uid);
394 
395 	return strcmp(name1, name2);
396 }
397 
sort_by_group(const void * item1,const void * item2)398 int sort_by_group(const void *item1, const void *item2)
399 {
400 	const DirItem *i1 = (DirItem *) item1;
401 	const DirItem *i2 = (DirItem *) item2;
402 	const gchar *name1;
403 	const gchar *name2;
404 
405 	if(i1->gid==i2->gid)
406 		return sort_by_name(item1, item2);
407 
408 	name1=group_name(i1->gid);
409 	name2=group_name(i2->gid);
410 
411 	return strcmp(name1, name2);
412 }
413 
sort_by_date(const void * item1,const void * item2)414 int sort_by_date(const void *item1, const void *item2)
415 {
416 	const DirItem *i1 = (DirItem *) item1;
417 	const DirItem *i2 = (DirItem *) item2;
418 
419 	/* SORT_DIRS; -- too confusing! */
420 
421 	return i1->mtime < i2->mtime ? -1 :
422 		i1->mtime > i2->mtime ? 1 :
423 		sort_by_name(item1, item2);
424 }
425 
sort_by_size(const void * item1,const void * item2)426 int sort_by_size(const void *item1, const void *item2)
427 {
428 	const DirItem *i1 = (DirItem *) item1;
429 	const DirItem *i2 = (DirItem *) item2;
430 
431 	SORT_DIRS;
432 
433 	return i1->size < i2->size ? -1 :
434 		i1->size > i2->size ? 1 :
435 		sort_by_name(item1, item2);
436 }
437 
display_set_sort_type(FilerWindow * filer_window,SortType sort_type,GtkSortType order)438 void display_set_sort_type(FilerWindow *filer_window, SortType sort_type,
439 			   GtkSortType order)
440 {
441 	if (filer_window->sort_type == sort_type &&
442 	    filer_window->sort_order == order)
443 		return;
444 
445 	filer_window->sort_type = sort_type;
446 	filer_window->sort_order = order;
447 
448 	view_sort(filer_window->view);
449 }
450 
451 /* Change the icon size and style.
452  * force_resize should only be TRUE for new windows.
453  */
display_set_layout(FilerWindow * filer_window,DisplayStyle style,DetailsType details,gboolean force_resize)454 void display_set_layout(FilerWindow  *filer_window,
455 			DisplayStyle style,
456 			DetailsType  details,
457 			gboolean     force_resize)
458 {
459 	gboolean style_changed = FALSE;
460 
461 	g_return_if_fail(filer_window != NULL);
462 
463 	if (filer_window->display_style_wanted != style
464 	    || filer_window->details_type != details)
465 	{
466 		style_changed = TRUE;
467 	}
468 
469 	display_style_set(filer_window, style);
470 	display_details_set(filer_window, details);
471 
472 	/* Recreate layouts because wrapping may have changed */
473 	view_style_changed(filer_window->view, VIEW_UPDATE_NAME);
474 
475 	if (force_resize || o_filer_auto_resize.int_value == RESIZE_ALWAYS
476 	    || (o_filer_auto_resize.int_value == RESIZE_STYLE && style_changed))
477 	{
478 		view_autosize(filer_window->view);
479 	}
480 }
481 
482 /* Set the 'Show Thumbnails' flag for this window */
display_set_thumbs(FilerWindow * filer_window,gboolean thumbs)483 void display_set_thumbs(FilerWindow *filer_window, gboolean thumbs)
484 {
485 	if (filer_window->show_thumbs == thumbs)
486 		return;
487 
488 	filer_window->show_thumbs = thumbs;
489 
490 	view_style_changed(filer_window->view, VIEW_UPDATE_VIEWDATA);
491 
492 	if (!thumbs)
493 		filer_cancel_thumbnails(filer_window);
494 
495 	filer_set_title(filer_window);
496 
497 	filer_create_thumbs(filer_window);
498 }
499 
display_update_hidden(FilerWindow * filer_window)500 void display_update_hidden(FilerWindow *filer_window)
501 {
502 	filer_detach_rescan(filer_window);	/* (updates titlebar) */
503 
504 	display_set_actual_size(filer_window, FALSE);
505 }
506 
507 /* Set the 'Show Hidden' flag for this window */
display_set_hidden(FilerWindow * filer_window,gboolean hidden)508 void display_set_hidden(FilerWindow *filer_window, gboolean hidden)
509 {
510 	if (filer_window->show_hidden == hidden)
511 		return;
512 
513 	/*
514 	filer_window->show_hidden = hidden;
515 	*/
516 	filer_set_hidden(filer_window, hidden);
517 
518 	display_update_hidden(filer_window);
519 }
520 
521 /* Set the 'Filter Directories' flag for this window */
display_set_filter_directories(FilerWindow * filer_window,gboolean filter_directories)522 void display_set_filter_directories(FilerWindow *filer_window, gboolean filter_directories)
523 {
524 	if (filer_window->filter_directories == filter_directories)
525 		return;
526 
527 	/*
528 	filer_window->show_hidden = hidden;
529 	*/
530 	filer_set_filter_directories(filer_window, filter_directories);
531 
532 	display_update_hidden(filer_window);
533 }
534 
display_set_filter(FilerWindow * filer_window,FilterType type,const gchar * filter_string)535 void display_set_filter(FilerWindow *filer_window, FilterType type,
536 			const gchar *filter_string)
537 {
538 	if (filer_set_filter(filer_window, type, filter_string))
539 		display_update_hidden(filer_window);
540 }
541 
542 
543 /* Highlight (wink or cursor) this item in the filer window. If the item
544  * isn't already there but we're scanning then highlight it if it
545  * appears later.
546  */
display_set_autoselect(FilerWindow * filer_window,const gchar * leaf)547 void display_set_autoselect(FilerWindow *filer_window, const gchar *leaf)
548 {
549 	gchar *new;
550 
551 	g_return_if_fail(filer_window != NULL);
552 	g_return_if_fail(leaf != NULL);
553 
554 	new = g_strdup(leaf);	/* leaf == old value sometimes */
555 
556 	null_g_free(&filer_window->auto_select);
557 
558 	if (view_autoselect(filer_window->view, new))
559 		g_free(new);
560 	else
561 		filer_window->auto_select = new;
562 }
563 
564 /* Change the icon size (wraps) */
display_change_size(FilerWindow * filer_window,gboolean bigger)565 void display_change_size(FilerWindow *filer_window, gboolean bigger)
566 {
567 	DisplayStyle	new;
568 
569 	g_return_if_fail(filer_window != NULL);
570 
571 	switch (filer_window->display_style)
572 	{
573 		case LARGE_ICONS:
574 			new = bigger ? HUGE_ICONS : SMALL_ICONS;
575 			break;
576 		case HUGE_ICONS:
577 			if (bigger)
578 				return;
579 			new = LARGE_ICONS;
580 			break;
581 		default:
582 			if (!bigger)
583 				return;
584 			new = LARGE_ICONS;
585 			break;
586 	}
587 
588 	display_set_layout(filer_window, new, filer_window->details_type,
589 			   FALSE);
590 }
591 
display_create_viewdata(FilerWindow * filer_window,DirItem * item)592 ViewData *display_create_viewdata(FilerWindow *filer_window, DirItem *item)
593 {
594 	ViewData *view;
595 
596 	view = g_new(ViewData, 1);
597 
598 	view->layout = NULL;
599 	view->details = NULL;
600 	view->image = NULL;
601 
602 	display_update_view(filer_window, item, view, TRUE);
603 
604 	return view;
605 }
606 
607 /* Set the display style to the desired style. If the desired style
608  * is AUTO_SIZE_ICONS, choose an appropriate size. Also resizes filer
609  * window, if requested.
610  */
display_set_actual_size(FilerWindow * filer_window,gboolean force_resize)611 void display_set_actual_size(FilerWindow *filer_window, gboolean force_resize)
612 {
613 	display_set_layout(filer_window, filer_window->display_style_wanted,
614 			   filer_window->details_type, force_resize);
615 }
616 
617 
618 /****************************************************************
619  *			INTERNAL FUNCTIONS			*
620  ****************************************************************/
621 
options_changed(void)622 static void options_changed(void)
623 {
624 	GList		*next;
625 
626 	for (next = all_filer_windows; next; next = next->next)
627 	{
628 		FilerWindow *filer_window = (FilerWindow *) next->data;
629 		int flags = 0;
630 
631 		if (o_display_dirs_first.has_changed ||
632 		    o_display_caps_first.has_changed)
633 			view_sort(VIEW(filer_window->view));
634 
635 		if (o_display_show_headers.has_changed)
636 			flags |= VIEW_UPDATE_HEADERS;
637 
638 		if (o_large_width.has_changed || o_small_width.has_changed)
639 			flags |= VIEW_UPDATE_NAME; /* Recreate PangoLayout */
640 
641 		view_style_changed(filer_window->view, flags);
642 	}
643 }
644 
645 /* Return a new string giving details of this item, or NULL if details
646  * are not being displayed. If details are not yet available, return
647  * a string of the right length.
648  */
details(FilerWindow * filer_window,DirItem * item)649 static char *details(FilerWindow *filer_window, DirItem *item)
650 {
651 	mode_t	m = item->mode;
652 	guchar 	*buf = NULL;
653 	gboolean scanned = item->base_type != TYPE_UNKNOWN;
654 
655 	if (filer_window->details_type == DETAILS_NONE)
656 		return NULL;
657 
658 	if (scanned && item->lstat_errno)
659 		buf = g_strdup_printf(_("lstat(2) failed: %s"),
660 				g_strerror(item->lstat_errno));
661 	else if (filer_window->details_type == DETAILS_TYPE)
662 	{
663 		MIME_type	*type = item->mime_type;
664 
665 		if (!scanned)
666 			return g_strdup("application/octet-stream");
667 
668 		buf = g_strdup_printf("%s/%s",
669 				      type->media_type, type->subtype);
670 	}
671 	else if (filer_window->details_type == DETAILS_TIMES)
672 	{
673 		guchar	*ctime, *mtime, *atime;
674 
675 		ctime = pretty_time(&item->ctime);
676 		mtime = pretty_time(&item->mtime);
677 		atime = pretty_time(&item->atime);
678 
679 		buf = g_strdup_printf("a[%s] c[%s] m[%s]", atime, ctime, mtime);
680 		g_free(ctime);
681 		g_free(mtime);
682 		g_free(atime);
683 	}
684 	else if (filer_window->details_type == DETAILS_PERMISSIONS)
685 	{
686 		if (!scanned)
687 			return g_strdup("---,---,---/--"
688 #ifdef S_ISVTX
689 					"-"
690 #endif
691 					" 12345678 12345678");
692 
693 		buf = g_strdup_printf("%s %-8.8s %-8.8s",
694 				pretty_permissions(m),
695 				user_name(item->uid),
696 				group_name(item->gid));
697 	}
698 	else
699 	{
700 		if (!scanned)
701 		{
702 			if (filer_window->display_style == SMALL_ICONS)
703 				return g_strdup("1234M");
704 			else
705 				return g_strdup("1234 bytes");
706 		}
707 
708 		if (item->base_type != TYPE_DIRECTORY)
709 		{
710 			if (filer_window->display_style == SMALL_ICONS)
711 				buf = g_strdup(format_size_aligned(item->size));
712 			else
713 				buf = g_strdup(format_size(item->size));
714 		}
715 		else
716 			buf = g_strdup("-");
717 	}
718 
719 	return buf;
720 }
721 
722 /* Note: Call style_changed after this */
display_details_set(FilerWindow * filer_window,DetailsType details)723 static void display_details_set(FilerWindow *filer_window, DetailsType details)
724 {
725 	filer_window->details_type = details;
726 }
727 
728 /* Note: Call style_changed after this */
display_style_set(FilerWindow * filer_window,DisplayStyle style)729 static void display_style_set(FilerWindow *filer_window, DisplayStyle style)
730 {
731 	filer_window->display_style_wanted = style;
732 	display_set_actual_size_real(filer_window);
733 }
734 
735 /* Each displayed item has a ViewData structure with some cached information
736  * to help quickly draw the item (eg, the PangoLayout). This function updates
737  * this information.
738  */
display_update_view(FilerWindow * filer_window,DirItem * item,ViewData * view,gboolean update_name_layout)739 void display_update_view(FilerWindow *filer_window,
740 			 DirItem *item,
741 			 ViewData *view,
742 			 gboolean update_name_layout)
743 {
744 	DisplayStyle	style = filer_window->display_style;
745 	int	w, h;
746 	int	wrap_width = -1;
747 	char	*str;
748 	static PangoFontDescription *monospace = NULL;
749 	PangoAttrList *list = NULL;
750 
751 	if (!monospace)
752 		monospace = pango_font_description_from_string("monospace");
753 
754 	if (view->details)
755 	{
756 		g_object_unref(G_OBJECT(view->details));
757 		view->details = NULL;
758 	}
759 
760 	str = details(filer_window, item);
761 	if (str)
762 	{
763 		PangoAttrList	*details_list;
764 		int	perm_offset = -1;
765 
766 		view->details = gtk_widget_create_pango_layout(
767 					filer_window->window, str);
768 		g_free(str);
769 
770 		pango_layout_set_font_description(view->details, monospace);
771 		pango_layout_get_size(view->details, &w, &h);
772 		view->details_width = w / PANGO_SCALE;
773 		view->details_height = h / PANGO_SCALE;
774 
775 		if (filer_window->details_type == DETAILS_PERMISSIONS)
776 			perm_offset = 0;
777 		if (perm_offset > -1)
778 		{
779 			PangoAttribute	*attr;
780 
781 			attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
782 
783 			perm_offset += 4 * applicable(item->uid, item->gid);
784 			attr->start_index = perm_offset;
785 			attr->end_index = perm_offset + 3;
786 
787 			details_list = pango_attr_list_new();
788 			pango_attr_list_insert(details_list, attr);
789 			pango_layout_set_attributes(view->details,
790 							details_list);
791 		}
792 	}
793 
794 	if (view->image)
795 	{
796 		g_object_unref(view->image);
797 		view->image = NULL;
798 	}
799 
800 	if (filer_window->show_thumbs && item->base_type == TYPE_FILE /*&&
801 									strcmp(item->mime_type->media_type, "image") == 0*/)
802 	{
803 		const guchar    *path;
804 
805 		path = make_path(filer_window->real_path, item->leafname);
806 
807 		view->image = g_fscache_lookup_full(pixmap_cache, path,
808 				FSCACHE_LOOKUP_ONLY_NEW, NULL);
809 	}
810 
811 	if (!view->image)
812 	{
813 		view->image = di_image(item);
814 		if (view->image)
815 			g_object_ref(view->image);
816 	}
817 
818 	if (view->layout && update_name_layout)
819 	{
820 		g_object_unref(G_OBJECT(view->layout));
821 		view->layout = NULL;
822 	}
823 
824 	if (view->layout)
825 	{
826 		/* Do nothing */
827 	}
828 	else if (g_utf8_validate(item->leafname, -1, NULL))
829 	{
830 		view->layout = gtk_widget_create_pango_layout(
831 				filer_window->window, item->leafname);
832 		pango_layout_set_auto_dir(view->layout, FALSE);
833 	}
834 	else
835 	{
836 		PangoAttribute	*attr;
837 		gchar *utf8;
838 
839 		utf8 = to_utf8(item->leafname);
840 		view->layout = gtk_widget_create_pango_layout(
841 				filer_window->window, utf8);
842 		g_free(utf8);
843 
844 		attr = pango_attr_foreground_new(0xffff, 0, 0);
845 		attr->start_index = 0;
846 		attr->end_index = -1;
847 		if (!list)
848 			list = pango_attr_list_new();
849 		pango_attr_list_insert(list, attr);
850 	}
851 
852 	if (item->flags & ITEM_FLAG_RECENT)
853 	{
854 		PangoAttribute	*attr;
855 
856 		attr = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
857 		attr->start_index = 0;
858 		attr->end_index = -1;
859 		if (!list)
860 			list = pango_attr_list_new();
861 		pango_attr_list_insert(list, attr);
862 	}
863 
864 	if (list)
865 		pango_layout_set_attributes(view->layout, list);
866 
867 	if (filer_window->details_type == DETAILS_NONE)
868 	{
869 		if (style == HUGE_ICONS)
870 			wrap_width = HUGE_WRAP * PANGO_SCALE;
871 		else if (style == LARGE_ICONS)
872 			wrap_width = o_large_width.int_value * PANGO_SCALE;
873 	}
874 
875 #ifdef USE_PANGO_WRAP_WORD_CHAR
876 	pango_layout_set_wrap(view->layout, PANGO_WRAP_WORD_CHAR);
877 #endif
878 	if (wrap_width != -1)
879 		pango_layout_set_width(view->layout, wrap_width);
880 
881 	pango_layout_get_size(view->layout, &w, &h);
882 	view->name_width = w / PANGO_SCALE;
883 	view->name_height = h / PANGO_SCALE;
884 }
885 
886 /* Sets display_style from display_style_wanted.
887  * See also display_set_actual_size().
888  */
display_set_actual_size_real(FilerWindow * filer_window)889 static void display_set_actual_size_real(FilerWindow *filer_window)
890 {
891 	DisplayStyle size = filer_window->display_style_wanted;
892 	int n;
893 
894 	g_return_if_fail(filer_window != NULL);
895 
896 	if (size == AUTO_SIZE_ICONS)
897 	{
898 		n = view_count_items(filer_window->view);
899 
900 		if (n >= o_filer_change_size_num.int_value)
901 			size = SMALL_ICONS;
902 		else
903 			size = LARGE_ICONS;
904 	}
905 
906 	filer_window->display_style = size;
907 }
908