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)
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 /* display.c - code for arranging and displaying file items */
31 #include <sys/param.h>
38 #include <gdk/gdkkeysyms.h>
45 #include "gui_support.h"
55 #include "minibuffer.h"
59 #include "view_iface.h"
61 #define HUGE_WRAP (1.5 * o_large_width.int_value)
64 static Option o_display_caps_first
;
65 static Option o_display_dirs_first
;
66 Option o_display_size
;
67 Option o_display_details
;
68 Option o_display_sort_by
;
69 static Option o_large_width
;
71 Option o_display_show_hidden
;
72 Option o_display_show_thumbs
;
73 Option o_display_show_headers
;
74 Option o_display_inherit_options
;
75 static Option o_filer_change_size_num
;
77 /* Static prototypes */
78 static void display_details_set(FilerWindow
*filer_window
, DetailsType details
);
79 static void display_style_set(FilerWindow
*filer_window
, DisplayStyle style
);
80 static void options_changed(void);
81 static char *details(FilerWindow
*filer_window
, DirItem
*item
);
82 static void display_set_actual_size_real(FilerWindow
*filer_window
);
84 /****************************************************************
85 * EXTERNAL INTERFACE *
86 ****************************************************************/
90 option_add_int(&o_display_caps_first
, "display_caps_first", FALSE
);
91 option_add_int(&o_display_dirs_first
, "display_dirs_first", FALSE
);
92 option_add_int(&o_display_size
, "display_icon_size", AUTO_SIZE_ICONS
);
93 option_add_int(&o_display_details
, "display_details", DETAILS_NONE
);
94 option_add_int(&o_display_sort_by
, "display_sort_by", SORT_NAME
);
95 option_add_int(&o_large_width
, "display_large_width", 89);
96 option_add_int(&o_small_width
, "display_small_width", 250);
97 option_add_int(&o_display_show_hidden
, "display_show_hidden", FALSE
);
98 option_add_int(&o_display_show_thumbs
, "display_show_thumbs", FALSE
);
99 option_add_int(&o_display_show_headers
, "display_show_headers", TRUE
);
100 option_add_int(&o_display_inherit_options
,
101 "display_inherit_options", FALSE
);
102 option_add_int(&o_filer_change_size_num
, "filer_change_size_num", 30);
104 option_add_notify(options_changed
);
107 /* Draw this icon (including any symlink or mount symbol) inside the
110 void draw_huge_icon(GdkWindow
*window
, GdkRectangle
*area
,
111 DirItem
*item
, MaskedPixmap
*image
, gboolean selected
)
120 width
= image
->huge_width
;
121 height
= image
->huge_height
;
122 image_x
= area
->x
+ ((area
->width
- width
) >> 1);
123 image_y
= MAX(0, area
->height
- height
- 6);
125 gdk_pixbuf_render_to_drawable_alpha(
126 selected
? image
->huge_pixbuf_lit
127 : image
->huge_pixbuf
,
130 image_x
, area
->y
+ image_y
, /* dest */
132 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
133 GDK_RGB_DITHER_NORMAL
, 0, 0);
135 if (item
->flags
& ITEM_FLAG_SYMLINK
)
137 gdk_pixbuf_render_to_drawable_alpha(im_symlink
->pixbuf
,
140 image_x
, area
->y
+ 2, /* dest */
142 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
143 GDK_RGB_DITHER_NORMAL
, 0, 0);
145 if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
147 MaskedPixmap
*mp
= item
->flags
& ITEM_FLAG_MOUNTED
151 gdk_pixbuf_render_to_drawable_alpha(mp
->pixbuf
,
154 image_x
, area
->y
+ 2, /* dest */
156 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
157 GDK_RGB_DITHER_NORMAL
, 0, 0);
161 /* Draw this icon (including any symlink or mount symbol) inside the
164 void draw_large_icon(GdkWindow
*window
,
178 width
= MIN(image
->width
, ICON_WIDTH
);
179 height
= MIN(image
->height
, ICON_HEIGHT
);
180 image_x
= area
->x
+ ((area
->width
- width
) >> 1);
181 image_y
= MAX(0, area
->height
- height
- 6);
183 gdk_pixbuf_render_to_drawable_alpha(
184 selected
? image
->pixbuf_lit
: image
->pixbuf
,
187 image_x
, area
->y
+ image_y
, /* dest */
189 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
190 GDK_RGB_DITHER_NORMAL
, 0, 0);
192 if (item
->flags
& ITEM_FLAG_SYMLINK
)
194 gdk_pixbuf_render_to_drawable_alpha(im_symlink
->pixbuf
,
197 image_x
, area
->y
+ 2, /* dest */
199 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
200 GDK_RGB_DITHER_NORMAL
, 0, 0);
202 if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
204 MaskedPixmap
*mp
= item
->flags
& ITEM_FLAG_MOUNTED
208 gdk_pixbuf_render_to_drawable_alpha(mp
->pixbuf
,
211 image_x
, area
->y
+ 2, /* dest */
213 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
214 GDK_RGB_DITHER_NORMAL
, 0, 0);
218 void draw_small_icon(GdkWindow
*window
, GdkRectangle
*area
,
219 DirItem
*item
, MaskedPixmap
*image
, gboolean selected
)
221 int width
, height
, image_x
, image_y
;
226 if (!image
->sm_pixbuf
)
227 pixmap_make_small(image
);
229 width
= MIN(image
->sm_width
, SMALL_WIDTH
);
230 height
= MIN(image
->sm_height
, SMALL_HEIGHT
);
231 image_x
= area
->x
+ ((area
->width
- width
) >> 1);
232 image_y
= MAX(0, SMALL_HEIGHT
- image
->sm_height
);
234 gdk_pixbuf_render_to_drawable_alpha(
235 selected
? image
->sm_pixbuf_lit
: image
->sm_pixbuf
,
238 image_x
, area
->y
+ image_y
, /* dest */
240 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
241 GDK_RGB_DITHER_NORMAL
, 0, 0);
243 if (item
->flags
& ITEM_FLAG_SYMLINK
)
245 gdk_pixbuf_render_to_drawable_alpha(im_symlink
->pixbuf
,
248 image_x
, area
->y
+ 8, /* dest */
250 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
251 GDK_RGB_DITHER_NORMAL
, 0, 0);
253 if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
255 MaskedPixmap
*mp
= item
->flags
& ITEM_FLAG_MOUNTED
259 gdk_pixbuf_render_to_drawable_alpha(mp
->pixbuf
,
262 image_x
+ 2, area
->y
+ 2, /* dest */
264 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
265 GDK_RGB_DITHER_NORMAL
, 0, 0);
269 /* The sort functions aren't called from outside, but they are
270 * passed as arguments to display_set_sort_fn().
273 #define IS_A_DIR(item) (item->base_type == TYPE_DIRECTORY && \
274 !(item->flags & ITEM_FLAG_APPDIR))
277 if (o_display_dirs_first.int_value) { \
278 gboolean id1 = IS_A_DIR(i1); \
279 gboolean id2 = IS_A_DIR(i2); \
280 if (id1 && !id2) return -1; \
281 if (id2 && !id1) return 1; \
284 int sort_by_name(const void *item1
, const void *item2
)
286 const DirItem
*i1
= (DirItem
*) item1
;
287 const DirItem
*i2
= (DirItem
*) item2
;
288 CollateKey
*n1
= i1
->leafname_collate
;
289 CollateKey
*n2
= i2
->leafname_collate
;
294 retval
= collate_key_cmp(n1
, n2
, o_display_caps_first
.int_value
);
296 return retval
? retval
: strcmp(i1
->leafname
, i2
->leafname
);
299 int sort_by_type(const void *item1
, const void *item2
)
301 const DirItem
*i1
= (DirItem
*) item1
;
302 const DirItem
*i2
= (DirItem
*) item2
;
305 int diff
= i1
->base_type
- i2
->base_type
;
308 diff
= (i1
->flags
& ITEM_FLAG_APPDIR
)
309 - (i2
->flags
& ITEM_FLAG_APPDIR
);
311 return diff
> 0 ? 1 : -1;
318 diff
= strcmp(m1
->media_type
, m2
->media_type
);
320 diff
= strcmp(m1
->subtype
, m2
->subtype
);
328 return diff
> 0 ? 1 : -1;
330 return sort_by_name(item1
, item2
);
333 int sort_by_owner(const void *item1
, const void *item2
)
335 const DirItem
*i1
= (DirItem
*) item1
;
336 const DirItem
*i2
= (DirItem
*) item2
;
338 return i1
->uid
< i2
->uid
? -1 :
339 i1
->uid
> i2
->uid
? 1 :
340 sort_by_name(item1
, item2
);
343 int sort_by_group(const void *item1
, const void *item2
)
345 const DirItem
*i1
= (DirItem
*) item1
;
346 const DirItem
*i2
= (DirItem
*) item2
;
348 return i1
->gid
< i2
->gid
? -1 :
349 i1
->gid
> i2
->gid
? 1 :
350 sort_by_name(item1
, item2
);
353 int sort_by_date(const void *item1
, const void *item2
)
355 const DirItem
*i1
= (DirItem
*) item1
;
356 const DirItem
*i2
= (DirItem
*) item2
;
358 /* SORT_DIRS; -- too confusing! */
360 return i1
->mtime
< i2
->mtime
? -1 :
361 i1
->mtime
> i2
->mtime
? 1 :
362 sort_by_name(item1
, item2
);
365 int sort_by_size(const void *item1
, const void *item2
)
367 const DirItem
*i1
= (DirItem
*) item1
;
368 const DirItem
*i2
= (DirItem
*) item2
;
372 return i1
->size
< i2
->size
? -1 :
373 i1
->size
> i2
->size
? 1 :
374 sort_by_name(item1
, item2
);
377 void display_set_sort_type(FilerWindow
*filer_window
, SortType sort_type
,
380 if (filer_window
->sort_type
== sort_type
&&
381 filer_window
->sort_order
== order
)
384 filer_window
->sort_type
= sort_type
;
385 filer_window
->sort_order
= order
;
387 view_sort(filer_window
->view
);
390 /* Change the icon size and style.
391 * force_resize should only be TRUE for new windows.
393 void display_set_layout(FilerWindow
*filer_window
,
396 gboolean force_resize
)
398 g_return_if_fail(filer_window
!= NULL
);
400 display_style_set(filer_window
, style
);
401 display_details_set(filer_window
, details
);
403 /* Recreate layouts because wrapping may have changed */
404 view_style_changed(filer_window
->view
, VIEW_UPDATE_NAME
);
406 if (force_resize
|| o_filer_auto_resize
.int_value
!= RESIZE_NEVER
)
407 view_autosize(filer_window
->view
);
410 /* Set the 'Show Thumbnails' flag for this window */
411 void display_set_thumbs(FilerWindow
*filer_window
, gboolean thumbs
)
413 if (filer_window
->show_thumbs
== thumbs
)
416 filer_window
->show_thumbs
= thumbs
;
418 view_style_changed(filer_window
->view
, VIEW_UPDATE_VIEWDATA
);
421 filer_cancel_thumbnails(filer_window
);
423 filer_set_title(filer_window
);
425 filer_create_thumbs(filer_window
);
428 /* Set the 'Show Hidden' flag for this window */
429 void display_set_hidden(FilerWindow
*filer_window
, gboolean hidden
)
431 if (filer_window
->show_hidden
== hidden
)
434 filer_window
->show_hidden
= hidden
;
436 filer_detach_rescan(filer_window
); /* (updates titlebar) */
438 display_set_actual_size(filer_window
, FALSE
);
441 /* Highlight (wink or cursor) this item in the filer window. If the item
442 * isn't already there but we're scanning then highlight it if it
445 void display_set_autoselect(FilerWindow
*filer_window
, const gchar
*leaf
)
449 g_return_if_fail(filer_window
!= NULL
);
450 g_return_if_fail(leaf
!= NULL
);
452 new = g_strdup(leaf
); /* leaf == old value sometimes */
454 null_g_free(&filer_window
->auto_select
);
456 if (view_autoselect(filer_window
->view
, new))
459 filer_window
->auto_select
= new;
462 /* Change the icon size (wraps) */
463 void display_change_size(FilerWindow
*filer_window
, gboolean bigger
)
467 g_return_if_fail(filer_window
!= NULL
);
469 switch (filer_window
->display_style
)
472 new = bigger
? HUGE_ICONS
: SMALL_ICONS
;
486 display_set_layout(filer_window
, new, filer_window
->details_type
,
490 ViewData
*display_create_viewdata(FilerWindow
*filer_window
, DirItem
*item
)
494 view
= g_new(ViewData
, 1);
497 view
->details
= NULL
;
500 display_update_view(filer_window
, item
, view
, TRUE
);
505 /* Set the display style to the desired style. If the desired style
506 * is AUTO_SIZE_ICONS, choose an appropriate size. Also resizes filer
507 * window, if requested.
509 void display_set_actual_size(FilerWindow
*filer_window
, gboolean force_resize
)
511 display_set_layout(filer_window
, filer_window
->display_style_wanted
,
512 filer_window
->details_type
, force_resize
);
516 /****************************************************************
517 * INTERNAL FUNCTIONS *
518 ****************************************************************/
520 static void options_changed(void)
524 for (next
= all_filer_windows
; next
; next
= next
->next
)
526 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
529 if (o_display_dirs_first
.has_changed
||
530 o_display_caps_first
.has_changed
)
531 view_sort(VIEW(filer_window
->view
));
533 if (o_display_show_headers
.has_changed
)
534 flags
|= VIEW_UPDATE_HEADERS
;
536 if (o_large_width
.has_changed
|| o_small_width
.has_changed
)
537 flags
|= VIEW_UPDATE_NAME
; /* Recreate PangoLayout */
539 view_style_changed(filer_window
->view
, flags
);
543 /* Return a new string giving details of this item, or NULL if details
544 * are not being displayed. If details are not yet available, return
545 * a string of the right length.
547 static char *details(FilerWindow
*filer_window
, DirItem
*item
)
549 mode_t m
= item
->mode
;
551 gboolean scanned
= item
->base_type
!= TYPE_UNKNOWN
;
553 if (filer_window
->details_type
== DETAILS_NONE
)
556 if (scanned
&& item
->lstat_errno
)
557 buf
= g_strdup_printf(_("lstat(2) failed: %s"),
558 g_strerror(item
->lstat_errno
));
559 else if (filer_window
->details_type
== DETAILS_TYPE
)
561 MIME_type
*type
= item
->mime_type
;
564 return g_strdup("application/octet-stream");
566 buf
= g_strdup_printf("%s/%s",
567 type
->media_type
, type
->subtype
);
569 else if (filer_window
->details_type
== DETAILS_TIMES
)
571 guchar
*ctime
, *mtime
, *atime
;
573 ctime
= pretty_time(&item
->ctime
);
574 mtime
= pretty_time(&item
->mtime
);
575 atime
= pretty_time(&item
->atime
);
577 buf
= g_strdup_printf("a[%s] c[%s] m[%s]", atime
, ctime
, mtime
);
582 else if (filer_window
->details_type
== DETAILS_PERMISSIONS
)
585 return g_strdup("---,---,---/--"
589 " 12345678 12345678");
591 buf
= g_strdup_printf("%s %-8.8s %-8.8s",
592 pretty_permissions(m
),
593 user_name(item
->uid
),
594 group_name(item
->gid
));
600 if (filer_window
->display_style
== SMALL_ICONS
)
601 return g_strdup("1234M");
603 return g_strdup("1234 bytes");
606 if (item
->base_type
!= TYPE_DIRECTORY
)
608 if (filer_window
->display_style
== SMALL_ICONS
)
609 buf
= g_strdup(format_size_aligned(item
->size
));
611 buf
= g_strdup(format_size(item
->size
));
620 /* Note: Call style_changed after this */
621 static void display_details_set(FilerWindow
*filer_window
, DetailsType details
)
623 filer_window
->details_type
= details
;
626 /* Note: Call style_changed after this */
627 static void display_style_set(FilerWindow
*filer_window
, DisplayStyle style
)
629 filer_window
->display_style_wanted
= style
;
630 display_set_actual_size_real(filer_window
);
633 /* Each displayed item has a ViewData structure with some cached information
634 * to help quickly draw the item (eg, the PangoLayout). This function updates
637 void display_update_view(FilerWindow
*filer_window
,
640 gboolean update_name_layout
)
642 DisplayStyle style
= filer_window
->display_style
;
646 static PangoFontDescription
*monospace
= NULL
;
647 PangoAttrList
*list
= NULL
;
650 monospace
= pango_font_description_from_string("monospace");
654 g_object_unref(G_OBJECT(view
->details
));
655 view
->details
= NULL
;
658 str
= details(filer_window
, item
);
661 PangoAttrList
*details_list
;
662 int perm_offset
= -1;
664 view
->details
= gtk_widget_create_pango_layout(
665 filer_window
->window
, str
);
668 pango_layout_set_font_description(view
->details
, monospace
);
669 pango_layout_get_size(view
->details
, &w
, &h
);
670 view
->details_width
= w
/ PANGO_SCALE
;
671 view
->details_height
= h
/ PANGO_SCALE
;
673 if (filer_window
->details_type
== DETAILS_PERMISSIONS
)
675 if (perm_offset
> -1)
677 PangoAttribute
*attr
;
679 attr
= pango_attr_underline_new(PANGO_UNDERLINE_SINGLE
);
681 perm_offset
+= 4 * applicable(item
->uid
, item
->gid
);
682 attr
->start_index
= perm_offset
;
683 attr
->end_index
= perm_offset
+ 3;
685 details_list
= pango_attr_list_new();
686 pango_attr_list_insert(details_list
, attr
);
687 pango_layout_set_attributes(view
->details
,
694 g_object_unref(view
->image
);
698 if (filer_window
->show_thumbs
&& item
->base_type
== TYPE_FILE
&&
699 strcmp(item
->mime_type
->media_type
, "image") == 0)
703 path
= make_path(filer_window
->real_path
, item
->leafname
);
705 view
->image
= g_fscache_lookup_full(pixmap_cache
, path
,
706 FSCACHE_LOOKUP_ONLY_NEW
, NULL
);
711 view
->image
= item
->image
;
713 g_object_ref(view
->image
);
716 if (view
->layout
&& update_name_layout
)
718 g_object_unref(G_OBJECT(view
->layout
));
726 else if (g_utf8_validate(item
->leafname
, -1, NULL
))
728 view
->layout
= gtk_widget_create_pango_layout(
729 filer_window
->window
, item
->leafname
);
733 PangoAttribute
*attr
;
736 utf8
= to_utf8(item
->leafname
);
737 view
->layout
= gtk_widget_create_pango_layout(
738 filer_window
->window
, utf8
);
741 attr
= pango_attr_foreground_new(0xffff, 0, 0);
742 attr
->start_index
= 0;
743 attr
->end_index
= -1;
745 list
= pango_attr_list_new();
746 pango_attr_list_insert(list
, attr
);
749 if (item
->flags
& ITEM_FLAG_RECENT
)
751 PangoAttribute
*attr
;
753 attr
= pango_attr_weight_new(PANGO_WEIGHT_BOLD
);
754 attr
->start_index
= 0;
755 attr
->end_index
= -1;
757 list
= pango_attr_list_new();
758 pango_attr_list_insert(list
, attr
);
762 pango_layout_set_attributes(view
->layout
, list
);
764 if (filer_window
->details_type
== DETAILS_NONE
)
766 if (style
== HUGE_ICONS
)
767 wrap_width
= HUGE_WRAP
* PANGO_SCALE
;
768 else if (style
== LARGE_ICONS
)
769 wrap_width
= o_large_width
.int_value
* PANGO_SCALE
;
772 #ifdef USE_PANGO_WRAP_WORD_CHAR
773 pango_layout_set_wrap(view
->layout
, PANGO_WRAP_WORD_CHAR
);
775 if (wrap_width
!= -1)
776 pango_layout_set_width(view
->layout
, wrap_width
);
778 pango_layout_get_size(view
->layout
, &w
, &h
);
779 view
->name_width
= w
/ PANGO_SCALE
;
780 view
->name_height
= h
/ PANGO_SCALE
;
783 /* Sets display_style from display_style_wanted.
784 * See also display_set_actual_size().
786 static void display_set_actual_size_real(FilerWindow
*filer_window
)
788 DisplayStyle size
= filer_window
->display_style_wanted
;
791 g_return_if_fail(filer_window
!= NULL
);
793 if (size
== AUTO_SIZE_ICONS
)
795 n
= view_count_items(filer_window
->view
);
797 if (n
>= o_filer_change_size_num
.int_value
)
803 filer_window
->display_style
= size
;