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 /* display.c - code for arranging and displaying file items */
31 #include <sys/param.h>
38 #include <gdk/gdkkeysyms.h>
44 #include "collection.h"
46 #include "gui_support.h"
56 #include "minibuffer.h"
61 #define ROW_HEIGHT_SMALL 20
62 #define ROW_HEIGHT_FULL_INFO 44
63 #define MIN_ITEM_WIDTH 64
65 #define MIN_TRUNCATE 0
66 #define MAX_TRUNCATE 250
68 #define HUGE_WRAP (1.5 * o_large_width.int_value)
71 static Option o_intelligent_sort
;
72 static Option o_display_dirs_first
;
73 Option o_display_size
;
74 Option o_display_details
;
75 Option o_display_sort_by
;
76 static Option o_large_width
;
77 static Option o_small_width
;
78 Option o_display_show_hidden
;
79 Option o_display_show_thumbs
;
80 Option o_display_inherit_options
;
83 /* GC for drawing colour filenames */
84 static GdkGC
*type_gc
= NULL
;
86 typedef struct _Template Template
;
90 GdkRectangle leafname
;
94 #define SHOW_RECT(ite, template) \
95 g_print("%s: %dx%d+%d+%d %dx%d+%d+%d\n", \
97 (template)->leafname.width, (template)->leafname.height,\
98 (template)->leafname.x, (template)->leafname.y, \
99 (template)->icon.width, (template)->icon.height, \
100 (template)->icon.x, (template)->icon.y)
102 /* Static prototypes */
103 static void fill_template(GdkRectangle
*area
, CollectionItem
*item
,
104 FilerWindow
*filer_window
, Template
*template);
105 static void huge_template(GdkRectangle
*area
, CollectionItem
*colitem
,
106 FilerWindow
*filer_window
, Template
*template);
107 static void large_template(GdkRectangle
*area
, CollectionItem
*colitem
,
108 FilerWindow
*filer_window
, Template
*template);
109 static void small_template(GdkRectangle
*area
, CollectionItem
*colitem
,
110 FilerWindow
*filer_window
, Template
*template);
111 static void huge_full_template(GdkRectangle
*area
, CollectionItem
*colitem
,
112 FilerWindow
*filer_window
, Template
*template);
113 static void large_full_template(GdkRectangle
*area
, CollectionItem
*colitem
,
114 FilerWindow
*filer_window
, Template
*template);
115 static void small_full_template(GdkRectangle
*area
, CollectionItem
*colitem
,
116 FilerWindow
*filer_window
, Template
*template);
117 static void draw_item(GtkWidget
*widget
,
118 CollectionItem
*item
,
120 FilerWindow
*filer_window
);
121 static gboolean
test_point(Collection
*collection
,
122 int point_x
, int point_y
,
123 CollectionItem
*item
,
124 int width
, int height
,
125 FilerWindow
*filer_window
);
126 static void display_details_set(FilerWindow
*filer_window
, DetailsType details
);
127 static void display_style_set(FilerWindow
*filer_window
, DisplayStyle style
);
128 static void options_changed(void);
129 static char *details(FilerWindow
*filer_window
, DirItem
*item
);
130 static void draw_string(GtkWidget
*widget
,
132 GdkRectangle
*area
, /* Area available on screen */
133 int width
, /* Width of the full string */
134 GtkStateType selection_state
,
145 /****************************************************************
146 * EXTERNAL INTERFACE *
147 ****************************************************************/
151 option_add_int(&o_intelligent_sort
, "display_intelligent_sort", 1);
152 option_add_int(&o_display_dirs_first
, "display_dirs_first", FALSE
);
153 option_add_int(&o_display_size
, "display_size", LARGE_ICONS
);
154 option_add_int(&o_display_details
, "display_details", DETAILS_NONE
);
155 option_add_int(&o_display_sort_by
, "display_sort_by", SORT_BY_NAME
);
156 option_add_int(&o_large_width
, "display_large_width", 89);
157 option_add_int(&o_small_width
, "display_small_width", 250);
158 option_add_int(&o_display_show_hidden
, "display_show_hidden", FALSE
);
159 option_add_int(&o_display_show_thumbs
, "display_show_thumbs", FALSE
);
160 option_add_int(&o_display_inherit_options
,"display_inherit_options",
162 option_add_notify(options_changed
);
165 /* A template contains the locations of the three rectangles (for the icon,
166 * name and extra details).
167 * Fill in the empty 'template' with the rectanges for this item.
169 static void fill_template(GdkRectangle
*area
, CollectionItem
*colitem
,
170 FilerWindow
*filer_window
, Template
*template)
172 DisplayStyle style
= filer_window
->display_style
;
173 ViewData
*view
= (ViewData
*) colitem
->view_data
;
177 template->details
.width
= view
->details_width
;
178 template->details
.height
= view
->details_height
;
180 if (style
== SMALL_ICONS
)
181 small_full_template(area
, colitem
,
182 filer_window
, template);
183 else if (style
== LARGE_ICONS
)
184 large_full_template(area
, colitem
,
185 filer_window
, template);
187 huge_full_template(area
, colitem
,
188 filer_window
, template);
192 if (style
== HUGE_ICONS
)
193 huge_template(area
, colitem
, filer_window
, template);
194 else if (style
== LARGE_ICONS
)
195 large_template(area
, colitem
, filer_window
, template);
197 small_template(area
, colitem
, filer_window
, template);
201 /* Return the size needed for this item */
202 void calc_size(FilerWindow
*filer_window
, CollectionItem
*colitem
,
203 int *width
, int *height
)
205 int pix_width
, pix_height
;
207 DisplayStyle style
= filer_window
->display_style
;
208 ViewData
*view
= (ViewData
*) colitem
->view_data
;
210 if (filer_window
->details_type
== DETAILS_NONE
)
212 if (style
== HUGE_ICONS
)
216 if (!view
->image
->huge_pixbuf
)
217 pixmap_make_huge(view
->image
);
218 pix_width
= view
->image
->huge_width
;
219 pix_height
= view
->image
->huge_height
;
223 pix_width
= HUGE_WIDTH
* 3 / 2;
224 pix_height
= HUGE_HEIGHT
* 3 / 2;
226 *width
= MAX(pix_width
, view
->name_width
) + 4;
227 *height
= view
->name_height
+ pix_height
+ 4;
229 else if (style
== SMALL_ICONS
)
231 w
= MIN(view
->name_width
, o_small_width
.int_value
);
232 *width
= SMALL_WIDTH
+ 12 + w
;
233 *height
= MAX(view
->name_height
, SMALL_HEIGHT
) + 4;
238 pix_width
= view
->image
->width
;
240 pix_width
= HUGE_WIDTH
* 3 / 2;
241 *width
= MAX(pix_width
, view
->name_width
) + 4;
242 *height
= view
->name_height
+ ICON_HEIGHT
+ 2;
247 w
= view
->details_width
;
248 if (style
== HUGE_ICONS
)
250 *width
= HUGE_WIDTH
+ 12 + MAX(w
, view
->name_width
);
251 *height
= HUGE_HEIGHT
- 4;
253 else if (style
== SMALL_ICONS
)
257 *width
= SMALL_WIDTH
+ view
->name_width
+ 12 + w
;
258 text_height
= MAX(view
->name_height
,
259 view
->details_height
);
260 *height
= MAX(text_height
, SMALL_HEIGHT
) + 4;
264 *width
= ICON_WIDTH
+ 12 + MAX(w
, view
->name_width
);
265 *height
= ICON_HEIGHT
;
270 /* Draw this icon (including any symlink or mount symbol) inside the
273 void draw_huge_icon(GtkWidget
*widget
,
286 width
= image
->huge_width
;
287 height
= image
->huge_height
;
288 image_x
= area
->x
+ ((area
->width
- width
) >> 1);
289 image_y
= MAX(0, area
->height
- height
- 6);
291 gdk_pixbuf_render_to_drawable_alpha(
292 selected
? image
->huge_pixbuf_lit
293 : image
->huge_pixbuf
,
296 image_x
, area
->y
+ image_y
, /* dest */
298 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
299 GDK_RGB_DITHER_NORMAL
, 0, 0);
301 if (item
->flags
& ITEM_FLAG_SYMLINK
)
303 gdk_pixbuf_render_to_drawable_alpha(im_symlink
->pixbuf
,
306 image_x
, area
->y
+ 2, /* dest */
308 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
309 GDK_RGB_DITHER_NORMAL
, 0, 0);
311 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
313 MaskedPixmap
*mp
= item
->flags
& ITEM_FLAG_MOUNTED
317 gdk_pixbuf_render_to_drawable_alpha(mp
->pixbuf
,
320 image_x
, area
->y
+ 2, /* dest */
322 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
323 GDK_RGB_DITHER_NORMAL
, 0, 0);
327 /* Draw this icon (including any symlink or mount symbol) inside the
330 void draw_large_icon(GtkWidget
*widget
,
344 width
= MIN(image
->width
, ICON_WIDTH
);
345 height
= MIN(image
->height
, ICON_HEIGHT
);
346 image_x
= area
->x
+ ((area
->width
- width
) >> 1);
347 image_y
= MAX(0, area
->height
- height
- 6);
349 gdk_pixbuf_render_to_drawable_alpha(
350 selected
? image
->pixbuf_lit
: image
->pixbuf
,
353 image_x
, area
->y
+ image_y
, /* dest */
355 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
356 GDK_RGB_DITHER_NORMAL
, 0, 0);
358 if (item
->flags
& ITEM_FLAG_SYMLINK
)
360 gdk_pixbuf_render_to_drawable_alpha(im_symlink
->pixbuf
,
363 image_x
, area
->y
+ 2, /* dest */
365 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
366 GDK_RGB_DITHER_NORMAL
, 0, 0);
368 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
370 MaskedPixmap
*mp
= item
->flags
& ITEM_FLAG_MOUNTED
374 gdk_pixbuf_render_to_drawable_alpha(mp
->pixbuf
,
377 image_x
, area
->y
+ 2, /* dest */
379 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
380 GDK_RGB_DITHER_NORMAL
, 0, 0);
384 /* The sort functions aren't called from outside, but they are
385 * passed as arguments to display_set_sort_fn().
388 #define IS_A_DIR(item) (item->base_type == TYPE_DIRECTORY && \
389 !(item->flags & ITEM_FLAG_APPDIR))
392 if (o_display_dirs_first.int_value) { \
393 gboolean id1 = IS_A_DIR(i1); \
394 gboolean id2 = IS_A_DIR(i2); \
395 if (id1 && !id2) return -1; \
396 if (id2 && !id1) return 1; \
399 int sort_by_name(const void *item1
, const void *item2
)
401 const DirItem
*i1
= (DirItem
*) ((CollectionItem
*) item1
)->data
;
402 const DirItem
*i2
= (DirItem
*) ((CollectionItem
*) item2
)->data
;
403 char *n1
= i1
->leafname_collate
;
404 char *n2
= i2
->leafname_collate
;
408 if (!o_intelligent_sort
.int_value
)
409 return strcmp(i1
->leafname
, i2
->leafname
);
411 /* The following code was copied from PicoGUI (was LGPL) */
413 /* Sort the files, in a way that should make sense to users.
414 * Case is ignored, punctuation is ignored. If the file contains
415 * numbers, the numbers are sorted numerically.
419 char c1
= *n1
, c2
= *n2
;
421 /* If they are both numbers, sort them numerically */
422 if (isdigit(c1
) && isdigit(c2
))
426 u1
= strtoul(n1
, &p
, 10);
428 u2
= strtoul(n2
, &p
, 10);
437 /* Do a case-insensitive asciibetical sort */
450 return 1; /* First string is longer, so comes after */
454 /* If the strings are equal at the end of this, fallback to a straight
455 * ASCII sort so at least it's deterministic.
457 return strcmp(i1
->leafname
, i2
->leafname
);
460 int sort_by_type(const void *item1
, const void *item2
)
462 const DirItem
*i1
= (DirItem
*) ((CollectionItem
*) item1
)->data
;
463 const DirItem
*i2
= (DirItem
*) ((CollectionItem
*) item2
)->data
;
466 int diff
= i1
->base_type
- i2
->base_type
;
469 diff
= (i1
->flags
& ITEM_FLAG_APPDIR
)
470 - (i2
->flags
& ITEM_FLAG_APPDIR
);
472 return diff
> 0 ? 1 : -1;
479 diff
= strcmp(m1
->media_type
, m2
->media_type
);
481 diff
= strcmp(m1
->subtype
, m2
->subtype
);
489 return diff
> 0 ? 1 : -1;
491 return sort_by_name(item1
, item2
);
494 int sort_by_date(const void *item1
, const void *item2
)
496 const DirItem
*i1
= (DirItem
*) ((CollectionItem
*) item1
)->data
;
497 const DirItem
*i2
= (DirItem
*) ((CollectionItem
*) item2
)->data
;
501 return i1
->mtime
> i2
->mtime
? -1 :
502 i1
->mtime
< i2
->mtime
? 1 :
503 sort_by_name(item1
, item2
);
506 int sort_by_size(const void *item1
, const void *item2
)
508 const DirItem
*i1
= (DirItem
*) ((CollectionItem
*) item1
)->data
;
509 const DirItem
*i2
= (DirItem
*) ((CollectionItem
*) item2
)->data
;
513 return i1
->size
> i2
->size
? -1 :
514 i1
->size
< i2
->size
? 1 :
515 sort_by_name(item1
, item2
);
518 /* Make the items as small as possible */
519 void shrink_grid(FilerWindow
*filer_window
)
522 Collection
*col
= filer_window
->collection
;
523 int width
= MIN_ITEM_WIDTH
;
524 int height
= SMALL_HEIGHT
;
526 for (i
= 0; i
< col
->number_of_items
; i
++)
530 calc_size(filer_window
, &col
->items
[i
], &w
, &h
);
537 collection_set_item_size(col
, width
, height
);
540 void display_set_sort_fn(FilerWindow
*filer_window
,
541 int (*fn
)(const void *a
, const void *b
))
543 if (filer_window
->sort_fn
== fn
)
546 filer_window
->sort_fn
= fn
;
548 collection_qsort(filer_window
->collection
,
549 filer_window
->sort_fn
);
552 void display_set_layout(FilerWindow
*filer_window
,
556 g_return_if_fail(filer_window
!= NULL
);
558 display_style_set(filer_window
, style
);
559 display_details_set(filer_window
, details
);
561 shrink_grid(filer_window
);
563 if (o_filer_auto_resize
.int_value
!= RESIZE_NEVER
)
564 filer_window_autosize(filer_window
, TRUE
);
567 /* Set the 'Show Thumbnails' flag for this window */
568 void display_set_thumbs(FilerWindow
*filer_window
, gboolean thumbs
)
570 if (filer_window
->show_thumbs
== thumbs
)
573 filer_window
->show_thumbs
= thumbs
;
575 display_update_views(filer_window
);
577 gtk_widget_queue_draw(GTK_WIDGET(filer_window
->collection
));
580 filer_cancel_thumbnails(filer_window
);
582 filer_set_title(filer_window
);
584 filer_create_thumbs(filer_window
);
587 /* Set the 'Show Hidden' flag for this window */
588 void display_set_hidden(FilerWindow
*filer_window
, gboolean hidden
)
590 if (filer_window
->show_hidden
== hidden
)
593 filer_window
->show_hidden
= hidden
;
595 filer_detach_rescan(filer_window
); /* (updates titlebar) */
598 /* Highlight (wink or cursor) this item in the filer window. If the item
599 * isn't already there but we're scanning then highlight it if it
602 void display_set_autoselect(FilerWindow
*filer_window
, const gchar
*leaf
)
607 g_return_if_fail(filer_window
!= NULL
);
608 col
= filer_window
->collection
;
610 for (i
= 0; i
< col
->number_of_items
; i
++)
612 DirItem
*item
= (DirItem
*) col
->items
[i
].data
;
614 if (strcmp(item
->leafname
, leaf
) == 0)
616 if (col
->cursor_item
!= -1)
617 collection_set_cursor_item(col
, i
);
619 collection_wink_item(col
, i
);
626 g_free(filer_window
->auto_select
);
628 filer_window
->auto_select
= g_strdup(leaf
);
630 filer_window
->auto_select
= NULL
;
633 gboolean
display_is_truncated(FilerWindow
*filer_window
, int i
)
636 Collection
*collection
= filer_window
->collection
;
637 CollectionItem
*colitem
= &collection
->items
[i
];
638 int col
= i
% collection
->columns
;
639 int row
= i
/ collection
->columns
;
641 ViewData
*view
= (ViewData
*) colitem
->view_data
;
643 if (filer_window
->display_style
== LARGE_ICONS
||
644 filer_window
->display_style
== HUGE_ICONS
)
645 return FALSE
; /* These wrap rather than truncate */
647 area
.x
= col
* collection
->item_width
;
648 area
.y
= row
* collection
->item_height
;
649 area
.height
= collection
->item_height
;
651 if (col
== collection
->columns
- 1)
652 area
.width
= GTK_WIDGET(collection
)->allocation
.width
- area
.x
;
654 area
.width
= collection
->item_width
;
656 fill_template(&area
, colitem
, filer_window
, &template);
658 return template.leafname
.width
< view
->name_width
;
661 /* Change the icon size (wraps) */
662 void display_change_size(FilerWindow
*filer_window
, gboolean bigger
)
666 g_return_if_fail(filer_window
!= NULL
);
668 switch (filer_window
->display_style
)
671 new = bigger
? HUGE_ICONS
: SMALL_ICONS
;
674 new = bigger
? SMALL_ICONS
: LARGE_ICONS
;
677 new = bigger
? LARGE_ICONS
: HUGE_ICONS
;
681 display_set_layout(filer_window
, new, filer_window
->details_type
);
684 ViewData
*display_create_viewdata(FilerWindow
*filer_window
, DirItem
*item
)
688 view
= g_new(ViewData
, 1);
691 view
->details
= NULL
;
694 display_update_view(filer_window
, item
, view
, TRUE
);
699 void display_free_colitem(Collection
*collection
, CollectionItem
*colitem
)
701 ViewData
*view
= (ViewData
*) colitem
->view_data
;
708 g_object_unref(G_OBJECT(view
->layout
));
712 g_object_unref(G_OBJECT(view
->details
));
715 g_object_unref(view
->image
);
720 /* Recalculate all the ViewData structs for this window.
721 * Useful when the display style has changed.
723 void display_update_views(FilerWindow
*filer_window
)
725 Collection
*collection
= filer_window
->collection
;
728 for (i
= 0; i
< collection
->number_of_items
; i
++)
730 CollectionItem
*ci
= &collection
->items
[i
];
732 display_update_view(filer_window
, (DirItem
*) ci
->data
,
733 (ViewData
*) ci
->view_data
, TRUE
);
737 /****************************************************************
738 * INTERNAL FUNCTIONS *
739 ****************************************************************/
741 static void options_changed(void)
745 for (next
= all_filer_windows
; next
; next
= next
->next
)
747 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
749 if (o_large_width
.has_changed
|| o_small_width
.has_changed
)
751 /* Recreate PangoLayout */
752 display_update_views(filer_window
);
755 if (o_intelligent_sort
.has_changed
||
756 o_display_dirs_first
.has_changed
)
758 collection_qsort(filer_window
->collection
,
759 filer_window
->sort_fn
);
761 shrink_grid(filer_window
);
762 gtk_widget_queue_draw(filer_window
->window
);
766 static void huge_template(GdkRectangle
*area
, CollectionItem
*colitem
,
767 FilerWindow
*filer_window
, Template
*template)
769 int col_width
= filer_window
->collection
->item_width
;
771 ViewData
*view
= (ViewData
*) colitem
->view_data
;
772 MaskedPixmap
*image
= view
->image
;
776 if (!image
->huge_pixbuf
)
777 pixmap_make_huge(image
);
778 template->icon
.width
= image
->huge_width
;
779 template->icon
.height
= image
->huge_height
;
783 template->icon
.width
= HUGE_WIDTH
* 3 / 2;
784 template->icon
.height
= HUGE_HEIGHT
;
787 template->leafname
.width
= view
->name_width
;
788 template->leafname
.height
= view
->name_height
;
790 text_x
= area
->x
+ ((col_width
- template->leafname
.width
) >> 1);
791 text_y
= area
->y
+ area
->height
- template->leafname
.height
;
793 template->leafname
.x
= text_x
;
794 template->leafname
.y
= text_y
;
796 template->icon
.x
= area
->x
+ ((col_width
- template->icon
.width
) >> 1);
797 template->icon
.y
= template->leafname
.y
- template->icon
.height
- 2;
800 static void large_template(GdkRectangle
*area
, CollectionItem
*colitem
,
801 FilerWindow
*filer_window
, Template
*template)
803 int col_width
= filer_window
->collection
->item_width
;
807 ViewData
*view
= (ViewData
*) colitem
->view_data
;
808 MaskedPixmap
*image
= view
->image
;
814 iwidth
= MIN(image
->width
, ICON_WIDTH
);
815 iheight
= MIN(image
->height
+ 6, ICON_HEIGHT
);
820 iheight
= ICON_HEIGHT
;
822 image_x
= area
->x
+ ((col_width
- iwidth
) >> 1);
824 template->leafname
.width
= view
->name_width
;
825 template->leafname
.height
= view
->name_height
;
827 text_x
= area
->x
+ ((col_width
- template->leafname
.width
) >> 1);
828 text_y
= area
->y
+ ICON_HEIGHT
+ 2;
830 template->leafname
.x
= text_x
;
831 template->leafname
.y
= text_y
;
833 image_y
= text_y
- iheight
;
834 image_y
= MAX(area
->y
, image_y
);
836 template->icon
.x
= image_x
;
837 template->icon
.y
= image_y
;
838 template->icon
.width
= iwidth
;
839 template->icon
.height
= MIN(ICON_HEIGHT
, iheight
);
842 static void small_template(GdkRectangle
*area
, CollectionItem
*colitem
,
843 FilerWindow
*filer_window
, Template
*template)
845 int text_x
= area
->x
+ SMALL_WIDTH
+ 4;
847 int max_text_width
= area
->width
- SMALL_WIDTH
- 4;
848 ViewData
*view
= (ViewData
*) colitem
->view_data
;
850 low_text_y
= area
->y
+ area
->height
/ 2 - view
->name_height
/ 2;
852 template->leafname
.x
= text_x
;
853 template->leafname
.y
= low_text_y
;
854 template->leafname
.width
= MIN(max_text_width
, view
->name_width
);
855 template->leafname
.height
= view
->name_height
;
857 template->icon
.x
= area
->x
;
858 template->icon
.y
= area
->y
+ 1;
859 template->icon
.width
= SMALL_WIDTH
;
860 template->icon
.height
= SMALL_HEIGHT
;
863 static void huge_full_template(GdkRectangle
*area
, CollectionItem
*colitem
,
864 FilerWindow
*filer_window
, Template
*template)
866 int max_text_width
= area
->width
- HUGE_WIDTH
- 4;
867 ViewData
*view
= (ViewData
*) colitem
->view_data
;
868 MaskedPixmap
*image
= view
->image
;
872 if (!image
->huge_pixbuf
)
873 pixmap_make_huge(image
);
874 template->icon
.width
= image
->huge_width
;
875 template->icon
.height
= image
->huge_height
;
879 template->icon
.width
= HUGE_WIDTH
* 3 / 2;
880 template->icon
.height
= HUGE_HEIGHT
;
883 template->icon
.x
= area
->x
+ (HUGE_WIDTH
- template->icon
.width
) / 2;
884 template->icon
.y
= area
->y
+ (area
->height
- template->icon
.height
) / 2;
886 template->leafname
.x
= area
->x
+ HUGE_WIDTH
+ 4;
887 template->leafname
.y
= area
->y
+ area
->height
/ 2
888 - (view
->name_height
+ 2 + view
->details_height
) / 2;
889 template->leafname
.width
= MIN(max_text_width
, view
->name_width
);
890 template->leafname
.height
= view
->name_height
;
893 return; /* Not scanned yet */
895 template->details
.x
= template->leafname
.x
;
896 template->details
.y
= template->leafname
.y
+ view
->name_height
+ 2;
899 static void large_full_template(GdkRectangle
*area
, CollectionItem
*colitem
,
900 FilerWindow
*filer_window
, Template
*template)
902 int max_text_width
= area
->width
- ICON_WIDTH
- 4;
903 ViewData
*view
= (ViewData
*) colitem
->view_data
;
904 MaskedPixmap
*image
= view
->image
;
908 template->icon
.width
= image
->width
;
909 template->icon
.height
= image
->height
;
913 template->icon
.width
= ICON_WIDTH
;
914 template->icon
.height
= ICON_HEIGHT
;
917 template->icon
.x
= area
->x
+ (ICON_WIDTH
- template->icon
.width
) / 2;
918 template->icon
.y
= area
->y
+ (area
->height
- template->icon
.height
) / 2;
921 template->leafname
.x
= area
->x
+ ICON_WIDTH
+ 4;
922 template->leafname
.y
= area
->y
+ area
->height
/ 2
923 - (view
->name_height
+ 2 + view
->details_height
) / 2;
924 template->leafname
.width
= MIN(max_text_width
, view
->name_width
);
925 template->leafname
.height
= view
->name_height
;
928 return; /* Not scanned yet */
930 template->details
.x
= template->leafname
.x
;
931 template->details
.y
= template->leafname
.y
+ view
->name_height
+ 2;
934 static void small_full_template(GdkRectangle
*area
, CollectionItem
*colitem
,
935 FilerWindow
*filer_window
, Template
*template)
937 int col_width
= filer_window
->collection
->item_width
;
938 ViewData
*view
= (ViewData
*) colitem
->view_data
;
940 small_template(area
, colitem
, filer_window
, template);
943 return; /* Not scanned yet */
945 template->details
.x
= area
->x
+ col_width
- template->details
.width
;
946 template->details
.y
= area
->y
+ area
->height
/ 2 - \
947 view
->details_height
/ 2;
950 #define INSIDE(px, py, area) \
951 (px >= area.x && py >= area.y && \
952 px <= area.x + area.width && py <= area.y + area.height)
954 static gboolean
test_point(Collection
*collection
,
955 int point_x
, int point_y
,
956 CollectionItem
*colitem
,
957 int width
, int height
,
958 FilerWindow
*filer_window
)
962 ViewData
*view
= (ViewData
*) colitem
->view_data
;
967 area
.height
= height
;
969 fill_template(&area
, colitem
, filer_window
, &template);
971 return INSIDE(point_x
, point_y
, template.leafname
) ||
972 INSIDE(point_x
, point_y
, template.icon
) ||
973 (view
->details
&& INSIDE(point_x
, point_y
, template.details
));
976 static void draw_small_icon(GtkWidget
*widget
,
982 int width
, height
, image_x
, image_y
;
987 if (!image
->sm_pixbuf
)
988 pixmap_make_small(image
);
990 width
= MIN(image
->sm_width
, SMALL_WIDTH
);
991 height
= MIN(image
->sm_height
, SMALL_HEIGHT
);
992 image_x
= area
->x
+ ((area
->width
- width
) >> 1);
993 image_y
= MAX(0, SMALL_HEIGHT
- image
->sm_height
);
995 gdk_pixbuf_render_to_drawable_alpha(
996 selected
? image
->sm_pixbuf_lit
: image
->sm_pixbuf
,
999 image_x
, area
->y
+ image_y
, /* dest */
1001 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
1002 GDK_RGB_DITHER_NORMAL
, 0, 0);
1004 if (item
->flags
& ITEM_FLAG_SYMLINK
)
1006 gdk_pixbuf_render_to_drawable_alpha(im_symlink
->pixbuf
,
1009 image_x
, area
->y
+ 8, /* dest */
1011 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
1012 GDK_RGB_DITHER_NORMAL
, 0, 0);
1014 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
1016 MaskedPixmap
*mp
= item
->flags
& ITEM_FLAG_MOUNTED
1020 gdk_pixbuf_render_to_drawable_alpha(mp
->pixbuf
,
1023 image_x
+ 2, area
->y
+ 2, /* dest */
1025 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
1026 GDK_RGB_DITHER_NORMAL
, 0, 0);
1030 /* Return a new string giving details of this item, or NULL if details
1031 * are not being displayed. If details are not yet available, return
1032 * a string of the right length.
1034 static char *details(FilerWindow
*filer_window
, DirItem
*item
)
1036 mode_t m
= item
->mode
;
1038 gboolean scanned
= item
->base_type
!= TYPE_UNKNOWN
;
1040 if (filer_window
->details_type
== DETAILS_NONE
)
1043 if (scanned
&& item
->lstat_errno
)
1044 buf
= g_strdup_printf(_("lstat(2) failed: %s"),
1045 g_strerror(item
->lstat_errno
));
1046 else if (filer_window
->details_type
== DETAILS_SUMMARY
)
1051 return g_strdup("XXXX ---,---,---/--"
1055 " 12345678 12345678 "
1056 "1234M 00:00:00 01 Mmm Yyyy");
1058 time
= pretty_time(&item
->mtime
);
1060 buf
= g_strdup_printf("%s %s %-8.8s %-8.8s %s %s",
1061 item
->flags
& ITEM_FLAG_APPDIR
? "App " :
1062 S_ISDIR(m
) ? "Dir " :
1063 S_ISCHR(m
) ? "Char" :
1064 S_ISBLK(m
) ? "Blck" :
1065 S_ISLNK(m
) ? "Link" :
1066 S_ISSOCK(m
) ? "Sock" :
1067 S_ISFIFO(m
) ? "Pipe" : "File",
1068 pretty_permissions(m
),
1069 user_name(item
->uid
),
1070 group_name(item
->gid
),
1071 format_size_aligned(item
->size
),
1075 else if (filer_window
->details_type
== DETAILS_TYPE
)
1077 MIME_type
*type
= item
->mime_type
;
1080 return g_strdup("(application/octet-stream)");
1082 buf
= g_strdup_printf("(%s/%s)",
1083 type
->media_type
, type
->subtype
);
1085 else if (filer_window
->details_type
== DETAILS_TIMES
)
1087 guchar
*ctime
, *mtime
, *atime
;
1089 ctime
= pretty_time(&item
->ctime
);
1090 mtime
= pretty_time(&item
->mtime
);
1091 atime
= pretty_time(&item
->atime
);
1093 buf
= g_strdup_printf("a[%s] c[%s] m[%s]", atime
, ctime
, mtime
);
1098 else if (filer_window
->details_type
== DETAILS_PERMISSIONS
)
1101 return g_strdup("---,---,---/--"
1105 " 12345678 12345678");
1107 buf
= g_strdup_printf("%s %-8.8s %-8.8s",
1108 pretty_permissions(m
),
1109 user_name(item
->uid
),
1110 group_name(item
->gid
));
1116 if (filer_window
->display_style
== SMALL_ICONS
)
1117 return g_strdup("12345M");
1119 return g_strdup("12345 bytes");
1122 if (item
->base_type
!= TYPE_DIRECTORY
)
1124 if (filer_window
->display_style
== SMALL_ICONS
)
1125 buf
= g_strdup(format_size_aligned(item
->size
));
1127 buf
= g_strdup(format_size(item
->size
));
1130 buf
= g_strdup("-");
1136 static void draw_item(GtkWidget
*widget
,
1137 CollectionItem
*colitem
,
1139 FilerWindow
*filer_window
)
1141 DirItem
*item
= (DirItem
*) colitem
->data
;
1142 gboolean selected
= colitem
->selected
;
1144 ViewData
*view
= (ViewData
*) colitem
->view_data
;
1146 g_return_if_fail(view
!= NULL
);
1148 fill_template(area
, colitem
, filer_window
, &template);
1150 /* Set up GC for coloured file types */
1152 type_gc
= gdk_gc_new(widget
->window
);
1154 gdk_gc_set_foreground(type_gc
, type_get_colour(item
,
1155 &widget
->style
->fg
[GTK_STATE_NORMAL
]));
1157 if (template.icon
.width
<= SMALL_WIDTH
&&
1158 template.icon
.height
<= SMALL_HEIGHT
)
1160 draw_small_icon(widget
, &template.icon
,
1161 item
, view
->image
, selected
);
1163 else if (template.icon
.width
<= ICON_WIDTH
&&
1164 template.icon
.height
<= ICON_HEIGHT
)
1166 draw_large_icon(widget
, &template.icon
,
1167 item
, view
->image
, selected
);
1171 draw_huge_icon(widget
, &template.icon
,
1172 item
, view
->image
, selected
);
1175 draw_string(widget
, view
->layout
,
1178 filer_window
->selection_state
,
1181 draw_string(widget
, view
->details
,
1183 template.details
.width
,
1184 filer_window
->selection_state
,
1188 /* Note: Call shrink_grid after this */
1189 static void display_details_set(FilerWindow
*filer_window
, DetailsType details
)
1191 if (filer_window
->details_type
== details
)
1193 filer_window
->details_type
= details
;
1194 display_update_views(filer_window
);
1196 gtk_widget_queue_draw(GTK_WIDGET(filer_window
->collection
));
1199 /* Note: Call shrink_grid after this */
1200 static void display_style_set(FilerWindow
*filer_window
, DisplayStyle style
)
1202 if (filer_window
->display_style
== style
)
1205 filer_window
->display_style
= style
;
1207 display_update_views(filer_window
);
1209 collection_set_functions(filer_window
->collection
,
1210 (CollectionDrawFunc
) draw_item
,
1211 (CollectionTestFunc
) test_point
,
1215 void display_update_view(FilerWindow
*filer_window
,
1218 gboolean update_name_layout
)
1220 DisplayStyle style
= filer_window
->display_style
;
1222 int wrap_width
= -1;
1224 static PangoFontDescription
*monospace
= NULL
;
1227 monospace
= pango_font_description_from_string("monospace");
1231 g_object_unref(G_OBJECT(view
->details
));
1232 view
->details
= NULL
;
1235 str
= details(filer_window
, item
);
1238 PangoAttrList
*list
;
1239 int perm_offset
= -1;
1241 view
->details
= gtk_widget_create_pango_layout(
1242 filer_window
->window
, str
);
1245 pango_layout_set_font_description(view
->details
, monospace
);
1246 pango_layout_get_size(view
->details
, &w
, &h
);
1247 view
->details_width
= w
/ PANGO_SCALE
;
1248 view
->details_height
= h
/ PANGO_SCALE
;
1250 if (filer_window
->details_type
== DETAILS_SUMMARY
)
1252 else if (filer_window
->details_type
== DETAILS_PERMISSIONS
)
1254 if (perm_offset
> -1)
1256 PangoAttribute
*attr
;
1258 attr
= pango_attr_underline_new(PANGO_UNDERLINE_SINGLE
);
1260 perm_offset
+= 4 * applicable(item
->uid
, item
->gid
);
1261 attr
->start_index
= perm_offset
;
1262 attr
->end_index
= perm_offset
+ 3;
1264 list
= pango_attr_list_new();
1265 pango_attr_list_insert(list
, attr
);
1266 pango_layout_set_attributes(view
->details
, list
);
1272 g_object_unref(view
->image
);
1276 if (filer_window
->show_thumbs
&& item
->base_type
== TYPE_FILE
&&
1277 strcmp(item
->mime_type
->media_type
, "image") == 0)
1281 path
= make_path(filer_window
->real_path
, item
->leafname
)->str
;
1283 view
->image
= g_fscache_lookup_full(pixmap_cache
, path
,
1284 FSCACHE_LOOKUP_ONLY_NEW
, NULL
);
1289 view
->image
= item
->image
;
1291 g_object_ref(view
->image
);
1294 if (view
->layout
&& update_name_layout
)
1296 g_object_unref(G_OBJECT(view
->layout
));
1297 view
->layout
= NULL
;
1304 else if (g_utf8_validate(item
->leafname
, -1, NULL
))
1306 view
->layout
= gtk_widget_create_pango_layout(
1307 filer_window
->window
, item
->leafname
);
1311 PangoAttrList
*list
;
1312 PangoAttribute
*attr
;
1315 utf8
= to_utf8(item
->leafname
);
1316 view
->layout
= gtk_widget_create_pango_layout(
1317 filer_window
->window
, utf8
);
1320 attr
= pango_attr_foreground_new(0xffff, 0, 0);
1321 attr
->start_index
= 0;
1322 attr
->end_index
= 1000;
1323 list
= pango_attr_list_new();
1324 pango_attr_list_insert(list
, attr
);
1325 pango_layout_set_attributes(view
->layout
, list
);
1328 if (filer_window
->details_type
== DETAILS_NONE
)
1330 if (style
== HUGE_ICONS
)
1331 wrap_width
= HUGE_WRAP
* PANGO_SCALE
;
1332 else if (style
== LARGE_ICONS
)
1333 wrap_width
= o_large_width
.int_value
* PANGO_SCALE
;
1336 if (wrap_width
!= -1)
1337 pango_layout_set_width(view
->layout
, wrap_width
);
1339 pango_layout_get_size(view
->layout
, &w
, &h
);
1340 view
->name_width
= w
/ PANGO_SCALE
;
1341 view
->name_height
= h
/ PANGO_SCALE
;
1344 /* 'box' renders a background box if the string is also selected */
1345 static void draw_string(GtkWidget
*widget
,
1346 PangoLayout
*layout
,
1347 GdkRectangle
*area
, /* Area available on screen */
1348 int width
, /* Width of the full string */
1349 GtkStateType selection_state
,
1353 GdkGC
*gc
= selected
1354 ? widget
->style
->fg_gc
[selection_state
]
1357 if (selected
&& box
)
1358 gtk_paint_flat_box(widget
->style
, widget
->window
,
1359 selection_state
, GTK_SHADOW_NONE
,
1360 NULL
, widget
, "text",
1362 MIN(width
, area
->width
),
1365 if (width
> area
->width
)
1367 gdk_gc_set_clip_origin(gc
, 0, 0);
1368 gdk_gc_set_clip_rectangle(gc
, area
);
1371 gdk_draw_layout(widget
->window
, gc
, area
->x
, area
->y
, layout
);
1373 if (width
> area
->width
)
1375 static GdkGC
*red_gc
= NULL
;
1380 GdkColor red
= {0, 0xffff, 0, 0};
1382 red_gc
= gdk_gc_new(widget
->window
);
1383 gdk_colormap_alloc_colors(
1384 gtk_widget_get_colormap(widget
),
1385 &red
, 1, FALSE
, TRUE
, &success
);
1386 gdk_gc_set_foreground(red_gc
, &red
);
1388 gdk_draw_rectangle(widget
->window
, red_gc
, TRUE
,
1389 area
->x
+ area
->width
- 1, area
->y
,
1391 gdk_gc_set_clip_rectangle(gc
, NULL
);