r336: Added an option to control the spring-loading feature. Added tooltips
[rox-filer.git] / ROX-Filer / src / filer.c
blob441859c30545309f4de5e18f877615f786f5cbd5
1 /*
2 * $Id$
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)
10 * any later version.
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
15 * more details.
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 */
24 #include "config.h"
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <time.h>
31 #include <ctype.h>
33 #include <gtk/gtk.h>
34 #include <gdk/gdkx.h>
35 #include <gdk/gdkkeysyms.h>
36 #include "collection.h"
38 #include "main.h"
39 #include "support.h"
40 #include "gui_support.h"
41 #include "filer.h"
42 #include "pixmaps.h"
43 #include "menu.h"
44 #include "dnd.h"
45 #include "run.h"
46 #include "mount.h"
47 #include "type.h"
48 #include "options.h"
49 #include "minibuffer.h"
50 #include "pinboard.h"
51 #include "toolbar.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;
63 /* Options bits */
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"),
76 create_options,
77 update_options,
78 set_options,
79 save_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,
98 GdkEventFocus *event,
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,
107 DirAction action,
108 GPtrArray *items,
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,
114 gpointer user_data);
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;
120 enum
122 TARGET_STRING,
123 TARGET_URI_LIST,
126 static GdkCursor *busy_cursor = NULL;
128 void filer_init()
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;
147 while (i--)
149 if (strcmp(leafname, r[i]->leafname) == 0)
150 return TRUE;
153 return FALSE;
156 static void update_item(FilerWindow *filer_window, DirItem *item)
158 int i;
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'))
165 return;
168 i = collection_find_item(filer_window->collection, item, dir_item_cmp);
170 if (i >= 0)
171 collection_draw_item(filer_window->collection, i, TRUE);
172 else
173 g_warning("Failed to find '%s'\n", item->leafname);
176 static void update_display(Directory *dir,
177 DirAction action,
178 GPtrArray *items,
179 FilerWindow *filer_window)
181 int old_num;
182 int i;
183 int cursor = filer_window->collection->cursor_item;
184 char *as;
185 Collection *collection = filer_window->collection;
187 switch (action)
189 case DIR_ADD:
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)
200 continue;
202 if (strcmp(as, item->leafname) != 0)
203 continue;
205 cursor = collection->number_of_items - 1;
206 if (filer_window->had_cursor)
208 collection_set_cursor_item(collection,
209 cursor);
210 filer_window->mini_cursor_base = cursor;
212 else
213 collection_wink_item(collection,
214 cursor);
217 if (old_num != collection->number_of_items)
218 collection_qsort(filer_window->collection,
219 filer_window->sort_fn);
220 break;
221 case DIR_REMOVE:
222 collection_delete_if(filer_window->collection,
223 if_deleted,
224 items);
225 break;
226 case DIR_START_SCAN:
227 set_scanning_display(filer_window, TRUE);
228 break;
229 case DIR_END_SCAN:
230 if (filer_window->window->window)
231 gdk_window_set_cursor(
232 filer_window->window->window,
233 NULL);
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);
242 break;
243 case DIR_UPDATE:
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);
252 break;
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,
262 filer_window);
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)
286 if (popup_menu)
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)
299 gtk_main_quit();
302 /* Add a single object to a directory display */
303 static void add_item(FilerWindow *filer_window, DirItem *item)
305 char *leafname = item->leafname;
306 int item_width;
308 if (leafname[0] == '.')
310 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
311 || (leafname[1] == '.' && leafname[2] == '\0'))
312 return;
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,
318 item_width,
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)
332 Directory *dir;
334 g_return_val_if_fail(filer_window != NULL, FALSE);
336 /* We do a fresh lookup (rather than update) because the inode may
337 * have changed.
339 dir = g_fscache_lookup(dir_cache, filer_window->path);
340 if (!dir)
342 if (warning)
343 delayed_error(PROJECT, _("Directory missing/deleted"));
344 gtk_widget_destroy(filer_window->window);
345 return FALSE;
347 if (dir == filer_window->directory)
348 g_fscache_data_unref(dir_cache, dir);
349 else
351 detach(filer_window);
352 filer_window->directory = dir;
353 attach(filer_window);
356 return TRUE;
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);
371 return TRUE;
374 /* Someone wants us to send them the selection */
375 static void selection_get(GtkWidget *widget,
376 GtkSelectionData *selection_data,
377 guint info,
378 guint time,
379 gpointer data)
381 GString *reply, *header;
382 FilerWindow *filer_window;
383 int i;
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);
391 switch (info)
393 case TARGET_STRING:
394 g_string_sprintf(header, " %s",
395 make_path(filer_window->path, "")->str);
396 break;
397 case TARGET_URI_LIST:
398 g_string_sprintf(header, " file://%s%s",
399 our_host_name(),
400 make_path(filer_window->path, "")->str);
401 break;
404 collection = filer_window->collection;
405 for (i = 0; i < collection->number_of_items; i++)
407 if (collection->items[i].selected)
409 DirItem *item =
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,
429 guint time,
430 gpointer user_data)
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,
439 time);
443 static void gain_selection(Collection *collection,
444 guint time,
445 gpointer user_data)
447 FilerWindow *filer_window = (FilerWindow *) user_data;
449 if (gtk_selection_owner_set(GTK_WIDGET(collection),
450 GDK_SELECTION_PRIMARY,
451 time))
453 window_with_selection = filer_window;
455 else
456 collection_clear_selection(filer_window->collection);
459 static void open_item(Collection *collection,
460 gpointer item_data, int item_number,
461 gpointer user_data)
463 FilerWindow *filer_window = (FilerWindow *) user_data;
464 GdkEvent *event;
465 GdkEventButton *bevent;
466 GdkEventKey *kevent;
467 OpenFlags flags = 0;
469 event = (GdkEvent *) gtk_get_current_event();
471 bevent = (GdkEventButton *) event;
472 kevent = (GdkEventKey *) event;
474 switch (event->type)
476 case GDK_2BUTTON_PRESS:
477 case GDK_BUTTON_PRESS:
478 case GDK_BUTTON_RELEASE:
479 if (bevent->state & GDK_SHIFT_MASK)
480 flags |= OPEN_SHIFT;
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;
491 break;
492 case GDK_KEY_PRESS:
493 flags |= OPEN_SAME_WINDOW;
494 if (kevent->state & GDK_SHIFT_MASK)
495 flags |= OPEN_SHIFT;
496 break;
497 default:
498 break;
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;
511 GtkWidget *widget;
512 DirItem *item = (DirItem *)
513 filer_window->collection->items[item_number].data;
514 guchar *full_path;
515 gboolean wink = TRUE;
516 Directory *old_dir;
518 widget = filer_window->window;
519 if (filer_window->mini_type == MINI_SHELL)
521 minibuffer_add(filer_window, item->leafname);
522 return;
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,
539 shift))
541 if (old_dir != filer_window->directory)
542 return;
544 if (close_window)
545 gtk_widget_destroy(filer_window->window);
546 else
548 if (wink)
549 collection_wink_item(filer_window->collection,
550 item_number);
551 if (close_mini)
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);
562 return FALSE;
565 static gint focus_in(GtkWidget *widget,
566 GdkEventFocus *event,
567 FilerWindow *filer_window)
569 window_with_focus = filer_window;
571 return FALSE;
574 static gint focus_out(GtkWidget *widget,
575 GdkEventFocus *event,
576 FilerWindow *filer_window)
578 /* TODO: Shade the cursor */
580 return FALSE;
583 /* Handle keys that can't be bound with the menu */
584 static gint key_press_event(GtkWidget *widget,
585 GdkEventKey *event,
586 FilerWindow *filer_window)
588 switch (event->keyval)
590 case GDK_BackSpace:
591 change_to_parent(filer_window);
592 break;
593 default:
594 return FALSE;
597 return TRUE;
600 void change_to_parent(FilerWindow *filer_window)
602 char *copy;
603 char *slash;
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, '/');
611 if (slash)
613 *slash = '\0';
614 filer_change_to(filer_window,
615 *copy ? copy : "/",
616 slash + 1);
618 else
619 g_warning("No / in directory path!\n");
621 g_free(copy);
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)
631 char *from_dup;
632 char *real_path = pathdup(path);
634 g_return_if_fail(filer_window != NULL);
636 if (o_unique_filer_windows)
638 FilerWindow *fw;
640 fw = find_filer_window(real_path, filer_window);
641 if (fw)
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,
652 filer_window->path);
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,
667 filer_window);
669 else
671 char *error;
673 g_free(from_dup);
674 error = g_strdup_printf(_("Directory '%s' is not accessible"),
675 path);
676 delayed_error(PROJECT, error);
677 g_free(error);
678 gtk_widget_destroy(filer_window->window);
682 void filer_open_parent(FilerWindow *filer_window)
684 char *copy;
685 char *slash;
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, '/');
693 if (slash)
695 *slash = '\0';
696 filer_opendir(*copy ? copy : "/", PANEL_NO);
698 else
699 g_warning("No / in directory path!\n");
701 g_free(copy);
704 int selected_item_number(Collection *collection)
706 int i;
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)
714 return i;
716 g_warning("selected_item: number_selected is wrong\n");
718 return -1;
721 DirItem *selected_item(Collection *collection)
723 int item;
725 item = selected_item_number(collection);
727 if (item > -1)
728 return (DirItem *) collection->items[item].data;
729 return NULL;
732 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
733 FilerWindow *window)
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... "
739 "really close?"),
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;
747 GString *leader;
748 int i, num_selected;
750 leader = g_string_new("file://");
751 if (!o_no_hostnames)
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");
768 num_selected--;
772 g_string_free(leader, TRUE);
775 static void start_drag_selection(Collection *collection,
776 GdkEventMotion *event,
777 int number_selected,
778 FilerWindow *filer_window)
780 GtkWidget *widget = (GtkWidget *) collection;
782 if (number_selected == 1)
784 DirItem *item;
786 item = selected_item(collection);
788 drag_one_item(widget, event,
789 make_path(filer_window->path, item->leafname)->str,
790 item,
791 filer_window->mini_type == MINI_RUN_ACTION);
793 else
795 GString *uris;
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},
814 char *real_path;
816 real_path = pathdup(path);
818 if (o_unique_filer_windows && panel_type == PANEL_NO)
820 FilerWindow *fw;
822 fw = find_filer_window(real_path, NULL);
824 if (fw)
826 /* TODO: this should bring the window to the front
827 * at the same coordinates.
829 gtk_widget_hide(fw->window);
830 g_free(real_path);
831 gtk_widget_show(fw->window);
832 return fw;
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,
848 filer_window->path);
849 if (!filer_window->directory)
851 char *error;
853 error = g_strdup_printf(_("Directory '%s' not found."), path);
854 delayed_error(PROJECT, error);
855 g_free(error);
856 g_free(filer_window->path);
857 g_free(filer_window);
858 return NULL;
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,
905 target_table,
906 sizeof(target_table) / sizeof(*target_table));
908 display_style_set(filer_window, last_display_style);
909 drag_set_dest(filer_window);
911 if (panel_type)
913 int swidth, sheight, iwidth, iheight;
914 GtkWidget *frame, *win = filer_window->window;
916 gtk_window_set_wmclass(GTK_WINDOW(win), "ROX-Panel",
917 PROJECT);
918 collection_set_panel(filer_window->collection, TRUE);
919 gtk_signal_connect(GTK_OBJECT(filer_window->window),
920 "delete_event",
921 GTK_SIGNAL_FUNC(filer_confirm_close),
922 filer_window);
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);
949 else
951 GtkWidget *vbox;
952 int col_height = ROW_HEIGHT_LARGE * 3;
954 gtk_signal_connect(GTK_OBJECT(collection),
955 "key_press_event",
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 :
961 col_height + 38);
963 hbox = gtk_hbox_new(FALSE, 0);
964 gtk_container_add(GTK_CONTAINER(filer_window->window),
965 hbox);
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)
972 GtkWidget *toolbar;
974 toolbar = toolbar_new(filer_window);
975 gtk_box_pack_start(GTK_BOX(vbox), toolbar,
976 FALSE, TRUE, 0);
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,
984 FALSE, TRUE, 0);
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),
991 collection);
993 gtk_widget_show(hbox);
994 gtk_widget_show(vbox);
995 gtk_widget_show(scrollbar);
996 gtk_widget_show(collection);
999 number_of_windows++;
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);
1012 return FALSE;
1015 static void set_scanning_display(FilerWindow *filer_window, gboolean scanning)
1017 if (scanning == filer_window->scanning)
1018 return;
1019 filer_window->scanning = scanning;
1021 if (scanning)
1022 filer_set_title(filer_window);
1023 else
1024 gtk_timeout_add(300, (GtkFunction) clear_scanning_display,
1025 filer_window);
1028 /* Build up some option widgets to go in the options dialog, but don't
1029 * fill them in yet.
1031 static GtkWidget *create_options(void)
1033 GtkWidget *vbox;
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,
1047 FALSE, TRUE, 0);
1049 toggle_menu_on_2 =
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,
1073 FALSE, TRUE, 0);
1075 return vbox;
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),
1082 o_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),
1086 o_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;
1123 return NULL;
1126 static char *filer_menu_on_2(char *data)
1128 collection_menu_button = atoi(data) != 0 ? 2 : 3;
1129 return NULL;
1132 static char *filer_single_click(char *data)
1134 o_single_click = atoi(data) != 0;
1135 collection_single_click = o_single_click ? TRUE : FALSE;
1136 return NULL;
1139 static char *filer_unique_windows(char *data)
1141 o_unique_filer_windows = atoi(data) != 0;
1142 return NULL;
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)
1155 mount_update(TRUE);
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;
1165 while (next)
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;
1176 next = next->next;
1179 return NULL;
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;
1186 char *slash;
1187 int len;
1189 len = strlen(path);
1191 while (next)
1193 FilerWindow *filer_window = (FilerWindow *) next->data;
1195 next = next->next;
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)
1209 guchar *parent;
1211 parent = g_strndup(path, slash - path);
1213 refresh_dirs(parent);
1215 g_free(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);
1229 return FALSE;
1232 gboolean filer_exists(FilerWindow *filer_window)
1234 GList *next;
1236 for (next = all_filer_windows; next; next = next->next)
1238 FilerWindow *fw = (FilerWindow *) next->data;
1240 if (fw == filer_window)
1241 return TRUE;
1244 return FALSE;
1247 static void filer_set_title(FilerWindow *filer_window)
1249 if (filer_window->scanning)
1251 guchar *title;
1253 title = g_strdup_printf(_("%s (Scanning)"), filer_window->path);
1254 gtk_window_set_title(GTK_WINDOW(filer_window->window),
1255 title);
1256 g_free(title);
1258 else
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
1264 * toggled).
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);