4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2000, Thomas Leonard, <tal197@users.sourceforge.net>.
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"
42 #include "gui_support.h"
52 #include "minibuffer.h"
56 #define PANEL_BORDER 2
58 extern int collection_menu_button
;
59 extern gboolean collection_single_click
;
61 FilerWindow
*window_with_focus
= NULL
;
62 GList
*all_filer_windows
= 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_single_click(char *data
);
72 static char *filer_unique_windows(char *data
);
73 static char *filer_menu_on_2(char *data
);
74 static char *filer_new_window_on_1(char *data
);
76 static OptionsSection options
=
78 N_("Filer window options"),
85 gboolean o_single_click
= TRUE
;
86 gboolean o_new_window_on_1
= FALSE
; /* Button 1 => New window */
87 gboolean o_unique_filer_windows
= FALSE
;
88 static GtkWidget
*toggle_single_click
;
89 static GtkWidget
*toggle_new_window_on_1
;
90 static GtkWidget
*toggle_menu_on_2
;
91 static GtkWidget
*toggle_unique_filer_windows
;
93 /* Static prototypes */
94 static void attach(FilerWindow
*filer_window
);
95 static void detach(FilerWindow
*filer_window
);
96 static void filer_window_destroyed(GtkWidget
*widget
,
97 FilerWindow
*filer_window
);
98 static void show_menu(Collection
*collection
, GdkEventButton
*event
,
99 int number_selected
, gpointer user_data
);
100 static gint
focus_in(GtkWidget
*widget
,
101 GdkEventFocus
*event
,
102 FilerWindow
*filer_window
);
103 static gint
focus_out(GtkWidget
*widget
,
104 GdkEventFocus
*event
,
105 FilerWindow
*filer_window
);
106 static void add_item(FilerWindow
*filer_window
, DirItem
*item
);
107 static int filer_confirm_close(GtkWidget
*widget
, GdkEvent
*event
,
108 FilerWindow
*window
);
109 static void update_display(Directory
*dir
,
112 FilerWindow
*filer_window
);
113 static void set_scanning_display(FilerWindow
*filer_window
, gboolean scanning
);
114 static gboolean
may_rescan(FilerWindow
*filer_window
, gboolean warning
);
115 static void open_item(Collection
*collection
,
116 gpointer item_data
, int item_number
,
118 static gboolean
minibuffer_show_cb(FilerWindow
*filer_window
);
119 static FilerWindow
*find_filer_window(char *path
, FilerWindow
*diff
);
120 static void filer_set_title(FilerWindow
*filer_window
);
122 static GdkAtom xa_string
;
129 static GdkCursor
*busy_cursor
= NULL
;
133 xa_string
= gdk_atom_intern("STRING", FALSE
);
135 options_sections
= g_slist_prepend(options_sections
, &options
);
136 option_register("filer_new_window_on_1", filer_new_window_on_1
);
137 option_register("filer_menu_on_2", filer_menu_on_2
);
138 option_register("filer_single_click", filer_single_click
);
139 option_register("filer_unique_windows", filer_unique_windows
);
141 busy_cursor
= gdk_cursor_new(GDK_WATCH
);
144 static gboolean
if_deleted(gpointer item
, gpointer removed
)
146 int i
= ((GPtrArray
*) removed
)->len
;
147 DirItem
**r
= (DirItem
**) ((GPtrArray
*) removed
)->pdata
;
148 char *leafname
= ((DirItem
*) item
)->leafname
;
152 if (strcmp(leafname
, r
[i
]->leafname
) == 0)
159 static void update_item(FilerWindow
*filer_window
, DirItem
*item
)
162 char *leafname
= item
->leafname
;
164 if (leafname
[0] == '.')
166 if (filer_window
->show_hidden
== FALSE
|| leafname
[1] == '\0'
167 || (leafname
[1] == '.' && leafname
[2] == '\0'))
171 i
= collection_find_item(filer_window
->collection
, item
, dir_item_cmp
);
174 collection_draw_item(filer_window
->collection
, i
, TRUE
);
176 g_warning("Failed to find '%s'\n", item
->leafname
);
179 static void update_display(Directory
*dir
,
182 FilerWindow
*filer_window
)
186 int cursor
= filer_window
->collection
->cursor_item
;
188 Collection
*collection
= filer_window
->collection
;
193 as
= filer_window
->auto_select
;
195 old_num
= collection
->number_of_items
;
196 for (i
= 0; i
< items
->len
; i
++)
198 DirItem
*item
= (DirItem
*) items
->pdata
[i
];
200 add_item(filer_window
, item
);
202 if (cursor
!= -1 || !as
)
205 if (strcmp(as
, item
->leafname
) != 0)
208 cursor
= collection
->number_of_items
- 1;
209 if (filer_window
->had_cursor
)
211 collection_set_cursor_item(collection
,
213 filer_window
->mini_cursor_base
= cursor
;
216 collection_wink_item(collection
,
220 if (old_num
!= collection
->number_of_items
)
221 collection_qsort(filer_window
->collection
,
222 filer_window
->sort_fn
);
225 collection_delete_if(filer_window
->collection
,
230 set_scanning_display(filer_window
, TRUE
);
233 if (filer_window
->window
->window
)
234 gdk_window_set_cursor(
235 filer_window
->window
->window
,
237 shrink_width(filer_window
);
238 if (filer_window
->had_cursor
&&
239 collection
->cursor_item
== -1)
241 collection_set_cursor_item(collection
, 0);
242 filer_window
->had_cursor
= FALSE
;
244 set_scanning_display(filer_window
, FALSE
);
247 for (i
= 0; i
< items
->len
; i
++)
249 DirItem
*item
= (DirItem
*) items
->pdata
[i
];
251 update_item(filer_window
, item
);
253 collection_qsort(filer_window
->collection
,
254 filer_window
->sort_fn
);
259 static void attach(FilerWindow
*filer_window
)
261 gdk_window_set_cursor(filer_window
->window
->window
, busy_cursor
);
262 collection_clear(filer_window
->collection
);
263 filer_window
->scanning
= TRUE
;
264 dir_attach(filer_window
->directory
, (DirCallback
) update_display
,
266 filer_set_title(filer_window
);
269 static void detach(FilerWindow
*filer_window
)
271 g_return_if_fail(filer_window
->directory
!= NULL
);
273 dir_detach(filer_window
->directory
,
274 (DirCallback
) update_display
, filer_window
);
275 g_fscache_data_unref(dir_cache
, filer_window
->directory
);
276 filer_window
->directory
= NULL
;
279 static void filer_window_destroyed(GtkWidget
*widget
,
280 FilerWindow
*filer_window
)
282 all_filer_windows
= g_list_remove(all_filer_windows
, filer_window
);
284 if (window_with_selection
== filer_window
)
285 window_with_selection
= NULL
;
287 if (window_with_focus
== filer_window
)
290 gtk_menu_popdown(GTK_MENU(popup_menu
));
291 window_with_focus
= NULL
;
294 if (filer_window
->directory
)
295 detach(filer_window
);
297 g_free(filer_window
->auto_select
);
298 g_free(filer_window
->path
);
299 g_free(filer_window
);
301 if (--number_of_windows
< 1)
305 /* Add a single object to a directory display */
306 static void add_item(FilerWindow
*filer_window
, DirItem
*item
)
308 char *leafname
= item
->leafname
;
311 if (leafname
[0] == '.')
313 if (filer_window
->show_hidden
== FALSE
|| leafname
[1] == '\0'
314 || (leafname
[1] == '.' && leafname
[2] == '\0'))
318 item_width
= calc_width(filer_window
, item
);
319 if (item_width
> filer_window
->collection
->item_width
)
320 collection_set_item_size(filer_window
->collection
,
322 filer_window
->collection
->item_height
);
323 collection_insert(filer_window
->collection
, item
);
326 static void show_menu(Collection
*collection
, GdkEventButton
*event
,
327 int item
, gpointer user_data
)
329 show_filer_menu((FilerWindow
*) user_data
, event
, item
);
332 /* Returns TRUE iff the directory still exists. */
333 static gboolean
may_rescan(FilerWindow
*filer_window
, gboolean warning
)
337 g_return_val_if_fail(filer_window
!= NULL
, FALSE
);
339 /* We do a fresh lookup (rather than update) because the inode may
342 dir
= g_fscache_lookup(dir_cache
, filer_window
->path
);
346 delayed_error(PROJECT
, _("Directory missing/deleted"));
347 gtk_widget_destroy(filer_window
->window
);
350 if (dir
== filer_window
->directory
)
351 g_fscache_data_unref(dir_cache
, dir
);
354 detach(filer_window
);
355 filer_window
->directory
= dir
;
356 attach(filer_window
);
362 /* Another app has grabbed the selection */
363 static gint
collection_lose_selection(GtkWidget
*widget
,
364 GdkEventSelection
*event
)
366 if (window_with_selection
&&
367 window_with_selection
->collection
== COLLECTION(widget
))
369 FilerWindow
*filer_window
= window_with_selection
;
370 window_with_selection
= NULL
;
371 collection_clear_selection(filer_window
->collection
);
377 /* Someone wants us to send them the selection */
378 static void selection_get(GtkWidget
*widget
,
379 GtkSelectionData
*selection_data
,
384 GString
*reply
, *header
;
385 FilerWindow
*filer_window
;
387 Collection
*collection
;
389 filer_window
= gtk_object_get_data(GTK_OBJECT(widget
), "filer_window");
391 reply
= g_string_new(NULL
);
392 header
= g_string_new(NULL
);
397 g_string_sprintf(header
, " %s",
398 make_path(filer_window
->path
, "")->str
);
400 case TARGET_URI_LIST
:
401 g_string_sprintf(header
, " file://%s%s",
403 make_path(filer_window
->path
, "")->str
);
407 collection
= filer_window
->collection
;
408 for (i
= 0; i
< collection
->number_of_items
; i
++)
410 if (collection
->items
[i
].selected
)
413 (DirItem
*) collection
->items
[i
].data
;
415 g_string_append(reply
, header
->str
);
416 g_string_append(reply
, item
->leafname
);
419 /* This works, but I don't think I like it... */
420 /* g_string_append_c(reply, ' '); */
422 gtk_selection_data_set(selection_data
, xa_string
,
423 8, reply
->str
+ 1, reply
->len
- 1);
424 g_string_free(reply
, TRUE
);
425 g_string_free(header
, TRUE
);
428 /* No items are now selected. This might be because another app claimed
429 * the selection or because the user unselected all the items.
431 static void lose_selection(Collection
*collection
,
435 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
437 if (window_with_selection
== filer_window
)
439 window_with_selection
= NULL
;
440 gtk_selection_owner_set(NULL
,
441 GDK_SELECTION_PRIMARY
,
446 static void gain_selection(Collection
*collection
,
450 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
452 if (gtk_selection_owner_set(GTK_WIDGET(collection
),
453 GDK_SELECTION_PRIMARY
,
456 window_with_selection
= filer_window
;
459 collection_clear_selection(filer_window
->collection
);
462 static void open_item(Collection
*collection
,
463 gpointer item_data
, int item_number
,
466 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
468 GdkEventButton
*bevent
;
472 event
= (GdkEvent
*) gtk_get_current_event();
474 bevent
= (GdkEventButton
*) event
;
475 kevent
= (GdkEventKey
*) event
;
479 case GDK_2BUTTON_PRESS
:
480 case GDK_BUTTON_PRESS
:
481 case GDK_BUTTON_RELEASE
:
482 if (bevent
->state
& GDK_SHIFT_MASK
)
485 if (o_new_window_on_1
^ (bevent
->button
== 1))
486 flags
|= OPEN_SAME_WINDOW
;
488 if (bevent
->button
!= 1)
489 flags
|= OPEN_CLOSE_WINDOW
;
491 if (o_single_click
== FALSE
&&
492 (bevent
->state
& GDK_CONTROL_MASK
) != 0)
493 flags
^= OPEN_SAME_WINDOW
| OPEN_CLOSE_WINDOW
;
496 flags
|= OPEN_SAME_WINDOW
;
497 if (kevent
->state
& GDK_SHIFT_MASK
)
504 filer_openitem(filer_window
, item_number
, flags
);
507 /* Open the item (or add it to the shell command minibuffer) */
508 void filer_openitem(FilerWindow
*filer_window
, int item_number
, OpenFlags flags
)
510 gboolean shift
= (flags
& OPEN_SHIFT
) != 0;
511 gboolean close_mini
= flags
& OPEN_FROM_MINI
;
512 gboolean close_window
= (flags
& OPEN_CLOSE_WINDOW
) != 0
513 && !filer_window
->panel_type
;
515 DirItem
*item
= (DirItem
*)
516 filer_window
->collection
->items
[item_number
].data
;
518 gboolean wink
= TRUE
;
521 widget
= filer_window
->window
;
522 if (filer_window
->mini_type
== MINI_SHELL
)
524 minibuffer_add(filer_window
, item
->leafname
);
528 if (item
->base_type
== TYPE_DIRECTORY
)
530 /* Never close a filer window when opening a directory
531 * (click on a dir or click on an app with shift).
533 if (shift
|| !(item
->flags
& ITEM_FLAG_APPDIR
))
534 close_window
= FALSE
;
537 full_path
= make_path(filer_window
->path
, item
->leafname
)->str
;
539 old_dir
= filer_window
->directory
;
540 if (run_diritem(full_path
, item
,
541 flags
& OPEN_SAME_WINDOW
? filer_window
: NULL
,
544 if (old_dir
!= filer_window
->directory
)
548 gtk_widget_destroy(filer_window
->window
);
552 collection_wink_item(filer_window
->collection
,
555 minibuffer_hide(filer_window
);
560 static gint
pointer_in(GtkWidget
*widget
,
561 GdkEventCrossing
*event
,
562 FilerWindow
*filer_window
)
564 may_rescan(filer_window
, TRUE
);
568 static gint
focus_in(GtkWidget
*widget
,
569 GdkEventFocus
*event
,
570 FilerWindow
*filer_window
)
572 window_with_focus
= filer_window
;
577 static gint
focus_out(GtkWidget
*widget
,
578 GdkEventFocus
*event
,
579 FilerWindow
*filer_window
)
581 /* TODO: Shade the cursor */
586 /* Move the cursor to the next selected item in direction 'dir'
589 static void next_selected(FilerWindow
*filer_window
, int dir
)
591 Collection
*collection
= filer_window
->collection
;
592 int to_check
= collection
->number_of_items
;
593 int item
= collection
->cursor_item
;
595 g_return_if_fail(dir
== 1 || dir
== -1);
597 if (to_check
> 0 && item
== -1)
599 /* Cursor not currently on */
603 item
= collection
->number_of_items
- 1;
605 if (collection
->items
[item
].selected
)
609 while (--to_check
> 0)
613 if (item
>= collection
->number_of_items
)
616 item
= collection
->number_of_items
- 1;
618 if (collection
->items
[item
].selected
)
625 collection_set_cursor_item(collection
, item
);
628 /* Handle keys that can't be bound with the menu */
629 static gint
key_press_event(GtkWidget
*widget
,
631 FilerWindow
*filer_window
)
633 switch (event
->keyval
)
635 case GDK_ISO_Left_Tab
:
636 next_selected(filer_window
, -1);
639 next_selected(filer_window
, 1);
642 change_to_parent(filer_window
);
648 gtk_signal_emit_stop_by_name(GTK_OBJECT(widget
), "key_press_event");
652 void change_to_parent(FilerWindow
*filer_window
)
657 if (filer_window
->path
[0] == '/' && filer_window
->path
[1] == '\0')
658 return; /* Already in the root */
660 copy
= g_strdup(filer_window
->path
);
661 slash
= strrchr(copy
, '/');
666 filer_change_to(filer_window
,
671 g_warning("No / in directory path!\n");
677 /* Make filer_window display path. When finished, highlight item 'from', or
678 * the first item if from is NULL. If there is currently no cursor then
679 * simply wink 'from' (if not NULL).
681 void filer_change_to(FilerWindow
*filer_window
, char *path
, char *from
)
684 char *real_path
= pathdup(path
);
686 g_return_if_fail(filer_window
!= NULL
);
688 if (o_unique_filer_windows
)
692 fw
= find_filer_window(real_path
, filer_window
);
694 gtk_widget_destroy(fw
->window
);
697 from_dup
= from
&& *from
? g_strdup(from
) : NULL
;
699 detach(filer_window
);
700 g_free(filer_window
->path
);
701 filer_window
->path
= real_path
;
703 filer_window
->directory
= g_fscache_lookup(dir_cache
,
705 if (filer_window
->directory
)
707 g_free(filer_window
->auto_select
);
708 filer_window
->had_cursor
=
709 filer_window
->collection
->cursor_item
!= -1
710 || filer_window
->had_cursor
;
711 filer_window
->auto_select
= from_dup
;
713 filer_set_title(filer_window
);
714 collection_set_cursor_item(filer_window
->collection
, -1);
715 attach(filer_window
);
717 if (filer_window
->mini_type
== MINI_PATH
)
718 gtk_idle_add((GtkFunction
) minibuffer_show_cb
,
726 error
= g_strdup_printf(_("Directory '%s' is not accessible"),
728 delayed_error(PROJECT
, error
);
730 gtk_widget_destroy(filer_window
->window
);
734 void filer_open_parent(FilerWindow
*filer_window
)
739 if (filer_window
->path
[0] == '/' && filer_window
->path
[1] == '\0')
740 return; /* Already in the root */
742 copy
= g_strdup(filer_window
->path
);
743 slash
= strrchr(copy
, '/');
748 filer_opendir(*copy
? copy
: "/");
751 g_warning("No / in directory path!\n");
756 int selected_item_number(Collection
*collection
)
760 g_return_val_if_fail(collection
!= NULL
, -1);
761 g_return_val_if_fail(IS_COLLECTION(collection
), -1);
762 g_return_val_if_fail(collection
->number_selected
== 1, -1);
764 for (i
= 0; i
< collection
->number_of_items
; i
++)
765 if (collection
->items
[i
].selected
)
768 g_warning("selected_item: number_selected is wrong\n");
773 DirItem
*selected_item(Collection
*collection
)
777 item
= selected_item_number(collection
);
780 return (DirItem
*) collection
->items
[item
].data
;
784 static int filer_confirm_close(GtkWidget
*widget
, GdkEvent
*event
,
787 /* TODO: We can open lots of these - very irritating! */
788 return get_choice(_("Close panel?"),
789 _("You have tried to close a panel via the window "
790 "manager - I usually find that this is accidental... "
792 2, _("Remove"), _("Cancel")) != 0;
795 /* Append all the URIs in the selection to the string */
796 static void create_uri_list(FilerWindow
*filer_window
, GString
*string
)
798 Collection
*collection
= filer_window
->collection
;
802 leader
= g_string_new("file://");
804 g_string_append(leader
, our_host_name());
805 g_string_append(leader
, filer_window
->path
);
806 if (leader
->str
[leader
->len
- 1] != '/')
807 g_string_append_c(leader
, '/');
809 num_selected
= collection
->number_selected
;
811 for (i
= 0; num_selected
> 0; i
++)
813 if (collection
->items
[i
].selected
)
815 DirItem
*item
= (DirItem
*) collection
->items
[i
].data
;
817 g_string_append(string
, leader
->str
);
818 g_string_append(string
, item
->leafname
);
819 g_string_append(string
, "\r\n");
824 g_string_free(leader
, TRUE
);
827 static void start_drag_selection(Collection
*collection
,
828 GdkEventMotion
*event
,
830 FilerWindow
*filer_window
)
832 GtkWidget
*widget
= (GtkWidget
*) collection
;
834 if (number_selected
== 1)
838 item
= selected_item(collection
);
840 drag_one_item(widget
, event
,
841 make_path(filer_window
->path
, item
->leafname
)->str
,
843 filer_window
->mini_type
== MINI_RUN_ACTION
);
849 uris
= g_string_new(NULL
);
850 create_uri_list(filer_window
, uris
);
851 drag_selection(widget
, event
, uris
->str
);
852 g_string_free(uris
, TRUE
);
856 /* Creates and shows a new filer window */
857 FilerWindow
*filer_opendir(char *path
)
859 return filer_openpanel(path
, PANEL_NO
);
862 /* Creates and shows a new filer window.
863 * panel_type may be PANEL_NO for a normal window.
865 FilerWindow
*filer_openpanel(char *path
, PanelType panel_type
)
867 GtkWidget
*hbox
, *scrollbar
, *collection
;
868 FilerWindow
*filer_window
;
869 GtkTargetEntry target_table
[] =
871 {"text/uri-list", 0, TARGET_URI_LIST
},
872 {"STRING", 0, TARGET_STRING
},
876 /* Get the real pathname of the directory and copy it */
877 real_path
= pathdup(path
);
879 /* If the user doesn't want duplicate windows then check
880 * for an existing one and close it if found.
882 if (o_unique_filer_windows
&& panel_type
== PANEL_NO
)
886 fw
= find_filer_window(real_path
, NULL
);
890 /* TODO: this should bring the window to the front
891 * at the same coordinates.
893 gtk_widget_hide(fw
->window
);
895 gtk_widget_show(fw
->window
);
900 filer_window
= g_new(FilerWindow
, 1);
901 filer_window
->minibuffer
= NULL
;
902 filer_window
->minibuffer_label
= NULL
;
903 filer_window
->minibuffer_area
= NULL
;
904 filer_window
->temp_show_hidden
= FALSE
;
905 filer_window
->path
= real_path
;
906 filer_window
->scanning
= FALSE
;
907 filer_window
->had_cursor
= FALSE
;
908 filer_window
->auto_select
= NULL
;
909 filer_window
->mini_type
= MINI_NONE
;
911 /* Finds the entry for this directory in the dir cache, creating
912 * a new one if needed. This does not cause a scan to start,
913 * so if a new entry is created then it will be empty.
915 filer_window
->directory
= g_fscache_lookup(dir_cache
,
917 if (!filer_window
->directory
)
921 error
= g_strdup_printf(_("Directory '%s' not found."), path
);
922 delayed_error(PROJECT
, error
);
924 g_free(filer_window
->path
);
925 g_free(filer_window
);
929 filer_window
->show_hidden
= last_show_hidden
;
930 filer_window
->panel_type
= panel_type
;
931 filer_window
->temp_item_selected
= FALSE
;
932 filer_window
->sort_fn
= last_sort_fn
;
933 filer_window
->flags
= (FilerFlags
) 0;
934 filer_window
->details_type
= DETAILS_SUMMARY
;
935 filer_window
->display_style
= UNKNOWN_STYLE
;
937 /* Create the top-level window widget */
938 filer_window
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
939 filer_set_title(filer_window
);
941 /* The collection is the area that actually displays the files */
942 collection
= collection_new(NULL
);
943 gtk_object_set_data(GTK_OBJECT(collection
),
944 "filer_window", filer_window
);
945 filer_window
->collection
= COLLECTION(collection
);
947 gtk_widget_add_events(filer_window
->window
, GDK_ENTER_NOTIFY
);
948 gtk_signal_connect(GTK_OBJECT(filer_window
->window
),
949 "enter-notify-event",
950 GTK_SIGNAL_FUNC(pointer_in
), filer_window
);
951 gtk_signal_connect(GTK_OBJECT(filer_window
->window
), "focus_in_event",
952 GTK_SIGNAL_FUNC(focus_in
), filer_window
);
953 gtk_signal_connect(GTK_OBJECT(filer_window
->window
), "focus_out_event",
954 GTK_SIGNAL_FUNC(focus_out
), filer_window
);
955 gtk_signal_connect(GTK_OBJECT(filer_window
->window
), "destroy",
956 filer_window_destroyed
, filer_window
);
958 gtk_signal_connect(GTK_OBJECT(filer_window
->collection
), "open_item",
959 open_item
, filer_window
);
960 gtk_signal_connect(GTK_OBJECT(collection
), "show_menu",
961 show_menu
, filer_window
);
962 gtk_signal_connect(GTK_OBJECT(collection
), "gain_selection",
963 gain_selection
, filer_window
);
964 gtk_signal_connect(GTK_OBJECT(collection
), "lose_selection",
965 lose_selection
, filer_window
);
966 gtk_signal_connect(GTK_OBJECT(collection
), "drag_selection",
967 start_drag_selection
, filer_window
);
968 gtk_signal_connect(GTK_OBJECT(collection
), "drag_data_get",
969 drag_data_get
, NULL
);
970 gtk_signal_connect(GTK_OBJECT(collection
), "selection_clear_event",
971 GTK_SIGNAL_FUNC(collection_lose_selection
), NULL
);
972 gtk_signal_connect(GTK_OBJECT(collection
), "selection_get",
973 GTK_SIGNAL_FUNC(selection_get
), NULL
);
974 gtk_selection_add_targets(collection
, GDK_SELECTION_PRIMARY
,
976 sizeof(target_table
) / sizeof(*target_table
));
978 display_set_layout(filer_window
, last_layout
);
979 drag_set_dest(filer_window
);
981 /* Add decorations appropriate to the window's type */
984 int swidth
, sheight
, iwidth
, iheight
;
985 GtkWidget
*frame
, *win
= filer_window
->window
;
987 gtk_window_set_wmclass(GTK_WINDOW(win
), "ROX-Panel",
989 collection_set_panel(filer_window
->collection
, TRUE
);
990 gtk_signal_connect(GTK_OBJECT(filer_window
->window
),
992 GTK_SIGNAL_FUNC(filer_confirm_close
),
995 gdk_window_get_size(GDK_ROOT_PARENT(), &swidth
, &sheight
);
996 iwidth
= filer_window
->collection
->item_width
;
997 iheight
= filer_window
->collection
->item_height
;
1000 int height
= iheight
+ PANEL_BORDER
;
1001 int y
= panel_type
== PANEL_TOP
1003 : sheight
- height
- PANEL_BORDER
;
1005 gtk_widget_set_usize(collection
, swidth
, height
);
1006 gtk_widget_set_uposition(win
, 0, y
);
1009 frame
= gtk_frame_new(NULL
);
1010 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_OUT
);
1011 gtk_container_add(GTK_CONTAINER(frame
), collection
);
1012 gtk_container_add(GTK_CONTAINER(win
), frame
);
1014 gtk_widget_show_all(frame
);
1015 gtk_widget_realize(win
);
1016 if (override_redirect
)
1017 gdk_window_set_override_redirect(win
->window
, TRUE
);
1018 make_panel_window(win
->window
);
1023 int col_height
= ROW_HEIGHT_LARGE
* 3;
1025 gtk_signal_connect(GTK_OBJECT(collection
),
1027 GTK_SIGNAL_FUNC(key_press_event
), filer_window
);
1028 gtk_window_set_default_size(GTK_WINDOW(filer_window
->window
),
1029 filer_window
->display_style
== LARGE_ICONS
? 400 : 512,
1030 o_toolbar
== TOOLBAR_NONE
? col_height
:
1031 o_toolbar
== TOOLBAR_NORMAL
? col_height
+ 24 :
1034 hbox
= gtk_hbox_new(FALSE
, 0);
1035 gtk_container_add(GTK_CONTAINER(filer_window
->window
),
1038 vbox
= gtk_vbox_new(FALSE
, 0);
1039 gtk_box_pack_start(GTK_BOX(hbox
), vbox
, TRUE
, TRUE
, 0);
1041 if (o_toolbar
!= TOOLBAR_NONE
)
1045 toolbar
= toolbar_new(filer_window
);
1046 gtk_box_pack_start(GTK_BOX(vbox
), toolbar
,
1048 gtk_widget_show_all(toolbar
);
1051 gtk_box_pack_start(GTK_BOX(vbox
), collection
, TRUE
, TRUE
, 0);
1053 create_minibuffer(filer_window
);
1054 gtk_box_pack_start(GTK_BOX(vbox
), filer_window
->minibuffer_area
,
1057 scrollbar
= gtk_vscrollbar_new(COLLECTION(collection
)->vadj
);
1058 gtk_box_pack_start(GTK_BOX(hbox
), scrollbar
, FALSE
, TRUE
, 0);
1059 gtk_accel_group_attach(filer_keys
,
1060 GTK_OBJECT(filer_window
->window
));
1061 gtk_window_set_focus(GTK_WINDOW(filer_window
->window
),
1064 gtk_widget_show(hbox
);
1065 gtk_widget_show(vbox
);
1066 gtk_widget_show(scrollbar
);
1067 gtk_widget_show(collection
);
1070 gtk_widget_realize(filer_window
->window
);
1072 /* The collection is created empty and then attach() is called, which
1073 * links the filer window to the entry in the directory cache we
1074 * looked up / created above.
1076 * The attach() function will immediately callback to the filer window
1077 * to deliver a list of all known entries in the directory (so,
1078 * collection->number_of_items may be valid after the call to
1079 * attach() returns).
1081 * BUT, if the directory was not in the cache (because it hadn't been
1082 * opened it before) then the cached dir will be empty and nothing gets
1083 * added until a while later when some entries are actually available.
1086 attach(filer_window
);
1088 /* Make the window visible */
1089 number_of_windows
++;
1090 all_filer_windows
= g_list_prepend(all_filer_windows
, filer_window
);
1091 gtk_widget_show(filer_window
->window
);
1093 return filer_window
;
1096 static gint
clear_scanning_display(FilerWindow
*filer_window
)
1098 if (filer_exists(filer_window
))
1099 filer_set_title(filer_window
);
1103 static void set_scanning_display(FilerWindow
*filer_window
, gboolean scanning
)
1105 if (scanning
== filer_window
->scanning
)
1107 filer_window
->scanning
= scanning
;
1110 filer_set_title(filer_window
);
1112 gtk_timeout_add(300, (GtkFunction
) clear_scanning_display
,
1116 /* Build up some option widgets to go in the options dialog, but don't
1119 static GtkWidget
*create_options(void)
1123 vbox
= gtk_vbox_new(FALSE
, 0);
1124 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 4);
1126 toggle_new_window_on_1
=
1127 gtk_check_button_new_with_label(
1128 _("New window on button 1 (RISC OS style)"));
1129 OPTION_TIP(toggle_new_window_on_1
,
1130 "Clicking with mouse button 1 (usually the "
1131 "left button) opens a directory in a new window "
1132 "with this turned on. Clicking with the button-2 "
1133 "(middle) will reuse the current window.");
1134 gtk_box_pack_start(GTK_BOX(vbox
), toggle_new_window_on_1
,
1138 gtk_check_button_new_with_label(
1139 _("Menu on button 2 (RISC OS style)"));
1140 OPTION_TIP(toggle_menu_on_2
,
1141 "Use button 2, the middle button (click both buttons "
1142 "at once on two button mice), to pop up the menu. "
1143 "If off, use button 3 (right) instead.");
1144 gtk_box_pack_start(GTK_BOX(vbox
), toggle_menu_on_2
, FALSE
, TRUE
, 0);
1146 toggle_single_click
=
1147 gtk_check_button_new_with_label(_("Single-click nagivation"));
1148 OPTION_TIP(toggle_single_click
,
1149 "Clicking on an item opens it with this on. Hold down "
1150 "Control to select the item instead. If off, clicking "
1151 "once selects an item; double click to open things.");
1152 gtk_box_pack_start(GTK_BOX(vbox
), toggle_single_click
, FALSE
, TRUE
, 0);
1154 toggle_unique_filer_windows
=
1155 gtk_check_button_new_with_label(_("Unique windows"));
1156 OPTION_TIP(toggle_unique_filer_windows
,
1157 "If you open a directory and that directory is "
1158 "already displayed in another window, then this "
1159 "option causes the other window to be closed.");
1160 gtk_box_pack_start(GTK_BOX(vbox
), toggle_unique_filer_windows
,
1166 /* Reflect current state by changing the widgets in the options box */
1167 static void update_options()
1169 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_new_window_on_1
),
1171 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_menu_on_2
),
1172 collection_menu_button
== 2 ? 1 : 0);
1173 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_single_click
),
1175 gtk_toggle_button_set_active(
1176 GTK_TOGGLE_BUTTON(toggle_unique_filer_windows
),
1177 o_unique_filer_windows
);
1180 /* Set current values by reading the states of the widgets in the options box */
1181 static void set_options()
1183 o_new_window_on_1
= gtk_toggle_button_get_active(
1184 GTK_TOGGLE_BUTTON(toggle_new_window_on_1
));
1186 collection_menu_button
= gtk_toggle_button_get_active(
1187 GTK_TOGGLE_BUTTON(toggle_menu_on_2
)) ? 2 : 3;
1189 o_single_click
= gtk_toggle_button_get_active(
1190 GTK_TOGGLE_BUTTON(toggle_single_click
));
1192 o_unique_filer_windows
= gtk_toggle_button_get_active(
1193 GTK_TOGGLE_BUTTON(toggle_unique_filer_windows
));
1195 collection_single_click
= o_single_click
? TRUE
: FALSE
;
1198 static void save_options()
1200 option_write("filer_new_window_on_1", o_new_window_on_1
? "1" : "0");
1201 option_write("filer_menu_on_2",
1202 collection_menu_button
== 2 ? "1" : "0");
1203 option_write("filer_single_click", o_single_click
? "1" : "0");
1204 option_write("filer_unique_windows",
1205 o_unique_filer_windows
? "1" : "0");
1208 static char *filer_new_window_on_1(char *data
)
1210 o_new_window_on_1
= atoi(data
) != 0;
1214 static char *filer_menu_on_2(char *data
)
1216 collection_menu_button
= atoi(data
) != 0 ? 2 : 3;
1220 static char *filer_single_click(char *data
)
1222 o_single_click
= atoi(data
) != 0;
1223 collection_single_click
= o_single_click
? TRUE
: FALSE
;
1227 static char *filer_unique_windows(char *data
)
1229 o_unique_filer_windows
= atoi(data
) != 0;
1233 /* Note that filer_window may not exist after this call. */
1234 void filer_update_dir(FilerWindow
*filer_window
, gboolean warning
)
1236 if (may_rescan(filer_window
, warning
))
1237 dir_update(filer_window
->directory
, filer_window
->path
);
1240 /* Refresh the various caches even if we don't think we need to */
1241 void full_refresh(void)
1246 /* See whether a filer window with a given path already exists
1247 * and is different from diff.
1249 static FilerWindow
*find_filer_window(char *path
, FilerWindow
*diff
)
1251 GList
*next
= all_filer_windows
;
1255 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1257 if (filer_window
->panel_type
== PANEL_NO
&&
1258 filer_window
!= diff
&&
1259 strcmp(path
, filer_window
->path
) == 0)
1261 return filer_window
;
1270 /* This path has been mounted/umounted/deleted some files - update all dirs */
1271 void filer_check_mounted(char *path
)
1273 GList
*next
= all_filer_windows
;
1281 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1285 if (strncmp(path
, filer_window
->path
, len
) == 0)
1287 char s
= filer_window
->path
[len
];
1289 if (s
== '/' || s
== '\0')
1290 filer_update_dir(filer_window
, FALSE
);
1294 slash
= strrchr(path
, '/');
1295 if (slash
&& slash
!= path
)
1299 parent
= g_strndup(path
, slash
- path
);
1301 refresh_dirs(parent
);
1306 pinboard_may_update(path
);
1309 /* Like minibuffer_show(), except that:
1310 * - It returns FALSE (to be used from an idle callback)
1311 * - It checks that the filer window still exists.
1313 static gboolean
minibuffer_show_cb(FilerWindow
*filer_window
)
1315 if (filer_exists(filer_window
))
1316 minibuffer_show(filer_window
, MINI_PATH
);
1320 gboolean
filer_exists(FilerWindow
*filer_window
)
1324 for (next
= all_filer_windows
; next
; next
= next
->next
)
1326 FilerWindow
*fw
= (FilerWindow
*) next
->data
;
1328 if (fw
== filer_window
)
1335 static void filer_set_title(FilerWindow
*filer_window
)
1337 if (filer_window
->scanning
)
1341 title
= g_strdup_printf(_("%s (Scanning)"), filer_window
->path
);
1342 gtk_window_set_title(GTK_WINDOW(filer_window
->window
),
1347 gtk_window_set_title(GTK_WINDOW(filer_window
->window
),
1348 filer_window
->path
);
1351 /* Reconnect to the same directory (used when the Show Hidden option is
1354 void filer_detach_rescan(FilerWindow
*filer_window
)
1356 Directory
*dir
= filer_window
->directory
;
1358 g_fscache_data_ref(dir_cache
, dir
);
1359 detach(filer_window
);
1360 filer_window
->directory
= dir
;
1361 attach(filer_window
);