r200: Fixed some problems with styles and the collection widget. Still not quite
[rox-filer.git] / ROX-Filer / src / filer.c
blob94a2b647615dd757a12a810962a8499154b50f3e
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 DisplayStyle last_display_style = LARGE_ICONS;
69 static gboolean last_show_hidden = FALSE;
70 static int (*last_sort_fn)(const void *a, const void *b) = sort_by_type;
72 static FilerWindow *window_with_selection = NULL;
74 /* Options bits */
75 static guchar *style_to_name(void);
76 static guchar *sort_fn_to_name(void);
77 static void update_options_label(void);
79 static GtkWidget *create_options();
80 static void update_options();
81 static void set_options();
82 static void save_options();
83 static char *filer_single_click(char *data);
84 static char *filer_menu_on_2(char *data);
85 static char *filer_new_window_on_1(char *data);
86 static char *filer_toolbar(char *data);
87 static char *filer_display_style(char *data);
88 static char *filer_sort_by(char *data);
90 static OptionsSection options =
92 "Filer window options",
93 create_options,
94 update_options,
95 set_options,
96 save_options
99 /* The values correspond to the menu indexes in the option widget */
100 typedef enum {
101 TOOLBAR_NONE = 0,
102 TOOLBAR_NORMAL = 1,
103 TOOLBAR_GNOME = 2,
104 } ToolbarType;
105 static ToolbarType o_toolbar = TOOLBAR_NORMAL;
106 static GtkWidget *menu_toolbar;
108 static GtkWidget *display_label;
110 static gboolean o_single_click = FALSE;
111 static GtkWidget *toggle_single_click;
112 static gboolean o_new_window_on_1 = FALSE; /* Button 1 => New window */
113 static GtkWidget *toggle_new_window_on_1;
114 static GtkWidget *toggle_menu_on_2;
116 /* Static prototypes */
117 static void attach(FilerWindow *filer_window);
118 static void detach(FilerWindow *filer_window);
119 static void filer_window_destroyed(GtkWidget *widget,
120 FilerWindow *filer_window);
121 static void show_menu(Collection *collection, GdkEventButton *event,
122 int number_selected, gpointer user_data);
123 static gint focus_in(GtkWidget *widget,
124 GdkEventFocus *event,
125 FilerWindow *filer_window);
126 static gint focus_out(GtkWidget *widget,
127 GdkEventFocus *event,
128 FilerWindow *filer_window);
129 static void add_item(FilerWindow *filer_window, DirItem *item);
130 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window);
131 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window);
132 static void add_button(GtkWidget *box, int pixmap,
133 GtkSignalFunc cb, FilerWindow *filer_window,
134 char *label, char *tip);
135 static GtkWidget *create_toolbar(FilerWindow *filer_window);
136 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
137 FilerWindow *window);
138 static int calc_width(FilerWindow *filer_window, DirItem *item);
139 static void draw_large_icon(GtkWidget *widget,
140 GdkRectangle *area,
141 DirItem *item,
142 gboolean selected);
143 static void draw_string(GtkWidget *widget,
144 GdkFont *font,
145 char *string,
146 int x,
147 int y,
148 int width,
149 gboolean selected);
150 static void draw_item_large(GtkWidget *widget,
151 CollectionItem *item,
152 GdkRectangle *area);
153 static void draw_item_small(GtkWidget *widget,
154 CollectionItem *item,
155 GdkRectangle *area);
156 static void draw_item_full_info(GtkWidget *widget,
157 CollectionItem *colitem,
158 GdkRectangle *area);
159 static gboolean test_point_large(Collection *collection,
160 int point_x, int point_y,
161 CollectionItem *item,
162 int width, int height);
163 static gboolean test_point_small(Collection *collection,
164 int point_x, int point_y,
165 CollectionItem *item,
166 int width, int height);
167 static gboolean test_point_full_info(Collection *collection,
168 int point_x, int point_y,
169 CollectionItem *item,
170 int width, int height);
171 static void update_display(Directory *dir,
172 DirAction action,
173 GPtrArray *items,
174 FilerWindow *filer_window);
175 static void shrink_width(FilerWindow *filer_window);
176 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning);
177 static void open_item(Collection *collection,
178 gpointer item_data, int item_number,
179 gpointer user_data);
180 static gboolean minibuffer_show_cb(FilerWindow *filer_window);
181 static void set_autoselect(FilerWindow *filer_window, guchar *leaf);
183 static GdkAtom xa_string;
184 enum
186 TARGET_STRING,
187 TARGET_URI_LIST,
190 static GdkCursor *busy_cursor = NULL;
191 static GtkTooltips *tooltips = NULL;
193 void filer_init()
195 xa_string = gdk_atom_intern("STRING", FALSE);
197 options_sections = g_slist_prepend(options_sections, &options);
198 option_register("filer_new_window_on_1", filer_new_window_on_1);
199 option_register("filer_menu_on_2", filer_menu_on_2);
200 option_register("filer_single_click", filer_single_click);
201 option_register("filer_toolbar", filer_toolbar);
202 option_register("filer_display_style", filer_display_style);
203 option_register("filer_sort_by", filer_sort_by);
205 busy_cursor = gdk_cursor_new(GDK_WATCH);
207 tooltips = gtk_tooltips_new();
210 static gboolean if_deleted(gpointer item, gpointer removed)
212 int i = ((GPtrArray *) removed)->len;
213 DirItem **r = (DirItem **) ((GPtrArray *) removed)->pdata;
214 char *leafname = ((DirItem *) item)->leafname;
216 while (i--)
218 if (strcmp(leafname, r[i]->leafname) == 0)
219 return TRUE;
222 return FALSE;
225 static void update_item(FilerWindow *filer_window, DirItem *item)
227 int i;
228 char *leafname = item->leafname;
230 if (leafname[0] == '.')
232 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
233 || (leafname[1] == '.' && leafname[2] == '\0'))
234 return;
237 i = collection_find_item(filer_window->collection, item, dir_item_cmp);
239 if (i >= 0)
240 collection_draw_item(filer_window->collection, i, TRUE);
241 else
242 g_warning("Failed to find '%s'\n", item->leafname);
245 static void update_display(Directory *dir,
246 DirAction action,
247 GPtrArray *items,
248 FilerWindow *filer_window)
250 int i;
251 int cursor = filer_window->collection->cursor_item;
252 char *as;
253 Collection *collection = filer_window->collection;
255 switch (action)
257 case DIR_ADD:
258 as = filer_window->auto_select;
260 for (i = 0; i < items->len; i++)
262 DirItem *item = (DirItem *) items->pdata[i];
264 add_item(filer_window, item);
266 if (cursor != -1 || !as)
267 continue;
269 if (strcmp(as, item->leafname) != 0)
270 continue;
272 cursor = collection->number_of_items - 1;
273 if (filer_window->had_cursor)
275 collection_set_cursor_item(collection,
276 cursor);
277 filer_window->mini_cursor_base = cursor;
279 else
280 collection_wink_item(collection,
281 cursor);
284 collection_qsort(filer_window->collection,
285 filer_window->sort_fn);
286 break;
287 case DIR_REMOVE:
288 collection_delete_if(filer_window->collection,
289 if_deleted,
290 items);
291 break;
292 case DIR_END_SCAN:
293 if (filer_window->window->window)
294 gdk_window_set_cursor(
295 filer_window->window->window,
296 NULL);
297 shrink_width(filer_window);
298 if (filer_window->had_cursor &&
299 collection->cursor_item == -1)
301 collection_set_cursor_item(collection, 0);
302 filer_window->had_cursor = FALSE;
304 break;
305 case DIR_UPDATE:
306 for (i = 0; i < items->len; i++)
308 DirItem *item = (DirItem *) items->pdata[i];
310 update_item(filer_window, item);
312 collection_qsort(filer_window->collection,
313 filer_window->sort_fn);
314 break;
318 static void attach(FilerWindow *filer_window)
320 gdk_window_set_cursor(filer_window->window->window, busy_cursor);
321 collection_clear(filer_window->collection);
322 dir_attach(filer_window->directory, (DirCallback) update_display,
323 filer_window);
326 static void detach(FilerWindow *filer_window)
328 g_return_if_fail(filer_window->directory != NULL);
330 dir_detach(filer_window->directory,
331 (DirCallback) update_display, filer_window);
332 g_fscache_data_unref(dir_cache, filer_window->directory);
333 filer_window->directory = NULL;
336 static void filer_window_destroyed(GtkWidget *widget,
337 FilerWindow *filer_window)
339 all_filer_windows = g_list_remove(all_filer_windows, filer_window);
341 if (window_with_selection == filer_window)
342 window_with_selection = NULL;
343 if (window_with_focus == filer_window)
344 window_with_focus = NULL;
346 if (filer_window->directory)
347 detach(filer_window);
349 g_free(filer_window->auto_select);
350 g_free(filer_window->path);
351 g_free(filer_window);
353 if (--number_of_windows < 1)
354 gtk_main_quit();
357 static int calc_width(FilerWindow *filer_window, DirItem *item)
359 int pix_width = item->image->width;
361 switch (filer_window->display_style)
363 case FULL_INFO:
364 return MAX_ICON_WIDTH + 12 +
365 MAX(item->details_width, item->name_width);
366 case SMALL_ICONS:
367 return SMALL_ICON_WIDTH + 12 + item->name_width;
368 default:
369 return MAX(pix_width, item->name_width) + 4;
373 /* Add a single object to a directory display */
374 static void add_item(FilerWindow *filer_window, DirItem *item)
376 char *leafname = item->leafname;
377 int item_width;
379 if (leafname[0] == '.')
381 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
382 || (leafname[1] == '.' && leafname[2] == '\0'))
383 return;
386 item_width = calc_width(filer_window, item);
387 if (item_width > filer_window->collection->item_width)
388 collection_set_item_size(filer_window->collection,
389 item_width,
390 filer_window->collection->item_height);
391 collection_insert(filer_window->collection, item);
394 /* Is a point inside an item? */
395 static gboolean test_point_large(Collection *collection,
396 int point_x, int point_y,
397 CollectionItem *colitem,
398 int width, int height)
400 DirItem *item = (DirItem *) colitem->data;
401 int text_height = item_font->ascent + item_font->descent;
402 MaskedPixmap *image = item->image;
403 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
404 int image_width = (image->width >> 1) + 2;
405 int text_width = (item->name_width >> 1) + 2;
406 int x_limit;
408 if (point_y < image_y)
409 return FALSE; /* Too high up (don't worry about too low) */
411 if (point_y <= image_y + image->height + 2)
412 x_limit = image_width;
413 else if (point_y > height - text_height - 2)
414 x_limit = text_width;
415 else
416 x_limit = MIN(image_width, text_width);
418 return ABS(point_x - (width >> 1)) < x_limit;
421 static gboolean test_point_full_info(Collection *collection,
422 int point_x, int point_y,
423 CollectionItem *colitem,
424 int width, int height)
426 DirItem *item = (DirItem *) colitem->data;
427 MaskedPixmap *image = item->image;
428 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
429 int low_top = height
430 - fixed_font->descent - 2 - fixed_font->ascent;
432 if (point_x < image->width + 2)
433 return point_x > 2 && point_y > image_y;
435 point_x -= MAX_ICON_WIDTH + 8;
437 if (point_y >= low_top)
438 return point_x < item->details_width;
439 if (point_y >= low_top - item_font->ascent - item_font->descent)
440 return point_x < item->name_width;
441 return FALSE;
444 static gboolean test_point_small(Collection *collection,
445 int point_x, int point_y,
446 CollectionItem *colitem,
447 int width, int height)
449 DirItem *item = (DirItem *) colitem->data;
450 MaskedPixmap *image = item->image;
451 int image_y = MAX(0, SMALL_ICON_HEIGHT - image->height);
452 int low_top = height
453 - fixed_font->descent - 2 - item_font->ascent;
454 int iwidth = MIN(SMALL_ICON_WIDTH, image->width);
456 if (point_x < iwidth + 2)
457 return point_x > 2 && point_y > image_y;
459 point_x -= SMALL_ICON_WIDTH + 4;
461 if (point_y >= low_top)
462 return point_x < item->name_width;
463 return FALSE;
466 static void draw_small_icon(GtkWidget *widget,
467 GdkRectangle *area,
468 DirItem *item,
469 gboolean selected)
471 MaskedPixmap *image = item->image;
472 int width = MIN(image->width, SMALL_ICON_WIDTH);
473 int height = MIN(image->height, SMALL_ICON_HEIGHT);
474 int image_x = area->x + ((area->width - width) >> 1);
475 int image_y;
476 GdkGC *gc = selected ? widget->style->white_gc
477 : widget->style->black_gc;
478 if (!item->image)
479 return;
481 gdk_gc_set_clip_mask(gc, item->image->mask);
483 image_y = MAX(0, SMALL_ICON_HEIGHT - image->height);
484 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
485 gdk_draw_pixmap(widget->window, gc,
486 item->image->pixmap,
487 0, 0, /* Source x,y */
488 image_x, area->y + image_y, /* Dest x,y */
489 width, height);
491 if (selected)
493 gdk_gc_set_function(gc, GDK_INVERT);
494 gdk_draw_rectangle(widget->window,
496 TRUE, image_x, area->y + image_y,
497 width, height);
498 gdk_gc_set_function(gc, GDK_COPY);
501 if (item->flags & ITEM_FLAG_SYMLINK)
503 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
504 gdk_gc_set_clip_mask(gc,
505 default_pixmap[TYPE_SYMLINK].mask);
506 gdk_draw_pixmap(widget->window, gc,
507 default_pixmap[TYPE_SYMLINK].pixmap,
508 0, 0, /* Source x,y */
509 image_x, area->y + 8, /* Dest x,y */
510 -1, -1);
512 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
514 int type = item->flags & ITEM_FLAG_MOUNTED
515 ? TYPE_MOUNTED
516 : TYPE_UNMOUNTED;
517 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
518 gdk_gc_set_clip_mask(gc,
519 default_pixmap[type].mask);
520 gdk_draw_pixmap(widget->window, gc,
521 default_pixmap[type].pixmap,
522 0, 0, /* Source x,y */
523 image_x, area->y + 8, /* Dest x,y */
524 -1, -1);
527 gdk_gc_set_clip_mask(gc, NULL);
528 gdk_gc_set_clip_origin(gc, 0, 0);
531 static void draw_large_icon(GtkWidget *widget,
532 GdkRectangle *area,
533 DirItem *item,
534 gboolean selected)
536 MaskedPixmap *image = item->image;
537 int width = MIN(image->width, MAX_ICON_WIDTH);
538 int height = MIN(image->height, MAX_ICON_WIDTH);
539 int image_x = area->x + ((area->width - width) >> 1);
540 int image_y;
541 GdkGC *gc = selected ? widget->style->white_gc
542 : widget->style->black_gc;
544 gdk_gc_set_clip_mask(gc, item->image->mask);
546 image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
547 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
548 gdk_draw_pixmap(widget->window, gc,
549 item->image->pixmap,
550 0, 0, /* Source x,y */
551 image_x, area->y + image_y, /* Dest x,y */
552 width, height);
554 if (selected)
556 gdk_gc_set_function(gc, GDK_INVERT);
557 gdk_draw_rectangle(widget->window,
559 TRUE, image_x, area->y + image_y,
560 width, height);
561 gdk_gc_set_function(gc, GDK_COPY);
564 if (item->flags & ITEM_FLAG_SYMLINK)
566 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
567 gdk_gc_set_clip_mask(gc,
568 default_pixmap[TYPE_SYMLINK].mask);
569 gdk_draw_pixmap(widget->window, gc,
570 default_pixmap[TYPE_SYMLINK].pixmap,
571 0, 0, /* Source x,y */
572 image_x, area->y + 8, /* Dest x,y */
573 -1, -1);
575 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
577 int type = item->flags & ITEM_FLAG_MOUNTED
578 ? TYPE_MOUNTED
579 : TYPE_UNMOUNTED;
580 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
581 gdk_gc_set_clip_mask(gc,
582 default_pixmap[type].mask);
583 gdk_draw_pixmap(widget->window, gc,
584 default_pixmap[type].pixmap,
585 0, 0, /* Source x,y */
586 image_x, area->y + 8, /* Dest x,y */
587 -1, -1);
590 gdk_gc_set_clip_mask(gc, NULL);
591 gdk_gc_set_clip_origin(gc, 0, 0);
594 static void draw_string(GtkWidget *widget,
595 GdkFont *font,
596 char *string,
597 int x,
598 int y,
599 int width,
600 gboolean selected)
602 int text_height = font->ascent + font->descent;
604 if (selected)
605 gtk_paint_flat_box(widget->style, widget->window,
606 GTK_STATE_SELECTED, GTK_SHADOW_NONE,
607 NULL, widget, "text",
608 x, y - font->ascent,
609 width,
610 text_height);
612 gdk_draw_text(widget->window,
613 font,
614 selected ? widget->style->white_gc
615 : widget->style->black_gc,
616 x, y,
617 string, strlen(string));
620 /* Return a string (valid until next call) giving details
621 * of this item.
623 char *details(DirItem *item)
625 mode_t m = item->mode;
626 static GString *buf = NULL;
627 static char time_buf[32];
628 if (!buf)
629 buf = g_string_new(NULL);
631 if (strftime(time_buf, sizeof(time_buf),
632 "%d-%b-%Y %T", localtime(&item->mtime)) == 0)
633 time_buf[0]= 0;
635 g_string_sprintf(buf, "%s%s %s %s",
636 item->flags & ITEM_FLAG_APPDIR? "App " :
637 S_ISDIR(m) ? "Dir " :
638 S_ISCHR(m) ? "Char " :
639 S_ISBLK(m) ? "Blck " :
640 S_ISLNK(m) ? "Link " :
641 S_ISSOCK(m) ? "Sock " :
642 S_ISFIFO(m) ? "Pipe " : "File ",
643 pretty_permissions(m),
644 format_size_aligned(item->size),
645 time_buf);
646 return buf->str;
649 static void draw_item_full_info(GtkWidget *widget,
650 CollectionItem *colitem,
651 GdkRectangle *area)
653 DirItem *item = (DirItem *) colitem->data;
654 MaskedPixmap *image = item->image;
655 int text_x = area->x + MAX_ICON_WIDTH + 8;
656 int low_text_y = area->y + area->height - fixed_font->descent - 2;
657 gboolean selected = colitem->selected;
658 GdkRectangle pic_area;
660 pic_area.x = area->x;
661 pic_area.y = area->y;
662 pic_area.width = image->width + 8;
663 pic_area.height = area->height;
665 draw_large_icon(widget, &pic_area, item, selected);
667 draw_string(widget,
668 item_font,
669 item->leafname,
670 text_x,
671 low_text_y - item_font->descent - fixed_font->ascent,
672 item->name_width,
673 selected);
674 draw_string(widget,
675 fixed_font,
676 details(item),
677 text_x, low_text_y,
678 item->details_width,
679 selected);
680 gdk_draw_rectangle(widget->window,
681 selected ? widget->style->white_gc
682 : widget->style->black_gc,
683 TRUE,
684 text_x - 1 + fixed_width *
685 (5 + 4 * applicable(item->uid, item->gid)),
686 low_text_y + fixed_font->descent - 1,
687 fixed_width * 3 + 1, 1);
690 static void draw_item_small(GtkWidget *widget,
691 CollectionItem *colitem,
692 GdkRectangle *area)
694 DirItem *item = (DirItem *) colitem->data;
695 int text_x = area->x + SMALL_ICON_WIDTH + 4;
696 int low_text_y = area->y + area->height - item_font->descent - 2;
697 gboolean selected = colitem->selected;
698 GdkRectangle pic_area;
700 pic_area.x = area->x;
701 pic_area.y = area->y;
702 pic_area.width = SMALL_ICON_WIDTH;
703 pic_area.height = SMALL_ICON_HEIGHT;
705 draw_small_icon(widget, &pic_area, item, selected);
707 draw_string(widget,
708 item_font,
709 item->leafname,
710 text_x,
711 low_text_y,
712 item->name_width,
713 selected);
716 static void draw_item_large(GtkWidget *widget,
717 CollectionItem *colitem,
718 GdkRectangle *area)
720 DirItem *item = (DirItem *) colitem->data;
721 int text_x = area->x + ((area->width - item->name_width) >> 1);
722 int text_y = area->y + area->height - item_font->descent - 2;
723 gboolean selected = colitem->selected;
725 draw_large_icon(widget, area, item, selected);
727 draw_string(widget,
728 item_font,
729 item->leafname,
730 text_x, text_y, item->name_width,
731 selected);
734 static void show_menu(Collection *collection, GdkEventButton *event,
735 int item, gpointer user_data)
737 show_filer_menu((FilerWindow *) user_data, event, item);
740 /* Returns TRUE iff the directory still exits. */
741 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning)
743 Directory *dir;
745 g_return_val_if_fail(filer_window != NULL, FALSE);
747 /* We do a fresh lookup (rather than update) because the inode may
748 * have changed.
750 dir = g_fscache_lookup(dir_cache, filer_window->path);
751 if (!dir)
753 if (warning)
754 delayed_error("ROX-Filer", "Directory missing/deleted");
755 gtk_widget_destroy(filer_window->window);
756 return FALSE;
758 if (dir == filer_window->directory)
759 g_fscache_data_unref(dir_cache, dir);
760 else
762 detach(filer_window);
763 filer_window->directory = dir;
764 attach(filer_window);
767 return TRUE;
770 /* Another app has grabbed the selection */
771 static gint collection_lose_selection(GtkWidget *widget,
772 GdkEventSelection *event)
774 if (window_with_selection &&
775 window_with_selection->collection == COLLECTION(widget))
777 FilerWindow *filer_window = window_with_selection;
778 window_with_selection = NULL;
779 collection_clear_selection(filer_window->collection);
782 return TRUE;
785 /* Someone wants us to send them the selection */
786 static void selection_get(GtkWidget *widget,
787 GtkSelectionData *selection_data,
788 guint info,
789 guint time,
790 gpointer data)
792 GString *reply, *header;
793 FilerWindow *filer_window;
794 int i;
795 Collection *collection;
797 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
799 reply = g_string_new(NULL);
800 header = g_string_new(NULL);
802 switch (info)
804 case TARGET_STRING:
805 g_string_sprintf(header, " %s",
806 make_path(filer_window->path, "")->str);
807 break;
808 case TARGET_URI_LIST:
809 g_string_sprintf(header, " file://%s%s",
810 our_host_name(),
811 make_path(filer_window->path, "")->str);
812 break;
815 collection = filer_window->collection;
816 for (i = 0; i < collection->number_of_items; i++)
818 if (collection->items[i].selected)
820 DirItem *item =
821 (DirItem *) collection->items[i].data;
823 g_string_append(reply, header->str);
824 g_string_append(reply, item->leafname);
827 /* This works, but I don't think I like it... */
828 /* g_string_append_c(reply, ' '); */
830 gtk_selection_data_set(selection_data, xa_string,
831 8, reply->str + 1, reply->len - 1);
832 g_string_free(reply, TRUE);
833 g_string_free(header, TRUE);
836 /* No items are now selected. This might be because another app claimed
837 * the selection or because the user unselected all the items.
839 static void lose_selection(Collection *collection,
840 guint time,
841 gpointer user_data)
843 FilerWindow *filer_window = (FilerWindow *) user_data;
845 if (window_with_selection == filer_window)
847 window_with_selection = NULL;
848 gtk_selection_owner_set(NULL,
849 GDK_SELECTION_PRIMARY,
850 time);
854 static void gain_selection(Collection *collection,
855 guint time,
856 gpointer user_data)
858 FilerWindow *filer_window = (FilerWindow *) user_data;
860 if (gtk_selection_owner_set(GTK_WIDGET(collection),
861 GDK_SELECTION_PRIMARY,
862 time))
864 window_with_selection = filer_window;
866 else
867 collection_clear_selection(filer_window->collection);
870 int sort_by_name(const void *item1, const void *item2)
872 return strcmp((*((DirItem **)item1))->leafname,
873 (*((DirItem **)item2))->leafname);
876 int sort_by_type(const void *item1, const void *item2)
878 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
879 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
880 MIME_type *m1, *m2;
882 int diff = i1->base_type - i2->base_type;
884 if (!diff)
885 diff = (i1->flags & ITEM_FLAG_APPDIR)
886 - (i2->flags & ITEM_FLAG_APPDIR);
887 if (diff)
888 return diff > 0 ? 1 : -1;
890 m1 = i1->mime_type;
891 m2 = i2->mime_type;
893 if (m1 && m2)
895 diff = strcmp(m1->media_type, m2->media_type);
896 if (!diff)
897 diff = strcmp(m1->subtype, m2->subtype);
899 else if (m1 || m2)
900 diff = m1 ? 1 : -1;
901 else
902 diff = 0;
904 if (diff)
905 return diff > 0 ? 1 : -1;
907 return sort_by_name(item1, item2);
910 int sort_by_date(const void *item1, const void *item2)
912 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
913 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
915 return i1->mtime > i2->mtime ? -1 :
916 i1->mtime < i2->mtime ? 1 :
917 sort_by_name(item1, item2);
920 int sort_by_size(const void *item1, const void *item2)
922 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
923 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
925 return i1->size > i2->size ? -1 :
926 i1->size < i2->size ? 1 :
927 sort_by_name(item1, item2);
930 static void open_item(Collection *collection,
931 gpointer item_data, int item_number,
932 gpointer user_data)
934 FilerWindow *filer_window = (FilerWindow *) user_data;
935 GdkEvent *event;
936 GdkEventButton *bevent;
937 GdkEventKey *kevent;
938 OpenFlags flags = 0;
940 event = (GdkEvent *) gtk_get_current_event();
942 bevent = (GdkEventButton *) event;
943 kevent = (GdkEventKey *) event;
945 switch (event->type)
947 case GDK_2BUTTON_PRESS:
948 case GDK_BUTTON_PRESS:
949 case GDK_BUTTON_RELEASE:
950 if (bevent->state & GDK_SHIFT_MASK)
951 flags |= OPEN_SHIFT;
953 if (o_new_window_on_1 ^ (bevent->button == 1))
954 flags |= OPEN_SAME_WINDOW;
956 if (bevent->button != 1)
957 flags |= OPEN_CLOSE_WINDOW;
959 if (o_single_click == FALSE &&
960 (bevent->state & GDK_CONTROL_MASK) != 0)
961 flags ^= OPEN_SAME_WINDOW | OPEN_CLOSE_WINDOW;
962 break;
963 case GDK_KEY_PRESS:
964 flags |= OPEN_SAME_WINDOW;
965 if (kevent->state & GDK_SHIFT_MASK)
966 flags |= OPEN_SHIFT;
967 break;
968 default:
969 break;
972 filer_openitem(filer_window, item_number, flags);
975 /* Return the full path to the directory containing object 'path'.
976 * Relative paths are resolved from the filerwindow's path.
978 static void follow_symlink(FilerWindow *filer_window, char *path,
979 gboolean same_window)
981 char *real, *slash;
982 char *new_dir;
984 if (path[0] != '/')
985 path = make_path(filer_window->path, path)->str;
987 real = pathdup(path);
988 slash = strrchr(real, '/');
989 if (!slash)
991 g_free(real);
992 delayed_error("ROX-Filer",
993 "Broken symlink (or you don't have permission "
994 "to follow it).");
995 return;
998 *slash = '\0';
1000 if (*real)
1001 new_dir = real;
1002 else
1003 new_dir = "/";
1005 if (filer_window->panel_type || !same_window)
1007 FilerWindow *new;
1009 new = filer_opendir(new_dir, PANEL_NO);
1010 set_autoselect(new, slash + 1);
1012 else
1013 filer_change_to(filer_window, new_dir, slash + 1);
1015 g_free(real);
1018 void filer_openitem(FilerWindow *filer_window, int item_number, OpenFlags flags)
1020 gboolean shift = (flags & OPEN_SHIFT) != 0;
1021 gboolean close_mini = flags & OPEN_FROM_MINI;
1022 gboolean same_window = (flags & OPEN_SAME_WINDOW) != 0
1023 && !filer_window->panel_type;
1024 gboolean close_window = (flags & OPEN_CLOSE_WINDOW) != 0
1025 && !filer_window->panel_type;
1026 GtkWidget *widget;
1027 char *full_path;
1028 DirItem *item = (DirItem *)
1029 filer_window->collection->items[item_number].data;
1030 gboolean wink = TRUE, destroy = FALSE;
1032 widget = filer_window->window;
1033 full_path = make_path(filer_window->path,
1034 item->leafname)->str;
1036 if (item->flags & ITEM_FLAG_SYMLINK && shift)
1038 char path[MAXPATHLEN + 1];
1039 int got;
1041 got = readlink(make_path(filer_window->path,
1042 item->leafname)->str,
1043 path, MAXPATHLEN);
1044 if (got < 0)
1045 delayed_error("ROX-Filer", g_strerror(errno));
1046 else
1048 g_return_if_fail(got <= MAXPATHLEN);
1049 path[got] = '\0';
1051 follow_symlink(filer_window, path,
1052 flags & OPEN_SAME_WINDOW);
1054 return;
1057 switch (item->base_type)
1059 case TYPE_DIRECTORY:
1060 if (item->flags & ITEM_FLAG_APPDIR && !shift)
1062 run_app(make_path(filer_window->path,
1063 item->leafname)->str);
1064 if (close_window)
1065 destroy = TRUE;
1066 break;
1069 if (item->flags & ITEM_FLAG_MOUNT_POINT && shift)
1071 action_mount(filer_window, item);
1072 if (item->flags & ITEM_FLAG_MOUNTED)
1073 break;
1076 if (same_window)
1078 wink = FALSE;
1079 filer_change_to(filer_window, full_path, NULL);
1080 close_mini = FALSE;
1082 else
1083 filer_opendir(full_path, PANEL_NO);
1084 break;
1085 case TYPE_FILE:
1086 if ((item->flags & ITEM_FLAG_EXEC_FILE) && !shift)
1088 char *argv[] = {NULL, NULL};
1090 argv[0] = full_path;
1092 if (spawn_full(argv, filer_window->path))
1094 if (close_window)
1095 destroy = TRUE;
1097 else
1098 report_error("ROX-Filer",
1099 "Failed to fork() child");
1101 else
1103 GString *message;
1104 MIME_type *type = shift ? &text_plain
1105 : item->mime_type;
1107 g_return_if_fail(type != NULL);
1109 if (type_open(full_path, type))
1111 if (close_window)
1112 destroy = TRUE;
1114 else
1116 message = g_string_new(NULL);
1117 g_string_sprintf(message, "No open "
1118 "action specified for files of "
1119 "this type (%s/%s)",
1120 type->media_type,
1121 type->subtype);
1122 report_error("ROX-Filer", message->str);
1123 g_string_free(message, TRUE);
1126 break;
1127 default:
1128 report_error("open_item",
1129 "I don't know how to open that");
1130 break;
1133 if (destroy)
1134 gtk_widget_destroy(filer_window->window);
1135 else
1137 if (wink)
1138 collection_wink_item(filer_window->collection,
1139 item_number);
1140 if (close_mini)
1141 minibuffer_hide(filer_window);
1145 static gint pointer_in(GtkWidget *widget,
1146 GdkEventCrossing *event,
1147 FilerWindow *filer_window)
1149 may_rescan(filer_window, TRUE);
1150 return FALSE;
1153 static gint focus_in(GtkWidget *widget,
1154 GdkEventFocus *event,
1155 FilerWindow *filer_window)
1157 window_with_focus = filer_window;
1159 return FALSE;
1162 static gint focus_out(GtkWidget *widget,
1163 GdkEventFocus *event,
1164 FilerWindow *filer_window)
1166 /* TODO: Shade the cursor */
1168 return FALSE;
1171 /* Handle keys that can't be bound with the menu */
1172 static gint key_press_event(GtkWidget *widget,
1173 GdkEventKey *event,
1174 FilerWindow *filer_window)
1176 switch (event->keyval)
1178 case GDK_BackSpace:
1179 change_to_parent(filer_window);
1180 break;
1181 default:
1182 return FALSE;
1185 return TRUE;
1188 static void toolbar_refresh_clicked(GtkWidget *widget,
1189 FilerWindow *filer_window)
1191 full_refresh();
1192 update_dir(filer_window, TRUE);
1195 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window)
1197 filer_change_to(filer_window, getenv("HOME"), NULL);
1200 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window)
1202 change_to_parent(filer_window);
1205 void change_to_parent(FilerWindow *filer_window)
1207 char *copy;
1208 char *slash;
1210 if (filer_window->path[0] == '/' && filer_window->path[1] == '\0')
1211 return; /* Already in the root */
1213 copy = g_strdup(filer_window->path);
1214 slash = strrchr(copy, '/');
1216 if (slash)
1218 *slash = '\0';
1219 filer_change_to(filer_window,
1220 *copy ? copy : "/",
1221 slash + 1);
1223 else
1224 g_warning("No / in directory path!\n");
1226 g_free(copy);
1230 /* Make filer_window display path. When finished, highlight item 'from', or
1231 * the first item if from is NULL. If there is currently no cursor then
1232 * simply wink 'from' (if not NULL).
1234 void filer_change_to(FilerWindow *filer_window, char *path, char *from)
1236 char *from_dup;
1238 from_dup = from && *from ? g_strdup(from) : NULL;
1240 detach(filer_window);
1241 g_free(filer_window->path);
1242 filer_window->path = pathdup(path);
1244 filer_window->directory = g_fscache_lookup(dir_cache,
1245 filer_window->path);
1246 if (filer_window->directory)
1248 g_free(filer_window->auto_select);
1249 filer_window->had_cursor =
1250 filer_window->collection->cursor_item != -1
1251 || filer_window->had_cursor;
1252 filer_window->auto_select = from_dup;
1254 gtk_window_set_title(GTK_WINDOW(filer_window->window),
1255 filer_window->path);
1256 collection_set_cursor_item(filer_window->collection, -1);
1257 attach(filer_window);
1259 if (GTK_WIDGET_VISIBLE(filer_window->minibuffer))
1260 gtk_idle_add((GtkFunction) minibuffer_show_cb,
1261 filer_window);
1263 else
1265 char *error;
1267 g_free(from_dup);
1268 error = g_strdup_printf("Directory '%s' is not accessible.",
1269 path);
1270 delayed_error("ROX-Filer", error);
1271 g_free(error);
1272 gtk_widget_destroy(filer_window->window);
1276 int selected_item_number(Collection *collection)
1278 int i;
1280 g_return_val_if_fail(collection != NULL, -1);
1281 g_return_val_if_fail(IS_COLLECTION(collection), -1);
1282 g_return_val_if_fail(collection->number_selected == 1, -1);
1284 for (i = 0; i < collection->number_of_items; i++)
1285 if (collection->items[i].selected)
1286 return i;
1288 g_warning("selected_item: number_selected is wrong\n");
1290 return -1;
1293 DirItem *selected_item(Collection *collection)
1295 int item;
1297 item = selected_item_number(collection);
1299 if (item > -1)
1300 return (DirItem *) collection->items[item].data;
1301 return NULL;
1304 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
1305 FilerWindow *window)
1307 /* TODO: We can open lots of these - very irritating! */
1308 return get_choice("Close panel?",
1309 "You have tried to close a panel via the window "
1310 "manager - I usually find that this is accidental... "
1311 "really close?",
1312 2, "Remove", "Cancel") != 0;
1315 /* Make the items as narrow as possible */
1316 static void shrink_width(FilerWindow *filer_window)
1318 int i;
1319 Collection *col = filer_window->collection;
1320 int width = MIN_ITEM_WIDTH;
1321 int this_width;
1322 DisplayStyle style = filer_window->display_style;
1323 int text_height;
1325 text_height = item_font->ascent + item_font->descent;
1327 for (i = 0; i < col->number_of_items; i++)
1329 this_width = calc_width(filer_window,
1330 (DirItem *) col->items[i].data);
1331 if (this_width > width)
1332 width = this_width;
1335 collection_set_item_size(filer_window->collection,
1336 width,
1337 style == FULL_INFO ? MAX_ICON_HEIGHT + 4 :
1338 style == SMALL_ICONS ? MAX(text_height, SMALL_ICON_HEIGHT) + 4
1339 : text_height + MAX_ICON_HEIGHT + 8);
1342 void filer_set_sort_fn(FilerWindow *filer_window,
1343 int (*fn)(const void *a, const void *b))
1345 if (filer_window->sort_fn == fn)
1346 return;
1348 filer_window->sort_fn = fn;
1349 last_sort_fn = fn;
1351 collection_qsort(filer_window->collection,
1352 filer_window->sort_fn);
1354 update_options_label();
1357 void filer_style_set(FilerWindow *filer_window, DisplayStyle style)
1359 if (filer_window->display_style == style)
1360 return;
1362 if (filer_window->panel_type)
1363 style = LARGE_ICONS;
1364 else
1365 last_display_style = style;
1367 filer_window->display_style = style;
1369 switch (style)
1371 case SMALL_ICONS:
1372 collection_set_functions(filer_window->collection,
1373 draw_item_small, test_point_small);
1374 break;
1375 case FULL_INFO:
1376 collection_set_functions(filer_window->collection,
1377 draw_item_full_info, test_point_full_info);
1378 break;
1379 default:
1380 collection_set_functions(filer_window->collection,
1381 draw_item_large, test_point_large);
1382 break;
1385 shrink_width(filer_window);
1387 update_options_label();
1390 FilerWindow *filer_opendir(char *path, PanelType panel_type)
1392 GtkWidget *hbox, *scrollbar, *collection;
1393 FilerWindow *filer_window;
1394 GtkTargetEntry target_table[] =
1396 {"text/uri-list", 0, TARGET_URI_LIST},
1397 {"STRING", 0, TARGET_STRING},
1400 filer_window = g_new(FilerWindow, 1);
1401 filer_window->minibuffer = NULL;
1402 filer_window->path = pathdup(path);
1403 filer_window->had_cursor = FALSE;
1404 filer_window->auto_select = NULL;
1406 filer_window->directory = g_fscache_lookup(dir_cache,
1407 filer_window->path);
1408 if (!filer_window->directory)
1410 char *error;
1412 error = g_strdup_printf("Directory '%s' not found.", path);
1413 delayed_error("ROX-Filer", error);
1414 g_free(error);
1415 g_free(filer_window->path);
1416 g_free(filer_window);
1417 return NULL;
1420 filer_window->show_hidden = last_show_hidden;
1421 filer_window->panel_type = panel_type;
1422 filer_window->temp_item_selected = FALSE;
1423 filer_window->sort_fn = last_sort_fn;
1424 filer_window->flags = (FilerFlags) 0;
1425 filer_window->display_style = UNKNOWN_STYLE;
1427 filer_window->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1428 gtk_window_set_title(GTK_WINDOW(filer_window->window),
1429 filer_window->path);
1431 collection = collection_new(NULL);
1432 gtk_object_set_data(GTK_OBJECT(collection),
1433 "filer_window", filer_window);
1434 filer_window->collection = COLLECTION(collection);
1436 gtk_widget_add_events(filer_window->window, GDK_ENTER_NOTIFY);
1437 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1438 "enter-notify-event",
1439 GTK_SIGNAL_FUNC(pointer_in), filer_window);
1440 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_in_event",
1441 GTK_SIGNAL_FUNC(focus_in), filer_window);
1442 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_out_event",
1443 GTK_SIGNAL_FUNC(focus_out), filer_window);
1444 gtk_signal_connect(GTK_OBJECT(filer_window->window), "destroy",
1445 filer_window_destroyed, filer_window);
1447 gtk_signal_connect(GTK_OBJECT(filer_window->collection), "open_item",
1448 open_item, filer_window);
1449 gtk_signal_connect(GTK_OBJECT(collection), "show_menu",
1450 show_menu, filer_window);
1451 gtk_signal_connect(GTK_OBJECT(collection), "gain_selection",
1452 gain_selection, filer_window);
1453 gtk_signal_connect(GTK_OBJECT(collection), "lose_selection",
1454 lose_selection, filer_window);
1455 gtk_signal_connect(GTK_OBJECT(collection), "drag_selection",
1456 drag_selection, filer_window);
1457 gtk_signal_connect(GTK_OBJECT(collection), "drag_data_get",
1458 drag_data_get, filer_window);
1459 gtk_signal_connect(GTK_OBJECT(collection), "selection_clear_event",
1460 GTK_SIGNAL_FUNC(collection_lose_selection), NULL);
1461 gtk_signal_connect (GTK_OBJECT(collection), "selection_get",
1462 GTK_SIGNAL_FUNC(selection_get), NULL);
1463 gtk_selection_add_targets(collection, GDK_SELECTION_PRIMARY,
1464 target_table,
1465 sizeof(target_table) / sizeof(*target_table));
1467 filer_style_set(filer_window, last_display_style);
1468 drag_set_dest(collection);
1470 if (panel_type)
1472 int swidth, sheight, iwidth, iheight;
1473 GtkWidget *frame, *win = filer_window->window;
1475 gtk_window_set_wmclass(GTK_WINDOW(win), "ROX-Panel",
1476 "ROX-Filer");
1477 collection_set_panel(filer_window->collection, TRUE);
1478 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1479 "delete_event",
1480 GTK_SIGNAL_FUNC(filer_confirm_close),
1481 filer_window);
1483 gdk_window_get_size(GDK_ROOT_PARENT(), &swidth, &sheight);
1484 iwidth = filer_window->collection->item_width;
1485 iheight = filer_window->collection->item_height;
1488 int height = iheight + PANEL_BORDER;
1489 int y = panel_type == PANEL_TOP
1490 ? -PANEL_BORDER
1491 : sheight - height - PANEL_BORDER;
1493 gtk_widget_set_usize(collection, swidth, height);
1494 gtk_widget_set_uposition(win, 0, y);
1497 frame = gtk_frame_new(NULL);
1498 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1499 gtk_container_add(GTK_CONTAINER(frame), collection);
1500 gtk_container_add(GTK_CONTAINER(win), frame);
1502 gtk_widget_show_all(frame);
1503 gtk_widget_realize(win);
1504 if (override_redirect)
1505 gdk_window_set_override_redirect(win->window, TRUE);
1506 make_panel_window(win->window);
1508 else
1510 GtkWidget *vbox;
1511 int col_height = ROW_HEIGHT_LARGE * 3;
1513 gtk_signal_connect(GTK_OBJECT(collection),
1514 "key_press_event",
1515 GTK_SIGNAL_FUNC(key_press_event), filer_window);
1516 gtk_window_set_default_size(GTK_WINDOW(filer_window->window),
1517 filer_window->display_style == LARGE_ICONS ? 400 : 512,
1518 o_toolbar == TOOLBAR_NONE ? col_height:
1519 o_toolbar == TOOLBAR_NORMAL ? col_height + 24 :
1520 col_height + 38);
1522 hbox = gtk_hbox_new(FALSE, 0);
1523 gtk_container_add(GTK_CONTAINER(filer_window->window),
1524 hbox);
1526 vbox = gtk_vbox_new(FALSE, 0);
1527 gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
1529 if (o_toolbar != TOOLBAR_NONE)
1531 GtkWidget *toolbar;
1533 toolbar = create_toolbar(filer_window);
1534 gtk_box_pack_start(GTK_BOX(vbox), toolbar,
1535 FALSE, TRUE, 0);
1536 gtk_widget_show_all(toolbar);
1539 gtk_box_pack_start(GTK_BOX(vbox), collection, TRUE, TRUE, 0);
1541 filer_window->minibuffer = create_minibuffer(filer_window);
1542 gtk_box_pack_start(GTK_BOX(vbox), filer_window->minibuffer,
1543 FALSE, TRUE, 0);
1545 scrollbar = gtk_vscrollbar_new(COLLECTION(collection)->vadj);
1546 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, TRUE, 0);
1547 gtk_accel_group_attach(filer_keys,
1548 GTK_OBJECT(filer_window->window));
1549 gtk_window_set_focus(GTK_WINDOW(filer_window->window),
1550 collection);
1552 gtk_widget_show(hbox);
1553 gtk_widget_show(vbox);
1554 gtk_widget_show(scrollbar);
1555 gtk_widget_show(collection);
1558 number_of_windows++;
1559 gtk_widget_show(filer_window->window);
1560 attach(filer_window);
1562 all_filer_windows = g_list_prepend(all_filer_windows, filer_window);
1564 return filer_window;
1567 static GtkWidget *create_toolbar(FilerWindow *filer_window)
1569 GtkWidget *frame, *box;
1571 if (o_toolbar == TOOLBAR_GNOME)
1573 frame = gtk_handle_box_new();
1574 box = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL,
1575 GTK_TOOLBAR_BOTH);
1576 gtk_container_set_border_width(GTK_CONTAINER(box), 2);
1577 gtk_toolbar_set_space_style(GTK_TOOLBAR(box),
1578 GTK_TOOLBAR_SPACE_LINE);
1579 gtk_toolbar_set_button_relief(GTK_TOOLBAR(box),
1580 GTK_RELIEF_NONE);
1582 else
1584 frame = gtk_frame_new(NULL);
1585 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1587 box = gtk_hbutton_box_new();
1588 gtk_button_box_set_child_size_default(16, 16);
1589 gtk_hbutton_box_set_spacing_default(2);
1590 gtk_button_box_set_layout(GTK_BUTTON_BOX(box),
1591 GTK_BUTTONBOX_START);
1594 gtk_container_add(GTK_CONTAINER(frame), box);
1596 add_button(box, TOOLBAR_UP_ICON,
1597 GTK_SIGNAL_FUNC(toolbar_up_clicked),
1598 filer_window,
1599 "Up", "Change to parent directory");
1600 add_button(box, TOOLBAR_HOME_ICON,
1601 GTK_SIGNAL_FUNC(toolbar_home_clicked),
1602 filer_window,
1603 "Home", "Change to home directory");
1604 add_button(box, TOOLBAR_REFRESH_ICON,
1605 GTK_SIGNAL_FUNC(toolbar_refresh_clicked),
1606 filer_window,
1607 "Rescan", "Rescan directory contents");
1609 return frame;
1612 static void add_button(GtkWidget *box, int pixmap,
1613 GtkSignalFunc cb, FilerWindow *filer_window,
1614 char *label, char *tip)
1616 GtkWidget *button, *icon;
1618 icon = gtk_pixmap_new(default_pixmap[pixmap].pixmap,
1619 default_pixmap[pixmap].mask);
1621 if (o_toolbar == TOOLBAR_GNOME)
1623 gtk_toolbar_append_element(GTK_TOOLBAR(box),
1624 GTK_TOOLBAR_CHILD_BUTTON,
1625 NULL,
1626 label,
1627 tip, NULL,
1628 icon,
1629 cb, filer_window);
1631 else
1633 button = gtk_button_new();
1634 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
1636 gtk_container_add(GTK_CONTAINER(button), icon);
1637 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1638 cb, filer_window);
1640 gtk_tooltips_set_tip(tooltips, button, tip, NULL);
1642 gtk_container_add(GTK_CONTAINER(box), button);
1646 /* Build up some option widgets to go in the options dialog, but don't
1647 * fill them in yet.
1649 static GtkWidget *create_options()
1651 GtkWidget *vbox, *menu, *hbox;
1653 vbox = gtk_vbox_new(FALSE, 0);
1654 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
1656 display_label = gtk_label_new("<>");
1657 gtk_label_set_line_wrap(GTK_LABEL(display_label), TRUE);
1658 gtk_box_pack_start(GTK_BOX(vbox), display_label, FALSE, TRUE, 0);
1660 toggle_new_window_on_1 =
1661 gtk_check_button_new_with_label("New window on button 1 "
1662 "(RISC OS style)");
1663 gtk_box_pack_start(GTK_BOX(vbox), toggle_new_window_on_1,
1664 FALSE, TRUE, 0);
1666 toggle_menu_on_2 =
1667 gtk_check_button_new_with_label("Menu on button 2 "
1668 "(RISC OS style)");
1669 gtk_box_pack_start(GTK_BOX(vbox), toggle_menu_on_2, FALSE, TRUE, 0);
1671 toggle_single_click =
1672 gtk_check_button_new_with_label("Single-click nagivation");
1673 gtk_box_pack_start(GTK_BOX(vbox), toggle_single_click, FALSE, TRUE, 0);
1675 hbox = gtk_hbox_new(FALSE, 4);
1676 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
1678 gtk_box_pack_start(GTK_BOX(hbox),
1679 gtk_label_new("Toolbar type for new windows"),
1680 FALSE, TRUE, 0);
1681 menu_toolbar = gtk_option_menu_new();
1682 menu = gtk_menu_new();
1683 gtk_menu_append(GTK_MENU(menu), gtk_menu_item_new_with_label("None"));
1684 gtk_menu_append(GTK_MENU(menu), gtk_menu_item_new_with_label("Normal"));
1685 gtk_menu_append(GTK_MENU(menu), gtk_menu_item_new_with_label("GNOME"));
1686 gtk_option_menu_set_menu(GTK_OPTION_MENU(menu_toolbar), menu);
1687 gtk_box_pack_start(GTK_BOX(hbox), menu_toolbar, TRUE, TRUE, 0);
1689 return vbox;
1692 static void update_options_label(void)
1694 guchar *str;
1696 str = g_strdup_printf("The last used display style (%s) and sort "
1697 "function (Sort By %s) will be saved if you click on "
1698 "Save.", style_to_name(), sort_fn_to_name());
1699 gtk_label_set_text(GTK_LABEL(display_label), str);
1700 g_free(str);
1703 /* Reflect current state by changing the widgets in the options box */
1704 static void update_options()
1706 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_new_window_on_1),
1707 o_new_window_on_1);
1708 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_menu_on_2),
1709 collection_menu_button == 2 ? 1 : 0);
1710 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_single_click),
1711 o_single_click);
1712 gtk_option_menu_set_history(GTK_OPTION_MENU(menu_toolbar), o_toolbar);
1714 update_options_label();
1717 /* Set current values by reading the states of the widgets in the options box */
1718 static void set_options()
1720 GtkWidget *item, *menu;
1721 GList *list;
1723 o_new_window_on_1 = gtk_toggle_button_get_active(
1724 GTK_TOGGLE_BUTTON(toggle_new_window_on_1));
1726 collection_menu_button = gtk_toggle_button_get_active(
1727 GTK_TOGGLE_BUTTON(toggle_menu_on_2)) ? 2 : 3;
1729 o_single_click = gtk_toggle_button_get_active(
1730 GTK_TOGGLE_BUTTON(toggle_single_click));
1731 collection_single_click = o_single_click ? TRUE : FALSE;
1733 menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(menu_toolbar));
1734 item = gtk_menu_get_active(GTK_MENU(menu));
1735 list = gtk_container_children(GTK_CONTAINER(menu));
1736 o_toolbar = (ToolbarType) g_list_index(list, item);
1737 g_list_free(list);
1741 static guchar *style_to_name(void)
1743 return last_display_style == LARGE_ICONS ? "Large Icons" :
1744 last_display_style == SMALL_ICONS ? "Small Icons" :
1745 "Full Info";
1748 static guchar *sort_fn_to_name(void)
1750 return last_sort_fn == sort_by_name ? "Name" :
1751 last_sort_fn == sort_by_type ? "Type" :
1752 last_sort_fn == sort_by_date ? "Date" :
1753 "Size";
1756 static void save_options()
1758 option_write("filer_new_window_on_1", o_new_window_on_1 ? "1" : "0");
1759 option_write("filer_menu_on_2",
1760 collection_menu_button == 2 ? "1" : "0");
1761 option_write("filer_single_click", o_single_click ? "1" : "0");
1762 option_write("filer_display_style", style_to_name());
1763 option_write("filer_sort_by", sort_fn_to_name());
1764 option_write("filer_toolbar", o_toolbar == TOOLBAR_NONE ? "None" :
1765 o_toolbar == TOOLBAR_NORMAL ? "Normal" :
1766 o_toolbar == TOOLBAR_GNOME ? "GNOME" :
1767 "Unknown");
1770 static char *filer_new_window_on_1(char *data)
1772 o_new_window_on_1 = atoi(data) != 0;
1773 return NULL;
1776 static char *filer_menu_on_2(char *data)
1778 collection_menu_button = atoi(data) != 0 ? 2 : 3;
1779 return NULL;
1782 static char *filer_single_click(char *data)
1784 o_single_click = atoi(data) != 0;
1785 collection_single_click = o_single_click ? TRUE : FALSE;
1786 return NULL;
1789 static char *filer_display_style(char *data)
1791 if (g_strcasecmp(data, "Large Icons") == 0)
1792 last_display_style = LARGE_ICONS;
1793 else if (g_strcasecmp(data, "Small Icons") == 0)
1794 last_display_style = SMALL_ICONS;
1795 else if (g_strcasecmp(data, "Full Info") == 0)
1796 last_display_style = FULL_INFO;
1797 else
1798 return "Unknown display style";
1800 return NULL;
1803 static char *filer_sort_by(char *data)
1805 if (g_strcasecmp(data, "Name") == 0)
1806 last_sort_fn = sort_by_name;
1807 else if (g_strcasecmp(data, "Type") == 0)
1808 last_sort_fn = sort_by_type;
1809 else if (g_strcasecmp(data, "Date") == 0)
1810 last_sort_fn = sort_by_date;
1811 else if (g_strcasecmp(data, "Size") == 0)
1812 last_sort_fn = sort_by_size;
1813 else
1814 return "Unknown sort type";
1816 return NULL;
1819 static char *filer_toolbar(char *data)
1821 if (g_strcasecmp(data, "None") == 0)
1822 o_toolbar = TOOLBAR_NONE;
1823 else if (g_strcasecmp(data, "Normal") == 0)
1824 o_toolbar = TOOLBAR_NORMAL;
1825 else if (g_strcasecmp(data, "GNOME") == 0)
1826 o_toolbar = TOOLBAR_GNOME;
1827 else
1828 return "Unknown toolbar type";
1830 return NULL;
1833 /* Note that filer_window may not exist after this call. */
1834 void update_dir(FilerWindow *filer_window, gboolean warning)
1836 if (may_rescan(filer_window, warning))
1837 dir_update(filer_window->directory, filer_window->path);
1840 void filer_set_hidden(FilerWindow *filer_window, gboolean hidden)
1842 Directory *dir = filer_window->directory;
1844 if (filer_window->show_hidden == hidden)
1845 return;
1847 filer_window->show_hidden = hidden;
1848 last_show_hidden = hidden;
1850 g_fscache_data_ref(dir_cache, dir);
1851 detach(filer_window);
1852 filer_window->directory = dir;
1853 attach(filer_window);
1856 /* Refresh the various caches even if we don't think we need to */
1857 void full_refresh(void)
1859 mount_update(TRUE);
1862 /* This path has been mounted/umounted - update all dirs */
1863 void filer_check_mounted(char *path)
1865 GList *next = all_filer_windows;
1866 int len;
1868 len = strlen(path);
1870 while (next)
1872 FilerWindow *filer_window = (FilerWindow *) next->data;
1874 next = next->next;
1876 if (strncmp(path, filer_window->path, len) == 0)
1878 char s = filer_window->path[len];
1880 if (s == '/' || s == '\0')
1881 update_dir(filer_window, FALSE);
1886 /* Like minibuffer_show(), except that:
1887 * - It returns FALSE (to be used from an idle callback)
1888 * - It checks that the filer window still exists.
1890 static gboolean minibuffer_show_cb(FilerWindow *filer_window)
1892 GList *next = all_filer_windows;
1894 while (next)
1896 FilerWindow *fw = (FilerWindow *) next->data;
1899 if (fw == filer_window)
1901 minibuffer_show(filer_window);
1902 break;
1905 next = next->next;
1908 return FALSE;
1911 /* Highlight (wink or cursor) this item in the filer window. If the item
1912 * isn't already there but we're scanning then highlight it if it
1913 * appears later.
1915 static void set_autoselect(FilerWindow *filer_window, guchar *leaf)
1917 Collection *col = filer_window->collection;
1918 int i;
1920 g_free(filer_window->auto_select);
1921 filer_window->auto_select = NULL;
1923 for (i = 0; i < col->number_of_items; i++)
1925 DirItem *item = (DirItem *) col->items[i].data;
1927 if (strcmp(item->leafname, leaf) == 0)
1929 if (col->cursor_item != -1)
1930 collection_set_cursor_item(col, i);
1931 else
1932 collection_wink_item(col, i);
1933 return;
1937 filer_window->auto_select = g_strdup(leaf);