r171: Fixed a compile problem on IRIX. Updating items now resorts.
[rox-filer/ma.git] / ROX-Filer / src / filer.c
blob22cec7eeeeb689243aaae2e412706b0184a8888c
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/stat.h>
29 #include <sys/param.h>
30 #include <unistd.h>
31 #include <time.h>
32 #include <ctype.h>
34 #include <gtk/gtk.h>
35 #include <gdk/gdkx.h>
36 #include <gdk/gdkkeysyms.h>
37 #include "collection.h"
39 #include "main.h"
40 #include "support.h"
41 #include "gui_support.h"
42 #include "filer.h"
43 #include "pixmaps.h"
44 #include "menu.h"
45 #include "dnd.h"
46 #include "run.h"
47 #include "mount.h"
48 #include "type.h"
49 #include "options.h"
50 #include "action.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 GdkFont *fixed_font = NULL;
67 GList *all_filer_windows = NULL;
69 static FilerWindow *window_with_selection = NULL;
71 /* Options bits */
72 static GtkWidget *create_options();
73 static void update_options();
74 static void set_options();
75 static void save_options();
76 static char *filer_single_click(char *data);
77 static char *filer_menu_on_2(char *data);
78 static char *filer_new_window_on_1(char *data);
79 static char *filer_toolbar(char *data);
81 static OptionsSection options =
83 "Filer window options",
84 create_options,
85 update_options,
86 set_options,
87 save_options
89 static gboolean o_toolbar = TRUE;
90 static GtkWidget *toggle_toolbar;
91 static gboolean o_single_click = FALSE;
92 static GtkWidget *toggle_single_click;
93 static gboolean o_new_window_on_1 = FALSE; /* Button 1 => New window */
94 static GtkWidget *toggle_new_window_on_1;
95 static GtkWidget *toggle_menu_on_2;
97 /* Static prototypes */
98 static void attach(FilerWindow *filer_window);
99 static void detach(FilerWindow *filer_window);
100 static void filer_window_destroyed(GtkWidget *widget,
101 FilerWindow *filer_window);
102 void show_menu(Collection *collection, GdkEventButton *event,
103 int number_selected, gpointer user_data);
104 static gint focus_in(GtkWidget *widget,
105 GdkEventFocus *event,
106 FilerWindow *filer_window);
107 static gint focus_out(GtkWidget *widget,
108 GdkEventFocus *event,
109 FilerWindow *filer_window);
110 static void add_item(FilerWindow *filer_window, DirItem *item);
111 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window);
112 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window);
113 static void add_button(GtkContainer *box, int pixmap,
114 GtkSignalFunc cb, gpointer data);
115 static GtkWidget *create_toolbar(FilerWindow *filer_window);
116 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
117 FilerWindow *window);
118 static int calc_width(FilerWindow *filer_window, DirItem *item);
119 static void draw_large_icon(GtkWidget *widget,
120 GdkRectangle *area,
121 DirItem *item,
122 gboolean selected);
123 static void draw_string(GtkWidget *widget,
124 GdkFont *font,
125 char *string,
126 int x,
127 int y,
128 int width,
129 gboolean selected);
130 static void draw_item_large(GtkWidget *widget,
131 CollectionItem *item,
132 GdkRectangle *area);
133 static void draw_item_small(GtkWidget *widget,
134 CollectionItem *item,
135 GdkRectangle *area);
136 static void draw_item_full_info(GtkWidget *widget,
137 CollectionItem *colitem,
138 GdkRectangle *area);
139 static gboolean test_point_large(Collection *collection,
140 int point_x, int point_y,
141 CollectionItem *item,
142 int width, int height);
143 static gboolean test_point_small(Collection *collection,
144 int point_x, int point_y,
145 CollectionItem *item,
146 int width, int height);
147 static gboolean test_point_full_info(Collection *collection,
148 int point_x, int point_y,
149 CollectionItem *item,
150 int width, int height);
151 static void update_display(Directory *dir,
152 DirAction action,
153 GPtrArray *items,
154 FilerWindow *filer_window);
155 static void filer_change_to(FilerWindow *filer_window, char *path, char *from);
156 static void shrink_width(FilerWindow *filer_window);
157 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning);
159 static GdkAtom xa_string;
160 enum
162 TARGET_STRING,
163 TARGET_URI_LIST,
166 static GdkCursor *busy_cursor = NULL;
168 void filer_init()
170 xa_string = gdk_atom_intern("STRING", FALSE);
172 options_sections = g_slist_prepend(options_sections, &options);
173 option_register("filer_new_window_on_1", filer_new_window_on_1);
174 option_register("filer_menu_on_2", filer_menu_on_2);
175 option_register("filer_single_click", filer_single_click);
176 option_register("filer_toolbar", filer_toolbar);
178 fixed_font = gdk_font_load("fixed");
180 busy_cursor = gdk_cursor_new(GDK_WATCH);
183 static gboolean if_deleted(gpointer item, gpointer removed)
185 int i = ((GPtrArray *) removed)->len;
186 DirItem **r = (DirItem **) ((GPtrArray *) removed)->pdata;
187 char *leafname = ((DirItem *) item)->leafname;
189 while (i--)
191 if (strcmp(leafname, r[i]->leafname) == 0)
192 return TRUE;
195 return FALSE;
198 static void update_item(FilerWindow *filer_window, DirItem *item)
200 int i;
201 char *leafname = item->leafname;
203 if (leafname[0] == '.')
205 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
206 || (leafname[1] == '.' && leafname[2] == '\0'))
207 return;
210 i = collection_find_item(filer_window->collection, item, dir_item_cmp);
212 if (i >= 0)
213 collection_draw_item(filer_window->collection, i, TRUE);
214 else
215 g_warning("Failed to find '%s'\n", item->leafname);
218 static void update_display(Directory *dir,
219 DirAction action,
220 GPtrArray *items,
221 FilerWindow *filer_window)
223 int i;
224 int cursor = filer_window->collection->cursor_item;
225 char *as;
226 Collection *collection = filer_window->collection;
228 switch (action)
230 case DIR_ADD:
231 as = filer_window->auto_select;
233 for (i = 0; i < items->len; i++)
235 DirItem *item = (DirItem *) items->pdata[i];
237 add_item(filer_window, item);
239 if (cursor != -1 || !as)
240 continue;
242 if (strcmp(as, item->leafname) != 0)
243 continue;
245 cursor = collection->number_of_items - 1;
246 if (filer_window->had_cursor)
247 collection_set_cursor_item(collection,
248 cursor);
249 else
250 collection_wink_item(collection,
251 cursor);
254 collection_qsort(filer_window->collection,
255 filer_window->sort_fn);
256 break;
257 case DIR_REMOVE:
258 collection_delete_if(filer_window->collection,
259 if_deleted,
260 items);
261 break;
262 case DIR_END_SCAN:
263 if (filer_window->window->window)
264 gdk_window_set_cursor(
265 filer_window->window->window,
266 NULL);
267 shrink_width(filer_window);
268 if (filer_window->had_cursor &&
269 collection->cursor_item == -1)
271 collection_set_cursor_item(collection, 0);
272 filer_window->had_cursor = FALSE;
274 break;
275 case DIR_UPDATE:
276 for (i = 0; i < items->len; i++)
278 DirItem *item = (DirItem *) items->pdata[i];
280 update_item(filer_window, item);
282 collection_qsort(filer_window->collection,
283 filer_window->sort_fn);
284 break;
288 static void attach(FilerWindow *filer_window)
290 gdk_window_set_cursor(filer_window->window->window, busy_cursor);
291 collection_clear(filer_window->collection);
292 dir_attach(filer_window->directory, (DirCallback) update_display,
293 filer_window);
296 static void detach(FilerWindow *filer_window)
298 g_return_if_fail(filer_window->directory != NULL);
300 dir_detach(filer_window->directory,
301 (DirCallback) update_display, filer_window);
302 g_fscache_data_unref(dir_cache, filer_window->directory);
303 filer_window->directory = NULL;
306 static void filer_window_destroyed(GtkWidget *widget,
307 FilerWindow *filer_window)
309 all_filer_windows = g_list_remove(all_filer_windows, filer_window);
311 if (window_with_selection == filer_window)
312 window_with_selection = NULL;
313 if (window_with_focus == filer_window)
314 window_with_focus = NULL;
316 if (filer_window->directory)
317 detach(filer_window);
319 g_free(filer_window->auto_select);
320 g_free(filer_window->path);
321 g_free(filer_window);
323 if (--number_of_windows < 1)
324 gtk_main_quit();
327 static int calc_width(FilerWindow *filer_window, DirItem *item)
329 int pix_width = item->image->width;
331 switch (filer_window->display_style)
333 case FULL_INFO:
334 return MAX_ICON_WIDTH + 12 +
335 MAX(item->details_width, item->name_width);
336 case SMALL_ICONS:
337 return SMALL_ICON_WIDTH + 12 + item->name_width;
338 default:
339 return MAX(pix_width, item->name_width) + 4;
343 /* Add a single object to a directory display */
344 static void add_item(FilerWindow *filer_window, DirItem *item)
346 char *leafname = item->leafname;
347 int item_width;
349 if (leafname[0] == '.')
351 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
352 || (leafname[1] == '.' && leafname[2] == '\0'))
353 return;
356 item_width = calc_width(filer_window, item);
357 if (item_width > filer_window->collection->item_width)
358 collection_set_item_size(filer_window->collection,
359 item_width,
360 filer_window->collection->item_height);
361 collection_insert(filer_window->collection, item);
364 /* Is a point inside an item? */
365 static gboolean test_point_large(Collection *collection,
366 int point_x, int point_y,
367 CollectionItem *colitem,
368 int width, int height)
370 DirItem *item = (DirItem *) colitem->data;
371 GdkFont *font = GTK_WIDGET(collection)->style->font;
372 int text_height = font->ascent + font->descent;
373 MaskedPixmap *image = item->image;
374 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
375 int image_width = (image->width >> 1) + 2;
376 int text_width = (item->name_width >> 1) + 2;
377 int x_limit;
379 if (point_y < image_y)
380 return FALSE; /* Too high up (don't worry about too low) */
382 if (point_y <= image_y + image->height + 2)
383 x_limit = image_width;
384 else if (point_y > height - text_height - 2)
385 x_limit = text_width;
386 else
387 x_limit = MIN(image_width, text_width);
389 return ABS(point_x - (width >> 1)) < x_limit;
392 static gboolean test_point_full_info(Collection *collection,
393 int point_x, int point_y,
394 CollectionItem *colitem,
395 int width, int height)
397 DirItem *item = (DirItem *) colitem->data;
398 GdkFont *font = GTK_WIDGET(collection)->style->font;
399 MaskedPixmap *image = item->image;
400 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
401 int low_top = height
402 - fixed_font->descent - 2 - fixed_font->ascent;
404 if (point_x < image->width + 2)
405 return point_x > 2 && point_y > image_y;
407 point_x -= MAX_ICON_WIDTH + 8;
409 if (point_y >= low_top)
410 return point_x < item->details_width;
411 if (point_y >= low_top - font->ascent - font->descent)
412 return point_x < item->name_width;
413 return FALSE;
416 static gboolean test_point_small(Collection *collection,
417 int point_x, int point_y,
418 CollectionItem *colitem,
419 int width, int height)
421 DirItem *item = (DirItem *) colitem->data;
422 MaskedPixmap *image = item->image;
423 int image_y = MAX(0, SMALL_ICON_HEIGHT - image->height);
424 GdkFont *font = GTK_WIDGET(collection)->style->font;
425 int low_top = height
426 - fixed_font->descent - 2 - font->ascent;
427 int iwidth = MIN(SMALL_ICON_WIDTH, image->width);
429 if (point_x < iwidth + 2)
430 return point_x > 2 && point_y > image_y;
432 point_x -= SMALL_ICON_WIDTH + 4;
434 if (point_y >= low_top)
435 return point_x < item->name_width;
436 return FALSE;
439 static void draw_small_icon(GtkWidget *widget,
440 GdkRectangle *area,
441 DirItem *item,
442 gboolean selected)
444 MaskedPixmap *image = item->image;
445 int width = MIN(image->width, SMALL_ICON_WIDTH);
446 int height = MIN(image->height, SMALL_ICON_HEIGHT);
447 int image_x = area->x + ((area->width - width) >> 1);
448 int image_y;
449 GdkGC *gc = selected ? widget->style->white_gc
450 : widget->style->black_gc;
451 if (!item->image)
452 return;
454 gdk_gc_set_clip_mask(gc, item->image->mask);
456 image_y = MAX(0, SMALL_ICON_HEIGHT - image->height);
457 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
458 gdk_draw_pixmap(widget->window, gc,
459 item->image->pixmap,
460 0, 0, /* Source x,y */
461 image_x, area->y + image_y, /* Dest x,y */
462 width, height);
464 if (selected)
466 gdk_gc_set_function(gc, GDK_INVERT);
467 gdk_draw_rectangle(widget->window,
469 TRUE, image_x, area->y + image_y,
470 width, height);
471 gdk_gc_set_function(gc, GDK_COPY);
474 if (item->flags & ITEM_FLAG_SYMLINK)
476 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
477 gdk_gc_set_clip_mask(gc,
478 default_pixmap[TYPE_SYMLINK].mask);
479 gdk_draw_pixmap(widget->window, gc,
480 default_pixmap[TYPE_SYMLINK].pixmap,
481 0, 0, /* Source x,y */
482 image_x, area->y + 8, /* Dest x,y */
483 -1, -1);
485 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
487 int type = item->flags & ITEM_FLAG_MOUNTED
488 ? TYPE_MOUNTED
489 : TYPE_UNMOUNTED;
490 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
491 gdk_gc_set_clip_mask(gc,
492 default_pixmap[type].mask);
493 gdk_draw_pixmap(widget->window, gc,
494 default_pixmap[type].pixmap,
495 0, 0, /* Source x,y */
496 image_x, area->y + 8, /* Dest x,y */
497 -1, -1);
500 gdk_gc_set_clip_mask(gc, NULL);
501 gdk_gc_set_clip_origin(gc, 0, 0);
504 static void draw_large_icon(GtkWidget *widget,
505 GdkRectangle *area,
506 DirItem *item,
507 gboolean selected)
509 MaskedPixmap *image = item->image;
510 int width = MIN(image->width, MAX_ICON_WIDTH);
511 int height = MIN(image->height, MAX_ICON_WIDTH);
512 int image_x = area->x + ((area->width - width) >> 1);
513 int image_y;
514 GdkGC *gc = selected ? widget->style->white_gc
515 : widget->style->black_gc;
517 gdk_gc_set_clip_mask(gc, item->image->mask);
519 image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
520 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
521 gdk_draw_pixmap(widget->window, gc,
522 item->image->pixmap,
523 0, 0, /* Source x,y */
524 image_x, area->y + image_y, /* Dest x,y */
525 width, height);
527 if (selected)
529 gdk_gc_set_function(gc, GDK_INVERT);
530 gdk_draw_rectangle(widget->window,
532 TRUE, image_x, area->y + image_y,
533 width, height);
534 gdk_gc_set_function(gc, GDK_COPY);
537 if (item->flags & ITEM_FLAG_SYMLINK)
539 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
540 gdk_gc_set_clip_mask(gc,
541 default_pixmap[TYPE_SYMLINK].mask);
542 gdk_draw_pixmap(widget->window, gc,
543 default_pixmap[TYPE_SYMLINK].pixmap,
544 0, 0, /* Source x,y */
545 image_x, area->y + 8, /* Dest x,y */
546 -1, -1);
548 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
550 int type = item->flags & ITEM_FLAG_MOUNTED
551 ? TYPE_MOUNTED
552 : TYPE_UNMOUNTED;
553 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
554 gdk_gc_set_clip_mask(gc,
555 default_pixmap[type].mask);
556 gdk_draw_pixmap(widget->window, gc,
557 default_pixmap[type].pixmap,
558 0, 0, /* Source x,y */
559 image_x, area->y + 8, /* Dest x,y */
560 -1, -1);
563 gdk_gc_set_clip_mask(gc, NULL);
564 gdk_gc_set_clip_origin(gc, 0, 0);
567 static void draw_string(GtkWidget *widget,
568 GdkFont *font,
569 char *string,
570 int x,
571 int y,
572 int width,
573 gboolean selected)
575 int text_height = font->ascent + font->descent;
577 if (selected)
578 gtk_paint_flat_box(widget->style, widget->window,
579 GTK_STATE_SELECTED, GTK_SHADOW_NONE,
580 NULL, widget, "text",
581 x, y - font->ascent,
582 width,
583 text_height);
585 gdk_draw_text(widget->window,
586 font,
587 selected ? widget->style->white_gc
588 : widget->style->black_gc,
589 x, y,
590 string, strlen(string));
593 /* Return a string (valid until next call) giving details
594 * of this item.
596 char *details(DirItem *item)
598 mode_t m = item->mode;
599 static GString *buf = NULL;
601 if (!buf)
602 buf = g_string_new(NULL);
604 g_string_sprintf(buf, "%s, %c%c%c:%c%c%c:%c%c%c:%c%c"
605 #ifdef S_ISVTX
606 "%c"
607 #endif
608 ", %s",
609 S_ISDIR(m) ? "Dir" :
610 S_ISCHR(m) ? "Char" :
611 S_ISBLK(m) ? "Blck" :
612 S_ISLNK(m) ? "Link" :
613 S_ISSOCK(m) ? "Sock" :
614 S_ISFIFO(m) ? "Pipe" : "File",
616 m & S_IRUSR ? 'r' : '-',
617 m & S_IWUSR ? 'w' : '-',
618 m & S_IXUSR ? 'x' : '-',
620 m & S_IRGRP ? 'r' : '-',
621 m & S_IWGRP ? 'w' : '-',
622 m & S_IXGRP ? 'x' : '-',
624 m & S_IROTH ? 'r' : '-',
625 m & S_IWOTH ? 'w' : '-',
626 m & S_IXOTH ? 'x' : '-',
628 m & S_ISUID ? 'U' : '-',
629 m & S_ISGID ? 'G' : '-',
630 #ifdef S_ISVTX
631 m & S_ISVTX ? 'T' : '-',
632 #endif
633 format_size(item->size));
635 return buf->str;
638 static void draw_item_full_info(GtkWidget *widget,
639 CollectionItem *colitem,
640 GdkRectangle *area)
642 DirItem *item = (DirItem *) colitem->data;
643 MaskedPixmap *image = item->image;
644 GdkFont *font = widget->style->font;
645 int text_x = area->x + MAX_ICON_WIDTH + 8;
646 int low_text_y = area->y + area->height - fixed_font->descent - 2;
647 gboolean selected = colitem->selected;
648 GdkRectangle pic_area;
650 pic_area.x = area->x;
651 pic_area.y = area->y;
652 pic_area.width = image->width + 8;
653 pic_area.height = area->height;
655 draw_large_icon(widget, &pic_area, item, selected);
657 draw_string(widget,
658 widget->style->font,
659 item->leafname,
660 text_x,
661 low_text_y - font->descent - fixed_font->ascent,
662 item->name_width,
663 selected);
664 draw_string(widget,
665 fixed_font,
666 details(item),
667 text_x, low_text_y,
668 item->details_width,
669 selected);
672 static void draw_item_small(GtkWidget *widget,
673 CollectionItem *colitem,
674 GdkRectangle *area)
676 DirItem *item = (DirItem *) colitem->data;
677 GdkFont *font = widget->style->font;
678 int text_x = area->x + SMALL_ICON_WIDTH + 4;
679 int low_text_y = area->y + area->height - font->descent - 2;
680 gboolean selected = colitem->selected;
681 GdkRectangle pic_area;
683 pic_area.x = area->x;
684 pic_area.y = area->y;
685 pic_area.width = SMALL_ICON_WIDTH;
686 pic_area.height = SMALL_ICON_HEIGHT;
688 draw_small_icon(widget, &pic_area, item, selected);
690 draw_string(widget,
691 widget->style->font,
692 item->leafname,
693 text_x,
694 low_text_y,
695 item->name_width,
696 selected);
699 static void draw_item_large(GtkWidget *widget,
700 CollectionItem *colitem,
701 GdkRectangle *area)
703 DirItem *item = (DirItem *) colitem->data;
704 GdkFont *font = widget->style->font;
705 int text_x = area->x + ((area->width - item->name_width) >> 1);
706 int text_y = area->y + area->height - font->descent - 2;
707 gboolean selected = colitem->selected;
709 draw_large_icon(widget, area, item, selected);
711 draw_string(widget,
712 widget->style->font,
713 item->leafname,
714 text_x, text_y, item->name_width,
715 selected);
718 void show_menu(Collection *collection, GdkEventButton *event,
719 int item, gpointer user_data)
721 show_filer_menu((FilerWindow *) user_data, event, item);
724 /* Returns TRUE iff the directory still exits. */
725 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning)
727 Directory *dir;
729 g_return_val_if_fail(filer_window != NULL, FALSE);
731 /* We do a fresh lookup (rather than update) because the inode may
732 * have changed.
734 dir = g_fscache_lookup(dir_cache, filer_window->path);
735 if (!dir)
737 if (warning)
738 delayed_error("ROX-Filer", "Directory missing/deleted");
739 gtk_widget_destroy(filer_window->window);
740 return FALSE;
742 if (dir == filer_window->directory)
743 g_fscache_data_unref(dir_cache, dir);
744 else
746 detach(filer_window);
747 filer_window->directory = dir;
748 attach(filer_window);
751 return TRUE;
754 /* Another app has grabbed the selection */
755 static gint collection_lose_selection(GtkWidget *widget,
756 GdkEventSelection *event)
758 if (window_with_selection &&
759 window_with_selection->collection == COLLECTION(widget))
761 FilerWindow *filer_window = window_with_selection;
762 window_with_selection = NULL;
763 collection_clear_selection(filer_window->collection);
766 return TRUE;
769 /* Someone wants us to send them the selection */
770 static void selection_get(GtkWidget *widget,
771 GtkSelectionData *selection_data,
772 guint info,
773 guint time,
774 gpointer data)
776 GString *reply, *header;
777 FilerWindow *filer_window;
778 int i;
779 Collection *collection;
781 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
783 reply = g_string_new(NULL);
784 header = g_string_new(NULL);
786 switch (info)
788 case TARGET_STRING:
789 g_string_sprintf(header, " %s",
790 make_path(filer_window->path, "")->str);
791 break;
792 case TARGET_URI_LIST:
793 g_string_sprintf(header, " file://%s%s",
794 our_host_name(),
795 make_path(filer_window->path, "")->str);
796 break;
799 collection = filer_window->collection;
800 for (i = 0; i < collection->number_of_items; i++)
802 if (collection->items[i].selected)
804 DirItem *item =
805 (DirItem *) collection->items[i].data;
807 g_string_append(reply, header->str);
808 g_string_append(reply, item->leafname);
811 /* This works, but I don't think I like it... */
812 /* g_string_append_c(reply, ' '); */
814 gtk_selection_data_set(selection_data, xa_string,
815 8, reply->str + 1, reply->len - 1);
816 g_string_free(reply, TRUE);
817 g_string_free(header, TRUE);
820 /* No items are now selected. This might be because another app claimed
821 * the selection or because the user unselected all the items.
823 static void lose_selection(Collection *collection,
824 guint time,
825 gpointer user_data)
827 FilerWindow *filer_window = (FilerWindow *) user_data;
829 if (window_with_selection == filer_window)
831 window_with_selection = NULL;
832 gtk_selection_owner_set(NULL,
833 GDK_SELECTION_PRIMARY,
834 time);
838 static void gain_selection(Collection *collection,
839 guint time,
840 gpointer user_data)
842 FilerWindow *filer_window = (FilerWindow *) user_data;
844 if (gtk_selection_owner_set(GTK_WIDGET(collection),
845 GDK_SELECTION_PRIMARY,
846 time))
848 window_with_selection = filer_window;
850 else
851 collection_clear_selection(filer_window->collection);
854 int sort_by_name(const void *item1, const void *item2)
856 return strcmp((*((DirItem **)item1))->leafname,
857 (*((DirItem **)item2))->leafname);
860 int sort_by_type(const void *item1, const void *item2)
862 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
863 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
864 MIME_type *m1, *m2;
866 int diff = i1->base_type - i2->base_type;
868 if (!diff)
869 diff = (i1->flags & ITEM_FLAG_APPDIR)
870 - (i2->flags & ITEM_FLAG_APPDIR);
871 if (diff)
872 return diff > 0 ? 1 : -1;
874 m1 = i1->mime_type;
875 m2 = i2->mime_type;
877 if (m1 && m2)
879 diff = strcmp(m1->media_type, m2->media_type);
880 if (!diff)
881 diff = strcmp(m1->subtype, m2->subtype);
883 else if (m1 || m2)
884 diff = m1 ? 1 : -1;
885 else
886 diff = 0;
888 if (diff)
889 return diff > 0 ? 1 : -1;
891 return sort_by_name(item1, item2);
894 int sort_by_date(const void *item1, const void *item2)
896 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
897 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
899 return i1->mtime > i2->mtime ? -1 :
900 i1->mtime < i2->mtime ? 1 :
901 sort_by_name(item1, item2);
904 int sort_by_size(const void *item1, const void *item2)
906 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
907 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
909 return i1->size > i2->size ? -1 :
910 i1->size < i2->size ? 1 :
911 sort_by_name(item1, item2);
914 void open_item(Collection *collection,
915 gpointer item_data, int item_number,
916 gpointer user_data)
918 FilerWindow *filer_window = (FilerWindow *) user_data;
919 GdkEvent *event;
920 GdkEventButton *bevent;
921 GdkEventKey *kevent;
922 gboolean shift;
923 gboolean adjust; /* do alternative action */
925 event = (GdkEvent *) gtk_get_current_event();
927 bevent = (GdkEventButton *) event;
928 kevent = (GdkEventKey *) event;
930 switch (event->type)
932 case GDK_2BUTTON_PRESS:
933 case GDK_BUTTON_PRESS:
934 case GDK_BUTTON_RELEASE:
935 shift = bevent->state & GDK_SHIFT_MASK;
936 adjust = (bevent->button != 1)
937 ^ ((bevent->state & GDK_CONTROL_MASK) != 0
938 && o_single_click == 0);
939 break;
940 case GDK_KEY_PRESS:
941 shift = kevent->state & GDK_SHIFT_MASK;
942 adjust = FALSE;
943 break;
944 default:
945 shift = FALSE;
946 adjust = FALSE;
947 break;
950 filer_openitem(filer_window, item_number, shift, adjust);
953 /* Return the full path to the directory containing object 'path'.
954 * Relative paths are resolved from the filerwindow's path.
956 static void follow_symlink(FilerWindow *filer_window, char *path)
958 char *real, *slash;
959 char *new_dir;
961 if (path[0] != '/')
962 path = make_path(filer_window->path, path)->str;
964 real = pathdup(path);
965 slash = strrchr(real, '/');
966 if (!slash)
968 g_free(real);
969 delayed_error("ROX-Filer",
970 "Broken symlink (or you don't have permission "
971 "to follow it.");
972 return;
975 *slash = '\0';
977 if (*real)
978 new_dir = real;
979 else
980 new_dir = "/";
982 if (filer_window->panel)
983 filer_opendir(new_dir, FALSE, BOTTOM);
984 else
985 filer_change_to(filer_window, new_dir, slash + 1);
987 g_free(real);
990 void filer_openitem(FilerWindow *filer_window, int item_number,
991 gboolean shift, gboolean adjust)
993 GtkWidget *widget;
994 char *full_path;
995 DirItem *item = (DirItem *)
996 filer_window->collection->items[item_number].data;
997 gboolean wink = TRUE;
999 widget = filer_window->window;
1000 full_path = make_path(filer_window->path,
1001 item->leafname)->str;
1003 if (item->flags & ITEM_FLAG_SYMLINK && shift)
1005 char path[MAXPATHLEN + 1];
1006 int got;
1008 got = readlink(make_path(filer_window->path,
1009 item->leafname)->str,
1010 path, MAXPATHLEN);
1011 if (got < 0)
1012 delayed_error("ROX-Filer", g_strerror(errno));
1013 else
1015 g_return_if_fail(got <= MAXPATHLEN);
1016 path[got] = '\0';
1018 follow_symlink(filer_window, path);
1020 return;
1023 switch (item->base_type)
1025 case TYPE_DIRECTORY:
1026 if (item->flags & ITEM_FLAG_APPDIR && !shift)
1028 run_app(make_path(filer_window->path,
1029 item->leafname)->str);
1030 if (adjust && !filer_window->panel)
1031 gtk_widget_destroy(widget);
1032 break;
1035 if (item->flags & ITEM_FLAG_MOUNT_POINT && shift)
1037 action_mount(filer_window, item);
1038 if (item->flags & ITEM_FLAG_MOUNTED)
1039 break;
1042 if ((adjust ^ o_new_window_on_1) || filer_window->panel)
1043 filer_opendir(full_path, FALSE, BOTTOM);
1044 else
1046 wink = FALSE;
1047 filer_change_to(filer_window, full_path, NULL);
1049 break;
1050 case TYPE_FILE:
1051 if (item->flags & ITEM_FLAG_EXEC_FILE
1052 && !shift)
1054 char *argv[] = {NULL, NULL};
1056 argv[0] = full_path;
1058 if (spawn_full(argv, getenv("HOME"), 0))
1060 if (adjust && !filer_window->panel)
1062 wink = FALSE;
1063 gtk_widget_destroy(widget);
1066 else
1067 report_error("ROX-Filer",
1068 "Failed to fork() child");
1070 else
1072 GString *message;
1073 MIME_type *type = shift ? &text_plain
1074 : item->mime_type;
1076 g_return_if_fail(type != NULL);
1078 if (type_open(full_path, type))
1080 if (adjust && !filer_window->panel)
1082 wink = FALSE;
1083 gtk_widget_destroy(widget);
1086 else
1088 message = g_string_new(NULL);
1089 g_string_sprintf(message, "No open "
1090 "action specified for files of "
1091 "this type (%s/%s)",
1092 type->media_type,
1093 type->subtype);
1094 report_error("ROX-Filer", message->str);
1095 g_string_free(message, TRUE);
1098 break;
1099 default:
1100 report_error("open_item",
1101 "I don't know how to open that");
1102 break;
1105 if (wink)
1106 collection_wink_item(filer_window->collection, item_number);
1109 static gint pointer_in(GtkWidget *widget,
1110 GdkEventCrossing *event,
1111 FilerWindow *filer_window)
1113 may_rescan(filer_window, TRUE);
1114 return FALSE;
1117 static gint focus_in(GtkWidget *widget,
1118 GdkEventFocus *event,
1119 FilerWindow *filer_window)
1121 window_with_focus = filer_window;
1123 return FALSE;
1126 static gint focus_out(GtkWidget *widget,
1127 GdkEventFocus *event,
1128 FilerWindow *filer_window)
1130 /* TODO: Shade the cursor */
1132 return FALSE;
1135 /* Handle keys that can't be bound with the menu */
1136 static gint key_press_event(GtkWidget *widget,
1137 GdkEventKey *event,
1138 FilerWindow *filer_window)
1140 switch (event->keyval)
1142 case GDK_BackSpace:
1143 change_to_parent(filer_window);
1144 break;
1145 default:
1146 return FALSE;
1149 return TRUE;
1152 static void toolbar_refresh_clicked(GtkWidget *widget,
1153 FilerWindow *filer_window)
1155 full_refresh();
1156 update_dir(filer_window, TRUE);
1159 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window)
1161 filer_change_to(filer_window, getenv("HOME"), NULL);
1164 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window)
1166 change_to_parent(filer_window);
1169 void change_to_parent(FilerWindow *filer_window)
1171 char *copy;
1172 char *slash;
1174 if (filer_window->path[0] == '/' && filer_window->path[1] == '\0')
1175 return; /* Already in the root */
1177 copy = g_strdup(filer_window->path);
1178 slash = strrchr(copy, '/');
1180 if (slash)
1182 *slash = '\0';
1183 filer_change_to(filer_window,
1184 *copy ? copy : "/",
1185 slash + 1);
1187 else
1188 g_warning("No / in directory path!\n");
1190 g_free(copy);
1194 /* Make filer_window display path. When finished, highlight item 'from', or
1195 * the first item if from is NULL. If there is currently no cursor then
1196 * simply wink 'from' (if not NULL).
1198 static void filer_change_to(FilerWindow *filer_window, char *path, char *from)
1200 char *from_dup;
1202 from_dup = from && *from ? g_strdup(from) : NULL;
1204 detach(filer_window);
1205 g_free(filer_window->path);
1206 filer_window->path = pathdup(path);
1208 filer_window->directory = g_fscache_lookup(dir_cache,
1209 filer_window->path);
1210 if (filer_window->directory)
1212 g_free(filer_window->auto_select);
1213 filer_window->had_cursor =
1214 filer_window->collection->cursor_item != -1
1215 || filer_window->had_cursor;
1216 filer_window->auto_select = from_dup;
1218 gtk_window_set_title(GTK_WINDOW(filer_window->window),
1219 filer_window->path);
1220 collection_set_cursor_item(filer_window->collection, -1);
1221 attach(filer_window);
1223 else
1225 char *error;
1227 g_free(from_dup);
1228 error = g_strdup_printf("Directory '%s' is not accessible.",
1229 path);
1230 delayed_error("ROX-Filer", error);
1231 g_free(error);
1232 gtk_widget_destroy(filer_window->window);
1236 int selected_item_number(Collection *collection)
1238 int i;
1240 g_return_val_if_fail(collection != NULL, -1);
1241 g_return_val_if_fail(IS_COLLECTION(collection), -1);
1242 g_return_val_if_fail(collection->number_selected == 1, -1);
1244 for (i = 0; i < collection->number_of_items; i++)
1245 if (collection->items[i].selected)
1246 return i;
1248 g_warning("selected_item: number_selected is wrong\n");
1250 return -1;
1253 DirItem *selected_item(Collection *collection)
1255 int item;
1257 item = selected_item_number(collection);
1259 if (item > -1)
1260 return (DirItem *) collection->items[item].data;
1261 return NULL;
1264 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
1265 FilerWindow *window)
1267 /* TODO: We can open lots of these - very irritating! */
1268 return get_choice("Close panel?",
1269 "You have tried to close a panel via the window "
1270 "manager - I usually find that this is accidental... "
1271 "really close?",
1272 2, "Remove", "Cancel") != 0;
1275 /* Make the items as narrow as possible */
1276 static void shrink_width(FilerWindow *filer_window)
1278 int i;
1279 Collection *col = filer_window->collection;
1280 int width = MIN_ITEM_WIDTH;
1281 int this_width;
1282 DisplayStyle style = filer_window->display_style;
1283 GdkFont *font;
1284 int text_height;
1286 font = gtk_widget_get_default_style()->font;
1287 text_height = font->ascent + font->descent;
1289 for (i = 0; i < col->number_of_items; i++)
1291 this_width = calc_width(filer_window,
1292 (DirItem *) col->items[i].data);
1293 if (this_width > width)
1294 width = this_width;
1297 collection_set_item_size(filer_window->collection,
1298 width,
1299 style == FULL_INFO ? MAX_ICON_HEIGHT + 4 :
1300 style == SMALL_ICONS ? MAX(text_height, SMALL_ICON_HEIGHT) + 4
1301 : text_height + MAX_ICON_HEIGHT + 8);
1304 void filer_set_sort_fn(FilerWindow *filer_window,
1305 int (*fn)(const void *a, const void *b))
1307 if (filer_window->sort_fn == fn)
1308 return;
1310 filer_window->sort_fn = fn;
1311 collection_qsort(filer_window->collection,
1312 filer_window->sort_fn);
1315 void filer_style_set(FilerWindow *filer_window, DisplayStyle style)
1317 if (filer_window->display_style == style)
1318 return;
1320 filer_window->display_style = style;
1321 switch (style)
1323 case SMALL_ICONS:
1324 collection_set_functions(filer_window->collection,
1325 draw_item_small, test_point_small);
1326 break;
1327 case FULL_INFO:
1328 collection_set_functions(filer_window->collection,
1329 draw_item_full_info, test_point_full_info);
1330 break;
1331 default:
1332 collection_set_functions(filer_window->collection,
1333 draw_item_large, test_point_large);
1334 break;
1337 shrink_width(filer_window);
1340 void filer_opendir(char *path, gboolean panel, Side panel_side)
1342 GtkWidget *hbox, *scrollbar, *collection;
1343 FilerWindow *filer_window;
1344 GtkTargetEntry target_table[] =
1346 {"text/uri-list", 0, TARGET_URI_LIST},
1347 {"STRING", 0, TARGET_STRING},
1350 filer_window = g_new(FilerWindow, 1);
1351 filer_window->path = pathdup(path);
1352 filer_window->had_cursor = FALSE;
1353 filer_window->auto_select = NULL;
1355 filer_window->directory = g_fscache_lookup(dir_cache,
1356 filer_window->path);
1357 if (!filer_window->directory)
1359 char *error;
1361 error = g_strdup_printf("Directory '%s' not found.", path);
1362 delayed_error("ROX-Filer", error);
1363 g_free(error);
1364 g_free(filer_window->path);
1365 g_free(filer_window);
1366 return;
1369 filer_window->show_hidden = FALSE;
1370 filer_window->panel = panel;
1371 filer_window->panel_side = panel_side;
1372 filer_window->temp_item_selected = FALSE;
1373 filer_window->sort_fn = sort_by_type;
1374 filer_window->flags = (FilerFlags) 0;
1375 filer_window->display_style = UNKNOWN_STYLE;
1377 filer_window->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1378 gtk_window_set_title(GTK_WINDOW(filer_window->window),
1379 filer_window->path);
1381 collection = collection_new(NULL);
1382 gtk_object_set_data(GTK_OBJECT(collection),
1383 "filer_window", filer_window);
1384 filer_window->collection = COLLECTION(collection);
1386 gtk_widget_add_events(filer_window->window, GDK_ENTER_NOTIFY);
1387 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1388 "enter-notify-event",
1389 GTK_SIGNAL_FUNC(pointer_in), filer_window);
1390 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_in_event",
1391 GTK_SIGNAL_FUNC(focus_in), filer_window);
1392 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_out_event",
1393 GTK_SIGNAL_FUNC(focus_out), filer_window);
1394 gtk_signal_connect(GTK_OBJECT(filer_window->window), "destroy",
1395 filer_window_destroyed, filer_window);
1397 gtk_signal_connect(GTK_OBJECT(filer_window->collection), "open_item",
1398 open_item, filer_window);
1399 gtk_signal_connect(GTK_OBJECT(collection), "show_menu",
1400 show_menu, filer_window);
1401 gtk_signal_connect(GTK_OBJECT(collection), "gain_selection",
1402 gain_selection, filer_window);
1403 gtk_signal_connect(GTK_OBJECT(collection), "lose_selection",
1404 lose_selection, filer_window);
1405 gtk_signal_connect(GTK_OBJECT(collection), "drag_selection",
1406 drag_selection, filer_window);
1407 gtk_signal_connect(GTK_OBJECT(collection), "drag_data_get",
1408 drag_data_get, filer_window);
1409 gtk_signal_connect(GTK_OBJECT(collection), "selection_clear_event",
1410 GTK_SIGNAL_FUNC(collection_lose_selection), NULL);
1411 gtk_signal_connect (GTK_OBJECT(collection), "selection_get",
1412 GTK_SIGNAL_FUNC(selection_get), NULL);
1413 gtk_selection_add_targets(collection, GDK_SELECTION_PRIMARY,
1414 target_table,
1415 sizeof(target_table) / sizeof(*target_table));
1417 filer_style_set(filer_window, LARGE_ICONS);
1418 drag_set_dest(collection);
1420 if (panel)
1422 int swidth, sheight, iwidth, iheight;
1423 GtkWidget *frame, *win = filer_window->window;
1425 collection_set_panel(filer_window->collection, TRUE);
1426 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1427 "delete_event",
1428 GTK_SIGNAL_FUNC(filer_confirm_close),
1429 filer_window);
1431 gdk_window_get_size(GDK_ROOT_PARENT(), &swidth, &sheight);
1432 iwidth = filer_window->collection->item_width;
1433 iheight = filer_window->collection->item_height;
1435 if (panel_side == TOP || panel_side == BOTTOM)
1437 int height = iheight + PANEL_BORDER;
1438 int y = panel_side == TOP
1439 ? -PANEL_BORDER
1440 : sheight - height - PANEL_BORDER;
1442 gtk_widget_set_usize(collection, swidth, height);
1443 gtk_widget_set_uposition(win, 0, y);
1445 else
1447 int width = iwidth + PANEL_BORDER;
1448 int x = panel_side == LEFT
1449 ? -PANEL_BORDER
1450 : swidth - width - PANEL_BORDER;
1452 gtk_widget_set_usize(collection, width, sheight);
1453 gtk_widget_set_uposition(win, x, 0);
1456 frame = gtk_frame_new(NULL);
1457 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1458 gtk_container_add(GTK_CONTAINER(frame), collection);
1459 gtk_container_add(GTK_CONTAINER(win), frame);
1461 gtk_widget_realize(win);
1462 if (override_redirect)
1463 gdk_window_set_override_redirect(win->window, TRUE);
1464 make_panel_window(win->window);
1466 else
1468 hbox = gtk_hbox_new(FALSE, 0);
1470 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1471 "key_press_event",
1472 GTK_SIGNAL_FUNC(key_press_event), filer_window);
1473 gtk_window_set_default_size(GTK_WINDOW(filer_window->window),
1474 filer_window->display_style == LARGE_ICONS ? 400 : 512,
1475 o_toolbar ? 220 : 200);
1477 gtk_container_add(GTK_CONTAINER(filer_window->window),
1478 hbox);
1479 if (o_toolbar)
1481 GtkWidget *vbox, *toolbar;
1484 vbox = gtk_vbox_new(FALSE, 0);
1485 gtk_box_pack_start(GTK_BOX(hbox), vbox,
1486 TRUE, TRUE, 0);
1487 toolbar = create_toolbar(filer_window);
1488 gtk_box_pack_start(GTK_BOX(vbox), toolbar,
1489 FALSE, TRUE, 0);
1491 gtk_box_pack_start(GTK_BOX(vbox), collection,
1492 TRUE, TRUE, 0);
1494 else
1495 gtk_box_pack_start(GTK_BOX(hbox), collection,
1496 TRUE, TRUE, 0);
1498 scrollbar = gtk_vscrollbar_new(COLLECTION(collection)->vadj);
1499 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, TRUE, 0);
1500 gtk_accel_group_attach(filer_keys,
1501 GTK_OBJECT(filer_window->window));
1502 gtk_window_set_focus(GTK_WINDOW(filer_window->window),
1503 collection);
1506 number_of_windows++;
1507 gtk_widget_show_all(filer_window->window);
1508 attach(filer_window);
1510 all_filer_windows = g_list_prepend(all_filer_windows, filer_window);
1513 static GtkWidget *create_toolbar(FilerWindow *filer_window)
1515 GtkWidget *frame, *box;
1517 frame = gtk_frame_new(NULL);
1518 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1520 box = gtk_hbutton_box_new();
1521 gtk_button_box_set_child_size_default(16, 16);
1522 gtk_hbutton_box_set_spacing_default(2);
1523 gtk_button_box_set_layout(GTK_BUTTON_BOX(box), GTK_BUTTONBOX_START);
1524 gtk_container_add(GTK_CONTAINER(frame), box);
1525 add_button(GTK_CONTAINER(box), TOOLBAR_UP_ICON,
1526 GTK_SIGNAL_FUNC(toolbar_up_clicked),
1527 filer_window);
1528 add_button(GTK_CONTAINER(box), TOOLBAR_HOME_ICON,
1529 GTK_SIGNAL_FUNC(toolbar_home_clicked),
1530 filer_window);
1531 add_button(GTK_CONTAINER(box), TOOLBAR_REFRESH_ICON,
1532 GTK_SIGNAL_FUNC(toolbar_refresh_clicked),
1533 filer_window);
1535 return frame;
1538 static void add_button(GtkContainer *box, int pixmap,
1539 GtkSignalFunc cb, gpointer data)
1541 GtkWidget *button, *icon;
1543 button = gtk_button_new();
1544 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
1545 gtk_container_add(box, button);
1547 icon = gtk_pixmap_new(default_pixmap[pixmap].pixmap,
1548 default_pixmap[pixmap].mask);
1549 gtk_container_add(GTK_CONTAINER(button), icon);
1550 gtk_signal_connect(GTK_OBJECT(button), "clicked", cb, data);
1553 /* Build up some option widgets to go in the options dialog, but don't
1554 * fill them in yet.
1556 static GtkWidget *create_options()
1558 GtkWidget *vbox;
1560 vbox = gtk_vbox_new(FALSE, 0);
1561 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
1563 toggle_new_window_on_1 =
1564 gtk_check_button_new_with_label("New window on button 1 "
1565 "(RISC OS style)");
1566 gtk_box_pack_start(GTK_BOX(vbox), toggle_new_window_on_1,
1567 FALSE, TRUE, 0);
1569 toggle_menu_on_2 =
1570 gtk_check_button_new_with_label("Menu on button 2 "
1571 "(RISC OS style)");
1572 gtk_box_pack_start(GTK_BOX(vbox), toggle_menu_on_2, FALSE, TRUE, 0);
1574 toggle_single_click =
1575 gtk_check_button_new_with_label("Single-click nagivation");
1576 gtk_box_pack_start(GTK_BOX(vbox), toggle_single_click, FALSE, TRUE, 0);
1578 toggle_toolbar =
1579 gtk_check_button_new_with_label("Show toolbar on new windows");
1580 gtk_box_pack_start(GTK_BOX(vbox), toggle_toolbar, FALSE, TRUE, 0);
1582 return vbox;
1585 /* Reflect current state by changing the widgets in the options box */
1586 static void update_options()
1588 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_new_window_on_1),
1589 o_new_window_on_1);
1590 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_menu_on_2),
1591 collection_menu_button == 2 ? 1 : 0);
1592 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_single_click),
1593 o_single_click);
1594 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_toolbar),
1595 o_toolbar);
1598 /* Set current values by reading the states of the widgets in the options box */
1599 static void set_options()
1601 o_new_window_on_1 = gtk_toggle_button_get_active(
1602 GTK_TOGGLE_BUTTON(toggle_new_window_on_1));
1604 collection_menu_button = gtk_toggle_button_get_active(
1605 GTK_TOGGLE_BUTTON(toggle_menu_on_2)) ? 2 : 3;
1607 o_single_click = gtk_toggle_button_get_active(
1608 GTK_TOGGLE_BUTTON(toggle_single_click));
1609 collection_single_click = o_single_click ? TRUE : FALSE;
1611 o_toolbar = gtk_toggle_button_get_active(
1612 GTK_TOGGLE_BUTTON(toggle_toolbar));
1615 static void save_options()
1617 option_write("filer_new_window_on_1", o_new_window_on_1 ? "1" : "0");
1618 option_write("filer_menu_on_2",
1619 collection_menu_button == 2 ? "1" : "0");
1620 option_write("filer_single_click", o_single_click ? "1" : "0");
1621 option_write("filer_toolbar", o_toolbar ? "1" : "0");
1624 static char *filer_new_window_on_1(char *data)
1626 o_new_window_on_1 = atoi(data) != 0;
1627 return NULL;
1630 static char *filer_menu_on_2(char *data)
1632 collection_menu_button = atoi(data) != 0 ? 2 : 3;
1633 return NULL;
1636 static char *filer_single_click(char *data)
1638 o_single_click = atoi(data) != 0;
1639 collection_single_click = o_single_click ? TRUE : FALSE;
1640 return NULL;
1643 static char *filer_toolbar(char *data)
1645 o_toolbar = atoi(data) != 0;
1646 return NULL;
1649 /* Note that filer_window may not exist after this call. */
1650 void update_dir(FilerWindow *filer_window, gboolean warning)
1652 if (may_rescan(filer_window, warning))
1653 dir_update(filer_window->directory, filer_window->path);
1656 void filer_set_hidden(FilerWindow *filer_window, gboolean hidden)
1658 Directory *dir = filer_window->directory;
1660 if (filer_window->show_hidden == hidden)
1661 return;
1663 filer_window->show_hidden = hidden;
1665 g_fscache_data_ref(dir_cache, dir);
1666 detach(filer_window);
1667 filer_window->directory = dir;
1668 attach(filer_window);
1671 /* Refresh the various caches even if we don't think we need to */
1672 void full_refresh(void)
1674 mount_update(TRUE);
1677 /* This path has been mounted/umounted - update all dirs */
1678 void filer_check_mounted(char *path)
1680 GList *next = all_filer_windows;
1681 int len;
1683 len = strlen(path);
1685 while (next)
1687 FilerWindow *filer_window = (FilerWindow *) next->data;
1689 next = next->next;
1691 if (strncmp(path, filer_window->path, len) == 0)
1693 char s = filer_window->path[len];
1695 if (s == '/' || s == '\0')
1696 update_dir(filer_window, FALSE);