r2180: Code tidying (Bernard Jungen).
[rox-filer.git] / ROX-Filer / src / filer.c
blobb549c24220f87c049f4d59739d6730e35dcf9595
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"
64 #include "action.h"
66 static XMLwrapper *groups = NULL;
68 /* This is rather badly named. It's actually the filer window which received
69 * the last key press or Menu click event.
71 FilerWindow *window_with_focus = NULL;
73 GList *all_filer_windows = NULL;
75 static FilerWindow *window_with_primary = NULL;
77 /* Static prototypes */
78 static void attach(FilerWindow *filer_window);
79 static void detach(FilerWindow *filer_window);
80 static void filer_window_destroyed(GtkWidget *widget,
81 FilerWindow *filer_window);
82 static void update_display(Directory *dir,
83 DirAction action,
84 GPtrArray *items,
85 FilerWindow *filer_window);
86 static void set_scanning_display(FilerWindow *filer_window, gboolean scanning);
87 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning);
88 static gboolean minibuffer_show_cb(FilerWindow *filer_window);
89 static FilerWindow *find_filer_window(const char *sym_path, FilerWindow *diff);
90 static void filer_add_widgets(FilerWindow *filer_window, const gchar *wm_class);
91 static void filer_add_signals(FilerWindow *filer_window);
93 static void set_selection_state(FilerWindow *filer_window, gboolean normal);
94 static void filer_next_thumb(GObject *window, const gchar *path);
95 static void start_thumb_scanning(FilerWindow *filer_window);
96 static void filer_options_changed(void);
97 static void set_style_by_number_of_items(FilerWindow *filer_window);
99 static GdkCursor *busy_cursor = NULL;
100 static GdkCursor *crosshair = NULL;
102 /* Indicates whether the filer's display is different to the machine it
103 * is actually running on.
105 static gboolean not_local = FALSE;
107 static Option o_filer_change_size, o_filer_change_size_num;
108 static Option o_short_flag_names;
109 Option o_filer_auto_resize, o_unique_filer_windows;
110 Option o_filer_size_limit;
112 void filer_init(void)
114 const gchar *ohost;
115 const gchar *dpy;
116 gchar *dpyhost, *tmp;
118 option_add_int(&o_filer_size_limit, "filer_size_limit", 75);
119 option_add_int(&o_filer_auto_resize, "filer_auto_resize",
120 RESIZE_ALWAYS);
121 option_add_int(&o_unique_filer_windows, "filer_unique_windows", 0);
123 option_add_int(&o_short_flag_names, "filer_short_flag_names", FALSE);
125 option_add_int(&o_filer_change_size, "filer_change_size", TRUE);
126 option_add_int(&o_filer_change_size_num, "filer_change_size_num", 30);
128 option_add_notify(filer_options_changed);
130 busy_cursor = gdk_cursor_new(GDK_WATCH);
131 crosshair = gdk_cursor_new(GDK_CROSSHAIR);
133 /* Is the display on the local machine, or are we being
134 * run remotely? See filer_set_title().
136 ohost = our_host_name();
137 dpy = gdk_get_display();
138 dpyhost = g_strdup(dpy);
139 tmp = strchr(dpyhost, ':');
140 if (tmp)
141 *tmp = '\0';
143 if (dpyhost[0] && strcmp(ohost, dpyhost) != 0)
145 /* Try the cannonical name for dpyhost (see our_host_name()
146 * in support.c).
148 struct hostent *ent;
150 ent = gethostbyname(dpyhost);
151 if (!ent || strcmp(ohost, ent->h_name) != 0)
152 not_local = TRUE;
155 g_free(dpyhost);
158 static gboolean if_deleted(gpointer item, gpointer removed)
160 int i = ((GPtrArray *) removed)->len;
161 DirItem **r = (DirItem **) ((GPtrArray *) removed)->pdata;
162 char *leafname = ((DirItem *) item)->leafname;
164 while (i--)
166 if (strcmp(leafname, r[i]->leafname) == 0)
167 return TRUE;
170 return FALSE;
173 /* Resize the filer window to w x h pixels, plus border (not clamped).
174 * If triggered by a key event, warp the pointer (for SloppyFocus users).
176 void filer_window_set_size(FilerWindow *filer_window, int w, int h)
178 GdkEvent *event;
179 GtkWidget *window;
181 g_return_if_fail(filer_window != NULL);
183 if (filer_window->scrollbar)
184 w += filer_window->scrollbar->allocation.width;
186 if (o_toolbar.int_value != TOOLBAR_NONE)
187 h += filer_window->toolbar->allocation.height;
188 if (filer_window->message)
189 h += filer_window->message->allocation.height;
191 window = filer_window->window;
193 if (GTK_WIDGET_VISIBLE(window))
195 gint x, y;
196 GtkRequisition *req = &window->requisition;
197 GdkWindow *gdk_window = window->window;
199 w = MAX(req->width, w);
200 h = MAX(req->height, h);
201 gdk_window_get_position(gdk_window, &x, &y);
203 if (x + w > screen_width || y + h > screen_height)
205 if (x + w > screen_width)
206 x = screen_width - w - 4;
207 if (y + h > screen_height)
208 y = screen_height - h - 4;
209 gdk_window_move_resize(gdk_window, x, y, w, h);
211 else
212 gdk_window_resize(gdk_window, w, h);
214 else
215 gtk_window_set_default_size(GTK_WINDOW(window), w, h);
217 event = gtk_get_current_event();
218 if (event && event->type == GDK_KEY_PRESS)
220 GdkWindow *win = filer_window->window->window;
222 XWarpPointer(gdk_x11_drawable_get_xdisplay(win),
223 None,
224 gdk_x11_drawable_get_xid(win),
225 0, 0, 0, 0,
226 w - 4, h - 4);
230 /* Resize the window to fit the items currently in the Directory.
231 * When opening a directory for the first time, the names will be known but not
232 * the types and images. We can still make a good estimate of the size.
234 void filer_window_autosize(FilerWindow *filer_window)
236 view_autosize(filer_window->view);
239 /* Called on a timeout while scanning or when scanning ends
240 * (whichever happens first).
242 static gint open_filer_window(FilerWindow *filer_window)
244 view_style_changed(filer_window->view, 0);
246 if (filer_window->open_timeout)
248 gtk_timeout_remove(filer_window->open_timeout);
249 filer_window->open_timeout = 0;
252 if (!GTK_WIDGET_VISIBLE(filer_window->window))
254 set_style_by_number_of_items(filer_window);
255 filer_window_autosize(filer_window);
256 gtk_widget_show(filer_window->window);
259 return FALSE;
262 static void update_display(Directory *dir,
263 DirAction action,
264 GPtrArray *items,
265 FilerWindow *filer_window)
267 ViewIface *view = (ViewIface *) filer_window->view;
269 switch (action)
271 case DIR_ADD:
272 view_add_items(view, items);
273 /* Open and resize if currently hidden */
274 open_filer_window(filer_window);
275 break;
276 case DIR_REMOVE:
277 view_delete_if(view, if_deleted, items);
278 toolbar_update_info(filer_window);
279 break;
280 case DIR_START_SCAN:
281 set_scanning_display(filer_window, TRUE);
282 toolbar_update_info(filer_window);
283 break;
284 case DIR_END_SCAN:
285 if (filer_window->window->window)
286 gdk_window_set_cursor(
287 filer_window->window->window,
288 NULL);
289 set_scanning_display(filer_window, FALSE);
290 toolbar_update_info(filer_window);
291 open_filer_window(filer_window);
293 if (filer_window->had_cursor &&
294 !view_cursor_visible(view))
296 ViewIter start;
297 view_get_iter(view, &start, 0);
298 if (start.next(&start))
299 view_cursor_to_iter(view, &start);
300 view_show_cursor(view);
301 filer_window->had_cursor = FALSE;
303 if (filer_window->auto_select)
304 display_set_autoselect(filer_window,
305 filer_window->auto_select);
306 null_g_free(&filer_window->auto_select);
308 filer_create_thumbs(filer_window);
310 if (filer_window->thumb_queue)
311 start_thumb_scanning(filer_window);
312 break;
313 case DIR_UPDATE:
314 view_update_items(view, items);
315 break;
319 static void attach(FilerWindow *filer_window)
321 gdk_window_set_cursor(filer_window->window->window, busy_cursor);
322 view_clear(filer_window->view);
323 filer_window->scanning = TRUE;
324 dir_attach(filer_window->directory, (DirCallback) update_display,
325 filer_window);
326 filer_set_title(filer_window);
329 static void detach(FilerWindow *filer_window)
331 g_return_if_fail(filer_window->directory != NULL);
333 dir_detach(filer_window->directory,
334 (DirCallback) update_display, filer_window);
335 g_object_unref(filer_window->directory);
336 filer_window->directory = NULL;
339 /* Returns TRUE to prevent closing the window. May offer to unmount a
340 * device.
342 gboolean filer_window_delete(GtkWidget *window,
343 GdkEvent *unused, /* (may be NULL) */
344 FilerWindow *filer_window)
346 if (mount_is_user_mounted(filer_window->real_path))
348 int action;
350 action = get_choice(PROJECT,
351 _("Do you want to unmount this device?\n\n"
352 "Unmounting a device makes it safe to remove "
353 "the disk."), 3,
354 GTK_STOCK_CANCEL, NULL,
355 GTK_STOCK_CLOSE, NULL,
356 ROX_STOCK_MOUNT, _("Unmount"));
358 if (action == 0)
359 return TRUE; /* Cancel close operation */
361 if (action == 2)
363 GList *list;
365 list = g_list_prepend(NULL, filer_window->sym_path);
366 action_mount(list, FALSE, TRUE);
367 g_list_free(list);
371 return FALSE;
374 static void filer_window_destroyed(GtkWidget *widget, FilerWindow *filer_window)
376 all_filer_windows = g_list_remove(all_filer_windows, filer_window);
378 g_object_set_data(G_OBJECT(widget), "filer_window", NULL);
380 if (window_with_primary == filer_window)
381 window_with_primary = NULL;
383 if (window_with_focus == filer_window)
385 menu_popdown();
386 window_with_focus = NULL;
389 if (filer_window->directory)
390 detach(filer_window);
392 if (filer_window->open_timeout)
394 gtk_timeout_remove(filer_window->open_timeout);
395 filer_window->open_timeout = 0;
398 if (filer_window->thumb_queue)
399 destroy_glist(&filer_window->thumb_queue);
401 tooltip_show(NULL);
403 g_free(filer_window->auto_select);
404 g_free(filer_window->real_path);
405 g_free(filer_window->sym_path);
406 g_free(filer_window);
408 one_less_window();
411 /* Returns TRUE iff the directory still exists. */
412 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning)
414 Directory *dir;
416 g_return_val_if_fail(filer_window != NULL, FALSE);
418 /* We do a fresh lookup (rather than update) because the inode may
419 * have changed.
421 dir = g_fscache_lookup(dir_cache, filer_window->real_path);
422 if (!dir)
424 if (warning)
425 info_message(_("Directory missing/deleted"));
426 gtk_widget_destroy(filer_window->window);
427 return FALSE;
429 if (dir == filer_window->directory)
430 g_object_unref(dir);
431 else
433 detach(filer_window);
434 filer_window->directory = dir;
435 attach(filer_window);
438 return TRUE;
441 /* No items are now selected. This might be because another app claimed
442 * the selection or because the user unselected all the items.
444 void filer_lost_selection(FilerWindow *filer_window, guint time)
446 if (window_with_primary == filer_window)
448 window_with_primary = NULL;
449 gtk_selection_owner_set(NULL, GDK_SELECTION_PRIMARY, time);
453 /* Another app has claimed the primary selection */
454 static void filer_lost_primary(GtkWidget *window,
455 GdkEventSelection *event,
456 gpointer user_data)
458 FilerWindow *filer_window = (FilerWindow *) user_data;
460 if (window_with_primary && window_with_primary == filer_window)
462 window_with_primary = NULL;
463 set_selection_state(filer_window, FALSE);
467 /* Someone wants us to send them the selection */
468 static void selection_get(GtkWidget *widget,
469 GtkSelectionData *selection_data,
470 guint info,
471 guint time,
472 gpointer data)
474 GString *reply, *header;
475 FilerWindow *filer_window = (FilerWindow *) data;
476 ViewIter iter;
477 DirItem *item;
479 reply = g_string_new(NULL);
480 header = g_string_new(NULL);
482 switch (info)
484 case TARGET_STRING:
485 g_string_printf(header, " %s",
486 make_path(filer_window->sym_path, ""));
487 break;
488 case TARGET_URI_LIST:
489 g_string_printf(header, " file://%s%s",
490 our_host_name_for_dnd(),
491 make_path(filer_window->sym_path, ""));
492 break;
495 view_get_iter(filer_window->view, &iter, VIEW_ITER_SELECTED);
497 while ((item = iter.next(&iter)))
499 g_string_append(reply, header->str);
500 g_string_append(reply, item->leafname);
503 if (reply->len > 0)
504 gtk_selection_data_set(selection_data, xa_string,
505 8, reply->str + 1, reply->len - 1);
506 else
508 g_warning("Attempt to paste empty selection!");
509 gtk_selection_data_set(selection_data, xa_string, 8, "", 0);
512 g_string_free(reply, TRUE);
513 g_string_free(header, TRUE);
516 /* Selection has been changed -- try to grab the primary selection
517 * if we don't have it. Also called when clicking on an insensitive selection
518 * to regain primary.
519 * Also updates toolbar info.
521 void filer_selection_changed(FilerWindow *filer_window, gint time)
523 toolbar_update_info(filer_window);
525 if (window_with_primary == filer_window)
526 return; /* Already got primary */
528 if (!view_count_selected(filer_window->view))
529 return; /* Nothing selected */
531 if (filer_window->temp_item_selected == FALSE &&
532 gtk_selection_owner_set(GTK_WIDGET(filer_window->window),
533 GDK_SELECTION_PRIMARY,
534 time))
536 window_with_primary = filer_window;
537 set_selection_state(filer_window, TRUE);
539 else
540 set_selection_state(filer_window, FALSE);
543 /* Open the item (or add it to the shell command minibuffer) */
544 void filer_openitem(FilerWindow *filer_window, ViewIter *iter, OpenFlags flags)
546 gboolean shift = (flags & OPEN_SHIFT) != 0;
547 gboolean close_mini = flags & OPEN_FROM_MINI;
548 gboolean close_window = (flags & OPEN_CLOSE_WINDOW) != 0;
549 DirItem *item;
550 const guchar *full_path;
551 gboolean wink = TRUE;
552 Directory *old_dir;
554 g_return_if_fail(filer_window != NULL && iter != NULL);
556 item = iter->peek(iter);
558 g_return_if_fail(item != NULL);
560 if (filer_window->mini_type == MINI_SHELL)
562 minibuffer_add(filer_window, item->leafname);
563 return;
566 if (!item->image)
567 dir_update_item(filer_window->directory, item->leafname);
569 if (item->base_type == TYPE_DIRECTORY)
571 /* Never close a filer window when opening a directory
572 * (click on a dir or click on an app with shift).
574 if (shift || !(item->flags & ITEM_FLAG_APPDIR))
575 close_window = FALSE;
578 full_path = make_path(filer_window->sym_path, item->leafname);
579 if (shift && (item->flags & ITEM_FLAG_SYMLINK))
580 wink = FALSE;
582 old_dir = filer_window->directory;
583 if (run_diritem(full_path, item,
584 flags & OPEN_SAME_WINDOW ? filer_window : NULL,
585 filer_window,
586 shift))
588 if (old_dir != filer_window->directory)
589 return;
591 if (close_window)
592 gtk_widget_destroy(filer_window->window);
593 else
595 if (wink)
596 view_wink_item(filer_window->view, iter);
597 if (close_mini)
598 minibuffer_hide(filer_window);
603 static gint pointer_in(GtkWidget *widget,
604 GdkEventCrossing *event,
605 FilerWindow *filer_window)
607 may_rescan(filer_window, TRUE);
608 return FALSE;
611 static gint pointer_out(GtkWidget *widget,
612 GdkEventCrossing *event,
613 FilerWindow *filer_window)
615 tooltip_show(NULL);
616 return FALSE;
619 /* Move the cursor to the next selected item in direction 'dir'
620 * (+1 or -1).
622 static void next_selected(FilerWindow *filer_window, int dir)
624 ViewIter iter, cursor;
625 gboolean have_cursor;
626 ViewIface *view = filer_window->view;
628 g_return_if_fail(dir == 1 || dir == -1);
630 view_get_cursor(view, &cursor);
631 have_cursor = cursor.peek(&cursor) != NULL;
633 view_get_iter(view, &iter,
634 VIEW_ITER_SELECTED |
635 (have_cursor ? VIEW_ITER_FROM_CURSOR : 0) |
636 (dir < 0 ? VIEW_ITER_BACKWARDS : 0));
638 if (have_cursor && view_get_selected(view, &cursor))
639 iter.next(&iter); /* Skip the cursor itself */
641 if (iter.next(&iter))
642 view_cursor_to_iter(view, &iter);
643 else
644 gdk_beep();
646 return;
649 static void return_pressed(FilerWindow *filer_window, GdkEventKey *event)
651 TargetFunc cb = filer_window->target_cb;
652 gpointer data = filer_window->target_data;
653 OpenFlags flags = 0;
654 ViewIter iter;
656 filer_target_mode(filer_window, NULL, NULL, NULL);
658 view_get_cursor(filer_window->view, &iter);
659 if (!iter.peek(&iter))
660 return;
662 if (cb)
664 cb(filer_window, &iter, data);
665 return;
668 if (event->state & GDK_SHIFT_MASK)
669 flags |= OPEN_SHIFT;
670 if (event->state & GDK_MOD1_MASK)
671 flags |= OPEN_CLOSE_WINDOW;
672 else
673 flags |= OPEN_SAME_WINDOW;
675 filer_openitem(filer_window, &iter, flags);
678 /* Makes sure that 'groups' is up-to-date, reloading from file if it has
679 * changed. If no groups were loaded and there is no file then initialised
680 * groups to an empty document.
681 * Return the node for the 'name' group.
683 static xmlNode *group_find(char *name)
685 xmlNode *node;
686 gchar *path;
688 /* Update the groups, if possible */
689 path = choices_find_path_load("Groups.xml", PROJECT);
690 if (path)
692 XMLwrapper *wrapper;
693 wrapper = xml_cache_load(path);
694 if (wrapper)
696 if (groups)
697 g_object_unref(groups);
698 groups = wrapper;
701 g_free(path);
704 if (!groups)
706 groups = xml_new(NULL);
707 groups->doc = xmlNewDoc("1.0");
709 xmlDocSetRootElement(groups->doc,
710 xmlNewDocNode(groups->doc, NULL, "groups", NULL));
711 return NULL;
714 node = xmlDocGetRootElement(groups->doc);
716 for (node = node->xmlChildrenNode; node; node = node->next)
718 guchar *gid;
720 gid = xmlGetProp(node, "name");
722 if (!gid)
723 continue;
725 if (strcmp(name, gid) != 0)
726 continue;
728 g_free(gid);
730 return node;
733 return NULL;
736 static void group_save(FilerWindow *filer_window, char *name)
738 xmlNode *group;
739 guchar *save_path;
740 DirItem *item;
741 ViewIter iter;
743 group = group_find(name);
744 if (group)
746 xmlUnlinkNode(group);
747 xmlFreeNode(group);
749 group = xmlNewChild(xmlDocGetRootElement(groups->doc),
750 NULL, "group", NULL);
751 xmlSetProp(group, "name", name);
753 xmlNewChild(group, NULL, "directory", filer_window->sym_path);
755 view_get_iter(filer_window->view, &iter, VIEW_ITER_SELECTED);
757 while ((item = iter.next(&iter)))
758 xmlNewChild(group, NULL, "item", item->leafname);
760 save_path = choices_find_path_save("Groups.xml", PROJECT, TRUE);
761 if (save_path)
763 save_xml_file(groups->doc, save_path);
764 g_free(save_path);
768 static gboolean group_restore_cb(ViewIter *iter, gpointer data)
770 GHashTable *in_group = (GHashTable *) data;
772 return g_hash_table_lookup(in_group,
773 iter->peek(iter)->leafname) != NULL;
776 static void group_restore(FilerWindow *filer_window, char *name)
778 GHashTable *in_group;
779 char *path;
780 xmlNode *group, *node;
782 group = group_find(name);
784 if (!group)
786 report_error(_("Group %s is not set. Select some files "
787 "and press Ctrl+%s to set the group. Press %s "
788 "on its own to reselect the files later.\n"
789 "Make sure NumLock is on if you use the keypad."),
790 name, name, name);
791 return;
794 node = get_subnode(group, NULL, "directory");
795 g_return_if_fail(node != NULL);
796 path = xmlNodeListGetString(groups->doc, node->xmlChildrenNode, 1);
797 g_return_if_fail(path != NULL);
799 if (strcmp(path, filer_window->sym_path) != 0)
800 filer_change_to(filer_window, path, NULL);
801 g_free(path);
803 in_group = g_hash_table_new(g_str_hash, g_str_equal);
804 for (node = group->xmlChildrenNode; node; node = node->next)
806 gchar *leaf;
807 if (node->type != XML_ELEMENT_NODE)
808 continue;
809 if (strcmp(node->name, "item") != 0)
810 continue;
812 leaf = xmlNodeListGetString(groups->doc,
813 node->xmlChildrenNode, 1);
814 if (!leaf)
815 g_warning("Missing leafname!\n");
816 else
817 g_hash_table_insert(in_group, leaf, filer_window);
820 view_select_if(filer_window->view, &group_restore_cb, in_group);
822 g_hash_table_foreach(in_group, (GHFunc) g_free, NULL);
823 g_hash_table_destroy(in_group);
826 static gboolean popup_menu(GtkWidget *widget, FilerWindow *filer_window)
828 ViewIter iter;
830 view_get_cursor(filer_window->view, &iter);
832 show_filer_menu(filer_window, NULL, &iter);
834 return TRUE;
837 /* Handle keys that can't be bound with the menu */
838 static gint key_press_event(GtkWidget *widget,
839 GdkEventKey *event,
840 FilerWindow *filer_window)
842 GtkWidget *focus = GTK_WINDOW(widget)->focus_widget;
843 guint key = event->keyval;
844 char group[2] = "1";
846 window_with_focus = filer_window;
848 /* Delay setting up the keys until now to speed loading... */
849 if (!filer_keys)
850 ensure_filer_menu(); /* Gets the keys working... */
852 if (!g_slist_find(filer_keys->acceleratables, widget))
853 gtk_window_add_accel_group(GTK_WINDOW(filer_window->window),
854 filer_keys);
856 if (focus && focus != widget &&
857 gtk_widget_get_toplevel(focus) == widget)
858 if (gtk_widget_event(focus, (GdkEvent *) event))
859 return TRUE; /* Handled */
861 switch (key)
863 case GDK_Escape:
864 filer_target_mode(filer_window, NULL, NULL, NULL);
865 return FALSE;
866 case GDK_Return:
867 return_pressed(filer_window, event);
868 break;
869 case GDK_ISO_Left_Tab:
870 next_selected(filer_window, -1);
871 break;
872 case GDK_Tab:
873 next_selected(filer_window, 1);
874 break;
875 case GDK_BackSpace:
876 change_to_parent(filer_window);
877 break;
878 case GDK_backslash:
880 ViewIter iter;
882 tooltip_show(NULL);
884 view_get_cursor(filer_window->view, &iter);
885 show_filer_menu(filer_window,
886 (GdkEvent *) event, &iter);
887 break;
889 default:
890 if (key >= GDK_0 && key <= GDK_9)
891 group[0] = key - GDK_0 + '0';
892 else if (key >= GDK_KP_0 && key <= GDK_KP_9)
893 group[0] = key - GDK_KP_0 + '0';
894 else
895 return FALSE;
898 if (event->state & GDK_CONTROL_MASK)
899 group_save(filer_window, group);
900 else
901 group_restore(filer_window, group);
904 return TRUE;
907 void filer_open_parent(FilerWindow *filer_window)
909 char *dir;
910 const char *current = filer_window->sym_path;
912 if (current[0] == '/' && current[1] == '\0')
913 return; /* Already in the root */
915 dir = g_path_get_dirname(current);
916 filer_opendir(dir, filer_window, NULL);
917 g_free(dir);
920 void change_to_parent(FilerWindow *filer_window)
922 char *dir;
923 const char *current = filer_window->sym_path;
925 if (current[0] == '/' && current[1] == '\0')
926 return; /* Already in the root */
928 dir = g_path_get_dirname(current);
929 filer_change_to(filer_window, dir, g_basename(current));
930 g_free(dir);
933 /* Removes trailing /s from path (modified in place) */
934 static void tidy_sympath(gchar *path)
936 int l;
938 g_return_if_fail(path != NULL);
940 l = strlen(path);
941 while (l > 1 && path[l - 1] == '/')
943 l--;
944 path[l] = '\0';
948 /* Make filer_window display path. When finished, highlight item 'from', or
949 * the first item if from is NULL. If there is currently no cursor then
950 * simply wink 'from' (if not NULL).
951 * If the cause was a key event and we resize, warp the pointer.
953 void filer_change_to(FilerWindow *filer_window,
954 const char *path, const char *from)
956 char *from_dup;
957 char *sym_path, *real_path;
958 Directory *new_dir;
960 g_return_if_fail(filer_window != NULL);
962 filer_cancel_thumbnails(filer_window);
964 tooltip_show(NULL);
966 sym_path = g_strdup(path);
967 real_path = pathdup(path);
968 new_dir = g_fscache_lookup(dir_cache, real_path);
970 if (!new_dir)
972 delayed_error(_("Directory '%s' is not accessible"),
973 sym_path);
974 g_free(real_path);
975 g_free(sym_path);
976 return;
979 if (o_unique_filer_windows.int_value)
981 FilerWindow *fw;
983 fw = find_filer_window(sym_path, filer_window);
984 if (fw)
985 gtk_widget_destroy(fw->window);
988 from_dup = from && *from ? g_strdup(from) : NULL;
990 detach(filer_window);
991 g_free(filer_window->real_path);
992 g_free(filer_window->sym_path);
993 filer_window->real_path = real_path;
994 filer_window->sym_path = sym_path;
995 tidy_sympath(filer_window->sym_path);
997 filer_window->directory = new_dir;
999 g_free(filer_window->auto_select);
1000 filer_window->auto_select = from_dup;
1002 filer_window->had_cursor = filer_window->had_cursor ||
1003 view_cursor_visible(filer_window->view);
1005 filer_set_title(filer_window);
1006 if (filer_window->window->window)
1007 gdk_window_set_role(filer_window->window->window,
1008 filer_window->sym_path);
1009 view_cursor_to_iter(filer_window->view, NULL);
1011 attach(filer_window);
1013 set_style_by_number_of_items(filer_window);
1015 if (o_filer_auto_resize.int_value == RESIZE_ALWAYS)
1016 filer_window_autosize(filer_window);
1018 if (filer_window->mini_type == MINI_PATH)
1019 gtk_idle_add((GtkFunction) minibuffer_show_cb,
1020 filer_window);
1023 /* Returns a list containing the full (sym) pathname of every selected item.
1024 * You must g_free() each item in the list.
1026 GList *filer_selected_items(FilerWindow *filer_window)
1028 GList *retval = NULL;
1029 guchar *dir = filer_window->sym_path;
1030 ViewIter iter;
1031 DirItem *item;
1033 view_get_iter(filer_window->view, &iter, VIEW_ITER_SELECTED);
1034 while ((item = iter.next(&iter)))
1036 retval = g_list_prepend(retval,
1037 g_strdup(make_path(dir, item->leafname)));
1040 return g_list_reverse(retval);
1043 /* Return the single selected item. Error if nothing is selected. */
1044 DirItem *filer_selected_item(FilerWindow *filer_window)
1046 ViewIter iter;
1047 DirItem *item;
1049 view_get_iter(filer_window->view, &iter, VIEW_ITER_SELECTED);
1051 item = iter.next(&iter);
1052 g_return_val_if_fail(item != NULL, NULL);
1053 g_return_val_if_fail(iter.next(&iter) == NULL, NULL);
1055 return item;
1058 /* Creates and shows a new filer window.
1059 * If src_win != NULL then display options can be taken from that source window.
1060 * Returns the new filer window, or NULL on error.
1061 * Note: if unique windows is in use, may return an existing window.
1063 FilerWindow *filer_opendir(const char *path, FilerWindow *src_win, const gchar *wm_class)
1065 FilerWindow *filer_window;
1066 char *real_path;
1067 DisplayStyle dstyle;
1068 DetailsType dtype;
1070 /* Get the real pathname of the directory and copy it */
1071 real_path = pathdup(path);
1073 if (o_unique_filer_windows.int_value)
1075 FilerWindow *same_dir_window;
1077 same_dir_window = find_filer_window(path, NULL);
1079 if (same_dir_window)
1081 gtk_window_present(GTK_WINDOW(same_dir_window->window));
1082 return same_dir_window;
1086 filer_window = g_new(FilerWindow, 1);
1087 filer_window->message = NULL;
1088 filer_window->minibuffer = NULL;
1089 filer_window->minibuffer_label = NULL;
1090 filer_window->minibuffer_area = NULL;
1091 filer_window->temp_show_hidden = FALSE;
1092 filer_window->sym_path = g_strdup(path);
1093 filer_window->real_path = real_path;
1094 filer_window->scanning = FALSE;
1095 filer_window->had_cursor = FALSE;
1096 filer_window->auto_select = NULL;
1097 filer_window->toolbar_text = NULL;
1098 filer_window->target_cb = NULL;
1099 filer_window->mini_type = MINI_NONE;
1100 filer_window->selection_state = GTK_STATE_INSENSITIVE;
1101 filer_window->toolbar = NULL;
1102 filer_window->toplevel_vbox = NULL;
1104 tidy_sympath(filer_window->sym_path);
1106 /* Finds the entry for this directory in the dir cache, creating
1107 * a new one if needed. This does not cause a scan to start,
1108 * so if a new entry is created then it will be empty.
1110 filer_window->directory = g_fscache_lookup(dir_cache, real_path);
1111 if (!filer_window->directory)
1113 delayed_error(_("Directory '%s' not found."), path);
1114 g_free(filer_window->real_path);
1115 g_free(filer_window->sym_path);
1116 g_free(filer_window);
1117 return NULL;
1120 filer_window->temp_item_selected = FALSE;
1121 filer_window->flags = (FilerFlags) 0;
1122 filer_window->details_type = DETAILS_SUMMARY;
1123 filer_window->display_style = UNKNOWN_STYLE;
1124 filer_window->thumb_queue = NULL;
1125 filer_window->max_thumbs = 0;
1127 if (src_win && o_display_inherit_options.int_value)
1129 filer_window->sort_fn = src_win->sort_fn;
1130 dstyle = src_win->display_style;
1131 dtype = src_win->details_type;
1132 filer_window->show_hidden = src_win->show_hidden;
1133 filer_window->show_thumbs = src_win->show_thumbs;
1135 else
1137 int i = o_display_sort_by.int_value;
1138 filer_window->sort_fn = i == 0 ? sort_by_name :
1139 i == 1 ? sort_by_type :
1140 i == 2 ? sort_by_date :
1141 sort_by_size;
1143 dstyle = o_display_size.int_value;
1144 dtype = o_display_details.int_value;
1145 filer_window->show_hidden =
1146 o_display_show_hidden.int_value;
1147 filer_window->show_thumbs =
1148 o_display_show_thumbs.int_value;
1151 /* Add all the user-interface elements & realise */
1152 filer_add_widgets(filer_window, wm_class);
1153 if (src_win)
1154 gtk_window_set_position(GTK_WINDOW(filer_window->window),
1155 GTK_WIN_POS_MOUSE);
1157 /* Connect to all the signal handlers */
1158 filer_add_signals(filer_window);
1160 display_set_layout(filer_window, dstyle, dtype);
1162 /* Open the window after a timeout, or when scanning stops.
1163 * Do this before attaching, because attach() might tell us to
1164 * stop scanning (if a scan isn't needed).
1166 filer_window->open_timeout = gtk_timeout_add(500,
1167 (GtkFunction) open_filer_window,
1168 filer_window);
1170 /* The view is created empty and then attach() is called, which
1171 * links the filer window to the entry in the directory cache we
1172 * looked up / created above.
1174 * The attach() function will immediately callback to the filer window
1175 * to deliver a list of all known entries in the directory (so,
1176 * the number of items will be known after attach() returns).
1178 * If the directory was not in the cache (because it hadn't been
1179 * opened it before) then the types and icons for the entries are
1180 * not know, but the list of names is.
1183 attach(filer_window);
1185 number_of_windows++;
1186 all_filer_windows = g_list_prepend(all_filer_windows, filer_window);
1188 return filer_window;
1191 /* This adds all the widgets to a new filer window. It is in a separate
1192 * function because filer_opendir() was getting too long...
1194 static void filer_add_widgets(FilerWindow *filer_window, const gchar *wm_class)
1196 GtkWidget *hbox, *vbox;
1198 /* Create the top-level window widget */
1199 filer_window->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1200 filer_set_title(filer_window);
1201 if (wm_class)
1202 gtk_window_set_wmclass(GTK_WINDOW(filer_window->window), wm_class, PROJECT);
1204 /* This property is cleared when the window is destroyed.
1205 * You can thus ref filer_window->window and use this to see
1206 * if the window no longer exists.
1208 g_object_set_data(G_OBJECT(filer_window->window),
1209 "filer_window", filer_window);
1211 /* Create this now to make the Adjustment before the View */
1212 filer_window->scrollbar = gtk_vscrollbar_new(NULL);
1214 /* The view is the area that actually displays the files */
1215 filer_window->view = VIEW(view_collection_new(filer_window));
1216 gtk_widget_show(GTK_WIDGET(filer_window->view));
1218 /* Scrollbar on the right, everything else on the left */
1219 hbox = gtk_hbox_new(FALSE, 0);
1220 gtk_container_add(GTK_CONTAINER(filer_window->window), hbox);
1222 vbox = gtk_vbox_new(FALSE, 0);
1223 gtk_box_pack_start_defaults(GTK_BOX(hbox), vbox);
1224 filer_window->toplevel_vbox = GTK_BOX(vbox);
1226 /* If we want a toolbar, create it now */
1227 toolbar_update_toolbar(filer_window);
1229 /* If there's a message that should be displayed in each window (eg
1230 * 'Running as root'), add it here.
1232 if (show_user_message)
1234 filer_window->message = gtk_label_new(show_user_message);
1235 gtk_box_pack_start(GTK_BOX(vbox), filer_window->message,
1236 FALSE, TRUE, 0);
1237 gtk_widget_show(filer_window->message);
1240 /* Now add the area for displaying the files */
1241 gtk_box_pack_start_defaults(GTK_BOX(vbox),
1242 GTK_WIDGET(filer_window->view));
1244 /* And the minibuffer (hidden to start with) */
1245 create_minibuffer(filer_window);
1246 gtk_box_pack_start(GTK_BOX(vbox), filer_window->minibuffer_area,
1247 FALSE, TRUE, 0);
1249 /* And the thumbnail progress bar (also hidden) */
1251 GtkWidget *cancel;
1253 filer_window->thumb_bar = gtk_hbox_new(FALSE, 2);
1254 gtk_box_pack_start(GTK_BOX(vbox), filer_window->thumb_bar,
1255 FALSE, TRUE, 0);
1257 filer_window->thumb_progress = gtk_progress_bar_new();
1259 gtk_box_pack_start(GTK_BOX(filer_window->thumb_bar),
1260 filer_window->thumb_progress, TRUE, TRUE, 0);
1262 cancel = gtk_button_new_with_label(_("Cancel"));
1263 GTK_WIDGET_UNSET_FLAGS(cancel, GTK_CAN_FOCUS);
1264 gtk_box_pack_start(GTK_BOX(filer_window->thumb_bar),
1265 cancel, FALSE, TRUE, 0);
1266 g_signal_connect_swapped(cancel, "clicked",
1267 G_CALLBACK(filer_cancel_thumbnails),
1268 filer_window);
1271 /* Put the scrollbar on the left of everything else... */
1272 gtk_box_pack_start(GTK_BOX(hbox),
1273 filer_window->scrollbar, FALSE, TRUE, 0);
1275 gtk_widget_show(hbox);
1276 gtk_widget_show(vbox);
1277 gtk_widget_show(filer_window->scrollbar);
1279 gtk_widget_realize(filer_window->window);
1281 gdk_window_set_role(filer_window->window->window,
1282 filer_window->sym_path);
1284 filer_window_set_size(filer_window, 4, 4);
1287 static void filer_add_signals(FilerWindow *filer_window)
1289 GtkTargetEntry target_table[] =
1291 {"text/uri-list", 0, TARGET_URI_LIST},
1292 {"STRING", 0, TARGET_STRING},
1293 {"COMPOUND_TEXT", 0, TARGET_STRING},/* XXX: Treats as STRING */
1296 /* Events on the top-level window */
1297 gtk_widget_add_events(filer_window->window, GDK_ENTER_NOTIFY);
1298 g_signal_connect(filer_window->window, "enter-notify-event",
1299 G_CALLBACK(pointer_in), filer_window);
1300 g_signal_connect(filer_window->window, "leave-notify-event",
1301 G_CALLBACK(pointer_out), filer_window);
1302 g_signal_connect(filer_window->window, "destroy",
1303 G_CALLBACK(filer_window_destroyed), filer_window);
1304 g_signal_connect(filer_window->window, "delete-event",
1305 G_CALLBACK(filer_window_delete), filer_window);
1307 g_signal_connect(filer_window->window, "selection_clear_event",
1308 G_CALLBACK(filer_lost_primary), filer_window);
1310 g_signal_connect(filer_window->window, "selection_get",
1311 G_CALLBACK(selection_get), filer_window);
1312 gtk_selection_add_targets(GTK_WIDGET(filer_window->window),
1313 GDK_SELECTION_PRIMARY,
1314 target_table,
1315 sizeof(target_table) / sizeof(*target_table));
1317 g_signal_connect(filer_window->window, "popup-menu",
1318 G_CALLBACK(popup_menu), filer_window);
1319 g_signal_connect(filer_window->window, "key_press_event",
1320 G_CALLBACK(key_press_event), filer_window);
1323 static gint clear_scanning_display(FilerWindow *filer_window)
1325 if (filer_exists(filer_window))
1326 filer_set_title(filer_window);
1327 return FALSE;
1330 static void set_scanning_display(FilerWindow *filer_window, gboolean scanning)
1332 if (scanning == filer_window->scanning)
1333 return;
1334 filer_window->scanning = scanning;
1336 if (scanning)
1337 filer_set_title(filer_window);
1338 else
1339 gtk_timeout_add(300, (GtkFunction) clear_scanning_display,
1340 filer_window);
1343 /* Note that filer_window may not exist after this call */
1344 void filer_update_dir(FilerWindow *filer_window, gboolean warning)
1346 if (may_rescan(filer_window, warning))
1347 dir_update(filer_window->directory, filer_window->sym_path);
1350 void filer_update_all(void)
1352 GList *next = all_filer_windows;
1354 while (next)
1356 FilerWindow *filer_window = (FilerWindow *) next->data;
1358 /* Updating directory may remove it from list -- stop sending
1359 * patches to move this line!
1361 next = next->next;
1363 filer_update_dir(filer_window, TRUE);
1367 /* Refresh the various caches even if we don't think we need to */
1368 void full_refresh(void)
1370 mount_update(TRUE);
1371 reread_mime_files();
1374 /* See whether a filer window with a given path already exists
1375 * and is different from diff.
1377 static FilerWindow *find_filer_window(const char *sym_path, FilerWindow *diff)
1379 GList *next;
1381 for (next = all_filer_windows; next; next = next->next)
1383 FilerWindow *filer_window = (FilerWindow *) next->data;
1385 if (filer_window != diff &&
1386 strcmp(sym_path, filer_window->sym_path) == 0)
1387 return filer_window;
1390 return NULL;
1393 /* This path has been mounted/umounted/deleted some files - update all dirs */
1394 void filer_check_mounted(const char *real_path)
1396 GList *next = all_filer_windows;
1397 gchar *parent;
1398 int len;
1400 len = strlen(real_path);
1402 while (next)
1404 FilerWindow *filer_window = (FilerWindow *) next->data;
1406 next = next->next;
1408 if (strncmp(real_path, filer_window->real_path, len) == 0)
1410 char s = filer_window->real_path[len];
1412 if (s == '/' || s == '\0')
1413 filer_update_dir(filer_window, FALSE);
1417 parent = g_path_get_dirname(real_path);
1418 refresh_dirs(parent);
1419 g_free(parent);
1421 icons_may_update(real_path);
1424 /* Close all windows displaying 'path' or subdirectories of 'path' */
1425 void filer_close_recursive(const char *path)
1427 GList *next = all_filer_windows;
1428 gchar *real;
1429 int len;
1431 real = pathdup(path);
1432 len = strlen(real);
1434 while (next)
1436 FilerWindow *filer_window = (FilerWindow *) next->data;
1438 next = next->next;
1440 if (strncmp(real, filer_window->real_path, len) == 0)
1442 char s = filer_window->real_path[len];
1444 if (len == 1 || s == '/' || s == '\0')
1445 gtk_widget_destroy(filer_window->window);
1450 /* Like minibuffer_show(), except that:
1451 * - It returns FALSE (to be used from an idle callback)
1452 * - It checks that the filer window still exists.
1454 static gboolean minibuffer_show_cb(FilerWindow *filer_window)
1456 if (filer_exists(filer_window))
1457 minibuffer_show(filer_window, MINI_PATH);
1458 return FALSE;
1461 /* TRUE iff filer_window points to an existing FilerWindow
1462 * structure.
1464 gboolean filer_exists(FilerWindow *filer_window)
1466 GList *next;
1468 for (next = all_filer_windows; next; next = next->next)
1470 FilerWindow *fw = (FilerWindow *) next->data;
1472 if (fw == filer_window)
1473 return TRUE;
1476 return FALSE;
1479 /* Make sure the window title is up-to-date */
1480 void filer_set_title(FilerWindow *filer_window)
1482 gchar *title = NULL;
1483 guchar *flags = "";
1485 if (filer_window->scanning || filer_window->show_hidden ||
1486 filer_window->show_thumbs)
1488 if (o_short_flag_names.int_value)
1490 flags = g_strconcat(" +",
1491 filer_window->scanning ? _("S") : "",
1492 filer_window->show_hidden ? _("A") : "",
1493 filer_window->show_thumbs ? _("T") : "",
1494 NULL);
1496 else
1498 flags = g_strconcat(" (",
1499 filer_window->scanning ? _("Scanning, ") : "",
1500 filer_window->show_hidden ? _("All, ") : "",
1501 filer_window->show_thumbs ? _("Thumbs, ") : "",
1502 NULL);
1503 flags[strlen(flags) - 2] = ')';
1507 if (not_local)
1508 title = g_strconcat("//", our_host_name(),
1509 filer_window->sym_path, flags, NULL);
1511 if (!title && home_dir_len > 1 &&
1512 strncmp(filer_window->sym_path, home_dir, home_dir_len) == 0)
1514 guchar sep = filer_window->sym_path[home_dir_len];
1516 if (sep == '\0' || sep == '/')
1517 title = g_strconcat("~",
1518 filer_window->sym_path + home_dir_len,
1519 flags,
1520 NULL);
1523 if (!title)
1524 title = g_strconcat(filer_window->sym_path, flags, NULL);
1526 ensure_utf8(&title);
1528 gtk_window_set_title(GTK_WINDOW(filer_window->window), title);
1530 g_free(title);
1532 if (flags[0] != '\0')
1533 g_free(flags);
1536 /* Reconnect to the same directory (used when the Show Hidden option is
1537 * toggled). This has the side-effect of updating the window title.
1539 void filer_detach_rescan(FilerWindow *filer_window)
1541 Directory *dir = filer_window->directory;
1543 g_object_ref(dir);
1544 detach(filer_window);
1545 filer_window->directory = dir;
1546 attach(filer_window);
1549 /* Puts the filer window into target mode. When an item is chosen,
1550 * fn(filer_window, iter, data) is called. 'reason' will be displayed
1551 * on the toolbar while target mode is active.
1553 * Use fn == NULL to cancel target mode.
1555 void filer_target_mode(FilerWindow *filer_window,
1556 TargetFunc fn,
1557 gpointer data,
1558 const char *reason)
1560 TargetFunc old_fn = filer_window->target_cb;
1562 if (fn != old_fn)
1563 gdk_window_set_cursor(
1564 GTK_WIDGET(filer_window->view)->window,
1565 fn ? crosshair : NULL);
1567 filer_window->target_cb = fn;
1568 filer_window->target_data = data;
1570 if (filer_window->toolbar_text == NULL)
1571 return;
1573 if (fn)
1574 gtk_label_set_text(
1575 GTK_LABEL(filer_window->toolbar_text), reason);
1576 else if (o_toolbar_info.int_value)
1578 if (old_fn)
1579 toolbar_update_info(filer_window);
1581 else
1582 gtk_label_set_text(GTK_LABEL(filer_window->toolbar_text), "");
1585 static void set_selection_state(FilerWindow *filer_window, gboolean normal)
1587 GtkStateType old_state = filer_window->selection_state;
1589 filer_window->selection_state = normal
1590 ? GTK_STATE_SELECTED : GTK_STATE_INSENSITIVE;
1592 if (old_state != filer_window->selection_state
1593 && view_count_selected(filer_window->view))
1594 gtk_widget_queue_draw(GTK_WIDGET(filer_window->view));
1597 void filer_cancel_thumbnails(FilerWindow *filer_window)
1599 gtk_widget_hide(filer_window->thumb_bar);
1601 destroy_glist(&filer_window->thumb_queue);
1602 filer_window->max_thumbs = 0;
1605 /* Generate the next thumb for this window. The window object is
1606 * unref'd when there is nothing more to do.
1607 * If the window no longer has a filer window, nothing is done.
1609 static gboolean filer_next_thumb_real(GObject *window)
1611 FilerWindow *filer_window;
1612 gchar *path;
1613 int done, total;
1615 filer_window = g_object_get_data(window, "filer_window");
1617 if (!filer_window)
1619 g_object_unref(window);
1620 return FALSE;
1623 if (!filer_window->thumb_queue)
1625 filer_cancel_thumbnails(filer_window);
1626 g_object_unref(window);
1627 return FALSE;
1630 total = filer_window->max_thumbs;
1631 done = total - g_list_length(filer_window->thumb_queue);
1633 path = (gchar *) filer_window->thumb_queue->data;
1635 pixmap_background_thumb(path, (GFunc) filer_next_thumb, window);
1637 filer_window->thumb_queue = g_list_remove(filer_window->thumb_queue,
1638 path);
1639 g_free(path);
1641 gtk_progress_bar_set_fraction(
1642 GTK_PROGRESS_BAR(filer_window->thumb_progress),
1643 done / (float) total);
1645 return FALSE;
1648 /* path is the thumb just loaded, if any.
1649 * window is unref'd (eventually).
1651 static void filer_next_thumb(GObject *window, const gchar *path)
1653 if (path)
1654 dir_force_update_path(path);
1656 gtk_idle_add((GtkFunction) filer_next_thumb_real, window);
1659 static void start_thumb_scanning(FilerWindow *filer_window)
1661 if (GTK_WIDGET_VISIBLE(filer_window->thumb_bar))
1662 return; /* Already scanning */
1664 gtk_widget_show_all(filer_window->thumb_bar);
1666 g_object_ref(G_OBJECT(filer_window->window));
1667 filer_next_thumb(G_OBJECT(filer_window->window), NULL);
1670 /* Set this image to be loaded some time in the future */
1671 void filer_create_thumb(FilerWindow *filer_window, const gchar *path)
1673 filer_window->max_thumbs++;
1675 filer_window->thumb_queue = g_list_append(filer_window->thumb_queue,
1676 g_strdup(path));
1678 if (filer_window->scanning)
1679 return; /* Will start when scan ends */
1681 start_thumb_scanning(filer_window);
1684 /* If thumbnail display is on, look through all the items in this directory
1685 * and start creating or updating the thumbnails as needed.
1687 void filer_create_thumbs(FilerWindow *filer_window)
1689 DirItem *item;
1690 ViewIter iter;
1692 if (!filer_window->show_thumbs)
1693 return;
1695 view_get_iter(filer_window->view, &iter, 0);
1697 while ((item = iter.next(&iter)))
1699 MaskedPixmap *pixmap;
1700 const guchar *path;
1701 gboolean found;
1703 if (item->base_type != TYPE_FILE)
1704 continue;
1706 if (strcmp(item->mime_type->media_type, "image") != 0)
1707 continue;
1709 path = make_path(filer_window->real_path, item->leafname);
1711 pixmap = g_fscache_lookup_full(pixmap_cache, path,
1712 FSCACHE_LOOKUP_ONLY_NEW, &found);
1713 if (pixmap)
1714 g_object_unref(pixmap);
1716 /* If we didn't get an image, it could be because:
1718 * - We're loading the image now. found is TRUE,
1719 * and we'll update the item later.
1720 * - We tried to load the image and failed. found
1721 * is TRUE.
1722 * - We haven't tried loading the image. found is
1723 * FALSE, and we start creating the thumb here.
1725 if (!found)
1726 filer_create_thumb(filer_window, path);
1730 static void filer_options_changed(void)
1732 if (o_short_flag_names.has_changed)
1734 GList *next;
1736 for (next = all_filer_windows; next; next = next->next)
1738 FilerWindow *filer_window = (FilerWindow *) next->data;
1740 filer_set_title(filer_window);
1745 /* Change to Large or Small icons depending on the number of items
1746 * in the directory, subject to options.
1748 static void set_style_by_number_of_items(FilerWindow *filer_window)
1750 int n;
1752 g_return_if_fail(filer_window != NULL);
1754 if (!o_filer_change_size.int_value)
1755 return; /* Don't auto-set style */
1757 if (filer_window->display_style != LARGE_ICONS &&
1758 filer_window->display_style != SMALL_ICONS)
1759 return; /* Only change between these two styles */
1761 n = view_count_items(filer_window->view);
1763 if (n >= o_filer_change_size_num.int_value)
1764 display_set_layout(filer_window, SMALL_ICONS,
1765 filer_window->details_type);
1766 else
1767 display_set_layout(filer_window, LARGE_ICONS,
1768 filer_window->details_type);
1771 /* Append interesting information to this GString */
1772 void filer_add_tip_details(FilerWindow *filer_window,
1773 GString *tip, DirItem *item)
1775 const guchar *fullpath = NULL;
1777 fullpath = make_path(filer_window->real_path, item->leafname);
1779 if (item->flags & ITEM_FLAG_SYMLINK)
1781 char *target;
1783 target = readlink_dup(fullpath);
1784 if (target)
1786 ensure_utf8(&target);
1788 g_string_append(tip, _("Symbolic link to "));
1789 g_string_append(tip, target);
1790 g_string_append_c(tip, '\n');
1791 g_free(target);
1795 if (item->flags & ITEM_FLAG_APPDIR)
1797 XMLwrapper *info;
1798 xmlNode *node;
1800 info = appinfo_get(fullpath, item);
1801 if (info && ((node = xml_get_section(info, NULL, "Summary"))))
1803 guchar *str;
1804 str = xmlNodeListGetString(node->doc,
1805 node->xmlChildrenNode, 1);
1806 if (str)
1808 g_string_append(tip, str);
1809 g_string_append_c(tip, '\n');
1810 g_free(str);
1813 if (info)
1814 g_object_unref(info);
1817 if (!g_utf8_validate(item->leafname, -1, NULL))
1818 g_string_append(tip,
1819 _("This filename is not valid UTF-8. "
1820 "You should rename it.\n"));