r246: Added support for i18n. No translations yet, though!
[rox-filer.git] / ROX-Filer / src / filer.c
bloba3134ea016b28ec7fc4357b3a9a44815fce8ca21
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 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 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 N_("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);
194 static GdkAtom xa_string;
195 enum
197 TARGET_STRING,
198 TARGET_URI_LIST,
201 static GdkCursor *busy_cursor = NULL;
202 static GtkTooltips *tooltips = NULL;
204 void filer_init()
206 xa_string = gdk_atom_intern("STRING", FALSE);
208 options_sections = g_slist_prepend(options_sections, &options);
209 option_register("filer_new_window_on_1", filer_new_window_on_1);
210 option_register("filer_menu_on_2", filer_menu_on_2);
211 option_register("filer_single_click", filer_single_click);
212 option_register("filer_unique_windows", filer_unique_windows);
213 option_register("filer_toolbar", filer_toolbar);
214 option_register("filer_display_style", filer_display_style);
215 option_register("filer_sort_by", filer_sort_by);
217 busy_cursor = gdk_cursor_new(GDK_WATCH);
219 tooltips = gtk_tooltips_new();
222 static gboolean if_deleted(gpointer item, gpointer removed)
224 int i = ((GPtrArray *) removed)->len;
225 DirItem **r = (DirItem **) ((GPtrArray *) removed)->pdata;
226 char *leafname = ((DirItem *) item)->leafname;
228 while (i--)
230 if (strcmp(leafname, r[i]->leafname) == 0)
231 return TRUE;
234 return FALSE;
237 static void update_item(FilerWindow *filer_window, DirItem *item)
239 int i;
240 char *leafname = item->leafname;
242 if (leafname[0] == '.')
244 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
245 || (leafname[1] == '.' && leafname[2] == '\0'))
246 return;
249 i = collection_find_item(filer_window->collection, item, dir_item_cmp);
251 if (i >= 0)
252 collection_draw_item(filer_window->collection, i, TRUE);
253 else
254 g_warning("Failed to find '%s'\n", item->leafname);
257 static void update_display(Directory *dir,
258 DirAction action,
259 GPtrArray *items,
260 FilerWindow *filer_window)
262 int old_num;
263 int i;
264 int cursor = filer_window->collection->cursor_item;
265 char *as;
266 Collection *collection = filer_window->collection;
268 switch (action)
270 case DIR_ADD:
271 as = filer_window->auto_select;
273 old_num = collection->number_of_items;
274 for (i = 0; i < items->len; i++)
276 DirItem *item = (DirItem *) items->pdata[i];
278 add_item(filer_window, item);
280 if (cursor != -1 || !as)
281 continue;
283 if (strcmp(as, item->leafname) != 0)
284 continue;
286 cursor = collection->number_of_items - 1;
287 if (filer_window->had_cursor)
289 collection_set_cursor_item(collection,
290 cursor);
291 filer_window->mini_cursor_base = cursor;
293 else
294 collection_wink_item(collection,
295 cursor);
298 if (old_num != collection->number_of_items)
299 collection_qsort(filer_window->collection,
300 filer_window->sort_fn);
301 break;
302 case DIR_REMOVE:
303 collection_delete_if(filer_window->collection,
304 if_deleted,
305 items);
306 break;
307 case DIR_START_SCAN:
308 set_scanning_display(filer_window, TRUE);
309 break;
310 case DIR_END_SCAN:
311 if (filer_window->window->window)
312 gdk_window_set_cursor(
313 filer_window->window->window,
314 NULL);
315 shrink_width(filer_window);
316 if (filer_window->had_cursor &&
317 collection->cursor_item == -1)
319 collection_set_cursor_item(collection, 0);
320 filer_window->had_cursor = FALSE;
322 set_scanning_display(filer_window, FALSE);
323 break;
324 case DIR_UPDATE:
325 for (i = 0; i < items->len; i++)
327 DirItem *item = (DirItem *) items->pdata[i];
329 update_item(filer_window, item);
331 collection_qsort(filer_window->collection,
332 filer_window->sort_fn);
333 break;
337 static void attach(FilerWindow *filer_window)
339 gdk_window_set_cursor(filer_window->window->window, busy_cursor);
340 collection_clear(filer_window->collection);
341 filer_window->scanning = TRUE;
342 dir_attach(filer_window->directory, (DirCallback) update_display,
343 filer_window);
344 filer_set_title(filer_window);
347 static void detach(FilerWindow *filer_window)
349 g_return_if_fail(filer_window->directory != NULL);
351 dir_detach(filer_window->directory,
352 (DirCallback) update_display, filer_window);
353 g_fscache_data_unref(dir_cache, filer_window->directory);
354 filer_window->directory = NULL;
357 static void filer_window_destroyed(GtkWidget *widget,
358 FilerWindow *filer_window)
360 all_filer_windows = g_list_remove(all_filer_windows, filer_window);
362 if (window_with_selection == filer_window)
363 window_with_selection = NULL;
364 if (window_with_focus == filer_window)
365 window_with_focus = NULL;
367 if (filer_window->directory)
368 detach(filer_window);
370 g_free(filer_window->auto_select);
371 g_free(filer_window->path);
372 g_free(filer_window);
374 if (--number_of_windows < 1)
375 gtk_main_quit();
378 static int calc_width(FilerWindow *filer_window, DirItem *item)
380 int pix_width = item->image->width;
382 switch (filer_window->display_style)
384 case FULL_INFO:
385 return MAX_ICON_WIDTH + 12 +
386 MAX(item->details_width, item->name_width);
387 case SMALL_ICONS:
388 return SMALL_ICON_WIDTH + 12 + item->name_width;
389 default:
390 return MAX(pix_width, item->name_width) + 4;
394 /* Add a single object to a directory display */
395 static void add_item(FilerWindow *filer_window, DirItem *item)
397 char *leafname = item->leafname;
398 int item_width;
400 if (leafname[0] == '.')
402 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
403 || (leafname[1] == '.' && leafname[2] == '\0'))
404 return;
407 item_width = calc_width(filer_window, item);
408 if (item_width > filer_window->collection->item_width)
409 collection_set_item_size(filer_window->collection,
410 item_width,
411 filer_window->collection->item_height);
412 collection_insert(filer_window->collection, item);
415 /* Is a point inside an item? */
416 static gboolean test_point_large(Collection *collection,
417 int point_x, int point_y,
418 CollectionItem *colitem,
419 int width, int height)
421 DirItem *item = (DirItem *) colitem->data;
422 int text_height = item_font->ascent + item_font->descent;
423 MaskedPixmap *image = item->image;
424 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
425 int image_width = (image->width >> 1) + 2;
426 int text_width = (item->name_width >> 1) + 2;
427 int x_limit;
429 if (point_y < image_y)
430 return FALSE; /* Too high up (don't worry about too low) */
432 if (point_y <= image_y + image->height + 2)
433 x_limit = image_width;
434 else if (point_y > height - text_height - 2)
435 x_limit = text_width;
436 else
437 x_limit = MIN(image_width, text_width);
439 return ABS(point_x - (width >> 1)) < x_limit;
442 static gboolean test_point_full_info(Collection *collection,
443 int point_x, int point_y,
444 CollectionItem *colitem,
445 int width, int height)
447 DirItem *item = (DirItem *) colitem->data;
448 MaskedPixmap *image = item->image;
449 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
450 int low_top = height
451 - fixed_font->descent - 2 - fixed_font->ascent;
453 if (point_x < image->width + 2)
454 return point_x > 2 && point_y > image_y;
456 point_x -= MAX_ICON_WIDTH + 8;
458 if (point_y >= low_top)
459 return point_x < item->details_width;
460 if (point_y >= low_top - item_font->ascent - item_font->descent)
461 return point_x < item->name_width;
462 return FALSE;
465 static gboolean test_point_small(Collection *collection,
466 int point_x, int point_y,
467 CollectionItem *colitem,
468 int width, int height)
470 DirItem *item = (DirItem *) colitem->data;
471 MaskedPixmap *image = item->image;
472 int image_y = MAX(0, SMALL_ICON_HEIGHT - image->height);
473 int low_top = height
474 - fixed_font->descent - 2 - item_font->ascent;
475 int iwidth = MIN(SMALL_ICON_WIDTH, image->width);
477 if (point_x < iwidth + 2)
478 return point_x > 2 && point_y > image_y;
480 point_x -= SMALL_ICON_WIDTH + 4;
482 if (point_y >= low_top)
483 return point_x < item->name_width;
484 return FALSE;
487 static void draw_small_icon(GtkWidget *widget,
488 GdkRectangle *area,
489 DirItem *item,
490 gboolean selected)
492 GdkGC *gc = selected ? widget->style->white_gc
493 : widget->style->black_gc;
494 MaskedPixmap *image = item->image;
495 int width, height, image_x, image_y;
497 if (!image)
498 return;
500 if (!image->sm_pixmap)
501 pixmap_make_small(image);
503 width = MIN(image->sm_width, SMALL_ICON_WIDTH);
504 height = MIN(image->sm_height, SMALL_ICON_HEIGHT);
505 image_x = area->x + ((area->width - width) >> 1);
507 gdk_gc_set_clip_mask(gc, item->image->sm_mask);
509 image_y = MAX(0, SMALL_ICON_HEIGHT - image->sm_height);
510 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
511 gdk_draw_pixmap(widget->window, gc,
512 item->image->sm_pixmap,
513 0, 0, /* Source x,y */
514 image_x, area->y + image_y, /* Dest x,y */
515 width, height);
517 if (selected)
519 gdk_gc_set_function(gc, GDK_INVERT);
520 gdk_draw_rectangle(widget->window,
522 TRUE, image_x, area->y + image_y,
523 width, height);
524 gdk_gc_set_function(gc, GDK_COPY);
527 if (item->flags & ITEM_FLAG_SYMLINK)
529 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
530 gdk_gc_set_clip_mask(gc,
531 default_pixmap[TYPE_SYMLINK]->mask);
532 gdk_draw_pixmap(widget->window, gc,
533 default_pixmap[TYPE_SYMLINK]->pixmap,
534 0, 0, /* Source x,y */
535 image_x, area->y + 8, /* Dest x,y */
536 -1, -1);
538 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
540 int type = item->flags & ITEM_FLAG_MOUNTED
541 ? TYPE_MOUNTED
542 : TYPE_UNMOUNTED;
543 MaskedPixmap *mp = default_pixmap[type];
545 if (!mp->sm_pixmap)
546 pixmap_make_small(mp);
547 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
548 gdk_gc_set_clip_mask(gc, mp->sm_mask);
549 gdk_draw_pixmap(widget->window, gc,
550 mp->sm_pixmap,
551 0, 0, /* Source x,y */
552 image_x, area->y + 8, /* Dest x,y */
553 -1, -1);
556 gdk_gc_set_clip_mask(gc, NULL);
557 gdk_gc_set_clip_origin(gc, 0, 0);
560 static void draw_large_icon(GtkWidget *widget,
561 GdkRectangle *area,
562 DirItem *item,
563 gboolean selected)
565 MaskedPixmap *image = item->image;
566 int width = MIN(image->width, MAX_ICON_WIDTH);
567 int height = MIN(image->height, MAX_ICON_WIDTH);
568 int image_x = area->x + ((area->width - width) >> 1);
569 int image_y;
570 GdkGC *gc = selected ? widget->style->white_gc
571 : widget->style->black_gc;
573 gdk_gc_set_clip_mask(gc, item->image->mask);
575 image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
576 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
577 gdk_draw_pixmap(widget->window, gc,
578 item->image->pixmap,
579 0, 0, /* Source x,y */
580 image_x, area->y + image_y, /* Dest x,y */
581 width, height);
583 if (selected)
585 gdk_gc_set_function(gc, GDK_INVERT);
586 gdk_draw_rectangle(widget->window,
588 TRUE, image_x, area->y + image_y,
589 width, height);
590 gdk_gc_set_function(gc, GDK_COPY);
593 if (item->flags & ITEM_FLAG_SYMLINK)
595 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
596 gdk_gc_set_clip_mask(gc,
597 default_pixmap[TYPE_SYMLINK]->mask);
598 gdk_draw_pixmap(widget->window, gc,
599 default_pixmap[TYPE_SYMLINK]->pixmap,
600 0, 0, /* Source x,y */
601 image_x, area->y + 8, /* Dest x,y */
602 -1, -1);
604 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
606 int type = item->flags & ITEM_FLAG_MOUNTED
607 ? TYPE_MOUNTED
608 : TYPE_UNMOUNTED;
609 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
610 gdk_gc_set_clip_mask(gc,
611 default_pixmap[type]->mask);
612 gdk_draw_pixmap(widget->window, gc,
613 default_pixmap[type]->pixmap,
614 0, 0, /* Source x,y */
615 image_x, area->y + 8, /* Dest x,y */
616 -1, -1);
619 gdk_gc_set_clip_mask(gc, NULL);
620 gdk_gc_set_clip_origin(gc, 0, 0);
623 static void draw_string(GtkWidget *widget,
624 GdkFont *font,
625 char *string,
626 int x,
627 int y,
628 int width,
629 gboolean selected)
631 int text_height = font->ascent + font->descent;
633 if (selected)
634 gtk_paint_flat_box(widget->style, widget->window,
635 GTK_STATE_SELECTED, GTK_SHADOW_NONE,
636 NULL, widget, "text",
637 x, y - font->ascent,
638 width,
639 text_height);
641 gdk_draw_text(widget->window,
642 font,
643 selected ? widget->style->white_gc
644 : widget->style->black_gc,
645 x, y,
646 string, strlen(string));
649 /* Return a string (valid until next call) giving details
650 * of this item.
652 char *details(DirItem *item)
654 mode_t m = item->mode;
655 static guchar *buf = NULL;
657 if (buf)
658 g_free(buf);
660 if (item->lstat_errno)
661 buf = g_strdup_printf(_("lstat(2) failed: %s"),
662 g_strerror(item->lstat_errno));
663 else
664 buf = g_strdup_printf("%s %s %-8.8s %-8.8s %s %s",
665 item->flags & ITEM_FLAG_APPDIR? "App " :
666 S_ISDIR(m) ? "Dir " :
667 S_ISCHR(m) ? "Char" :
668 S_ISBLK(m) ? "Blck" :
669 S_ISLNK(m) ? "Link" :
670 S_ISSOCK(m) ? "Sock" :
671 S_ISFIFO(m) ? "Pipe" : "File",
672 pretty_permissions(m),
673 user_name(item->uid),
674 group_name(item->gid),
675 format_size_aligned(item->size),
676 pretty_time(&item->mtime));
677 return buf;
680 static void draw_item_full_info(GtkWidget *widget,
681 CollectionItem *colitem,
682 GdkRectangle *area)
684 DirItem *item = (DirItem *) colitem->data;
685 MaskedPixmap *image = item->image;
686 int text_x = area->x + MAX_ICON_WIDTH + 8;
687 int low_text_y = area->y + area->height - fixed_font->descent - 2;
688 gboolean selected = colitem->selected;
689 GdkRectangle pic_area;
691 pic_area.x = area->x;
692 pic_area.y = area->y;
693 pic_area.width = image->width + 8;
694 pic_area.height = area->height;
696 draw_large_icon(widget, &pic_area, item, selected);
698 draw_string(widget,
699 item_font,
700 item->leafname,
701 text_x,
702 low_text_y - item_font->descent - fixed_font->ascent,
703 item->name_width,
704 selected);
705 draw_string(widget,
706 fixed_font,
707 details(item),
708 text_x, low_text_y,
709 item->details_width,
710 selected);
712 if (item->lstat_errno)
713 return;
715 /* Underline the effective permissions */
716 gdk_draw_rectangle(widget->window,
717 selected ? widget->style->white_gc
718 : widget->style->black_gc,
719 TRUE,
720 text_x - 1 + fixed_width *
721 (5 + 4 * applicable(item->uid, item->gid)),
722 low_text_y + fixed_font->descent - 1,
723 fixed_width * 3 + 1, 1);
726 static void draw_item_small(GtkWidget *widget,
727 CollectionItem *colitem,
728 GdkRectangle *area)
730 DirItem *item = (DirItem *) colitem->data;
731 int text_x = area->x + SMALL_ICON_WIDTH + 4;
732 int low_text_y = area->y + area->height - item_font->descent - 2;
733 gboolean selected = colitem->selected;
734 GdkRectangle pic_area;
736 pic_area.x = area->x;
737 pic_area.y = area->y;
738 pic_area.width = SMALL_ICON_WIDTH;
739 pic_area.height = SMALL_ICON_HEIGHT;
741 draw_small_icon(widget, &pic_area, item, selected);
743 draw_string(widget,
744 item_font,
745 item->leafname,
746 text_x,
747 low_text_y,
748 item->name_width,
749 selected);
752 static void draw_item_large(GtkWidget *widget,
753 CollectionItem *colitem,
754 GdkRectangle *area)
756 DirItem *item = (DirItem *) colitem->data;
757 int text_x = area->x + ((area->width - item->name_width) >> 1);
758 int text_y = area->y + area->height - item_font->descent - 2;
759 gboolean selected = colitem->selected;
761 draw_large_icon(widget, area, item, selected);
763 draw_string(widget,
764 item_font,
765 item->leafname,
766 text_x, text_y, item->name_width,
767 selected);
770 static void show_menu(Collection *collection, GdkEventButton *event,
771 int item, gpointer user_data)
773 show_filer_menu((FilerWindow *) user_data, event, item);
776 /* Returns TRUE iff the directory still exists. */
777 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning)
779 Directory *dir;
781 g_return_val_if_fail(filer_window != NULL, FALSE);
783 /* We do a fresh lookup (rather than update) because the inode may
784 * have changed.
786 dir = g_fscache_lookup(dir_cache, filer_window->path);
787 if (!dir)
789 if (warning)
790 delayed_error(PROJECT, _("Directory missing/deleted"));
791 gtk_widget_destroy(filer_window->window);
792 return FALSE;
794 if (dir == filer_window->directory)
795 g_fscache_data_unref(dir_cache, dir);
796 else
798 detach(filer_window);
799 filer_window->directory = dir;
800 attach(filer_window);
803 return TRUE;
806 /* Another app has grabbed the selection */
807 static gint collection_lose_selection(GtkWidget *widget,
808 GdkEventSelection *event)
810 if (window_with_selection &&
811 window_with_selection->collection == COLLECTION(widget))
813 FilerWindow *filer_window = window_with_selection;
814 window_with_selection = NULL;
815 collection_clear_selection(filer_window->collection);
818 return TRUE;
821 /* Someone wants us to send them the selection */
822 static void selection_get(GtkWidget *widget,
823 GtkSelectionData *selection_data,
824 guint info,
825 guint time,
826 gpointer data)
828 GString *reply, *header;
829 FilerWindow *filer_window;
830 int i;
831 Collection *collection;
833 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
835 reply = g_string_new(NULL);
836 header = g_string_new(NULL);
838 switch (info)
840 case TARGET_STRING:
841 g_string_sprintf(header, " %s",
842 make_path(filer_window->path, "")->str);
843 break;
844 case TARGET_URI_LIST:
845 g_string_sprintf(header, " file://%s%s",
846 our_host_name(),
847 make_path(filer_window->path, "")->str);
848 break;
851 collection = filer_window->collection;
852 for (i = 0; i < collection->number_of_items; i++)
854 if (collection->items[i].selected)
856 DirItem *item =
857 (DirItem *) collection->items[i].data;
859 g_string_append(reply, header->str);
860 g_string_append(reply, item->leafname);
863 /* This works, but I don't think I like it... */
864 /* g_string_append_c(reply, ' '); */
866 gtk_selection_data_set(selection_data, xa_string,
867 8, reply->str + 1, reply->len - 1);
868 g_string_free(reply, TRUE);
869 g_string_free(header, TRUE);
872 /* No items are now selected. This might be because another app claimed
873 * the selection or because the user unselected all the items.
875 static void lose_selection(Collection *collection,
876 guint time,
877 gpointer user_data)
879 FilerWindow *filer_window = (FilerWindow *) user_data;
881 if (window_with_selection == filer_window)
883 window_with_selection = NULL;
884 gtk_selection_owner_set(NULL,
885 GDK_SELECTION_PRIMARY,
886 time);
890 static void gain_selection(Collection *collection,
891 guint time,
892 gpointer user_data)
894 FilerWindow *filer_window = (FilerWindow *) user_data;
896 if (gtk_selection_owner_set(GTK_WIDGET(collection),
897 GDK_SELECTION_PRIMARY,
898 time))
900 window_with_selection = filer_window;
902 else
903 collection_clear_selection(filer_window->collection);
906 int sort_by_name(const void *item1, const void *item2)
908 return strcmp((*((DirItem **)item1))->leafname,
909 (*((DirItem **)item2))->leafname);
912 int sort_by_type(const void *item1, const void *item2)
914 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
915 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
916 MIME_type *m1, *m2;
918 int diff = i1->base_type - i2->base_type;
920 if (!diff)
921 diff = (i1->flags & ITEM_FLAG_APPDIR)
922 - (i2->flags & ITEM_FLAG_APPDIR);
923 if (diff)
924 return diff > 0 ? 1 : -1;
926 m1 = i1->mime_type;
927 m2 = i2->mime_type;
929 if (m1 && m2)
931 diff = strcmp(m1->media_type, m2->media_type);
932 if (!diff)
933 diff = strcmp(m1->subtype, m2->subtype);
935 else if (m1 || m2)
936 diff = m1 ? 1 : -1;
937 else
938 diff = 0;
940 if (diff)
941 return diff > 0 ? 1 : -1;
943 return sort_by_name(item1, item2);
946 int sort_by_date(const void *item1, const void *item2)
948 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
949 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
951 return i1->mtime > i2->mtime ? -1 :
952 i1->mtime < i2->mtime ? 1 :
953 sort_by_name(item1, item2);
956 int sort_by_size(const void *item1, const void *item2)
958 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
959 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
961 return i1->size > i2->size ? -1 :
962 i1->size < i2->size ? 1 :
963 sort_by_name(item1, item2);
966 static void open_item(Collection *collection,
967 gpointer item_data, int item_number,
968 gpointer user_data)
970 FilerWindow *filer_window = (FilerWindow *) user_data;
971 GdkEvent *event;
972 GdkEventButton *bevent;
973 GdkEventKey *kevent;
974 OpenFlags flags = 0;
976 event = (GdkEvent *) gtk_get_current_event();
978 bevent = (GdkEventButton *) event;
979 kevent = (GdkEventKey *) event;
981 switch (event->type)
983 case GDK_2BUTTON_PRESS:
984 case GDK_BUTTON_PRESS:
985 case GDK_BUTTON_RELEASE:
986 if (bevent->state & GDK_SHIFT_MASK)
987 flags |= OPEN_SHIFT;
989 if (o_new_window_on_1 ^ (bevent->button == 1))
990 flags |= OPEN_SAME_WINDOW;
992 if (bevent->button != 1)
993 flags |= OPEN_CLOSE_WINDOW;
995 if (o_single_click == FALSE &&
996 (bevent->state & GDK_CONTROL_MASK) != 0)
997 flags ^= OPEN_SAME_WINDOW | OPEN_CLOSE_WINDOW;
998 break;
999 case GDK_KEY_PRESS:
1000 flags |= OPEN_SAME_WINDOW;
1001 if (kevent->state & GDK_SHIFT_MASK)
1002 flags |= OPEN_SHIFT;
1003 break;
1004 default:
1005 break;
1008 filer_openitem(filer_window, item_number, flags);
1011 /* Return the full path to the directory containing object 'path'.
1012 * Relative paths are resolved from the filerwindow's path.
1014 static void follow_symlink(FilerWindow *filer_window, char *path,
1015 gboolean same_window)
1017 char *real, *slash;
1018 char *new_dir;
1020 if (path[0] != '/')
1021 path = make_path(filer_window->path, path)->str;
1023 real = pathdup(path);
1024 slash = strrchr(real, '/');
1025 if (!slash)
1027 g_free(real);
1028 delayed_error(PROJECT,
1029 _("Broken symlink (or you don't have permission "
1030 "to follow it)."));
1031 return;
1034 *slash = '\0';
1036 if (*real)
1037 new_dir = real;
1038 else
1039 new_dir = "/";
1041 if (filer_window->panel_type || !same_window)
1043 FilerWindow *new;
1045 new = filer_opendir(new_dir, PANEL_NO);
1046 filer_set_autoselect(new, slash + 1);
1048 else
1049 filer_change_to(filer_window, new_dir, slash + 1);
1051 g_free(real);
1054 /* Open the item (or add it to the shell command minibuffer) */
1055 void filer_openitem(FilerWindow *filer_window, int item_number, OpenFlags flags)
1057 gboolean shift = (flags & OPEN_SHIFT) != 0;
1058 gboolean close_mini = flags & OPEN_FROM_MINI;
1059 gboolean same_window = (flags & OPEN_SAME_WINDOW) != 0
1060 && !filer_window->panel_type;
1061 gboolean close_window = (flags & OPEN_CLOSE_WINDOW) != 0
1062 && !filer_window->panel_type;
1063 GtkWidget *widget;
1064 char *full_path;
1065 DirItem *item = (DirItem *)
1066 filer_window->collection->items[item_number].data;
1067 gboolean wink = TRUE, destroy = FALSE;
1069 widget = filer_window->window;
1070 if (filer_window->mini_type == MINI_SHELL)
1072 minibuffer_add(filer_window, item->leafname);
1073 return;
1076 full_path = make_path(filer_window->path,
1077 item->leafname)->str;
1079 if (item->flags & ITEM_FLAG_SYMLINK && shift)
1081 char path[MAXPATHLEN + 1];
1082 int got;
1084 got = readlink(make_path(filer_window->path,
1085 item->leafname)->str,
1086 path, MAXPATHLEN);
1087 if (got < 0)
1088 delayed_error(PROJECT, g_strerror(errno));
1089 else
1091 g_return_if_fail(got <= MAXPATHLEN);
1092 path[got] = '\0';
1094 follow_symlink(filer_window, path,
1095 flags & OPEN_SAME_WINDOW);
1097 return;
1100 switch (item->base_type)
1102 case TYPE_DIRECTORY:
1103 if (item->flags & ITEM_FLAG_APPDIR && !shift)
1105 run_app(make_path(filer_window->path,
1106 item->leafname)->str);
1107 if (close_window)
1108 destroy = TRUE;
1109 break;
1112 if (item->flags & ITEM_FLAG_MOUNT_POINT && shift)
1114 action_mount(filer_window, item);
1115 if (item->flags & ITEM_FLAG_MOUNTED)
1116 break;
1119 if (same_window)
1121 wink = FALSE;
1122 filer_change_to(filer_window, full_path, NULL);
1123 close_mini = FALSE;
1125 else
1126 filer_opendir(full_path, PANEL_NO);
1127 break;
1128 case TYPE_FILE:
1129 if ((item->flags & ITEM_FLAG_EXEC_FILE) && !shift)
1131 char *argv[] = {NULL, NULL};
1133 argv[0] = full_path;
1135 if (spawn_full(argv, filer_window->path))
1137 if (close_window)
1138 destroy = TRUE;
1140 else
1141 report_error(PROJECT,
1142 _("Failed to fork() child"));
1144 else
1146 GString *message;
1147 MIME_type *type = shift ? &text_plain
1148 : item->mime_type;
1150 g_return_if_fail(type != NULL);
1152 if (type_open(full_path, type))
1154 if (close_window)
1155 destroy = TRUE;
1157 else
1159 message = g_string_new(NULL);
1160 g_string_sprintf(message,
1161 _("No open action specified for files of this type (%s/%s)"),
1162 type->media_type,
1163 type->subtype);
1164 report_error(PROJECT, message->str);
1165 g_string_free(message, TRUE);
1168 break;
1169 default:
1170 report_error("open_item",
1171 "I don't know how to open that");
1172 break;
1175 if (destroy)
1176 gtk_widget_destroy(filer_window->window);
1177 else
1179 if (wink)
1180 collection_wink_item(filer_window->collection,
1181 item_number);
1182 if (close_mini)
1183 minibuffer_hide(filer_window);
1187 static gint pointer_in(GtkWidget *widget,
1188 GdkEventCrossing *event,
1189 FilerWindow *filer_window)
1191 may_rescan(filer_window, TRUE);
1192 return FALSE;
1195 static gint focus_in(GtkWidget *widget,
1196 GdkEventFocus *event,
1197 FilerWindow *filer_window)
1199 window_with_focus = filer_window;
1201 return FALSE;
1204 static gint focus_out(GtkWidget *widget,
1205 GdkEventFocus *event,
1206 FilerWindow *filer_window)
1208 /* TODO: Shade the cursor */
1210 return FALSE;
1213 /* Handle keys that can't be bound with the menu */
1214 static gint key_press_event(GtkWidget *widget,
1215 GdkEventKey *event,
1216 FilerWindow *filer_window)
1218 switch (event->keyval)
1220 case GDK_BackSpace:
1221 change_to_parent(filer_window);
1222 break;
1223 default:
1224 return FALSE;
1227 return TRUE;
1230 static void toolbar_refresh_clicked(GtkWidget *widget,
1231 FilerWindow *filer_window)
1233 full_refresh();
1234 filer_update_dir(filer_window, TRUE);
1237 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window)
1239 filer_change_to(filer_window, home_dir, NULL);
1242 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window)
1244 change_to_parent(filer_window);
1247 void change_to_parent(FilerWindow *filer_window)
1249 char *copy;
1250 char *slash;
1252 if (filer_window->path[0] == '/' && filer_window->path[1] == '\0')
1253 return; /* Already in the root */
1255 copy = g_strdup(filer_window->path);
1256 slash = strrchr(copy, '/');
1258 if (slash)
1260 *slash = '\0';
1261 filer_change_to(filer_window,
1262 *copy ? copy : "/",
1263 slash + 1);
1265 else
1266 g_warning("No / in directory path!\n");
1268 g_free(copy);
1272 /* Make filer_window display path. When finished, highlight item 'from', or
1273 * the first item if from is NULL. If there is currently no cursor then
1274 * simply wink 'from' (if not NULL).
1276 void filer_change_to(FilerWindow *filer_window, char *path, char *from)
1278 char *from_dup;
1279 char *real_path = pathdup(path);
1281 if (o_unique_filer_windows)
1283 FilerWindow *fw;
1285 fw = find_filer_window(real_path, filer_window);
1286 if (fw)
1287 gtk_widget_destroy(fw->window);
1290 from_dup = from && *from ? g_strdup(from) : NULL;
1292 detach(filer_window);
1293 g_free(filer_window->path);
1294 filer_window->path = real_path;
1296 filer_window->directory = g_fscache_lookup(dir_cache,
1297 filer_window->path);
1298 if (filer_window->directory)
1300 g_free(filer_window->auto_select);
1301 filer_window->had_cursor =
1302 filer_window->collection->cursor_item != -1
1303 || filer_window->had_cursor;
1304 filer_window->auto_select = from_dup;
1306 filer_set_title(filer_window);
1307 collection_set_cursor_item(filer_window->collection, -1);
1308 attach(filer_window);
1310 if (filer_window->mini_type == MINI_PATH)
1311 gtk_idle_add((GtkFunction) minibuffer_show_cb,
1312 filer_window);
1314 else
1316 char *error;
1318 g_free(from_dup);
1319 error = g_strdup_printf(_("Directory '%s' is not accessible"),
1320 path);
1321 delayed_error(PROJECT, error);
1322 g_free(error);
1323 gtk_widget_destroy(filer_window->window);
1327 int selected_item_number(Collection *collection)
1329 int i;
1331 g_return_val_if_fail(collection != NULL, -1);
1332 g_return_val_if_fail(IS_COLLECTION(collection), -1);
1333 g_return_val_if_fail(collection->number_selected == 1, -1);
1335 for (i = 0; i < collection->number_of_items; i++)
1336 if (collection->items[i].selected)
1337 return i;
1339 g_warning("selected_item: number_selected is wrong\n");
1341 return -1;
1344 DirItem *selected_item(Collection *collection)
1346 int item;
1348 item = selected_item_number(collection);
1350 if (item > -1)
1351 return (DirItem *) collection->items[item].data;
1352 return NULL;
1355 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
1356 FilerWindow *window)
1358 /* TODO: We can open lots of these - very irritating! */
1359 return get_choice(_("Close panel?"),
1360 _("You have tried to close a panel via the window "
1361 "manager - I usually find that this is accidental... "
1362 "really close?"),
1363 2, _("Remove"), _("Cancel")) != 0;
1366 /* Make the items as narrow as possible */
1367 static void shrink_width(FilerWindow *filer_window)
1369 int i;
1370 Collection *col = filer_window->collection;
1371 int width = MIN_ITEM_WIDTH;
1372 int this_width;
1373 DisplayStyle style = filer_window->display_style;
1374 int text_height;
1376 text_height = item_font->ascent + item_font->descent;
1378 for (i = 0; i < col->number_of_items; i++)
1380 this_width = calc_width(filer_window,
1381 (DirItem *) col->items[i].data);
1382 if (this_width > width)
1383 width = this_width;
1386 collection_set_item_size(filer_window->collection,
1387 width,
1388 style == FULL_INFO ? MAX_ICON_HEIGHT + 4 :
1389 style == SMALL_ICONS ? MAX(text_height, SMALL_ICON_HEIGHT) + 4
1390 : text_height + MAX_ICON_HEIGHT + 8);
1393 void filer_set_sort_fn(FilerWindow *filer_window,
1394 int (*fn)(const void *a, const void *b))
1396 if (filer_window->sort_fn == fn)
1397 return;
1399 filer_window->sort_fn = fn;
1400 last_sort_fn = fn;
1402 collection_qsort(filer_window->collection,
1403 filer_window->sort_fn);
1405 update_options_label();
1408 void filer_style_set(FilerWindow *filer_window, DisplayStyle style)
1410 if (filer_window->display_style == style)
1411 return;
1413 if (filer_window->panel_type)
1414 style = LARGE_ICONS;
1415 else
1416 last_display_style = style;
1418 filer_window->display_style = style;
1420 switch (style)
1422 case SMALL_ICONS:
1423 collection_set_functions(filer_window->collection,
1424 draw_item_small, test_point_small);
1425 break;
1426 case FULL_INFO:
1427 collection_set_functions(filer_window->collection,
1428 draw_item_full_info, test_point_full_info);
1429 break;
1430 default:
1431 collection_set_functions(filer_window->collection,
1432 draw_item_large, test_point_large);
1433 break;
1436 shrink_width(filer_window);
1438 update_options_label();
1441 FilerWindow *filer_opendir(char *path, PanelType panel_type)
1443 GtkWidget *hbox, *scrollbar, *collection;
1444 FilerWindow *filer_window;
1445 GtkTargetEntry target_table[] =
1447 {"text/uri-list", 0, TARGET_URI_LIST},
1448 {"STRING", 0, TARGET_STRING},
1450 char *real_path;
1452 real_path = pathdup(path);
1454 if (o_unique_filer_windows && panel_type == PANEL_NO)
1456 FilerWindow *fw;
1458 fw = find_filer_window(real_path, NULL);
1460 if (fw)
1462 /* TODO: this should bring the window to the front
1463 * at the same coordinates.
1465 gtk_widget_hide(fw->window);
1466 gtk_widget_show(fw->window);
1467 g_free(real_path);
1468 return fw;
1472 filer_window = g_new(FilerWindow, 1);
1473 filer_window->minibuffer = NULL;
1474 filer_window->path = real_path;
1475 filer_window->scanning = FALSE;
1476 filer_window->had_cursor = FALSE;
1477 filer_window->auto_select = NULL;
1478 filer_window->mini_type = MINI_NONE;
1480 filer_window->directory = g_fscache_lookup(dir_cache,
1481 filer_window->path);
1482 if (!filer_window->directory)
1484 char *error;
1486 error = g_strdup_printf(_("Directory '%s' not found."), path);
1487 delayed_error(PROJECT, error);
1488 g_free(error);
1489 g_free(filer_window->path);
1490 g_free(filer_window);
1491 return NULL;
1494 filer_window->show_hidden = last_show_hidden;
1495 filer_window->panel_type = panel_type;
1496 filer_window->temp_item_selected = FALSE;
1497 filer_window->sort_fn = last_sort_fn;
1498 filer_window->flags = (FilerFlags) 0;
1499 filer_window->display_style = UNKNOWN_STYLE;
1501 filer_window->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1502 filer_set_title(filer_window);
1504 collection = collection_new(NULL);
1505 gtk_object_set_data(GTK_OBJECT(collection),
1506 "filer_window", filer_window);
1507 filer_window->collection = COLLECTION(collection);
1509 gtk_widget_add_events(filer_window->window, GDK_ENTER_NOTIFY);
1510 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1511 "enter-notify-event",
1512 GTK_SIGNAL_FUNC(pointer_in), filer_window);
1513 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_in_event",
1514 GTK_SIGNAL_FUNC(focus_in), filer_window);
1515 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_out_event",
1516 GTK_SIGNAL_FUNC(focus_out), filer_window);
1517 gtk_signal_connect(GTK_OBJECT(filer_window->window), "destroy",
1518 filer_window_destroyed, filer_window);
1520 gtk_signal_connect(GTK_OBJECT(filer_window->collection), "open_item",
1521 open_item, filer_window);
1522 gtk_signal_connect(GTK_OBJECT(collection), "show_menu",
1523 show_menu, filer_window);
1524 gtk_signal_connect(GTK_OBJECT(collection), "gain_selection",
1525 gain_selection, filer_window);
1526 gtk_signal_connect(GTK_OBJECT(collection), "lose_selection",
1527 lose_selection, filer_window);
1528 gtk_signal_connect(GTK_OBJECT(collection), "drag_selection",
1529 drag_selection, filer_window);
1530 gtk_signal_connect(GTK_OBJECT(collection), "drag_data_get",
1531 drag_data_get, filer_window);
1532 gtk_signal_connect(GTK_OBJECT(collection), "selection_clear_event",
1533 GTK_SIGNAL_FUNC(collection_lose_selection), NULL);
1534 gtk_signal_connect (GTK_OBJECT(collection), "selection_get",
1535 GTK_SIGNAL_FUNC(selection_get), NULL);
1536 gtk_selection_add_targets(collection, GDK_SELECTION_PRIMARY,
1537 target_table,
1538 sizeof(target_table) / sizeof(*target_table));
1540 filer_style_set(filer_window, last_display_style);
1541 drag_set_dest(filer_window);
1543 if (panel_type)
1545 int swidth, sheight, iwidth, iheight;
1546 GtkWidget *frame, *win = filer_window->window;
1548 gtk_window_set_wmclass(GTK_WINDOW(win), "ROX-Panel",
1549 PROJECT);
1550 collection_set_panel(filer_window->collection, TRUE);
1551 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1552 "delete_event",
1553 GTK_SIGNAL_FUNC(filer_confirm_close),
1554 filer_window);
1556 gdk_window_get_size(GDK_ROOT_PARENT(), &swidth, &sheight);
1557 iwidth = filer_window->collection->item_width;
1558 iheight = filer_window->collection->item_height;
1561 int height = iheight + PANEL_BORDER;
1562 int y = panel_type == PANEL_TOP
1563 ? -PANEL_BORDER
1564 : sheight - height - PANEL_BORDER;
1566 gtk_widget_set_usize(collection, swidth, height);
1567 gtk_widget_set_uposition(win, 0, y);
1570 frame = gtk_frame_new(NULL);
1571 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1572 gtk_container_add(GTK_CONTAINER(frame), collection);
1573 gtk_container_add(GTK_CONTAINER(win), frame);
1575 gtk_widget_show_all(frame);
1576 gtk_widget_realize(win);
1577 if (override_redirect)
1578 gdk_window_set_override_redirect(win->window, TRUE);
1579 make_panel_window(win->window);
1581 else
1583 GtkWidget *vbox;
1584 int col_height = ROW_HEIGHT_LARGE * 3;
1586 gtk_signal_connect(GTK_OBJECT(collection),
1587 "key_press_event",
1588 GTK_SIGNAL_FUNC(key_press_event), filer_window);
1589 gtk_window_set_default_size(GTK_WINDOW(filer_window->window),
1590 filer_window->display_style == LARGE_ICONS ? 400 : 512,
1591 o_toolbar == TOOLBAR_NONE ? col_height:
1592 o_toolbar == TOOLBAR_NORMAL ? col_height + 24 :
1593 col_height + 38);
1595 hbox = gtk_hbox_new(FALSE, 0);
1596 gtk_container_add(GTK_CONTAINER(filer_window->window),
1597 hbox);
1599 vbox = gtk_vbox_new(FALSE, 0);
1600 gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
1602 if (o_toolbar != TOOLBAR_NONE)
1604 GtkWidget *toolbar;
1606 toolbar = create_toolbar(filer_window);
1607 gtk_box_pack_start(GTK_BOX(vbox), toolbar,
1608 FALSE, TRUE, 0);
1609 gtk_widget_show_all(toolbar);
1612 gtk_box_pack_start(GTK_BOX(vbox), collection, TRUE, TRUE, 0);
1614 filer_window->minibuffer = create_minibuffer(filer_window);
1615 gtk_box_pack_start(GTK_BOX(vbox), filer_window->minibuffer,
1616 FALSE, TRUE, 0);
1618 scrollbar = gtk_vscrollbar_new(COLLECTION(collection)->vadj);
1619 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, TRUE, 0);
1620 gtk_accel_group_attach(filer_keys,
1621 GTK_OBJECT(filer_window->window));
1622 gtk_window_set_focus(GTK_WINDOW(filer_window->window),
1623 collection);
1625 gtk_widget_show(hbox);
1626 gtk_widget_show(vbox);
1627 gtk_widget_show(scrollbar);
1628 gtk_widget_show(collection);
1631 number_of_windows++;
1632 gtk_widget_show(filer_window->window);
1633 attach(filer_window);
1635 all_filer_windows = g_list_prepend(all_filer_windows, filer_window);
1637 return filer_window;
1640 static gint clear_scanning_display(FilerWindow *filer_window)
1642 if (filer_exists(filer_window))
1643 filer_set_title(filer_window);
1644 return FALSE;
1647 static void set_scanning_display(FilerWindow *filer_window, gboolean scanning)
1649 if (scanning == filer_window->scanning)
1650 return;
1651 filer_window->scanning = scanning;
1653 if (scanning)
1654 filer_set_title(filer_window);
1655 else
1656 gtk_timeout_add(300, (GtkFunction) clear_scanning_display,
1657 filer_window);
1660 static GtkWidget *create_toolbar(FilerWindow *filer_window)
1662 GtkWidget *frame, *box;
1664 if (o_toolbar == TOOLBAR_GNOME)
1666 frame = gtk_handle_box_new();
1667 box = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL,
1668 GTK_TOOLBAR_BOTH);
1669 gtk_container_set_border_width(GTK_CONTAINER(box), 2);
1670 gtk_toolbar_set_space_style(GTK_TOOLBAR(box),
1671 GTK_TOOLBAR_SPACE_LINE);
1672 gtk_toolbar_set_button_relief(GTK_TOOLBAR(box),
1673 GTK_RELIEF_NONE);
1675 else
1677 frame = gtk_frame_new(NULL);
1678 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1680 box = gtk_hbutton_box_new();
1681 gtk_button_box_set_child_size_default(16, 16);
1682 gtk_hbutton_box_set_spacing_default(2);
1683 gtk_button_box_set_layout(GTK_BUTTON_BOX(box),
1684 GTK_BUTTONBOX_START);
1687 gtk_container_add(GTK_CONTAINER(frame), box);
1689 add_button(box, TOOLBAR_UP_ICON,
1690 GTK_SIGNAL_FUNC(toolbar_up_clicked),
1691 filer_window,
1692 _("Up"), _("Change to parent directory"));
1693 add_button(box, TOOLBAR_HOME_ICON,
1694 GTK_SIGNAL_FUNC(toolbar_home_clicked),
1695 filer_window,
1696 _("Home"), _("Change to home directory"));
1697 add_button(box, TOOLBAR_REFRESH_ICON,
1698 GTK_SIGNAL_FUNC(toolbar_refresh_clicked),
1699 filer_window,
1700 _("Rescan"), _("Rescan directory contents"));
1702 return frame;
1705 static void add_button(GtkWidget *box, int pixmap,
1706 GtkSignalFunc cb, FilerWindow *filer_window,
1707 char *label, char *tip)
1709 GtkWidget *button, *icon;
1711 icon = gtk_pixmap_new(default_pixmap[pixmap]->pixmap,
1712 default_pixmap[pixmap]->mask);
1714 if (o_toolbar == TOOLBAR_GNOME)
1716 gtk_toolbar_append_element(GTK_TOOLBAR(box),
1717 GTK_TOOLBAR_CHILD_BUTTON,
1718 NULL,
1719 label,
1720 tip, NULL,
1721 icon,
1722 cb, filer_window);
1724 else
1726 button = gtk_button_new();
1727 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
1729 gtk_container_add(GTK_CONTAINER(button), icon);
1730 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1731 cb, filer_window);
1733 gtk_tooltips_set_tip(tooltips, button, tip, NULL);
1735 gtk_container_add(GTK_CONTAINER(box), button);
1739 /* Build up some option widgets to go in the options dialog, but don't
1740 * fill them in yet.
1742 static GtkWidget *create_options()
1744 GtkWidget *vbox, *menu, *hbox;
1746 vbox = gtk_vbox_new(FALSE, 0);
1747 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
1749 display_label = gtk_label_new("<>");
1750 gtk_label_set_line_wrap(GTK_LABEL(display_label), TRUE);
1751 gtk_box_pack_start(GTK_BOX(vbox), display_label, FALSE, TRUE, 0);
1753 toggle_new_window_on_1 =
1754 gtk_check_button_new_with_label(
1755 _("New window on button 1 (RISC OS style)"));
1756 gtk_box_pack_start(GTK_BOX(vbox), toggle_new_window_on_1,
1757 FALSE, TRUE, 0);
1759 toggle_menu_on_2 =
1760 gtk_check_button_new_with_label(
1761 _("Menu on button 2 (RISC OS style)"));
1762 gtk_box_pack_start(GTK_BOX(vbox), toggle_menu_on_2, FALSE, TRUE, 0);
1764 toggle_single_click =
1765 gtk_check_button_new_with_label(_("Single-click nagivation"));
1766 gtk_box_pack_start(GTK_BOX(vbox), toggle_single_click, FALSE, TRUE, 0);
1768 toggle_unique_filer_windows =
1769 gtk_check_button_new_with_label(_("Unique windows"));
1770 gtk_box_pack_start(GTK_BOX(vbox), toggle_unique_filer_windows,
1771 FALSE, TRUE, 0);
1773 hbox = gtk_hbox_new(FALSE, 4);
1774 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
1776 gtk_box_pack_start(GTK_BOX(hbox),
1777 gtk_label_new(_("Toolbar type for new windows")),
1778 FALSE, TRUE, 0);
1779 menu_toolbar = gtk_option_menu_new();
1780 menu = gtk_menu_new();
1781 gtk_menu_append(GTK_MENU(menu),
1782 gtk_menu_item_new_with_label(_("None")));
1783 gtk_menu_append(GTK_MENU(menu),
1784 gtk_menu_item_new_with_label(_("Normal")));
1785 gtk_menu_append(GTK_MENU(menu),
1786 gtk_menu_item_new_with_label(_("GNOME")));
1787 gtk_option_menu_set_menu(GTK_OPTION_MENU(menu_toolbar), menu);
1788 gtk_box_pack_start(GTK_BOX(hbox), menu_toolbar, TRUE, TRUE, 0);
1790 return vbox;
1793 static void update_options_label(void)
1795 guchar *str;
1797 str = g_strdup_printf(_("The last used display style (%s) and sort "
1798 "function (Sort By %s) will be saved if you click on "
1799 "Save."), style_to_name(), sort_fn_to_name());
1800 gtk_label_set_text(GTK_LABEL(display_label), str);
1801 g_free(str);
1804 /* Reflect current state by changing the widgets in the options box */
1805 static void update_options()
1807 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_new_window_on_1),
1808 o_new_window_on_1);
1809 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_menu_on_2),
1810 collection_menu_button == 2 ? 1 : 0);
1811 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_single_click),
1812 o_single_click);
1813 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_unique_filer_windows),
1814 o_unique_filer_windows);
1815 gtk_option_menu_set_history(GTK_OPTION_MENU(menu_toolbar), o_toolbar);
1817 update_options_label();
1820 /* Set current values by reading the states of the widgets in the options box */
1821 static void set_options()
1823 GtkWidget *item, *menu;
1824 GList *list;
1826 o_new_window_on_1 = gtk_toggle_button_get_active(
1827 GTK_TOGGLE_BUTTON(toggle_new_window_on_1));
1829 collection_menu_button = gtk_toggle_button_get_active(
1830 GTK_TOGGLE_BUTTON(toggle_menu_on_2)) ? 2 : 3;
1832 o_single_click = gtk_toggle_button_get_active(
1833 GTK_TOGGLE_BUTTON(toggle_single_click));
1835 o_unique_filer_windows = gtk_toggle_button_get_active(
1836 GTK_TOGGLE_BUTTON(toggle_unique_filer_windows));
1838 collection_single_click = o_single_click ? TRUE : FALSE;
1840 menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(menu_toolbar));
1841 item = gtk_menu_get_active(GTK_MENU(menu));
1842 list = gtk_container_children(GTK_CONTAINER(menu));
1843 o_toolbar = (ToolbarType) g_list_index(list, item);
1844 g_list_free(list);
1848 static guchar *style_to_name(void)
1850 return last_display_style == LARGE_ICONS ? _("Large Icons") :
1851 last_display_style == SMALL_ICONS ? _("Small Icons") :
1852 _("Full Info");
1855 static guchar *sort_fn_to_name(void)
1857 return last_sort_fn == sort_by_name ? _("Name") :
1858 last_sort_fn == sort_by_type ? _("Type") :
1859 last_sort_fn == sort_by_date ? _("Date") :
1860 _("Size");
1863 static void save_options()
1865 option_write("filer_new_window_on_1", o_new_window_on_1 ? "1" : "0");
1866 option_write("filer_menu_on_2",
1867 collection_menu_button == 2 ? "1" : "0");
1868 option_write("filer_single_click", o_single_click ? "1" : "0");
1869 option_write("filer_unique_windows",
1870 o_unique_filer_windows ? "1" : "0");
1871 option_write("filer_display_style",
1872 last_display_style == LARGE_ICONS ? "Large Icons" :
1873 last_display_style == SMALL_ICONS ? "Small Icons" :
1874 "Full Info");
1875 option_write("filer_sort_by",
1876 last_sort_fn == sort_by_name ? "Name" :
1877 last_sort_fn == sort_by_type ? "Type" :
1878 last_sort_fn == sort_by_date ? "Date" :
1879 "Size");
1880 option_write("filer_toolbar", o_toolbar == TOOLBAR_NONE ? "None" :
1881 o_toolbar == TOOLBAR_NORMAL ? "Normal" :
1882 o_toolbar == TOOLBAR_GNOME ? "GNOME" :
1883 "Unknown");
1886 static char *filer_new_window_on_1(char *data)
1888 o_new_window_on_1 = atoi(data) != 0;
1889 return NULL;
1892 static char *filer_menu_on_2(char *data)
1894 collection_menu_button = atoi(data) != 0 ? 2 : 3;
1895 return NULL;
1898 static char *filer_single_click(char *data)
1900 o_single_click = atoi(data) != 0;
1901 collection_single_click = o_single_click ? TRUE : FALSE;
1902 return NULL;
1905 static char *filer_unique_windows(char *data)
1907 o_unique_filer_windows = atoi(data) != 0;
1908 return NULL;
1911 static char *filer_display_style(char *data)
1913 if (g_strcasecmp(data, "Large Icons") == 0)
1914 last_display_style = LARGE_ICONS;
1915 else if (g_strcasecmp(data, "Small Icons") == 0)
1916 last_display_style = SMALL_ICONS;
1917 else if (g_strcasecmp(data, "Full Info") == 0)
1918 last_display_style = FULL_INFO;
1919 else
1920 return _("Unknown display style");
1922 return NULL;
1925 static char *filer_sort_by(char *data)
1927 if (g_strcasecmp(data, "Name") == 0)
1928 last_sort_fn = sort_by_name;
1929 else if (g_strcasecmp(data, "Type") == 0)
1930 last_sort_fn = sort_by_type;
1931 else if (g_strcasecmp(data, "Date") == 0)
1932 last_sort_fn = sort_by_date;
1933 else if (g_strcasecmp(data, "Size") == 0)
1934 last_sort_fn = sort_by_size;
1935 else
1936 return _("Unknown sort type");
1938 return NULL;
1941 static char *filer_toolbar(char *data)
1943 if (g_strcasecmp(data, "None") == 0)
1944 o_toolbar = TOOLBAR_NONE;
1945 else if (g_strcasecmp(data, "Normal") == 0)
1946 o_toolbar = TOOLBAR_NORMAL;
1947 else if (g_strcasecmp(data, "GNOME") == 0)
1948 o_toolbar = TOOLBAR_GNOME;
1949 else
1950 return _("Unknown toolbar type");
1952 return NULL;
1955 /* Note that filer_window may not exist after this call. */
1956 void filer_update_dir(FilerWindow *filer_window, gboolean warning)
1958 if (may_rescan(filer_window, warning))
1959 dir_update(filer_window->directory, filer_window->path);
1962 void filer_set_hidden(FilerWindow *filer_window, gboolean hidden)
1964 Directory *dir = filer_window->directory;
1966 if (filer_window->show_hidden == hidden)
1967 return;
1969 filer_window->show_hidden = hidden;
1970 last_show_hidden = hidden;
1972 g_fscache_data_ref(dir_cache, dir);
1973 detach(filer_window);
1974 filer_window->directory = dir;
1975 attach(filer_window);
1978 /* Refresh the various caches even if we don't think we need to */
1979 void full_refresh(void)
1981 mount_update(TRUE);
1984 /* See whether a filer window with a given path already exists
1985 * and is different from diff.
1987 static FilerWindow *find_filer_window(char *path, FilerWindow *diff)
1989 GList *next = all_filer_windows;
1991 while (next)
1993 FilerWindow *filer_window = (FilerWindow *) next->data;
1995 if (filer_window->panel_type == PANEL_NO &&
1996 filer_window != diff &&
1997 strcmp(path, filer_window->path) == 0)
1999 return filer_window;
2002 next = next->next;
2005 return NULL;
2008 /* This path has been mounted/umounted/deleted some files - update all dirs */
2009 void filer_check_mounted(char *path)
2011 GList *next = all_filer_windows;
2012 int len;
2014 len = strlen(path);
2016 while (next)
2018 FilerWindow *filer_window = (FilerWindow *) next->data;
2020 next = next->next;
2022 if (strncmp(path, filer_window->path, len) == 0)
2024 char s = filer_window->path[len];
2026 if (s == '/' || s == '\0')
2027 filer_update_dir(filer_window, FALSE);
2032 /* Like minibuffer_show(), except that:
2033 * - It returns FALSE (to be used from an idle callback)
2034 * - It checks that the filer window still exists.
2036 static gboolean minibuffer_show_cb(FilerWindow *filer_window)
2038 if (filer_exists(filer_window))
2039 minibuffer_show(filer_window, MINI_PATH);
2040 return FALSE;
2043 gboolean filer_exists(FilerWindow *filer_window)
2045 GList *next;
2047 for (next = all_filer_windows; next; next = next->next)
2049 FilerWindow *fw = (FilerWindow *) next->data;
2051 if (fw == filer_window)
2052 return TRUE;
2055 return FALSE;
2058 /* Highlight (wink or cursor) this item in the filer window. If the item
2059 * isn't already there but we're scanning then highlight it if it
2060 * appears later.
2062 void filer_set_autoselect(FilerWindow *filer_window, guchar *leaf)
2064 Collection *col = filer_window->collection;
2065 int i;
2067 g_free(filer_window->auto_select);
2068 filer_window->auto_select = NULL;
2070 for (i = 0; i < col->number_of_items; i++)
2072 DirItem *item = (DirItem *) col->items[i].data;
2074 if (strcmp(item->leafname, leaf) == 0)
2076 if (col->cursor_item != -1)
2077 collection_set_cursor_item(col, i);
2078 else
2079 collection_wink_item(col, i);
2080 return;
2084 filer_window->auto_select = g_strdup(leaf);
2087 static void filer_set_title(FilerWindow *filer_window)
2089 if (filer_window->scanning)
2091 guchar *title;
2093 title = g_strdup_printf(_("%s (Scanning)"), filer_window->path);
2094 gtk_window_set_title(GTK_WINDOW(filer_window->window),
2095 title);
2096 g_free(title);
2098 else
2099 gtk_window_set_title(GTK_WINDOW(filer_window->window),
2100 filer_window->path);