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 */
33 #include <gdk/gdkprivate.h> /* XXX - find another way to do this */
34 #include <gdk/gdkkeysyms.h>
35 #include "collection.h"
39 #include "gui_support.h"
49 #define MAX_ICON_HEIGHT 42
50 #define PANEL_BORDER 2
52 FilerWindow
*window_with_focus
= NULL
;
54 /* Link paths to GLists of filer windows */
55 GHashTable
*path_to_window_list
= NULL
;
57 static FilerWindow
*window_with_selection
= NULL
;
60 static GtkWidget
*create_options();
61 static void update_options();
62 static void set_options();
63 static void save_options();
64 static char *filer_ro_bindings(char *data
);
66 static OptionsSection options
=
68 "Filer window options",
74 static gboolean o_ro_bindings
= FALSE
;
75 static GtkWidget
*toggle_ro_bindings
;
77 /* Static prototypes */
78 static void filer_window_destroyed(GtkWidget
*widget
,
79 FilerWindow
*filer_window
);
80 static gboolean
idle_scan_dir(gpointer data
);
81 static void draw_item(GtkWidget
*widget
,
84 void show_menu(Collection
*collection
, GdkEventButton
*event
,
85 int number_selected
, gpointer user_data
);
86 static int sort_by_name(const void *item1
, const void *item2
);
87 static void add_item(FilerWindow
*filer_window
, char *leafname
);
88 static gboolean
test_point(Collection
*collection
,
89 int point_x
, int point_y
,
91 int width
, int height
);
92 static void stop_scanning(FilerWindow
*filer_window
);
93 static gint
focus_in(GtkWidget
*widget
,
95 FilerWindow
*filer_window
);
96 static gint
focus_out(GtkWidget
*widget
,
98 FilerWindow
*filer_window
);
99 static void add_view(FilerWindow
*filer_window
);
100 static void remove_view(FilerWindow
*filer_window
);
101 static void free_item(FileItem
*item
);
102 static gboolean
remove_deleted(gpointer item_data
, gpointer data
);
104 static GdkAtom xa_string
;
113 xa_string
= gdk_atom_intern("STRING", FALSE
);
115 path_to_window_list
= g_hash_table_new(g_str_hash
, g_str_equal
);
117 options_sections
= g_slist_prepend(options_sections
, &options
);
118 option_register("filer_ro_bindings", filer_ro_bindings
);
122 /* When a filer window shows a directory, use this function to add
123 * it to the list of directories to be updated when the contents
126 static void add_view(FilerWindow
*filer_window
)
128 GList
*list
, *newlist
;
130 g_return_if_fail(filer_window
!= NULL
);
132 list
= g_hash_table_lookup(path_to_window_list
, filer_window
->path
);
133 newlist
= g_list_prepend(list
, filer_window
);
135 g_hash_table_insert(path_to_window_list
, filer_window
->path
,
139 /* When a filer window no longer shows a directory, call this to reverse
140 * the effect of add_view().
142 static void remove_view(FilerWindow
*filer_window
)
144 GList
*list
, *newlist
;
146 g_return_if_fail(filer_window
!= NULL
);
148 list
= g_hash_table_lookup(path_to_window_list
, filer_window
->path
);
149 newlist
= g_list_remove(list
, filer_window
);
151 g_hash_table_insert(path_to_window_list
, filer_window
->path
,
155 /* Go though all the FileItems in a collection, freeing all items.
156 * TODO: Maybe we should cache icons?
158 static void free_items(FilerWindow
*filer_window
)
161 Collection
*collection
= filer_window
->collection
;
163 for (i
= 0; i
< collection
->number_of_items
; i
++)
165 free_item((FileItem
*) collection
->items
[i
].data
);
166 collection
->items
[i
].data
= NULL
;
170 /* Set one item in a collection to NULL, freeing all data for it */
171 static void free_item(FileItem
*item
)
173 if (item
->flags
& ITEM_FLAG_TEMP_ICON
)
175 pixmap_unref(item
->image
);
176 item
->image
= default_pixmap
+ TYPE_ERROR
;
178 g_free(item
->leafname
);
182 static void filer_window_destroyed(GtkWidget
*widget
,
183 FilerWindow
*filer_window
)
185 if (window_with_selection
== filer_window
)
186 window_with_selection
= NULL
;
187 if (window_with_focus
== filer_window
)
188 window_with_focus
= NULL
;
190 remove_view(filer_window
);
192 free_items(filer_window
);
193 if (filer_window
->dir
)
194 stop_scanning(filer_window
);
195 g_free(filer_window
->path
);
196 g_free(filer_window
);
198 if (--number_of_windows
< 1)
202 static void stop_scanning(FilerWindow
*filer_window
)
204 g_return_if_fail(filer_window
->dir
!= NULL
);
206 closedir(filer_window
->dir
);
207 gtk_idle_remove(filer_window
->idle_scan_id
);
208 filer_window
->dir
= NULL
;
209 if (filer_window
->window
->window
)
210 gdk_window_set_cursor(filer_window
->window
->window
, NULL
);
213 /* This is called while we are scanning the directory */
214 static gboolean
idle_scan_dir(gpointer data
)
217 FilerWindow
*filer_window
= (FilerWindow
*) data
;
221 next
= readdir(filer_window
->dir
);
224 closedir(filer_window
->dir
);
225 filer_window
->dir
= NULL
;
226 gdk_window_set_cursor(filer_window
->window
->window
,
229 collection_set_item_size(filer_window
->collection
,
230 filer_window
->scan_min_width
,
231 filer_window
->collection
->item_height
);
232 collection_qsort(filer_window
->collection
,
233 filer_window
->sort_fn
);
234 if (filer_window
->flags
& FILER_UPDATING
)
236 collection_delete_if(filer_window
->collection
,
237 remove_deleted
, NULL
);
239 if (filer_window
->flags
& FILER_NEEDS_RESCAN
)
240 update_dir(filer_window
);
242 return FALSE
; /* Finished */
245 add_item(filer_window
, next
->d_name
);
246 } while (!gtk_events_pending());
251 /* Add a single object to a directory display */
252 static void add_item(FilerWindow
*filer_window
, char *leafname
)
261 if (leafname
[0] == '.')
263 if (filer_window
->show_hidden
== FALSE
|| leafname
[1] == '\0'
264 || (leafname
[1] == '.' && leafname
[2] == '\0'))
268 item
= g_malloc(sizeof(FileItem
));
269 item
->leafname
= g_strdup(leafname
);
270 item
->flags
= (ItemFlags
) 0;
272 path
= make_path(filer_window
->path
, leafname
);
273 if (lstat(path
->str
, &info
))
274 base_type
= TYPE_ERROR
;
277 if (S_ISREG(info
.st_mode
))
278 base_type
= TYPE_FILE
;
279 else if (S_ISDIR(info
.st_mode
))
281 base_type
= TYPE_DIRECTORY
;
283 if (g_hash_table_lookup(mtab_mounts
, path
->str
))
284 item
->flags
|= ITEM_FLAG_MOUNT_POINT
286 else if (g_hash_table_lookup(fstab_mounts
, path
->str
))
287 item
->flags
|= ITEM_FLAG_MOUNT_POINT
;
289 else if (S_ISBLK(info
.st_mode
))
290 base_type
= TYPE_BLOCK_DEVICE
;
291 else if (S_ISCHR(info
.st_mode
))
292 base_type
= TYPE_CHAR_DEVICE
;
293 else if (S_ISFIFO(info
.st_mode
))
294 base_type
= TYPE_PIPE
;
295 else if (S_ISSOCK(info
.st_mode
))
296 base_type
= TYPE_SOCKET
;
297 else if (S_ISLNK(info
.st_mode
))
299 if (stat(path
->str
, &info
))
301 base_type
= TYPE_ERROR
;
305 if (S_ISREG(info
.st_mode
))
306 base_type
= TYPE_FILE
;
307 else if (S_ISDIR(info
.st_mode
))
308 base_type
= TYPE_DIRECTORY
;
309 else if (S_ISBLK(info
.st_mode
))
310 base_type
= TYPE_BLOCK_DEVICE
;
311 else if (S_ISCHR(info
.st_mode
))
312 base_type
= TYPE_CHAR_DEVICE
;
313 else if (S_ISFIFO(info
.st_mode
))
314 base_type
= TYPE_PIPE
;
315 else if (S_ISSOCK(info
.st_mode
))
316 base_type
= TYPE_SOCKET
;
318 base_type
= TYPE_UNKNOWN
;
321 item
->flags
|= ITEM_FLAG_SYMLINK
;
324 base_type
= TYPE_UNKNOWN
;
327 item
->base_type
= base_type
;
328 item
->mime_type
= NULL
;
330 if (base_type
== TYPE_DIRECTORY
&&
331 !(item
->flags
& ITEM_FLAG_MOUNT_POINT
))
333 uid_t uid
= info
.st_uid
;
335 /* Might be an application directory - better check...
336 * AppRun must have the same owner as the directory
337 * (to stop people putting an AppRun in, eg, /tmp)
339 g_string_append(path
, "/AppRun");
340 if (!stat(path
->str
, &info
) && info
.st_uid
== uid
)
342 item
->flags
|= ITEM_FLAG_APPDIR
;
346 if (item
->flags
& ITEM_FLAG_APPDIR
) /* path still ends /AppRun */
348 MaskedPixmap
*app_icon
;
350 g_string_truncate(path
, path
->len
- 3);
351 g_string_append(path
, "Icon.xpm");
352 app_icon
= load_pixmap_from(filer_window
->window
, path
->str
);
355 item
->image
= app_icon
;
356 item
->flags
|= ITEM_FLAG_TEMP_ICON
;
359 item
->image
= default_pixmap
+ TYPE_APPDIR
;
363 if (base_type
== TYPE_FILE
&&
364 (info
.st_mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
)))
366 item
->image
= default_pixmap
+ TYPE_EXEC_FILE
;
367 item
->flags
|= ITEM_FLAG_EXEC_FILE
;
369 else if (base_type
== TYPE_FILE
)
371 item
->mime_type
= type_from_path(path
->str
);
372 item
->image
= type_to_icon(filer_window
->window
,
374 item
->flags
|= ITEM_FLAG_TEMP_ICON
;
377 item
->image
= default_pixmap
+ base_type
;
380 item
->text_width
= gdk_string_width(filer_window
->window
->style
->font
,
384 item
->image
= default_pixmap
+ TYPE_UNKNOWN
;
386 /* XXX: Must be a better way... */
387 item
->pix_width
= ((GdkPixmapPrivate
*) item
->image
->pixmap
)->width
;
388 item
->pix_height
= ((GdkPixmapPrivate
*) item
->image
->pixmap
)->height
;
390 item_width
= MAX(item
->pix_width
, item
->text_width
) + 4;
392 if (item_width
> filer_window
->scan_min_width
)
393 filer_window
->scan_min_width
= item_width
;
395 if (item_width
> filer_window
->collection
->item_width
)
396 collection_set_item_size(filer_window
->collection
,
398 filer_window
->collection
->item_height
);
400 old_item
= collection_find_item(filer_window
->collection
, item
,
405 CollectionItem
*old
=
406 &filer_window
->collection
->items
[old_item
];
408 free_item((FileItem
*) old
->data
);
410 collection_draw_item(filer_window
->collection
, old_item
, TRUE
);
413 collection_insert(filer_window
->collection
, item
);
416 /* Is a point inside an item? */
417 static gboolean
test_point(Collection
*collection
,
418 int point_x
, int point_y
,
419 CollectionItem
*colitem
,
420 int width
, int height
)
422 FileItem
*item
= (FileItem
*) colitem
->data
;
423 GdkFont
*font
= GTK_WIDGET(collection
)->style
->font
;
424 int text_height
= font
->ascent
+ font
->descent
;
425 int image_y
= MAX(0, MAX_ICON_HEIGHT
- item
->pix_height
);
426 int image_width
= (item
->pix_width
>> 1) + 2;
427 int text_width
= (item
->text_width
>> 1) + 2;
430 if (point_y
< image_y
)
431 return FALSE
; /* Too high up (don't worry about too low) */
433 if (point_y
<= image_y
+ item
->pix_height
+ 2)
434 x_limit
= image_width
;
435 else if (point_y
> height
- text_height
- 2)
436 x_limit
= text_width
;
438 x_limit
= MIN(image_width
, text_width
);
440 return ABS(point_x
- (width
>> 1)) < x_limit
;
443 static void draw_item(GtkWidget
*widget
,
444 CollectionItem
*colitem
,
447 FileItem
*item
= (FileItem
*) colitem
->data
;
448 GdkGC
*gc
= colitem
->selected
? widget
->style
->white_gc
449 : widget
->style
->black_gc
;
450 int image_x
= area
->x
+ ((area
->width
- item
->pix_width
) >> 1);
451 GdkFont
*font
= widget
->style
->font
;
452 int text_x
= area
->x
+ ((area
->width
- item
->text_width
) >> 1);
453 int text_y
= area
->y
+ area
->height
- font
->descent
- 2;
454 int text_height
= font
->ascent
+ font
->descent
;
460 gdk_gc_set_clip_mask(gc
, item
->image
->mask
);
462 image_y
= MAX(0, MAX_ICON_HEIGHT
- item
->pix_height
);
463 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ image_y
);
464 gdk_draw_pixmap(widget
->window
, gc
,
466 0, 0, /* Source x,y */
467 image_x
, area
->y
+ image_y
, /* Dest x,y */
468 -1, MIN(item
->pix_height
, MAX_ICON_HEIGHT
));
470 if (item
->flags
& ITEM_FLAG_SYMLINK
)
472 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ 8);
473 gdk_gc_set_clip_mask(gc
,
474 default_pixmap
[TYPE_SYMLINK
].mask
);
475 gdk_draw_pixmap(widget
->window
, gc
,
476 default_pixmap
[TYPE_SYMLINK
].pixmap
,
477 0, 0, /* Source x,y */
478 image_x
, area
->y
+ 8, /* Dest x,y */
481 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
483 int type
= item
->flags
& ITEM_FLAG_MOUNTED
486 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ 8);
487 gdk_gc_set_clip_mask(gc
,
488 default_pixmap
[type
].mask
);
489 gdk_draw_pixmap(widget
->window
, gc
,
490 default_pixmap
[type
].pixmap
,
491 0, 0, /* Source x,y */
492 image_x
, area
->y
+ 8, /* Dest x,y */
496 gdk_gc_set_clip_mask(gc
, NULL
);
497 gdk_gc_set_clip_origin(gc
, 0, 0);
500 if (colitem
->selected
)
501 gtk_paint_flat_box(widget
->style
, widget
->window
,
502 GTK_STATE_SELECTED
, GTK_SHADOW_NONE
,
503 NULL
, widget
, "text",
504 text_x
, text_y
- font
->ascent
,
508 gdk_draw_text(widget
->window
,
510 colitem
->selected
? widget
->style
->white_gc
511 : widget
->style
->black_gc
,
513 item
->leafname
, strlen(item
->leafname
));
516 void show_menu(Collection
*collection
, GdkEventButton
*event
,
517 int item
, gpointer user_data
)
519 show_filer_menu((FilerWindow
*) user_data
, event
, item
);
522 static void may_rescan(FilerWindow
*filer_window
)
526 g_return_if_fail(filer_window
!= NULL
);
528 if (stat(filer_window
->path
, &info
))
530 delayed_error("ROX-Filer", "Directory deleted");
531 gtk_widget_destroy(filer_window
->window
);
533 else if (info
.st_mtime
> filer_window
->m_time
)
535 if (filer_window
->dir
)
536 filer_window
->flags
|= FILER_NEEDS_RESCAN
;
538 update_dir(filer_window
);
542 /* Callback to collection_delete_if() */
543 static gboolean
remove_deleted(gpointer item_data
, gpointer data
)
545 FileItem
*item
= (FileItem
*) item_data
;
547 if (item
->flags
& ITEM_FLAG_MAY_DELETE
)
556 /* Forget the old contents of a filer window and scan the directory
557 * from the start. If we are already scanning then rescan later.
559 void scan_dir(FilerWindow
*filer_window
)
561 if (filer_window
->dir
)
562 stop_scanning(filer_window
);
564 free_items(filer_window
);
565 collection_clear(filer_window
->collection
);
567 update_dir(filer_window
);
568 filer_window
->flags
&= ~FILER_UPDATING
;
570 gdk_window_set_cursor(filer_window
->window
->window
,
571 gdk_cursor_new(GDK_WATCH
));
574 /* Like scan_dir(), but assume new display will be similar to the old
575 * one (less flicker and doesn't lose the selection).
577 void update_dir(FilerWindow
*filer_window
)
579 Collection
*collection
= filer_window
->collection
;
583 if (filer_window
->dir
)
585 /* Already scanning - start again when we finish */
586 filer_window
->flags
|= FILER_NEEDS_RESCAN
;
589 filer_window
->flags
&= ~FILER_NEEDS_RESCAN
;
590 filer_window
->flags
|= FILER_UPDATING
;
592 for (i
= 0; i
< collection
->number_of_items
; i
++)
594 FileItem
*item
= (FileItem
*) collection
->items
[i
].data
;
595 item
->flags
|= ITEM_FLAG_MAY_DELETE
;
600 gtk_window_set_title(GTK_WINDOW(filer_window
->window
),
603 if (stat(filer_window
->path
, &info
))
605 report_error("Error statting directory", g_strerror(errno
));
608 filer_window
->m_time
= info
.st_mtime
;
610 filer_window
->dir
= opendir(filer_window
->path
);
611 if (!filer_window
->dir
)
613 report_error("Error scanning directory", g_strerror(errno
));
617 filer_window
->scan_min_width
= 64;
619 filer_window
->idle_scan_id
= gtk_idle_add(idle_scan_dir
, filer_window
);
622 /* Another app has grabbed the selection */
623 static gint
collection_lose_selection(GtkWidget
*widget
,
624 GdkEventSelection
*event
)
626 if (window_with_selection
&&
627 window_with_selection
->collection
== COLLECTION(widget
))
629 FilerWindow
*filer_window
= window_with_selection
;
630 window_with_selection
= NULL
;
631 collection_clear_selection(filer_window
->collection
);
637 /* Someone wants us to send them the selection */
638 static void selection_get(GtkWidget
*widget
,
639 GtkSelectionData
*selection_data
,
644 GString
*reply
, *header
;
645 FilerWindow
*filer_window
;
647 Collection
*collection
;
649 filer_window
= gtk_object_get_data(GTK_OBJECT(widget
), "filer_window");
651 reply
= g_string_new(NULL
);
652 header
= g_string_new(NULL
);
657 g_string_sprintf(header
, " %s",
658 make_path(filer_window
->path
, "")->str
);
660 case TARGET_URI_LIST
:
661 g_string_sprintf(header
, " file://%s%s",
663 make_path(filer_window
->path
, "")->str
);
667 collection
= filer_window
->collection
;
668 for (i
= 0; i
< collection
->number_of_items
; i
++)
670 if (collection
->items
[i
].selected
)
673 (FileItem
*) collection
->items
[i
].data
;
675 g_string_append(reply
, header
->str
);
676 g_string_append(reply
, item
->leafname
);
679 /* This works, but I don't think I like it... */
680 /* g_string_append_c(reply, ' '); */
682 gtk_selection_data_set(selection_data
, xa_string
,
683 8, reply
->str
+ 1, reply
->len
- 1);
684 g_string_free(reply
, TRUE
);
685 g_string_free(header
, TRUE
);
688 /* No items are now selected. This might be because another app claimed
689 * the selection or because the user unselected all the items.
691 static void lose_selection(Collection
*collection
,
695 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
697 if (window_with_selection
== filer_window
)
699 window_with_selection
= NULL
;
700 gtk_selection_owner_set(NULL
,
701 GDK_SELECTION_PRIMARY
,
706 static void gain_selection(Collection
*collection
,
710 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
712 if (gtk_selection_owner_set(GTK_WIDGET(collection
),
713 GDK_SELECTION_PRIMARY
,
716 window_with_selection
= filer_window
;
719 collection_clear_selection(filer_window
->collection
);
722 static int sort_by_name(const void *item1
, const void *item2
)
724 return strcmp((*((FileItem
**)item1
))->leafname
,
725 (*((FileItem
**)item2
))->leafname
);
728 static int sort_by_type(const void *item1
, const void *item2
)
730 const FileItem
*i1
= (FileItem
*) ((CollectionItem
*) item1
)->data
;
731 const FileItem
*i2
= (FileItem
*) ((CollectionItem
*) item2
)->data
;
734 int diff
= i1
->base_type
- i2
->base_type
;
737 diff
= (i1
->flags
& ITEM_FLAG_APPDIR
)
738 - (i2
->flags
& ITEM_FLAG_APPDIR
);
740 return diff
> 0 ? 1 : -1;
747 diff
= strcmp(m1
->media_type
, m2
->media_type
);
749 diff
= strcmp(m1
->subtype
, m2
->subtype
);
757 return diff
> 0 ? 1 : -1;
759 return sort_by_name(item1
, item2
);
762 void open_item(Collection
*collection
,
763 gpointer item_data
, int item_number
,
766 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
767 FileItem
*item
= (FileItem
*) item_data
;
768 GdkEventButton
*event
;
772 gboolean adjust
; /* do alternative action */
774 event
= (GdkEventButton
*) gtk_get_current_event();
775 full_path
= make_path(filer_window
->path
, item
->leafname
)->str
;
777 collection_wink_item(filer_window
->collection
, item_number
);
779 if (event
->type
== GDK_2BUTTON_PRESS
|| event
->type
== GDK_BUTTON_PRESS
)
781 shift
= event
->state
& GDK_SHIFT_MASK
;
782 adjust
= (event
->button
!= 1)
783 ^ ((event
->state
& GDK_CONTROL_MASK
) != 0);
791 widget
= filer_window
->window
;
793 switch (item
->base_type
)
796 if (item
->flags
& ITEM_FLAG_APPDIR
&& !shift
)
798 run_app(make_path(filer_window
->path
,
799 item
->leafname
)->str
);
800 if (adjust
&& !filer_window
->panel
)
801 gtk_widget_destroy(widget
);
804 if ((adjust
^ o_ro_bindings
) || filer_window
->panel
)
805 filer_opendir(full_path
, FALSE
, BOTTOM
);
808 remove_view(filer_window
);
809 filer_window
->path
= pathdup(full_path
);
810 add_view(filer_window
);
811 scan_dir(filer_window
);
815 if (item
->flags
& ITEM_FLAG_EXEC_FILE
&& !shift
)
817 char *argv
[] = {NULL
, NULL
};
821 if (spawn_full(argv
, getenv("HOME"), 0))
823 if (adjust
&& !filer_window
->panel
)
824 gtk_widget_destroy(widget
);
827 report_error("ROX-Filer",
828 "Failed to fork() child");
833 MIME_type
*type
= shift
? &text_plain
836 g_return_if_fail(type
!= NULL
);
838 if (type_open(full_path
, type
))
840 if (adjust
&& !filer_window
->panel
)
841 gtk_widget_destroy(widget
);
845 message
= g_string_new(NULL
);
846 g_string_sprintf(message
, "No open "
847 "action specified for files of "
851 report_error("ROX-Filer", message
->str
);
852 g_string_free(message
, TRUE
);
857 report_error("open_item",
858 "I don't know how to open that");
863 static gint
pointer_in(GtkWidget
*widget
,
864 GdkEventCrossing
*event
,
865 FilerWindow
*filer_window
)
867 may_rescan(filer_window
);
871 static gint
focus_in(GtkWidget
*widget
,
872 GdkEventFocus
*event
,
873 FilerWindow
*filer_window
)
875 window_with_focus
= filer_window
;
880 static gint
focus_out(GtkWidget
*widget
,
881 GdkEventFocus
*event
,
882 FilerWindow
*filer_window
)
884 /* TODO: Shade the cursor */
889 /* Handle keys that can't be bound with the menu */
890 static gint
key_press_event(GtkWidget
*widget
,
892 FilerWindow
*filer_window
)
894 switch (event
->keyval
)
912 change_to_parent(filer_window
);
919 void change_to_parent(FilerWindow
*filer_window
)
921 remove_view(filer_window
);
922 filer_window
->path
= pathdup(make_path(
925 add_view(filer_window
);
926 scan_dir(filer_window
);
929 FileItem
*selected_item(Collection
*collection
)
933 g_return_val_if_fail(collection
!= NULL
, NULL
);
934 g_return_val_if_fail(IS_COLLECTION(collection
), NULL
);
935 g_return_val_if_fail(collection
->number_selected
== 1, NULL
);
937 for (i
= 0; i
< collection
->number_of_items
; i
++)
938 if (collection
->items
[i
].selected
)
939 return (FileItem
*) collection
->items
[i
].data
;
941 g_warning("selected_item: number_selected is wrong\n");
946 /* Refresh all windows onto this directory */
947 void refresh_dirs(char *path
)
952 real
= pathdup(path
);
953 list
= g_hash_table_lookup(path_to_window_list
, real
);
959 update_dir((FilerWindow
*) list
->data
);
964 void filer_opendir(char *path
, gboolean panel
, Side panel_side
)
966 GtkWidget
*hbox
, *scrollbar
, *collection
;
967 FilerWindow
*filer_window
;
968 GtkTargetEntry target_table
[] =
970 {"text/uri-list", 0, TARGET_URI_LIST
},
971 {"STRING", 0, TARGET_STRING
},
974 filer_window
= g_malloc(sizeof(FilerWindow
));
975 filer_window
->path
= pathdup(path
);
976 filer_window
->dir
= NULL
; /* Not scanning */
977 filer_window
->show_hidden
= FALSE
;
978 filer_window
->panel
= panel
;
979 filer_window
->panel_side
= panel_side
;
980 filer_window
->temp_item_selected
= FALSE
;
981 filer_window
->sort_fn
= sort_by_type
;
982 filer_window
->flags
= (FilerFlags
) 0;
984 filer_window
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
986 collection
= collection_new(NULL
);
987 gtk_object_set_data(GTK_OBJECT(collection
),
988 "filer_window", filer_window
);
989 filer_window
->collection
= COLLECTION(collection
);
990 collection_set_item_size(filer_window
->collection
, 64, 64);
991 collection_set_functions(filer_window
->collection
,
992 draw_item
, test_point
);
994 gtk_widget_add_events(filer_window
->window
, GDK_ENTER_NOTIFY
);
995 gtk_signal_connect(GTK_OBJECT(filer_window
->window
),
996 "enter-notify-event",
997 GTK_SIGNAL_FUNC(pointer_in
), filer_window
);
998 gtk_signal_connect(GTK_OBJECT(filer_window
->window
), "focus_in_event",
999 GTK_SIGNAL_FUNC(focus_in
), filer_window
);
1000 gtk_signal_connect(GTK_OBJECT(filer_window
->window
), "focus_out_event",
1001 GTK_SIGNAL_FUNC(focus_out
), filer_window
);
1002 gtk_signal_connect(GTK_OBJECT(filer_window
->window
), "destroy",
1003 filer_window_destroyed
, filer_window
);
1005 gtk_signal_connect(GTK_OBJECT(filer_window
->collection
), "open_item",
1006 open_item
, filer_window
);
1007 gtk_signal_connect(GTK_OBJECT(collection
), "show_menu",
1008 show_menu
, filer_window
);
1009 gtk_signal_connect(GTK_OBJECT(collection
), "gain_selection",
1010 gain_selection
, filer_window
);
1011 gtk_signal_connect(GTK_OBJECT(collection
), "lose_selection",
1012 lose_selection
, filer_window
);
1013 gtk_signal_connect(GTK_OBJECT(collection
), "drag_selection",
1014 drag_selection
, filer_window
);
1015 gtk_signal_connect(GTK_OBJECT(collection
), "drag_data_get",
1016 drag_data_get
, filer_window
);
1017 gtk_signal_connect(GTK_OBJECT(collection
), "selection_clear_event",
1018 GTK_SIGNAL_FUNC(collection_lose_selection
), NULL
);
1019 gtk_signal_connect (GTK_OBJECT(collection
), "selection_get",
1020 GTK_SIGNAL_FUNC(selection_get
), NULL
);
1021 gtk_selection_add_targets(collection
, GDK_SELECTION_PRIMARY
,
1023 sizeof(target_table
) / sizeof(*target_table
));
1025 drag_set_dest(collection
);
1029 int swidth
, sheight
, iwidth
, iheight
;
1030 GtkWidget
*frame
, *win
= filer_window
->window
;
1032 collection_set_panel(filer_window
->collection
, TRUE
);
1034 gdk_window_get_size(GDK_ROOT_PARENT(), &swidth
, &sheight
);
1035 iwidth
= filer_window
->collection
->item_width
;
1036 iheight
= filer_window
->collection
->item_height
;
1038 if (panel_side
== TOP
|| panel_side
== BOTTOM
)
1040 int height
= iheight
+ PANEL_BORDER
;
1041 int y
= panel_side
== TOP
1043 : sheight
- height
- PANEL_BORDER
;
1045 gtk_widget_set_usize(collection
, swidth
, height
);
1046 gtk_widget_set_uposition(win
, 0, y
);
1050 int width
= iwidth
+ PANEL_BORDER
;
1051 int x
= panel_side
== LEFT
1053 : swidth
- width
- PANEL_BORDER
;
1055 gtk_widget_set_usize(collection
, width
, sheight
);
1056 gtk_widget_set_uposition(win
, x
, 0);
1059 frame
= gtk_frame_new(NULL
);
1060 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_OUT
);
1061 gtk_container_add(GTK_CONTAINER(frame
), collection
);
1062 gtk_container_add(GTK_CONTAINER(win
), frame
);
1064 gtk_widget_realize(win
);
1065 if (override_redirect
)
1066 gdk_window_set_override_redirect(win
->window
, TRUE
);
1067 make_panel_window(win
->window
);
1071 gtk_signal_connect(GTK_OBJECT(filer_window
->window
),
1073 GTK_SIGNAL_FUNC(key_press_event
), filer_window
);
1074 gtk_window_set_default_size(GTK_WINDOW(filer_window
->window
),
1077 hbox
= gtk_hbox_new(FALSE
, 0);
1078 gtk_container_add(GTK_CONTAINER(filer_window
->window
), hbox
);
1080 gtk_box_pack_start(GTK_BOX(hbox
), collection
, TRUE
, TRUE
, 0);
1082 scrollbar
= gtk_vscrollbar_new(COLLECTION(collection
)->vadj
);
1083 gtk_box_pack_start(GTK_BOX(hbox
), scrollbar
, FALSE
, TRUE
, 0);
1084 gtk_accel_group_attach(filer_keys
,
1085 GTK_OBJECT(filer_window
->window
));
1088 gtk_widget_show_all(filer_window
->window
);
1089 number_of_windows
++;
1091 load_default_pixmaps(collection
->window
);
1093 add_view(filer_window
);
1094 scan_dir(filer_window
);
1097 /* Build up some option widgets to go in the options dialog, but don't
1100 static GtkWidget
*create_options()
1104 vbox
= gtk_vbox_new(FALSE
, 0);
1105 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 4);
1107 toggle_ro_bindings
=
1108 gtk_check_button_new_with_label("Use RISC OS mouse bindings");
1109 gtk_box_pack_start(GTK_BOX(vbox
), toggle_ro_bindings
, FALSE
, TRUE
, 0);
1114 /* Reflect current state by changing the widgets in the options box */
1115 static void update_options()
1117 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_ro_bindings
),
1121 /* Set current values by reading the states of the widgets in the options box */
1122 static void set_options()
1124 o_ro_bindings
= gtk_toggle_button_get_active(
1125 GTK_TOGGLE_BUTTON(toggle_ro_bindings
));
1128 static void save_options()
1130 option_write("filer_ro_bindings", o_ro_bindings
? "1" : "0");
1133 static char *filer_ro_bindings(char *data
)
1135 o_ro_bindings
= atoi(data
) != 0;