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
);
65 static char *filer_toolbar(char *data
);
67 static OptionsSection options
=
69 "Filer window options",
75 static gboolean o_toolbar
= TRUE
;
76 static GtkWidget
*toggle_toolbar
;
77 static gboolean o_ro_bindings
= FALSE
;
78 static GtkWidget
*toggle_ro_bindings
;
80 /* Static prototypes */
81 static void filer_window_destroyed(GtkWidget
*widget
,
82 FilerWindow
*filer_window
);
83 static gboolean
idle_scan_dir(gpointer data
);
84 static void draw_item(GtkWidget
*widget
,
87 void show_menu(Collection
*collection
, GdkEventButton
*event
,
88 int number_selected
, gpointer user_data
);
89 static int sort_by_name(const void *item1
, const void *item2
);
90 static void add_item(FilerWindow
*filer_window
, char *leafname
);
91 static gboolean
test_point(Collection
*collection
,
92 int point_x
, int point_y
,
94 int width
, int height
);
95 static void stop_scanning(FilerWindow
*filer_window
);
96 static gint
focus_in(GtkWidget
*widget
,
98 FilerWindow
*filer_window
);
99 static gint
focus_out(GtkWidget
*widget
,
100 GdkEventFocus
*event
,
101 FilerWindow
*filer_window
);
102 static void add_view(FilerWindow
*filer_window
);
103 static void remove_view(FilerWindow
*filer_window
);
104 static void free_item(FileItem
*item
);
105 static gboolean
remove_deleted(gpointer item_data
, gpointer data
);
106 static void toolbar_up_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
);
107 static void toolbar_home_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
);
108 static void add_button(GtkContainer
*box
, int pixmap
,
109 GtkSignalFunc cb
, gpointer data
);
110 static GtkWidget
*create_toolbar(FilerWindow
*filer_window
);
112 static GdkAtom xa_string
;
121 xa_string
= gdk_atom_intern("STRING", FALSE
);
123 path_to_window_list
= g_hash_table_new(g_str_hash
, g_str_equal
);
125 options_sections
= g_slist_prepend(options_sections
, &options
);
126 option_register("filer_ro_bindings", filer_ro_bindings
);
127 option_register("filer_toolbar", filer_toolbar
);
131 /* When a filer window shows a directory, use this function to add
132 * it to the list of directories to be updated when the contents
135 static void add_view(FilerWindow
*filer_window
)
137 GList
*list
, *newlist
;
139 g_return_if_fail(filer_window
!= NULL
);
141 list
= g_hash_table_lookup(path_to_window_list
, filer_window
->path
);
142 newlist
= g_list_prepend(list
, filer_window
);
144 g_hash_table_insert(path_to_window_list
, filer_window
->path
,
148 /* When a filer window no longer shows a directory, call this to reverse
149 * the effect of add_view().
151 static void remove_view(FilerWindow
*filer_window
)
153 GList
*list
, *newlist
;
155 g_return_if_fail(filer_window
!= NULL
);
157 list
= g_hash_table_lookup(path_to_window_list
, filer_window
->path
);
158 newlist
= g_list_remove(list
, filer_window
);
160 g_hash_table_insert(path_to_window_list
, filer_window
->path
,
164 /* Go though all the FileItems in a collection, freeing all items.
165 * TODO: Maybe we should cache icons?
167 static void free_items(FilerWindow
*filer_window
)
170 Collection
*collection
= filer_window
->collection
;
172 for (i
= 0; i
< collection
->number_of_items
; i
++)
174 free_item((FileItem
*) collection
->items
[i
].data
);
175 collection
->items
[i
].data
= NULL
;
179 /* Set one item in a collection to NULL, freeing all data for it */
180 static void free_item(FileItem
*item
)
182 if (item
->flags
& ITEM_FLAG_TEMP_ICON
)
184 pixmap_unref(item
->image
);
185 item
->image
= default_pixmap
+ TYPE_ERROR
;
187 g_free(item
->leafname
);
191 static void filer_window_destroyed(GtkWidget
*widget
,
192 FilerWindow
*filer_window
)
194 if (window_with_selection
== filer_window
)
195 window_with_selection
= NULL
;
196 if (window_with_focus
== filer_window
)
197 window_with_focus
= NULL
;
199 remove_view(filer_window
);
201 free_items(filer_window
);
202 if (filer_window
->dir
)
203 stop_scanning(filer_window
);
204 g_free(filer_window
->path
);
205 g_free(filer_window
);
207 if (--number_of_windows
< 1)
211 static void stop_scanning(FilerWindow
*filer_window
)
213 g_return_if_fail(filer_window
->dir
!= NULL
);
215 closedir(filer_window
->dir
);
216 gtk_idle_remove(filer_window
->idle_scan_id
);
217 filer_window
->dir
= NULL
;
218 if (filer_window
->window
->window
)
219 gdk_window_set_cursor(filer_window
->window
->window
, NULL
);
222 /* This is called while we are scanning the directory */
223 static gboolean
idle_scan_dir(gpointer data
)
226 FilerWindow
*filer_window
= (FilerWindow
*) data
;
230 next
= readdir(filer_window
->dir
);
233 closedir(filer_window
->dir
);
234 filer_window
->dir
= NULL
;
235 gdk_window_set_cursor(filer_window
->window
->window
,
238 collection_set_item_size(filer_window
->collection
,
239 filer_window
->scan_min_width
,
240 filer_window
->collection
->item_height
);
241 collection_qsort(filer_window
->collection
,
242 filer_window
->sort_fn
);
243 if (filer_window
->flags
& FILER_UPDATING
)
245 collection_delete_if(filer_window
->collection
,
246 remove_deleted
, NULL
);
248 if (filer_window
->flags
& FILER_NEEDS_RESCAN
)
249 update_dir(filer_window
);
251 return FALSE
; /* Finished */
254 add_item(filer_window
, next
->d_name
);
255 } while (!gtk_events_pending());
260 /* Add a single object to a directory display */
261 static void add_item(FilerWindow
*filer_window
, char *leafname
)
270 if (leafname
[0] == '.')
272 if (filer_window
->show_hidden
== FALSE
|| leafname
[1] == '\0'
273 || (leafname
[1] == '.' && leafname
[2] == '\0'))
277 item
= g_malloc(sizeof(FileItem
));
278 item
->leafname
= g_strdup(leafname
);
279 item
->flags
= (ItemFlags
) 0;
281 path
= make_path(filer_window
->path
, leafname
);
282 if (lstat(path
->str
, &info
))
283 base_type
= TYPE_ERROR
;
286 if (S_ISREG(info
.st_mode
))
287 base_type
= TYPE_FILE
;
288 else if (S_ISDIR(info
.st_mode
))
290 base_type
= TYPE_DIRECTORY
;
292 if (g_hash_table_lookup(mtab_mounts
, path
->str
))
293 item
->flags
|= ITEM_FLAG_MOUNT_POINT
295 else if (g_hash_table_lookup(fstab_mounts
, path
->str
))
296 item
->flags
|= ITEM_FLAG_MOUNT_POINT
;
298 else if (S_ISBLK(info
.st_mode
))
299 base_type
= TYPE_BLOCK_DEVICE
;
300 else if (S_ISCHR(info
.st_mode
))
301 base_type
= TYPE_CHAR_DEVICE
;
302 else if (S_ISFIFO(info
.st_mode
))
303 base_type
= TYPE_PIPE
;
304 else if (S_ISSOCK(info
.st_mode
))
305 base_type
= TYPE_SOCKET
;
306 else if (S_ISLNK(info
.st_mode
))
308 if (stat(path
->str
, &info
))
310 base_type
= TYPE_ERROR
;
314 if (S_ISREG(info
.st_mode
))
315 base_type
= TYPE_FILE
;
316 else if (S_ISDIR(info
.st_mode
))
317 base_type
= TYPE_DIRECTORY
;
318 else if (S_ISBLK(info
.st_mode
))
319 base_type
= TYPE_BLOCK_DEVICE
;
320 else if (S_ISCHR(info
.st_mode
))
321 base_type
= TYPE_CHAR_DEVICE
;
322 else if (S_ISFIFO(info
.st_mode
))
323 base_type
= TYPE_PIPE
;
324 else if (S_ISSOCK(info
.st_mode
))
325 base_type
= TYPE_SOCKET
;
327 base_type
= TYPE_UNKNOWN
;
330 item
->flags
|= ITEM_FLAG_SYMLINK
;
333 base_type
= TYPE_UNKNOWN
;
336 item
->base_type
= base_type
;
337 item
->mime_type
= NULL
;
339 if (base_type
== TYPE_DIRECTORY
&&
340 !(item
->flags
& ITEM_FLAG_MOUNT_POINT
))
342 uid_t uid
= info
.st_uid
;
344 /* Might be an application directory - better check...
345 * AppRun must have the same owner as the directory
346 * (to stop people putting an AppRun in, eg, /tmp)
348 g_string_append(path
, "/AppRun");
349 if (!stat(path
->str
, &info
) && info
.st_uid
== uid
)
351 item
->flags
|= ITEM_FLAG_APPDIR
;
355 if (item
->flags
& ITEM_FLAG_APPDIR
) /* path still ends /AppRun */
357 MaskedPixmap
*app_icon
;
359 g_string_truncate(path
, path
->len
- 3);
360 g_string_append(path
, "Icon.xpm");
361 app_icon
= load_pixmap_from(filer_window
->window
, path
->str
);
364 item
->image
= app_icon
;
365 item
->flags
|= ITEM_FLAG_TEMP_ICON
;
368 item
->image
= default_pixmap
+ TYPE_APPDIR
;
372 if (base_type
== TYPE_FILE
&&
373 (info
.st_mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
)))
375 item
->image
= default_pixmap
+ TYPE_EXEC_FILE
;
376 item
->flags
|= ITEM_FLAG_EXEC_FILE
;
378 else if (base_type
== TYPE_FILE
)
380 item
->mime_type
= type_from_path(path
->str
);
381 item
->image
= type_to_icon(filer_window
->window
,
383 item
->flags
|= ITEM_FLAG_TEMP_ICON
;
386 item
->image
= default_pixmap
+ base_type
;
389 item
->text_width
= gdk_string_width(filer_window
->window
->style
->font
,
393 item
->image
= default_pixmap
+ TYPE_UNKNOWN
;
395 /* XXX: Must be a better way... */
396 item
->pix_width
= ((GdkPixmapPrivate
*) item
->image
->pixmap
)->width
;
397 item
->pix_height
= ((GdkPixmapPrivate
*) item
->image
->pixmap
)->height
;
399 item_width
= MAX(item
->pix_width
, item
->text_width
) + 4;
401 if (item_width
> filer_window
->scan_min_width
)
402 filer_window
->scan_min_width
= item_width
;
404 if (item_width
> filer_window
->collection
->item_width
)
405 collection_set_item_size(filer_window
->collection
,
407 filer_window
->collection
->item_height
);
409 old_item
= collection_find_item(filer_window
->collection
, item
,
414 CollectionItem
*old
=
415 &filer_window
->collection
->items
[old_item
];
417 free_item((FileItem
*) old
->data
);
419 collection_draw_item(filer_window
->collection
, old_item
, TRUE
);
422 collection_insert(filer_window
->collection
, item
);
425 /* Is a point inside an item? */
426 static gboolean
test_point(Collection
*collection
,
427 int point_x
, int point_y
,
428 CollectionItem
*colitem
,
429 int width
, int height
)
431 FileItem
*item
= (FileItem
*) colitem
->data
;
432 GdkFont
*font
= GTK_WIDGET(collection
)->style
->font
;
433 int text_height
= font
->ascent
+ font
->descent
;
434 int image_y
= MAX(0, MAX_ICON_HEIGHT
- item
->pix_height
);
435 int image_width
= (item
->pix_width
>> 1) + 2;
436 int text_width
= (item
->text_width
>> 1) + 2;
439 if (point_y
< image_y
)
440 return FALSE
; /* Too high up (don't worry about too low) */
442 if (point_y
<= image_y
+ item
->pix_height
+ 2)
443 x_limit
= image_width
;
444 else if (point_y
> height
- text_height
- 2)
445 x_limit
= text_width
;
447 x_limit
= MIN(image_width
, text_width
);
449 return ABS(point_x
- (width
>> 1)) < x_limit
;
452 static void draw_item(GtkWidget
*widget
,
453 CollectionItem
*colitem
,
456 FileItem
*item
= (FileItem
*) colitem
->data
;
457 GdkGC
*gc
= colitem
->selected
? widget
->style
->white_gc
458 : widget
->style
->black_gc
;
459 int image_x
= area
->x
+ ((area
->width
- item
->pix_width
) >> 1);
460 GdkFont
*font
= widget
->style
->font
;
461 int text_x
= area
->x
+ ((area
->width
- item
->text_width
) >> 1);
462 int text_y
= area
->y
+ area
->height
- font
->descent
- 2;
463 int text_height
= font
->ascent
+ font
->descent
;
469 gdk_gc_set_clip_mask(gc
, item
->image
->mask
);
471 image_y
= MAX(0, MAX_ICON_HEIGHT
- item
->pix_height
);
472 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ image_y
);
473 gdk_draw_pixmap(widget
->window
, gc
,
475 0, 0, /* Source x,y */
476 image_x
, area
->y
+ image_y
, /* Dest x,y */
477 -1, MIN(item
->pix_height
, MAX_ICON_HEIGHT
));
479 if (item
->flags
& ITEM_FLAG_SYMLINK
)
481 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ 8);
482 gdk_gc_set_clip_mask(gc
,
483 default_pixmap
[TYPE_SYMLINK
].mask
);
484 gdk_draw_pixmap(widget
->window
, gc
,
485 default_pixmap
[TYPE_SYMLINK
].pixmap
,
486 0, 0, /* Source x,y */
487 image_x
, area
->y
+ 8, /* Dest x,y */
490 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
492 int type
= item
->flags
& ITEM_FLAG_MOUNTED
495 gdk_gc_set_clip_origin(gc
, image_x
, area
->y
+ 8);
496 gdk_gc_set_clip_mask(gc
,
497 default_pixmap
[type
].mask
);
498 gdk_draw_pixmap(widget
->window
, gc
,
499 default_pixmap
[type
].pixmap
,
500 0, 0, /* Source x,y */
501 image_x
, area
->y
+ 8, /* Dest x,y */
505 gdk_gc_set_clip_mask(gc
, NULL
);
506 gdk_gc_set_clip_origin(gc
, 0, 0);
509 if (colitem
->selected
)
510 gtk_paint_flat_box(widget
->style
, widget
->window
,
511 GTK_STATE_SELECTED
, GTK_SHADOW_NONE
,
512 NULL
, widget
, "text",
513 text_x
, text_y
- font
->ascent
,
517 gdk_draw_text(widget
->window
,
519 colitem
->selected
? widget
->style
->white_gc
520 : widget
->style
->black_gc
,
522 item
->leafname
, strlen(item
->leafname
));
525 void show_menu(Collection
*collection
, GdkEventButton
*event
,
526 int item
, gpointer user_data
)
528 show_filer_menu((FilerWindow
*) user_data
, event
, item
);
531 static void may_rescan(FilerWindow
*filer_window
)
535 g_return_if_fail(filer_window
!= NULL
);
537 if (stat(filer_window
->path
, &info
))
539 delayed_error("ROX-Filer", "Directory deleted");
540 gtk_widget_destroy(filer_window
->window
);
542 else if (info
.st_mtime
> filer_window
->m_time
)
544 if (filer_window
->dir
)
545 filer_window
->flags
|= FILER_NEEDS_RESCAN
;
547 update_dir(filer_window
);
551 /* Callback to collection_delete_if() */
552 static gboolean
remove_deleted(gpointer item_data
, gpointer data
)
554 FileItem
*item
= (FileItem
*) item_data
;
556 if (item
->flags
& ITEM_FLAG_MAY_DELETE
)
565 /* Forget the old contents of a filer window and scan the directory
566 * from the start. If we are already scanning then rescan later.
568 void scan_dir(FilerWindow
*filer_window
)
570 if (filer_window
->dir
)
571 stop_scanning(filer_window
);
573 free_items(filer_window
);
574 collection_clear(filer_window
->collection
);
576 update_dir(filer_window
);
577 filer_window
->flags
&= ~FILER_UPDATING
;
579 gdk_window_set_cursor(filer_window
->window
->window
,
580 gdk_cursor_new(GDK_WATCH
));
583 /* Like scan_dir(), but assume new display will be similar to the old
584 * one (less flicker and doesn't lose the selection).
586 void update_dir(FilerWindow
*filer_window
)
588 Collection
*collection
= filer_window
->collection
;
592 if (filer_window
->dir
)
594 /* Already scanning - start again when we finish */
595 filer_window
->flags
|= FILER_NEEDS_RESCAN
;
598 filer_window
->flags
&= ~FILER_NEEDS_RESCAN
;
599 filer_window
->flags
|= FILER_UPDATING
;
601 for (i
= 0; i
< collection
->number_of_items
; i
++)
603 FileItem
*item
= (FileItem
*) collection
->items
[i
].data
;
604 item
->flags
|= ITEM_FLAG_MAY_DELETE
;
609 gtk_window_set_title(GTK_WINDOW(filer_window
->window
),
612 if (stat(filer_window
->path
, &info
))
614 report_error("Error statting directory", g_strerror(errno
));
617 filer_window
->m_time
= info
.st_mtime
;
619 filer_window
->dir
= opendir(filer_window
->path
);
620 if (!filer_window
->dir
)
622 report_error("Error scanning directory", g_strerror(errno
));
626 filer_window
->scan_min_width
= 64;
628 filer_window
->idle_scan_id
= gtk_idle_add(idle_scan_dir
, filer_window
);
631 /* Another app has grabbed the selection */
632 static gint
collection_lose_selection(GtkWidget
*widget
,
633 GdkEventSelection
*event
)
635 if (window_with_selection
&&
636 window_with_selection
->collection
== COLLECTION(widget
))
638 FilerWindow
*filer_window
= window_with_selection
;
639 window_with_selection
= NULL
;
640 collection_clear_selection(filer_window
->collection
);
646 /* Someone wants us to send them the selection */
647 static void selection_get(GtkWidget
*widget
,
648 GtkSelectionData
*selection_data
,
653 GString
*reply
, *header
;
654 FilerWindow
*filer_window
;
656 Collection
*collection
;
658 filer_window
= gtk_object_get_data(GTK_OBJECT(widget
), "filer_window");
660 reply
= g_string_new(NULL
);
661 header
= g_string_new(NULL
);
666 g_string_sprintf(header
, " %s",
667 make_path(filer_window
->path
, "")->str
);
669 case TARGET_URI_LIST
:
670 g_string_sprintf(header
, " file://%s%s",
672 make_path(filer_window
->path
, "")->str
);
676 collection
= filer_window
->collection
;
677 for (i
= 0; i
< collection
->number_of_items
; i
++)
679 if (collection
->items
[i
].selected
)
682 (FileItem
*) collection
->items
[i
].data
;
684 g_string_append(reply
, header
->str
);
685 g_string_append(reply
, item
->leafname
);
688 /* This works, but I don't think I like it... */
689 /* g_string_append_c(reply, ' '); */
691 gtk_selection_data_set(selection_data
, xa_string
,
692 8, reply
->str
+ 1, reply
->len
- 1);
693 g_string_free(reply
, TRUE
);
694 g_string_free(header
, TRUE
);
697 /* No items are now selected. This might be because another app claimed
698 * the selection or because the user unselected all the items.
700 static void lose_selection(Collection
*collection
,
704 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
706 if (window_with_selection
== filer_window
)
708 window_with_selection
= NULL
;
709 gtk_selection_owner_set(NULL
,
710 GDK_SELECTION_PRIMARY
,
715 static void gain_selection(Collection
*collection
,
719 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
721 if (gtk_selection_owner_set(GTK_WIDGET(collection
),
722 GDK_SELECTION_PRIMARY
,
725 window_with_selection
= filer_window
;
728 collection_clear_selection(filer_window
->collection
);
731 static int sort_by_name(const void *item1
, const void *item2
)
733 return strcmp((*((FileItem
**)item1
))->leafname
,
734 (*((FileItem
**)item2
))->leafname
);
737 static int sort_by_type(const void *item1
, const void *item2
)
739 const FileItem
*i1
= (FileItem
*) ((CollectionItem
*) item1
)->data
;
740 const FileItem
*i2
= (FileItem
*) ((CollectionItem
*) item2
)->data
;
743 int diff
= i1
->base_type
- i2
->base_type
;
746 diff
= (i1
->flags
& ITEM_FLAG_APPDIR
)
747 - (i2
->flags
& ITEM_FLAG_APPDIR
);
749 return diff
> 0 ? 1 : -1;
756 diff
= strcmp(m1
->media_type
, m2
->media_type
);
758 diff
= strcmp(m1
->subtype
, m2
->subtype
);
766 return diff
> 0 ? 1 : -1;
768 return sort_by_name(item1
, item2
);
771 void open_item(Collection
*collection
,
772 gpointer item_data
, int item_number
,
775 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
776 FileItem
*item
= (FileItem
*) item_data
;
777 GdkEventButton
*event
;
781 gboolean adjust
; /* do alternative action */
783 event
= (GdkEventButton
*) gtk_get_current_event();
784 full_path
= make_path(filer_window
->path
, item
->leafname
)->str
;
786 collection_wink_item(filer_window
->collection
, item_number
);
788 if (event
->type
== GDK_2BUTTON_PRESS
|| event
->type
== GDK_BUTTON_PRESS
)
790 shift
= event
->state
& GDK_SHIFT_MASK
;
791 adjust
= (event
->button
!= 1)
792 ^ ((event
->state
& GDK_CONTROL_MASK
) != 0);
800 widget
= filer_window
->window
;
802 switch (item
->base_type
)
805 if (item
->flags
& ITEM_FLAG_APPDIR
&& !shift
)
807 run_app(make_path(filer_window
->path
,
808 item
->leafname
)->str
);
809 if (adjust
&& !filer_window
->panel
)
810 gtk_widget_destroy(widget
);
813 if ((adjust
^ o_ro_bindings
) || filer_window
->panel
)
814 filer_opendir(full_path
, FALSE
, BOTTOM
);
817 remove_view(filer_window
);
818 filer_window
->path
= pathdup(full_path
);
819 add_view(filer_window
);
820 scan_dir(filer_window
);
824 if (item
->flags
& ITEM_FLAG_EXEC_FILE
&& !shift
)
826 char *argv
[] = {NULL
, NULL
};
830 if (spawn_full(argv
, getenv("HOME"), 0))
832 if (adjust
&& !filer_window
->panel
)
833 gtk_widget_destroy(widget
);
836 report_error("ROX-Filer",
837 "Failed to fork() child");
842 MIME_type
*type
= shift
? &text_plain
845 g_return_if_fail(type
!= NULL
);
847 if (type_open(full_path
, type
))
849 if (adjust
&& !filer_window
->panel
)
850 gtk_widget_destroy(widget
);
854 message
= g_string_new(NULL
);
855 g_string_sprintf(message
, "No open "
856 "action specified for files of "
860 report_error("ROX-Filer", message
->str
);
861 g_string_free(message
, TRUE
);
866 report_error("open_item",
867 "I don't know how to open that");
872 static gint
pointer_in(GtkWidget
*widget
,
873 GdkEventCrossing
*event
,
874 FilerWindow
*filer_window
)
876 may_rescan(filer_window
);
880 static gint
focus_in(GtkWidget
*widget
,
881 GdkEventFocus
*event
,
882 FilerWindow
*filer_window
)
884 window_with_focus
= filer_window
;
889 static gint
focus_out(GtkWidget
*widget
,
890 GdkEventFocus
*event
,
891 FilerWindow
*filer_window
)
893 /* TODO: Shade the cursor */
898 /* Handle keys that can't be bound with the menu */
899 static gint
key_press_event(GtkWidget
*widget
,
901 FilerWindow
*filer_window
)
903 switch (event
->keyval
)
921 change_to_parent(filer_window
);
928 static void toolbar_home_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
)
930 remove_view(filer_window
);
931 filer_window
->path
= pathdup(getenv("HOME"));
932 add_view(filer_window
);
933 scan_dir(filer_window
);
936 static void toolbar_up_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
)
938 change_to_parent(filer_window
);
941 void change_to_parent(FilerWindow
*filer_window
)
943 remove_view(filer_window
);
944 filer_window
->path
= pathdup(make_path(
947 add_view(filer_window
);
948 scan_dir(filer_window
);
951 FileItem
*selected_item(Collection
*collection
)
955 g_return_val_if_fail(collection
!= NULL
, NULL
);
956 g_return_val_if_fail(IS_COLLECTION(collection
), NULL
);
957 g_return_val_if_fail(collection
->number_selected
== 1, NULL
);
959 for (i
= 0; i
< collection
->number_of_items
; i
++)
960 if (collection
->items
[i
].selected
)
961 return (FileItem
*) collection
->items
[i
].data
;
963 g_warning("selected_item: number_selected is wrong\n");
968 /* Refresh all windows onto this directory */
969 void refresh_dirs(char *path
)
974 real
= pathdup(path
);
975 list
= g_hash_table_lookup(path_to_window_list
, real
);
981 update_dir((FilerWindow
*) list
->data
);
986 void filer_opendir(char *path
, gboolean panel
, Side panel_side
)
988 GtkWidget
*hbox
, *scrollbar
, *collection
;
989 FilerWindow
*filer_window
;
990 GtkTargetEntry target_table
[] =
992 {"text/uri-list", 0, TARGET_URI_LIST
},
993 {"STRING", 0, TARGET_STRING
},
996 filer_window
= g_malloc(sizeof(FilerWindow
));
997 filer_window
->path
= pathdup(path
);
998 filer_window
->dir
= NULL
; /* Not scanning */
999 filer_window
->show_hidden
= FALSE
;
1000 filer_window
->panel
= panel
;
1001 filer_window
->panel_side
= panel_side
;
1002 filer_window
->temp_item_selected
= FALSE
;
1003 filer_window
->sort_fn
= sort_by_type
;
1004 filer_window
->flags
= (FilerFlags
) 0;
1006 filer_window
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
1008 collection
= collection_new(NULL
);
1009 gtk_object_set_data(GTK_OBJECT(collection
),
1010 "filer_window", filer_window
);
1011 filer_window
->collection
= COLLECTION(collection
);
1012 collection_set_item_size(filer_window
->collection
, 64, 64);
1013 collection_set_functions(filer_window
->collection
,
1014 draw_item
, test_point
);
1016 gtk_widget_add_events(filer_window
->window
, GDK_ENTER_NOTIFY
);
1017 gtk_signal_connect(GTK_OBJECT(filer_window
->window
),
1018 "enter-notify-event",
1019 GTK_SIGNAL_FUNC(pointer_in
), filer_window
);
1020 gtk_signal_connect(GTK_OBJECT(filer_window
->window
), "focus_in_event",
1021 GTK_SIGNAL_FUNC(focus_in
), filer_window
);
1022 gtk_signal_connect(GTK_OBJECT(filer_window
->window
), "focus_out_event",
1023 GTK_SIGNAL_FUNC(focus_out
), filer_window
);
1024 gtk_signal_connect(GTK_OBJECT(filer_window
->window
), "destroy",
1025 filer_window_destroyed
, filer_window
);
1027 gtk_signal_connect(GTK_OBJECT(filer_window
->collection
), "open_item",
1028 open_item
, filer_window
);
1029 gtk_signal_connect(GTK_OBJECT(collection
), "show_menu",
1030 show_menu
, filer_window
);
1031 gtk_signal_connect(GTK_OBJECT(collection
), "gain_selection",
1032 gain_selection
, filer_window
);
1033 gtk_signal_connect(GTK_OBJECT(collection
), "lose_selection",
1034 lose_selection
, filer_window
);
1035 gtk_signal_connect(GTK_OBJECT(collection
), "drag_selection",
1036 drag_selection
, filer_window
);
1037 gtk_signal_connect(GTK_OBJECT(collection
), "drag_data_get",
1038 drag_data_get
, filer_window
);
1039 gtk_signal_connect(GTK_OBJECT(collection
), "selection_clear_event",
1040 GTK_SIGNAL_FUNC(collection_lose_selection
), NULL
);
1041 gtk_signal_connect (GTK_OBJECT(collection
), "selection_get",
1042 GTK_SIGNAL_FUNC(selection_get
), NULL
);
1043 gtk_selection_add_targets(collection
, GDK_SELECTION_PRIMARY
,
1045 sizeof(target_table
) / sizeof(*target_table
));
1047 drag_set_dest(collection
);
1051 int swidth
, sheight
, iwidth
, iheight
;
1052 GtkWidget
*frame
, *win
= filer_window
->window
;
1054 collection_set_panel(filer_window
->collection
, TRUE
);
1056 gdk_window_get_size(GDK_ROOT_PARENT(), &swidth
, &sheight
);
1057 iwidth
= filer_window
->collection
->item_width
;
1058 iheight
= filer_window
->collection
->item_height
;
1060 if (panel_side
== TOP
|| panel_side
== BOTTOM
)
1062 int height
= iheight
+ PANEL_BORDER
;
1063 int y
= panel_side
== TOP
1065 : sheight
- height
- PANEL_BORDER
;
1067 gtk_widget_set_usize(collection
, swidth
, height
);
1068 gtk_widget_set_uposition(win
, 0, y
);
1072 int width
= iwidth
+ PANEL_BORDER
;
1073 int x
= panel_side
== LEFT
1075 : swidth
- width
- PANEL_BORDER
;
1077 gtk_widget_set_usize(collection
, width
, sheight
);
1078 gtk_widget_set_uposition(win
, x
, 0);
1081 frame
= gtk_frame_new(NULL
);
1082 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_OUT
);
1083 gtk_container_add(GTK_CONTAINER(frame
), collection
);
1084 gtk_container_add(GTK_CONTAINER(win
), frame
);
1086 gtk_widget_realize(win
);
1087 if (override_redirect
)
1088 gdk_window_set_override_redirect(win
->window
, TRUE
);
1089 make_panel_window(win
->window
);
1093 hbox
= gtk_hbox_new(FALSE
, 0);
1095 gtk_signal_connect(GTK_OBJECT(filer_window
->window
),
1097 GTK_SIGNAL_FUNC(key_press_event
), filer_window
);
1098 gtk_window_set_default_size(GTK_WINDOW(filer_window
->window
),
1100 o_toolbar
? 220 : 200);
1102 gtk_container_add(GTK_CONTAINER(filer_window
->window
),
1106 GtkWidget
*vbox
, *toolbar
;
1109 vbox
= gtk_vbox_new(FALSE
, 0);
1110 gtk_box_pack_start(GTK_BOX(hbox
), vbox
,
1112 toolbar
= create_toolbar(filer_window
);
1113 gtk_box_pack_start(GTK_BOX(vbox
), toolbar
,
1116 gtk_box_pack_start(GTK_BOX(vbox
), collection
,
1120 gtk_box_pack_start(GTK_BOX(hbox
), collection
,
1123 scrollbar
= gtk_vscrollbar_new(COLLECTION(collection
)->vadj
);
1124 gtk_box_pack_start(GTK_BOX(hbox
), scrollbar
, FALSE
, TRUE
, 0);
1125 gtk_accel_group_attach(filer_keys
,
1126 GTK_OBJECT(filer_window
->window
));
1129 gtk_widget_show_all(filer_window
->window
);
1130 number_of_windows
++;
1132 add_view(filer_window
);
1133 scan_dir(filer_window
);
1136 static GtkWidget
*create_toolbar(FilerWindow
*filer_window
)
1138 GtkWidget
*frame
, *box
;
1140 frame
= gtk_frame_new(NULL
);
1141 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_OUT
);
1143 box
= gtk_hbutton_box_new();
1144 gtk_button_box_set_child_size_default(16, 16);
1145 gtk_hbutton_box_set_spacing_default(2);
1146 gtk_button_box_set_layout(GTK_BUTTON_BOX(box
), GTK_BUTTONBOX_START
);
1147 gtk_container_add(GTK_CONTAINER(frame
), box
);
1148 add_button(GTK_CONTAINER(box
), TOOLBAR_UP_ICON
,
1149 GTK_SIGNAL_FUNC(toolbar_up_clicked
),
1151 add_button(GTK_CONTAINER(box
), TOOLBAR_HOME_ICON
,
1152 GTK_SIGNAL_FUNC(toolbar_home_clicked
),
1158 static void add_button(GtkContainer
*box
, int pixmap
,
1159 GtkSignalFunc cb
, gpointer data
)
1161 GtkWidget
*button
, *icon
;
1163 button
= gtk_button_new();
1164 GTK_WIDGET_UNSET_FLAGS(button
, GTK_CAN_FOCUS
);
1165 gtk_container_add(box
, button
);
1167 icon
= gtk_pixmap_new(default_pixmap
[pixmap
].pixmap
,
1168 default_pixmap
[pixmap
].mask
);
1169 gtk_container_add(GTK_CONTAINER(button
), icon
);
1170 gtk_signal_connect(GTK_OBJECT(button
), "clicked", cb
, data
);
1173 /* Build up some option widgets to go in the options dialog, but don't
1176 static GtkWidget
*create_options()
1180 vbox
= gtk_vbox_new(FALSE
, 0);
1181 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 4);
1183 toggle_ro_bindings
=
1184 gtk_check_button_new_with_label("Use RISC OS mouse bindings");
1185 gtk_box_pack_start(GTK_BOX(vbox
), toggle_ro_bindings
, FALSE
, TRUE
, 0);
1188 gtk_check_button_new_with_label("Show toolbar on new windows");
1189 gtk_box_pack_start(GTK_BOX(vbox
), toggle_toolbar
, FALSE
, TRUE
, 0);
1194 /* Reflect current state by changing the widgets in the options box */
1195 static void update_options()
1197 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_ro_bindings
),
1199 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_toolbar
),
1203 /* Set current values by reading the states of the widgets in the options box */
1204 static void set_options()
1206 o_ro_bindings
= gtk_toggle_button_get_active(
1207 GTK_TOGGLE_BUTTON(toggle_ro_bindings
));
1208 o_toolbar
= gtk_toggle_button_get_active(
1209 GTK_TOGGLE_BUTTON(toggle_toolbar
));
1212 static void save_options()
1214 option_write("filer_ro_bindings", o_ro_bindings
? "1" : "0");
1215 option_write("filer_toolbar", o_toolbar
? "1" : "0");
1218 static char *filer_ro_bindings(char *data
)
1220 o_ro_bindings
= atoi(data
) != 0;
1225 static char *filer_toolbar(char *data
)
1227 o_toolbar
= atoi(data
) != 0;