r173: Finished the minibuffer code. It's nice!
[rox-filer.git] / ROX-Filer / src / filer.c
blob53a530e2bd58b15bf32805987f266504c588edd0
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"
51 #include "minibuffer.h"
53 #define ROW_HEIGHT_LARGE 64
54 #define ROW_HEIGHT_SMALL 32
55 #define ROW_HEIGHT_FULL_INFO 44
56 #define SMALL_ICON_HEIGHT 20
57 #define SMALL_ICON_WIDTH 48
58 #define MAX_ICON_HEIGHT 42
59 #define MAX_ICON_WIDTH 48
60 #define PANEL_BORDER 2
61 #define MIN_ITEM_WIDTH 64
63 extern int collection_menu_button;
64 extern gboolean collection_single_click;
66 FilerWindow *window_with_focus = NULL;
67 GdkFont *fixed_font = NULL;
68 GList *all_filer_windows = NULL;
70 static FilerWindow *window_with_selection = NULL;
72 /* Options bits */
73 static GtkWidget *create_options();
74 static void update_options();
75 static void set_options();
76 static void save_options();
77 static char *filer_single_click(char *data);
78 static char *filer_menu_on_2(char *data);
79 static char *filer_new_window_on_1(char *data);
80 static char *filer_toolbar(char *data);
82 static OptionsSection options =
84 "Filer window options",
85 create_options,
86 update_options,
87 set_options,
88 save_options
90 static gboolean o_toolbar = TRUE;
91 static GtkWidget *toggle_toolbar;
92 static gboolean o_single_click = FALSE;
93 static GtkWidget *toggle_single_click;
94 static gboolean o_new_window_on_1 = FALSE; /* Button 1 => New window */
95 static GtkWidget *toggle_new_window_on_1;
96 static GtkWidget *toggle_menu_on_2;
98 /* Static prototypes */
99 static void attach(FilerWindow *filer_window);
100 static void detach(FilerWindow *filer_window);
101 static void filer_window_destroyed(GtkWidget *widget,
102 FilerWindow *filer_window);
103 static void show_menu(Collection *collection, GdkEventButton *event,
104 int number_selected, gpointer user_data);
105 static gint focus_in(GtkWidget *widget,
106 GdkEventFocus *event,
107 FilerWindow *filer_window);
108 static gint focus_out(GtkWidget *widget,
109 GdkEventFocus *event,
110 FilerWindow *filer_window);
111 static void add_item(FilerWindow *filer_window, DirItem *item);
112 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window);
113 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window);
114 static void add_button(GtkContainer *box, int pixmap,
115 GtkSignalFunc cb, gpointer data, char *tip);
116 static GtkWidget *create_toolbar(FilerWindow *filer_window);
117 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
118 FilerWindow *window);
119 static int calc_width(FilerWindow *filer_window, DirItem *item);
120 static void draw_large_icon(GtkWidget *widget,
121 GdkRectangle *area,
122 DirItem *item,
123 gboolean selected);
124 static void draw_string(GtkWidget *widget,
125 GdkFont *font,
126 char *string,
127 int x,
128 int y,
129 int width,
130 gboolean selected);
131 static void draw_item_large(GtkWidget *widget,
132 CollectionItem *item,
133 GdkRectangle *area);
134 static void draw_item_small(GtkWidget *widget,
135 CollectionItem *item,
136 GdkRectangle *area);
137 static void draw_item_full_info(GtkWidget *widget,
138 CollectionItem *colitem,
139 GdkRectangle *area);
140 static gboolean test_point_large(Collection *collection,
141 int point_x, int point_y,
142 CollectionItem *item,
143 int width, int height);
144 static gboolean test_point_small(Collection *collection,
145 int point_x, int point_y,
146 CollectionItem *item,
147 int width, int height);
148 static gboolean test_point_full_info(Collection *collection,
149 int point_x, int point_y,
150 CollectionItem *item,
151 int width, int height);
152 static void update_display(Directory *dir,
153 DirAction action,
154 GPtrArray *items,
155 FilerWindow *filer_window);
156 static void shrink_width(FilerWindow *filer_window);
157 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning);
158 static void open_item(Collection *collection,
159 gpointer item_data, int item_number,
160 gpointer user_data);
162 static GdkAtom xa_string;
163 enum
165 TARGET_STRING,
166 TARGET_URI_LIST,
169 static GdkCursor *busy_cursor = NULL;
170 static GtkTooltips *tooltips = NULL;
172 void filer_init()
174 xa_string = gdk_atom_intern("STRING", FALSE);
176 options_sections = g_slist_prepend(options_sections, &options);
177 option_register("filer_new_window_on_1", filer_new_window_on_1);
178 option_register("filer_menu_on_2", filer_menu_on_2);
179 option_register("filer_single_click", filer_single_click);
180 option_register("filer_toolbar", filer_toolbar);
182 fixed_font = gdk_font_load("fixed");
184 busy_cursor = gdk_cursor_new(GDK_WATCH);
186 tooltips = gtk_tooltips_new();
189 static gboolean if_deleted(gpointer item, gpointer removed)
191 int i = ((GPtrArray *) removed)->len;
192 DirItem **r = (DirItem **) ((GPtrArray *) removed)->pdata;
193 char *leafname = ((DirItem *) item)->leafname;
195 while (i--)
197 if (strcmp(leafname, r[i]->leafname) == 0)
198 return TRUE;
201 return FALSE;
204 static void update_item(FilerWindow *filer_window, DirItem *item)
206 int i;
207 char *leafname = item->leafname;
209 if (leafname[0] == '.')
211 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
212 || (leafname[1] == '.' && leafname[2] == '\0'))
213 return;
216 i = collection_find_item(filer_window->collection, item, dir_item_cmp);
218 if (i >= 0)
219 collection_draw_item(filer_window->collection, i, TRUE);
220 else
221 g_warning("Failed to find '%s'\n", item->leafname);
224 static void update_display(Directory *dir,
225 DirAction action,
226 GPtrArray *items,
227 FilerWindow *filer_window)
229 int i;
230 int cursor = filer_window->collection->cursor_item;
231 char *as;
232 Collection *collection = filer_window->collection;
234 switch (action)
236 case DIR_ADD:
237 as = filer_window->auto_select;
239 for (i = 0; i < items->len; i++)
241 DirItem *item = (DirItem *) items->pdata[i];
243 add_item(filer_window, item);
245 if (cursor != -1 || !as)
246 continue;
248 if (strcmp(as, item->leafname) != 0)
249 continue;
251 cursor = collection->number_of_items - 1;
252 if (filer_window->had_cursor)
254 collection_set_cursor_item(collection,
255 cursor);
256 filer_window->mini_cursor_base = cursor;
258 else
259 collection_wink_item(collection,
260 cursor);
263 collection_qsort(filer_window->collection,
264 filer_window->sort_fn);
265 break;
266 case DIR_REMOVE:
267 collection_delete_if(filer_window->collection,
268 if_deleted,
269 items);
270 break;
271 case DIR_END_SCAN:
272 if (filer_window->window->window)
273 gdk_window_set_cursor(
274 filer_window->window->window,
275 NULL);
276 shrink_width(filer_window);
277 if (filer_window->had_cursor &&
278 collection->cursor_item == -1)
280 collection_set_cursor_item(collection, 0);
281 filer_window->had_cursor = FALSE;
283 break;
284 case DIR_UPDATE:
285 for (i = 0; i < items->len; i++)
287 DirItem *item = (DirItem *) items->pdata[i];
289 update_item(filer_window, item);
291 collection_qsort(filer_window->collection,
292 filer_window->sort_fn);
293 break;
297 static void attach(FilerWindow *filer_window)
299 gdk_window_set_cursor(filer_window->window->window, busy_cursor);
300 collection_clear(filer_window->collection);
301 dir_attach(filer_window->directory, (DirCallback) update_display,
302 filer_window);
305 static void detach(FilerWindow *filer_window)
307 g_return_if_fail(filer_window->directory != NULL);
309 dir_detach(filer_window->directory,
310 (DirCallback) update_display, filer_window);
311 g_fscache_data_unref(dir_cache, filer_window->directory);
312 filer_window->directory = NULL;
315 static void filer_window_destroyed(GtkWidget *widget,
316 FilerWindow *filer_window)
318 all_filer_windows = g_list_remove(all_filer_windows, filer_window);
320 gtk_idle_remove_by_data(filer_window);
322 if (window_with_selection == filer_window)
323 window_with_selection = NULL;
324 if (window_with_focus == filer_window)
325 window_with_focus = NULL;
327 if (filer_window->directory)
328 detach(filer_window);
330 g_free(filer_window->auto_select);
331 g_free(filer_window->path);
332 g_free(filer_window);
334 if (--number_of_windows < 1)
335 gtk_main_quit();
338 static int calc_width(FilerWindow *filer_window, DirItem *item)
340 int pix_width = item->image->width;
342 switch (filer_window->display_style)
344 case FULL_INFO:
345 return MAX_ICON_WIDTH + 12 +
346 MAX(item->details_width, item->name_width);
347 case SMALL_ICONS:
348 return SMALL_ICON_WIDTH + 12 + item->name_width;
349 default:
350 return MAX(pix_width, item->name_width) + 4;
354 /* Add a single object to a directory display */
355 static void add_item(FilerWindow *filer_window, DirItem *item)
357 char *leafname = item->leafname;
358 int item_width;
360 if (leafname[0] == '.')
362 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
363 || (leafname[1] == '.' && leafname[2] == '\0'))
364 return;
367 item_width = calc_width(filer_window, item);
368 if (item_width > filer_window->collection->item_width)
369 collection_set_item_size(filer_window->collection,
370 item_width,
371 filer_window->collection->item_height);
372 collection_insert(filer_window->collection, item);
375 /* Is a point inside an item? */
376 static gboolean test_point_large(Collection *collection,
377 int point_x, int point_y,
378 CollectionItem *colitem,
379 int width, int height)
381 DirItem *item = (DirItem *) colitem->data;
382 GdkFont *font = GTK_WIDGET(collection)->style->font;
383 int text_height = font->ascent + font->descent;
384 MaskedPixmap *image = item->image;
385 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
386 int image_width = (image->width >> 1) + 2;
387 int text_width = (item->name_width >> 1) + 2;
388 int x_limit;
390 if (point_y < image_y)
391 return FALSE; /* Too high up (don't worry about too low) */
393 if (point_y <= image_y + image->height + 2)
394 x_limit = image_width;
395 else if (point_y > height - text_height - 2)
396 x_limit = text_width;
397 else
398 x_limit = MIN(image_width, text_width);
400 return ABS(point_x - (width >> 1)) < x_limit;
403 static gboolean test_point_full_info(Collection *collection,
404 int point_x, int point_y,
405 CollectionItem *colitem,
406 int width, int height)
408 DirItem *item = (DirItem *) colitem->data;
409 GdkFont *font = GTK_WIDGET(collection)->style->font;
410 MaskedPixmap *image = item->image;
411 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
412 int low_top = height
413 - fixed_font->descent - 2 - fixed_font->ascent;
415 if (point_x < image->width + 2)
416 return point_x > 2 && point_y > image_y;
418 point_x -= MAX_ICON_WIDTH + 8;
420 if (point_y >= low_top)
421 return point_x < item->details_width;
422 if (point_y >= low_top - font->ascent - font->descent)
423 return point_x < item->name_width;
424 return FALSE;
427 static gboolean test_point_small(Collection *collection,
428 int point_x, int point_y,
429 CollectionItem *colitem,
430 int width, int height)
432 DirItem *item = (DirItem *) colitem->data;
433 MaskedPixmap *image = item->image;
434 int image_y = MAX(0, SMALL_ICON_HEIGHT - image->height);
435 GdkFont *font = GTK_WIDGET(collection)->style->font;
436 int low_top = height
437 - fixed_font->descent - 2 - font->ascent;
438 int iwidth = MIN(SMALL_ICON_WIDTH, image->width);
440 if (point_x < iwidth + 2)
441 return point_x > 2 && point_y > image_y;
443 point_x -= SMALL_ICON_WIDTH + 4;
445 if (point_y >= low_top)
446 return point_x < item->name_width;
447 return FALSE;
450 static void draw_small_icon(GtkWidget *widget,
451 GdkRectangle *area,
452 DirItem *item,
453 gboolean selected)
455 MaskedPixmap *image = item->image;
456 int width = MIN(image->width, SMALL_ICON_WIDTH);
457 int height = MIN(image->height, SMALL_ICON_HEIGHT);
458 int image_x = area->x + ((area->width - width) >> 1);
459 int image_y;
460 GdkGC *gc = selected ? widget->style->white_gc
461 : widget->style->black_gc;
462 if (!item->image)
463 return;
465 gdk_gc_set_clip_mask(gc, item->image->mask);
467 image_y = MAX(0, SMALL_ICON_HEIGHT - image->height);
468 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
469 gdk_draw_pixmap(widget->window, gc,
470 item->image->pixmap,
471 0, 0, /* Source x,y */
472 image_x, area->y + image_y, /* Dest x,y */
473 width, height);
475 if (selected)
477 gdk_gc_set_function(gc, GDK_INVERT);
478 gdk_draw_rectangle(widget->window,
480 TRUE, image_x, area->y + image_y,
481 width, height);
482 gdk_gc_set_function(gc, GDK_COPY);
485 if (item->flags & ITEM_FLAG_SYMLINK)
487 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
488 gdk_gc_set_clip_mask(gc,
489 default_pixmap[TYPE_SYMLINK].mask);
490 gdk_draw_pixmap(widget->window, gc,
491 default_pixmap[TYPE_SYMLINK].pixmap,
492 0, 0, /* Source x,y */
493 image_x, area->y + 8, /* Dest x,y */
494 -1, -1);
496 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
498 int type = item->flags & ITEM_FLAG_MOUNTED
499 ? TYPE_MOUNTED
500 : TYPE_UNMOUNTED;
501 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
502 gdk_gc_set_clip_mask(gc,
503 default_pixmap[type].mask);
504 gdk_draw_pixmap(widget->window, gc,
505 default_pixmap[type].pixmap,
506 0, 0, /* Source x,y */
507 image_x, area->y + 8, /* Dest x,y */
508 -1, -1);
511 gdk_gc_set_clip_mask(gc, NULL);
512 gdk_gc_set_clip_origin(gc, 0, 0);
515 static void draw_large_icon(GtkWidget *widget,
516 GdkRectangle *area,
517 DirItem *item,
518 gboolean selected)
520 MaskedPixmap *image = item->image;
521 int width = MIN(image->width, MAX_ICON_WIDTH);
522 int height = MIN(image->height, MAX_ICON_WIDTH);
523 int image_x = area->x + ((area->width - width) >> 1);
524 int image_y;
525 GdkGC *gc = selected ? widget->style->white_gc
526 : widget->style->black_gc;
528 gdk_gc_set_clip_mask(gc, item->image->mask);
530 image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
531 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
532 gdk_draw_pixmap(widget->window, gc,
533 item->image->pixmap,
534 0, 0, /* Source x,y */
535 image_x, area->y + image_y, /* Dest x,y */
536 width, height);
538 if (selected)
540 gdk_gc_set_function(gc, GDK_INVERT);
541 gdk_draw_rectangle(widget->window,
543 TRUE, image_x, area->y + image_y,
544 width, height);
545 gdk_gc_set_function(gc, GDK_COPY);
548 if (item->flags & ITEM_FLAG_SYMLINK)
550 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
551 gdk_gc_set_clip_mask(gc,
552 default_pixmap[TYPE_SYMLINK].mask);
553 gdk_draw_pixmap(widget->window, gc,
554 default_pixmap[TYPE_SYMLINK].pixmap,
555 0, 0, /* Source x,y */
556 image_x, area->y + 8, /* Dest x,y */
557 -1, -1);
559 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
561 int type = item->flags & ITEM_FLAG_MOUNTED
562 ? TYPE_MOUNTED
563 : TYPE_UNMOUNTED;
564 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
565 gdk_gc_set_clip_mask(gc,
566 default_pixmap[type].mask);
567 gdk_draw_pixmap(widget->window, gc,
568 default_pixmap[type].pixmap,
569 0, 0, /* Source x,y */
570 image_x, area->y + 8, /* Dest x,y */
571 -1, -1);
574 gdk_gc_set_clip_mask(gc, NULL);
575 gdk_gc_set_clip_origin(gc, 0, 0);
578 static void draw_string(GtkWidget *widget,
579 GdkFont *font,
580 char *string,
581 int x,
582 int y,
583 int width,
584 gboolean selected)
586 int text_height = font->ascent + font->descent;
588 if (selected)
589 gtk_paint_flat_box(widget->style, widget->window,
590 GTK_STATE_SELECTED, GTK_SHADOW_NONE,
591 NULL, widget, "text",
592 x, y - font->ascent,
593 width,
594 text_height);
596 gdk_draw_text(widget->window,
597 font,
598 selected ? widget->style->white_gc
599 : widget->style->black_gc,
600 x, y,
601 string, strlen(string));
604 /* Return a string (valid until next call) giving details
605 * of this item.
607 char *details(DirItem *item)
609 mode_t m = item->mode;
610 static GString *buf = NULL;
612 if (!buf)
613 buf = g_string_new(NULL);
615 g_string_sprintf(buf, "%s, %c%c%c:%c%c%c:%c%c%c:%c%c"
616 #ifdef S_ISVTX
617 "%c"
618 #endif
619 ", %s",
620 S_ISDIR(m) ? "Dir" :
621 S_ISCHR(m) ? "Char" :
622 S_ISBLK(m) ? "Blck" :
623 S_ISLNK(m) ? "Link" :
624 S_ISSOCK(m) ? "Sock" :
625 S_ISFIFO(m) ? "Pipe" : "File",
627 m & S_IRUSR ? 'r' : '-',
628 m & S_IWUSR ? 'w' : '-',
629 m & S_IXUSR ? 'x' : '-',
631 m & S_IRGRP ? 'r' : '-',
632 m & S_IWGRP ? 'w' : '-',
633 m & S_IXGRP ? 'x' : '-',
635 m & S_IROTH ? 'r' : '-',
636 m & S_IWOTH ? 'w' : '-',
637 m & S_IXOTH ? 'x' : '-',
639 m & S_ISUID ? 'U' : '-',
640 m & S_ISGID ? 'G' : '-',
641 #ifdef S_ISVTX
642 m & S_ISVTX ? 'T' : '-',
643 #endif
644 format_size(item->size));
646 return buf->str;
649 static void draw_item_full_info(GtkWidget *widget,
650 CollectionItem *colitem,
651 GdkRectangle *area)
653 DirItem *item = (DirItem *) colitem->data;
654 MaskedPixmap *image = item->image;
655 GdkFont *font = widget->style->font;
656 int text_x = area->x + MAX_ICON_WIDTH + 8;
657 int low_text_y = area->y + area->height - fixed_font->descent - 2;
658 gboolean selected = colitem->selected;
659 GdkRectangle pic_area;
661 pic_area.x = area->x;
662 pic_area.y = area->y;
663 pic_area.width = image->width + 8;
664 pic_area.height = area->height;
666 draw_large_icon(widget, &pic_area, item, selected);
668 draw_string(widget,
669 widget->style->font,
670 item->leafname,
671 text_x,
672 low_text_y - font->descent - fixed_font->ascent,
673 item->name_width,
674 selected);
675 draw_string(widget,
676 fixed_font,
677 details(item),
678 text_x, low_text_y,
679 item->details_width,
680 selected);
683 static void draw_item_small(GtkWidget *widget,
684 CollectionItem *colitem,
685 GdkRectangle *area)
687 DirItem *item = (DirItem *) colitem->data;
688 GdkFont *font = widget->style->font;
689 int text_x = area->x + SMALL_ICON_WIDTH + 4;
690 int low_text_y = area->y + area->height - font->descent - 2;
691 gboolean selected = colitem->selected;
692 GdkRectangle pic_area;
694 pic_area.x = area->x;
695 pic_area.y = area->y;
696 pic_area.width = SMALL_ICON_WIDTH;
697 pic_area.height = SMALL_ICON_HEIGHT;
699 draw_small_icon(widget, &pic_area, item, selected);
701 draw_string(widget,
702 widget->style->font,
703 item->leafname,
704 text_x,
705 low_text_y,
706 item->name_width,
707 selected);
710 static void draw_item_large(GtkWidget *widget,
711 CollectionItem *colitem,
712 GdkRectangle *area)
714 DirItem *item = (DirItem *) colitem->data;
715 GdkFont *font = widget->style->font;
716 int text_x = area->x + ((area->width - item->name_width) >> 1);
717 int text_y = area->y + area->height - font->descent - 2;
718 gboolean selected = colitem->selected;
720 draw_large_icon(widget, area, item, selected);
722 draw_string(widget,
723 widget->style->font,
724 item->leafname,
725 text_x, text_y, item->name_width,
726 selected);
729 static void show_menu(Collection *collection, GdkEventButton *event,
730 int item, gpointer user_data)
732 show_filer_menu((FilerWindow *) user_data, event, item);
735 /* Returns TRUE iff the directory still exits. */
736 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning)
738 Directory *dir;
740 g_return_val_if_fail(filer_window != NULL, FALSE);
742 /* We do a fresh lookup (rather than update) because the inode may
743 * have changed.
745 dir = g_fscache_lookup(dir_cache, filer_window->path);
746 if (!dir)
748 if (warning)
749 delayed_error("ROX-Filer", "Directory missing/deleted");
750 gtk_widget_destroy(filer_window->window);
751 return FALSE;
753 if (dir == filer_window->directory)
754 g_fscache_data_unref(dir_cache, dir);
755 else
757 detach(filer_window);
758 filer_window->directory = dir;
759 attach(filer_window);
762 return TRUE;
765 /* Another app has grabbed the selection */
766 static gint collection_lose_selection(GtkWidget *widget,
767 GdkEventSelection *event)
769 if (window_with_selection &&
770 window_with_selection->collection == COLLECTION(widget))
772 FilerWindow *filer_window = window_with_selection;
773 window_with_selection = NULL;
774 collection_clear_selection(filer_window->collection);
777 return TRUE;
780 /* Someone wants us to send them the selection */
781 static void selection_get(GtkWidget *widget,
782 GtkSelectionData *selection_data,
783 guint info,
784 guint time,
785 gpointer data)
787 GString *reply, *header;
788 FilerWindow *filer_window;
789 int i;
790 Collection *collection;
792 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
794 reply = g_string_new(NULL);
795 header = g_string_new(NULL);
797 switch (info)
799 case TARGET_STRING:
800 g_string_sprintf(header, " %s",
801 make_path(filer_window->path, "")->str);
802 break;
803 case TARGET_URI_LIST:
804 g_string_sprintf(header, " file://%s%s",
805 our_host_name(),
806 make_path(filer_window->path, "")->str);
807 break;
810 collection = filer_window->collection;
811 for (i = 0; i < collection->number_of_items; i++)
813 if (collection->items[i].selected)
815 DirItem *item =
816 (DirItem *) collection->items[i].data;
818 g_string_append(reply, header->str);
819 g_string_append(reply, item->leafname);
822 /* This works, but I don't think I like it... */
823 /* g_string_append_c(reply, ' '); */
825 gtk_selection_data_set(selection_data, xa_string,
826 8, reply->str + 1, reply->len - 1);
827 g_string_free(reply, TRUE);
828 g_string_free(header, TRUE);
831 /* No items are now selected. This might be because another app claimed
832 * the selection or because the user unselected all the items.
834 static void lose_selection(Collection *collection,
835 guint time,
836 gpointer user_data)
838 FilerWindow *filer_window = (FilerWindow *) user_data;
840 if (window_with_selection == filer_window)
842 window_with_selection = NULL;
843 gtk_selection_owner_set(NULL,
844 GDK_SELECTION_PRIMARY,
845 time);
849 static void gain_selection(Collection *collection,
850 guint time,
851 gpointer user_data)
853 FilerWindow *filer_window = (FilerWindow *) user_data;
855 if (gtk_selection_owner_set(GTK_WIDGET(collection),
856 GDK_SELECTION_PRIMARY,
857 time))
859 window_with_selection = filer_window;
861 else
862 collection_clear_selection(filer_window->collection);
865 int sort_by_name(const void *item1, const void *item2)
867 return strcmp((*((DirItem **)item1))->leafname,
868 (*((DirItem **)item2))->leafname);
871 int sort_by_type(const void *item1, const void *item2)
873 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
874 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
875 MIME_type *m1, *m2;
877 int diff = i1->base_type - i2->base_type;
879 if (!diff)
880 diff = (i1->flags & ITEM_FLAG_APPDIR)
881 - (i2->flags & ITEM_FLAG_APPDIR);
882 if (diff)
883 return diff > 0 ? 1 : -1;
885 m1 = i1->mime_type;
886 m2 = i2->mime_type;
888 if (m1 && m2)
890 diff = strcmp(m1->media_type, m2->media_type);
891 if (!diff)
892 diff = strcmp(m1->subtype, m2->subtype);
894 else if (m1 || m2)
895 diff = m1 ? 1 : -1;
896 else
897 diff = 0;
899 if (diff)
900 return diff > 0 ? 1 : -1;
902 return sort_by_name(item1, item2);
905 int sort_by_date(const void *item1, const void *item2)
907 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
908 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
910 return i1->mtime > i2->mtime ? -1 :
911 i1->mtime < i2->mtime ? 1 :
912 sort_by_name(item1, item2);
915 int sort_by_size(const void *item1, const void *item2)
917 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
918 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
920 return i1->size > i2->size ? -1 :
921 i1->size < i2->size ? 1 :
922 sort_by_name(item1, item2);
925 static void open_item(Collection *collection,
926 gpointer item_data, int item_number,
927 gpointer user_data)
929 FilerWindow *filer_window = (FilerWindow *) user_data;
930 GdkEvent *event;
931 GdkEventButton *bevent;
932 GdkEventKey *kevent;
933 gboolean shift;
934 gboolean adjust; /* do alternative action */
936 event = (GdkEvent *) gtk_get_current_event();
938 bevent = (GdkEventButton *) event;
939 kevent = (GdkEventKey *) event;
941 switch (event->type)
943 case GDK_2BUTTON_PRESS:
944 case GDK_BUTTON_PRESS:
945 case GDK_BUTTON_RELEASE:
946 shift = bevent->state & GDK_SHIFT_MASK;
947 adjust = (bevent->button != 1)
948 ^ ((bevent->state & GDK_CONTROL_MASK) != 0
949 && o_single_click == 0);
950 break;
951 case GDK_KEY_PRESS:
952 shift = kevent->state & GDK_SHIFT_MASK;
953 adjust = FALSE;
954 break;
955 default:
956 shift = FALSE;
957 adjust = FALSE;
958 break;
961 filer_openitem(filer_window, item_number, shift, adjust);
964 /* Return the full path to the directory containing object 'path'.
965 * Relative paths are resolved from the filerwindow's path.
967 static void follow_symlink(FilerWindow *filer_window, char *path)
969 char *real, *slash;
970 char *new_dir;
972 if (path[0] != '/')
973 path = make_path(filer_window->path, path)->str;
975 real = pathdup(path);
976 slash = strrchr(real, '/');
977 if (!slash)
979 g_free(real);
980 delayed_error("ROX-Filer",
981 "Broken symlink (or you don't have permission "
982 "to follow it.");
983 return;
986 *slash = '\0';
988 if (*real)
989 new_dir = real;
990 else
991 new_dir = "/";
993 if (filer_window->panel)
994 filer_opendir(new_dir, FALSE, BOTTOM);
995 else
996 filer_change_to(filer_window, new_dir, slash + 1);
998 g_free(real);
1001 void filer_openitem(FilerWindow *filer_window, int item_number,
1002 gboolean shift, gboolean adjust)
1004 GtkWidget *widget;
1005 char *full_path;
1006 DirItem *item = (DirItem *)
1007 filer_window->collection->items[item_number].data;
1008 gboolean wink = TRUE;
1010 widget = filer_window->window;
1011 full_path = make_path(filer_window->path,
1012 item->leafname)->str;
1014 if (item->flags & ITEM_FLAG_SYMLINK && shift)
1016 char path[MAXPATHLEN + 1];
1017 int got;
1019 got = readlink(make_path(filer_window->path,
1020 item->leafname)->str,
1021 path, MAXPATHLEN);
1022 if (got < 0)
1023 delayed_error("ROX-Filer", g_strerror(errno));
1024 else
1026 g_return_if_fail(got <= MAXPATHLEN);
1027 path[got] = '\0';
1029 follow_symlink(filer_window, path);
1031 return;
1034 switch (item->base_type)
1036 case TYPE_DIRECTORY:
1037 if (item->flags & ITEM_FLAG_APPDIR && !shift)
1039 run_app(make_path(filer_window->path,
1040 item->leafname)->str);
1041 if (adjust && !filer_window->panel)
1042 gtk_widget_destroy(widget);
1043 break;
1046 if (item->flags & ITEM_FLAG_MOUNT_POINT && shift)
1048 action_mount(filer_window, item);
1049 if (item->flags & ITEM_FLAG_MOUNTED)
1050 break;
1053 if ((adjust ^ o_new_window_on_1) || filer_window->panel)
1054 filer_opendir(full_path, FALSE, BOTTOM);
1055 else
1057 wink = FALSE;
1058 filer_change_to(filer_window, full_path, NULL);
1060 break;
1061 case TYPE_FILE:
1062 if (item->flags & ITEM_FLAG_EXEC_FILE
1063 && !shift)
1065 char *argv[] = {NULL, NULL};
1067 argv[0] = full_path;
1069 if (spawn_full(argv, getenv("HOME"), 0))
1071 if (adjust && !filer_window->panel)
1073 wink = FALSE;
1074 gtk_widget_destroy(widget);
1077 else
1078 report_error("ROX-Filer",
1079 "Failed to fork() child");
1081 else
1083 GString *message;
1084 MIME_type *type = shift ? &text_plain
1085 : item->mime_type;
1087 g_return_if_fail(type != NULL);
1089 if (type_open(full_path, type))
1091 if (adjust && !filer_window->panel)
1093 wink = FALSE;
1094 gtk_widget_destroy(widget);
1097 else
1099 message = g_string_new(NULL);
1100 g_string_sprintf(message, "No open "
1101 "action specified for files of "
1102 "this type (%s/%s)",
1103 type->media_type,
1104 type->subtype);
1105 report_error("ROX-Filer", message->str);
1106 g_string_free(message, TRUE);
1109 break;
1110 default:
1111 report_error("open_item",
1112 "I don't know how to open that");
1113 break;
1116 if (wink)
1117 collection_wink_item(filer_window->collection, item_number);
1120 static gint pointer_in(GtkWidget *widget,
1121 GdkEventCrossing *event,
1122 FilerWindow *filer_window)
1124 may_rescan(filer_window, TRUE);
1125 return FALSE;
1128 static gint focus_in(GtkWidget *widget,
1129 GdkEventFocus *event,
1130 FilerWindow *filer_window)
1132 window_with_focus = filer_window;
1134 return FALSE;
1137 static gint focus_out(GtkWidget *widget,
1138 GdkEventFocus *event,
1139 FilerWindow *filer_window)
1141 /* TODO: Shade the cursor */
1143 return FALSE;
1146 /* Handle keys that can't be bound with the menu */
1147 static gint key_press_event(GtkWidget *widget,
1148 GdkEventKey *event,
1149 FilerWindow *filer_window)
1151 switch (event->keyval)
1153 case GDK_BackSpace:
1154 change_to_parent(filer_window);
1155 break;
1156 default:
1157 return FALSE;
1160 return TRUE;
1163 static void toolbar_refresh_clicked(GtkWidget *widget,
1164 FilerWindow *filer_window)
1166 full_refresh();
1167 update_dir(filer_window, TRUE);
1170 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window)
1172 filer_change_to(filer_window, getenv("HOME"), NULL);
1175 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window)
1177 change_to_parent(filer_window);
1180 void change_to_parent(FilerWindow *filer_window)
1182 char *copy;
1183 char *slash;
1185 if (filer_window->path[0] == '/' && filer_window->path[1] == '\0')
1186 return; /* Already in the root */
1188 copy = g_strdup(filer_window->path);
1189 slash = strrchr(copy, '/');
1191 if (slash)
1193 *slash = '\0';
1194 filer_change_to(filer_window,
1195 *copy ? copy : "/",
1196 slash + 1);
1198 else
1199 g_warning("No / in directory path!\n");
1201 g_free(copy);
1205 /* Make filer_window display path. When finished, highlight item 'from', or
1206 * the first item if from is NULL. If there is currently no cursor then
1207 * simply wink 'from' (if not NULL).
1209 void filer_change_to(FilerWindow *filer_window, char *path, char *from)
1211 char *from_dup;
1213 from_dup = from && *from ? g_strdup(from) : NULL;
1215 detach(filer_window);
1216 g_free(filer_window->path);
1217 filer_window->path = pathdup(path);
1219 filer_window->directory = g_fscache_lookup(dir_cache,
1220 filer_window->path);
1221 if (filer_window->directory)
1223 g_free(filer_window->auto_select);
1224 filer_window->had_cursor =
1225 filer_window->collection->cursor_item != -1
1226 || filer_window->had_cursor;
1227 filer_window->auto_select = from_dup;
1229 gtk_window_set_title(GTK_WINDOW(filer_window->window),
1230 filer_window->path);
1231 collection_set_cursor_item(filer_window->collection, -1);
1232 attach(filer_window);
1234 if (GTK_WIDGET_VISIBLE(filer_window->minibuffer))
1235 gtk_idle_add((GtkFunction) minibuffer_show,
1236 filer_window);
1238 else
1240 char *error;
1242 g_free(from_dup);
1243 error = g_strdup_printf("Directory '%s' is not accessible.",
1244 path);
1245 delayed_error("ROX-Filer", error);
1246 g_free(error);
1247 gtk_widget_destroy(filer_window->window);
1251 int selected_item_number(Collection *collection)
1253 int i;
1255 g_return_val_if_fail(collection != NULL, -1);
1256 g_return_val_if_fail(IS_COLLECTION(collection), -1);
1257 g_return_val_if_fail(collection->number_selected == 1, -1);
1259 for (i = 0; i < collection->number_of_items; i++)
1260 if (collection->items[i].selected)
1261 return i;
1263 g_warning("selected_item: number_selected is wrong\n");
1265 return -1;
1268 DirItem *selected_item(Collection *collection)
1270 int item;
1272 item = selected_item_number(collection);
1274 if (item > -1)
1275 return (DirItem *) collection->items[item].data;
1276 return NULL;
1279 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
1280 FilerWindow *window)
1282 /* TODO: We can open lots of these - very irritating! */
1283 return get_choice("Close panel?",
1284 "You have tried to close a panel via the window "
1285 "manager - I usually find that this is accidental... "
1286 "really close?",
1287 2, "Remove", "Cancel") != 0;
1290 /* Make the items as narrow as possible */
1291 static void shrink_width(FilerWindow *filer_window)
1293 int i;
1294 Collection *col = filer_window->collection;
1295 int width = MIN_ITEM_WIDTH;
1296 int this_width;
1297 DisplayStyle style = filer_window->display_style;
1298 GdkFont *font;
1299 int text_height;
1301 font = gtk_widget_get_default_style()->font;
1302 text_height = font->ascent + font->descent;
1304 for (i = 0; i < col->number_of_items; i++)
1306 this_width = calc_width(filer_window,
1307 (DirItem *) col->items[i].data);
1308 if (this_width > width)
1309 width = this_width;
1312 collection_set_item_size(filer_window->collection,
1313 width,
1314 style == FULL_INFO ? MAX_ICON_HEIGHT + 4 :
1315 style == SMALL_ICONS ? MAX(text_height, SMALL_ICON_HEIGHT) + 4
1316 : text_height + MAX_ICON_HEIGHT + 8);
1319 void filer_set_sort_fn(FilerWindow *filer_window,
1320 int (*fn)(const void *a, const void *b))
1322 if (filer_window->sort_fn == fn)
1323 return;
1325 filer_window->sort_fn = fn;
1326 collection_qsort(filer_window->collection,
1327 filer_window->sort_fn);
1330 void filer_style_set(FilerWindow *filer_window, DisplayStyle style)
1332 if (filer_window->display_style == style)
1333 return;
1335 filer_window->display_style = style;
1336 switch (style)
1338 case SMALL_ICONS:
1339 collection_set_functions(filer_window->collection,
1340 draw_item_small, test_point_small);
1341 break;
1342 case FULL_INFO:
1343 collection_set_functions(filer_window->collection,
1344 draw_item_full_info, test_point_full_info);
1345 break;
1346 default:
1347 collection_set_functions(filer_window->collection,
1348 draw_item_large, test_point_large);
1349 break;
1352 shrink_width(filer_window);
1355 void filer_opendir(char *path, gboolean panel, Side panel_side)
1357 GtkWidget *hbox, *scrollbar, *collection;
1358 FilerWindow *filer_window;
1359 GtkTargetEntry target_table[] =
1361 {"text/uri-list", 0, TARGET_URI_LIST},
1362 {"STRING", 0, TARGET_STRING},
1365 filer_window = g_new(FilerWindow, 1);
1366 filer_window->minibuffer = NULL;
1367 filer_window->path = pathdup(path);
1368 filer_window->had_cursor = FALSE;
1369 filer_window->auto_select = NULL;
1371 filer_window->directory = g_fscache_lookup(dir_cache,
1372 filer_window->path);
1373 if (!filer_window->directory)
1375 char *error;
1377 error = g_strdup_printf("Directory '%s' not found.", path);
1378 delayed_error("ROX-Filer", error);
1379 g_free(error);
1380 g_free(filer_window->path);
1381 g_free(filer_window);
1382 return;
1385 filer_window->show_hidden = FALSE;
1386 filer_window->panel = panel;
1387 filer_window->panel_side = panel_side;
1388 filer_window->temp_item_selected = FALSE;
1389 filer_window->sort_fn = sort_by_type;
1390 filer_window->flags = (FilerFlags) 0;
1391 filer_window->display_style = UNKNOWN_STYLE;
1393 filer_window->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1394 gtk_window_set_title(GTK_WINDOW(filer_window->window),
1395 filer_window->path);
1397 collection = collection_new(NULL);
1398 gtk_object_set_data(GTK_OBJECT(collection),
1399 "filer_window", filer_window);
1400 filer_window->collection = COLLECTION(collection);
1402 gtk_widget_add_events(filer_window->window, GDK_ENTER_NOTIFY);
1403 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1404 "enter-notify-event",
1405 GTK_SIGNAL_FUNC(pointer_in), filer_window);
1406 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_in_event",
1407 GTK_SIGNAL_FUNC(focus_in), filer_window);
1408 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_out_event",
1409 GTK_SIGNAL_FUNC(focus_out), filer_window);
1410 gtk_signal_connect(GTK_OBJECT(filer_window->window), "destroy",
1411 filer_window_destroyed, filer_window);
1413 gtk_signal_connect(GTK_OBJECT(filer_window->collection), "open_item",
1414 open_item, filer_window);
1415 gtk_signal_connect(GTK_OBJECT(collection), "show_menu",
1416 show_menu, filer_window);
1417 gtk_signal_connect(GTK_OBJECT(collection), "gain_selection",
1418 gain_selection, filer_window);
1419 gtk_signal_connect(GTK_OBJECT(collection), "lose_selection",
1420 lose_selection, filer_window);
1421 gtk_signal_connect(GTK_OBJECT(collection), "drag_selection",
1422 drag_selection, filer_window);
1423 gtk_signal_connect(GTK_OBJECT(collection), "drag_data_get",
1424 drag_data_get, filer_window);
1425 gtk_signal_connect(GTK_OBJECT(collection), "selection_clear_event",
1426 GTK_SIGNAL_FUNC(collection_lose_selection), NULL);
1427 gtk_signal_connect (GTK_OBJECT(collection), "selection_get",
1428 GTK_SIGNAL_FUNC(selection_get), NULL);
1429 gtk_selection_add_targets(collection, GDK_SELECTION_PRIMARY,
1430 target_table,
1431 sizeof(target_table) / sizeof(*target_table));
1433 filer_style_set(filer_window, LARGE_ICONS);
1434 drag_set_dest(collection);
1436 if (panel)
1438 int swidth, sheight, iwidth, iheight;
1439 GtkWidget *frame, *win = filer_window->window;
1441 gtk_window_set_wmclass(GTK_WINDOW(win), "ROX-Panel",
1442 "ROX-Filer");
1443 collection_set_panel(filer_window->collection, TRUE);
1444 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1445 "delete_event",
1446 GTK_SIGNAL_FUNC(filer_confirm_close),
1447 filer_window);
1449 gdk_window_get_size(GDK_ROOT_PARENT(), &swidth, &sheight);
1450 iwidth = filer_window->collection->item_width;
1451 iheight = filer_window->collection->item_height;
1453 if (panel_side == TOP || panel_side == BOTTOM)
1455 int height = iheight + PANEL_BORDER;
1456 int y = panel_side == TOP
1457 ? -PANEL_BORDER
1458 : sheight - height - PANEL_BORDER;
1460 gtk_widget_set_usize(collection, swidth, height);
1461 gtk_widget_set_uposition(win, 0, y);
1463 else
1465 int width = iwidth + PANEL_BORDER;
1466 int x = panel_side == LEFT
1467 ? -PANEL_BORDER
1468 : swidth - width - PANEL_BORDER;
1470 gtk_widget_set_usize(collection, width, sheight);
1471 gtk_widget_set_uposition(win, x, 0);
1474 frame = gtk_frame_new(NULL);
1475 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1476 gtk_container_add(GTK_CONTAINER(frame), collection);
1477 gtk_container_add(GTK_CONTAINER(win), frame);
1479 gtk_widget_show_all(frame);
1480 gtk_widget_realize(win);
1481 if (override_redirect)
1482 gdk_window_set_override_redirect(win->window, TRUE);
1483 make_panel_window(win->window);
1485 else
1487 GtkWidget *vbox;
1489 gtk_signal_connect(GTK_OBJECT(collection),
1490 "key_press_event",
1491 GTK_SIGNAL_FUNC(key_press_event), filer_window);
1492 gtk_window_set_default_size(GTK_WINDOW(filer_window->window),
1493 filer_window->display_style == LARGE_ICONS ? 400 : 512,
1494 o_toolbar ? 220 : 200);
1496 hbox = gtk_hbox_new(FALSE, 0);
1497 gtk_container_add(GTK_CONTAINER(filer_window->window),
1498 hbox);
1500 vbox = gtk_vbox_new(FALSE, 0);
1501 gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
1503 if (o_toolbar)
1505 GtkWidget *toolbar;
1507 toolbar = create_toolbar(filer_window);
1508 gtk_box_pack_start(GTK_BOX(vbox), toolbar,
1509 FALSE, TRUE, 0);
1510 gtk_widget_show_all(toolbar);
1513 gtk_box_pack_start(GTK_BOX(vbox), collection, TRUE, TRUE, 0);
1515 filer_window->minibuffer = create_minibuffer(filer_window);
1516 gtk_box_pack_start(GTK_BOX(vbox), filer_window->minibuffer,
1517 FALSE, TRUE, 0);
1519 scrollbar = gtk_vscrollbar_new(COLLECTION(collection)->vadj);
1520 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, TRUE, 0);
1521 gtk_accel_group_attach(filer_keys,
1522 GTK_OBJECT(filer_window->window));
1523 gtk_window_set_focus(GTK_WINDOW(filer_window->window),
1524 collection);
1526 gtk_widget_show(hbox);
1527 gtk_widget_show(vbox);
1528 gtk_widget_show(scrollbar);
1529 gtk_widget_show(collection);
1532 number_of_windows++;
1533 gtk_widget_show(filer_window->window);
1534 attach(filer_window);
1536 all_filer_windows = g_list_prepend(all_filer_windows, filer_window);
1539 static GtkWidget *create_toolbar(FilerWindow *filer_window)
1541 GtkWidget *frame, *box;
1543 frame = gtk_frame_new(NULL);
1544 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1546 box = gtk_hbutton_box_new();
1547 gtk_button_box_set_child_size_default(16, 16);
1548 gtk_hbutton_box_set_spacing_default(2);
1549 gtk_button_box_set_layout(GTK_BUTTON_BOX(box), GTK_BUTTONBOX_START);
1550 gtk_container_add(GTK_CONTAINER(frame), box);
1552 add_button(GTK_CONTAINER(box), TOOLBAR_UP_ICON,
1553 GTK_SIGNAL_FUNC(toolbar_up_clicked),
1554 filer_window, "Change to parent directory");
1555 add_button(GTK_CONTAINER(box), TOOLBAR_HOME_ICON,
1556 GTK_SIGNAL_FUNC(toolbar_home_clicked),
1557 filer_window, "Change to home directory");
1558 add_button(GTK_CONTAINER(box), TOOLBAR_REFRESH_ICON,
1559 GTK_SIGNAL_FUNC(toolbar_refresh_clicked),
1560 filer_window, "Rescan directory contents");
1562 return frame;
1565 static void add_button(GtkContainer *box, int pixmap,
1566 GtkSignalFunc cb, gpointer data,
1567 char *tip)
1569 GtkWidget *button, *icon;
1571 button = gtk_button_new();
1572 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
1573 gtk_container_add(box, button);
1575 icon = gtk_pixmap_new(default_pixmap[pixmap].pixmap,
1576 default_pixmap[pixmap].mask);
1577 gtk_container_add(GTK_CONTAINER(button), icon);
1578 gtk_signal_connect(GTK_OBJECT(button), "clicked", cb, data);
1580 gtk_tooltips_set_tip(tooltips, button, tip, NULL);
1583 /* Build up some option widgets to go in the options dialog, but don't
1584 * fill them in yet.
1586 static GtkWidget *create_options()
1588 GtkWidget *vbox;
1590 vbox = gtk_vbox_new(FALSE, 0);
1591 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
1593 toggle_new_window_on_1 =
1594 gtk_check_button_new_with_label("New window on button 1 "
1595 "(RISC OS style)");
1596 gtk_box_pack_start(GTK_BOX(vbox), toggle_new_window_on_1,
1597 FALSE, TRUE, 0);
1599 toggle_menu_on_2 =
1600 gtk_check_button_new_with_label("Menu on button 2 "
1601 "(RISC OS style)");
1602 gtk_box_pack_start(GTK_BOX(vbox), toggle_menu_on_2, FALSE, TRUE, 0);
1604 toggle_single_click =
1605 gtk_check_button_new_with_label("Single-click nagivation");
1606 gtk_box_pack_start(GTK_BOX(vbox), toggle_single_click, FALSE, TRUE, 0);
1608 toggle_toolbar =
1609 gtk_check_button_new_with_label("Show toolbar on new windows");
1610 gtk_box_pack_start(GTK_BOX(vbox), toggle_toolbar, FALSE, TRUE, 0);
1612 return vbox;
1615 /* Reflect current state by changing the widgets in the options box */
1616 static void update_options()
1618 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_new_window_on_1),
1619 o_new_window_on_1);
1620 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_menu_on_2),
1621 collection_menu_button == 2 ? 1 : 0);
1622 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_single_click),
1623 o_single_click);
1624 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_toolbar),
1625 o_toolbar);
1628 /* Set current values by reading the states of the widgets in the options box */
1629 static void set_options()
1631 o_new_window_on_1 = gtk_toggle_button_get_active(
1632 GTK_TOGGLE_BUTTON(toggle_new_window_on_1));
1634 collection_menu_button = gtk_toggle_button_get_active(
1635 GTK_TOGGLE_BUTTON(toggle_menu_on_2)) ? 2 : 3;
1637 o_single_click = gtk_toggle_button_get_active(
1638 GTK_TOGGLE_BUTTON(toggle_single_click));
1639 collection_single_click = o_single_click ? TRUE : FALSE;
1641 o_toolbar = gtk_toggle_button_get_active(
1642 GTK_TOGGLE_BUTTON(toggle_toolbar));
1645 static void save_options()
1647 option_write("filer_new_window_on_1", o_new_window_on_1 ? "1" : "0");
1648 option_write("filer_menu_on_2",
1649 collection_menu_button == 2 ? "1" : "0");
1650 option_write("filer_single_click", o_single_click ? "1" : "0");
1651 option_write("filer_toolbar", o_toolbar ? "1" : "0");
1654 static char *filer_new_window_on_1(char *data)
1656 o_new_window_on_1 = atoi(data) != 0;
1657 return NULL;
1660 static char *filer_menu_on_2(char *data)
1662 collection_menu_button = atoi(data) != 0 ? 2 : 3;
1663 return NULL;
1666 static char *filer_single_click(char *data)
1668 o_single_click = atoi(data) != 0;
1669 collection_single_click = o_single_click ? TRUE : FALSE;
1670 return NULL;
1673 static char *filer_toolbar(char *data)
1675 o_toolbar = atoi(data) != 0;
1676 return NULL;
1679 /* Note that filer_window may not exist after this call. */
1680 void update_dir(FilerWindow *filer_window, gboolean warning)
1682 if (may_rescan(filer_window, warning))
1683 dir_update(filer_window->directory, filer_window->path);
1686 void filer_set_hidden(FilerWindow *filer_window, gboolean hidden)
1688 Directory *dir = filer_window->directory;
1690 if (filer_window->show_hidden == hidden)
1691 return;
1693 filer_window->show_hidden = hidden;
1695 g_fscache_data_ref(dir_cache, dir);
1696 detach(filer_window);
1697 filer_window->directory = dir;
1698 attach(filer_window);
1701 /* Refresh the various caches even if we don't think we need to */
1702 void full_refresh(void)
1704 mount_update(TRUE);
1707 /* This path has been mounted/umounted - update all dirs */
1708 void filer_check_mounted(char *path)
1710 GList *next = all_filer_windows;
1711 int len;
1713 len = strlen(path);
1715 while (next)
1717 FilerWindow *filer_window = (FilerWindow *) next->data;
1719 next = next->next;
1721 if (strncmp(path, filer_window->path, len) == 0)
1723 char s = filer_window->path[len];
1725 if (s == '/' || s == '\0')
1726 update_dir(filer_window, FALSE);