4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, 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"
67 static XMLwrapper
*groups
= NULL
;
69 /* This is rather badly named. It's actually the filer window which received
70 * the last key press or Menu click event.
72 FilerWindow
*window_with_focus
= NULL
;
74 GList
*all_filer_windows
= NULL
;
76 static FilerWindow
*window_with_primary
= NULL
;
78 /* Static prototypes */
79 static void attach(FilerWindow
*filer_window
);
80 static void detach(FilerWindow
*filer_window
);
81 static void filer_window_destroyed(GtkWidget
*widget
,
82 FilerWindow
*filer_window
);
83 static void update_display(Directory
*dir
,
86 FilerWindow
*filer_window
);
87 static void set_scanning_display(FilerWindow
*filer_window
, gboolean scanning
);
88 static gboolean
may_rescan(FilerWindow
*filer_window
, gboolean warning
);
89 static gboolean
minibuffer_show_cb(FilerWindow
*filer_window
);
90 static FilerWindow
*find_filer_window(const char *sym_path
, FilerWindow
*diff
);
91 static void filer_add_widgets(FilerWindow
*filer_window
, const gchar
*wm_class
);
92 static void filer_add_signals(FilerWindow
*filer_window
);
94 static void set_selection_state(FilerWindow
*filer_window
, gboolean normal
);
95 static void filer_next_thumb(GObject
*window
, const gchar
*path
);
96 static void start_thumb_scanning(FilerWindow
*filer_window
);
97 static void filer_options_changed(void);
98 static void set_style_by_number_of_items(FilerWindow
*filer_window
);
100 static GdkCursor
*busy_cursor
= NULL
;
101 static GdkCursor
*crosshair
= NULL
;
103 /* Indicates whether the filer's display is different to the machine it
104 * is actually running on.
106 static gboolean not_local
= FALSE
;
108 static Option o_filer_change_size
, o_filer_change_size_num
;
109 static Option o_short_flag_names
;
110 Option o_filer_auto_resize
, o_unique_filer_windows
;
111 Option o_filer_size_limit
;
113 void filer_init(void)
117 gchar
*dpyhost
, *tmp
;
119 option_add_int(&o_filer_size_limit
, "filer_size_limit", 75);
120 option_add_int(&o_filer_auto_resize
, "filer_auto_resize",
122 option_add_int(&o_unique_filer_windows
, "filer_unique_windows", 0);
124 option_add_int(&o_short_flag_names
, "filer_short_flag_names", FALSE
);
126 option_add_int(&o_filer_change_size
, "filer_change_size", TRUE
);
127 option_add_int(&o_filer_change_size_num
, "filer_change_size_num", 30);
129 option_add_notify(filer_options_changed
);
131 busy_cursor
= gdk_cursor_new(GDK_WATCH
);
132 crosshair
= gdk_cursor_new(GDK_CROSSHAIR
);
134 /* Is the display on the local machine, or are we being
135 * run remotely? See filer_set_title().
137 ohost
= our_host_name();
138 dpy
= gdk_get_display();
139 dpyhost
= g_strdup(dpy
);
140 tmp
= strchr(dpyhost
, ':');
144 if (dpyhost
[0] && strcmp(ohost
, dpyhost
) != 0)
146 /* Try the cannonical name for dpyhost (see our_host_name()
151 ent
= gethostbyname(dpyhost
);
152 if (!ent
|| strcmp(ohost
, ent
->h_name
) != 0)
159 static gboolean
if_deleted(gpointer item
, gpointer removed
)
161 int i
= ((GPtrArray
*) removed
)->len
;
162 DirItem
**r
= (DirItem
**) ((GPtrArray
*) removed
)->pdata
;
163 char *leafname
= ((DirItem
*) item
)->leafname
;
167 if (strcmp(leafname
, r
[i
]->leafname
) == 0)
174 /* Resize the filer window to w x h pixels, plus border (not clamped).
175 * If triggered by a key event, warp the pointer (for SloppyFocus users).
177 void filer_window_set_size(FilerWindow
*filer_window
, int w
, int h
)
182 g_return_if_fail(filer_window
!= NULL
);
184 if (filer_window
->scrollbar
)
185 w
+= filer_window
->scrollbar
->allocation
.width
;
187 if (o_toolbar
.int_value
!= TOOLBAR_NONE
)
188 h
+= filer_window
->toolbar
->allocation
.height
;
189 if (filer_window
->message
)
190 h
+= filer_window
->message
->allocation
.height
;
192 window
= filer_window
->window
;
194 if (GTK_WIDGET_VISIBLE(window
))
197 GtkRequisition
*req
= &window
->requisition
;
198 GdkWindow
*gdk_window
= window
->window
;
200 w
= MAX(req
->width
, w
);
201 h
= MAX(req
->height
, h
);
202 gdk_window_get_position(gdk_window
, &x
, &y
);
204 if (x
+ w
> screen_width
|| y
+ h
> screen_height
)
206 if (x
+ w
> screen_width
)
207 x
= screen_width
- w
- 4;
208 if (y
+ h
> screen_height
)
209 y
= screen_height
- h
- 4;
210 gdk_window_move_resize(gdk_window
, x
, y
, w
, h
);
213 gdk_window_resize(gdk_window
, w
, h
);
216 gtk_window_set_default_size(GTK_WINDOW(window
), w
, h
);
218 event
= gtk_get_current_event();
219 if (event
&& event
->type
== GDK_KEY_PRESS
)
221 GdkWindow
*win
= filer_window
->window
->window
;
223 XWarpPointer(gdk_x11_drawable_get_xdisplay(win
),
225 gdk_x11_drawable_get_xid(win
),
231 /* Resize the window to fit the items currently in the Directory.
232 * When opening a directory for the first time, the names will be known but not
233 * the types and images. We can still make a good estimate of the size.
235 void filer_window_autosize(FilerWindow
*filer_window
)
237 view_autosize(filer_window
->view
);
240 /* Called on a timeout while scanning or when scanning ends
241 * (whichever happens first).
243 static gint
open_filer_window(FilerWindow
*filer_window
)
245 view_style_changed(filer_window
->view
, 0);
247 if (filer_window
->open_timeout
)
249 gtk_timeout_remove(filer_window
->open_timeout
);
250 filer_window
->open_timeout
= 0;
253 if (!GTK_WIDGET_VISIBLE(filer_window
->window
))
255 set_style_by_number_of_items(filer_window
);
256 filer_window_autosize(filer_window
);
257 gtk_widget_show(filer_window
->window
);
263 static void update_display(Directory
*dir
,
266 FilerWindow
*filer_window
)
268 ViewIface
*view
= (ViewIface
*) filer_window
->view
;
273 view_add_items(view
, items
);
274 /* Open and resize if currently hidden */
275 open_filer_window(filer_window
);
278 view_delete_if(view
, if_deleted
, items
);
279 toolbar_update_info(filer_window
);
282 set_scanning_display(filer_window
, TRUE
);
283 toolbar_update_info(filer_window
);
286 if (filer_window
->window
->window
)
287 gdk_window_set_cursor(
288 filer_window
->window
->window
,
290 set_scanning_display(filer_window
, FALSE
);
291 toolbar_update_info(filer_window
);
292 open_filer_window(filer_window
);
294 if (filer_window
->had_cursor
&&
295 !view_cursor_visible(view
))
298 view_get_iter(view
, &start
, 0);
299 if (start
.next(&start
))
300 view_cursor_to_iter(view
, &start
);
301 view_show_cursor(view
);
302 filer_window
->had_cursor
= FALSE
;
304 if (filer_window
->auto_select
)
305 display_set_autoselect(filer_window
,
306 filer_window
->auto_select
);
307 null_g_free(&filer_window
->auto_select
);
309 filer_create_thumbs(filer_window
);
311 if (filer_window
->thumb_queue
)
312 start_thumb_scanning(filer_window
);
315 view_update_items(view
, items
);
320 static void attach(FilerWindow
*filer_window
)
322 gdk_window_set_cursor(filer_window
->window
->window
, busy_cursor
);
323 view_clear(filer_window
->view
);
324 filer_window
->scanning
= TRUE
;
325 dir_attach(filer_window
->directory
, (DirCallback
) update_display
,
327 filer_set_title(filer_window
);
330 static void detach(FilerWindow
*filer_window
)
332 g_return_if_fail(filer_window
->directory
!= NULL
);
334 dir_detach(filer_window
->directory
,
335 (DirCallback
) update_display
, filer_window
);
336 g_object_unref(filer_window
->directory
);
337 filer_window
->directory
= NULL
;
340 /* Returns TRUE to prevent closing the window. May offer to unmount a
343 gboolean
filer_window_delete(GtkWidget
*window
,
344 GdkEvent
*unused
, /* (may be NULL) */
345 FilerWindow
*filer_window
)
347 if (mount_is_user_mounted(filer_window
->real_path
))
351 action
= get_choice(PROJECT
,
352 _("Do you want to unmount this device?\n\n"
353 "Unmounting a device makes it safe to remove "
355 GTK_STOCK_CANCEL
, NULL
,
356 GTK_STOCK_CLOSE
, NULL
,
357 ROX_STOCK_MOUNT
, _("Unmount"));
360 return TRUE
; /* Cancel close operation */
366 list
= g_list_prepend(NULL
, filer_window
->sym_path
);
367 action_mount(list
, FALSE
, TRUE
);
375 static void filer_window_destroyed(GtkWidget
*widget
, FilerWindow
*filer_window
)
377 all_filer_windows
= g_list_remove(all_filer_windows
, filer_window
);
379 g_object_set_data(G_OBJECT(widget
), "filer_window", NULL
);
381 if (window_with_primary
== filer_window
)
382 window_with_primary
= NULL
;
384 if (window_with_focus
== filer_window
)
387 window_with_focus
= NULL
;
390 if (filer_window
->directory
)
391 detach(filer_window
);
393 if (filer_window
->open_timeout
)
395 gtk_timeout_remove(filer_window
->open_timeout
);
396 filer_window
->open_timeout
= 0;
399 if (filer_window
->thumb_queue
)
400 destroy_glist(&filer_window
->thumb_queue
);
404 g_free(filer_window
->auto_select
);
405 g_free(filer_window
->real_path
);
406 g_free(filer_window
->sym_path
);
407 g_free(filer_window
);
412 /* Returns TRUE iff the directory still exists. */
413 static gboolean
may_rescan(FilerWindow
*filer_window
, gboolean warning
)
417 g_return_val_if_fail(filer_window
!= NULL
, FALSE
);
419 /* We do a fresh lookup (rather than update) because the inode may
422 dir
= g_fscache_lookup(dir_cache
, filer_window
->real_path
);
426 info_message(_("Directory missing/deleted"));
427 gtk_widget_destroy(filer_window
->window
);
430 if (dir
== filer_window
->directory
)
434 detach(filer_window
);
435 filer_window
->directory
= dir
;
436 attach(filer_window
);
442 /* No items are now selected. This might be because another app claimed
443 * the selection or because the user unselected all the items.
445 void filer_lost_selection(FilerWindow
*filer_window
, guint time
)
447 if (window_with_primary
== filer_window
)
449 window_with_primary
= NULL
;
450 gtk_selection_owner_set(NULL
, GDK_SELECTION_PRIMARY
, time
);
454 /* Another app has claimed the primary selection */
455 static void filer_lost_primary(GtkWidget
*window
,
456 GdkEventSelection
*event
,
459 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
461 if (window_with_primary
&& window_with_primary
== filer_window
)
463 window_with_primary
= NULL
;
464 set_selection_state(filer_window
, FALSE
);
468 /* Someone wants us to send them the selection */
469 static void selection_get(GtkWidget
*widget
,
470 GtkSelectionData
*selection_data
,
475 GString
*reply
, *header
;
476 FilerWindow
*filer_window
= (FilerWindow
*) data
;
480 reply
= g_string_new(NULL
);
481 header
= g_string_new(NULL
);
486 g_string_printf(header
, " %s",
487 make_path(filer_window
->sym_path
, ""));
489 case TARGET_URI_LIST
:
490 g_string_printf(header
, " file://%s%s",
491 our_host_name_for_dnd(),
492 make_path(filer_window
->sym_path
, ""));
496 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
498 while ((item
= iter
.next(&iter
)))
500 g_string_append(reply
, header
->str
);
501 g_string_append(reply
, item
->leafname
);
505 gtk_selection_data_set(selection_data
, xa_string
,
506 8, reply
->str
+ 1, reply
->len
- 1);
509 g_warning("Attempt to paste empty selection!");
510 gtk_selection_data_set(selection_data
, xa_string
, 8, "", 0);
513 g_string_free(reply
, TRUE
);
514 g_string_free(header
, TRUE
);
517 /* Selection has been changed -- try to grab the primary selection
518 * if we don't have it. Also called when clicking on an insensitive selection
520 * Also updates toolbar info.
522 void filer_selection_changed(FilerWindow
*filer_window
, gint time
)
524 toolbar_update_info(filer_window
);
526 if (window_with_primary
== filer_window
)
527 return; /* Already got primary */
529 if (!view_count_selected(filer_window
->view
))
530 return; /* Nothing selected */
532 if (filer_window
->temp_item_selected
== FALSE
&&
533 gtk_selection_owner_set(GTK_WIDGET(filer_window
->window
),
534 GDK_SELECTION_PRIMARY
,
537 window_with_primary
= filer_window
;
538 set_selection_state(filer_window
, TRUE
);
541 set_selection_state(filer_window
, FALSE
);
544 /* Open the item (or add it to the shell command minibuffer) */
545 void filer_openitem(FilerWindow
*filer_window
, ViewIter
*iter
, OpenFlags flags
)
547 gboolean shift
= (flags
& OPEN_SHIFT
) != 0;
548 gboolean close_mini
= flags
& OPEN_FROM_MINI
;
549 gboolean close_window
= (flags
& OPEN_CLOSE_WINDOW
) != 0;
551 const guchar
*full_path
;
552 gboolean wink
= TRUE
;
555 g_return_if_fail(filer_window
!= NULL
&& iter
!= NULL
);
557 item
= iter
->peek(iter
);
559 g_return_if_fail(item
!= NULL
);
561 if (filer_window
->mini_type
== MINI_SHELL
)
563 minibuffer_add(filer_window
, item
->leafname
);
568 dir_update_item(filer_window
->directory
, item
->leafname
);
570 if (item
->base_type
== TYPE_DIRECTORY
)
572 /* Never close a filer window when opening a directory
573 * (click on a dir or click on an app with shift).
575 if (shift
|| !(item
->flags
& ITEM_FLAG_APPDIR
))
576 close_window
= FALSE
;
579 full_path
= make_path(filer_window
->sym_path
, item
->leafname
);
580 if (shift
&& (item
->flags
& ITEM_FLAG_SYMLINK
))
583 old_dir
= filer_window
->directory
;
584 if (run_diritem(full_path
, item
,
585 flags
& OPEN_SAME_WINDOW
? filer_window
: NULL
,
589 if (old_dir
!= filer_window
->directory
)
593 gtk_widget_destroy(filer_window
->window
);
597 view_wink_item(filer_window
->view
, iter
);
599 minibuffer_hide(filer_window
);
604 static gint
pointer_in(GtkWidget
*widget
,
605 GdkEventCrossing
*event
,
606 FilerWindow
*filer_window
)
608 may_rescan(filer_window
, TRUE
);
612 static gint
pointer_out(GtkWidget
*widget
,
613 GdkEventCrossing
*event
,
614 FilerWindow
*filer_window
)
620 /* Move the cursor to the next selected item in direction 'dir'
623 static void next_selected(FilerWindow
*filer_window
, int dir
)
625 ViewIter iter
, cursor
;
626 gboolean have_cursor
;
627 ViewIface
*view
= filer_window
->view
;
629 g_return_if_fail(dir
== 1 || dir
== -1);
631 view_get_cursor(view
, &cursor
);
632 have_cursor
= cursor
.peek(&cursor
) != NULL
;
634 view_get_iter(view
, &iter
,
636 (have_cursor
? VIEW_ITER_FROM_CURSOR
: 0) |
637 (dir
< 0 ? VIEW_ITER_BACKWARDS
: 0));
639 if (have_cursor
&& view_get_selected(view
, &cursor
))
640 iter
.next(&iter
); /* Skip the cursor itself */
642 if (iter
.next(&iter
))
643 view_cursor_to_iter(view
, &iter
);
650 static void return_pressed(FilerWindow
*filer_window
, GdkEventKey
*event
)
652 TargetFunc cb
= filer_window
->target_cb
;
653 gpointer data
= filer_window
->target_data
;
657 filer_target_mode(filer_window
, NULL
, NULL
, NULL
);
659 view_get_cursor(filer_window
->view
, &iter
);
660 if (!iter
.peek(&iter
))
665 cb(filer_window
, &iter
, data
);
669 if (event
->state
& GDK_SHIFT_MASK
)
671 if (event
->state
& GDK_MOD1_MASK
)
672 flags
|= OPEN_CLOSE_WINDOW
;
674 flags
|= OPEN_SAME_WINDOW
;
676 filer_openitem(filer_window
, &iter
, flags
);
679 /* Makes sure that 'groups' is up-to-date, reloading from file if it has
680 * changed. If no groups were loaded and there is no file then initialised
681 * groups to an empty document.
682 * Return the node for the 'name' group.
684 static xmlNode
*group_find(char *name
)
689 /* Update the groups, if possible */
690 path
= choices_find_path_load("Groups.xml", PROJECT
);
694 wrapper
= xml_cache_load(path
);
698 g_object_unref(groups
);
707 groups
= xml_new(NULL
);
708 groups
->doc
= xmlNewDoc("1.0");
710 xmlDocSetRootElement(groups
->doc
,
711 xmlNewDocNode(groups
->doc
, NULL
, "groups", NULL
));
715 node
= xmlDocGetRootElement(groups
->doc
);
717 for (node
= node
->xmlChildrenNode
; node
; node
= node
->next
)
721 gid
= xmlGetProp(node
, "name");
726 if (strcmp(name
, gid
) != 0)
737 static void group_save(FilerWindow
*filer_window
, char *name
)
744 group
= group_find(name
);
747 xmlUnlinkNode(group
);
750 group
= xmlNewChild(xmlDocGetRootElement(groups
->doc
),
751 NULL
, "group", NULL
);
752 xmlSetProp(group
, "name", name
);
754 xmlNewChild(group
, NULL
, "directory", filer_window
->sym_path
);
756 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
758 while ((item
= iter
.next(&iter
)))
759 xmlNewChild(group
, NULL
, "item", item
->leafname
);
761 save_path
= choices_find_path_save("Groups.xml", PROJECT
, TRUE
);
764 save_xml_file(groups
->doc
, save_path
);
769 static gboolean
group_restore_cb(ViewIter
*iter
, gpointer data
)
771 GHashTable
*in_group
= (GHashTable
*) data
;
773 return g_hash_table_lookup(in_group
,
774 iter
->peek(iter
)->leafname
) != NULL
;
777 static void group_restore(FilerWindow
*filer_window
, char *name
)
779 GHashTable
*in_group
;
781 xmlNode
*group
, *node
;
783 group
= group_find(name
);
787 report_error(_("Group %s is not set. Select some files "
788 "and press Ctrl+%s to set the group. Press %s "
789 "on its own to reselect the files later.\n"
790 "Make sure NumLock is on if you use the keypad."),
795 node
= get_subnode(group
, NULL
, "directory");
796 g_return_if_fail(node
!= NULL
);
797 path
= xmlNodeListGetString(groups
->doc
, node
->xmlChildrenNode
, 1);
798 g_return_if_fail(path
!= NULL
);
800 if (strcmp(path
, filer_window
->sym_path
) != 0)
801 filer_change_to(filer_window
, path
, NULL
);
804 in_group
= g_hash_table_new(g_str_hash
, g_str_equal
);
805 for (node
= group
->xmlChildrenNode
; node
; node
= node
->next
)
808 if (node
->type
!= XML_ELEMENT_NODE
)
810 if (strcmp(node
->name
, "item") != 0)
813 leaf
= xmlNodeListGetString(groups
->doc
,
814 node
->xmlChildrenNode
, 1);
816 g_warning("Missing leafname!\n");
818 g_hash_table_insert(in_group
, leaf
, filer_window
);
821 view_select_if(filer_window
->view
, &group_restore_cb
, in_group
);
823 g_hash_table_foreach(in_group
, (GHFunc
) g_free
, NULL
);
824 g_hash_table_destroy(in_group
);
827 static gboolean
popup_menu(GtkWidget
*widget
, FilerWindow
*filer_window
)
831 view_get_cursor(filer_window
->view
, &iter
);
833 show_filer_menu(filer_window
, NULL
, &iter
);
838 void filer_window_toggle_cursor_item_selected(FilerWindow
*filer_window
)
840 ViewIface
*view
= filer_window
->view
;
843 view_get_iter(view
, &iter
, VIEW_ITER_FROM_CURSOR
);
844 if (!iter
.next(&iter
))
845 return; /* No cursor */
847 if (view_get_selected(view
, &iter
))
848 view_set_selected(view
, &iter
, FALSE
);
850 view_set_selected(view
, &iter
, TRUE
);
852 if (iter
.next(&iter
))
853 view_cursor_to_iter(view
, &iter
);
856 /* Handle keys that can't be bound with the menu */
857 static gint
key_press_event(GtkWidget
*widget
,
859 FilerWindow
*filer_window
)
861 GtkWidget
*focus
= GTK_WINDOW(widget
)->focus_widget
;
862 guint key
= event
->keyval
;
865 window_with_focus
= filer_window
;
867 /* Delay setting up the keys until now to speed loading... */
869 ensure_filer_menu(); /* Gets the keys working... */
871 if (!g_slist_find(filer_keys
->acceleratables
, widget
))
872 gtk_window_add_accel_group(GTK_WINDOW(filer_window
->window
),
875 if (focus
&& focus
!= widget
&&
876 gtk_widget_get_toplevel(focus
) == widget
)
877 if (gtk_widget_event(focus
, (GdkEvent
*) event
))
878 return TRUE
; /* Handled */
883 filer_target_mode(filer_window
, NULL
, NULL
, NULL
);
884 view_cursor_to_iter(filer_window
->view
, NULL
);
885 view_clear_selection(filer_window
->view
);
888 return_pressed(filer_window
, event
);
890 case GDK_ISO_Left_Tab
:
891 next_selected(filer_window
, -1);
894 next_selected(filer_window
, 1);
897 change_to_parent(filer_window
);
905 view_get_cursor(filer_window
->view
, &iter
);
906 show_filer_menu(filer_window
,
907 (GdkEvent
*) event
, &iter
);
911 filer_window_toggle_cursor_item_selected(filer_window
);
914 if (key
>= GDK_0
&& key
<= GDK_9
)
915 group
[0] = key
- GDK_0
+ '0';
916 else if (key
>= GDK_KP_0
&& key
<= GDK_KP_9
)
917 group
[0] = key
- GDK_KP_0
+ '0';
922 if (event
->state
& GDK_CONTROL_MASK
)
923 group_save(filer_window
, group
);
925 group_restore(filer_window
, group
);
931 void filer_open_parent(FilerWindow
*filer_window
)
934 const char *current
= filer_window
->sym_path
;
936 if (current
[0] == '/' && current
[1] == '\0')
937 return; /* Already in the root */
939 dir
= g_path_get_dirname(current
);
940 filer_opendir(dir
, filer_window
, NULL
);
944 void change_to_parent(FilerWindow
*filer_window
)
947 const char *current
= filer_window
->sym_path
;
949 if (current
[0] == '/' && current
[1] == '\0')
950 return; /* Already in the root */
952 dir
= g_path_get_dirname(current
);
953 filer_change_to(filer_window
, dir
, g_basename(current
));
957 /* Removes trailing /s from path (modified in place) */
958 static void tidy_sympath(gchar
*path
)
962 g_return_if_fail(path
!= NULL
);
965 while (l
> 1 && path
[l
- 1] == '/')
972 /* Make filer_window display path. When finished, highlight item 'from', or
973 * the first item if from is NULL. If there is currently no cursor then
974 * simply wink 'from' (if not NULL).
975 * If the cause was a key event and we resize, warp the pointer.
977 void filer_change_to(FilerWindow
*filer_window
,
978 const char *path
, const char *from
)
981 char *sym_path
, *real_path
;
984 g_return_if_fail(filer_window
!= NULL
);
986 filer_cancel_thumbnails(filer_window
);
990 sym_path
= g_strdup(path
);
991 real_path
= pathdup(path
);
992 new_dir
= g_fscache_lookup(dir_cache
, real_path
);
996 delayed_error(_("Directory '%s' is not accessible"),
1003 if (o_unique_filer_windows
.int_value
)
1007 fw
= find_filer_window(sym_path
, filer_window
);
1009 gtk_widget_destroy(fw
->window
);
1012 from_dup
= from
&& *from
? g_strdup(from
) : NULL
;
1014 detach(filer_window
);
1015 g_free(filer_window
->real_path
);
1016 g_free(filer_window
->sym_path
);
1017 filer_window
->real_path
= real_path
;
1018 filer_window
->sym_path
= sym_path
;
1019 tidy_sympath(filer_window
->sym_path
);
1021 filer_window
->directory
= new_dir
;
1023 g_free(filer_window
->auto_select
);
1024 filer_window
->auto_select
= from_dup
;
1026 filer_window
->had_cursor
= filer_window
->had_cursor
||
1027 view_cursor_visible(filer_window
->view
);
1029 filer_set_title(filer_window
);
1030 if (filer_window
->window
->window
)
1031 gdk_window_set_role(filer_window
->window
->window
,
1032 filer_window
->sym_path
);
1033 view_cursor_to_iter(filer_window
->view
, NULL
);
1035 attach(filer_window
);
1037 set_style_by_number_of_items(filer_window
);
1039 if (o_filer_auto_resize
.int_value
== RESIZE_ALWAYS
)
1040 filer_window_autosize(filer_window
);
1042 if (filer_window
->mini_type
== MINI_PATH
)
1043 gtk_idle_add((GtkFunction
) minibuffer_show_cb
,
1047 /* Returns a list containing the full (sym) pathname of every selected item.
1048 * You must g_free() each item in the list.
1050 GList
*filer_selected_items(FilerWindow
*filer_window
)
1052 GList
*retval
= NULL
;
1053 guchar
*dir
= filer_window
->sym_path
;
1057 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
1058 while ((item
= iter
.next(&iter
)))
1060 retval
= g_list_prepend(retval
,
1061 g_strdup(make_path(dir
, item
->leafname
)));
1064 return g_list_reverse(retval
);
1067 /* Return the single selected item. Error if nothing is selected. */
1068 DirItem
*filer_selected_item(FilerWindow
*filer_window
)
1073 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
1075 item
= iter
.next(&iter
);
1076 g_return_val_if_fail(item
!= NULL
, NULL
);
1077 g_return_val_if_fail(iter
.next(&iter
) == NULL
, NULL
);
1082 /* Creates and shows a new filer window.
1083 * If src_win != NULL then display options can be taken from that source window.
1084 * Returns the new filer window, or NULL on error.
1085 * Note: if unique windows is in use, may return an existing window.
1087 FilerWindow
*filer_opendir(const char *path
, FilerWindow
*src_win
,
1088 const gchar
*wm_class
)
1090 FilerWindow
*filer_window
;
1092 DisplayStyle dstyle
;
1095 /* Get the real pathname of the directory and copy it */
1096 real_path
= pathdup(path
);
1098 if (o_unique_filer_windows
.int_value
)
1100 FilerWindow
*same_dir_window
;
1102 same_dir_window
= find_filer_window(path
, NULL
);
1104 if (same_dir_window
)
1106 gtk_window_present(GTK_WINDOW(same_dir_window
->window
));
1107 return same_dir_window
;
1111 filer_window
= g_new(FilerWindow
, 1);
1112 filer_window
->message
= NULL
;
1113 filer_window
->minibuffer
= NULL
;
1114 filer_window
->minibuffer_label
= NULL
;
1115 filer_window
->minibuffer_area
= NULL
;
1116 filer_window
->temp_show_hidden
= FALSE
;
1117 filer_window
->sym_path
= g_strdup(path
);
1118 filer_window
->real_path
= real_path
;
1119 filer_window
->scanning
= FALSE
;
1120 filer_window
->had_cursor
= FALSE
;
1121 filer_window
->auto_select
= NULL
;
1122 filer_window
->toolbar_text
= NULL
;
1123 filer_window
->target_cb
= NULL
;
1124 filer_window
->mini_type
= MINI_NONE
;
1125 filer_window
->selection_state
= GTK_STATE_INSENSITIVE
;
1126 filer_window
->toolbar
= NULL
;
1127 filer_window
->toplevel_vbox
= NULL
;
1128 filer_window
->view
= NULL
;
1129 filer_window
->scrollbar
= NULL
;
1131 tidy_sympath(filer_window
->sym_path
);
1133 /* Finds the entry for this directory in the dir cache, creating
1134 * a new one if needed. This does not cause a scan to start,
1135 * so if a new entry is created then it will be empty.
1137 filer_window
->directory
= g_fscache_lookup(dir_cache
, real_path
);
1138 if (!filer_window
->directory
)
1140 delayed_error(_("Directory '%s' not found."), path
);
1141 g_free(filer_window
->real_path
);
1142 g_free(filer_window
->sym_path
);
1143 g_free(filer_window
);
1147 filer_window
->temp_item_selected
= FALSE
;
1148 filer_window
->flags
= (FilerFlags
) 0;
1149 filer_window
->details_type
= DETAILS_SUMMARY
;
1150 filer_window
->display_style
= UNKNOWN_STYLE
;
1151 filer_window
->thumb_queue
= NULL
;
1152 filer_window
->max_thumbs
= 0;
1154 if (src_win
&& o_display_inherit_options
.int_value
)
1156 filer_window
->sort_fn
= src_win
->sort_fn
;
1157 dstyle
= src_win
->display_style
;
1158 dtype
= src_win
->details_type
;
1159 filer_window
->show_hidden
= src_win
->show_hidden
;
1160 filer_window
->show_thumbs
= src_win
->show_thumbs
;
1164 int i
= o_display_sort_by
.int_value
;
1165 filer_window
->sort_fn
= i
== 0 ? sort_by_name
:
1166 i
== 1 ? sort_by_type
:
1167 i
== 2 ? sort_by_date
:
1170 dstyle
= o_display_size
.int_value
;
1171 dtype
= o_display_details
.int_value
;
1172 filer_window
->show_hidden
=
1173 o_display_show_hidden
.int_value
;
1174 filer_window
->show_thumbs
=
1175 o_display_show_thumbs
.int_value
;
1178 /* Add all the user-interface elements & realise */
1179 filer_add_widgets(filer_window
, wm_class
);
1181 gtk_window_set_position(GTK_WINDOW(filer_window
->window
),
1184 /* Connect to all the signal handlers */
1185 filer_add_signals(filer_window
);
1187 display_set_layout(filer_window
, dstyle
, dtype
);
1189 /* Open the window after a timeout, or when scanning stops.
1190 * Do this before attaching, because attach() might tell us to
1191 * stop scanning (if a scan isn't needed).
1193 filer_window
->open_timeout
= gtk_timeout_add(500,
1194 (GtkFunction
) open_filer_window
,
1197 /* The view is created empty and then attach() is called, which
1198 * links the filer window to the entry in the directory cache we
1199 * looked up / created above.
1201 * The attach() function will immediately callback to the filer window
1202 * to deliver a list of all known entries in the directory (so,
1203 * the number of items will be known after attach() returns).
1205 * If the directory was not in the cache (because it hadn't been
1206 * opened it before) then the types and icons for the entries are
1207 * not know, but the list of names is.
1210 attach(filer_window
);
1212 number_of_windows
++;
1213 all_filer_windows
= g_list_prepend(all_filer_windows
, filer_window
);
1215 return filer_window
;
1218 void filer_set_view_type(FilerWindow
*filer_window
, ViewType type
)
1220 GtkWidget
*view
= NULL
;
1221 Directory
*dir
= NULL
;
1223 g_return_if_fail(filer_window
!= NULL
);
1225 if (filer_window
->view
)
1227 gtk_widget_destroy(GTK_WIDGET(filer_window
->view
));
1228 filer_window
->view
= NULL
;
1230 dir
= filer_window
->directory
;
1232 detach(filer_window
);
1237 case VIEW_TYPE_COLLECTION
:
1238 view
= view_collection_new(filer_window
);
1240 case VIEW_TYPE_DETAILS
:
1241 view
= view_details_new(filer_window
);
1245 g_return_if_fail(view
!= NULL
);
1247 filer_window
->view
= VIEW(view
);
1248 filer_window
->view_type
= type
;
1250 gtk_box_pack_start(filer_window
->toplevel_vbox
, view
, TRUE
, TRUE
, 0);
1251 gtk_widget_show(view
);
1255 /* Only when changing type. Otherwise, will attach later. */
1256 filer_window
->directory
= dir
;
1257 attach(filer_window
);
1261 /* This adds all the widgets to a new filer window. It is in a separate
1262 * function because filer_opendir() was getting too long...
1264 static void filer_add_widgets(FilerWindow
*filer_window
, const gchar
*wm_class
)
1266 GtkWidget
*hbox
, *vbox
;
1268 /* Create the top-level window widget */
1269 filer_window
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
1270 filer_set_title(filer_window
);
1272 gtk_window_set_wmclass(GTK_WINDOW(filer_window
->window
),
1275 /* This property is cleared when the window is destroyed.
1276 * You can thus ref filer_window->window and use this to see
1277 * if the window no longer exists.
1279 g_object_set_data(G_OBJECT(filer_window
->window
),
1280 "filer_window", filer_window
);
1282 /* Create this now to make the Adjustment before the View */
1283 filer_window
->scrollbar
= gtk_vscrollbar_new(NULL
);
1285 /* Scrollbar on the right, everything else on the left */
1286 hbox
= gtk_hbox_new(FALSE
, 0);
1287 gtk_container_add(GTK_CONTAINER(filer_window
->window
), hbox
);
1289 vbox
= gtk_vbox_new(FALSE
, 0);
1290 gtk_box_pack_start_defaults(GTK_BOX(hbox
), vbox
);
1291 filer_window
->toplevel_vbox
= GTK_BOX(vbox
);
1293 filer_set_view_type(filer_window
, VIEW_TYPE_COLLECTION
);
1295 /* If we want a toolbar, create it now */
1296 toolbar_update_toolbar(filer_window
);
1298 /* If there's a message that should be displayed in each window (eg
1299 * 'Running as root'), add it here.
1301 if (show_user_message
)
1303 filer_window
->message
= gtk_label_new(show_user_message
);
1304 gtk_box_pack_start(GTK_BOX(vbox
), filer_window
->message
,
1306 gtk_widget_show(filer_window
->message
);
1309 /* And the minibuffer (hidden to start with) */
1310 create_minibuffer(filer_window
);
1311 gtk_box_pack_end(GTK_BOX(vbox
), filer_window
->minibuffer_area
,
1314 /* And the thumbnail progress bar (also hidden) */
1318 filer_window
->thumb_bar
= gtk_hbox_new(FALSE
, 2);
1319 gtk_box_pack_end(GTK_BOX(vbox
), filer_window
->thumb_bar
,
1322 filer_window
->thumb_progress
= gtk_progress_bar_new();
1324 gtk_box_pack_start(GTK_BOX(filer_window
->thumb_bar
),
1325 filer_window
->thumb_progress
, TRUE
, TRUE
, 0);
1327 cancel
= gtk_button_new_with_label(_("Cancel"));
1328 GTK_WIDGET_UNSET_FLAGS(cancel
, GTK_CAN_FOCUS
);
1329 gtk_box_pack_start(GTK_BOX(filer_window
->thumb_bar
),
1330 cancel
, FALSE
, TRUE
, 0);
1331 g_signal_connect_swapped(cancel
, "clicked",
1332 G_CALLBACK(filer_cancel_thumbnails
),
1336 /* Put the scrollbar on the left of everything else... */
1337 gtk_box_pack_start(GTK_BOX(hbox
),
1338 filer_window
->scrollbar
, FALSE
, TRUE
, 0);
1340 gtk_widget_show(hbox
);
1341 gtk_widget_show(vbox
);
1342 gtk_widget_show(filer_window
->scrollbar
);
1344 gtk_widget_realize(filer_window
->window
);
1346 gdk_window_set_role(filer_window
->window
->window
,
1347 filer_window
->sym_path
);
1349 filer_window_set_size(filer_window
, 4, 4);
1352 static void filer_add_signals(FilerWindow
*filer_window
)
1354 GtkTargetEntry target_table
[] =
1356 {"text/uri-list", 0, TARGET_URI_LIST
},
1357 {"STRING", 0, TARGET_STRING
},
1358 {"COMPOUND_TEXT", 0, TARGET_STRING
},/* XXX: Treats as STRING */
1361 /* Events on the top-level window */
1362 gtk_widget_add_events(filer_window
->window
, GDK_ENTER_NOTIFY
);
1363 g_signal_connect(filer_window
->window
, "enter-notify-event",
1364 G_CALLBACK(pointer_in
), filer_window
);
1365 g_signal_connect(filer_window
->window
, "leave-notify-event",
1366 G_CALLBACK(pointer_out
), filer_window
);
1367 g_signal_connect(filer_window
->window
, "destroy",
1368 G_CALLBACK(filer_window_destroyed
), filer_window
);
1369 g_signal_connect(filer_window
->window
, "delete-event",
1370 G_CALLBACK(filer_window_delete
), filer_window
);
1372 g_signal_connect(filer_window
->window
, "selection_clear_event",
1373 G_CALLBACK(filer_lost_primary
), filer_window
);
1375 g_signal_connect(filer_window
->window
, "selection_get",
1376 G_CALLBACK(selection_get
), filer_window
);
1377 gtk_selection_add_targets(GTK_WIDGET(filer_window
->window
),
1378 GDK_SELECTION_PRIMARY
,
1380 sizeof(target_table
) / sizeof(*target_table
));
1382 g_signal_connect(filer_window
->window
, "popup-menu",
1383 G_CALLBACK(popup_menu
), filer_window
);
1384 g_signal_connect(filer_window
->window
, "key_press_event",
1385 G_CALLBACK(key_press_event
), filer_window
);
1388 static gint
clear_scanning_display(FilerWindow
*filer_window
)
1390 if (filer_exists(filer_window
))
1391 filer_set_title(filer_window
);
1395 static void set_scanning_display(FilerWindow
*filer_window
, gboolean scanning
)
1397 if (scanning
== filer_window
->scanning
)
1399 filer_window
->scanning
= scanning
;
1402 filer_set_title(filer_window
);
1404 gtk_timeout_add(300, (GtkFunction
) clear_scanning_display
,
1408 /* Note that filer_window may not exist after this call */
1409 void filer_update_dir(FilerWindow
*filer_window
, gboolean warning
)
1411 if (may_rescan(filer_window
, warning
))
1412 dir_update(filer_window
->directory
, filer_window
->sym_path
);
1415 void filer_update_all(void)
1417 GList
*next
= all_filer_windows
;
1421 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1423 /* Updating directory may remove it from list -- stop sending
1424 * patches to move this line!
1428 filer_update_dir(filer_window
, TRUE
);
1432 /* Refresh the various caches even if we don't think we need to */
1433 void full_refresh(void)
1436 reread_mime_files();
1439 /* See whether a filer window with a given path already exists
1440 * and is different from diff.
1442 static FilerWindow
*find_filer_window(const char *sym_path
, FilerWindow
*diff
)
1446 for (next
= all_filer_windows
; next
; next
= next
->next
)
1448 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1450 if (filer_window
!= diff
&&
1451 strcmp(sym_path
, filer_window
->sym_path
) == 0)
1452 return filer_window
;
1458 /* This path has been mounted/umounted/deleted some files - update all dirs */
1459 void filer_check_mounted(const char *real_path
)
1461 GList
*next
= all_filer_windows
;
1465 len
= strlen(real_path
);
1469 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1473 if (strncmp(real_path
, filer_window
->real_path
, len
) == 0)
1475 char s
= filer_window
->real_path
[len
];
1477 if (s
== '/' || s
== '\0')
1478 filer_update_dir(filer_window
, FALSE
);
1482 parent
= g_path_get_dirname(real_path
);
1483 refresh_dirs(parent
);
1486 icons_may_update(real_path
);
1489 /* Close all windows displaying 'path' or subdirectories of 'path' */
1490 void filer_close_recursive(const char *path
)
1492 GList
*next
= all_filer_windows
;
1496 real
= pathdup(path
);
1501 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1505 if (strncmp(real
, filer_window
->real_path
, len
) == 0)
1507 char s
= filer_window
->real_path
[len
];
1509 if (len
== 1 || s
== '/' || s
== '\0')
1510 gtk_widget_destroy(filer_window
->window
);
1515 /* Like minibuffer_show(), except that:
1516 * - It returns FALSE (to be used from an idle callback)
1517 * - It checks that the filer window still exists.
1519 static gboolean
minibuffer_show_cb(FilerWindow
*filer_window
)
1521 if (filer_exists(filer_window
))
1522 minibuffer_show(filer_window
, MINI_PATH
);
1526 /* TRUE iff filer_window points to an existing FilerWindow
1529 gboolean
filer_exists(FilerWindow
*filer_window
)
1533 for (next
= all_filer_windows
; next
; next
= next
->next
)
1535 FilerWindow
*fw
= (FilerWindow
*) next
->data
;
1537 if (fw
== filer_window
)
1544 /* Make sure the window title is up-to-date */
1545 void filer_set_title(FilerWindow
*filer_window
)
1547 gchar
*title
= NULL
;
1550 if (filer_window
->scanning
|| filer_window
->show_hidden
||
1551 filer_window
->show_thumbs
)
1553 if (o_short_flag_names
.int_value
)
1555 flags
= g_strconcat(" +",
1556 filer_window
->scanning
? _("S") : "",
1557 filer_window
->show_hidden
? _("A") : "",
1558 filer_window
->show_thumbs
? _("T") : "",
1563 flags
= g_strconcat(" (",
1564 filer_window
->scanning
? _("Scanning, ") : "",
1565 filer_window
->show_hidden
? _("All, ") : "",
1566 filer_window
->show_thumbs
? _("Thumbs, ") : "",
1568 flags
[strlen(flags
) - 2] = ')';
1573 title
= g_strconcat("//", our_host_name(),
1574 filer_window
->sym_path
, flags
, NULL
);
1576 if (!title
&& home_dir_len
> 1 &&
1577 strncmp(filer_window
->sym_path
, home_dir
, home_dir_len
) == 0)
1579 guchar sep
= filer_window
->sym_path
[home_dir_len
];
1581 if (sep
== '\0' || sep
== '/')
1582 title
= g_strconcat("~",
1583 filer_window
->sym_path
+ home_dir_len
,
1589 title
= g_strconcat(filer_window
->sym_path
, flags
, NULL
);
1591 ensure_utf8(&title
);
1593 gtk_window_set_title(GTK_WINDOW(filer_window
->window
), title
);
1597 if (flags
[0] != '\0')
1601 /* Reconnect to the same directory (used when the Show Hidden option is
1602 * toggled). This has the side-effect of updating the window title.
1604 void filer_detach_rescan(FilerWindow
*filer_window
)
1606 Directory
*dir
= filer_window
->directory
;
1609 detach(filer_window
);
1610 filer_window
->directory
= dir
;
1611 attach(filer_window
);
1614 /* Puts the filer window into target mode. When an item is chosen,
1615 * fn(filer_window, iter, data) is called. 'reason' will be displayed
1616 * on the toolbar while target mode is active.
1618 * Use fn == NULL to cancel target mode.
1620 void filer_target_mode(FilerWindow
*filer_window
,
1625 TargetFunc old_fn
= filer_window
->target_cb
;
1628 gdk_window_set_cursor(
1629 GTK_WIDGET(filer_window
->view
)->window
,
1630 fn
? crosshair
: NULL
);
1632 filer_window
->target_cb
= fn
;
1633 filer_window
->target_data
= data
;
1635 if (filer_window
->toolbar_text
== NULL
)
1640 GTK_LABEL(filer_window
->toolbar_text
), reason
);
1641 else if (o_toolbar_info
.int_value
)
1644 toolbar_update_info(filer_window
);
1647 gtk_label_set_text(GTK_LABEL(filer_window
->toolbar_text
), "");
1650 static void set_selection_state(FilerWindow
*filer_window
, gboolean normal
)
1652 GtkStateType old_state
= filer_window
->selection_state
;
1654 filer_window
->selection_state
= normal
1655 ? GTK_STATE_SELECTED
: GTK_STATE_INSENSITIVE
;
1657 if (old_state
!= filer_window
->selection_state
1658 && view_count_selected(filer_window
->view
))
1659 gtk_widget_queue_draw(GTK_WIDGET(filer_window
->view
));
1662 void filer_cancel_thumbnails(FilerWindow
*filer_window
)
1664 gtk_widget_hide(filer_window
->thumb_bar
);
1666 destroy_glist(&filer_window
->thumb_queue
);
1667 filer_window
->max_thumbs
= 0;
1670 /* Generate the next thumb for this window. The window object is
1671 * unref'd when there is nothing more to do.
1672 * If the window no longer has a filer window, nothing is done.
1674 static gboolean
filer_next_thumb_real(GObject
*window
)
1676 FilerWindow
*filer_window
;
1680 filer_window
= g_object_get_data(window
, "filer_window");
1684 g_object_unref(window
);
1688 if (!filer_window
->thumb_queue
)
1690 filer_cancel_thumbnails(filer_window
);
1691 g_object_unref(window
);
1695 total
= filer_window
->max_thumbs
;
1696 done
= total
- g_list_length(filer_window
->thumb_queue
);
1698 path
= (gchar
*) filer_window
->thumb_queue
->data
;
1700 pixmap_background_thumb(path
, (GFunc
) filer_next_thumb
, window
);
1702 filer_window
->thumb_queue
= g_list_remove(filer_window
->thumb_queue
,
1706 gtk_progress_bar_set_fraction(
1707 GTK_PROGRESS_BAR(filer_window
->thumb_progress
),
1708 done
/ (float) total
);
1713 /* path is the thumb just loaded, if any.
1714 * window is unref'd (eventually).
1716 static void filer_next_thumb(GObject
*window
, const gchar
*path
)
1719 dir_force_update_path(path
);
1721 gtk_idle_add((GtkFunction
) filer_next_thumb_real
, window
);
1724 static void start_thumb_scanning(FilerWindow
*filer_window
)
1726 if (GTK_WIDGET_VISIBLE(filer_window
->thumb_bar
))
1727 return; /* Already scanning */
1729 gtk_widget_show_all(filer_window
->thumb_bar
);
1731 g_object_ref(G_OBJECT(filer_window
->window
));
1732 filer_next_thumb(G_OBJECT(filer_window
->window
), NULL
);
1735 /* Set this image to be loaded some time in the future */
1736 void filer_create_thumb(FilerWindow
*filer_window
, const gchar
*path
)
1738 filer_window
->max_thumbs
++;
1740 filer_window
->thumb_queue
= g_list_append(filer_window
->thumb_queue
,
1743 if (filer_window
->scanning
)
1744 return; /* Will start when scan ends */
1746 start_thumb_scanning(filer_window
);
1749 /* If thumbnail display is on, look through all the items in this directory
1750 * and start creating or updating the thumbnails as needed.
1752 void filer_create_thumbs(FilerWindow
*filer_window
)
1757 if (!filer_window
->show_thumbs
)
1760 view_get_iter(filer_window
->view
, &iter
, 0);
1762 while ((item
= iter
.next(&iter
)))
1764 MaskedPixmap
*pixmap
;
1768 if (item
->base_type
!= TYPE_FILE
)
1771 if (strcmp(item
->mime_type
->media_type
, "image") != 0)
1774 path
= make_path(filer_window
->real_path
, item
->leafname
);
1776 pixmap
= g_fscache_lookup_full(pixmap_cache
, path
,
1777 FSCACHE_LOOKUP_ONLY_NEW
, &found
);
1779 g_object_unref(pixmap
);
1781 /* If we didn't get an image, it could be because:
1783 * - We're loading the image now. found is TRUE,
1784 * and we'll update the item later.
1785 * - We tried to load the image and failed. found
1787 * - We haven't tried loading the image. found is
1788 * FALSE, and we start creating the thumb here.
1791 filer_create_thumb(filer_window
, path
);
1795 static void filer_options_changed(void)
1797 if (o_short_flag_names
.has_changed
)
1801 for (next
= all_filer_windows
; next
; next
= next
->next
)
1803 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1805 filer_set_title(filer_window
);
1810 /* Change to Large or Small icons depending on the number of items
1811 * in the directory, subject to options.
1813 static void set_style_by_number_of_items(FilerWindow
*filer_window
)
1817 g_return_if_fail(filer_window
!= NULL
);
1819 if (!o_filer_change_size
.int_value
)
1820 return; /* Don't auto-set style */
1822 if (filer_window
->display_style
!= LARGE_ICONS
&&
1823 filer_window
->display_style
!= SMALL_ICONS
)
1824 return; /* Only change between these two styles */
1826 n
= view_count_items(filer_window
->view
);
1828 if (n
>= o_filer_change_size_num
.int_value
)
1829 display_set_layout(filer_window
, SMALL_ICONS
,
1830 filer_window
->details_type
);
1832 display_set_layout(filer_window
, LARGE_ICONS
,
1833 filer_window
->details_type
);
1836 /* Append interesting information to this GString */
1837 void filer_add_tip_details(FilerWindow
*filer_window
,
1838 GString
*tip
, DirItem
*item
)
1840 const guchar
*fullpath
= NULL
;
1842 fullpath
= make_path(filer_window
->real_path
, item
->leafname
);
1844 if (item
->flags
& ITEM_FLAG_SYMLINK
)
1848 target
= readlink_dup(fullpath
);
1851 ensure_utf8(&target
);
1853 g_string_append(tip
, _("Symbolic link to "));
1854 g_string_append(tip
, target
);
1855 g_string_append_c(tip
, '\n');
1860 if (item
->flags
& ITEM_FLAG_APPDIR
)
1865 info
= appinfo_get(fullpath
, item
);
1866 if (info
&& ((node
= xml_get_section(info
, NULL
, "Summary"))))
1869 str
= xmlNodeListGetString(node
->doc
,
1870 node
->xmlChildrenNode
, 1);
1873 g_string_append(tip
, str
);
1874 g_string_append_c(tip
, '\n');
1879 g_object_unref(info
);
1882 if (!g_utf8_validate(item
->leafname
, -1, NULL
))
1883 g_string_append(tip
,
1884 _("This filename is not valid UTF-8. "
1885 "You should rename it.\n"));