r1996: Cope slightly better with invalid filenames in various places (reported by
[rox-filer.git] / ROX-Filer / src / filer.c
blob7cc83a6b998756881610b8db68ed60434d433386
1 /*
2 * $Id$
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)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* filer.c - code for handling filer windows */
24 #include "config.h"
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <ctype.h>
31 #include <netdb.h>
32 #include <sys/param.h>
34 #include <gtk/gtk.h>
35 #include <gdk/gdkx.h>
36 #include <gdk/gdkkeysyms.h>
38 #include "global.h"
40 #include "display.h"
41 #include "main.h"
42 #include "fscache.h"
43 #include "support.h"
44 #include "gui_support.h"
45 #include "filer.h"
46 #include "choices.h"
47 #include "pixmaps.h"
48 #include "menu.h"
49 #include "dnd.h"
50 #include "dir.h"
51 #include "diritem.h"
52 #include "run.h"
53 #include "type.h"
54 #include "options.h"
55 #include "minibuffer.h"
56 #include "icon.h"
57 #include "toolbar.h"
58 #include "bind.h"
59 #include "appinfo.h"
60 #include "mount.h"
61 #include "xml.h"
62 #include "view_iface.h"
63 #include "view_collection.h"
65 static XMLwrapper *groups = NULL;
67 /* This is rather badly named. It's actually the filer window which received
68 * the last key press or Menu click event.
70 FilerWindow *window_with_focus = NULL;
72 GList *all_filer_windows = NULL;
74 static FilerWindow *window_with_primary = NULL;
76 /* Static prototypes */
77 static void attach(FilerWindow *filer_window);
78 static void detach(FilerWindow *filer_window);
79 static void filer_window_destroyed(GtkWidget *widget,
80 FilerWindow *filer_window);
81 static void update_display(Directory *dir,
82 DirAction action,
83 GPtrArray *items,
84 FilerWindow *filer_window);
85 static void set_scanning_display(FilerWindow *filer_window, gboolean scanning);
86 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning);
87 static gboolean minibuffer_show_cb(FilerWindow *filer_window);
88 static FilerWindow *find_filer_window(const char *sym_path, FilerWindow *diff);
89 static void filer_add_widgets(FilerWindow *filer_window, const gchar *wm_class);
90 static void filer_add_signals(FilerWindow *filer_window);
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_short_flag_names;
108 Option o_filer_auto_resize, o_unique_filer_windows;
109 Option o_filer_size_limit;
111 void filer_init(void)
113 const gchar *ohost;
114 const gchar *dpy;
115 gchar *dpyhost, *tmp;
117 option_add_int(&o_filer_size_limit, "filer_size_limit", 75);
118 option_add_int(&o_filer_auto_resize, "filer_auto_resize",
119 RESIZE_ALWAYS);
120 option_add_int(&o_unique_filer_windows, "filer_unique_windows", 0);
122 option_add_int(&o_short_flag_names, "filer_short_flag_names", FALSE);
124 option_add_int(&o_filer_change_size, "filer_change_size", TRUE);
125 option_add_int(&o_filer_change_size_num, "filer_change_size_num", 30);
127 option_add_notify(filer_options_changed);
129 busy_cursor = gdk_cursor_new(GDK_WATCH);
130 crosshair = gdk_cursor_new(GDK_CROSSHAIR);
132 /* Is the display on the local machine, or are we being
133 * run remotely? See filer_set_title().
135 ohost = our_host_name();
136 dpy = gdk_get_display();
137 dpyhost = g_strdup(dpy);
138 tmp = strchr(dpyhost, ':');
139 if (tmp)
140 *tmp = '\0';
142 if (dpyhost[0] && strcmp(ohost, dpyhost) != 0)
144 /* Try the cannonical name for dpyhost (see our_host_name()
145 * in support.c).
147 struct hostent *ent;
149 ent = gethostbyname(dpyhost);
150 if (!ent || strcmp(ohost, ent->h_name) != 0)
151 not_local = TRUE;
154 g_free(dpyhost);
157 static gboolean if_deleted(gpointer item, gpointer removed)
159 int i = ((GPtrArray *) removed)->len;
160 DirItem **r = (DirItem **) ((GPtrArray *) removed)->pdata;
161 char *leafname = ((DirItem *) item)->leafname;
163 while (i--)
165 if (strcmp(leafname, r[i]->leafname) == 0)
166 return TRUE;
169 return FALSE;
172 /* Resize the filer window to w x h pixels, plus border (not clamped).
173 * If triggered by a key event, warp the pointer (for SloppyFocus users).
175 void filer_window_set_size(FilerWindow *filer_window, int w, int h)
177 GdkEvent *event;
178 GtkWidget *window;
180 g_return_if_fail(filer_window != NULL);
182 if (filer_window->scrollbar)
183 w += filer_window->scrollbar->allocation.width;
185 if (o_toolbar.int_value != TOOLBAR_NONE)
186 h += filer_window->toolbar->allocation.height;
187 if (filer_window->message)
188 h += filer_window->message->allocation.height;
190 window = filer_window->window;
192 if (GTK_WIDGET_VISIBLE(window))
194 gint x, y;
195 GtkRequisition *req = &window->requisition;
196 GdkWindow *gdk_window = window->window;
198 w = MAX(req->width, w);
199 h = MAX(req->height, h);
200 gdk_window_get_position(gdk_window, &x, &y);
202 if (x + w > screen_width || y + h > screen_height)
204 if (x + w > screen_width)
205 x = screen_width - w - 4;
206 if (y + h > screen_height)
207 y = screen_height - h - 4;
208 gdk_window_move_resize(gdk_window, x, y, w, h);
210 else
211 gdk_window_resize(gdk_window, w, h);
213 else
214 gtk_window_set_default_size(GTK_WINDOW(window), w, h);
216 event = gtk_get_current_event();
217 if (event && event->type == GDK_KEY_PRESS)
219 GdkWindow *win = filer_window->window->window;
221 XWarpPointer(gdk_x11_drawable_get_xdisplay(win),
222 None,
223 gdk_x11_drawable_get_xid(win),
224 0, 0, 0, 0,
225 w - 4, h - 4);
229 /* Resize the window to fit the items currently in the Directory.
230 * When opening a directory for the first time, the names will be known but not
231 * the types and images. We can still make a good estimate of the size.
233 void filer_window_autosize(FilerWindow *filer_window)
235 view_autosize(filer_window->view);
238 /* Called on a timeout while scanning or when scanning ends
239 * (whichever happens first).
241 static gint open_filer_window(FilerWindow *filer_window)
243 view_style_changed(filer_window->view, 0);
245 if (filer_window->open_timeout)
247 gtk_timeout_remove(filer_window->open_timeout);
248 filer_window->open_timeout = 0;
251 if (!GTK_WIDGET_VISIBLE(filer_window->window))
253 set_style_by_number_of_items(filer_window);
254 filer_window_autosize(filer_window);
255 gtk_widget_show(filer_window->window);
258 return FALSE;
261 static void update_display(Directory *dir,
262 DirAction action,
263 GPtrArray *items,
264 FilerWindow *filer_window)
266 ViewIface *view = (ViewIface *) filer_window->view;
268 switch (action)
270 case DIR_ADD:
271 view_add_items(view, items);
272 /* Open and resize if currently hidden */
273 open_filer_window(filer_window);
274 break;
275 case DIR_REMOVE:
276 view_delete_if(view, if_deleted, items);
277 toolbar_update_info(filer_window);
278 break;
279 case DIR_START_SCAN:
280 set_scanning_display(filer_window, TRUE);
281 toolbar_update_info(filer_window);
282 break;
283 case DIR_END_SCAN:
284 if (filer_window->window->window)
285 gdk_window_set_cursor(
286 filer_window->window->window,
287 NULL);
288 set_scanning_display(filer_window, FALSE);
289 toolbar_update_info(filer_window);
290 open_filer_window(filer_window);
292 if (filer_window->had_cursor &&
293 !view_cursor_visible(view))
295 ViewIter start;
296 view_get_iter(view, &start, 0);
297 if (start.next(&start))
298 view_cursor_to_iter(view, &start);
299 view_show_cursor(view);
300 filer_window->had_cursor = FALSE;
302 if (filer_window->auto_select)
303 display_set_autoselect(filer_window,
304 filer_window->auto_select);
305 null_g_free(&filer_window->auto_select);
307 filer_create_thumbs(filer_window);
309 if (filer_window->thumb_queue)
310 start_thumb_scanning(filer_window);
311 break;
312 case DIR_UPDATE:
313 view_update_items(view, items);
314 break;
318 static void attach(FilerWindow *filer_window)
320 gdk_window_set_cursor(filer_window->window->window, busy_cursor);
321 view_clear(filer_window->view);
322 filer_window->scanning = TRUE;
323 dir_attach(filer_window->directory, (DirCallback) update_display,
324 filer_window);
325 filer_set_title(filer_window);
328 static void detach(FilerWindow *filer_window)
330 g_return_if_fail(filer_window->directory != NULL);
332 dir_detach(filer_window->directory,
333 (DirCallback) update_display, filer_window);
334 g_object_unref(filer_window->directory);
335 filer_window->directory = NULL;
338 static void filer_window_destroyed(GtkWidget *widget,
339 FilerWindow *filer_window)
341 all_filer_windows = g_list_remove(all_filer_windows, filer_window);
343 g_object_set_data(G_OBJECT(widget), "filer_window", NULL);
345 if (window_with_primary == filer_window)
346 window_with_primary = NULL;
348 if (window_with_focus == filer_window)
350 menu_popdown();
351 window_with_focus = NULL;
354 if (filer_window->directory)
355 detach(filer_window);
357 if (filer_window->open_timeout)
359 gtk_timeout_remove(filer_window->open_timeout);
360 filer_window->open_timeout = 0;
363 if (filer_window->thumb_queue)
364 destroy_glist(&filer_window->thumb_queue);
366 tooltip_show(NULL);
368 g_free(filer_window->auto_select);
369 g_free(filer_window->real_path);
370 g_free(filer_window->sym_path);
371 g_free(filer_window);
373 one_less_window();
376 /* Returns TRUE iff the directory still exists. */
377 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning)
379 Directory *dir;
381 g_return_val_if_fail(filer_window != NULL, FALSE);
383 /* We do a fresh lookup (rather than update) because the inode may
384 * have changed.
386 dir = g_fscache_lookup(dir_cache, filer_window->real_path);
387 if (!dir)
389 if (warning)
390 info_message(_("Directory missing/deleted"));
391 gtk_widget_destroy(filer_window->window);
392 return FALSE;
394 if (dir == filer_window->directory)
395 g_object_unref(dir);
396 else
398 detach(filer_window);
399 filer_window->directory = dir;
400 attach(filer_window);
403 return TRUE;
406 /* No items are now selected. This might be because another app claimed
407 * the selection or because the user unselected all the items.
409 void filer_lost_selection(FilerWindow *filer_window, guint time)
411 if (window_with_primary == filer_window)
413 window_with_primary = NULL;
414 gtk_selection_owner_set(NULL, GDK_SELECTION_PRIMARY, time);
418 /* Another app has claimed the primary selection */
419 static void filer_lost_primary(GtkWidget *window,
420 GdkEventSelection *event,
421 gpointer user_data)
423 FilerWindow *filer_window = (FilerWindow *) user_data;
425 if (window_with_primary && window_with_primary == filer_window)
427 window_with_primary = NULL;
428 set_selection_state(filer_window, FALSE);
432 /* Someone wants us to send them the selection */
433 static void selection_get(GtkWidget *widget,
434 GtkSelectionData *selection_data,
435 guint info,
436 guint time,
437 gpointer data)
439 GString *reply, *header;
440 FilerWindow *filer_window = (FilerWindow *) data;
441 ViewIter iter;
442 DirItem *item;
444 reply = g_string_new(NULL);
445 header = g_string_new(NULL);
447 switch (info)
449 case TARGET_STRING:
450 g_string_sprintf(header, " %s",
451 make_path(filer_window->sym_path, "")->str);
452 break;
453 case TARGET_URI_LIST:
454 g_string_sprintf(header, " file://%s%s",
455 our_host_name_for_dnd(),
456 make_path(filer_window->sym_path, "")->str);
457 break;
460 view_get_iter(filer_window->view, &iter, VIEW_ITER_SELECTED);
462 while ((item = iter.next(&iter)))
464 g_string_append(reply, header->str);
465 g_string_append(reply, item->leafname);
468 if (reply->len > 0)
469 gtk_selection_data_set(selection_data, xa_string,
470 8, reply->str + 1, reply->len - 1);
471 else
473 g_warning("Attempt to paste empty selection!");
474 gtk_selection_data_set(selection_data, xa_string, 8, "", 0);
477 g_string_free(reply, TRUE);
478 g_string_free(header, TRUE);
481 /* Selection has been changed -- try to grab the primary selection
482 * if we don't have it. Also called when clicking on an insensitive selection
483 * to regain primary.
484 * Also updates toolbar info.
486 void filer_selection_changed(FilerWindow *filer_window, gint time)
488 toolbar_update_info(filer_window);
490 if (window_with_primary == filer_window)
491 return; /* Already got primary */
493 if (!view_count_selected(filer_window->view))
494 return; /* Nothing selected */
496 if (filer_window->temp_item_selected == FALSE &&
497 gtk_selection_owner_set(GTK_WIDGET(filer_window->window),
498 GDK_SELECTION_PRIMARY,
499 time))
501 window_with_primary = filer_window;
502 set_selection_state(filer_window, TRUE);
504 else
505 set_selection_state(filer_window, FALSE);
508 /* Open the item (or add it to the shell command minibuffer) */
509 void filer_openitem(FilerWindow *filer_window, ViewIter *iter, OpenFlags flags)
511 gboolean shift = (flags & OPEN_SHIFT) != 0;
512 gboolean close_mini = flags & OPEN_FROM_MINI;
513 gboolean close_window = (flags & OPEN_CLOSE_WINDOW) != 0;
514 DirItem *item;
515 guchar *full_path;
516 gboolean wink = TRUE;
517 Directory *old_dir;
519 g_return_if_fail(filer_window != NULL && iter != NULL);
521 item = iter->peek(iter);
523 g_return_if_fail(item != NULL);
525 if (filer_window->mini_type == MINI_SHELL)
527 minibuffer_add(filer_window, item->leafname);
528 return;
531 if (!item->image)
532 dir_update_item(filer_window->directory, item->leafname);
534 if (item->base_type == TYPE_DIRECTORY)
536 /* Never close a filer window when opening a directory
537 * (click on a dir or click on an app with shift).
539 if (shift || !(item->flags & ITEM_FLAG_APPDIR))
540 close_window = FALSE;
543 full_path = make_path(filer_window->sym_path, item->leafname)->str;
544 if (shift && (item->flags & ITEM_FLAG_SYMLINK))
545 wink = FALSE;
547 old_dir = filer_window->directory;
548 if (run_diritem(full_path, item,
549 flags & OPEN_SAME_WINDOW ? filer_window : NULL,
550 filer_window,
551 shift))
553 if (old_dir != filer_window->directory)
554 return;
556 if (close_window)
557 gtk_widget_destroy(filer_window->window);
558 else
560 if (wink)
561 view_wink_item(filer_window->view, iter);
562 if (close_mini)
563 minibuffer_hide(filer_window);
568 static gint pointer_in(GtkWidget *widget,
569 GdkEventCrossing *event,
570 FilerWindow *filer_window)
572 may_rescan(filer_window, TRUE);
573 return FALSE;
576 static gint pointer_out(GtkWidget *widget,
577 GdkEventCrossing *event,
578 FilerWindow *filer_window)
580 tooltip_show(NULL);
581 return FALSE;
584 /* Move the cursor to the next selected item in direction 'dir'
585 * (+1 or -1).
587 static void next_selected(FilerWindow *filer_window, int dir)
589 ViewIter iter, cursor;
590 gboolean have_cursor;
591 ViewIface *view = filer_window->view;
593 g_return_if_fail(dir == 1 || dir == -1);
595 view_get_cursor(view, &cursor);
596 have_cursor = cursor.peek(&cursor) != NULL;
598 view_get_iter(view, &iter,
599 VIEW_ITER_SELECTED |
600 (have_cursor ? VIEW_ITER_FROM_CURSOR : 0) |
601 (dir < 0 ? VIEW_ITER_BACKWARDS : 0));
603 if (have_cursor && view_get_selected(view, &cursor))
604 iter.next(&iter); /* Skip the cursor itself */
606 if (iter.next(&iter))
607 view_cursor_to_iter(view, &iter);
608 else
609 gdk_beep();
611 return;
614 static void return_pressed(FilerWindow *filer_window, GdkEventKey *event)
616 TargetFunc cb = filer_window->target_cb;
617 gpointer data = filer_window->target_data;
618 OpenFlags flags = 0;
619 ViewIter iter;
621 filer_target_mode(filer_window, NULL, NULL, NULL);
623 view_get_cursor(filer_window->view, &iter);
624 if (!iter.peek(&iter))
625 return;
627 if (cb)
629 cb(filer_window, &iter, data);
630 return;
633 if (event->state & GDK_SHIFT_MASK)
634 flags |= OPEN_SHIFT;
635 if (event->state & GDK_MOD1_MASK)
636 flags |= OPEN_CLOSE_WINDOW;
637 else
638 flags |= OPEN_SAME_WINDOW;
640 filer_openitem(filer_window, &iter, flags);
643 /* Makes sure that 'groups' is up-to-date, reloading from file if it has
644 * changed. If no groups were loaded and there is no file then initialised
645 * groups to an empty document.
646 * Return the node for the 'name' group.
648 static xmlNode *group_find(char *name)
650 xmlNode *node;
651 gchar *path;
653 /* Update the groups, if possible */
654 path = choices_find_path_load("Groups.xml", PROJECT);
655 if (path)
657 XMLwrapper *wrapper;
658 wrapper = xml_cache_load(path);
659 if (wrapper)
661 if (groups)
662 g_object_unref(groups);
663 groups = wrapper;
666 g_free(path);
669 if (!groups)
671 groups = xml_new(NULL);
672 groups->doc = xmlNewDoc("1.0");
674 xmlDocSetRootElement(groups->doc,
675 xmlNewDocNode(groups->doc, NULL, "groups", NULL));
676 return NULL;
679 node = xmlDocGetRootElement(groups->doc);
681 for (node = node->xmlChildrenNode; node; node = node->next)
683 guchar *gid;
685 gid = xmlGetProp(node, "name");
687 if (!gid)
688 continue;
690 if (strcmp(name, gid) != 0)
691 continue;
693 g_free(gid);
695 return node;
698 return NULL;
701 static void group_save(FilerWindow *filer_window, char *name)
703 xmlNode *group;
704 guchar *save_path;
705 DirItem *item;
706 ViewIter iter;
708 group = group_find(name);
709 if (group)
711 xmlUnlinkNode(group);
712 xmlFreeNode(group);
714 group = xmlNewChild(xmlDocGetRootElement(groups->doc),
715 NULL, "group", NULL);
716 xmlSetProp(group, "name", name);
718 xmlNewChild(group, NULL, "directory", filer_window->sym_path);
720 view_get_iter(filer_window->view, &iter, VIEW_ITER_SELECTED);
722 while ((item = iter.next(&iter)))
723 xmlNewChild(group, NULL, "item", item->leafname);
725 save_path = choices_find_path_save("Groups.xml", PROJECT, TRUE);
726 if (save_path)
728 save_xml_file(groups->doc, save_path);
729 g_free(save_path);
733 static gboolean group_restore_cb(ViewIter *iter, gpointer data)
735 GHashTable *in_group = (GHashTable *) data;
737 return g_hash_table_lookup(in_group,
738 iter->peek(iter)->leafname) != NULL;
741 static void group_restore(FilerWindow *filer_window, char *name)
743 GHashTable *in_group;
744 char *path;
745 xmlNode *group, *node;
747 group = group_find(name);
749 if (!group)
751 report_error(_("Group %s is not set. Select some files "
752 "and press Ctrl+%s to set the group. Press %s "
753 "on its own to reselect the files later.\n"
754 "Make sure NumLock is on if you use the keypad."),
755 name, name, name);
756 return;
759 node = get_subnode(group, NULL, "directory");
760 g_return_if_fail(node != NULL);
761 path = xmlNodeListGetString(groups->doc, node->xmlChildrenNode, 1);
762 g_return_if_fail(path != NULL);
764 if (strcmp(path, filer_window->sym_path) != 0)
765 filer_change_to(filer_window, path, NULL);
766 g_free(path);
768 in_group = g_hash_table_new(g_str_hash, g_str_equal);
769 for (node = group->xmlChildrenNode; node; node = node->next)
771 gchar *leaf;
772 if (node->type != XML_ELEMENT_NODE)
773 continue;
774 if (strcmp(node->name, "item") != 0)
775 continue;
777 leaf = xmlNodeListGetString(groups->doc,
778 node->xmlChildrenNode, 1);
779 if (!leaf)
780 g_warning("Missing leafname!\n");
781 else
782 g_hash_table_insert(in_group, leaf, filer_window);
785 view_select_if(filer_window->view, &group_restore_cb, in_group);
787 g_hash_table_foreach(in_group, (GHFunc) g_free, NULL);
788 g_hash_table_destroy(in_group);
791 static gboolean popup_menu(GtkWidget *widget, FilerWindow *filer_window)
793 ViewIter iter;
795 view_get_cursor(filer_window->view, &iter);
797 show_filer_menu(filer_window, NULL, &iter);
799 return TRUE;
802 /* Handle keys that can't be bound with the menu */
803 static gint key_press_event(GtkWidget *widget,
804 GdkEventKey *event,
805 FilerWindow *filer_window)
807 GtkWidget *focus = GTK_WINDOW(widget)->focus_widget;
808 guint key = event->keyval;
809 char group[2] = "1";
811 window_with_focus = filer_window;
813 /* Delay setting up the keys until now to speed loading... */
814 if (!filer_keys)
815 ensure_filer_menu(); /* Gets the keys working... */
817 if (!g_slist_find(filer_keys->acceleratables, widget))
818 gtk_window_add_accel_group(GTK_WINDOW(filer_window->window),
819 filer_keys);
821 if (focus && focus != widget &&
822 gtk_widget_get_toplevel(focus) == widget)
823 if (gtk_widget_event(focus, (GdkEvent *) event))
824 return TRUE; /* Handled */
826 switch (key)
828 case GDK_Escape:
829 filer_target_mode(filer_window, NULL, NULL, NULL);
830 return FALSE;
831 case GDK_Return:
832 return_pressed(filer_window, event);
833 break;
834 case GDK_ISO_Left_Tab:
835 next_selected(filer_window, -1);
836 break;
837 case GDK_Tab:
838 next_selected(filer_window, 1);
839 break;
840 case GDK_BackSpace:
841 change_to_parent(filer_window);
842 break;
843 case GDK_backslash:
845 ViewIter iter;
847 tooltip_show(NULL);
849 view_get_cursor(filer_window->view, &iter);
850 show_filer_menu(filer_window,
851 (GdkEvent *) event, &iter);
852 break;
854 default:
855 if (key >= GDK_0 && key <= GDK_9)
856 group[0] = key - GDK_0 + '0';
857 else if (key >= GDK_KP_0 && key <= GDK_KP_9)
858 group[0] = key - GDK_KP_0 + '0';
859 else
860 return FALSE;
863 if (event->state & GDK_CONTROL_MASK)
864 group_save(filer_window, group);
865 else
866 group_restore(filer_window, group);
869 return TRUE;
872 void filer_open_parent(FilerWindow *filer_window)
874 char *dir;
875 const char *current = filer_window->sym_path;
877 if (current[0] == '/' && current[1] == '\0')
878 return; /* Already in the root */
880 dir = g_dirname(current);
881 filer_opendir(dir, filer_window, NULL);
882 g_free(dir);
885 void change_to_parent(FilerWindow *filer_window)
887 char *dir;
888 const char *current = filer_window->sym_path;
890 if (current[0] == '/' && current[1] == '\0')
891 return; /* Already in the root */
893 dir = g_dirname(current);
894 filer_change_to(filer_window, dir, g_basename(current));
895 g_free(dir);
898 /* Removes trailing /s from path (modified in place) */
899 static void tidy_sympath(gchar *path)
901 int l;
903 g_return_if_fail(path != NULL);
905 l = strlen(path);
906 while (l > 1 && path[l - 1] == '/')
908 l--;
909 path[l] = '\0';
913 /* Make filer_window display path. When finished, highlight item 'from', or
914 * the first item if from is NULL. If there is currently no cursor then
915 * simply wink 'from' (if not NULL).
916 * If the cause was a key event and we resize, warp the pointer.
918 void filer_change_to(FilerWindow *filer_window,
919 const char *path, const char *from)
921 char *from_dup;
922 char *sym_path, *real_path;
923 Directory *new_dir;
925 g_return_if_fail(filer_window != NULL);
927 filer_cancel_thumbnails(filer_window);
929 tooltip_show(NULL);
931 sym_path = g_strdup(path);
932 real_path = pathdup(path);
933 new_dir = g_fscache_lookup(dir_cache, real_path);
935 if (!new_dir)
937 delayed_error(_("Directory '%s' is not accessible"),
938 sym_path);
939 g_free(real_path);
940 g_free(sym_path);
941 return;
944 if (o_unique_filer_windows.int_value)
946 FilerWindow *fw;
948 fw = find_filer_window(sym_path, filer_window);
949 if (fw)
950 gtk_widget_destroy(fw->window);
953 from_dup = from && *from ? g_strdup(from) : NULL;
955 detach(filer_window);
956 g_free(filer_window->real_path);
957 g_free(filer_window->sym_path);
958 filer_window->real_path = real_path;
959 filer_window->sym_path = sym_path;
960 tidy_sympath(filer_window->sym_path);
962 filer_window->directory = new_dir;
964 g_free(filer_window->auto_select);
965 filer_window->auto_select = from_dup;
967 filer_window->had_cursor = filer_window->had_cursor ||
968 view_cursor_visible(filer_window->view);
970 filer_set_title(filer_window);
971 if (filer_window->window->window)
972 gdk_window_set_role(filer_window->window->window,
973 filer_window->sym_path);
974 view_cursor_to_iter(filer_window->view, NULL);
976 attach(filer_window);
978 set_style_by_number_of_items(filer_window);
980 if (o_filer_auto_resize.int_value == RESIZE_ALWAYS)
981 filer_window_autosize(filer_window);
983 if (filer_window->mini_type == MINI_PATH)
984 gtk_idle_add((GtkFunction) minibuffer_show_cb,
985 filer_window);
988 /* Returns a list containing the full (sym) pathname of every selected item.
989 * You must g_free() each item in the list.
991 GList *filer_selected_items(FilerWindow *filer_window)
993 GList *retval = NULL;
994 guchar *dir = filer_window->sym_path;
995 ViewIter iter;
996 DirItem *item;
998 view_get_iter(filer_window->view, &iter, VIEW_ITER_SELECTED);
999 while ((item = iter.next(&iter)))
1001 retval = g_list_prepend(retval,
1002 g_strdup(make_path(dir, item->leafname)->str));
1005 return g_list_reverse(retval);
1008 /* Return the single selected item. Error if nothing is selected. */
1009 DirItem *filer_selected_item(FilerWindow *filer_window)
1011 ViewIter iter;
1012 DirItem *item;
1014 view_get_iter(filer_window->view, &iter, VIEW_ITER_SELECTED);
1016 item = iter.next(&iter);
1017 g_return_val_if_fail(item != NULL, NULL);
1018 g_return_val_if_fail(iter.next(&iter) == NULL, NULL);
1020 return item;
1023 /* Creates and shows a new filer window.
1024 * If src_win != NULL then display options can be taken from that source window.
1025 * Returns the new filer window, or NULL on error.
1026 * Note: if unique windows is in use, may return an existing window.
1028 FilerWindow *filer_opendir(const char *path, FilerWindow *src_win, const gchar *wm_class)
1030 FilerWindow *filer_window;
1031 char *real_path;
1032 DisplayStyle dstyle;
1033 DetailsType dtype;
1035 /* Get the real pathname of the directory and copy it */
1036 real_path = pathdup(path);
1038 if (o_unique_filer_windows.int_value)
1040 FilerWindow *same_dir_window;
1042 same_dir_window = find_filer_window(path, NULL);
1044 if (same_dir_window)
1046 gtk_window_present(GTK_WINDOW(same_dir_window->window));
1047 return same_dir_window;
1051 filer_window = g_new(FilerWindow, 1);
1052 filer_window->message = NULL;
1053 filer_window->minibuffer = NULL;
1054 filer_window->minibuffer_label = NULL;
1055 filer_window->minibuffer_area = NULL;
1056 filer_window->temp_show_hidden = FALSE;
1057 filer_window->sym_path = g_strdup(path);
1058 filer_window->real_path = real_path;
1059 filer_window->scanning = FALSE;
1060 filer_window->had_cursor = FALSE;
1061 filer_window->auto_select = NULL;
1062 filer_window->toolbar_text = NULL;
1063 filer_window->target_cb = NULL;
1064 filer_window->mini_type = MINI_NONE;
1065 filer_window->selection_state = GTK_STATE_INSENSITIVE;
1066 filer_window->toolbar = NULL;
1067 filer_window->toplevel_vbox = NULL;
1069 tidy_sympath(filer_window->sym_path);
1071 /* Finds the entry for this directory in the dir cache, creating
1072 * a new one if needed. This does not cause a scan to start,
1073 * so if a new entry is created then it will be empty.
1075 filer_window->directory = g_fscache_lookup(dir_cache, real_path);
1076 if (!filer_window->directory)
1078 delayed_error(_("Directory '%s' not found."), path);
1079 g_free(filer_window->real_path);
1080 g_free(filer_window->sym_path);
1081 g_free(filer_window);
1082 return NULL;
1085 filer_window->temp_item_selected = FALSE;
1086 filer_window->flags = (FilerFlags) 0;
1087 filer_window->details_type = DETAILS_SUMMARY;
1088 filer_window->display_style = UNKNOWN_STYLE;
1089 filer_window->thumb_queue = NULL;
1090 filer_window->max_thumbs = 0;
1092 if (src_win && o_display_inherit_options.int_value)
1094 filer_window->sort_fn = src_win->sort_fn;
1095 dstyle = src_win->display_style;
1096 dtype = src_win->details_type;
1097 filer_window->show_hidden = src_win->show_hidden;
1098 filer_window->show_thumbs = src_win->show_thumbs;
1100 else
1102 int i = o_display_sort_by.int_value;
1103 filer_window->sort_fn = i == 0 ? sort_by_name :
1104 i == 1 ? sort_by_type :
1105 i == 2 ? sort_by_date :
1106 sort_by_size;
1108 dstyle = o_display_size.int_value;
1109 dtype = o_display_details.int_value;
1110 filer_window->show_hidden =
1111 o_display_show_hidden.int_value;
1112 filer_window->show_thumbs =
1113 o_display_show_thumbs.int_value;
1116 /* Add all the user-interface elements & realise */
1117 filer_add_widgets(filer_window, wm_class);
1118 if (src_win)
1119 gtk_window_set_position(GTK_WINDOW(filer_window->window),
1120 GTK_WIN_POS_MOUSE);
1122 /* Connect to all the signal handlers */
1123 filer_add_signals(filer_window);
1125 display_set_layout(filer_window, dstyle, dtype);
1127 /* Open the window after a timeout, or when scanning stops.
1128 * Do this before attaching, because attach() might tell us to
1129 * stop scanning (if a scan isn't needed).
1131 filer_window->open_timeout = gtk_timeout_add(500,
1132 (GtkFunction) open_filer_window,
1133 filer_window);
1135 /* The view is created empty and then attach() is called, which
1136 * links the filer window to the entry in the directory cache we
1137 * looked up / created above.
1139 * The attach() function will immediately callback to the filer window
1140 * to deliver a list of all known entries in the directory (so,
1141 * the number of items will be known after attach() returns).
1143 * If the directory was not in the cache (because it hadn't been
1144 * opened it before) then the types and icons for the entries are
1145 * not know, but the list of names is.
1148 attach(filer_window);
1150 number_of_windows++;
1151 all_filer_windows = g_list_prepend(all_filer_windows, filer_window);
1153 return filer_window;
1156 /* This adds all the widgets to a new filer window. It is in a separate
1157 * function because filer_opendir() was getting too long...
1159 static void filer_add_widgets(FilerWindow *filer_window, const gchar *wm_class)
1161 GtkWidget *hbox, *vbox;
1163 /* Create the top-level window widget */
1164 filer_window->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1165 filer_set_title(filer_window);
1166 if (wm_class)
1167 gtk_window_set_wmclass(GTK_WINDOW(filer_window->window), wm_class, PROJECT);
1169 /* This property is cleared when the window is destroyed.
1170 * You can thus ref filer_window->window and use this to see
1171 * if the window no longer exists.
1173 g_object_set_data(G_OBJECT(filer_window->window),
1174 "filer_window", filer_window);
1176 /* Create this now to make the Adjustment before the View */
1177 filer_window->scrollbar = gtk_vscrollbar_new(NULL);
1179 /* The view is the area that actually displays the files */
1180 filer_window->view = VIEW(view_collection_new(filer_window));
1181 gtk_widget_show(GTK_WIDGET(filer_window->view));
1183 /* Scrollbar on the right, everything else on the left */
1184 hbox = gtk_hbox_new(FALSE, 0);
1185 gtk_container_add(GTK_CONTAINER(filer_window->window), hbox);
1187 vbox = gtk_vbox_new(FALSE, 0);
1188 gtk_box_pack_start_defaults(GTK_BOX(hbox), vbox);
1189 filer_window->toplevel_vbox = GTK_BOX(vbox);
1191 /* If we want a toolbar, create it now */
1192 toolbar_update_toolbar(filer_window);
1194 /* If there's a message that should be displayed in each window (eg
1195 * 'Running as root'), add it here.
1197 if (show_user_message)
1199 filer_window->message = gtk_label_new(show_user_message);
1200 gtk_box_pack_start(GTK_BOX(vbox), filer_window->message,
1201 FALSE, TRUE, 0);
1202 gtk_widget_show(filer_window->message);
1205 /* Now add the area for displaying the files */
1206 gtk_box_pack_start_defaults(GTK_BOX(vbox),
1207 GTK_WIDGET(filer_window->view));
1209 /* And the minibuffer (hidden to start with) */
1210 create_minibuffer(filer_window);
1211 gtk_box_pack_start(GTK_BOX(vbox), filer_window->minibuffer_area,
1212 FALSE, TRUE, 0);
1214 /* And the thumbnail progress bar (also hidden) */
1216 GtkWidget *cancel;
1218 filer_window->thumb_bar = gtk_hbox_new(FALSE, 2);
1219 gtk_box_pack_start(GTK_BOX(vbox), filer_window->thumb_bar,
1220 FALSE, TRUE, 0);
1222 filer_window->thumb_progress = gtk_progress_bar_new();
1224 gtk_box_pack_start(GTK_BOX(filer_window->thumb_bar),
1225 filer_window->thumb_progress, TRUE, TRUE, 0);
1227 cancel = gtk_button_new_with_label(_("Cancel"));
1228 GTK_WIDGET_UNSET_FLAGS(cancel, GTK_CAN_FOCUS);
1229 gtk_box_pack_start(GTK_BOX(filer_window->thumb_bar),
1230 cancel, FALSE, TRUE, 0);
1231 g_signal_connect_swapped(cancel, "clicked",
1232 G_CALLBACK(filer_cancel_thumbnails),
1233 filer_window);
1236 /* Put the scrollbar on the left of everything else... */
1237 gtk_box_pack_start(GTK_BOX(hbox),
1238 filer_window->scrollbar, FALSE, TRUE, 0);
1240 gtk_widget_show(hbox);
1241 gtk_widget_show(vbox);
1242 gtk_widget_show(filer_window->scrollbar);
1244 gtk_widget_realize(filer_window->window);
1246 gdk_window_set_role(filer_window->window->window,
1247 filer_window->sym_path);
1249 filer_window_set_size(filer_window, 4, 4);
1252 static void filer_add_signals(FilerWindow *filer_window)
1254 GtkTargetEntry target_table[] =
1256 {"text/uri-list", 0, TARGET_URI_LIST},
1257 {"STRING", 0, TARGET_STRING},
1258 {"COMPOUND_TEXT", 0, TARGET_STRING},/* XXX: Treats as STRING */
1261 /* Events on the top-level window */
1262 gtk_widget_add_events(filer_window->window, GDK_ENTER_NOTIFY);
1263 g_signal_connect(filer_window->window, "enter-notify-event",
1264 G_CALLBACK(pointer_in), filer_window);
1265 g_signal_connect(filer_window->window, "leave-notify-event",
1266 G_CALLBACK(pointer_out), filer_window);
1267 g_signal_connect(filer_window->window, "destroy",
1268 G_CALLBACK(filer_window_destroyed), filer_window);
1270 g_signal_connect(filer_window->window, "selection_clear_event",
1271 G_CALLBACK(filer_lost_primary), filer_window);
1273 g_signal_connect(filer_window->window, "selection_get",
1274 G_CALLBACK(selection_get), filer_window);
1275 gtk_selection_add_targets(GTK_WIDGET(filer_window->window),
1276 GDK_SELECTION_PRIMARY,
1277 target_table,
1278 sizeof(target_table) / sizeof(*target_table));
1280 g_signal_connect(filer_window->window, "popup-menu",
1281 G_CALLBACK(popup_menu), filer_window);
1282 g_signal_connect(filer_window->window, "key_press_event",
1283 G_CALLBACK(key_press_event), filer_window);
1286 static gint clear_scanning_display(FilerWindow *filer_window)
1288 if (filer_exists(filer_window))
1289 filer_set_title(filer_window);
1290 return FALSE;
1293 static void set_scanning_display(FilerWindow *filer_window, gboolean scanning)
1295 if (scanning == filer_window->scanning)
1296 return;
1297 filer_window->scanning = scanning;
1299 if (scanning)
1300 filer_set_title(filer_window);
1301 else
1302 gtk_timeout_add(300, (GtkFunction) clear_scanning_display,
1303 filer_window);
1306 /* Note that filer_window may not exist after this call */
1307 void filer_update_dir(FilerWindow *filer_window, gboolean warning)
1309 if (may_rescan(filer_window, warning))
1310 dir_update(filer_window->directory, filer_window->sym_path);
1313 void filer_update_all(void)
1315 GList *next = all_filer_windows;
1317 while (next)
1319 FilerWindow *filer_window = (FilerWindow *) next->data;
1321 /* Updating directory may remove it from list -- stop sending
1322 * patches to move this line!
1324 next = next->next;
1326 filer_update_dir(filer_window, TRUE);
1330 /* Refresh the various caches even if we don't think we need to */
1331 void full_refresh(void)
1333 mount_update(TRUE);
1334 reread_mime_files();
1337 /* See whether a filer window with a given path already exists
1338 * and is different from diff.
1340 static FilerWindow *find_filer_window(const char *sym_path, FilerWindow *diff)
1342 GList *next;
1344 for (next = all_filer_windows; next; next = next->next)
1346 FilerWindow *filer_window = (FilerWindow *) next->data;
1348 if (filer_window != diff &&
1349 strcmp(sym_path, filer_window->sym_path) == 0)
1350 return filer_window;
1353 return NULL;
1356 /* This path has been mounted/umounted/deleted some files - update all dirs */
1357 void filer_check_mounted(const char *real_path)
1359 GList *next = all_filer_windows;
1360 gchar *parent;
1361 int len;
1363 len = strlen(real_path);
1365 while (next)
1367 FilerWindow *filer_window = (FilerWindow *) next->data;
1369 next = next->next;
1371 if (strncmp(real_path, filer_window->real_path, len) == 0)
1373 char s = filer_window->real_path[len];
1375 if (s == '/' || s == '\0')
1376 filer_update_dir(filer_window, FALSE);
1380 parent = g_dirname(real_path);
1381 refresh_dirs(parent);
1382 g_free(parent);
1384 icons_may_update(real_path);
1387 /* Close all windows displaying 'path' or subdirectories of 'path' */
1388 void filer_close_recursive(const char *path)
1390 GList *next = all_filer_windows;
1391 gchar *real;
1392 int len;
1394 real = pathdup(path);
1395 len = strlen(real);
1397 while (next)
1399 FilerWindow *filer_window = (FilerWindow *) next->data;
1401 next = next->next;
1403 if (strncmp(real, filer_window->real_path, len) == 0)
1405 char s = filer_window->real_path[len];
1407 if (len == 1 || s == '/' || s == '\0')
1408 gtk_widget_destroy(filer_window->window);
1413 /* Like minibuffer_show(), except that:
1414 * - It returns FALSE (to be used from an idle callback)
1415 * - It checks that the filer window still exists.
1417 static gboolean minibuffer_show_cb(FilerWindow *filer_window)
1419 if (filer_exists(filer_window))
1420 minibuffer_show(filer_window, MINI_PATH);
1421 return FALSE;
1424 /* TRUE iff filer_window points to an existing FilerWindow
1425 * structure.
1427 gboolean filer_exists(FilerWindow *filer_window)
1429 GList *next;
1431 for (next = all_filer_windows; next; next = next->next)
1433 FilerWindow *fw = (FilerWindow *) next->data;
1435 if (fw == filer_window)
1436 return TRUE;
1439 return FALSE;
1442 /* Make sure the window title is up-to-date */
1443 void filer_set_title(FilerWindow *filer_window)
1445 guchar *title = NULL;
1446 guchar *flags = "";
1448 if (filer_window->scanning || filer_window->show_hidden ||
1449 filer_window->show_thumbs)
1451 if (o_short_flag_names.int_value)
1453 flags = g_strconcat(" +",
1454 filer_window->scanning ? _("S") : "",
1455 filer_window->show_hidden ? _("A") : "",
1456 filer_window->show_thumbs ? _("T") : "",
1457 NULL);
1459 else
1461 flags = g_strconcat(" (",
1462 filer_window->scanning ? _("Scanning, ") : "",
1463 filer_window->show_hidden ? _("All, ") : "",
1464 filer_window->show_thumbs ? _("Thumbs, ") : "",
1465 NULL);
1466 flags[strlen(flags) - 2] = ')';
1470 if (not_local)
1471 title = g_strconcat("//", our_host_name(),
1472 filer_window->sym_path, flags, NULL);
1474 if (!title && home_dir_len > 1 &&
1475 strncmp(filer_window->sym_path, home_dir, home_dir_len) == 0)
1477 guchar sep = filer_window->sym_path[home_dir_len];
1479 if (sep == '\0' || sep == '/')
1480 title = g_strconcat("~",
1481 filer_window->sym_path + home_dir_len,
1482 flags,
1483 NULL);
1486 if (!title)
1487 title = g_strconcat(filer_window->sym_path, flags, NULL);
1489 if (!g_utf8_validate(title, -1, NULL))
1491 char *tmp = title;
1492 title = to_utf8(tmp);
1493 g_free(tmp);
1496 gtk_window_set_title(GTK_WINDOW(filer_window->window), title);
1498 g_free(title);
1500 if (flags[0] != '\0')
1501 g_free(flags);
1504 /* Reconnect to the same directory (used when the Show Hidden option is
1505 * toggled). This has the side-effect of updating the window title.
1507 void filer_detach_rescan(FilerWindow *filer_window)
1509 Directory *dir = filer_window->directory;
1511 g_object_ref(dir);
1512 detach(filer_window);
1513 filer_window->directory = dir;
1514 attach(filer_window);
1517 /* Puts the filer window into target mode. When an item is chosen,
1518 * fn(filer_window, iter, data) is called. 'reason' will be displayed
1519 * on the toolbar while target mode is active.
1521 * Use fn == NULL to cancel target mode.
1523 void filer_target_mode(FilerWindow *filer_window,
1524 TargetFunc fn,
1525 gpointer data,
1526 const char *reason)
1528 TargetFunc old_fn = filer_window->target_cb;
1530 if (fn != old_fn)
1531 gdk_window_set_cursor(
1532 GTK_WIDGET(filer_window->view)->window,
1533 fn ? crosshair : NULL);
1535 filer_window->target_cb = fn;
1536 filer_window->target_data = data;
1538 if (filer_window->toolbar_text == NULL)
1539 return;
1541 if (fn)
1542 gtk_label_set_text(
1543 GTK_LABEL(filer_window->toolbar_text), reason);
1544 else if (o_toolbar_info.int_value)
1546 if (old_fn)
1547 toolbar_update_info(filer_window);
1549 else
1550 gtk_label_set_text(GTK_LABEL(filer_window->toolbar_text), "");
1553 static void set_selection_state(FilerWindow *filer_window, gboolean normal)
1555 GtkStateType old_state = filer_window->selection_state;
1557 filer_window->selection_state = normal
1558 ? GTK_STATE_SELECTED : GTK_STATE_INSENSITIVE;
1560 if (old_state != filer_window->selection_state
1561 && view_count_selected(filer_window->view))
1562 gtk_widget_queue_draw(GTK_WIDGET(filer_window->view));
1565 void filer_cancel_thumbnails(FilerWindow *filer_window)
1567 gtk_widget_hide(filer_window->thumb_bar);
1569 destroy_glist(&filer_window->thumb_queue);
1570 filer_window->max_thumbs = 0;
1573 /* Generate the next thumb for this window. The window object is
1574 * unref'd when there is nothing more to do.
1575 * If the window no longer has a filer window, nothing is done.
1577 static gboolean filer_next_thumb_real(GObject *window)
1579 FilerWindow *filer_window;
1580 gchar *path;
1581 int done, total;
1583 filer_window = g_object_get_data(window, "filer_window");
1585 if (!filer_window)
1587 g_object_unref(window);
1588 return FALSE;
1591 if (!filer_window->thumb_queue)
1593 filer_cancel_thumbnails(filer_window);
1594 g_object_unref(window);
1595 return FALSE;
1598 total = filer_window->max_thumbs;
1599 done = total - g_list_length(filer_window->thumb_queue);
1601 path = (gchar *) filer_window->thumb_queue->data;
1603 pixmap_background_thumb(path, (GFunc) filer_next_thumb, window);
1605 filer_window->thumb_queue = g_list_remove(filer_window->thumb_queue,
1606 path);
1607 g_free(path);
1609 gtk_progress_bar_set_fraction(
1610 GTK_PROGRESS_BAR(filer_window->thumb_progress),
1611 done / (float) total);
1613 return FALSE;
1616 /* path is the thumb just loaded, if any.
1617 * window is unref'd (eventually).
1619 static void filer_next_thumb(GObject *window, const gchar *path)
1621 if (path)
1622 dir_force_update_path(path);
1624 gtk_idle_add((GtkFunction) filer_next_thumb_real, window);
1627 static void start_thumb_scanning(FilerWindow *filer_window)
1629 if (GTK_WIDGET_VISIBLE(filer_window->thumb_bar))
1630 return; /* Already scanning */
1632 gtk_widget_show_all(filer_window->thumb_bar);
1634 g_object_ref(G_OBJECT(filer_window->window));
1635 filer_next_thumb(G_OBJECT(filer_window->window), NULL);
1638 /* Set this image to be loaded some time in the future */
1639 void filer_create_thumb(FilerWindow *filer_window, const gchar *path)
1641 filer_window->max_thumbs++;
1643 filer_window->thumb_queue = g_list_append(filer_window->thumb_queue,
1644 g_strdup(path));
1646 if (filer_window->scanning)
1647 return; /* Will start when scan ends */
1649 start_thumb_scanning(filer_window);
1652 /* If thumbnail display is on, look through all the items in this directory
1653 * and start creating or updating the thumbnails as needed.
1655 void filer_create_thumbs(FilerWindow *filer_window)
1657 DirItem *item;
1658 ViewIter iter;
1660 if (!filer_window->show_thumbs)
1661 return;
1663 view_get_iter(filer_window->view, &iter, 0);
1665 while ((item = iter.next(&iter)))
1667 MaskedPixmap *pixmap;
1668 gchar *path;
1669 gboolean found;
1671 if (item->base_type != TYPE_FILE)
1672 continue;
1674 if (strcmp(item->mime_type->media_type, "image") != 0)
1675 continue;
1677 path = make_path(filer_window->real_path, item->leafname)->str;
1679 pixmap = g_fscache_lookup_full(pixmap_cache, path,
1680 FSCACHE_LOOKUP_ONLY_NEW, &found);
1681 if (pixmap)
1682 g_object_unref(pixmap);
1684 /* If we didn't get an image, it could be because:
1686 * - We're loading the image now. found is TRUE,
1687 * and we'll update the item later.
1688 * - We tried to load the image and failed. found
1689 * is TRUE.
1690 * - We haven't tried loading the image. found is
1691 * FALSE, and we start creating the thumb here.
1693 if (!found)
1694 filer_create_thumb(filer_window, path);
1698 static void filer_options_changed(void)
1700 if (o_short_flag_names.has_changed)
1702 GList *next;
1704 for (next = all_filer_windows; next; next = next->next)
1706 FilerWindow *filer_window = (FilerWindow *) next->data;
1708 filer_set_title(filer_window);
1713 /* Change to Large or Small icons depending on the number of items
1714 * in the directory, subject to options.
1716 static void set_style_by_number_of_items(FilerWindow *filer_window)
1718 int n;
1720 g_return_if_fail(filer_window != NULL);
1722 if (!o_filer_change_size.int_value)
1723 return; /* Don't auto-set style */
1725 if (filer_window->display_style != LARGE_ICONS &&
1726 filer_window->display_style != SMALL_ICONS)
1727 return; /* Only change between these two styles */
1729 n = view_count_items(filer_window->view);
1731 if (n >= o_filer_change_size_num.int_value)
1732 display_set_layout(filer_window, SMALL_ICONS,
1733 filer_window->details_type);
1734 else
1735 display_set_layout(filer_window, LARGE_ICONS,
1736 filer_window->details_type);
1739 /* Append interesting information to this GString */
1740 void filer_add_tip_details(FilerWindow *filer_window,
1741 GString *tip, DirItem *item)
1743 guchar *fullpath = NULL;
1745 fullpath = make_path(filer_window->real_path, item->leafname)->str;
1747 if (item->flags & ITEM_FLAG_SYMLINK)
1749 char *target;
1751 target = readlink_dup(fullpath);
1752 if (target)
1754 g_string_append(tip, _("Symbolic link to "));
1755 g_string_append(tip, target);
1756 g_string_append_c(tip, '\n');
1757 g_free(target);
1761 if (item->flags & ITEM_FLAG_APPDIR)
1763 XMLwrapper *info;
1764 xmlNode *node;
1766 info = appinfo_get(fullpath, item);
1767 if (info && ((node = xml_get_section(info, NULL, "Summary"))))
1769 guchar *str;
1770 str = xmlNodeListGetString(node->doc,
1771 node->xmlChildrenNode, 1);
1772 if (str)
1774 g_string_append(tip, str);
1775 g_string_append_c(tip, '\n');
1776 g_free(str);
1779 if (info)
1780 g_object_unref(info);
1783 if (!g_utf8_validate(item->leafname, -1, NULL))
1784 g_string_append(tip,
1785 _("This filename is not valid UTF-8. "
1786 "You should rename it.\n"));