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)
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
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 /* display.c - code for arranging and displaying file items */
29 #include <sys/param.h>
36 #include <gdk/gdkkeysyms.h>
44 #include "gui_support.h"
53 #include "minibuffer.h"
57 #include "view_iface.h"
60 #define HUGE_WRAP (1.5 * o_large_width.int_value)
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
;
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
;
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
);
86 /****************************************************************
87 * EXTERNAL INTERFACE *
88 ****************************************************************/
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
);
110 option_add_notify(options_changed
);
113 void draw_emblem_on_icon(GdkWindow
*window
, GtkStyle
*style
,
114 const char *stock_id
,
117 GtkIconSet
*icon_set
;
120 icon_set
= gtk_style_lookup_icon_set(style
,
124 pixbuf
= gtk_icon_set_render_icon(icon_set
,
134 pixbuf
=im_unknown
->pixbuf
;
135 g_object_ref(pixbuf
);
138 gdk_pixbuf_render_to_drawable_alpha(pixbuf
,
143 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
144 GDK_RGB_DITHER_NORMAL
, 0, 0);
146 *x
+=gdk_pixbuf_get_width(pixbuf
)+1;
147 g_object_unref(pixbuf
);
150 /* Draw this icon (including any symlink or mount symbol) inside the
153 void draw_huge_icon(GdkWindow
*window
, GtkStyle
*style
, GdkRectangle
*area
,
154 DirItem
*item
, MaskedPixmap
*image
, gboolean selected
,
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);
171 ? create_spotlight_pixbuf(image
->huge_pixbuf
, color
)
172 : image
->huge_pixbuf
;
174 gdk_pixbuf_render_to_drawable_alpha(
178 image_x
, area
->y
+ image_y
, /* dest */
180 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
181 GDK_RGB_DITHER_NORMAL
, 0, 0);
184 g_object_unref(pixbuf
);
186 if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
188 const char *mp
= item
->flags
& ITEM_FLAG_MOUNTED
191 draw_emblem_on_icon(window
, style
, mp
, &image_x
, area
->y
+ 2);
193 if (item
->flags
& ITEM_FLAG_SYMLINK
)
195 draw_emblem_on_icon(window
, style
, ROX_STOCK_SYMLINK
,
196 &image_x
, area
->y
+ 2);
198 if ((item
->flags
& ITEM_FLAG_HAS_XATTR
) && o_xattr_show
.int_value
)
200 draw_emblem_on_icon(window
, style
, ROX_STOCK_XATTR
,
201 &image_x
, area
->y
+ 2);
205 /* Draw this icon (including any symlink or mount symbol) inside the
208 void draw_large_icon(GdkWindow
*window
,
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);
231 ? create_spotlight_pixbuf(image
->pixbuf
, color
)
234 gdk_pixbuf_render_to_drawable_alpha(
238 image_x
, area
->y
+ image_y
, /* dest */
240 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
241 GDK_RGB_DITHER_NORMAL
, 0, 0);
244 g_object_unref(pixbuf
);
246 if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
248 const char *mp
= item
->flags
& ITEM_FLAG_MOUNTED
251 draw_emblem_on_icon(window
, style
, mp
, &image_x
, area
->y
+ 2);
253 if (item
->flags
& ITEM_FLAG_SYMLINK
)
255 draw_emblem_on_icon(window
, style
, ROX_STOCK_SYMLINK
,
256 &image_x
, area
->y
+ 2);
258 if ((item
->flags
& ITEM_FLAG_HAS_XATTR
) && o_xattr_show
.int_value
)
260 draw_emblem_on_icon(window
, style
, ROX_STOCK_XATTR
,
261 &image_x
, area
->y
+ 2);
265 void draw_small_icon(GdkWindow
*window
, GtkStyle
*style
, GdkRectangle
*area
,
266 DirItem
*item
, MaskedPixmap
*image
, gboolean selected
,
269 int width
, height
, image_x
, image_y
;
275 if (!image
->sm_pixbuf
)
276 pixmap_make_small(image
);
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
);
284 ? create_spotlight_pixbuf(image
->sm_pixbuf
, color
)
287 gdk_pixbuf_render_to_drawable_alpha(
291 image_x
, area
->y
+ image_y
, /* dest */
293 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
294 GDK_RGB_DITHER_NORMAL
, 0, 0);
297 g_object_unref(pixbuf
);
299 if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
301 const char *mp
= item
->flags
& ITEM_FLAG_MOUNTED
304 draw_emblem_on_icon(window
, style
, mp
, &image_x
, area
->y
+ 2);
306 if (item
->flags
& ITEM_FLAG_SYMLINK
)
308 draw_emblem_on_icon(window
, style
, ROX_STOCK_SYMLINK
,
309 &image_x
, area
->y
+ 8);
311 if ((item
->flags
& ITEM_FLAG_HAS_XATTR
) && o_xattr_show
.int_value
)
313 draw_emblem_on_icon(window
, style
, ROX_STOCK_XATTR
,
314 &image_x
, area
->y
+ 8);
318 /* The sort functions aren't called from outside, but they are
319 * passed as arguments to display_set_sort_fn().
322 #define IS_A_DIR(item) (item->base_type == TYPE_DIRECTORY && \
323 !(item->flags & ITEM_FLAG_APPDIR))
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; \
333 int sort_by_name(const void *item1
, const void *item2
)
335 const DirItem
*i1
= (DirItem
*) item1
;
336 const DirItem
*i2
= (DirItem
*) item2
;
337 CollateKey
*n1
= i1
->leafname_collate
;
338 CollateKey
*n2
= i2
->leafname_collate
;
343 retval
= collate_key_cmp(n1
, n2
, o_display_caps_first
.int_value
);
345 return retval
? retval
: strcmp(i1
->leafname
, i2
->leafname
);
348 int sort_by_type(const void *item1
, const void *item2
)
350 const DirItem
*i1
= (DirItem
*) item1
;
351 const DirItem
*i2
= (DirItem
*) item2
;
354 int diff
= i1
->base_type
- i2
->base_type
;
357 diff
= (i1
->flags
& ITEM_FLAG_APPDIR
)
358 - (i2
->flags
& ITEM_FLAG_APPDIR
);
360 return diff
> 0 ? 1 : -1;
367 diff
= strcmp(m1
->media_type
, m2
->media_type
);
369 diff
= strcmp(m1
->subtype
, m2
->subtype
);
377 return diff
> 0 ? 1 : -1;
379 return sort_by_name(item1
, item2
);
382 int sort_by_owner(const void *item1
, const void *item2
)
384 const DirItem
*i1
= (DirItem
*) item1
;
385 const DirItem
*i2
= (DirItem
*) item2
;
390 return sort_by_name(item1
, item2
);
392 name1
=user_name(i1
->uid
);
393 name2
=user_name(i2
->uid
);
395 return strcmp(name1
, name2
);
398 int sort_by_group(const void *item1
, const void *item2
)
400 const DirItem
*i1
= (DirItem
*) item1
;
401 const DirItem
*i2
= (DirItem
*) item2
;
406 return sort_by_name(item1
, item2
);
408 name1
=group_name(i1
->gid
);
409 name2
=group_name(i2
->gid
);
411 return strcmp(name1
, name2
);
414 int sort_by_date(const void *item1
, const void *item2
)
416 const DirItem
*i1
= (DirItem
*) item1
;
417 const DirItem
*i2
= (DirItem
*) item2
;
419 /* SORT_DIRS; -- too confusing! */
421 return i1
->mtime
< i2
->mtime
? -1 :
422 i1
->mtime
> i2
->mtime
? 1 :
423 sort_by_name(item1
, item2
);
426 int sort_by_size(const void *item1
, const void *item2
)
428 const DirItem
*i1
= (DirItem
*) item1
;
429 const DirItem
*i2
= (DirItem
*) item2
;
433 return i1
->size
< i2
->size
? -1 :
434 i1
->size
> i2
->size
? 1 :
435 sort_by_name(item1
, item2
);
438 void display_set_sort_type(FilerWindow
*filer_window
, SortType sort_type
,
441 if (filer_window
->sort_type
== sort_type
&&
442 filer_window
->sort_order
== order
)
445 filer_window
->sort_type
= sort_type
;
446 filer_window
->sort_order
= order
;
448 view_sort(filer_window
->view
);
451 /* Change the icon size and style.
452 * force_resize should only be TRUE for new windows.
454 void display_set_layout(FilerWindow
*filer_window
,
457 gboolean force_resize
)
459 gboolean style_changed
= FALSE
;
461 g_return_if_fail(filer_window
!= NULL
);
463 if (filer_window
->display_style_wanted
!= style
464 || filer_window
->details_type
!= details
)
466 style_changed
= TRUE
;
469 display_style_set(filer_window
, style
);
470 display_details_set(filer_window
, details
);
472 /* Recreate layouts because wrapping may have changed */
473 view_style_changed(filer_window
->view
, VIEW_UPDATE_NAME
);
475 if (force_resize
|| o_filer_auto_resize
.int_value
== RESIZE_ALWAYS
476 || (o_filer_auto_resize
.int_value
== RESIZE_STYLE
&& style_changed
))
478 view_autosize(filer_window
->view
);
482 /* Set the 'Show Thumbnails' flag for this window */
483 void display_set_thumbs(FilerWindow
*filer_window
, gboolean thumbs
)
485 if (filer_window
->show_thumbs
== thumbs
)
488 filer_window
->show_thumbs
= thumbs
;
490 view_style_changed(filer_window
->view
, VIEW_UPDATE_VIEWDATA
);
493 filer_cancel_thumbnails(filer_window
);
495 filer_set_title(filer_window
);
497 filer_create_thumbs(filer_window
);
500 void display_update_hidden(FilerWindow
*filer_window
)
502 filer_detach_rescan(filer_window
); /* (updates titlebar) */
504 display_set_actual_size(filer_window
, FALSE
);
507 /* Set the 'Show Hidden' flag for this window */
508 void display_set_hidden(FilerWindow
*filer_window
, gboolean hidden
)
510 if (filer_window
->show_hidden
== hidden
)
514 filer_window->show_hidden = hidden;
516 filer_set_hidden(filer_window
, hidden
);
518 display_update_hidden(filer_window
);
521 /* Set the 'Filter Directories' flag for this window */
522 void display_set_filter_directories(FilerWindow
*filer_window
, gboolean filter_directories
)
524 if (filer_window
->filter_directories
== filter_directories
)
528 filer_window->show_hidden = hidden;
530 filer_set_filter_directories(filer_window
, filter_directories
);
532 display_update_hidden(filer_window
);
535 void display_set_filter(FilerWindow
*filer_window
, FilterType type
,
536 const gchar
*filter_string
)
538 if (filer_set_filter(filer_window
, type
, filter_string
))
539 display_update_hidden(filer_window
);
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
547 void display_set_autoselect(FilerWindow
*filer_window
, const gchar
*leaf
)
551 g_return_if_fail(filer_window
!= NULL
);
552 g_return_if_fail(leaf
!= NULL
);
554 new = g_strdup(leaf
); /* leaf == old value sometimes */
556 null_g_free(&filer_window
->auto_select
);
558 if (view_autoselect(filer_window
->view
, new))
561 filer_window
->auto_select
= new;
564 /* Change the icon size (wraps) */
565 void display_change_size(FilerWindow
*filer_window
, gboolean bigger
)
569 g_return_if_fail(filer_window
!= NULL
);
571 switch (filer_window
->display_style
)
574 new = bigger
? HUGE_ICONS
: SMALL_ICONS
;
588 display_set_layout(filer_window
, new, filer_window
->details_type
,
592 ViewData
*display_create_viewdata(FilerWindow
*filer_window
, DirItem
*item
)
596 view
= g_new(ViewData
, 1);
599 view
->details
= NULL
;
602 display_update_view(filer_window
, item
, view
, TRUE
);
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.
611 void display_set_actual_size(FilerWindow
*filer_window
, gboolean force_resize
)
613 display_set_layout(filer_window
, filer_window
->display_style_wanted
,
614 filer_window
->details_type
, force_resize
);
618 /****************************************************************
619 * INTERNAL FUNCTIONS *
620 ****************************************************************/
622 static void options_changed(void)
626 for (next
= all_filer_windows
; next
; next
= next
->next
)
628 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
631 if (o_display_dirs_first
.has_changed
||
632 o_display_caps_first
.has_changed
)
633 view_sort(VIEW(filer_window
->view
));
635 if (o_display_show_headers
.has_changed
)
636 flags
|= VIEW_UPDATE_HEADERS
;
638 if (o_large_width
.has_changed
|| o_small_width
.has_changed
)
639 flags
|= VIEW_UPDATE_NAME
; /* Recreate PangoLayout */
641 view_style_changed(filer_window
->view
, flags
);
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.
649 static char *details(FilerWindow
*filer_window
, DirItem
*item
)
651 mode_t m
= item
->mode
;
653 gboolean scanned
= item
->base_type
!= TYPE_UNKNOWN
;
655 if (filer_window
->details_type
== DETAILS_NONE
)
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
)
663 MIME_type
*type
= item
->mime_type
;
666 return g_strdup("application/octet-stream");
668 buf
= g_strdup_printf("%s/%s",
669 type
->media_type
, type
->subtype
);
671 else if (filer_window
->details_type
== DETAILS_TIMES
)
673 guchar
*ctime
, *mtime
, *atime
;
675 ctime
= pretty_time(&item
->ctime
);
676 mtime
= pretty_time(&item
->mtime
);
677 atime
= pretty_time(&item
->atime
);
679 buf
= g_strdup_printf("a[%s] c[%s] m[%s]", atime
, ctime
, mtime
);
684 else if (filer_window
->details_type
== DETAILS_PERMISSIONS
)
687 return g_strdup("---,---,---/--"
691 " 12345678 12345678");
693 buf
= g_strdup_printf("%s %-8.8s %-8.8s",
694 pretty_permissions(m
),
695 user_name(item
->uid
),
696 group_name(item
->gid
));
702 if (filer_window
->display_style
== SMALL_ICONS
)
703 return g_strdup("1234M");
705 return g_strdup("1234 bytes");
708 if (item
->base_type
!= TYPE_DIRECTORY
)
710 if (filer_window
->display_style
== SMALL_ICONS
)
711 buf
= g_strdup(format_size_aligned(item
->size
));
713 buf
= g_strdup(format_size(item
->size
));
722 /* Note: Call style_changed after this */
723 static void display_details_set(FilerWindow
*filer_window
, DetailsType details
)
725 filer_window
->details_type
= details
;
728 /* Note: Call style_changed after this */
729 static void display_style_set(FilerWindow
*filer_window
, DisplayStyle style
)
731 filer_window
->display_style_wanted
= style
;
732 display_set_actual_size_real(filer_window
);
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
739 void display_update_view(FilerWindow
*filer_window
,
742 gboolean update_name_layout
)
744 DisplayStyle style
= filer_window
->display_style
;
748 static PangoFontDescription
*monospace
= NULL
;
749 PangoAttrList
*list
= NULL
;
752 monospace
= pango_font_description_from_string("monospace");
756 g_object_unref(G_OBJECT(view
->details
));
757 view
->details
= NULL
;
760 str
= details(filer_window
, item
);
763 PangoAttrList
*details_list
;
764 int perm_offset
= -1;
766 view
->details
= gtk_widget_create_pango_layout(
767 filer_window
->window
, str
);
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
;
775 if (filer_window
->details_type
== DETAILS_PERMISSIONS
)
777 if (perm_offset
> -1)
779 PangoAttribute
*attr
;
781 attr
= pango_attr_underline_new(PANGO_UNDERLINE_SINGLE
);
783 perm_offset
+= 4 * applicable(item
->uid
, item
->gid
);
784 attr
->start_index
= perm_offset
;
785 attr
->end_index
= perm_offset
+ 3;
787 details_list
= pango_attr_list_new();
788 pango_attr_list_insert(details_list
, attr
);
789 pango_layout_set_attributes(view
->details
,
796 g_object_unref(view
->image
);
800 if (filer_window
->show_thumbs
&& item
->base_type
== TYPE_FILE
/*&&
801 strcmp(item->mime_type->media_type, "image") == 0*/)
805 path
= make_path(filer_window
->real_path
, item
->leafname
);
807 view
->image
= g_fscache_lookup_full(pixmap_cache
, path
,
808 FSCACHE_LOOKUP_ONLY_NEW
, NULL
);
813 view
->image
= di_image(item
);
815 g_object_ref(view
->image
);
818 if (view
->layout
&& update_name_layout
)
820 g_object_unref(G_OBJECT(view
->layout
));
828 else if (g_utf8_validate(item
->leafname
, -1, NULL
))
830 view
->layout
= gtk_widget_create_pango_layout(
831 filer_window
->window
, item
->leafname
);
832 pango_layout_set_auto_dir(view
->layout
, FALSE
);
836 PangoAttribute
*attr
;
839 utf8
= to_utf8(item
->leafname
);
840 view
->layout
= gtk_widget_create_pango_layout(
841 filer_window
->window
, utf8
);
844 attr
= pango_attr_foreground_new(0xffff, 0, 0);
845 attr
->start_index
= 0;
846 attr
->end_index
= -1;
848 list
= pango_attr_list_new();
849 pango_attr_list_insert(list
, attr
);
852 if (item
->flags
& ITEM_FLAG_RECENT
)
854 PangoAttribute
*attr
;
856 attr
= pango_attr_weight_new(PANGO_WEIGHT_BOLD
);
857 attr
->start_index
= 0;
858 attr
->end_index
= -1;
860 list
= pango_attr_list_new();
861 pango_attr_list_insert(list
, attr
);
865 pango_layout_set_attributes(view
->layout
, list
);
867 if (filer_window
->details_type
== DETAILS_NONE
)
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
;
875 #ifdef USE_PANGO_WRAP_WORD_CHAR
876 pango_layout_set_wrap(view
->layout
, PANGO_WRAP_WORD_CHAR
);
878 if (wrap_width
!= -1)
879 pango_layout_set_width(view
->layout
, wrap_width
);
881 pango_layout_get_size(view
->layout
, &w
, &h
);
882 view
->name_width
= w
/ PANGO_SCALE
;
883 view
->name_height
= h
/ PANGO_SCALE
;
886 /* Sets display_style from display_style_wanted.
887 * See also display_set_actual_size().
889 static void display_set_actual_size_real(FilerWindow
*filer_window
)
891 DisplayStyle size
= filer_window
->display_style_wanted
;
894 g_return_if_fail(filer_window
!= NULL
);
896 if (size
== AUTO_SIZE_ICONS
)
898 n
= view_count_items(filer_window
->view
);
900 if (n
>= o_filer_change_size_num
.int_value
)
906 filer_window
->display_style
= size
;