r291: Improved theme support (pixmap backgrounds work now).
[rox-filer/ma.git] / ROX-Filer / src / filer.c
blob7024b702534bb0e80df45c94b0ad0e2d22f6c73e
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 20
56 #define ROW_HEIGHT_FULL_INFO 44
57 #define SMALL_ICON_HEIGHT 20
58 #ifdef HAVE_IMLIB
59 # define SMALL_ICON_WIDTH 24
60 #else
61 # define SMALL_ICON_WIDTH 48
62 #endif
63 #define MAX_ICON_HEIGHT 42
64 #define MAX_ICON_WIDTH 48
65 #define PANEL_BORDER 2
66 #define MIN_ITEM_WIDTH 64
68 #define MIN_TRUNCATE 0
69 #define MAX_TRUNCATE 250
71 extern int collection_menu_button;
72 extern gboolean collection_single_click;
74 FilerWindow *window_with_focus = NULL;
75 GList *all_filer_windows = NULL;
77 static DisplayStyle last_display_style = LARGE_ICONS;
78 static gboolean last_show_hidden = FALSE;
79 static int (*last_sort_fn)(const void *a, const void *b) = sort_by_type;
81 static FilerWindow *window_with_selection = NULL;
83 /* Options bits */
84 static guchar *style_to_name(void);
85 static guchar *sort_fn_to_name(void);
86 static void update_options_label(void);
88 static GtkWidget *create_options();
89 static void update_options();
90 static void set_options();
91 static void save_options();
92 static char *filer_sort_nocase(char *data);
93 static char *filer_single_click(char *data);
94 static char *filer_unique_windows(char *data);
95 static char *filer_menu_on_2(char *data);
96 static char *filer_new_window_on_1(char *data);
97 static char *filer_toolbar(char *data);
98 static char *filer_display_style(char *data);
99 static char *filer_sort_by(char *data);
100 static char *filer_truncate(char *data);
102 static OptionsSection options =
104 N_("Filer window options"),
105 create_options,
106 update_options,
107 set_options,
108 save_options
111 /* The values correspond to the menu indexes in the option widget */
112 typedef enum {
113 TOOLBAR_NONE = 0,
114 TOOLBAR_NORMAL = 1,
115 TOOLBAR_GNOME = 2,
116 } ToolbarType;
117 static ToolbarType o_toolbar = TOOLBAR_NORMAL;
118 static GtkWidget *menu_toolbar;
120 static GtkWidget *display_label;
122 static gboolean o_sort_nocase = FALSE;
123 static gboolean o_single_click = FALSE;
124 static gboolean o_new_window_on_1 = FALSE; /* Button 1 => New window */
125 gboolean o_unique_filer_windows = FALSE;
126 static gint o_small_truncate = 250;
127 static gint o_large_truncate = 89;
128 static GtkAdjustment *adj_small_truncate;
129 static GtkAdjustment *adj_large_truncate;
130 static GtkWidget *toggle_sort_nocase;
131 static GtkWidget *toggle_single_click;
132 static GtkWidget *toggle_new_window_on_1;
133 static GtkWidget *toggle_menu_on_2;
134 static GtkWidget *toggle_unique_filer_windows;
136 /* Static prototypes */
137 static void attach(FilerWindow *filer_window);
138 static void detach(FilerWindow *filer_window);
139 static void filer_window_destroyed(GtkWidget *widget,
140 FilerWindow *filer_window);
141 static void show_menu(Collection *collection, GdkEventButton *event,
142 int number_selected, gpointer user_data);
143 static gint focus_in(GtkWidget *widget,
144 GdkEventFocus *event,
145 FilerWindow *filer_window);
146 static gint focus_out(GtkWidget *widget,
147 GdkEventFocus *event,
148 FilerWindow *filer_window);
149 static void add_item(FilerWindow *filer_window, DirItem *item);
150 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window);
151 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window);
152 static void add_button(GtkWidget *box, MaskedPixmap *icon,
153 GtkSignalFunc cb, FilerWindow *filer_window,
154 char *label, char *tip);
155 static GtkWidget *create_toolbar(FilerWindow *filer_window);
156 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
157 FilerWindow *window);
158 static int calc_width(FilerWindow *filer_window, DirItem *item);
159 static void draw_large_icon(GtkWidget *widget,
160 GdkRectangle *area,
161 DirItem *item,
162 gboolean selected);
163 static void draw_string(GtkWidget *widget,
164 GdkFont *font,
165 char *string,
166 int x,
167 int y,
168 int width,
169 int area_width,
170 gboolean selected);
171 static void draw_item_large(GtkWidget *widget,
172 CollectionItem *item,
173 GdkRectangle *area);
174 static void draw_item_small(GtkWidget *widget,
175 CollectionItem *item,
176 GdkRectangle *area);
177 static void draw_item_full_info(GtkWidget *widget,
178 CollectionItem *colitem,
179 GdkRectangle *area);
180 static gboolean test_point_large(Collection *collection,
181 int point_x, int point_y,
182 CollectionItem *item,
183 int width, int height);
184 static gboolean test_point_small(Collection *collection,
185 int point_x, int point_y,
186 CollectionItem *item,
187 int width, int height);
188 static gboolean test_point_full_info(Collection *collection,
189 int point_x, int point_y,
190 CollectionItem *item,
191 int width, int height);
192 static void update_display(Directory *dir,
193 DirAction action,
194 GPtrArray *items,
195 FilerWindow *filer_window);
196 static void set_scanning_display(FilerWindow *filer_window, gboolean scanning);
197 static void shrink_width(FilerWindow *filer_window);
198 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning);
199 static void open_item(Collection *collection,
200 gpointer item_data, int item_number,
201 gpointer user_data);
202 static gboolean minibuffer_show_cb(FilerWindow *filer_window);
203 static FilerWindow *find_filer_window(char *path, FilerWindow *diff);
204 static void filer_set_title(FilerWindow *filer_window);
206 static GdkAtom xa_string;
207 enum
209 TARGET_STRING,
210 TARGET_URI_LIST,
213 static GdkCursor *busy_cursor = NULL;
214 static GtkTooltips *tooltips = NULL;
216 void filer_init()
218 xa_string = gdk_atom_intern("STRING", FALSE);
220 options_sections = g_slist_prepend(options_sections, &options);
221 option_register("filer_sort_nocase", filer_sort_nocase);
222 option_register("filer_new_window_on_1", filer_new_window_on_1);
223 option_register("filer_menu_on_2", filer_menu_on_2);
224 option_register("filer_single_click", filer_single_click);
225 option_register("filer_unique_windows", filer_unique_windows);
226 option_register("filer_toolbar", filer_toolbar);
227 option_register("filer_display_style", filer_display_style);
228 option_register("filer_sort_by", filer_sort_by);
229 option_register("filer_truncate", filer_truncate);
231 busy_cursor = gdk_cursor_new(GDK_WATCH);
233 tooltips = gtk_tooltips_new();
236 static gboolean if_deleted(gpointer item, gpointer removed)
238 int i = ((GPtrArray *) removed)->len;
239 DirItem **r = (DirItem **) ((GPtrArray *) removed)->pdata;
240 char *leafname = ((DirItem *) item)->leafname;
242 while (i--)
244 if (strcmp(leafname, r[i]->leafname) == 0)
245 return TRUE;
248 return FALSE;
251 static void update_item(FilerWindow *filer_window, DirItem *item)
253 int i;
254 char *leafname = item->leafname;
256 if (leafname[0] == '.')
258 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
259 || (leafname[1] == '.' && leafname[2] == '\0'))
260 return;
263 i = collection_find_item(filer_window->collection, item, dir_item_cmp);
265 if (i >= 0)
266 collection_draw_item(filer_window->collection, i, TRUE);
267 else
268 g_warning("Failed to find '%s'\n", item->leafname);
271 static void update_display(Directory *dir,
272 DirAction action,
273 GPtrArray *items,
274 FilerWindow *filer_window)
276 int old_num;
277 int i;
278 int cursor = filer_window->collection->cursor_item;
279 char *as;
280 Collection *collection = filer_window->collection;
282 switch (action)
284 case DIR_ADD:
285 as = filer_window->auto_select;
287 old_num = collection->number_of_items;
288 for (i = 0; i < items->len; i++)
290 DirItem *item = (DirItem *) items->pdata[i];
292 add_item(filer_window, item);
294 if (cursor != -1 || !as)
295 continue;
297 if (strcmp(as, item->leafname) != 0)
298 continue;
300 cursor = collection->number_of_items - 1;
301 if (filer_window->had_cursor)
303 collection_set_cursor_item(collection,
304 cursor);
305 filer_window->mini_cursor_base = cursor;
307 else
308 collection_wink_item(collection,
309 cursor);
312 if (old_num != collection->number_of_items)
313 collection_qsort(filer_window->collection,
314 filer_window->sort_fn);
315 break;
316 case DIR_REMOVE:
317 collection_delete_if(filer_window->collection,
318 if_deleted,
319 items);
320 break;
321 case DIR_START_SCAN:
322 set_scanning_display(filer_window, TRUE);
323 break;
324 case DIR_END_SCAN:
325 if (filer_window->window->window)
326 gdk_window_set_cursor(
327 filer_window->window->window,
328 NULL);
329 shrink_width(filer_window);
330 if (filer_window->had_cursor &&
331 collection->cursor_item == -1)
333 collection_set_cursor_item(collection, 0);
334 filer_window->had_cursor = FALSE;
336 set_scanning_display(filer_window, FALSE);
337 break;
338 case DIR_UPDATE:
339 for (i = 0; i < items->len; i++)
341 DirItem *item = (DirItem *) items->pdata[i];
343 update_item(filer_window, item);
345 collection_qsort(filer_window->collection,
346 filer_window->sort_fn);
347 break;
351 static void attach(FilerWindow *filer_window)
353 gdk_window_set_cursor(filer_window->window->window, busy_cursor);
354 collection_clear(filer_window->collection);
355 filer_window->scanning = TRUE;
356 dir_attach(filer_window->directory, (DirCallback) update_display,
357 filer_window);
358 filer_set_title(filer_window);
361 static void detach(FilerWindow *filer_window)
363 g_return_if_fail(filer_window->directory != NULL);
365 dir_detach(filer_window->directory,
366 (DirCallback) update_display, filer_window);
367 g_fscache_data_unref(dir_cache, filer_window->directory);
368 filer_window->directory = NULL;
371 static void filer_window_destroyed(GtkWidget *widget,
372 FilerWindow *filer_window)
374 all_filer_windows = g_list_remove(all_filer_windows, filer_window);
376 if (window_with_selection == filer_window)
377 window_with_selection = NULL;
378 if (window_with_focus == filer_window)
379 window_with_focus = NULL;
381 if (filer_window->directory)
382 detach(filer_window);
384 g_free(filer_window->auto_select);
385 g_free(filer_window->path);
386 g_free(filer_window);
388 if (--number_of_windows < 1)
389 gtk_main_quit();
392 static int calc_width(FilerWindow *filer_window, DirItem *item)
394 int pix_width = item->image->width;
395 int w;
397 switch (filer_window->display_style)
399 case FULL_INFO:
400 return MAX_ICON_WIDTH + 12 +
401 MAX(item->details_width, item->name_width);
402 case SMALL_ICONS:
403 w = MIN(item->name_width, o_small_truncate);
404 return SMALL_ICON_WIDTH + 12 + w;
405 default:
406 w = MIN(item->name_width, o_large_truncate);
407 return MAX(pix_width, w) + 4;
411 /* Add a single object to a directory display */
412 static void add_item(FilerWindow *filer_window, DirItem *item)
414 char *leafname = item->leafname;
415 int item_width;
417 if (leafname[0] == '.')
419 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
420 || (leafname[1] == '.' && leafname[2] == '\0'))
421 return;
424 item_width = calc_width(filer_window, item);
425 if (item_width > filer_window->collection->item_width)
426 collection_set_item_size(filer_window->collection,
427 item_width,
428 filer_window->collection->item_height);
429 collection_insert(filer_window->collection, item);
432 /* Is a point inside an item? */
433 static gboolean test_point_large(Collection *collection,
434 int point_x, int point_y,
435 CollectionItem *colitem,
436 int width, int height)
438 DirItem *item = (DirItem *) colitem->data;
439 int text_height = item_font->ascent + item_font->descent;
440 MaskedPixmap *image = item->image;
441 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
442 int image_width = (image->width >> 1) + 2;
443 int text_width = (item->name_width >> 1) + 2;
444 int x_limit;
446 if (point_y < image_y)
447 return FALSE; /* Too high up (don't worry about too low) */
449 if (point_y <= image_y + image->height + 2)
450 x_limit = image_width;
451 else if (point_y > height - text_height - 2)
452 x_limit = text_width;
453 else
454 x_limit = MIN(image_width, text_width);
456 return ABS(point_x - (width >> 1)) < x_limit;
459 static gboolean test_point_full_info(Collection *collection,
460 int point_x, int point_y,
461 CollectionItem *colitem,
462 int width, int height)
464 DirItem *item = (DirItem *) colitem->data;
465 MaskedPixmap *image = item->image;
466 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
467 int low_top = height
468 - fixed_font->descent - 2 - fixed_font->ascent;
470 if (point_x < image->width + 2)
471 return point_x > 2 && point_y > image_y;
473 point_x -= MAX_ICON_WIDTH + 8;
475 if (point_y >= low_top)
476 return point_x < item->details_width;
477 if (point_y >= low_top - item_font->ascent - item_font->descent)
478 return point_x < item->name_width;
479 return FALSE;
482 static gboolean test_point_small(Collection *collection,
483 int point_x, int point_y,
484 CollectionItem *colitem,
485 int width, int height)
487 DirItem *item = (DirItem *) colitem->data;
488 MaskedPixmap *image = item->image;
489 int image_y = MAX(0, SMALL_ICON_HEIGHT - image->height);
490 int low_top = height
491 - fixed_font->descent - 2 - item_font->ascent;
492 int iwidth = MIN(SMALL_ICON_WIDTH, image->width);
494 if (point_x < iwidth + 2)
495 return point_x > 2 && point_y > image_y;
497 point_x -= SMALL_ICON_WIDTH + 4;
499 if (point_y >= low_top)
500 return point_x < item->name_width;
501 return FALSE;
504 static void draw_small_icon(GtkWidget *widget,
505 GdkRectangle *area,
506 DirItem *item,
507 gboolean selected)
509 GdkGC *gc = selected ? widget->style->white_gc
510 : widget->style->black_gc;
511 MaskedPixmap *image = item->image;
512 int width, height, image_x, image_y;
514 if (!image)
515 return;
517 if (!image->sm_pixmap)
518 pixmap_make_small(image);
520 width = MIN(image->sm_width, SMALL_ICON_WIDTH);
521 height = MIN(image->sm_height, SMALL_ICON_HEIGHT);
522 image_x = area->x + ((area->width - width) >> 1);
524 gdk_gc_set_clip_mask(gc, item->image->sm_mask);
526 image_y = MAX(0, SMALL_ICON_HEIGHT - image->sm_height);
527 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
528 gdk_draw_pixmap(widget->window, gc,
529 item->image->sm_pixmap,
530 0, 0, /* Source x,y */
531 image_x, area->y + image_y, /* Dest x,y */
532 width, height);
534 if (selected)
536 gdk_gc_set_function(gc, GDK_INVERT);
537 gdk_draw_rectangle(widget->window,
539 TRUE, image_x, area->y + image_y,
540 width, height);
541 gdk_gc_set_function(gc, GDK_COPY);
544 if (item->flags & ITEM_FLAG_SYMLINK)
546 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
547 gdk_gc_set_clip_mask(gc, im_symlink->mask);
548 gdk_draw_pixmap(widget->window, gc, im_symlink->pixmap,
549 0, 0, /* Source x,y */
550 image_x, area->y + 8, /* Dest x,y */
551 -1, -1);
553 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
555 MaskedPixmap *mp = item->flags & ITEM_FLAG_MOUNTED
556 ? im_mounted
557 : im_unmounted;
559 if (!mp->sm_pixmap)
560 pixmap_make_small(mp);
561 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
562 gdk_gc_set_clip_mask(gc, mp->sm_mask);
563 gdk_draw_pixmap(widget->window, gc,
564 mp->sm_pixmap,
565 0, 0, /* Source x,y */
566 image_x, area->y + 8, /* Dest x,y */
567 -1, -1);
570 gdk_gc_set_clip_mask(gc, NULL);
571 gdk_gc_set_clip_origin(gc, 0, 0);
574 static void draw_large_icon(GtkWidget *widget,
575 GdkRectangle *area,
576 DirItem *item,
577 gboolean selected)
579 MaskedPixmap *image = item->image;
580 int width = MIN(image->width, MAX_ICON_WIDTH);
581 int height = MIN(image->height, MAX_ICON_WIDTH);
582 int image_x = area->x + ((area->width - width) >> 1);
583 int image_y;
584 GdkGC *gc = selected ? widget->style->white_gc
585 : widget->style->black_gc;
587 gdk_gc_set_clip_mask(gc, item->image->mask);
589 image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
590 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
591 gdk_draw_pixmap(widget->window, gc,
592 item->image->pixmap,
593 0, 0, /* Source x,y */
594 image_x, area->y + image_y, /* Dest x,y */
595 width, height);
597 if (selected)
599 gdk_gc_set_function(gc, GDK_INVERT);
600 gdk_draw_rectangle(widget->window,
602 TRUE, image_x, area->y + image_y,
603 width, height);
604 gdk_gc_set_function(gc, GDK_COPY);
607 if (item->flags & ITEM_FLAG_SYMLINK)
609 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
610 gdk_gc_set_clip_mask(gc, im_symlink->mask);
611 gdk_draw_pixmap(widget->window, gc, im_symlink->pixmap,
612 0, 0, /* Source x,y */
613 image_x, area->y + 8, /* Dest x,y */
614 -1, -1);
616 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
618 MaskedPixmap *mp = item->flags & ITEM_FLAG_MOUNTED
619 ? im_mounted
620 : im_unmounted;
622 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
623 gdk_gc_set_clip_mask(gc, mp->mask);
624 gdk_draw_pixmap(widget->window, gc, mp->pixmap,
625 0, 0, /* Source x,y */
626 image_x, area->y + 8, /* Dest x,y */
627 -1, -1);
630 gdk_gc_set_clip_mask(gc, NULL);
631 gdk_gc_set_clip_origin(gc, 0, 0);
634 static void draw_string(GtkWidget *widget,
635 GdkFont *font,
636 char *string,
637 int x,
638 int y,
639 int width,
640 int area_width,
641 gboolean selected)
643 int text_height = font->ascent + font->descent;
644 GdkRectangle clip;
645 GdkGC *gc = selected
646 ? widget->style->fg_gc[GTK_STATE_SELECTED]
647 : widget->style->fg_gc[GTK_STATE_NORMAL];
649 if (selected)
650 gtk_paint_flat_box(widget->style, widget->window,
651 GTK_STATE_SELECTED, GTK_SHADOW_NONE,
652 NULL, widget, "text",
653 x, y - font->ascent,
654 MIN(width, area_width),
655 text_height);
657 if (width > area_width)
659 clip.x = x;
660 clip.y = y - font->ascent;
661 clip.width = area_width;
662 clip.height = text_height;
663 gdk_gc_set_clip_origin(gc, 0, 0);
664 gdk_gc_set_clip_rectangle(gc, &clip);
667 gdk_draw_text(widget->window,
668 font,
670 x, y,
671 string, strlen(string));
673 if (width > area_width)
675 if (!red_gc)
677 red_gc = gdk_gc_new(widget->window);
678 gdk_gc_set_foreground(red_gc, &red);
680 gdk_draw_rectangle(widget->window, red_gc, TRUE,
681 x + area_width - 1, clip.y, 1, text_height);
682 gdk_gc_set_clip_rectangle(gc, NULL);
686 /* Return a string (valid until next call) giving details
687 * of this item.
689 char *details(DirItem *item)
691 mode_t m = item->mode;
692 static guchar *buf = NULL;
694 if (buf)
695 g_free(buf);
697 if (item->lstat_errno)
698 buf = g_strdup_printf(_("lstat(2) failed: %s"),
699 g_strerror(item->lstat_errno));
700 else
701 buf = g_strdup_printf("%s %s %-8.8s %-8.8s %s %s",
702 item->flags & ITEM_FLAG_APPDIR? "App " :
703 S_ISDIR(m) ? "Dir " :
704 S_ISCHR(m) ? "Char" :
705 S_ISBLK(m) ? "Blck" :
706 S_ISLNK(m) ? "Link" :
707 S_ISSOCK(m) ? "Sock" :
708 S_ISFIFO(m) ? "Pipe" : "File",
709 pretty_permissions(m),
710 user_name(item->uid),
711 group_name(item->gid),
712 format_size_aligned(item->size),
713 pretty_time(&item->mtime));
714 return buf;
717 static void draw_item_full_info(GtkWidget *widget,
718 CollectionItem *colitem,
719 GdkRectangle *area)
721 DirItem *item = (DirItem *) colitem->data;
722 MaskedPixmap *image = item->image;
723 int text_x = area->x + MAX_ICON_WIDTH + 8;
724 int low_text_y = area->y + area->height - fixed_font->descent - 2;
725 gboolean selected = colitem->selected;
726 GdkRectangle pic_area;
727 int text_area_width = area->width - (text_x - area->x);
729 pic_area.x = area->x;
730 pic_area.y = area->y;
731 pic_area.width = image->width + 8;
732 pic_area.height = area->height;
734 draw_large_icon(widget, &pic_area, item, selected);
736 draw_string(widget,
737 item_font,
738 item->leafname,
739 text_x,
740 low_text_y - item_font->descent - fixed_font->ascent,
741 item->name_width,
742 text_area_width,
743 selected);
744 draw_string(widget,
745 fixed_font,
746 details(item),
747 text_x, low_text_y,
748 item->details_width,
749 text_area_width,
750 selected);
752 if (item->lstat_errno)
753 return;
755 /* Underline the effective permissions */
756 gdk_draw_rectangle(widget->window,
757 selected ? widget->style->white_gc
758 : widget->style->black_gc,
759 TRUE,
760 text_x - 1 + fixed_width *
761 (5 + 4 * applicable(item->uid, item->gid)),
762 low_text_y + fixed_font->descent - 1,
763 fixed_width * 3 + 1, 1);
766 static void draw_item_small(GtkWidget *widget,
767 CollectionItem *colitem,
768 GdkRectangle *area)
770 DirItem *item = (DirItem *) colitem->data;
771 int text_x = area->x + SMALL_ICON_WIDTH + 4;
772 int low_text_y = area->y + area->height - item_font->descent - 2;
773 gboolean selected = colitem->selected;
774 GdkRectangle pic_area;
776 pic_area.x = area->x;
777 pic_area.y = area->y;
778 pic_area.width = SMALL_ICON_WIDTH;
779 pic_area.height = SMALL_ICON_HEIGHT;
781 draw_small_icon(widget, &pic_area, item, selected);
783 draw_string(widget,
784 item_font,
785 item->leafname,
786 text_x,
787 low_text_y,
788 item->name_width,
789 area->width - (text_x - area->x),
790 selected);
793 static void draw_item_large(GtkWidget *widget,
794 CollectionItem *colitem,
795 GdkRectangle *area)
797 DirItem *item = (DirItem *) colitem->data;
798 int text_width = item->name_width;
799 int text_x = area->x + ((area->width - text_width) >> 1);
800 int text_y = area->y + area->height - item_font->descent - 2;
801 gboolean selected = colitem->selected;
803 draw_large_icon(widget, area, item, selected);
805 if (text_x < area->x)
806 text_x = area->x;
808 draw_string(widget,
809 item_font,
810 item->leafname,
811 text_x, text_y,
812 item->name_width,
813 area->width,
814 selected);
817 static void show_menu(Collection *collection, GdkEventButton *event,
818 int item, gpointer user_data)
820 show_filer_menu((FilerWindow *) user_data, event, item);
823 /* Returns TRUE iff the directory still exists. */
824 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning)
826 Directory *dir;
828 g_return_val_if_fail(filer_window != NULL, FALSE);
830 /* We do a fresh lookup (rather than update) because the inode may
831 * have changed.
833 dir = g_fscache_lookup(dir_cache, filer_window->path);
834 if (!dir)
836 if (warning)
837 delayed_error(PROJECT, _("Directory missing/deleted"));
838 gtk_widget_destroy(filer_window->window);
839 return FALSE;
841 if (dir == filer_window->directory)
842 g_fscache_data_unref(dir_cache, dir);
843 else
845 detach(filer_window);
846 filer_window->directory = dir;
847 attach(filer_window);
850 return TRUE;
853 /* Another app has grabbed the selection */
854 static gint collection_lose_selection(GtkWidget *widget,
855 GdkEventSelection *event)
857 if (window_with_selection &&
858 window_with_selection->collection == COLLECTION(widget))
860 FilerWindow *filer_window = window_with_selection;
861 window_with_selection = NULL;
862 collection_clear_selection(filer_window->collection);
865 return TRUE;
868 /* Someone wants us to send them the selection */
869 static void selection_get(GtkWidget *widget,
870 GtkSelectionData *selection_data,
871 guint info,
872 guint time,
873 gpointer data)
875 GString *reply, *header;
876 FilerWindow *filer_window;
877 int i;
878 Collection *collection;
880 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
882 reply = g_string_new(NULL);
883 header = g_string_new(NULL);
885 switch (info)
887 case TARGET_STRING:
888 g_string_sprintf(header, " %s",
889 make_path(filer_window->path, "")->str);
890 break;
891 case TARGET_URI_LIST:
892 g_string_sprintf(header, " file://%s%s",
893 our_host_name(),
894 make_path(filer_window->path, "")->str);
895 break;
898 collection = filer_window->collection;
899 for (i = 0; i < collection->number_of_items; i++)
901 if (collection->items[i].selected)
903 DirItem *item =
904 (DirItem *) collection->items[i].data;
906 g_string_append(reply, header->str);
907 g_string_append(reply, item->leafname);
910 /* This works, but I don't think I like it... */
911 /* g_string_append_c(reply, ' '); */
913 gtk_selection_data_set(selection_data, xa_string,
914 8, reply->str + 1, reply->len - 1);
915 g_string_free(reply, TRUE);
916 g_string_free(header, TRUE);
919 /* No items are now selected. This might be because another app claimed
920 * the selection or because the user unselected all the items.
922 static void lose_selection(Collection *collection,
923 guint time,
924 gpointer user_data)
926 FilerWindow *filer_window = (FilerWindow *) user_data;
928 if (window_with_selection == filer_window)
930 window_with_selection = NULL;
931 gtk_selection_owner_set(NULL,
932 GDK_SELECTION_PRIMARY,
933 time);
937 static void gain_selection(Collection *collection,
938 guint time,
939 gpointer user_data)
941 FilerWindow *filer_window = (FilerWindow *) user_data;
943 if (gtk_selection_owner_set(GTK_WIDGET(collection),
944 GDK_SELECTION_PRIMARY,
945 time))
947 window_with_selection = filer_window;
949 else
950 collection_clear_selection(filer_window->collection);
953 int sort_by_name(const void *item1, const void *item2)
955 if (o_sort_nocase)
956 return g_strcasecmp((*((DirItem **)item1))->leafname,
957 (*((DirItem **)item2))->leafname);
958 return strcmp((*((DirItem **)item1))->leafname,
959 (*((DirItem **)item2))->leafname);
962 int sort_by_type(const void *item1, const void *item2)
964 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
965 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
966 MIME_type *m1, *m2;
968 int diff = i1->base_type - i2->base_type;
970 if (!diff)
971 diff = (i1->flags & ITEM_FLAG_APPDIR)
972 - (i2->flags & ITEM_FLAG_APPDIR);
973 if (diff)
974 return diff > 0 ? 1 : -1;
976 m1 = i1->mime_type;
977 m2 = i2->mime_type;
979 if (m1 && m2)
981 diff = strcmp(m1->media_type, m2->media_type);
982 if (!diff)
983 diff = strcmp(m1->subtype, m2->subtype);
985 else if (m1 || m2)
986 diff = m1 ? 1 : -1;
987 else
988 diff = 0;
990 if (diff)
991 return diff > 0 ? 1 : -1;
993 return sort_by_name(item1, item2);
996 int sort_by_date(const void *item1, const void *item2)
998 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
999 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
1001 return i1->mtime > i2->mtime ? -1 :
1002 i1->mtime < i2->mtime ? 1 :
1003 sort_by_name(item1, item2);
1006 int sort_by_size(const void *item1, const void *item2)
1008 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
1009 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
1011 return i1->size > i2->size ? -1 :
1012 i1->size < i2->size ? 1 :
1013 sort_by_name(item1, item2);
1016 static void open_item(Collection *collection,
1017 gpointer item_data, int item_number,
1018 gpointer user_data)
1020 FilerWindow *filer_window = (FilerWindow *) user_data;
1021 GdkEvent *event;
1022 GdkEventButton *bevent;
1023 GdkEventKey *kevent;
1024 OpenFlags flags = 0;
1026 event = (GdkEvent *) gtk_get_current_event();
1028 bevent = (GdkEventButton *) event;
1029 kevent = (GdkEventKey *) event;
1031 switch (event->type)
1033 case GDK_2BUTTON_PRESS:
1034 case GDK_BUTTON_PRESS:
1035 case GDK_BUTTON_RELEASE:
1036 if (bevent->state & GDK_SHIFT_MASK)
1037 flags |= OPEN_SHIFT;
1039 if (o_new_window_on_1 ^ (bevent->button == 1))
1040 flags |= OPEN_SAME_WINDOW;
1042 if (bevent->button != 1)
1043 flags |= OPEN_CLOSE_WINDOW;
1045 if (o_single_click == FALSE &&
1046 (bevent->state & GDK_CONTROL_MASK) != 0)
1047 flags ^= OPEN_SAME_WINDOW | OPEN_CLOSE_WINDOW;
1048 break;
1049 case GDK_KEY_PRESS:
1050 flags |= OPEN_SAME_WINDOW;
1051 if (kevent->state & GDK_SHIFT_MASK)
1052 flags |= OPEN_SHIFT;
1053 break;
1054 default:
1055 break;
1058 filer_openitem(filer_window, item_number, flags);
1061 /* Return the full path to the directory containing object 'path'.
1062 * Relative paths are resolved from the filerwindow's path.
1064 static void follow_symlink(FilerWindow *filer_window, char *path,
1065 gboolean same_window)
1067 char *real, *slash;
1068 char *new_dir;
1070 if (path[0] != '/')
1071 path = make_path(filer_window->path, path)->str;
1073 real = pathdup(path);
1074 slash = strrchr(real, '/');
1075 if (!slash)
1077 g_free(real);
1078 delayed_error(PROJECT,
1079 _("Broken symlink (or you don't have permission "
1080 "to follow it)."));
1081 return;
1084 *slash = '\0';
1086 if (*real)
1087 new_dir = real;
1088 else
1089 new_dir = "/";
1091 if (filer_window->panel_type || !same_window)
1093 FilerWindow *new;
1095 new = filer_opendir(new_dir, PANEL_NO);
1096 filer_set_autoselect(new, slash + 1);
1098 else
1099 filer_change_to(filer_window, new_dir, slash + 1);
1101 g_free(real);
1104 /* Open the item (or add it to the shell command minibuffer) */
1105 void filer_openitem(FilerWindow *filer_window, int item_number, OpenFlags flags)
1107 gboolean shift = (flags & OPEN_SHIFT) != 0;
1108 gboolean close_mini = flags & OPEN_FROM_MINI;
1109 gboolean same_window = (flags & OPEN_SAME_WINDOW) != 0
1110 && !filer_window->panel_type;
1111 gboolean close_window = (flags & OPEN_CLOSE_WINDOW) != 0
1112 && !filer_window->panel_type;
1113 GtkWidget *widget;
1114 char *full_path;
1115 DirItem *item = (DirItem *)
1116 filer_window->collection->items[item_number].data;
1117 gboolean wink = TRUE, destroy = FALSE;
1119 widget = filer_window->window;
1120 if (filer_window->mini_type == MINI_SHELL)
1122 minibuffer_add(filer_window, item->leafname);
1123 return;
1126 full_path = make_path(filer_window->path,
1127 item->leafname)->str;
1129 if (item->flags & ITEM_FLAG_SYMLINK && shift)
1131 char path[MAXPATHLEN + 1];
1132 int got;
1134 got = readlink(make_path(filer_window->path,
1135 item->leafname)->str,
1136 path, MAXPATHLEN);
1137 if (got < 0)
1138 delayed_error(PROJECT, g_strerror(errno));
1139 else
1141 g_return_if_fail(got <= MAXPATHLEN);
1142 path[got] = '\0';
1144 follow_symlink(filer_window, path,
1145 flags & OPEN_SAME_WINDOW);
1147 return;
1150 switch (item->base_type)
1152 case TYPE_DIRECTORY:
1153 if (item->flags & ITEM_FLAG_APPDIR && !shift)
1155 run_app(make_path(filer_window->path,
1156 item->leafname)->str);
1157 if (close_window)
1158 destroy = TRUE;
1159 break;
1162 if (item->flags & ITEM_FLAG_MOUNT_POINT && shift)
1164 action_mount(filer_window, item);
1165 if (item->flags & ITEM_FLAG_MOUNTED)
1166 break;
1169 if (same_window)
1171 wink = FALSE;
1172 filer_change_to(filer_window, full_path, NULL);
1173 close_mini = FALSE;
1175 else
1176 filer_opendir(full_path, PANEL_NO);
1177 break;
1178 case TYPE_FILE:
1179 if ((item->flags & ITEM_FLAG_EXEC_FILE) && !shift)
1181 char *argv[] = {NULL, NULL};
1183 argv[0] = full_path;
1185 if (spawn_full(argv, filer_window->path))
1187 if (close_window)
1188 destroy = TRUE;
1190 else
1191 report_error(PROJECT,
1192 _("Failed to fork() child"));
1194 else
1196 GString *message;
1197 MIME_type *type = shift ? &text_plain
1198 : item->mime_type;
1200 g_return_if_fail(type != NULL);
1202 if (type_open(full_path, type))
1204 if (close_window)
1205 destroy = TRUE;
1207 else
1209 message = g_string_new(NULL);
1210 g_string_sprintf(message,
1211 _("No run action specified for files of this type (%s/%s) - "
1212 "you can set a run action using by choosing `Set Run Action' "
1213 "from the Window menu"),
1214 type->media_type,
1215 type->subtype);
1216 report_error(PROJECT, message->str);
1217 g_string_free(message, TRUE);
1220 break;
1221 default:
1222 report_error("open_item",
1223 "I don't know how to open that");
1224 break;
1227 if (destroy)
1228 gtk_widget_destroy(filer_window->window);
1229 else
1231 if (wink)
1232 collection_wink_item(filer_window->collection,
1233 item_number);
1234 if (close_mini)
1235 minibuffer_hide(filer_window);
1239 static gint pointer_in(GtkWidget *widget,
1240 GdkEventCrossing *event,
1241 FilerWindow *filer_window)
1243 may_rescan(filer_window, TRUE);
1244 return FALSE;
1247 static gint focus_in(GtkWidget *widget,
1248 GdkEventFocus *event,
1249 FilerWindow *filer_window)
1251 window_with_focus = filer_window;
1253 return FALSE;
1256 static gint focus_out(GtkWidget *widget,
1257 GdkEventFocus *event,
1258 FilerWindow *filer_window)
1260 /* TODO: Shade the cursor */
1262 return FALSE;
1265 /* Handle keys that can't be bound with the menu */
1266 static gint key_press_event(GtkWidget *widget,
1267 GdkEventKey *event,
1268 FilerWindow *filer_window)
1270 switch (event->keyval)
1272 case GDK_BackSpace:
1273 change_to_parent(filer_window);
1274 break;
1275 default:
1276 return FALSE;
1279 return TRUE;
1282 static void toolbar_help_clicked(GtkWidget *widget, FilerWindow *filer_window)
1284 filer_opendir(make_path(getenv("APP_DIR"), "Help")->str, PANEL_NO);
1287 static void toolbar_refresh_clicked(GtkWidget *widget,
1288 FilerWindow *filer_window)
1290 GdkEvent *event;
1292 event = gtk_get_current_event();
1293 if (event->type == GDK_BUTTON_RELEASE &&
1294 ((GdkEventButton *) event)->button != 1)
1296 filer_opendir(filer_window->path, PANEL_NO);
1298 else
1300 full_refresh();
1301 filer_update_dir(filer_window, TRUE);
1305 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window)
1307 GdkEvent *event;
1309 event = gtk_get_current_event();
1310 if (event->type == GDK_BUTTON_RELEASE &&
1311 ((GdkEventButton *) event)->button != 1)
1313 filer_opendir(home_dir, PANEL_NO);
1315 else
1316 filer_change_to(filer_window, home_dir, NULL);
1319 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window)
1321 GdkEvent *event;
1323 event = gtk_get_current_event();
1324 if (event->type == GDK_BUTTON_RELEASE &&
1325 ((GdkEventButton *) event)->button != 1)
1327 filer_open_parent(filer_window);
1329 else
1330 change_to_parent(filer_window);
1333 void change_to_parent(FilerWindow *filer_window)
1335 char *copy;
1336 char *slash;
1338 if (filer_window->path[0] == '/' && filer_window->path[1] == '\0')
1339 return; /* Already in the root */
1341 copy = g_strdup(filer_window->path);
1342 slash = strrchr(copy, '/');
1344 if (slash)
1346 *slash = '\0';
1347 filer_change_to(filer_window,
1348 *copy ? copy : "/",
1349 slash + 1);
1351 else
1352 g_warning("No / in directory path!\n");
1354 g_free(copy);
1358 /* Make filer_window display path. When finished, highlight item 'from', or
1359 * the first item if from is NULL. If there is currently no cursor then
1360 * simply wink 'from' (if not NULL).
1362 void filer_change_to(FilerWindow *filer_window, char *path, char *from)
1364 char *from_dup;
1365 char *real_path = pathdup(path);
1367 if (o_unique_filer_windows)
1369 FilerWindow *fw;
1371 fw = find_filer_window(real_path, filer_window);
1372 if (fw)
1373 gtk_widget_destroy(fw->window);
1376 from_dup = from && *from ? g_strdup(from) : NULL;
1378 detach(filer_window);
1379 g_free(filer_window->path);
1380 filer_window->path = real_path;
1382 filer_window->directory = g_fscache_lookup(dir_cache,
1383 filer_window->path);
1384 if (filer_window->directory)
1386 g_free(filer_window->auto_select);
1387 filer_window->had_cursor =
1388 filer_window->collection->cursor_item != -1
1389 || filer_window->had_cursor;
1390 filer_window->auto_select = from_dup;
1392 filer_set_title(filer_window);
1393 collection_set_cursor_item(filer_window->collection, -1);
1394 attach(filer_window);
1396 if (filer_window->mini_type == MINI_PATH)
1397 gtk_idle_add((GtkFunction) minibuffer_show_cb,
1398 filer_window);
1400 else
1402 char *error;
1404 g_free(from_dup);
1405 error = g_strdup_printf(_("Directory '%s' is not accessible"),
1406 path);
1407 delayed_error(PROJECT, error);
1408 g_free(error);
1409 gtk_widget_destroy(filer_window->window);
1413 void filer_open_parent(FilerWindow *filer_window)
1415 char *copy;
1416 char *slash;
1418 if (filer_window->path[0] == '/' && filer_window->path[1] == '\0')
1419 return; /* Already in the root */
1421 copy = g_strdup(filer_window->path);
1422 slash = strrchr(copy, '/');
1424 if (slash)
1426 *slash = '\0';
1427 filer_opendir(*copy ? copy : "/", PANEL_NO);
1429 else
1430 g_warning("No / in directory path!\n");
1432 g_free(copy);
1435 int selected_item_number(Collection *collection)
1437 int i;
1439 g_return_val_if_fail(collection != NULL, -1);
1440 g_return_val_if_fail(IS_COLLECTION(collection), -1);
1441 g_return_val_if_fail(collection->number_selected == 1, -1);
1443 for (i = 0; i < collection->number_of_items; i++)
1444 if (collection->items[i].selected)
1445 return i;
1447 g_warning("selected_item: number_selected is wrong\n");
1449 return -1;
1452 DirItem *selected_item(Collection *collection)
1454 int item;
1456 item = selected_item_number(collection);
1458 if (item > -1)
1459 return (DirItem *) collection->items[item].data;
1460 return NULL;
1463 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
1464 FilerWindow *window)
1466 /* TODO: We can open lots of these - very irritating! */
1467 return get_choice(_("Close panel?"),
1468 _("You have tried to close a panel via the window "
1469 "manager - I usually find that this is accidental... "
1470 "really close?"),
1471 2, _("Remove"), _("Cancel")) != 0;
1474 /* Make the items as narrow as possible */
1475 static void shrink_width(FilerWindow *filer_window)
1477 int i;
1478 Collection *col = filer_window->collection;
1479 int width = MIN_ITEM_WIDTH;
1480 int this_width;
1481 DisplayStyle style = filer_window->display_style;
1482 int text_height;
1484 text_height = item_font->ascent + item_font->descent;
1486 for (i = 0; i < col->number_of_items; i++)
1488 this_width = calc_width(filer_window,
1489 (DirItem *) col->items[i].data);
1490 if (this_width > width)
1491 width = this_width;
1494 collection_set_item_size(filer_window->collection,
1495 width,
1496 style == FULL_INFO ? MAX_ICON_HEIGHT + 4 :
1497 style == SMALL_ICONS ? MAX(text_height, SMALL_ICON_HEIGHT) + 4
1498 : text_height + MAX_ICON_HEIGHT + 8);
1501 void filer_set_sort_fn(FilerWindow *filer_window,
1502 int (*fn)(const void *a, const void *b))
1504 if (filer_window->sort_fn == fn)
1505 return;
1507 filer_window->sort_fn = fn;
1508 last_sort_fn = fn;
1510 collection_qsort(filer_window->collection,
1511 filer_window->sort_fn);
1513 update_options_label();
1516 void filer_style_set(FilerWindow *filer_window, DisplayStyle style)
1518 if (filer_window->display_style == style)
1519 return;
1521 if (filer_window->panel_type)
1522 style = LARGE_ICONS;
1523 else
1524 last_display_style = style;
1526 filer_window->display_style = style;
1528 switch (style)
1530 case SMALL_ICONS:
1531 collection_set_functions(filer_window->collection,
1532 draw_item_small, test_point_small);
1533 break;
1534 case FULL_INFO:
1535 collection_set_functions(filer_window->collection,
1536 draw_item_full_info, test_point_full_info);
1537 break;
1538 default:
1539 collection_set_functions(filer_window->collection,
1540 draw_item_large, test_point_large);
1541 break;
1544 shrink_width(filer_window);
1546 update_options_label();
1549 FilerWindow *filer_opendir(char *path, PanelType panel_type)
1551 GtkWidget *hbox, *scrollbar, *collection;
1552 FilerWindow *filer_window;
1553 GtkTargetEntry target_table[] =
1555 {"text/uri-list", 0, TARGET_URI_LIST},
1556 {"STRING", 0, TARGET_STRING},
1558 char *real_path;
1560 real_path = pathdup(path);
1562 if (o_unique_filer_windows && panel_type == PANEL_NO)
1564 FilerWindow *fw;
1566 fw = find_filer_window(real_path, NULL);
1568 if (fw)
1570 /* TODO: this should bring the window to the front
1571 * at the same coordinates.
1573 gtk_widget_hide(fw->window);
1574 gtk_widget_show(fw->window);
1575 g_free(real_path);
1576 return fw;
1580 filer_window = g_new(FilerWindow, 1);
1581 filer_window->minibuffer = NULL;
1582 filer_window->minibuffer_label = NULL;
1583 filer_window->minibuffer_area = NULL;
1584 filer_window->path = real_path;
1585 filer_window->scanning = FALSE;
1586 filer_window->had_cursor = FALSE;
1587 filer_window->auto_select = NULL;
1588 filer_window->mini_type = MINI_NONE;
1590 filer_window->directory = g_fscache_lookup(dir_cache,
1591 filer_window->path);
1592 if (!filer_window->directory)
1594 char *error;
1596 error = g_strdup_printf(_("Directory '%s' not found."), path);
1597 delayed_error(PROJECT, error);
1598 g_free(error);
1599 g_free(filer_window->path);
1600 g_free(filer_window);
1601 return NULL;
1604 filer_window->show_hidden = last_show_hidden;
1605 filer_window->panel_type = panel_type;
1606 filer_window->temp_item_selected = FALSE;
1607 filer_window->sort_fn = last_sort_fn;
1608 filer_window->flags = (FilerFlags) 0;
1609 filer_window->display_style = UNKNOWN_STYLE;
1611 filer_window->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1612 filer_set_title(filer_window);
1614 collection = collection_new(NULL);
1615 gtk_object_set_data(GTK_OBJECT(collection),
1616 "filer_window", filer_window);
1617 filer_window->collection = COLLECTION(collection);
1619 gtk_widget_add_events(filer_window->window, GDK_ENTER_NOTIFY);
1620 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1621 "enter-notify-event",
1622 GTK_SIGNAL_FUNC(pointer_in), filer_window);
1623 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_in_event",
1624 GTK_SIGNAL_FUNC(focus_in), filer_window);
1625 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_out_event",
1626 GTK_SIGNAL_FUNC(focus_out), filer_window);
1627 gtk_signal_connect(GTK_OBJECT(filer_window->window), "destroy",
1628 filer_window_destroyed, filer_window);
1630 gtk_signal_connect(GTK_OBJECT(filer_window->collection), "open_item",
1631 open_item, filer_window);
1632 gtk_signal_connect(GTK_OBJECT(collection), "show_menu",
1633 show_menu, filer_window);
1634 gtk_signal_connect(GTK_OBJECT(collection), "gain_selection",
1635 gain_selection, filer_window);
1636 gtk_signal_connect(GTK_OBJECT(collection), "lose_selection",
1637 lose_selection, filer_window);
1638 gtk_signal_connect(GTK_OBJECT(collection), "drag_selection",
1639 drag_selection, filer_window);
1640 gtk_signal_connect(GTK_OBJECT(collection), "drag_data_get",
1641 drag_data_get, filer_window);
1642 gtk_signal_connect(GTK_OBJECT(collection), "selection_clear_event",
1643 GTK_SIGNAL_FUNC(collection_lose_selection), NULL);
1644 gtk_signal_connect (GTK_OBJECT(collection), "selection_get",
1645 GTK_SIGNAL_FUNC(selection_get), NULL);
1646 gtk_selection_add_targets(collection, GDK_SELECTION_PRIMARY,
1647 target_table,
1648 sizeof(target_table) / sizeof(*target_table));
1650 filer_style_set(filer_window, last_display_style);
1651 drag_set_dest(filer_window);
1653 if (panel_type)
1655 int swidth, sheight, iwidth, iheight;
1656 GtkWidget *frame, *win = filer_window->window;
1658 gtk_window_set_wmclass(GTK_WINDOW(win), "ROX-Panel",
1659 PROJECT);
1660 collection_set_panel(filer_window->collection, TRUE);
1661 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1662 "delete_event",
1663 GTK_SIGNAL_FUNC(filer_confirm_close),
1664 filer_window);
1666 gdk_window_get_size(GDK_ROOT_PARENT(), &swidth, &sheight);
1667 iwidth = filer_window->collection->item_width;
1668 iheight = filer_window->collection->item_height;
1671 int height = iheight + PANEL_BORDER;
1672 int y = panel_type == PANEL_TOP
1673 ? -PANEL_BORDER
1674 : sheight - height - PANEL_BORDER;
1676 gtk_widget_set_usize(collection, swidth, height);
1677 gtk_widget_set_uposition(win, 0, y);
1680 frame = gtk_frame_new(NULL);
1681 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1682 gtk_container_add(GTK_CONTAINER(frame), collection);
1683 gtk_container_add(GTK_CONTAINER(win), frame);
1685 gtk_widget_show_all(frame);
1686 gtk_widget_realize(win);
1687 if (override_redirect)
1688 gdk_window_set_override_redirect(win->window, TRUE);
1689 make_panel_window(win->window);
1691 else
1693 GtkWidget *vbox;
1694 int col_height = ROW_HEIGHT_LARGE * 3;
1696 gtk_signal_connect(GTK_OBJECT(collection),
1697 "key_press_event",
1698 GTK_SIGNAL_FUNC(key_press_event), filer_window);
1699 gtk_window_set_default_size(GTK_WINDOW(filer_window->window),
1700 filer_window->display_style == LARGE_ICONS ? 400 : 512,
1701 o_toolbar == TOOLBAR_NONE ? col_height:
1702 o_toolbar == TOOLBAR_NORMAL ? col_height + 24 :
1703 col_height + 38);
1705 hbox = gtk_hbox_new(FALSE, 0);
1706 gtk_container_add(GTK_CONTAINER(filer_window->window),
1707 hbox);
1709 vbox = gtk_vbox_new(FALSE, 0);
1710 gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
1712 if (o_toolbar != TOOLBAR_NONE)
1714 GtkWidget *toolbar;
1716 toolbar = create_toolbar(filer_window);
1717 gtk_box_pack_start(GTK_BOX(vbox), toolbar,
1718 FALSE, TRUE, 0);
1719 gtk_widget_show_all(toolbar);
1722 gtk_box_pack_start(GTK_BOX(vbox), collection, TRUE, TRUE, 0);
1724 create_minibuffer(filer_window);
1725 gtk_box_pack_start(GTK_BOX(vbox), filer_window->minibuffer_area,
1726 FALSE, TRUE, 0);
1728 scrollbar = gtk_vscrollbar_new(COLLECTION(collection)->vadj);
1729 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, TRUE, 0);
1730 gtk_accel_group_attach(filer_keys,
1731 GTK_OBJECT(filer_window->window));
1732 gtk_window_set_focus(GTK_WINDOW(filer_window->window),
1733 collection);
1735 gtk_widget_show(hbox);
1736 gtk_widget_show(vbox);
1737 gtk_widget_show(scrollbar);
1738 gtk_widget_show(collection);
1741 number_of_windows++;
1742 gtk_widget_show(filer_window->window);
1743 attach(filer_window);
1745 all_filer_windows = g_list_prepend(all_filer_windows, filer_window);
1747 return filer_window;
1750 static gint clear_scanning_display(FilerWindow *filer_window)
1752 if (filer_exists(filer_window))
1753 filer_set_title(filer_window);
1754 return FALSE;
1757 static void set_scanning_display(FilerWindow *filer_window, gboolean scanning)
1759 if (scanning == filer_window->scanning)
1760 return;
1761 filer_window->scanning = scanning;
1763 if (scanning)
1764 filer_set_title(filer_window);
1765 else
1766 gtk_timeout_add(300, (GtkFunction) clear_scanning_display,
1767 filer_window);
1770 static GtkWidget *create_toolbar(FilerWindow *filer_window)
1772 GtkWidget *frame, *box;
1774 if (o_toolbar == TOOLBAR_GNOME)
1776 frame = gtk_handle_box_new();
1777 box = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL,
1778 GTK_TOOLBAR_BOTH);
1779 gtk_container_set_border_width(GTK_CONTAINER(box), 2);
1780 gtk_toolbar_set_space_style(GTK_TOOLBAR(box),
1781 GTK_TOOLBAR_SPACE_LINE);
1782 gtk_toolbar_set_button_relief(GTK_TOOLBAR(box),
1783 GTK_RELIEF_NONE);
1785 else
1787 frame = gtk_frame_new(NULL);
1788 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1790 box = gtk_hbutton_box_new();
1791 gtk_button_box_set_child_size_default(16, 16);
1792 gtk_hbutton_box_set_spacing_default(0);
1793 gtk_button_box_set_layout(GTK_BUTTON_BOX(box),
1794 GTK_BUTTONBOX_START);
1797 gtk_container_add(GTK_CONTAINER(frame), box);
1799 add_button(box, im_up_icon,
1800 GTK_SIGNAL_FUNC(toolbar_up_clicked),
1801 filer_window,
1802 _("Up"), _("Change to parent directory"));
1803 add_button(box, im_home_icon,
1804 GTK_SIGNAL_FUNC(toolbar_home_clicked),
1805 filer_window,
1806 _("Home"), _("Change to home directory"));
1807 add_button(box, im_refresh_icon,
1808 GTK_SIGNAL_FUNC(toolbar_refresh_clicked),
1809 filer_window,
1810 _("Rescan"), _("Rescan directory contents"));
1811 add_button(box, im_help,
1812 GTK_SIGNAL_FUNC(toolbar_help_clicked),
1813 filer_window,
1814 _("Help"), _("Show ROX-Filer help"));
1816 return frame;
1819 /* This is used to simulate a click when button 3 is used (GtkButton
1820 * normally ignores this). Currently, this button does not pop in -
1821 * this may be fixed in future versions of GTK+.
1823 static gint toolbar_other_button = 0;
1824 static gint toolbar_adjust_pressed(GtkButton *button,
1825 GdkEventButton *event,
1826 FilerWindow *filer_window)
1828 gint b = event->button;
1830 if ((b == 2 || b == 3) && toolbar_other_button == 0)
1832 toolbar_other_button = event->button;
1833 gtk_grab_add(GTK_WIDGET(button));
1834 gtk_button_pressed(button);
1837 return TRUE;
1840 static gint toolbar_adjust_released(GtkButton *button,
1841 GdkEventButton *event,
1842 FilerWindow *filer_window)
1844 if (event->button == toolbar_other_button)
1846 toolbar_other_button = 0;
1847 gtk_grab_remove(GTK_WIDGET(button));
1848 gtk_button_released(button);
1851 return TRUE;
1854 static void add_button(GtkWidget *box, MaskedPixmap *icon,
1855 GtkSignalFunc cb, FilerWindow *filer_window,
1856 char *label, char *tip)
1858 GtkWidget *button, *icon_widget;
1860 icon_widget = gtk_pixmap_new(icon->pixmap, icon->mask);
1862 if (o_toolbar == TOOLBAR_GNOME)
1864 gtk_toolbar_append_element(GTK_TOOLBAR(box),
1865 GTK_TOOLBAR_CHILD_BUTTON,
1866 NULL,
1867 label,
1868 tip, NULL,
1869 icon_widget,
1870 cb, filer_window);
1872 else
1874 button = gtk_button_new();
1875 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
1876 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
1878 gtk_container_add(GTK_CONTAINER(button), icon_widget);
1879 gtk_signal_connect(GTK_OBJECT(button), "button_press_event",
1880 GTK_SIGNAL_FUNC(toolbar_adjust_pressed), filer_window);
1881 gtk_signal_connect(GTK_OBJECT(button), "button_release_event",
1882 GTK_SIGNAL_FUNC(toolbar_adjust_released), filer_window);
1883 gtk_signal_connect(GTK_OBJECT(button), "clicked",
1884 cb, filer_window);
1886 gtk_tooltips_set_tip(tooltips, button, tip, NULL);
1888 gtk_container_add(GTK_CONTAINER(box), button);
1892 /* Build up some option widgets to go in the options dialog, but don't
1893 * fill them in yet.
1895 static GtkWidget *create_options()
1897 GtkWidget *vbox, *menu, *hbox, *slide;
1899 vbox = gtk_vbox_new(FALSE, 0);
1900 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
1902 display_label = gtk_label_new("<>");
1903 gtk_label_set_line_wrap(GTK_LABEL(display_label), TRUE);
1904 gtk_box_pack_start(GTK_BOX(vbox), display_label, FALSE, TRUE, 0);
1906 toggle_sort_nocase =
1907 gtk_check_button_new_with_label(_("Ignore case when sorting"));
1908 gtk_box_pack_start(GTK_BOX(vbox), toggle_sort_nocase, FALSE, TRUE, 0);
1910 toggle_new_window_on_1 =
1911 gtk_check_button_new_with_label(
1912 _("New window on button 1 (RISC OS style)"));
1913 gtk_box_pack_start(GTK_BOX(vbox), toggle_new_window_on_1,
1914 FALSE, TRUE, 0);
1916 toggle_menu_on_2 =
1917 gtk_check_button_new_with_label(
1918 _("Menu on button 2 (RISC OS style)"));
1919 gtk_box_pack_start(GTK_BOX(vbox), toggle_menu_on_2, FALSE, TRUE, 0);
1921 toggle_single_click =
1922 gtk_check_button_new_with_label(_("Single-click nagivation"));
1923 gtk_box_pack_start(GTK_BOX(vbox), toggle_single_click, FALSE, TRUE, 0);
1925 toggle_unique_filer_windows =
1926 gtk_check_button_new_with_label(_("Unique windows"));
1927 gtk_box_pack_start(GTK_BOX(vbox), toggle_unique_filer_windows,
1928 FALSE, TRUE, 0);
1930 hbox = gtk_hbox_new(FALSE, 4);
1931 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
1933 gtk_box_pack_start(GTK_BOX(hbox),
1934 gtk_label_new(_("Toolbar type for new windows")),
1935 FALSE, TRUE, 0);
1936 menu_toolbar = gtk_option_menu_new();
1937 menu = gtk_menu_new();
1938 gtk_menu_append(GTK_MENU(menu),
1939 gtk_menu_item_new_with_label(_("None")));
1940 gtk_menu_append(GTK_MENU(menu),
1941 gtk_menu_item_new_with_label(_("Normal")));
1942 gtk_menu_append(GTK_MENU(menu),
1943 gtk_menu_item_new_with_label(_("GNOME")));
1944 gtk_option_menu_set_menu(GTK_OPTION_MENU(menu_toolbar), menu);
1945 gtk_box_pack_start(GTK_BOX(hbox), menu_toolbar, TRUE, TRUE, 0);
1947 hbox = gtk_hbox_new(FALSE, 4);
1948 gtk_box_pack_start(GTK_BOX(hbox),
1949 gtk_label_new(_("Max Large Icons width")),
1950 TRUE, TRUE, 0);
1951 adj_large_truncate = GTK_ADJUSTMENT(gtk_adjustment_new(0,
1952 MIN_TRUNCATE, MAX_TRUNCATE, 1, 10, 0));
1953 slide = gtk_hscale_new(adj_large_truncate);
1954 gtk_widget_set_usize(slide, MAX_TRUNCATE, 24);
1955 gtk_scale_set_draw_value(GTK_SCALE(slide), FALSE);
1956 gtk_box_pack_start(GTK_BOX(hbox), slide, FALSE, TRUE, 0);
1957 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
1959 hbox = gtk_hbox_new(FALSE, 4);
1960 gtk_box_pack_start(GTK_BOX(hbox),
1961 gtk_label_new(_("Max Small Icons width")),
1962 TRUE, TRUE, 0);
1963 adj_small_truncate = GTK_ADJUSTMENT(gtk_adjustment_new(0,
1964 MIN_TRUNCATE, MAX_TRUNCATE, 1, 10, 0));
1965 slide = gtk_hscale_new(adj_small_truncate);
1966 gtk_widget_set_usize(slide, MAX_TRUNCATE, 24);
1967 gtk_scale_set_draw_value(GTK_SCALE(slide), FALSE);
1968 gtk_box_pack_start(GTK_BOX(hbox), slide, FALSE, TRUE, 0);
1969 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
1971 return vbox;
1974 static void update_options_label(void)
1976 guchar *str;
1978 str = g_strdup_printf(_("The last used display style (%s) and sort "
1979 "function (Sort By %s) will be saved if you click on "
1980 "Save."), style_to_name(), sort_fn_to_name());
1981 gtk_label_set_text(GTK_LABEL(display_label), str);
1982 g_free(str);
1985 /* Reflect current state by changing the widgets in the options box */
1986 static void update_options()
1988 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_sort_nocase),
1989 o_sort_nocase);
1990 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_new_window_on_1),
1991 o_new_window_on_1);
1992 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_menu_on_2),
1993 collection_menu_button == 2 ? 1 : 0);
1994 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_single_click),
1995 o_single_click);
1996 gtk_toggle_button_set_active(
1997 GTK_TOGGLE_BUTTON(toggle_unique_filer_windows),
1998 o_unique_filer_windows);
1999 gtk_option_menu_set_history(GTK_OPTION_MENU(menu_toolbar), o_toolbar);
2001 gtk_adjustment_set_value(adj_small_truncate, o_small_truncate);
2002 gtk_adjustment_set_value(adj_large_truncate, o_large_truncate);
2004 update_options_label();
2007 /* Set current values by reading the states of the widgets in the options box */
2008 static void set_options()
2010 GtkWidget *item, *menu;
2011 GList *list;
2012 gboolean old_case = o_sort_nocase;
2013 GList *next = all_filer_windows;
2015 o_sort_nocase = gtk_toggle_button_get_active(
2016 GTK_TOGGLE_BUTTON(toggle_sort_nocase));
2018 o_new_window_on_1 = gtk_toggle_button_get_active(
2019 GTK_TOGGLE_BUTTON(toggle_new_window_on_1));
2021 collection_menu_button = gtk_toggle_button_get_active(
2022 GTK_TOGGLE_BUTTON(toggle_menu_on_2)) ? 2 : 3;
2024 o_single_click = gtk_toggle_button_get_active(
2025 GTK_TOGGLE_BUTTON(toggle_single_click));
2027 o_unique_filer_windows = gtk_toggle_button_get_active(
2028 GTK_TOGGLE_BUTTON(toggle_unique_filer_windows));
2030 collection_single_click = o_single_click ? TRUE : FALSE;
2032 menu = gtk_option_menu_get_menu(GTK_OPTION_MENU(menu_toolbar));
2033 item = gtk_menu_get_active(GTK_MENU(menu));
2034 list = gtk_container_children(GTK_CONTAINER(menu));
2035 o_toolbar = (ToolbarType) g_list_index(list, item);
2036 g_list_free(list);
2038 o_small_truncate = adj_small_truncate->value;
2039 o_large_truncate = adj_large_truncate->value;
2041 while (next)
2043 FilerWindow *filer_window = (FilerWindow *) next->data;
2045 if (o_sort_nocase != old_case)
2047 collection_qsort(filer_window->collection,
2048 filer_window->sort_fn);
2050 shrink_width(filer_window);
2052 next = next->next;
2056 static guchar *style_to_name(void)
2058 return last_display_style == LARGE_ICONS ? _("Large Icons") :
2059 last_display_style == SMALL_ICONS ? _("Small Icons") :
2060 _("Full Info");
2063 static guchar *sort_fn_to_name(void)
2065 return last_sort_fn == sort_by_name ? _("Name") :
2066 last_sort_fn == sort_by_type ? _("Type") :
2067 last_sort_fn == sort_by_date ? _("Date") :
2068 _("Size");
2071 static void save_options()
2073 guchar *tmp;
2075 option_write("filer_sort_nocase", o_sort_nocase ? "1" : "0");
2076 option_write("filer_new_window_on_1", o_new_window_on_1 ? "1" : "0");
2077 option_write("filer_menu_on_2",
2078 collection_menu_button == 2 ? "1" : "0");
2079 option_write("filer_single_click", o_single_click ? "1" : "0");
2080 option_write("filer_unique_windows",
2081 o_unique_filer_windows ? "1" : "0");
2082 option_write("filer_display_style",
2083 last_display_style == LARGE_ICONS ? "Large Icons" :
2084 last_display_style == SMALL_ICONS ? "Small Icons" :
2085 "Full Info");
2086 option_write("filer_sort_by",
2087 last_sort_fn == sort_by_name ? "Name" :
2088 last_sort_fn == sort_by_type ? "Type" :
2089 last_sort_fn == sort_by_date ? "Date" :
2090 "Size");
2091 option_write("filer_toolbar", o_toolbar == TOOLBAR_NONE ? "None" :
2092 o_toolbar == TOOLBAR_NORMAL ? "Normal" :
2093 o_toolbar == TOOLBAR_GNOME ? "GNOME" :
2094 "Unknown");
2096 tmp = g_strdup_printf("%d, %d", o_large_truncate, o_small_truncate);
2097 option_write("filer_truncate", tmp);
2098 g_free(tmp);
2101 static char *filer_sort_nocase(char *data)
2103 o_sort_nocase = atoi(data) != 0;
2104 return NULL;
2107 static char *filer_new_window_on_1(char *data)
2109 o_new_window_on_1 = atoi(data) != 0;
2110 return NULL;
2113 static char *filer_menu_on_2(char *data)
2115 collection_menu_button = atoi(data) != 0 ? 2 : 3;
2116 return NULL;
2119 static char *filer_single_click(char *data)
2121 o_single_click = atoi(data) != 0;
2122 collection_single_click = o_single_click ? TRUE : FALSE;
2123 return NULL;
2126 static char *filer_unique_windows(char *data)
2128 o_unique_filer_windows = atoi(data) != 0;
2129 return NULL;
2132 static char *filer_display_style(char *data)
2134 if (g_strcasecmp(data, "Large Icons") == 0)
2135 last_display_style = LARGE_ICONS;
2136 else if (g_strcasecmp(data, "Small Icons") == 0)
2137 last_display_style = SMALL_ICONS;
2138 else if (g_strcasecmp(data, "Full Info") == 0)
2139 last_display_style = FULL_INFO;
2140 else
2141 return _("Unknown display style");
2143 return NULL;
2146 static char *filer_sort_by(char *data)
2148 if (g_strcasecmp(data, "Name") == 0)
2149 last_sort_fn = sort_by_name;
2150 else if (g_strcasecmp(data, "Type") == 0)
2151 last_sort_fn = sort_by_type;
2152 else if (g_strcasecmp(data, "Date") == 0)
2153 last_sort_fn = sort_by_date;
2154 else if (g_strcasecmp(data, "Size") == 0)
2155 last_sort_fn = sort_by_size;
2156 else
2157 return _("Unknown sort type");
2159 return NULL;
2162 static char *filer_truncate(char *data)
2164 guchar *comma;
2166 comma = strchr(data, ',');
2167 if (!comma)
2168 return "Missing , in filer_truncate";
2170 o_large_truncate = CLAMP(atoi(data), MIN_TRUNCATE, MAX_TRUNCATE);
2171 o_small_truncate = CLAMP(atoi(comma + 1), MIN_TRUNCATE, MAX_TRUNCATE);
2173 return NULL;
2176 static char *filer_toolbar(char *data)
2178 if (g_strcasecmp(data, "None") == 0)
2179 o_toolbar = TOOLBAR_NONE;
2180 else if (g_strcasecmp(data, "Normal") == 0)
2181 o_toolbar = TOOLBAR_NORMAL;
2182 else if (g_strcasecmp(data, "GNOME") == 0)
2183 o_toolbar = TOOLBAR_GNOME;
2184 else
2185 return _("Unknown toolbar type");
2187 return NULL;
2190 /* Note that filer_window may not exist after this call. */
2191 void filer_update_dir(FilerWindow *filer_window, gboolean warning)
2193 if (may_rescan(filer_window, warning))
2194 dir_update(filer_window->directory, filer_window->path);
2197 void filer_set_hidden(FilerWindow *filer_window, gboolean hidden)
2199 Directory *dir = filer_window->directory;
2201 if (filer_window->show_hidden == hidden)
2202 return;
2204 filer_window->show_hidden = hidden;
2205 last_show_hidden = hidden;
2207 g_fscache_data_ref(dir_cache, dir);
2208 detach(filer_window);
2209 filer_window->directory = dir;
2210 attach(filer_window);
2213 /* Refresh the various caches even if we don't think we need to */
2214 void full_refresh(void)
2216 mount_update(TRUE);
2219 /* See whether a filer window with a given path already exists
2220 * and is different from diff.
2222 static FilerWindow *find_filer_window(char *path, FilerWindow *diff)
2224 GList *next = all_filer_windows;
2226 while (next)
2228 FilerWindow *filer_window = (FilerWindow *) next->data;
2230 if (filer_window->panel_type == PANEL_NO &&
2231 filer_window != diff &&
2232 strcmp(path, filer_window->path) == 0)
2234 return filer_window;
2237 next = next->next;
2240 return NULL;
2243 /* This path has been mounted/umounted/deleted some files - update all dirs */
2244 void filer_check_mounted(char *path)
2246 GList *next = all_filer_windows;
2247 int len;
2249 len = strlen(path);
2251 while (next)
2253 FilerWindow *filer_window = (FilerWindow *) next->data;
2255 next = next->next;
2257 if (strncmp(path, filer_window->path, len) == 0)
2259 char s = filer_window->path[len];
2261 if (s == '/' || s == '\0')
2262 filer_update_dir(filer_window, FALSE);
2267 /* Like minibuffer_show(), except that:
2268 * - It returns FALSE (to be used from an idle callback)
2269 * - It checks that the filer window still exists.
2271 static gboolean minibuffer_show_cb(FilerWindow *filer_window)
2273 if (filer_exists(filer_window))
2274 minibuffer_show(filer_window, MINI_PATH);
2275 return FALSE;
2278 gboolean filer_exists(FilerWindow *filer_window)
2280 GList *next;
2282 for (next = all_filer_windows; next; next = next->next)
2284 FilerWindow *fw = (FilerWindow *) next->data;
2286 if (fw == filer_window)
2287 return TRUE;
2290 return FALSE;
2293 /* Highlight (wink or cursor) this item in the filer window. If the item
2294 * isn't already there but we're scanning then highlight it if it
2295 * appears later.
2297 void filer_set_autoselect(FilerWindow *filer_window, guchar *leaf)
2299 Collection *col = filer_window->collection;
2300 int i;
2302 g_free(filer_window->auto_select);
2303 filer_window->auto_select = NULL;
2305 for (i = 0; i < col->number_of_items; i++)
2307 DirItem *item = (DirItem *) col->items[i].data;
2309 if (strcmp(item->leafname, leaf) == 0)
2311 if (col->cursor_item != -1)
2312 collection_set_cursor_item(col, i);
2313 else
2314 collection_wink_item(col, i);
2315 return;
2319 filer_window->auto_select = g_strdup(leaf);
2322 static void filer_set_title(FilerWindow *filer_window)
2324 if (filer_window->scanning)
2326 guchar *title;
2328 title = g_strdup_printf(_("%s (Scanning)"), filer_window->path);
2329 gtk_window_set_title(GTK_WINDOW(filer_window->window),
2330 title);
2331 g_free(title);
2333 else
2334 gtk_window_set_title(GTK_WINDOW(filer_window->window),
2335 filer_window->path);