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)
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
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 */
30 #include <sys/param.h>
37 #include <gdk/gdkkeysyms.h>
38 #include "collection.h"
42 #include "gui_support.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
59 # define SMALL_ICON_WIDTH 24
61 # define SMALL_ICON_WIDTH 48
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
;
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"),
111 /* The values correspond to the menu indexes in the option widget */
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
,
163 static void draw_string(GtkWidget
*widget
,
171 static void draw_item_large(GtkWidget
*widget
,
172 CollectionItem
*item
,
174 static void draw_item_small(GtkWidget
*widget
,
175 CollectionItem
*item
,
177 static void draw_item_full_info(GtkWidget
*widget
,
178 CollectionItem
*colitem
,
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
,
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
,
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
;
213 static GdkCursor
*busy_cursor
= NULL
;
214 static GtkTooltips
*tooltips
= NULL
;
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
;
244 if (strcmp(leafname
, r
[i
]->leafname
) == 0)
251 static void update_item(FilerWindow
*filer_window
, DirItem
*item
)
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'))
263 i
= collection_find_item(filer_window
->collection
, item
, dir_item_cmp
);
266 collection_draw_item(filer_window
->collection
, i
, TRUE
);
268 g_warning("Failed to find '%s'\n", item
->leafname
);
271 static void update_display(Directory
*dir
,
274 FilerWindow
*filer_window
)
278 int cursor
= filer_window
->collection
->cursor_item
;
280 Collection
*collection
= filer_window
->collection
;
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
)
297 if (strcmp(as
, item
->leafname
) != 0)
300 cursor
= collection
->number_of_items
- 1;
301 if (filer_window
->had_cursor
)
303 collection_set_cursor_item(collection
,
305 filer_window
->mini_cursor_base
= cursor
;
308 collection_wink_item(collection
,
312 if (old_num
!= collection
->number_of_items
)
313 collection_qsort(filer_window
->collection
,
314 filer_window
->sort_fn
);
317 collection_delete_if(filer_window
->collection
,
322 set_scanning_display(filer_window
, TRUE
);
325 if (filer_window
->window
->window
)
326 gdk_window_set_cursor(
327 filer_window
->window
->window
,
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
);
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
);
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
,
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)
392 static int calc_width(FilerWindow
*filer_window
, DirItem
*item
)
394 int pix_width
= item
->image
->width
;
397 switch (filer_window
->display_style
)
400 return MAX_ICON_WIDTH
+ 12 +
401 MAX(item
->details_width
, item
->name_width
);
403 w
= MIN(item
->name_width
, o_small_truncate
);
404 return SMALL_ICON_WIDTH
+ 12 + w
;
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
;
417 if (leafname
[0] == '.')
419 if (filer_window
->show_hidden
== FALSE
|| leafname
[1] == '\0'
420 || (leafname
[1] == '.' && leafname
[2] == '\0'))
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
,
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;
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
;
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
);
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
;
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
);
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
;
504 static void draw_small_icon(GtkWidget
*widget
,
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
;
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 */
536 gdk_gc_set_function(gc
, GDK_INVERT
);
537 gdk_draw_rectangle(widget
->window
,
539 TRUE
, image_x
, area
->y
+ image_y
,
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 */
553 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
555 MaskedPixmap
*mp
= item
->flags
& ITEM_FLAG_MOUNTED
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
,
565 0, 0, /* Source x,y */
566 image_x
, area
->y
+ 8, /* Dest x,y */
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
,
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);
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
,
593 0, 0, /* Source x,y */
594 image_x
, area
->y
+ image_y
, /* Dest x,y */
599 gdk_gc_set_function(gc
, GDK_INVERT
);
600 gdk_draw_rectangle(widget
->window
,
602 TRUE
, image_x
, area
->y
+ image_y
,
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 */
616 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
618 MaskedPixmap
*mp
= item
->flags
& ITEM_FLAG_MOUNTED
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 */
630 gdk_gc_set_clip_mask(gc
, NULL
);
631 gdk_gc_set_clip_origin(gc
, 0, 0);
634 static void draw_string(GtkWidget
*widget
,
643 int text_height
= font
->ascent
+ font
->descent
;
646 ? widget
->style
->fg_gc
[GTK_STATE_SELECTED
]
647 : widget
->style
->fg_gc
[GTK_STATE_NORMAL
];
650 gtk_paint_flat_box(widget
->style
, widget
->window
,
651 GTK_STATE_SELECTED
, GTK_SHADOW_NONE
,
652 NULL
, widget
, "text",
654 MIN(width
, area_width
),
657 if (width
> area_width
)
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
,
671 string
, strlen(string
));
673 if (width
> area_width
)
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
689 char *details(DirItem
*item
)
691 mode_t m
= item
->mode
;
692 static guchar
*buf
= NULL
;
697 if (item
->lstat_errno
)
698 buf
= g_strdup_printf(_("lstat(2) failed: %s"),
699 g_strerror(item
->lstat_errno
));
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
));
717 static void draw_item_full_info(GtkWidget
*widget
,
718 CollectionItem
*colitem
,
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
);
740 low_text_y
- item_font
->descent
- fixed_font
->ascent
,
752 if (item
->lstat_errno
)
755 /* Underline the effective permissions */
756 gdk_draw_rectangle(widget
->window
,
757 selected
? widget
->style
->white_gc
758 : widget
->style
->black_gc
,
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
,
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
);
789 area
->width
- (text_x
- area
->x
),
793 static void draw_item_large(GtkWidget
*widget
,
794 CollectionItem
*colitem
,
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
)
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
)
828 g_return_val_if_fail(filer_window
!= NULL
, FALSE
);
830 /* We do a fresh lookup (rather than update) because the inode may
833 dir
= g_fscache_lookup(dir_cache
, filer_window
->path
);
837 delayed_error(PROJECT
, _("Directory missing/deleted"));
838 gtk_widget_destroy(filer_window
->window
);
841 if (dir
== filer_window
->directory
)
842 g_fscache_data_unref(dir_cache
, dir
);
845 detach(filer_window
);
846 filer_window
->directory
= dir
;
847 attach(filer_window
);
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
);
868 /* Someone wants us to send them the selection */
869 static void selection_get(GtkWidget
*widget
,
870 GtkSelectionData
*selection_data
,
875 GString
*reply
, *header
;
876 FilerWindow
*filer_window
;
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
);
888 g_string_sprintf(header
, " %s",
889 make_path(filer_window
->path
, "")->str
);
891 case TARGET_URI_LIST
:
892 g_string_sprintf(header
, " file://%s%s",
894 make_path(filer_window
->path
, "")->str
);
898 collection
= filer_window
->collection
;
899 for (i
= 0; i
< collection
->number_of_items
; i
++)
901 if (collection
->items
[i
].selected
)
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
,
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
,
937 static void gain_selection(Collection
*collection
,
941 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
943 if (gtk_selection_owner_set(GTK_WIDGET(collection
),
944 GDK_SELECTION_PRIMARY
,
947 window_with_selection
= filer_window
;
950 collection_clear_selection(filer_window
->collection
);
953 int sort_by_name(const void *item1
, const void *item2
)
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
;
968 int diff
= i1
->base_type
- i2
->base_type
;
971 diff
= (i1
->flags
& ITEM_FLAG_APPDIR
)
972 - (i2
->flags
& ITEM_FLAG_APPDIR
);
974 return diff
> 0 ? 1 : -1;
981 diff
= strcmp(m1
->media_type
, m2
->media_type
);
983 diff
= strcmp(m1
->subtype
, m2
->subtype
);
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
,
1020 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
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
;
1050 flags
|= OPEN_SAME_WINDOW
;
1051 if (kevent
->state
& GDK_SHIFT_MASK
)
1052 flags
|= OPEN_SHIFT
;
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
)
1071 path
= make_path(filer_window
->path
, path
)->str
;
1073 real
= pathdup(path
);
1074 slash
= strrchr(real
, '/');
1078 delayed_error(PROJECT
,
1079 _("Broken symlink (or you don't have permission "
1091 if (filer_window
->panel_type
|| !same_window
)
1095 new = filer_opendir(new_dir
, PANEL_NO
);
1096 filer_set_autoselect(new, slash
+ 1);
1099 filer_change_to(filer_window
, new_dir
, slash
+ 1);
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
;
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
);
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];
1134 got
= readlink(make_path(filer_window
->path
,
1135 item
->leafname
)->str
,
1138 delayed_error(PROJECT
, g_strerror(errno
));
1141 g_return_if_fail(got
<= MAXPATHLEN
);
1144 follow_symlink(filer_window
, path
,
1145 flags
& OPEN_SAME_WINDOW
);
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
);
1162 if (item
->flags
& ITEM_FLAG_MOUNT_POINT
&& shift
)
1164 action_mount(filer_window
, item
);
1165 if (item
->flags
& ITEM_FLAG_MOUNTED
)
1172 filer_change_to(filer_window
, full_path
, NULL
);
1176 filer_opendir(full_path
, PANEL_NO
);
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
))
1191 report_error(PROJECT
,
1192 _("Failed to fork() child"));
1197 MIME_type
*type
= shift
? &text_plain
1200 g_return_if_fail(type
!= NULL
);
1202 if (type_open(full_path
, type
))
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"),
1216 report_error(PROJECT
, message
->str
);
1217 g_string_free(message
, TRUE
);
1222 report_error("open_item",
1223 "I don't know how to open that");
1228 gtk_widget_destroy(filer_window
->window
);
1232 collection_wink_item(filer_window
->collection
,
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
);
1247 static gint
focus_in(GtkWidget
*widget
,
1248 GdkEventFocus
*event
,
1249 FilerWindow
*filer_window
)
1251 window_with_focus
= filer_window
;
1256 static gint
focus_out(GtkWidget
*widget
,
1257 GdkEventFocus
*event
,
1258 FilerWindow
*filer_window
)
1260 /* TODO: Shade the cursor */
1265 /* Handle keys that can't be bound with the menu */
1266 static gint
key_press_event(GtkWidget
*widget
,
1268 FilerWindow
*filer_window
)
1270 switch (event
->keyval
)
1273 change_to_parent(filer_window
);
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
)
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
);
1301 filer_update_dir(filer_window
, TRUE
);
1305 static void toolbar_home_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
)
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
);
1316 filer_change_to(filer_window
, home_dir
, NULL
);
1319 static void toolbar_up_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
)
1323 event
= gtk_get_current_event();
1324 if (event
->type
== GDK_BUTTON_RELEASE
&&
1325 ((GdkEventButton
*) event
)->button
!= 1)
1327 filer_open_parent(filer_window
);
1330 change_to_parent(filer_window
);
1333 void change_to_parent(FilerWindow
*filer_window
)
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
, '/');
1347 filer_change_to(filer_window
,
1352 g_warning("No / in directory path!\n");
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
)
1365 char *real_path
= pathdup(path
);
1367 if (o_unique_filer_windows
)
1371 fw
= find_filer_window(real_path
, filer_window
);
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
,
1405 error
= g_strdup_printf(_("Directory '%s' is not accessible"),
1407 delayed_error(PROJECT
, error
);
1409 gtk_widget_destroy(filer_window
->window
);
1413 void filer_open_parent(FilerWindow
*filer_window
)
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
, '/');
1427 filer_opendir(*copy
? copy
: "/", PANEL_NO
);
1430 g_warning("No / in directory path!\n");
1435 int selected_item_number(Collection
*collection
)
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
)
1447 g_warning("selected_item: number_selected is wrong\n");
1452 DirItem
*selected_item(Collection
*collection
)
1456 item
= selected_item_number(collection
);
1459 return (DirItem
*) collection
->items
[item
].data
;
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... "
1471 2, _("Remove"), _("Cancel")) != 0;
1474 /* Make the items as narrow as possible */
1475 static void shrink_width(FilerWindow
*filer_window
)
1478 Collection
*col
= filer_window
->collection
;
1479 int width
= MIN_ITEM_WIDTH
;
1481 DisplayStyle style
= filer_window
->display_style
;
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
)
1494 collection_set_item_size(filer_window
->collection
,
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
)
1507 filer_window
->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
)
1521 if (filer_window
->panel_type
)
1522 style
= LARGE_ICONS
;
1524 last_display_style
= style
;
1526 filer_window
->display_style
= style
;
1531 collection_set_functions(filer_window
->collection
,
1532 draw_item_small
, test_point_small
);
1535 collection_set_functions(filer_window
->collection
,
1536 draw_item_full_info
, test_point_full_info
);
1539 collection_set_functions(filer_window
->collection
,
1540 draw_item_large
, test_point_large
);
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
},
1560 real_path
= pathdup(path
);
1562 if (o_unique_filer_windows
&& panel_type
== PANEL_NO
)
1566 fw
= find_filer_window(real_path
, NULL
);
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
);
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
)
1596 error
= g_strdup_printf(_("Directory '%s' not found."), path
);
1597 delayed_error(PROJECT
, error
);
1599 g_free(filer_window
->path
);
1600 g_free(filer_window
);
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
,
1648 sizeof(target_table
) / sizeof(*target_table
));
1650 filer_style_set(filer_window
, last_display_style
);
1651 drag_set_dest(filer_window
);
1655 int swidth
, sheight
, iwidth
, iheight
;
1656 GtkWidget
*frame
, *win
= filer_window
->window
;
1658 gtk_window_set_wmclass(GTK_WINDOW(win
), "ROX-Panel",
1660 collection_set_panel(filer_window
->collection
, TRUE
);
1661 gtk_signal_connect(GTK_OBJECT(filer_window
->window
),
1663 GTK_SIGNAL_FUNC(filer_confirm_close
),
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
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
);
1694 int col_height
= ROW_HEIGHT_LARGE
* 3;
1696 gtk_signal_connect(GTK_OBJECT(collection
),
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 :
1705 hbox
= gtk_hbox_new(FALSE
, 0);
1706 gtk_container_add(GTK_CONTAINER(filer_window
->window
),
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
)
1716 toolbar
= create_toolbar(filer_window
);
1717 gtk_box_pack_start(GTK_BOX(vbox
), toolbar
,
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
,
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
),
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
);
1757 static void set_scanning_display(FilerWindow
*filer_window
, gboolean scanning
)
1759 if (scanning
== filer_window
->scanning
)
1761 filer_window
->scanning
= scanning
;
1764 filer_set_title(filer_window
);
1766 gtk_timeout_add(300, (GtkFunction
) clear_scanning_display
,
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
,
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
),
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
),
1802 _("Up"), _("Change to parent directory"));
1803 add_button(box
, im_home_icon
,
1804 GTK_SIGNAL_FUNC(toolbar_home_clicked
),
1806 _("Home"), _("Change to home directory"));
1807 add_button(box
, im_refresh_icon
,
1808 GTK_SIGNAL_FUNC(toolbar_refresh_clicked
),
1810 _("Rescan"), _("Rescan directory contents"));
1811 add_button(box
, im_help
,
1812 GTK_SIGNAL_FUNC(toolbar_help_clicked
),
1814 _("Help"), _("Show ROX-Filer help"));
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
);
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
);
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
,
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",
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
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
,
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
,
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")),
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")),
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")),
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);
1974 static void update_options_label(void)
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
);
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
),
1990 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_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
),
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
;
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
);
2038 o_small_truncate
= adj_small_truncate
->value
;
2039 o_large_truncate
= adj_large_truncate
->value
;
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
);
2056 static guchar
*style_to_name(void)
2058 return last_display_style
== LARGE_ICONS
? _("Large Icons") :
2059 last_display_style
== SMALL_ICONS
? _("Small Icons") :
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") :
2071 static void save_options()
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" :
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" :
2091 option_write("filer_toolbar", o_toolbar
== TOOLBAR_NONE
? "None" :
2092 o_toolbar
== TOOLBAR_NORMAL
? "Normal" :
2093 o_toolbar
== TOOLBAR_GNOME
? "GNOME" :
2096 tmp
= g_strdup_printf("%d, %d", o_large_truncate
, o_small_truncate
);
2097 option_write("filer_truncate", tmp
);
2101 static char *filer_sort_nocase(char *data
)
2103 o_sort_nocase
= atoi(data
) != 0;
2107 static char *filer_new_window_on_1(char *data
)
2109 o_new_window_on_1
= atoi(data
) != 0;
2113 static char *filer_menu_on_2(char *data
)
2115 collection_menu_button
= atoi(data
) != 0 ? 2 : 3;
2119 static char *filer_single_click(char *data
)
2121 o_single_click
= atoi(data
) != 0;
2122 collection_single_click
= o_single_click
? TRUE
: FALSE
;
2126 static char *filer_unique_windows(char *data
)
2128 o_unique_filer_windows
= atoi(data
) != 0;
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
;
2141 return _("Unknown display style");
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
;
2157 return _("Unknown sort type");
2162 static char *filer_truncate(char *data
)
2166 comma
= strchr(data
, ',');
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
);
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
;
2185 return _("Unknown toolbar type");
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
)
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)
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
;
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
;
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
;
2253 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
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
);
2278 gboolean
filer_exists(FilerWindow
*filer_window
)
2282 for (next
= all_filer_windows
; next
; next
= next
->next
)
2284 FilerWindow
*fw
= (FilerWindow
*) next
->data
;
2286 if (fw
== filer_window
)
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
2297 void filer_set_autoselect(FilerWindow
*filer_window
, guchar
*leaf
)
2299 Collection
*col
= filer_window
->collection
;
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
);
2314 collection_wink_item(col
, i
);
2319 filer_window
->auto_select
= g_strdup(leaf
);
2322 static void filer_set_title(FilerWindow
*filer_window
)
2324 if (filer_window
->scanning
)
2328 title
= g_strdup_printf(_("%s (Scanning)"), filer_window
->path
);
2329 gtk_window_set_title(GTK_WINDOW(filer_window
->window
),
2334 gtk_window_set_title(GTK_WINDOW(filer_window
->window
),
2335 filer_window
->path
);