r242: The mounted indicator is also scaled to the display style size.
[rox-filer.git] / ROX-Filer / src / filer.c
blob4b028b5030f436fb72633a984ba8b0a345635965
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 /* filer.c - code for handling filer windows */
24 #include "config.h"
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <sys/param.h>
31 #include <unistd.h>
32 #include <time.h>
33 #include <ctype.h>
35 #include <gtk/gtk.h>
36 #include <gdk/gdkx.h>
37 #include <gdk/gdkkeysyms.h>
38 #include "collection.h"
40 #include "main.h"
41 #include "support.h"
42 #include "gui_support.h"
43 #include "filer.h"
44 #include "pixmaps.h"
45 #include "menu.h"
46 #include "dnd.h"
47 #include "run.h"
48 #include "mount.h"
49 #include "type.h"
50 #include "options.h"
51 #include "action.h"
52 #include "minibuffer.h"
54 #define ROW_HEIGHT_LARGE 64
55 #define ROW_HEIGHT_SMALL 28
56 #define ROW_HEIGHT_FULL_INFO 44
57 #define SMALL_ICON_HEIGHT 20
58 #ifdef HAVE_IMLIB
59 # define SMALL_ICON_WIDTH 32
60 #else
61 # define SMALL_ICON_WIDTH 48
62 #endif
63 #define MAX_ICON_HEIGHT 42
64 #define MAX_ICON_WIDTH 48
65 #define PANEL_BORDER 2
66 #define MIN_ITEM_WIDTH 64
68 extern int collection_menu_button;
69 extern gboolean collection_single_click;
71 FilerWindow *window_with_focus = NULL;
72 GList *all_filer_windows = NULL;
74 static DisplayStyle last_display_style = LARGE_ICONS;
75 static gboolean last_show_hidden = FALSE;
76 static int (*last_sort_fn)(const void *a, const void *b) = sort_by_type;
78 static FilerWindow *window_with_selection = NULL;
80 /* Options bits */
81 static guchar *style_to_name(void);
82 static guchar *sort_fn_to_name(void);
83 static void update_options_label(void);
85 static GtkWidget *create_options();
86 static void update_options();
87 static void set_options();
88 static void save_options();
89 static char *filer_single_click(char *data);
90 static char *filer_unique_windows(char *data);
91 static char *filer_menu_on_2(char *data);
92 static char *filer_new_window_on_1(char *data);
93 static char *filer_toolbar(char *data);
94 static char *filer_display_style(char *data);
95 static char *filer_sort_by(char *data);
97 static OptionsSection options =
99 "Filer window options",
100 create_options,
101 update_options,
102 set_options,
103 save_options
106 /* The values correspond to the menu indexes in the option widget */
107 typedef enum {
108 TOOLBAR_NONE = 0,
109 TOOLBAR_NORMAL = 1,
110 TOOLBAR_GNOME = 2,
111 } ToolbarType;
112 static ToolbarType o_toolbar = TOOLBAR_NORMAL;
113 static GtkWidget *menu_toolbar;
115 static GtkWidget *display_label;
117 static gboolean o_single_click = FALSE;
118 static GtkWidget *toggle_single_click;
119 static gboolean o_new_window_on_1 = FALSE; /* Button 1 => New window */
120 static GtkWidget *toggle_new_window_on_1;
121 static GtkWidget *toggle_menu_on_2;
122 static GtkWidget *toggle_unique_filer_windows;
123 gboolean o_unique_filer_windows = FALSE;
125 /* Static prototypes */
126 static void attach(FilerWindow *filer_window);
127 static void detach(FilerWindow *filer_window);
128 static void filer_window_destroyed(GtkWidget *widget,
129 FilerWindow *filer_window);
130 static void show_menu(Collection *collection, GdkEventButton *event,
131 int number_selected, gpointer user_data);
132 static gint focus_in(GtkWidget *widget,
133 GdkEventFocus *event,
134 FilerWindow *filer_window);
135 static gint focus_out(GtkWidget *widget,
136 GdkEventFocus *event,
137 FilerWindow *filer_window);
138 static void add_item(FilerWindow *filer_window, DirItem *item);
139 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window);
140 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window);
141 static void add_button(GtkWidget *box, int pixmap,
142 GtkSignalFunc cb, FilerWindow *filer_window,
143 char *label, char *tip);
144 static GtkWidget *create_toolbar(FilerWindow *filer_window);
145 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
146 FilerWindow *window);
147 static int calc_width(FilerWindow *filer_window, DirItem *item);
148 static void draw_large_icon(GtkWidget *widget,
149 GdkRectangle *area,
150 DirItem *item,
151 gboolean selected);
152 static void draw_string(GtkWidget *widget,
153 GdkFont *font,
154 char *string,
155 int x,
156 int y,
157 int width,
158 gboolean selected);
159 static void draw_item_large(GtkWidget *widget,
160 CollectionItem *item,
161 GdkRectangle *area);
162 static void draw_item_small(GtkWidget *widget,
163 CollectionItem *item,
164 GdkRectangle *area);
165 static void draw_item_full_info(GtkWidget *widget,
166 CollectionItem *colitem,
167 GdkRectangle *area);
168 static gboolean test_point_large(Collection *collection,
169 int point_x, int point_y,
170 CollectionItem *item,
171 int width, int height);
172 static gboolean test_point_small(Collection *collection,
173 int point_x, int point_y,
174 CollectionItem *item,
175 int width, int height);
176 static gboolean test_point_full_info(Collection *collection,
177 int point_x, int point_y,
178 CollectionItem *item,
179 int width, int height);
180 static void update_display(Directory *dir,
181 DirAction action,
182 GPtrArray *items,
183 FilerWindow *filer_window);
184 static void set_scanning_display(FilerWindow *filer_window, gboolean scanning);
185 static void shrink_width(FilerWindow *filer_window);
186 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning);
187 static void open_item(Collection *collection,
188 gpointer item_data, int item_number,
189 gpointer user_data);
190 static gboolean minibuffer_show_cb(FilerWindow *filer_window);
191 static FilerWindow *find_filer_window(char *path, FilerWindow *diff);
192 static void filer_set_title(FilerWindow *filer_window);
193 static gboolean exists(FilerWindow *filer_window);
195 static GdkAtom xa_string;
196 enum
198 TARGET_STRING,
199 TARGET_URI_LIST,
202 static GdkCursor *busy_cursor = NULL;
203 static GtkTooltips *tooltips = NULL;
205 void filer_init()
207 xa_string = gdk_atom_intern("STRING", FALSE);
209 options_sections = g_slist_prepend(options_sections, &options);
210 option_register("filer_new_window_on_1", filer_new_window_on_1);
211 option_register("filer_menu_on_2", filer_menu_on_2);
212 option_register("filer_single_click", filer_single_click);
213 option_register("filer_unique_windows", filer_unique_windows);
214 option_register("filer_toolbar", filer_toolbar);
215 option_register("filer_display_style", filer_display_style);
216 option_register("filer_sort_by", filer_sort_by);
218 busy_cursor = gdk_cursor_new(GDK_WATCH);
220 tooltips = gtk_tooltips_new();
223 static gboolean if_deleted(gpointer item, gpointer removed)
225 int i = ((GPtrArray *) removed)->len;
226 DirItem **r = (DirItem **) ((GPtrArray *) removed)->pdata;
227 char *leafname = ((DirItem *) item)->leafname;
229 while (i--)
231 if (strcmp(leafname, r[i]->leafname) == 0)
232 return TRUE;
235 return FALSE;
238 static void update_item(FilerWindow *filer_window, DirItem *item)
240 int i;
241 char *leafname = item->leafname;
243 if (leafname[0] == '.')
245 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
246 || (leafname[1] == '.' && leafname[2] == '\0'))
247 return;
250 i = collection_find_item(filer_window->collection, item, dir_item_cmp);
252 if (i >= 0)
253 collection_draw_item(filer_window->collection, i, TRUE);
254 else
255 g_warning("Failed to find '%s'\n", item->leafname);
258 static void update_display(Directory *dir,
259 DirAction action,
260 GPtrArray *items,
261 FilerWindow *filer_window)
263 int old_num;
264 int i;
265 int cursor = filer_window->collection->cursor_item;
266 char *as;
267 Collection *collection = filer_window->collection;
269 switch (action)
271 case DIR_ADD:
272 as = filer_window->auto_select;
274 old_num = collection->number_of_items;
275 for (i = 0; i < items->len; i++)
277 DirItem *item = (DirItem *) items->pdata[i];
279 add_item(filer_window, item);
281 if (cursor != -1 || !as)
282 continue;
284 if (strcmp(as, item->leafname) != 0)
285 continue;
287 cursor = collection->number_of_items - 1;
288 if (filer_window->had_cursor)
290 collection_set_cursor_item(collection,
291 cursor);
292 filer_window->mini_cursor_base = cursor;
294 else
295 collection_wink_item(collection,
296 cursor);
299 if (old_num != collection->number_of_items)
300 collection_qsort(filer_window->collection,
301 filer_window->sort_fn);
302 break;
303 case DIR_REMOVE:
304 collection_delete_if(filer_window->collection,
305 if_deleted,
306 items);
307 break;
308 case DIR_START_SCAN:
309 set_scanning_display(filer_window, TRUE);
310 break;
311 case DIR_END_SCAN:
312 if (filer_window->window->window)
313 gdk_window_set_cursor(
314 filer_window->window->window,
315 NULL);
316 shrink_width(filer_window);
317 if (filer_window->had_cursor &&
318 collection->cursor_item == -1)
320 collection_set_cursor_item(collection, 0);
321 filer_window->had_cursor = FALSE;
323 set_scanning_display(filer_window, FALSE);
324 break;
325 case DIR_UPDATE:
326 for (i = 0; i < items->len; i++)
328 DirItem *item = (DirItem *) items->pdata[i];
330 update_item(filer_window, item);
332 collection_qsort(filer_window->collection,
333 filer_window->sort_fn);
334 break;
338 static void attach(FilerWindow *filer_window)
340 gdk_window_set_cursor(filer_window->window->window, busy_cursor);
341 collection_clear(filer_window->collection);
342 filer_window->scanning = TRUE;
343 dir_attach(filer_window->directory, (DirCallback) update_display,
344 filer_window);
345 filer_set_title(filer_window);
348 static void detach(FilerWindow *filer_window)
350 g_return_if_fail(filer_window->directory != NULL);
352 dir_detach(filer_window->directory,
353 (DirCallback) update_display, filer_window);
354 g_fscache_data_unref(dir_cache, filer_window->directory);
355 filer_window->directory = NULL;
358 static void filer_window_destroyed(GtkWidget *widget,
359 FilerWindow *filer_window)
361 all_filer_windows = g_list_remove(all_filer_windows, filer_window);
363 if (window_with_selection == filer_window)
364 window_with_selection = NULL;
365 if (window_with_focus == filer_window)
366 window_with_focus = NULL;
368 if (filer_window->directory)
369 detach(filer_window);
371 g_free(filer_window->auto_select);
372 g_free(filer_window->path);
373 g_free(filer_window);
375 if (--number_of_windows < 1)
376 gtk_main_quit();
379 static int calc_width(FilerWindow *filer_window, DirItem *item)
381 int pix_width = item->image->width;
383 switch (filer_window->display_style)
385 case FULL_INFO:
386 return MAX_ICON_WIDTH + 12 +
387 MAX(item->details_width, item->name_width);
388 case SMALL_ICONS:
389 return SMALL_ICON_WIDTH + 12 + item->name_width;
390 default:
391 return MAX(pix_width, item->name_width) + 4;
395 /* Add a single object to a directory display */
396 static void add_item(FilerWindow *filer_window, DirItem *item)
398 char *leafname = item->leafname;
399 int item_width;
401 if (leafname[0] == '.')
403 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
404 || (leafname[1] == '.' && leafname[2] == '\0'))
405 return;
408 item_width = calc_width(filer_window, item);
409 if (item_width > filer_window->collection->item_width)
410 collection_set_item_size(filer_window->collection,
411 item_width,
412 filer_window->collection->item_height);
413 collection_insert(filer_window->collection, item);
416 /* Is a point inside an item? */
417 static gboolean test_point_large(Collection *collection,
418 int point_x, int point_y,
419 CollectionItem *colitem,
420 int width, int height)
422 DirItem *item = (DirItem *) colitem->data;
423 int text_height = item_font->ascent + item_font->descent;
424 MaskedPixmap *image = item->image;
425 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
426 int image_width = (image->width >> 1) + 2;
427 int text_width = (item->name_width >> 1) + 2;
428 int x_limit;
430 if (point_y < image_y)
431 return FALSE; /* Too high up (don't worry about too low) */
433 if (point_y <= image_y + image->height + 2)
434 x_limit = image_width;
435 else if (point_y > height - text_height - 2)
436 x_limit = text_width;
437 else
438 x_limit = MIN(image_width, text_width);
440 return ABS(point_x - (width >> 1)) < x_limit;
443 static gboolean test_point_full_info(Collection *collection,
444 int point_x, int point_y,
445 CollectionItem *colitem,
446 int width, int height)
448 DirItem *item = (DirItem *) colitem->data;
449 MaskedPixmap *image = item->image;
450 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
451 int low_top = height
452 - fixed_font->descent - 2 - fixed_font->ascent;
454 if (point_x < image->width + 2)
455 return point_x > 2 && point_y > image_y;
457 point_x -= MAX_ICON_WIDTH + 8;
459 if (point_y >= low_top)
460 return point_x < item->details_width;
461 if (point_y >= low_top - item_font->ascent - item_font->descent)
462 return point_x < item->name_width;
463 return FALSE;
466 static gboolean test_point_small(Collection *collection,
467 int point_x, int point_y,
468 CollectionItem *colitem,
469 int width, int height)
471 DirItem *item = (DirItem *) colitem->data;
472 MaskedPixmap *image = item->image;
473 int image_y = MAX(0, SMALL_ICON_HEIGHT - image->height);
474 int low_top = height
475 - fixed_font->descent - 2 - item_font->ascent;
476 int iwidth = MIN(SMALL_ICON_WIDTH, image->width);
478 if (point_x < iwidth + 2)
479 return point_x > 2 && point_y > image_y;
481 point_x -= SMALL_ICON_WIDTH + 4;
483 if (point_y >= low_top)
484 return point_x < item->name_width;
485 return FALSE;
488 static void draw_small_icon(GtkWidget *widget,
489 GdkRectangle *area,
490 DirItem *item,
491 gboolean selected)
493 GdkGC *gc = selected ? widget->style->white_gc
494 : widget->style->black_gc;
495 MaskedPixmap *image = item->image;
496 int width, height, image_x, image_y;
498 if (!image)
499 return;
501 if (!image->sm_pixmap)
502 pixmap_make_small(image);
504 width = MIN(image->sm_width, SMALL_ICON_WIDTH);
505 height = MIN(image->sm_height, SMALL_ICON_HEIGHT);
506 image_x = area->x + ((area->width - width) >> 1);
508 gdk_gc_set_clip_mask(gc, item->image->sm_mask);
510 image_y = MAX(0, SMALL_ICON_HEIGHT - image->sm_height);
511 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
512 gdk_draw_pixmap(widget->window, gc,
513 item->image->sm_pixmap,
514 0, 0, /* Source x,y */
515 image_x, area->y + image_y, /* Dest x,y */
516 width, height);
518 if (selected)
520 gdk_gc_set_function(gc, GDK_INVERT);
521 gdk_draw_rectangle(widget->window,
523 TRUE, image_x, area->y + image_y,
524 width, height);
525 gdk_gc_set_function(gc, GDK_COPY);
528 if (item->flags & ITEM_FLAG_SYMLINK)
530 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
531 gdk_gc_set_clip_mask(gc,
532 default_pixmap[TYPE_SYMLINK]->mask);
533 gdk_draw_pixmap(widget->window, gc,
534 default_pixmap[TYPE_SYMLINK]->pixmap,
535 0, 0, /* Source x,y */
536 image_x, area->y + 8, /* Dest x,y */
537 -1, -1);
539 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
541 int type = item->flags & ITEM_FLAG_MOUNTED
542 ? TYPE_MOUNTED
543 : TYPE_UNMOUNTED;
544 MaskedPixmap *mp = default_pixmap[type];
546 if (!mp->sm_pixmap)
547 pixmap_make_small(mp);
548 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
549 gdk_gc_set_clip_mask(gc, mp->sm_mask);
550 gdk_draw_pixmap(widget->window, gc,
551 mp->sm_pixmap,
552 0, 0, /* Source x,y */
553 image_x, area->y + 8, /* Dest x,y */
554 -1, -1);
557 gdk_gc_set_clip_mask(gc, NULL);
558 gdk_gc_set_clip_origin(gc, 0, 0);
561 static void draw_large_icon(GtkWidget *widget,
562 GdkRectangle *area,
563 DirItem *item,
564 gboolean selected)
566 MaskedPixmap *image = item->image;
567 int width = MIN(image->width, MAX_ICON_WIDTH);
568 int height = MIN(image->height, MAX_ICON_WIDTH);
569 int image_x = area->x + ((area->width - width) >> 1);
570 int image_y;
571 GdkGC *gc = selected ? widget->style->white_gc
572 : widget->style->black_gc;
574 gdk_gc_set_clip_mask(gc, item->image->mask);
576 image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
577 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
578 gdk_draw_pixmap(widget->window, gc,
579 item->image->pixmap,
580 0, 0, /* Source x,y */
581 image_x, area->y + image_y, /* Dest x,y */
582 width, height);
584 if (selected)
586 gdk_gc_set_function(gc, GDK_INVERT);
587 gdk_draw_rectangle(widget->window,
589 TRUE, image_x, area->y + image_y,
590 width, height);
591 gdk_gc_set_function(gc, GDK_COPY);
594 if (item->flags & ITEM_FLAG_SYMLINK)
596 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
597 gdk_gc_set_clip_mask(gc,
598 default_pixmap[TYPE_SYMLINK]->mask);
599 gdk_draw_pixmap(widget->window, gc,
600 default_pixmap[TYPE_SYMLINK]->pixmap,
601 0, 0, /* Source x,y */
602 image_x, area->y + 8, /* Dest x,y */
603 -1, -1);
605 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
607 int type = item->flags & ITEM_FLAG_MOUNTED
608 ? TYPE_MOUNTED
609 : TYPE_UNMOUNTED;
610 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
611 gdk_gc_set_clip_mask(gc,
612 default_pixmap[type]->mask);
613 gdk_draw_pixmap(widget->window, gc,
614 default_pixmap[type]->pixmap,
615 0, 0, /* Source x,y */
616 image_x, area->y + 8, /* Dest x,y */
617 -1, -1);
620 gdk_gc_set_clip_mask(gc, NULL);
621 gdk_gc_set_clip_origin(gc, 0, 0);
624 static void draw_string(GtkWidget *widget,
625 GdkFont *font,
626 char *string,
627 int x,
628 int y,
629 int width,
630 gboolean selected)
632 int text_height = font->ascent + font->descent;
634 if (selected)
635 gtk_paint_flat_box(widget->style, widget->window,
636 GTK_STATE_SELECTED, GTK_SHADOW_NONE,
637 NULL, widget, "text",
638 x, y - font->ascent,
639 width,
640 text_height);
642 gdk_draw_text(widget->window,
643 font,
644 selected ? widget->style->white_gc
645 : widget->style->black_gc,
646 x, y,
647 string, strlen(string));
650 /* Return a string (valid until next call) giving details
651 * of this item.
653 char *details(DirItem *item)
655 mode_t m = item->mode;
656 static guchar *buf = NULL;
658 if (buf)
659 g_free(buf);
661 if (item->lstat_errno)
662 buf = g_strdup_printf("lstat(2) failed: %s",
663 g_strerror(item->lstat_errno));
664 else
665 buf = g_strdup_printf("%s %s %-8.8s %-8.8s %s %s",
666 item->flags & ITEM_FLAG_APPDIR? "App " :
667 S_ISDIR(m) ? "Dir " :
668 S_ISCHR(m) ? "Char" :
669 S_ISBLK(m) ? "Blck" :
670 S_ISLNK(m) ? "Link" :
671 S_ISSOCK(m) ? "Sock" :
672 S_ISFIFO(m) ? "Pipe" : "File",
673 pretty_permissions(m),
674 user_name(item->uid),
675 group_name(item->gid),
676 format_size_aligned(item->size),
677 pretty_time(&item->mtime));
678 return buf;
681 static void draw_item_full_info(GtkWidget *widget,
682 CollectionItem *colitem,
683 GdkRectangle *area)
685 DirItem *item = (DirItem *) colitem->data;
686 MaskedPixmap *image = item->image;
687 int text_x = area->x + MAX_ICON_WIDTH + 8;
688 int low_text_y = area->y + area->height - fixed_font->descent - 2;
689 gboolean selected = colitem->selected;
690 GdkRectangle pic_area;
692 pic_area.x = area->x;
693 pic_area.y = area->y;
694 pic_area.width = image->width + 8;
695 pic_area.height = area->height;
697 draw_large_icon(widget, &pic_area, item, selected);
699 draw_string(widget,
700 item_font,
701 item->leafname,
702 text_x,
703 low_text_y - item_font->descent - fixed_font->ascent,
704 item->name_width,
705 selected);
706 draw_string(widget,
707 fixed_font,
708 details(item),
709 text_x, low_text_y,
710 item->details_width,
711 selected);
713 if (item->lstat_errno)
714 return;
716 /* Underline the effective permissions */
717 gdk_draw_rectangle(widget->window,
718 selected ? widget->style->white_gc
719 : widget->style->black_gc,
720 TRUE,
721 text_x - 1 + fixed_width *
722 (5 + 4 * applicable(item->uid, item->gid)),
723 low_text_y + fixed_font->descent - 1,
724 fixed_width * 3 + 1, 1);
727 static void draw_item_small(GtkWidget *widget,
728 CollectionItem *colitem,
729 GdkRectangle *area)
731 DirItem *item = (DirItem *) colitem->data;
732 int text_x = area->x + SMALL_ICON_WIDTH + 4;
733 int low_text_y = area->y + area->height - item_font->descent - 2;
734 gboolean selected = colitem->selected;
735 GdkRectangle pic_area;
737 pic_area.x = area->x;
738 pic_area.y = area->y;
739 pic_area.width = SMALL_ICON_WIDTH;
740 pic_area.height = SMALL_ICON_HEIGHT;
742 draw_small_icon(widget, &pic_area, item, selected);
744 draw_string(widget,
745 item_font,
746 item->leafname,
747 text_x,
748 low_text_y,
749 item->name_width,
750 selected);
753 static void draw_item_large(GtkWidget *widget,
754 CollectionItem *colitem,
755 GdkRectangle *area)
757 DirItem *item = (DirItem *) colitem->data;
758 int text_x = area->x + ((area->width - item->name_width) >> 1);
759 int text_y = area->y + area->height - item_font->descent - 2;
760 gboolean selected = colitem->selected;
762 draw_large_icon(widget, area, item, selected);
764 draw_string(widget,
765 item_font,
766 item->leafname,
767 text_x, text_y, item->name_width,
768 selected);
771 static void show_menu(Collection *collection, GdkEventButton *event,
772 int item, gpointer user_data)
774 show_filer_menu((FilerWindow *) user_data, event, item);
777 /* Returns TRUE iff the directory still exists. */
778 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning)
780 Directory *dir;
782 g_return_val_if_fail(filer_window != NULL, FALSE);
784 /* We do a fresh lookup (rather than update) because the inode may
785 * have changed.
787 dir = g_fscache_lookup(dir_cache, filer_window->path);
788 if (!dir)
790 if (warning)
791 delayed_error("ROX-Filer", "Directory missing/deleted");
792 gtk_widget_destroy(filer_window->window);
793 return FALSE;
795 if (dir == filer_window->directory)
796 g_fscache_data_unref(dir_cache, dir);
797 else
799 detach(filer_window);
800 filer_window->directory = dir;
801 attach(filer_window);
804 return TRUE;
807 /* Another app has grabbed the selection */
808 static gint collection_lose_selection(GtkWidget *widget,
809 GdkEventSelection *event)
811 if (window_with_selection &&
812 window_with_selection->collection == COLLECTION(widget))
814 FilerWindow *filer_window = window_with_selection;
815 window_with_selection = NULL;
816 collection_clear_selection(filer_window->collection);
819 return TRUE;
822 /* Someone wants us to send them the selection */
823 static void selection_get(GtkWidget *widget,
824 GtkSelectionData *selection_data,
825 guint info,
826 guint time,
827 gpointer data)
829 GString *reply, *header;
830 FilerWindow *filer_window;
831 int i;
832 Collection *collection;
834 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
836 reply = g_string_new(NULL);
837 header = g_string_new(NULL);
839 switch (info)
841 case TARGET_STRING:
842 g_string_sprintf(header, " %s",
843 make_path(filer_window->path, "")->str);
844 break;
845 case TARGET_URI_LIST:
846 g_string_sprintf(header, " file://%s%s",
847 our_host_name(),
848 make_path(filer_window->path, "")->str);
849 break;
852 collection = filer_window->collection;
853 for (i = 0; i < collection->number_of_items; i++)
855 if (collection->items[i].selected)
857 DirItem *item =
858 (DirItem *) collection->items[i].data;
860 g_string_append(reply, header->str);
861 g_string_append(reply, item->leafname);
864 /* This works, but I don't think I like it... */
865 /* g_string_append_c(reply, ' '); */
867 gtk_selection_data_set(selection_data, xa_string,
868 8, reply->str + 1, reply->len - 1);
869 g_string_free(reply, TRUE);
870 g_string_free(header, TRUE);
873 /* No items are now selected. This might be because another app claimed
874 * the selection or because the user unselected all the items.
876 static void lose_selection(Collection *collection,
877 guint time,
878 gpointer user_data)
880 FilerWindow *filer_window = (FilerWindow *) user_data;
882 if (window_with_selection == filer_window)
884 window_with_selection = NULL;
885 gtk_selection_owner_set(NULL,
886 GDK_SELECTION_PRIMARY,
887 time);
891 static void gain_selection(Collection *collection,
892 guint time,
893 gpointer user_data)
895 FilerWindow *filer_window = (FilerWindow *) user_data;
897 if (gtk_selection_owner_set(GTK_WIDGET(collection),
898 GDK_SELECTION_PRIMARY,
899 time))
901 window_with_selection = filer_window;
903 else
904 collection_clear_selection(filer_window->collection);
907 int sort_by_name(const void *item1, const void *item2)
909 return strcmp((*((DirItem **)item1))->leafname,
910 (*((DirItem **)item2))->leafname);
913 int sort_by_type(const void *item1, const void *item2)
915 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
916 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
917 MIME_type *m1, *m2;
919 int diff = i1->base_type - i2->base_type;
921 if (!diff)
922 diff = (i1->flags & ITEM_FLAG_APPDIR)
923 - (i2->flags & ITEM_FLAG_APPDIR);
924 if (diff)
925 return diff > 0 ? 1 : -1;
927 m1 = i1->mime_type;
928 m2 = i2->mime_type;
930 if (m1 && m2)
932 diff = strcmp(m1->media_type, m2->media_type);
933 if (!diff)
934 diff = strcmp(m1->subtype, m2->subtype);
936 else if (m1 || m2)
937 diff = m1 ? 1 : -1;
938 else
939 diff = 0;
941 if (diff)
942 return diff > 0 ? 1 : -1;
944 return sort_by_name(item1, item2);
947 int sort_by_date(const void *item1, const void *item2)
949 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
950 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
952 return i1->mtime > i2->mtime ? -1 :
953 i1->mtime < i2->mtime ? 1 :
954 sort_by_name(item1, item2);
957 int sort_by_size(const void *item1, const void *item2)
959 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
960 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
962 return i1->size > i2->size ? -1 :
963 i1->size < i2->size ? 1 :
964 sort_by_name(item1, item2);
967 static void open_item(Collection *collection,
968 gpointer item_data, int item_number,
969 gpointer user_data)
971 FilerWindow *filer_window = (FilerWindow *) user_data;
972 GdkEvent *event;
973 GdkEventButton *bevent;
974 GdkEventKey *kevent;
975 OpenFlags flags = 0;
977 event = (GdkEvent *) gtk_get_current_event();
979 bevent = (GdkEventButton *) event;
980 kevent = (GdkEventKey *) event;
982 switch (event->type)
984 case GDK_2BUTTON_PRESS:
985 case GDK_BUTTON_PRESS:
986 case GDK_BUTTON_RELEASE:
987 if (bevent->state & GDK_SHIFT_MASK)
988 flags |= OPEN_SHIFT;
990 if (o_new_window_on_1 ^ (bevent->button == 1))
991 flags |= OPEN_SAME_WINDOW;
993 if (bevent->button != 1)
994 flags |= OPEN_CLOSE_WINDOW;
996 if (o_single_click == FALSE &&
997 (bevent->state & GDK_CONTROL_MASK) != 0)
998 flags ^= OPEN_SAME_WINDOW | OPEN_CLOSE_WINDOW;
999 break;
1000 case GDK_KEY_PRESS:
1001 flags |= OPEN_SAME_WINDOW;
1002 if (kevent->state & GDK_SHIFT_MASK)
1003 flags |= OPEN_SHIFT;
1004 break;
1005 default:
1006 break;
1009 filer_openitem(filer_window, item_number, flags);
1012 /* Return the full path to the directory containing object 'path'.
1013 * Relative paths are resolved from the filerwindow's path.
1015 static void follow_symlink(FilerWindow *filer_window, char *path,
1016 gboolean same_window)
1018 char *real, *slash;
1019 char *new_dir;
1021 if (path[0] != '/')
1022 path = make_path(filer_window->path, path)->str;
1024 real = pathdup(path);
1025 slash = strrchr(real, '/');
1026 if (!slash)
1028 g_free(real);
1029 delayed_error("ROX-Filer",
1030 "Broken symlink (or you don't have permission "
1031 "to follow it).");
1032 return;
1035 *slash = '\0';
1037 if (*real)
1038 new_dir = real;
1039 else
1040 new_dir = "/";
1042 if (filer_window->panel_type || !same_window)
1044 FilerWindow *new;
1046 new = filer_opendir(new_dir, PANEL_NO);
1047 filer_set_autoselect(new, slash + 1);
1049 else
1050 filer_change_to(filer_window, new_dir, slash + 1);
1052 g_free(real);
1055 /* Open the item (or add it to the shell command minibuffer) */
1056 void filer_openitem(FilerWindow *filer_window, int item_number, OpenFlags flags)
1058 gboolean shift = (flags & OPEN_SHIFT) != 0;
1059 gboolean close_mini = flags & OPEN_FROM_MINI;
1060 gboolean same_window = (flags & OPEN_SAME_WINDOW) != 0
1061 && !filer_window->panel_type;
1062 gboolean close_window = (flags & OPEN_CLOSE_WINDOW) != 0
1063 && !filer_window->panel_type;
1064 GtkWidget *widget;
1065 char *full_path;
1066 DirItem *item = (DirItem *)
1067 filer_window->collection->items[item_number].data;
1068 gboolean wink = TRUE, destroy = FALSE;
1070 widget = filer_window->window;
1071 if (filer_window->mini_type == MINI_SHELL)
1073 minibuffer_add(filer_window, item->leafname);
1074 return;
1077 full_path = make_path(filer_window->path,
1078 item->leafname)->str;
1080 if (item->flags & ITEM_FLAG_SYMLINK && shift)
1082 char path[MAXPATHLEN + 1];
1083 int got;
1085 got = readlink(make_path(filer_window->path,
1086 item->leafname)->str,
1087 path, MAXPATHLEN);
1088 if (got < 0)
1089 delayed_error("ROX-Filer", g_strerror(errno));
1090 else
1092 g_return_if_fail(got <= MAXPATHLEN);
1093 path[got] = '\0';
1095 follow_symlink(filer_window, path,
1096 flags & OPEN_SAME_WINDOW);
1098 return;
1101 switch (item->base_type)
1103 case TYPE_DIRECTORY:
1104 if (item->flags & ITEM_FLAG_APPDIR && !shift)
1106 run_app(make_path(filer_window->path,
1107 item->leafname)->str);
1108 if (close_window)
1109 destroy = TRUE;
1110 break;
1113 if (item->flags & ITEM_FLAG_MOUNT_POINT && shift)
1115 action_mount(filer_window, item);
1116 if (item->flags & ITEM_FLAG_MOUNTED)
1117 break;
1120 if (same_window)
1122 wink = FALSE;
1123 filer_change_to(filer_window, full_path, NULL);
1124 close_mini = FALSE;
1126 else
1127 filer_opendir(full_path, PANEL_NO);
1128 break;
1129 case TYPE_FILE:
1130 if ((item->flags & ITEM_FLAG_EXEC_FILE) && !shift)
1132 char *argv[] = {NULL, NULL};
1134 argv[0] = full_path;
1136 if (spawn_full(argv, filer_window->path))
1138 if (close_window)
1139 destroy = TRUE;
1141 else
1142 report_error("ROX-Filer",
1143 "Failed to fork() child");
1145 else
1147 GString *message;
1148 MIME_type *type = shift ? &text_plain
1149 : item->mime_type;
1151 g_return_if_fail(type != NULL);
1153 if (type_open(full_path, type))
1155 if (close_window)
1156 destroy = TRUE;
1158 else
1160 message = g_string_new(NULL);
1161 g_string_sprintf(message, "No open "
1162 "action specified for files of "
1163 "this type (%s/%s)",
1164 type->media_type,
1165 type->subtype);
1166 report_error("ROX-Filer", message->str);
1167 g_string_free(message, TRUE);
1170 break;
1171 default:
1172 report_error("open_item",
1173 "I don't know how to open that");
1174 break;
1177 if (destroy)
1178 gtk_widget_destroy(filer_window->window);
1179 else
1181 if (wink)
1182 collection_wink_item(filer_window->collection,
1183 item_number);
1184 if (close_mini)
1185 minibuffer_hide(filer_window);
1189 static gint pointer_in(GtkWidget *widget,
1190 GdkEventCrossing *event,
1191 FilerWindow *filer_window)
1193 may_rescan(filer_window, TRUE);
1194 return FALSE;
1197 static gint focus_in(GtkWidget *widget,
1198 GdkEventFocus *event,
1199 FilerWindow *filer_window)
1201 window_with_focus = filer_window;
1203 return FALSE;
1206 static gint focus_out(GtkWidget *widget,
1207 GdkEventFocus *event,
1208 FilerWindow *filer_window)
1210 /* TODO: Shade the cursor */
1212 return FALSE;
1215 /* Handle keys that can't be bound with the menu */
1216 static gint key_press_event(GtkWidget *widget,
1217 GdkEventKey *event,
1218 FilerWindow *filer_window)
1220 switch (event->keyval)
1222 case GDK_BackSpace:
1223 change_to_parent(filer_window);
1224 break;
1225 default:
1226 return FALSE;
1229 return TRUE;
1232 static void toolbar_refresh_clicked(GtkWidget *widget,
1233 FilerWindow *filer_window)
1235 full_refresh();
1236 update_dir(filer_window, TRUE);
1239 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window)
1241 filer_change_to(filer_window, home_dir, NULL);
1244 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window)
1246 change_to_parent(filer_window);
1249 void change_to_parent(FilerWindow *filer_window)
1251 char *copy;
1252 char *slash;
1254 if (filer_window->path[0] == '/' && filer_window->path[1] == '\0')
1255 return; /* Already in the root */
1257 copy = g_strdup(filer_window->path);
1258 slash = strrchr(copy, '/');
1260 if (slash)
1262 *slash = '\0';
1263 filer_change_to(filer_window,
1264 *copy ? copy : "/",
1265 slash + 1);
1267 else
1268 g_warning("No / in directory path!\n");
1270 g_free(copy);
1274 /* Make filer_window display path. When finished, highlight item 'from', or
1275 * the first item if from is NULL. If there is currently no cursor then
1276 * simply wink 'from' (if not NULL).
1278 void filer_change_to(FilerWindow *filer_window, char *path, char *from)
1280 char *from_dup;
1281 char *real_path = pathdup(path);
1283 if (o_unique_filer_windows)
1285 FilerWindow *fw;
1287 fw = find_filer_window(real_path, filer_window);
1288 if (fw)
1289 gtk_widget_destroy(fw->window);
1292 from_dup = from && *from ? g_strdup(from) : NULL;
1294 detach(filer_window);
1295 g_free(filer_window->path);
1296 filer_window->path = real_path;
1298 filer_window->directory = g_fscache_lookup(dir_cache,
1299 filer_window->path);
1300 if (filer_window->directory)
1302 g_free(filer_window->auto_select);
1303 filer_window->had_cursor =
1304 filer_window->collection->cursor_item != -1
1305 || filer_window->had_cursor;
1306 filer_window->auto_select = from_dup;
1308 filer_set_title(filer_window);
1309 collection_set_cursor_item(filer_window->collection, -1);
1310 attach(filer_window);
1312 if (filer_window->mini_type == MINI_PATH)
1313 gtk_idle_add((GtkFunction) minibuffer_show_cb,
1314 filer_window);
1316 else
1318 char *error;
1320 g_free(from_dup);
1321 error = g_strdup_printf("Directory '%s' is not accessible.",
1322 path);
1323 delayed_error("ROX-Filer", error);
1324 g_free(error);
1325 gtk_widget_destroy(filer_window->window);
1329 int selected_item_number(Collection *collection)
1331 int i;
1333 g_return_val_if_fail(collection != NULL, -1);
1334 g_return_val_if_fail(IS_COLLECTION(collection), -1);
1335 g_return_val_if_fail(collection->number_selected == 1, -1);
1337 for (i = 0; i < collection->number_of_items; i++)
1338 if (collection->items[i].selected)
1339 return i;
1341 g_warning("selected_item: number_selected is wrong\n");
1343 return -1;
1346 DirItem *selected_item(Collection *collection)
1348 int item;
1350 item = selected_item_number(collection);
1352 if (item > -1)
1353 return (DirItem *) collection->items[item].data;
1354 return NULL;
1357 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
1358 FilerWindow *window)
1360 /* TODO: We can open lots of these - very irritating! */
1361 return get_choice("Close panel?",
1362 "You have tried to close a panel via the window "
1363 "manager - I usually find that this is accidental... "
1364 "really close?",
1365 2, "Remove", "Cancel") != 0;
1368 /* Make the items as narrow as possible */
1369 static void shrink_width(FilerWindow *filer_window)
1371 int i;
1372 Collection *col = filer_window->collection;
1373 int width = MIN_ITEM_WIDTH;
1374 int this_width;
1375 DisplayStyle style = filer_window->display_style;
1376 int text_height;
1378 text_height = item_font->ascent + item_font->descent;
1380 for (i = 0; i < col->number_of_items; i++)
1382 this_width = calc_width(filer_window,
1383 (DirItem *) col->items[i].data);
1384 if (this_width > width)
1385 width = this_width;
1388 collection_set_item_size(filer_window->collection,
1389 width,
1390 style == FULL_INFO ? MAX_ICON_HEIGHT + 4 :
1391 style == SMALL_ICONS ? MAX(text_height, SMALL_ICON_HEIGHT) + 4
1392 : text_height + MAX_ICON_HEIGHT + 8);
1395 void filer_set_sort_fn(FilerWindow *filer_window,
1396 int (*fn)(const void *a, const void *b))
1398 if (filer_window->sort_fn == fn)
1399 return;
1401 filer_window->sort_fn = fn;
1402 last_sort_fn = fn;
1404 collection_qsort(filer_window->collection,
1405 filer_window->sort_fn);
1407 update_options_label();
1410 void filer_style_set(FilerWindow *filer_window, DisplayStyle style)
1412 if (filer_window->display_style == style)
1413 return;
1415 if (filer_window->panel_type)
1416 style = LARGE_ICONS;
1417 else
1418 last_display_style = style;
1420 filer_window->display_style = style;
1422 switch (style)
1424 case SMALL_ICONS:
1425 collection_set_functions(filer_window->collection,
1426 draw_item_small, test_point_small);
1427 break;
1428 case FULL_INFO:
1429 collection_set_functions(filer_window->collection,
1430 draw_item_full_info, test_point_full_info);
1431 break;
1432 default:
1433 collection_set_functions(filer_window->collection,
1434 draw_item_large, test_point_large);
1435 break;
1438 shrink_width(filer_window);
1440 update_options_label();
1443 FilerWindow *filer_opendir(char *path, PanelType panel_type)
1445 GtkWidget *hbox, *scrollbar, *collection;
1446 FilerWindow *filer_window;
1447 GtkTargetEntry target_table[] =
1449 {"text/uri-list", 0, TARGET_URI_LIST},
1450 {"STRING", 0, TARGET_STRING},
1452 char *real_path;
1454 real_path = pathdup(path);
1456 if (o_unique_filer_windows && panel_type == PANEL_NO)
1458 FilerWindow *fw;
1460 fw = find_filer_window(real_path, NULL);
1462 if (fw)
1464 /* TODO: this should bring the window to the front
1465 * at the same coordinates.
1467 gtk_widget_hide(fw->window);
1468 gtk_widget_show(fw->window);
1469 g_free(real_path);
1470 return fw;
1474 filer_window = g_new(FilerWindow, 1);
1475 filer_window->minibuffer = NULL;
1476 filer_window->path = real_path;
1477 filer_window->scanning = FALSE;
1478 filer_window->had_cursor = FALSE;
1479 filer_window->auto_select = NULL;
1480 filer_window->mini_type = MINI_NONE;
1482 filer_window->directory = g_fscache_lookup(dir_cache,
1483 filer_window->path);
1484 if (!filer_window->directory)
1486 char *error;
1488 error = g_strdup_printf("Directory '%s' not found.", path);
1489 delayed_error("ROX-Filer", error);
1490 g_free(error);
1491 g_free(filer_window->path);
1492 g_free(filer_window);
1493 return NULL;
1496 filer_window->show_hidden = last_show_hidden;
1497 filer_window->panel_type = panel_type;
1498 filer_window->temp_item_selected = FALSE;
1499 filer_window->sort_fn = last_sort_fn;
1500 filer_window->flags = (FilerFlags) 0;
1501 filer_window->display_style = UNKNOWN_STYLE;
1503 filer_window->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1504 filer_set_title(filer_window);
1506 collection = collection_new(NULL);
1507 gtk_object_set_data(GTK_OBJECT(collection),
1508 "filer_window", filer_window);
1509 filer_window->collection = COLLECTION(collection);
1511 gtk_widget_add_events(filer_window->window, GDK_ENTER_NOTIFY);
1512 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1513 "enter-notify-event",
1514 GTK_SIGNAL_FUNC(pointer_in), filer_window);
1515 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_in_event",
1516 GTK_SIGNAL_FUNC(focus_in), filer_window);
1517 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_out_event",
1518 GTK_SIGNAL_FUNC(focus_out), filer_window);
1519 gtk_signal_connect(GTK_OBJECT(filer_window->window), "destroy",
1520 filer_window_destroyed, filer_window);
1522 gtk_signal_connect(GTK_OBJECT(filer_window->collection), "open_item",
1523 open_item, filer_window);
1524 gtk_signal_connect(GTK_OBJECT(collection), "show_menu",
1525 show_menu, filer_window);
1526 gtk_signal_connect(GTK_OBJECT(collection), "gain_selection",
1527 gain_selection, filer_window);
1528 gtk_signal_connect(GTK_OBJECT(collection), "lose_selection",
1529 lose_selection, filer_window);
1530 gtk_signal_connect(GTK_OBJECT(collection), "drag_selection",
1531 drag_selection, filer_window);
1532 gtk_signal_connect(GTK_OBJECT(collection), "drag_data_get",
1533 drag_data_get, filer_window);
1534 gtk_signal_connect(GTK_OBJECT(collection), "selection_clear_event",
1535 GTK_SIGNAL_FUNC(collection_lose_selection), NULL);
1536 gtk_signal_connect (GTK_OBJECT(collection), "selection_get",
1537 GTK_SIGNAL_FUNC(selection_get), NULL);
1538 gtk_selection_add_targets(collection, GDK_SELECTION_PRIMARY,
1539 target_table,
1540 sizeof(target_table) / sizeof(*target_table));
1542 filer_style_set(filer_window, last_display_style);
1543 drag_set_dest(filer_window);
1545 if (panel_type)
1547 int swidth, sheight, iwidth, iheight;
1548 GtkWidget *frame, *win = filer_window->window;
1550 gtk_window_set_wmclass(GTK_WINDOW(win), "ROX-Panel",
1551 "ROX-Filer");
1552 collection_set_panel(filer_window->collection, TRUE);
1553 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1554 "delete_event",
1555 GTK_SIGNAL_FUNC(filer_confirm_close),
1556 filer_window);
1558 gdk_window_get_size(GDK_ROOT_PARENT(), &swidth, &sheight);
1559 iwidth = filer_window->collection->item_width;
1560 iheight = filer_window->collection->item_height;
1563 int height = iheight + PANEL_BORDER;
1564 int y = panel_type == PANEL_TOP
1565 ? -PANEL_BORDER
1566 : sheight - height - PANEL_BORDER;
1568 gtk_widget_set_usize(collection, swidth, height);
1569 gtk_widget_set_uposition(win, 0, y);
1572 frame = gtk_frame_new(NULL);
1573 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1574 gtk_container_add(GTK_CONTAINER(frame), collection);
1575 gtk_container_add(GTK_CONTAINER(win), frame);
1577 gtk_widget_show_all(frame);
1578 gtk_widget_realize(win);
1579 if (override_redirect)
1580 gdk_window_set_override_redirect(win->window, TRUE);
1581 make_panel_window(win->window);
1583 else
1585 GtkWidget *vbox;
1586 int col_height = ROW_HEIGHT_LARGE * 3;
1588 gtk_signal_connect(GTK_OBJECT(collection),
1589 "key_press_event",
1590 GTK_SIGNAL_FUNC(key_press_event), filer_window);
1591 gtk_window_set_default_size(GTK_WINDOW(filer_window->window),
1592 filer_window->display_style == LARGE_ICONS ? 400 : 512,
1593 o_toolbar == TOOLBAR_NONE ? col_height:
1594 o_toolbar == TOOLBAR_NORMAL ? col_height + 24 :
1595 col_height + 38);
1597 hbox = gtk_hbox_new(FALSE, 0);
1598 gtk_container_add(GTK_CONTAINER(filer_window->window),
1599 hbox);
1601 vbox = gtk_vbox_new(FALSE, 0);
1602 gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
1604 if (o_toolbar != TOOLBAR_NONE)
1606 GtkWidget *toolbar;
1608 toolbar = create_toolbar(filer_window);
1609 gtk_box_pack_start(GTK_BOX(vbox), toolbar,
1610 FALSE, TRUE, 0);
1611 gtk_widget_show_all(toolbar);
1614 gtk_box_pack_start(GTK_BOX(vbox), collection, TRUE, TRUE, 0);
1616 filer_window->minibuffer = create_minibuffer(filer_window);
1617 gtk_box_pack_start(GTK_BOX(vbox), filer_window->minibuffer,
1618 FALSE, TRUE, 0);
1620 scrollbar = gtk_vscrollbar_new(COLLECTION(collection)->vadj);
1621 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, TRUE, 0);
1622 gtk_accel_group_attach(filer_keys,
1623 GTK_OBJECT(filer_window->window));
1624 gtk_window_set_focus(GTK_WINDOW(filer_window->window),
1625 collection);
1627 gtk_widget_show(hbox);
1628 gtk_widget_show(vbox);
1629 gtk_widget_show(scrollbar);
1630 gtk_widget_show(collection);
1633 number_of_windows++;
1634 gtk_widget_show(filer_window->window);
1635 attach(filer_window);
1637 all_filer_windows = g_list_prepend(all_filer_windows, filer_window);
1639 return filer_window;
1642 static gint clear_scanning_display(FilerWindow *filer_window)
1644 if (exists(filer_window))
1645 filer_set_title(filer_window);
1646 return FALSE;
1649 static void set_scanning_display(FilerWindow *filer_window, gboolean scanning)
1651 if (scanning == filer_window->scanning)
1652 return;
1653 filer_window->scanning = scanning;
1655 if (scanning)
1656 filer_set_title(filer_window);
1657 else
1658 gtk_timeout_add(300, (GtkFunction) clear_scanning_display,
1659 filer_window);
1662 static GtkWidget *create_toolbar(FilerWindow *filer_window)
1664 GtkWidget *frame, *box;
1666 if (o_toolbar == TOOLBAR_GNOME)
1668 frame = gtk_handle_box_new();
1669 box = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL,
1670 GTK_TOOLBAR_BOTH);
1671 gtk_container_set_border_width(GTK_CONTAINER(box), 2);
1672 gtk_toolbar_set_space_style(GTK_TOOLBAR(box),
1673 GTK_TOOLBAR_SPACE_LINE);
1674 gtk_toolbar_set_button_relief(GTK_TOOLBAR(box),
1675 GTK_RELIEF_NONE);
1677 else
1679 frame = gtk_frame_new(NULL);
1680 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1682 box = gtk_hbutton_box_new();
1683 gtk_button_box_set_child_size_default(16, 16);
1684 gtk_hbutton_box_set_spacing_default(2);
1685 gtk_button_box_set_layout(GTK_BUTTON_BOX(box),
1686 GTK_BUTTONBOX_START);
1689 gtk_container_add(GTK_CONTAINER(frame), box);
1691 add_button(box, TOOLBAR_UP_ICON,
1692 GTK_SIGNAL_FUNC(toolbar_up_clicked),
1693 filer_window,
1694 "Up", "Change to parent directory");
1695 add_button(box, TOOLBAR_HOME_ICON,
1696 GTK_SIGNAL_FUNC(toolbar_home_clicked),
1697 filer_window,
1698 "Home", "Change to home directory");
1699 add_button(box, TOOLBAR_REFRESH_ICON,
1700 GTK_SIGNAL_FUNC(toolbar_refresh_clicked),
1701 filer_window,
1702 "Rescan", "Rescan directory contents");
1704 return frame;
1707 static void add_button(GtkWidget *box, int pixmap,
1708 GtkSignalFunc cb, FilerWindow *filer_window,
1709 char *label, char *tip)
1711 GtkWidget *button, *icon;
1713 icon = gtk_pixmap_new(default_pixmap[pixmap]->pixmap,
1714 default_pixmap[pixmap]->mask);
1716 if (o_toolbar == TOOLBAR_GNOME)
1718 gtk_toolbar_append_element(GTK_TOOLBAR(box),
1719 GTK_TOOLBAR_CHILD_BUTTON,
1720 NULL,
1721 label,
1722 tip, NULL,
1723 icon,
1724 cb, filer_window);
1726 else
1728 button = gtk_button_new();
1729 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
1731 gtk_container_add(GTK_CONTAINER(button), icon);
1732 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1733 cb, filer_window);
1735 gtk_tooltips_set_tip(tooltips, button, tip, NULL);
1737 gtk_container_add(GTK_CONTAINER(box), button);
1741 /* Build up some option widgets to go in the options dialog, but don't
1742 * fill them in yet.
1744 static GtkWidget *create_options()
1746 GtkWidget *vbox, *menu, *hbox;
1748 vbox = gtk_vbox_new(FALSE, 0);
1749 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
1751 display_label = gtk_label_new("<>");
1752 gtk_label_set_line_wrap(GTK_LABEL(display_label), TRUE);
1753 gtk_box_pack_start(GTK_BOX(vbox), display_label, FALSE, TRUE, 0);
1755 toggle_new_window_on_1 =
1756 gtk_check_button_new_with_label("New window on button 1 "
1757 "(RISC OS style)");
1758 gtk_box_pack_start(GTK_BOX(vbox), toggle_new_window_on_1,
1759 FALSE, TRUE, 0);
1761 toggle_menu_on_2 =
1762 gtk_check_button_new_with_label("Menu on button 2 "
1763 "(RISC OS style)");
1764 gtk_box_pack_start(GTK_BOX(vbox), toggle_menu_on_2, FALSE, TRUE, 0);
1766 toggle_single_click =
1767 gtk_check_button_new_with_label("Single-click nagivation");
1768 gtk_box_pack_start(GTK_BOX(vbox), toggle_single_click, FALSE, TRUE, 0);
1770 toggle_unique_filer_windows =
1771 gtk_check_button_new_with_label("Unique windows");
1772 gtk_box_pack_start(GTK_BOX(vbox), toggle_unique_filer_windows, FALSE, TRUE, 0);
1774 hbox = gtk_hbox_new(FALSE, 4);
1775 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
1777 gtk_box_pack_start(GTK_BOX(hbox),
1778 gtk_label_new("Toolbar type for new windows"),
1779 FALSE, TRUE, 0);
1780 menu_toolbar = gtk_option_menu_new();
1781 menu = gtk_menu_new();
1782 gtk_menu_append(GTK_MENU(menu), gtk_menu_item_new_with_label("None"));
1783 gtk_menu_append(GTK_MENU(menu), gtk_menu_item_new_with_label("Normal"));
1784 gtk_menu_append(GTK_MENU(menu), gtk_menu_item_new_with_label("GNOME"));
1785 gtk_option_menu_set_menu(GTK_OPTION_MENU(menu_toolbar), menu);
1786 gtk_box_pack_start(GTK_BOX(hbox), menu_toolbar, TRUE, TRUE, 0);
1788 return vbox;
1791 static void update_options_label(void)
1793 guchar *str;
1795 str = g_strdup_printf("The last used display style (%s) and sort "
1796 "function (Sort By %s) will be saved if you click on "
1797 "Save.", style_to_name(), sort_fn_to_name());
1798 gtk_label_set_text(GTK_LABEL(display_label), str);
1799 g_free(str);
1802 /* Reflect current state by changing the widgets in the options box */
1803 static void update_options()
1805 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_new_window_on_1),
1806 o_new_window_on_1);
1807 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_menu_on_2),
1808 collection_menu_button == 2 ? 1 : 0);
1809 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_single_click),
1810 o_single_click);
1811 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_unique_filer_windows),
1812 o_unique_filer_windows);
1813 gtk_option_menu_set_history(GTK_OPTION_MENU(menu_toolbar), o_toolbar);
1815 update_options_label();
1818 /* Set current values by reading the states of the widgets in the options box */
1819 static void set_options()
1821 GtkWidget *item, *menu;
1822 GList *list;
1824 o_new_window_on_1 = gtk_toggle_button_get_active(
1825 GTK_TOGGLE_BUTTON(toggle_new_window_on_1));
1827 collection_menu_button = gtk_toggle_button_get_active(
1828 GTK_TOGGLE_BUTTON(toggle_menu_on_2)) ? 2 : 3;
1830 o_single_click = gtk_toggle_button_get_active(
1831 GTK_TOGGLE_BUTTON(toggle_single_click));
1833 o_unique_filer_windows = gtk_toggle_button_get_active(
1834 GTK_TOGGLE_BUTTON(toggle_unique_filer_windows));
1836 collection_single_click = o_single_click ? TRUE : FALSE;
1838 menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(menu_toolbar));
1839 item = gtk_menu_get_active(GTK_MENU(menu));
1840 list = gtk_container_children(GTK_CONTAINER(menu));
1841 o_toolbar = (ToolbarType) g_list_index(list, item);
1842 g_list_free(list);
1846 static guchar *style_to_name(void)
1848 return last_display_style == LARGE_ICONS ? "Large Icons" :
1849 last_display_style == SMALL_ICONS ? "Small Icons" :
1850 "Full Info";
1853 static guchar *sort_fn_to_name(void)
1855 return last_sort_fn == sort_by_name ? "Name" :
1856 last_sort_fn == sort_by_type ? "Type" :
1857 last_sort_fn == sort_by_date ? "Date" :
1858 "Size";
1861 static void save_options()
1863 option_write("filer_new_window_on_1", o_new_window_on_1 ? "1" : "0");
1864 option_write("filer_menu_on_2",
1865 collection_menu_button == 2 ? "1" : "0");
1866 option_write("filer_single_click", o_single_click ? "1" : "0");
1867 option_write("filer_unique_windows", o_unique_filer_windows ? "1" : "0");
1868 option_write("filer_display_style", style_to_name());
1869 option_write("filer_sort_by", sort_fn_to_name());
1870 option_write("filer_toolbar", o_toolbar == TOOLBAR_NONE ? "None" :
1871 o_toolbar == TOOLBAR_NORMAL ? "Normal" :
1872 o_toolbar == TOOLBAR_GNOME ? "GNOME" :
1873 "Unknown");
1876 static char *filer_new_window_on_1(char *data)
1878 o_new_window_on_1 = atoi(data) != 0;
1879 return NULL;
1882 static char *filer_menu_on_2(char *data)
1884 collection_menu_button = atoi(data) != 0 ? 2 : 3;
1885 return NULL;
1888 static char *filer_single_click(char *data)
1890 o_single_click = atoi(data) != 0;
1891 collection_single_click = o_single_click ? TRUE : FALSE;
1892 return NULL;
1895 static char *filer_unique_windows(char *data)
1897 o_unique_filer_windows = atoi(data) != 0;
1898 return NULL;
1901 static char *filer_display_style(char *data)
1903 if (g_strcasecmp(data, "Large Icons") == 0)
1904 last_display_style = LARGE_ICONS;
1905 else if (g_strcasecmp(data, "Small Icons") == 0)
1906 last_display_style = SMALL_ICONS;
1907 else if (g_strcasecmp(data, "Full Info") == 0)
1908 last_display_style = FULL_INFO;
1909 else
1910 return "Unknown display style";
1912 return NULL;
1915 static char *filer_sort_by(char *data)
1917 if (g_strcasecmp(data, "Name") == 0)
1918 last_sort_fn = sort_by_name;
1919 else if (g_strcasecmp(data, "Type") == 0)
1920 last_sort_fn = sort_by_type;
1921 else if (g_strcasecmp(data, "Date") == 0)
1922 last_sort_fn = sort_by_date;
1923 else if (g_strcasecmp(data, "Size") == 0)
1924 last_sort_fn = sort_by_size;
1925 else
1926 return "Unknown sort type";
1928 return NULL;
1931 static char *filer_toolbar(char *data)
1933 if (g_strcasecmp(data, "None") == 0)
1934 o_toolbar = TOOLBAR_NONE;
1935 else if (g_strcasecmp(data, "Normal") == 0)
1936 o_toolbar = TOOLBAR_NORMAL;
1937 else if (g_strcasecmp(data, "GNOME") == 0)
1938 o_toolbar = TOOLBAR_GNOME;
1939 else
1940 return "Unknown toolbar type";
1942 return NULL;
1945 /* Note that filer_window may not exist after this call. */
1946 void update_dir(FilerWindow *filer_window, gboolean warning)
1948 if (may_rescan(filer_window, warning))
1949 dir_update(filer_window->directory, filer_window->path);
1952 void filer_set_hidden(FilerWindow *filer_window, gboolean hidden)
1954 Directory *dir = filer_window->directory;
1956 if (filer_window->show_hidden == hidden)
1957 return;
1959 filer_window->show_hidden = hidden;
1960 last_show_hidden = hidden;
1962 g_fscache_data_ref(dir_cache, dir);
1963 detach(filer_window);
1964 filer_window->directory = dir;
1965 attach(filer_window);
1968 /* Refresh the various caches even if we don't think we need to */
1969 void full_refresh(void)
1971 mount_update(TRUE);
1974 /* See whether a filer window with a given path already exists
1975 * and is different from diff.
1977 static FilerWindow *find_filer_window(char *path, FilerWindow *diff)
1979 GList *next = all_filer_windows;
1981 while (next)
1983 FilerWindow *filer_window = (FilerWindow *) next->data;
1985 if (filer_window->panel_type == PANEL_NO &&
1986 filer_window != diff &&
1987 strcmp(path, filer_window->path) == 0)
1989 return filer_window;
1992 next = next->next;
1995 return NULL;
1998 /* This path has been mounted/umounted/deleted some files - update all dirs */
1999 void filer_check_mounted(char *path)
2001 GList *next = all_filer_windows;
2002 int len;
2004 len = strlen(path);
2006 while (next)
2008 FilerWindow *filer_window = (FilerWindow *) next->data;
2010 next = next->next;
2012 if (strncmp(path, filer_window->path, len) == 0)
2014 char s = filer_window->path[len];
2016 if (s == '/' || s == '\0')
2017 update_dir(filer_window, FALSE);
2022 /* Like minibuffer_show(), except that:
2023 * - It returns FALSE (to be used from an idle callback)
2024 * - It checks that the filer window still exists.
2026 static gboolean minibuffer_show_cb(FilerWindow *filer_window)
2028 if (exists(filer_window))
2029 minibuffer_show(filer_window, MINI_PATH);
2030 return FALSE;
2033 static gboolean exists(FilerWindow *filer_window)
2035 GList *next;
2037 for (next = all_filer_windows; next; next = next->next)
2039 FilerWindow *fw = (FilerWindow *) next->data;
2041 if (fw == filer_window)
2042 return TRUE;
2045 return FALSE;
2048 /* Highlight (wink or cursor) this item in the filer window. If the item
2049 * isn't already there but we're scanning then highlight it if it
2050 * appears later.
2052 void filer_set_autoselect(FilerWindow *filer_window, guchar *leaf)
2054 Collection *col = filer_window->collection;
2055 int i;
2057 g_free(filer_window->auto_select);
2058 filer_window->auto_select = NULL;
2060 for (i = 0; i < col->number_of_items; i++)
2062 DirItem *item = (DirItem *) col->items[i].data;
2064 if (strcmp(item->leafname, leaf) == 0)
2066 if (col->cursor_item != -1)
2067 collection_set_cursor_item(col, i);
2068 else
2069 collection_wink_item(col, i);
2070 return;
2074 filer_window->auto_select = g_strdup(leaf);
2077 static void filer_set_title(FilerWindow *filer_window)
2079 if (filer_window->scanning)
2081 guchar *title;
2083 title = g_strdup_printf("%s (Scanning)", filer_window->path);
2084 gtk_window_set_title(GTK_WINDOW(filer_window->window),
2085 title);
2086 g_free(title);
2088 else
2089 gtk_window_set_title(GTK_WINDOW(filer_window->window),
2090 filer_window->path);