4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2000, Thomas Leonard, <tal197@ecs.soton.ac.uk>.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* filer.c - code for handling filer windows */
35 #include <gdk/gdkkeysyms.h>
36 #include "collection.h"
40 #include "gui_support.h"
49 #include "minibuffer.h"
53 #define PANEL_BORDER 2
55 extern int collection_menu_button
;
56 extern gboolean collection_single_click
;
58 FilerWindow
*window_with_focus
= NULL
;
59 GList
*all_filer_windows
= NULL
;
61 static FilerWindow
*window_with_selection
= NULL
;
64 static GtkWidget
*create_options();
65 static void update_options();
66 static void set_options();
67 static void save_options();
68 static char *filer_single_click(char *data
);
69 static char *filer_unique_windows(char *data
);
70 static char *filer_menu_on_2(char *data
);
71 static char *filer_new_window_on_1(char *data
);
73 static OptionsSection options
=
75 N_("Filer window options"),
82 gboolean o_single_click
= TRUE
;
83 gboolean o_new_window_on_1
= FALSE
; /* Button 1 => New window */
84 gboolean o_unique_filer_windows
= FALSE
;
85 static GtkWidget
*toggle_single_click
;
86 static GtkWidget
*toggle_new_window_on_1
;
87 static GtkWidget
*toggle_menu_on_2
;
88 static GtkWidget
*toggle_unique_filer_windows
;
90 /* Static prototypes */
91 static void attach(FilerWindow
*filer_window
);
92 static void detach(FilerWindow
*filer_window
);
93 static void filer_window_destroyed(GtkWidget
*widget
,
94 FilerWindow
*filer_window
);
95 static void show_menu(Collection
*collection
, GdkEventButton
*event
,
96 int number_selected
, gpointer user_data
);
97 static gint
focus_in(GtkWidget
*widget
,
99 FilerWindow
*filer_window
);
100 static gint
focus_out(GtkWidget
*widget
,
101 GdkEventFocus
*event
,
102 FilerWindow
*filer_window
);
103 static void add_item(FilerWindow
*filer_window
, DirItem
*item
);
104 static int filer_confirm_close(GtkWidget
*widget
, GdkEvent
*event
,
105 FilerWindow
*window
);
106 static void update_display(Directory
*dir
,
109 FilerWindow
*filer_window
);
110 static void set_scanning_display(FilerWindow
*filer_window
, gboolean scanning
);
111 static gboolean
may_rescan(FilerWindow
*filer_window
, gboolean warning
);
112 static void open_item(Collection
*collection
,
113 gpointer item_data
, int item_number
,
115 static gboolean
minibuffer_show_cb(FilerWindow
*filer_window
);
116 static FilerWindow
*find_filer_window(char *path
, FilerWindow
*diff
);
117 static void filer_set_title(FilerWindow
*filer_window
);
119 static GdkAtom xa_string
;
126 static GdkCursor
*busy_cursor
= NULL
;
130 xa_string
= gdk_atom_intern("STRING", FALSE
);
132 options_sections
= g_slist_prepend(options_sections
, &options
);
133 option_register("filer_new_window_on_1", filer_new_window_on_1
);
134 option_register("filer_menu_on_2", filer_menu_on_2
);
135 option_register("filer_single_click", filer_single_click
);
136 option_register("filer_unique_windows", filer_unique_windows
);
138 busy_cursor
= gdk_cursor_new(GDK_WATCH
);
141 static gboolean
if_deleted(gpointer item
, gpointer removed
)
143 int i
= ((GPtrArray
*) removed
)->len
;
144 DirItem
**r
= (DirItem
**) ((GPtrArray
*) removed
)->pdata
;
145 char *leafname
= ((DirItem
*) item
)->leafname
;
149 if (strcmp(leafname
, r
[i
]->leafname
) == 0)
156 static void update_item(FilerWindow
*filer_window
, DirItem
*item
)
159 char *leafname
= item
->leafname
;
161 if (leafname
[0] == '.')
163 if (filer_window
->show_hidden
== FALSE
|| leafname
[1] == '\0'
164 || (leafname
[1] == '.' && leafname
[2] == '\0'))
168 i
= collection_find_item(filer_window
->collection
, item
, dir_item_cmp
);
171 collection_draw_item(filer_window
->collection
, i
, TRUE
);
173 g_warning("Failed to find '%s'\n", item
->leafname
);
176 static void update_display(Directory
*dir
,
179 FilerWindow
*filer_window
)
183 int cursor
= filer_window
->collection
->cursor_item
;
185 Collection
*collection
= filer_window
->collection
;
190 as
= filer_window
->auto_select
;
192 old_num
= collection
->number_of_items
;
193 for (i
= 0; i
< items
->len
; i
++)
195 DirItem
*item
= (DirItem
*) items
->pdata
[i
];
197 add_item(filer_window
, item
);
199 if (cursor
!= -1 || !as
)
202 if (strcmp(as
, item
->leafname
) != 0)
205 cursor
= collection
->number_of_items
- 1;
206 if (filer_window
->had_cursor
)
208 collection_set_cursor_item(collection
,
210 filer_window
->mini_cursor_base
= cursor
;
213 collection_wink_item(collection
,
217 if (old_num
!= collection
->number_of_items
)
218 collection_qsort(filer_window
->collection
,
219 filer_window
->sort_fn
);
222 collection_delete_if(filer_window
->collection
,
227 set_scanning_display(filer_window
, TRUE
);
230 if (filer_window
->window
->window
)
231 gdk_window_set_cursor(
232 filer_window
->window
->window
,
234 shrink_width(filer_window
);
235 if (filer_window
->had_cursor
&&
236 collection
->cursor_item
== -1)
238 collection_set_cursor_item(collection
, 0);
239 filer_window
->had_cursor
= FALSE
;
241 set_scanning_display(filer_window
, FALSE
);
244 for (i
= 0; i
< items
->len
; i
++)
246 DirItem
*item
= (DirItem
*) items
->pdata
[i
];
248 update_item(filer_window
, item
);
250 collection_qsort(filer_window
->collection
,
251 filer_window
->sort_fn
);
256 static void attach(FilerWindow
*filer_window
)
258 gdk_window_set_cursor(filer_window
->window
->window
, busy_cursor
);
259 collection_clear(filer_window
->collection
);
260 filer_window
->scanning
= TRUE
;
261 dir_attach(filer_window
->directory
, (DirCallback
) update_display
,
263 filer_set_title(filer_window
);
266 static void detach(FilerWindow
*filer_window
)
268 g_return_if_fail(filer_window
->directory
!= NULL
);
270 dir_detach(filer_window
->directory
,
271 (DirCallback
) update_display
, filer_window
);
272 g_fscache_data_unref(dir_cache
, filer_window
->directory
);
273 filer_window
->directory
= NULL
;
276 static void filer_window_destroyed(GtkWidget
*widget
,
277 FilerWindow
*filer_window
)
279 all_filer_windows
= g_list_remove(all_filer_windows
, filer_window
);
281 if (window_with_selection
== filer_window
)
282 window_with_selection
= NULL
;
284 if (window_with_focus
== filer_window
)
287 gtk_menu_popdown(GTK_MENU(popup_menu
));
288 window_with_focus
= NULL
;
291 if (filer_window
->directory
)
292 detach(filer_window
);
294 g_free(filer_window
->auto_select
);
295 g_free(filer_window
->path
);
296 g_free(filer_window
);
298 if (--number_of_windows
< 1)
302 /* Add a single object to a directory display */
303 static void add_item(FilerWindow
*filer_window
, DirItem
*item
)
305 char *leafname
= item
->leafname
;
308 if (leafname
[0] == '.')
310 if (filer_window
->show_hidden
== FALSE
|| leafname
[1] == '\0'
311 || (leafname
[1] == '.' && leafname
[2] == '\0'))
315 item_width
= calc_width(filer_window
, item
);
316 if (item_width
> filer_window
->collection
->item_width
)
317 collection_set_item_size(filer_window
->collection
,
319 filer_window
->collection
->item_height
);
320 collection_insert(filer_window
->collection
, item
);
323 static void show_menu(Collection
*collection
, GdkEventButton
*event
,
324 int item
, gpointer user_data
)
326 show_filer_menu((FilerWindow
*) user_data
, event
, item
);
329 /* Returns TRUE iff the directory still exists. */
330 static gboolean
may_rescan(FilerWindow
*filer_window
, gboolean warning
)
334 g_return_val_if_fail(filer_window
!= NULL
, FALSE
);
336 /* We do a fresh lookup (rather than update) because the inode may
339 dir
= g_fscache_lookup(dir_cache
, filer_window
->path
);
343 delayed_error(PROJECT
, _("Directory missing/deleted"));
344 gtk_widget_destroy(filer_window
->window
);
347 if (dir
== filer_window
->directory
)
348 g_fscache_data_unref(dir_cache
, dir
);
351 detach(filer_window
);
352 filer_window
->directory
= dir
;
353 attach(filer_window
);
359 /* Another app has grabbed the selection */
360 static gint
collection_lose_selection(GtkWidget
*widget
,
361 GdkEventSelection
*event
)
363 if (window_with_selection
&&
364 window_with_selection
->collection
== COLLECTION(widget
))
366 FilerWindow
*filer_window
= window_with_selection
;
367 window_with_selection
= NULL
;
368 collection_clear_selection(filer_window
->collection
);
374 /* Someone wants us to send them the selection */
375 static void selection_get(GtkWidget
*widget
,
376 GtkSelectionData
*selection_data
,
381 GString
*reply
, *header
;
382 FilerWindow
*filer_window
;
384 Collection
*collection
;
386 filer_window
= gtk_object_get_data(GTK_OBJECT(widget
), "filer_window");
388 reply
= g_string_new(NULL
);
389 header
= g_string_new(NULL
);
394 g_string_sprintf(header
, " %s",
395 make_path(filer_window
->path
, "")->str
);
397 case TARGET_URI_LIST
:
398 g_string_sprintf(header
, " file://%s%s",
400 make_path(filer_window
->path
, "")->str
);
404 collection
= filer_window
->collection
;
405 for (i
= 0; i
< collection
->number_of_items
; i
++)
407 if (collection
->items
[i
].selected
)
410 (DirItem
*) collection
->items
[i
].data
;
412 g_string_append(reply
, header
->str
);
413 g_string_append(reply
, item
->leafname
);
416 /* This works, but I don't think I like it... */
417 /* g_string_append_c(reply, ' '); */
419 gtk_selection_data_set(selection_data
, xa_string
,
420 8, reply
->str
+ 1, reply
->len
- 1);
421 g_string_free(reply
, TRUE
);
422 g_string_free(header
, TRUE
);
425 /* No items are now selected. This might be because another app claimed
426 * the selection or because the user unselected all the items.
428 static void lose_selection(Collection
*collection
,
432 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
434 if (window_with_selection
== filer_window
)
436 window_with_selection
= NULL
;
437 gtk_selection_owner_set(NULL
,
438 GDK_SELECTION_PRIMARY
,
443 static void gain_selection(Collection
*collection
,
447 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
449 if (gtk_selection_owner_set(GTK_WIDGET(collection
),
450 GDK_SELECTION_PRIMARY
,
453 window_with_selection
= filer_window
;
456 collection_clear_selection(filer_window
->collection
);
459 static void open_item(Collection
*collection
,
460 gpointer item_data
, int item_number
,
463 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
465 GdkEventButton
*bevent
;
469 event
= (GdkEvent
*) gtk_get_current_event();
471 bevent
= (GdkEventButton
*) event
;
472 kevent
= (GdkEventKey
*) event
;
476 case GDK_2BUTTON_PRESS
:
477 case GDK_BUTTON_PRESS
:
478 case GDK_BUTTON_RELEASE
:
479 if (bevent
->state
& GDK_SHIFT_MASK
)
482 if (o_new_window_on_1
^ (bevent
->button
== 1))
483 flags
|= OPEN_SAME_WINDOW
;
485 if (bevent
->button
!= 1)
486 flags
|= OPEN_CLOSE_WINDOW
;
488 if (o_single_click
== FALSE
&&
489 (bevent
->state
& GDK_CONTROL_MASK
) != 0)
490 flags
^= OPEN_SAME_WINDOW
| OPEN_CLOSE_WINDOW
;
493 flags
|= OPEN_SAME_WINDOW
;
494 if (kevent
->state
& GDK_SHIFT_MASK
)
501 filer_openitem(filer_window
, item_number
, flags
);
504 /* Open the item (or add it to the shell command minibuffer) */
505 void filer_openitem(FilerWindow
*filer_window
, int item_number
, OpenFlags flags
)
507 gboolean shift
= (flags
& OPEN_SHIFT
) != 0;
508 gboolean close_mini
= flags
& OPEN_FROM_MINI
;
509 gboolean close_window
= (flags
& OPEN_CLOSE_WINDOW
) != 0
510 && !filer_window
->panel_type
;
512 DirItem
*item
= (DirItem
*)
513 filer_window
->collection
->items
[item_number
].data
;
515 gboolean wink
= TRUE
;
518 widget
= filer_window
->window
;
519 if (filer_window
->mini_type
== MINI_SHELL
)
521 minibuffer_add(filer_window
, item
->leafname
);
525 if (item
->base_type
== TYPE_DIRECTORY
)
527 /* Never close a filer window when opening a directory
528 * (click on a dir or click on an app with shift).
530 if (shift
|| !(item
->flags
& ITEM_FLAG_APPDIR
))
531 close_window
= FALSE
;
534 full_path
= make_path(filer_window
->path
, item
->leafname
)->str
;
536 old_dir
= filer_window
->directory
;
537 if (run_diritem(full_path
, item
,
538 flags
& OPEN_SAME_WINDOW
? filer_window
: NULL
,
541 if (old_dir
!= filer_window
->directory
)
545 gtk_widget_destroy(filer_window
->window
);
549 collection_wink_item(filer_window
->collection
,
552 minibuffer_hide(filer_window
);
557 static gint
pointer_in(GtkWidget
*widget
,
558 GdkEventCrossing
*event
,
559 FilerWindow
*filer_window
)
561 may_rescan(filer_window
, TRUE
);
565 static gint
focus_in(GtkWidget
*widget
,
566 GdkEventFocus
*event
,
567 FilerWindow
*filer_window
)
569 window_with_focus
= filer_window
;
574 static gint
focus_out(GtkWidget
*widget
,
575 GdkEventFocus
*event
,
576 FilerWindow
*filer_window
)
578 /* TODO: Shade the cursor */
583 /* Handle keys that can't be bound with the menu */
584 static gint
key_press_event(GtkWidget
*widget
,
586 FilerWindow
*filer_window
)
588 switch (event
->keyval
)
591 change_to_parent(filer_window
);
600 void change_to_parent(FilerWindow
*filer_window
)
605 if (filer_window
->path
[0] == '/' && filer_window
->path
[1] == '\0')
606 return; /* Already in the root */
608 copy
= g_strdup(filer_window
->path
);
609 slash
= strrchr(copy
, '/');
614 filer_change_to(filer_window
,
619 g_warning("No / in directory path!\n");
625 /* Make filer_window display path. When finished, highlight item 'from', or
626 * the first item if from is NULL. If there is currently no cursor then
627 * simply wink 'from' (if not NULL).
629 void filer_change_to(FilerWindow
*filer_window
, char *path
, char *from
)
632 char *real_path
= pathdup(path
);
634 g_return_if_fail(filer_window
!= NULL
);
636 if (o_unique_filer_windows
)
640 fw
= find_filer_window(real_path
, filer_window
);
642 gtk_widget_destroy(fw
->window
);
645 from_dup
= from
&& *from
? g_strdup(from
) : NULL
;
647 detach(filer_window
);
648 g_free(filer_window
->path
);
649 filer_window
->path
= real_path
;
651 filer_window
->directory
= g_fscache_lookup(dir_cache
,
653 if (filer_window
->directory
)
655 g_free(filer_window
->auto_select
);
656 filer_window
->had_cursor
=
657 filer_window
->collection
->cursor_item
!= -1
658 || filer_window
->had_cursor
;
659 filer_window
->auto_select
= from_dup
;
661 filer_set_title(filer_window
);
662 collection_set_cursor_item(filer_window
->collection
, -1);
663 attach(filer_window
);
665 if (filer_window
->mini_type
== MINI_PATH
)
666 gtk_idle_add((GtkFunction
) minibuffer_show_cb
,
674 error
= g_strdup_printf(_("Directory '%s' is not accessible"),
676 delayed_error(PROJECT
, error
);
678 gtk_widget_destroy(filer_window
->window
);
682 void filer_open_parent(FilerWindow
*filer_window
)
687 if (filer_window
->path
[0] == '/' && filer_window
->path
[1] == '\0')
688 return; /* Already in the root */
690 copy
= g_strdup(filer_window
->path
);
691 slash
= strrchr(copy
, '/');
696 filer_opendir(*copy
? copy
: "/", PANEL_NO
);
699 g_warning("No / in directory path!\n");
704 int selected_item_number(Collection
*collection
)
708 g_return_val_if_fail(collection
!= NULL
, -1);
709 g_return_val_if_fail(IS_COLLECTION(collection
), -1);
710 g_return_val_if_fail(collection
->number_selected
== 1, -1);
712 for (i
= 0; i
< collection
->number_of_items
; i
++)
713 if (collection
->items
[i
].selected
)
716 g_warning("selected_item: number_selected is wrong\n");
721 DirItem
*selected_item(Collection
*collection
)
725 item
= selected_item_number(collection
);
728 return (DirItem
*) collection
->items
[item
].data
;
732 static int filer_confirm_close(GtkWidget
*widget
, GdkEvent
*event
,
735 /* TODO: We can open lots of these - very irritating! */
736 return get_choice(_("Close panel?"),
737 _("You have tried to close a panel via the window "
738 "manager - I usually find that this is accidental... "
740 2, _("Remove"), _("Cancel")) != 0;
743 /* Append all the URIs in the selection to the string */
744 static void create_uri_list(FilerWindow
*filer_window
, GString
*string
)
746 Collection
*collection
= filer_window
->collection
;
750 leader
= g_string_new("file://");
752 g_string_append(leader
, our_host_name());
753 g_string_append(leader
, filer_window
->path
);
754 if (leader
->str
[leader
->len
- 1] != '/')
755 g_string_append_c(leader
, '/');
757 num_selected
= collection
->number_selected
;
759 for (i
= 0; num_selected
> 0; i
++)
761 if (collection
->items
[i
].selected
)
763 DirItem
*item
= (DirItem
*) collection
->items
[i
].data
;
765 g_string_append(string
, leader
->str
);
766 g_string_append(string
, item
->leafname
);
767 g_string_append(string
, "\r\n");
772 g_string_free(leader
, TRUE
);
775 static void start_drag_selection(Collection
*collection
,
776 GdkEventMotion
*event
,
778 FilerWindow
*filer_window
)
780 GtkWidget
*widget
= (GtkWidget
*) collection
;
782 if (number_selected
== 1)
786 item
= selected_item(collection
);
788 drag_one_item(widget
, event
,
789 make_path(filer_window
->path
, item
->leafname
)->str
,
791 filer_window
->mini_type
== MINI_RUN_ACTION
);
797 uris
= g_string_new(NULL
);
798 create_uri_list(filer_window
, uris
);
799 drag_selection(widget
, event
, uris
->str
);
800 g_string_free(uris
, TRUE
);
804 /* Creates and realises a new filer window */
805 FilerWindow
*filer_opendir(char *path
, PanelType panel_type
)
807 GtkWidget
*hbox
, *scrollbar
, *collection
;
808 FilerWindow
*filer_window
;
809 GtkTargetEntry target_table
[] =
811 {"text/uri-list", 0, TARGET_URI_LIST
},
812 {"STRING", 0, TARGET_STRING
},
816 real_path
= pathdup(path
);
818 if (o_unique_filer_windows
&& panel_type
== PANEL_NO
)
822 fw
= find_filer_window(real_path
, NULL
);
826 /* TODO: this should bring the window to the front
827 * at the same coordinates.
829 gtk_widget_hide(fw
->window
);
831 gtk_widget_show(fw
->window
);
836 filer_window
= g_new(FilerWindow
, 1);
837 filer_window
->minibuffer
= NULL
;
838 filer_window
->minibuffer_label
= NULL
;
839 filer_window
->minibuffer_area
= NULL
;
840 filer_window
->temp_show_hidden
= FALSE
;
841 filer_window
->path
= real_path
;
842 filer_window
->scanning
= FALSE
;
843 filer_window
->had_cursor
= FALSE
;
844 filer_window
->auto_select
= NULL
;
845 filer_window
->mini_type
= MINI_NONE
;
847 filer_window
->directory
= g_fscache_lookup(dir_cache
,
849 if (!filer_window
->directory
)
853 error
= g_strdup_printf(_("Directory '%s' not found."), path
);
854 delayed_error(PROJECT
, error
);
856 g_free(filer_window
->path
);
857 g_free(filer_window
);
861 filer_window
->show_hidden
= last_show_hidden
;
862 filer_window
->panel_type
= panel_type
;
863 filer_window
->temp_item_selected
= FALSE
;
864 filer_window
->sort_fn
= last_sort_fn
;
865 filer_window
->flags
= (FilerFlags
) 0;
866 filer_window
->details_type
= last_details_type
;
867 filer_window
->display_style
= UNKNOWN_STYLE
;
869 filer_window
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
870 filer_set_title(filer_window
);
872 collection
= collection_new(NULL
);
873 gtk_object_set_data(GTK_OBJECT(collection
),
874 "filer_window", filer_window
);
875 filer_window
->collection
= COLLECTION(collection
);
877 gtk_widget_add_events(filer_window
->window
, GDK_ENTER_NOTIFY
);
878 gtk_signal_connect(GTK_OBJECT(filer_window
->window
),
879 "enter-notify-event",
880 GTK_SIGNAL_FUNC(pointer_in
), filer_window
);
881 gtk_signal_connect(GTK_OBJECT(filer_window
->window
), "focus_in_event",
882 GTK_SIGNAL_FUNC(focus_in
), filer_window
);
883 gtk_signal_connect(GTK_OBJECT(filer_window
->window
), "focus_out_event",
884 GTK_SIGNAL_FUNC(focus_out
), filer_window
);
885 gtk_signal_connect(GTK_OBJECT(filer_window
->window
), "destroy",
886 filer_window_destroyed
, filer_window
);
888 gtk_signal_connect(GTK_OBJECT(filer_window
->collection
), "open_item",
889 open_item
, filer_window
);
890 gtk_signal_connect(GTK_OBJECT(collection
), "show_menu",
891 show_menu
, filer_window
);
892 gtk_signal_connect(GTK_OBJECT(collection
), "gain_selection",
893 gain_selection
, filer_window
);
894 gtk_signal_connect(GTK_OBJECT(collection
), "lose_selection",
895 lose_selection
, filer_window
);
896 gtk_signal_connect(GTK_OBJECT(collection
), "drag_selection",
897 start_drag_selection
, filer_window
);
898 gtk_signal_connect(GTK_OBJECT(collection
), "drag_data_get",
899 drag_data_get
, NULL
);
900 gtk_signal_connect(GTK_OBJECT(collection
), "selection_clear_event",
901 GTK_SIGNAL_FUNC(collection_lose_selection
), NULL
);
902 gtk_signal_connect(GTK_OBJECT(collection
), "selection_get",
903 GTK_SIGNAL_FUNC(selection_get
), NULL
);
904 gtk_selection_add_targets(collection
, GDK_SELECTION_PRIMARY
,
906 sizeof(target_table
) / sizeof(*target_table
));
908 display_style_set(filer_window
, last_display_style
);
909 drag_set_dest(filer_window
);
913 int swidth
, sheight
, iwidth
, iheight
;
914 GtkWidget
*frame
, *win
= filer_window
->window
;
916 gtk_window_set_wmclass(GTK_WINDOW(win
), "ROX-Panel",
918 collection_set_panel(filer_window
->collection
, TRUE
);
919 gtk_signal_connect(GTK_OBJECT(filer_window
->window
),
921 GTK_SIGNAL_FUNC(filer_confirm_close
),
924 gdk_window_get_size(GDK_ROOT_PARENT(), &swidth
, &sheight
);
925 iwidth
= filer_window
->collection
->item_width
;
926 iheight
= filer_window
->collection
->item_height
;
929 int height
= iheight
+ PANEL_BORDER
;
930 int y
= panel_type
== PANEL_TOP
932 : sheight
- height
- PANEL_BORDER
;
934 gtk_widget_set_usize(collection
, swidth
, height
);
935 gtk_widget_set_uposition(win
, 0, y
);
938 frame
= gtk_frame_new(NULL
);
939 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_OUT
);
940 gtk_container_add(GTK_CONTAINER(frame
), collection
);
941 gtk_container_add(GTK_CONTAINER(win
), frame
);
943 gtk_widget_show_all(frame
);
944 gtk_widget_realize(win
);
945 if (override_redirect
)
946 gdk_window_set_override_redirect(win
->window
, TRUE
);
947 make_panel_window(win
->window
);
952 int col_height
= ROW_HEIGHT_LARGE
* 3;
954 gtk_signal_connect(GTK_OBJECT(collection
),
956 GTK_SIGNAL_FUNC(key_press_event
), filer_window
);
957 gtk_window_set_default_size(GTK_WINDOW(filer_window
->window
),
958 filer_window
->display_style
== LARGE_ICONS
? 400 : 512,
959 o_toolbar
== TOOLBAR_NONE
? col_height
:
960 o_toolbar
== TOOLBAR_NORMAL
? col_height
+ 24 :
963 hbox
= gtk_hbox_new(FALSE
, 0);
964 gtk_container_add(GTK_CONTAINER(filer_window
->window
),
967 vbox
= gtk_vbox_new(FALSE
, 0);
968 gtk_box_pack_start(GTK_BOX(hbox
), vbox
, TRUE
, TRUE
, 0);
970 if (o_toolbar
!= TOOLBAR_NONE
)
974 toolbar
= toolbar_new(filer_window
);
975 gtk_box_pack_start(GTK_BOX(vbox
), toolbar
,
977 gtk_widget_show_all(toolbar
);
980 gtk_box_pack_start(GTK_BOX(vbox
), collection
, TRUE
, TRUE
, 0);
982 create_minibuffer(filer_window
);
983 gtk_box_pack_start(GTK_BOX(vbox
), filer_window
->minibuffer_area
,
986 scrollbar
= gtk_vscrollbar_new(COLLECTION(collection
)->vadj
);
987 gtk_box_pack_start(GTK_BOX(hbox
), scrollbar
, FALSE
, TRUE
, 0);
988 gtk_accel_group_attach(filer_keys
,
989 GTK_OBJECT(filer_window
->window
));
990 gtk_window_set_focus(GTK_WINDOW(filer_window
->window
),
993 gtk_widget_show(hbox
);
994 gtk_widget_show(vbox
);
995 gtk_widget_show(scrollbar
);
996 gtk_widget_show(collection
);
1000 gtk_widget_show(filer_window
->window
);
1001 attach(filer_window
);
1003 all_filer_windows
= g_list_prepend(all_filer_windows
, filer_window
);
1005 return filer_window
;
1008 static gint
clear_scanning_display(FilerWindow
*filer_window
)
1010 if (filer_exists(filer_window
))
1011 filer_set_title(filer_window
);
1015 static void set_scanning_display(FilerWindow
*filer_window
, gboolean scanning
)
1017 if (scanning
== filer_window
->scanning
)
1019 filer_window
->scanning
= scanning
;
1022 filer_set_title(filer_window
);
1024 gtk_timeout_add(300, (GtkFunction
) clear_scanning_display
,
1028 /* Build up some option widgets to go in the options dialog, but don't
1031 static GtkWidget
*create_options(void)
1035 vbox
= gtk_vbox_new(FALSE
, 0);
1036 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 4);
1038 toggle_new_window_on_1
=
1039 gtk_check_button_new_with_label(
1040 _("New window on button 1 (RISC OS style)"));
1041 OPTION_TIP(toggle_new_window_on_1
,
1042 "Clicking with mouse button 1 (usually the "
1043 "left button) opens a directory in a new window "
1044 "with this turned on. Clicking with the button-2 "
1045 "(middle) will reuse the current window.");
1046 gtk_box_pack_start(GTK_BOX(vbox
), toggle_new_window_on_1
,
1050 gtk_check_button_new_with_label(
1051 _("Menu on button 2 (RISC OS style)"));
1052 OPTION_TIP(toggle_menu_on_2
,
1053 "Use button 2, the middle button (click both buttons "
1054 "at once on two button mice), to pop up the menu. "
1055 "If off, use button 3 (right) instead.");
1056 gtk_box_pack_start(GTK_BOX(vbox
), toggle_menu_on_2
, FALSE
, TRUE
, 0);
1058 toggle_single_click
=
1059 gtk_check_button_new_with_label(_("Single-click nagivation"));
1060 OPTION_TIP(toggle_single_click
,
1061 "Clicking on an item opens it with this on. Hold down "
1062 "Control to select the item instead. If off, clicking "
1063 "once selects an item; double click to open things.");
1064 gtk_box_pack_start(GTK_BOX(vbox
), toggle_single_click
, FALSE
, TRUE
, 0);
1066 toggle_unique_filer_windows
=
1067 gtk_check_button_new_with_label(_("Unique windows"));
1068 OPTION_TIP(toggle_unique_filer_windows
,
1069 "If you open a directory and that directory is "
1070 "already displayed in another window, then this "
1071 "option causes the other window to be closed.");
1072 gtk_box_pack_start(GTK_BOX(vbox
), toggle_unique_filer_windows
,
1078 /* Reflect current state by changing the widgets in the options box */
1079 static void update_options()
1081 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_new_window_on_1
),
1083 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_menu_on_2
),
1084 collection_menu_button
== 2 ? 1 : 0);
1085 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_single_click
),
1087 gtk_toggle_button_set_active(
1088 GTK_TOGGLE_BUTTON(toggle_unique_filer_windows
),
1089 o_unique_filer_windows
);
1092 /* Set current values by reading the states of the widgets in the options box */
1093 static void set_options()
1095 o_new_window_on_1
= gtk_toggle_button_get_active(
1096 GTK_TOGGLE_BUTTON(toggle_new_window_on_1
));
1098 collection_menu_button
= gtk_toggle_button_get_active(
1099 GTK_TOGGLE_BUTTON(toggle_menu_on_2
)) ? 2 : 3;
1101 o_single_click
= gtk_toggle_button_get_active(
1102 GTK_TOGGLE_BUTTON(toggle_single_click
));
1104 o_unique_filer_windows
= gtk_toggle_button_get_active(
1105 GTK_TOGGLE_BUTTON(toggle_unique_filer_windows
));
1107 collection_single_click
= o_single_click
? TRUE
: FALSE
;
1110 static void save_options()
1112 option_write("filer_new_window_on_1", o_new_window_on_1
? "1" : "0");
1113 option_write("filer_menu_on_2",
1114 collection_menu_button
== 2 ? "1" : "0");
1115 option_write("filer_single_click", o_single_click
? "1" : "0");
1116 option_write("filer_unique_windows",
1117 o_unique_filer_windows
? "1" : "0");
1120 static char *filer_new_window_on_1(char *data
)
1122 o_new_window_on_1
= atoi(data
) != 0;
1126 static char *filer_menu_on_2(char *data
)
1128 collection_menu_button
= atoi(data
) != 0 ? 2 : 3;
1132 static char *filer_single_click(char *data
)
1134 o_single_click
= atoi(data
) != 0;
1135 collection_single_click
= o_single_click
? TRUE
: FALSE
;
1139 static char *filer_unique_windows(char *data
)
1141 o_unique_filer_windows
= atoi(data
) != 0;
1145 /* Note that filer_window may not exist after this call. */
1146 void filer_update_dir(FilerWindow
*filer_window
, gboolean warning
)
1148 if (may_rescan(filer_window
, warning
))
1149 dir_update(filer_window
->directory
, filer_window
->path
);
1152 /* Refresh the various caches even if we don't think we need to */
1153 void full_refresh(void)
1158 /* See whether a filer window with a given path already exists
1159 * and is different from diff.
1161 static FilerWindow
*find_filer_window(char *path
, FilerWindow
*diff
)
1163 GList
*next
= all_filer_windows
;
1167 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1169 if (filer_window
->panel_type
== PANEL_NO
&&
1170 filer_window
!= diff
&&
1171 strcmp(path
, filer_window
->path
) == 0)
1173 return filer_window
;
1182 /* This path has been mounted/umounted/deleted some files - update all dirs */
1183 void filer_check_mounted(char *path
)
1185 GList
*next
= all_filer_windows
;
1193 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1197 if (strncmp(path
, filer_window
->path
, len
) == 0)
1199 char s
= filer_window
->path
[len
];
1201 if (s
== '/' || s
== '\0')
1202 filer_update_dir(filer_window
, FALSE
);
1206 slash
= strrchr(path
, '/');
1207 if (slash
&& slash
!= path
)
1211 parent
= g_strndup(path
, slash
- path
);
1213 refresh_dirs(parent
);
1218 pinboard_may_update(path
);
1221 /* Like minibuffer_show(), except that:
1222 * - It returns FALSE (to be used from an idle callback)
1223 * - It checks that the filer window still exists.
1225 static gboolean
minibuffer_show_cb(FilerWindow
*filer_window
)
1227 if (filer_exists(filer_window
))
1228 minibuffer_show(filer_window
, MINI_PATH
);
1232 gboolean
filer_exists(FilerWindow
*filer_window
)
1236 for (next
= all_filer_windows
; next
; next
= next
->next
)
1238 FilerWindow
*fw
= (FilerWindow
*) next
->data
;
1240 if (fw
== filer_window
)
1247 static void filer_set_title(FilerWindow
*filer_window
)
1249 if (filer_window
->scanning
)
1253 title
= g_strdup_printf(_("%s (Scanning)"), filer_window
->path
);
1254 gtk_window_set_title(GTK_WINDOW(filer_window
->window
),
1259 gtk_window_set_title(GTK_WINDOW(filer_window
->window
),
1260 filer_window
->path
);
1263 /* Reconnect to the same directory (used when the Show Hidden option is
1266 void filer_detach_rescan(FilerWindow
*filer_window
)
1268 Directory
*dir
= filer_window
->directory
;
1270 g_fscache_data_ref(dir_cache
, dir
);
1271 detach(filer_window
);
1272 filer_window
->directory
= dir
;
1273 attach(filer_window
);