r328: Changed the install script so that the CVS directories don't get installed.
[rox-filer.git] / ROX-Filer / src / display.c
blob0e1772eb0539afde83965de0be58c1c384e4675f
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2000, Thomas Leonard, <tal197@ecs.soton.ac.uk>.
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)
10 * any later version.
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
15 * more details.
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 */
24 #include "config.h"
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <math.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <sys/param.h>
32 #include <unistd.h>
33 #include <time.h>
34 #include <ctype.h>
36 #include <gtk/gtk.h>
37 #include <gdk/gdkx.h>
38 #include <gdk/gdkkeysyms.h>
39 #include "collection.h"
41 #include "main.h"
42 #include "support.h"
43 #include "gui_support.h"
44 #include "filer.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"
55 #define ROW_HEIGHT_SMALL 20
56 #define ROW_HEIGHT_FULL_INFO 44
57 #define SMALL_ICON_HEIGHT 20
58 #ifdef HAVE_IMLIB
59 # define SMALL_ICON_WIDTH 24
60 #else
61 # define SMALL_ICON_WIDTH 48
62 #endif
63 #define MAX_ICON_HEIGHT 42
64 #define MAX_ICON_WIDTH 48
65 #define MIN_ITEM_WIDTH 64
67 #define MIN_TRUNCATE 0
68 #define MAX_TRUNCATE 250
70 DetailsType last_details_type = DETAILS_SUMMARY;
71 DisplayStyle last_display_style = LARGE_ICONS;
72 gboolean last_show_hidden = FALSE;
73 int (*last_sort_fn)(const void *a, const void *b) = sort_by_type;
75 /* Options bits */
76 static guchar *style_to_name(void);
77 static guchar *sort_fn_to_name(void);
78 static void update_options_label(void);
80 static GtkWidget *create_options();
81 static void update_options();
82 static void set_options();
83 static void save_options();
84 static char *display_sort_nocase(char *data);
85 static char *display_display_style(char *data);
86 static char *display_sort_by(char *data);
87 static char *display_truncate(char *data);
89 static OptionsSection options =
91 N_("Display options"),
92 create_options,
93 update_options,
94 set_options,
95 save_options
98 static GtkWidget *display_label;
100 static gboolean o_sort_nocase = TRUE;
101 static gint o_small_truncate = 250;
102 static gint o_large_truncate = 89;
103 static GtkAdjustment *adj_small_truncate;
104 static GtkAdjustment *adj_large_truncate;
105 static GtkWidget *toggle_sort_nocase;
107 /* Static prototypes */
108 static void draw_large_icon(GtkWidget *widget,
109 GdkRectangle *area,
110 DirItem *item,
111 gboolean selected);
112 static void draw_string(GtkWidget *widget,
113 GdkFont *font,
114 char *string,
115 int x,
116 int y,
117 int width,
118 int area_width,
119 gboolean selected,
120 gboolean box);
121 static void draw_item_large(GtkWidget *widget,
122 CollectionItem *item,
123 GdkRectangle *area,
124 FilerWindow *filer_window);
125 static void draw_item_small(GtkWidget *widget,
126 CollectionItem *item,
127 GdkRectangle *area,
128 FilerWindow *filer_window);
129 static void draw_item_large_full(GtkWidget *widget,
130 CollectionItem *colitem,
131 GdkRectangle *area,
132 FilerWindow *filer_window);
133 static void draw_item_small_full(GtkWidget *widget,
134 CollectionItem *colitem,
135 GdkRectangle *area,
136 FilerWindow *filer_window);
137 static gboolean test_point_large(Collection *collection,
138 int point_x, int point_y,
139 CollectionItem *item,
140 int width, int height,
141 FilerWindow *filer_window);
142 static gboolean test_point_small(Collection *collection,
143 int point_x, int point_y,
144 CollectionItem *item,
145 int width, int height,
146 FilerWindow *filer_window);
147 static gboolean test_point_large_full(Collection *collection,
148 int point_x, int point_y,
149 CollectionItem *item,
150 int width, int height,
151 FilerWindow *filer_window);
152 static gboolean test_point_small_full(Collection *collection,
153 int point_x, int point_y,
154 CollectionItem *item,
155 int width, int height,
156 FilerWindow *filer_window);
158 void display_init()
160 options_sections = g_slist_prepend(options_sections, &options);
161 option_register("display_sort_nocase", display_sort_nocase);
162 option_register("display_display_style", display_display_style);
163 option_register("display_sort_by", display_sort_by);
164 option_register("display_truncate", display_truncate);
167 #define BAR_SIZE(size) ((size) > 0 ? (log((size) + 1) * 16) : 0)
168 #define MAX_BAR_SIZE 460
170 static int details_width(FilerWindow *filer_window, DirItem *item)
172 int bar = 0;
174 if (filer_window->details_type == DETAILS_SIZE_BARS &&
175 item->base_type != TYPE_DIRECTORY)
176 return 6 * fixed_width + MAX_BAR_SIZE + 16;
178 return bar + fixed_width * strlen(details(filer_window, item));
181 int calc_width(FilerWindow *filer_window, DirItem *item)
183 int pix_width = item->image->width;
184 int w;
186 switch (filer_window->display_style)
188 case LARGE_FULL_INFO:
189 w = details_width(filer_window, item);
190 return MAX_ICON_WIDTH + 12 +
191 MAX(w, item->name_width);
192 case SMALL_FULL_INFO:
193 w = details_width(filer_window, item);
194 return SMALL_ICON_WIDTH + item->name_width + 12 + w;
195 case SMALL_ICONS:
196 w = MIN(item->name_width, o_small_truncate);
197 return SMALL_ICON_WIDTH + 12 + w;
198 default:
199 w = MIN(item->name_width, o_large_truncate);
200 return MAX(pix_width, w) + 4;
204 /* Is a point inside an item? */
205 static gboolean test_point_large(Collection *collection,
206 int point_x, int point_y,
207 CollectionItem *colitem,
208 int width, int height,
209 FilerWindow *filer_window)
211 DirItem *item = (DirItem *) colitem->data;
212 int text_height = item_font->ascent + item_font->descent;
213 MaskedPixmap *image = item->image;
214 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
215 int image_width = (image->width >> 1) + 2;
216 int text_width = (item->name_width >> 1) + 2;
217 int x_limit;
219 if (point_y < image_y)
220 return FALSE; /* Too high up (don't worry about too low) */
222 if (point_y <= image_y + image->height + 2)
223 x_limit = image_width;
224 else if (point_y > height - text_height - 2)
225 x_limit = text_width;
226 else
227 x_limit = MIN(image_width, text_width);
229 return ABS(point_x - (width >> 1)) < x_limit;
232 static gboolean test_point_large_full(Collection *collection,
233 int point_x, int point_y,
234 CollectionItem *colitem,
235 int width, int height,
236 FilerWindow *filer_window)
238 DirItem *item = (DirItem *) colitem->data;
239 MaskedPixmap *image = item->image;
240 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
241 int low_top = height
242 - fixed_font->descent - 2 - fixed_font->ascent;
244 if (point_x < image->width + 2)
245 return point_x > 2 && point_y > image_y;
247 point_x -= MAX_ICON_WIDTH + 8;
249 if (point_y >= low_top)
250 return point_x < details_width(filer_window, item);
251 if (point_y >= low_top - item_font->ascent - item_font->descent)
252 return point_x < item->name_width;
253 return FALSE;
256 static gboolean test_point_small_full(Collection *collection,
257 int point_x, int point_y,
258 CollectionItem *colitem,
259 int width, int height,
260 FilerWindow *filer_window)
262 DirItem *item = (DirItem *) colitem->data;
263 MaskedPixmap *image = item->image;
264 int image_y = MAX(0, SMALL_ICON_HEIGHT - image->height);
265 int low_top = height
266 - fixed_font->descent - 2 - item_font->ascent;
267 int iwidth = MIN(SMALL_ICON_WIDTH, image->width);
269 if (point_x < iwidth + 2)
270 return point_x > 2 && point_y > image_y;
272 if (point_y < low_top)
273 return FALSE;
275 if (filer_window->details_type == DETAILS_SIZE_BARS)
277 int bar_x = width - MAX_BAR_SIZE - 4;
278 int bar_size;
280 if (item->base_type == TYPE_DIRECTORY)
281 bar_size = 0;
282 else
283 bar_size = BAR_SIZE(item->size);
285 return point_x < bar_x + bar_size;
288 return TRUE;
291 static gboolean test_point_small(Collection *collection,
292 int point_x, int point_y,
293 CollectionItem *colitem,
294 int width, int height,
295 FilerWindow *filer_window)
297 DirItem *item = (DirItem *) colitem->data;
298 MaskedPixmap *image = item->image;
299 int image_y = MAX(0, SMALL_ICON_HEIGHT - image->height);
300 int low_top = height
301 - fixed_font->descent - 2 - item_font->ascent;
302 int iwidth = MIN(SMALL_ICON_WIDTH, image->width);
304 if (point_x < iwidth + 2)
305 return point_x > 2 && point_y > image_y;
307 point_x -= SMALL_ICON_WIDTH + 4;
309 if (point_y >= low_top)
310 return point_x < item->name_width;
311 return FALSE;
314 static void draw_small_icon(GtkWidget *widget,
315 GdkRectangle *area,
316 DirItem *item,
317 gboolean selected)
319 GdkGC *gc = selected ? widget->style->white_gc
320 : widget->style->black_gc;
321 MaskedPixmap *image = item->image;
322 int width, height, image_x, image_y;
324 if (!image)
325 return;
327 if (!image->sm_pixmap)
328 pixmap_make_small(image);
330 width = MIN(image->sm_width, SMALL_ICON_WIDTH);
331 height = MIN(image->sm_height, SMALL_ICON_HEIGHT);
332 image_x = area->x + ((area->width - width) >> 1);
334 gdk_gc_set_clip_mask(gc, item->image->sm_mask);
336 image_y = MAX(0, SMALL_ICON_HEIGHT - image->sm_height);
337 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
338 gdk_draw_pixmap(widget->window, gc,
339 item->image->sm_pixmap,
340 0, 0, /* Source x,y */
341 image_x, area->y + image_y, /* Dest x,y */
342 width, height);
344 if (selected)
346 gdk_gc_set_function(gc, GDK_INVERT);
347 gdk_draw_rectangle(widget->window,
349 TRUE, image_x, area->y + image_y,
350 width, height);
351 gdk_gc_set_function(gc, GDK_COPY);
354 if (item->flags & ITEM_FLAG_SYMLINK)
356 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
357 gdk_gc_set_clip_mask(gc, im_symlink->mask);
358 gdk_draw_pixmap(widget->window, gc, im_symlink->pixmap,
359 0, 0, /* Source x,y */
360 image_x, area->y + 8, /* Dest x,y */
361 -1, -1);
363 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
365 MaskedPixmap *mp = item->flags & ITEM_FLAG_MOUNTED
366 ? im_mounted
367 : im_unmounted;
369 if (!mp->sm_pixmap)
370 pixmap_make_small(mp);
371 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
372 gdk_gc_set_clip_mask(gc, mp->sm_mask);
373 gdk_draw_pixmap(widget->window, gc,
374 mp->sm_pixmap,
375 0, 0, /* Source x,y */
376 image_x, area->y + 8, /* Dest x,y */
377 -1, -1);
380 gdk_gc_set_clip_mask(gc, NULL);
381 gdk_gc_set_clip_origin(gc, 0, 0);
384 static void draw_large_icon(GtkWidget *widget,
385 GdkRectangle *area,
386 DirItem *item,
387 gboolean selected)
389 MaskedPixmap *image = item->image;
390 int width = MIN(image->width, MAX_ICON_WIDTH);
391 int height = MIN(image->height, MAX_ICON_WIDTH);
392 int image_x = area->x + ((area->width - width) >> 1);
393 int image_y;
394 GdkGC *gc = selected ? widget->style->white_gc
395 : widget->style->black_gc;
397 gdk_gc_set_clip_mask(gc, item->image->mask);
399 image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
400 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
401 gdk_draw_pixmap(widget->window, gc,
402 item->image->pixmap,
403 0, 0, /* Source x,y */
404 image_x, area->y + image_y, /* Dest x,y */
405 width, height);
407 if (selected)
409 gdk_gc_set_function(gc, GDK_INVERT);
410 gdk_draw_rectangle(widget->window,
412 TRUE, image_x, area->y + image_y,
413 width, height);
414 gdk_gc_set_function(gc, GDK_COPY);
417 if (item->flags & ITEM_FLAG_SYMLINK)
419 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
420 gdk_gc_set_clip_mask(gc, im_symlink->mask);
421 gdk_draw_pixmap(widget->window, gc, im_symlink->pixmap,
422 0, 0, /* Source x,y */
423 image_x, area->y + 8, /* Dest x,y */
424 -1, -1);
426 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
428 MaskedPixmap *mp = item->flags & ITEM_FLAG_MOUNTED
429 ? im_mounted
430 : im_unmounted;
432 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
433 gdk_gc_set_clip_mask(gc, mp->mask);
434 gdk_draw_pixmap(widget->window, gc, mp->pixmap,
435 0, 0, /* Source x,y */
436 image_x, area->y + 8, /* Dest x,y */
437 -1, -1);
440 gdk_gc_set_clip_mask(gc, NULL);
441 gdk_gc_set_clip_origin(gc, 0, 0);
444 /* 'box' renders a background box if the string is also selected */
445 static void draw_string(GtkWidget *widget,
446 GdkFont *font,
447 char *string,
448 int x,
449 int y,
450 int width,
451 int area_width,
452 gboolean selected,
453 gboolean box)
455 int text_height = font->ascent + font->descent;
456 GdkRectangle clip;
457 GdkGC *gc = selected
458 ? widget->style->fg_gc[GTK_STATE_SELECTED]
459 : widget->style->fg_gc[GTK_STATE_NORMAL];
461 if (selected && box)
462 gtk_paint_flat_box(widget->style, widget->window,
463 GTK_STATE_SELECTED, GTK_SHADOW_NONE,
464 NULL, widget, "text",
465 x, y - font->ascent,
466 MIN(width, area_width),
467 text_height);
469 if (width > area_width)
471 clip.x = x;
472 clip.y = y - font->ascent;
473 clip.width = area_width;
474 clip.height = text_height;
475 gdk_gc_set_clip_origin(gc, 0, 0);
476 gdk_gc_set_clip_rectangle(gc, &clip);
479 gdk_draw_text(widget->window,
480 font,
482 x, y,
483 string, strlen(string));
485 if (width > area_width)
487 if (!red_gc)
489 red_gc = gdk_gc_new(widget->window);
490 gdk_gc_set_foreground(red_gc, &red);
492 gdk_draw_rectangle(widget->window, red_gc, TRUE,
493 x + area_width - 1, clip.y, 1, text_height);
494 gdk_gc_set_clip_rectangle(gc, NULL);
498 /* Render the details somewhere */
499 static void draw_details(FilerWindow *filer_window, DirItem *item, int x, int y,
500 int width, gboolean selected, gboolean box)
502 GtkWidget *widget = GTK_WIDGET(filer_window->collection);
503 int w;
505 w = details_width(filer_window, item);
507 draw_string(widget,
508 fixed_font,
509 details(filer_window, item),
510 x, y,
512 width,
513 selected, box);
515 if (item->lstat_errno)
516 return;
518 if (filer_window->details_type == DETAILS_SIZE_BARS)
520 int bar;
521 int bar_x = x + width - MAX_BAR_SIZE - 4;
523 if (item->base_type == TYPE_DIRECTORY)
524 return;
526 bar = BAR_SIZE(item->size);
528 gdk_draw_rectangle(widget->window,
529 widget->style->black_gc,
530 TRUE,
531 bar_x, y - fixed_font->ascent,
532 bar + 4,
533 fixed_font->ascent + fixed_font->descent);
534 gdk_draw_rectangle(widget->window,
535 widget->style->white_gc,
536 TRUE,
537 bar_x + 2,
538 y - fixed_font->ascent + 2,
539 bar,
540 fixed_font->ascent + fixed_font->descent - 4);
542 return;
545 if (filer_window->details_type == DETAILS_SUMMARY)
547 /* Underline the effective permissions */
548 gdk_draw_rectangle(widget->window,
549 widget->style->fg_gc[selected
550 ? GTK_STATE_SELECTED
551 : GTK_STATE_NORMAL],
552 TRUE,
553 x - 1 + fixed_width *
554 (5 + 4 * applicable(item->uid, item->gid)),
555 y + fixed_font->descent - 1,
556 fixed_width * 3 + 1, 1);
560 /* Return a string (valid until next call) giving details
561 * of this item.
563 char *details(FilerWindow *filer_window, DirItem *item)
565 mode_t m = item->mode;
566 static guchar *buf = NULL;
568 if (buf)
569 g_free(buf);
571 if (item->lstat_errno)
572 buf = g_strdup_printf(_("lstat(2) failed: %s"),
573 g_strerror(item->lstat_errno));
574 else if (filer_window->details_type == DETAILS_SUMMARY)
576 buf = g_strdup_printf("%s %s %-8.8s %-8.8s %s %s",
577 item->flags & ITEM_FLAG_APPDIR? "App " :
578 S_ISDIR(m) ? "Dir " :
579 S_ISCHR(m) ? "Char" :
580 S_ISBLK(m) ? "Blck" :
581 S_ISLNK(m) ? "Link" :
582 S_ISSOCK(m) ? "Sock" :
583 S_ISFIFO(m) ? "Pipe" : "File",
584 pretty_permissions(m),
585 user_name(item->uid),
586 group_name(item->gid),
587 format_size_aligned(item->size),
588 pretty_time(&item->mtime));
590 else
592 if (item->base_type != TYPE_DIRECTORY)
594 if (filer_window->display_style == SMALL_FULL_INFO)
595 buf = g_strdup(format_size_aligned(item->size));
596 else
597 buf = g_strdup(format_size(item->size));
599 else
600 buf = g_strdup("-");
603 return buf;
606 static void draw_item_large_full(GtkWidget *widget,
607 CollectionItem *colitem,
608 GdkRectangle *area,
609 FilerWindow *filer_window)
611 DirItem *item = (DirItem *) colitem->data;
612 MaskedPixmap *image = item->image;
613 int text_x = area->x + MAX_ICON_WIDTH + 8;
614 int low_text_y = area->y + area->height - fixed_font->descent - 2;
615 gboolean selected = colitem->selected;
616 GdkRectangle pic_area;
617 int text_area_width = area->width - (text_x - area->x);
619 pic_area.x = area->x;
620 pic_area.y = area->y;
621 pic_area.width = image->width + 8;
622 pic_area.height = area->height;
624 draw_large_icon(widget, &pic_area, item, selected);
626 draw_string(widget,
627 item_font,
628 item->leafname,
629 text_x,
630 low_text_y - item_font->descent - fixed_font->ascent,
631 item->name_width,
632 text_area_width,
633 selected, TRUE);
635 draw_details(filer_window, item, text_x, low_text_y,
636 text_area_width, selected, TRUE);
639 static void draw_item_small_full(GtkWidget *widget,
640 CollectionItem *colitem,
641 GdkRectangle *area,
642 FilerWindow *filer_window)
644 DirItem *item = (DirItem *) colitem->data;
645 int text_x = area->x + SMALL_ICON_WIDTH + 4;
646 int bottom = area->y + area->height;
647 int low_text_y = bottom - item_font->descent - 2;
648 gboolean selected = colitem->selected;
649 GdkRectangle pic_area;
650 int text_height = item_font->ascent + item_font->descent;
651 int fixed_height = fixed_font->ascent + fixed_font->descent;
652 int box_height = MAX(text_height, fixed_height) + 2;
653 int w;
655 pic_area.x = area->x;
656 pic_area.y = area->y;
657 pic_area.width = SMALL_ICON_WIDTH;
658 pic_area.height = SMALL_ICON_HEIGHT;
660 draw_small_icon(widget, &pic_area, item, selected);
662 if (selected)
663 gtk_paint_flat_box(widget->style, widget->window,
664 GTK_STATE_SELECTED, GTK_SHADOW_NONE,
665 NULL, widget, "text",
666 text_x, bottom - 1 - box_height,
667 area->width - (text_x - area->x) - 1,
668 box_height);
669 draw_string(widget,
670 item_font,
671 item->leafname,
672 text_x,
673 low_text_y,
674 item->name_width,
675 area->width - (text_x - area->x),
676 selected, FALSE);
678 w = details_width(filer_window, item);
679 text_x = area->width - w - 4 + area->x;
680 draw_details(filer_window,
681 item, text_x, bottom - fixed_font->descent - 2,
682 w + 2, selected, FALSE);
685 static void draw_item_small(GtkWidget *widget,
686 CollectionItem *colitem,
687 GdkRectangle *area,
688 FilerWindow *filer_window)
690 DirItem *item = (DirItem *) colitem->data;
691 int text_x = area->x + SMALL_ICON_WIDTH + 4;
692 int low_text_y = area->y + area->height - item_font->descent - 2;
693 gboolean selected = colitem->selected;
694 GdkRectangle pic_area;
696 pic_area.x = area->x;
697 pic_area.y = area->y;
698 pic_area.width = SMALL_ICON_WIDTH;
699 pic_area.height = SMALL_ICON_HEIGHT;
701 draw_small_icon(widget, &pic_area, item, selected);
703 draw_string(widget,
704 item_font,
705 item->leafname,
706 text_x,
707 low_text_y,
708 item->name_width,
709 area->width - (text_x - area->x),
710 selected, TRUE);
713 static void draw_item_large(GtkWidget *widget,
714 CollectionItem *colitem,
715 GdkRectangle *area,
716 FilerWindow *filer_window)
718 DirItem *item = (DirItem *) colitem->data;
719 int text_width = item->name_width;
720 int text_x = area->x + ((area->width - text_width) >> 1);
721 int text_y = area->y + area->height - item_font->descent - 2;
722 gboolean selected = colitem->selected;
724 draw_large_icon(widget, area, item, selected);
726 if (text_x < area->x)
727 text_x = area->x;
729 draw_string(widget,
730 item_font,
731 item->leafname,
732 text_x, text_y,
733 item->name_width,
734 area->width,
735 selected, TRUE);
738 int sort_by_name(const void *item1, const void *item2)
740 if (o_sort_nocase)
741 return g_strcasecmp((*((DirItem **)item1))->leafname,
742 (*((DirItem **)item2))->leafname);
743 return strcmp((*((DirItem **)item1))->leafname,
744 (*((DirItem **)item2))->leafname);
747 int sort_by_type(const void *item1, const void *item2)
749 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
750 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
751 MIME_type *m1, *m2;
753 int diff = i1->base_type - i2->base_type;
755 if (!diff)
756 diff = (i1->flags & ITEM_FLAG_APPDIR)
757 - (i2->flags & ITEM_FLAG_APPDIR);
758 if (diff)
759 return diff > 0 ? 1 : -1;
761 m1 = i1->mime_type;
762 m2 = i2->mime_type;
764 if (m1 && m2)
766 diff = strcmp(m1->media_type, m2->media_type);
767 if (!diff)
768 diff = strcmp(m1->subtype, m2->subtype);
770 else if (m1 || m2)
771 diff = m1 ? 1 : -1;
772 else
773 diff = 0;
775 if (diff)
776 return diff > 0 ? 1 : -1;
778 return sort_by_name(item1, item2);
781 int sort_by_date(const void *item1, const void *item2)
783 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
784 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
786 return i1->mtime > i2->mtime ? -1 :
787 i1->mtime < i2->mtime ? 1 :
788 sort_by_name(item1, item2);
791 int sort_by_size(const void *item1, const void *item2)
793 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
794 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
796 return i1->size > i2->size ? -1 :
797 i1->size < i2->size ? 1 :
798 sort_by_name(item1, item2);
801 /* Make the items as narrow as possible */
802 void shrink_width(FilerWindow *filer_window)
804 int i;
805 Collection *col = filer_window->collection;
806 int width = MIN_ITEM_WIDTH;
807 int this_width;
808 DisplayStyle style = filer_window->display_style;
809 int text_height, fixed_height;
810 int height;
812 text_height = item_font->ascent + item_font->descent;
814 for (i = 0; i < col->number_of_items; i++)
816 this_width = calc_width(filer_window,
817 (DirItem *) col->items[i].data);
818 if (this_width > width)
819 width = this_width;
822 switch (style)
824 case LARGE_FULL_INFO:
825 height = MAX_ICON_HEIGHT + 4;
826 break;
827 case SMALL_FULL_INFO:
828 fixed_height = fixed_font->ascent + fixed_font->descent;
829 text_height = MAX(text_height, fixed_height);
830 /* No break */
831 case SMALL_ICONS:
832 height = MAX(text_height, SMALL_ICON_HEIGHT) + 4;
833 break;
834 case LARGE_ICONS:
835 default:
836 height = text_height + MAX_ICON_HEIGHT + 8;
837 break;
840 collection_set_item_size(filer_window->collection, width, height);
843 void display_set_sort_fn(FilerWindow *filer_window,
844 int (*fn)(const void *a, const void *b))
846 if (filer_window->sort_fn == fn)
847 return;
849 filer_window->sort_fn = fn;
850 last_sort_fn = fn;
852 collection_qsort(filer_window->collection,
853 filer_window->sort_fn);
855 update_options_label();
858 void display_details_set(FilerWindow *filer_window, DetailsType details)
860 if (filer_window->details_type == details)
861 return;
862 filer_window->details_type = details;
863 shrink_width(filer_window);
864 update_options_label();
867 void display_style_set(FilerWindow *filer_window, DisplayStyle style)
869 if (filer_window->display_style == style)
870 return;
872 if (filer_window->panel_type)
873 style = LARGE_ICONS;
874 else
875 last_display_style = style;
877 filer_window->display_style = style;
879 switch (style)
881 case SMALL_FULL_INFO:
882 collection_set_functions(filer_window->collection,
883 (CollectionDrawFunc) draw_item_small_full,
884 (CollectionTestFunc) test_point_small_full,
885 filer_window);
886 break;
887 case SMALL_ICONS:
888 collection_set_functions(filer_window->collection,
889 (CollectionDrawFunc) draw_item_small,
890 (CollectionTestFunc) test_point_small,
891 filer_window);
892 break;
893 case LARGE_FULL_INFO:
894 collection_set_functions(filer_window->collection,
895 (CollectionDrawFunc) draw_item_large_full,
896 (CollectionTestFunc) test_point_large_full,
897 filer_window);
898 break;
899 default:
900 collection_set_functions(filer_window->collection,
901 (CollectionDrawFunc) draw_item_large,
902 (CollectionTestFunc) test_point_large,
903 filer_window);
904 break;
907 shrink_width(filer_window);
909 update_options_label();
912 /* Build up some option widgets to go in the options dialog, but don't
913 * fill them in yet.
915 static GtkWidget *create_options()
917 GtkWidget *vbox, *hbox, *slide;
919 vbox = gtk_vbox_new(FALSE, 0);
920 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
922 display_label = gtk_label_new("<>");
923 gtk_label_set_line_wrap(GTK_LABEL(display_label), TRUE);
924 gtk_box_pack_start(GTK_BOX(vbox), display_label, FALSE, TRUE, 0);
926 toggle_sort_nocase =
927 gtk_check_button_new_with_label(_("Ignore case when sorting"));
928 gtk_box_pack_start(GTK_BOX(vbox), toggle_sort_nocase, FALSE, TRUE, 0);
930 hbox = gtk_hbox_new(FALSE, 4);
931 gtk_box_pack_start(GTK_BOX(hbox),
932 gtk_label_new(_("Max Large Icons width")),
933 TRUE, TRUE, 0);
934 adj_large_truncate = GTK_ADJUSTMENT(gtk_adjustment_new(0,
935 MIN_TRUNCATE, MAX_TRUNCATE, 1, 10, 0));
936 slide = gtk_hscale_new(adj_large_truncate);
937 gtk_widget_set_usize(slide, MAX_TRUNCATE, 24);
938 gtk_scale_set_draw_value(GTK_SCALE(slide), FALSE);
939 gtk_box_pack_start(GTK_BOX(hbox), slide, FALSE, TRUE, 0);
940 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
942 hbox = gtk_hbox_new(FALSE, 4);
943 gtk_box_pack_start(GTK_BOX(hbox),
944 gtk_label_new(_("Max Small Icons width")),
945 TRUE, TRUE, 0);
946 adj_small_truncate = GTK_ADJUSTMENT(gtk_adjustment_new(0,
947 MIN_TRUNCATE, MAX_TRUNCATE, 1, 10, 0));
948 slide = gtk_hscale_new(adj_small_truncate);
949 gtk_widget_set_usize(slide, MAX_TRUNCATE, 24);
950 gtk_scale_set_draw_value(GTK_SCALE(slide), FALSE);
951 gtk_box_pack_start(GTK_BOX(hbox), slide, FALSE, TRUE, 0);
952 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
954 return vbox;
957 static void update_options_label(void)
959 guchar *str;
961 str = g_strdup_printf(_("The last used display style (%s) and sort "
962 "function (Sort By %s) will be saved if you click on "
963 "Save."), style_to_name(), sort_fn_to_name());
964 gtk_label_set_text(GTK_LABEL(display_label), str);
965 g_free(str);
968 /* Reflect current state by changing the widgets in the options box */
969 static void update_options()
971 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_sort_nocase),
972 o_sort_nocase);
974 gtk_adjustment_set_value(adj_small_truncate, o_small_truncate);
975 gtk_adjustment_set_value(adj_large_truncate, o_large_truncate);
977 update_options_label();
980 /* Set current values by reading the states of the widgets in the options box */
981 static void set_options()
983 gboolean old_case = o_sort_nocase;
984 GList *next = all_filer_windows;
986 o_sort_nocase = gtk_toggle_button_get_active(
987 GTK_TOGGLE_BUTTON(toggle_sort_nocase));
989 o_small_truncate = adj_small_truncate->value;
990 o_large_truncate = adj_large_truncate->value;
992 while (next)
994 FilerWindow *filer_window = (FilerWindow *) next->data;
996 if (o_sort_nocase != old_case)
998 collection_qsort(filer_window->collection,
999 filer_window->sort_fn);
1001 shrink_width(filer_window);
1003 next = next->next;
1007 static guchar *style_to_name(void)
1009 return last_display_style == LARGE_ICONS ? _("Large Icons") :
1010 last_display_style == SMALL_ICONS ? _("Small Icons") :
1011 _("Full Info");
1014 static guchar *sort_fn_to_name(void)
1016 return last_sort_fn == sort_by_name ? _("Name") :
1017 last_sort_fn == sort_by_type ? _("Type") :
1018 last_sort_fn == sort_by_date ? _("Date") :
1019 _("Size");
1022 static void save_options()
1024 guchar *tmp;
1026 option_write("display_sort_nocase", o_sort_nocase ? "1" : "0");
1027 option_write("display_display_style",
1028 last_display_style == LARGE_ICONS ? "Large Icons" :
1029 last_display_style == SMALL_ICONS ? "Small Icons" :
1030 last_display_style == SMALL_FULL_INFO ? "Small, Full Info" :
1031 "Large, Full Info");
1032 option_write("display_sort_by",
1033 last_sort_fn == sort_by_name ? "Name" :
1034 last_sort_fn == sort_by_type ? "Type" :
1035 last_sort_fn == sort_by_date ? "Date" :
1036 "Size");
1038 tmp = g_strdup_printf("%d, %d", o_large_truncate, o_small_truncate);
1039 option_write("display_truncate", tmp);
1040 g_free(tmp);
1043 static char *display_sort_nocase(char *data)
1045 o_sort_nocase = atoi(data) != 0;
1046 return NULL;
1049 static char *display_display_style(char *data)
1051 if (g_strcasecmp(data, "Large Icons") == 0)
1052 last_display_style = LARGE_ICONS;
1053 else if (g_strcasecmp(data, "Small Icons") == 0)
1054 last_display_style = SMALL_ICONS;
1055 else if (g_strcasecmp(data, "Large, Full Info") == 0)
1056 last_display_style = LARGE_FULL_INFO;
1057 else if (g_strcasecmp(data, "Small, Full Info") == 0)
1058 last_display_style = SMALL_FULL_INFO;
1059 else
1060 return _("Unknown display style");
1062 return NULL;
1065 static char *display_sort_by(char *data)
1067 if (g_strcasecmp(data, "Name") == 0)
1068 last_sort_fn = sort_by_name;
1069 else if (g_strcasecmp(data, "Type") == 0)
1070 last_sort_fn = sort_by_type;
1071 else if (g_strcasecmp(data, "Date") == 0)
1072 last_sort_fn = sort_by_date;
1073 else if (g_strcasecmp(data, "Size") == 0)
1074 last_sort_fn = sort_by_size;
1075 else
1076 return _("Unknown sort type");
1078 return NULL;
1081 static char *display_truncate(char *data)
1083 guchar *comma;
1085 comma = strchr(data, ',');
1086 if (!comma)
1087 return "Missing , in display_truncate";
1089 o_large_truncate = CLAMP(atoi(data), MIN_TRUNCATE, MAX_TRUNCATE);
1090 o_small_truncate = CLAMP(atoi(comma + 1), MIN_TRUNCATE, MAX_TRUNCATE);
1092 return NULL;
1095 /* Set the 'Show Hidden' flag for this window */
1096 void display_set_hidden(FilerWindow *filer_window, gboolean hidden)
1098 if (filer_window->show_hidden == hidden)
1099 return;
1101 filer_window->show_hidden = hidden;
1102 last_show_hidden = hidden;
1104 filer_detach_rescan(filer_window);
1107 /* Highlight (wink or cursor) this item in the filer window. If the item
1108 * isn't already there but we're scanning then highlight it if it
1109 * appears later.
1111 void display_set_autoselect(FilerWindow *filer_window, guchar *leaf)
1113 Collection *col = filer_window->collection;
1114 int i;
1116 g_free(filer_window->auto_select);
1117 filer_window->auto_select = NULL;
1119 for (i = 0; i < col->number_of_items; i++)
1121 DirItem *item = (DirItem *) col->items[i].data;
1123 if (strcmp(item->leafname, leaf) == 0)
1125 if (col->cursor_item != -1)
1126 collection_set_cursor_item(col, i);
1127 else
1128 collection_wink_item(col, i);
1129 return;
1133 filer_window->auto_select = g_strdup(leaf);