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 */
34 #include <gdk/gdkkeysyms.h>
35 #include "collection.h"
39 #include "gui_support.h"
49 #define ROW_HEIGHT_LARGE 64
50 #define ROW_HEIGHT_SMALL 32
51 #define ROW_HEIGHT_FULL_INFO 44
52 #define SMALL_ICON_HEIGHT 20
53 #define SMALL_ICON_WIDTH 48
54 #define MAX_ICON_HEIGHT 42
55 #define MAX_ICON_WIDTH 48
56 #define PANEL_BORDER 2
57 #define MIN_ITEM_WIDTH 64
59 extern int collection_menu_button
;
61 FilerWindow
*window_with_focus
= NULL
;
62 GdkFont
*fixed_font
= NULL
;
64 static FilerWindow
*window_with_selection
= NULL
;
67 static GtkWidget
*create_options();
68 static void update_options();
69 static void set_options();
70 static void save_options();
71 static char *filer_ro_bindings(char *data
);
72 static char *filer_toolbar(char *data
);
74 static OptionsSection options
=
76 "Filer window options",
82 static gboolean o_toolbar
= TRUE
;
83 static GtkWidget
*toggle_toolbar
;
84 static gboolean o_ro_bindings
= FALSE
;
85 static GtkWidget
*toggle_ro_bindings
;
87 /* Static prototypes */
88 static void attach(FilerWindow
*filer_window
);
89 static void detach(FilerWindow
*filer_window
);
90 static void filer_window_destroyed(GtkWidget
*widget
,
91 FilerWindow
*filer_window
);
92 void show_menu(Collection
*collection
, GdkEventButton
*event
,
93 int number_selected
, gpointer user_data
);
94 static gint
focus_in(GtkWidget
*widget
,
96 FilerWindow
*filer_window
);
97 static gint
focus_out(GtkWidget
*widget
,
99 FilerWindow
*filer_window
);
100 static void add_item(FilerWindow
*filer_window
, DirItem
*item
);
101 static void toolbar_up_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
);
102 static void toolbar_home_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
);
103 static void add_button(GtkContainer
*box
, int pixmap
,
104 GtkSignalFunc cb
, gpointer data
);
105 static GtkWidget
*create_toolbar(FilerWindow
*filer_window
);
106 static int filer_confirm_close(GtkWidget
*widget
, GdkEvent
*event
,
107 FilerWindow
*window
);
108 static int calc_width(FilerWindow
*filer_window
, DirItem
*item
);
109 static void draw_large_icon(GtkWidget
*widget
,
113 static void draw_string(GtkWidget
*widget
,
120 static void draw_item_large(GtkWidget
*widget
,
121 CollectionItem
*item
,
123 static void draw_item_small(GtkWidget
*widget
,
124 CollectionItem
*item
,
126 static void draw_item_full_info(GtkWidget
*widget
,
127 CollectionItem
*colitem
,
129 static gboolean
test_point_large(Collection
*collection
,
130 int point_x
, int point_y
,
131 CollectionItem
*item
,
132 int width
, int height
);
133 static gboolean
test_point_small(Collection
*collection
,
134 int point_x
, int point_y
,
135 CollectionItem
*item
,
136 int width
, int height
);
137 static gboolean
test_point_full_info(Collection
*collection
,
138 int point_x
, int point_y
,
139 CollectionItem
*item
,
140 int width
, int height
);
141 static void update_display(Directory
*dir
,
144 FilerWindow
*filer_window
);
145 void filer_change_to(FilerWindow
*filer_window
, char *path
);
146 static void shrink_width(FilerWindow
*filer_window
);
148 static GdkAtom xa_string
;
157 xa_string
= gdk_atom_intern("STRING", FALSE
);
159 options_sections
= g_slist_prepend(options_sections
, &options
);
160 option_register("filer_ro_bindings", filer_ro_bindings
);
161 option_register("filer_toolbar", filer_toolbar
);
163 fixed_font
= gdk_font_load("fixed");
166 static gboolean
if_deleted(gpointer item
, gpointer removed
)
168 int i
= ((GPtrArray
*) removed
)->len
;
169 DirItem
**r
= (DirItem
**) ((GPtrArray
*) removed
)->pdata
;
170 char *leafname
= ((DirItem
*) item
)->leafname
;
174 if (strcmp(leafname
, r
[i
]->leafname
) == 0)
181 static void update_item(FilerWindow
*filer_window
, DirItem
*item
)
184 char *leafname
= item
->leafname
;
186 if (leafname
[0] == '.')
188 if (filer_window
->show_hidden
== FALSE
|| leafname
[1] == '\0'
189 || (leafname
[1] == '.' && leafname
[2] == '\0'))
193 i
= collection_find_item(filer_window
->collection
, item
, dir_item_cmp
);
196 collection_draw_item(filer_window
->collection
, i
, TRUE
);
198 g_warning("Failed to find '%s'\n", item
->leafname
);
201 static void update_display(Directory
*dir
,
204 FilerWindow
*filer_window
)
211 for (i
= 0; i
< items
->len
; i
++)
213 DirItem
*item
= (DirItem
*) items
->pdata
[i
];
215 add_item(filer_window
, item
);
218 collection_qsort(filer_window
->collection
,
219 filer_window
->sort_fn
);
222 collection_delete_if(filer_window
->collection
,
227 if (filer_window
->window
->window
)
228 gdk_window_set_cursor(
229 filer_window
->window
->window
,
231 shrink_width(filer_window
);
234 for (i
= 0; i
< items
->len
; i
++)
236 DirItem
*item
= (DirItem
*) items
->pdata
[i
];
238 update_item(filer_window
, item
);
244 static void attach(FilerWindow
*filer_window
)
246 gdk_window_set_cursor(filer_window
->window
->window
,
247 gdk_cursor_new(GDK_WATCH
));
248 collection_clear(filer_window
->collection
);
249 dir_attach(filer_window
->directory
, (DirCallback
) update_display
,
253 static void detach(FilerWindow
*filer_window
)
255 g_return_if_fail(filer_window
->directory
!= NULL
);
257 dir_detach(filer_window
->directory
,
258 (DirCallback
) update_display
, filer_window
);
259 g_fscache_data_unref(dir_cache
, filer_window
->directory
);
260 filer_window
->directory
= NULL
;
263 static void filer_window_destroyed(GtkWidget
*widget
,
264 FilerWindow
*filer_window
)
266 if (window_with_selection
== filer_window
)
267 window_with_selection
= NULL
;
268 if (window_with_focus
== filer_window
)
269 window_with_focus
= NULL
;
271 if (filer_window
->directory
)
272 detach(filer_window
);
274 g_free(filer_window
->path
);
275 g_free(filer_window
);
277 if (--number_of_windows
< 1)
281 static int calc_width(FilerWindow
*filer_window
, DirItem
*item
)
283 int pix_width
= item
->image
->width
;
285 switch (filer_window
->display_style
)
288 return MAX_ICON_WIDTH
+ 12 +
289 MAX(item
->details_width
, item
->name_width
);
291 return SMALL_ICON_WIDTH
+ 12 + item
->name_width
;
293 return MAX(pix_width
, item
->name_width
) + 4;
297 /* Add a single object to a directory display */
298 static void add_item(FilerWindow
*filer_window
, DirItem
*item
)
300 char *leafname
= item
->leafname
;
303 if (leafname
[0] == '.')
305 if (filer_window
->show_hidden
== FALSE
|| leafname
[1] == '\0'
306 || (leafname
[1] == '.' && leafname
[2] == '\0'))
310 item_width
= calc_width(filer_window
, item
);
311 if (item_width
> filer_window
->collection
->item_width
)
312 collection_set_item_size(filer_window
->collection
,
314 filer_window
->collection
->item_height
);
315 collection_insert(filer_window
->collection
, item
);
318 /* Is a point inside an item? */
319 static gboolean
test_point_large(Collection
*collection
,
320 int point_x
, int point_y
,
321 CollectionItem
*colitem
,
322 int width
, int height
)
324 DirItem
*item
= (DirItem
*) colitem
->data
;
325 GdkFont
*font
= GTK_WIDGET(collection
)->style
->font
;
326 int text_height
= font
->ascent
+ font
->descent
;
327 MaskedPixmap
*image
= item
->image
;
328 int image_y
= MAX(0, MAX_ICON_HEIGHT
- image
->height
);
329 int image_width
= (image
->width
>> 1) + 2;
330 int text_width
= (item
->name_width
>> 1) + 2;
333 if (point_y
< image_y
)
334 return FALSE
; /* Too high up (don't worry about too low) */
336 if (point_y
<= image_y
+ image
->height
+ 2)
337 x_limit
= image_width
;
338 else if (point_y
> height
- text_height
- 2)
339 x_limit
= text_width
;
341 x_limit
= MIN(image_width
, text_width
);
343 return ABS(point_x
- (width
>> 1)) < x_limit
;
346 static gboolean
test_point_full_info(Collection
*collection
,
347 int point_x
, int point_y
,
348 CollectionItem
*colitem
,
349 int width
, int height
)
351 DirItem
*item
= (DirItem
*) colitem
->data
;
352 GdkFont
*font
= GTK_WIDGET(collection
)->style
->font
;
353 MaskedPixmap
*image
= item
->image
;
354 int image_y
= MAX(0, MAX_ICON_HEIGHT
- image
->height
);
356 - fixed_font
->descent
- 2 - fixed_font
->ascent
;
358 if (point_x
< image
->width
+ 2)
359 return point_x
> 2 && point_y
> image_y
;
361 point_x
-= MAX_ICON_WIDTH
+ 8;
363 if (point_y
>= low_top
)
364 return point_x
< item
->details_width
;
365 if (point_y
>= low_top
- font
->ascent
- font
->descent
)
366 return point_x
< item
->name_width
;
370 static gboolean
test_point_small(Collection
*collection
,
371 int point_x
, int point_y
,
372 CollectionItem
*colitem
,
373 int width
, int height
)
375 DirItem
*item
= (DirItem
*) colitem
->data
;
376 MaskedPixmap
*image
= item
->image
;
377 int image_y
= MAX(0, SMALL_ICON_HEIGHT
- image
->height
);
378 GdkFont
*font
= GTK_WIDGET(collection
)->style
->font
;
380 - fixed_font
->descent
- 2 - font
->ascent
;
381 int iwidth
= MIN(SMALL_ICON_WIDTH
, image
->width
);
383 if (point_x
< iwidth
+ 2)
384 return point_x
> 2 && point_y
> image_y
;
386 point_x
-= SMALL_ICON_WIDTH
+ 4;
388 if (point_y
>= low_top
)
389 return point_x
< item
->name_width
;
393 static void draw_small_icon(GtkWidget
*widget
,
398 MaskedPixmap
*image
= item
->image
;
399 int width
= MIN(image
->width
, SMALL_ICON_WIDTH
);
400 int height
= MIN(image
->height
, SMALL_ICON_HEIGHT
);
401 int image_x
= area
->x
+ ((area
->width
- width
) >> 1);
403 GdkGC
*gc
= selected
? widget
->style
->white_gc
404 : widget
->style
->black_gc
;
408 gdk_gc_set_clip_mask(gc
, item
->image
->mask
);
410 image_y
= MAX(0, SMALL_ICON_HEIGHT
- image
->height
);
411 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ image_y
);
412 gdk_draw_pixmap(widget
->window
, gc
,
414 0, 0, /* Source x,y */
415 image_x
, area
->y
+ image_y
, /* Dest x,y */
420 gdk_gc_set_function(gc
, GDK_INVERT
);
421 gdk_draw_rectangle(widget
->window
,
423 TRUE
, image_x
, area
->y
+ image_y
,
425 gdk_gc_set_function(gc
, GDK_COPY
);
428 if (item
->flags
& ITEM_FLAG_SYMLINK
)
430 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ 8);
431 gdk_gc_set_clip_mask(gc
,
432 default_pixmap
[TYPE_SYMLINK
].mask
);
433 gdk_draw_pixmap(widget
->window
, gc
,
434 default_pixmap
[TYPE_SYMLINK
].pixmap
,
435 0, 0, /* Source x,y */
436 image_x
, area
->y
+ 8, /* Dest x,y */
439 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
441 int type
= item
->flags
& ITEM_FLAG_MOUNTED
444 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ 8);
445 gdk_gc_set_clip_mask(gc
,
446 default_pixmap
[type
].mask
);
447 gdk_draw_pixmap(widget
->window
, gc
,
448 default_pixmap
[type
].pixmap
,
449 0, 0, /* Source x,y */
450 image_x
, area
->y
+ 8, /* Dest x,y */
454 gdk_gc_set_clip_mask(gc
, NULL
);
455 gdk_gc_set_clip_origin(gc
, 0, 0);
458 static void draw_large_icon(GtkWidget
*widget
,
463 MaskedPixmap
*image
= item
->image
;
464 int width
= MIN(image
->width
, MAX_ICON_WIDTH
);
465 int height
= MIN(image
->height
, MAX_ICON_WIDTH
);
466 int image_x
= area
->x
+ ((area
->width
- width
) >> 1);
468 GdkGC
*gc
= selected
? widget
->style
->white_gc
469 : widget
->style
->black_gc
;
471 gdk_gc_set_clip_mask(gc
, item
->image
->mask
);
473 image_y
= MAX(0, MAX_ICON_HEIGHT
- image
->height
);
474 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ image_y
);
475 gdk_draw_pixmap(widget
->window
, gc
,
477 0, 0, /* Source x,y */
478 image_x
, area
->y
+ image_y
, /* Dest x,y */
483 gdk_gc_set_function(gc
, GDK_INVERT
);
484 gdk_draw_rectangle(widget
->window
,
486 TRUE
, image_x
, area
->y
+ image_y
,
488 gdk_gc_set_function(gc
, GDK_COPY
);
491 if (item
->flags
& ITEM_FLAG_SYMLINK
)
493 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ 8);
494 gdk_gc_set_clip_mask(gc
,
495 default_pixmap
[TYPE_SYMLINK
].mask
);
496 gdk_draw_pixmap(widget
->window
, gc
,
497 default_pixmap
[TYPE_SYMLINK
].pixmap
,
498 0, 0, /* Source x,y */
499 image_x
, area
->y
+ 8, /* Dest x,y */
502 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
504 int type
= item
->flags
& ITEM_FLAG_MOUNTED
507 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ 8);
508 gdk_gc_set_clip_mask(gc
,
509 default_pixmap
[type
].mask
);
510 gdk_draw_pixmap(widget
->window
, gc
,
511 default_pixmap
[type
].pixmap
,
512 0, 0, /* Source x,y */
513 image_x
, area
->y
+ 8, /* Dest x,y */
517 gdk_gc_set_clip_mask(gc
, NULL
);
518 gdk_gc_set_clip_origin(gc
, 0, 0);
521 static void draw_string(GtkWidget
*widget
,
529 int text_height
= font
->ascent
+ font
->descent
;
532 gtk_paint_flat_box(widget
->style
, widget
->window
,
533 GTK_STATE_SELECTED
, GTK_SHADOW_NONE
,
534 NULL
, widget
, "text",
539 gdk_draw_text(widget
->window
,
541 selected
? widget
->style
->white_gc
542 : widget
->style
->black_gc
,
544 string
, strlen(string
));
547 /* Return a string (valid until next call) giving details
550 char *details(DirItem
*item
)
552 mode_t m
= item
->mode
;
553 static GString
*buf
= NULL
;
556 buf
= g_string_new(NULL
);
558 g_string_sprintf(buf
, "%s, %c%c%c:%c%c%c:%c%c%c:%c%c"
564 S_ISCHR(m
) ? "Char" :
565 S_ISBLK(m
) ? "Blck" :
566 S_ISLNK(m
) ? "Link" :
567 S_ISSOCK(m
) ? "Sock" :
568 S_ISFIFO(m
) ? "Pipe" : "File",
570 m
& S_IRUSR
? 'r' : '-',
571 m
& S_IWUSR
? 'w' : '-',
572 m
& S_IXUSR
? 'x' : '-',
574 m
& S_IRGRP
? 'r' : '-',
575 m
& S_IWGRP
? 'w' : '-',
576 m
& S_IXGRP
? 'x' : '-',
578 m
& S_IROTH
? 'r' : '-',
579 m
& S_IWOTH
? 'w' : '-',
580 m
& S_IXOTH
? 'x' : '-',
582 m
& S_ISUID
? 'U' : '-',
583 m
& S_ISGID
? 'G' : '-',
585 m
& S_ISVTX
? 'T' : '-',
587 format_size(item
->size
));
592 static void draw_item_full_info(GtkWidget
*widget
,
593 CollectionItem
*colitem
,
596 DirItem
*item
= (DirItem
*) colitem
->data
;
597 MaskedPixmap
*image
= item
->image
;
598 GdkFont
*font
= widget
->style
->font
;
599 int text_x
= area
->x
+ MAX_ICON_WIDTH
+ 8;
600 int low_text_y
= area
->y
+ area
->height
- fixed_font
->descent
- 2;
601 gboolean selected
= colitem
->selected
;
602 GdkRectangle pic_area
;
604 pic_area
.x
= area
->x
;
605 pic_area
.y
= area
->y
;
606 pic_area
.width
= image
->width
+ 8;
607 pic_area
.height
= area
->height
;
609 draw_large_icon(widget
, &pic_area
, item
, selected
);
615 low_text_y
- font
->descent
- fixed_font
->ascent
,
626 static void draw_item_small(GtkWidget
*widget
,
627 CollectionItem
*colitem
,
630 DirItem
*item
= (DirItem
*) colitem
->data
;
631 GdkFont
*font
= widget
->style
->font
;
632 int text_x
= area
->x
+ SMALL_ICON_WIDTH
+ 4;
633 int low_text_y
= area
->y
+ area
->height
- font
->descent
- 2;
634 gboolean selected
= colitem
->selected
;
635 GdkRectangle pic_area
;
637 pic_area
.x
= area
->x
;
638 pic_area
.y
= area
->y
;
639 pic_area
.width
= SMALL_ICON_WIDTH
;
640 pic_area
.height
= SMALL_ICON_HEIGHT
;
642 draw_small_icon(widget
, &pic_area
, item
, selected
);
653 static void draw_item_large(GtkWidget
*widget
,
654 CollectionItem
*colitem
,
657 DirItem
*item
= (DirItem
*) colitem
->data
;
658 GdkFont
*font
= widget
->style
->font
;
659 int text_x
= area
->x
+ ((area
->width
- item
->name_width
) >> 1);
660 int text_y
= area
->y
+ area
->height
- font
->descent
- 2;
661 gboolean selected
= colitem
->selected
;
663 draw_large_icon(widget
, area
, item
, selected
);
668 text_x
, text_y
, item
->name_width
,
672 void show_menu(Collection
*collection
, GdkEventButton
*event
,
673 int item
, gpointer user_data
)
675 show_filer_menu((FilerWindow
*) user_data
, event
, item
);
678 /* Returns TRUE iff the directory still exits. */
679 static gboolean
may_rescan(FilerWindow
*filer_window
)
683 g_return_val_if_fail(filer_window
!= NULL
, FALSE
);
685 /* We do a fresh lookup (rather than update) because the inode may
688 dir
= g_fscache_lookup(dir_cache
, filer_window
->path
);
691 delayed_error("ROX-Filer", "Directory missing/deleted");
692 gtk_widget_destroy(filer_window
->window
);
695 if (dir
== filer_window
->directory
)
696 g_fscache_data_unref(dir_cache
, dir
);
699 detach(filer_window
);
700 filer_window
->directory
= dir
;
701 attach(filer_window
);
707 /* Callback to collection_delete_if() */
709 static gboolean
remove_deleted(gpointer item_data
, gpointer data
)
711 DirItem
*item
= (DirItem
*) item_data
;
713 if (item
->flags
& ITEM_FLAG_MAY_DELETE
)
723 /* Another app has grabbed the selection */
724 static gint
collection_lose_selection(GtkWidget
*widget
,
725 GdkEventSelection
*event
)
727 if (window_with_selection
&&
728 window_with_selection
->collection
== COLLECTION(widget
))
730 FilerWindow
*filer_window
= window_with_selection
;
731 window_with_selection
= NULL
;
732 collection_clear_selection(filer_window
->collection
);
738 /* Someone wants us to send them the selection */
739 static void selection_get(GtkWidget
*widget
,
740 GtkSelectionData
*selection_data
,
745 GString
*reply
, *header
;
746 FilerWindow
*filer_window
;
748 Collection
*collection
;
750 filer_window
= gtk_object_get_data(GTK_OBJECT(widget
), "filer_window");
752 reply
= g_string_new(NULL
);
753 header
= g_string_new(NULL
);
758 g_string_sprintf(header
, " %s",
759 make_path(filer_window
->path
, "")->str
);
761 case TARGET_URI_LIST
:
762 g_string_sprintf(header
, " file://%s%s",
764 make_path(filer_window
->path
, "")->str
);
768 collection
= filer_window
->collection
;
769 for (i
= 0; i
< collection
->number_of_items
; i
++)
771 if (collection
->items
[i
].selected
)
774 (DirItem
*) collection
->items
[i
].data
;
776 g_string_append(reply
, header
->str
);
777 g_string_append(reply
, item
->leafname
);
780 /* This works, but I don't think I like it... */
781 /* g_string_append_c(reply, ' '); */
783 gtk_selection_data_set(selection_data
, xa_string
,
784 8, reply
->str
+ 1, reply
->len
- 1);
785 g_string_free(reply
, TRUE
);
786 g_string_free(header
, TRUE
);
789 /* No items are now selected. This might be because another app claimed
790 * the selection or because the user unselected all the items.
792 static void lose_selection(Collection
*collection
,
796 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
798 if (window_with_selection
== filer_window
)
800 window_with_selection
= NULL
;
801 gtk_selection_owner_set(NULL
,
802 GDK_SELECTION_PRIMARY
,
807 static void gain_selection(Collection
*collection
,
811 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
813 if (gtk_selection_owner_set(GTK_WIDGET(collection
),
814 GDK_SELECTION_PRIMARY
,
817 window_with_selection
= filer_window
;
820 collection_clear_selection(filer_window
->collection
);
823 int sort_by_name(const void *item1
, const void *item2
)
825 return strcmp((*((DirItem
**)item1
))->leafname
,
826 (*((DirItem
**)item2
))->leafname
);
829 int sort_by_type(const void *item1
, const void *item2
)
831 const DirItem
*i1
= (DirItem
*) ((CollectionItem
*) item1
)->data
;
832 const DirItem
*i2
= (DirItem
*) ((CollectionItem
*) item2
)->data
;
835 int diff
= i1
->base_type
- i2
->base_type
;
838 diff
= (i1
->flags
& ITEM_FLAG_APPDIR
)
839 - (i2
->flags
& ITEM_FLAG_APPDIR
);
841 return diff
> 0 ? 1 : -1;
848 diff
= strcmp(m1
->media_type
, m2
->media_type
);
850 diff
= strcmp(m1
->subtype
, m2
->subtype
);
858 return diff
> 0 ? 1 : -1;
860 return sort_by_name(item1
, item2
);
863 int sort_by_date(const void *item1
, const void *item2
)
865 const DirItem
*i1
= (DirItem
*) ((CollectionItem
*) item1
)->data
;
866 const DirItem
*i2
= (DirItem
*) ((CollectionItem
*) item2
)->data
;
868 return i1
->mtime
> i2
->mtime
? -1 :
869 i1
->mtime
< i2
->mtime
? 1 :
870 sort_by_name(item1
, item2
);
873 int sort_by_size(const void *item1
, const void *item2
)
875 const DirItem
*i1
= (DirItem
*) ((CollectionItem
*) item1
)->data
;
876 const DirItem
*i2
= (DirItem
*) ((CollectionItem
*) item2
)->data
;
878 return i1
->size
> i2
->size
? -1 :
879 i1
->size
< i2
->size
? 1 :
880 sort_by_name(item1
, item2
);
883 void open_item(Collection
*collection
,
884 gpointer item_data
, int item_number
,
887 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
888 DirItem
*item
= (DirItem
*) item_data
;
889 GdkEventButton
*event
;
893 gboolean adjust
; /* do alternative action */
895 event
= (GdkEventButton
*) gtk_get_current_event();
896 full_path
= make_path(filer_window
->path
,
897 item
->leafname
)->str
;
899 collection_wink_item(filer_window
->collection
, item_number
);
901 if (event
->type
== GDK_2BUTTON_PRESS
|| event
->type
== GDK_BUTTON_PRESS
)
903 shift
= event
->state
& GDK_SHIFT_MASK
;
904 adjust
= (event
->button
!= 1)
905 ^ ((event
->state
& GDK_CONTROL_MASK
) != 0);
913 widget
= filer_window
->window
;
915 switch (item
->base_type
)
918 if (item
->flags
& ITEM_FLAG_APPDIR
&& !shift
)
920 run_app(make_path(filer_window
->path
,
921 item
->leafname
)->str
);
922 if (adjust
&& !filer_window
->panel
)
923 gtk_widget_destroy(widget
);
926 if ((adjust
^ o_ro_bindings
) || filer_window
->panel
)
927 filer_opendir(full_path
, FALSE
, BOTTOM
);
929 filer_change_to(filer_window
, full_path
);
932 if (item
->flags
& ITEM_FLAG_EXEC_FILE
935 char *argv
[] = {NULL
, NULL
};
939 if (spawn_full(argv
, getenv("HOME"), 0))
941 if (adjust
&& !filer_window
->panel
)
942 gtk_widget_destroy(widget
);
945 report_error("ROX-Filer",
946 "Failed to fork() child");
951 MIME_type
*type
= shift
? &text_plain
954 g_return_if_fail(type
!= NULL
);
956 if (type_open(full_path
, type
))
958 if (adjust
&& !filer_window
->panel
)
959 gtk_widget_destroy(widget
);
963 message
= g_string_new(NULL
);
964 g_string_sprintf(message
, "No open "
965 "action specified for files of "
969 report_error("ROX-Filer", message
->str
);
970 g_string_free(message
, TRUE
);
975 report_error("open_item",
976 "I don't know how to open that");
981 static gint
pointer_in(GtkWidget
*widget
,
982 GdkEventCrossing
*event
,
983 FilerWindow
*filer_window
)
985 may_rescan(filer_window
);
989 static gint
focus_in(GtkWidget
*widget
,
990 GdkEventFocus
*event
,
991 FilerWindow
*filer_window
)
993 window_with_focus
= filer_window
;
998 static gint
focus_out(GtkWidget
*widget
,
999 GdkEventFocus
*event
,
1000 FilerWindow
*filer_window
)
1002 /* TODO: Shade the cursor */
1007 /* Handle keys that can't be bound with the menu */
1008 static gint
key_press_event(GtkWidget
*widget
,
1010 FilerWindow
*filer_window
)
1012 switch (event
->keyval
)
1030 change_to_parent(filer_window
);
1037 static void toolbar_home_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
)
1039 filer_change_to(filer_window
, getenv("HOME"));
1042 static void toolbar_up_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
)
1044 change_to_parent(filer_window
);
1047 void change_to_parent(FilerWindow
*filer_window
)
1049 filer_change_to(filer_window
, make_path(filer_window
->path
, "..")->str
);
1052 void filer_change_to(FilerWindow
*filer_window
, char *path
)
1054 detach(filer_window
);
1055 g_free(filer_window
->path
);
1056 filer_window
->path
= pathdup(path
);
1058 filer_window
->directory
= g_fscache_lookup(dir_cache
,
1059 filer_window
->path
);
1060 if (filer_window
->directory
)
1062 gtk_window_set_title(GTK_WINDOW(filer_window
->window
),
1063 filer_window
->path
);
1064 attach(filer_window
);
1070 error
= g_strdup_printf("Directory '%s' is not accessible.",
1072 delayed_error("ROX-Filer", error
);
1074 gtk_widget_destroy(filer_window
->window
);
1078 DirItem
*selected_item(Collection
*collection
)
1082 g_return_val_if_fail(collection
!= NULL
, NULL
);
1083 g_return_val_if_fail(IS_COLLECTION(collection
), NULL
);
1084 g_return_val_if_fail(collection
->number_selected
== 1, NULL
);
1086 for (i
= 0; i
< collection
->number_of_items
; i
++)
1087 if (collection
->items
[i
].selected
)
1088 return (DirItem
*) collection
->items
[i
].data
;
1090 g_warning("selected_item: number_selected is wrong\n");
1095 static int filer_confirm_close(GtkWidget
*widget
, GdkEvent
*event
,
1096 FilerWindow
*window
)
1098 /* TODO: We can open lots of these - very irritating! */
1099 return get_choice("Close panel?",
1100 "You have tried to close a panel via the window "
1101 "manager - I usually find that this is accidental... "
1103 2, "Remove", "Cancel") != 0;
1106 /* Make the items as narrow as possible */
1107 static void shrink_width(FilerWindow
*filer_window
)
1110 Collection
*col
= filer_window
->collection
;
1111 int width
= MIN_ITEM_WIDTH
;
1113 DisplayStyle style
= filer_window
->display_style
;
1117 font
= gtk_widget_get_default_style()->font
;
1118 text_height
= font
->ascent
+ font
->descent
;
1120 for (i
= 0; i
< col
->number_of_items
; i
++)
1122 this_width
= calc_width(filer_window
,
1123 (DirItem
*) col
->items
[i
].data
);
1124 if (this_width
> width
)
1128 collection_set_item_size(filer_window
->collection
,
1130 style
== FULL_INFO
? MAX_ICON_HEIGHT
+ 4 :
1131 style
== SMALL_ICONS
? MAX(text_height
, SMALL_ICON_HEIGHT
) + 4
1132 : text_height
+ MAX_ICON_HEIGHT
+ 8);
1135 void filer_set_sort_fn(FilerWindow
*filer_window
,
1136 int (*fn
)(const void *a
, const void *b
))
1138 if (filer_window
->sort_fn
== fn
)
1141 filer_window
->sort_fn
= fn
;
1142 collection_qsort(filer_window
->collection
,
1143 filer_window
->sort_fn
);
1146 void filer_style_set(FilerWindow
*filer_window
, DisplayStyle style
)
1148 if (filer_window
->display_style
== style
)
1151 filer_window
->display_style
= style
;
1155 collection_set_functions(filer_window
->collection
,
1156 draw_item_small
, test_point_small
);
1159 collection_set_functions(filer_window
->collection
,
1160 draw_item_full_info
, test_point_full_info
);
1163 collection_set_functions(filer_window
->collection
,
1164 draw_item_large
, test_point_large
);
1168 shrink_width(filer_window
);
1171 void filer_opendir(char *path
, gboolean panel
, Side panel_side
)
1173 GtkWidget
*hbox
, *scrollbar
, *collection
;
1174 FilerWindow
*filer_window
;
1175 GtkTargetEntry target_table
[] =
1177 {"text/uri-list", 0, TARGET_URI_LIST
},
1178 {"STRING", 0, TARGET_STRING
},
1181 filer_window
= g_new(FilerWindow
, 1);
1182 filer_window
->path
= pathdup(path
);
1184 filer_window
->directory
= g_fscache_lookup(dir_cache
,
1185 filer_window
->path
);
1186 if (!filer_window
->directory
)
1190 error
= g_strdup_printf("Directory '%s' not found.", path
);
1191 delayed_error("ROX-Filer", error
);
1193 g_free(filer_window
->path
);
1194 g_free(filer_window
);
1198 filer_window
->show_hidden
= FALSE
;
1199 filer_window
->panel
= panel
;
1200 filer_window
->panel_side
= panel_side
;
1201 filer_window
->temp_item_selected
= FALSE
;
1202 filer_window
->sort_fn
= sort_by_type
;
1203 filer_window
->flags
= (FilerFlags
) 0;
1204 filer_window
->display_style
= UNKNOWN_STYLE
;
1206 filer_window
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
1207 gtk_window_set_title(GTK_WINDOW(filer_window
->window
),
1208 filer_window
->path
);
1210 collection
= collection_new(NULL
);
1211 gtk_object_set_data(GTK_OBJECT(collection
),
1212 "filer_window", filer_window
);
1213 filer_window
->collection
= COLLECTION(collection
);
1215 gtk_widget_add_events(filer_window
->window
, GDK_ENTER_NOTIFY
);
1216 gtk_signal_connect(GTK_OBJECT(filer_window
->window
),
1217 "enter-notify-event",
1218 GTK_SIGNAL_FUNC(pointer_in
), filer_window
);
1219 gtk_signal_connect(GTK_OBJECT(filer_window
->window
), "focus_in_event",
1220 GTK_SIGNAL_FUNC(focus_in
), filer_window
);
1221 gtk_signal_connect(GTK_OBJECT(filer_window
->window
), "focus_out_event",
1222 GTK_SIGNAL_FUNC(focus_out
), filer_window
);
1223 gtk_signal_connect(GTK_OBJECT(filer_window
->window
), "destroy",
1224 filer_window_destroyed
, filer_window
);
1226 gtk_signal_connect(GTK_OBJECT(filer_window
->collection
), "open_item",
1227 open_item
, filer_window
);
1228 gtk_signal_connect(GTK_OBJECT(collection
), "show_menu",
1229 show_menu
, filer_window
);
1230 gtk_signal_connect(GTK_OBJECT(collection
), "gain_selection",
1231 gain_selection
, filer_window
);
1232 gtk_signal_connect(GTK_OBJECT(collection
), "lose_selection",
1233 lose_selection
, filer_window
);
1234 gtk_signal_connect(GTK_OBJECT(collection
), "drag_selection",
1235 drag_selection
, filer_window
);
1236 gtk_signal_connect(GTK_OBJECT(collection
), "drag_data_get",
1237 drag_data_get
, filer_window
);
1238 gtk_signal_connect(GTK_OBJECT(collection
), "selection_clear_event",
1239 GTK_SIGNAL_FUNC(collection_lose_selection
), NULL
);
1240 gtk_signal_connect (GTK_OBJECT(collection
), "selection_get",
1241 GTK_SIGNAL_FUNC(selection_get
), NULL
);
1242 gtk_selection_add_targets(collection
, GDK_SELECTION_PRIMARY
,
1244 sizeof(target_table
) / sizeof(*target_table
));
1246 filer_style_set(filer_window
, LARGE_ICONS
);
1247 drag_set_dest(collection
);
1251 int swidth
, sheight
, iwidth
, iheight
;
1252 GtkWidget
*frame
, *win
= filer_window
->window
;
1254 collection_set_panel(filer_window
->collection
, TRUE
);
1255 gtk_signal_connect(GTK_OBJECT(filer_window
->window
),
1257 GTK_SIGNAL_FUNC(filer_confirm_close
),
1260 gdk_window_get_size(GDK_ROOT_PARENT(), &swidth
, &sheight
);
1261 iwidth
= filer_window
->collection
->item_width
;
1262 iheight
= filer_window
->collection
->item_height
;
1264 if (panel_side
== TOP
|| panel_side
== BOTTOM
)
1266 int height
= iheight
+ PANEL_BORDER
;
1267 int y
= panel_side
== TOP
1269 : sheight
- height
- PANEL_BORDER
;
1271 gtk_widget_set_usize(collection
, swidth
, height
);
1272 gtk_widget_set_uposition(win
, 0, y
);
1276 int width
= iwidth
+ PANEL_BORDER
;
1277 int x
= panel_side
== LEFT
1279 : swidth
- width
- PANEL_BORDER
;
1281 gtk_widget_set_usize(collection
, width
, sheight
);
1282 gtk_widget_set_uposition(win
, x
, 0);
1285 frame
= gtk_frame_new(NULL
);
1286 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_OUT
);
1287 gtk_container_add(GTK_CONTAINER(frame
), collection
);
1288 gtk_container_add(GTK_CONTAINER(win
), frame
);
1290 gtk_widget_realize(win
);
1291 if (override_redirect
)
1292 gdk_window_set_override_redirect(win
->window
, TRUE
);
1293 make_panel_window(win
->window
);
1297 hbox
= gtk_hbox_new(FALSE
, 0);
1299 gtk_signal_connect(GTK_OBJECT(filer_window
->window
),
1301 GTK_SIGNAL_FUNC(key_press_event
), filer_window
);
1302 gtk_window_set_default_size(GTK_WINDOW(filer_window
->window
),
1303 filer_window
->display_style
== LARGE_ICONS
? 400 : 512,
1304 o_toolbar
? 220 : 200);
1306 gtk_container_add(GTK_CONTAINER(filer_window
->window
),
1310 GtkWidget
*vbox
, *toolbar
;
1313 vbox
= gtk_vbox_new(FALSE
, 0);
1314 gtk_box_pack_start(GTK_BOX(hbox
), vbox
,
1316 toolbar
= create_toolbar(filer_window
);
1317 gtk_box_pack_start(GTK_BOX(vbox
), toolbar
,
1320 gtk_box_pack_start(GTK_BOX(vbox
), collection
,
1324 gtk_box_pack_start(GTK_BOX(hbox
), collection
,
1327 scrollbar
= gtk_vscrollbar_new(COLLECTION(collection
)->vadj
);
1328 gtk_box_pack_start(GTK_BOX(hbox
), scrollbar
, FALSE
, TRUE
, 0);
1329 gtk_accel_group_attach(filer_keys
,
1330 GTK_OBJECT(filer_window
->window
));
1333 number_of_windows
++;
1334 gtk_widget_show_all(filer_window
->window
);
1335 attach(filer_window
);
1338 static GtkWidget
*create_toolbar(FilerWindow
*filer_window
)
1340 GtkWidget
*frame
, *box
;
1342 frame
= gtk_frame_new(NULL
);
1343 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_OUT
);
1345 box
= gtk_hbutton_box_new();
1346 gtk_button_box_set_child_size_default(16, 16);
1347 gtk_hbutton_box_set_spacing_default(2);
1348 gtk_button_box_set_layout(GTK_BUTTON_BOX(box
), GTK_BUTTONBOX_START
);
1349 gtk_container_add(GTK_CONTAINER(frame
), box
);
1350 add_button(GTK_CONTAINER(box
), TOOLBAR_UP_ICON
,
1351 GTK_SIGNAL_FUNC(toolbar_up_clicked
),
1353 add_button(GTK_CONTAINER(box
), TOOLBAR_HOME_ICON
,
1354 GTK_SIGNAL_FUNC(toolbar_home_clicked
),
1360 static void add_button(GtkContainer
*box
, int pixmap
,
1361 GtkSignalFunc cb
, gpointer data
)
1363 GtkWidget
*button
, *icon
;
1365 button
= gtk_button_new();
1366 GTK_WIDGET_UNSET_FLAGS(button
, GTK_CAN_FOCUS
);
1367 gtk_container_add(box
, button
);
1369 icon
= gtk_pixmap_new(default_pixmap
[pixmap
].pixmap
,
1370 default_pixmap
[pixmap
].mask
);
1371 gtk_container_add(GTK_CONTAINER(button
), icon
);
1372 gtk_signal_connect(GTK_OBJECT(button
), "clicked", cb
, data
);
1375 /* Build up some option widgets to go in the options dialog, but don't
1378 static GtkWidget
*create_options()
1382 vbox
= gtk_vbox_new(FALSE
, 0);
1383 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 4);
1385 toggle_ro_bindings
=
1386 gtk_check_button_new_with_label("Use RISC OS mouse bindings");
1387 gtk_box_pack_start(GTK_BOX(vbox
), toggle_ro_bindings
, FALSE
, TRUE
, 0);
1390 gtk_check_button_new_with_label("Show toolbar on new windows");
1391 gtk_box_pack_start(GTK_BOX(vbox
), toggle_toolbar
, FALSE
, TRUE
, 0);
1396 /* Reflect current state by changing the widgets in the options box */
1397 static void update_options()
1399 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_ro_bindings
),
1401 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_toolbar
),
1405 /* Set current values by reading the states of the widgets in the options box */
1406 static void set_options()
1408 o_ro_bindings
= gtk_toggle_button_get_active(
1409 GTK_TOGGLE_BUTTON(toggle_ro_bindings
));
1410 o_toolbar
= gtk_toggle_button_get_active(
1411 GTK_TOGGLE_BUTTON(toggle_toolbar
));
1412 collection_menu_button
= o_ro_bindings
? 2 : 3;
1415 static void save_options()
1417 option_write("filer_ro_bindings", o_ro_bindings
? "1" : "0");
1418 option_write("filer_toolbar", o_toolbar
? "1" : "0");
1421 static char *filer_ro_bindings(char *data
)
1423 o_ro_bindings
= atoi(data
) != 0;
1427 static char *filer_toolbar(char *data
)
1429 o_toolbar
= atoi(data
) != 0;
1433 /* Note that filer_window may not exist after this call. */
1434 void update_dir(FilerWindow
*filer_window
)
1436 if (may_rescan(filer_window
))
1437 dir_update(filer_window
->directory
, filer_window
->path
);
1440 void filer_set_hidden(FilerWindow
*filer_window
, gboolean hidden
)
1442 Directory
*dir
= filer_window
->directory
;
1444 if (filer_window
->show_hidden
== hidden
)
1447 filer_window
->show_hidden
= hidden
;
1449 g_fscache_data_ref(dir_cache
, dir
);
1450 detach(filer_window
);
1451 filer_window
->directory
= dir
;
1452 attach(filer_window
);