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 extern int collection_menu_button
;
69 extern gboolean collection_single_click
;
71 FilerWindow
*window_with_focus
= NULL
;
72 GList
*all_filer_windows
= NULL
;
74 static DisplayStyle last_display_style
= LARGE_ICONS
;
75 static gboolean last_show_hidden
= FALSE
;
76 static int (*last_sort_fn
)(const void *a
, const void *b
) = sort_by_type
;
78 static FilerWindow
*window_with_selection
= NULL
;
81 static guchar
*style_to_name(void);
82 static guchar
*sort_fn_to_name(void);
83 static void update_options_label(void);
85 static GtkWidget
*create_options();
86 static void update_options();
87 static void set_options();
88 static void save_options();
89 static char *filer_sort_nocase(char *data
);
90 static char *filer_single_click(char *data
);
91 static char *filer_unique_windows(char *data
);
92 static char *filer_menu_on_2(char *data
);
93 static char *filer_new_window_on_1(char *data
);
94 static char *filer_toolbar(char *data
);
95 static char *filer_display_style(char *data
);
96 static char *filer_sort_by(char *data
);
98 static OptionsSection options
=
100 N_("Filer window options"),
107 /* The values correspond to the menu indexes in the option widget */
113 static ToolbarType o_toolbar
= TOOLBAR_NORMAL
;
114 static GtkWidget
*menu_toolbar
;
116 static GtkWidget
*display_label
;
118 static gboolean o_sort_nocase
= FALSE
;
119 static gboolean o_single_click
= FALSE
;
120 static gboolean o_new_window_on_1
= FALSE
; /* Button 1 => New window */
121 gboolean o_unique_filer_windows
= FALSE
;
122 static GtkWidget
*toggle_sort_nocase
;
123 static GtkWidget
*toggle_single_click
;
124 static GtkWidget
*toggle_new_window_on_1
;
125 static GtkWidget
*toggle_menu_on_2
;
126 static GtkWidget
*toggle_unique_filer_windows
;
128 /* Static prototypes */
129 static void attach(FilerWindow
*filer_window
);
130 static void detach(FilerWindow
*filer_window
);
131 static void filer_window_destroyed(GtkWidget
*widget
,
132 FilerWindow
*filer_window
);
133 static void show_menu(Collection
*collection
, GdkEventButton
*event
,
134 int number_selected
, gpointer user_data
);
135 static gint
focus_in(GtkWidget
*widget
,
136 GdkEventFocus
*event
,
137 FilerWindow
*filer_window
);
138 static gint
focus_out(GtkWidget
*widget
,
139 GdkEventFocus
*event
,
140 FilerWindow
*filer_window
);
141 static void add_item(FilerWindow
*filer_window
, DirItem
*item
);
142 static void toolbar_up_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
);
143 static void toolbar_home_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
);
144 static void add_button(GtkWidget
*box
, int pixmap
,
145 GtkSignalFunc cb
, FilerWindow
*filer_window
,
146 char *label
, char *tip
);
147 static GtkWidget
*create_toolbar(FilerWindow
*filer_window
);
148 static int filer_confirm_close(GtkWidget
*widget
, GdkEvent
*event
,
149 FilerWindow
*window
);
150 static int calc_width(FilerWindow
*filer_window
, DirItem
*item
);
151 static void draw_large_icon(GtkWidget
*widget
,
155 static void draw_string(GtkWidget
*widget
,
162 static void draw_item_large(GtkWidget
*widget
,
163 CollectionItem
*item
,
165 static void draw_item_small(GtkWidget
*widget
,
166 CollectionItem
*item
,
168 static void draw_item_full_info(GtkWidget
*widget
,
169 CollectionItem
*colitem
,
171 static gboolean
test_point_large(Collection
*collection
,
172 int point_x
, int point_y
,
173 CollectionItem
*item
,
174 int width
, int height
);
175 static gboolean
test_point_small(Collection
*collection
,
176 int point_x
, int point_y
,
177 CollectionItem
*item
,
178 int width
, int height
);
179 static gboolean
test_point_full_info(Collection
*collection
,
180 int point_x
, int point_y
,
181 CollectionItem
*item
,
182 int width
, int height
);
183 static void update_display(Directory
*dir
,
186 FilerWindow
*filer_window
);
187 static void set_scanning_display(FilerWindow
*filer_window
, gboolean scanning
);
188 static void shrink_width(FilerWindow
*filer_window
);
189 static gboolean
may_rescan(FilerWindow
*filer_window
, gboolean warning
);
190 static void open_item(Collection
*collection
,
191 gpointer item_data
, int item_number
,
193 static gboolean
minibuffer_show_cb(FilerWindow
*filer_window
);
194 static FilerWindow
*find_filer_window(char *path
, FilerWindow
*diff
);
195 static void filer_set_title(FilerWindow
*filer_window
);
197 static GdkAtom xa_string
;
204 static GdkCursor
*busy_cursor
= NULL
;
205 static GtkTooltips
*tooltips
= NULL
;
209 xa_string
= gdk_atom_intern("STRING", FALSE
);
211 options_sections
= g_slist_prepend(options_sections
, &options
);
212 option_register("filer_sort_nocase", filer_sort_nocase
);
213 option_register("filer_new_window_on_1", filer_new_window_on_1
);
214 option_register("filer_menu_on_2", filer_menu_on_2
);
215 option_register("filer_single_click", filer_single_click
);
216 option_register("filer_unique_windows", filer_unique_windows
);
217 option_register("filer_toolbar", filer_toolbar
);
218 option_register("filer_display_style", filer_display_style
);
219 option_register("filer_sort_by", filer_sort_by
);
221 busy_cursor
= gdk_cursor_new(GDK_WATCH
);
223 tooltips
= gtk_tooltips_new();
226 static gboolean
if_deleted(gpointer item
, gpointer removed
)
228 int i
= ((GPtrArray
*) removed
)->len
;
229 DirItem
**r
= (DirItem
**) ((GPtrArray
*) removed
)->pdata
;
230 char *leafname
= ((DirItem
*) item
)->leafname
;
234 if (strcmp(leafname
, r
[i
]->leafname
) == 0)
241 static void update_item(FilerWindow
*filer_window
, DirItem
*item
)
244 char *leafname
= item
->leafname
;
246 if (leafname
[0] == '.')
248 if (filer_window
->show_hidden
== FALSE
|| leafname
[1] == '\0'
249 || (leafname
[1] == '.' && leafname
[2] == '\0'))
253 i
= collection_find_item(filer_window
->collection
, item
, dir_item_cmp
);
256 collection_draw_item(filer_window
->collection
, i
, TRUE
);
258 g_warning("Failed to find '%s'\n", item
->leafname
);
261 static void update_display(Directory
*dir
,
264 FilerWindow
*filer_window
)
268 int cursor
= filer_window
->collection
->cursor_item
;
270 Collection
*collection
= filer_window
->collection
;
275 as
= filer_window
->auto_select
;
277 old_num
= collection
->number_of_items
;
278 for (i
= 0; i
< items
->len
; i
++)
280 DirItem
*item
= (DirItem
*) items
->pdata
[i
];
282 add_item(filer_window
, item
);
284 if (cursor
!= -1 || !as
)
287 if (strcmp(as
, item
->leafname
) != 0)
290 cursor
= collection
->number_of_items
- 1;
291 if (filer_window
->had_cursor
)
293 collection_set_cursor_item(collection
,
295 filer_window
->mini_cursor_base
= cursor
;
298 collection_wink_item(collection
,
302 if (old_num
!= collection
->number_of_items
)
303 collection_qsort(filer_window
->collection
,
304 filer_window
->sort_fn
);
307 collection_delete_if(filer_window
->collection
,
312 set_scanning_display(filer_window
, TRUE
);
315 if (filer_window
->window
->window
)
316 gdk_window_set_cursor(
317 filer_window
->window
->window
,
319 shrink_width(filer_window
);
320 if (filer_window
->had_cursor
&&
321 collection
->cursor_item
== -1)
323 collection_set_cursor_item(collection
, 0);
324 filer_window
->had_cursor
= FALSE
;
326 set_scanning_display(filer_window
, FALSE
);
329 for (i
= 0; i
< items
->len
; i
++)
331 DirItem
*item
= (DirItem
*) items
->pdata
[i
];
333 update_item(filer_window
, item
);
335 collection_qsort(filer_window
->collection
,
336 filer_window
->sort_fn
);
341 static void attach(FilerWindow
*filer_window
)
343 gdk_window_set_cursor(filer_window
->window
->window
, busy_cursor
);
344 collection_clear(filer_window
->collection
);
345 filer_window
->scanning
= TRUE
;
346 dir_attach(filer_window
->directory
, (DirCallback
) update_display
,
348 filer_set_title(filer_window
);
351 static void detach(FilerWindow
*filer_window
)
353 g_return_if_fail(filer_window
->directory
!= NULL
);
355 dir_detach(filer_window
->directory
,
356 (DirCallback
) update_display
, filer_window
);
357 g_fscache_data_unref(dir_cache
, filer_window
->directory
);
358 filer_window
->directory
= NULL
;
361 static void filer_window_destroyed(GtkWidget
*widget
,
362 FilerWindow
*filer_window
)
364 all_filer_windows
= g_list_remove(all_filer_windows
, filer_window
);
366 if (window_with_selection
== filer_window
)
367 window_with_selection
= NULL
;
368 if (window_with_focus
== filer_window
)
369 window_with_focus
= NULL
;
371 if (filer_window
->directory
)
372 detach(filer_window
);
374 g_free(filer_window
->auto_select
);
375 g_free(filer_window
->path
);
376 g_free(filer_window
);
378 if (--number_of_windows
< 1)
382 static int calc_width(FilerWindow
*filer_window
, DirItem
*item
)
384 int pix_width
= item
->image
->width
;
386 switch (filer_window
->display_style
)
389 return MAX_ICON_WIDTH
+ 12 +
390 MAX(item
->details_width
, item
->name_width
);
392 return SMALL_ICON_WIDTH
+ 12 + item
->name_width
;
394 return MAX(pix_width
, item
->name_width
) + 4;
398 /* Add a single object to a directory display */
399 static void add_item(FilerWindow
*filer_window
, DirItem
*item
)
401 char *leafname
= item
->leafname
;
404 if (leafname
[0] == '.')
406 if (filer_window
->show_hidden
== FALSE
|| leafname
[1] == '\0'
407 || (leafname
[1] == '.' && leafname
[2] == '\0'))
411 item_width
= calc_width(filer_window
, item
);
412 if (item_width
> filer_window
->collection
->item_width
)
413 collection_set_item_size(filer_window
->collection
,
415 filer_window
->collection
->item_height
);
416 collection_insert(filer_window
->collection
, item
);
419 /* Is a point inside an item? */
420 static gboolean
test_point_large(Collection
*collection
,
421 int point_x
, int point_y
,
422 CollectionItem
*colitem
,
423 int width
, int height
)
425 DirItem
*item
= (DirItem
*) colitem
->data
;
426 int text_height
= item_font
->ascent
+ item_font
->descent
;
427 MaskedPixmap
*image
= item
->image
;
428 int image_y
= MAX(0, MAX_ICON_HEIGHT
- image
->height
);
429 int image_width
= (image
->width
>> 1) + 2;
430 int text_width
= (item
->name_width
>> 1) + 2;
433 if (point_y
< image_y
)
434 return FALSE
; /* Too high up (don't worry about too low) */
436 if (point_y
<= image_y
+ image
->height
+ 2)
437 x_limit
= image_width
;
438 else if (point_y
> height
- text_height
- 2)
439 x_limit
= text_width
;
441 x_limit
= MIN(image_width
, text_width
);
443 return ABS(point_x
- (width
>> 1)) < x_limit
;
446 static gboolean
test_point_full_info(Collection
*collection
,
447 int point_x
, int point_y
,
448 CollectionItem
*colitem
,
449 int width
, int height
)
451 DirItem
*item
= (DirItem
*) colitem
->data
;
452 MaskedPixmap
*image
= item
->image
;
453 int image_y
= MAX(0, MAX_ICON_HEIGHT
- image
->height
);
455 - fixed_font
->descent
- 2 - fixed_font
->ascent
;
457 if (point_x
< image
->width
+ 2)
458 return point_x
> 2 && point_y
> image_y
;
460 point_x
-= MAX_ICON_WIDTH
+ 8;
462 if (point_y
>= low_top
)
463 return point_x
< item
->details_width
;
464 if (point_y
>= low_top
- item_font
->ascent
- item_font
->descent
)
465 return point_x
< item
->name_width
;
469 static gboolean
test_point_small(Collection
*collection
,
470 int point_x
, int point_y
,
471 CollectionItem
*colitem
,
472 int width
, int height
)
474 DirItem
*item
= (DirItem
*) colitem
->data
;
475 MaskedPixmap
*image
= item
->image
;
476 int image_y
= MAX(0, SMALL_ICON_HEIGHT
- image
->height
);
478 - fixed_font
->descent
- 2 - item_font
->ascent
;
479 int iwidth
= MIN(SMALL_ICON_WIDTH
, image
->width
);
481 if (point_x
< iwidth
+ 2)
482 return point_x
> 2 && point_y
> image_y
;
484 point_x
-= SMALL_ICON_WIDTH
+ 4;
486 if (point_y
>= low_top
)
487 return point_x
< item
->name_width
;
491 static void draw_small_icon(GtkWidget
*widget
,
496 GdkGC
*gc
= selected
? widget
->style
->white_gc
497 : widget
->style
->black_gc
;
498 MaskedPixmap
*image
= item
->image
;
499 int width
, height
, image_x
, image_y
;
504 if (!image
->sm_pixmap
)
505 pixmap_make_small(image
);
507 width
= MIN(image
->sm_width
, SMALL_ICON_WIDTH
);
508 height
= MIN(image
->sm_height
, SMALL_ICON_HEIGHT
);
509 image_x
= area
->x
+ ((area
->width
- width
) >> 1);
511 gdk_gc_set_clip_mask(gc
, item
->image
->sm_mask
);
513 image_y
= MAX(0, SMALL_ICON_HEIGHT
- image
->sm_height
);
514 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ image_y
);
515 gdk_draw_pixmap(widget
->window
, gc
,
516 item
->image
->sm_pixmap
,
517 0, 0, /* Source x,y */
518 image_x
, area
->y
+ image_y
, /* Dest x,y */
523 gdk_gc_set_function(gc
, GDK_INVERT
);
524 gdk_draw_rectangle(widget
->window
,
526 TRUE
, image_x
, area
->y
+ image_y
,
528 gdk_gc_set_function(gc
, GDK_COPY
);
531 if (item
->flags
& ITEM_FLAG_SYMLINK
)
533 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ 8);
534 gdk_gc_set_clip_mask(gc
,
535 default_pixmap
[TYPE_SYMLINK
]->mask
);
536 gdk_draw_pixmap(widget
->window
, gc
,
537 default_pixmap
[TYPE_SYMLINK
]->pixmap
,
538 0, 0, /* Source x,y */
539 image_x
, area
->y
+ 8, /* Dest x,y */
542 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
544 int type
= item
->flags
& ITEM_FLAG_MOUNTED
547 MaskedPixmap
*mp
= default_pixmap
[type
];
550 pixmap_make_small(mp
);
551 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ 8);
552 gdk_gc_set_clip_mask(gc
, mp
->sm_mask
);
553 gdk_draw_pixmap(widget
->window
, gc
,
555 0, 0, /* Source x,y */
556 image_x
, area
->y
+ 8, /* Dest x,y */
560 gdk_gc_set_clip_mask(gc
, NULL
);
561 gdk_gc_set_clip_origin(gc
, 0, 0);
564 static void draw_large_icon(GtkWidget
*widget
,
569 MaskedPixmap
*image
= item
->image
;
570 int width
= MIN(image
->width
, MAX_ICON_WIDTH
);
571 int height
= MIN(image
->height
, MAX_ICON_WIDTH
);
572 int image_x
= area
->x
+ ((area
->width
- width
) >> 1);
574 GdkGC
*gc
= selected
? widget
->style
->white_gc
575 : widget
->style
->black_gc
;
577 gdk_gc_set_clip_mask(gc
, item
->image
->mask
);
579 image_y
= MAX(0, MAX_ICON_HEIGHT
- image
->height
);
580 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ image_y
);
581 gdk_draw_pixmap(widget
->window
, gc
,
583 0, 0, /* Source x,y */
584 image_x
, area
->y
+ image_y
, /* Dest x,y */
589 gdk_gc_set_function(gc
, GDK_INVERT
);
590 gdk_draw_rectangle(widget
->window
,
592 TRUE
, image_x
, area
->y
+ image_y
,
594 gdk_gc_set_function(gc
, GDK_COPY
);
597 if (item
->flags
& ITEM_FLAG_SYMLINK
)
599 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ 8);
600 gdk_gc_set_clip_mask(gc
,
601 default_pixmap
[TYPE_SYMLINK
]->mask
);
602 gdk_draw_pixmap(widget
->window
, gc
,
603 default_pixmap
[TYPE_SYMLINK
]->pixmap
,
604 0, 0, /* Source x,y */
605 image_x
, area
->y
+ 8, /* Dest x,y */
608 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
610 int type
= item
->flags
& ITEM_FLAG_MOUNTED
613 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ 8);
614 gdk_gc_set_clip_mask(gc
,
615 default_pixmap
[type
]->mask
);
616 gdk_draw_pixmap(widget
->window
, gc
,
617 default_pixmap
[type
]->pixmap
,
618 0, 0, /* Source x,y */
619 image_x
, area
->y
+ 8, /* Dest x,y */
623 gdk_gc_set_clip_mask(gc
, NULL
);
624 gdk_gc_set_clip_origin(gc
, 0, 0);
627 static void draw_string(GtkWidget
*widget
,
635 int text_height
= font
->ascent
+ font
->descent
;
638 gtk_paint_flat_box(widget
->style
, widget
->window
,
639 GTK_STATE_SELECTED
, GTK_SHADOW_NONE
,
640 NULL
, widget
, "text",
645 gdk_draw_text(widget
->window
,
647 selected
? widget
->style
->white_gc
648 : widget
->style
->black_gc
,
650 string
, strlen(string
));
653 /* Return a string (valid until next call) giving details
656 char *details(DirItem
*item
)
658 mode_t m
= item
->mode
;
659 static guchar
*buf
= NULL
;
664 if (item
->lstat_errno
)
665 buf
= g_strdup_printf(_("lstat(2) failed: %s"),
666 g_strerror(item
->lstat_errno
));
668 buf
= g_strdup_printf("%s %s %-8.8s %-8.8s %s %s",
669 item
->flags
& ITEM_FLAG_APPDIR
? "App " :
670 S_ISDIR(m
) ? "Dir " :
671 S_ISCHR(m
) ? "Char" :
672 S_ISBLK(m
) ? "Blck" :
673 S_ISLNK(m
) ? "Link" :
674 S_ISSOCK(m
) ? "Sock" :
675 S_ISFIFO(m
) ? "Pipe" : "File",
676 pretty_permissions(m
),
677 user_name(item
->uid
),
678 group_name(item
->gid
),
679 format_size_aligned(item
->size
),
680 pretty_time(&item
->mtime
));
684 static void draw_item_full_info(GtkWidget
*widget
,
685 CollectionItem
*colitem
,
688 DirItem
*item
= (DirItem
*) colitem
->data
;
689 MaskedPixmap
*image
= item
->image
;
690 int text_x
= area
->x
+ MAX_ICON_WIDTH
+ 8;
691 int low_text_y
= area
->y
+ area
->height
- fixed_font
->descent
- 2;
692 gboolean selected
= colitem
->selected
;
693 GdkRectangle pic_area
;
695 pic_area
.x
= area
->x
;
696 pic_area
.y
= area
->y
;
697 pic_area
.width
= image
->width
+ 8;
698 pic_area
.height
= area
->height
;
700 draw_large_icon(widget
, &pic_area
, item
, selected
);
706 low_text_y
- item_font
->descent
- fixed_font
->ascent
,
716 if (item
->lstat_errno
)
719 /* Underline the effective permissions */
720 gdk_draw_rectangle(widget
->window
,
721 selected
? widget
->style
->white_gc
722 : widget
->style
->black_gc
,
724 text_x
- 1 + fixed_width
*
725 (5 + 4 * applicable(item
->uid
, item
->gid
)),
726 low_text_y
+ fixed_font
->descent
- 1,
727 fixed_width
* 3 + 1, 1);
730 static void draw_item_small(GtkWidget
*widget
,
731 CollectionItem
*colitem
,
734 DirItem
*item
= (DirItem
*) colitem
->data
;
735 int text_x
= area
->x
+ SMALL_ICON_WIDTH
+ 4;
736 int low_text_y
= area
->y
+ area
->height
- item_font
->descent
- 2;
737 gboolean selected
= colitem
->selected
;
738 GdkRectangle pic_area
;
740 pic_area
.x
= area
->x
;
741 pic_area
.y
= area
->y
;
742 pic_area
.width
= SMALL_ICON_WIDTH
;
743 pic_area
.height
= SMALL_ICON_HEIGHT
;
745 draw_small_icon(widget
, &pic_area
, item
, selected
);
756 static void draw_item_large(GtkWidget
*widget
,
757 CollectionItem
*colitem
,
760 DirItem
*item
= (DirItem
*) colitem
->data
;
761 int text_x
= area
->x
+ ((area
->width
- item
->name_width
) >> 1);
762 int text_y
= area
->y
+ area
->height
- item_font
->descent
- 2;
763 gboolean selected
= colitem
->selected
;
765 draw_large_icon(widget
, area
, item
, selected
);
770 text_x
, text_y
, item
->name_width
,
774 static void show_menu(Collection
*collection
, GdkEventButton
*event
,
775 int item
, gpointer user_data
)
777 show_filer_menu((FilerWindow
*) user_data
, event
, item
);
780 /* Returns TRUE iff the directory still exists. */
781 static gboolean
may_rescan(FilerWindow
*filer_window
, gboolean warning
)
785 g_return_val_if_fail(filer_window
!= NULL
, FALSE
);
787 /* We do a fresh lookup (rather than update) because the inode may
790 dir
= g_fscache_lookup(dir_cache
, filer_window
->path
);
794 delayed_error(PROJECT
, _("Directory missing/deleted"));
795 gtk_widget_destroy(filer_window
->window
);
798 if (dir
== filer_window
->directory
)
799 g_fscache_data_unref(dir_cache
, dir
);
802 detach(filer_window
);
803 filer_window
->directory
= dir
;
804 attach(filer_window
);
810 /* Another app has grabbed the selection */
811 static gint
collection_lose_selection(GtkWidget
*widget
,
812 GdkEventSelection
*event
)
814 if (window_with_selection
&&
815 window_with_selection
->collection
== COLLECTION(widget
))
817 FilerWindow
*filer_window
= window_with_selection
;
818 window_with_selection
= NULL
;
819 collection_clear_selection(filer_window
->collection
);
825 /* Someone wants us to send them the selection */
826 static void selection_get(GtkWidget
*widget
,
827 GtkSelectionData
*selection_data
,
832 GString
*reply
, *header
;
833 FilerWindow
*filer_window
;
835 Collection
*collection
;
837 filer_window
= gtk_object_get_data(GTK_OBJECT(widget
), "filer_window");
839 reply
= g_string_new(NULL
);
840 header
= g_string_new(NULL
);
845 g_string_sprintf(header
, " %s",
846 make_path(filer_window
->path
, "")->str
);
848 case TARGET_URI_LIST
:
849 g_string_sprintf(header
, " file://%s%s",
851 make_path(filer_window
->path
, "")->str
);
855 collection
= filer_window
->collection
;
856 for (i
= 0; i
< collection
->number_of_items
; i
++)
858 if (collection
->items
[i
].selected
)
861 (DirItem
*) collection
->items
[i
].data
;
863 g_string_append(reply
, header
->str
);
864 g_string_append(reply
, item
->leafname
);
867 /* This works, but I don't think I like it... */
868 /* g_string_append_c(reply, ' '); */
870 gtk_selection_data_set(selection_data
, xa_string
,
871 8, reply
->str
+ 1, reply
->len
- 1);
872 g_string_free(reply
, TRUE
);
873 g_string_free(header
, TRUE
);
876 /* No items are now selected. This might be because another app claimed
877 * the selection or because the user unselected all the items.
879 static void lose_selection(Collection
*collection
,
883 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
885 if (window_with_selection
== filer_window
)
887 window_with_selection
= NULL
;
888 gtk_selection_owner_set(NULL
,
889 GDK_SELECTION_PRIMARY
,
894 static void gain_selection(Collection
*collection
,
898 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
900 if (gtk_selection_owner_set(GTK_WIDGET(collection
),
901 GDK_SELECTION_PRIMARY
,
904 window_with_selection
= filer_window
;
907 collection_clear_selection(filer_window
->collection
);
910 int sort_by_name(const void *item1
, const void *item2
)
913 return g_strcasecmp((*((DirItem
**)item1
))->leafname
,
914 (*((DirItem
**)item2
))->leafname
);
915 return strcmp((*((DirItem
**)item1
))->leafname
,
916 (*((DirItem
**)item2
))->leafname
);
919 int sort_by_type(const void *item1
, const void *item2
)
921 const DirItem
*i1
= (DirItem
*) ((CollectionItem
*) item1
)->data
;
922 const DirItem
*i2
= (DirItem
*) ((CollectionItem
*) item2
)->data
;
925 int diff
= i1
->base_type
- i2
->base_type
;
928 diff
= (i1
->flags
& ITEM_FLAG_APPDIR
)
929 - (i2
->flags
& ITEM_FLAG_APPDIR
);
931 return diff
> 0 ? 1 : -1;
938 diff
= strcmp(m1
->media_type
, m2
->media_type
);
940 diff
= strcmp(m1
->subtype
, m2
->subtype
);
948 return diff
> 0 ? 1 : -1;
950 return sort_by_name(item1
, item2
);
953 int sort_by_date(const void *item1
, const void *item2
)
955 const DirItem
*i1
= (DirItem
*) ((CollectionItem
*) item1
)->data
;
956 const DirItem
*i2
= (DirItem
*) ((CollectionItem
*) item2
)->data
;
958 return i1
->mtime
> i2
->mtime
? -1 :
959 i1
->mtime
< i2
->mtime
? 1 :
960 sort_by_name(item1
, item2
);
963 int sort_by_size(const void *item1
, const void *item2
)
965 const DirItem
*i1
= (DirItem
*) ((CollectionItem
*) item1
)->data
;
966 const DirItem
*i2
= (DirItem
*) ((CollectionItem
*) item2
)->data
;
968 return i1
->size
> i2
->size
? -1 :
969 i1
->size
< i2
->size
? 1 :
970 sort_by_name(item1
, item2
);
973 static void open_item(Collection
*collection
,
974 gpointer item_data
, int item_number
,
977 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
979 GdkEventButton
*bevent
;
983 event
= (GdkEvent
*) gtk_get_current_event();
985 bevent
= (GdkEventButton
*) event
;
986 kevent
= (GdkEventKey
*) event
;
990 case GDK_2BUTTON_PRESS
:
991 case GDK_BUTTON_PRESS
:
992 case GDK_BUTTON_RELEASE
:
993 if (bevent
->state
& GDK_SHIFT_MASK
)
996 if (o_new_window_on_1
^ (bevent
->button
== 1))
997 flags
|= OPEN_SAME_WINDOW
;
999 if (bevent
->button
!= 1)
1000 flags
|= OPEN_CLOSE_WINDOW
;
1002 if (o_single_click
== FALSE
&&
1003 (bevent
->state
& GDK_CONTROL_MASK
) != 0)
1004 flags
^= OPEN_SAME_WINDOW
| OPEN_CLOSE_WINDOW
;
1007 flags
|= OPEN_SAME_WINDOW
;
1008 if (kevent
->state
& GDK_SHIFT_MASK
)
1009 flags
|= OPEN_SHIFT
;
1015 filer_openitem(filer_window
, item_number
, flags
);
1018 /* Return the full path to the directory containing object 'path'.
1019 * Relative paths are resolved from the filerwindow's path.
1021 static void follow_symlink(FilerWindow
*filer_window
, char *path
,
1022 gboolean same_window
)
1028 path
= make_path(filer_window
->path
, path
)->str
;
1030 real
= pathdup(path
);
1031 slash
= strrchr(real
, '/');
1035 delayed_error(PROJECT
,
1036 _("Broken symlink (or you don't have permission "
1048 if (filer_window
->panel_type
|| !same_window
)
1052 new = filer_opendir(new_dir
, PANEL_NO
);
1053 filer_set_autoselect(new, slash
+ 1);
1056 filer_change_to(filer_window
, new_dir
, slash
+ 1);
1061 /* Open the item (or add it to the shell command minibuffer) */
1062 void filer_openitem(FilerWindow
*filer_window
, int item_number
, OpenFlags flags
)
1064 gboolean shift
= (flags
& OPEN_SHIFT
) != 0;
1065 gboolean close_mini
= flags
& OPEN_FROM_MINI
;
1066 gboolean same_window
= (flags
& OPEN_SAME_WINDOW
) != 0
1067 && !filer_window
->panel_type
;
1068 gboolean close_window
= (flags
& OPEN_CLOSE_WINDOW
) != 0
1069 && !filer_window
->panel_type
;
1072 DirItem
*item
= (DirItem
*)
1073 filer_window
->collection
->items
[item_number
].data
;
1074 gboolean wink
= TRUE
, destroy
= FALSE
;
1076 widget
= filer_window
->window
;
1077 if (filer_window
->mini_type
== MINI_SHELL
)
1079 minibuffer_add(filer_window
, item
->leafname
);
1083 full_path
= make_path(filer_window
->path
,
1084 item
->leafname
)->str
;
1086 if (item
->flags
& ITEM_FLAG_SYMLINK
&& shift
)
1088 char path
[MAXPATHLEN
+ 1];
1091 got
= readlink(make_path(filer_window
->path
,
1092 item
->leafname
)->str
,
1095 delayed_error(PROJECT
, g_strerror(errno
));
1098 g_return_if_fail(got
<= MAXPATHLEN
);
1101 follow_symlink(filer_window
, path
,
1102 flags
& OPEN_SAME_WINDOW
);
1107 switch (item
->base_type
)
1109 case TYPE_DIRECTORY
:
1110 if (item
->flags
& ITEM_FLAG_APPDIR
&& !shift
)
1112 run_app(make_path(filer_window
->path
,
1113 item
->leafname
)->str
);
1119 if (item
->flags
& ITEM_FLAG_MOUNT_POINT
&& shift
)
1121 action_mount(filer_window
, item
);
1122 if (item
->flags
& ITEM_FLAG_MOUNTED
)
1129 filer_change_to(filer_window
, full_path
, NULL
);
1133 filer_opendir(full_path
, PANEL_NO
);
1136 if ((item
->flags
& ITEM_FLAG_EXEC_FILE
) && !shift
)
1138 char *argv
[] = {NULL
, NULL
};
1140 argv
[0] = full_path
;
1142 if (spawn_full(argv
, filer_window
->path
))
1148 report_error(PROJECT
,
1149 _("Failed to fork() child"));
1154 MIME_type
*type
= shift
? &text_plain
1157 g_return_if_fail(type
!= NULL
);
1159 if (type_open(full_path
, type
))
1166 message
= g_string_new(NULL
);
1167 g_string_sprintf(message
,
1168 _("No run action specified for files of this type (%s/%s) - "
1169 "you can set a run action using by choosing `Set Run Action' "
1170 "from the Window menu"),
1173 report_error(PROJECT
, message
->str
);
1174 g_string_free(message
, TRUE
);
1179 report_error("open_item",
1180 "I don't know how to open that");
1185 gtk_widget_destroy(filer_window
->window
);
1189 collection_wink_item(filer_window
->collection
,
1192 minibuffer_hide(filer_window
);
1196 static gint
pointer_in(GtkWidget
*widget
,
1197 GdkEventCrossing
*event
,
1198 FilerWindow
*filer_window
)
1200 may_rescan(filer_window
, TRUE
);
1204 static gint
focus_in(GtkWidget
*widget
,
1205 GdkEventFocus
*event
,
1206 FilerWindow
*filer_window
)
1208 window_with_focus
= filer_window
;
1213 static gint
focus_out(GtkWidget
*widget
,
1214 GdkEventFocus
*event
,
1215 FilerWindow
*filer_window
)
1217 /* TODO: Shade the cursor */
1222 /* Handle keys that can't be bound with the menu */
1223 static gint
key_press_event(GtkWidget
*widget
,
1225 FilerWindow
*filer_window
)
1227 switch (event
->keyval
)
1230 change_to_parent(filer_window
);
1239 static void toolbar_refresh_clicked(GtkWidget
*widget
,
1240 FilerWindow
*filer_window
)
1244 event
= gtk_get_current_event();
1245 if (event
->type
== GDK_BUTTON_RELEASE
&&
1246 ((GdkEventButton
*) event
)->button
== 3)
1248 filer_opendir(filer_window
->path
, PANEL_NO
);
1253 filer_update_dir(filer_window
, TRUE
);
1257 static void toolbar_home_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
)
1261 event
= gtk_get_current_event();
1262 if (event
->type
== GDK_BUTTON_RELEASE
&&
1263 ((GdkEventButton
*) event
)->button
== 3)
1265 filer_opendir(home_dir
, PANEL_NO
);
1268 filer_change_to(filer_window
, home_dir
, NULL
);
1271 static void toolbar_up_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
)
1275 event
= gtk_get_current_event();
1276 if (event
->type
== GDK_BUTTON_RELEASE
&&
1277 ((GdkEventButton
*) event
)->button
== 3)
1279 filer_open_parent(filer_window
);
1282 change_to_parent(filer_window
);
1285 void change_to_parent(FilerWindow
*filer_window
)
1290 if (filer_window
->path
[0] == '/' && filer_window
->path
[1] == '\0')
1291 return; /* Already in the root */
1293 copy
= g_strdup(filer_window
->path
);
1294 slash
= strrchr(copy
, '/');
1299 filer_change_to(filer_window
,
1304 g_warning("No / in directory path!\n");
1310 /* Make filer_window display path. When finished, highlight item 'from', or
1311 * the first item if from is NULL. If there is currently no cursor then
1312 * simply wink 'from' (if not NULL).
1314 void filer_change_to(FilerWindow
*filer_window
, char *path
, char *from
)
1317 char *real_path
= pathdup(path
);
1319 if (o_unique_filer_windows
)
1323 fw
= find_filer_window(real_path
, filer_window
);
1325 gtk_widget_destroy(fw
->window
);
1328 from_dup
= from
&& *from
? g_strdup(from
) : NULL
;
1330 detach(filer_window
);
1331 g_free(filer_window
->path
);
1332 filer_window
->path
= real_path
;
1334 filer_window
->directory
= g_fscache_lookup(dir_cache
,
1335 filer_window
->path
);
1336 if (filer_window
->directory
)
1338 g_free(filer_window
->auto_select
);
1339 filer_window
->had_cursor
=
1340 filer_window
->collection
->cursor_item
!= -1
1341 || filer_window
->had_cursor
;
1342 filer_window
->auto_select
= from_dup
;
1344 filer_set_title(filer_window
);
1345 collection_set_cursor_item(filer_window
->collection
, -1);
1346 attach(filer_window
);
1348 if (filer_window
->mini_type
== MINI_PATH
)
1349 gtk_idle_add((GtkFunction
) minibuffer_show_cb
,
1357 error
= g_strdup_printf(_("Directory '%s' is not accessible"),
1359 delayed_error(PROJECT
, error
);
1361 gtk_widget_destroy(filer_window
->window
);
1365 void filer_open_parent(FilerWindow
*filer_window
)
1370 if (filer_window
->path
[0] == '/' && filer_window
->path
[1] == '\0')
1371 return; /* Already in the root */
1373 copy
= g_strdup(filer_window
->path
);
1374 slash
= strrchr(copy
, '/');
1379 filer_opendir(*copy
? copy
: "/", PANEL_NO
);
1382 g_warning("No / in directory path!\n");
1387 int selected_item_number(Collection
*collection
)
1391 g_return_val_if_fail(collection
!= NULL
, -1);
1392 g_return_val_if_fail(IS_COLLECTION(collection
), -1);
1393 g_return_val_if_fail(collection
->number_selected
== 1, -1);
1395 for (i
= 0; i
< collection
->number_of_items
; i
++)
1396 if (collection
->items
[i
].selected
)
1399 g_warning("selected_item: number_selected is wrong\n");
1404 DirItem
*selected_item(Collection
*collection
)
1408 item
= selected_item_number(collection
);
1411 return (DirItem
*) collection
->items
[item
].data
;
1415 static int filer_confirm_close(GtkWidget
*widget
, GdkEvent
*event
,
1416 FilerWindow
*window
)
1418 /* TODO: We can open lots of these - very irritating! */
1419 return get_choice(_("Close panel?"),
1420 _("You have tried to close a panel via the window "
1421 "manager - I usually find that this is accidental... "
1423 2, _("Remove"), _("Cancel")) != 0;
1426 /* Make the items as narrow as possible */
1427 static void shrink_width(FilerWindow
*filer_window
)
1430 Collection
*col
= filer_window
->collection
;
1431 int width
= MIN_ITEM_WIDTH
;
1433 DisplayStyle style
= filer_window
->display_style
;
1436 text_height
= item_font
->ascent
+ item_font
->descent
;
1438 for (i
= 0; i
< col
->number_of_items
; i
++)
1440 this_width
= calc_width(filer_window
,
1441 (DirItem
*) col
->items
[i
].data
);
1442 if (this_width
> width
)
1446 collection_set_item_size(filer_window
->collection
,
1448 style
== FULL_INFO
? MAX_ICON_HEIGHT
+ 4 :
1449 style
== SMALL_ICONS
? MAX(text_height
, SMALL_ICON_HEIGHT
) + 4
1450 : text_height
+ MAX_ICON_HEIGHT
+ 8);
1453 void filer_set_sort_fn(FilerWindow
*filer_window
,
1454 int (*fn
)(const void *a
, const void *b
))
1456 if (filer_window
->sort_fn
== fn
)
1459 filer_window
->sort_fn
= fn
;
1462 collection_qsort(filer_window
->collection
,
1463 filer_window
->sort_fn
);
1465 update_options_label();
1468 void filer_style_set(FilerWindow
*filer_window
, DisplayStyle style
)
1470 if (filer_window
->display_style
== style
)
1473 if (filer_window
->panel_type
)
1474 style
= LARGE_ICONS
;
1476 last_display_style
= style
;
1478 filer_window
->display_style
= style
;
1483 collection_set_functions(filer_window
->collection
,
1484 draw_item_small
, test_point_small
);
1487 collection_set_functions(filer_window
->collection
,
1488 draw_item_full_info
, test_point_full_info
);
1491 collection_set_functions(filer_window
->collection
,
1492 draw_item_large
, test_point_large
);
1496 shrink_width(filer_window
);
1498 update_options_label();
1501 FilerWindow
*filer_opendir(char *path
, PanelType panel_type
)
1503 GtkWidget
*hbox
, *scrollbar
, *collection
;
1504 FilerWindow
*filer_window
;
1505 GtkTargetEntry target_table
[] =
1507 {"text/uri-list", 0, TARGET_URI_LIST
},
1508 {"STRING", 0, TARGET_STRING
},
1512 real_path
= pathdup(path
);
1514 if (o_unique_filer_windows
&& panel_type
== PANEL_NO
)
1518 fw
= find_filer_window(real_path
, NULL
);
1522 /* TODO: this should bring the window to the front
1523 * at the same coordinates.
1525 gtk_widget_hide(fw
->window
);
1526 gtk_widget_show(fw
->window
);
1532 filer_window
= g_new(FilerWindow
, 1);
1533 filer_window
->minibuffer
= NULL
;
1534 filer_window
->minibuffer_label
= NULL
;
1535 filer_window
->minibuffer_area
= NULL
;
1536 filer_window
->path
= real_path
;
1537 filer_window
->scanning
= FALSE
;
1538 filer_window
->had_cursor
= FALSE
;
1539 filer_window
->auto_select
= NULL
;
1540 filer_window
->mini_type
= MINI_NONE
;
1542 filer_window
->directory
= g_fscache_lookup(dir_cache
,
1543 filer_window
->path
);
1544 if (!filer_window
->directory
)
1548 error
= g_strdup_printf(_("Directory '%s' not found."), path
);
1549 delayed_error(PROJECT
, error
);
1551 g_free(filer_window
->path
);
1552 g_free(filer_window
);
1556 filer_window
->show_hidden
= last_show_hidden
;
1557 filer_window
->panel_type
= panel_type
;
1558 filer_window
->temp_item_selected
= FALSE
;
1559 filer_window
->sort_fn
= last_sort_fn
;
1560 filer_window
->flags
= (FilerFlags
) 0;
1561 filer_window
->display_style
= UNKNOWN_STYLE
;
1563 filer_window
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
1564 filer_set_title(filer_window
);
1566 collection
= collection_new(NULL
);
1567 gtk_object_set_data(GTK_OBJECT(collection
),
1568 "filer_window", filer_window
);
1569 filer_window
->collection
= COLLECTION(collection
);
1571 gtk_widget_add_events(filer_window
->window
, GDK_ENTER_NOTIFY
);
1572 gtk_signal_connect(GTK_OBJECT(filer_window
->window
),
1573 "enter-notify-event",
1574 GTK_SIGNAL_FUNC(pointer_in
), filer_window
);
1575 gtk_signal_connect(GTK_OBJECT(filer_window
->window
), "focus_in_event",
1576 GTK_SIGNAL_FUNC(focus_in
), filer_window
);
1577 gtk_signal_connect(GTK_OBJECT(filer_window
->window
), "focus_out_event",
1578 GTK_SIGNAL_FUNC(focus_out
), filer_window
);
1579 gtk_signal_connect(GTK_OBJECT(filer_window
->window
), "destroy",
1580 filer_window_destroyed
, filer_window
);
1582 gtk_signal_connect(GTK_OBJECT(filer_window
->collection
), "open_item",
1583 open_item
, filer_window
);
1584 gtk_signal_connect(GTK_OBJECT(collection
), "show_menu",
1585 show_menu
, filer_window
);
1586 gtk_signal_connect(GTK_OBJECT(collection
), "gain_selection",
1587 gain_selection
, filer_window
);
1588 gtk_signal_connect(GTK_OBJECT(collection
), "lose_selection",
1589 lose_selection
, filer_window
);
1590 gtk_signal_connect(GTK_OBJECT(collection
), "drag_selection",
1591 drag_selection
, filer_window
);
1592 gtk_signal_connect(GTK_OBJECT(collection
), "drag_data_get",
1593 drag_data_get
, filer_window
);
1594 gtk_signal_connect(GTK_OBJECT(collection
), "selection_clear_event",
1595 GTK_SIGNAL_FUNC(collection_lose_selection
), NULL
);
1596 gtk_signal_connect (GTK_OBJECT(collection
), "selection_get",
1597 GTK_SIGNAL_FUNC(selection_get
), NULL
);
1598 gtk_selection_add_targets(collection
, GDK_SELECTION_PRIMARY
,
1600 sizeof(target_table
) / sizeof(*target_table
));
1602 filer_style_set(filer_window
, last_display_style
);
1603 drag_set_dest(filer_window
);
1607 int swidth
, sheight
, iwidth
, iheight
;
1608 GtkWidget
*frame
, *win
= filer_window
->window
;
1610 gtk_window_set_wmclass(GTK_WINDOW(win
), "ROX-Panel",
1612 collection_set_panel(filer_window
->collection
, TRUE
);
1613 gtk_signal_connect(GTK_OBJECT(filer_window
->window
),
1615 GTK_SIGNAL_FUNC(filer_confirm_close
),
1618 gdk_window_get_size(GDK_ROOT_PARENT(), &swidth
, &sheight
);
1619 iwidth
= filer_window
->collection
->item_width
;
1620 iheight
= filer_window
->collection
->item_height
;
1623 int height
= iheight
+ PANEL_BORDER
;
1624 int y
= panel_type
== PANEL_TOP
1626 : sheight
- height
- PANEL_BORDER
;
1628 gtk_widget_set_usize(collection
, swidth
, height
);
1629 gtk_widget_set_uposition(win
, 0, y
);
1632 frame
= gtk_frame_new(NULL
);
1633 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_OUT
);
1634 gtk_container_add(GTK_CONTAINER(frame
), collection
);
1635 gtk_container_add(GTK_CONTAINER(win
), frame
);
1637 gtk_widget_show_all(frame
);
1638 gtk_widget_realize(win
);
1639 if (override_redirect
)
1640 gdk_window_set_override_redirect(win
->window
, TRUE
);
1641 make_panel_window(win
->window
);
1646 int col_height
= ROW_HEIGHT_LARGE
* 3;
1648 gtk_signal_connect(GTK_OBJECT(collection
),
1650 GTK_SIGNAL_FUNC(key_press_event
), filer_window
);
1651 gtk_window_set_default_size(GTK_WINDOW(filer_window
->window
),
1652 filer_window
->display_style
== LARGE_ICONS
? 400 : 512,
1653 o_toolbar
== TOOLBAR_NONE
? col_height
:
1654 o_toolbar
== TOOLBAR_NORMAL
? col_height
+ 24 :
1657 hbox
= gtk_hbox_new(FALSE
, 0);
1658 gtk_container_add(GTK_CONTAINER(filer_window
->window
),
1661 vbox
= gtk_vbox_new(FALSE
, 0);
1662 gtk_box_pack_start(GTK_BOX(hbox
), vbox
, TRUE
, TRUE
, 0);
1664 if (o_toolbar
!= TOOLBAR_NONE
)
1668 toolbar
= create_toolbar(filer_window
);
1669 gtk_box_pack_start(GTK_BOX(vbox
), toolbar
,
1671 gtk_widget_show_all(toolbar
);
1674 gtk_box_pack_start(GTK_BOX(vbox
), collection
, TRUE
, TRUE
, 0);
1676 create_minibuffer(filer_window
);
1677 gtk_box_pack_start(GTK_BOX(vbox
), filer_window
->minibuffer_area
,
1680 scrollbar
= gtk_vscrollbar_new(COLLECTION(collection
)->vadj
);
1681 gtk_box_pack_start(GTK_BOX(hbox
), scrollbar
, FALSE
, TRUE
, 0);
1682 gtk_accel_group_attach(filer_keys
,
1683 GTK_OBJECT(filer_window
->window
));
1684 gtk_window_set_focus(GTK_WINDOW(filer_window
->window
),
1687 gtk_widget_show(hbox
);
1688 gtk_widget_show(vbox
);
1689 gtk_widget_show(scrollbar
);
1690 gtk_widget_show(collection
);
1693 number_of_windows
++;
1694 gtk_widget_show(filer_window
->window
);
1695 attach(filer_window
);
1697 all_filer_windows
= g_list_prepend(all_filer_windows
, filer_window
);
1699 return filer_window
;
1702 static gint
clear_scanning_display(FilerWindow
*filer_window
)
1704 if (filer_exists(filer_window
))
1705 filer_set_title(filer_window
);
1709 static void set_scanning_display(FilerWindow
*filer_window
, gboolean scanning
)
1711 if (scanning
== filer_window
->scanning
)
1713 filer_window
->scanning
= scanning
;
1716 filer_set_title(filer_window
);
1718 gtk_timeout_add(300, (GtkFunction
) clear_scanning_display
,
1722 static GtkWidget
*create_toolbar(FilerWindow
*filer_window
)
1724 GtkWidget
*frame
, *box
;
1726 if (o_toolbar
== TOOLBAR_GNOME
)
1728 frame
= gtk_handle_box_new();
1729 box
= gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL
,
1731 gtk_container_set_border_width(GTK_CONTAINER(box
), 2);
1732 gtk_toolbar_set_space_style(GTK_TOOLBAR(box
),
1733 GTK_TOOLBAR_SPACE_LINE
);
1734 gtk_toolbar_set_button_relief(GTK_TOOLBAR(box
),
1739 frame
= gtk_frame_new(NULL
);
1740 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_OUT
);
1742 box
= gtk_hbutton_box_new();
1743 gtk_button_box_set_child_size_default(16, 16);
1744 gtk_hbutton_box_set_spacing_default(2);
1745 gtk_button_box_set_layout(GTK_BUTTON_BOX(box
),
1746 GTK_BUTTONBOX_START
);
1749 gtk_container_add(GTK_CONTAINER(frame
), box
);
1751 add_button(box
, TOOLBAR_UP_ICON
,
1752 GTK_SIGNAL_FUNC(toolbar_up_clicked
),
1754 _("Up"), _("Change to parent directory"));
1755 add_button(box
, TOOLBAR_HOME_ICON
,
1756 GTK_SIGNAL_FUNC(toolbar_home_clicked
),
1758 _("Home"), _("Change to home directory"));
1759 add_button(box
, TOOLBAR_REFRESH_ICON
,
1760 GTK_SIGNAL_FUNC(toolbar_refresh_clicked
),
1762 _("Rescan"), _("Rescan directory contents"));
1767 /* This is used to simulate a click when button 3 is used (GtkButton
1768 * normally ignores this). Currently, this button does not pop in -
1769 * this may be fixed in future versions of GTK+.
1771 static gint
toolbar_adjust_pressed(GtkButton
*button
,
1772 GdkEventButton
*event
,
1773 FilerWindow
*filer_window
)
1775 if (event
->button
== 3)
1777 gtk_grab_add(GTK_WIDGET(button
));
1778 gtk_button_pressed(button
);
1784 static gint
toolbar_adjust_released(GtkButton
*button
,
1785 GdkEventButton
*event
,
1786 FilerWindow
*filer_window
)
1788 if (event
->button
== 3)
1790 gtk_grab_remove(GTK_WIDGET(button
));
1791 gtk_button_released(button
);
1797 static void add_button(GtkWidget
*box
, int pixmap
,
1798 GtkSignalFunc cb
, FilerWindow
*filer_window
,
1799 char *label
, char *tip
)
1801 GtkWidget
*button
, *icon
;
1803 icon
= gtk_pixmap_new(default_pixmap
[pixmap
]->pixmap
,
1804 default_pixmap
[pixmap
]->mask
);
1806 if (o_toolbar
== TOOLBAR_GNOME
)
1808 gtk_toolbar_append_element(GTK_TOOLBAR(box
),
1809 GTK_TOOLBAR_CHILD_BUTTON
,
1818 button
= gtk_button_new();
1819 GTK_WIDGET_UNSET_FLAGS(button
, GTK_CAN_FOCUS
);
1821 gtk_container_add(GTK_CONTAINER(button
), icon
);
1822 gtk_signal_connect(GTK_OBJECT(button
), "button_press_event",
1823 GTK_SIGNAL_FUNC(toolbar_adjust_pressed
), filer_window
);
1824 gtk_signal_connect(GTK_OBJECT(button
), "button_release_event",
1825 GTK_SIGNAL_FUNC(toolbar_adjust_released
), filer_window
);
1826 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
1829 gtk_tooltips_set_tip(tooltips
, button
, tip
, NULL
);
1831 gtk_container_add(GTK_CONTAINER(box
), button
);
1835 /* Build up some option widgets to go in the options dialog, but don't
1838 static GtkWidget
*create_options()
1840 GtkWidget
*vbox
, *menu
, *hbox
;
1842 vbox
= gtk_vbox_new(FALSE
, 0);
1843 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 4);
1845 display_label
= gtk_label_new("<>");
1846 gtk_label_set_line_wrap(GTK_LABEL(display_label
), TRUE
);
1847 gtk_box_pack_start(GTK_BOX(vbox
), display_label
, FALSE
, TRUE
, 0);
1849 toggle_sort_nocase
=
1850 gtk_check_button_new_with_label(_("Ignore case when sorting"));
1851 gtk_box_pack_start(GTK_BOX(vbox
), toggle_sort_nocase
, FALSE
, TRUE
, 0);
1853 toggle_new_window_on_1
=
1854 gtk_check_button_new_with_label(
1855 _("New window on button 1 (RISC OS style)"));
1856 gtk_box_pack_start(GTK_BOX(vbox
), toggle_new_window_on_1
,
1860 gtk_check_button_new_with_label(
1861 _("Menu on button 2 (RISC OS style)"));
1862 gtk_box_pack_start(GTK_BOX(vbox
), toggle_menu_on_2
, FALSE
, TRUE
, 0);
1864 toggle_single_click
=
1865 gtk_check_button_new_with_label(_("Single-click nagivation"));
1866 gtk_box_pack_start(GTK_BOX(vbox
), toggle_single_click
, FALSE
, TRUE
, 0);
1868 toggle_unique_filer_windows
=
1869 gtk_check_button_new_with_label(_("Unique windows"));
1870 gtk_box_pack_start(GTK_BOX(vbox
), toggle_unique_filer_windows
,
1873 hbox
= gtk_hbox_new(FALSE
, 4);
1874 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, TRUE
, 0);
1876 gtk_box_pack_start(GTK_BOX(hbox
),
1877 gtk_label_new(_("Toolbar type for new windows")),
1879 menu_toolbar
= gtk_option_menu_new();
1880 menu
= gtk_menu_new();
1881 gtk_menu_append(GTK_MENU(menu
),
1882 gtk_menu_item_new_with_label(_("None")));
1883 gtk_menu_append(GTK_MENU(menu
),
1884 gtk_menu_item_new_with_label(_("Normal")));
1885 gtk_menu_append(GTK_MENU(menu
),
1886 gtk_menu_item_new_with_label(_("GNOME")));
1887 gtk_option_menu_set_menu(GTK_OPTION_MENU(menu_toolbar
), menu
);
1888 gtk_box_pack_start(GTK_BOX(hbox
), menu_toolbar
, TRUE
, TRUE
, 0);
1893 static void update_options_label(void)
1897 str
= g_strdup_printf(_("The last used display style (%s) and sort "
1898 "function (Sort By %s) will be saved if you click on "
1899 "Save."), style_to_name(), sort_fn_to_name());
1900 gtk_label_set_text(GTK_LABEL(display_label
), str
);
1904 /* Reflect current state by changing the widgets in the options box */
1905 static void update_options()
1907 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_sort_nocase
),
1909 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_new_window_on_1
),
1911 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_menu_on_2
),
1912 collection_menu_button
== 2 ? 1 : 0);
1913 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_single_click
),
1915 gtk_toggle_button_set_active(
1916 GTK_TOGGLE_BUTTON(toggle_unique_filer_windows
),
1917 o_unique_filer_windows
);
1918 gtk_option_menu_set_history(GTK_OPTION_MENU(menu_toolbar
), o_toolbar
);
1920 update_options_label();
1923 /* Set current values by reading the states of the widgets in the options box */
1924 static void set_options()
1926 GtkWidget
*item
, *menu
;
1928 gboolean old_case
= o_sort_nocase
;
1930 o_sort_nocase
= gtk_toggle_button_get_active(
1931 GTK_TOGGLE_BUTTON(toggle_sort_nocase
));
1933 o_new_window_on_1
= gtk_toggle_button_get_active(
1934 GTK_TOGGLE_BUTTON(toggle_new_window_on_1
));
1936 collection_menu_button
= gtk_toggle_button_get_active(
1937 GTK_TOGGLE_BUTTON(toggle_menu_on_2
)) ? 2 : 3;
1939 o_single_click
= gtk_toggle_button_get_active(
1940 GTK_TOGGLE_BUTTON(toggle_single_click
));
1942 o_unique_filer_windows
= gtk_toggle_button_get_active(
1943 GTK_TOGGLE_BUTTON(toggle_unique_filer_windows
));
1945 collection_single_click
= o_single_click
? TRUE
: FALSE
;
1947 menu
= gtk_option_menu_get_menu(GTK_OPTION_MENU(menu_toolbar
));
1948 item
= gtk_menu_get_active(GTK_MENU(menu
));
1949 list
= gtk_container_children(GTK_CONTAINER(menu
));
1950 o_toolbar
= (ToolbarType
) g_list_index(list
, item
);
1953 if (o_sort_nocase
!= old_case
)
1955 GList
*next
= all_filer_windows
;
1959 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1961 collection_qsort(filer_window
->collection
,
1962 filer_window
->sort_fn
);
1969 static guchar
*style_to_name(void)
1971 return last_display_style
== LARGE_ICONS
? _("Large Icons") :
1972 last_display_style
== SMALL_ICONS
? _("Small Icons") :
1976 static guchar
*sort_fn_to_name(void)
1978 return last_sort_fn
== sort_by_name
? _("Name") :
1979 last_sort_fn
== sort_by_type
? _("Type") :
1980 last_sort_fn
== sort_by_date
? _("Date") :
1984 static void save_options()
1986 option_write("filer_sort_nocase", o_sort_nocase
? "1" : "0");
1987 option_write("filer_new_window_on_1", o_new_window_on_1
? "1" : "0");
1988 option_write("filer_menu_on_2",
1989 collection_menu_button
== 2 ? "1" : "0");
1990 option_write("filer_single_click", o_single_click
? "1" : "0");
1991 option_write("filer_unique_windows",
1992 o_unique_filer_windows
? "1" : "0");
1993 option_write("filer_display_style",
1994 last_display_style
== LARGE_ICONS
? "Large Icons" :
1995 last_display_style
== SMALL_ICONS
? "Small Icons" :
1997 option_write("filer_sort_by",
1998 last_sort_fn
== sort_by_name
? "Name" :
1999 last_sort_fn
== sort_by_type
? "Type" :
2000 last_sort_fn
== sort_by_date
? "Date" :
2002 option_write("filer_toolbar", o_toolbar
== TOOLBAR_NONE
? "None" :
2003 o_toolbar
== TOOLBAR_NORMAL
? "Normal" :
2004 o_toolbar
== TOOLBAR_GNOME
? "GNOME" :
2008 static char *filer_sort_nocase(char *data
)
2010 o_sort_nocase
= atoi(data
) != 0;
2014 static char *filer_new_window_on_1(char *data
)
2016 o_new_window_on_1
= atoi(data
) != 0;
2020 static char *filer_menu_on_2(char *data
)
2022 collection_menu_button
= atoi(data
) != 0 ? 2 : 3;
2026 static char *filer_single_click(char *data
)
2028 o_single_click
= atoi(data
) != 0;
2029 collection_single_click
= o_single_click
? TRUE
: FALSE
;
2033 static char *filer_unique_windows(char *data
)
2035 o_unique_filer_windows
= atoi(data
) != 0;
2039 static char *filer_display_style(char *data
)
2041 if (g_strcasecmp(data
, "Large Icons") == 0)
2042 last_display_style
= LARGE_ICONS
;
2043 else if (g_strcasecmp(data
, "Small Icons") == 0)
2044 last_display_style
= SMALL_ICONS
;
2045 else if (g_strcasecmp(data
, "Full Info") == 0)
2046 last_display_style
= FULL_INFO
;
2048 return _("Unknown display style");
2053 static char *filer_sort_by(char *data
)
2055 if (g_strcasecmp(data
, "Name") == 0)
2056 last_sort_fn
= sort_by_name
;
2057 else if (g_strcasecmp(data
, "Type") == 0)
2058 last_sort_fn
= sort_by_type
;
2059 else if (g_strcasecmp(data
, "Date") == 0)
2060 last_sort_fn
= sort_by_date
;
2061 else if (g_strcasecmp(data
, "Size") == 0)
2062 last_sort_fn
= sort_by_size
;
2064 return _("Unknown sort type");
2069 static char *filer_toolbar(char *data
)
2071 if (g_strcasecmp(data
, "None") == 0)
2072 o_toolbar
= TOOLBAR_NONE
;
2073 else if (g_strcasecmp(data
, "Normal") == 0)
2074 o_toolbar
= TOOLBAR_NORMAL
;
2075 else if (g_strcasecmp(data
, "GNOME") == 0)
2076 o_toolbar
= TOOLBAR_GNOME
;
2078 return _("Unknown toolbar type");
2083 /* Note that filer_window may not exist after this call. */
2084 void filer_update_dir(FilerWindow
*filer_window
, gboolean warning
)
2086 if (may_rescan(filer_window
, warning
))
2087 dir_update(filer_window
->directory
, filer_window
->path
);
2090 void filer_set_hidden(FilerWindow
*filer_window
, gboolean hidden
)
2092 Directory
*dir
= filer_window
->directory
;
2094 if (filer_window
->show_hidden
== hidden
)
2097 filer_window
->show_hidden
= hidden
;
2098 last_show_hidden
= hidden
;
2100 g_fscache_data_ref(dir_cache
, dir
);
2101 detach(filer_window
);
2102 filer_window
->directory
= dir
;
2103 attach(filer_window
);
2106 /* Refresh the various caches even if we don't think we need to */
2107 void full_refresh(void)
2112 /* See whether a filer window with a given path already exists
2113 * and is different from diff.
2115 static FilerWindow
*find_filer_window(char *path
, FilerWindow
*diff
)
2117 GList
*next
= all_filer_windows
;
2121 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
2123 if (filer_window
->panel_type
== PANEL_NO
&&
2124 filer_window
!= diff
&&
2125 strcmp(path
, filer_window
->path
) == 0)
2127 return filer_window
;
2136 /* This path has been mounted/umounted/deleted some files - update all dirs */
2137 void filer_check_mounted(char *path
)
2139 GList
*next
= all_filer_windows
;
2146 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
2150 if (strncmp(path
, filer_window
->path
, len
) == 0)
2152 char s
= filer_window
->path
[len
];
2154 if (s
== '/' || s
== '\0')
2155 filer_update_dir(filer_window
, FALSE
);
2160 /* Like minibuffer_show(), except that:
2161 * - It returns FALSE (to be used from an idle callback)
2162 * - It checks that the filer window still exists.
2164 static gboolean
minibuffer_show_cb(FilerWindow
*filer_window
)
2166 if (filer_exists(filer_window
))
2167 minibuffer_show(filer_window
, MINI_PATH
);
2171 gboolean
filer_exists(FilerWindow
*filer_window
)
2175 for (next
= all_filer_windows
; next
; next
= next
->next
)
2177 FilerWindow
*fw
= (FilerWindow
*) next
->data
;
2179 if (fw
== filer_window
)
2186 /* Highlight (wink or cursor) this item in the filer window. If the item
2187 * isn't already there but we're scanning then highlight it if it
2190 void filer_set_autoselect(FilerWindow
*filer_window
, guchar
*leaf
)
2192 Collection
*col
= filer_window
->collection
;
2195 g_free(filer_window
->auto_select
);
2196 filer_window
->auto_select
= NULL
;
2198 for (i
= 0; i
< col
->number_of_items
; i
++)
2200 DirItem
*item
= (DirItem
*) col
->items
[i
].data
;
2202 if (strcmp(item
->leafname
, leaf
) == 0)
2204 if (col
->cursor_item
!= -1)
2205 collection_set_cursor_item(col
, i
);
2207 collection_wink_item(col
, i
);
2212 filer_window
->auto_select
= g_strdup(leaf
);
2215 static void filer_set_title(FilerWindow
*filer_window
)
2217 if (filer_window
->scanning
)
2221 title
= g_strdup_printf(_("%s (Scanning)"), filer_window
->path
);
2222 gtk_window_set_title(GTK_WINDOW(filer_window
->window
),
2227 gtk_window_set_title(GTK_WINDOW(filer_window
->window
),
2228 filer_window
->path
);