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>
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_intelligent_sort
;
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_inherit_options
;
75 /* Static prototypes */
76 static void display_details_set(FilerWindow
*filer_window
, DetailsType details
);
77 static void display_style_set(FilerWindow
*filer_window
, DisplayStyle style
);
78 static void options_changed(void);
79 static char *details(FilerWindow
*filer_window
, DirItem
*item
);
88 /****************************************************************
89 * EXTERNAL INTERFACE *
90 ****************************************************************/
94 option_add_int(&o_intelligent_sort
, "display_intelligent_sort", 1);
95 option_add_int(&o_display_dirs_first
, "display_dirs_first", FALSE
);
96 option_add_int(&o_display_size
, "display_size", LARGE_ICONS
);
97 option_add_int(&o_display_details
, "display_details", DETAILS_NONE
);
98 option_add_int(&o_display_sort_by
, "display_sort_by", SORT_BY_NAME
);
99 option_add_int(&o_large_width
, "display_large_width", 89);
100 option_add_int(&o_small_width
, "display_small_width", 250);
101 option_add_int(&o_display_show_hidden
, "display_show_hidden", FALSE
);
102 option_add_int(&o_display_show_thumbs
, "display_show_thumbs", FALSE
);
103 option_add_int(&o_display_inherit_options
,
104 "display_inherit_options", FALSE
);
106 option_add_notify(options_changed
);
109 /* Draw this icon (including any symlink or mount symbol) inside the
112 void draw_large_icon(GtkWidget
*widget
,
126 width
= MIN(image
->width
, ICON_WIDTH
);
127 height
= MIN(image
->height
, ICON_HEIGHT
);
128 image_x
= area
->x
+ ((area
->width
- width
) >> 1);
129 image_y
= MAX(0, area
->height
- height
- 6);
131 gdk_pixbuf_render_to_drawable_alpha(
132 selected
? image
->pixbuf_lit
: image
->pixbuf
,
135 image_x
, area
->y
+ image_y
, /* dest */
137 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
138 GDK_RGB_DITHER_NORMAL
, 0, 0);
140 if (item
->flags
& ITEM_FLAG_SYMLINK
)
142 gdk_pixbuf_render_to_drawable_alpha(im_symlink
->pixbuf
,
145 image_x
, area
->y
+ 2, /* dest */
147 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
148 GDK_RGB_DITHER_NORMAL
, 0, 0);
150 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
152 MaskedPixmap
*mp
= item
->flags
& ITEM_FLAG_MOUNTED
156 gdk_pixbuf_render_to_drawable_alpha(mp
->pixbuf
,
159 image_x
, area
->y
+ 2, /* dest */
161 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
162 GDK_RGB_DITHER_NORMAL
, 0, 0);
166 /* The sort functions aren't called from outside, but they are
167 * passed as arguments to display_set_sort_fn().
170 #define IS_A_DIR(item) (item->base_type == TYPE_DIRECTORY && \
171 !(item->flags & ITEM_FLAG_APPDIR))
174 if (o_display_dirs_first.int_value) { \
175 gboolean id1 = IS_A_DIR(i1); \
176 gboolean id2 = IS_A_DIR(i2); \
177 if (id1 && !id2) return -1; \
178 if (id2 && !id1) return 1; \
181 int sort_by_name(const void *item1
, const void *item2
)
183 const DirItem
*i1
= (DirItem
*) item1
;
184 const DirItem
*i2
= (DirItem
*) item2
;
185 char *n1
= i1
->leafname_collate
;
186 char *n2
= i2
->leafname_collate
;
190 if (!o_intelligent_sort
.int_value
)
191 return strcmp(i1
->leafname
, i2
->leafname
);
193 /* The following code was copied from PicoGUI (was LGPL) */
195 /* Sort the files, in a way that should make sense to users.
196 * Case is ignored, punctuation is ignored. If the file contains
197 * numbers, the numbers are sorted numerically.
201 char c1
= *n1
, c2
= *n2
;
203 /* If they are both numbers, sort them numerically */
204 if (isdigit(c1
) && isdigit(c2
))
208 u1
= strtoul(n1
, &p
, 10);
210 u2
= strtoul(n2
, &p
, 10);
219 /* Do a case-insensitive asciibetical sort */
232 return 1; /* First string is longer, so comes after */
236 /* If the strings are equal at the end of this, fallback to a straight
237 * ASCII sort so at least it's deterministic.
239 return strcmp(i1
->leafname
, i2
->leafname
);
242 int sort_by_type(const void *item1
, const void *item2
)
244 const DirItem
*i1
= (DirItem
*) item1
;
245 const DirItem
*i2
= (DirItem
*) item2
;
248 int diff
= i1
->base_type
- i2
->base_type
;
251 diff
= (i1
->flags
& ITEM_FLAG_APPDIR
)
252 - (i2
->flags
& ITEM_FLAG_APPDIR
);
254 return diff
> 0 ? 1 : -1;
261 diff
= strcmp(m1
->media_type
, m2
->media_type
);
263 diff
= strcmp(m1
->subtype
, m2
->subtype
);
271 return diff
> 0 ? 1 : -1;
273 return sort_by_name(item1
, item2
);
276 int sort_by_date(const void *item1
, const void *item2
)
278 const DirItem
*i1
= (DirItem
*) item1
;
279 const DirItem
*i2
= (DirItem
*) item2
;
283 return i1
->mtime
> i2
->mtime
? -1 :
284 i1
->mtime
< i2
->mtime
? 1 :
285 sort_by_name(item1
, item2
);
288 int sort_by_size(const void *item1
, const void *item2
)
290 const DirItem
*i1
= (DirItem
*) item1
;
291 const DirItem
*i2
= (DirItem
*) item2
;
295 return i1
->size
> i2
->size
? -1 :
296 i1
->size
< i2
->size
? 1 :
297 sort_by_name(item1
, item2
);
300 void display_set_sort_fn(FilerWindow
*filer_window
,
301 int (*fn
)(const void *a
, const void *b
))
303 if (filer_window
->sort_fn
== fn
)
306 filer_window
->sort_fn
= fn
;
308 view_sort(filer_window
->view
);
311 void display_set_layout(FilerWindow
*filer_window
,
315 g_return_if_fail(filer_window
!= NULL
);
317 display_style_set(filer_window
, style
);
318 display_details_set(filer_window
, details
);
320 /* Recreate layouts because wrapping may have changed */
321 view_style_changed(filer_window
->view
, VIEW_UPDATE_NAME
);
323 if (o_filer_auto_resize
.int_value
!= RESIZE_NEVER
)
324 filer_window_autosize(filer_window
, TRUE
);
327 /* Set the 'Show Thumbnails' flag for this window */
328 void display_set_thumbs(FilerWindow
*filer_window
, gboolean thumbs
)
330 if (filer_window
->show_thumbs
== thumbs
)
333 filer_window
->show_thumbs
= thumbs
;
335 view_style_changed(filer_window
->view
, VIEW_UPDATE_VIEWDATA
);
338 filer_cancel_thumbnails(filer_window
);
340 filer_set_title(filer_window
);
342 filer_create_thumbs(filer_window
);
345 /* Set the 'Show Hidden' flag for this window */
346 void display_set_hidden(FilerWindow
*filer_window
, gboolean hidden
)
348 if (filer_window
->show_hidden
== hidden
)
351 filer_window
->show_hidden
= hidden
;
353 filer_detach_rescan(filer_window
); /* (updates titlebar) */
356 /* Highlight (wink or cursor) this item in the filer window. If the item
357 * isn't already there but we're scanning then highlight it if it
360 void display_set_autoselect(FilerWindow
*filer_window
, const gchar
*leaf
)
364 g_return_if_fail(filer_window
!= NULL
);
365 g_return_if_fail(leaf
!= NULL
);
367 new = g_strdup(leaf
); /* leaf == old value sometimes */
369 g_free(filer_window
->auto_select
);
370 filer_window
->auto_select
= NULL
;
372 if (view_autoselect(filer_window
->view
, new))
375 filer_window
->auto_select
= new;
378 /* Change the icon size (wraps) */
379 void display_change_size(FilerWindow
*filer_window
, gboolean bigger
)
383 g_return_if_fail(filer_window
!= NULL
);
385 switch (filer_window
->display_style
)
388 new = bigger
? HUGE_ICONS
: SMALL_ICONS
;
391 new = bigger
? SMALL_ICONS
: LARGE_ICONS
;
394 new = bigger
? LARGE_ICONS
: HUGE_ICONS
;
398 display_set_layout(filer_window
, new, filer_window
->details_type
);
401 ViewData
*display_create_viewdata(FilerWindow
*filer_window
, DirItem
*item
)
405 view
= g_new(ViewData
, 1);
408 view
->details
= NULL
;
411 display_update_view(filer_window
, item
, view
, TRUE
);
416 /****************************************************************
417 * INTERNAL FUNCTIONS *
418 ****************************************************************/
420 static void options_changed(void)
424 for (next
= all_filer_windows
; next
; next
= next
->next
)
426 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
429 if (o_intelligent_sort
.has_changed
||
430 o_display_dirs_first
.has_changed
)
431 view_sort(VIEW(filer_window
->view
));
433 if (o_large_width
.has_changed
|| o_small_width
.has_changed
)
434 flags
|= VIEW_UPDATE_NAME
; /* Recreate PangoLayout */
436 view_style_changed(filer_window
->view
, flags
);
440 /* Return a new string giving details of this item, or NULL if details
441 * are not being displayed. If details are not yet available, return
442 * a string of the right length.
444 static char *details(FilerWindow
*filer_window
, DirItem
*item
)
446 mode_t m
= item
->mode
;
448 gboolean scanned
= item
->base_type
!= TYPE_UNKNOWN
;
450 if (filer_window
->details_type
== DETAILS_NONE
)
453 if (scanned
&& item
->lstat_errno
)
454 buf
= g_strdup_printf(_("lstat(2) failed: %s"),
455 g_strerror(item
->lstat_errno
));
456 else if (filer_window
->details_type
== DETAILS_SUMMARY
)
461 return g_strdup("XXXX ---,---,---/--"
465 " 12345678 12345678 "
466 "1234M 00:00:00 01 Mmm Yyyy");
468 time
= pretty_time(&item
->mtime
);
470 buf
= g_strdup_printf("%s %s %-8.8s %-8.8s %s %s",
471 item
->flags
& ITEM_FLAG_APPDIR
? "App " :
472 S_ISDIR(m
) ? "Dir " :
473 S_ISCHR(m
) ? "Char" :
474 S_ISBLK(m
) ? "Blck" :
475 S_ISLNK(m
) ? "Link" :
476 S_ISSOCK(m
) ? "Sock" :
477 S_ISFIFO(m
) ? "Pipe" : "File",
478 pretty_permissions(m
),
479 user_name(item
->uid
),
480 group_name(item
->gid
),
481 format_size_aligned(item
->size
),
485 else if (filer_window
->details_type
== DETAILS_TYPE
)
487 MIME_type
*type
= item
->mime_type
;
490 return g_strdup("(application/octet-stream)");
492 buf
= g_strdup_printf("(%s/%s)",
493 type
->media_type
, type
->subtype
);
495 else if (filer_window
->details_type
== DETAILS_TIMES
)
497 guchar
*ctime
, *mtime
, *atime
;
499 ctime
= pretty_time(&item
->ctime
);
500 mtime
= pretty_time(&item
->mtime
);
501 atime
= pretty_time(&item
->atime
);
503 buf
= g_strdup_printf("a[%s] c[%s] m[%s]", atime
, ctime
, mtime
);
508 else if (filer_window
->details_type
== DETAILS_PERMISSIONS
)
511 return g_strdup("---,---,---/--"
515 " 12345678 12345678");
517 buf
= g_strdup_printf("%s %-8.8s %-8.8s",
518 pretty_permissions(m
),
519 user_name(item
->uid
),
520 group_name(item
->gid
));
526 if (filer_window
->display_style
== SMALL_ICONS
)
527 return g_strdup("12345M");
529 return g_strdup("12345 bytes");
532 if (item
->base_type
!= TYPE_DIRECTORY
)
534 if (filer_window
->display_style
== SMALL_ICONS
)
535 buf
= g_strdup(format_size_aligned(item
->size
));
537 buf
= g_strdup(format_size(item
->size
));
546 /* Note: Call style_changed after this */
547 static void display_details_set(FilerWindow
*filer_window
, DetailsType details
)
549 if (filer_window
->details_type
== details
)
551 filer_window
->details_type
= details
;
554 /* Note: Call style_changed after this */
555 static void display_style_set(FilerWindow
*filer_window
, DisplayStyle style
)
557 if (filer_window
->display_style
== style
)
560 filer_window
->display_style
= style
;
563 void display_update_view(FilerWindow
*filer_window
,
566 gboolean update_name_layout
)
568 DisplayStyle style
= filer_window
->display_style
;
572 static PangoFontDescription
*monospace
= NULL
;
575 monospace
= pango_font_description_from_string("monospace");
579 g_object_unref(G_OBJECT(view
->details
));
580 view
->details
= NULL
;
583 str
= details(filer_window
, item
);
587 int perm_offset
= -1;
589 view
->details
= gtk_widget_create_pango_layout(
590 filer_window
->window
, str
);
593 pango_layout_set_font_description(view
->details
, monospace
);
594 pango_layout_get_size(view
->details
, &w
, &h
);
595 view
->details_width
= w
/ PANGO_SCALE
;
596 view
->details_height
= h
/ PANGO_SCALE
;
598 if (filer_window
->details_type
== DETAILS_SUMMARY
)
600 else if (filer_window
->details_type
== DETAILS_PERMISSIONS
)
602 if (perm_offset
> -1)
604 PangoAttribute
*attr
;
606 attr
= pango_attr_underline_new(PANGO_UNDERLINE_SINGLE
);
608 perm_offset
+= 4 * applicable(item
->uid
, item
->gid
);
609 attr
->start_index
= perm_offset
;
610 attr
->end_index
= perm_offset
+ 3;
612 list
= pango_attr_list_new();
613 pango_attr_list_insert(list
, attr
);
614 pango_layout_set_attributes(view
->details
, list
);
620 g_object_unref(view
->image
);
624 if (filer_window
->show_thumbs
&& item
->base_type
== TYPE_FILE
&&
625 strcmp(item
->mime_type
->media_type
, "image") == 0)
629 path
= make_path(filer_window
->real_path
, item
->leafname
)->str
;
631 view
->image
= g_fscache_lookup_full(pixmap_cache
, path
,
632 FSCACHE_LOOKUP_ONLY_NEW
, NULL
);
637 view
->image
= item
->image
;
639 g_object_ref(view
->image
);
642 if (view
->layout
&& update_name_layout
)
644 g_object_unref(G_OBJECT(view
->layout
));
652 else if (g_utf8_validate(item
->leafname
, -1, NULL
))
654 view
->layout
= gtk_widget_create_pango_layout(
655 filer_window
->window
, item
->leafname
);
660 PangoAttribute
*attr
;
663 utf8
= to_utf8(item
->leafname
);
664 view
->layout
= gtk_widget_create_pango_layout(
665 filer_window
->window
, utf8
);
668 attr
= pango_attr_foreground_new(0xffff, 0, 0);
669 attr
->start_index
= 0;
670 attr
->end_index
= 1000;
671 list
= pango_attr_list_new();
672 pango_attr_list_insert(list
, attr
);
673 pango_layout_set_attributes(view
->layout
, list
);
676 if (filer_window
->details_type
== DETAILS_NONE
)
678 if (style
== HUGE_ICONS
)
679 wrap_width
= HUGE_WRAP
* PANGO_SCALE
;
680 else if (style
== LARGE_ICONS
)
681 wrap_width
= o_large_width
.int_value
* PANGO_SCALE
;
684 if (wrap_width
!= -1)
685 pango_layout_set_width(view
->layout
, wrap_width
);
687 pango_layout_get_size(view
->layout
, &w
, &h
);
688 view
->name_width
= w
/ PANGO_SCALE
;
689 view
->name_height
= h
/ PANGO_SCALE
;