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 */
33 #include <sys/param.h>
37 #include <gdk/gdkkeysyms.h>
41 #include "collection.h"
46 #include "gui_support.h"
57 #include "minibuffer.h"
64 #include "view_iface.h"
65 #include "view_collection.h"
67 static XMLwrapper
*groups
= NULL
;
69 FilerWindow
*window_with_focus
= NULL
;
70 GList
*all_filer_windows
= NULL
;
72 static FilerWindow
*window_with_primary
= NULL
;
74 /* Static prototypes */
75 static void attach(FilerWindow
*filer_window
);
76 static void detach(FilerWindow
*filer_window
);
77 static void filer_window_destroyed(GtkWidget
*widget
,
78 FilerWindow
*filer_window
);
79 static void update_display(Directory
*dir
,
82 FilerWindow
*filer_window
);
83 static void set_scanning_display(FilerWindow
*filer_window
, gboolean scanning
);
84 static gboolean
may_rescan(FilerWindow
*filer_window
, gboolean warning
);
85 static gboolean
minibuffer_show_cb(FilerWindow
*filer_window
);
86 static FilerWindow
*find_filer_window(const char *sym_path
, FilerWindow
*diff
);
87 static void filer_add_widgets(FilerWindow
*filer_window
);
88 static void filer_add_signals(FilerWindow
*filer_window
);
89 static void filer_size_for(FilerWindow
*filer_window
,
90 int w
, int h
, int n
, gboolean allow_shrink
);
92 static void set_selection_state(FilerWindow
*filer_window
, gboolean normal
);
93 static void filer_next_thumb(GObject
*window
, const gchar
*path
);
94 static void start_thumb_scanning(FilerWindow
*filer_window
);
95 static void filer_options_changed(void);
96 static void set_style_by_number_of_items(FilerWindow
*filer_window
);
98 static GdkCursor
*busy_cursor
= NULL
;
99 static GdkCursor
*crosshair
= NULL
;
101 /* Indicates whether the filer's display is different to the machine it
102 * is actually running on.
104 static gboolean not_local
= FALSE
;
106 static Option o_filer_change_size
, o_filer_change_size_num
;
107 static Option o_filer_size_limit
, o_short_flag_names
;
108 Option o_filer_auto_resize
, o_unique_filer_windows
;
110 void filer_init(void)
114 gchar
*dpyhost
, *tmp
;
116 option_add_int(&o_filer_size_limit
, "filer_size_limit", 75);
117 option_add_int(&o_filer_auto_resize
, "filer_auto_resize",
119 option_add_int(&o_unique_filer_windows
, "filer_unique_windows", 0);
121 option_add_int(&o_short_flag_names
, "filer_short_flag_names", FALSE
);
123 option_add_int(&o_filer_change_size
, "filer_change_size", TRUE
);
124 option_add_int(&o_filer_change_size_num
, "filer_change_size_num", 30);
126 option_add_notify(filer_options_changed
);
128 busy_cursor
= gdk_cursor_new(GDK_WATCH
);
129 crosshair
= gdk_cursor_new(GDK_CROSSHAIR
);
131 /* Is the display on the local machine, or are we being
132 * run remotely? See filer_set_title().
134 ohost
= our_host_name();
135 dpy
= gdk_get_display();
136 dpyhost
= g_strdup(dpy
);
137 tmp
= strchr(dpyhost
, ':');
141 if (dpyhost
[0] && strcmp(ohost
, dpyhost
) != 0)
143 /* Try the cannonical name for dpyhost (see our_host_name()
148 ent
= gethostbyname(dpyhost
);
149 if (!ent
|| strcmp(ohost
, ent
->h_name
) != 0)
156 static gboolean
if_deleted(gpointer item
, gpointer removed
)
158 int i
= ((GPtrArray
*) removed
)->len
;
159 DirItem
**r
= (DirItem
**) ((GPtrArray
*) removed
)->pdata
;
160 char *leafname
= ((DirItem
*) item
)->leafname
;
164 if (strcmp(leafname
, r
[i
]->leafname
) == 0)
171 /* Resize the filer window to w x h pixels, plus border (not clamped) */
172 static void filer_window_set_size(FilerWindow
*filer_window
,
174 gboolean allow_shrink
)
178 g_return_if_fail(filer_window
!= NULL
);
180 if (filer_window
->scrollbar
)
181 w
+= filer_window
->scrollbar
->allocation
.width
;
183 if (o_toolbar
.int_value
!= TOOLBAR_NONE
)
184 h
+= filer_window
->toolbar
->allocation
.height
;
185 if (filer_window
->message
)
186 h
+= filer_window
->message
->allocation
.height
;
188 window
= filer_window
->window
;
190 if (GTK_WIDGET_VISIBLE(window
))
193 GtkRequisition
*req
= &window
->requisition
;
194 GdkWindow
*gdk_window
= window
->window
;
196 w
= MAX(req
->width
, w
);
197 h
= MAX(req
->height
, h
);
198 gdk_window_get_position(gdk_window
, &x
, &y
);
204 gdk_drawable_get_size(gdk_window
, &old_w
, &old_h
);
208 if (w
== old_w
&& h
== old_h
)
212 if (x
+ w
> screen_width
|| y
+ h
> screen_height
)
214 if (x
+ w
> screen_width
)
215 x
= screen_width
- w
- 4;
216 if (y
+ h
> screen_height
)
217 y
= screen_height
- h
- 4;
218 gdk_window_move_resize(gdk_window
, x
, y
, w
, h
);
221 gdk_window_resize(gdk_window
, w
, h
);
224 gtk_window_set_default_size(GTK_WINDOW(window
), w
, h
);
227 /* Resize the window to fit the items currently in the Directory.
228 * This should be used once the Directory has been fully scanned, otherwise
229 * the window will appear too small. When opening a directory for the first
230 * time, the names will be known but not the types and images. We can
231 * still make a good estimate of the size.
233 void filer_window_autosize(FilerWindow
*filer_window
, gboolean allow_shrink
)
235 Collection
*collection
= filer_window
->collection
;
238 n
= collection
->number_of_items
;
241 filer_size_for(filer_window
,
242 collection
->item_width
,
243 collection
->item_height
,
247 /* Choose a good size for this window, assuming n items of size (w, h) */
248 static void filer_size_for(FilerWindow
*filer_window
,
249 int w
, int h
, int n
, gboolean allow_shrink
)
259 size_limit
= o_filer_size_limit
.int_value
;
261 /* Get the extra height required for the toolbar and minibuffer,
264 if (o_toolbar
.int_value
!= TOOLBAR_NONE
)
265 t
= filer_window
->toolbar
->allocation
.height
;
266 if (filer_window
->message
)
267 t
+= filer_window
->message
->allocation
.height
;
268 if (GTK_WIDGET_VISIBLE(filer_window
->minibuffer_area
))
272 gtk_widget_size_request(filer_window
->minibuffer_area
, &req
);
273 space
= req
.height
+ 2;
277 max_x
= (size_limit
* screen_width
) / 100;
278 max_rows
= (size_limit
* screen_height
) / (h
* 100);
280 /* Aim for a size where
281 * x = r(y + t + h), (1)
282 * unless that's too wide.
284 * t = toolbar (and minibuffer) height
285 * r = desired (width / height) ratio
287 * Want to display all items:
290 * => x(x/r - t - h) = nwh (from 1)
291 * => xx - x.rt - hr(1 - nw) = 0
292 * => 2x = rt +/- sqrt(rt.rt + 4hr(nw - 1))
296 * sqrt(rt.rt + ...) > rt
298 * So, the +/- must be +:
300 * => x = (rt + sqrt(rt.rt + 4hr(nw - 1))) / 2
302 * ( + w - 1 to round up)
304 x
= (r
* t
+ sqrt(r
*r
*t
*t
+ 4*h
*r
* (n
*w
- 1))) / 2 + w
- 1;
313 /* Choose rows to display all items given our chosen x.
314 * Don't make the window *too* big!
316 rows
= (n
+ cols
- 1) / cols
;
320 /* Leave some room for extra icons, but only in Small Icons mode
321 * otherwise it takes up too much space.
322 * Also, don't add space if the minibuffer is open.
325 space
= filer_window
->display_style
== SMALL_ICONS
? h
: 2;
327 filer_window_set_size(filer_window
,
329 h
* MAX(rows
, 1) + space
,
333 /* Called on a timeout while scanning or when scanning ends
334 * (whichever happens first).
336 static gint
open_filer_window(FilerWindow
*filer_window
)
338 view_style_changed(VIEW(filer_window
->view
), 0);
340 if (filer_window
->open_timeout
)
342 gtk_timeout_remove(filer_window
->open_timeout
);
343 filer_window
->open_timeout
= 0;
346 if (!GTK_WIDGET_VISIBLE(filer_window
->window
))
348 set_style_by_number_of_items(filer_window
);
349 filer_window_autosize(filer_window
, TRUE
);
350 gtk_widget_show(filer_window
->window
);
356 static void update_display(Directory
*dir
,
359 FilerWindow
*filer_window
)
361 Collection
*collection
= filer_window
->collection
;
362 ViewIface
*view
= (ViewIface
*) filer_window
->view
;
367 view_add_items(view
, items
);
368 /* Open and resize if currently hidden */
369 open_filer_window(filer_window
);
372 view_delete_if(view
, if_deleted
, items
);
375 set_scanning_display(filer_window
, TRUE
);
376 toolbar_update_info(filer_window
);
379 if (filer_window
->window
->window
)
380 gdk_window_set_cursor(
381 filer_window
->window
->window
,
383 set_scanning_display(filer_window
, FALSE
);
384 toolbar_update_info(filer_window
);
385 open_filer_window(filer_window
);
387 if (filer_window
->had_cursor
&&
388 collection
->cursor_item
== -1)
390 collection_set_cursor_item(collection
, 0);
391 filer_window
->had_cursor
= FALSE
;
393 if (filer_window
->auto_select
)
394 display_set_autoselect(filer_window
,
395 filer_window
->auto_select
);
396 g_free(filer_window
->auto_select
);
397 filer_window
->auto_select
= NULL
;
399 filer_create_thumbs(filer_window
);
401 if (filer_window
->thumb_queue
)
402 start_thumb_scanning(filer_window
);
405 view_update_items(view
, items
);
410 static void attach(FilerWindow
*filer_window
)
412 gdk_window_set_cursor(filer_window
->window
->window
, busy_cursor
);
413 view_clear(VIEW(filer_window
->view
));
414 filer_window
->scanning
= TRUE
;
415 dir_attach(filer_window
->directory
, (DirCallback
) update_display
,
417 filer_set_title(filer_window
);
420 static void detach(FilerWindow
*filer_window
)
422 g_return_if_fail(filer_window
->directory
!= NULL
);
424 dir_detach(filer_window
->directory
,
425 (DirCallback
) update_display
, filer_window
);
426 g_object_unref(filer_window
->directory
);
427 filer_window
->directory
= NULL
;
430 static void filer_window_destroyed(GtkWidget
*widget
,
431 FilerWindow
*filer_window
)
433 all_filer_windows
= g_list_remove(all_filer_windows
, filer_window
);
435 g_object_set_data(G_OBJECT(widget
), "filer_window", NULL
);
437 if (window_with_primary
== filer_window
)
438 window_with_primary
= NULL
;
440 if (window_with_focus
== filer_window
)
443 window_with_focus
= NULL
;
446 if (filer_window
->directory
)
447 detach(filer_window
);
449 if (filer_window
->open_timeout
)
451 gtk_timeout_remove(filer_window
->open_timeout
);
452 filer_window
->open_timeout
= 0;
455 if (filer_window
->thumb_queue
)
457 g_list_foreach(filer_window
->thumb_queue
, (GFunc
) g_free
, NULL
);
458 g_list_free(filer_window
->thumb_queue
);
463 g_free(filer_window
->auto_select
);
464 g_free(filer_window
->real_path
);
465 g_free(filer_window
->sym_path
);
466 g_free(filer_window
);
471 /* Returns TRUE iff the directory still exists. */
472 static gboolean
may_rescan(FilerWindow
*filer_window
, gboolean warning
)
476 g_return_val_if_fail(filer_window
!= NULL
, FALSE
);
478 /* We do a fresh lookup (rather than update) because the inode may
481 dir
= g_fscache_lookup(dir_cache
, filer_window
->real_path
);
485 info_message(_("Directory missing/deleted"));
486 gtk_widget_destroy(filer_window
->window
);
489 if (dir
== filer_window
->directory
)
493 detach(filer_window
);
494 filer_window
->directory
= dir
;
495 attach(filer_window
);
501 /* No items are now selected. This might be because another app claimed
502 * the selection or because the user unselected all the items.
504 void filer_lost_selection(FilerWindow
*filer_window
, gint time
)
506 if (window_with_primary
== filer_window
)
508 window_with_primary
= NULL
;
509 gtk_selection_owner_set(NULL
, GDK_SELECTION_PRIMARY
, time
);
513 /* Another app has claimed the primary selection */
514 void filer_lost_primary(FilerWindow
*filer_window
)
516 if (window_with_primary
&& window_with_primary
== filer_window
)
518 window_with_primary
= NULL
;
519 set_selection_state(filer_window
, FALSE
);
523 /* Someone wants us to send them the selection */
524 static void selection_get(GtkWidget
*widget
,
525 GtkSelectionData
*selection_data
,
530 GString
*reply
, *header
;
531 FilerWindow
*filer_window
;
533 Collection
*collection
;
535 filer_window
= g_object_get_data(G_OBJECT(widget
), "filer_window");
537 reply
= g_string_new(NULL
);
538 header
= g_string_new(NULL
);
543 g_string_sprintf(header
, " %s",
544 make_path(filer_window
->sym_path
, "")->str
);
546 case TARGET_URI_LIST
:
547 g_string_sprintf(header
, " file://%s%s",
548 our_host_name_for_dnd(),
549 make_path(filer_window
->sym_path
, "")->str
);
553 collection
= filer_window
->collection
;
554 for (i
= 0; i
< collection
->number_of_items
; i
++)
556 if (collection
->items
[i
].selected
)
559 (DirItem
*) collection
->items
[i
].data
;
561 g_string_append(reply
, header
->str
);
562 g_string_append(reply
, item
->leafname
);
565 /* This works, but I don't think I like it... */
566 /* g_string_append_c(reply, ' '); */
569 gtk_selection_data_set(selection_data
, xa_string
,
570 8, reply
->str
+ 1, reply
->len
- 1);
573 g_warning("Attempt to paste empty selection!");
574 gtk_selection_data_set(selection_data
, xa_string
, 8, "", 0);
577 g_string_free(reply
, TRUE
);
578 g_string_free(header
, TRUE
);
581 void filer_selection_changed(FilerWindow
*filer_window
, gint time
)
583 /* Selection has been changed -- try to grab the primary selection
584 * if we don't have it.
586 if (window_with_primary
== filer_window
)
587 return; /* Already got it */
589 if (!filer_window
->collection
->number_selected
)
590 return; /* Nothing selected */
592 if (filer_window
->temp_item_selected
== FALSE
&&
593 gtk_selection_owner_set(GTK_WIDGET(filer_window
->collection
),
594 GDK_SELECTION_PRIMARY
,
597 window_with_primary
= filer_window
;
598 set_selection_state(filer_window
, TRUE
);
601 set_selection_state(filer_window
, FALSE
);
604 /* Open the item (or add it to the shell command minibuffer) */
605 void filer_openitem(FilerWindow
*filer_window
, int item_number
, OpenFlags flags
)
607 gboolean shift
= (flags
& OPEN_SHIFT
) != 0;
608 gboolean close_mini
= flags
& OPEN_FROM_MINI
;
609 gboolean close_window
= (flags
& OPEN_CLOSE_WINDOW
) != 0;
610 DirItem
*item
= (DirItem
*)
611 filer_window
->collection
->items
[item_number
].data
;
613 gboolean wink
= TRUE
;
616 if (filer_window
->mini_type
== MINI_SHELL
)
618 minibuffer_add(filer_window
, item
->leafname
);
623 dir_update_item(filer_window
->directory
, item
->leafname
);
625 if (item
->base_type
== TYPE_DIRECTORY
)
627 /* Never close a filer window when opening a directory
628 * (click on a dir or click on an app with shift).
630 if (shift
|| !(item
->flags
& ITEM_FLAG_APPDIR
))
631 close_window
= FALSE
;
634 full_path
= make_path(filer_window
->sym_path
, item
->leafname
)->str
;
635 if (shift
&& (item
->flags
& ITEM_FLAG_SYMLINK
))
638 old_dir
= filer_window
->directory
;
639 if (run_diritem(full_path
, item
,
640 flags
& OPEN_SAME_WINDOW
? filer_window
: NULL
,
644 if (old_dir
!= filer_window
->directory
)
648 gtk_widget_destroy(filer_window
->window
);
652 collection_wink_item(filer_window
->collection
,
655 minibuffer_hide(filer_window
);
660 static gint
pointer_in(GtkWidget
*widget
,
661 GdkEventCrossing
*event
,
662 FilerWindow
*filer_window
)
664 may_rescan(filer_window
, TRUE
);
668 static gint
pointer_out(GtkWidget
*widget
,
669 GdkEventCrossing
*event
,
670 FilerWindow
*filer_window
)
676 /* Move the cursor to the next selected item in direction 'dir'
679 static void next_selected(FilerWindow
*filer_window
, int dir
)
681 Collection
*collection
= filer_window
->collection
;
682 int to_check
= collection
->number_of_items
;
683 int item
= collection
->cursor_item
;
685 g_return_if_fail(dir
== 1 || dir
== -1);
687 if (to_check
> 0 && item
== -1)
689 /* Cursor not currently on */
693 item
= collection
->number_of_items
- 1;
695 if (collection
->items
[item
].selected
)
699 while (--to_check
> 0)
703 if (item
>= collection
->number_of_items
)
706 item
= collection
->number_of_items
- 1;
708 if (collection
->items
[item
].selected
)
715 collection_set_cursor_item(collection
, item
);
718 static void return_pressed(FilerWindow
*filer_window
, GdkEventKey
*event
)
720 Collection
*collection
= filer_window
->collection
;
721 int item
= collection
->cursor_item
;
722 TargetFunc cb
= filer_window
->target_cb
;
723 gpointer data
= filer_window
->target_data
;
724 OpenFlags flags
= OPEN_SAME_WINDOW
;
726 filer_target_mode(filer_window
, NULL
, NULL
, NULL
);
727 if (item
< 0 || item
>= collection
->number_of_items
)
732 cb(filer_window
, item
, data
);
736 if (event
->state
& GDK_SHIFT_MASK
)
739 filer_openitem(filer_window
, item
, flags
);
742 /* Makes sure that 'groups' is up-to-date, reloading from file if it has
743 * changed. If no groups were loaded and there is no file then initialised
744 * groups to an empty document.
745 * Return the node for the 'name' group.
747 static xmlNode
*group_find(char *name
)
752 /* Update the groups, if possible */
753 path
= choices_find_path_load("Groups.xml", PROJECT
);
757 wrapper
= xml_cache_load(path
);
761 g_object_unref(groups
);
770 groups
= xml_new(NULL
);
771 groups
->doc
= xmlNewDoc("1.0");
773 xmlDocSetRootElement(groups
->doc
,
774 xmlNewDocNode(groups
->doc
, NULL
, "groups", NULL
));
778 node
= xmlDocGetRootElement(groups
->doc
);
780 for (node
= node
->xmlChildrenNode
; node
; node
= node
->next
)
784 gid
= xmlGetProp(node
, "name");
789 if (strcmp(name
, gid
) != 0)
800 static void group_save(FilerWindow
*filer_window
, char *name
)
802 Collection
*collection
= filer_window
->collection
;
807 group
= group_find(name
);
810 xmlUnlinkNode(group
);
813 group
= xmlNewChild(xmlDocGetRootElement(groups
->doc
),
814 NULL
, "group", NULL
);
815 xmlSetProp(group
, "name", name
);
817 xmlNewChild(group
, NULL
, "directory", filer_window
->sym_path
);
819 for (i
= 0; i
< collection
->number_of_items
; i
++)
821 DirItem
*item
= (DirItem
*) collection
->items
[i
].data
;
822 gchar
*u8_leaf
= item
->leafname
;
824 if (!collection
->items
[i
].selected
)
827 xmlNewChild(group
, NULL
, "item", u8_leaf
);
830 save_path
= choices_find_path_save("Groups.xml", PROJECT
, TRUE
);
833 save_xml_file(groups
->doc
, save_path
);
838 static void group_restore(FilerWindow
*filer_window
, char *name
)
840 GHashTable
*in_group
;
841 Collection
*collection
= filer_window
->collection
;
844 xmlNode
*group
, *node
;
846 group
= group_find(name
);
850 report_error(_("Group %s is not set. Select some files "
851 "and press Ctrl+%s to set the group. Press %s "
852 "on its own to reselect the files later."),
857 node
= get_subnode(group
, NULL
, "directory");
858 g_return_if_fail(node
!= NULL
);
859 path
= xmlNodeListGetString(groups
->doc
, node
->xmlChildrenNode
, 1);
860 g_return_if_fail(path
!= NULL
);
862 if (strcmp(path
, filer_window
->sym_path
) != 0)
863 filer_change_to(filer_window
, path
, NULL
);
866 /* If an item at the start is selected then we could lose the
867 * primary selection after checking that item and then need to
868 * gain it again at the end. Therefore, if anything is selected
869 * then select the last item until the end of the search.
871 n
= collection
->number_of_items
;
872 if (collection
->number_selected
)
873 collection_select_item(collection
, n
- 1);
875 in_group
= g_hash_table_new(g_str_hash
, g_str_equal
);
876 for (node
= group
->xmlChildrenNode
; node
; node
= node
->next
)
879 if (node
->type
!= XML_ELEMENT_NODE
)
881 if (strcmp(node
->name
, "item") != 0)
884 leaf
= xmlNodeListGetString(groups
->doc
,
885 node
->xmlChildrenNode
, 1);
887 g_warning("Missing leafname!\n");
889 g_hash_table_insert(in_group
, leaf
, filer_window
);
892 for (j
= 0; j
< n
; j
++)
894 DirItem
*item
= (DirItem
*) collection
->items
[j
].data
;
896 if (g_hash_table_lookup(in_group
, item
->leafname
))
897 collection_select_item(collection
, j
);
899 collection_unselect_item(collection
, j
);
902 g_hash_table_foreach(in_group
, (GHFunc
) g_free
, NULL
);
903 g_hash_table_destroy(in_group
);
906 /* Handle keys that can't be bound with the menu */
907 static gint
key_press_event(GtkWidget
*widget
,
909 FilerWindow
*filer_window
)
912 guint key
= event
->keyval
;
915 window_with_focus
= filer_window
;
917 /* Note: Not convinced this is the way Gtk's key system is supposed
921 ensure_filer_menu(); /* Gets the keys working... */
922 gtk_window_add_accel_group(GTK_WINDOW(filer_window
->window
),
924 handled
= gtk_accel_groups_activate(G_OBJECT(filer_window
->window
),
925 event
->keyval
, event
->state
);
926 if (window_with_focus
)
927 gtk_window_remove_accel_group(GTK_WINDOW(filer_window
->window
),
930 return TRUE
; /* Window no longer exists */
937 filer_target_mode(filer_window
, NULL
, NULL
, NULL
);
940 return_pressed(filer_window
, event
);
942 case GDK_ISO_Left_Tab
:
943 next_selected(filer_window
, -1);
946 next_selected(filer_window
, 1);
949 change_to_parent(filer_window
);
953 show_filer_menu(filer_window
, (GdkEvent
*) event
,
954 filer_window
->collection
->cursor_item
);
957 if (key
>= GDK_0
&& key
<= GDK_9
)
958 group
[0] = key
- GDK_0
+ '0';
959 else if (key
>= GDK_KP_0
&& key
<= GDK_KP_9
)
960 group
[0] = key
- GDK_KP_0
+ '0';
965 if (event
->state
& GDK_CONTROL_MASK
)
966 group_save(filer_window
, group
);
968 group_restore(filer_window
, group
);
974 void filer_open_parent(FilerWindow
*filer_window
)
977 const char *current
= filer_window
->sym_path
;
979 if (current
[0] == '/' && current
[1] == '\0')
980 return; /* Already in the root */
982 dir
= g_dirname(current
);
983 filer_opendir(dir
, filer_window
);
987 void change_to_parent(FilerWindow
*filer_window
)
990 const char *current
= filer_window
->sym_path
;
992 if (current
[0] == '/' && current
[1] == '\0')
993 return; /* Already in the root */
995 dir
= g_dirname(current
);
996 filer_change_to(filer_window
, dir
, g_basename(current
));
1000 /* Removes trailing /s from path (modified in place) */
1001 static void tidy_sympath(gchar
*path
)
1005 g_return_if_fail(path
!= NULL
);
1008 while (l
> 1 && path
[l
- 1] == '/')
1015 /* Make filer_window display path. When finished, highlight item 'from', or
1016 * the first item if from is NULL. If there is currently no cursor then
1017 * simply wink 'from' (if not NULL).
1019 void filer_change_to(FilerWindow
*filer_window
,
1020 const char *path
, const char *from
)
1023 char *sym_path
, *real_path
;
1026 g_return_if_fail(filer_window
!= NULL
);
1028 filer_cancel_thumbnails(filer_window
);
1032 sym_path
= g_strdup(path
);
1033 real_path
= pathdup(path
);
1034 new_dir
= g_fscache_lookup(dir_cache
, real_path
);
1038 delayed_error(_("Directory '%s' is not accessible"),
1045 if (o_unique_filer_windows
.int_value
)
1049 fw
= find_filer_window(sym_path
, filer_window
);
1051 gtk_widget_destroy(fw
->window
);
1054 from_dup
= from
&& *from
? g_strdup(from
) : NULL
;
1056 detach(filer_window
);
1057 g_free(filer_window
->real_path
);
1058 g_free(filer_window
->sym_path
);
1059 filer_window
->real_path
= real_path
;
1060 filer_window
->sym_path
= sym_path
;
1061 tidy_sympath(filer_window
->sym_path
);
1063 filer_window
->directory
= new_dir
;
1065 g_free(filer_window
->auto_select
);
1066 filer_window
->had_cursor
= filer_window
->collection
->cursor_item
!= -1
1067 || filer_window
->had_cursor
;
1068 filer_window
->auto_select
= from_dup
;
1070 filer_set_title(filer_window
);
1071 if (filer_window
->window
->window
)
1072 gdk_window_set_role(filer_window
->window
->window
,
1073 filer_window
->sym_path
);
1074 collection_set_cursor_item(filer_window
->collection
, -1);
1076 attach(filer_window
);
1078 set_style_by_number_of_items(filer_window
);
1080 if (o_filer_auto_resize
.int_value
== RESIZE_ALWAYS
)
1081 filer_window_autosize(filer_window
, TRUE
);
1083 if (filer_window
->mini_type
== MINI_PATH
)
1084 gtk_idle_add((GtkFunction
) minibuffer_show_cb
,
1088 /* Returns a list containing the full (sym) pathname of every selected item.
1089 * You must g_free() each item in the list.
1091 GList
*filer_selected_items(FilerWindow
*filer_window
)
1093 Collection
*collection
= filer_window
->collection
;
1094 GList
*retval
= NULL
;
1095 guchar
*dir
= filer_window
->sym_path
;
1098 for (i
= 0; i
< collection
->number_of_items
; i
++)
1100 if (collection
->items
[i
].selected
)
1102 DirItem
*item
= (DirItem
*) collection
->items
[i
].data
;
1104 retval
= g_list_prepend(retval
,
1105 g_strdup(make_path(dir
, item
->leafname
)->str
));
1109 return g_list_reverse(retval
);
1112 DirItem
*filer_selected_item(FilerWindow
*filer_window
)
1114 Collection
*collection
= filer_window
->collection
;
1117 item
= collection_selected_item_number(collection
);
1120 return (DirItem
*) collection
->items
[item
].data
;
1124 /* Creates and shows a new filer window.
1125 * If src_win != NULL then display options can be taken from that source window.
1126 * Returns the new filer window, or NULL on error.
1127 * Note: if unique windows is in use, may return an existing window.
1129 FilerWindow
*filer_opendir(const char *path
, FilerWindow
*src_win
)
1131 FilerWindow
*filer_window
;
1133 DisplayStyle dstyle
;
1136 /* Get the real pathname of the directory and copy it */
1137 real_path
= pathdup(path
);
1139 if (o_unique_filer_windows
.int_value
)
1141 FilerWindow
*same_dir_window
;
1143 same_dir_window
= find_filer_window(path
, NULL
);
1145 if (same_dir_window
)
1147 gtk_window_present(GTK_WINDOW(same_dir_window
->window
));
1148 return same_dir_window
;
1152 filer_window
= g_new(FilerWindow
, 1);
1153 filer_window
->message
= NULL
;
1154 filer_window
->minibuffer
= NULL
;
1155 filer_window
->minibuffer_label
= NULL
;
1156 filer_window
->minibuffer_area
= NULL
;
1157 filer_window
->temp_show_hidden
= FALSE
;
1158 filer_window
->sym_path
= g_strdup(path
);
1159 filer_window
->real_path
= real_path
;
1160 filer_window
->scanning
= FALSE
;
1161 filer_window
->had_cursor
= FALSE
;
1162 filer_window
->auto_select
= NULL
;
1163 filer_window
->toolbar_text
= NULL
;
1164 filer_window
->target_cb
= NULL
;
1165 filer_window
->mini_type
= MINI_NONE
;
1166 filer_window
->selection_state
= GTK_STATE_INSENSITIVE
;
1167 filer_window
->toolbar
= NULL
;
1168 filer_window
->toplevel_vbox
= NULL
;
1170 tidy_sympath(filer_window
->sym_path
);
1172 /* Finds the entry for this directory in the dir cache, creating
1173 * a new one if needed. This does not cause a scan to start,
1174 * so if a new entry is created then it will be empty.
1176 filer_window
->directory
= g_fscache_lookup(dir_cache
, real_path
);
1177 if (!filer_window
->directory
)
1179 delayed_error(_("Directory '%s' not found."), path
);
1180 g_free(filer_window
->real_path
);
1181 g_free(filer_window
->sym_path
);
1182 g_free(filer_window
);
1186 filer_window
->temp_item_selected
= FALSE
;
1187 filer_window
->flags
= (FilerFlags
) 0;
1188 filer_window
->details_type
= DETAILS_SUMMARY
;
1189 filer_window
->display_style
= UNKNOWN_STYLE
;
1190 filer_window
->thumb_queue
= NULL
;
1191 filer_window
->max_thumbs
= 0;
1193 if (src_win
&& o_display_inherit_options
.int_value
)
1195 filer_window
->sort_fn
= src_win
->sort_fn
;
1196 dstyle
= src_win
->display_style
;
1197 dtype
= src_win
->details_type
;
1198 filer_window
->show_hidden
= src_win
->show_hidden
;
1199 filer_window
->show_thumbs
= src_win
->show_thumbs
;
1203 int i
= o_display_sort_by
.int_value
;
1204 filer_window
->sort_fn
= i
== 0 ? sort_by_name
:
1205 i
== 1 ? sort_by_type
:
1206 i
== 2 ? sort_by_date
:
1209 dstyle
= o_display_size
.int_value
;
1210 dtype
= o_display_details
.int_value
;
1211 filer_window
->show_hidden
=
1212 o_display_show_hidden
.int_value
;
1213 filer_window
->show_thumbs
=
1214 o_display_show_thumbs
.int_value
;
1217 /* Add all the user-interface elements & realise */
1218 filer_add_widgets(filer_window
);
1220 gtk_window_set_position(GTK_WINDOW(filer_window
->window
),
1223 /* Connect to all the signal handlers */
1224 filer_add_signals(filer_window
);
1226 display_set_layout(filer_window
, dstyle
, dtype
);
1228 /* Open the window after a timeout, or when scanning stops.
1229 * Do this before attaching, because attach() might tell us to
1230 * stop scanning (if a scan isn't needed).
1232 filer_window
->open_timeout
= gtk_timeout_add(500,
1233 (GtkFunction
) open_filer_window
,
1236 /* The collection is created empty and then attach() is called, which
1237 * links the filer window to the entry in the directory cache we
1238 * looked up / created above.
1240 * The attach() function will immediately callback to the filer window
1241 * to deliver a list of all known entries in the directory (so,
1242 * collection->number_of_items may be valid after the call to
1243 * attach() returns).
1245 * If the directory was not in the cache (because it hadn't been
1246 * opened it before) then the types and icons for the entries are
1247 * not know, but the list of names is.
1250 attach(filer_window
);
1252 number_of_windows
++;
1253 all_filer_windows
= g_list_prepend(all_filer_windows
, filer_window
);
1255 return filer_window
;
1258 /* This adds all the widgets to a new filer window. It is in a separate
1259 * function because filer_opendir() was getting too long...
1261 static void filer_add_widgets(FilerWindow
*filer_window
)
1263 GtkWidget
*hbox
, *vbox
, *collection
;
1265 /* Create the top-level window widget */
1266 filer_window
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
1267 filer_set_title(filer_window
);
1269 /* This property is cleared when the window is destroyed.
1270 * You can thus ref filer_window->window and use this to see
1271 * if the window no longer exists.
1273 g_object_set_data(G_OBJECT(filer_window
->window
),
1274 "filer_window", filer_window
);
1276 /* The view is the area that actually displays the files */
1277 filer_window
->view
= view_collection_new(filer_window
);
1278 gtk_widget_show(filer_window
->view
);
1279 collection
= GTK_WIDGET(filer_window
->collection
); /* XXX */
1280 g_object_set_data(G_OBJECT(collection
), "filer_window", filer_window
);
1282 /* Scrollbar on the right, everything else on the left */
1283 hbox
= gtk_hbox_new(FALSE
, 0);
1284 gtk_container_add(GTK_CONTAINER(filer_window
->window
), hbox
);
1286 vbox
= gtk_vbox_new(FALSE
, 0);
1287 gtk_box_pack_start_defaults(GTK_BOX(hbox
), vbox
);
1288 filer_window
->toplevel_vbox
= GTK_BOX(vbox
);
1290 /* If we want a toolbar, create it now */
1291 toolbar_update_toolbar(filer_window
);
1293 /* If there's a message that should be displayed in each window (eg
1294 * 'Running as root'), add it here.
1296 if (show_user_message
)
1298 filer_window
->message
= gtk_label_new(show_user_message
);
1299 gtk_box_pack_start(GTK_BOX(vbox
), filer_window
->message
,
1301 gtk_widget_show(filer_window
->message
);
1304 /* Now add the area for displaying the files.
1305 * The collection is one huge window that goes in a Viewport.
1307 gtk_box_pack_start_defaults(GTK_BOX(vbox
), filer_window
->view
);
1308 filer_window
->scrollbar
=
1309 gtk_vscrollbar_new(filer_window
->collection
->vadj
);
1311 /* And the minibuffer (hidden to start with) */
1312 create_minibuffer(filer_window
);
1313 gtk_box_pack_start(GTK_BOX(vbox
), filer_window
->minibuffer_area
,
1316 /* And the thumbnail progress bar (also hidden) */
1320 filer_window
->thumb_bar
= gtk_hbox_new(FALSE
, 2);
1321 gtk_box_pack_start(GTK_BOX(vbox
), filer_window
->thumb_bar
,
1324 filer_window
->thumb_progress
= gtk_progress_bar_new();
1326 gtk_box_pack_start(GTK_BOX(filer_window
->thumb_bar
),
1327 filer_window
->thumb_progress
, TRUE
, TRUE
, 0);
1329 cancel
= gtk_button_new_with_label(_("Cancel"));
1330 GTK_WIDGET_UNSET_FLAGS(cancel
, GTK_CAN_FOCUS
);
1331 gtk_box_pack_start(GTK_BOX(filer_window
->thumb_bar
),
1332 cancel
, FALSE
, TRUE
, 0);
1333 g_signal_connect_swapped(cancel
, "clicked",
1334 G_CALLBACK(filer_cancel_thumbnails
),
1338 /* Put the scrollbar on the left of everything else... */
1339 gtk_box_pack_start(GTK_BOX(hbox
),
1340 filer_window
->scrollbar
, FALSE
, TRUE
, 0);
1342 gtk_window_set_focus(GTK_WINDOW(filer_window
->window
), collection
);
1344 gtk_widget_show(hbox
);
1345 gtk_widget_show(vbox
);
1346 gtk_widget_show(filer_window
->scrollbar
);
1347 gtk_widget_show(collection
);
1349 gtk_widget_realize(filer_window
->window
);
1351 gdk_window_set_role(filer_window
->window
->window
,
1352 filer_window
->sym_path
);
1354 filer_window_set_size(filer_window
, 4, 4, TRUE
);
1357 static void filer_add_signals(FilerWindow
*filer_window
)
1359 GtkObject
*collection
= GTK_OBJECT(filer_window
->collection
);
1360 GtkTargetEntry target_table
[] =
1362 {"text/uri-list", 0, TARGET_URI_LIST
},
1363 {"STRING", 0, TARGET_STRING
},
1364 {"COMPOUND_TEXT", 0, TARGET_STRING
},/* XXX: Treats as STRING */
1367 /* Events on the top-level window */
1368 gtk_widget_add_events(filer_window
->window
, GDK_ENTER_NOTIFY
);
1369 g_signal_connect(filer_window
->window
, "enter-notify-event",
1370 G_CALLBACK(pointer_in
), filer_window
);
1371 g_signal_connect(filer_window
->window
, "leave-notify-event",
1372 G_CALLBACK(pointer_out
), filer_window
);
1373 g_signal_connect(filer_window
->window
, "destroy",
1374 G_CALLBACK(filer_window_destroyed
), filer_window
);
1376 /* Events on the collection widget */
1377 gtk_widget_set_events(GTK_WIDGET(collection
),
1378 GDK_BUTTON1_MOTION_MASK
| GDK_BUTTON2_MOTION_MASK
|
1379 GDK_BUTTON3_MOTION_MASK
| GDK_POINTER_MOTION_MASK
|
1380 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
);
1382 g_signal_connect(collection
, "selection_get",
1383 G_CALLBACK(selection_get
), NULL
);
1384 gtk_selection_add_targets(GTK_WIDGET(collection
), GDK_SELECTION_PRIMARY
,
1386 sizeof(target_table
) / sizeof(*target_table
));
1388 g_signal_connect(collection
, "key_press_event",
1389 G_CALLBACK(key_press_event
), filer_window
);
1391 /* Drag and drop events */
1392 g_signal_connect(collection
, "drag_data_get",
1393 GTK_SIGNAL_FUNC(drag_data_get
), NULL
);
1394 drag_set_dest(filer_window
);
1397 static gint
clear_scanning_display(FilerWindow
*filer_window
)
1399 if (filer_exists(filer_window
))
1400 filer_set_title(filer_window
);
1404 static void set_scanning_display(FilerWindow
*filer_window
, gboolean scanning
)
1406 if (scanning
== filer_window
->scanning
)
1408 filer_window
->scanning
= scanning
;
1411 filer_set_title(filer_window
);
1413 gtk_timeout_add(300, (GtkFunction
) clear_scanning_display
,
1417 /* Note that filer_window may not exist after this call */
1418 void filer_update_dir(FilerWindow
*filer_window
, gboolean warning
)
1420 if (may_rescan(filer_window
, warning
))
1421 dir_update(filer_window
->directory
, filer_window
->sym_path
);
1424 void filer_update_all(void)
1426 GList
*next
= all_filer_windows
;
1430 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1432 /* Updating directory may remove it from list -- stop sending
1433 * patches to move this line!
1437 filer_update_dir(filer_window
, TRUE
);
1441 /* Refresh the various caches even if we don't think we need to */
1442 void full_refresh(void)
1447 /* See whether a filer window with a given path already exists
1448 * and is different from diff.
1450 static FilerWindow
*find_filer_window(const char *sym_path
, FilerWindow
*diff
)
1454 for (next
= all_filer_windows
; next
; next
= next
->next
)
1456 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1458 if (filer_window
!= diff
&&
1459 strcmp(sym_path
, filer_window
->sym_path
) == 0)
1460 return filer_window
;
1466 /* This path has been mounted/umounted/deleted some files - update all dirs */
1467 void filer_check_mounted(const char *real_path
)
1469 GList
*next
= all_filer_windows
;
1473 len
= strlen(real_path
);
1477 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1481 if (strncmp(real_path
, filer_window
->real_path
, len
) == 0)
1483 char s
= filer_window
->real_path
[len
];
1485 if (s
== '/' || s
== '\0')
1486 filer_update_dir(filer_window
, FALSE
);
1490 parent
= g_dirname(real_path
);
1491 refresh_dirs(parent
);
1494 icons_may_update(real_path
);
1497 /* Close all windows displaying 'path' or subdirectories of 'path' */
1498 void filer_close_recursive(const char *path
)
1500 GList
*next
= all_filer_windows
;
1504 real
= pathdup(path
);
1509 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1513 if (strncmp(real
, filer_window
->real_path
, len
) == 0)
1515 char s
= filer_window
->real_path
[len
];
1517 if (len
== 1 || s
== '/' || s
== '\0')
1518 gtk_widget_destroy(filer_window
->window
);
1523 /* Like minibuffer_show(), except that:
1524 * - It returns FALSE (to be used from an idle callback)
1525 * - It checks that the filer window still exists.
1527 static gboolean
minibuffer_show_cb(FilerWindow
*filer_window
)
1529 if (filer_exists(filer_window
))
1530 minibuffer_show(filer_window
, MINI_PATH
);
1534 /* TRUE iff filer_window points to an existing FilerWindow
1537 gboolean
filer_exists(FilerWindow
*filer_window
)
1541 for (next
= all_filer_windows
; next
; next
= next
->next
)
1543 FilerWindow
*fw
= (FilerWindow
*) next
->data
;
1545 if (fw
== filer_window
)
1552 /* Make sure the window title is up-to-date */
1553 void filer_set_title(FilerWindow
*filer_window
)
1555 guchar
*title
= NULL
;
1558 if (filer_window
->scanning
|| filer_window
->show_hidden
||
1559 filer_window
->show_thumbs
)
1561 if (o_short_flag_names
.int_value
)
1563 flags
= g_strconcat(" +",
1564 filer_window
->scanning
? _("S") : "",
1565 filer_window
->show_hidden
? _("A") : "",
1566 filer_window
->show_thumbs
? _("T") : "",
1571 flags
= g_strconcat(" (",
1572 filer_window
->scanning
? _("Scanning, ") : "",
1573 filer_window
->show_hidden
? _("All, ") : "",
1574 filer_window
->show_thumbs
? _("Thumbs, ") : "",
1576 flags
[strlen(flags
) - 2] = ')';
1581 title
= g_strconcat("//", our_host_name(),
1582 filer_window
->sym_path
, flags
, NULL
);
1584 if (!title
&& home_dir_len
> 1 &&
1585 strncmp(filer_window
->sym_path
, home_dir
, home_dir_len
) == 0)
1587 guchar sep
= filer_window
->sym_path
[home_dir_len
];
1589 if (sep
== '\0' || sep
== '/')
1590 title
= g_strconcat("~",
1591 filer_window
->sym_path
+ home_dir_len
,
1597 title
= g_strconcat(filer_window
->sym_path
, flags
, NULL
);
1599 gtk_window_set_title(GTK_WINDOW(filer_window
->window
), title
);
1602 if (flags
[0] != '\0')
1606 /* Reconnect to the same directory (used when the Show Hidden option is
1607 * toggled). This has the side-effect of updating the window title.
1609 void filer_detach_rescan(FilerWindow
*filer_window
)
1611 Directory
*dir
= filer_window
->directory
;
1614 detach(filer_window
);
1615 filer_window
->directory
= dir
;
1616 attach(filer_window
);
1619 /* Puts the filer window into target mode. When an item is chosen,
1620 * fn(filer_window, item, data) is called. 'reason' will be displayed
1621 * on the toolbar while target mode is active.
1623 * Use fn == NULL to cancel target mode.
1625 void filer_target_mode(FilerWindow
*filer_window
,
1630 TargetFunc old_fn
= filer_window
->target_cb
;
1633 gdk_window_set_cursor(
1634 GTK_WIDGET(filer_window
->collection
)->window
,
1635 fn
? crosshair
: NULL
);
1637 filer_window
->target_cb
= fn
;
1638 filer_window
->target_data
= data
;
1640 if (filer_window
->toolbar_text
== NULL
)
1645 GTK_LABEL(filer_window
->toolbar_text
), reason
);
1646 else if (o_toolbar_info
.int_value
)
1649 toolbar_update_info(filer_window
);
1652 gtk_label_set_text(GTK_LABEL(filer_window
->toolbar_text
), "");
1655 static void set_selection_state(FilerWindow
*filer_window
, gboolean normal
)
1657 GtkStateType old_state
= filer_window
->selection_state
;
1659 filer_window
->selection_state
= normal
1660 ? GTK_STATE_SELECTED
: GTK_STATE_INSENSITIVE
;
1662 if (old_state
!= filer_window
->selection_state
1663 && filer_window
->collection
->number_selected
)
1664 gtk_widget_queue_draw(GTK_WIDGET(filer_window
->collection
));
1667 void filer_cancel_thumbnails(FilerWindow
*filer_window
)
1669 gtk_widget_hide(filer_window
->thumb_bar
);
1671 g_list_foreach(filer_window
->thumb_queue
, (GFunc
) g_free
, NULL
);
1672 g_list_free(filer_window
->thumb_queue
);
1673 filer_window
->thumb_queue
= NULL
;
1674 filer_window
->max_thumbs
= 0;
1677 /* Generate the next thumb for this window. The collection object is
1678 * unref'd when there is nothing more to do.
1679 * If the collection no longer has a filer window, nothing is done.
1681 static gboolean
filer_next_thumb_real(GObject
*window
)
1683 FilerWindow
*filer_window
;
1687 filer_window
= g_object_get_data(window
, "filer_window");
1691 g_object_unref(window
);
1695 if (!filer_window
->thumb_queue
)
1697 filer_cancel_thumbnails(filer_window
);
1698 g_object_unref(window
);
1702 total
= filer_window
->max_thumbs
;
1703 done
= total
- g_list_length(filer_window
->thumb_queue
);
1705 path
= (gchar
*) filer_window
->thumb_queue
->data
;
1707 pixmap_background_thumb(path
, (GFunc
) filer_next_thumb
, window
);
1709 filer_window
->thumb_queue
= g_list_remove(filer_window
->thumb_queue
,
1713 gtk_progress_bar_set_fraction(
1714 GTK_PROGRESS_BAR(filer_window
->thumb_progress
),
1715 done
/ (float) total
);
1720 /* path is the thumb just loaded, if any.
1721 * collection is unref'd (eventually).
1723 static void filer_next_thumb(GObject
*window
, const gchar
*path
)
1726 dir_force_update_path(path
);
1728 gtk_idle_add((GtkFunction
) filer_next_thumb_real
, window
);
1731 static void start_thumb_scanning(FilerWindow
*filer_window
)
1733 if (GTK_WIDGET_VISIBLE(filer_window
->thumb_bar
))
1734 return; /* Already scanning */
1736 gtk_widget_show_all(filer_window
->thumb_bar
);
1738 g_object_ref(G_OBJECT(filer_window
->window
));
1739 filer_next_thumb(G_OBJECT(filer_window
->window
), NULL
);
1742 /* Set this image to be loaded some time in the future */
1743 void filer_create_thumb(FilerWindow
*filer_window
, const gchar
*path
)
1745 filer_window
->max_thumbs
++;
1747 filer_window
->thumb_queue
= g_list_append(filer_window
->thumb_queue
,
1750 if (filer_window
->scanning
)
1751 return; /* Will start when scan ends */
1753 start_thumb_scanning(filer_window
);
1756 /* If thumbnail display is on, look through all the items in this directory
1757 * and start creating or updating the thumbnails as needed.
1759 void filer_create_thumbs(FilerWindow
*filer_window
)
1761 Collection
*collection
= filer_window
->collection
;
1764 if (!filer_window
->show_thumbs
)
1767 for (i
= 0; i
< collection
->number_of_items
; i
++)
1769 MaskedPixmap
*pixmap
;
1770 DirItem
*item
= (DirItem
*) collection
->items
[i
].data
;
1774 if (item
->base_type
!= TYPE_FILE
)
1777 if (strcmp(item
->mime_type
->media_type
, "image") != 0)
1780 path
= make_path(filer_window
->real_path
, item
->leafname
)->str
;
1782 pixmap
= g_fscache_lookup_full(pixmap_cache
, path
,
1783 FSCACHE_LOOKUP_ONLY_NEW
, &found
);
1785 g_object_unref(pixmap
);
1787 /* If we didn't get an image, it could be because:
1789 * - We're loading the image now. found is TRUE,
1790 * and we'll update the item later.
1791 * - We tried to load the image and failed. found
1793 * - We haven't tried loading the image. found is
1794 * FALSE, and we start creating the thumb here.
1797 filer_create_thumb(filer_window
, path
);
1801 static void filer_options_changed(void)
1803 if (o_short_flag_names
.has_changed
)
1807 for (next
= all_filer_windows
; next
; next
= next
->next
)
1809 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1811 filer_set_title(filer_window
);
1816 /* Change to Large or Small icons depending on the number of items
1817 * in the directory, subject to options.
1819 static void set_style_by_number_of_items(FilerWindow
*filer_window
)
1823 g_return_if_fail(filer_window
!= NULL
);
1825 if (!o_filer_change_size
.int_value
)
1826 return; /* Don't auto-set style */
1828 if (filer_window
->display_style
!= LARGE_ICONS
&&
1829 filer_window
->display_style
!= SMALL_ICONS
)
1830 return; /* Only change between these two styles */
1832 n
= filer_window
->collection
->number_of_items
;
1834 if (n
>= o_filer_change_size_num
.int_value
)
1835 display_set_layout(filer_window
, SMALL_ICONS
,
1836 filer_window
->details_type
);
1838 display_set_layout(filer_window
, LARGE_ICONS
,
1839 filer_window
->details_type
);
1842 /* Append interesting information to this GString */
1843 void filer_add_tip_details(FilerWindow
*filer_window
,
1844 GString
*tip
, DirItem
*item
)
1846 guchar
*fullpath
= NULL
;
1848 fullpath
= make_path(filer_window
->real_path
, item
->leafname
)->str
;
1850 if (item
->flags
& ITEM_FLAG_SYMLINK
)
1854 target
= readlink_dup(fullpath
);
1857 g_string_append(tip
, _("Symbolic link to "));
1858 g_string_append(tip
, target
);
1859 g_string_append_c(tip
, '\n');
1864 if (item
->flags
& ITEM_FLAG_APPDIR
)
1869 info
= appinfo_get(fullpath
, item
);
1870 if (info
&& ((node
= xml_get_section(info
, NULL
, "Summary"))))
1873 str
= xmlNodeListGetString(node
->doc
,
1874 node
->xmlChildrenNode
, 1);
1877 g_string_append(tip
, str
);
1878 g_string_append_c(tip
, '\n');
1883 g_object_unref(info
);
1886 if (!g_utf8_validate(item
->leafname
, -1, NULL
))
1887 g_string_append(tip
,
1888 _("This filename is not valid UTF-8. "
1889 "You should rename it.\n"));