4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 1999, Thomas Leonard, <tal197@ecs.soton.ac.uk>.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
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 */
35 #include <gdk/gdkkeysyms.h>
36 #include "collection.h"
40 #include "gui_support.h"
51 #define ROW_HEIGHT_LARGE 64
52 #define ROW_HEIGHT_SMALL 32
53 #define ROW_HEIGHT_FULL_INFO 44
54 #define SMALL_ICON_HEIGHT 20
55 #define SMALL_ICON_WIDTH 48
56 #define MAX_ICON_HEIGHT 42
57 #define MAX_ICON_WIDTH 48
58 #define PANEL_BORDER 2
59 #define MIN_ITEM_WIDTH 64
61 extern int collection_menu_button
;
62 extern gboolean collection_single_click
;
64 FilerWindow
*window_with_focus
= NULL
;
65 GdkFont
*fixed_font
= NULL
;
66 GList
*all_filer_windows
= NULL
;
68 static FilerWindow
*window_with_selection
= NULL
;
71 static GtkWidget
*create_options();
72 static void update_options();
73 static void set_options();
74 static void save_options();
75 static char *filer_single_click(char *data
);
76 static char *filer_ro_bindings(char *data
);
77 static char *filer_toolbar(char *data
);
79 static OptionsSection options
=
81 "Filer window options",
87 static gboolean o_toolbar
= TRUE
;
88 static GtkWidget
*toggle_toolbar
;
89 static gboolean o_single_click
= FALSE
;
90 static GtkWidget
*toggle_single_click
;
91 static gboolean o_ro_bindings
= FALSE
;
92 static GtkWidget
*toggle_ro_bindings
;
94 /* Static prototypes */
95 static void attach(FilerWindow
*filer_window
);
96 static void detach(FilerWindow
*filer_window
);
97 static void filer_window_destroyed(GtkWidget
*widget
,
98 FilerWindow
*filer_window
);
99 void show_menu(Collection
*collection
, GdkEventButton
*event
,
100 int number_selected
, gpointer user_data
);
101 static gint
focus_in(GtkWidget
*widget
,
102 GdkEventFocus
*event
,
103 FilerWindow
*filer_window
);
104 static gint
focus_out(GtkWidget
*widget
,
105 GdkEventFocus
*event
,
106 FilerWindow
*filer_window
);
107 static void add_item(FilerWindow
*filer_window
, DirItem
*item
);
108 static void toolbar_up_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
);
109 static void toolbar_home_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
);
110 static void add_button(GtkContainer
*box
, int pixmap
,
111 GtkSignalFunc cb
, gpointer data
);
112 static GtkWidget
*create_toolbar(FilerWindow
*filer_window
);
113 static int filer_confirm_close(GtkWidget
*widget
, GdkEvent
*event
,
114 FilerWindow
*window
);
115 static int calc_width(FilerWindow
*filer_window
, DirItem
*item
);
116 static void draw_large_icon(GtkWidget
*widget
,
120 static void draw_string(GtkWidget
*widget
,
127 static void draw_item_large(GtkWidget
*widget
,
128 CollectionItem
*item
,
130 static void draw_item_small(GtkWidget
*widget
,
131 CollectionItem
*item
,
133 static void draw_item_full_info(GtkWidget
*widget
,
134 CollectionItem
*colitem
,
136 static gboolean
test_point_large(Collection
*collection
,
137 int point_x
, int point_y
,
138 CollectionItem
*item
,
139 int width
, int height
);
140 static gboolean
test_point_small(Collection
*collection
,
141 int point_x
, int point_y
,
142 CollectionItem
*item
,
143 int width
, int height
);
144 static gboolean
test_point_full_info(Collection
*collection
,
145 int point_x
, int point_y
,
146 CollectionItem
*item
,
147 int width
, int height
);
148 static void update_display(Directory
*dir
,
151 FilerWindow
*filer_window
);
152 void filer_change_to(FilerWindow
*filer_window
, char *path
);
153 static void shrink_width(FilerWindow
*filer_window
);
154 static gboolean
may_rescan(FilerWindow
*filer_window
, gboolean warning
);
156 static GdkAtom xa_string
;
163 static GdkCursor
*busy_cursor
= NULL
;
167 xa_string
= gdk_atom_intern("STRING", FALSE
);
169 options_sections
= g_slist_prepend(options_sections
, &options
);
170 option_register("filer_ro_bindings", filer_ro_bindings
);
171 option_register("filer_single_click", filer_single_click
);
172 option_register("filer_toolbar", filer_toolbar
);
174 fixed_font
= gdk_font_load("fixed");
176 busy_cursor
= gdk_cursor_new(GDK_WATCH
);
179 static gboolean
if_deleted(gpointer item
, gpointer removed
)
181 int i
= ((GPtrArray
*) removed
)->len
;
182 DirItem
**r
= (DirItem
**) ((GPtrArray
*) removed
)->pdata
;
183 char *leafname
= ((DirItem
*) item
)->leafname
;
187 if (strcmp(leafname
, r
[i
]->leafname
) == 0)
194 static void update_item(FilerWindow
*filer_window
, DirItem
*item
)
197 char *leafname
= item
->leafname
;
199 if (leafname
[0] == '.')
201 if (filer_window
->show_hidden
== FALSE
|| leafname
[1] == '\0'
202 || (leafname
[1] == '.' && leafname
[2] == '\0'))
206 i
= collection_find_item(filer_window
->collection
, item
, dir_item_cmp
);
209 collection_draw_item(filer_window
->collection
, i
, TRUE
);
211 g_warning("Failed to find '%s'\n", item
->leafname
);
214 static void update_display(Directory
*dir
,
217 FilerWindow
*filer_window
)
224 for (i
= 0; i
< items
->len
; i
++)
226 DirItem
*item
= (DirItem
*) items
->pdata
[i
];
228 add_item(filer_window
, item
);
231 collection_qsort(filer_window
->collection
,
232 filer_window
->sort_fn
);
235 collection_delete_if(filer_window
->collection
,
240 if (filer_window
->window
->window
)
241 gdk_window_set_cursor(
242 filer_window
->window
->window
,
244 shrink_width(filer_window
);
247 for (i
= 0; i
< items
->len
; i
++)
249 DirItem
*item
= (DirItem
*) items
->pdata
[i
];
251 update_item(filer_window
, item
);
257 static void attach(FilerWindow
*filer_window
)
259 gdk_window_set_cursor(filer_window
->window
->window
, busy_cursor
);
260 collection_clear(filer_window
->collection
);
261 dir_attach(filer_window
->directory
, (DirCallback
) update_display
,
265 static void detach(FilerWindow
*filer_window
)
267 g_return_if_fail(filer_window
->directory
!= NULL
);
269 dir_detach(filer_window
->directory
,
270 (DirCallback
) update_display
, filer_window
);
271 g_fscache_data_unref(dir_cache
, filer_window
->directory
);
272 filer_window
->directory
= NULL
;
275 static void filer_window_destroyed(GtkWidget
*widget
,
276 FilerWindow
*filer_window
)
278 all_filer_windows
= g_list_remove(all_filer_windows
, filer_window
);
280 if (window_with_selection
== filer_window
)
281 window_with_selection
= NULL
;
282 if (window_with_focus
== filer_window
)
283 window_with_focus
= NULL
;
285 if (filer_window
->directory
)
286 detach(filer_window
);
288 g_free(filer_window
->path
);
289 g_free(filer_window
);
291 if (--number_of_windows
< 1)
295 static int calc_width(FilerWindow
*filer_window
, DirItem
*item
)
297 int pix_width
= item
->image
->width
;
299 switch (filer_window
->display_style
)
302 return MAX_ICON_WIDTH
+ 12 +
303 MAX(item
->details_width
, item
->name_width
);
305 return SMALL_ICON_WIDTH
+ 12 + item
->name_width
;
307 return MAX(pix_width
, item
->name_width
) + 4;
311 /* Add a single object to a directory display */
312 static void add_item(FilerWindow
*filer_window
, DirItem
*item
)
314 char *leafname
= item
->leafname
;
317 if (leafname
[0] == '.')
319 if (filer_window
->show_hidden
== FALSE
|| leafname
[1] == '\0'
320 || (leafname
[1] == '.' && leafname
[2] == '\0'))
324 item_width
= calc_width(filer_window
, item
);
325 if (item_width
> filer_window
->collection
->item_width
)
326 collection_set_item_size(filer_window
->collection
,
328 filer_window
->collection
->item_height
);
329 collection_insert(filer_window
->collection
, item
);
332 /* Is a point inside an item? */
333 static gboolean
test_point_large(Collection
*collection
,
334 int point_x
, int point_y
,
335 CollectionItem
*colitem
,
336 int width
, int height
)
338 DirItem
*item
= (DirItem
*) colitem
->data
;
339 GdkFont
*font
= GTK_WIDGET(collection
)->style
->font
;
340 int text_height
= font
->ascent
+ font
->descent
;
341 MaskedPixmap
*image
= item
->image
;
342 int image_y
= MAX(0, MAX_ICON_HEIGHT
- image
->height
);
343 int image_width
= (image
->width
>> 1) + 2;
344 int text_width
= (item
->name_width
>> 1) + 2;
347 if (point_y
< image_y
)
348 return FALSE
; /* Too high up (don't worry about too low) */
350 if (point_y
<= image_y
+ image
->height
+ 2)
351 x_limit
= image_width
;
352 else if (point_y
> height
- text_height
- 2)
353 x_limit
= text_width
;
355 x_limit
= MIN(image_width
, text_width
);
357 return ABS(point_x
- (width
>> 1)) < x_limit
;
360 static gboolean
test_point_full_info(Collection
*collection
,
361 int point_x
, int point_y
,
362 CollectionItem
*colitem
,
363 int width
, int height
)
365 DirItem
*item
= (DirItem
*) colitem
->data
;
366 GdkFont
*font
= GTK_WIDGET(collection
)->style
->font
;
367 MaskedPixmap
*image
= item
->image
;
368 int image_y
= MAX(0, MAX_ICON_HEIGHT
- image
->height
);
370 - fixed_font
->descent
- 2 - fixed_font
->ascent
;
372 if (point_x
< image
->width
+ 2)
373 return point_x
> 2 && point_y
> image_y
;
375 point_x
-= MAX_ICON_WIDTH
+ 8;
377 if (point_y
>= low_top
)
378 return point_x
< item
->details_width
;
379 if (point_y
>= low_top
- font
->ascent
- font
->descent
)
380 return point_x
< item
->name_width
;
384 static gboolean
test_point_small(Collection
*collection
,
385 int point_x
, int point_y
,
386 CollectionItem
*colitem
,
387 int width
, int height
)
389 DirItem
*item
= (DirItem
*) colitem
->data
;
390 MaskedPixmap
*image
= item
->image
;
391 int image_y
= MAX(0, SMALL_ICON_HEIGHT
- image
->height
);
392 GdkFont
*font
= GTK_WIDGET(collection
)->style
->font
;
394 - fixed_font
->descent
- 2 - font
->ascent
;
395 int iwidth
= MIN(SMALL_ICON_WIDTH
, image
->width
);
397 if (point_x
< iwidth
+ 2)
398 return point_x
> 2 && point_y
> image_y
;
400 point_x
-= SMALL_ICON_WIDTH
+ 4;
402 if (point_y
>= low_top
)
403 return point_x
< item
->name_width
;
407 static void draw_small_icon(GtkWidget
*widget
,
412 MaskedPixmap
*image
= item
->image
;
413 int width
= MIN(image
->width
, SMALL_ICON_WIDTH
);
414 int height
= MIN(image
->height
, SMALL_ICON_HEIGHT
);
415 int image_x
= area
->x
+ ((area
->width
- width
) >> 1);
417 GdkGC
*gc
= selected
? widget
->style
->white_gc
418 : widget
->style
->black_gc
;
422 gdk_gc_set_clip_mask(gc
, item
->image
->mask
);
424 image_y
= MAX(0, SMALL_ICON_HEIGHT
- image
->height
);
425 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ image_y
);
426 gdk_draw_pixmap(widget
->window
, gc
,
428 0, 0, /* Source x,y */
429 image_x
, area
->y
+ image_y
, /* Dest x,y */
434 gdk_gc_set_function(gc
, GDK_INVERT
);
435 gdk_draw_rectangle(widget
->window
,
437 TRUE
, image_x
, area
->y
+ image_y
,
439 gdk_gc_set_function(gc
, GDK_COPY
);
442 if (item
->flags
& ITEM_FLAG_SYMLINK
)
444 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ 8);
445 gdk_gc_set_clip_mask(gc
,
446 default_pixmap
[TYPE_SYMLINK
].mask
);
447 gdk_draw_pixmap(widget
->window
, gc
,
448 default_pixmap
[TYPE_SYMLINK
].pixmap
,
449 0, 0, /* Source x,y */
450 image_x
, area
->y
+ 8, /* Dest x,y */
453 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
455 int type
= item
->flags
& ITEM_FLAG_MOUNTED
458 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ 8);
459 gdk_gc_set_clip_mask(gc
,
460 default_pixmap
[type
].mask
);
461 gdk_draw_pixmap(widget
->window
, gc
,
462 default_pixmap
[type
].pixmap
,
463 0, 0, /* Source x,y */
464 image_x
, area
->y
+ 8, /* Dest x,y */
468 gdk_gc_set_clip_mask(gc
, NULL
);
469 gdk_gc_set_clip_origin(gc
, 0, 0);
472 static void draw_large_icon(GtkWidget
*widget
,
477 MaskedPixmap
*image
= item
->image
;
478 int width
= MIN(image
->width
, MAX_ICON_WIDTH
);
479 int height
= MIN(image
->height
, MAX_ICON_WIDTH
);
480 int image_x
= area
->x
+ ((area
->width
- width
) >> 1);
482 GdkGC
*gc
= selected
? widget
->style
->white_gc
483 : widget
->style
->black_gc
;
485 gdk_gc_set_clip_mask(gc
, item
->image
->mask
);
487 image_y
= MAX(0, MAX_ICON_HEIGHT
- image
->height
);
488 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ image_y
);
489 gdk_draw_pixmap(widget
->window
, gc
,
491 0, 0, /* Source x,y */
492 image_x
, area
->y
+ image_y
, /* Dest x,y */
497 gdk_gc_set_function(gc
, GDK_INVERT
);
498 gdk_draw_rectangle(widget
->window
,
500 TRUE
, image_x
, area
->y
+ image_y
,
502 gdk_gc_set_function(gc
, GDK_COPY
);
505 if (item
->flags
& ITEM_FLAG_SYMLINK
)
507 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ 8);
508 gdk_gc_set_clip_mask(gc
,
509 default_pixmap
[TYPE_SYMLINK
].mask
);
510 gdk_draw_pixmap(widget
->window
, gc
,
511 default_pixmap
[TYPE_SYMLINK
].pixmap
,
512 0, 0, /* Source x,y */
513 image_x
, area
->y
+ 8, /* Dest x,y */
516 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
518 int type
= item
->flags
& ITEM_FLAG_MOUNTED
521 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ 8);
522 gdk_gc_set_clip_mask(gc
,
523 default_pixmap
[type
].mask
);
524 gdk_draw_pixmap(widget
->window
, gc
,
525 default_pixmap
[type
].pixmap
,
526 0, 0, /* Source x,y */
527 image_x
, area
->y
+ 8, /* Dest x,y */
531 gdk_gc_set_clip_mask(gc
, NULL
);
532 gdk_gc_set_clip_origin(gc
, 0, 0);
535 static void draw_string(GtkWidget
*widget
,
543 int text_height
= font
->ascent
+ font
->descent
;
546 gtk_paint_flat_box(widget
->style
, widget
->window
,
547 GTK_STATE_SELECTED
, GTK_SHADOW_NONE
,
548 NULL
, widget
, "text",
553 gdk_draw_text(widget
->window
,
555 selected
? widget
->style
->white_gc
556 : widget
->style
->black_gc
,
558 string
, strlen(string
));
561 /* Return a string (valid until next call) giving details
564 char *details(DirItem
*item
)
566 mode_t m
= item
->mode
;
567 static GString
*buf
= NULL
;
570 buf
= g_string_new(NULL
);
572 g_string_sprintf(buf
, "%s, %c%c%c:%c%c%c:%c%c%c:%c%c"
578 S_ISCHR(m
) ? "Char" :
579 S_ISBLK(m
) ? "Blck" :
580 S_ISLNK(m
) ? "Link" :
581 S_ISSOCK(m
) ? "Sock" :
582 S_ISFIFO(m
) ? "Pipe" : "File",
584 m
& S_IRUSR
? 'r' : '-',
585 m
& S_IWUSR
? 'w' : '-',
586 m
& S_IXUSR
? 'x' : '-',
588 m
& S_IRGRP
? 'r' : '-',
589 m
& S_IWGRP
? 'w' : '-',
590 m
& S_IXGRP
? 'x' : '-',
592 m
& S_IROTH
? 'r' : '-',
593 m
& S_IWOTH
? 'w' : '-',
594 m
& S_IXOTH
? 'x' : '-',
596 m
& S_ISUID
? 'U' : '-',
597 m
& S_ISGID
? 'G' : '-',
599 m
& S_ISVTX
? 'T' : '-',
601 format_size(item
->size
));
606 static void draw_item_full_info(GtkWidget
*widget
,
607 CollectionItem
*colitem
,
610 DirItem
*item
= (DirItem
*) colitem
->data
;
611 MaskedPixmap
*image
= item
->image
;
612 GdkFont
*font
= widget
->style
->font
;
613 int text_x
= area
->x
+ MAX_ICON_WIDTH
+ 8;
614 int low_text_y
= area
->y
+ area
->height
- fixed_font
->descent
- 2;
615 gboolean selected
= colitem
->selected
;
616 GdkRectangle pic_area
;
618 pic_area
.x
= area
->x
;
619 pic_area
.y
= area
->y
;
620 pic_area
.width
= image
->width
+ 8;
621 pic_area
.height
= area
->height
;
623 draw_large_icon(widget
, &pic_area
, item
, selected
);
629 low_text_y
- font
->descent
- fixed_font
->ascent
,
640 static void draw_item_small(GtkWidget
*widget
,
641 CollectionItem
*colitem
,
644 DirItem
*item
= (DirItem
*) colitem
->data
;
645 GdkFont
*font
= widget
->style
->font
;
646 int text_x
= area
->x
+ SMALL_ICON_WIDTH
+ 4;
647 int low_text_y
= area
->y
+ area
->height
- font
->descent
- 2;
648 gboolean selected
= colitem
->selected
;
649 GdkRectangle pic_area
;
651 pic_area
.x
= area
->x
;
652 pic_area
.y
= area
->y
;
653 pic_area
.width
= SMALL_ICON_WIDTH
;
654 pic_area
.height
= SMALL_ICON_HEIGHT
;
656 draw_small_icon(widget
, &pic_area
, item
, selected
);
667 static void draw_item_large(GtkWidget
*widget
,
668 CollectionItem
*colitem
,
671 DirItem
*item
= (DirItem
*) colitem
->data
;
672 GdkFont
*font
= widget
->style
->font
;
673 int text_x
= area
->x
+ ((area
->width
- item
->name_width
) >> 1);
674 int text_y
= area
->y
+ area
->height
- font
->descent
- 2;
675 gboolean selected
= colitem
->selected
;
677 draw_large_icon(widget
, area
, item
, selected
);
682 text_x
, text_y
, item
->name_width
,
686 void show_menu(Collection
*collection
, GdkEventButton
*event
,
687 int item
, gpointer user_data
)
689 show_filer_menu((FilerWindow
*) user_data
, event
, item
);
692 /* Returns TRUE iff the directory still exits. */
693 static gboolean
may_rescan(FilerWindow
*filer_window
, gboolean warning
)
697 g_return_val_if_fail(filer_window
!= NULL
, FALSE
);
699 /* We do a fresh lookup (rather than update) because the inode may
702 dir
= g_fscache_lookup(dir_cache
, filer_window
->path
);
706 delayed_error("ROX-Filer", "Directory missing/deleted");
707 gtk_widget_destroy(filer_window
->window
);
710 if (dir
== filer_window
->directory
)
711 g_fscache_data_unref(dir_cache
, dir
);
714 detach(filer_window
);
715 filer_window
->directory
= dir
;
716 attach(filer_window
);
722 /* Another app has grabbed the selection */
723 static gint
collection_lose_selection(GtkWidget
*widget
,
724 GdkEventSelection
*event
)
726 if (window_with_selection
&&
727 window_with_selection
->collection
== COLLECTION(widget
))
729 FilerWindow
*filer_window
= window_with_selection
;
730 window_with_selection
= NULL
;
731 collection_clear_selection(filer_window
->collection
);
737 /* Someone wants us to send them the selection */
738 static void selection_get(GtkWidget
*widget
,
739 GtkSelectionData
*selection_data
,
744 GString
*reply
, *header
;
745 FilerWindow
*filer_window
;
747 Collection
*collection
;
749 filer_window
= gtk_object_get_data(GTK_OBJECT(widget
), "filer_window");
751 reply
= g_string_new(NULL
);
752 header
= g_string_new(NULL
);
757 g_string_sprintf(header
, " %s",
758 make_path(filer_window
->path
, "")->str
);
760 case TARGET_URI_LIST
:
761 g_string_sprintf(header
, " file://%s%s",
763 make_path(filer_window
->path
, "")->str
);
767 collection
= filer_window
->collection
;
768 for (i
= 0; i
< collection
->number_of_items
; i
++)
770 if (collection
->items
[i
].selected
)
773 (DirItem
*) collection
->items
[i
].data
;
775 g_string_append(reply
, header
->str
);
776 g_string_append(reply
, item
->leafname
);
779 /* This works, but I don't think I like it... */
780 /* g_string_append_c(reply, ' '); */
782 gtk_selection_data_set(selection_data
, xa_string
,
783 8, reply
->str
+ 1, reply
->len
- 1);
784 g_string_free(reply
, TRUE
);
785 g_string_free(header
, TRUE
);
788 /* No items are now selected. This might be because another app claimed
789 * the selection or because the user unselected all the items.
791 static void lose_selection(Collection
*collection
,
795 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
797 if (window_with_selection
== filer_window
)
799 window_with_selection
= NULL
;
800 gtk_selection_owner_set(NULL
,
801 GDK_SELECTION_PRIMARY
,
806 static void gain_selection(Collection
*collection
,
810 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
812 if (gtk_selection_owner_set(GTK_WIDGET(collection
),
813 GDK_SELECTION_PRIMARY
,
816 window_with_selection
= filer_window
;
819 collection_clear_selection(filer_window
->collection
);
822 int sort_by_name(const void *item1
, const void *item2
)
824 return strcmp((*((DirItem
**)item1
))->leafname
,
825 (*((DirItem
**)item2
))->leafname
);
828 int sort_by_type(const void *item1
, const void *item2
)
830 const DirItem
*i1
= (DirItem
*) ((CollectionItem
*) item1
)->data
;
831 const DirItem
*i2
= (DirItem
*) ((CollectionItem
*) item2
)->data
;
834 int diff
= i1
->base_type
- i2
->base_type
;
837 diff
= (i1
->flags
& ITEM_FLAG_APPDIR
)
838 - (i2
->flags
& ITEM_FLAG_APPDIR
);
840 return diff
> 0 ? 1 : -1;
847 diff
= strcmp(m1
->media_type
, m2
->media_type
);
849 diff
= strcmp(m1
->subtype
, m2
->subtype
);
857 return diff
> 0 ? 1 : -1;
859 return sort_by_name(item1
, item2
);
862 int sort_by_date(const void *item1
, const void *item2
)
864 const DirItem
*i1
= (DirItem
*) ((CollectionItem
*) item1
)->data
;
865 const DirItem
*i2
= (DirItem
*) ((CollectionItem
*) item2
)->data
;
867 return i1
->mtime
> i2
->mtime
? -1 :
868 i1
->mtime
< i2
->mtime
? 1 :
869 sort_by_name(item1
, item2
);
872 int sort_by_size(const void *item1
, const void *item2
)
874 const DirItem
*i1
= (DirItem
*) ((CollectionItem
*) item1
)->data
;
875 const DirItem
*i2
= (DirItem
*) ((CollectionItem
*) item2
)->data
;
877 return i1
->size
> i2
->size
? -1 :
878 i1
->size
< i2
->size
? 1 :
879 sort_by_name(item1
, item2
);
882 void open_item(Collection
*collection
,
883 gpointer item_data
, int item_number
,
886 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
887 DirItem
*item
= (DirItem
*) item_data
;
888 GdkEventButton
*event
;
890 gboolean adjust
; /* do alternative action */
892 collection_wink_item(filer_window
->collection
, item_number
);
894 event
= (GdkEventButton
*) gtk_get_current_event();
895 if (event
->type
== GDK_2BUTTON_PRESS
|| event
->type
== GDK_BUTTON_PRESS
896 || event
->type
== GDK_BUTTON_RELEASE
)
898 shift
= event
->state
& GDK_SHIFT_MASK
;
899 adjust
= (event
->button
!= 1)
900 ^ ((event
->state
& GDK_CONTROL_MASK
) != 0
901 && o_single_click
== 0);
909 filer_openitem(filer_window
, item
, shift
, adjust
);
912 void filer_openitem(FilerWindow
*filer_window
, DirItem
*item
,
913 gboolean shift
, gboolean adjust
)
918 widget
= filer_window
->window
;
919 full_path
= make_path(filer_window
->path
,
920 item
->leafname
)->str
;
922 switch (item
->base_type
)
925 if (item
->flags
& ITEM_FLAG_APPDIR
&& !shift
)
927 run_app(make_path(filer_window
->path
,
928 item
->leafname
)->str
);
929 if (adjust
&& !filer_window
->panel
)
930 gtk_widget_destroy(widget
);
934 if (item
->flags
& ITEM_FLAG_MOUNT_POINT
&& shift
)
936 action_mount(filer_window
, item
);
937 if (item
->flags
& ITEM_FLAG_MOUNTED
)
941 if ((adjust
^ o_ro_bindings
) || filer_window
->panel
)
942 filer_opendir(full_path
, FALSE
, BOTTOM
);
944 filer_change_to(filer_window
, full_path
);
947 if (item
->flags
& ITEM_FLAG_EXEC_FILE
950 char *argv
[] = {NULL
, NULL
};
954 if (spawn_full(argv
, getenv("HOME"), 0))
956 if (adjust
&& !filer_window
->panel
)
957 gtk_widget_destroy(widget
);
960 report_error("ROX-Filer",
961 "Failed to fork() child");
966 MIME_type
*type
= shift
? &text_plain
969 g_return_if_fail(type
!= NULL
);
971 if (type_open(full_path
, type
))
973 if (adjust
&& !filer_window
->panel
)
974 gtk_widget_destroy(widget
);
978 message
= g_string_new(NULL
);
979 g_string_sprintf(message
, "No open "
980 "action specified for files of "
984 report_error("ROX-Filer", message
->str
);
985 g_string_free(message
, TRUE
);
990 report_error("open_item",
991 "I don't know how to open that");
996 static gint
pointer_in(GtkWidget
*widget
,
997 GdkEventCrossing
*event
,
998 FilerWindow
*filer_window
)
1000 may_rescan(filer_window
, TRUE
);
1004 static gint
focus_in(GtkWidget
*widget
,
1005 GdkEventFocus
*event
,
1006 FilerWindow
*filer_window
)
1008 window_with_focus
= filer_window
;
1013 static gint
focus_out(GtkWidget
*widget
,
1014 GdkEventFocus
*event
,
1015 FilerWindow
*filer_window
)
1017 /* TODO: Shade the cursor */
1022 /* Handle keys that can't be bound with the menu */
1023 static gint
key_press_event(GtkWidget
*widget
,
1025 FilerWindow
*filer_window
)
1027 switch (event
->keyval
)
1045 change_to_parent(filer_window
);
1052 static void toolbar_refresh_clicked(GtkWidget
*widget
,
1053 FilerWindow
*filer_window
)
1056 update_dir(filer_window
, TRUE
);
1059 static void toolbar_home_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
)
1061 filer_change_to(filer_window
, getenv("HOME"));
1064 static void toolbar_up_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
)
1066 change_to_parent(filer_window
);
1069 void change_to_parent(FilerWindow
*filer_window
)
1071 filer_change_to(filer_window
, make_path(filer_window
->path
, "..")->str
);
1074 void filer_change_to(FilerWindow
*filer_window
, char *path
)
1076 detach(filer_window
);
1077 g_free(filer_window
->path
);
1078 filer_window
->path
= pathdup(path
);
1080 filer_window
->directory
= g_fscache_lookup(dir_cache
,
1081 filer_window
->path
);
1082 if (filer_window
->directory
)
1084 gtk_window_set_title(GTK_WINDOW(filer_window
->window
),
1085 filer_window
->path
);
1086 attach(filer_window
);
1092 error
= g_strdup_printf("Directory '%s' is not accessible.",
1094 delayed_error("ROX-Filer", error
);
1096 gtk_widget_destroy(filer_window
->window
);
1100 DirItem
*selected_item(Collection
*collection
)
1104 g_return_val_if_fail(collection
!= NULL
, NULL
);
1105 g_return_val_if_fail(IS_COLLECTION(collection
), NULL
);
1106 g_return_val_if_fail(collection
->number_selected
== 1, NULL
);
1108 for (i
= 0; i
< collection
->number_of_items
; i
++)
1109 if (collection
->items
[i
].selected
)
1110 return (DirItem
*) collection
->items
[i
].data
;
1112 g_warning("selected_item: number_selected is wrong\n");
1117 static int filer_confirm_close(GtkWidget
*widget
, GdkEvent
*event
,
1118 FilerWindow
*window
)
1120 /* TODO: We can open lots of these - very irritating! */
1121 return get_choice("Close panel?",
1122 "You have tried to close a panel via the window "
1123 "manager - I usually find that this is accidental... "
1125 2, "Remove", "Cancel") != 0;
1128 /* Make the items as narrow as possible */
1129 static void shrink_width(FilerWindow
*filer_window
)
1132 Collection
*col
= filer_window
->collection
;
1133 int width
= MIN_ITEM_WIDTH
;
1135 DisplayStyle style
= filer_window
->display_style
;
1139 font
= gtk_widget_get_default_style()->font
;
1140 text_height
= font
->ascent
+ font
->descent
;
1142 for (i
= 0; i
< col
->number_of_items
; i
++)
1144 this_width
= calc_width(filer_window
,
1145 (DirItem
*) col
->items
[i
].data
);
1146 if (this_width
> width
)
1150 collection_set_item_size(filer_window
->collection
,
1152 style
== FULL_INFO
? MAX_ICON_HEIGHT
+ 4 :
1153 style
== SMALL_ICONS
? MAX(text_height
, SMALL_ICON_HEIGHT
) + 4
1154 : text_height
+ MAX_ICON_HEIGHT
+ 8);
1157 void filer_set_sort_fn(FilerWindow
*filer_window
,
1158 int (*fn
)(const void *a
, const void *b
))
1160 if (filer_window
->sort_fn
== fn
)
1163 filer_window
->sort_fn
= fn
;
1164 collection_qsort(filer_window
->collection
,
1165 filer_window
->sort_fn
);
1168 void filer_style_set(FilerWindow
*filer_window
, DisplayStyle style
)
1170 if (filer_window
->display_style
== style
)
1173 filer_window
->display_style
= style
;
1177 collection_set_functions(filer_window
->collection
,
1178 draw_item_small
, test_point_small
);
1181 collection_set_functions(filer_window
->collection
,
1182 draw_item_full_info
, test_point_full_info
);
1185 collection_set_functions(filer_window
->collection
,
1186 draw_item_large
, test_point_large
);
1190 shrink_width(filer_window
);
1193 void filer_opendir(char *path
, gboolean panel
, Side panel_side
)
1195 GtkWidget
*hbox
, *scrollbar
, *collection
;
1196 FilerWindow
*filer_window
;
1197 GtkTargetEntry target_table
[] =
1199 {"text/uri-list", 0, TARGET_URI_LIST
},
1200 {"STRING", 0, TARGET_STRING
},
1203 filer_window
= g_new(FilerWindow
, 1);
1204 filer_window
->path
= pathdup(path
);
1206 filer_window
->directory
= g_fscache_lookup(dir_cache
,
1207 filer_window
->path
);
1208 if (!filer_window
->directory
)
1212 error
= g_strdup_printf("Directory '%s' not found.", path
);
1213 delayed_error("ROX-Filer", error
);
1215 g_free(filer_window
->path
);
1216 g_free(filer_window
);
1220 filer_window
->show_hidden
= FALSE
;
1221 filer_window
->panel
= panel
;
1222 filer_window
->panel_side
= panel_side
;
1223 filer_window
->temp_item_selected
= FALSE
;
1224 filer_window
->sort_fn
= sort_by_type
;
1225 filer_window
->flags
= (FilerFlags
) 0;
1226 filer_window
->display_style
= UNKNOWN_STYLE
;
1228 filer_window
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
1229 gtk_window_set_title(GTK_WINDOW(filer_window
->window
),
1230 filer_window
->path
);
1232 collection
= collection_new(NULL
);
1233 gtk_object_set_data(GTK_OBJECT(collection
),
1234 "filer_window", filer_window
);
1235 filer_window
->collection
= COLLECTION(collection
);
1237 gtk_widget_add_events(filer_window
->window
, GDK_ENTER_NOTIFY
);
1238 gtk_signal_connect(GTK_OBJECT(filer_window
->window
),
1239 "enter-notify-event",
1240 GTK_SIGNAL_FUNC(pointer_in
), filer_window
);
1241 gtk_signal_connect(GTK_OBJECT(filer_window
->window
), "focus_in_event",
1242 GTK_SIGNAL_FUNC(focus_in
), filer_window
);
1243 gtk_signal_connect(GTK_OBJECT(filer_window
->window
), "focus_out_event",
1244 GTK_SIGNAL_FUNC(focus_out
), filer_window
);
1245 gtk_signal_connect(GTK_OBJECT(filer_window
->window
), "destroy",
1246 filer_window_destroyed
, filer_window
);
1248 gtk_signal_connect(GTK_OBJECT(filer_window
->collection
), "open_item",
1249 open_item
, filer_window
);
1250 gtk_signal_connect(GTK_OBJECT(collection
), "show_menu",
1251 show_menu
, filer_window
);
1252 gtk_signal_connect(GTK_OBJECT(collection
), "gain_selection",
1253 gain_selection
, filer_window
);
1254 gtk_signal_connect(GTK_OBJECT(collection
), "lose_selection",
1255 lose_selection
, filer_window
);
1256 gtk_signal_connect(GTK_OBJECT(collection
), "drag_selection",
1257 drag_selection
, filer_window
);
1258 gtk_signal_connect(GTK_OBJECT(collection
), "drag_data_get",
1259 drag_data_get
, filer_window
);
1260 gtk_signal_connect(GTK_OBJECT(collection
), "selection_clear_event",
1261 GTK_SIGNAL_FUNC(collection_lose_selection
), NULL
);
1262 gtk_signal_connect (GTK_OBJECT(collection
), "selection_get",
1263 GTK_SIGNAL_FUNC(selection_get
), NULL
);
1264 gtk_selection_add_targets(collection
, GDK_SELECTION_PRIMARY
,
1266 sizeof(target_table
) / sizeof(*target_table
));
1268 filer_style_set(filer_window
, LARGE_ICONS
);
1269 drag_set_dest(collection
);
1273 int swidth
, sheight
, iwidth
, iheight
;
1274 GtkWidget
*frame
, *win
= filer_window
->window
;
1276 collection_set_panel(filer_window
->collection
, TRUE
);
1277 gtk_signal_connect(GTK_OBJECT(filer_window
->window
),
1279 GTK_SIGNAL_FUNC(filer_confirm_close
),
1282 gdk_window_get_size(GDK_ROOT_PARENT(), &swidth
, &sheight
);
1283 iwidth
= filer_window
->collection
->item_width
;
1284 iheight
= filer_window
->collection
->item_height
;
1286 if (panel_side
== TOP
|| panel_side
== BOTTOM
)
1288 int height
= iheight
+ PANEL_BORDER
;
1289 int y
= panel_side
== TOP
1291 : sheight
- height
- PANEL_BORDER
;
1293 gtk_widget_set_usize(collection
, swidth
, height
);
1294 gtk_widget_set_uposition(win
, 0, y
);
1298 int width
= iwidth
+ PANEL_BORDER
;
1299 int x
= panel_side
== LEFT
1301 : swidth
- width
- PANEL_BORDER
;
1303 gtk_widget_set_usize(collection
, width
, sheight
);
1304 gtk_widget_set_uposition(win
, x
, 0);
1307 frame
= gtk_frame_new(NULL
);
1308 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_OUT
);
1309 gtk_container_add(GTK_CONTAINER(frame
), collection
);
1310 gtk_container_add(GTK_CONTAINER(win
), frame
);
1312 gtk_widget_realize(win
);
1313 if (override_redirect
)
1314 gdk_window_set_override_redirect(win
->window
, TRUE
);
1315 make_panel_window(win
->window
);
1319 hbox
= gtk_hbox_new(FALSE
, 0);
1321 gtk_signal_connect(GTK_OBJECT(filer_window
->window
),
1323 GTK_SIGNAL_FUNC(key_press_event
), filer_window
);
1324 gtk_window_set_default_size(GTK_WINDOW(filer_window
->window
),
1325 filer_window
->display_style
== LARGE_ICONS
? 400 : 512,
1326 o_toolbar
? 220 : 200);
1328 gtk_container_add(GTK_CONTAINER(filer_window
->window
),
1332 GtkWidget
*vbox
, *toolbar
;
1335 vbox
= gtk_vbox_new(FALSE
, 0);
1336 gtk_box_pack_start(GTK_BOX(hbox
), vbox
,
1338 toolbar
= create_toolbar(filer_window
);
1339 gtk_box_pack_start(GTK_BOX(vbox
), toolbar
,
1342 gtk_box_pack_start(GTK_BOX(vbox
), collection
,
1346 gtk_box_pack_start(GTK_BOX(hbox
), collection
,
1349 scrollbar
= gtk_vscrollbar_new(COLLECTION(collection
)->vadj
);
1350 gtk_box_pack_start(GTK_BOX(hbox
), scrollbar
, FALSE
, TRUE
, 0);
1351 gtk_accel_group_attach(filer_keys
,
1352 GTK_OBJECT(filer_window
->window
));
1355 number_of_windows
++;
1356 gtk_widget_show_all(filer_window
->window
);
1357 attach(filer_window
);
1359 all_filer_windows
= g_list_prepend(all_filer_windows
, filer_window
);
1362 static GtkWidget
*create_toolbar(FilerWindow
*filer_window
)
1364 GtkWidget
*frame
, *box
;
1366 frame
= gtk_frame_new(NULL
);
1367 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_OUT
);
1369 box
= gtk_hbutton_box_new();
1370 gtk_button_box_set_child_size_default(16, 16);
1371 gtk_hbutton_box_set_spacing_default(2);
1372 gtk_button_box_set_layout(GTK_BUTTON_BOX(box
), GTK_BUTTONBOX_START
);
1373 gtk_container_add(GTK_CONTAINER(frame
), box
);
1374 add_button(GTK_CONTAINER(box
), TOOLBAR_UP_ICON
,
1375 GTK_SIGNAL_FUNC(toolbar_up_clicked
),
1377 add_button(GTK_CONTAINER(box
), TOOLBAR_HOME_ICON
,
1378 GTK_SIGNAL_FUNC(toolbar_home_clicked
),
1380 add_button(GTK_CONTAINER(box
), TOOLBAR_REFRESH_ICON
,
1381 GTK_SIGNAL_FUNC(toolbar_refresh_clicked
),
1387 static void add_button(GtkContainer
*box
, int pixmap
,
1388 GtkSignalFunc cb
, gpointer data
)
1390 GtkWidget
*button
, *icon
;
1392 button
= gtk_button_new();
1393 GTK_WIDGET_UNSET_FLAGS(button
, GTK_CAN_FOCUS
);
1394 gtk_container_add(box
, button
);
1396 icon
= gtk_pixmap_new(default_pixmap
[pixmap
].pixmap
,
1397 default_pixmap
[pixmap
].mask
);
1398 gtk_container_add(GTK_CONTAINER(button
), icon
);
1399 gtk_signal_connect(GTK_OBJECT(button
), "clicked", cb
, data
);
1402 /* Build up some option widgets to go in the options dialog, but don't
1405 static GtkWidget
*create_options()
1409 vbox
= gtk_vbox_new(FALSE
, 0);
1410 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 4);
1412 toggle_ro_bindings
=
1413 gtk_check_button_new_with_label("Use RISC OS mouse bindings");
1414 gtk_box_pack_start(GTK_BOX(vbox
), toggle_ro_bindings
, FALSE
, TRUE
, 0);
1416 toggle_single_click
=
1417 gtk_check_button_new_with_label("Single-click nagivation");
1418 gtk_box_pack_start(GTK_BOX(vbox
), toggle_single_click
, FALSE
, TRUE
, 0);
1421 gtk_check_button_new_with_label("Show toolbar on new windows");
1422 gtk_box_pack_start(GTK_BOX(vbox
), toggle_toolbar
, FALSE
, TRUE
, 0);
1427 /* Reflect current state by changing the widgets in the options box */
1428 static void update_options()
1430 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_ro_bindings
),
1432 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_single_click
),
1434 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_toolbar
),
1438 /* Set current values by reading the states of the widgets in the options box */
1439 static void set_options()
1441 o_ro_bindings
= gtk_toggle_button_get_active(
1442 GTK_TOGGLE_BUTTON(toggle_ro_bindings
));
1444 o_single_click
= gtk_toggle_button_get_active(
1445 GTK_TOGGLE_BUTTON(toggle_single_click
));
1446 collection_single_click
= o_single_click
? TRUE
: FALSE
;
1448 o_toolbar
= gtk_toggle_button_get_active(
1449 GTK_TOGGLE_BUTTON(toggle_toolbar
));
1450 collection_menu_button
= o_ro_bindings
? 2 : 3;
1453 static void save_options()
1455 option_write("filer_ro_bindings", o_ro_bindings
? "1" : "0");
1456 option_write("filer_single_click", o_single_click
? "1" : "0");
1457 option_write("filer_toolbar", o_toolbar
? "1" : "0");
1460 static char *filer_ro_bindings(char *data
)
1462 o_ro_bindings
= atoi(data
) != 0;
1463 collection_menu_button
= o_ro_bindings
? 2 : 3;
1467 static char *filer_single_click(char *data
)
1469 o_single_click
= atoi(data
) != 0;
1470 collection_single_click
= o_single_click
? TRUE
: FALSE
;
1474 static char *filer_toolbar(char *data
)
1476 o_toolbar
= atoi(data
) != 0;
1480 /* Note that filer_window may not exist after this call. */
1481 void update_dir(FilerWindow
*filer_window
, gboolean warning
)
1483 if (may_rescan(filer_window
, warning
))
1484 dir_update(filer_window
->directory
, filer_window
->path
);
1487 void filer_set_hidden(FilerWindow
*filer_window
, gboolean hidden
)
1489 Directory
*dir
= filer_window
->directory
;
1491 if (filer_window
->show_hidden
== hidden
)
1494 filer_window
->show_hidden
= hidden
;
1496 g_fscache_data_ref(dir_cache
, dir
);
1497 detach(filer_window
);
1498 filer_window
->directory
= dir
;
1499 attach(filer_window
);
1502 /* Refresh the various caches even if we don't think we need to */
1503 void full_refresh(void)
1508 /* This path has been mounted/umounted - update all dirs */
1509 void filer_check_mounted(char *path
)
1511 GList
*next
= all_filer_windows
;
1518 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1522 if (strncmp(path
, filer_window
->path
, len
) == 0)
1524 char s
= filer_window
->path
[len
];
1526 if (s
== '/' || s
== '\0')
1527 update_dir(filer_window
, FALSE
);