r189: Effective permissions are shown in brackets.
[rox-filer.git] / ROX-Filer / src / filer.c
blobc57c20dca76db0c018f3b31333cf412206a2ef26
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 1999, Thomas Leonard, <tal197@ecs.soton.ac.uk>.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* filer.c - code for handling filer windows */
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <sys/param.h>
29 #include <unistd.h>
30 #include <time.h>
31 #include <ctype.h>
33 #include <gtk/gtk.h>
34 #include <gdk/gdkx.h>
35 #include <gdk/gdkkeysyms.h>
36 #include "collection.h"
38 #include "main.h"
39 #include "support.h"
40 #include "gui_support.h"
41 #include "filer.h"
42 #include "pixmaps.h"
43 #include "menu.h"
44 #include "dnd.h"
45 #include "run.h"
46 #include "mount.h"
47 #include "type.h"
48 #include "options.h"
49 #include "action.h"
50 #include "minibuffer.h"
52 #define ROW_HEIGHT_LARGE 64
53 #define ROW_HEIGHT_SMALL 32
54 #define ROW_HEIGHT_FULL_INFO 44
55 #define SMALL_ICON_HEIGHT 20
56 #define SMALL_ICON_WIDTH 48
57 #define MAX_ICON_HEIGHT 42
58 #define MAX_ICON_WIDTH 48
59 #define PANEL_BORDER 2
60 #define MIN_ITEM_WIDTH 64
62 extern int collection_menu_button;
63 extern gboolean collection_single_click;
65 FilerWindow *window_with_focus = NULL;
66 GList *all_filer_windows = NULL;
68 static FilerWindow *window_with_selection = NULL;
70 /* Options bits */
71 static GtkWidget *create_options();
72 static void update_options();
73 static void set_options();
74 static void save_options();
75 static char *filer_single_click(char *data);
76 static char *filer_menu_on_2(char *data);
77 static char *filer_new_window_on_1(char *data);
78 static char *filer_toolbar(char *data);
80 static OptionsSection options =
82 "Filer window options",
83 create_options,
84 update_options,
85 set_options,
86 save_options
89 /* The values correspond to the menu indexes in the option widget */
90 typedef enum {
91 TOOLBAR_NONE = 0,
92 TOOLBAR_NORMAL = 1,
93 TOOLBAR_GNOME = 2,
94 } ToolbarType;
95 static ToolbarType o_toolbar = TOOLBAR_NORMAL;
96 static GtkWidget *menu_toolbar;
98 static gboolean o_single_click = FALSE;
99 static GtkWidget *toggle_single_click;
100 static gboolean o_new_window_on_1 = FALSE; /* Button 1 => New window */
101 static GtkWidget *toggle_new_window_on_1;
102 static GtkWidget *toggle_menu_on_2;
104 /* Static prototypes */
105 static void attach(FilerWindow *filer_window);
106 static void detach(FilerWindow *filer_window);
107 static void filer_window_destroyed(GtkWidget *widget,
108 FilerWindow *filer_window);
109 static void show_menu(Collection *collection, GdkEventButton *event,
110 int number_selected, gpointer user_data);
111 static gint focus_in(GtkWidget *widget,
112 GdkEventFocus *event,
113 FilerWindow *filer_window);
114 static gint focus_out(GtkWidget *widget,
115 GdkEventFocus *event,
116 FilerWindow *filer_window);
117 static void add_item(FilerWindow *filer_window, DirItem *item);
118 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window);
119 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window);
120 static void add_button(GtkWidget *box, int pixmap,
121 GtkSignalFunc cb, FilerWindow *filer_window,
122 char *label, char *tip);
123 static GtkWidget *create_toolbar(FilerWindow *filer_window);
124 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
125 FilerWindow *window);
126 static int calc_width(FilerWindow *filer_window, DirItem *item);
127 static void draw_large_icon(GtkWidget *widget,
128 GdkRectangle *area,
129 DirItem *item,
130 gboolean selected);
131 static void draw_string(GtkWidget *widget,
132 GdkFont *font,
133 char *string,
134 int x,
135 int y,
136 int width,
137 gboolean selected);
138 static void draw_item_large(GtkWidget *widget,
139 CollectionItem *item,
140 GdkRectangle *area);
141 static void draw_item_small(GtkWidget *widget,
142 CollectionItem *item,
143 GdkRectangle *area);
144 static void draw_item_full_info(GtkWidget *widget,
145 CollectionItem *colitem,
146 GdkRectangle *area);
147 static gboolean test_point_large(Collection *collection,
148 int point_x, int point_y,
149 CollectionItem *item,
150 int width, int height);
151 static gboolean test_point_small(Collection *collection,
152 int point_x, int point_y,
153 CollectionItem *item,
154 int width, int height);
155 static gboolean test_point_full_info(Collection *collection,
156 int point_x, int point_y,
157 CollectionItem *item,
158 int width, int height);
159 static void update_display(Directory *dir,
160 DirAction action,
161 GPtrArray *items,
162 FilerWindow *filer_window);
163 static void shrink_width(FilerWindow *filer_window);
164 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning);
165 static void open_item(Collection *collection,
166 gpointer item_data, int item_number,
167 gpointer user_data);
168 static gboolean minibuffer_show_cb(FilerWindow *filer_window);
169 static void set_autoselect(FilerWindow *filer_window, guchar *leaf);
171 static GdkAtom xa_string;
172 enum
174 TARGET_STRING,
175 TARGET_URI_LIST,
178 static GdkCursor *busy_cursor = NULL;
179 static GtkTooltips *tooltips = NULL;
181 void filer_init()
183 xa_string = gdk_atom_intern("STRING", FALSE);
185 options_sections = g_slist_prepend(options_sections, &options);
186 option_register("filer_new_window_on_1", filer_new_window_on_1);
187 option_register("filer_menu_on_2", filer_menu_on_2);
188 option_register("filer_single_click", filer_single_click);
189 option_register("filer_toolbar", filer_toolbar);
191 busy_cursor = gdk_cursor_new(GDK_WATCH);
193 tooltips = gtk_tooltips_new();
196 static gboolean if_deleted(gpointer item, gpointer removed)
198 int i = ((GPtrArray *) removed)->len;
199 DirItem **r = (DirItem **) ((GPtrArray *) removed)->pdata;
200 char *leafname = ((DirItem *) item)->leafname;
202 while (i--)
204 if (strcmp(leafname, r[i]->leafname) == 0)
205 return TRUE;
208 return FALSE;
211 static void update_item(FilerWindow *filer_window, DirItem *item)
213 int i;
214 char *leafname = item->leafname;
216 if (leafname[0] == '.')
218 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
219 || (leafname[1] == '.' && leafname[2] == '\0'))
220 return;
223 i = collection_find_item(filer_window->collection, item, dir_item_cmp);
225 if (i >= 0)
226 collection_draw_item(filer_window->collection, i, TRUE);
227 else
228 g_warning("Failed to find '%s'\n", item->leafname);
231 static void update_display(Directory *dir,
232 DirAction action,
233 GPtrArray *items,
234 FilerWindow *filer_window)
236 int i;
237 int cursor = filer_window->collection->cursor_item;
238 char *as;
239 Collection *collection = filer_window->collection;
241 switch (action)
243 case DIR_ADD:
244 as = filer_window->auto_select;
246 for (i = 0; i < items->len; i++)
248 DirItem *item = (DirItem *) items->pdata[i];
250 add_item(filer_window, item);
252 if (cursor != -1 || !as)
253 continue;
255 if (strcmp(as, item->leafname) != 0)
256 continue;
258 cursor = collection->number_of_items - 1;
259 if (filer_window->had_cursor)
261 collection_set_cursor_item(collection,
262 cursor);
263 filer_window->mini_cursor_base = cursor;
265 else
266 collection_wink_item(collection,
267 cursor);
270 collection_qsort(filer_window->collection,
271 filer_window->sort_fn);
272 break;
273 case DIR_REMOVE:
274 collection_delete_if(filer_window->collection,
275 if_deleted,
276 items);
277 break;
278 case DIR_END_SCAN:
279 if (filer_window->window->window)
280 gdk_window_set_cursor(
281 filer_window->window->window,
282 NULL);
283 shrink_width(filer_window);
284 if (filer_window->had_cursor &&
285 collection->cursor_item == -1)
287 collection_set_cursor_item(collection, 0);
288 filer_window->had_cursor = FALSE;
290 break;
291 case DIR_UPDATE:
292 for (i = 0; i < items->len; i++)
294 DirItem *item = (DirItem *) items->pdata[i];
296 update_item(filer_window, item);
298 collection_qsort(filer_window->collection,
299 filer_window->sort_fn);
300 break;
304 static void attach(FilerWindow *filer_window)
306 gdk_window_set_cursor(filer_window->window->window, busy_cursor);
307 collection_clear(filer_window->collection);
308 dir_attach(filer_window->directory, (DirCallback) update_display,
309 filer_window);
312 static void detach(FilerWindow *filer_window)
314 g_return_if_fail(filer_window->directory != NULL);
316 dir_detach(filer_window->directory,
317 (DirCallback) update_display, filer_window);
318 g_fscache_data_unref(dir_cache, filer_window->directory);
319 filer_window->directory = NULL;
322 static void filer_window_destroyed(GtkWidget *widget,
323 FilerWindow *filer_window)
325 all_filer_windows = g_list_remove(all_filer_windows, filer_window);
327 if (window_with_selection == filer_window)
328 window_with_selection = NULL;
329 if (window_with_focus == filer_window)
330 window_with_focus = NULL;
332 if (filer_window->directory)
333 detach(filer_window);
335 g_free(filer_window->auto_select);
336 g_free(filer_window->path);
337 g_free(filer_window);
339 if (--number_of_windows < 1)
340 gtk_main_quit();
343 static int calc_width(FilerWindow *filer_window, DirItem *item)
345 int pix_width = item->image->width;
347 switch (filer_window->display_style)
349 case FULL_INFO:
350 return MAX_ICON_WIDTH + 12 +
351 MAX(item->details_width, item->name_width);
352 case SMALL_ICONS:
353 return SMALL_ICON_WIDTH + 12 + item->name_width;
354 default:
355 return MAX(pix_width, item->name_width) + 4;
359 /* Add a single object to a directory display */
360 static void add_item(FilerWindow *filer_window, DirItem *item)
362 char *leafname = item->leafname;
363 int item_width;
365 if (leafname[0] == '.')
367 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
368 || (leafname[1] == '.' && leafname[2] == '\0'))
369 return;
372 item_width = calc_width(filer_window, item);
373 if (item_width > filer_window->collection->item_width)
374 collection_set_item_size(filer_window->collection,
375 item_width,
376 filer_window->collection->item_height);
377 collection_insert(filer_window->collection, item);
380 /* Is a point inside an item? */
381 static gboolean test_point_large(Collection *collection,
382 int point_x, int point_y,
383 CollectionItem *colitem,
384 int width, int height)
386 DirItem *item = (DirItem *) colitem->data;
387 GdkFont *font = GTK_WIDGET(collection)->style->font;
388 int text_height = font->ascent + font->descent;
389 MaskedPixmap *image = item->image;
390 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
391 int image_width = (image->width >> 1) + 2;
392 int text_width = (item->name_width >> 1) + 2;
393 int x_limit;
395 if (point_y < image_y)
396 return FALSE; /* Too high up (don't worry about too low) */
398 if (point_y <= image_y + image->height + 2)
399 x_limit = image_width;
400 else if (point_y > height - text_height - 2)
401 x_limit = text_width;
402 else
403 x_limit = MIN(image_width, text_width);
405 return ABS(point_x - (width >> 1)) < x_limit;
408 static gboolean test_point_full_info(Collection *collection,
409 int point_x, int point_y,
410 CollectionItem *colitem,
411 int width, int height)
413 DirItem *item = (DirItem *) colitem->data;
414 GdkFont *font = GTK_WIDGET(collection)->style->font;
415 MaskedPixmap *image = item->image;
416 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
417 int low_top = height
418 - fixed_font->descent - 2 - fixed_font->ascent;
420 if (point_x < image->width + 2)
421 return point_x > 2 && point_y > image_y;
423 point_x -= MAX_ICON_WIDTH + 8;
425 if (point_y >= low_top)
426 return point_x < item->details_width;
427 if (point_y >= low_top - font->ascent - font->descent)
428 return point_x < item->name_width;
429 return FALSE;
432 static gboolean test_point_small(Collection *collection,
433 int point_x, int point_y,
434 CollectionItem *colitem,
435 int width, int height)
437 DirItem *item = (DirItem *) colitem->data;
438 MaskedPixmap *image = item->image;
439 int image_y = MAX(0, SMALL_ICON_HEIGHT - image->height);
440 GdkFont *font = GTK_WIDGET(collection)->style->font;
441 int low_top = height
442 - fixed_font->descent - 2 - font->ascent;
443 int iwidth = MIN(SMALL_ICON_WIDTH, image->width);
445 if (point_x < iwidth + 2)
446 return point_x > 2 && point_y > image_y;
448 point_x -= SMALL_ICON_WIDTH + 4;
450 if (point_y >= low_top)
451 return point_x < item->name_width;
452 return FALSE;
455 static void draw_small_icon(GtkWidget *widget,
456 GdkRectangle *area,
457 DirItem *item,
458 gboolean selected)
460 MaskedPixmap *image = item->image;
461 int width = MIN(image->width, SMALL_ICON_WIDTH);
462 int height = MIN(image->height, SMALL_ICON_HEIGHT);
463 int image_x = area->x + ((area->width - width) >> 1);
464 int image_y;
465 GdkGC *gc = selected ? widget->style->white_gc
466 : widget->style->black_gc;
467 if (!item->image)
468 return;
470 gdk_gc_set_clip_mask(gc, item->image->mask);
472 image_y = MAX(0, SMALL_ICON_HEIGHT - image->height);
473 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
474 gdk_draw_pixmap(widget->window, gc,
475 item->image->pixmap,
476 0, 0, /* Source x,y */
477 image_x, area->y + image_y, /* Dest x,y */
478 width, height);
480 if (selected)
482 gdk_gc_set_function(gc, GDK_INVERT);
483 gdk_draw_rectangle(widget->window,
485 TRUE, image_x, area->y + image_y,
486 width, height);
487 gdk_gc_set_function(gc, GDK_COPY);
490 if (item->flags & ITEM_FLAG_SYMLINK)
492 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
493 gdk_gc_set_clip_mask(gc,
494 default_pixmap[TYPE_SYMLINK].mask);
495 gdk_draw_pixmap(widget->window, gc,
496 default_pixmap[TYPE_SYMLINK].pixmap,
497 0, 0, /* Source x,y */
498 image_x, area->y + 8, /* Dest x,y */
499 -1, -1);
501 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
503 int type = item->flags & ITEM_FLAG_MOUNTED
504 ? TYPE_MOUNTED
505 : TYPE_UNMOUNTED;
506 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
507 gdk_gc_set_clip_mask(gc,
508 default_pixmap[type].mask);
509 gdk_draw_pixmap(widget->window, gc,
510 default_pixmap[type].pixmap,
511 0, 0, /* Source x,y */
512 image_x, area->y + 8, /* Dest x,y */
513 -1, -1);
516 gdk_gc_set_clip_mask(gc, NULL);
517 gdk_gc_set_clip_origin(gc, 0, 0);
520 static void draw_large_icon(GtkWidget *widget,
521 GdkRectangle *area,
522 DirItem *item,
523 gboolean selected)
525 MaskedPixmap *image = item->image;
526 int width = MIN(image->width, MAX_ICON_WIDTH);
527 int height = MIN(image->height, MAX_ICON_WIDTH);
528 int image_x = area->x + ((area->width - width) >> 1);
529 int image_y;
530 GdkGC *gc = selected ? widget->style->white_gc
531 : widget->style->black_gc;
533 gdk_gc_set_clip_mask(gc, item->image->mask);
535 image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
536 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
537 gdk_draw_pixmap(widget->window, gc,
538 item->image->pixmap,
539 0, 0, /* Source x,y */
540 image_x, area->y + image_y, /* Dest x,y */
541 width, height);
543 if (selected)
545 gdk_gc_set_function(gc, GDK_INVERT);
546 gdk_draw_rectangle(widget->window,
548 TRUE, image_x, area->y + image_y,
549 width, height);
550 gdk_gc_set_function(gc, GDK_COPY);
553 if (item->flags & ITEM_FLAG_SYMLINK)
555 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
556 gdk_gc_set_clip_mask(gc,
557 default_pixmap[TYPE_SYMLINK].mask);
558 gdk_draw_pixmap(widget->window, gc,
559 default_pixmap[TYPE_SYMLINK].pixmap,
560 0, 0, /* Source x,y */
561 image_x, area->y + 8, /* Dest x,y */
562 -1, -1);
564 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
566 int type = item->flags & ITEM_FLAG_MOUNTED
567 ? TYPE_MOUNTED
568 : TYPE_UNMOUNTED;
569 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
570 gdk_gc_set_clip_mask(gc,
571 default_pixmap[type].mask);
572 gdk_draw_pixmap(widget->window, gc,
573 default_pixmap[type].pixmap,
574 0, 0, /* Source x,y */
575 image_x, area->y + 8, /* Dest x,y */
576 -1, -1);
579 gdk_gc_set_clip_mask(gc, NULL);
580 gdk_gc_set_clip_origin(gc, 0, 0);
583 static void draw_string(GtkWidget *widget,
584 GdkFont *font,
585 char *string,
586 int x,
587 int y,
588 int width,
589 gboolean selected)
591 int text_height = font->ascent + font->descent;
593 if (selected)
594 gtk_paint_flat_box(widget->style, widget->window,
595 GTK_STATE_SELECTED, GTK_SHADOW_NONE,
596 NULL, widget, "text",
597 x, y - font->ascent,
598 width,
599 text_height);
601 gdk_draw_text(widget->window,
602 font,
603 selected ? widget->style->white_gc
604 : widget->style->black_gc,
605 x, y,
606 string, strlen(string));
609 /* Return a string (valid until next call) giving details
610 * of this item.
612 char *details(DirItem *item)
614 mode_t m = item->mode;
615 static GString *buf = NULL;
617 if (!buf)
618 buf = g_string_new(NULL);
620 g_string_sprintf(buf, "%s, %s, %s",
621 S_ISDIR(m) ? "Dir" :
622 S_ISCHR(m) ? "Char" :
623 S_ISBLK(m) ? "Blck" :
624 S_ISLNK(m) ? "Link" :
625 S_ISSOCK(m) ? "Sock" :
626 S_ISFIFO(m) ? "Pipe" : "File",
627 pretty_permissions(item->uid, item->gid, m),
628 format_size(item->size));
629 return buf->str;
632 static void draw_item_full_info(GtkWidget *widget,
633 CollectionItem *colitem,
634 GdkRectangle *area)
636 DirItem *item = (DirItem *) colitem->data;
637 MaskedPixmap *image = item->image;
638 GdkFont *font = widget->style->font;
639 int text_x = area->x + MAX_ICON_WIDTH + 8;
640 int low_text_y = area->y + area->height - fixed_font->descent - 2;
641 gboolean selected = colitem->selected;
642 GdkRectangle pic_area;
644 pic_area.x = area->x;
645 pic_area.y = area->y;
646 pic_area.width = image->width + 8;
647 pic_area.height = area->height;
649 draw_large_icon(widget, &pic_area, item, selected);
651 draw_string(widget,
652 widget->style->font,
653 item->leafname,
654 text_x,
655 low_text_y - font->descent - fixed_font->ascent,
656 item->name_width,
657 selected);
658 draw_string(widget,
659 fixed_font,
660 details(item),
661 text_x, low_text_y,
662 item->details_width,
663 selected);
666 static void draw_item_small(GtkWidget *widget,
667 CollectionItem *colitem,
668 GdkRectangle *area)
670 DirItem *item = (DirItem *) colitem->data;
671 GdkFont *font = widget->style->font;
672 int text_x = area->x + SMALL_ICON_WIDTH + 4;
673 int low_text_y = area->y + area->height - font->descent - 2;
674 gboolean selected = colitem->selected;
675 GdkRectangle pic_area;
677 pic_area.x = area->x;
678 pic_area.y = area->y;
679 pic_area.width = SMALL_ICON_WIDTH;
680 pic_area.height = SMALL_ICON_HEIGHT;
682 draw_small_icon(widget, &pic_area, item, selected);
684 draw_string(widget,
685 widget->style->font,
686 item->leafname,
687 text_x,
688 low_text_y,
689 item->name_width,
690 selected);
693 static void draw_item_large(GtkWidget *widget,
694 CollectionItem *colitem,
695 GdkRectangle *area)
697 DirItem *item = (DirItem *) colitem->data;
698 GdkFont *font = widget->style->font;
699 int text_x = area->x + ((area->width - item->name_width) >> 1);
700 int text_y = area->y + area->height - font->descent - 2;
701 gboolean selected = colitem->selected;
703 draw_large_icon(widget, area, item, selected);
705 draw_string(widget,
706 widget->style->font,
707 item->leafname,
708 text_x, text_y, item->name_width,
709 selected);
712 static void show_menu(Collection *collection, GdkEventButton *event,
713 int item, gpointer user_data)
715 show_filer_menu((FilerWindow *) user_data, event, item);
718 /* Returns TRUE iff the directory still exits. */
719 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning)
721 Directory *dir;
723 g_return_val_if_fail(filer_window != NULL, FALSE);
725 /* We do a fresh lookup (rather than update) because the inode may
726 * have changed.
728 dir = g_fscache_lookup(dir_cache, filer_window->path);
729 if (!dir)
731 if (warning)
732 delayed_error("ROX-Filer", "Directory missing/deleted");
733 gtk_widget_destroy(filer_window->window);
734 return FALSE;
736 if (dir == filer_window->directory)
737 g_fscache_data_unref(dir_cache, dir);
738 else
740 detach(filer_window);
741 filer_window->directory = dir;
742 attach(filer_window);
745 return TRUE;
748 /* Another app has grabbed the selection */
749 static gint collection_lose_selection(GtkWidget *widget,
750 GdkEventSelection *event)
752 if (window_with_selection &&
753 window_with_selection->collection == COLLECTION(widget))
755 FilerWindow *filer_window = window_with_selection;
756 window_with_selection = NULL;
757 collection_clear_selection(filer_window->collection);
760 return TRUE;
763 /* Someone wants us to send them the selection */
764 static void selection_get(GtkWidget *widget,
765 GtkSelectionData *selection_data,
766 guint info,
767 guint time,
768 gpointer data)
770 GString *reply, *header;
771 FilerWindow *filer_window;
772 int i;
773 Collection *collection;
775 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
777 reply = g_string_new(NULL);
778 header = g_string_new(NULL);
780 switch (info)
782 case TARGET_STRING:
783 g_string_sprintf(header, " %s",
784 make_path(filer_window->path, "")->str);
785 break;
786 case TARGET_URI_LIST:
787 g_string_sprintf(header, " file://%s%s",
788 our_host_name(),
789 make_path(filer_window->path, "")->str);
790 break;
793 collection = filer_window->collection;
794 for (i = 0; i < collection->number_of_items; i++)
796 if (collection->items[i].selected)
798 DirItem *item =
799 (DirItem *) collection->items[i].data;
801 g_string_append(reply, header->str);
802 g_string_append(reply, item->leafname);
805 /* This works, but I don't think I like it... */
806 /* g_string_append_c(reply, ' '); */
808 gtk_selection_data_set(selection_data, xa_string,
809 8, reply->str + 1, reply->len - 1);
810 g_string_free(reply, TRUE);
811 g_string_free(header, TRUE);
814 /* No items are now selected. This might be because another app claimed
815 * the selection or because the user unselected all the items.
817 static void lose_selection(Collection *collection,
818 guint time,
819 gpointer user_data)
821 FilerWindow *filer_window = (FilerWindow *) user_data;
823 if (window_with_selection == filer_window)
825 window_with_selection = NULL;
826 gtk_selection_owner_set(NULL,
827 GDK_SELECTION_PRIMARY,
828 time);
832 static void gain_selection(Collection *collection,
833 guint time,
834 gpointer user_data)
836 FilerWindow *filer_window = (FilerWindow *) user_data;
838 if (gtk_selection_owner_set(GTK_WIDGET(collection),
839 GDK_SELECTION_PRIMARY,
840 time))
842 window_with_selection = filer_window;
844 else
845 collection_clear_selection(filer_window->collection);
848 int sort_by_name(const void *item1, const void *item2)
850 return strcmp((*((DirItem **)item1))->leafname,
851 (*((DirItem **)item2))->leafname);
854 int sort_by_type(const void *item1, const void *item2)
856 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
857 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
858 MIME_type *m1, *m2;
860 int diff = i1->base_type - i2->base_type;
862 if (!diff)
863 diff = (i1->flags & ITEM_FLAG_APPDIR)
864 - (i2->flags & ITEM_FLAG_APPDIR);
865 if (diff)
866 return diff > 0 ? 1 : -1;
868 m1 = i1->mime_type;
869 m2 = i2->mime_type;
871 if (m1 && m2)
873 diff = strcmp(m1->media_type, m2->media_type);
874 if (!diff)
875 diff = strcmp(m1->subtype, m2->subtype);
877 else if (m1 || m2)
878 diff = m1 ? 1 : -1;
879 else
880 diff = 0;
882 if (diff)
883 return diff > 0 ? 1 : -1;
885 return sort_by_name(item1, item2);
888 int sort_by_date(const void *item1, const void *item2)
890 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
891 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
893 return i1->mtime > i2->mtime ? -1 :
894 i1->mtime < i2->mtime ? 1 :
895 sort_by_name(item1, item2);
898 int sort_by_size(const void *item1, const void *item2)
900 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
901 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
903 return i1->size > i2->size ? -1 :
904 i1->size < i2->size ? 1 :
905 sort_by_name(item1, item2);
908 static void open_item(Collection *collection,
909 gpointer item_data, int item_number,
910 gpointer user_data)
912 FilerWindow *filer_window = (FilerWindow *) user_data;
913 GdkEvent *event;
914 GdkEventButton *bevent;
915 GdkEventKey *kevent;
916 OpenFlags flags = 0;
918 event = (GdkEvent *) gtk_get_current_event();
920 bevent = (GdkEventButton *) event;
921 kevent = (GdkEventKey *) event;
923 switch (event->type)
925 case GDK_2BUTTON_PRESS:
926 case GDK_BUTTON_PRESS:
927 case GDK_BUTTON_RELEASE:
928 if (bevent->state & GDK_SHIFT_MASK)
929 flags |= OPEN_SHIFT;
931 if (o_new_window_on_1 ^ (bevent->button == 1))
932 flags |= OPEN_SAME_WINDOW;
934 if (bevent->button != 1)
935 flags |= OPEN_CLOSE_WINDOW;
937 if (o_single_click == FALSE &&
938 (bevent->state & GDK_CONTROL_MASK) != 0)
939 flags ^= OPEN_SAME_WINDOW | OPEN_CLOSE_WINDOW;
940 break;
941 case GDK_KEY_PRESS:
942 flags |= OPEN_SAME_WINDOW;
943 if (kevent->state & GDK_SHIFT_MASK)
944 flags |= OPEN_SHIFT;
945 break;
946 default:
947 break;
950 filer_openitem(filer_window, item_number, flags);
953 /* Return the full path to the directory containing object 'path'.
954 * Relative paths are resolved from the filerwindow's path.
956 static void follow_symlink(FilerWindow *filer_window, char *path,
957 gboolean same_window)
959 char *real, *slash;
960 char *new_dir;
962 if (path[0] != '/')
963 path = make_path(filer_window->path, path)->str;
965 real = pathdup(path);
966 slash = strrchr(real, '/');
967 if (!slash)
969 g_free(real);
970 delayed_error("ROX-Filer",
971 "Broken symlink (or you don't have permission "
972 "to follow it).");
973 return;
976 *slash = '\0';
978 if (*real)
979 new_dir = real;
980 else
981 new_dir = "/";
983 if (filer_window->panel || !same_window)
985 FilerWindow *new;
987 new = filer_opendir(new_dir, FALSE, BOTTOM);
988 set_autoselect(new, slash + 1);
990 else
991 filer_change_to(filer_window, new_dir, slash + 1);
993 g_free(real);
996 void filer_openitem(FilerWindow *filer_window, int item_number, OpenFlags flags)
998 gboolean shift = (flags & OPEN_SHIFT) != 0;
999 gboolean close_mini = flags & OPEN_FROM_MINI;
1000 gboolean same_window = (flags & OPEN_SAME_WINDOW) != 0
1001 && !filer_window->panel;
1002 gboolean close_window = (flags & OPEN_CLOSE_WINDOW) != 0
1003 && !filer_window->panel;
1004 GtkWidget *widget;
1005 char *full_path;
1006 DirItem *item = (DirItem *)
1007 filer_window->collection->items[item_number].data;
1008 gboolean wink = TRUE, destroy = FALSE;
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,
1030 flags & OPEN_SAME_WINDOW);
1032 return;
1035 switch (item->base_type)
1037 case TYPE_DIRECTORY:
1038 if (item->flags & ITEM_FLAG_APPDIR && !shift)
1040 run_app(make_path(filer_window->path,
1041 item->leafname)->str);
1042 if (close_window)
1043 destroy = TRUE;
1044 break;
1047 if (item->flags & ITEM_FLAG_MOUNT_POINT && shift)
1049 action_mount(filer_window, item);
1050 if (item->flags & ITEM_FLAG_MOUNTED)
1051 break;
1054 if (same_window)
1056 wink = FALSE;
1057 filer_change_to(filer_window, full_path, NULL);
1058 close_mini = FALSE;
1060 else
1061 filer_opendir(full_path, FALSE, BOTTOM);
1062 break;
1063 case TYPE_FILE:
1064 if ((item->flags & ITEM_FLAG_EXEC_FILE) && !shift)
1066 char *argv[] = {NULL, NULL};
1068 argv[0] = full_path;
1070 if (spawn_full(argv, getenv("HOME")))
1072 if (close_window)
1073 destroy = TRUE;
1075 else
1076 report_error("ROX-Filer",
1077 "Failed to fork() child");
1079 else
1081 GString *message;
1082 MIME_type *type = shift ? &text_plain
1083 : item->mime_type;
1085 g_return_if_fail(type != NULL);
1087 if (type_open(full_path, type))
1089 if (close_window)
1090 destroy = TRUE;
1092 else
1094 message = g_string_new(NULL);
1095 g_string_sprintf(message, "No open "
1096 "action specified for files of "
1097 "this type (%s/%s)",
1098 type->media_type,
1099 type->subtype);
1100 report_error("ROX-Filer", message->str);
1101 g_string_free(message, TRUE);
1104 break;
1105 default:
1106 report_error("open_item",
1107 "I don't know how to open that");
1108 break;
1111 if (destroy)
1112 gtk_widget_destroy(filer_window->window);
1113 else
1115 if (wink)
1116 collection_wink_item(filer_window->collection,
1117 item_number);
1118 if (close_mini)
1119 minibuffer_hide(filer_window);
1123 static gint pointer_in(GtkWidget *widget,
1124 GdkEventCrossing *event,
1125 FilerWindow *filer_window)
1127 may_rescan(filer_window, TRUE);
1128 return FALSE;
1131 static gint focus_in(GtkWidget *widget,
1132 GdkEventFocus *event,
1133 FilerWindow *filer_window)
1135 window_with_focus = filer_window;
1137 return FALSE;
1140 static gint focus_out(GtkWidget *widget,
1141 GdkEventFocus *event,
1142 FilerWindow *filer_window)
1144 /* TODO: Shade the cursor */
1146 return FALSE;
1149 /* Handle keys that can't be bound with the menu */
1150 static gint key_press_event(GtkWidget *widget,
1151 GdkEventKey *event,
1152 FilerWindow *filer_window)
1154 switch (event->keyval)
1156 case GDK_BackSpace:
1157 change_to_parent(filer_window);
1158 break;
1159 default:
1160 return FALSE;
1163 return TRUE;
1166 static void toolbar_refresh_clicked(GtkWidget *widget,
1167 FilerWindow *filer_window)
1169 full_refresh();
1170 update_dir(filer_window, TRUE);
1173 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window)
1175 filer_change_to(filer_window, getenv("HOME"), NULL);
1178 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window)
1180 change_to_parent(filer_window);
1183 void change_to_parent(FilerWindow *filer_window)
1185 char *copy;
1186 char *slash;
1188 if (filer_window->path[0] == '/' && filer_window->path[1] == '\0')
1189 return; /* Already in the root */
1191 copy = g_strdup(filer_window->path);
1192 slash = strrchr(copy, '/');
1194 if (slash)
1196 *slash = '\0';
1197 filer_change_to(filer_window,
1198 *copy ? copy : "/",
1199 slash + 1);
1201 else
1202 g_warning("No / in directory path!\n");
1204 g_free(copy);
1208 /* Make filer_window display path. When finished, highlight item 'from', or
1209 * the first item if from is NULL. If there is currently no cursor then
1210 * simply wink 'from' (if not NULL).
1212 void filer_change_to(FilerWindow *filer_window, char *path, char *from)
1214 char *from_dup;
1216 from_dup = from && *from ? g_strdup(from) : NULL;
1218 detach(filer_window);
1219 g_free(filer_window->path);
1220 filer_window->path = pathdup(path);
1222 filer_window->directory = g_fscache_lookup(dir_cache,
1223 filer_window->path);
1224 if (filer_window->directory)
1226 g_free(filer_window->auto_select);
1227 filer_window->had_cursor =
1228 filer_window->collection->cursor_item != -1
1229 || filer_window->had_cursor;
1230 filer_window->auto_select = from_dup;
1232 gtk_window_set_title(GTK_WINDOW(filer_window->window),
1233 filer_window->path);
1234 collection_set_cursor_item(filer_window->collection, -1);
1235 attach(filer_window);
1237 if (GTK_WIDGET_VISIBLE(filer_window->minibuffer))
1238 gtk_idle_add((GtkFunction) minibuffer_show_cb,
1239 filer_window);
1241 else
1243 char *error;
1245 g_free(from_dup);
1246 error = g_strdup_printf("Directory '%s' is not accessible.",
1247 path);
1248 delayed_error("ROX-Filer", error);
1249 g_free(error);
1250 gtk_widget_destroy(filer_window->window);
1254 int selected_item_number(Collection *collection)
1256 int i;
1258 g_return_val_if_fail(collection != NULL, -1);
1259 g_return_val_if_fail(IS_COLLECTION(collection), -1);
1260 g_return_val_if_fail(collection->number_selected == 1, -1);
1262 for (i = 0; i < collection->number_of_items; i++)
1263 if (collection->items[i].selected)
1264 return i;
1266 g_warning("selected_item: number_selected is wrong\n");
1268 return -1;
1271 DirItem *selected_item(Collection *collection)
1273 int item;
1275 item = selected_item_number(collection);
1277 if (item > -1)
1278 return (DirItem *) collection->items[item].data;
1279 return NULL;
1282 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
1283 FilerWindow *window)
1285 /* TODO: We can open lots of these - very irritating! */
1286 return get_choice("Close panel?",
1287 "You have tried to close a panel via the window "
1288 "manager - I usually find that this is accidental... "
1289 "really close?",
1290 2, "Remove", "Cancel") != 0;
1293 /* Make the items as narrow as possible */
1294 static void shrink_width(FilerWindow *filer_window)
1296 int i;
1297 Collection *col = filer_window->collection;
1298 int width = MIN_ITEM_WIDTH;
1299 int this_width;
1300 DisplayStyle style = filer_window->display_style;
1301 GdkFont *font;
1302 int text_height;
1304 font = gtk_widget_get_default_style()->font;
1305 text_height = font->ascent + font->descent;
1307 for (i = 0; i < col->number_of_items; i++)
1309 this_width = calc_width(filer_window,
1310 (DirItem *) col->items[i].data);
1311 if (this_width > width)
1312 width = this_width;
1315 collection_set_item_size(filer_window->collection,
1316 width,
1317 style == FULL_INFO ? MAX_ICON_HEIGHT + 4 :
1318 style == SMALL_ICONS ? MAX(text_height, SMALL_ICON_HEIGHT) + 4
1319 : text_height + MAX_ICON_HEIGHT + 8);
1322 void filer_set_sort_fn(FilerWindow *filer_window,
1323 int (*fn)(const void *a, const void *b))
1325 if (filer_window->sort_fn == fn)
1326 return;
1328 filer_window->sort_fn = fn;
1329 collection_qsort(filer_window->collection,
1330 filer_window->sort_fn);
1333 void filer_style_set(FilerWindow *filer_window, DisplayStyle style)
1335 if (filer_window->display_style == style)
1336 return;
1338 filer_window->display_style = style;
1339 switch (style)
1341 case SMALL_ICONS:
1342 collection_set_functions(filer_window->collection,
1343 draw_item_small, test_point_small);
1344 break;
1345 case FULL_INFO:
1346 collection_set_functions(filer_window->collection,
1347 draw_item_full_info, test_point_full_info);
1348 break;
1349 default:
1350 collection_set_functions(filer_window->collection,
1351 draw_item_large, test_point_large);
1352 break;
1355 shrink_width(filer_window);
1358 FilerWindow *filer_opendir(char *path, gboolean panel, Side panel_side)
1360 GtkWidget *hbox, *scrollbar, *collection;
1361 FilerWindow *filer_window;
1362 GtkTargetEntry target_table[] =
1364 {"text/uri-list", 0, TARGET_URI_LIST},
1365 {"STRING", 0, TARGET_STRING},
1368 filer_window = g_new(FilerWindow, 1);
1369 filer_window->minibuffer = NULL;
1370 filer_window->path = pathdup(path);
1371 filer_window->had_cursor = FALSE;
1372 filer_window->auto_select = NULL;
1374 filer_window->directory = g_fscache_lookup(dir_cache,
1375 filer_window->path);
1376 if (!filer_window->directory)
1378 char *error;
1380 error = g_strdup_printf("Directory '%s' not found.", path);
1381 delayed_error("ROX-Filer", error);
1382 g_free(error);
1383 g_free(filer_window->path);
1384 g_free(filer_window);
1385 return NULL;
1388 filer_window->show_hidden = FALSE;
1389 filer_window->panel = panel;
1390 filer_window->panel_side = panel_side;
1391 filer_window->temp_item_selected = FALSE;
1392 filer_window->sort_fn = sort_by_type;
1393 filer_window->flags = (FilerFlags) 0;
1394 filer_window->display_style = UNKNOWN_STYLE;
1396 filer_window->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1397 gtk_window_set_title(GTK_WINDOW(filer_window->window),
1398 filer_window->path);
1400 collection = collection_new(NULL);
1401 gtk_object_set_data(GTK_OBJECT(collection),
1402 "filer_window", filer_window);
1403 filer_window->collection = COLLECTION(collection);
1405 gtk_widget_add_events(filer_window->window, GDK_ENTER_NOTIFY);
1406 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1407 "enter-notify-event",
1408 GTK_SIGNAL_FUNC(pointer_in), filer_window);
1409 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_in_event",
1410 GTK_SIGNAL_FUNC(focus_in), filer_window);
1411 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_out_event",
1412 GTK_SIGNAL_FUNC(focus_out), filer_window);
1413 gtk_signal_connect(GTK_OBJECT(filer_window->window), "destroy",
1414 filer_window_destroyed, filer_window);
1416 gtk_signal_connect(GTK_OBJECT(filer_window->collection), "open_item",
1417 open_item, filer_window);
1418 gtk_signal_connect(GTK_OBJECT(collection), "show_menu",
1419 show_menu, filer_window);
1420 gtk_signal_connect(GTK_OBJECT(collection), "gain_selection",
1421 gain_selection, filer_window);
1422 gtk_signal_connect(GTK_OBJECT(collection), "lose_selection",
1423 lose_selection, filer_window);
1424 gtk_signal_connect(GTK_OBJECT(collection), "drag_selection",
1425 drag_selection, filer_window);
1426 gtk_signal_connect(GTK_OBJECT(collection), "drag_data_get",
1427 drag_data_get, filer_window);
1428 gtk_signal_connect(GTK_OBJECT(collection), "selection_clear_event",
1429 GTK_SIGNAL_FUNC(collection_lose_selection), NULL);
1430 gtk_signal_connect (GTK_OBJECT(collection), "selection_get",
1431 GTK_SIGNAL_FUNC(selection_get), NULL);
1432 gtk_selection_add_targets(collection, GDK_SELECTION_PRIMARY,
1433 target_table,
1434 sizeof(target_table) / sizeof(*target_table));
1436 filer_style_set(filer_window, LARGE_ICONS);
1437 drag_set_dest(collection);
1439 if (panel)
1441 int swidth, sheight, iwidth, iheight;
1442 GtkWidget *frame, *win = filer_window->window;
1444 gtk_window_set_wmclass(GTK_WINDOW(win), "ROX-Panel",
1445 "ROX-Filer");
1446 collection_set_panel(filer_window->collection, TRUE);
1447 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1448 "delete_event",
1449 GTK_SIGNAL_FUNC(filer_confirm_close),
1450 filer_window);
1452 gdk_window_get_size(GDK_ROOT_PARENT(), &swidth, &sheight);
1453 iwidth = filer_window->collection->item_width;
1454 iheight = filer_window->collection->item_height;
1456 if (panel_side == TOP || panel_side == BOTTOM)
1458 int height = iheight + PANEL_BORDER;
1459 int y = panel_side == TOP
1460 ? -PANEL_BORDER
1461 : sheight - height - PANEL_BORDER;
1463 gtk_widget_set_usize(collection, swidth, height);
1464 gtk_widget_set_uposition(win, 0, y);
1466 else
1468 int width = iwidth + PANEL_BORDER;
1469 int x = panel_side == LEFT
1470 ? -PANEL_BORDER
1471 : swidth - width - PANEL_BORDER;
1473 gtk_widget_set_usize(collection, width, sheight);
1474 gtk_widget_set_uposition(win, x, 0);
1477 frame = gtk_frame_new(NULL);
1478 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1479 gtk_container_add(GTK_CONTAINER(frame), collection);
1480 gtk_container_add(GTK_CONTAINER(win), frame);
1482 gtk_widget_show_all(frame);
1483 gtk_widget_realize(win);
1484 if (override_redirect)
1485 gdk_window_set_override_redirect(win->window, TRUE);
1486 make_panel_window(win->window);
1488 else
1490 GtkWidget *vbox;
1491 int col_height = ROW_HEIGHT_LARGE * 3;
1493 gtk_signal_connect(GTK_OBJECT(collection),
1494 "key_press_event",
1495 GTK_SIGNAL_FUNC(key_press_event), filer_window);
1496 gtk_window_set_default_size(GTK_WINDOW(filer_window->window),
1497 filer_window->display_style == LARGE_ICONS ? 400 : 512,
1498 o_toolbar == TOOLBAR_NONE ? col_height:
1499 o_toolbar == TOOLBAR_NORMAL ? col_height + 24 :
1500 col_height + 38);
1502 hbox = gtk_hbox_new(FALSE, 0);
1503 gtk_container_add(GTK_CONTAINER(filer_window->window),
1504 hbox);
1506 vbox = gtk_vbox_new(FALSE, 0);
1507 gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
1509 if (o_toolbar != TOOLBAR_NONE)
1511 GtkWidget *toolbar;
1513 toolbar = create_toolbar(filer_window);
1514 gtk_box_pack_start(GTK_BOX(vbox), toolbar,
1515 FALSE, TRUE, 0);
1516 gtk_widget_show_all(toolbar);
1519 gtk_box_pack_start(GTK_BOX(vbox), collection, TRUE, TRUE, 0);
1521 filer_window->minibuffer = create_minibuffer(filer_window);
1522 gtk_box_pack_start(GTK_BOX(vbox), filer_window->minibuffer,
1523 FALSE, TRUE, 0);
1525 scrollbar = gtk_vscrollbar_new(COLLECTION(collection)->vadj);
1526 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, TRUE, 0);
1527 gtk_accel_group_attach(filer_keys,
1528 GTK_OBJECT(filer_window->window));
1529 gtk_window_set_focus(GTK_WINDOW(filer_window->window),
1530 collection);
1532 gtk_widget_show(hbox);
1533 gtk_widget_show(vbox);
1534 gtk_widget_show(scrollbar);
1535 gtk_widget_show(collection);
1538 number_of_windows++;
1539 gtk_widget_show(filer_window->window);
1540 attach(filer_window);
1542 all_filer_windows = g_list_prepend(all_filer_windows, filer_window);
1544 return filer_window;
1547 static GtkWidget *create_toolbar(FilerWindow *filer_window)
1549 GtkWidget *frame, *box;
1551 if (o_toolbar == TOOLBAR_GNOME)
1553 frame = gtk_handle_box_new();
1554 box = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL,
1555 GTK_TOOLBAR_BOTH);
1556 gtk_container_set_border_width(GTK_CONTAINER(box), 2);
1557 gtk_toolbar_set_space_style(GTK_TOOLBAR(box),
1558 GTK_TOOLBAR_SPACE_LINE);
1559 gtk_toolbar_set_button_relief(GTK_TOOLBAR(box),
1560 GTK_RELIEF_NONE);
1562 else
1564 frame = gtk_frame_new(NULL);
1565 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1567 box = gtk_hbutton_box_new();
1568 gtk_button_box_set_child_size_default(16, 16);
1569 gtk_hbutton_box_set_spacing_default(2);
1570 gtk_button_box_set_layout(GTK_BUTTON_BOX(box),
1571 GTK_BUTTONBOX_START);
1574 gtk_container_add(GTK_CONTAINER(frame), box);
1576 add_button(box, TOOLBAR_UP_ICON,
1577 GTK_SIGNAL_FUNC(toolbar_up_clicked),
1578 filer_window,
1579 "Up", "Change to parent directory");
1580 add_button(box, TOOLBAR_HOME_ICON,
1581 GTK_SIGNAL_FUNC(toolbar_home_clicked),
1582 filer_window,
1583 "Home", "Change to home directory");
1584 add_button(box, TOOLBAR_REFRESH_ICON,
1585 GTK_SIGNAL_FUNC(toolbar_refresh_clicked),
1586 filer_window,
1587 "Rescan", "Rescan directory contents");
1589 return frame;
1592 static void add_button(GtkWidget *box, int pixmap,
1593 GtkSignalFunc cb, FilerWindow *filer_window,
1594 char *label, char *tip)
1596 GtkWidget *button, *icon;
1598 icon = gtk_pixmap_new(default_pixmap[pixmap].pixmap,
1599 default_pixmap[pixmap].mask);
1601 if (o_toolbar == TOOLBAR_GNOME)
1603 gtk_toolbar_append_element(GTK_TOOLBAR(box),
1604 GTK_TOOLBAR_CHILD_BUTTON,
1605 NULL,
1606 label,
1607 tip, NULL,
1608 icon,
1609 cb, filer_window);
1611 else
1613 button = gtk_button_new();
1614 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
1616 gtk_container_add(GTK_CONTAINER(button), icon);
1617 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1618 cb, filer_window);
1620 gtk_tooltips_set_tip(tooltips, button, tip, NULL);
1622 gtk_container_add(GTK_CONTAINER(box), button);
1626 /* Build up some option widgets to go in the options dialog, but don't
1627 * fill them in yet.
1629 static GtkWidget *create_options()
1631 GtkWidget *vbox, *menu, *hbox;
1633 vbox = gtk_vbox_new(FALSE, 0);
1634 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
1636 toggle_new_window_on_1 =
1637 gtk_check_button_new_with_label("New window on button 1 "
1638 "(RISC OS style)");
1639 gtk_box_pack_start(GTK_BOX(vbox), toggle_new_window_on_1,
1640 FALSE, TRUE, 0);
1642 toggle_menu_on_2 =
1643 gtk_check_button_new_with_label("Menu on button 2 "
1644 "(RISC OS style)");
1645 gtk_box_pack_start(GTK_BOX(vbox), toggle_menu_on_2, FALSE, TRUE, 0);
1647 toggle_single_click =
1648 gtk_check_button_new_with_label("Single-click nagivation");
1649 gtk_box_pack_start(GTK_BOX(vbox), toggle_single_click, FALSE, TRUE, 0);
1651 hbox = gtk_hbox_new(FALSE, 4);
1652 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
1654 gtk_box_pack_start(GTK_BOX(hbox),
1655 gtk_label_new("Toolbar type for new windows"),
1656 FALSE, TRUE, 0);
1657 menu_toolbar = gtk_option_menu_new();
1658 menu = gtk_menu_new();
1659 gtk_menu_append(GTK_MENU(menu), gtk_menu_item_new_with_label("None"));
1660 gtk_menu_append(GTK_MENU(menu), gtk_menu_item_new_with_label("Normal"));
1661 gtk_menu_append(GTK_MENU(menu), gtk_menu_item_new_with_label("GNOME"));
1662 gtk_option_menu_set_menu(GTK_OPTION_MENU(menu_toolbar), menu);
1663 gtk_box_pack_start(GTK_BOX(hbox), menu_toolbar, TRUE, TRUE, 0);
1665 return vbox;
1668 /* Reflect current state by changing the widgets in the options box */
1669 static void update_options()
1671 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_new_window_on_1),
1672 o_new_window_on_1);
1673 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_menu_on_2),
1674 collection_menu_button == 2 ? 1 : 0);
1675 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_single_click),
1676 o_single_click);
1677 gtk_option_menu_set_history(GTK_OPTION_MENU(menu_toolbar), o_toolbar);
1680 /* Set current values by reading the states of the widgets in the options box */
1681 static void set_options()
1683 GtkWidget *item, *menu;
1684 GList *list;
1686 o_new_window_on_1 = gtk_toggle_button_get_active(
1687 GTK_TOGGLE_BUTTON(toggle_new_window_on_1));
1689 collection_menu_button = gtk_toggle_button_get_active(
1690 GTK_TOGGLE_BUTTON(toggle_menu_on_2)) ? 2 : 3;
1692 o_single_click = gtk_toggle_button_get_active(
1693 GTK_TOGGLE_BUTTON(toggle_single_click));
1694 collection_single_click = o_single_click ? TRUE : FALSE;
1696 menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(menu_toolbar));
1697 item = gtk_menu_get_active(GTK_MENU(menu));
1698 list = gtk_container_children(GTK_CONTAINER(menu));
1699 o_toolbar = (ToolbarType) g_list_index(list, item);
1700 g_list_free(list);
1704 static void save_options()
1706 option_write("filer_new_window_on_1", o_new_window_on_1 ? "1" : "0");
1707 option_write("filer_menu_on_2",
1708 collection_menu_button == 2 ? "1" : "0");
1709 option_write("filer_single_click", o_single_click ? "1" : "0");
1710 option_write("filer_toolbar", o_toolbar == TOOLBAR_NONE ? "None" :
1711 o_toolbar == TOOLBAR_NORMAL ? "Normal" :
1712 o_toolbar == TOOLBAR_GNOME ? "GNOME" :
1713 "Unknown");
1716 static char *filer_new_window_on_1(char *data)
1718 o_new_window_on_1 = atoi(data) != 0;
1719 return NULL;
1722 static char *filer_menu_on_2(char *data)
1724 collection_menu_button = atoi(data) != 0 ? 2 : 3;
1725 return NULL;
1728 static char *filer_single_click(char *data)
1730 o_single_click = atoi(data) != 0;
1731 collection_single_click = o_single_click ? TRUE : FALSE;
1732 return NULL;
1735 static char *filer_toolbar(char *data)
1737 if (g_strcasecmp(data, "None") == 0)
1738 o_toolbar = TOOLBAR_NONE;
1739 else if (g_strcasecmp(data, "Normal") == 0)
1740 o_toolbar = TOOLBAR_NORMAL;
1741 else if (g_strcasecmp(data, "GNOME") == 0)
1742 o_toolbar = TOOLBAR_GNOME;
1743 else
1744 return "Unknown toolbar type";
1746 return NULL;
1749 /* Note that filer_window may not exist after this call. */
1750 void update_dir(FilerWindow *filer_window, gboolean warning)
1752 if (may_rescan(filer_window, warning))
1753 dir_update(filer_window->directory, filer_window->path);
1756 void filer_set_hidden(FilerWindow *filer_window, gboolean hidden)
1758 Directory *dir = filer_window->directory;
1760 if (filer_window->show_hidden == hidden)
1761 return;
1763 filer_window->show_hidden = hidden;
1765 g_fscache_data_ref(dir_cache, dir);
1766 detach(filer_window);
1767 filer_window->directory = dir;
1768 attach(filer_window);
1771 /* Refresh the various caches even if we don't think we need to */
1772 void full_refresh(void)
1774 mount_update(TRUE);
1777 /* This path has been mounted/umounted - update all dirs */
1778 void filer_check_mounted(char *path)
1780 GList *next = all_filer_windows;
1781 int len;
1783 len = strlen(path);
1785 while (next)
1787 FilerWindow *filer_window = (FilerWindow *) next->data;
1789 next = next->next;
1791 if (strncmp(path, filer_window->path, len) == 0)
1793 char s = filer_window->path[len];
1795 if (s == '/' || s == '\0')
1796 update_dir(filer_window, FALSE);
1801 /* Like minibuffer_show(), except that:
1802 * - It returns FALSE (to be used from an idle callback)
1803 * - It checks that the filer window still exists.
1805 static gboolean minibuffer_show_cb(FilerWindow *filer_window)
1807 GList *next = all_filer_windows;
1809 while (next)
1811 FilerWindow *fw = (FilerWindow *) next->data;
1814 if (fw == filer_window)
1816 minibuffer_show(filer_window);
1817 break;
1820 next = next->next;
1823 return FALSE;
1826 /* Highlight (wink or cursor) this item in the filer window. If the item
1827 * isn't already there but we're scanning then highlight it if it
1828 * appears later.
1830 static void set_autoselect(FilerWindow *filer_window, guchar *leaf)
1832 Collection *col = filer_window->collection;
1833 int i;
1835 g_free(filer_window->auto_select);
1836 filer_window->auto_select = NULL;
1838 for (i = 0; i < col->number_of_items; i++)
1840 DirItem *item = (DirItem *) col->items[i].data;
1842 if (strcmp(item->leafname, leaf) == 0)
1844 if (col->cursor_item != -1)
1845 collection_set_cursor_item(col, i);
1846 else
1847 collection_wink_item(col, i);
1848 return;
1852 filer_window->auto_select = g_strdup(leaf);