r180: Updated filer action code to use exec instead of system (stops problems
[rox-filer/ma.git] / ROX-Filer / src / filer.c
bloba7d1cf65b8a0ae5c92031322e68b21b99ed63fac
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
91 /* The values correspond to the menu indexes in the option widget */
92 typedef enum {
93 TOOLBAR_NONE = 0,
94 TOOLBAR_NORMAL = 1,
95 TOOLBAR_GNOME = 2,
96 } ToolbarType;
97 static ToolbarType o_toolbar = TOOLBAR_NORMAL;
98 static GtkWidget *menu_toolbar;
100 static gboolean o_single_click = FALSE;
101 static GtkWidget *toggle_single_click;
102 static gboolean o_new_window_on_1 = FALSE; /* Button 1 => New window */
103 static GtkWidget *toggle_new_window_on_1;
104 static GtkWidget *toggle_menu_on_2;
106 /* Static prototypes */
107 static void attach(FilerWindow *filer_window);
108 static void detach(FilerWindow *filer_window);
109 static void filer_window_destroyed(GtkWidget *widget,
110 FilerWindow *filer_window);
111 static void show_menu(Collection *collection, GdkEventButton *event,
112 int number_selected, gpointer user_data);
113 static gint focus_in(GtkWidget *widget,
114 GdkEventFocus *event,
115 FilerWindow *filer_window);
116 static gint focus_out(GtkWidget *widget,
117 GdkEventFocus *event,
118 FilerWindow *filer_window);
119 static void add_item(FilerWindow *filer_window, DirItem *item);
120 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window);
121 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window);
122 static void add_button(GtkWidget *box, int pixmap,
123 GtkSignalFunc cb, FilerWindow *filer_window,
124 char *label, char *tip);
125 static GtkWidget *create_toolbar(FilerWindow *filer_window);
126 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
127 FilerWindow *window);
128 static int calc_width(FilerWindow *filer_window, DirItem *item);
129 static void draw_large_icon(GtkWidget *widget,
130 GdkRectangle *area,
131 DirItem *item,
132 gboolean selected);
133 static void draw_string(GtkWidget *widget,
134 GdkFont *font,
135 char *string,
136 int x,
137 int y,
138 int width,
139 gboolean selected);
140 static void draw_item_large(GtkWidget *widget,
141 CollectionItem *item,
142 GdkRectangle *area);
143 static void draw_item_small(GtkWidget *widget,
144 CollectionItem *item,
145 GdkRectangle *area);
146 static void draw_item_full_info(GtkWidget *widget,
147 CollectionItem *colitem,
148 GdkRectangle *area);
149 static gboolean test_point_large(Collection *collection,
150 int point_x, int point_y,
151 CollectionItem *item,
152 int width, int height);
153 static gboolean test_point_small(Collection *collection,
154 int point_x, int point_y,
155 CollectionItem *item,
156 int width, int height);
157 static gboolean test_point_full_info(Collection *collection,
158 int point_x, int point_y,
159 CollectionItem *item,
160 int width, int height);
161 static void update_display(Directory *dir,
162 DirAction action,
163 GPtrArray *items,
164 FilerWindow *filer_window);
165 static void shrink_width(FilerWindow *filer_window);
166 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning);
167 static void open_item(Collection *collection,
168 gpointer item_data, int item_number,
169 gpointer user_data);
170 static gboolean minibuffer_show_cb(FilerWindow *filer_window);
172 static GdkAtom xa_string;
173 enum
175 TARGET_STRING,
176 TARGET_URI_LIST,
179 static GdkCursor *busy_cursor = NULL;
180 static GtkTooltips *tooltips = NULL;
182 void filer_init()
184 xa_string = gdk_atom_intern("STRING", FALSE);
186 options_sections = g_slist_prepend(options_sections, &options);
187 option_register("filer_new_window_on_1", filer_new_window_on_1);
188 option_register("filer_menu_on_2", filer_menu_on_2);
189 option_register("filer_single_click", filer_single_click);
190 option_register("filer_toolbar", filer_toolbar);
192 fixed_font = gdk_font_load("fixed");
194 busy_cursor = gdk_cursor_new(GDK_WATCH);
196 tooltips = gtk_tooltips_new();
199 static gboolean if_deleted(gpointer item, gpointer removed)
201 int i = ((GPtrArray *) removed)->len;
202 DirItem **r = (DirItem **) ((GPtrArray *) removed)->pdata;
203 char *leafname = ((DirItem *) item)->leafname;
205 while (i--)
207 if (strcmp(leafname, r[i]->leafname) == 0)
208 return TRUE;
211 return FALSE;
214 static void update_item(FilerWindow *filer_window, DirItem *item)
216 int i;
217 char *leafname = item->leafname;
219 if (leafname[0] == '.')
221 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
222 || (leafname[1] == '.' && leafname[2] == '\0'))
223 return;
226 i = collection_find_item(filer_window->collection, item, dir_item_cmp);
228 if (i >= 0)
229 collection_draw_item(filer_window->collection, i, TRUE);
230 else
231 g_warning("Failed to find '%s'\n", item->leafname);
234 static void update_display(Directory *dir,
235 DirAction action,
236 GPtrArray *items,
237 FilerWindow *filer_window)
239 int i;
240 int cursor = filer_window->collection->cursor_item;
241 char *as;
242 Collection *collection = filer_window->collection;
244 switch (action)
246 case DIR_ADD:
247 as = filer_window->auto_select;
249 for (i = 0; i < items->len; i++)
251 DirItem *item = (DirItem *) items->pdata[i];
253 add_item(filer_window, item);
255 if (cursor != -1 || !as)
256 continue;
258 if (strcmp(as, item->leafname) != 0)
259 continue;
261 cursor = collection->number_of_items - 1;
262 if (filer_window->had_cursor)
264 collection_set_cursor_item(collection,
265 cursor);
266 filer_window->mini_cursor_base = cursor;
268 else
269 collection_wink_item(collection,
270 cursor);
273 collection_qsort(filer_window->collection,
274 filer_window->sort_fn);
275 break;
276 case DIR_REMOVE:
277 collection_delete_if(filer_window->collection,
278 if_deleted,
279 items);
280 break;
281 case DIR_END_SCAN:
282 if (filer_window->window->window)
283 gdk_window_set_cursor(
284 filer_window->window->window,
285 NULL);
286 shrink_width(filer_window);
287 if (filer_window->had_cursor &&
288 collection->cursor_item == -1)
290 collection_set_cursor_item(collection, 0);
291 filer_window->had_cursor = FALSE;
293 break;
294 case DIR_UPDATE:
295 for (i = 0; i < items->len; i++)
297 DirItem *item = (DirItem *) items->pdata[i];
299 update_item(filer_window, item);
301 collection_qsort(filer_window->collection,
302 filer_window->sort_fn);
303 break;
307 static void attach(FilerWindow *filer_window)
309 gdk_window_set_cursor(filer_window->window->window, busy_cursor);
310 collection_clear(filer_window->collection);
311 dir_attach(filer_window->directory, (DirCallback) update_display,
312 filer_window);
315 static void detach(FilerWindow *filer_window)
317 g_return_if_fail(filer_window->directory != NULL);
319 dir_detach(filer_window->directory,
320 (DirCallback) update_display, filer_window);
321 g_fscache_data_unref(dir_cache, filer_window->directory);
322 filer_window->directory = NULL;
325 static void filer_window_destroyed(GtkWidget *widget,
326 FilerWindow *filer_window)
328 all_filer_windows = g_list_remove(all_filer_windows, filer_window);
330 if (window_with_selection == filer_window)
331 window_with_selection = NULL;
332 if (window_with_focus == filer_window)
333 window_with_focus = NULL;
335 if (filer_window->directory)
336 detach(filer_window);
338 g_free(filer_window->auto_select);
339 g_free(filer_window->path);
340 g_free(filer_window);
342 if (--number_of_windows < 1)
343 gtk_main_quit();
346 static int calc_width(FilerWindow *filer_window, DirItem *item)
348 int pix_width = item->image->width;
350 switch (filer_window->display_style)
352 case FULL_INFO:
353 return MAX_ICON_WIDTH + 12 +
354 MAX(item->details_width, item->name_width);
355 case SMALL_ICONS:
356 return SMALL_ICON_WIDTH + 12 + item->name_width;
357 default:
358 return MAX(pix_width, item->name_width) + 4;
362 /* Add a single object to a directory display */
363 static void add_item(FilerWindow *filer_window, DirItem *item)
365 char *leafname = item->leafname;
366 int item_width;
368 if (leafname[0] == '.')
370 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
371 || (leafname[1] == '.' && leafname[2] == '\0'))
372 return;
375 item_width = calc_width(filer_window, item);
376 if (item_width > filer_window->collection->item_width)
377 collection_set_item_size(filer_window->collection,
378 item_width,
379 filer_window->collection->item_height);
380 collection_insert(filer_window->collection, item);
383 /* Is a point inside an item? */
384 static gboolean test_point_large(Collection *collection,
385 int point_x, int point_y,
386 CollectionItem *colitem,
387 int width, int height)
389 DirItem *item = (DirItem *) colitem->data;
390 GdkFont *font = GTK_WIDGET(collection)->style->font;
391 int text_height = font->ascent + font->descent;
392 MaskedPixmap *image = item->image;
393 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
394 int image_width = (image->width >> 1) + 2;
395 int text_width = (item->name_width >> 1) + 2;
396 int x_limit;
398 if (point_y < image_y)
399 return FALSE; /* Too high up (don't worry about too low) */
401 if (point_y <= image_y + image->height + 2)
402 x_limit = image_width;
403 else if (point_y > height - text_height - 2)
404 x_limit = text_width;
405 else
406 x_limit = MIN(image_width, text_width);
408 return ABS(point_x - (width >> 1)) < x_limit;
411 static gboolean test_point_full_info(Collection *collection,
412 int point_x, int point_y,
413 CollectionItem *colitem,
414 int width, int height)
416 DirItem *item = (DirItem *) colitem->data;
417 GdkFont *font = GTK_WIDGET(collection)->style->font;
418 MaskedPixmap *image = item->image;
419 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
420 int low_top = height
421 - fixed_font->descent - 2 - fixed_font->ascent;
423 if (point_x < image->width + 2)
424 return point_x > 2 && point_y > image_y;
426 point_x -= MAX_ICON_WIDTH + 8;
428 if (point_y >= low_top)
429 return point_x < item->details_width;
430 if (point_y >= low_top - font->ascent - font->descent)
431 return point_x < item->name_width;
432 return FALSE;
435 static gboolean test_point_small(Collection *collection,
436 int point_x, int point_y,
437 CollectionItem *colitem,
438 int width, int height)
440 DirItem *item = (DirItem *) colitem->data;
441 MaskedPixmap *image = item->image;
442 int image_y = MAX(0, SMALL_ICON_HEIGHT - image->height);
443 GdkFont *font = GTK_WIDGET(collection)->style->font;
444 int low_top = height
445 - fixed_font->descent - 2 - font->ascent;
446 int iwidth = MIN(SMALL_ICON_WIDTH, image->width);
448 if (point_x < iwidth + 2)
449 return point_x > 2 && point_y > image_y;
451 point_x -= SMALL_ICON_WIDTH + 4;
453 if (point_y >= low_top)
454 return point_x < item->name_width;
455 return FALSE;
458 static void draw_small_icon(GtkWidget *widget,
459 GdkRectangle *area,
460 DirItem *item,
461 gboolean selected)
463 MaskedPixmap *image = item->image;
464 int width = MIN(image->width, SMALL_ICON_WIDTH);
465 int height = MIN(image->height, SMALL_ICON_HEIGHT);
466 int image_x = area->x + ((area->width - width) >> 1);
467 int image_y;
468 GdkGC *gc = selected ? widget->style->white_gc
469 : widget->style->black_gc;
470 if (!item->image)
471 return;
473 gdk_gc_set_clip_mask(gc, item->image->mask);
475 image_y = MAX(0, SMALL_ICON_HEIGHT - image->height);
476 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
477 gdk_draw_pixmap(widget->window, gc,
478 item->image->pixmap,
479 0, 0, /* Source x,y */
480 image_x, area->y + image_y, /* Dest x,y */
481 width, height);
483 if (selected)
485 gdk_gc_set_function(gc, GDK_INVERT);
486 gdk_draw_rectangle(widget->window,
488 TRUE, image_x, area->y + image_y,
489 width, height);
490 gdk_gc_set_function(gc, GDK_COPY);
493 if (item->flags & ITEM_FLAG_SYMLINK)
495 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
496 gdk_gc_set_clip_mask(gc,
497 default_pixmap[TYPE_SYMLINK].mask);
498 gdk_draw_pixmap(widget->window, gc,
499 default_pixmap[TYPE_SYMLINK].pixmap,
500 0, 0, /* Source x,y */
501 image_x, area->y + 8, /* Dest x,y */
502 -1, -1);
504 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
506 int type = item->flags & ITEM_FLAG_MOUNTED
507 ? TYPE_MOUNTED
508 : TYPE_UNMOUNTED;
509 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
510 gdk_gc_set_clip_mask(gc,
511 default_pixmap[type].mask);
512 gdk_draw_pixmap(widget->window, gc,
513 default_pixmap[type].pixmap,
514 0, 0, /* Source x,y */
515 image_x, area->y + 8, /* Dest x,y */
516 -1, -1);
519 gdk_gc_set_clip_mask(gc, NULL);
520 gdk_gc_set_clip_origin(gc, 0, 0);
523 static void draw_large_icon(GtkWidget *widget,
524 GdkRectangle *area,
525 DirItem *item,
526 gboolean selected)
528 MaskedPixmap *image = item->image;
529 int width = MIN(image->width, MAX_ICON_WIDTH);
530 int height = MIN(image->height, MAX_ICON_WIDTH);
531 int image_x = area->x + ((area->width - width) >> 1);
532 int image_y;
533 GdkGC *gc = selected ? widget->style->white_gc
534 : widget->style->black_gc;
536 gdk_gc_set_clip_mask(gc, item->image->mask);
538 image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
539 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
540 gdk_draw_pixmap(widget->window, gc,
541 item->image->pixmap,
542 0, 0, /* Source x,y */
543 image_x, area->y + image_y, /* Dest x,y */
544 width, height);
546 if (selected)
548 gdk_gc_set_function(gc, GDK_INVERT);
549 gdk_draw_rectangle(widget->window,
551 TRUE, image_x, area->y + image_y,
552 width, height);
553 gdk_gc_set_function(gc, GDK_COPY);
556 if (item->flags & ITEM_FLAG_SYMLINK)
558 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
559 gdk_gc_set_clip_mask(gc,
560 default_pixmap[TYPE_SYMLINK].mask);
561 gdk_draw_pixmap(widget->window, gc,
562 default_pixmap[TYPE_SYMLINK].pixmap,
563 0, 0, /* Source x,y */
564 image_x, area->y + 8, /* Dest x,y */
565 -1, -1);
567 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
569 int type = item->flags & ITEM_FLAG_MOUNTED
570 ? TYPE_MOUNTED
571 : TYPE_UNMOUNTED;
572 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
573 gdk_gc_set_clip_mask(gc,
574 default_pixmap[type].mask);
575 gdk_draw_pixmap(widget->window, gc,
576 default_pixmap[type].pixmap,
577 0, 0, /* Source x,y */
578 image_x, area->y + 8, /* Dest x,y */
579 -1, -1);
582 gdk_gc_set_clip_mask(gc, NULL);
583 gdk_gc_set_clip_origin(gc, 0, 0);
586 static void draw_string(GtkWidget *widget,
587 GdkFont *font,
588 char *string,
589 int x,
590 int y,
591 int width,
592 gboolean selected)
594 int text_height = font->ascent + font->descent;
596 if (selected)
597 gtk_paint_flat_box(widget->style, widget->window,
598 GTK_STATE_SELECTED, GTK_SHADOW_NONE,
599 NULL, widget, "text",
600 x, y - font->ascent,
601 width,
602 text_height);
604 gdk_draw_text(widget->window,
605 font,
606 selected ? widget->style->white_gc
607 : widget->style->black_gc,
608 x, y,
609 string, strlen(string));
612 /* Return a string (valid until next call) giving details
613 * of this item.
615 char *details(DirItem *item)
617 mode_t m = item->mode;
618 static GString *buf = NULL;
620 if (!buf)
621 buf = g_string_new(NULL);
623 g_string_sprintf(buf, "%s, %c%c%c:%c%c%c:%c%c%c:%c%c"
624 #ifdef S_ISVTX
625 "%c"
626 #endif
627 ", %s",
628 S_ISDIR(m) ? "Dir" :
629 S_ISCHR(m) ? "Char" :
630 S_ISBLK(m) ? "Blck" :
631 S_ISLNK(m) ? "Link" :
632 S_ISSOCK(m) ? "Sock" :
633 S_ISFIFO(m) ? "Pipe" : "File",
635 m & S_IRUSR ? 'r' : '-',
636 m & S_IWUSR ? 'w' : '-',
637 m & S_IXUSR ? 'x' : '-',
639 m & S_IRGRP ? 'r' : '-',
640 m & S_IWGRP ? 'w' : '-',
641 m & S_IXGRP ? 'x' : '-',
643 m & S_IROTH ? 'r' : '-',
644 m & S_IWOTH ? 'w' : '-',
645 m & S_IXOTH ? 'x' : '-',
647 m & S_ISUID ? 'U' : '-',
648 m & S_ISGID ? 'G' : '-',
649 #ifdef S_ISVTX
650 m & S_ISVTX ? 'T' : '-',
651 #endif
652 format_size(item->size));
654 return buf->str;
657 static void draw_item_full_info(GtkWidget *widget,
658 CollectionItem *colitem,
659 GdkRectangle *area)
661 DirItem *item = (DirItem *) colitem->data;
662 MaskedPixmap *image = item->image;
663 GdkFont *font = widget->style->font;
664 int text_x = area->x + MAX_ICON_WIDTH + 8;
665 int low_text_y = area->y + area->height - fixed_font->descent - 2;
666 gboolean selected = colitem->selected;
667 GdkRectangle pic_area;
669 pic_area.x = area->x;
670 pic_area.y = area->y;
671 pic_area.width = image->width + 8;
672 pic_area.height = area->height;
674 draw_large_icon(widget, &pic_area, item, selected);
676 draw_string(widget,
677 widget->style->font,
678 item->leafname,
679 text_x,
680 low_text_y - font->descent - fixed_font->ascent,
681 item->name_width,
682 selected);
683 draw_string(widget,
684 fixed_font,
685 details(item),
686 text_x, low_text_y,
687 item->details_width,
688 selected);
691 static void draw_item_small(GtkWidget *widget,
692 CollectionItem *colitem,
693 GdkRectangle *area)
695 DirItem *item = (DirItem *) colitem->data;
696 GdkFont *font = widget->style->font;
697 int text_x = area->x + SMALL_ICON_WIDTH + 4;
698 int low_text_y = area->y + area->height - font->descent - 2;
699 gboolean selected = colitem->selected;
700 GdkRectangle pic_area;
702 pic_area.x = area->x;
703 pic_area.y = area->y;
704 pic_area.width = SMALL_ICON_WIDTH;
705 pic_area.height = SMALL_ICON_HEIGHT;
707 draw_small_icon(widget, &pic_area, item, selected);
709 draw_string(widget,
710 widget->style->font,
711 item->leafname,
712 text_x,
713 low_text_y,
714 item->name_width,
715 selected);
718 static void draw_item_large(GtkWidget *widget,
719 CollectionItem *colitem,
720 GdkRectangle *area)
722 DirItem *item = (DirItem *) colitem->data;
723 GdkFont *font = widget->style->font;
724 int text_x = area->x + ((area->width - item->name_width) >> 1);
725 int text_y = area->y + area->height - font->descent - 2;
726 gboolean selected = colitem->selected;
728 draw_large_icon(widget, area, item, selected);
730 draw_string(widget,
731 widget->style->font,
732 item->leafname,
733 text_x, text_y, item->name_width,
734 selected);
737 static void show_menu(Collection *collection, GdkEventButton *event,
738 int item, gpointer user_data)
740 show_filer_menu((FilerWindow *) user_data, event, item);
743 /* Returns TRUE iff the directory still exits. */
744 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning)
746 Directory *dir;
748 g_return_val_if_fail(filer_window != NULL, FALSE);
750 /* We do a fresh lookup (rather than update) because the inode may
751 * have changed.
753 dir = g_fscache_lookup(dir_cache, filer_window->path);
754 if (!dir)
756 if (warning)
757 delayed_error("ROX-Filer", "Directory missing/deleted");
758 gtk_widget_destroy(filer_window->window);
759 return FALSE;
761 if (dir == filer_window->directory)
762 g_fscache_data_unref(dir_cache, dir);
763 else
765 detach(filer_window);
766 filer_window->directory = dir;
767 attach(filer_window);
770 return TRUE;
773 /* Another app has grabbed the selection */
774 static gint collection_lose_selection(GtkWidget *widget,
775 GdkEventSelection *event)
777 if (window_with_selection &&
778 window_with_selection->collection == COLLECTION(widget))
780 FilerWindow *filer_window = window_with_selection;
781 window_with_selection = NULL;
782 collection_clear_selection(filer_window->collection);
785 return TRUE;
788 /* Someone wants us to send them the selection */
789 static void selection_get(GtkWidget *widget,
790 GtkSelectionData *selection_data,
791 guint info,
792 guint time,
793 gpointer data)
795 GString *reply, *header;
796 FilerWindow *filer_window;
797 int i;
798 Collection *collection;
800 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
802 reply = g_string_new(NULL);
803 header = g_string_new(NULL);
805 switch (info)
807 case TARGET_STRING:
808 g_string_sprintf(header, " %s",
809 make_path(filer_window->path, "")->str);
810 break;
811 case TARGET_URI_LIST:
812 g_string_sprintf(header, " file://%s%s",
813 our_host_name(),
814 make_path(filer_window->path, "")->str);
815 break;
818 collection = filer_window->collection;
819 for (i = 0; i < collection->number_of_items; i++)
821 if (collection->items[i].selected)
823 DirItem *item =
824 (DirItem *) collection->items[i].data;
826 g_string_append(reply, header->str);
827 g_string_append(reply, item->leafname);
830 /* This works, but I don't think I like it... */
831 /* g_string_append_c(reply, ' '); */
833 gtk_selection_data_set(selection_data, xa_string,
834 8, reply->str + 1, reply->len - 1);
835 g_string_free(reply, TRUE);
836 g_string_free(header, TRUE);
839 /* No items are now selected. This might be because another app claimed
840 * the selection or because the user unselected all the items.
842 static void lose_selection(Collection *collection,
843 guint time,
844 gpointer user_data)
846 FilerWindow *filer_window = (FilerWindow *) user_data;
848 if (window_with_selection == filer_window)
850 window_with_selection = NULL;
851 gtk_selection_owner_set(NULL,
852 GDK_SELECTION_PRIMARY,
853 time);
857 static void gain_selection(Collection *collection,
858 guint time,
859 gpointer user_data)
861 FilerWindow *filer_window = (FilerWindow *) user_data;
863 if (gtk_selection_owner_set(GTK_WIDGET(collection),
864 GDK_SELECTION_PRIMARY,
865 time))
867 window_with_selection = filer_window;
869 else
870 collection_clear_selection(filer_window->collection);
873 int sort_by_name(const void *item1, const void *item2)
875 return strcmp((*((DirItem **)item1))->leafname,
876 (*((DirItem **)item2))->leafname);
879 int sort_by_type(const void *item1, const void *item2)
881 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
882 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
883 MIME_type *m1, *m2;
885 int diff = i1->base_type - i2->base_type;
887 if (!diff)
888 diff = (i1->flags & ITEM_FLAG_APPDIR)
889 - (i2->flags & ITEM_FLAG_APPDIR);
890 if (diff)
891 return diff > 0 ? 1 : -1;
893 m1 = i1->mime_type;
894 m2 = i2->mime_type;
896 if (m1 && m2)
898 diff = strcmp(m1->media_type, m2->media_type);
899 if (!diff)
900 diff = strcmp(m1->subtype, m2->subtype);
902 else if (m1 || m2)
903 diff = m1 ? 1 : -1;
904 else
905 diff = 0;
907 if (diff)
908 return diff > 0 ? 1 : -1;
910 return sort_by_name(item1, item2);
913 int sort_by_date(const void *item1, const void *item2)
915 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
916 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
918 return i1->mtime > i2->mtime ? -1 :
919 i1->mtime < i2->mtime ? 1 :
920 sort_by_name(item1, item2);
923 int sort_by_size(const void *item1, const void *item2)
925 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
926 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
928 return i1->size > i2->size ? -1 :
929 i1->size < i2->size ? 1 :
930 sort_by_name(item1, item2);
933 static void open_item(Collection *collection,
934 gpointer item_data, int item_number,
935 gpointer user_data)
937 FilerWindow *filer_window = (FilerWindow *) user_data;
938 GdkEvent *event;
939 GdkEventButton *bevent;
940 GdkEventKey *kevent;
941 OpenFlags flags = 0;
943 event = (GdkEvent *) gtk_get_current_event();
945 bevent = (GdkEventButton *) event;
946 kevent = (GdkEventKey *) event;
948 switch (event->type)
950 case GDK_2BUTTON_PRESS:
951 case GDK_BUTTON_PRESS:
952 case GDK_BUTTON_RELEASE:
953 if (bevent->state & GDK_SHIFT_MASK)
954 flags |= OPEN_SHIFT;
956 if (o_new_window_on_1 ^ (bevent->button == 1))
957 flags |= OPEN_SAME_WINDOW;
959 if (bevent->button != 1)
960 flags |= OPEN_CLOSE_WINDOW;
962 if (o_single_click == FALSE &&
963 (bevent->state & GDK_CONTROL_MASK) != 0)
964 flags ^= OPEN_SAME_WINDOW | OPEN_CLOSE_WINDOW;
965 break;
966 case GDK_KEY_PRESS:
967 flags |= OPEN_SAME_WINDOW;
968 if (kevent->state & GDK_SHIFT_MASK)
969 flags |= OPEN_SHIFT;
970 break;
971 default:
972 break;
975 filer_openitem(filer_window, item_number, flags);
978 /* Return the full path to the directory containing object 'path'.
979 * Relative paths are resolved from the filerwindow's path.
981 static void follow_symlink(FilerWindow *filer_window, char *path)
983 char *real, *slash;
984 char *new_dir;
986 if (path[0] != '/')
987 path = make_path(filer_window->path, path)->str;
989 real = pathdup(path);
990 slash = strrchr(real, '/');
991 if (!slash)
993 g_free(real);
994 delayed_error("ROX-Filer",
995 "Broken symlink (or you don't have permission "
996 "to follow it.");
997 return;
1000 *slash = '\0';
1002 if (*real)
1003 new_dir = real;
1004 else
1005 new_dir = "/";
1007 if (filer_window->panel)
1009 FilerWindow *new;
1011 new = filer_opendir(new_dir, FALSE, BOTTOM);
1012 g_free(new->auto_select);
1013 new->auto_select = g_strdup(slash + 1);
1015 else
1016 filer_change_to(filer_window, new_dir, slash + 1);
1018 g_free(real);
1021 void filer_openitem(FilerWindow *filer_window, int item_number, OpenFlags flags)
1023 gboolean shift = (flags & OPEN_SHIFT) != 0;
1024 gboolean close_mini = flags & OPEN_FROM_MINI;
1025 gboolean same_window = (flags & OPEN_SAME_WINDOW) != 0
1026 && !filer_window->panel;
1027 gboolean close_window = (flags & OPEN_CLOSE_WINDOW) != 0
1028 && !filer_window->panel;
1029 GtkWidget *widget;
1030 char *full_path;
1031 DirItem *item = (DirItem *)
1032 filer_window->collection->items[item_number].data;
1033 gboolean wink = TRUE;
1035 widget = filer_window->window;
1036 full_path = make_path(filer_window->path,
1037 item->leafname)->str;
1039 if (item->flags & ITEM_FLAG_SYMLINK && shift)
1041 char path[MAXPATHLEN + 1];
1042 int got;
1044 got = readlink(make_path(filer_window->path,
1045 item->leafname)->str,
1046 path, MAXPATHLEN);
1047 if (got < 0)
1048 delayed_error("ROX-Filer", g_strerror(errno));
1049 else
1051 g_return_if_fail(got <= MAXPATHLEN);
1052 path[got] = '\0';
1054 follow_symlink(filer_window, path);
1056 return;
1059 switch (item->base_type)
1061 case TYPE_DIRECTORY:
1062 if (item->flags & ITEM_FLAG_APPDIR && !shift)
1064 run_app(make_path(filer_window->path,
1065 item->leafname)->str);
1066 if (close_window)
1067 gtk_widget_destroy(widget);
1068 break;
1071 if (item->flags & ITEM_FLAG_MOUNT_POINT && shift)
1073 action_mount(filer_window, item);
1074 if (item->flags & ITEM_FLAG_MOUNTED)
1075 break;
1078 if (same_window)
1080 wink = FALSE;
1081 filer_change_to(filer_window, full_path, NULL);
1082 close_mini = FALSE;
1084 else
1085 filer_opendir(full_path, FALSE, BOTTOM);
1086 break;
1087 case TYPE_FILE:
1088 if ((item->flags & ITEM_FLAG_EXEC_FILE) && !shift)
1090 char *argv[] = {NULL, NULL};
1092 argv[0] = full_path;
1094 if (spawn_full(argv, getenv("HOME")))
1096 if (close_window)
1098 wink = FALSE;
1099 gtk_widget_destroy(widget);
1102 else
1103 report_error("ROX-Filer",
1104 "Failed to fork() child");
1106 else
1108 GString *message;
1109 MIME_type *type = shift ? &text_plain
1110 : item->mime_type;
1112 g_return_if_fail(type != NULL);
1114 if (type_open(full_path, type))
1116 if (close_window)
1118 wink = FALSE;
1119 gtk_widget_destroy(widget);
1122 else
1124 message = g_string_new(NULL);
1125 g_string_sprintf(message, "No open "
1126 "action specified for files of "
1127 "this type (%s/%s)",
1128 type->media_type,
1129 type->subtype);
1130 report_error("ROX-Filer", message->str);
1131 g_string_free(message, TRUE);
1134 break;
1135 default:
1136 report_error("open_item",
1137 "I don't know how to open that");
1138 break;
1141 if (wink)
1142 collection_wink_item(filer_window->collection, item_number);
1144 if (close_mini)
1145 minibuffer_hide(filer_window);
1148 static gint pointer_in(GtkWidget *widget,
1149 GdkEventCrossing *event,
1150 FilerWindow *filer_window)
1152 may_rescan(filer_window, TRUE);
1153 return FALSE;
1156 static gint focus_in(GtkWidget *widget,
1157 GdkEventFocus *event,
1158 FilerWindow *filer_window)
1160 window_with_focus = filer_window;
1162 return FALSE;
1165 static gint focus_out(GtkWidget *widget,
1166 GdkEventFocus *event,
1167 FilerWindow *filer_window)
1169 /* TODO: Shade the cursor */
1171 return FALSE;
1174 /* Handle keys that can't be bound with the menu */
1175 static gint key_press_event(GtkWidget *widget,
1176 GdkEventKey *event,
1177 FilerWindow *filer_window)
1179 switch (event->keyval)
1181 case GDK_BackSpace:
1182 change_to_parent(filer_window);
1183 break;
1184 default:
1185 return FALSE;
1188 return TRUE;
1191 static void toolbar_refresh_clicked(GtkWidget *widget,
1192 FilerWindow *filer_window)
1194 full_refresh();
1195 update_dir(filer_window, TRUE);
1198 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window)
1200 filer_change_to(filer_window, getenv("HOME"), NULL);
1203 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window)
1205 change_to_parent(filer_window);
1208 void change_to_parent(FilerWindow *filer_window)
1210 char *copy;
1211 char *slash;
1213 if (filer_window->path[0] == '/' && filer_window->path[1] == '\0')
1214 return; /* Already in the root */
1216 copy = g_strdup(filer_window->path);
1217 slash = strrchr(copy, '/');
1219 if (slash)
1221 *slash = '\0';
1222 filer_change_to(filer_window,
1223 *copy ? copy : "/",
1224 slash + 1);
1226 else
1227 g_warning("No / in directory path!\n");
1229 g_free(copy);
1233 /* Make filer_window display path. When finished, highlight item 'from', or
1234 * the first item if from is NULL. If there is currently no cursor then
1235 * simply wink 'from' (if not NULL).
1237 void filer_change_to(FilerWindow *filer_window, char *path, char *from)
1239 char *from_dup;
1241 from_dup = from && *from ? g_strdup(from) : NULL;
1243 detach(filer_window);
1244 g_free(filer_window->path);
1245 filer_window->path = pathdup(path);
1247 filer_window->directory = g_fscache_lookup(dir_cache,
1248 filer_window->path);
1249 if (filer_window->directory)
1251 g_free(filer_window->auto_select);
1252 filer_window->had_cursor =
1253 filer_window->collection->cursor_item != -1
1254 || filer_window->had_cursor;
1255 filer_window->auto_select = from_dup;
1257 gtk_window_set_title(GTK_WINDOW(filer_window->window),
1258 filer_window->path);
1259 collection_set_cursor_item(filer_window->collection, -1);
1260 attach(filer_window);
1262 if (GTK_WIDGET_VISIBLE(filer_window->minibuffer))
1263 gtk_idle_add((GtkFunction) minibuffer_show_cb,
1264 filer_window);
1266 else
1268 char *error;
1270 g_free(from_dup);
1271 error = g_strdup_printf("Directory '%s' is not accessible.",
1272 path);
1273 delayed_error("ROX-Filer", error);
1274 g_free(error);
1275 gtk_widget_destroy(filer_window->window);
1279 int selected_item_number(Collection *collection)
1281 int i;
1283 g_return_val_if_fail(collection != NULL, -1);
1284 g_return_val_if_fail(IS_COLLECTION(collection), -1);
1285 g_return_val_if_fail(collection->number_selected == 1, -1);
1287 for (i = 0; i < collection->number_of_items; i++)
1288 if (collection->items[i].selected)
1289 return i;
1291 g_warning("selected_item: number_selected is wrong\n");
1293 return -1;
1296 DirItem *selected_item(Collection *collection)
1298 int item;
1300 item = selected_item_number(collection);
1302 if (item > -1)
1303 return (DirItem *) collection->items[item].data;
1304 return NULL;
1307 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
1308 FilerWindow *window)
1310 /* TODO: We can open lots of these - very irritating! */
1311 return get_choice("Close panel?",
1312 "You have tried to close a panel via the window "
1313 "manager - I usually find that this is accidental... "
1314 "really close?",
1315 2, "Remove", "Cancel") != 0;
1318 /* Make the items as narrow as possible */
1319 static void shrink_width(FilerWindow *filer_window)
1321 int i;
1322 Collection *col = filer_window->collection;
1323 int width = MIN_ITEM_WIDTH;
1324 int this_width;
1325 DisplayStyle style = filer_window->display_style;
1326 GdkFont *font;
1327 int text_height;
1329 font = gtk_widget_get_default_style()->font;
1330 text_height = font->ascent + font->descent;
1332 for (i = 0; i < col->number_of_items; i++)
1334 this_width = calc_width(filer_window,
1335 (DirItem *) col->items[i].data);
1336 if (this_width > width)
1337 width = this_width;
1340 collection_set_item_size(filer_window->collection,
1341 width,
1342 style == FULL_INFO ? MAX_ICON_HEIGHT + 4 :
1343 style == SMALL_ICONS ? MAX(text_height, SMALL_ICON_HEIGHT) + 4
1344 : text_height + MAX_ICON_HEIGHT + 8);
1347 void filer_set_sort_fn(FilerWindow *filer_window,
1348 int (*fn)(const void *a, const void *b))
1350 if (filer_window->sort_fn == fn)
1351 return;
1353 filer_window->sort_fn = fn;
1354 collection_qsort(filer_window->collection,
1355 filer_window->sort_fn);
1358 void filer_style_set(FilerWindow *filer_window, DisplayStyle style)
1360 if (filer_window->display_style == style)
1361 return;
1363 filer_window->display_style = style;
1364 switch (style)
1366 case SMALL_ICONS:
1367 collection_set_functions(filer_window->collection,
1368 draw_item_small, test_point_small);
1369 break;
1370 case FULL_INFO:
1371 collection_set_functions(filer_window->collection,
1372 draw_item_full_info, test_point_full_info);
1373 break;
1374 default:
1375 collection_set_functions(filer_window->collection,
1376 draw_item_large, test_point_large);
1377 break;
1380 shrink_width(filer_window);
1383 FilerWindow *filer_opendir(char *path, gboolean panel, Side panel_side)
1385 GtkWidget *hbox, *scrollbar, *collection;
1386 FilerWindow *filer_window;
1387 GtkTargetEntry target_table[] =
1389 {"text/uri-list", 0, TARGET_URI_LIST},
1390 {"STRING", 0, TARGET_STRING},
1393 filer_window = g_new(FilerWindow, 1);
1394 filer_window->minibuffer = NULL;
1395 filer_window->path = pathdup(path);
1396 filer_window->had_cursor = FALSE;
1397 filer_window->auto_select = NULL;
1399 filer_window->directory = g_fscache_lookup(dir_cache,
1400 filer_window->path);
1401 if (!filer_window->directory)
1403 char *error;
1405 error = g_strdup_printf("Directory '%s' not found.", path);
1406 delayed_error("ROX-Filer", error);
1407 g_free(error);
1408 g_free(filer_window->path);
1409 g_free(filer_window);
1410 return NULL;
1413 filer_window->show_hidden = FALSE;
1414 filer_window->panel = panel;
1415 filer_window->panel_side = panel_side;
1416 filer_window->temp_item_selected = FALSE;
1417 filer_window->sort_fn = sort_by_type;
1418 filer_window->flags = (FilerFlags) 0;
1419 filer_window->display_style = UNKNOWN_STYLE;
1421 filer_window->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1422 gtk_window_set_title(GTK_WINDOW(filer_window->window),
1423 filer_window->path);
1425 collection = collection_new(NULL);
1426 gtk_object_set_data(GTK_OBJECT(collection),
1427 "filer_window", filer_window);
1428 filer_window->collection = COLLECTION(collection);
1430 gtk_widget_add_events(filer_window->window, GDK_ENTER_NOTIFY);
1431 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1432 "enter-notify-event",
1433 GTK_SIGNAL_FUNC(pointer_in), filer_window);
1434 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_in_event",
1435 GTK_SIGNAL_FUNC(focus_in), filer_window);
1436 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_out_event",
1437 GTK_SIGNAL_FUNC(focus_out), filer_window);
1438 gtk_signal_connect(GTK_OBJECT(filer_window->window), "destroy",
1439 filer_window_destroyed, filer_window);
1441 gtk_signal_connect(GTK_OBJECT(filer_window->collection), "open_item",
1442 open_item, filer_window);
1443 gtk_signal_connect(GTK_OBJECT(collection), "show_menu",
1444 show_menu, filer_window);
1445 gtk_signal_connect(GTK_OBJECT(collection), "gain_selection",
1446 gain_selection, filer_window);
1447 gtk_signal_connect(GTK_OBJECT(collection), "lose_selection",
1448 lose_selection, filer_window);
1449 gtk_signal_connect(GTK_OBJECT(collection), "drag_selection",
1450 drag_selection, filer_window);
1451 gtk_signal_connect(GTK_OBJECT(collection), "drag_data_get",
1452 drag_data_get, filer_window);
1453 gtk_signal_connect(GTK_OBJECT(collection), "selection_clear_event",
1454 GTK_SIGNAL_FUNC(collection_lose_selection), NULL);
1455 gtk_signal_connect (GTK_OBJECT(collection), "selection_get",
1456 GTK_SIGNAL_FUNC(selection_get), NULL);
1457 gtk_selection_add_targets(collection, GDK_SELECTION_PRIMARY,
1458 target_table,
1459 sizeof(target_table) / sizeof(*target_table));
1461 filer_style_set(filer_window, LARGE_ICONS);
1462 drag_set_dest(collection);
1464 if (panel)
1466 int swidth, sheight, iwidth, iheight;
1467 GtkWidget *frame, *win = filer_window->window;
1469 gtk_window_set_wmclass(GTK_WINDOW(win), "ROX-Panel",
1470 "ROX-Filer");
1471 collection_set_panel(filer_window->collection, TRUE);
1472 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1473 "delete_event",
1474 GTK_SIGNAL_FUNC(filer_confirm_close),
1475 filer_window);
1477 gdk_window_get_size(GDK_ROOT_PARENT(), &swidth, &sheight);
1478 iwidth = filer_window->collection->item_width;
1479 iheight = filer_window->collection->item_height;
1481 if (panel_side == TOP || panel_side == BOTTOM)
1483 int height = iheight + PANEL_BORDER;
1484 int y = panel_side == TOP
1485 ? -PANEL_BORDER
1486 : sheight - height - PANEL_BORDER;
1488 gtk_widget_set_usize(collection, swidth, height);
1489 gtk_widget_set_uposition(win, 0, y);
1491 else
1493 int width = iwidth + PANEL_BORDER;
1494 int x = panel_side == LEFT
1495 ? -PANEL_BORDER
1496 : swidth - width - PANEL_BORDER;
1498 gtk_widget_set_usize(collection, width, sheight);
1499 gtk_widget_set_uposition(win, x, 0);
1502 frame = gtk_frame_new(NULL);
1503 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1504 gtk_container_add(GTK_CONTAINER(frame), collection);
1505 gtk_container_add(GTK_CONTAINER(win), frame);
1507 gtk_widget_show_all(frame);
1508 gtk_widget_realize(win);
1509 if (override_redirect)
1510 gdk_window_set_override_redirect(win->window, TRUE);
1511 make_panel_window(win->window);
1513 else
1515 GtkWidget *vbox;
1516 int col_height = ROW_HEIGHT_LARGE * 3;
1518 gtk_signal_connect(GTK_OBJECT(collection),
1519 "key_press_event",
1520 GTK_SIGNAL_FUNC(key_press_event), filer_window);
1521 gtk_window_set_default_size(GTK_WINDOW(filer_window->window),
1522 filer_window->display_style == LARGE_ICONS ? 400 : 512,
1523 o_toolbar == TOOLBAR_NONE ? col_height:
1524 o_toolbar == TOOLBAR_NORMAL ? col_height + 24 :
1525 col_height + 38);
1527 hbox = gtk_hbox_new(FALSE, 0);
1528 gtk_container_add(GTK_CONTAINER(filer_window->window),
1529 hbox);
1531 vbox = gtk_vbox_new(FALSE, 0);
1532 gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
1534 if (o_toolbar != TOOLBAR_NONE)
1536 GtkWidget *toolbar;
1538 toolbar = create_toolbar(filer_window);
1539 gtk_box_pack_start(GTK_BOX(vbox), toolbar,
1540 FALSE, TRUE, 0);
1541 gtk_widget_show_all(toolbar);
1544 gtk_box_pack_start(GTK_BOX(vbox), collection, TRUE, TRUE, 0);
1546 filer_window->minibuffer = create_minibuffer(filer_window);
1547 gtk_box_pack_start(GTK_BOX(vbox), filer_window->minibuffer,
1548 FALSE, TRUE, 0);
1550 scrollbar = gtk_vscrollbar_new(COLLECTION(collection)->vadj);
1551 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, TRUE, 0);
1552 gtk_accel_group_attach(filer_keys,
1553 GTK_OBJECT(filer_window->window));
1554 gtk_window_set_focus(GTK_WINDOW(filer_window->window),
1555 collection);
1557 gtk_widget_show(hbox);
1558 gtk_widget_show(vbox);
1559 gtk_widget_show(scrollbar);
1560 gtk_widget_show(collection);
1563 number_of_windows++;
1564 gtk_widget_show(filer_window->window);
1565 attach(filer_window);
1567 all_filer_windows = g_list_prepend(all_filer_windows, filer_window);
1569 return filer_window;
1572 static GtkWidget *create_toolbar(FilerWindow *filer_window)
1574 GtkWidget *frame, *box;
1576 if (o_toolbar == TOOLBAR_GNOME)
1578 frame = gtk_handle_box_new();
1579 box = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL,
1580 GTK_TOOLBAR_BOTH);
1581 gtk_container_set_border_width(GTK_CONTAINER(box), 2);
1582 gtk_toolbar_set_space_style(GTK_TOOLBAR(box),
1583 GTK_TOOLBAR_SPACE_LINE);
1584 gtk_toolbar_set_button_relief(GTK_TOOLBAR(box),
1585 GTK_RELIEF_NONE);
1587 else
1589 frame = gtk_frame_new(NULL);
1590 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1592 box = gtk_hbutton_box_new();
1593 gtk_button_box_set_child_size_default(16, 16);
1594 gtk_hbutton_box_set_spacing_default(2);
1595 gtk_button_box_set_layout(GTK_BUTTON_BOX(box),
1596 GTK_BUTTONBOX_START);
1599 gtk_container_add(GTK_CONTAINER(frame), box);
1601 add_button(box, TOOLBAR_UP_ICON,
1602 GTK_SIGNAL_FUNC(toolbar_up_clicked),
1603 filer_window,
1604 "Up", "Change to parent directory");
1605 add_button(box, TOOLBAR_HOME_ICON,
1606 GTK_SIGNAL_FUNC(toolbar_home_clicked),
1607 filer_window,
1608 "Home", "Change to home directory");
1609 add_button(box, TOOLBAR_REFRESH_ICON,
1610 GTK_SIGNAL_FUNC(toolbar_refresh_clicked),
1611 filer_window,
1612 "Rescan", "Rescan directory contents");
1614 return frame;
1617 static void add_button(GtkWidget *box, int pixmap,
1618 GtkSignalFunc cb, FilerWindow *filer_window,
1619 char *label, char *tip)
1621 GtkWidget *button, *icon;
1623 icon = gtk_pixmap_new(default_pixmap[pixmap].pixmap,
1624 default_pixmap[pixmap].mask);
1626 if (o_toolbar == TOOLBAR_GNOME)
1628 gtk_toolbar_append_element(GTK_TOOLBAR(box),
1629 GTK_TOOLBAR_CHILD_BUTTON,
1630 NULL,
1631 label,
1632 tip, NULL,
1633 icon,
1634 cb, filer_window);
1636 else
1638 button = gtk_button_new();
1639 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
1641 gtk_container_add(GTK_CONTAINER(button), icon);
1642 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1643 cb, filer_window);
1645 gtk_tooltips_set_tip(tooltips, button, tip, NULL);
1647 gtk_container_add(GTK_CONTAINER(box), button);
1651 /* Build up some option widgets to go in the options dialog, but don't
1652 * fill them in yet.
1654 static GtkWidget *create_options()
1656 GtkWidget *vbox, *menu, *hbox;
1658 vbox = gtk_vbox_new(FALSE, 0);
1659 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
1661 toggle_new_window_on_1 =
1662 gtk_check_button_new_with_label("New window on button 1 "
1663 "(RISC OS style)");
1664 gtk_box_pack_start(GTK_BOX(vbox), toggle_new_window_on_1,
1665 FALSE, TRUE, 0);
1667 toggle_menu_on_2 =
1668 gtk_check_button_new_with_label("Menu on button 2 "
1669 "(RISC OS style)");
1670 gtk_box_pack_start(GTK_BOX(vbox), toggle_menu_on_2, FALSE, TRUE, 0);
1672 toggle_single_click =
1673 gtk_check_button_new_with_label("Single-click nagivation");
1674 gtk_box_pack_start(GTK_BOX(vbox), toggle_single_click, FALSE, TRUE, 0);
1676 hbox = gtk_hbox_new(FALSE, 4);
1677 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
1679 gtk_box_pack_start(GTK_BOX(hbox),
1680 gtk_label_new("Toolbar type for new windows"),
1681 FALSE, TRUE, 0);
1682 menu_toolbar = gtk_option_menu_new();
1683 menu = gtk_menu_new();
1684 gtk_menu_append(GTK_MENU(menu), gtk_menu_item_new_with_label("None"));
1685 gtk_menu_append(GTK_MENU(menu), gtk_menu_item_new_with_label("Normal"));
1686 gtk_menu_append(GTK_MENU(menu), gtk_menu_item_new_with_label("GNOME"));
1687 gtk_option_menu_set_menu(GTK_OPTION_MENU(menu_toolbar), menu);
1688 gtk_box_pack_start(GTK_BOX(hbox), menu_toolbar, TRUE, TRUE, 0);
1690 return vbox;
1693 /* Reflect current state by changing the widgets in the options box */
1694 static void update_options()
1696 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_new_window_on_1),
1697 o_new_window_on_1);
1698 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_menu_on_2),
1699 collection_menu_button == 2 ? 1 : 0);
1700 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_single_click),
1701 o_single_click);
1702 gtk_option_menu_set_history(GTK_OPTION_MENU(menu_toolbar), o_toolbar);
1705 /* Set current values by reading the states of the widgets in the options box */
1706 static void set_options()
1708 GtkWidget *item, *menu;
1709 GList *list;
1711 o_new_window_on_1 = gtk_toggle_button_get_active(
1712 GTK_TOGGLE_BUTTON(toggle_new_window_on_1));
1714 collection_menu_button = gtk_toggle_button_get_active(
1715 GTK_TOGGLE_BUTTON(toggle_menu_on_2)) ? 2 : 3;
1717 o_single_click = gtk_toggle_button_get_active(
1718 GTK_TOGGLE_BUTTON(toggle_single_click));
1719 collection_single_click = o_single_click ? TRUE : FALSE;
1721 menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(menu_toolbar));
1722 item = gtk_menu_get_active(GTK_MENU(menu));
1723 list = gtk_container_children(GTK_CONTAINER(menu));
1724 o_toolbar = (ToolbarType) g_list_index(list, item);
1725 g_list_free(list);
1729 static void save_options()
1731 option_write("filer_new_window_on_1", o_new_window_on_1 ? "1" : "0");
1732 option_write("filer_menu_on_2",
1733 collection_menu_button == 2 ? "1" : "0");
1734 option_write("filer_single_click", o_single_click ? "1" : "0");
1735 option_write("filer_toolbar", o_toolbar == TOOLBAR_NONE ? "None" :
1736 o_toolbar == TOOLBAR_NORMAL ? "Normal" :
1737 o_toolbar == TOOLBAR_GNOME ? "GNOME" :
1738 "Unknown");
1741 static char *filer_new_window_on_1(char *data)
1743 o_new_window_on_1 = atoi(data) != 0;
1744 return NULL;
1747 static char *filer_menu_on_2(char *data)
1749 collection_menu_button = atoi(data) != 0 ? 2 : 3;
1750 return NULL;
1753 static char *filer_single_click(char *data)
1755 o_single_click = atoi(data) != 0;
1756 collection_single_click = o_single_click ? TRUE : FALSE;
1757 return NULL;
1760 static char *filer_toolbar(char *data)
1762 if (g_strcasecmp(data, "None") == 0)
1763 o_toolbar = TOOLBAR_NONE;
1764 else if (g_strcasecmp(data, "Normal") == 0)
1765 o_toolbar = TOOLBAR_NORMAL;
1766 else if (g_strcasecmp(data, "GNOME") == 0)
1767 o_toolbar = TOOLBAR_GNOME;
1768 else
1769 return "Unknown toolbar type";
1771 return NULL;
1774 /* Note that filer_window may not exist after this call. */
1775 void update_dir(FilerWindow *filer_window, gboolean warning)
1777 if (may_rescan(filer_window, warning))
1778 dir_update(filer_window->directory, filer_window->path);
1781 void filer_set_hidden(FilerWindow *filer_window, gboolean hidden)
1783 Directory *dir = filer_window->directory;
1785 if (filer_window->show_hidden == hidden)
1786 return;
1788 filer_window->show_hidden = hidden;
1790 g_fscache_data_ref(dir_cache, dir);
1791 detach(filer_window);
1792 filer_window->directory = dir;
1793 attach(filer_window);
1796 /* Refresh the various caches even if we don't think we need to */
1797 void full_refresh(void)
1799 mount_update(TRUE);
1802 /* This path has been mounted/umounted - update all dirs */
1803 void filer_check_mounted(char *path)
1805 GList *next = all_filer_windows;
1806 int len;
1808 len = strlen(path);
1810 while (next)
1812 FilerWindow *filer_window = (FilerWindow *) next->data;
1814 next = next->next;
1816 if (strncmp(path, filer_window->path, len) == 0)
1818 char s = filer_window->path[len];
1820 if (s == '/' || s == '\0')
1821 update_dir(filer_window, FALSE);
1826 /* Like minibuffer_show(), except that:
1827 * - It returns FALSE (to be used from an idle callback)
1828 * - It checks that the filer window still exists.
1830 static gboolean minibuffer_show_cb(FilerWindow *filer_window)
1832 GList *next = all_filer_windows;
1834 while (next)
1836 FilerWindow *fw = (FilerWindow *) next->data;
1839 if (fw == filer_window)
1841 minibuffer_show(filer_window);
1842 break;
1845 next = next->next;
1848 return FALSE;