r208: Added Bernard Jungen's patch:
[rox-filer.git] / ROX-Filer / src / filer.c
blob7ab8c7d33164146055dc607df94253b5db65fd22
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 1999, 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 <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <sys/param.h>
29 #include <unistd.h>
30 #include <time.h>
31 #include <ctype.h>
33 #include <gtk/gtk.h>
34 #include <gdk/gdkx.h>
35 #include <gdk/gdkkeysyms.h>
36 #include "collection.h"
38 #include "main.h"
39 #include "support.h"
40 #include "gui_support.h"
41 #include "filer.h"
42 #include "pixmaps.h"
43 #include "menu.h"
44 #include "dnd.h"
45 #include "run.h"
46 #include "mount.h"
47 #include "type.h"
48 #include "options.h"
49 #include "action.h"
50 #include "minibuffer.h"
52 #define ROW_HEIGHT_LARGE 64
53 #define ROW_HEIGHT_SMALL 32
54 #define ROW_HEIGHT_FULL_INFO 44
55 #define SMALL_ICON_HEIGHT 20
56 #define SMALL_ICON_WIDTH 48
57 #define MAX_ICON_HEIGHT 42
58 #define MAX_ICON_WIDTH 48
59 #define PANEL_BORDER 2
60 #define MIN_ITEM_WIDTH 64
62 extern int collection_menu_button;
63 extern gboolean collection_single_click;
65 FilerWindow *window_with_focus = NULL;
66 GList *all_filer_windows = NULL;
68 static DisplayStyle last_display_style = LARGE_ICONS;
69 static gboolean last_show_hidden = FALSE;
70 static int (*last_sort_fn)(const void *a, const void *b) = sort_by_type;
72 static FilerWindow *window_with_selection = NULL;
74 /* Options bits */
75 static guchar *style_to_name(void);
76 static guchar *sort_fn_to_name(void);
77 static void update_options_label(void);
79 static GtkWidget *create_options();
80 static void update_options();
81 static void set_options();
82 static void save_options();
83 static char *filer_single_click(char *data);
84 static char *filer_unique_windows(char *data);
85 static char *filer_menu_on_2(char *data);
86 static char *filer_new_window_on_1(char *data);
87 static char *filer_toolbar(char *data);
88 static char *filer_display_style(char *data);
89 static char *filer_sort_by(char *data);
91 static OptionsSection options =
93 "Filer window options",
94 create_options,
95 update_options,
96 set_options,
97 save_options
100 /* The values correspond to the menu indexes in the option widget */
101 typedef enum {
102 TOOLBAR_NONE = 0,
103 TOOLBAR_NORMAL = 1,
104 TOOLBAR_GNOME = 2,
105 } ToolbarType;
106 static ToolbarType o_toolbar = TOOLBAR_NORMAL;
107 static GtkWidget *menu_toolbar;
109 static GtkWidget *display_label;
111 static gboolean o_single_click = FALSE;
112 static GtkWidget *toggle_single_click;
113 static gboolean o_new_window_on_1 = FALSE; /* Button 1 => New window */
114 static GtkWidget *toggle_new_window_on_1;
115 static GtkWidget *toggle_menu_on_2;
116 static GtkWidget *toggle_unique_filer_windows;
117 static gboolean o_unique_filer_windows = FALSE;
119 /* Static prototypes */
120 static void attach(FilerWindow *filer_window);
121 static void detach(FilerWindow *filer_window);
122 static void filer_window_destroyed(GtkWidget *widget,
123 FilerWindow *filer_window);
124 static void show_menu(Collection *collection, GdkEventButton *event,
125 int number_selected, gpointer user_data);
126 static gint focus_in(GtkWidget *widget,
127 GdkEventFocus *event,
128 FilerWindow *filer_window);
129 static gint focus_out(GtkWidget *widget,
130 GdkEventFocus *event,
131 FilerWindow *filer_window);
132 static void add_item(FilerWindow *filer_window, DirItem *item);
133 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window);
134 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window);
135 static void add_button(GtkWidget *box, int pixmap,
136 GtkSignalFunc cb, FilerWindow *filer_window,
137 char *label, char *tip);
138 static GtkWidget *create_toolbar(FilerWindow *filer_window);
139 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
140 FilerWindow *window);
141 static int calc_width(FilerWindow *filer_window, DirItem *item);
142 static void draw_large_icon(GtkWidget *widget,
143 GdkRectangle *area,
144 DirItem *item,
145 gboolean selected);
146 static void draw_string(GtkWidget *widget,
147 GdkFont *font,
148 char *string,
149 int x,
150 int y,
151 int width,
152 gboolean selected);
153 static void draw_item_large(GtkWidget *widget,
154 CollectionItem *item,
155 GdkRectangle *area);
156 static void draw_item_small(GtkWidget *widget,
157 CollectionItem *item,
158 GdkRectangle *area);
159 static void draw_item_full_info(GtkWidget *widget,
160 CollectionItem *colitem,
161 GdkRectangle *area);
162 static gboolean test_point_large(Collection *collection,
163 int point_x, int point_y,
164 CollectionItem *item,
165 int width, int height);
166 static gboolean test_point_small(Collection *collection,
167 int point_x, int point_y,
168 CollectionItem *item,
169 int width, int height);
170 static gboolean test_point_full_info(Collection *collection,
171 int point_x, int point_y,
172 CollectionItem *item,
173 int width, int height);
174 static void update_display(Directory *dir,
175 DirAction action,
176 GPtrArray *items,
177 FilerWindow *filer_window);
178 static void set_scanning_display(FilerWindow *filer_window, gboolean scanning);
179 static void shrink_width(FilerWindow *filer_window);
180 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning);
181 static void open_item(Collection *collection,
182 gpointer item_data, int item_number,
183 gpointer user_data);
184 static gboolean minibuffer_show_cb(FilerWindow *filer_window);
185 static void set_autoselect(FilerWindow *filer_window, guchar *leaf);
186 static FilerWindow *find_filer_window(char *path);
187 static void filer_set_title(FilerWindow *filer_window);
188 static gboolean exists(FilerWindow *filer_window);
190 static GdkAtom xa_string;
191 enum
193 TARGET_STRING,
194 TARGET_URI_LIST,
197 static GdkCursor *busy_cursor = NULL;
198 static GtkTooltips *tooltips = NULL;
200 void filer_init()
202 xa_string = gdk_atom_intern("STRING", FALSE);
204 options_sections = g_slist_prepend(options_sections, &options);
205 option_register("filer_new_window_on_1", filer_new_window_on_1);
206 option_register("filer_menu_on_2", filer_menu_on_2);
207 option_register("filer_single_click", filer_single_click);
208 option_register("filer_unique_windows", filer_unique_windows);
209 option_register("filer_toolbar", filer_toolbar);
210 option_register("filer_display_style", filer_display_style);
211 option_register("filer_sort_by", filer_sort_by);
213 busy_cursor = gdk_cursor_new(GDK_WATCH);
215 tooltips = gtk_tooltips_new();
218 static gboolean if_deleted(gpointer item, gpointer removed)
220 int i = ((GPtrArray *) removed)->len;
221 DirItem **r = (DirItem **) ((GPtrArray *) removed)->pdata;
222 char *leafname = ((DirItem *) item)->leafname;
224 while (i--)
226 if (strcmp(leafname, r[i]->leafname) == 0)
227 return TRUE;
230 return FALSE;
233 static void update_item(FilerWindow *filer_window, DirItem *item)
235 int i;
236 char *leafname = item->leafname;
238 if (leafname[0] == '.')
240 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
241 || (leafname[1] == '.' && leafname[2] == '\0'))
242 return;
245 i = collection_find_item(filer_window->collection, item, dir_item_cmp);
247 if (i >= 0)
248 collection_draw_item(filer_window->collection, i, TRUE);
249 else
250 g_warning("Failed to find '%s'\n", item->leafname);
253 static void update_display(Directory *dir,
254 DirAction action,
255 GPtrArray *items,
256 FilerWindow *filer_window)
258 int i;
259 int cursor = filer_window->collection->cursor_item;
260 char *as;
261 Collection *collection = filer_window->collection;
263 switch (action)
265 case DIR_ADD:
266 as = filer_window->auto_select;
268 for (i = 0; i < items->len; i++)
270 DirItem *item = (DirItem *) items->pdata[i];
272 add_item(filer_window, item);
274 if (cursor != -1 || !as)
275 continue;
277 if (strcmp(as, item->leafname) != 0)
278 continue;
280 cursor = collection->number_of_items - 1;
281 if (filer_window->had_cursor)
283 collection_set_cursor_item(collection,
284 cursor);
285 filer_window->mini_cursor_base = cursor;
287 else
288 collection_wink_item(collection,
289 cursor);
292 collection_qsort(filer_window->collection,
293 filer_window->sort_fn);
294 break;
295 case DIR_REMOVE:
296 collection_delete_if(filer_window->collection,
297 if_deleted,
298 items);
299 break;
300 case DIR_START_SCAN:
301 set_scanning_display(filer_window, TRUE);
302 break;
303 case DIR_END_SCAN:
304 if (filer_window->window->window)
305 gdk_window_set_cursor(
306 filer_window->window->window,
307 NULL);
308 shrink_width(filer_window);
309 if (filer_window->had_cursor &&
310 collection->cursor_item == -1)
312 collection_set_cursor_item(collection, 0);
313 filer_window->had_cursor = FALSE;
315 set_scanning_display(filer_window, FALSE);
316 break;
317 case DIR_UPDATE:
318 for (i = 0; i < items->len; i++)
320 DirItem *item = (DirItem *) items->pdata[i];
322 update_item(filer_window, item);
324 collection_qsort(filer_window->collection,
325 filer_window->sort_fn);
326 break;
330 static void attach(FilerWindow *filer_window)
332 gdk_window_set_cursor(filer_window->window->window, busy_cursor);
333 collection_clear(filer_window->collection);
334 filer_window->scanning = TRUE;
335 dir_attach(filer_window->directory, (DirCallback) update_display,
336 filer_window);
337 filer_set_title(filer_window);
340 static void detach(FilerWindow *filer_window)
342 g_return_if_fail(filer_window->directory != NULL);
344 dir_detach(filer_window->directory,
345 (DirCallback) update_display, filer_window);
346 g_fscache_data_unref(dir_cache, filer_window->directory);
347 filer_window->directory = NULL;
350 static void filer_window_destroyed(GtkWidget *widget,
351 FilerWindow *filer_window)
353 all_filer_windows = g_list_remove(all_filer_windows, filer_window);
355 if (window_with_selection == filer_window)
356 window_with_selection = NULL;
357 if (window_with_focus == filer_window)
358 window_with_focus = NULL;
360 if (filer_window->directory)
361 detach(filer_window);
363 g_free(filer_window->auto_select);
364 g_free(filer_window->path);
365 g_free(filer_window);
367 if (--number_of_windows < 1)
368 gtk_main_quit();
371 static int calc_width(FilerWindow *filer_window, DirItem *item)
373 int pix_width = item->image->width;
375 switch (filer_window->display_style)
377 case FULL_INFO:
378 return MAX_ICON_WIDTH + 12 +
379 MAX(item->details_width, item->name_width);
380 case SMALL_ICONS:
381 return SMALL_ICON_WIDTH + 12 + item->name_width;
382 default:
383 return MAX(pix_width, item->name_width) + 4;
387 /* Add a single object to a directory display */
388 static void add_item(FilerWindow *filer_window, DirItem *item)
390 char *leafname = item->leafname;
391 int item_width;
393 if (leafname[0] == '.')
395 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
396 || (leafname[1] == '.' && leafname[2] == '\0'))
397 return;
400 item_width = calc_width(filer_window, item);
401 if (item_width > filer_window->collection->item_width)
402 collection_set_item_size(filer_window->collection,
403 item_width,
404 filer_window->collection->item_height);
405 collection_insert(filer_window->collection, item);
408 /* Is a point inside an item? */
409 static gboolean test_point_large(Collection *collection,
410 int point_x, int point_y,
411 CollectionItem *colitem,
412 int width, int height)
414 DirItem *item = (DirItem *) colitem->data;
415 int text_height = item_font->ascent + item_font->descent;
416 MaskedPixmap *image = item->image;
417 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
418 int image_width = (image->width >> 1) + 2;
419 int text_width = (item->name_width >> 1) + 2;
420 int x_limit;
422 if (point_y < image_y)
423 return FALSE; /* Too high up (don't worry about too low) */
425 if (point_y <= image_y + image->height + 2)
426 x_limit = image_width;
427 else if (point_y > height - text_height - 2)
428 x_limit = text_width;
429 else
430 x_limit = MIN(image_width, text_width);
432 return ABS(point_x - (width >> 1)) < x_limit;
435 static gboolean test_point_full_info(Collection *collection,
436 int point_x, int point_y,
437 CollectionItem *colitem,
438 int width, int height)
440 DirItem *item = (DirItem *) colitem->data;
441 MaskedPixmap *image = item->image;
442 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
443 int low_top = height
444 - fixed_font->descent - 2 - fixed_font->ascent;
446 if (point_x < image->width + 2)
447 return point_x > 2 && point_y > image_y;
449 point_x -= MAX_ICON_WIDTH + 8;
451 if (point_y >= low_top)
452 return point_x < item->details_width;
453 if (point_y >= low_top - item_font->ascent - item_font->descent)
454 return point_x < item->name_width;
455 return FALSE;
458 static gboolean test_point_small(Collection *collection,
459 int point_x, int point_y,
460 CollectionItem *colitem,
461 int width, int height)
463 DirItem *item = (DirItem *) colitem->data;
464 MaskedPixmap *image = item->image;
465 int image_y = MAX(0, SMALL_ICON_HEIGHT - image->height);
466 int low_top = height
467 - fixed_font->descent - 2 - item_font->ascent;
468 int iwidth = MIN(SMALL_ICON_WIDTH, image->width);
470 if (point_x < iwidth + 2)
471 return point_x > 2 && point_y > image_y;
473 point_x -= SMALL_ICON_WIDTH + 4;
475 if (point_y >= low_top)
476 return point_x < item->name_width;
477 return FALSE;
480 static void draw_small_icon(GtkWidget *widget,
481 GdkRectangle *area,
482 DirItem *item,
483 gboolean selected)
485 MaskedPixmap *image = item->image;
486 int width = MIN(image->width, SMALL_ICON_WIDTH);
487 int height = MIN(image->height, SMALL_ICON_HEIGHT);
488 int image_x = area->x + ((area->width - width) >> 1);
489 int image_y;
490 GdkGC *gc = selected ? widget->style->white_gc
491 : widget->style->black_gc;
492 if (!item->image)
493 return;
495 gdk_gc_set_clip_mask(gc, item->image->mask);
497 image_y = MAX(0, SMALL_ICON_HEIGHT - image->height);
498 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
499 gdk_draw_pixmap(widget->window, gc,
500 item->image->pixmap,
501 0, 0, /* Source x,y */
502 image_x, area->y + image_y, /* Dest x,y */
503 width, height);
505 if (selected)
507 gdk_gc_set_function(gc, GDK_INVERT);
508 gdk_draw_rectangle(widget->window,
510 TRUE, image_x, area->y + image_y,
511 width, height);
512 gdk_gc_set_function(gc, GDK_COPY);
515 if (item->flags & ITEM_FLAG_SYMLINK)
517 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
518 gdk_gc_set_clip_mask(gc,
519 default_pixmap[TYPE_SYMLINK].mask);
520 gdk_draw_pixmap(widget->window, gc,
521 default_pixmap[TYPE_SYMLINK].pixmap,
522 0, 0, /* Source x,y */
523 image_x, area->y + 8, /* Dest x,y */
524 -1, -1);
526 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
528 int type = item->flags & ITEM_FLAG_MOUNTED
529 ? TYPE_MOUNTED
530 : TYPE_UNMOUNTED;
531 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
532 gdk_gc_set_clip_mask(gc,
533 default_pixmap[type].mask);
534 gdk_draw_pixmap(widget->window, gc,
535 default_pixmap[type].pixmap,
536 0, 0, /* Source x,y */
537 image_x, area->y + 8, /* Dest x,y */
538 -1, -1);
541 gdk_gc_set_clip_mask(gc, NULL);
542 gdk_gc_set_clip_origin(gc, 0, 0);
545 static void draw_large_icon(GtkWidget *widget,
546 GdkRectangle *area,
547 DirItem *item,
548 gboolean selected)
550 MaskedPixmap *image = item->image;
551 int width = MIN(image->width, MAX_ICON_WIDTH);
552 int height = MIN(image->height, MAX_ICON_WIDTH);
553 int image_x = area->x + ((area->width - width) >> 1);
554 int image_y;
555 GdkGC *gc = selected ? widget->style->white_gc
556 : widget->style->black_gc;
558 gdk_gc_set_clip_mask(gc, item->image->mask);
560 image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
561 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
562 gdk_draw_pixmap(widget->window, gc,
563 item->image->pixmap,
564 0, 0, /* Source x,y */
565 image_x, area->y + image_y, /* Dest x,y */
566 width, height);
568 if (selected)
570 gdk_gc_set_function(gc, GDK_INVERT);
571 gdk_draw_rectangle(widget->window,
573 TRUE, image_x, area->y + image_y,
574 width, height);
575 gdk_gc_set_function(gc, GDK_COPY);
578 if (item->flags & ITEM_FLAG_SYMLINK)
580 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
581 gdk_gc_set_clip_mask(gc,
582 default_pixmap[TYPE_SYMLINK].mask);
583 gdk_draw_pixmap(widget->window, gc,
584 default_pixmap[TYPE_SYMLINK].pixmap,
585 0, 0, /* Source x,y */
586 image_x, area->y + 8, /* Dest x,y */
587 -1, -1);
589 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
591 int type = item->flags & ITEM_FLAG_MOUNTED
592 ? TYPE_MOUNTED
593 : TYPE_UNMOUNTED;
594 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
595 gdk_gc_set_clip_mask(gc,
596 default_pixmap[type].mask);
597 gdk_draw_pixmap(widget->window, gc,
598 default_pixmap[type].pixmap,
599 0, 0, /* Source x,y */
600 image_x, area->y + 8, /* Dest x,y */
601 -1, -1);
604 gdk_gc_set_clip_mask(gc, NULL);
605 gdk_gc_set_clip_origin(gc, 0, 0);
608 static void draw_string(GtkWidget *widget,
609 GdkFont *font,
610 char *string,
611 int x,
612 int y,
613 int width,
614 gboolean selected)
616 int text_height = font->ascent + font->descent;
618 if (selected)
619 gtk_paint_flat_box(widget->style, widget->window,
620 GTK_STATE_SELECTED, GTK_SHADOW_NONE,
621 NULL, widget, "text",
622 x, y - font->ascent,
623 width,
624 text_height);
626 gdk_draw_text(widget->window,
627 font,
628 selected ? widget->style->white_gc
629 : widget->style->black_gc,
630 x, y,
631 string, strlen(string));
634 /* Return a string (valid until next call) giving details
635 * of this item.
637 char *details(DirItem *item)
639 mode_t m = item->mode;
640 static GString *buf = NULL;
642 if (!buf)
643 buf = g_string_new(NULL);
645 g_string_sprintf(buf, "%s %s %8s %8s %s %s",
646 item->flags & ITEM_FLAG_APPDIR? "App " :
647 S_ISDIR(m) ? "Dir " :
648 S_ISCHR(m) ? "Char" :
649 S_ISBLK(m) ? "Blck" :
650 S_ISLNK(m) ? "Link" :
651 S_ISSOCK(m) ? "Sock" :
652 S_ISFIFO(m) ? "Pipe" : "File",
653 pretty_permissions(m),
654 user_name(item->uid),
655 group_name(item->gid),
656 format_size_aligned(item->size),
657 pretty_time(&item->mtime));
658 return buf->str;
661 static void draw_item_full_info(GtkWidget *widget,
662 CollectionItem *colitem,
663 GdkRectangle *area)
665 DirItem *item = (DirItem *) colitem->data;
666 MaskedPixmap *image = item->image;
667 int text_x = area->x + MAX_ICON_WIDTH + 8;
668 int low_text_y = area->y + area->height - fixed_font->descent - 2;
669 gboolean selected = colitem->selected;
670 GdkRectangle pic_area;
672 pic_area.x = area->x;
673 pic_area.y = area->y;
674 pic_area.width = image->width + 8;
675 pic_area.height = area->height;
677 draw_large_icon(widget, &pic_area, item, selected);
679 draw_string(widget,
680 item_font,
681 item->leafname,
682 text_x,
683 low_text_y - item_font->descent - fixed_font->ascent,
684 item->name_width,
685 selected);
686 draw_string(widget,
687 fixed_font,
688 details(item),
689 text_x, low_text_y,
690 item->details_width,
691 selected);
692 gdk_draw_rectangle(widget->window,
693 selected ? widget->style->white_gc
694 : widget->style->black_gc,
695 TRUE,
696 text_x - 1 + fixed_width *
697 (5 + 4 * applicable(item->uid, item->gid)),
698 low_text_y + fixed_font->descent - 1,
699 fixed_width * 3 + 1, 1);
702 static void draw_item_small(GtkWidget *widget,
703 CollectionItem *colitem,
704 GdkRectangle *area)
706 DirItem *item = (DirItem *) colitem->data;
707 int text_x = area->x + SMALL_ICON_WIDTH + 4;
708 int low_text_y = area->y + area->height - item_font->descent - 2;
709 gboolean selected = colitem->selected;
710 GdkRectangle pic_area;
712 pic_area.x = area->x;
713 pic_area.y = area->y;
714 pic_area.width = SMALL_ICON_WIDTH;
715 pic_area.height = SMALL_ICON_HEIGHT;
717 draw_small_icon(widget, &pic_area, item, selected);
719 draw_string(widget,
720 item_font,
721 item->leafname,
722 text_x,
723 low_text_y,
724 item->name_width,
725 selected);
728 static void draw_item_large(GtkWidget *widget,
729 CollectionItem *colitem,
730 GdkRectangle *area)
732 DirItem *item = (DirItem *) colitem->data;
733 int text_x = area->x + ((area->width - item->name_width) >> 1);
734 int text_y = area->y + area->height - item_font->descent - 2;
735 gboolean selected = colitem->selected;
737 draw_large_icon(widget, area, item, selected);
739 draw_string(widget,
740 item_font,
741 item->leafname,
742 text_x, text_y, item->name_width,
743 selected);
746 static void show_menu(Collection *collection, GdkEventButton *event,
747 int item, gpointer user_data)
749 show_filer_menu((FilerWindow *) user_data, event, item);
752 /* Returns TRUE iff the directory still exits. */
753 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning)
755 Directory *dir;
757 g_return_val_if_fail(filer_window != NULL, FALSE);
759 /* We do a fresh lookup (rather than update) because the inode may
760 * have changed.
762 dir = g_fscache_lookup(dir_cache, filer_window->path);
763 if (!dir)
765 if (warning)
766 delayed_error("ROX-Filer", "Directory missing/deleted");
767 gtk_widget_destroy(filer_window->window);
768 return FALSE;
770 if (dir == filer_window->directory)
771 g_fscache_data_unref(dir_cache, dir);
772 else
774 detach(filer_window);
775 filer_window->directory = dir;
776 attach(filer_window);
779 return TRUE;
782 /* Another app has grabbed the selection */
783 static gint collection_lose_selection(GtkWidget *widget,
784 GdkEventSelection *event)
786 if (window_with_selection &&
787 window_with_selection->collection == COLLECTION(widget))
789 FilerWindow *filer_window = window_with_selection;
790 window_with_selection = NULL;
791 collection_clear_selection(filer_window->collection);
794 return TRUE;
797 /* Someone wants us to send them the selection */
798 static void selection_get(GtkWidget *widget,
799 GtkSelectionData *selection_data,
800 guint info,
801 guint time,
802 gpointer data)
804 GString *reply, *header;
805 FilerWindow *filer_window;
806 int i;
807 Collection *collection;
809 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
811 reply = g_string_new(NULL);
812 header = g_string_new(NULL);
814 switch (info)
816 case TARGET_STRING:
817 g_string_sprintf(header, " %s",
818 make_path(filer_window->path, "")->str);
819 break;
820 case TARGET_URI_LIST:
821 g_string_sprintf(header, " file://%s%s",
822 our_host_name(),
823 make_path(filer_window->path, "")->str);
824 break;
827 collection = filer_window->collection;
828 for (i = 0; i < collection->number_of_items; i++)
830 if (collection->items[i].selected)
832 DirItem *item =
833 (DirItem *) collection->items[i].data;
835 g_string_append(reply, header->str);
836 g_string_append(reply, item->leafname);
839 /* This works, but I don't think I like it... */
840 /* g_string_append_c(reply, ' '); */
842 gtk_selection_data_set(selection_data, xa_string,
843 8, reply->str + 1, reply->len - 1);
844 g_string_free(reply, TRUE);
845 g_string_free(header, TRUE);
848 /* No items are now selected. This might be because another app claimed
849 * the selection or because the user unselected all the items.
851 static void lose_selection(Collection *collection,
852 guint time,
853 gpointer user_data)
855 FilerWindow *filer_window = (FilerWindow *) user_data;
857 if (window_with_selection == filer_window)
859 window_with_selection = NULL;
860 gtk_selection_owner_set(NULL,
861 GDK_SELECTION_PRIMARY,
862 time);
866 static void gain_selection(Collection *collection,
867 guint time,
868 gpointer user_data)
870 FilerWindow *filer_window = (FilerWindow *) user_data;
872 if (gtk_selection_owner_set(GTK_WIDGET(collection),
873 GDK_SELECTION_PRIMARY,
874 time))
876 window_with_selection = filer_window;
878 else
879 collection_clear_selection(filer_window->collection);
882 int sort_by_name(const void *item1, const void *item2)
884 return strcmp((*((DirItem **)item1))->leafname,
885 (*((DirItem **)item2))->leafname);
888 int sort_by_type(const void *item1, const void *item2)
890 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
891 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
892 MIME_type *m1, *m2;
894 int diff = i1->base_type - i2->base_type;
896 if (!diff)
897 diff = (i1->flags & ITEM_FLAG_APPDIR)
898 - (i2->flags & ITEM_FLAG_APPDIR);
899 if (diff)
900 return diff > 0 ? 1 : -1;
902 m1 = i1->mime_type;
903 m2 = i2->mime_type;
905 if (m1 && m2)
907 diff = strcmp(m1->media_type, m2->media_type);
908 if (!diff)
909 diff = strcmp(m1->subtype, m2->subtype);
911 else if (m1 || m2)
912 diff = m1 ? 1 : -1;
913 else
914 diff = 0;
916 if (diff)
917 return diff > 0 ? 1 : -1;
919 return sort_by_name(item1, item2);
922 int sort_by_date(const void *item1, const void *item2)
924 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
925 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
927 return i1->mtime > i2->mtime ? -1 :
928 i1->mtime < i2->mtime ? 1 :
929 sort_by_name(item1, item2);
932 int sort_by_size(const void *item1, const void *item2)
934 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
935 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
937 return i1->size > i2->size ? -1 :
938 i1->size < i2->size ? 1 :
939 sort_by_name(item1, item2);
942 static void open_item(Collection *collection,
943 gpointer item_data, int item_number,
944 gpointer user_data)
946 FilerWindow *filer_window = (FilerWindow *) user_data;
947 GdkEvent *event;
948 GdkEventButton *bevent;
949 GdkEventKey *kevent;
950 OpenFlags flags = 0;
952 event = (GdkEvent *) gtk_get_current_event();
954 bevent = (GdkEventButton *) event;
955 kevent = (GdkEventKey *) event;
957 switch (event->type)
959 case GDK_2BUTTON_PRESS:
960 case GDK_BUTTON_PRESS:
961 case GDK_BUTTON_RELEASE:
962 if (bevent->state & GDK_SHIFT_MASK)
963 flags |= OPEN_SHIFT;
965 if (o_new_window_on_1 ^ (bevent->button == 1))
966 flags |= OPEN_SAME_WINDOW;
968 if (bevent->button != 1)
969 flags |= OPEN_CLOSE_WINDOW;
971 if (o_single_click == FALSE &&
972 (bevent->state & GDK_CONTROL_MASK) != 0)
973 flags ^= OPEN_SAME_WINDOW | OPEN_CLOSE_WINDOW;
974 break;
975 case GDK_KEY_PRESS:
976 flags |= OPEN_SAME_WINDOW;
977 if (kevent->state & GDK_SHIFT_MASK)
978 flags |= OPEN_SHIFT;
979 break;
980 default:
981 break;
984 filer_openitem(filer_window, item_number, flags);
987 /* Return the full path to the directory containing object 'path'.
988 * Relative paths are resolved from the filerwindow's path.
990 static void follow_symlink(FilerWindow *filer_window, char *path,
991 gboolean same_window)
993 char *real, *slash;
994 char *new_dir;
996 if (path[0] != '/')
997 path = make_path(filer_window->path, path)->str;
999 real = pathdup(path);
1000 slash = strrchr(real, '/');
1001 if (!slash)
1003 g_free(real);
1004 delayed_error("ROX-Filer",
1005 "Broken symlink (or you don't have permission "
1006 "to follow it).");
1007 return;
1010 *slash = '\0';
1012 if (*real)
1013 new_dir = real;
1014 else
1015 new_dir = "/";
1017 if (filer_window->panel_type || !same_window)
1019 FilerWindow *new;
1021 new = filer_opendir(new_dir, PANEL_NO);
1022 set_autoselect(new, slash + 1);
1024 else
1025 filer_change_to(filer_window, new_dir, slash + 1);
1027 g_free(real);
1030 void filer_openitem(FilerWindow *filer_window, int item_number, OpenFlags flags)
1032 gboolean shift = (flags & OPEN_SHIFT) != 0;
1033 gboolean close_mini = flags & OPEN_FROM_MINI;
1034 gboolean same_window = (flags & OPEN_SAME_WINDOW) != 0
1035 && !filer_window->panel_type;
1036 gboolean close_window = (flags & OPEN_CLOSE_WINDOW) != 0
1037 && !filer_window->panel_type;
1038 GtkWidget *widget;
1039 char *full_path;
1040 DirItem *item = (DirItem *)
1041 filer_window->collection->items[item_number].data;
1042 gboolean wink = TRUE, destroy = FALSE;
1044 widget = filer_window->window;
1045 full_path = make_path(filer_window->path,
1046 item->leafname)->str;
1048 if (item->flags & ITEM_FLAG_SYMLINK && shift)
1050 char path[MAXPATHLEN + 1];
1051 int got;
1053 got = readlink(make_path(filer_window->path,
1054 item->leafname)->str,
1055 path, MAXPATHLEN);
1056 if (got < 0)
1057 delayed_error("ROX-Filer", g_strerror(errno));
1058 else
1060 g_return_if_fail(got <= MAXPATHLEN);
1061 path[got] = '\0';
1063 follow_symlink(filer_window, path,
1064 flags & OPEN_SAME_WINDOW);
1066 return;
1069 switch (item->base_type)
1071 case TYPE_DIRECTORY:
1072 if (item->flags & ITEM_FLAG_APPDIR && !shift)
1074 run_app(make_path(filer_window->path,
1075 item->leafname)->str);
1076 if (close_window)
1077 destroy = TRUE;
1078 break;
1081 if (item->flags & ITEM_FLAG_MOUNT_POINT && shift)
1083 action_mount(filer_window, item);
1084 if (item->flags & ITEM_FLAG_MOUNTED)
1085 break;
1088 if (same_window)
1090 wink = FALSE;
1091 filer_change_to(filer_window, full_path, NULL);
1092 close_mini = FALSE;
1094 else
1095 filer_opendir(full_path, PANEL_NO);
1096 break;
1097 case TYPE_FILE:
1098 if ((item->flags & ITEM_FLAG_EXEC_FILE) && !shift)
1100 char *argv[] = {NULL, NULL};
1102 argv[0] = full_path;
1104 if (spawn_full(argv, filer_window->path))
1106 if (close_window)
1107 destroy = TRUE;
1109 else
1110 report_error("ROX-Filer",
1111 "Failed to fork() child");
1113 else
1115 GString *message;
1116 MIME_type *type = shift ? &text_plain
1117 : item->mime_type;
1119 g_return_if_fail(type != NULL);
1121 if (type_open(full_path, type))
1123 if (close_window)
1124 destroy = TRUE;
1126 else
1128 message = g_string_new(NULL);
1129 g_string_sprintf(message, "No open "
1130 "action specified for files of "
1131 "this type (%s/%s)",
1132 type->media_type,
1133 type->subtype);
1134 report_error("ROX-Filer", message->str);
1135 g_string_free(message, TRUE);
1138 break;
1139 default:
1140 report_error("open_item",
1141 "I don't know how to open that");
1142 break;
1145 if (destroy)
1146 gtk_widget_destroy(filer_window->window);
1147 else
1149 if (wink)
1150 collection_wink_item(filer_window->collection,
1151 item_number);
1152 if (close_mini)
1153 minibuffer_hide(filer_window);
1157 static gint pointer_in(GtkWidget *widget,
1158 GdkEventCrossing *event,
1159 FilerWindow *filer_window)
1161 may_rescan(filer_window, TRUE);
1162 return FALSE;
1165 static gint focus_in(GtkWidget *widget,
1166 GdkEventFocus *event,
1167 FilerWindow *filer_window)
1169 window_with_focus = filer_window;
1171 return FALSE;
1174 static gint focus_out(GtkWidget *widget,
1175 GdkEventFocus *event,
1176 FilerWindow *filer_window)
1178 /* TODO: Shade the cursor */
1180 return FALSE;
1183 /* Handle keys that can't be bound with the menu */
1184 static gint key_press_event(GtkWidget *widget,
1185 GdkEventKey *event,
1186 FilerWindow *filer_window)
1188 switch (event->keyval)
1190 case GDK_BackSpace:
1191 change_to_parent(filer_window);
1192 break;
1193 default:
1194 return FALSE;
1197 return TRUE;
1200 static void toolbar_refresh_clicked(GtkWidget *widget,
1201 FilerWindow *filer_window)
1203 full_refresh();
1204 update_dir(filer_window, TRUE);
1207 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window)
1209 filer_change_to(filer_window, home_dir, NULL);
1212 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window)
1214 change_to_parent(filer_window);
1217 void change_to_parent(FilerWindow *filer_window)
1219 char *copy;
1220 char *slash;
1222 if (filer_window->path[0] == '/' && filer_window->path[1] == '\0')
1223 return; /* Already in the root */
1225 copy = g_strdup(filer_window->path);
1226 slash = strrchr(copy, '/');
1228 if (slash)
1230 *slash = '\0';
1231 filer_change_to(filer_window,
1232 *copy ? copy : "/",
1233 slash + 1);
1235 else
1236 g_warning("No / in directory path!\n");
1238 g_free(copy);
1242 /* Make filer_window display path. When finished, highlight item 'from', or
1243 * the first item if from is NULL. If there is currently no cursor then
1244 * simply wink 'from' (if not NULL).
1246 void filer_change_to(FilerWindow *filer_window, char *path, char *from)
1248 char *from_dup;
1249 char *real_path = pathdup(path);
1251 if (o_unique_filer_windows)
1253 FilerWindow *fw;
1255 fw = find_filer_window(real_path);
1256 if (fw && fw != filer_window)
1257 gtk_widget_destroy(fw->window);
1260 from_dup = from && *from ? g_strdup(from) : NULL;
1262 detach(filer_window);
1263 g_free(filer_window->path);
1264 filer_window->path = real_path;
1266 filer_window->directory = g_fscache_lookup(dir_cache,
1267 filer_window->path);
1268 if (filer_window->directory)
1270 g_free(filer_window->auto_select);
1271 filer_window->had_cursor =
1272 filer_window->collection->cursor_item != -1
1273 || filer_window->had_cursor;
1274 filer_window->auto_select = from_dup;
1276 filer_set_title(filer_window);
1277 collection_set_cursor_item(filer_window->collection, -1);
1278 attach(filer_window);
1280 if (GTK_WIDGET_VISIBLE(filer_window->minibuffer))
1281 gtk_idle_add((GtkFunction) minibuffer_show_cb,
1282 filer_window);
1284 else
1286 char *error;
1288 g_free(from_dup);
1289 error = g_strdup_printf("Directory '%s' is not accessible.",
1290 path);
1291 delayed_error("ROX-Filer", error);
1292 g_free(error);
1293 gtk_widget_destroy(filer_window->window);
1297 int selected_item_number(Collection *collection)
1299 int i;
1301 g_return_val_if_fail(collection != NULL, -1);
1302 g_return_val_if_fail(IS_COLLECTION(collection), -1);
1303 g_return_val_if_fail(collection->number_selected == 1, -1);
1305 for (i = 0; i < collection->number_of_items; i++)
1306 if (collection->items[i].selected)
1307 return i;
1309 g_warning("selected_item: number_selected is wrong\n");
1311 return -1;
1314 DirItem *selected_item(Collection *collection)
1316 int item;
1318 item = selected_item_number(collection);
1320 if (item > -1)
1321 return (DirItem *) collection->items[item].data;
1322 return NULL;
1325 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
1326 FilerWindow *window)
1328 /* TODO: We can open lots of these - very irritating! */
1329 return get_choice("Close panel?",
1330 "You have tried to close a panel via the window "
1331 "manager - I usually find that this is accidental... "
1332 "really close?",
1333 2, "Remove", "Cancel") != 0;
1336 /* Make the items as narrow as possible */
1337 static void shrink_width(FilerWindow *filer_window)
1339 int i;
1340 Collection *col = filer_window->collection;
1341 int width = MIN_ITEM_WIDTH;
1342 int this_width;
1343 DisplayStyle style = filer_window->display_style;
1344 int text_height;
1346 text_height = item_font->ascent + item_font->descent;
1348 for (i = 0; i < col->number_of_items; i++)
1350 this_width = calc_width(filer_window,
1351 (DirItem *) col->items[i].data);
1352 if (this_width > width)
1353 width = this_width;
1356 collection_set_item_size(filer_window->collection,
1357 width,
1358 style == FULL_INFO ? MAX_ICON_HEIGHT + 4 :
1359 style == SMALL_ICONS ? MAX(text_height, SMALL_ICON_HEIGHT) + 4
1360 : text_height + MAX_ICON_HEIGHT + 8);
1363 void filer_set_sort_fn(FilerWindow *filer_window,
1364 int (*fn)(const void *a, const void *b))
1366 if (filer_window->sort_fn == fn)
1367 return;
1369 filer_window->sort_fn = fn;
1370 last_sort_fn = fn;
1372 collection_qsort(filer_window->collection,
1373 filer_window->sort_fn);
1375 update_options_label();
1378 void filer_style_set(FilerWindow *filer_window, DisplayStyle style)
1380 if (filer_window->display_style == style)
1381 return;
1383 if (filer_window->panel_type)
1384 style = LARGE_ICONS;
1385 else
1386 last_display_style = style;
1388 filer_window->display_style = style;
1390 switch (style)
1392 case SMALL_ICONS:
1393 collection_set_functions(filer_window->collection,
1394 draw_item_small, test_point_small);
1395 break;
1396 case FULL_INFO:
1397 collection_set_functions(filer_window->collection,
1398 draw_item_full_info, test_point_full_info);
1399 break;
1400 default:
1401 collection_set_functions(filer_window->collection,
1402 draw_item_large, test_point_large);
1403 break;
1406 shrink_width(filer_window);
1408 update_options_label();
1411 FilerWindow *filer_opendir(char *path, PanelType panel_type)
1413 GtkWidget *hbox, *scrollbar, *collection;
1414 FilerWindow *filer_window;
1415 GtkTargetEntry target_table[] =
1417 {"text/uri-list", 0, TARGET_URI_LIST},
1418 {"STRING", 0, TARGET_STRING},
1420 char *real_path;
1422 real_path = pathdup(path);
1424 if (o_unique_filer_windows && panel_type == PANEL_NO)
1426 FilerWindow *fw;
1428 fw = find_filer_window(real_path);
1430 if (fw)
1432 /* TODO: this should bring the window to the front
1433 * at the same coordinates.
1435 gtk_widget_hide(fw->window);
1436 gtk_widget_show(fw->window);
1437 g_free(real_path);
1438 return fw;
1442 filer_window = g_new(FilerWindow, 1);
1443 filer_window->minibuffer = NULL;
1444 filer_window->path = real_path;
1445 filer_window->scanning = FALSE;
1446 filer_window->had_cursor = FALSE;
1447 filer_window->auto_select = NULL;
1449 filer_window->directory = g_fscache_lookup(dir_cache,
1450 filer_window->path);
1451 if (!filer_window->directory)
1453 char *error;
1455 error = g_strdup_printf("Directory '%s' not found.", path);
1456 delayed_error("ROX-Filer", error);
1457 g_free(error);
1458 g_free(filer_window->path);
1459 g_free(filer_window);
1460 return NULL;
1463 filer_window->show_hidden = last_show_hidden;
1464 filer_window->panel_type = panel_type;
1465 filer_window->temp_item_selected = FALSE;
1466 filer_window->sort_fn = last_sort_fn;
1467 filer_window->flags = (FilerFlags) 0;
1468 filer_window->display_style = UNKNOWN_STYLE;
1470 filer_window->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1471 filer_set_title(filer_window);
1473 collection = collection_new(NULL);
1474 gtk_object_set_data(GTK_OBJECT(collection),
1475 "filer_window", filer_window);
1476 filer_window->collection = COLLECTION(collection);
1478 gtk_widget_add_events(filer_window->window, GDK_ENTER_NOTIFY);
1479 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1480 "enter-notify-event",
1481 GTK_SIGNAL_FUNC(pointer_in), filer_window);
1482 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_in_event",
1483 GTK_SIGNAL_FUNC(focus_in), filer_window);
1484 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_out_event",
1485 GTK_SIGNAL_FUNC(focus_out), filer_window);
1486 gtk_signal_connect(GTK_OBJECT(filer_window->window), "destroy",
1487 filer_window_destroyed, filer_window);
1489 gtk_signal_connect(GTK_OBJECT(filer_window->collection), "open_item",
1490 open_item, filer_window);
1491 gtk_signal_connect(GTK_OBJECT(collection), "show_menu",
1492 show_menu, filer_window);
1493 gtk_signal_connect(GTK_OBJECT(collection), "gain_selection",
1494 gain_selection, filer_window);
1495 gtk_signal_connect(GTK_OBJECT(collection), "lose_selection",
1496 lose_selection, filer_window);
1497 gtk_signal_connect(GTK_OBJECT(collection), "drag_selection",
1498 drag_selection, filer_window);
1499 gtk_signal_connect(GTK_OBJECT(collection), "drag_data_get",
1500 drag_data_get, filer_window);
1501 gtk_signal_connect(GTK_OBJECT(collection), "selection_clear_event",
1502 GTK_SIGNAL_FUNC(collection_lose_selection), NULL);
1503 gtk_signal_connect (GTK_OBJECT(collection), "selection_get",
1504 GTK_SIGNAL_FUNC(selection_get), NULL);
1505 gtk_selection_add_targets(collection, GDK_SELECTION_PRIMARY,
1506 target_table,
1507 sizeof(target_table) / sizeof(*target_table));
1509 filer_style_set(filer_window, last_display_style);
1510 drag_set_dest(collection);
1512 if (panel_type)
1514 int swidth, sheight, iwidth, iheight;
1515 GtkWidget *frame, *win = filer_window->window;
1517 gtk_window_set_wmclass(GTK_WINDOW(win), "ROX-Panel",
1518 "ROX-Filer");
1519 collection_set_panel(filer_window->collection, TRUE);
1520 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1521 "delete_event",
1522 GTK_SIGNAL_FUNC(filer_confirm_close),
1523 filer_window);
1525 gdk_window_get_size(GDK_ROOT_PARENT(), &swidth, &sheight);
1526 iwidth = filer_window->collection->item_width;
1527 iheight = filer_window->collection->item_height;
1530 int height = iheight + PANEL_BORDER;
1531 int y = panel_type == PANEL_TOP
1532 ? -PANEL_BORDER
1533 : sheight - height - PANEL_BORDER;
1535 gtk_widget_set_usize(collection, swidth, height);
1536 gtk_widget_set_uposition(win, 0, y);
1539 frame = gtk_frame_new(NULL);
1540 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1541 gtk_container_add(GTK_CONTAINER(frame), collection);
1542 gtk_container_add(GTK_CONTAINER(win), frame);
1544 gtk_widget_show_all(frame);
1545 gtk_widget_realize(win);
1546 if (override_redirect)
1547 gdk_window_set_override_redirect(win->window, TRUE);
1548 make_panel_window(win->window);
1550 else
1552 GtkWidget *vbox;
1553 int col_height = ROW_HEIGHT_LARGE * 3;
1555 gtk_signal_connect(GTK_OBJECT(collection),
1556 "key_press_event",
1557 GTK_SIGNAL_FUNC(key_press_event), filer_window);
1558 gtk_window_set_default_size(GTK_WINDOW(filer_window->window),
1559 filer_window->display_style == LARGE_ICONS ? 400 : 512,
1560 o_toolbar == TOOLBAR_NONE ? col_height:
1561 o_toolbar == TOOLBAR_NORMAL ? col_height + 24 :
1562 col_height + 38);
1564 hbox = gtk_hbox_new(FALSE, 0);
1565 gtk_container_add(GTK_CONTAINER(filer_window->window),
1566 hbox);
1568 vbox = gtk_vbox_new(FALSE, 0);
1569 gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
1571 if (o_toolbar != TOOLBAR_NONE)
1573 GtkWidget *toolbar;
1575 toolbar = create_toolbar(filer_window);
1576 gtk_box_pack_start(GTK_BOX(vbox), toolbar,
1577 FALSE, TRUE, 0);
1578 gtk_widget_show_all(toolbar);
1581 gtk_box_pack_start(GTK_BOX(vbox), collection, TRUE, TRUE, 0);
1583 filer_window->minibuffer = create_minibuffer(filer_window);
1584 gtk_box_pack_start(GTK_BOX(vbox), filer_window->minibuffer,
1585 FALSE, TRUE, 0);
1587 scrollbar = gtk_vscrollbar_new(COLLECTION(collection)->vadj);
1588 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, TRUE, 0);
1589 gtk_accel_group_attach(filer_keys,
1590 GTK_OBJECT(filer_window->window));
1591 gtk_window_set_focus(GTK_WINDOW(filer_window->window),
1592 collection);
1594 gtk_widget_show(hbox);
1595 gtk_widget_show(vbox);
1596 gtk_widget_show(scrollbar);
1597 gtk_widget_show(collection);
1600 number_of_windows++;
1601 gtk_widget_show(filer_window->window);
1602 attach(filer_window);
1604 all_filer_windows = g_list_prepend(all_filer_windows, filer_window);
1606 return filer_window;
1609 static gint clear_scanning_display(FilerWindow *filer_window)
1611 if (exists(filer_window))
1612 filer_set_title(filer_window);
1613 return FALSE;
1616 static void set_scanning_display(FilerWindow *filer_window, gboolean scanning)
1618 if (scanning == filer_window->scanning)
1619 return;
1620 filer_window->scanning = scanning;
1622 if (scanning)
1623 filer_set_title(filer_window);
1624 else
1625 gtk_timeout_add(300, (GtkFunction) clear_scanning_display,
1626 filer_window);
1629 static GtkWidget *create_toolbar(FilerWindow *filer_window)
1631 GtkWidget *frame, *box;
1633 if (o_toolbar == TOOLBAR_GNOME)
1635 frame = gtk_handle_box_new();
1636 box = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL,
1637 GTK_TOOLBAR_BOTH);
1638 gtk_container_set_border_width(GTK_CONTAINER(box), 2);
1639 gtk_toolbar_set_space_style(GTK_TOOLBAR(box),
1640 GTK_TOOLBAR_SPACE_LINE);
1641 gtk_toolbar_set_button_relief(GTK_TOOLBAR(box),
1642 GTK_RELIEF_NONE);
1644 else
1646 frame = gtk_frame_new(NULL);
1647 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1649 box = gtk_hbutton_box_new();
1650 gtk_button_box_set_child_size_default(16, 16);
1651 gtk_hbutton_box_set_spacing_default(2);
1652 gtk_button_box_set_layout(GTK_BUTTON_BOX(box),
1653 GTK_BUTTONBOX_START);
1656 gtk_container_add(GTK_CONTAINER(frame), box);
1658 add_button(box, TOOLBAR_UP_ICON,
1659 GTK_SIGNAL_FUNC(toolbar_up_clicked),
1660 filer_window,
1661 "Up", "Change to parent directory");
1662 add_button(box, TOOLBAR_HOME_ICON,
1663 GTK_SIGNAL_FUNC(toolbar_home_clicked),
1664 filer_window,
1665 "Home", "Change to home directory");
1666 add_button(box, TOOLBAR_REFRESH_ICON,
1667 GTK_SIGNAL_FUNC(toolbar_refresh_clicked),
1668 filer_window,
1669 "Rescan", "Rescan directory contents");
1671 return frame;
1674 static void add_button(GtkWidget *box, int pixmap,
1675 GtkSignalFunc cb, FilerWindow *filer_window,
1676 char *label, char *tip)
1678 GtkWidget *button, *icon;
1680 icon = gtk_pixmap_new(default_pixmap[pixmap].pixmap,
1681 default_pixmap[pixmap].mask);
1683 if (o_toolbar == TOOLBAR_GNOME)
1685 gtk_toolbar_append_element(GTK_TOOLBAR(box),
1686 GTK_TOOLBAR_CHILD_BUTTON,
1687 NULL,
1688 label,
1689 tip, NULL,
1690 icon,
1691 cb, filer_window);
1693 else
1695 button = gtk_button_new();
1696 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
1698 gtk_container_add(GTK_CONTAINER(button), icon);
1699 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1700 cb, filer_window);
1702 gtk_tooltips_set_tip(tooltips, button, tip, NULL);
1704 gtk_container_add(GTK_CONTAINER(box), button);
1708 /* Build up some option widgets to go in the options dialog, but don't
1709 * fill them in yet.
1711 static GtkWidget *create_options()
1713 GtkWidget *vbox, *menu, *hbox;
1715 vbox = gtk_vbox_new(FALSE, 0);
1716 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
1718 display_label = gtk_label_new("<>");
1719 gtk_label_set_line_wrap(GTK_LABEL(display_label), TRUE);
1720 gtk_box_pack_start(GTK_BOX(vbox), display_label, FALSE, TRUE, 0);
1722 toggle_new_window_on_1 =
1723 gtk_check_button_new_with_label("New window on button 1 "
1724 "(RISC OS style)");
1725 gtk_box_pack_start(GTK_BOX(vbox), toggle_new_window_on_1,
1726 FALSE, TRUE, 0);
1728 toggle_menu_on_2 =
1729 gtk_check_button_new_with_label("Menu on button 2 "
1730 "(RISC OS style)");
1731 gtk_box_pack_start(GTK_BOX(vbox), toggle_menu_on_2, FALSE, TRUE, 0);
1733 toggle_single_click =
1734 gtk_check_button_new_with_label("Single-click nagivation");
1735 gtk_box_pack_start(GTK_BOX(vbox), toggle_single_click, FALSE, TRUE, 0);
1737 toggle_unique_filer_windows =
1738 gtk_check_button_new_with_label("Unique windows");
1739 gtk_box_pack_start(GTK_BOX(vbox), toggle_unique_filer_windows, FALSE, TRUE, 0);
1741 hbox = gtk_hbox_new(FALSE, 4);
1742 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
1744 gtk_box_pack_start(GTK_BOX(hbox),
1745 gtk_label_new("Toolbar type for new windows"),
1746 FALSE, TRUE, 0);
1747 menu_toolbar = gtk_option_menu_new();
1748 menu = gtk_menu_new();
1749 gtk_menu_append(GTK_MENU(menu), gtk_menu_item_new_with_label("None"));
1750 gtk_menu_append(GTK_MENU(menu), gtk_menu_item_new_with_label("Normal"));
1751 gtk_menu_append(GTK_MENU(menu), gtk_menu_item_new_with_label("GNOME"));
1752 gtk_option_menu_set_menu(GTK_OPTION_MENU(menu_toolbar), menu);
1753 gtk_box_pack_start(GTK_BOX(hbox), menu_toolbar, TRUE, TRUE, 0);
1755 return vbox;
1758 static void update_options_label(void)
1760 guchar *str;
1762 str = g_strdup_printf("The last used display style (%s) and sort "
1763 "function (Sort By %s) will be saved if you click on "
1764 "Save.", style_to_name(), sort_fn_to_name());
1765 gtk_label_set_text(GTK_LABEL(display_label), str);
1766 g_free(str);
1769 /* Reflect current state by changing the widgets in the options box */
1770 static void update_options()
1772 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_new_window_on_1),
1773 o_new_window_on_1);
1774 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_menu_on_2),
1775 collection_menu_button == 2 ? 1 : 0);
1776 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_single_click),
1777 o_single_click);
1778 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_unique_filer_windows),
1779 o_unique_filer_windows);
1780 gtk_option_menu_set_history(GTK_OPTION_MENU(menu_toolbar), o_toolbar);
1782 update_options_label();
1785 /* Set current values by reading the states of the widgets in the options box */
1786 static void set_options()
1788 GtkWidget *item, *menu;
1789 GList *list;
1791 o_new_window_on_1 = gtk_toggle_button_get_active(
1792 GTK_TOGGLE_BUTTON(toggle_new_window_on_1));
1794 collection_menu_button = gtk_toggle_button_get_active(
1795 GTK_TOGGLE_BUTTON(toggle_menu_on_2)) ? 2 : 3;
1797 o_single_click = gtk_toggle_button_get_active(
1798 GTK_TOGGLE_BUTTON(toggle_single_click));
1800 o_unique_filer_windows = gtk_toggle_button_get_active(
1801 GTK_TOGGLE_BUTTON(toggle_unique_filer_windows));
1803 collection_single_click = o_single_click ? TRUE : FALSE;
1805 menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(menu_toolbar));
1806 item = gtk_menu_get_active(GTK_MENU(menu));
1807 list = gtk_container_children(GTK_CONTAINER(menu));
1808 o_toolbar = (ToolbarType) g_list_index(list, item);
1809 g_list_free(list);
1813 static guchar *style_to_name(void)
1815 return last_display_style == LARGE_ICONS ? "Large Icons" :
1816 last_display_style == SMALL_ICONS ? "Small Icons" :
1817 "Full Info";
1820 static guchar *sort_fn_to_name(void)
1822 return last_sort_fn == sort_by_name ? "Name" :
1823 last_sort_fn == sort_by_type ? "Type" :
1824 last_sort_fn == sort_by_date ? "Date" :
1825 "Size";
1828 static void save_options()
1830 option_write("filer_new_window_on_1", o_new_window_on_1 ? "1" : "0");
1831 option_write("filer_menu_on_2",
1832 collection_menu_button == 2 ? "1" : "0");
1833 option_write("filer_single_click", o_single_click ? "1" : "0");
1834 option_write("filer_unique_windows", o_unique_filer_windows ? "1" : "0");
1835 option_write("filer_display_style", style_to_name());
1836 option_write("filer_sort_by", sort_fn_to_name());
1837 option_write("filer_toolbar", o_toolbar == TOOLBAR_NONE ? "None" :
1838 o_toolbar == TOOLBAR_NORMAL ? "Normal" :
1839 o_toolbar == TOOLBAR_GNOME ? "GNOME" :
1840 "Unknown");
1843 static char *filer_new_window_on_1(char *data)
1845 o_new_window_on_1 = atoi(data) != 0;
1846 return NULL;
1849 static char *filer_menu_on_2(char *data)
1851 collection_menu_button = atoi(data) != 0 ? 2 : 3;
1852 return NULL;
1855 static char *filer_single_click(char *data)
1857 o_single_click = atoi(data) != 0;
1858 collection_single_click = o_single_click ? TRUE : FALSE;
1859 return NULL;
1862 static char *filer_unique_windows(char *data)
1864 o_unique_filer_windows = atoi(data) != 0;
1865 return NULL;
1868 static char *filer_display_style(char *data)
1870 if (g_strcasecmp(data, "Large Icons") == 0)
1871 last_display_style = LARGE_ICONS;
1872 else if (g_strcasecmp(data, "Small Icons") == 0)
1873 last_display_style = SMALL_ICONS;
1874 else if (g_strcasecmp(data, "Full Info") == 0)
1875 last_display_style = FULL_INFO;
1876 else
1877 return "Unknown display style";
1879 return NULL;
1882 static char *filer_sort_by(char *data)
1884 if (g_strcasecmp(data, "Name") == 0)
1885 last_sort_fn = sort_by_name;
1886 else if (g_strcasecmp(data, "Type") == 0)
1887 last_sort_fn = sort_by_type;
1888 else if (g_strcasecmp(data, "Date") == 0)
1889 last_sort_fn = sort_by_date;
1890 else if (g_strcasecmp(data, "Size") == 0)
1891 last_sort_fn = sort_by_size;
1892 else
1893 return "Unknown sort type";
1895 return NULL;
1898 static char *filer_toolbar(char *data)
1900 if (g_strcasecmp(data, "None") == 0)
1901 o_toolbar = TOOLBAR_NONE;
1902 else if (g_strcasecmp(data, "Normal") == 0)
1903 o_toolbar = TOOLBAR_NORMAL;
1904 else if (g_strcasecmp(data, "GNOME") == 0)
1905 o_toolbar = TOOLBAR_GNOME;
1906 else
1907 return "Unknown toolbar type";
1909 return NULL;
1912 /* Note that filer_window may not exist after this call. */
1913 void update_dir(FilerWindow *filer_window, gboolean warning)
1915 if (may_rescan(filer_window, warning))
1916 dir_update(filer_window->directory, filer_window->path);
1919 void filer_set_hidden(FilerWindow *filer_window, gboolean hidden)
1921 Directory *dir = filer_window->directory;
1923 if (filer_window->show_hidden == hidden)
1924 return;
1926 filer_window->show_hidden = hidden;
1927 last_show_hidden = hidden;
1929 g_fscache_data_ref(dir_cache, dir);
1930 detach(filer_window);
1931 filer_window->directory = dir;
1932 attach(filer_window);
1935 /* Refresh the various caches even if we don't think we need to */
1936 void full_refresh(void)
1938 mount_update(TRUE);
1941 /* See whether a filer window with a given path already exists */
1942 static FilerWindow *find_filer_window(char *path)
1944 GList *next = all_filer_windows;
1946 while (next)
1948 FilerWindow *filer_window = (FilerWindow *) next->data;
1950 if (filer_window->panel_type == PANEL_NO
1951 && strcmp(path, filer_window->path) == 0)
1953 return filer_window;
1956 next = next->next;
1959 return NULL;
1962 /* This path has been mounted/umounted - update all dirs */
1963 void filer_check_mounted(char *path)
1965 GList *next = all_filer_windows;
1966 int len;
1968 len = strlen(path);
1970 while (next)
1972 FilerWindow *filer_window = (FilerWindow *) next->data;
1974 next = next->next;
1976 if (strncmp(path, filer_window->path, len) == 0)
1978 char s = filer_window->path[len];
1980 if (s == '/' || s == '\0')
1981 update_dir(filer_window, FALSE);
1986 /* Like minibuffer_show(), except that:
1987 * - It returns FALSE (to be used from an idle callback)
1988 * - It checks that the filer window still exists.
1990 static gboolean minibuffer_show_cb(FilerWindow *filer_window)
1992 if (exists(filer_window))
1993 minibuffer_show(filer_window);
1994 return FALSE;
1997 static gboolean exists(FilerWindow *filer_window)
1999 GList *next;
2001 for (next = all_filer_windows; next; next = next->next)
2003 FilerWindow *fw = (FilerWindow *) next->data;
2005 if (fw == filer_window)
2006 return TRUE;
2009 return FALSE;
2012 /* Highlight (wink or cursor) this item in the filer window. If the item
2013 * isn't already there but we're scanning then highlight it if it
2014 * appears later.
2016 static void set_autoselect(FilerWindow *filer_window, guchar *leaf)
2018 Collection *col = filer_window->collection;
2019 int i;
2021 g_free(filer_window->auto_select);
2022 filer_window->auto_select = NULL;
2024 for (i = 0; i < col->number_of_items; i++)
2026 DirItem *item = (DirItem *) col->items[i].data;
2028 if (strcmp(item->leafname, leaf) == 0)
2030 if (col->cursor_item != -1)
2031 collection_set_cursor_item(col, i);
2032 else
2033 collection_wink_item(col, i);
2034 return;
2038 filer_window->auto_select = g_strdup(leaf);
2041 static void filer_set_title(FilerWindow *filer_window)
2043 if (filer_window->scanning)
2045 guchar *title;
2047 title = g_strdup_printf("%s (Scanning)", filer_window->path);
2048 gtk_window_set_title(GTK_WINDOW(filer_window->window),
2049 title);
2050 g_free(title);
2052 else
2053 gtk_window_set_title(GTK_WINDOW(filer_window->window),
2054 filer_window->path);