r240: Tidied up the pixmap code - now uses ImLib functions if possible.
[rox-filer.git] / ROX-Filer / src / filer.c
blob67e15aef13626d8cbf0b874818dcf767d0423cf1
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2000, 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 "config.h"
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <sys/param.h>
31 #include <unistd.h>
32 #include <time.h>
33 #include <ctype.h>
35 #include <gtk/gtk.h>
36 #include <gdk/gdkx.h>
37 #include <gdk/gdkkeysyms.h>
38 #include "collection.h"
40 #include "main.h"
41 #include "support.h"
42 #include "gui_support.h"
43 #include "filer.h"
44 #include "pixmaps.h"
45 #include "menu.h"
46 #include "dnd.h"
47 #include "run.h"
48 #include "mount.h"
49 #include "type.h"
50 #include "options.h"
51 #include "action.h"
52 #include "minibuffer.h"
54 #define ROW_HEIGHT_LARGE 64
55 #define ROW_HEIGHT_SMALL 32
56 #define ROW_HEIGHT_FULL_INFO 44
57 #define SMALL_ICON_HEIGHT 20
58 #define SMALL_ICON_WIDTH 48
59 #define MAX_ICON_HEIGHT 42
60 #define MAX_ICON_WIDTH 48
61 #define PANEL_BORDER 2
62 #define MIN_ITEM_WIDTH 64
64 extern int collection_menu_button;
65 extern gboolean collection_single_click;
67 FilerWindow *window_with_focus = NULL;
68 GList *all_filer_windows = NULL;
70 static DisplayStyle last_display_style = LARGE_ICONS;
71 static gboolean last_show_hidden = FALSE;
72 static int (*last_sort_fn)(const void *a, const void *b) = sort_by_type;
74 static FilerWindow *window_with_selection = NULL;
76 /* Options bits */
77 static guchar *style_to_name(void);
78 static guchar *sort_fn_to_name(void);
79 static void update_options_label(void);
81 static GtkWidget *create_options();
82 static void update_options();
83 static void set_options();
84 static void save_options();
85 static char *filer_single_click(char *data);
86 static char *filer_unique_windows(char *data);
87 static char *filer_menu_on_2(char *data);
88 static char *filer_new_window_on_1(char *data);
89 static char *filer_toolbar(char *data);
90 static char *filer_display_style(char *data);
91 static char *filer_sort_by(char *data);
93 static OptionsSection options =
95 "Filer window options",
96 create_options,
97 update_options,
98 set_options,
99 save_options
102 /* The values correspond to the menu indexes in the option widget */
103 typedef enum {
104 TOOLBAR_NONE = 0,
105 TOOLBAR_NORMAL = 1,
106 TOOLBAR_GNOME = 2,
107 } ToolbarType;
108 static ToolbarType o_toolbar = TOOLBAR_NORMAL;
109 static GtkWidget *menu_toolbar;
111 static GtkWidget *display_label;
113 static gboolean o_single_click = FALSE;
114 static GtkWidget *toggle_single_click;
115 static gboolean o_new_window_on_1 = FALSE; /* Button 1 => New window */
116 static GtkWidget *toggle_new_window_on_1;
117 static GtkWidget *toggle_menu_on_2;
118 static GtkWidget *toggle_unique_filer_windows;
119 gboolean o_unique_filer_windows = FALSE;
121 /* Static prototypes */
122 static void attach(FilerWindow *filer_window);
123 static void detach(FilerWindow *filer_window);
124 static void filer_window_destroyed(GtkWidget *widget,
125 FilerWindow *filer_window);
126 static void show_menu(Collection *collection, GdkEventButton *event,
127 int number_selected, gpointer user_data);
128 static gint focus_in(GtkWidget *widget,
129 GdkEventFocus *event,
130 FilerWindow *filer_window);
131 static gint focus_out(GtkWidget *widget,
132 GdkEventFocus *event,
133 FilerWindow *filer_window);
134 static void add_item(FilerWindow *filer_window, DirItem *item);
135 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window);
136 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window);
137 static void add_button(GtkWidget *box, int pixmap,
138 GtkSignalFunc cb, FilerWindow *filer_window,
139 char *label, char *tip);
140 static GtkWidget *create_toolbar(FilerWindow *filer_window);
141 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
142 FilerWindow *window);
143 static int calc_width(FilerWindow *filer_window, DirItem *item);
144 static void draw_large_icon(GtkWidget *widget,
145 GdkRectangle *area,
146 DirItem *item,
147 gboolean selected);
148 static void draw_string(GtkWidget *widget,
149 GdkFont *font,
150 char *string,
151 int x,
152 int y,
153 int width,
154 gboolean selected);
155 static void draw_item_large(GtkWidget *widget,
156 CollectionItem *item,
157 GdkRectangle *area);
158 static void draw_item_small(GtkWidget *widget,
159 CollectionItem *item,
160 GdkRectangle *area);
161 static void draw_item_full_info(GtkWidget *widget,
162 CollectionItem *colitem,
163 GdkRectangle *area);
164 static gboolean test_point_large(Collection *collection,
165 int point_x, int point_y,
166 CollectionItem *item,
167 int width, int height);
168 static gboolean test_point_small(Collection *collection,
169 int point_x, int point_y,
170 CollectionItem *item,
171 int width, int height);
172 static gboolean test_point_full_info(Collection *collection,
173 int point_x, int point_y,
174 CollectionItem *item,
175 int width, int height);
176 static void update_display(Directory *dir,
177 DirAction action,
178 GPtrArray *items,
179 FilerWindow *filer_window);
180 static void set_scanning_display(FilerWindow *filer_window, gboolean scanning);
181 static void shrink_width(FilerWindow *filer_window);
182 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning);
183 static void open_item(Collection *collection,
184 gpointer item_data, int item_number,
185 gpointer user_data);
186 static gboolean minibuffer_show_cb(FilerWindow *filer_window);
187 static FilerWindow *find_filer_window(char *path, FilerWindow *diff);
188 static void filer_set_title(FilerWindow *filer_window);
189 static gboolean exists(FilerWindow *filer_window);
191 static GdkAtom xa_string;
192 enum
194 TARGET_STRING,
195 TARGET_URI_LIST,
198 static GdkCursor *busy_cursor = NULL;
199 static GtkTooltips *tooltips = NULL;
201 void filer_init()
203 xa_string = gdk_atom_intern("STRING", FALSE);
205 options_sections = g_slist_prepend(options_sections, &options);
206 option_register("filer_new_window_on_1", filer_new_window_on_1);
207 option_register("filer_menu_on_2", filer_menu_on_2);
208 option_register("filer_single_click", filer_single_click);
209 option_register("filer_unique_windows", filer_unique_windows);
210 option_register("filer_toolbar", filer_toolbar);
211 option_register("filer_display_style", filer_display_style);
212 option_register("filer_sort_by", filer_sort_by);
214 busy_cursor = gdk_cursor_new(GDK_WATCH);
216 tooltips = gtk_tooltips_new();
219 static gboolean if_deleted(gpointer item, gpointer removed)
221 int i = ((GPtrArray *) removed)->len;
222 DirItem **r = (DirItem **) ((GPtrArray *) removed)->pdata;
223 char *leafname = ((DirItem *) item)->leafname;
225 while (i--)
227 if (strcmp(leafname, r[i]->leafname) == 0)
228 return TRUE;
231 return FALSE;
234 static void update_item(FilerWindow *filer_window, DirItem *item)
236 int i;
237 char *leafname = item->leafname;
239 if (leafname[0] == '.')
241 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
242 || (leafname[1] == '.' && leafname[2] == '\0'))
243 return;
246 i = collection_find_item(filer_window->collection, item, dir_item_cmp);
248 if (i >= 0)
249 collection_draw_item(filer_window->collection, i, TRUE);
250 else
251 g_warning("Failed to find '%s'\n", item->leafname);
254 static void update_display(Directory *dir,
255 DirAction action,
256 GPtrArray *items,
257 FilerWindow *filer_window)
259 int old_num;
260 int i;
261 int cursor = filer_window->collection->cursor_item;
262 char *as;
263 Collection *collection = filer_window->collection;
265 switch (action)
267 case DIR_ADD:
268 as = filer_window->auto_select;
270 old_num = collection->number_of_items;
271 for (i = 0; i < items->len; i++)
273 DirItem *item = (DirItem *) items->pdata[i];
275 add_item(filer_window, item);
277 if (cursor != -1 || !as)
278 continue;
280 if (strcmp(as, item->leafname) != 0)
281 continue;
283 cursor = collection->number_of_items - 1;
284 if (filer_window->had_cursor)
286 collection_set_cursor_item(collection,
287 cursor);
288 filer_window->mini_cursor_base = cursor;
290 else
291 collection_wink_item(collection,
292 cursor);
295 if (old_num != collection->number_of_items)
296 collection_qsort(filer_window->collection,
297 filer_window->sort_fn);
298 break;
299 case DIR_REMOVE:
300 collection_delete_if(filer_window->collection,
301 if_deleted,
302 items);
303 break;
304 case DIR_START_SCAN:
305 set_scanning_display(filer_window, TRUE);
306 break;
307 case DIR_END_SCAN:
308 if (filer_window->window->window)
309 gdk_window_set_cursor(
310 filer_window->window->window,
311 NULL);
312 shrink_width(filer_window);
313 if (filer_window->had_cursor &&
314 collection->cursor_item == -1)
316 collection_set_cursor_item(collection, 0);
317 filer_window->had_cursor = FALSE;
319 set_scanning_display(filer_window, FALSE);
320 break;
321 case DIR_UPDATE:
322 for (i = 0; i < items->len; i++)
324 DirItem *item = (DirItem *) items->pdata[i];
326 update_item(filer_window, item);
328 collection_qsort(filer_window->collection,
329 filer_window->sort_fn);
330 break;
334 static void attach(FilerWindow *filer_window)
336 gdk_window_set_cursor(filer_window->window->window, busy_cursor);
337 collection_clear(filer_window->collection);
338 filer_window->scanning = TRUE;
339 dir_attach(filer_window->directory, (DirCallback) update_display,
340 filer_window);
341 filer_set_title(filer_window);
344 static void detach(FilerWindow *filer_window)
346 g_return_if_fail(filer_window->directory != NULL);
348 dir_detach(filer_window->directory,
349 (DirCallback) update_display, filer_window);
350 g_fscache_data_unref(dir_cache, filer_window->directory);
351 filer_window->directory = NULL;
354 static void filer_window_destroyed(GtkWidget *widget,
355 FilerWindow *filer_window)
357 all_filer_windows = g_list_remove(all_filer_windows, filer_window);
359 if (window_with_selection == filer_window)
360 window_with_selection = NULL;
361 if (window_with_focus == filer_window)
362 window_with_focus = NULL;
364 if (filer_window->directory)
365 detach(filer_window);
367 g_free(filer_window->auto_select);
368 g_free(filer_window->path);
369 g_free(filer_window);
371 if (--number_of_windows < 1)
372 gtk_main_quit();
375 static int calc_width(FilerWindow *filer_window, DirItem *item)
377 int pix_width = item->image->width;
379 switch (filer_window->display_style)
381 case FULL_INFO:
382 return MAX_ICON_WIDTH + 12 +
383 MAX(item->details_width, item->name_width);
384 case SMALL_ICONS:
385 return SMALL_ICON_WIDTH + 12 + item->name_width;
386 default:
387 return MAX(pix_width, item->name_width) + 4;
391 /* Add a single object to a directory display */
392 static void add_item(FilerWindow *filer_window, DirItem *item)
394 char *leafname = item->leafname;
395 int item_width;
397 if (leafname[0] == '.')
399 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
400 || (leafname[1] == '.' && leafname[2] == '\0'))
401 return;
404 item_width = calc_width(filer_window, item);
405 if (item_width > filer_window->collection->item_width)
406 collection_set_item_size(filer_window->collection,
407 item_width,
408 filer_window->collection->item_height);
409 collection_insert(filer_window->collection, item);
412 /* Is a point inside an item? */
413 static gboolean test_point_large(Collection *collection,
414 int point_x, int point_y,
415 CollectionItem *colitem,
416 int width, int height)
418 DirItem *item = (DirItem *) colitem->data;
419 int text_height = item_font->ascent + item_font->descent;
420 MaskedPixmap *image = item->image;
421 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
422 int image_width = (image->width >> 1) + 2;
423 int text_width = (item->name_width >> 1) + 2;
424 int x_limit;
426 if (point_y < image_y)
427 return FALSE; /* Too high up (don't worry about too low) */
429 if (point_y <= image_y + image->height + 2)
430 x_limit = image_width;
431 else if (point_y > height - text_height - 2)
432 x_limit = text_width;
433 else
434 x_limit = MIN(image_width, text_width);
436 return ABS(point_x - (width >> 1)) < x_limit;
439 static gboolean test_point_full_info(Collection *collection,
440 int point_x, int point_y,
441 CollectionItem *colitem,
442 int width, int height)
444 DirItem *item = (DirItem *) colitem->data;
445 MaskedPixmap *image = item->image;
446 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
447 int low_top = height
448 - fixed_font->descent - 2 - fixed_font->ascent;
450 if (point_x < image->width + 2)
451 return point_x > 2 && point_y > image_y;
453 point_x -= MAX_ICON_WIDTH + 8;
455 if (point_y >= low_top)
456 return point_x < item->details_width;
457 if (point_y >= low_top - item_font->ascent - item_font->descent)
458 return point_x < item->name_width;
459 return FALSE;
462 static gboolean test_point_small(Collection *collection,
463 int point_x, int point_y,
464 CollectionItem *colitem,
465 int width, int height)
467 DirItem *item = (DirItem *) colitem->data;
468 MaskedPixmap *image = item->image;
469 int image_y = MAX(0, SMALL_ICON_HEIGHT - image->height);
470 int low_top = height
471 - fixed_font->descent - 2 - item_font->ascent;
472 int iwidth = MIN(SMALL_ICON_WIDTH, image->width);
474 if (point_x < iwidth + 2)
475 return point_x > 2 && point_y > image_y;
477 point_x -= SMALL_ICON_WIDTH + 4;
479 if (point_y >= low_top)
480 return point_x < item->name_width;
481 return FALSE;
484 static void draw_small_icon(GtkWidget *widget,
485 GdkRectangle *area,
486 DirItem *item,
487 gboolean selected)
489 MaskedPixmap *image = item->image;
490 int width = MIN(image->width, SMALL_ICON_WIDTH);
491 int height = MIN(image->height, SMALL_ICON_HEIGHT);
492 int image_x = area->x + ((area->width - width) >> 1);
493 int image_y;
494 GdkGC *gc = selected ? widget->style->white_gc
495 : widget->style->black_gc;
496 if (!item->image)
497 return;
499 gdk_gc_set_clip_mask(gc, item->image->mask);
501 image_y = MAX(0, SMALL_ICON_HEIGHT - image->height);
502 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
503 gdk_draw_pixmap(widget->window, gc,
504 item->image->pixmap,
505 0, 0, /* Source x,y */
506 image_x, area->y + image_y, /* Dest x,y */
507 width, height);
509 if (selected)
511 gdk_gc_set_function(gc, GDK_INVERT);
512 gdk_draw_rectangle(widget->window,
514 TRUE, image_x, area->y + image_y,
515 width, height);
516 gdk_gc_set_function(gc, GDK_COPY);
519 if (item->flags & ITEM_FLAG_SYMLINK)
521 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
522 gdk_gc_set_clip_mask(gc,
523 default_pixmap[TYPE_SYMLINK]->mask);
524 gdk_draw_pixmap(widget->window, gc,
525 default_pixmap[TYPE_SYMLINK]->pixmap,
526 0, 0, /* Source x,y */
527 image_x, area->y + 8, /* Dest x,y */
528 -1, -1);
530 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
532 int type = item->flags & ITEM_FLAG_MOUNTED
533 ? TYPE_MOUNTED
534 : TYPE_UNMOUNTED;
535 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
536 gdk_gc_set_clip_mask(gc,
537 default_pixmap[type]->mask);
538 gdk_draw_pixmap(widget->window, gc,
539 default_pixmap[type]->pixmap,
540 0, 0, /* Source x,y */
541 image_x, area->y + 8, /* Dest x,y */
542 -1, -1);
545 gdk_gc_set_clip_mask(gc, NULL);
546 gdk_gc_set_clip_origin(gc, 0, 0);
549 static void draw_large_icon(GtkWidget *widget,
550 GdkRectangle *area,
551 DirItem *item,
552 gboolean selected)
554 MaskedPixmap *image = item->image;
555 int width = MIN(image->width, MAX_ICON_WIDTH);
556 int height = MIN(image->height, MAX_ICON_WIDTH);
557 int image_x = area->x + ((area->width - width) >> 1);
558 int image_y;
559 GdkGC *gc = selected ? widget->style->white_gc
560 : widget->style->black_gc;
562 gdk_gc_set_clip_mask(gc, item->image->mask);
564 image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
565 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
566 gdk_draw_pixmap(widget->window, gc,
567 item->image->pixmap,
568 0, 0, /* Source x,y */
569 image_x, area->y + image_y, /* Dest x,y */
570 width, height);
572 if (selected)
574 gdk_gc_set_function(gc, GDK_INVERT);
575 gdk_draw_rectangle(widget->window,
577 TRUE, image_x, area->y + image_y,
578 width, height);
579 gdk_gc_set_function(gc, GDK_COPY);
582 if (item->flags & ITEM_FLAG_SYMLINK)
584 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
585 gdk_gc_set_clip_mask(gc,
586 default_pixmap[TYPE_SYMLINK]->mask);
587 gdk_draw_pixmap(widget->window, gc,
588 default_pixmap[TYPE_SYMLINK]->pixmap,
589 0, 0, /* Source x,y */
590 image_x, area->y + 8, /* Dest x,y */
591 -1, -1);
593 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
595 int type = item->flags & ITEM_FLAG_MOUNTED
596 ? TYPE_MOUNTED
597 : TYPE_UNMOUNTED;
598 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
599 gdk_gc_set_clip_mask(gc,
600 default_pixmap[type]->mask);
601 gdk_draw_pixmap(widget->window, gc,
602 default_pixmap[type]->pixmap,
603 0, 0, /* Source x,y */
604 image_x, area->y + 8, /* Dest x,y */
605 -1, -1);
608 gdk_gc_set_clip_mask(gc, NULL);
609 gdk_gc_set_clip_origin(gc, 0, 0);
612 static void draw_string(GtkWidget *widget,
613 GdkFont *font,
614 char *string,
615 int x,
616 int y,
617 int width,
618 gboolean selected)
620 int text_height = font->ascent + font->descent;
622 if (selected)
623 gtk_paint_flat_box(widget->style, widget->window,
624 GTK_STATE_SELECTED, GTK_SHADOW_NONE,
625 NULL, widget, "text",
626 x, y - font->ascent,
627 width,
628 text_height);
630 gdk_draw_text(widget->window,
631 font,
632 selected ? widget->style->white_gc
633 : widget->style->black_gc,
634 x, y,
635 string, strlen(string));
638 /* Return a string (valid until next call) giving details
639 * of this item.
641 char *details(DirItem *item)
643 mode_t m = item->mode;
644 static guchar *buf = NULL;
646 if (buf)
647 g_free(buf);
649 if (item->lstat_errno)
650 buf = g_strdup_printf("lstat(2) failed: %s",
651 g_strerror(item->lstat_errno));
652 else
653 buf = g_strdup_printf("%s %s %-8.8s %-8.8s %s %s",
654 item->flags & ITEM_FLAG_APPDIR? "App " :
655 S_ISDIR(m) ? "Dir " :
656 S_ISCHR(m) ? "Char" :
657 S_ISBLK(m) ? "Blck" :
658 S_ISLNK(m) ? "Link" :
659 S_ISSOCK(m) ? "Sock" :
660 S_ISFIFO(m) ? "Pipe" : "File",
661 pretty_permissions(m),
662 user_name(item->uid),
663 group_name(item->gid),
664 format_size_aligned(item->size),
665 pretty_time(&item->mtime));
666 return buf;
669 static void draw_item_full_info(GtkWidget *widget,
670 CollectionItem *colitem,
671 GdkRectangle *area)
673 DirItem *item = (DirItem *) colitem->data;
674 MaskedPixmap *image = item->image;
675 int text_x = area->x + MAX_ICON_WIDTH + 8;
676 int low_text_y = area->y + area->height - fixed_font->descent - 2;
677 gboolean selected = colitem->selected;
678 GdkRectangle pic_area;
680 pic_area.x = area->x;
681 pic_area.y = area->y;
682 pic_area.width = image->width + 8;
683 pic_area.height = area->height;
685 draw_large_icon(widget, &pic_area, item, selected);
687 draw_string(widget,
688 item_font,
689 item->leafname,
690 text_x,
691 low_text_y - item_font->descent - fixed_font->ascent,
692 item->name_width,
693 selected);
694 draw_string(widget,
695 fixed_font,
696 details(item),
697 text_x, low_text_y,
698 item->details_width,
699 selected);
701 if (item->lstat_errno)
702 return;
704 /* Underline the effective permissions */
705 gdk_draw_rectangle(widget->window,
706 selected ? widget->style->white_gc
707 : widget->style->black_gc,
708 TRUE,
709 text_x - 1 + fixed_width *
710 (5 + 4 * applicable(item->uid, item->gid)),
711 low_text_y + fixed_font->descent - 1,
712 fixed_width * 3 + 1, 1);
715 static void draw_item_small(GtkWidget *widget,
716 CollectionItem *colitem,
717 GdkRectangle *area)
719 DirItem *item = (DirItem *) colitem->data;
720 int text_x = area->x + SMALL_ICON_WIDTH + 4;
721 int low_text_y = area->y + area->height - item_font->descent - 2;
722 gboolean selected = colitem->selected;
723 GdkRectangle pic_area;
725 pic_area.x = area->x;
726 pic_area.y = area->y;
727 pic_area.width = SMALL_ICON_WIDTH;
728 pic_area.height = SMALL_ICON_HEIGHT;
730 draw_small_icon(widget, &pic_area, item, selected);
732 draw_string(widget,
733 item_font,
734 item->leafname,
735 text_x,
736 low_text_y,
737 item->name_width,
738 selected);
741 static void draw_item_large(GtkWidget *widget,
742 CollectionItem *colitem,
743 GdkRectangle *area)
745 DirItem *item = (DirItem *) colitem->data;
746 int text_x = area->x + ((area->width - item->name_width) >> 1);
747 int text_y = area->y + area->height - item_font->descent - 2;
748 gboolean selected = colitem->selected;
750 draw_large_icon(widget, area, item, selected);
752 draw_string(widget,
753 item_font,
754 item->leafname,
755 text_x, text_y, item->name_width,
756 selected);
759 static void show_menu(Collection *collection, GdkEventButton *event,
760 int item, gpointer user_data)
762 show_filer_menu((FilerWindow *) user_data, event, item);
765 /* Returns TRUE iff the directory still exists. */
766 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning)
768 Directory *dir;
770 g_return_val_if_fail(filer_window != NULL, FALSE);
772 /* We do a fresh lookup (rather than update) because the inode may
773 * have changed.
775 dir = g_fscache_lookup(dir_cache, filer_window->path);
776 if (!dir)
778 if (warning)
779 delayed_error("ROX-Filer", "Directory missing/deleted");
780 gtk_widget_destroy(filer_window->window);
781 return FALSE;
783 if (dir == filer_window->directory)
784 g_fscache_data_unref(dir_cache, dir);
785 else
787 detach(filer_window);
788 filer_window->directory = dir;
789 attach(filer_window);
792 return TRUE;
795 /* Another app has grabbed the selection */
796 static gint collection_lose_selection(GtkWidget *widget,
797 GdkEventSelection *event)
799 if (window_with_selection &&
800 window_with_selection->collection == COLLECTION(widget))
802 FilerWindow *filer_window = window_with_selection;
803 window_with_selection = NULL;
804 collection_clear_selection(filer_window->collection);
807 return TRUE;
810 /* Someone wants us to send them the selection */
811 static void selection_get(GtkWidget *widget,
812 GtkSelectionData *selection_data,
813 guint info,
814 guint time,
815 gpointer data)
817 GString *reply, *header;
818 FilerWindow *filer_window;
819 int i;
820 Collection *collection;
822 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
824 reply = g_string_new(NULL);
825 header = g_string_new(NULL);
827 switch (info)
829 case TARGET_STRING:
830 g_string_sprintf(header, " %s",
831 make_path(filer_window->path, "")->str);
832 break;
833 case TARGET_URI_LIST:
834 g_string_sprintf(header, " file://%s%s",
835 our_host_name(),
836 make_path(filer_window->path, "")->str);
837 break;
840 collection = filer_window->collection;
841 for (i = 0; i < collection->number_of_items; i++)
843 if (collection->items[i].selected)
845 DirItem *item =
846 (DirItem *) collection->items[i].data;
848 g_string_append(reply, header->str);
849 g_string_append(reply, item->leafname);
852 /* This works, but I don't think I like it... */
853 /* g_string_append_c(reply, ' '); */
855 gtk_selection_data_set(selection_data, xa_string,
856 8, reply->str + 1, reply->len - 1);
857 g_string_free(reply, TRUE);
858 g_string_free(header, TRUE);
861 /* No items are now selected. This might be because another app claimed
862 * the selection or because the user unselected all the items.
864 static void lose_selection(Collection *collection,
865 guint time,
866 gpointer user_data)
868 FilerWindow *filer_window = (FilerWindow *) user_data;
870 if (window_with_selection == filer_window)
872 window_with_selection = NULL;
873 gtk_selection_owner_set(NULL,
874 GDK_SELECTION_PRIMARY,
875 time);
879 static void gain_selection(Collection *collection,
880 guint time,
881 gpointer user_data)
883 FilerWindow *filer_window = (FilerWindow *) user_data;
885 if (gtk_selection_owner_set(GTK_WIDGET(collection),
886 GDK_SELECTION_PRIMARY,
887 time))
889 window_with_selection = filer_window;
891 else
892 collection_clear_selection(filer_window->collection);
895 int sort_by_name(const void *item1, const void *item2)
897 return strcmp((*((DirItem **)item1))->leafname,
898 (*((DirItem **)item2))->leafname);
901 int sort_by_type(const void *item1, const void *item2)
903 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
904 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
905 MIME_type *m1, *m2;
907 int diff = i1->base_type - i2->base_type;
909 if (!diff)
910 diff = (i1->flags & ITEM_FLAG_APPDIR)
911 - (i2->flags & ITEM_FLAG_APPDIR);
912 if (diff)
913 return diff > 0 ? 1 : -1;
915 m1 = i1->mime_type;
916 m2 = i2->mime_type;
918 if (m1 && m2)
920 diff = strcmp(m1->media_type, m2->media_type);
921 if (!diff)
922 diff = strcmp(m1->subtype, m2->subtype);
924 else if (m1 || m2)
925 diff = m1 ? 1 : -1;
926 else
927 diff = 0;
929 if (diff)
930 return diff > 0 ? 1 : -1;
932 return sort_by_name(item1, item2);
935 int sort_by_date(const void *item1, const void *item2)
937 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
938 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
940 return i1->mtime > i2->mtime ? -1 :
941 i1->mtime < i2->mtime ? 1 :
942 sort_by_name(item1, item2);
945 int sort_by_size(const void *item1, const void *item2)
947 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
948 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
950 return i1->size > i2->size ? -1 :
951 i1->size < i2->size ? 1 :
952 sort_by_name(item1, item2);
955 static void open_item(Collection *collection,
956 gpointer item_data, int item_number,
957 gpointer user_data)
959 FilerWindow *filer_window = (FilerWindow *) user_data;
960 GdkEvent *event;
961 GdkEventButton *bevent;
962 GdkEventKey *kevent;
963 OpenFlags flags = 0;
965 event = (GdkEvent *) gtk_get_current_event();
967 bevent = (GdkEventButton *) event;
968 kevent = (GdkEventKey *) event;
970 switch (event->type)
972 case GDK_2BUTTON_PRESS:
973 case GDK_BUTTON_PRESS:
974 case GDK_BUTTON_RELEASE:
975 if (bevent->state & GDK_SHIFT_MASK)
976 flags |= OPEN_SHIFT;
978 if (o_new_window_on_1 ^ (bevent->button == 1))
979 flags |= OPEN_SAME_WINDOW;
981 if (bevent->button != 1)
982 flags |= OPEN_CLOSE_WINDOW;
984 if (o_single_click == FALSE &&
985 (bevent->state & GDK_CONTROL_MASK) != 0)
986 flags ^= OPEN_SAME_WINDOW | OPEN_CLOSE_WINDOW;
987 break;
988 case GDK_KEY_PRESS:
989 flags |= OPEN_SAME_WINDOW;
990 if (kevent->state & GDK_SHIFT_MASK)
991 flags |= OPEN_SHIFT;
992 break;
993 default:
994 break;
997 filer_openitem(filer_window, item_number, flags);
1000 /* Return the full path to the directory containing object 'path'.
1001 * Relative paths are resolved from the filerwindow's path.
1003 static void follow_symlink(FilerWindow *filer_window, char *path,
1004 gboolean same_window)
1006 char *real, *slash;
1007 char *new_dir;
1009 if (path[0] != '/')
1010 path = make_path(filer_window->path, path)->str;
1012 real = pathdup(path);
1013 slash = strrchr(real, '/');
1014 if (!slash)
1016 g_free(real);
1017 delayed_error("ROX-Filer",
1018 "Broken symlink (or you don't have permission "
1019 "to follow it).");
1020 return;
1023 *slash = '\0';
1025 if (*real)
1026 new_dir = real;
1027 else
1028 new_dir = "/";
1030 if (filer_window->panel_type || !same_window)
1032 FilerWindow *new;
1034 new = filer_opendir(new_dir, PANEL_NO);
1035 filer_set_autoselect(new, slash + 1);
1037 else
1038 filer_change_to(filer_window, new_dir, slash + 1);
1040 g_free(real);
1043 /* Open the item (or add it to the shell command minibuffer) */
1044 void filer_openitem(FilerWindow *filer_window, int item_number, OpenFlags flags)
1046 gboolean shift = (flags & OPEN_SHIFT) != 0;
1047 gboolean close_mini = flags & OPEN_FROM_MINI;
1048 gboolean same_window = (flags & OPEN_SAME_WINDOW) != 0
1049 && !filer_window->panel_type;
1050 gboolean close_window = (flags & OPEN_CLOSE_WINDOW) != 0
1051 && !filer_window->panel_type;
1052 GtkWidget *widget;
1053 char *full_path;
1054 DirItem *item = (DirItem *)
1055 filer_window->collection->items[item_number].data;
1056 gboolean wink = TRUE, destroy = FALSE;
1058 widget = filer_window->window;
1059 if (filer_window->mini_type == MINI_SHELL)
1061 minibuffer_add(filer_window, item->leafname);
1062 return;
1065 full_path = make_path(filer_window->path,
1066 item->leafname)->str;
1068 if (item->flags & ITEM_FLAG_SYMLINK && shift)
1070 char path[MAXPATHLEN + 1];
1071 int got;
1073 got = readlink(make_path(filer_window->path,
1074 item->leafname)->str,
1075 path, MAXPATHLEN);
1076 if (got < 0)
1077 delayed_error("ROX-Filer", g_strerror(errno));
1078 else
1080 g_return_if_fail(got <= MAXPATHLEN);
1081 path[got] = '\0';
1083 follow_symlink(filer_window, path,
1084 flags & OPEN_SAME_WINDOW);
1086 return;
1089 switch (item->base_type)
1091 case TYPE_DIRECTORY:
1092 if (item->flags & ITEM_FLAG_APPDIR && !shift)
1094 run_app(make_path(filer_window->path,
1095 item->leafname)->str);
1096 if (close_window)
1097 destroy = TRUE;
1098 break;
1101 if (item->flags & ITEM_FLAG_MOUNT_POINT && shift)
1103 action_mount(filer_window, item);
1104 if (item->flags & ITEM_FLAG_MOUNTED)
1105 break;
1108 if (same_window)
1110 wink = FALSE;
1111 filer_change_to(filer_window, full_path, NULL);
1112 close_mini = FALSE;
1114 else
1115 filer_opendir(full_path, PANEL_NO);
1116 break;
1117 case TYPE_FILE:
1118 if ((item->flags & ITEM_FLAG_EXEC_FILE) && !shift)
1120 char *argv[] = {NULL, NULL};
1122 argv[0] = full_path;
1124 if (spawn_full(argv, filer_window->path))
1126 if (close_window)
1127 destroy = TRUE;
1129 else
1130 report_error("ROX-Filer",
1131 "Failed to fork() child");
1133 else
1135 GString *message;
1136 MIME_type *type = shift ? &text_plain
1137 : item->mime_type;
1139 g_return_if_fail(type != NULL);
1141 if (type_open(full_path, type))
1143 if (close_window)
1144 destroy = TRUE;
1146 else
1148 message = g_string_new(NULL);
1149 g_string_sprintf(message, "No open "
1150 "action specified for files of "
1151 "this type (%s/%s)",
1152 type->media_type,
1153 type->subtype);
1154 report_error("ROX-Filer", message->str);
1155 g_string_free(message, TRUE);
1158 break;
1159 default:
1160 report_error("open_item",
1161 "I don't know how to open that");
1162 break;
1165 if (destroy)
1166 gtk_widget_destroy(filer_window->window);
1167 else
1169 if (wink)
1170 collection_wink_item(filer_window->collection,
1171 item_number);
1172 if (close_mini)
1173 minibuffer_hide(filer_window);
1177 static gint pointer_in(GtkWidget *widget,
1178 GdkEventCrossing *event,
1179 FilerWindow *filer_window)
1181 may_rescan(filer_window, TRUE);
1182 return FALSE;
1185 static gint focus_in(GtkWidget *widget,
1186 GdkEventFocus *event,
1187 FilerWindow *filer_window)
1189 window_with_focus = filer_window;
1191 return FALSE;
1194 static gint focus_out(GtkWidget *widget,
1195 GdkEventFocus *event,
1196 FilerWindow *filer_window)
1198 /* TODO: Shade the cursor */
1200 return FALSE;
1203 /* Handle keys that can't be bound with the menu */
1204 static gint key_press_event(GtkWidget *widget,
1205 GdkEventKey *event,
1206 FilerWindow *filer_window)
1208 switch (event->keyval)
1210 case GDK_BackSpace:
1211 change_to_parent(filer_window);
1212 break;
1213 default:
1214 return FALSE;
1217 return TRUE;
1220 static void toolbar_refresh_clicked(GtkWidget *widget,
1221 FilerWindow *filer_window)
1223 full_refresh();
1224 update_dir(filer_window, TRUE);
1227 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window)
1229 filer_change_to(filer_window, home_dir, NULL);
1232 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window)
1234 change_to_parent(filer_window);
1237 void change_to_parent(FilerWindow *filer_window)
1239 char *copy;
1240 char *slash;
1242 if (filer_window->path[0] == '/' && filer_window->path[1] == '\0')
1243 return; /* Already in the root */
1245 copy = g_strdup(filer_window->path);
1246 slash = strrchr(copy, '/');
1248 if (slash)
1250 *slash = '\0';
1251 filer_change_to(filer_window,
1252 *copy ? copy : "/",
1253 slash + 1);
1255 else
1256 g_warning("No / in directory path!\n");
1258 g_free(copy);
1262 /* Make filer_window display path. When finished, highlight item 'from', or
1263 * the first item if from is NULL. If there is currently no cursor then
1264 * simply wink 'from' (if not NULL).
1266 void filer_change_to(FilerWindow *filer_window, char *path, char *from)
1268 char *from_dup;
1269 char *real_path = pathdup(path);
1271 if (o_unique_filer_windows)
1273 FilerWindow *fw;
1275 fw = find_filer_window(real_path, filer_window);
1276 if (fw)
1277 gtk_widget_destroy(fw->window);
1280 from_dup = from && *from ? g_strdup(from) : NULL;
1282 detach(filer_window);
1283 g_free(filer_window->path);
1284 filer_window->path = real_path;
1286 filer_window->directory = g_fscache_lookup(dir_cache,
1287 filer_window->path);
1288 if (filer_window->directory)
1290 g_free(filer_window->auto_select);
1291 filer_window->had_cursor =
1292 filer_window->collection->cursor_item != -1
1293 || filer_window->had_cursor;
1294 filer_window->auto_select = from_dup;
1296 filer_set_title(filer_window);
1297 collection_set_cursor_item(filer_window->collection, -1);
1298 attach(filer_window);
1300 if (filer_window->mini_type == MINI_PATH)
1301 gtk_idle_add((GtkFunction) minibuffer_show_cb,
1302 filer_window);
1304 else
1306 char *error;
1308 g_free(from_dup);
1309 error = g_strdup_printf("Directory '%s' is not accessible.",
1310 path);
1311 delayed_error("ROX-Filer", error);
1312 g_free(error);
1313 gtk_widget_destroy(filer_window->window);
1317 int selected_item_number(Collection *collection)
1319 int i;
1321 g_return_val_if_fail(collection != NULL, -1);
1322 g_return_val_if_fail(IS_COLLECTION(collection), -1);
1323 g_return_val_if_fail(collection->number_selected == 1, -1);
1325 for (i = 0; i < collection->number_of_items; i++)
1326 if (collection->items[i].selected)
1327 return i;
1329 g_warning("selected_item: number_selected is wrong\n");
1331 return -1;
1334 DirItem *selected_item(Collection *collection)
1336 int item;
1338 item = selected_item_number(collection);
1340 if (item > -1)
1341 return (DirItem *) collection->items[item].data;
1342 return NULL;
1345 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
1346 FilerWindow *window)
1348 /* TODO: We can open lots of these - very irritating! */
1349 return get_choice("Close panel?",
1350 "You have tried to close a panel via the window "
1351 "manager - I usually find that this is accidental... "
1352 "really close?",
1353 2, "Remove", "Cancel") != 0;
1356 /* Make the items as narrow as possible */
1357 static void shrink_width(FilerWindow *filer_window)
1359 int i;
1360 Collection *col = filer_window->collection;
1361 int width = MIN_ITEM_WIDTH;
1362 int this_width;
1363 DisplayStyle style = filer_window->display_style;
1364 int text_height;
1366 text_height = item_font->ascent + item_font->descent;
1368 for (i = 0; i < col->number_of_items; i++)
1370 this_width = calc_width(filer_window,
1371 (DirItem *) col->items[i].data);
1372 if (this_width > width)
1373 width = this_width;
1376 collection_set_item_size(filer_window->collection,
1377 width,
1378 style == FULL_INFO ? MAX_ICON_HEIGHT + 4 :
1379 style == SMALL_ICONS ? MAX(text_height, SMALL_ICON_HEIGHT) + 4
1380 : text_height + MAX_ICON_HEIGHT + 8);
1383 void filer_set_sort_fn(FilerWindow *filer_window,
1384 int (*fn)(const void *a, const void *b))
1386 if (filer_window->sort_fn == fn)
1387 return;
1389 filer_window->sort_fn = fn;
1390 last_sort_fn = fn;
1392 collection_qsort(filer_window->collection,
1393 filer_window->sort_fn);
1395 update_options_label();
1398 void filer_style_set(FilerWindow *filer_window, DisplayStyle style)
1400 if (filer_window->display_style == style)
1401 return;
1403 if (filer_window->panel_type)
1404 style = LARGE_ICONS;
1405 else
1406 last_display_style = style;
1408 filer_window->display_style = style;
1410 switch (style)
1412 case SMALL_ICONS:
1413 collection_set_functions(filer_window->collection,
1414 draw_item_small, test_point_small);
1415 break;
1416 case FULL_INFO:
1417 collection_set_functions(filer_window->collection,
1418 draw_item_full_info, test_point_full_info);
1419 break;
1420 default:
1421 collection_set_functions(filer_window->collection,
1422 draw_item_large, test_point_large);
1423 break;
1426 shrink_width(filer_window);
1428 update_options_label();
1431 FilerWindow *filer_opendir(char *path, PanelType panel_type)
1433 GtkWidget *hbox, *scrollbar, *collection;
1434 FilerWindow *filer_window;
1435 GtkTargetEntry target_table[] =
1437 {"text/uri-list", 0, TARGET_URI_LIST},
1438 {"STRING", 0, TARGET_STRING},
1440 char *real_path;
1442 real_path = pathdup(path);
1444 if (o_unique_filer_windows && panel_type == PANEL_NO)
1446 FilerWindow *fw;
1448 fw = find_filer_window(real_path, NULL);
1450 if (fw)
1452 /* TODO: this should bring the window to the front
1453 * at the same coordinates.
1455 gtk_widget_hide(fw->window);
1456 gtk_widget_show(fw->window);
1457 g_free(real_path);
1458 return fw;
1462 filer_window = g_new(FilerWindow, 1);
1463 filer_window->minibuffer = NULL;
1464 filer_window->path = real_path;
1465 filer_window->scanning = FALSE;
1466 filer_window->had_cursor = FALSE;
1467 filer_window->auto_select = NULL;
1468 filer_window->mini_type = MINI_NONE;
1470 filer_window->directory = g_fscache_lookup(dir_cache,
1471 filer_window->path);
1472 if (!filer_window->directory)
1474 char *error;
1476 error = g_strdup_printf("Directory '%s' not found.", path);
1477 delayed_error("ROX-Filer", error);
1478 g_free(error);
1479 g_free(filer_window->path);
1480 g_free(filer_window);
1481 return NULL;
1484 filer_window->show_hidden = last_show_hidden;
1485 filer_window->panel_type = panel_type;
1486 filer_window->temp_item_selected = FALSE;
1487 filer_window->sort_fn = last_sort_fn;
1488 filer_window->flags = (FilerFlags) 0;
1489 filer_window->display_style = UNKNOWN_STYLE;
1491 filer_window->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1492 filer_set_title(filer_window);
1494 collection = collection_new(NULL);
1495 gtk_object_set_data(GTK_OBJECT(collection),
1496 "filer_window", filer_window);
1497 filer_window->collection = COLLECTION(collection);
1499 gtk_widget_add_events(filer_window->window, GDK_ENTER_NOTIFY);
1500 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1501 "enter-notify-event",
1502 GTK_SIGNAL_FUNC(pointer_in), filer_window);
1503 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_in_event",
1504 GTK_SIGNAL_FUNC(focus_in), filer_window);
1505 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_out_event",
1506 GTK_SIGNAL_FUNC(focus_out), filer_window);
1507 gtk_signal_connect(GTK_OBJECT(filer_window->window), "destroy",
1508 filer_window_destroyed, filer_window);
1510 gtk_signal_connect(GTK_OBJECT(filer_window->collection), "open_item",
1511 open_item, filer_window);
1512 gtk_signal_connect(GTK_OBJECT(collection), "show_menu",
1513 show_menu, filer_window);
1514 gtk_signal_connect(GTK_OBJECT(collection), "gain_selection",
1515 gain_selection, filer_window);
1516 gtk_signal_connect(GTK_OBJECT(collection), "lose_selection",
1517 lose_selection, filer_window);
1518 gtk_signal_connect(GTK_OBJECT(collection), "drag_selection",
1519 drag_selection, filer_window);
1520 gtk_signal_connect(GTK_OBJECT(collection), "drag_data_get",
1521 drag_data_get, filer_window);
1522 gtk_signal_connect(GTK_OBJECT(collection), "selection_clear_event",
1523 GTK_SIGNAL_FUNC(collection_lose_selection), NULL);
1524 gtk_signal_connect (GTK_OBJECT(collection), "selection_get",
1525 GTK_SIGNAL_FUNC(selection_get), NULL);
1526 gtk_selection_add_targets(collection, GDK_SELECTION_PRIMARY,
1527 target_table,
1528 sizeof(target_table) / sizeof(*target_table));
1530 filer_style_set(filer_window, last_display_style);
1531 drag_set_dest(filer_window);
1533 if (panel_type)
1535 int swidth, sheight, iwidth, iheight;
1536 GtkWidget *frame, *win = filer_window->window;
1538 gtk_window_set_wmclass(GTK_WINDOW(win), "ROX-Panel",
1539 "ROX-Filer");
1540 collection_set_panel(filer_window->collection, TRUE);
1541 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1542 "delete_event",
1543 GTK_SIGNAL_FUNC(filer_confirm_close),
1544 filer_window);
1546 gdk_window_get_size(GDK_ROOT_PARENT(), &swidth, &sheight);
1547 iwidth = filer_window->collection->item_width;
1548 iheight = filer_window->collection->item_height;
1551 int height = iheight + PANEL_BORDER;
1552 int y = panel_type == PANEL_TOP
1553 ? -PANEL_BORDER
1554 : sheight - height - PANEL_BORDER;
1556 gtk_widget_set_usize(collection, swidth, height);
1557 gtk_widget_set_uposition(win, 0, y);
1560 frame = gtk_frame_new(NULL);
1561 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1562 gtk_container_add(GTK_CONTAINER(frame), collection);
1563 gtk_container_add(GTK_CONTAINER(win), frame);
1565 gtk_widget_show_all(frame);
1566 gtk_widget_realize(win);
1567 if (override_redirect)
1568 gdk_window_set_override_redirect(win->window, TRUE);
1569 make_panel_window(win->window);
1571 else
1573 GtkWidget *vbox;
1574 int col_height = ROW_HEIGHT_LARGE * 3;
1576 gtk_signal_connect(GTK_OBJECT(collection),
1577 "key_press_event",
1578 GTK_SIGNAL_FUNC(key_press_event), filer_window);
1579 gtk_window_set_default_size(GTK_WINDOW(filer_window->window),
1580 filer_window->display_style == LARGE_ICONS ? 400 : 512,
1581 o_toolbar == TOOLBAR_NONE ? col_height:
1582 o_toolbar == TOOLBAR_NORMAL ? col_height + 24 :
1583 col_height + 38);
1585 hbox = gtk_hbox_new(FALSE, 0);
1586 gtk_container_add(GTK_CONTAINER(filer_window->window),
1587 hbox);
1589 vbox = gtk_vbox_new(FALSE, 0);
1590 gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
1592 if (o_toolbar != TOOLBAR_NONE)
1594 GtkWidget *toolbar;
1596 toolbar = create_toolbar(filer_window);
1597 gtk_box_pack_start(GTK_BOX(vbox), toolbar,
1598 FALSE, TRUE, 0);
1599 gtk_widget_show_all(toolbar);
1602 gtk_box_pack_start(GTK_BOX(vbox), collection, TRUE, TRUE, 0);
1604 filer_window->minibuffer = create_minibuffer(filer_window);
1605 gtk_box_pack_start(GTK_BOX(vbox), filer_window->minibuffer,
1606 FALSE, TRUE, 0);
1608 scrollbar = gtk_vscrollbar_new(COLLECTION(collection)->vadj);
1609 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, TRUE, 0);
1610 gtk_accel_group_attach(filer_keys,
1611 GTK_OBJECT(filer_window->window));
1612 gtk_window_set_focus(GTK_WINDOW(filer_window->window),
1613 collection);
1615 gtk_widget_show(hbox);
1616 gtk_widget_show(vbox);
1617 gtk_widget_show(scrollbar);
1618 gtk_widget_show(collection);
1621 number_of_windows++;
1622 gtk_widget_show(filer_window->window);
1623 attach(filer_window);
1625 all_filer_windows = g_list_prepend(all_filer_windows, filer_window);
1627 return filer_window;
1630 static gint clear_scanning_display(FilerWindow *filer_window)
1632 if (exists(filer_window))
1633 filer_set_title(filer_window);
1634 return FALSE;
1637 static void set_scanning_display(FilerWindow *filer_window, gboolean scanning)
1639 if (scanning == filer_window->scanning)
1640 return;
1641 filer_window->scanning = scanning;
1643 if (scanning)
1644 filer_set_title(filer_window);
1645 else
1646 gtk_timeout_add(300, (GtkFunction) clear_scanning_display,
1647 filer_window);
1650 static GtkWidget *create_toolbar(FilerWindow *filer_window)
1652 GtkWidget *frame, *box;
1654 if (o_toolbar == TOOLBAR_GNOME)
1656 frame = gtk_handle_box_new();
1657 box = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL,
1658 GTK_TOOLBAR_BOTH);
1659 gtk_container_set_border_width(GTK_CONTAINER(box), 2);
1660 gtk_toolbar_set_space_style(GTK_TOOLBAR(box),
1661 GTK_TOOLBAR_SPACE_LINE);
1662 gtk_toolbar_set_button_relief(GTK_TOOLBAR(box),
1663 GTK_RELIEF_NONE);
1665 else
1667 frame = gtk_frame_new(NULL);
1668 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1670 box = gtk_hbutton_box_new();
1671 gtk_button_box_set_child_size_default(16, 16);
1672 gtk_hbutton_box_set_spacing_default(2);
1673 gtk_button_box_set_layout(GTK_BUTTON_BOX(box),
1674 GTK_BUTTONBOX_START);
1677 gtk_container_add(GTK_CONTAINER(frame), box);
1679 add_button(box, TOOLBAR_UP_ICON,
1680 GTK_SIGNAL_FUNC(toolbar_up_clicked),
1681 filer_window,
1682 "Up", "Change to parent directory");
1683 add_button(box, TOOLBAR_HOME_ICON,
1684 GTK_SIGNAL_FUNC(toolbar_home_clicked),
1685 filer_window,
1686 "Home", "Change to home directory");
1687 add_button(box, TOOLBAR_REFRESH_ICON,
1688 GTK_SIGNAL_FUNC(toolbar_refresh_clicked),
1689 filer_window,
1690 "Rescan", "Rescan directory contents");
1692 return frame;
1695 static void add_button(GtkWidget *box, int pixmap,
1696 GtkSignalFunc cb, FilerWindow *filer_window,
1697 char *label, char *tip)
1699 GtkWidget *button, *icon;
1701 icon = gtk_pixmap_new(default_pixmap[pixmap]->pixmap,
1702 default_pixmap[pixmap]->mask);
1704 if (o_toolbar == TOOLBAR_GNOME)
1706 gtk_toolbar_append_element(GTK_TOOLBAR(box),
1707 GTK_TOOLBAR_CHILD_BUTTON,
1708 NULL,
1709 label,
1710 tip, NULL,
1711 icon,
1712 cb, filer_window);
1714 else
1716 button = gtk_button_new();
1717 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
1719 gtk_container_add(GTK_CONTAINER(button), icon);
1720 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1721 cb, filer_window);
1723 gtk_tooltips_set_tip(tooltips, button, tip, NULL);
1725 gtk_container_add(GTK_CONTAINER(box), button);
1729 /* Build up some option widgets to go in the options dialog, but don't
1730 * fill them in yet.
1732 static GtkWidget *create_options()
1734 GtkWidget *vbox, *menu, *hbox;
1736 vbox = gtk_vbox_new(FALSE, 0);
1737 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
1739 display_label = gtk_label_new("<>");
1740 gtk_label_set_line_wrap(GTK_LABEL(display_label), TRUE);
1741 gtk_box_pack_start(GTK_BOX(vbox), display_label, FALSE, TRUE, 0);
1743 toggle_new_window_on_1 =
1744 gtk_check_button_new_with_label("New window on button 1 "
1745 "(RISC OS style)");
1746 gtk_box_pack_start(GTK_BOX(vbox), toggle_new_window_on_1,
1747 FALSE, TRUE, 0);
1749 toggle_menu_on_2 =
1750 gtk_check_button_new_with_label("Menu on button 2 "
1751 "(RISC OS style)");
1752 gtk_box_pack_start(GTK_BOX(vbox), toggle_menu_on_2, FALSE, TRUE, 0);
1754 toggle_single_click =
1755 gtk_check_button_new_with_label("Single-click nagivation");
1756 gtk_box_pack_start(GTK_BOX(vbox), toggle_single_click, FALSE, TRUE, 0);
1758 toggle_unique_filer_windows =
1759 gtk_check_button_new_with_label("Unique windows");
1760 gtk_box_pack_start(GTK_BOX(vbox), toggle_unique_filer_windows, FALSE, TRUE, 0);
1762 hbox = gtk_hbox_new(FALSE, 4);
1763 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
1765 gtk_box_pack_start(GTK_BOX(hbox),
1766 gtk_label_new("Toolbar type for new windows"),
1767 FALSE, TRUE, 0);
1768 menu_toolbar = gtk_option_menu_new();
1769 menu = gtk_menu_new();
1770 gtk_menu_append(GTK_MENU(menu), gtk_menu_item_new_with_label("None"));
1771 gtk_menu_append(GTK_MENU(menu), gtk_menu_item_new_with_label("Normal"));
1772 gtk_menu_append(GTK_MENU(menu), gtk_menu_item_new_with_label("GNOME"));
1773 gtk_option_menu_set_menu(GTK_OPTION_MENU(menu_toolbar), menu);
1774 gtk_box_pack_start(GTK_BOX(hbox), menu_toolbar, TRUE, TRUE, 0);
1776 return vbox;
1779 static void update_options_label(void)
1781 guchar *str;
1783 str = g_strdup_printf("The last used display style (%s) and sort "
1784 "function (Sort By %s) will be saved if you click on "
1785 "Save.", style_to_name(), sort_fn_to_name());
1786 gtk_label_set_text(GTK_LABEL(display_label), str);
1787 g_free(str);
1790 /* Reflect current state by changing the widgets in the options box */
1791 static void update_options()
1793 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_new_window_on_1),
1794 o_new_window_on_1);
1795 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_menu_on_2),
1796 collection_menu_button == 2 ? 1 : 0);
1797 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_single_click),
1798 o_single_click);
1799 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_unique_filer_windows),
1800 o_unique_filer_windows);
1801 gtk_option_menu_set_history(GTK_OPTION_MENU(menu_toolbar), o_toolbar);
1803 update_options_label();
1806 /* Set current values by reading the states of the widgets in the options box */
1807 static void set_options()
1809 GtkWidget *item, *menu;
1810 GList *list;
1812 o_new_window_on_1 = gtk_toggle_button_get_active(
1813 GTK_TOGGLE_BUTTON(toggle_new_window_on_1));
1815 collection_menu_button = gtk_toggle_button_get_active(
1816 GTK_TOGGLE_BUTTON(toggle_menu_on_2)) ? 2 : 3;
1818 o_single_click = gtk_toggle_button_get_active(
1819 GTK_TOGGLE_BUTTON(toggle_single_click));
1821 o_unique_filer_windows = gtk_toggle_button_get_active(
1822 GTK_TOGGLE_BUTTON(toggle_unique_filer_windows));
1824 collection_single_click = o_single_click ? TRUE : FALSE;
1826 menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(menu_toolbar));
1827 item = gtk_menu_get_active(GTK_MENU(menu));
1828 list = gtk_container_children(GTK_CONTAINER(menu));
1829 o_toolbar = (ToolbarType) g_list_index(list, item);
1830 g_list_free(list);
1834 static guchar *style_to_name(void)
1836 return last_display_style == LARGE_ICONS ? "Large Icons" :
1837 last_display_style == SMALL_ICONS ? "Small Icons" :
1838 "Full Info";
1841 static guchar *sort_fn_to_name(void)
1843 return last_sort_fn == sort_by_name ? "Name" :
1844 last_sort_fn == sort_by_type ? "Type" :
1845 last_sort_fn == sort_by_date ? "Date" :
1846 "Size";
1849 static void save_options()
1851 option_write("filer_new_window_on_1", o_new_window_on_1 ? "1" : "0");
1852 option_write("filer_menu_on_2",
1853 collection_menu_button == 2 ? "1" : "0");
1854 option_write("filer_single_click", o_single_click ? "1" : "0");
1855 option_write("filer_unique_windows", o_unique_filer_windows ? "1" : "0");
1856 option_write("filer_display_style", style_to_name());
1857 option_write("filer_sort_by", sort_fn_to_name());
1858 option_write("filer_toolbar", o_toolbar == TOOLBAR_NONE ? "None" :
1859 o_toolbar == TOOLBAR_NORMAL ? "Normal" :
1860 o_toolbar == TOOLBAR_GNOME ? "GNOME" :
1861 "Unknown");
1864 static char *filer_new_window_on_1(char *data)
1866 o_new_window_on_1 = atoi(data) != 0;
1867 return NULL;
1870 static char *filer_menu_on_2(char *data)
1872 collection_menu_button = atoi(data) != 0 ? 2 : 3;
1873 return NULL;
1876 static char *filer_single_click(char *data)
1878 o_single_click = atoi(data) != 0;
1879 collection_single_click = o_single_click ? TRUE : FALSE;
1880 return NULL;
1883 static char *filer_unique_windows(char *data)
1885 o_unique_filer_windows = atoi(data) != 0;
1886 return NULL;
1889 static char *filer_display_style(char *data)
1891 if (g_strcasecmp(data, "Large Icons") == 0)
1892 last_display_style = LARGE_ICONS;
1893 else if (g_strcasecmp(data, "Small Icons") == 0)
1894 last_display_style = SMALL_ICONS;
1895 else if (g_strcasecmp(data, "Full Info") == 0)
1896 last_display_style = FULL_INFO;
1897 else
1898 return "Unknown display style";
1900 return NULL;
1903 static char *filer_sort_by(char *data)
1905 if (g_strcasecmp(data, "Name") == 0)
1906 last_sort_fn = sort_by_name;
1907 else if (g_strcasecmp(data, "Type") == 0)
1908 last_sort_fn = sort_by_type;
1909 else if (g_strcasecmp(data, "Date") == 0)
1910 last_sort_fn = sort_by_date;
1911 else if (g_strcasecmp(data, "Size") == 0)
1912 last_sort_fn = sort_by_size;
1913 else
1914 return "Unknown sort type";
1916 return NULL;
1919 static char *filer_toolbar(char *data)
1921 if (g_strcasecmp(data, "None") == 0)
1922 o_toolbar = TOOLBAR_NONE;
1923 else if (g_strcasecmp(data, "Normal") == 0)
1924 o_toolbar = TOOLBAR_NORMAL;
1925 else if (g_strcasecmp(data, "GNOME") == 0)
1926 o_toolbar = TOOLBAR_GNOME;
1927 else
1928 return "Unknown toolbar type";
1930 return NULL;
1933 /* Note that filer_window may not exist after this call. */
1934 void update_dir(FilerWindow *filer_window, gboolean warning)
1936 if (may_rescan(filer_window, warning))
1937 dir_update(filer_window->directory, filer_window->path);
1940 void filer_set_hidden(FilerWindow *filer_window, gboolean hidden)
1942 Directory *dir = filer_window->directory;
1944 if (filer_window->show_hidden == hidden)
1945 return;
1947 filer_window->show_hidden = hidden;
1948 last_show_hidden = hidden;
1950 g_fscache_data_ref(dir_cache, dir);
1951 detach(filer_window);
1952 filer_window->directory = dir;
1953 attach(filer_window);
1956 /* Refresh the various caches even if we don't think we need to */
1957 void full_refresh(void)
1959 mount_update(TRUE);
1962 /* See whether a filer window with a given path already exists
1963 * and is different from diff.
1965 static FilerWindow *find_filer_window(char *path, FilerWindow *diff)
1967 GList *next = all_filer_windows;
1969 while (next)
1971 FilerWindow *filer_window = (FilerWindow *) next->data;
1973 if (filer_window->panel_type == PANEL_NO &&
1974 filer_window != diff &&
1975 strcmp(path, filer_window->path) == 0)
1977 return filer_window;
1980 next = next->next;
1983 return NULL;
1986 /* This path has been mounted/umounted/deleted some files - update all dirs */
1987 void filer_check_mounted(char *path)
1989 GList *next = all_filer_windows;
1990 int len;
1992 len = strlen(path);
1994 while (next)
1996 FilerWindow *filer_window = (FilerWindow *) next->data;
1998 next = next->next;
2000 if (strncmp(path, filer_window->path, len) == 0)
2002 char s = filer_window->path[len];
2004 if (s == '/' || s == '\0')
2005 update_dir(filer_window, FALSE);
2010 /* Like minibuffer_show(), except that:
2011 * - It returns FALSE (to be used from an idle callback)
2012 * - It checks that the filer window still exists.
2014 static gboolean minibuffer_show_cb(FilerWindow *filer_window)
2016 if (exists(filer_window))
2017 minibuffer_show(filer_window, MINI_PATH);
2018 return FALSE;
2021 static gboolean exists(FilerWindow *filer_window)
2023 GList *next;
2025 for (next = all_filer_windows; next; next = next->next)
2027 FilerWindow *fw = (FilerWindow *) next->data;
2029 if (fw == filer_window)
2030 return TRUE;
2033 return FALSE;
2036 /* Highlight (wink or cursor) this item in the filer window. If the item
2037 * isn't already there but we're scanning then highlight it if it
2038 * appears later.
2040 void filer_set_autoselect(FilerWindow *filer_window, guchar *leaf)
2042 Collection *col = filer_window->collection;
2043 int i;
2045 g_free(filer_window->auto_select);
2046 filer_window->auto_select = NULL;
2048 for (i = 0; i < col->number_of_items; i++)
2050 DirItem *item = (DirItem *) col->items[i].data;
2052 if (strcmp(item->leafname, leaf) == 0)
2054 if (col->cursor_item != -1)
2055 collection_set_cursor_item(col, i);
2056 else
2057 collection_wink_item(col, i);
2058 return;
2062 filer_window->auto_select = g_strdup(leaf);
2065 static void filer_set_title(FilerWindow *filer_window)
2067 if (filer_window->scanning)
2069 guchar *title;
2071 title = g_strdup_printf("%s (Scanning)", filer_window->path);
2072 gtk_window_set_title(GTK_WINDOW(filer_window->window),
2073 title);
2074 g_free(title);
2076 else
2077 gtk_window_set_title(GTK_WINDOW(filer_window->window),
2078 filer_window->path);