r216: Check for copying a directory onto itself, as well as into itself.
[rox-filer.git] / ROX-Filer / src / filer.c
blobfbda708e9e778181e6c50c4a0473a9a4286bc8e3
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 32
56 #define ROW_HEIGHT_FULL_INFO 44
57 #define SMALL_ICON_HEIGHT 20
58 #define SMALL_ICON_WIDTH 48
59 #define MAX_ICON_HEIGHT 42
60 #define MAX_ICON_WIDTH 48
61 #define PANEL_BORDER 2
62 #define MIN_ITEM_WIDTH 64
64 extern int collection_menu_button;
65 extern gboolean collection_single_click;
67 FilerWindow *window_with_focus = NULL;
68 GList *all_filer_windows = NULL;
70 static DisplayStyle last_display_style = LARGE_ICONS;
71 static gboolean last_show_hidden = FALSE;
72 static int (*last_sort_fn)(const void *a, const void *b) = sort_by_type;
74 static FilerWindow *window_with_selection = NULL;
76 /* Options bits */
77 static guchar *style_to_name(void);
78 static guchar *sort_fn_to_name(void);
79 static void update_options_label(void);
81 static GtkWidget *create_options();
82 static void update_options();
83 static void set_options();
84 static void save_options();
85 static char *filer_single_click(char *data);
86 static char *filer_unique_windows(char *data);
87 static char *filer_menu_on_2(char *data);
88 static char *filer_new_window_on_1(char *data);
89 static char *filer_toolbar(char *data);
90 static char *filer_display_style(char *data);
91 static char *filer_sort_by(char *data);
93 static OptionsSection options =
95 "Filer window options",
96 create_options,
97 update_options,
98 set_options,
99 save_options
102 /* The values correspond to the menu indexes in the option widget */
103 typedef enum {
104 TOOLBAR_NONE = 0,
105 TOOLBAR_NORMAL = 1,
106 TOOLBAR_GNOME = 2,
107 } ToolbarType;
108 static ToolbarType o_toolbar = TOOLBAR_NORMAL;
109 static GtkWidget *menu_toolbar;
111 static GtkWidget *display_label;
113 static gboolean o_single_click = FALSE;
114 static GtkWidget *toggle_single_click;
115 static gboolean o_new_window_on_1 = FALSE; /* Button 1 => New window */
116 static GtkWidget *toggle_new_window_on_1;
117 static GtkWidget *toggle_menu_on_2;
118 static GtkWidget *toggle_unique_filer_windows;
119 gboolean o_unique_filer_windows = FALSE;
121 /* Static prototypes */
122 static void attach(FilerWindow *filer_window);
123 static void detach(FilerWindow *filer_window);
124 static void filer_window_destroyed(GtkWidget *widget,
125 FilerWindow *filer_window);
126 static void show_menu(Collection *collection, GdkEventButton *event,
127 int number_selected, gpointer user_data);
128 static gint focus_in(GtkWidget *widget,
129 GdkEventFocus *event,
130 FilerWindow *filer_window);
131 static gint focus_out(GtkWidget *widget,
132 GdkEventFocus *event,
133 FilerWindow *filer_window);
134 static void add_item(FilerWindow *filer_window, DirItem *item);
135 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window);
136 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window);
137 static void add_button(GtkWidget *box, int pixmap,
138 GtkSignalFunc cb, FilerWindow *filer_window,
139 char *label, char *tip);
140 static GtkWidget *create_toolbar(FilerWindow *filer_window);
141 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
142 FilerWindow *window);
143 static int calc_width(FilerWindow *filer_window, DirItem *item);
144 static void draw_large_icon(GtkWidget *widget,
145 GdkRectangle *area,
146 DirItem *item,
147 gboolean selected);
148 static void draw_string(GtkWidget *widget,
149 GdkFont *font,
150 char *string,
151 int x,
152 int y,
153 int width,
154 gboolean selected);
155 static void draw_item_large(GtkWidget *widget,
156 CollectionItem *item,
157 GdkRectangle *area);
158 static void draw_item_small(GtkWidget *widget,
159 CollectionItem *item,
160 GdkRectangle *area);
161 static void draw_item_full_info(GtkWidget *widget,
162 CollectionItem *colitem,
163 GdkRectangle *area);
164 static gboolean test_point_large(Collection *collection,
165 int point_x, int point_y,
166 CollectionItem *item,
167 int width, int height);
168 static gboolean test_point_small(Collection *collection,
169 int point_x, int point_y,
170 CollectionItem *item,
171 int width, int height);
172 static gboolean test_point_full_info(Collection *collection,
173 int point_x, int point_y,
174 CollectionItem *item,
175 int width, int height);
176 static void update_display(Directory *dir,
177 DirAction action,
178 GPtrArray *items,
179 FilerWindow *filer_window);
180 static void set_scanning_display(FilerWindow *filer_window, gboolean scanning);
181 static void shrink_width(FilerWindow *filer_window);
182 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning);
183 static void open_item(Collection *collection,
184 gpointer item_data, int item_number,
185 gpointer user_data);
186 static gboolean minibuffer_show_cb(FilerWindow *filer_window);
187 static void set_autoselect(FilerWindow *filer_window, guchar *leaf);
188 static FilerWindow *find_filer_window(char *path, FilerWindow *diff);
189 static void filer_set_title(FilerWindow *filer_window);
190 static gboolean exists(FilerWindow *filer_window);
192 static GdkAtom xa_string;
193 enum
195 TARGET_STRING,
196 TARGET_URI_LIST,
199 static GdkCursor *busy_cursor = NULL;
200 static GtkTooltips *tooltips = NULL;
202 void filer_init()
204 xa_string = gdk_atom_intern("STRING", FALSE);
206 options_sections = g_slist_prepend(options_sections, &options);
207 option_register("filer_new_window_on_1", filer_new_window_on_1);
208 option_register("filer_menu_on_2", filer_menu_on_2);
209 option_register("filer_single_click", filer_single_click);
210 option_register("filer_unique_windows", filer_unique_windows);
211 option_register("filer_toolbar", filer_toolbar);
212 option_register("filer_display_style", filer_display_style);
213 option_register("filer_sort_by", filer_sort_by);
215 busy_cursor = gdk_cursor_new(GDK_WATCH);
217 tooltips = gtk_tooltips_new();
220 static gboolean if_deleted(gpointer item, gpointer removed)
222 int i = ((GPtrArray *) removed)->len;
223 DirItem **r = (DirItem **) ((GPtrArray *) removed)->pdata;
224 char *leafname = ((DirItem *) item)->leafname;
226 while (i--)
228 if (strcmp(leafname, r[i]->leafname) == 0)
229 return TRUE;
232 return FALSE;
235 static void update_item(FilerWindow *filer_window, DirItem *item)
237 int i;
238 char *leafname = item->leafname;
240 if (leafname[0] == '.')
242 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
243 || (leafname[1] == '.' && leafname[2] == '\0'))
244 return;
247 i = collection_find_item(filer_window->collection, item, dir_item_cmp);
249 if (i >= 0)
250 collection_draw_item(filer_window->collection, i, TRUE);
251 else
252 g_warning("Failed to find '%s'\n", item->leafname);
255 static void update_display(Directory *dir,
256 DirAction action,
257 GPtrArray *items,
258 FilerWindow *filer_window)
260 int i;
261 int cursor = filer_window->collection->cursor_item;
262 char *as;
263 Collection *collection = filer_window->collection;
265 switch (action)
267 case DIR_ADD:
268 as = filer_window->auto_select;
270 for (i = 0; i < items->len; i++)
272 DirItem *item = (DirItem *) items->pdata[i];
274 add_item(filer_window, item);
276 if (cursor != -1 || !as)
277 continue;
279 if (strcmp(as, item->leafname) != 0)
280 continue;
282 cursor = collection->number_of_items - 1;
283 if (filer_window->had_cursor)
285 collection_set_cursor_item(collection,
286 cursor);
287 filer_window->mini_cursor_base = cursor;
289 else
290 collection_wink_item(collection,
291 cursor);
294 collection_qsort(filer_window->collection,
295 filer_window->sort_fn);
296 break;
297 case DIR_REMOVE:
298 collection_delete_if(filer_window->collection,
299 if_deleted,
300 items);
301 break;
302 case DIR_START_SCAN:
303 set_scanning_display(filer_window, TRUE);
304 break;
305 case DIR_END_SCAN:
306 if (filer_window->window->window)
307 gdk_window_set_cursor(
308 filer_window->window->window,
309 NULL);
310 shrink_width(filer_window);
311 if (filer_window->had_cursor &&
312 collection->cursor_item == -1)
314 collection_set_cursor_item(collection, 0);
315 filer_window->had_cursor = FALSE;
317 set_scanning_display(filer_window, FALSE);
318 break;
319 case DIR_UPDATE:
320 for (i = 0; i < items->len; i++)
322 DirItem *item = (DirItem *) items->pdata[i];
324 update_item(filer_window, item);
326 collection_qsort(filer_window->collection,
327 filer_window->sort_fn);
328 break;
332 static void attach(FilerWindow *filer_window)
334 gdk_window_set_cursor(filer_window->window->window, busy_cursor);
335 collection_clear(filer_window->collection);
336 filer_window->scanning = TRUE;
337 dir_attach(filer_window->directory, (DirCallback) update_display,
338 filer_window);
339 filer_set_title(filer_window);
342 static void detach(FilerWindow *filer_window)
344 g_return_if_fail(filer_window->directory != NULL);
346 dir_detach(filer_window->directory,
347 (DirCallback) update_display, filer_window);
348 g_fscache_data_unref(dir_cache, filer_window->directory);
349 filer_window->directory = NULL;
352 static void filer_window_destroyed(GtkWidget *widget,
353 FilerWindow *filer_window)
355 all_filer_windows = g_list_remove(all_filer_windows, filer_window);
357 if (window_with_selection == filer_window)
358 window_with_selection = NULL;
359 if (window_with_focus == filer_window)
360 window_with_focus = NULL;
362 if (filer_window->directory)
363 detach(filer_window);
365 g_free(filer_window->auto_select);
366 g_free(filer_window->path);
367 g_free(filer_window);
369 if (--number_of_windows < 1)
370 gtk_main_quit();
373 static int calc_width(FilerWindow *filer_window, DirItem *item)
375 int pix_width = item->image->width;
377 switch (filer_window->display_style)
379 case FULL_INFO:
380 return MAX_ICON_WIDTH + 12 +
381 MAX(item->details_width, item->name_width);
382 case SMALL_ICONS:
383 return SMALL_ICON_WIDTH + 12 + item->name_width;
384 default:
385 return MAX(pix_width, item->name_width) + 4;
389 /* Add a single object to a directory display */
390 static void add_item(FilerWindow *filer_window, DirItem *item)
392 char *leafname = item->leafname;
393 int item_width;
395 if (leafname[0] == '.')
397 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
398 || (leafname[1] == '.' && leafname[2] == '\0'))
399 return;
402 item_width = calc_width(filer_window, item);
403 if (item_width > filer_window->collection->item_width)
404 collection_set_item_size(filer_window->collection,
405 item_width,
406 filer_window->collection->item_height);
407 collection_insert(filer_window->collection, item);
410 /* Is a point inside an item? */
411 static gboolean test_point_large(Collection *collection,
412 int point_x, int point_y,
413 CollectionItem *colitem,
414 int width, int height)
416 DirItem *item = (DirItem *) colitem->data;
417 int text_height = item_font->ascent + item_font->descent;
418 MaskedPixmap *image = item->image;
419 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
420 int image_width = (image->width >> 1) + 2;
421 int text_width = (item->name_width >> 1) + 2;
422 int x_limit;
424 if (point_y < image_y)
425 return FALSE; /* Too high up (don't worry about too low) */
427 if (point_y <= image_y + image->height + 2)
428 x_limit = image_width;
429 else if (point_y > height - text_height - 2)
430 x_limit = text_width;
431 else
432 x_limit = MIN(image_width, text_width);
434 return ABS(point_x - (width >> 1)) < x_limit;
437 static gboolean test_point_full_info(Collection *collection,
438 int point_x, int point_y,
439 CollectionItem *colitem,
440 int width, int height)
442 DirItem *item = (DirItem *) colitem->data;
443 MaskedPixmap *image = item->image;
444 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
445 int low_top = height
446 - fixed_font->descent - 2 - fixed_font->ascent;
448 if (point_x < image->width + 2)
449 return point_x > 2 && point_y > image_y;
451 point_x -= MAX_ICON_WIDTH + 8;
453 if (point_y >= low_top)
454 return point_x < item->details_width;
455 if (point_y >= low_top - item_font->ascent - item_font->descent)
456 return point_x < item->name_width;
457 return FALSE;
460 static gboolean test_point_small(Collection *collection,
461 int point_x, int point_y,
462 CollectionItem *colitem,
463 int width, int height)
465 DirItem *item = (DirItem *) colitem->data;
466 MaskedPixmap *image = item->image;
467 int image_y = MAX(0, SMALL_ICON_HEIGHT - image->height);
468 int low_top = height
469 - fixed_font->descent - 2 - item_font->ascent;
470 int iwidth = MIN(SMALL_ICON_WIDTH, image->width);
472 if (point_x < iwidth + 2)
473 return point_x > 2 && point_y > image_y;
475 point_x -= SMALL_ICON_WIDTH + 4;
477 if (point_y >= low_top)
478 return point_x < item->name_width;
479 return FALSE;
482 static void draw_small_icon(GtkWidget *widget,
483 GdkRectangle *area,
484 DirItem *item,
485 gboolean selected)
487 MaskedPixmap *image = item->image;
488 int width = MIN(image->width, SMALL_ICON_WIDTH);
489 int height = MIN(image->height, SMALL_ICON_HEIGHT);
490 int image_x = area->x + ((area->width - width) >> 1);
491 int image_y;
492 GdkGC *gc = selected ? widget->style->white_gc
493 : widget->style->black_gc;
494 if (!item->image)
495 return;
497 gdk_gc_set_clip_mask(gc, item->image->mask);
499 image_y = MAX(0, SMALL_ICON_HEIGHT - image->height);
500 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
501 gdk_draw_pixmap(widget->window, gc,
502 item->image->pixmap,
503 0, 0, /* Source x,y */
504 image_x, area->y + image_y, /* Dest x,y */
505 width, height);
507 if (selected)
509 gdk_gc_set_function(gc, GDK_INVERT);
510 gdk_draw_rectangle(widget->window,
512 TRUE, image_x, area->y + image_y,
513 width, height);
514 gdk_gc_set_function(gc, GDK_COPY);
517 if (item->flags & ITEM_FLAG_SYMLINK)
519 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
520 gdk_gc_set_clip_mask(gc,
521 default_pixmap[TYPE_SYMLINK].mask);
522 gdk_draw_pixmap(widget->window, gc,
523 default_pixmap[TYPE_SYMLINK].pixmap,
524 0, 0, /* Source x,y */
525 image_x, area->y + 8, /* Dest x,y */
526 -1, -1);
528 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
530 int type = item->flags & ITEM_FLAG_MOUNTED
531 ? TYPE_MOUNTED
532 : TYPE_UNMOUNTED;
533 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
534 gdk_gc_set_clip_mask(gc,
535 default_pixmap[type].mask);
536 gdk_draw_pixmap(widget->window, gc,
537 default_pixmap[type].pixmap,
538 0, 0, /* Source x,y */
539 image_x, area->y + 8, /* Dest x,y */
540 -1, -1);
543 gdk_gc_set_clip_mask(gc, NULL);
544 gdk_gc_set_clip_origin(gc, 0, 0);
547 static void draw_large_icon(GtkWidget *widget,
548 GdkRectangle *area,
549 DirItem *item,
550 gboolean selected)
552 MaskedPixmap *image = item->image;
553 int width = MIN(image->width, MAX_ICON_WIDTH);
554 int height = MIN(image->height, MAX_ICON_WIDTH);
555 int image_x = area->x + ((area->width - width) >> 1);
556 int image_y;
557 GdkGC *gc = selected ? widget->style->white_gc
558 : widget->style->black_gc;
560 gdk_gc_set_clip_mask(gc, item->image->mask);
562 image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
563 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
564 gdk_draw_pixmap(widget->window, gc,
565 item->image->pixmap,
566 0, 0, /* Source x,y */
567 image_x, area->y + image_y, /* Dest x,y */
568 width, height);
570 if (selected)
572 gdk_gc_set_function(gc, GDK_INVERT);
573 gdk_draw_rectangle(widget->window,
575 TRUE, image_x, area->y + image_y,
576 width, height);
577 gdk_gc_set_function(gc, GDK_COPY);
580 if (item->flags & ITEM_FLAG_SYMLINK)
582 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
583 gdk_gc_set_clip_mask(gc,
584 default_pixmap[TYPE_SYMLINK].mask);
585 gdk_draw_pixmap(widget->window, gc,
586 default_pixmap[TYPE_SYMLINK].pixmap,
587 0, 0, /* Source x,y */
588 image_x, area->y + 8, /* Dest x,y */
589 -1, -1);
591 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
593 int type = item->flags & ITEM_FLAG_MOUNTED
594 ? TYPE_MOUNTED
595 : TYPE_UNMOUNTED;
596 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
597 gdk_gc_set_clip_mask(gc,
598 default_pixmap[type].mask);
599 gdk_draw_pixmap(widget->window, gc,
600 default_pixmap[type].pixmap,
601 0, 0, /* Source x,y */
602 image_x, area->y + 8, /* Dest x,y */
603 -1, -1);
606 gdk_gc_set_clip_mask(gc, NULL);
607 gdk_gc_set_clip_origin(gc, 0, 0);
610 static void draw_string(GtkWidget *widget,
611 GdkFont *font,
612 char *string,
613 int x,
614 int y,
615 int width,
616 gboolean selected)
618 int text_height = font->ascent + font->descent;
620 if (selected)
621 gtk_paint_flat_box(widget->style, widget->window,
622 GTK_STATE_SELECTED, GTK_SHADOW_NONE,
623 NULL, widget, "text",
624 x, y - font->ascent,
625 width,
626 text_height);
628 gdk_draw_text(widget->window,
629 font,
630 selected ? widget->style->white_gc
631 : widget->style->black_gc,
632 x, y,
633 string, strlen(string));
636 /* Return a string (valid until next call) giving details
637 * of this item.
639 char *details(DirItem *item)
641 mode_t m = item->mode;
642 static GString *buf = NULL;
644 if (!buf)
645 buf = g_string_new(NULL);
647 g_string_sprintf(buf, "%s %s %-8.8s %-8.8s %s %s",
648 item->flags & ITEM_FLAG_APPDIR? "App " :
649 S_ISDIR(m) ? "Dir " :
650 S_ISCHR(m) ? "Char" :
651 S_ISBLK(m) ? "Blck" :
652 S_ISLNK(m) ? "Link" :
653 S_ISSOCK(m) ? "Sock" :
654 S_ISFIFO(m) ? "Pipe" : "File",
655 pretty_permissions(m),
656 user_name(item->uid),
657 group_name(item->gid),
658 format_size_aligned(item->size),
659 pretty_time(&item->mtime));
660 return buf->str;
663 static void draw_item_full_info(GtkWidget *widget,
664 CollectionItem *colitem,
665 GdkRectangle *area)
667 DirItem *item = (DirItem *) colitem->data;
668 MaskedPixmap *image = item->image;
669 int text_x = area->x + MAX_ICON_WIDTH + 8;
670 int low_text_y = area->y + area->height - fixed_font->descent - 2;
671 gboolean selected = colitem->selected;
672 GdkRectangle pic_area;
674 pic_area.x = area->x;
675 pic_area.y = area->y;
676 pic_area.width = image->width + 8;
677 pic_area.height = area->height;
679 draw_large_icon(widget, &pic_area, item, selected);
681 draw_string(widget,
682 item_font,
683 item->leafname,
684 text_x,
685 low_text_y - item_font->descent - fixed_font->ascent,
686 item->name_width,
687 selected);
688 draw_string(widget,
689 fixed_font,
690 details(item),
691 text_x, low_text_y,
692 item->details_width,
693 selected);
694 gdk_draw_rectangle(widget->window,
695 selected ? widget->style->white_gc
696 : widget->style->black_gc,
697 TRUE,
698 text_x - 1 + fixed_width *
699 (5 + 4 * applicable(item->uid, item->gid)),
700 low_text_y + fixed_font->descent - 1,
701 fixed_width * 3 + 1, 1);
704 static void draw_item_small(GtkWidget *widget,
705 CollectionItem *colitem,
706 GdkRectangle *area)
708 DirItem *item = (DirItem *) colitem->data;
709 int text_x = area->x + SMALL_ICON_WIDTH + 4;
710 int low_text_y = area->y + area->height - item_font->descent - 2;
711 gboolean selected = colitem->selected;
712 GdkRectangle pic_area;
714 pic_area.x = area->x;
715 pic_area.y = area->y;
716 pic_area.width = SMALL_ICON_WIDTH;
717 pic_area.height = SMALL_ICON_HEIGHT;
719 draw_small_icon(widget, &pic_area, item, selected);
721 draw_string(widget,
722 item_font,
723 item->leafname,
724 text_x,
725 low_text_y,
726 item->name_width,
727 selected);
730 static void draw_item_large(GtkWidget *widget,
731 CollectionItem *colitem,
732 GdkRectangle *area)
734 DirItem *item = (DirItem *) colitem->data;
735 int text_x = area->x + ((area->width - item->name_width) >> 1);
736 int text_y = area->y + area->height - item_font->descent - 2;
737 gboolean selected = colitem->selected;
739 draw_large_icon(widget, area, item, selected);
741 draw_string(widget,
742 item_font,
743 item->leafname,
744 text_x, text_y, item->name_width,
745 selected);
748 static void show_menu(Collection *collection, GdkEventButton *event,
749 int item, gpointer user_data)
751 show_filer_menu((FilerWindow *) user_data, event, item);
754 /* Returns TRUE iff the directory still exists. */
755 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning)
757 Directory *dir;
759 g_return_val_if_fail(filer_window != NULL, FALSE);
761 /* We do a fresh lookup (rather than update) because the inode may
762 * have changed.
764 dir = g_fscache_lookup(dir_cache, filer_window->path);
765 if (!dir)
767 if (warning)
768 delayed_error("ROX-Filer", "Directory missing/deleted");
769 gtk_widget_destroy(filer_window->window);
770 return FALSE;
772 if (dir == filer_window->directory)
773 g_fscache_data_unref(dir_cache, dir);
774 else
776 detach(filer_window);
777 filer_window->directory = dir;
778 attach(filer_window);
781 return TRUE;
784 /* Another app has grabbed the selection */
785 static gint collection_lose_selection(GtkWidget *widget,
786 GdkEventSelection *event)
788 if (window_with_selection &&
789 window_with_selection->collection == COLLECTION(widget))
791 FilerWindow *filer_window = window_with_selection;
792 window_with_selection = NULL;
793 collection_clear_selection(filer_window->collection);
796 return TRUE;
799 /* Someone wants us to send them the selection */
800 static void selection_get(GtkWidget *widget,
801 GtkSelectionData *selection_data,
802 guint info,
803 guint time,
804 gpointer data)
806 GString *reply, *header;
807 FilerWindow *filer_window;
808 int i;
809 Collection *collection;
811 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
813 reply = g_string_new(NULL);
814 header = g_string_new(NULL);
816 switch (info)
818 case TARGET_STRING:
819 g_string_sprintf(header, " %s",
820 make_path(filer_window->path, "")->str);
821 break;
822 case TARGET_URI_LIST:
823 g_string_sprintf(header, " file://%s%s",
824 our_host_name(),
825 make_path(filer_window->path, "")->str);
826 break;
829 collection = filer_window->collection;
830 for (i = 0; i < collection->number_of_items; i++)
832 if (collection->items[i].selected)
834 DirItem *item =
835 (DirItem *) collection->items[i].data;
837 g_string_append(reply, header->str);
838 g_string_append(reply, item->leafname);
841 /* This works, but I don't think I like it... */
842 /* g_string_append_c(reply, ' '); */
844 gtk_selection_data_set(selection_data, xa_string,
845 8, reply->str + 1, reply->len - 1);
846 g_string_free(reply, TRUE);
847 g_string_free(header, TRUE);
850 /* No items are now selected. This might be because another app claimed
851 * the selection or because the user unselected all the items.
853 static void lose_selection(Collection *collection,
854 guint time,
855 gpointer user_data)
857 FilerWindow *filer_window = (FilerWindow *) user_data;
859 if (window_with_selection == filer_window)
861 window_with_selection = NULL;
862 gtk_selection_owner_set(NULL,
863 GDK_SELECTION_PRIMARY,
864 time);
868 static void gain_selection(Collection *collection,
869 guint time,
870 gpointer user_data)
872 FilerWindow *filer_window = (FilerWindow *) user_data;
874 if (gtk_selection_owner_set(GTK_WIDGET(collection),
875 GDK_SELECTION_PRIMARY,
876 time))
878 window_with_selection = filer_window;
880 else
881 collection_clear_selection(filer_window->collection);
884 int sort_by_name(const void *item1, const void *item2)
886 return strcmp((*((DirItem **)item1))->leafname,
887 (*((DirItem **)item2))->leafname);
890 int sort_by_type(const void *item1, const void *item2)
892 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
893 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
894 MIME_type *m1, *m2;
896 int diff = i1->base_type - i2->base_type;
898 if (!diff)
899 diff = (i1->flags & ITEM_FLAG_APPDIR)
900 - (i2->flags & ITEM_FLAG_APPDIR);
901 if (diff)
902 return diff > 0 ? 1 : -1;
904 m1 = i1->mime_type;
905 m2 = i2->mime_type;
907 if (m1 && m2)
909 diff = strcmp(m1->media_type, m2->media_type);
910 if (!diff)
911 diff = strcmp(m1->subtype, m2->subtype);
913 else if (m1 || m2)
914 diff = m1 ? 1 : -1;
915 else
916 diff = 0;
918 if (diff)
919 return diff > 0 ? 1 : -1;
921 return sort_by_name(item1, item2);
924 int sort_by_date(const void *item1, const void *item2)
926 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
927 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
929 return i1->mtime > i2->mtime ? -1 :
930 i1->mtime < i2->mtime ? 1 :
931 sort_by_name(item1, item2);
934 int sort_by_size(const void *item1, const void *item2)
936 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
937 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
939 return i1->size > i2->size ? -1 :
940 i1->size < i2->size ? 1 :
941 sort_by_name(item1, item2);
944 static void open_item(Collection *collection,
945 gpointer item_data, int item_number,
946 gpointer user_data)
948 FilerWindow *filer_window = (FilerWindow *) user_data;
949 GdkEvent *event;
950 GdkEventButton *bevent;
951 GdkEventKey *kevent;
952 OpenFlags flags = 0;
954 event = (GdkEvent *) gtk_get_current_event();
956 bevent = (GdkEventButton *) event;
957 kevent = (GdkEventKey *) event;
959 switch (event->type)
961 case GDK_2BUTTON_PRESS:
962 case GDK_BUTTON_PRESS:
963 case GDK_BUTTON_RELEASE:
964 if (bevent->state & GDK_SHIFT_MASK)
965 flags |= OPEN_SHIFT;
967 if (o_new_window_on_1 ^ (bevent->button == 1))
968 flags |= OPEN_SAME_WINDOW;
970 if (bevent->button != 1)
971 flags |= OPEN_CLOSE_WINDOW;
973 if (o_single_click == FALSE &&
974 (bevent->state & GDK_CONTROL_MASK) != 0)
975 flags ^= OPEN_SAME_WINDOW | OPEN_CLOSE_WINDOW;
976 break;
977 case GDK_KEY_PRESS:
978 flags |= OPEN_SAME_WINDOW;
979 if (kevent->state & GDK_SHIFT_MASK)
980 flags |= OPEN_SHIFT;
981 break;
982 default:
983 break;
986 filer_openitem(filer_window, item_number, flags);
989 /* Return the full path to the directory containing object 'path'.
990 * Relative paths are resolved from the filerwindow's path.
992 static void follow_symlink(FilerWindow *filer_window, char *path,
993 gboolean same_window)
995 char *real, *slash;
996 char *new_dir;
998 if (path[0] != '/')
999 path = make_path(filer_window->path, path)->str;
1001 real = pathdup(path);
1002 slash = strrchr(real, '/');
1003 if (!slash)
1005 g_free(real);
1006 delayed_error("ROX-Filer",
1007 "Broken symlink (or you don't have permission "
1008 "to follow it).");
1009 return;
1012 *slash = '\0';
1014 if (*real)
1015 new_dir = real;
1016 else
1017 new_dir = "/";
1019 if (filer_window->panel_type || !same_window)
1021 FilerWindow *new;
1023 new = filer_opendir(new_dir, PANEL_NO);
1024 set_autoselect(new, slash + 1);
1026 else
1027 filer_change_to(filer_window, new_dir, slash + 1);
1029 g_free(real);
1032 void filer_openitem(FilerWindow *filer_window, int item_number, OpenFlags flags)
1034 gboolean shift = (flags & OPEN_SHIFT) != 0;
1035 gboolean close_mini = flags & OPEN_FROM_MINI;
1036 gboolean same_window = (flags & OPEN_SAME_WINDOW) != 0
1037 && !filer_window->panel_type;
1038 gboolean close_window = (flags & OPEN_CLOSE_WINDOW) != 0
1039 && !filer_window->panel_type;
1040 GtkWidget *widget;
1041 char *full_path;
1042 DirItem *item = (DirItem *)
1043 filer_window->collection->items[item_number].data;
1044 gboolean wink = TRUE, destroy = FALSE;
1046 widget = filer_window->window;
1047 full_path = make_path(filer_window->path,
1048 item->leafname)->str;
1050 if (item->flags & ITEM_FLAG_SYMLINK && shift)
1052 char path[MAXPATHLEN + 1];
1053 int got;
1055 got = readlink(make_path(filer_window->path,
1056 item->leafname)->str,
1057 path, MAXPATHLEN);
1058 if (got < 0)
1059 delayed_error("ROX-Filer", g_strerror(errno));
1060 else
1062 g_return_if_fail(got <= MAXPATHLEN);
1063 path[got] = '\0';
1065 follow_symlink(filer_window, path,
1066 flags & OPEN_SAME_WINDOW);
1068 return;
1071 switch (item->base_type)
1073 case TYPE_DIRECTORY:
1074 if (item->flags & ITEM_FLAG_APPDIR && !shift)
1076 run_app(make_path(filer_window->path,
1077 item->leafname)->str);
1078 if (close_window)
1079 destroy = TRUE;
1080 break;
1083 if (item->flags & ITEM_FLAG_MOUNT_POINT && shift)
1085 action_mount(filer_window, item);
1086 if (item->flags & ITEM_FLAG_MOUNTED)
1087 break;
1090 if (same_window)
1092 wink = FALSE;
1093 filer_change_to(filer_window, full_path, NULL);
1094 close_mini = FALSE;
1096 else
1097 filer_opendir(full_path, PANEL_NO);
1098 break;
1099 case TYPE_FILE:
1100 if ((item->flags & ITEM_FLAG_EXEC_FILE) && !shift)
1102 char *argv[] = {NULL, NULL};
1104 argv[0] = full_path;
1106 if (spawn_full(argv, filer_window->path))
1108 if (close_window)
1109 destroy = TRUE;
1111 else
1112 report_error("ROX-Filer",
1113 "Failed to fork() child");
1115 else
1117 GString *message;
1118 MIME_type *type = shift ? &text_plain
1119 : item->mime_type;
1121 g_return_if_fail(type != NULL);
1123 if (type_open(full_path, type))
1125 if (close_window)
1126 destroy = TRUE;
1128 else
1130 message = g_string_new(NULL);
1131 g_string_sprintf(message, "No open "
1132 "action specified for files of "
1133 "this type (%s/%s)",
1134 type->media_type,
1135 type->subtype);
1136 report_error("ROX-Filer", message->str);
1137 g_string_free(message, TRUE);
1140 break;
1141 default:
1142 report_error("open_item",
1143 "I don't know how to open that");
1144 break;
1147 if (destroy)
1148 gtk_widget_destroy(filer_window->window);
1149 else
1151 if (wink)
1152 collection_wink_item(filer_window->collection,
1153 item_number);
1154 if (close_mini)
1155 minibuffer_hide(filer_window);
1159 static gint pointer_in(GtkWidget *widget,
1160 GdkEventCrossing *event,
1161 FilerWindow *filer_window)
1163 may_rescan(filer_window, TRUE);
1164 return FALSE;
1167 static gint focus_in(GtkWidget *widget,
1168 GdkEventFocus *event,
1169 FilerWindow *filer_window)
1171 window_with_focus = filer_window;
1173 return FALSE;
1176 static gint focus_out(GtkWidget *widget,
1177 GdkEventFocus *event,
1178 FilerWindow *filer_window)
1180 /* TODO: Shade the cursor */
1182 return FALSE;
1185 /* Handle keys that can't be bound with the menu */
1186 static gint key_press_event(GtkWidget *widget,
1187 GdkEventKey *event,
1188 FilerWindow *filer_window)
1190 switch (event->keyval)
1192 case GDK_BackSpace:
1193 change_to_parent(filer_window);
1194 break;
1195 default:
1196 return FALSE;
1199 return TRUE;
1202 static void toolbar_refresh_clicked(GtkWidget *widget,
1203 FilerWindow *filer_window)
1205 full_refresh();
1206 update_dir(filer_window, TRUE);
1209 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window)
1211 filer_change_to(filer_window, home_dir, NULL);
1214 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window)
1216 change_to_parent(filer_window);
1219 void change_to_parent(FilerWindow *filer_window)
1221 char *copy;
1222 char *slash;
1224 if (filer_window->path[0] == '/' && filer_window->path[1] == '\0')
1225 return; /* Already in the root */
1227 copy = g_strdup(filer_window->path);
1228 slash = strrchr(copy, '/');
1230 if (slash)
1232 *slash = '\0';
1233 filer_change_to(filer_window,
1234 *copy ? copy : "/",
1235 slash + 1);
1237 else
1238 g_warning("No / in directory path!\n");
1240 g_free(copy);
1244 /* Make filer_window display path. When finished, highlight item 'from', or
1245 * the first item if from is NULL. If there is currently no cursor then
1246 * simply wink 'from' (if not NULL).
1248 void filer_change_to(FilerWindow *filer_window, char *path, char *from)
1250 char *from_dup;
1251 char *real_path = pathdup(path);
1253 if (o_unique_filer_windows)
1255 FilerWindow *fw;
1257 fw = find_filer_window(real_path, filer_window);
1258 if (fw)
1259 gtk_widget_destroy(fw->window);
1262 from_dup = from && *from ? g_strdup(from) : NULL;
1264 detach(filer_window);
1265 g_free(filer_window->path);
1266 filer_window->path = real_path;
1268 filer_window->directory = g_fscache_lookup(dir_cache,
1269 filer_window->path);
1270 if (filer_window->directory)
1272 g_free(filer_window->auto_select);
1273 filer_window->had_cursor =
1274 filer_window->collection->cursor_item != -1
1275 || filer_window->had_cursor;
1276 filer_window->auto_select = from_dup;
1278 filer_set_title(filer_window);
1279 collection_set_cursor_item(filer_window->collection, -1);
1280 attach(filer_window);
1282 if (GTK_WIDGET_VISIBLE(filer_window->minibuffer))
1283 gtk_idle_add((GtkFunction) minibuffer_show_cb,
1284 filer_window);
1286 else
1288 char *error;
1290 g_free(from_dup);
1291 error = g_strdup_printf("Directory '%s' is not accessible.",
1292 path);
1293 delayed_error("ROX-Filer", error);
1294 g_free(error);
1295 gtk_widget_destroy(filer_window->window);
1299 int selected_item_number(Collection *collection)
1301 int i;
1303 g_return_val_if_fail(collection != NULL, -1);
1304 g_return_val_if_fail(IS_COLLECTION(collection), -1);
1305 g_return_val_if_fail(collection->number_selected == 1, -1);
1307 for (i = 0; i < collection->number_of_items; i++)
1308 if (collection->items[i].selected)
1309 return i;
1311 g_warning("selected_item: number_selected is wrong\n");
1313 return -1;
1316 DirItem *selected_item(Collection *collection)
1318 int item;
1320 item = selected_item_number(collection);
1322 if (item > -1)
1323 return (DirItem *) collection->items[item].data;
1324 return NULL;
1327 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
1328 FilerWindow *window)
1330 /* TODO: We can open lots of these - very irritating! */
1331 return get_choice("Close panel?",
1332 "You have tried to close a panel via the window "
1333 "manager - I usually find that this is accidental... "
1334 "really close?",
1335 2, "Remove", "Cancel") != 0;
1338 /* Make the items as narrow as possible */
1339 static void shrink_width(FilerWindow *filer_window)
1341 int i;
1342 Collection *col = filer_window->collection;
1343 int width = MIN_ITEM_WIDTH;
1344 int this_width;
1345 DisplayStyle style = filer_window->display_style;
1346 int text_height;
1348 text_height = item_font->ascent + item_font->descent;
1350 for (i = 0; i < col->number_of_items; i++)
1352 this_width = calc_width(filer_window,
1353 (DirItem *) col->items[i].data);
1354 if (this_width > width)
1355 width = this_width;
1358 collection_set_item_size(filer_window->collection,
1359 width,
1360 style == FULL_INFO ? MAX_ICON_HEIGHT + 4 :
1361 style == SMALL_ICONS ? MAX(text_height, SMALL_ICON_HEIGHT) + 4
1362 : text_height + MAX_ICON_HEIGHT + 8);
1365 void filer_set_sort_fn(FilerWindow *filer_window,
1366 int (*fn)(const void *a, const void *b))
1368 if (filer_window->sort_fn == fn)
1369 return;
1371 filer_window->sort_fn = fn;
1372 last_sort_fn = fn;
1374 collection_qsort(filer_window->collection,
1375 filer_window->sort_fn);
1377 update_options_label();
1380 void filer_style_set(FilerWindow *filer_window, DisplayStyle style)
1382 if (filer_window->display_style == style)
1383 return;
1385 if (filer_window->panel_type)
1386 style = LARGE_ICONS;
1387 else
1388 last_display_style = style;
1390 filer_window->display_style = style;
1392 switch (style)
1394 case SMALL_ICONS:
1395 collection_set_functions(filer_window->collection,
1396 draw_item_small, test_point_small);
1397 break;
1398 case FULL_INFO:
1399 collection_set_functions(filer_window->collection,
1400 draw_item_full_info, test_point_full_info);
1401 break;
1402 default:
1403 collection_set_functions(filer_window->collection,
1404 draw_item_large, test_point_large);
1405 break;
1408 shrink_width(filer_window);
1410 update_options_label();
1413 FilerWindow *filer_opendir(char *path, PanelType panel_type)
1415 GtkWidget *hbox, *scrollbar, *collection;
1416 FilerWindow *filer_window;
1417 GtkTargetEntry target_table[] =
1419 {"text/uri-list", 0, TARGET_URI_LIST},
1420 {"STRING", 0, TARGET_STRING},
1422 char *real_path;
1424 real_path = pathdup(path);
1426 if (o_unique_filer_windows && panel_type == PANEL_NO)
1428 FilerWindow *fw;
1430 fw = find_filer_window(real_path, NULL);
1432 if (fw)
1434 /* TODO: this should bring the window to the front
1435 * at the same coordinates.
1437 gtk_widget_hide(fw->window);
1438 gtk_widget_show(fw->window);
1439 g_free(real_path);
1440 return fw;
1444 filer_window = g_new(FilerWindow, 1);
1445 filer_window->minibuffer = NULL;
1446 filer_window->path = real_path;
1447 filer_window->scanning = FALSE;
1448 filer_window->had_cursor = FALSE;
1449 filer_window->auto_select = NULL;
1451 filer_window->directory = g_fscache_lookup(dir_cache,
1452 filer_window->path);
1453 if (!filer_window->directory)
1455 char *error;
1457 error = g_strdup_printf("Directory '%s' not found.", path);
1458 delayed_error("ROX-Filer", error);
1459 g_free(error);
1460 g_free(filer_window->path);
1461 g_free(filer_window);
1462 return NULL;
1465 filer_window->show_hidden = last_show_hidden;
1466 filer_window->panel_type = panel_type;
1467 filer_window->temp_item_selected = FALSE;
1468 filer_window->sort_fn = last_sort_fn;
1469 filer_window->flags = (FilerFlags) 0;
1470 filer_window->display_style = UNKNOWN_STYLE;
1472 filer_window->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1473 filer_set_title(filer_window);
1475 collection = collection_new(NULL);
1476 gtk_object_set_data(GTK_OBJECT(collection),
1477 "filer_window", filer_window);
1478 filer_window->collection = COLLECTION(collection);
1480 gtk_widget_add_events(filer_window->window, GDK_ENTER_NOTIFY);
1481 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1482 "enter-notify-event",
1483 GTK_SIGNAL_FUNC(pointer_in), filer_window);
1484 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_in_event",
1485 GTK_SIGNAL_FUNC(focus_in), filer_window);
1486 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_out_event",
1487 GTK_SIGNAL_FUNC(focus_out), filer_window);
1488 gtk_signal_connect(GTK_OBJECT(filer_window->window), "destroy",
1489 filer_window_destroyed, filer_window);
1491 gtk_signal_connect(GTK_OBJECT(filer_window->collection), "open_item",
1492 open_item, filer_window);
1493 gtk_signal_connect(GTK_OBJECT(collection), "show_menu",
1494 show_menu, filer_window);
1495 gtk_signal_connect(GTK_OBJECT(collection), "gain_selection",
1496 gain_selection, filer_window);
1497 gtk_signal_connect(GTK_OBJECT(collection), "lose_selection",
1498 lose_selection, filer_window);
1499 gtk_signal_connect(GTK_OBJECT(collection), "drag_selection",
1500 drag_selection, filer_window);
1501 gtk_signal_connect(GTK_OBJECT(collection), "drag_data_get",
1502 drag_data_get, filer_window);
1503 gtk_signal_connect(GTK_OBJECT(collection), "selection_clear_event",
1504 GTK_SIGNAL_FUNC(collection_lose_selection), NULL);
1505 gtk_signal_connect (GTK_OBJECT(collection), "selection_get",
1506 GTK_SIGNAL_FUNC(selection_get), NULL);
1507 gtk_selection_add_targets(collection, GDK_SELECTION_PRIMARY,
1508 target_table,
1509 sizeof(target_table) / sizeof(*target_table));
1511 filer_style_set(filer_window, last_display_style);
1512 drag_set_dest(filer_window);
1514 if (panel_type)
1516 int swidth, sheight, iwidth, iheight;
1517 GtkWidget *frame, *win = filer_window->window;
1519 gtk_window_set_wmclass(GTK_WINDOW(win), "ROX-Panel",
1520 "ROX-Filer");
1521 collection_set_panel(filer_window->collection, TRUE);
1522 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1523 "delete_event",
1524 GTK_SIGNAL_FUNC(filer_confirm_close),
1525 filer_window);
1527 gdk_window_get_size(GDK_ROOT_PARENT(), &swidth, &sheight);
1528 iwidth = filer_window->collection->item_width;
1529 iheight = filer_window->collection->item_height;
1532 int height = iheight + PANEL_BORDER;
1533 int y = panel_type == PANEL_TOP
1534 ? -PANEL_BORDER
1535 : sheight - height - PANEL_BORDER;
1537 gtk_widget_set_usize(collection, swidth, height);
1538 gtk_widget_set_uposition(win, 0, y);
1541 frame = gtk_frame_new(NULL);
1542 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1543 gtk_container_add(GTK_CONTAINER(frame), collection);
1544 gtk_container_add(GTK_CONTAINER(win), frame);
1546 gtk_widget_show_all(frame);
1547 gtk_widget_realize(win);
1548 if (override_redirect)
1549 gdk_window_set_override_redirect(win->window, TRUE);
1550 make_panel_window(win->window);
1552 else
1554 GtkWidget *vbox;
1555 int col_height = ROW_HEIGHT_LARGE * 3;
1557 gtk_signal_connect(GTK_OBJECT(collection),
1558 "key_press_event",
1559 GTK_SIGNAL_FUNC(key_press_event), filer_window);
1560 gtk_window_set_default_size(GTK_WINDOW(filer_window->window),
1561 filer_window->display_style == LARGE_ICONS ? 400 : 512,
1562 o_toolbar == TOOLBAR_NONE ? col_height:
1563 o_toolbar == TOOLBAR_NORMAL ? col_height + 24 :
1564 col_height + 38);
1566 hbox = gtk_hbox_new(FALSE, 0);
1567 gtk_container_add(GTK_CONTAINER(filer_window->window),
1568 hbox);
1570 vbox = gtk_vbox_new(FALSE, 0);
1571 gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
1573 if (o_toolbar != TOOLBAR_NONE)
1575 GtkWidget *toolbar;
1577 toolbar = create_toolbar(filer_window);
1578 gtk_box_pack_start(GTK_BOX(vbox), toolbar,
1579 FALSE, TRUE, 0);
1580 gtk_widget_show_all(toolbar);
1583 gtk_box_pack_start(GTK_BOX(vbox), collection, TRUE, TRUE, 0);
1585 filer_window->minibuffer = create_minibuffer(filer_window);
1586 gtk_box_pack_start(GTK_BOX(vbox), filer_window->minibuffer,
1587 FALSE, TRUE, 0);
1589 scrollbar = gtk_vscrollbar_new(COLLECTION(collection)->vadj);
1590 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, TRUE, 0);
1591 gtk_accel_group_attach(filer_keys,
1592 GTK_OBJECT(filer_window->window));
1593 gtk_window_set_focus(GTK_WINDOW(filer_window->window),
1594 collection);
1596 gtk_widget_show(hbox);
1597 gtk_widget_show(vbox);
1598 gtk_widget_show(scrollbar);
1599 gtk_widget_show(collection);
1602 number_of_windows++;
1603 gtk_widget_show(filer_window->window);
1604 attach(filer_window);
1606 all_filer_windows = g_list_prepend(all_filer_windows, filer_window);
1608 return filer_window;
1611 static gint clear_scanning_display(FilerWindow *filer_window)
1613 if (exists(filer_window))
1614 filer_set_title(filer_window);
1615 return FALSE;
1618 static void set_scanning_display(FilerWindow *filer_window, gboolean scanning)
1620 if (scanning == filer_window->scanning)
1621 return;
1622 filer_window->scanning = scanning;
1624 if (scanning)
1625 filer_set_title(filer_window);
1626 else
1627 gtk_timeout_add(300, (GtkFunction) clear_scanning_display,
1628 filer_window);
1631 static GtkWidget *create_toolbar(FilerWindow *filer_window)
1633 GtkWidget *frame, *box;
1635 if (o_toolbar == TOOLBAR_GNOME)
1637 frame = gtk_handle_box_new();
1638 box = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL,
1639 GTK_TOOLBAR_BOTH);
1640 gtk_container_set_border_width(GTK_CONTAINER(box), 2);
1641 gtk_toolbar_set_space_style(GTK_TOOLBAR(box),
1642 GTK_TOOLBAR_SPACE_LINE);
1643 gtk_toolbar_set_button_relief(GTK_TOOLBAR(box),
1644 GTK_RELIEF_NONE);
1646 else
1648 frame = gtk_frame_new(NULL);
1649 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1651 box = gtk_hbutton_box_new();
1652 gtk_button_box_set_child_size_default(16, 16);
1653 gtk_hbutton_box_set_spacing_default(2);
1654 gtk_button_box_set_layout(GTK_BUTTON_BOX(box),
1655 GTK_BUTTONBOX_START);
1658 gtk_container_add(GTK_CONTAINER(frame), box);
1660 add_button(box, TOOLBAR_UP_ICON,
1661 GTK_SIGNAL_FUNC(toolbar_up_clicked),
1662 filer_window,
1663 "Up", "Change to parent directory");
1664 add_button(box, TOOLBAR_HOME_ICON,
1665 GTK_SIGNAL_FUNC(toolbar_home_clicked),
1666 filer_window,
1667 "Home", "Change to home directory");
1668 add_button(box, TOOLBAR_REFRESH_ICON,
1669 GTK_SIGNAL_FUNC(toolbar_refresh_clicked),
1670 filer_window,
1671 "Rescan", "Rescan directory contents");
1673 return frame;
1676 static void add_button(GtkWidget *box, int pixmap,
1677 GtkSignalFunc cb, FilerWindow *filer_window,
1678 char *label, char *tip)
1680 GtkWidget *button, *icon;
1682 icon = gtk_pixmap_new(default_pixmap[pixmap].pixmap,
1683 default_pixmap[pixmap].mask);
1685 if (o_toolbar == TOOLBAR_GNOME)
1687 gtk_toolbar_append_element(GTK_TOOLBAR(box),
1688 GTK_TOOLBAR_CHILD_BUTTON,
1689 NULL,
1690 label,
1691 tip, NULL,
1692 icon,
1693 cb, filer_window);
1695 else
1697 button = gtk_button_new();
1698 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
1700 gtk_container_add(GTK_CONTAINER(button), icon);
1701 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1702 cb, filer_window);
1704 gtk_tooltips_set_tip(tooltips, button, tip, NULL);
1706 gtk_container_add(GTK_CONTAINER(box), button);
1710 /* Build up some option widgets to go in the options dialog, but don't
1711 * fill them in yet.
1713 static GtkWidget *create_options()
1715 GtkWidget *vbox, *menu, *hbox;
1717 vbox = gtk_vbox_new(FALSE, 0);
1718 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
1720 display_label = gtk_label_new("<>");
1721 gtk_label_set_line_wrap(GTK_LABEL(display_label), TRUE);
1722 gtk_box_pack_start(GTK_BOX(vbox), display_label, FALSE, TRUE, 0);
1724 toggle_new_window_on_1 =
1725 gtk_check_button_new_with_label("New window on button 1 "
1726 "(RISC OS style)");
1727 gtk_box_pack_start(GTK_BOX(vbox), toggle_new_window_on_1,
1728 FALSE, TRUE, 0);
1730 toggle_menu_on_2 =
1731 gtk_check_button_new_with_label("Menu on button 2 "
1732 "(RISC OS style)");
1733 gtk_box_pack_start(GTK_BOX(vbox), toggle_menu_on_2, FALSE, TRUE, 0);
1735 toggle_single_click =
1736 gtk_check_button_new_with_label("Single-click nagivation");
1737 gtk_box_pack_start(GTK_BOX(vbox), toggle_single_click, FALSE, TRUE, 0);
1739 toggle_unique_filer_windows =
1740 gtk_check_button_new_with_label("Unique windows");
1741 gtk_box_pack_start(GTK_BOX(vbox), toggle_unique_filer_windows, FALSE, TRUE, 0);
1743 hbox = gtk_hbox_new(FALSE, 4);
1744 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
1746 gtk_box_pack_start(GTK_BOX(hbox),
1747 gtk_label_new("Toolbar type for new windows"),
1748 FALSE, TRUE, 0);
1749 menu_toolbar = gtk_option_menu_new();
1750 menu = gtk_menu_new();
1751 gtk_menu_append(GTK_MENU(menu), gtk_menu_item_new_with_label("None"));
1752 gtk_menu_append(GTK_MENU(menu), gtk_menu_item_new_with_label("Normal"));
1753 gtk_menu_append(GTK_MENU(menu), gtk_menu_item_new_with_label("GNOME"));
1754 gtk_option_menu_set_menu(GTK_OPTION_MENU(menu_toolbar), menu);
1755 gtk_box_pack_start(GTK_BOX(hbox), menu_toolbar, TRUE, TRUE, 0);
1757 return vbox;
1760 static void update_options_label(void)
1762 guchar *str;
1764 str = g_strdup_printf("The last used display style (%s) and sort "
1765 "function (Sort By %s) will be saved if you click on "
1766 "Save.", style_to_name(), sort_fn_to_name());
1767 gtk_label_set_text(GTK_LABEL(display_label), str);
1768 g_free(str);
1771 /* Reflect current state by changing the widgets in the options box */
1772 static void update_options()
1774 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_new_window_on_1),
1775 o_new_window_on_1);
1776 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_menu_on_2),
1777 collection_menu_button == 2 ? 1 : 0);
1778 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_single_click),
1779 o_single_click);
1780 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_unique_filer_windows),
1781 o_unique_filer_windows);
1782 gtk_option_menu_set_history(GTK_OPTION_MENU(menu_toolbar), o_toolbar);
1784 update_options_label();
1787 /* Set current values by reading the states of the widgets in the options box */
1788 static void set_options()
1790 GtkWidget *item, *menu;
1791 GList *list;
1793 o_new_window_on_1 = gtk_toggle_button_get_active(
1794 GTK_TOGGLE_BUTTON(toggle_new_window_on_1));
1796 collection_menu_button = gtk_toggle_button_get_active(
1797 GTK_TOGGLE_BUTTON(toggle_menu_on_2)) ? 2 : 3;
1799 o_single_click = gtk_toggle_button_get_active(
1800 GTK_TOGGLE_BUTTON(toggle_single_click));
1802 o_unique_filer_windows = gtk_toggle_button_get_active(
1803 GTK_TOGGLE_BUTTON(toggle_unique_filer_windows));
1805 collection_single_click = o_single_click ? TRUE : FALSE;
1807 menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(menu_toolbar));
1808 item = gtk_menu_get_active(GTK_MENU(menu));
1809 list = gtk_container_children(GTK_CONTAINER(menu));
1810 o_toolbar = (ToolbarType) g_list_index(list, item);
1811 g_list_free(list);
1815 static guchar *style_to_name(void)
1817 return last_display_style == LARGE_ICONS ? "Large Icons" :
1818 last_display_style == SMALL_ICONS ? "Small Icons" :
1819 "Full Info";
1822 static guchar *sort_fn_to_name(void)
1824 return last_sort_fn == sort_by_name ? "Name" :
1825 last_sort_fn == sort_by_type ? "Type" :
1826 last_sort_fn == sort_by_date ? "Date" :
1827 "Size";
1830 static void save_options()
1832 option_write("filer_new_window_on_1", o_new_window_on_1 ? "1" : "0");
1833 option_write("filer_menu_on_2",
1834 collection_menu_button == 2 ? "1" : "0");
1835 option_write("filer_single_click", o_single_click ? "1" : "0");
1836 option_write("filer_unique_windows", o_unique_filer_windows ? "1" : "0");
1837 option_write("filer_display_style", style_to_name());
1838 option_write("filer_sort_by", sort_fn_to_name());
1839 option_write("filer_toolbar", o_toolbar == TOOLBAR_NONE ? "None" :
1840 o_toolbar == TOOLBAR_NORMAL ? "Normal" :
1841 o_toolbar == TOOLBAR_GNOME ? "GNOME" :
1842 "Unknown");
1845 static char *filer_new_window_on_1(char *data)
1847 o_new_window_on_1 = atoi(data) != 0;
1848 return NULL;
1851 static char *filer_menu_on_2(char *data)
1853 collection_menu_button = atoi(data) != 0 ? 2 : 3;
1854 return NULL;
1857 static char *filer_single_click(char *data)
1859 o_single_click = atoi(data) != 0;
1860 collection_single_click = o_single_click ? TRUE : FALSE;
1861 return NULL;
1864 static char *filer_unique_windows(char *data)
1866 o_unique_filer_windows = atoi(data) != 0;
1867 return NULL;
1870 static char *filer_display_style(char *data)
1872 if (g_strcasecmp(data, "Large Icons") == 0)
1873 last_display_style = LARGE_ICONS;
1874 else if (g_strcasecmp(data, "Small Icons") == 0)
1875 last_display_style = SMALL_ICONS;
1876 else if (g_strcasecmp(data, "Full Info") == 0)
1877 last_display_style = FULL_INFO;
1878 else
1879 return "Unknown display style";
1881 return NULL;
1884 static char *filer_sort_by(char *data)
1886 if (g_strcasecmp(data, "Name") == 0)
1887 last_sort_fn = sort_by_name;
1888 else if (g_strcasecmp(data, "Type") == 0)
1889 last_sort_fn = sort_by_type;
1890 else if (g_strcasecmp(data, "Date") == 0)
1891 last_sort_fn = sort_by_date;
1892 else if (g_strcasecmp(data, "Size") == 0)
1893 last_sort_fn = sort_by_size;
1894 else
1895 return "Unknown sort type";
1897 return NULL;
1900 static char *filer_toolbar(char *data)
1902 if (g_strcasecmp(data, "None") == 0)
1903 o_toolbar = TOOLBAR_NONE;
1904 else if (g_strcasecmp(data, "Normal") == 0)
1905 o_toolbar = TOOLBAR_NORMAL;
1906 else if (g_strcasecmp(data, "GNOME") == 0)
1907 o_toolbar = TOOLBAR_GNOME;
1908 else
1909 return "Unknown toolbar type";
1911 return NULL;
1914 /* Note that filer_window may not exist after this call. */
1915 void update_dir(FilerWindow *filer_window, gboolean warning)
1917 if (may_rescan(filer_window, warning))
1918 dir_update(filer_window->directory, filer_window->path);
1921 void filer_set_hidden(FilerWindow *filer_window, gboolean hidden)
1923 Directory *dir = filer_window->directory;
1925 if (filer_window->show_hidden == hidden)
1926 return;
1928 filer_window->show_hidden = hidden;
1929 last_show_hidden = hidden;
1931 g_fscache_data_ref(dir_cache, dir);
1932 detach(filer_window);
1933 filer_window->directory = dir;
1934 attach(filer_window);
1937 /* Refresh the various caches even if we don't think we need to */
1938 void full_refresh(void)
1940 mount_update(TRUE);
1943 /* See whether a filer window with a given path already exists
1944 * and is different from diff.
1946 static FilerWindow *find_filer_window(char *path, FilerWindow *diff)
1948 GList *next = all_filer_windows;
1950 while (next)
1952 FilerWindow *filer_window = (FilerWindow *) next->data;
1954 if (filer_window->panel_type == PANEL_NO &&
1955 filer_window != diff &&
1956 strcmp(path, filer_window->path) == 0)
1958 return filer_window;
1961 next = next->next;
1964 return NULL;
1967 /* This path has been mounted/umounted/deleted some files - update all dirs */
1968 void filer_check_mounted(char *path)
1970 GList *next = all_filer_windows;
1971 int len;
1973 len = strlen(path);
1975 while (next)
1977 FilerWindow *filer_window = (FilerWindow *) next->data;
1979 next = next->next;
1981 if (strncmp(path, filer_window->path, len) == 0)
1983 char s = filer_window->path[len];
1985 if (s == '/' || s == '\0')
1986 update_dir(filer_window, FALSE);
1991 /* Like minibuffer_show(), except that:
1992 * - It returns FALSE (to be used from an idle callback)
1993 * - It checks that the filer window still exists.
1995 static gboolean minibuffer_show_cb(FilerWindow *filer_window)
1997 if (exists(filer_window))
1998 minibuffer_show(filer_window);
1999 return FALSE;
2002 static gboolean exists(FilerWindow *filer_window)
2004 GList *next;
2006 for (next = all_filer_windows; next; next = next->next)
2008 FilerWindow *fw = (FilerWindow *) next->data;
2010 if (fw == filer_window)
2011 return TRUE;
2014 return FALSE;
2017 /* Highlight (wink or cursor) this item in the filer window. If the item
2018 * isn't already there but we're scanning then highlight it if it
2019 * appears later.
2021 static void set_autoselect(FilerWindow *filer_window, guchar *leaf)
2023 Collection *col = filer_window->collection;
2024 int i;
2026 g_free(filer_window->auto_select);
2027 filer_window->auto_select = NULL;
2029 for (i = 0; i < col->number_of_items; i++)
2031 DirItem *item = (DirItem *) col->items[i].data;
2033 if (strcmp(item->leafname, leaf) == 0)
2035 if (col->cursor_item != -1)
2036 collection_set_cursor_item(col, i);
2037 else
2038 collection_wink_item(col, i);
2039 return;
2043 filer_window->auto_select = g_strdup(leaf);
2046 static void filer_set_title(FilerWindow *filer_window)
2048 if (filer_window->scanning)
2050 guchar *title;
2052 title = g_strdup_printf("%s (Scanning)", filer_window->path);
2053 gtk_window_set_title(GTK_WINDOW(filer_window->window),
2054 title);
2055 g_free(title);
2057 else
2058 gtk_window_set_title(GTK_WINDOW(filer_window->window),
2059 filer_window->path);