4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2003, the ROX-Filer team.
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 */
32 #include <sys/param.h>
36 #include <gdk/gdkkeysyms.h>
44 #include "gui_support.h"
55 #include "minibuffer.h"
62 #include "view_iface.h"
63 #include "view_collection.h"
64 #include "view_details.h"
66 #include "bookmarks.h"
68 static XMLwrapper
*groups
= NULL
;
70 /* Item we are about to display a tooltip for */
71 static DirItem
*tip_item
= NULL
;
73 /* The window which the motion event for the tooltip came from. Use this
74 * to get the correct widget for finding the item under the pointer.
76 static GdkWindow
*motion_window
= NULL
;
78 /* This is rather badly named. It's actually the filer window which received
79 * the last key press or Menu click event.
81 FilerWindow
*window_with_focus
= NULL
;
83 GList
*all_filer_windows
= NULL
;
85 static FilerWindow
*window_with_primary
= NULL
;
87 /* Static prototypes */
88 static void attach(FilerWindow
*filer_window
);
89 static void detach(FilerWindow
*filer_window
);
90 static void filer_window_destroyed(GtkWidget
*widget
,
91 FilerWindow
*filer_window
);
92 static void update_display(Directory
*dir
,
95 FilerWindow
*filer_window
);
96 static void set_scanning_display(FilerWindow
*filer_window
, gboolean scanning
);
97 static gboolean
may_rescan(FilerWindow
*filer_window
, gboolean warning
);
98 static gboolean
minibuffer_show_cb(FilerWindow
*filer_window
);
99 static FilerWindow
*find_filer_window(const char *sym_path
, FilerWindow
*diff
);
100 static void filer_add_widgets(FilerWindow
*filer_window
, const gchar
*wm_class
);
101 static void filer_add_signals(FilerWindow
*filer_window
);
103 static void set_selection_state(FilerWindow
*filer_window
, gboolean normal
);
104 static void filer_next_thumb(GObject
*window
, const gchar
*path
);
105 static void start_thumb_scanning(FilerWindow
*filer_window
);
106 static void filer_options_changed(void);
107 static void drag_end(GtkWidget
*widget
, GdkDragContext
*context
,
108 FilerWindow
*filer_window
);
109 static void drag_leave(GtkWidget
*widget
,
110 GdkDragContext
*context
,
112 FilerWindow
*filer_window
);
113 static gboolean
drag_motion(GtkWidget
*widget
,
114 GdkDragContext
*context
,
118 FilerWindow
*filer_window
);
120 static GdkCursor
*busy_cursor
= NULL
;
121 static GdkCursor
*crosshair
= NULL
;
123 /* Indicates whether the filer's display is different to the machine it
124 * is actually running on.
126 static gboolean not_local
= FALSE
;
128 static Option o_short_flag_names
;
129 static Option o_filer_view_type
;
130 Option o_filer_auto_resize
, o_unique_filer_windows
;
131 Option o_filer_size_limit
;
133 void filer_init(void)
137 gchar
*dpyhost
, *tmp
;
139 option_add_int(&o_filer_size_limit
, "filer_size_limit", 75);
140 option_add_int(&o_filer_auto_resize
, "filer_auto_resize",
142 option_add_int(&o_unique_filer_windows
, "filer_unique_windows", 0);
144 option_add_int(&o_short_flag_names
, "filer_short_flag_names", FALSE
);
146 option_add_int(&o_filer_view_type
, "filer_view_type",
147 VIEW_TYPE_COLLECTION
);
149 option_add_notify(filer_options_changed
);
151 busy_cursor
= gdk_cursor_new(GDK_WATCH
);
152 crosshair
= gdk_cursor_new(GDK_CROSSHAIR
);
154 /* Is the display on the local machine, or are we being
155 * run remotely? See filer_set_title().
157 ohost
= our_host_name();
158 dpy
= gdk_get_display();
159 dpyhost
= g_strdup(dpy
);
160 tmp
= strchr(dpyhost
, ':');
164 if (dpyhost
[0] && strcmp(ohost
, dpyhost
) != 0)
166 /* Try the cannonical name for dpyhost (see our_host_name()
171 ent
= gethostbyname(dpyhost
);
172 if (!ent
|| strcmp(ohost
, ent
->h_name
) != 0)
179 static gboolean
if_deleted(gpointer item
, gpointer removed
)
181 int i
= ((GPtrArray
*) removed
)->len
;
182 DirItem
**r
= (DirItem
**) ((GPtrArray
*) removed
)->pdata
;
183 char *leafname
= ((DirItem
*) item
)->leafname
;
187 if (strcmp(leafname
, r
[i
]->leafname
) == 0)
194 /* Resize the filer window to w x h pixels, plus border (not clamped).
195 * If triggered by a key event, warp the pointer (for SloppyFocus users).
197 void filer_window_set_size(FilerWindow
*filer_window
, int w
, int h
)
202 g_return_if_fail(filer_window
!= NULL
);
204 if (filer_window
->scrollbar
)
205 w
+= filer_window
->scrollbar
->allocation
.width
;
207 if (o_toolbar
.int_value
!= TOOLBAR_NONE
)
208 h
+= filer_window
->toolbar
->allocation
.height
;
209 if (filer_window
->message
)
210 h
+= filer_window
->message
->allocation
.height
;
212 window
= filer_window
->window
;
214 if (GTK_WIDGET_VISIBLE(window
))
217 GtkRequisition
*req
= &window
->requisition
;
218 GdkWindow
*gdk_window
= window
->window
;
220 w
= MAX(req
->width
, w
);
221 h
= MAX(req
->height
, h
);
222 gdk_window_get_position(gdk_window
, &x
, &y
);
224 if (x
+ w
> screen_width
|| y
+ h
> screen_height
)
226 if (x
+ w
> screen_width
)
227 x
= screen_width
- w
- 4;
228 if (y
+ h
> screen_height
)
229 y
= screen_height
- h
- 4;
230 gdk_window_move_resize(gdk_window
, x
, y
, w
, h
);
233 gdk_window_resize(gdk_window
, w
, h
);
236 gtk_window_set_default_size(GTK_WINDOW(window
), w
, h
);
238 event
= gtk_get_current_event();
239 if (event
&& event
->type
== GDK_KEY_PRESS
)
241 GdkWindow
*win
= filer_window
->window
->window
;
243 XWarpPointer(gdk_x11_drawable_get_xdisplay(win
),
245 gdk_x11_drawable_get_xid(win
),
251 /* Called on a timeout while scanning or when scanning ends
252 * (whichever happens first).
254 static gint
open_filer_window(FilerWindow
*filer_window
)
256 view_style_changed(filer_window
->view
, 0);
258 if (filer_window
->open_timeout
)
260 gtk_timeout_remove(filer_window
->open_timeout
);
261 filer_window
->open_timeout
= 0;
264 if (!GTK_WIDGET_VISIBLE(filer_window
->window
))
266 display_set_actual_size(filer_window
, TRUE
);
267 gtk_widget_show(filer_window
->window
);
273 static void update_display(Directory
*dir
,
276 FilerWindow
*filer_window
)
278 ViewIface
*view
= (ViewIface
*) filer_window
->view
;
283 view_add_items(view
, items
);
284 /* Open and resize if currently hidden */
285 open_filer_window(filer_window
);
288 view_delete_if(view
, if_deleted
, items
);
289 toolbar_update_info(filer_window
);
292 set_scanning_display(filer_window
, TRUE
);
293 toolbar_update_info(filer_window
);
296 if (filer_window
->window
->window
)
297 gdk_window_set_cursor(
298 filer_window
->window
->window
,
300 set_scanning_display(filer_window
, FALSE
);
301 toolbar_update_info(filer_window
);
302 open_filer_window(filer_window
);
304 if (filer_window
->had_cursor
&&
305 !view_cursor_visible(view
))
308 view_get_iter(view
, &start
, 0);
309 if (start
.next(&start
))
310 view_cursor_to_iter(view
, &start
);
311 view_show_cursor(view
);
312 filer_window
->had_cursor
= FALSE
;
314 if (filer_window
->auto_select
)
315 display_set_autoselect(filer_window
,
316 filer_window
->auto_select
);
317 null_g_free(&filer_window
->auto_select
);
319 filer_create_thumbs(filer_window
);
321 if (filer_window
->thumb_queue
)
322 start_thumb_scanning(filer_window
);
325 view_update_items(view
, items
);
327 case DIR_ERROR_CHANGED
:
328 filer_set_title(filer_window
);
333 static void attach(FilerWindow
*filer_window
)
335 gdk_window_set_cursor(filer_window
->window
->window
, busy_cursor
);
336 view_clear(filer_window
->view
);
337 filer_window
->scanning
= TRUE
;
338 dir_attach(filer_window
->directory
, (DirCallback
) update_display
,
340 filer_set_title(filer_window
);
341 bookmarks_add_history(filer_window
->sym_path
);
343 if (filer_window
->directory
->error
)
345 if (spring_in_progress
)
346 g_printerr(_("Error scanning '%s':\n%s\n"),
347 filer_window
->sym_path
,
348 filer_window
->directory
->error
);
350 delayed_error(_("Error scanning '%s':\n%s"),
351 filer_window
->sym_path
,
352 filer_window
->directory
->error
);
356 static void detach(FilerWindow
*filer_window
)
358 g_return_if_fail(filer_window
->directory
!= NULL
);
360 dir_detach(filer_window
->directory
,
361 (DirCallback
) update_display
, filer_window
);
362 g_object_unref(filer_window
->directory
);
363 filer_window
->directory
= NULL
;
366 /* Returns TRUE to prevent closing the window. May offer to unmount a
369 gboolean
filer_window_delete(GtkWidget
*window
,
370 GdkEvent
*unused
, /* (may be NULL) */
371 FilerWindow
*filer_window
)
373 if (mount_is_user_mounted(filer_window
->real_path
))
377 action
= get_choice(PROJECT
,
378 _("Do you want to unmount this device?\n\n"
379 "Unmounting a device makes it safe to remove "
381 GTK_STOCK_CANCEL
, NULL
,
382 GTK_STOCK_CLOSE
, NULL
,
383 ROX_STOCK_MOUNT
, _("Unmount"));
386 return TRUE
; /* Cancel close operation */
392 list
= g_list_prepend(NULL
, filer_window
->sym_path
);
393 action_mount(list
, FALSE
, TRUE
);
403 static void filer_window_destroyed(GtkWidget
*widget
, FilerWindow
*filer_window
)
405 all_filer_windows
= g_list_remove(all_filer_windows
, filer_window
);
407 g_object_set_data(G_OBJECT(widget
), "filer_window", NULL
);
409 if (window_with_primary
== filer_window
)
410 window_with_primary
= NULL
;
412 if (window_with_focus
== filer_window
)
415 window_with_focus
= NULL
;
418 if (filer_window
->directory
)
419 detach(filer_window
);
421 if (filer_window
->open_timeout
)
423 gtk_timeout_remove(filer_window
->open_timeout
);
424 filer_window
->open_timeout
= 0;
427 if (filer_window
->auto_scroll
!= -1)
429 gtk_timeout_remove(filer_window
->auto_scroll
);
430 filer_window
->auto_scroll
= -1;
433 if (filer_window
->thumb_queue
)
434 destroy_glist(&filer_window
->thumb_queue
);
438 g_free(filer_window
->auto_select
);
439 g_free(filer_window
->real_path
);
440 g_free(filer_window
->sym_path
);
441 g_free(filer_window
);
446 /* Returns TRUE iff the directory still exists. */
447 static gboolean
may_rescan(FilerWindow
*filer_window
, gboolean warning
)
451 g_return_val_if_fail(filer_window
!= NULL
, FALSE
);
453 /* We do a fresh lookup (rather than update) because the inode may
456 dir
= g_fscache_lookup(dir_cache
, filer_window
->real_path
);
460 info_message(_("Directory missing/deleted"));
461 gtk_widget_destroy(filer_window
->window
);
464 if (dir
== filer_window
->directory
)
468 detach(filer_window
);
469 filer_window
->directory
= dir
;
470 attach(filer_window
);
476 /* No items are now selected. This might be because another app claimed
477 * the selection or because the user unselected all the items.
479 void filer_lost_selection(FilerWindow
*filer_window
, guint time
)
481 if (window_with_primary
== filer_window
)
483 window_with_primary
= NULL
;
484 gtk_selection_owner_set(NULL
, GDK_SELECTION_PRIMARY
, time
);
488 /* Another app has claimed the primary selection */
489 static void filer_lost_primary(GtkWidget
*window
,
490 GdkEventSelection
*event
,
493 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
495 if (window_with_primary
&& window_with_primary
== filer_window
)
497 window_with_primary
= NULL
;
498 set_selection_state(filer_window
, FALSE
);
502 /* Someone wants us to send them the selection */
503 static void selection_get(GtkWidget
*widget
,
504 GtkSelectionData
*selection_data
,
509 GString
*reply
, *header
;
510 FilerWindow
*filer_window
= (FilerWindow
*) data
;
514 reply
= g_string_new(NULL
);
515 header
= g_string_new(NULL
);
520 g_string_printf(header
, " %s",
521 make_path(filer_window
->sym_path
, ""));
523 case TARGET_URI_LIST
:
524 g_string_printf(header
, " file://%s%s",
525 our_host_name_for_dnd(),
526 make_path(filer_window
->sym_path
, ""));
530 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
532 while ((item
= iter
.next(&iter
)))
534 g_string_append(reply
, header
->str
);
535 g_string_append(reply
, item
->leafname
);
539 gtk_selection_data_set(selection_data
, xa_string
,
540 8, reply
->str
+ 1, reply
->len
- 1);
543 g_warning("Attempt to paste empty selection!");
544 gtk_selection_data_set(selection_data
, xa_string
, 8, "", 0);
547 g_string_free(reply
, TRUE
);
548 g_string_free(header
, TRUE
);
551 /* Selection has been changed -- try to grab the primary selection
552 * if we don't have it. Also called when clicking on an insensitive selection
554 * Also updates toolbar info.
556 void filer_selection_changed(FilerWindow
*filer_window
, gint time
)
558 toolbar_update_info(filer_window
);
560 if (window_with_primary
== filer_window
)
561 return; /* Already got primary */
563 if (!view_count_selected(filer_window
->view
))
564 return; /* Nothing selected */
566 if (filer_window
->temp_item_selected
== FALSE
&&
567 gtk_selection_owner_set(GTK_WIDGET(filer_window
->window
),
568 GDK_SELECTION_PRIMARY
,
571 window_with_primary
= filer_window
;
572 set_selection_state(filer_window
, TRUE
);
575 set_selection_state(filer_window
, FALSE
);
578 /* Open the item (or add it to the shell command minibuffer) */
579 void filer_openitem(FilerWindow
*filer_window
, ViewIter
*iter
, OpenFlags flags
)
581 gboolean shift
= (flags
& OPEN_SHIFT
) != 0;
582 gboolean close_mini
= flags
& OPEN_FROM_MINI
;
583 gboolean close_window
= (flags
& OPEN_CLOSE_WINDOW
) != 0;
585 const guchar
*full_path
;
586 gboolean wink
= TRUE
;
589 g_return_if_fail(filer_window
!= NULL
&& iter
!= NULL
);
591 item
= iter
->peek(iter
);
593 g_return_if_fail(item
!= NULL
);
595 if (filer_window
->mini_type
== MINI_SHELL
)
597 minibuffer_add(filer_window
, item
->leafname
);
602 dir_update_item(filer_window
->directory
, item
->leafname
);
604 if (item
->base_type
== TYPE_DIRECTORY
)
606 /* Never close a filer window when opening a directory
607 * (click on a dir or click on an app with shift).
609 if (shift
|| !(item
->flags
& ITEM_FLAG_APPDIR
))
610 close_window
= FALSE
;
613 full_path
= make_path(filer_window
->sym_path
, item
->leafname
);
614 if (shift
&& (item
->flags
& ITEM_FLAG_SYMLINK
))
617 old_dir
= filer_window
->directory
;
618 if (run_diritem(full_path
, item
,
619 flags
& OPEN_SAME_WINDOW
? filer_window
: NULL
,
623 if (old_dir
!= filer_window
->directory
)
627 gtk_widget_destroy(filer_window
->window
);
631 view_wink_item(filer_window
->view
, iter
);
633 minibuffer_hide(filer_window
);
638 static gint
pointer_in(GtkWidget
*widget
,
639 GdkEventCrossing
*event
,
640 FilerWindow
*filer_window
)
642 may_rescan(filer_window
, TRUE
);
646 static gint
pointer_out(GtkWidget
*widget
,
647 GdkEventCrossing
*event
,
648 FilerWindow
*filer_window
)
654 /* Move the cursor to the next selected item in direction 'dir'
657 static void next_selected(FilerWindow
*filer_window
, int dir
)
659 ViewIter iter
, cursor
;
660 gboolean have_cursor
;
661 ViewIface
*view
= filer_window
->view
;
663 g_return_if_fail(dir
== 1 || dir
== -1);
665 view_get_cursor(view
, &cursor
);
666 have_cursor
= cursor
.peek(&cursor
) != NULL
;
668 view_get_iter(view
, &iter
,
670 (have_cursor
? VIEW_ITER_FROM_CURSOR
: 0) |
671 (dir
< 0 ? VIEW_ITER_BACKWARDS
: 0));
673 if (have_cursor
&& view_get_selected(view
, &cursor
))
674 iter
.next(&iter
); /* Skip the cursor itself */
676 if (iter
.next(&iter
))
677 view_cursor_to_iter(view
, &iter
);
684 static void return_pressed(FilerWindow
*filer_window
, GdkEventKey
*event
)
686 TargetFunc cb
= filer_window
->target_cb
;
687 gpointer data
= filer_window
->target_data
;
691 filer_target_mode(filer_window
, NULL
, NULL
, NULL
);
693 view_get_cursor(filer_window
->view
, &iter
);
694 if (!iter
.peek(&iter
))
699 cb(filer_window
, &iter
, data
);
703 if (event
->state
& GDK_SHIFT_MASK
)
705 if (event
->state
& GDK_MOD1_MASK
)
706 flags
|= OPEN_CLOSE_WINDOW
;
708 flags
|= OPEN_SAME_WINDOW
;
710 filer_openitem(filer_window
, &iter
, flags
);
713 /* Makes sure that 'groups' is up-to-date, reloading from file if it has
714 * changed. If no groups were loaded and there is no file then initialised
715 * groups to an empty document.
716 * Return the node for the 'name' group.
718 static xmlNode
*group_find(char *name
)
723 /* Update the groups, if possible */
724 path
= choices_find_path_load("Groups.xml", PROJECT
);
728 wrapper
= xml_cache_load(path
);
732 g_object_unref(groups
);
741 groups
= xml_new(NULL
);
742 groups
->doc
= xmlNewDoc("1.0");
744 xmlDocSetRootElement(groups
->doc
,
745 xmlNewDocNode(groups
->doc
, NULL
, "groups", NULL
));
749 node
= xmlDocGetRootElement(groups
->doc
);
751 for (node
= node
->xmlChildrenNode
; node
; node
= node
->next
)
755 gid
= xmlGetProp(node
, "name");
760 if (strcmp(name
, gid
) != 0)
771 static void group_save(FilerWindow
*filer_window
, char *name
)
778 group
= group_find(name
);
781 xmlUnlinkNode(group
);
784 group
= xmlNewChild(xmlDocGetRootElement(groups
->doc
),
785 NULL
, "group", NULL
);
786 xmlSetProp(group
, "name", name
);
788 xmlNewChild(group
, NULL
, "directory", filer_window
->sym_path
);
790 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
792 while ((item
= iter
.next(&iter
)))
793 xmlNewChild(group
, NULL
, "item", item
->leafname
);
795 save_path
= choices_find_path_save("Groups.xml", PROJECT
, TRUE
);
798 save_xml_file(groups
->doc
, save_path
);
803 static gboolean
group_restore_cb(ViewIter
*iter
, gpointer data
)
805 GHashTable
*in_group
= (GHashTable
*) data
;
807 return g_hash_table_lookup(in_group
,
808 iter
->peek(iter
)->leafname
) != NULL
;
811 static void group_restore(FilerWindow
*filer_window
, char *name
)
813 GHashTable
*in_group
;
815 xmlNode
*group
, *node
;
817 group
= group_find(name
);
821 report_error(_("Group %s is not set. Select some files "
822 "and press Ctrl+%s to set the group. Press %s "
823 "on its own to reselect the files later.\n"
824 "Make sure NumLock is on if you use the keypad."),
829 node
= get_subnode(group
, NULL
, "directory");
830 g_return_if_fail(node
!= NULL
);
831 path
= xmlNodeListGetString(groups
->doc
, node
->xmlChildrenNode
, 1);
832 g_return_if_fail(path
!= NULL
);
834 if (strcmp(path
, filer_window
->sym_path
) != 0)
835 filer_change_to(filer_window
, path
, NULL
);
838 in_group
= g_hash_table_new(g_str_hash
, g_str_equal
);
839 for (node
= group
->xmlChildrenNode
; node
; node
= node
->next
)
842 if (node
->type
!= XML_ELEMENT_NODE
)
844 if (strcmp(node
->name
, "item") != 0)
847 leaf
= xmlNodeListGetString(groups
->doc
,
848 node
->xmlChildrenNode
, 1);
850 g_warning("Missing leafname!\n");
852 g_hash_table_insert(in_group
, leaf
, filer_window
);
855 view_select_if(filer_window
->view
, &group_restore_cb
, in_group
);
857 g_hash_table_foreach(in_group
, (GHFunc
) g_free
, NULL
);
858 g_hash_table_destroy(in_group
);
861 static gboolean
popup_menu(GtkWidget
*widget
, FilerWindow
*filer_window
)
865 view_get_cursor(filer_window
->view
, &iter
);
867 show_filer_menu(filer_window
, NULL
, &iter
);
872 void filer_window_toggle_cursor_item_selected(FilerWindow
*filer_window
)
874 ViewIface
*view
= filer_window
->view
;
877 view_get_iter(view
, &iter
, VIEW_ITER_FROM_CURSOR
);
878 if (!iter
.next(&iter
))
879 return; /* No cursor */
881 if (view_get_selected(view
, &iter
))
882 view_set_selected(view
, &iter
, FALSE
);
884 view_set_selected(view
, &iter
, TRUE
);
886 if (iter
.next(&iter
))
887 view_cursor_to_iter(view
, &iter
);
890 gint
filer_key_press_event(GtkWidget
*widget
,
892 FilerWindow
*filer_window
)
894 ViewIface
*view
= filer_window
->view
;
896 GtkWidget
*focus
= GTK_WINDOW(widget
)->focus_widget
;
897 guint key
= event
->keyval
;
900 window_with_focus
= filer_window
;
902 /* Delay setting up the keys until now to speed loading... */
903 if (ensure_filer_menu())
905 /* Gtk updates in an idle-handler, so force a recheck now */
906 g_signal_emit_by_name(widget
, "keys_changed");
909 if (focus
&& focus
== filer_window
->minibuffer
)
910 if (gtk_widget_event(focus
, (GdkEvent
*) event
))
911 return TRUE
; /* Handled */
914 gtk_widget_grab_focus(GTK_WIDGET(view
));
916 view_get_cursor(view
, &cursor
);
917 if (!cursor
.peek(&cursor
) && (key
== GDK_Up
|| key
== GDK_Down
))
920 view_get_iter(view
, &iter
, 0);
921 if (iter
.next(&iter
))
922 view_cursor_to_iter(view
, &iter
);
923 gtk_widget_grab_focus(GTK_WIDGET(view
)); /* Needed? */
930 filer_target_mode(filer_window
, NULL
, NULL
, NULL
);
931 view_cursor_to_iter(filer_window
->view
, NULL
);
932 view_clear_selection(filer_window
->view
);
935 return_pressed(filer_window
, event
);
937 case GDK_ISO_Left_Tab
:
938 next_selected(filer_window
, -1);
941 next_selected(filer_window
, 1);
944 change_to_parent(filer_window
);
952 view_get_cursor(filer_window
->view
, &iter
);
953 show_filer_menu(filer_window
,
954 (GdkEvent
*) event
, &iter
);
958 filer_window_toggle_cursor_item_selected(filer_window
);
961 if (key
>= GDK_0
&& key
<= GDK_9
)
962 group
[0] = key
- GDK_0
+ '0';
963 else if (key
>= GDK_KP_0
&& key
<= GDK_KP_9
)
964 group
[0] = key
- GDK_KP_0
+ '0';
967 if (focus
&& focus
!= widget
&&
968 gtk_widget_get_toplevel(focus
) == widget
)
969 if (gtk_widget_event(focus
,
971 return TRUE
; /* Handled */
975 if (event
->state
& GDK_CONTROL_MASK
)
976 group_save(filer_window
, group
);
978 group_restore(filer_window
, group
);
984 void filer_open_parent(FilerWindow
*filer_window
)
987 const char *current
= filer_window
->sym_path
;
989 if (current
[0] == '/' && current
[1] == '\0')
990 return; /* Already in the root */
992 dir
= g_path_get_dirname(current
);
993 filer_opendir(dir
, filer_window
, NULL
);
997 void change_to_parent(FilerWindow
*filer_window
)
1000 const char *current
= filer_window
->sym_path
;
1002 if (current
[0] == '/' && current
[1] == '\0')
1003 return; /* Already in the root */
1005 dir
= g_path_get_dirname(current
);
1006 filer_change_to(filer_window
, dir
, g_basename(current
));
1010 /* Removes trailing /s from path (modified in place) */
1011 static void tidy_sympath(gchar
*path
)
1015 g_return_if_fail(path
!= NULL
);
1018 while (l
> 1 && path
[l
- 1] == '/')
1025 /* Make filer_window display path. When finished, highlight item 'from', or
1026 * the first item if from is NULL. If there is currently no cursor then
1027 * simply wink 'from' (if not NULL).
1028 * If the cause was a key event and we resize, warp the pointer.
1030 void filer_change_to(FilerWindow
*filer_window
,
1031 const char *path
, const char *from
)
1034 char *sym_path
, *real_path
;
1037 g_return_if_fail(filer_window
!= NULL
);
1039 filer_cancel_thumbnails(filer_window
);
1043 sym_path
= g_strdup(path
);
1044 real_path
= pathdup(path
);
1045 new_dir
= g_fscache_lookup(dir_cache
, real_path
);
1049 delayed_error(_("Directory '%s' is not accessible"),
1056 if (o_unique_filer_windows
.int_value
&& !spring_in_progress
)
1060 fw
= find_filer_window(sym_path
, filer_window
);
1062 gtk_widget_destroy(fw
->window
);
1065 from_dup
= from
&& *from
? g_strdup(from
) : NULL
;
1067 detach(filer_window
);
1068 g_free(filer_window
->real_path
);
1069 g_free(filer_window
->sym_path
);
1070 filer_window
->real_path
= real_path
;
1071 filer_window
->sym_path
= sym_path
;
1072 tidy_sympath(filer_window
->sym_path
);
1074 filer_window
->directory
= new_dir
;
1076 g_free(filer_window
->auto_select
);
1077 filer_window
->auto_select
= from_dup
;
1079 filer_window
->had_cursor
= filer_window
->had_cursor
||
1080 view_cursor_visible(filer_window
->view
);
1082 filer_set_title(filer_window
);
1083 if (filer_window
->window
->window
)
1084 gdk_window_set_role(filer_window
->window
->window
,
1085 filer_window
->sym_path
);
1086 view_cursor_to_iter(filer_window
->view
, NULL
);
1088 attach(filer_window
);
1090 display_set_actual_size(filer_window
, FALSE
);
1092 if (o_filer_auto_resize
.int_value
== RESIZE_ALWAYS
)
1093 view_autosize(filer_window
->view
);
1095 if (filer_window
->mini_type
== MINI_PATH
)
1096 gtk_idle_add((GtkFunction
) minibuffer_show_cb
,
1100 /* Returns a list containing the full (sym) pathname of every selected item.
1101 * You must g_free() each item in the list.
1103 GList
*filer_selected_items(FilerWindow
*filer_window
)
1105 GList
*retval
= NULL
;
1106 guchar
*dir
= filer_window
->sym_path
;
1110 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
1111 while ((item
= iter
.next(&iter
)))
1113 retval
= g_list_prepend(retval
,
1114 g_strdup(make_path(dir
, item
->leafname
)));
1117 return g_list_reverse(retval
);
1120 /* Return the single selected item. Error if nothing is selected. */
1121 DirItem
*filer_selected_item(FilerWindow
*filer_window
)
1126 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
1128 item
= iter
.next(&iter
);
1129 g_return_val_if_fail(item
!= NULL
, NULL
);
1130 g_return_val_if_fail(iter
.next(&iter
) == NULL
, NULL
);
1135 /* Creates and shows a new filer window.
1136 * If src_win != NULL then display options can be taken from that source window.
1137 * Returns the new filer window, or NULL on error.
1138 * Note: if unique windows is in use, may return an existing window.
1140 FilerWindow
*filer_opendir(const char *path
, FilerWindow
*src_win
,
1141 const gchar
*wm_class
)
1143 FilerWindow
*filer_window
;
1145 DisplayStyle dstyle
;
1148 GtkSortType s_order
;
1150 /* Get the real pathname of the directory and copy it */
1151 real_path
= pathdup(path
);
1153 if (o_unique_filer_windows
.int_value
&& !spring_in_progress
)
1155 FilerWindow
*same_dir_window
;
1157 same_dir_window
= find_filer_window(path
, NULL
);
1159 if (same_dir_window
)
1161 gtk_window_present(GTK_WINDOW(same_dir_window
->window
));
1162 return same_dir_window
;
1166 filer_window
= g_new(FilerWindow
, 1);
1167 filer_window
->message
= NULL
;
1168 filer_window
->minibuffer
= NULL
;
1169 filer_window
->minibuffer_label
= NULL
;
1170 filer_window
->minibuffer_area
= NULL
;
1171 filer_window
->temp_show_hidden
= FALSE
;
1172 filer_window
->sym_path
= g_strdup(path
);
1173 filer_window
->real_path
= real_path
;
1174 filer_window
->scanning
= FALSE
;
1175 filer_window
->had_cursor
= FALSE
;
1176 filer_window
->auto_select
= NULL
;
1177 filer_window
->toolbar_text
= NULL
;
1178 filer_window
->target_cb
= NULL
;
1179 filer_window
->mini_type
= MINI_NONE
;
1180 filer_window
->selection_state
= GTK_STATE_INSENSITIVE
;
1181 filer_window
->toolbar
= NULL
;
1182 filer_window
->toplevel_vbox
= NULL
;
1183 filer_window
->view_hbox
= NULL
;
1184 filer_window
->view
= NULL
;
1185 filer_window
->scrollbar
= NULL
;
1186 filer_window
->auto_scroll
= -1;
1188 tidy_sympath(filer_window
->sym_path
);
1190 /* Finds the entry for this directory in the dir cache, creating
1191 * a new one if needed. This does not cause a scan to start,
1192 * so if a new entry is created then it will be empty.
1194 filer_window
->directory
= g_fscache_lookup(dir_cache
, real_path
);
1195 if (!filer_window
->directory
)
1197 delayed_error(_("Directory '%s' not found."), path
);
1198 g_free(filer_window
->real_path
);
1199 g_free(filer_window
->sym_path
);
1200 g_free(filer_window
);
1204 filer_window
->temp_item_selected
= FALSE
;
1205 filer_window
->flags
= (FilerFlags
) 0;
1206 filer_window
->details_type
= DETAILS_TIMES
;
1207 filer_window
->display_style
= UNKNOWN_STYLE
;
1208 filer_window
->display_style_wanted
= UNKNOWN_STYLE
;
1209 filer_window
->thumb_queue
= NULL
;
1210 filer_window
->max_thumbs
= 0;
1211 filer_window
->sort_type
= -1;
1213 if (src_win
&& o_display_inherit_options
.int_value
)
1215 s_type
= src_win
->sort_type
;
1216 s_order
= src_win
->sort_order
;
1217 dstyle
= src_win
->display_style_wanted
;
1218 dtype
= src_win
->details_type
;
1219 filer_window
->show_hidden
= src_win
->show_hidden
;
1220 filer_window
->show_thumbs
= src_win
->show_thumbs
;
1221 filer_window
->view_type
= src_win
->view_type
;
1225 s_type
= o_display_sort_by
.int_value
;
1226 s_order
= GTK_SORT_ASCENDING
;
1227 dstyle
= o_display_size
.int_value
;
1228 dtype
= o_display_details
.int_value
;
1229 filer_window
->show_hidden
= o_display_show_hidden
.int_value
;
1230 filer_window
->show_thumbs
= o_display_show_thumbs
.int_value
;
1231 filer_window
->view_type
= o_filer_view_type
.int_value
;
1234 /* Add all the user-interface elements & realise */
1235 filer_add_widgets(filer_window
, wm_class
);
1237 gtk_window_set_position(GTK_WINDOW(filer_window
->window
),
1240 /* Connect to all the signal handlers */
1241 filer_add_signals(filer_window
);
1243 display_set_layout(filer_window
, dstyle
, dtype
, TRUE
);
1244 display_set_sort_type(filer_window
, s_type
, s_order
);
1246 /* Open the window after a timeout, or when scanning stops.
1247 * Do this before attaching, because attach() might tell us to
1248 * stop scanning (if a scan isn't needed).
1250 filer_window
->open_timeout
= gtk_timeout_add(500,
1251 (GtkFunction
) open_filer_window
,
1254 /* The view is created empty and then attach() is called, which
1255 * links the filer window to the entry in the directory cache we
1256 * looked up / created above.
1258 * The attach() function will immediately callback to the filer window
1259 * to deliver a list of all known entries in the directory (so,
1260 * the number of items will be known after attach() returns).
1262 * If the directory was not in the cache (because it hadn't been
1263 * opened it before) then the types and icons for the entries are
1264 * not know, but the list of names is.
1267 attach(filer_window
);
1269 number_of_windows
++;
1270 all_filer_windows
= g_list_prepend(all_filer_windows
, filer_window
);
1272 return filer_window
;
1275 void filer_set_view_type(FilerWindow
*filer_window
, ViewType type
)
1277 GtkWidget
*view
= NULL
;
1278 Directory
*dir
= NULL
;
1279 GHashTable
*selected
= NULL
;
1281 g_return_if_fail(filer_window
!= NULL
);
1283 motion_window
= NULL
;
1285 if (filer_window
->view
)
1287 /* Save the current selection */
1291 selected
= g_hash_table_new(g_str_hash
, g_str_equal
);
1292 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
1293 while ((item
= iter
.next(&iter
)))
1294 g_hash_table_insert(selected
, item
->leafname
, "yes");
1296 /* Destroy the old view */
1297 gtk_widget_destroy(GTK_WIDGET(filer_window
->view
));
1298 filer_window
->view
= NULL
;
1300 dir
= filer_window
->directory
;
1302 detach(filer_window
);
1307 case VIEW_TYPE_COLLECTION
:
1308 view
= view_collection_new(filer_window
);
1310 case VIEW_TYPE_DETAILS
:
1311 view
= view_details_new(filer_window
);
1315 g_return_if_fail(view
!= NULL
);
1317 filer_window
->view
= VIEW(view
);
1318 filer_window
->view_type
= type
;
1320 gtk_box_pack_start(filer_window
->view_hbox
, view
, TRUE
, TRUE
, 0);
1321 gtk_widget_show(view
);
1323 /* Drag and drop events */
1324 make_drop_target(view
, 0);
1325 g_signal_connect(view
, "drag_motion",
1326 G_CALLBACK(drag_motion
), filer_window
);
1327 g_signal_connect(view
, "drag_leave",
1328 G_CALLBACK(drag_leave
), filer_window
);
1329 g_signal_connect(view
, "drag_end",
1330 G_CALLBACK(drag_end
), filer_window
);
1331 /* Dragging from us... */
1332 g_signal_connect(view
, "drag_data_get",
1333 GTK_SIGNAL_FUNC(drag_data_get
), NULL
);
1337 /* Only when changing type. Otherwise, will attach later. */
1338 filer_window
->directory
= dir
;
1339 attach(filer_window
);
1341 if (o_filer_auto_resize
.int_value
!= RESIZE_NEVER
)
1342 view_autosize(filer_window
->view
);
1350 view_get_iter(filer_window
->view
, &iter
, 0);
1351 while ((item
= iter
.next(&iter
)))
1353 if (g_hash_table_lookup(selected
, item
->leafname
))
1354 view_set_selected(filer_window
->view
,
1357 g_hash_table_destroy(selected
);
1361 /* This adds all the widgets to a new filer window. It is in a separate
1362 * function because filer_opendir() was getting too long...
1364 static void filer_add_widgets(FilerWindow
*filer_window
, const gchar
*wm_class
)
1366 GtkWidget
*hbox
, *vbox
;
1368 /* Create the top-level window widget */
1369 filer_window
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
1370 filer_set_title(filer_window
);
1372 gtk_window_set_wmclass(GTK_WINDOW(filer_window
->window
),
1375 /* This property is cleared when the window is destroyed.
1376 * You can thus ref filer_window->window and use this to see
1377 * if the window no longer exists.
1379 g_object_set_data(G_OBJECT(filer_window
->window
),
1380 "filer_window", filer_window
);
1382 /* Create this now to make the Adjustment before the View */
1383 filer_window
->scrollbar
= gtk_vscrollbar_new(NULL
);
1385 vbox
= gtk_vbox_new(FALSE
, 0);
1386 gtk_container_add(GTK_CONTAINER(filer_window
->window
), vbox
);
1388 filer_window
->toplevel_vbox
= GTK_BOX(vbox
);
1390 /* If there's a message that should be displayed in each window (eg
1391 * 'Running as root'), add it here.
1393 if (show_user_message
)
1395 filer_window
->message
= gtk_label_new(show_user_message
);
1396 gtk_box_pack_start(GTK_BOX(vbox
), filer_window
->message
,
1398 gtk_widget_show(filer_window
->message
);
1401 hbox
= gtk_hbox_new(FALSE
, 0);
1402 filer_window
->view_hbox
= GTK_BOX(hbox
);
1403 gtk_box_pack_start_defaults(GTK_BOX(vbox
), hbox
);
1404 /* Add the main View widget */
1405 filer_set_view_type(filer_window
, filer_window
->view_type
);
1406 /* Put the scrollbar next to the View */
1407 gtk_box_pack_end(GTK_BOX(hbox
),
1408 filer_window
->scrollbar
, FALSE
, TRUE
, 0);
1409 gtk_widget_show(hbox
);
1411 /* If we want a toolbar, create it now */
1412 toolbar_update_toolbar(filer_window
);
1414 /* And the minibuffer (hidden to start with) */
1415 create_minibuffer(filer_window
);
1416 gtk_box_pack_end(GTK_BOX(vbox
), filer_window
->minibuffer_area
,
1419 /* And the thumbnail progress bar (also hidden) */
1423 filer_window
->thumb_bar
= gtk_hbox_new(FALSE
, 2);
1424 gtk_box_pack_end(GTK_BOX(vbox
), filer_window
->thumb_bar
,
1427 filer_window
->thumb_progress
= gtk_progress_bar_new();
1429 gtk_box_pack_start(GTK_BOX(filer_window
->thumb_bar
),
1430 filer_window
->thumb_progress
, TRUE
, TRUE
, 0);
1432 cancel
= gtk_button_new_with_label(_("Cancel"));
1433 GTK_WIDGET_UNSET_FLAGS(cancel
, GTK_CAN_FOCUS
);
1434 gtk_box_pack_start(GTK_BOX(filer_window
->thumb_bar
),
1435 cancel
, FALSE
, TRUE
, 0);
1436 g_signal_connect_swapped(cancel
, "clicked",
1437 G_CALLBACK(filer_cancel_thumbnails
),
1441 gtk_widget_show(vbox
);
1442 gtk_widget_show(filer_window
->scrollbar
);
1444 gtk_widget_realize(filer_window
->window
);
1446 gdk_window_set_role(filer_window
->window
->window
,
1447 filer_window
->sym_path
);
1449 filer_window_set_size(filer_window
, 4, 4);
1452 static void filer_add_signals(FilerWindow
*filer_window
)
1454 GtkTargetEntry target_table
[] =
1456 {"text/uri-list", 0, TARGET_URI_LIST
},
1457 {"STRING", 0, TARGET_STRING
},
1458 {"COMPOUND_TEXT", 0, TARGET_STRING
},/* XXX: Treats as STRING */
1459 {"UTF8_STRING", 0, TARGET_STRING
},
1462 /* Events on the top-level window */
1463 gtk_widget_add_events(filer_window
->window
, GDK_ENTER_NOTIFY
);
1464 g_signal_connect(filer_window
->window
, "enter-notify-event",
1465 G_CALLBACK(pointer_in
), filer_window
);
1466 g_signal_connect(filer_window
->window
, "leave-notify-event",
1467 G_CALLBACK(pointer_out
), filer_window
);
1468 g_signal_connect(filer_window
->window
, "destroy",
1469 G_CALLBACK(filer_window_destroyed
), filer_window
);
1470 g_signal_connect(filer_window
->window
, "delete-event",
1471 G_CALLBACK(filer_window_delete
), filer_window
);
1473 g_signal_connect(filer_window
->window
, "selection_clear_event",
1474 G_CALLBACK(filer_lost_primary
), filer_window
);
1476 g_signal_connect(filer_window
->window
, "selection_get",
1477 G_CALLBACK(selection_get
), filer_window
);
1478 gtk_selection_add_targets(GTK_WIDGET(filer_window
->window
),
1479 GDK_SELECTION_PRIMARY
,
1481 sizeof(target_table
) / sizeof(*target_table
));
1483 g_signal_connect(filer_window
->window
, "popup-menu",
1484 G_CALLBACK(popup_menu
), filer_window
);
1485 g_signal_connect(filer_window
->window
, "key_press_event",
1486 G_CALLBACK(filer_key_press_event
), filer_window
);
1488 gtk_window_add_accel_group(GTK_WINDOW(filer_window
->window
),
1492 static gint
clear_scanning_display(FilerWindow
*filer_window
)
1494 if (filer_exists(filer_window
))
1495 filer_set_title(filer_window
);
1499 static void set_scanning_display(FilerWindow
*filer_window
, gboolean scanning
)
1501 if (scanning
== filer_window
->scanning
)
1503 filer_window
->scanning
= scanning
;
1506 filer_set_title(filer_window
);
1508 gtk_timeout_add(300, (GtkFunction
) clear_scanning_display
,
1512 /* Note that filer_window may not exist after this call.
1513 * Returns TRUE iff the directory still exists.
1515 gboolean
filer_update_dir(FilerWindow
*filer_window
, gboolean warning
)
1517 gboolean still_exists
;
1519 still_exists
= may_rescan(filer_window
, warning
);
1522 dir_update(filer_window
->directory
, filer_window
->sym_path
);
1524 return still_exists
;
1527 void filer_update_all(void)
1529 GList
*next
= all_filer_windows
;
1533 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1535 /* Updating directory may remove it from list -- stop sending
1536 * patches to move this line!
1540 filer_update_dir(filer_window
, TRUE
);
1544 /* Refresh the various caches even if we don't think we need to */
1545 void full_refresh(void)
1548 reread_mime_files(); /* Refreshes all windows */
1551 /* See whether a filer window with a given path already exists
1552 * and is different from diff.
1554 static FilerWindow
*find_filer_window(const char *sym_path
, FilerWindow
*diff
)
1558 for (next
= all_filer_windows
; next
; next
= next
->next
)
1560 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1562 if (filer_window
!= diff
&&
1563 strcmp(sym_path
, filer_window
->sym_path
) == 0)
1564 return filer_window
;
1570 /* This path has been mounted/umounted/deleted some files - update all dirs */
1571 void filer_check_mounted(const char *real_path
)
1573 GList
*next
= all_filer_windows
;
1576 gboolean resize
= o_filer_auto_resize
.int_value
== RESIZE_ALWAYS
;
1578 /* DOS disks, etc, often don't change the mtime of the root directory
1579 * on modification, so force a refresh now.
1581 g_fscache_update(dir_cache
, real_path
);
1583 len
= strlen(real_path
);
1587 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1591 if (strncmp(real_path
, filer_window
->real_path
, len
) == 0)
1593 char s
= filer_window
->real_path
[len
];
1595 if (s
== '/' || s
== '\0')
1597 if (filer_update_dir(filer_window
, FALSE
) &&
1599 view_autosize(filer_window
->view
);
1604 parent
= g_path_get_dirname(real_path
);
1605 refresh_dirs(parent
);
1608 icons_may_update(real_path
);
1611 /* Close all windows displaying 'path' or subdirectories of 'path' */
1612 void filer_close_recursive(const char *path
)
1614 GList
*next
= all_filer_windows
;
1618 real
= pathdup(path
);
1623 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1627 if (strncmp(real
, filer_window
->real_path
, len
) == 0)
1629 char s
= filer_window
->real_path
[len
];
1631 if (len
== 1 || s
== '/' || s
== '\0')
1632 gtk_widget_destroy(filer_window
->window
);
1637 /* Like minibuffer_show(), except that:
1638 * - It returns FALSE (to be used from an idle callback)
1639 * - It checks that the filer window still exists.
1641 static gboolean
minibuffer_show_cb(FilerWindow
*filer_window
)
1643 if (filer_exists(filer_window
))
1644 minibuffer_show(filer_window
, MINI_PATH
);
1648 /* TRUE iff filer_window points to an existing FilerWindow
1651 gboolean
filer_exists(FilerWindow
*filer_window
)
1655 for (next
= all_filer_windows
; next
; next
= next
->next
)
1657 FilerWindow
*fw
= (FilerWindow
*) next
->data
;
1659 if (fw
== filer_window
)
1666 /* Make sure the window title is up-to-date */
1667 void filer_set_title(FilerWindow
*filer_window
)
1669 gchar
*title
= NULL
;
1672 if (filer_window
->scanning
|| filer_window
->show_hidden
||
1673 filer_window
->show_thumbs
)
1675 if (o_short_flag_names
.int_value
)
1677 flags
= g_strconcat(" +",
1678 filer_window
->scanning
? _("S") : "",
1679 filer_window
->show_hidden
? _("A") : "",
1680 filer_window
->show_thumbs
? _("T") : "",
1685 flags
= g_strconcat(" (",
1686 filer_window
->scanning
? _("Scanning, ") : "",
1687 filer_window
->show_hidden
? _("All, ") : "",
1688 filer_window
->show_thumbs
? _("Thumbs, ") : "",
1690 flags
[strlen(flags
) - 2] = ')';
1695 title
= g_strconcat("//", our_host_name(),
1696 filer_window
->sym_path
, flags
, NULL
);
1698 if (!title
&& home_dir_len
> 1 &&
1699 strncmp(filer_window
->sym_path
, home_dir
, home_dir_len
) == 0)
1701 guchar sep
= filer_window
->sym_path
[home_dir_len
];
1703 if (sep
== '\0' || sep
== '/')
1704 title
= g_strconcat("~",
1705 filer_window
->sym_path
+ home_dir_len
,
1711 title
= g_strconcat(filer_window
->sym_path
, flags
, NULL
);
1713 ensure_utf8(&title
);
1715 if (filer_window
->directory
->error
)
1718 title
= g_strconcat(old
, ": ", filer_window
->directory
->error
,
1723 gtk_window_set_title(GTK_WINDOW(filer_window
->window
), title
);
1727 if (flags
[0] != '\0')
1731 /* Reconnect to the same directory (used when the Show Hidden option is
1732 * toggled). This has the side-effect of updating the window title.
1734 void filer_detach_rescan(FilerWindow
*filer_window
)
1736 Directory
*dir
= filer_window
->directory
;
1739 detach(filer_window
);
1740 filer_window
->directory
= dir
;
1741 attach(filer_window
);
1744 /* Puts the filer window into target mode. When an item is chosen,
1745 * fn(filer_window, iter, data) is called. 'reason' will be displayed
1746 * on the toolbar while target mode is active.
1748 * Use fn == NULL to cancel target mode.
1750 void filer_target_mode(FilerWindow
*filer_window
,
1755 TargetFunc old_fn
= filer_window
->target_cb
;
1758 gdk_window_set_cursor(
1759 GTK_WIDGET(filer_window
->view
)->window
,
1760 fn
? crosshair
: NULL
);
1762 filer_window
->target_cb
= fn
;
1763 filer_window
->target_data
= data
;
1765 if (filer_window
->toolbar_text
== NULL
)
1770 GTK_LABEL(filer_window
->toolbar_text
), reason
);
1771 else if (o_toolbar_info
.int_value
)
1774 toolbar_update_info(filer_window
);
1777 gtk_label_set_text(GTK_LABEL(filer_window
->toolbar_text
), "");
1780 static void set_selection_state(FilerWindow
*filer_window
, gboolean normal
)
1782 GtkStateType old_state
= filer_window
->selection_state
;
1784 filer_window
->selection_state
= normal
1785 ? GTK_STATE_SELECTED
: GTK_STATE_INSENSITIVE
;
1787 if (old_state
!= filer_window
->selection_state
1788 && view_count_selected(filer_window
->view
))
1789 gtk_widget_queue_draw(GTK_WIDGET(filer_window
->view
));
1792 void filer_cancel_thumbnails(FilerWindow
*filer_window
)
1794 gtk_widget_hide(filer_window
->thumb_bar
);
1796 destroy_glist(&filer_window
->thumb_queue
);
1797 filer_window
->max_thumbs
= 0;
1800 /* Generate the next thumb for this window. The window object is
1801 * unref'd when there is nothing more to do.
1802 * If the window no longer has a filer window, nothing is done.
1804 static gboolean
filer_next_thumb_real(GObject
*window
)
1806 FilerWindow
*filer_window
;
1810 filer_window
= g_object_get_data(window
, "filer_window");
1814 g_object_unref(window
);
1818 if (!filer_window
->thumb_queue
)
1820 filer_cancel_thumbnails(filer_window
);
1821 g_object_unref(window
);
1825 total
= filer_window
->max_thumbs
;
1826 done
= total
- g_list_length(filer_window
->thumb_queue
);
1828 path
= (gchar
*) filer_window
->thumb_queue
->data
;
1830 pixmap_background_thumb(path
, (GFunc
) filer_next_thumb
, window
);
1832 filer_window
->thumb_queue
= g_list_remove(filer_window
->thumb_queue
,
1836 gtk_progress_bar_set_fraction(
1837 GTK_PROGRESS_BAR(filer_window
->thumb_progress
),
1838 done
/ (float) total
);
1843 /* path is the thumb just loaded, if any.
1844 * window is unref'd (eventually).
1846 static void filer_next_thumb(GObject
*window
, const gchar
*path
)
1849 dir_force_update_path(path
);
1851 gtk_idle_add((GtkFunction
) filer_next_thumb_real
, window
);
1854 static void start_thumb_scanning(FilerWindow
*filer_window
)
1856 if (GTK_WIDGET_VISIBLE(filer_window
->thumb_bar
))
1857 return; /* Already scanning */
1859 gtk_widget_show_all(filer_window
->thumb_bar
);
1861 g_object_ref(G_OBJECT(filer_window
->window
));
1862 filer_next_thumb(G_OBJECT(filer_window
->window
), NULL
);
1865 /* Set this image to be loaded some time in the future */
1866 void filer_create_thumb(FilerWindow
*filer_window
, const gchar
*path
)
1868 filer_window
->max_thumbs
++;
1870 filer_window
->thumb_queue
= g_list_append(filer_window
->thumb_queue
,
1873 if (filer_window
->scanning
)
1874 return; /* Will start when scan ends */
1876 start_thumb_scanning(filer_window
);
1879 /* If thumbnail display is on, look through all the items in this directory
1880 * and start creating or updating the thumbnails as needed.
1882 void filer_create_thumbs(FilerWindow
*filer_window
)
1887 if (!filer_window
->show_thumbs
)
1890 view_get_iter(filer_window
->view
, &iter
, 0);
1892 while ((item
= iter
.next(&iter
)))
1894 MaskedPixmap
*pixmap
;
1898 if (item
->base_type
!= TYPE_FILE
)
1901 if (strcmp(item
->mime_type
->media_type
, "image") != 0)
1904 path
= make_path(filer_window
->real_path
, item
->leafname
);
1906 pixmap
= g_fscache_lookup_full(pixmap_cache
, path
,
1907 FSCACHE_LOOKUP_ONLY_NEW
, &found
);
1909 g_object_unref(pixmap
);
1911 /* If we didn't get an image, it could be because:
1913 * - We're loading the image now. found is TRUE,
1914 * and we'll update the item later.
1915 * - We tried to load the image and failed. found
1917 * - We haven't tried loading the image. found is
1918 * FALSE, and we start creating the thumb here.
1921 filer_create_thumb(filer_window
, path
);
1925 static void filer_options_changed(void)
1927 if (o_short_flag_names
.has_changed
)
1931 for (next
= all_filer_windows
; next
; next
= next
->next
)
1933 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1935 filer_set_title(filer_window
);
1940 /* Append interesting information to this GString */
1941 void filer_add_tip_details(FilerWindow
*filer_window
,
1942 GString
*tip
, DirItem
*item
)
1944 const guchar
*fullpath
= NULL
;
1946 fullpath
= make_path(filer_window
->real_path
, item
->leafname
);
1948 if (item
->flags
& ITEM_FLAG_SYMLINK
)
1952 target
= readlink_dup(fullpath
);
1955 ensure_utf8(&target
);
1957 g_string_append(tip
, _("Symbolic link to "));
1958 g_string_append(tip
, target
);
1959 g_string_append_c(tip
, '\n');
1964 if (item
->flags
& ITEM_FLAG_APPDIR
)
1969 info
= appinfo_get(fullpath
, item
);
1970 if (info
&& ((node
= xml_get_section(info
, NULL
, "Summary"))))
1973 str
= xmlNodeListGetString(node
->doc
,
1974 node
->xmlChildrenNode
, 1);
1977 g_string_append(tip
, str
);
1978 g_string_append_c(tip
, '\n');
1983 g_object_unref(info
);
1986 if (!g_utf8_validate(item
->leafname
, -1, NULL
))
1987 g_string_append(tip
,
1988 _("This filename is not valid UTF-8. "
1989 "You should rename it.\n"));
1992 /* Return the selection as a text/uri-list.
1993 * g_free() the result.
1995 static guchar
*filer_create_uri_list(FilerWindow
*filer_window
)
2003 g_return_val_if_fail(filer_window
!= NULL
, NULL
);
2005 string
= g_string_new(NULL
);
2007 leader
= g_string_new(filer_window
->sym_path
);
2008 if (leader
->str
[leader
->len
- 1] != '/')
2009 g_string_append_c(leader
, '/');
2011 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
2012 while ((item
= iter
.next(&iter
)))
2016 path
= g_strconcat(leader
->str
, item
->leafname
, NULL
);
2017 uri
= encode_path_as_uri(path
);
2018 g_string_append(string
, uri
);
2019 g_string_append(string
, "\r\n");
2024 g_string_free(leader
, TRUE
);
2025 retval
= string
->str
;
2026 g_string_free(string
, FALSE
);
2031 void filer_perform_action(FilerWindow
*filer_window
, GdkEventButton
*event
)
2034 ViewIface
*view
= filer_window
->view
;
2035 DirItem
*item
= NULL
;
2036 gboolean press
= event
->type
== GDK_BUTTON_PRESS
;
2038 OpenFlags flags
= 0;
2040 if (event
->button
> 3)
2043 view_get_iter_at_point(view
, &iter
, event
->window
, event
->x
, event
->y
);
2044 item
= iter
.peek(&iter
);
2046 if (item
&& event
->button
== 1 &&
2047 view_get_selected(view
, &iter
) &&
2048 filer_window
->selection_state
== GTK_STATE_INSENSITIVE
)
2050 /* Possibly a really slow DnD operation? */
2051 filer_window
->temp_item_selected
= FALSE
;
2053 filer_selection_changed(filer_window
, event
->time
);
2057 if (filer_window
->target_cb
)
2059 dnd_motion_ungrab();
2060 if (item
&& press
&& event
->button
== 1)
2061 filer_window
->target_cb(filer_window
, &iter
,
2062 filer_window
->target_data
);
2064 filer_target_mode(filer_window
, NULL
, NULL
, NULL
);
2069 if (!o_single_click
.int_value
)
2071 /* Make sure both parts of a double-click fall on
2074 static guchar
*first_click
= NULL
;
2075 static guchar
*second_click
= NULL
;
2077 if (event
->type
== GDK_BUTTON_PRESS
)
2079 g_free(first_click
);
2080 first_click
= second_click
;
2083 second_click
= g_strdup(item
->leafname
);
2085 second_click
= NULL
;
2088 if (event
->type
== GDK_2BUTTON_PRESS
)
2090 if (first_click
&& second_click
&&
2091 strcmp(first_click
, second_click
) != 0)
2093 if ((first_click
|| second_click
) &&
2094 !(first_click
&& second_click
))
2099 action
= bind_lookup_bev(
2100 item
? BIND_DIRECTORY_ICON
: BIND_DIRECTORY
,
2105 case ACT_CLEAR_SELECTION
:
2106 view_clear_selection(view
);
2108 case ACT_TOGGLE_SELECTED
:
2109 view_set_selected(view
, &iter
,
2110 !view_get_selected(view
, &iter
));
2112 case ACT_SELECT_EXCL
:
2113 view_select_only(view
, &iter
);
2116 flags
|= OPEN_SHIFT
;
2119 if (event
->button
!= 1 || event
->state
& GDK_MOD1_MASK
)
2120 flags
|= OPEN_CLOSE_WINDOW
;
2122 flags
|= OPEN_SAME_WINDOW
;
2123 if (o_new_button_1
.int_value
)
2124 flags
^= OPEN_SAME_WINDOW
;
2125 if (event
->type
== GDK_2BUTTON_PRESS
)
2126 view_set_selected(view
, &iter
, FALSE
);
2127 dnd_motion_ungrab();
2129 filer_openitem(filer_window
, &iter
, flags
);
2131 case ACT_POPUP_MENU
:
2132 dnd_motion_ungrab();
2134 show_filer_menu(filer_window
,
2135 (GdkEvent
*) event
, &iter
);
2137 case ACT_PRIME_AND_SELECT
:
2138 if (item
&& !view_get_selected(view
, &iter
))
2139 view_select_only(view
, &iter
);
2140 dnd_motion_start(MOTION_READY_FOR_DND
);
2142 case ACT_PRIME_AND_TOGGLE
:
2143 view_set_selected(view
, &iter
,
2144 !view_get_selected(view
, &iter
));
2145 dnd_motion_start(MOTION_READY_FOR_DND
);
2147 case ACT_PRIME_FOR_DND
:
2148 dnd_motion_start(MOTION_READY_FOR_DND
);
2151 if (press
&& event
->button
< 4)
2154 view_wink_item(view
, &iter
);
2155 dnd_motion_start(MOTION_NONE
);
2158 case ACT_LASSO_CLEAR
:
2159 view_clear_selection(view
);
2161 case ACT_LASSO_MODIFY
:
2162 view_start_lasso_box(view
, event
);
2165 view_autosize(filer_window
->view
);
2168 g_warning("Unsupported action : %d\n", action
);
2173 /* It's time to make the tooltip appear. If we're not over the item any
2174 * more, or the item doesn't need a tooltip, do nothing.
2176 static gboolean
tooltip_activate(GtkWidget
*window
)
2178 FilerWindow
*filer_window
;
2182 DirItem
*item
= NULL
;
2183 GString
*tip
= NULL
;
2185 g_return_val_if_fail(tip_item
!= NULL
, 0);
2187 filer_window
= g_object_get_data(G_OBJECT(window
), "filer_window");
2189 if (!motion_window
|| !filer_window
)
2190 return FALSE
; /* Window has been destroyed */
2192 view
= filer_window
->view
;
2196 gdk_window_get_pointer(motion_window
, &x
, &y
, NULL
);
2197 view_get_iter_at_point(view
, &iter
, motion_window
, x
, y
);
2199 item
= iter
.peek(&iter
);
2200 if (item
!= tip_item
)
2201 return FALSE
; /* Not still under the pointer */
2203 /* OK, the filer window still exists and the pointer is still
2204 * over the same item. Do we need to show a tip?
2207 tip
= g_string_new(NULL
);
2209 view_extend_tip(filer_window
->view
, &iter
, tip
);
2211 filer_add_tip_details(filer_window
, tip
, tip_item
);
2215 g_string_truncate(tip
, tip
->len
- 1);
2217 tooltip_show(tip
->str
);
2220 g_string_free(tip
, TRUE
);
2225 /* Motion detected on the View widget */
2226 gint
filer_motion_notify(FilerWindow
*filer_window
, GdkEventMotion
*event
)
2228 ViewIface
*view
= filer_window
->view
;
2232 view_get_iter_at_point(view
, &iter
, event
->window
, event
->x
, event
->y
);
2233 item
= iter
.peek(&iter
);
2237 if (item
!= tip_item
)
2242 motion_window
= event
->window
;
2243 tooltip_prime((GtkFunction
) tooltip_activate
,
2244 G_OBJECT(filer_window
->window
));
2253 if (motion_state
!= MOTION_READY_FOR_DND
)
2256 if (!dnd_motion_moved(event
))
2259 view_get_iter_at_point(view
, &iter
,
2261 event
->x
- (event
->x_root
- drag_start_x
),
2262 event
->y
- (event
->y_root
- drag_start_y
));
2263 item
= iter
.peek(&iter
);
2267 view_wink_item(view
, NULL
);
2269 if (!view_get_selected(view
, &iter
))
2271 if (event
->state
& GDK_BUTTON1_MASK
)
2273 /* Select just this one */
2274 filer_window
->temp_item_selected
= TRUE
;
2275 view_select_only(view
, &iter
);
2279 if (view_count_selected(view
) == 0)
2280 filer_window
->temp_item_selected
= TRUE
;
2281 view_set_selected(view
, &iter
, TRUE
);
2285 g_return_val_if_fail(view_count_selected(view
) > 0, TRUE
);
2287 if (view_count_selected(view
) == 1)
2290 item
= dir_update_item(filer_window
->directory
,
2295 report_error(_("Item no longer exists!"));
2299 drag_one_item(GTK_WIDGET(view
), event
,
2300 make_path(filer_window
->sym_path
, item
->leafname
),
2303 /* XXX: Use thumbnail */
2304 item
, view
? view
->image
: NULL
);
2311 uris
= filer_create_uri_list(filer_window
);
2312 drag_selection(GTK_WIDGET(view
), event
, uris
);
2319 static void drag_end(GtkWidget
*widget
, GdkDragContext
*context
,
2320 FilerWindow
*filer_window
)
2322 filer_set_autoscroll(filer_window
, FALSE
);
2324 if (filer_window
->temp_item_selected
)
2326 view_clear_selection(filer_window
->view
);
2327 filer_window
->temp_item_selected
= FALSE
;
2331 /* Remove highlights */
2332 static void drag_leave(GtkWidget
*widget
,
2333 GdkDragContext
*context
,
2335 FilerWindow
*filer_window
)
2340 /* Called during the drag when the mouse is in a widget registered
2341 * as a drop target. Returns TRUE if we can accept the drop.
2343 static gboolean
drag_motion(GtkWidget
*widget
,
2344 GdkDragContext
*context
,
2348 FilerWindow
*filer_window
)
2351 ViewIface
*view
= filer_window
->view
;
2353 GdkDragAction action
= context
->suggested_action
;
2354 const guchar
*new_path
= NULL
;
2355 const char *type
= NULL
;
2356 gboolean retval
= FALSE
;
2357 gboolean same_window
;
2359 if ((context
->actions
& GDK_ACTION_ASK
) && o_dnd_left_menu
.int_value
)
2362 gdk_window_get_pointer(NULL
, NULL
, NULL
, &state
);
2363 if (state
& GDK_BUTTON1_MASK
)
2364 action
= GDK_ACTION_ASK
;
2367 same_window
= gtk_drag_get_source_widget(context
) == widget
;
2369 filer_set_autoscroll(filer_window
, TRUE
);
2371 if (filer_window
->view_type
== VIEW_TYPE_DETAILS
)
2375 /* Correct for position of bin window */
2376 bin
= gtk_tree_view_get_bin_window(GTK_TREE_VIEW(view
));
2377 gdk_window_get_position(bin
, NULL
, &bin_y
);
2381 if (o_dnd_drag_to_icons
.int_value
)
2383 view_get_iter_at_point(view
, &iter
, widget
->window
, x
, y
);
2384 item
= iter
.peek(&iter
);
2389 if (item
&& same_window
&& view_get_selected(view
, &iter
))
2392 type
= dnd_motion_item(context
, &item
);
2397 /* Don't allow drops to non-writeable directories. BUT, still
2398 * allow drops on non-writeable SUBdirectories so that we can
2399 * do the spring-open thing.
2401 if (item
&& type
== drop_dest_dir
&&
2402 !(item
->flags
& ITEM_FLAG_APPDIR
))
2404 dnd_spring_load(context
, filer_window
);
2410 view_cursor_to_iter(view
, &iter
);
2413 view_cursor_to_iter(view
, NULL
);
2415 /* Disallow background drops within a single window */
2416 if (type
&& same_window
)
2423 new_path
= make_path(filer_window
->sym_path
,
2426 new_path
= filer_window
->sym_path
;
2429 /* Don't ask about dragging to an application! */
2430 if (type
== drop_dest_prog
&& action
== GDK_ACTION_ASK
)
2431 action
= GDK_ACTION_COPY
;
2433 g_dataset_set_data(context
, "drop_dest_type", (gpointer
) type
);
2436 gdk_drag_status(context
, action
, time
);
2437 g_dataset_set_data_full(context
, "drop_dest_path",
2438 g_strdup(new_path
), g_free
);
2445 static gboolean
as_timeout(FilerWindow
*filer_window
)
2449 retval
= view_auto_scroll_callback(filer_window
->view
);
2452 filer_window
->auto_scroll
= -1;
2457 /* When autoscroll is on, a timer keeps track of the pointer position.
2458 * While it's near the top or bottom of the window, the window scrolls.
2460 * If the mouse buttons are released, the pointer leaves the window, or
2461 * a drag-and-drop operation finishes, auto_scroll is turned off.
2463 void filer_set_autoscroll(FilerWindow
*filer_window
, gboolean auto_scroll
)
2465 g_return_if_fail(filer_window
!= NULL
);
2469 if (filer_window
->auto_scroll
!= -1)
2470 return; /* Already on! */
2472 filer_window
->auto_scroll
= gtk_timeout_add(50,
2473 (GtkFunction
) as_timeout
,
2478 if (filer_window
->auto_scroll
== -1)
2479 return; /* Already off! */
2481 gtk_timeout_remove(filer_window
->auto_scroll
);
2482 filer_window
->auto_scroll
= -1;
2486 #define ZERO_MNT "/uri/0install"
2488 static void refresh_done(FilerWindow
*filer_window
)
2490 if (filer_exists(filer_window
))
2491 filer_update_dir(filer_window
, TRUE
);
2494 void filer_refresh(FilerWindow
*filer_window
)
2496 if (!strncmp(ZERO_MNT
"/", filer_window
->real_path
, sizeof(ZERO_MNT
)))
2498 /* Try to run 0refresh */
2500 const gchar
*argv
[] = {"0refresh",
2501 filer_window
->real_path
, NULL
};
2502 pid
= rox_spawn(filer_window
->real_path
, argv
);
2504 on_child_death(pid
, (CallbackFn
) refresh_done
,