r3697: Some improvements to saved display settings
[rox-filer/dt.git] / ROX-Filer / src / filer.c
bloba60fe1062fd254d62a221c3637ef59358998d3cd
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2003, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
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>
33 #include <fnmatch.h>
35 #include <gtk/gtk.h>
36 #include <gdk/gdkx.h>
37 #include <gdk/gdkkeysyms.h>
39 #include "global.h"
41 #include "filer.h"
42 #include "display.h"
43 #include "main.h"
44 #include "fscache.h"
45 #include "support.h"
46 #include "gui_support.h"
47 #include "choices.h"
48 #include "pixmaps.h"
49 #include "menu.h"
50 #include "dnd.h"
51 #include "dir.h"
52 #include "diritem.h"
53 #include "run.h"
54 #include "type.h"
55 #include "options.h"
56 #include "minibuffer.h"
57 #include "icon.h"
58 #include "toolbar.h"
59 #include "bind.h"
60 #include "appinfo.h"
61 #include "mount.h"
62 #include "xml.h"
63 #include "view_iface.h"
64 #include "view_collection.h"
65 #include "view_details.h"
66 #include "action.h"
67 #include "bookmarks.h"
69 static XMLwrapper *groups = NULL;
71 /* Item we are about to display a tooltip for */
72 static DirItem *tip_item = NULL;
74 /* The window which the motion event for the tooltip came from. Use this
75 * to get the correct widget for finding the item under the pointer.
77 static GdkWindow *motion_window = NULL;
79 /* This is rather badly named. It's actually the filer window which received
80 * the last key press or Menu click event.
82 FilerWindow *window_with_focus = NULL;
84 GList *all_filer_windows = NULL;
86 static GHashTable *window_with_id = NULL;
88 static FilerWindow *window_with_primary = NULL;
90 static GHashTable *settings_table=NULL;
92 typedef struct {
93 gchar *path;
95 guint flags; /* Which parts are valid, see below */
97 gint x, y;
98 gint width, height;
99 gboolean show_hidden;
100 ViewType view_type;
101 SortType sort_type;
102 GtkSortType sort_order;
103 gboolean show_thumbs;
105 DetailsType details_type;
106 DisplayStyle display_style;
108 FilterType filter_type;
109 char *filter;
110 } Settings;
112 enum settings_flags{
113 SET_POSITION=1, /* x, y */
114 SET_SIZE=2, /* width, height */
115 SET_HIDDEN=4, /* show_hidden */
116 SET_STYLE=8, /* display_style */
117 SET_SORT=16, /* sort_type, sort_order */
118 SET_DETAILS=32, /* view_type, details_type */
119 SET_THUMBS=64, /* show_thumbs */
120 SET_FILTER=128, /* filter_type, filter */
123 /* Static prototypes */
124 static void attach(FilerWindow *filer_window);
125 static void detach(FilerWindow *filer_window);
126 static void filer_window_destroyed(GtkWidget *widget,
127 FilerWindow *filer_window);
128 static void update_display(Directory *dir,
129 DirAction action,
130 GPtrArray *items,
131 FilerWindow *filer_window);
132 static void set_scanning_display(FilerWindow *filer_window, gboolean scanning);
133 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning);
134 static gboolean minibuffer_show_cb(FilerWindow *filer_window);
135 static FilerWindow *find_filer_window(const char *sym_path, FilerWindow *diff);
136 static void filer_add_widgets(FilerWindow *filer_window, const gchar *wm_class);
137 static void filer_add_signals(FilerWindow *filer_window);
139 static void set_selection_state(FilerWindow *filer_window, gboolean normal);
140 static void filer_next_thumb(GObject *window, const gchar *path);
141 static void start_thumb_scanning(FilerWindow *filer_window);
142 static void filer_options_changed(void);
143 static void drag_end(GtkWidget *widget, GdkDragContext *context,
144 FilerWindow *filer_window);
145 static void drag_leave(GtkWidget *widget,
146 GdkDragContext *context,
147 guint32 time,
148 FilerWindow *filer_window);
149 static gboolean drag_motion(GtkWidget *widget,
150 GdkDragContext *context,
151 gint x,
152 gint y,
153 guint time,
154 FilerWindow *filer_window);
156 static void load_settings(void);
157 static void save_settings(void);
158 static void check_settings(FilerWindow *filer_window);
160 static GdkCursor *busy_cursor = NULL;
161 static GdkCursor *crosshair = NULL;
163 /* Indicates whether the filer's display is different to the machine it
164 * is actually running on.
166 static gboolean not_local = FALSE;
168 static Option o_short_flag_names;
169 static Option o_filer_view_type;
170 Option o_filer_auto_resize, o_unique_filer_windows;
171 Option o_filer_size_limit;
173 void filer_init(void)
175 const gchar *ohost;
176 const gchar *dpy;
177 gchar *dpyhost, *tmp;
179 option_add_int(&o_filer_size_limit, "filer_size_limit", 75);
180 option_add_int(&o_filer_auto_resize, "filer_auto_resize",
181 RESIZE_ALWAYS);
182 option_add_int(&o_unique_filer_windows, "filer_unique_windows", 0);
184 option_add_int(&o_short_flag_names, "filer_short_flag_names", FALSE);
186 option_add_int(&o_filer_view_type, "filer_view_type",
187 VIEW_TYPE_COLLECTION);
189 option_add_notify(filer_options_changed);
191 busy_cursor = gdk_cursor_new(GDK_WATCH);
192 crosshair = gdk_cursor_new(GDK_CROSSHAIR);
194 window_with_id = g_hash_table_new_full(g_str_hash, g_str_equal,
195 NULL, NULL);
197 /* Is the display on the local machine, or are we being
198 * run remotely? See filer_set_title().
200 ohost = our_host_name();
201 dpy = gdk_get_display();
202 dpyhost = g_strdup(dpy);
203 tmp = strchr(dpyhost, ':');
204 if (tmp)
205 *tmp = '\0';
207 if (dpyhost[0] && strcmp(ohost, dpyhost) != 0)
209 /* Try the cannonical name for dpyhost (see our_host_name()
210 * in support.c).
212 struct hostent *ent;
214 ent = gethostbyname(dpyhost);
215 if (!ent || strcmp(ohost, ent->h_name) != 0)
216 not_local = TRUE;
219 g_free(dpyhost);
221 load_settings();
224 static gboolean if_deleted(gpointer item, gpointer removed)
226 int i = ((GPtrArray *) removed)->len;
227 DirItem **r = (DirItem **) ((GPtrArray *) removed)->pdata;
228 char *leafname = ((DirItem *) item)->leafname;
230 while (i--)
232 if (strcmp(leafname, r[i]->leafname) == 0)
233 return TRUE;
236 return FALSE;
239 #define DECOR_BORDER 32
241 /* Resize the filer window to w x h pixels, plus border (not clamped).
242 * If triggered by a key event, warp the pointer (for SloppyFocus users).
244 void filer_window_set_size(FilerWindow *filer_window, int w, int h)
246 GdkEvent *event;
247 GtkWidget *window;
249 g_return_if_fail(filer_window != NULL);
251 if (filer_window->scrollbar)
252 w += filer_window->scrollbar->allocation.width;
254 if (o_toolbar.int_value != TOOLBAR_NONE)
255 h += filer_window->toolbar->allocation.height;
256 if (filer_window->message)
257 h += filer_window->message->allocation.height;
259 window = filer_window->window;
261 if (GTK_WIDGET_VISIBLE(window))
263 gint x, y, m;
264 GtkRequisition *req = &window->requisition;
265 GdkWindow *gdk_window = window->window;
267 w = MAX(req->width, w);
268 h = MAX(req->height, h);
269 gdk_window_get_pointer(NULL, &x, &y, NULL);
270 m = gdk_screen_get_monitor_at_point(gdk_screen_get_default(),
271 x, y);
272 gdk_window_get_position(gdk_window, &x, &y);
274 if (x + w + DECOR_BORDER >
275 monitor_geom[m].x + monitor_geom[m].width ||
276 y + h + DECOR_BORDER >
277 monitor_geom[m].y + monitor_geom[m].height)
279 if (x + w + DECOR_BORDER >
280 monitor_geom[m].x + monitor_geom[m].width)
282 x = monitor_geom[m].x + monitor_geom[m].width -
283 w - 4 - DECOR_BORDER;
285 if (y + h + DECOR_BORDER >
286 monitor_geom[m].y + monitor_geom[m].height)
288 y = monitor_geom[m].y + monitor_geom[m].height -
289 h - 4 - DECOR_BORDER;
291 gdk_window_move_resize(gdk_window, x, y, w, h);
293 else
294 gdk_window_resize(gdk_window, w, h);
296 else
297 gtk_window_set_default_size(GTK_WINDOW(window), w, h);
299 event = gtk_get_current_event();
300 if (event && event->type == GDK_KEY_PRESS)
302 int x, y;
303 int nx, ny;
305 GdkWindow *win = filer_window->window->window;
307 gdk_window_get_pointer(filer_window->window->window,
308 &x, &y, NULL);
310 nx = CLAMP(x, 4, w - 4);
311 ny = CLAMP(y, 4, h - 4);
313 if (nx != x || ny != y)
315 XWarpPointer(gdk_x11_drawable_get_xdisplay(win),
316 None,
317 gdk_x11_drawable_get_xid(win),
318 0, 0, 0, 0,
319 nx, ny);
324 /* Called on a timeout while scanning or when scanning ends
325 * (whichever happens first).
327 static gint open_filer_window(FilerWindow *filer_window)
329 Settings *dir_settings = (Settings *) g_hash_table_lookup(settings_table,
330 filer_window->sym_path);
331 gboolean force_resize = !(o_filer_auto_resize.int_value == RESIZE_NEVER && dir_settings && dir_settings->flags & SET_POSITION);
333 view_style_changed(filer_window->view, 0);
335 if (filer_window->open_timeout)
337 g_source_remove(filer_window->open_timeout);
338 filer_window->open_timeout = 0;
341 if (!GTK_WIDGET_VISIBLE(filer_window->window))
343 display_set_actual_size(filer_window, force_resize);
344 gtk_widget_show(filer_window->window);
347 return FALSE;
350 /* Look through all items we want to display, and queue a recheck on any
351 * that require it.
353 static void queue_interesting(FilerWindow *filer_window)
355 DirItem *item;
356 ViewIter iter;
358 view_get_iter(filer_window->view, &iter, 0);
359 while ((item = iter.next(&iter)))
361 if (item->flags & ITEM_FLAG_NEED_RESCAN_QUEUE)
362 dir_queue_recheck(filer_window->directory, item);
366 static void update_display(Directory *dir,
367 DirAction action,
368 GPtrArray *items,
369 FilerWindow *filer_window)
371 ViewIface *view = (ViewIface *) filer_window->view;
373 switch (action)
375 case DIR_ADD:
376 view_add_items(view, items);
377 /* Open and resize if currently hidden */
378 open_filer_window(filer_window);
379 break;
380 case DIR_REMOVE:
381 view_delete_if(view, if_deleted, items);
382 toolbar_update_info(filer_window);
383 break;
384 case DIR_START_SCAN:
385 set_scanning_display(filer_window, TRUE);
386 toolbar_update_info(filer_window);
387 break;
388 case DIR_END_SCAN:
389 if (filer_window->window->window)
390 gdk_window_set_cursor(
391 filer_window->window->window,
392 NULL);
393 set_scanning_display(filer_window, FALSE);
394 toolbar_update_info(filer_window);
395 open_filer_window(filer_window);
397 if (filer_window->had_cursor &&
398 !view_cursor_visible(view))
400 ViewIter start;
401 view_get_iter(view, &start, 0);
402 if (start.next(&start))
403 view_cursor_to_iter(view, &start);
404 view_show_cursor(view);
405 filer_window->had_cursor = FALSE;
407 if (filer_window->auto_select)
408 display_set_autoselect(filer_window,
409 filer_window->auto_select);
410 null_g_free(&filer_window->auto_select);
412 filer_create_thumbs(filer_window);
414 if (filer_window->thumb_queue)
415 start_thumb_scanning(filer_window);
416 break;
417 case DIR_UPDATE:
418 view_update_items(view, items);
419 break;
420 case DIR_ERROR_CHANGED:
421 filer_set_title(filer_window);
422 break;
423 case DIR_QUEUE_INTERESTING:
424 queue_interesting(filer_window);
425 break;
429 static void attach(FilerWindow *filer_window)
431 gdk_window_set_cursor(filer_window->window->window, busy_cursor);
432 view_clear(filer_window->view);
433 filer_window->scanning = TRUE;
434 dir_attach(filer_window->directory, (DirCallback) update_display,
435 filer_window);
436 filer_set_title(filer_window);
437 bookmarks_add_history(filer_window->sym_path);
439 if (filer_window->directory->error)
441 if (spring_in_progress)
442 g_printerr(_("Error scanning '%s':\n%s\n"),
443 filer_window->sym_path,
444 filer_window->directory->error);
445 else
446 delayed_error(_("Error scanning '%s':\n%s"),
447 filer_window->sym_path,
448 filer_window->directory->error);
452 static void detach(FilerWindow *filer_window)
454 g_return_if_fail(filer_window->directory != NULL);
456 dir_detach(filer_window->directory,
457 (DirCallback) update_display, filer_window);
458 g_object_unref(filer_window->directory);
459 filer_window->directory = NULL;
462 /* Returns TRUE to prevent closing the window. May offer to unmount a
463 * device.
465 gboolean filer_window_delete(GtkWidget *window,
466 GdkEvent *unused, /* (may be NULL) */
467 FilerWindow *filer_window)
469 if (mount_is_user_mounted(filer_window->real_path))
471 int action;
473 action = get_choice(PROJECT,
474 _("Do you want to unmount this device?\n\n"
475 "Unmounting a device makes it safe to remove "
476 "the disk."), 3,
477 GTK_STOCK_CANCEL, NULL,
478 GTK_STOCK_CLOSE, NULL,
479 ROX_STOCK_MOUNT, _("Unmount"));
481 if (action == 0)
482 return TRUE; /* Cancel close operation */
484 if (action == 2)
486 GList *list;
488 list = g_list_prepend(NULL, filer_window->sym_path);
489 action_mount(list, FALSE, TRUE);
490 g_list_free(list);
492 return TRUE;
496 return FALSE;
499 static void filer_window_destroyed(GtkWidget *widget, FilerWindow *filer_window)
501 all_filer_windows = g_list_remove(all_filer_windows, filer_window);
503 g_object_set_data(G_OBJECT(widget), "filer_window", NULL);
505 if (window_with_primary == filer_window)
506 window_with_primary = NULL;
508 if (window_with_focus == filer_window)
510 menu_popdown();
511 window_with_focus = NULL;
514 if (filer_window->directory)
515 detach(filer_window);
517 if (filer_window->open_timeout)
519 g_source_remove(filer_window->open_timeout);
520 filer_window->open_timeout = 0;
523 if (filer_window->auto_scroll != -1)
525 g_source_remove(filer_window->auto_scroll);
526 filer_window->auto_scroll = -1;
529 if (filer_window->thumb_queue)
530 destroy_glist(&filer_window->thumb_queue);
532 tooltip_show(NULL);
534 filer_set_id(filer_window, NULL);
536 if(filer_window->filter_string)
537 g_free(filer_window->filter_string);
538 if(filer_window->regexp)
539 g_free(filer_window->regexp);
541 g_free(filer_window->auto_select);
542 g_free(filer_window->real_path);
543 g_free(filer_window->sym_path);
544 g_free(filer_window);
546 one_less_window();
549 /* Returns TRUE iff the directory still exists. */
550 static gboolean may_rescan(FilerWindow *filer_window, gboolean warning)
552 Directory *dir;
554 g_return_val_if_fail(filer_window != NULL, FALSE);
556 /* We do a fresh lookup (rather than update) because the inode may
557 * have changed.
559 dir = g_fscache_lookup(dir_cache, filer_window->real_path);
560 if (!dir)
562 if (warning)
563 info_message(_("Directory missing/deleted"));
564 gtk_widget_destroy(filer_window->window);
565 return FALSE;
567 if (dir == filer_window->directory)
568 g_object_unref(dir);
569 else
571 detach(filer_window);
572 filer_window->directory = dir;
573 attach(filer_window);
576 return TRUE;
579 /* No items are now selected. This might be because another app claimed
580 * the selection or because the user unselected all the items.
582 void filer_lost_selection(FilerWindow *filer_window, guint time)
584 if (window_with_primary == filer_window)
586 window_with_primary = NULL;
587 gtk_selection_owner_set(NULL, GDK_SELECTION_PRIMARY, time);
591 /* Another app has claimed the primary selection */
592 static void filer_lost_primary(GtkWidget *window,
593 GdkEventSelection *event,
594 gpointer user_data)
596 FilerWindow *filer_window = (FilerWindow *) user_data;
598 if (window_with_primary && window_with_primary == filer_window)
600 window_with_primary = NULL;
601 set_selection_state(filer_window, FALSE);
605 /* Someone wants us to send them the selection */
606 static void selection_get(GtkWidget *widget,
607 GtkSelectionData *selection_data,
608 guint info,
609 guint time,
610 gpointer data)
612 GString *reply, *header;
613 FilerWindow *filer_window = (FilerWindow *) data;
614 ViewIter iter;
615 DirItem *item;
617 reply = g_string_new(NULL);
618 header = g_string_new(NULL);
620 switch (info)
622 case TARGET_STRING:
623 g_string_printf(header, " %s",
624 make_path(filer_window->sym_path, ""));
625 break;
626 case TARGET_URI_LIST:
627 g_string_printf(header, " file://%s%s",
628 our_host_name_for_dnd(),
629 make_path(filer_window->sym_path, ""));
630 break;
633 view_get_iter(filer_window->view, &iter, VIEW_ITER_SELECTED);
635 while ((item = iter.next(&iter)))
637 g_string_append(reply, header->str);
638 g_string_append(reply, item->leafname);
641 if (reply->len > 0)
642 gtk_selection_data_set(selection_data, xa_string,
643 8, reply->str + 1, reply->len - 1);
644 else
646 g_warning("Attempt to paste empty selection!");
647 gtk_selection_data_set(selection_data, xa_string, 8, "", 0);
650 g_string_free(reply, TRUE);
651 g_string_free(header, TRUE);
654 /* Selection has been changed -- try to grab the primary selection
655 * if we don't have it. Also called when clicking on an insensitive selection
656 * to regain primary.
657 * Also updates toolbar info.
659 void filer_selection_changed(FilerWindow *filer_window, gint time)
661 toolbar_update_info(filer_window);
663 if (window_with_primary == filer_window)
664 return; /* Already got primary */
666 if (!view_count_selected(filer_window->view))
667 return; /* Nothing selected */
669 if (filer_window->temp_item_selected == FALSE &&
670 gtk_selection_owner_set(GTK_WIDGET(filer_window->window),
671 GDK_SELECTION_PRIMARY,
672 time))
674 window_with_primary = filer_window;
675 set_selection_state(filer_window, TRUE);
677 else
678 set_selection_state(filer_window, FALSE);
681 /* Open the item (or add it to the shell command minibuffer) */
682 void filer_openitem(FilerWindow *filer_window, ViewIter *iter, OpenFlags flags)
684 gboolean shift = (flags & OPEN_SHIFT) != 0;
685 gboolean close_mini = flags & OPEN_FROM_MINI;
686 gboolean close_window = (flags & OPEN_CLOSE_WINDOW) != 0;
687 DirItem *item;
688 const guchar *full_path;
689 gboolean wink = TRUE;
690 Directory *old_dir;
692 g_return_if_fail(filer_window != NULL && iter != NULL);
694 item = iter->peek(iter);
696 g_return_if_fail(item != NULL);
698 if (filer_window->mini_type == MINI_SHELL)
700 minibuffer_add(filer_window, item->leafname);
701 return;
704 if (item->base_type == TYPE_UNKNOWN)
705 dir_update_item(filer_window->directory, item->leafname);
707 if (item->base_type == TYPE_DIRECTORY)
709 /* Never close a filer window when opening a directory
710 * (click on a dir or click on an app with shift).
712 if (shift || !(item->flags & ITEM_FLAG_APPDIR))
713 close_window = FALSE;
716 full_path = make_path(filer_window->sym_path, item->leafname);
717 if (shift && (item->flags & ITEM_FLAG_SYMLINK))
718 wink = FALSE;
720 old_dir = filer_window->directory;
721 if (run_diritem(full_path, item,
722 flags & OPEN_SAME_WINDOW ? filer_window : NULL,
723 filer_window,
724 shift))
726 if (old_dir != filer_window->directory)
727 return;
729 if (close_window)
730 gtk_widget_destroy(filer_window->window);
731 else
733 if (wink)
734 view_wink_item(filer_window->view, iter);
735 if (close_mini)
736 minibuffer_hide(filer_window);
741 static gint pointer_in(GtkWidget *widget,
742 GdkEventCrossing *event,
743 FilerWindow *filer_window)
745 may_rescan(filer_window, TRUE);
746 return FALSE;
749 static gint pointer_out(GtkWidget *widget,
750 GdkEventCrossing *event,
751 FilerWindow *filer_window)
753 tooltip_show(NULL);
754 return FALSE;
757 /* Move the cursor to the next selected item in direction 'dir'
758 * (+1 or -1).
760 void filer_next_selected(FilerWindow *filer_window, int dir)
762 ViewIter iter, cursor;
763 gboolean have_cursor;
764 ViewIface *view = filer_window->view;
766 g_return_if_fail(dir == 1 || dir == -1);
768 view_get_cursor(view, &cursor);
769 have_cursor = cursor.peek(&cursor) != NULL;
771 view_get_iter(view, &iter,
772 VIEW_ITER_SELECTED |
773 (have_cursor ? VIEW_ITER_FROM_CURSOR : 0) |
774 (dir < 0 ? VIEW_ITER_BACKWARDS : 0));
776 if (have_cursor && view_get_selected(view, &cursor))
777 iter.next(&iter); /* Skip the cursor itself */
779 if (iter.next(&iter))
780 view_cursor_to_iter(view, &iter);
781 else
782 gdk_beep();
784 return;
787 static void return_pressed(FilerWindow *filer_window, GdkEventKey *event)
789 TargetFunc cb = filer_window->target_cb;
790 gpointer data = filer_window->target_data;
791 OpenFlags flags = 0;
792 ViewIter iter;
794 filer_target_mode(filer_window, NULL, NULL, NULL);
796 view_get_cursor(filer_window->view, &iter);
797 if (!iter.peek(&iter))
798 return;
800 if (cb)
802 cb(filer_window, &iter, data);
803 return;
806 if (event->state & GDK_SHIFT_MASK)
807 flags |= OPEN_SHIFT;
808 if (event->state & GDK_MOD1_MASK)
809 flags |= OPEN_CLOSE_WINDOW;
810 else
811 flags |= OPEN_SAME_WINDOW;
813 filer_openitem(filer_window, &iter, flags);
816 /* Makes sure that 'groups' is up-to-date, reloading from file if it has
817 * changed. If no groups were loaded and there is no file then initialised
818 * groups to an empty document.
819 * Return the node for the 'name' group.
821 static xmlNode *group_find(char *name)
823 xmlNode *node;
824 gchar *path;
826 /* Update the groups, if possible */
827 path = choices_find_path_load("Groups.xml", PROJECT);
828 if (path)
830 XMLwrapper *wrapper;
831 wrapper = xml_cache_load(path);
832 if (wrapper)
834 if (groups)
835 g_object_unref(groups);
836 groups = wrapper;
839 g_free(path);
842 if (!groups)
844 groups = xml_new(NULL);
845 groups->doc = xmlNewDoc("1.0");
847 xmlDocSetRootElement(groups->doc,
848 xmlNewDocNode(groups->doc, NULL, "groups", NULL));
849 return NULL;
852 node = xmlDocGetRootElement(groups->doc);
854 for (node = node->xmlChildrenNode; node; node = node->next)
856 guchar *gid;
858 gid = xmlGetProp(node, "name");
860 if (!gid)
861 continue;
863 if (strcmp(name, gid) != 0)
864 continue;
866 g_free(gid);
868 return node;
871 return NULL;
874 static void group_save(FilerWindow *filer_window, char *name)
876 xmlNode *group;
877 guchar *save_path;
878 DirItem *item;
879 ViewIter iter;
881 group = group_find(name);
882 if (group)
884 xmlUnlinkNode(group);
885 xmlFreeNode(group);
887 group = xmlNewChild(xmlDocGetRootElement(groups->doc),
888 NULL, "group", NULL);
889 xmlSetProp(group, "name", name);
891 xmlNewTextChild(group, NULL, "directory", filer_window->sym_path);
893 view_get_iter(filer_window->view, &iter, VIEW_ITER_SELECTED);
895 while ((item = iter.next(&iter)))
896 xmlNewTextChild(group, NULL, "item", item->leafname);
898 save_path = choices_find_path_save("Groups.xml", PROJECT, TRUE);
899 if (save_path)
901 save_xml_file(groups->doc, save_path);
902 g_free(save_path);
906 static gboolean group_restore_cb(ViewIter *iter, gpointer data)
908 GHashTable *in_group = (GHashTable *) data;
910 return g_hash_table_lookup(in_group,
911 iter->peek(iter)->leafname) != NULL;
914 static void group_restore(FilerWindow *filer_window, char *name)
916 GHashTable *in_group;
917 char *path;
918 xmlNode *group, *node;
920 group = group_find(name);
922 if (!group)
924 report_error(_("Group %s is not set. Select some files "
925 "and press Ctrl+%s to set the group. Press %s "
926 "on its own to reselect the files later.\n"
927 "Make sure NumLock is on if you use the keypad."),
928 name, name, name);
929 return;
932 node = get_subnode(group, NULL, "directory");
933 g_return_if_fail(node != NULL);
934 path = xmlNodeListGetString(groups->doc, node->xmlChildrenNode, 1);
935 g_return_if_fail(path != NULL);
937 if (strcmp(path, filer_window->sym_path) != 0)
938 filer_change_to(filer_window, path, NULL);
939 g_free(path);
941 in_group = g_hash_table_new(g_str_hash, g_str_equal);
942 for (node = group->xmlChildrenNode; node; node = node->next)
944 gchar *leaf;
945 if (node->type != XML_ELEMENT_NODE)
946 continue;
947 if (strcmp(node->name, "item") != 0)
948 continue;
950 leaf = xmlNodeListGetString(groups->doc,
951 node->xmlChildrenNode, 1);
952 if (!leaf)
953 g_warning("Missing leafname!\n");
954 else
955 g_hash_table_insert(in_group, leaf, filer_window);
958 view_select_if(filer_window->view, &group_restore_cb, in_group);
960 g_hash_table_foreach(in_group, (GHFunc) g_free, NULL);
961 g_hash_table_destroy(in_group);
964 static gboolean popup_menu(GtkWidget *widget, FilerWindow *filer_window)
966 ViewIter iter;
968 view_get_cursor(filer_window->view, &iter);
970 show_filer_menu(filer_window, NULL, &iter);
972 return TRUE;
975 void filer_window_toggle_cursor_item_selected(FilerWindow *filer_window)
977 ViewIface *view = filer_window->view;
978 ViewIter iter;
980 view_get_iter(view, &iter, VIEW_ITER_FROM_CURSOR);
981 if (!iter.next(&iter))
982 return; /* No cursor */
984 if (view_get_selected(view, &iter))
985 view_set_selected(view, &iter, FALSE);
986 else
987 view_set_selected(view, &iter, TRUE);
989 if (iter.next(&iter))
990 view_cursor_to_iter(view, &iter);
993 gint filer_key_press_event(GtkWidget *widget,
994 GdkEventKey *event,
995 FilerWindow *filer_window)
997 ViewIface *view = filer_window->view;
998 ViewIter cursor;
999 GtkWidget *focus = GTK_WINDOW(widget)->focus_widget;
1000 guint key = event->keyval;
1001 char group[2] = "1";
1003 window_with_focus = filer_window;
1005 /* Delay setting up the keys until now to speed loading... */
1006 if (ensure_filer_menu())
1008 /* Gtk updates in an idle-handler, so force a recheck now */
1009 g_signal_emit_by_name(widget, "keys_changed");
1012 if (focus && focus == filer_window->minibuffer)
1013 if (gtk_widget_event(focus, (GdkEvent *) event))
1014 return TRUE; /* Handled */
1016 if (!focus)
1017 gtk_widget_grab_focus(GTK_WIDGET(view));
1019 view_get_cursor(view, &cursor);
1020 if (!cursor.peek(&cursor) && (key == GDK_Up || key == GDK_Down))
1022 ViewIter iter;
1023 view_get_iter(view, &iter, 0);
1024 if (iter.next(&iter))
1025 view_cursor_to_iter(view, &iter);
1026 gtk_widget_grab_focus(GTK_WIDGET(view)); /* Needed? */
1027 return TRUE;
1030 switch (key)
1032 case GDK_Escape:
1033 filer_target_mode(filer_window, NULL, NULL, NULL);
1034 view_cursor_to_iter(filer_window->view, NULL);
1035 view_clear_selection(filer_window->view);
1036 return FALSE;
1037 case GDK_Return:
1038 return_pressed(filer_window, event);
1039 break;
1040 case GDK_ISO_Left_Tab:
1041 filer_next_selected(filer_window, -1);
1042 break;
1043 case GDK_Tab:
1044 filer_next_selected(filer_window, 1);
1045 break;
1046 case GDK_BackSpace:
1047 change_to_parent(filer_window);
1048 break;
1049 case GDK_backslash:
1051 ViewIter iter;
1053 tooltip_show(NULL);
1055 view_get_cursor(filer_window->view, &iter);
1056 show_filer_menu(filer_window,
1057 (GdkEvent *) event, &iter);
1058 break;
1060 case ' ':
1061 filer_window_toggle_cursor_item_selected(filer_window);
1062 break;
1063 default:
1064 if (key >= GDK_0 && key <= GDK_9)
1065 group[0] = key - GDK_0 + '0';
1066 else if (key >= GDK_KP_0 && key <= GDK_KP_9)
1067 group[0] = key - GDK_KP_0 + '0';
1068 else
1070 if (focus && focus != widget &&
1071 gtk_widget_get_toplevel(focus) == widget)
1072 if (gtk_widget_event(focus,
1073 (GdkEvent *) event))
1074 return TRUE; /* Handled */
1075 return FALSE;
1078 if (event->state & GDK_CONTROL_MASK)
1079 group_save(filer_window, group);
1080 else
1081 group_restore(filer_window, group);
1084 return TRUE;
1087 void filer_open_parent(FilerWindow *filer_window)
1089 char *dir;
1090 const char *current = filer_window->sym_path;
1092 if (current[0] == '/' && current[1] == '\0')
1093 return; /* Already in the root */
1095 dir = g_path_get_dirname(current);
1096 filer_opendir(dir, filer_window, NULL);
1097 g_free(dir);
1100 void change_to_parent(FilerWindow *filer_window)
1102 char *dir;
1103 const char *current = filer_window->sym_path;
1105 if (current[0] == '/' && current[1] == '\0')
1106 return; /* Already in the root */
1108 dir = g_path_get_dirname(current);
1109 filer_change_to(filer_window, dir, g_basename(current));
1110 g_free(dir);
1113 /* Removes trailing /s from path (modified in place) */
1114 static void tidy_sympath(gchar *path)
1116 int l;
1118 g_return_if_fail(path != NULL);
1120 l = strlen(path);
1121 while (l > 1 && path[l - 1] == '/')
1123 l--;
1124 path[l] = '\0';
1128 /* Make filer_window display path. When finished, highlight item 'from', or
1129 * the first item if from is NULL. If there is currently no cursor then
1130 * simply wink 'from' (if not NULL).
1131 * If the cause was a key event and we resize, warp the pointer.
1133 void filer_change_to(FilerWindow *filer_window,
1134 const char *path, const char *from)
1136 char *from_dup;
1137 char *sym_path, *real_path;
1138 Directory *new_dir;
1140 g_return_if_fail(filer_window != NULL);
1142 filer_cancel_thumbnails(filer_window);
1144 tooltip_show(NULL);
1146 sym_path = g_strdup(path);
1147 real_path = pathdup(path);
1148 new_dir = g_fscache_lookup(dir_cache, real_path);
1150 if (!new_dir)
1152 delayed_error(_("Directory '%s' is not accessible"),
1153 sym_path);
1154 g_free(real_path);
1155 g_free(sym_path);
1156 return;
1159 if (o_unique_filer_windows.int_value && !spring_in_progress)
1161 FilerWindow *fw;
1163 fw = find_filer_window(sym_path, filer_window);
1164 if (fw)
1165 gtk_widget_destroy(fw->window);
1168 from_dup = from && *from ? g_strdup(from) : NULL;
1170 detach(filer_window);
1171 g_free(filer_window->real_path);
1172 g_free(filer_window->sym_path);
1173 filer_window->real_path = real_path;
1174 filer_window->sym_path = sym_path;
1175 tidy_sympath(filer_window->sym_path);
1177 filer_window->directory = new_dir;
1179 g_free(filer_window->auto_select);
1180 filer_window->auto_select = from_dup;
1182 filer_window->had_cursor = filer_window->had_cursor ||
1183 view_cursor_visible(filer_window->view);
1185 filer_set_title(filer_window);
1186 if (filer_window->window->window)
1187 gdk_window_set_role(filer_window->window->window,
1188 filer_window->sym_path);
1189 view_cursor_to_iter(filer_window->view, NULL);
1191 attach(filer_window);
1193 check_settings(filer_window);
1195 display_set_actual_size(filer_window, FALSE);
1197 if (o_filer_auto_resize.int_value == RESIZE_ALWAYS)
1198 view_autosize(filer_window->view);
1200 if (filer_window->mini_type == MINI_PATH)
1201 g_idle_add((GSourceFunc) minibuffer_show_cb, filer_window);
1204 /* Returns a list containing the full (sym) pathname of every selected item.
1205 * You must g_free() each item in the list.
1207 GList *filer_selected_items(FilerWindow *filer_window)
1209 GList *retval = NULL;
1210 guchar *dir = filer_window->sym_path;
1211 ViewIter iter;
1212 DirItem *item;
1214 view_get_iter(filer_window->view, &iter, VIEW_ITER_SELECTED);
1215 while ((item = iter.next(&iter)))
1217 retval = g_list_prepend(retval,
1218 g_strdup(make_path(dir, item->leafname)));
1221 return g_list_reverse(retval);
1224 /* Return the single selected item. Error if nothing is selected. */
1225 DirItem *filer_selected_item(FilerWindow *filer_window)
1227 ViewIter iter;
1228 DirItem *item;
1230 view_get_iter(filer_window->view, &iter, VIEW_ITER_SELECTED);
1232 item = iter.next(&iter);
1233 g_return_val_if_fail(item != NULL, NULL);
1234 g_return_val_if_fail(iter.next(&iter) == NULL, NULL);
1236 return item;
1239 /* Creates and shows a new filer window.
1240 * If src_win != NULL then display options can be taken from that source window.
1241 * Returns the new filer window, or NULL on error.
1242 * Note: if unique windows is in use, may return an existing window.
1244 FilerWindow *filer_opendir(const char *path, FilerWindow *src_win,
1245 const gchar *wm_class)
1247 FilerWindow *filer_window;
1248 char *real_path;
1249 DisplayStyle dstyle;
1250 DetailsType dtype;
1251 SortType s_type;
1252 GtkSortType s_order;
1253 Settings *dir_settings = NULL;
1254 gboolean force_resize = TRUE;
1256 /* Get the real pathname of the directory and copy it */
1257 real_path = pathdup(path);
1259 if (o_unique_filer_windows.int_value && !spring_in_progress)
1261 FilerWindow *same_dir_window;
1263 same_dir_window = find_filer_window(path, NULL);
1265 if (same_dir_window)
1267 gtk_window_present(GTK_WINDOW(same_dir_window->window));
1268 return same_dir_window;
1272 filer_window = g_new(FilerWindow, 1);
1273 filer_window->message = NULL;
1274 filer_window->minibuffer = NULL;
1275 filer_window->minibuffer_label = NULL;
1276 filer_window->minibuffer_area = NULL;
1277 filer_window->temp_show_hidden = FALSE;
1278 filer_window->sym_path = g_strdup(path);
1279 filer_window->real_path = real_path;
1280 filer_window->scanning = FALSE;
1281 filer_window->had_cursor = FALSE;
1282 filer_window->auto_select = NULL;
1283 filer_window->toolbar_text = NULL;
1284 filer_window->target_cb = NULL;
1285 filer_window->mini_type = MINI_NONE;
1286 filer_window->selection_state = GTK_STATE_INSENSITIVE;
1287 filer_window->toolbar = NULL;
1288 filer_window->toplevel_vbox = NULL;
1289 filer_window->view_hbox = NULL;
1290 filer_window->view = NULL;
1291 filer_window->scrollbar = NULL;
1292 filer_window->auto_scroll = -1;
1293 filer_window->window_id = NULL;
1295 tidy_sympath(filer_window->sym_path);
1297 /* Finds the entry for this directory in the dir cache, creating
1298 * a new one if needed. This does not cause a scan to start,
1299 * so if a new entry is created then it will be empty.
1301 filer_window->directory = g_fscache_lookup(dir_cache, real_path);
1302 if (!filer_window->directory)
1304 delayed_error(_("Directory '%s' not found."), path);
1305 g_free(filer_window->real_path);
1306 g_free(filer_window->sym_path);
1307 g_free(filer_window);
1308 return NULL;
1311 filer_window->temp_item_selected = FALSE;
1312 filer_window->flags = (FilerFlags) 0;
1313 filer_window->details_type = DETAILS_TIMES;
1314 filer_window->display_style = UNKNOWN_STYLE;
1315 filer_window->display_style_wanted = UNKNOWN_STYLE;
1316 filer_window->thumb_queue = NULL;
1317 filer_window->max_thumbs = 0;
1318 filer_window->sort_type = -1;
1320 filer_window->filter = FILER_SHOW_ALL;
1321 filer_window->filter_string = NULL;
1322 filer_window->regexp = NULL;
1324 if (src_win && o_display_inherit_options.int_value)
1326 s_type = src_win->sort_type;
1327 s_order = src_win->sort_order;
1328 dstyle = src_win->display_style_wanted;
1329 dtype = src_win->details_type;
1330 filer_window->show_hidden = src_win->show_hidden;
1331 filer_window->show_thumbs = src_win->show_thumbs;
1332 filer_window->view_type = src_win->view_type;
1334 filer_set_filter(filer_window, src_win->filter,
1335 src_win->filter_string);
1337 else
1339 s_type = o_display_sort_by.int_value;
1340 s_order = GTK_SORT_ASCENDING;
1341 dstyle = o_display_size.int_value;
1342 dtype = o_display_details.int_value;
1343 filer_window->show_hidden = o_display_show_hidden.int_value;
1344 filer_window->show_thumbs = o_display_show_thumbs.int_value;
1345 filer_window->view_type = o_filer_view_type.int_value;
1348 dir_settings = (Settings *) g_hash_table_lookup(settings_table,
1349 filer_window->sym_path);
1350 if (dir_settings)
1352 /* Override the current defaults with the per-directory
1353 * user settings.
1355 if (dir_settings->flags & SET_HIDDEN)
1356 filer_window->show_hidden = dir_settings->show_hidden;
1358 if (dir_settings->flags & SET_STYLE)
1359 dstyle = dir_settings->display_style;
1361 if (dir_settings->flags & SET_DETAILS)
1363 dtype = dir_settings->details_type;
1364 filer_window->view_type = dir_settings->view_type;
1367 if (dir_settings->flags & SET_SORT)
1369 s_type = dir_settings->sort_type;
1370 s_order = dir_settings->sort_order;
1373 if (dir_settings->flags & SET_THUMBS)
1374 filer_window->show_thumbs = dir_settings->show_thumbs;
1376 if (dir_settings->flags & SET_FILTER)
1377 filer_set_filter(filer_window,
1378 dir_settings->filter_type,
1379 dir_settings->filter);
1382 /* Add all the user-interface elements & realise */
1383 filer_add_widgets(filer_window, wm_class);
1384 if (src_win)
1385 gtk_window_set_position(GTK_WINDOW(filer_window->window),
1386 GTK_WIN_POS_MOUSE);
1388 if (dir_settings)
1390 if(dir_settings->flags & SET_POSITION)
1391 gtk_window_move(GTK_WINDOW(filer_window->window),
1392 dir_settings->x, dir_settings->y);
1393 if(dir_settings->flags & SET_SIZE) {
1394 filer_window_set_size(filer_window,
1395 dir_settings->width,
1396 dir_settings->height);
1397 force_resize = o_filer_auto_resize.int_value != RESIZE_NEVER;
1401 /* Connect to all the signal handlers */
1402 filer_add_signals(filer_window);
1404 display_set_layout(filer_window, dstyle, dtype, force_resize);
1405 display_set_sort_type(filer_window, s_type, s_order);
1407 /* Open the window after a timeout, or when scanning stops.
1408 * Do this before attaching, because attach() might tell us to
1409 * stop scanning (if a scan isn't needed).
1411 filer_window->open_timeout = g_timeout_add(500,
1412 (GSourceFunc) open_filer_window,
1413 filer_window);
1415 /* The view is created empty and then attach() is called, which
1416 * links the filer window to the entry in the directory cache we
1417 * looked up / created above.
1419 * The attach() function will immediately callback to the filer window
1420 * to deliver a list of all known entries in the directory (so,
1421 * the number of items will be known after attach() returns).
1423 * If the directory was not in the cache (because it hadn't been
1424 * opened it before) then the types and icons for the entries are
1425 * not know, but the list of names is.
1428 attach(filer_window);
1430 number_of_windows++;
1431 all_filer_windows = g_list_prepend(all_filer_windows, filer_window);
1433 return filer_window;
1436 void filer_set_view_type(FilerWindow *filer_window, ViewType type)
1438 GtkWidget *view = NULL;
1439 Directory *dir = NULL;
1440 GHashTable *selected = NULL;
1442 g_return_if_fail(filer_window != NULL);
1444 motion_window = NULL;
1446 if (filer_window->view)
1448 /* Save the current selection */
1449 ViewIter iter;
1450 DirItem *item;
1452 selected = g_hash_table_new(g_str_hash, g_str_equal);
1453 view_get_iter(filer_window->view, &iter, VIEW_ITER_SELECTED);
1454 while ((item = iter.next(&iter)))
1455 g_hash_table_insert(selected, item->leafname, "yes");
1457 /* Destroy the old view */
1458 gtk_widget_destroy(GTK_WIDGET(filer_window->view));
1459 filer_window->view = NULL;
1461 dir = filer_window->directory;
1462 g_object_ref(dir);
1463 detach(filer_window);
1466 switch (type)
1468 case VIEW_TYPE_COLLECTION:
1469 view = view_collection_new(filer_window);
1470 break;
1471 case VIEW_TYPE_DETAILS:
1472 view = view_details_new(filer_window);
1473 break;
1476 g_return_if_fail(view != NULL);
1478 filer_window->view = VIEW(view);
1479 filer_window->view_type = type;
1481 gtk_box_pack_start(filer_window->view_hbox, view, TRUE, TRUE, 0);
1482 gtk_widget_show(view);
1484 /* Drag and drop events */
1485 make_drop_target(view, 0);
1486 g_signal_connect(view, "drag_motion",
1487 G_CALLBACK(drag_motion), filer_window);
1488 g_signal_connect(view, "drag_leave",
1489 G_CALLBACK(drag_leave), filer_window);
1490 g_signal_connect(view, "drag_end",
1491 G_CALLBACK(drag_end), filer_window);
1492 /* Dragging from us... */
1493 g_signal_connect(view, "drag_data_get",
1494 GTK_SIGNAL_FUNC(drag_data_get), NULL);
1496 if (dir)
1498 /* Only when changing type. Otherwise, will attach later. */
1499 filer_window->directory = dir;
1500 attach(filer_window);
1502 if (o_filer_auto_resize.int_value != RESIZE_NEVER)
1503 view_autosize(filer_window->view);
1506 if (selected)
1508 ViewIter iter;
1509 DirItem *item;
1511 view_get_iter(filer_window->view, &iter, 0);
1512 while ((item = iter.next(&iter)))
1514 if (g_hash_table_lookup(selected, item->leafname))
1515 view_set_selected(filer_window->view,
1516 &iter, TRUE);
1518 g_hash_table_destroy(selected);
1522 /* This adds all the widgets to a new filer window. It is in a separate
1523 * function because filer_opendir() was getting too long...
1525 static void filer_add_widgets(FilerWindow *filer_window, const gchar *wm_class)
1527 GtkWidget *hbox, *vbox;
1529 /* Create the top-level window widget */
1530 filer_window->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1531 filer_set_title(filer_window);
1532 if (wm_class)
1533 gtk_window_set_wmclass(GTK_WINDOW(filer_window->window),
1534 wm_class, PROJECT);
1536 /* This property is cleared when the window is destroyed.
1537 * You can thus ref filer_window->window and use this to see
1538 * if the window no longer exists.
1540 g_object_set_data(G_OBJECT(filer_window->window),
1541 "filer_window", filer_window);
1543 /* Create this now to make the Adjustment before the View */
1544 filer_window->scrollbar = gtk_vscrollbar_new(NULL);
1546 vbox = gtk_vbox_new(FALSE, 0);
1547 gtk_container_add(GTK_CONTAINER(filer_window->window), vbox);
1549 filer_window->toplevel_vbox = GTK_BOX(vbox);
1551 /* If there's a message that should be displayed in each window (eg
1552 * 'Running as root'), add it here.
1554 if (show_user_message)
1556 filer_window->message = gtk_label_new(show_user_message);
1557 gtk_box_pack_start(GTK_BOX(vbox), filer_window->message,
1558 FALSE, TRUE, 0);
1559 gtk_widget_show(filer_window->message);
1562 hbox = gtk_hbox_new(FALSE, 0);
1563 filer_window->view_hbox = GTK_BOX(hbox);
1564 gtk_box_pack_start_defaults(GTK_BOX(vbox), hbox);
1565 /* Add the main View widget */
1566 filer_set_view_type(filer_window, filer_window->view_type);
1567 /* Put the scrollbar next to the View */
1568 gtk_box_pack_end(GTK_BOX(hbox),
1569 filer_window->scrollbar, FALSE, TRUE, 0);
1570 gtk_widget_show(hbox);
1572 /* If we want a toolbar, create it now */
1573 toolbar_update_toolbar(filer_window);
1575 /* And the minibuffer (hidden to start with) */
1576 create_minibuffer(filer_window);
1577 gtk_box_pack_end(GTK_BOX(vbox), filer_window->minibuffer_area,
1578 FALSE, TRUE, 0);
1580 /* And the thumbnail progress bar (also hidden) */
1582 GtkWidget *cancel;
1584 filer_window->thumb_bar = gtk_hbox_new(FALSE, 2);
1585 gtk_box_pack_end(GTK_BOX(vbox), filer_window->thumb_bar,
1586 FALSE, TRUE, 0);
1588 filer_window->thumb_progress = gtk_progress_bar_new();
1590 gtk_box_pack_start(GTK_BOX(filer_window->thumb_bar),
1591 filer_window->thumb_progress, TRUE, TRUE, 0);
1593 cancel = gtk_button_new_with_label(_("Cancel"));
1594 GTK_WIDGET_UNSET_FLAGS(cancel, GTK_CAN_FOCUS);
1595 gtk_box_pack_start(GTK_BOX(filer_window->thumb_bar),
1596 cancel, FALSE, TRUE, 0);
1597 g_signal_connect_swapped(cancel, "clicked",
1598 G_CALLBACK(filer_cancel_thumbnails),
1599 filer_window);
1602 gtk_widget_show(vbox);
1603 gtk_widget_show(filer_window->scrollbar);
1605 gtk_widget_realize(filer_window->window);
1607 gdk_window_set_role(filer_window->window->window,
1608 filer_window->sym_path);
1610 filer_window_set_size(filer_window, 4, 4);
1613 static void filer_add_signals(FilerWindow *filer_window)
1615 GtkTargetEntry target_table[] =
1617 {"text/uri-list", 0, TARGET_URI_LIST},
1618 {"STRING", 0, TARGET_STRING},
1619 {"COMPOUND_TEXT", 0, TARGET_STRING},/* XXX: Treats as STRING */
1620 {"UTF8_STRING", 0, TARGET_STRING},
1623 /* Events on the top-level window */
1624 gtk_widget_add_events(filer_window->window, GDK_ENTER_NOTIFY);
1625 g_signal_connect(filer_window->window, "enter-notify-event",
1626 G_CALLBACK(pointer_in), filer_window);
1627 g_signal_connect(filer_window->window, "leave-notify-event",
1628 G_CALLBACK(pointer_out), filer_window);
1629 g_signal_connect(filer_window->window, "destroy",
1630 G_CALLBACK(filer_window_destroyed), filer_window);
1631 g_signal_connect(filer_window->window, "delete-event",
1632 G_CALLBACK(filer_window_delete), filer_window);
1634 g_signal_connect(filer_window->window, "selection_clear_event",
1635 G_CALLBACK(filer_lost_primary), filer_window);
1637 g_signal_connect(filer_window->window, "selection_get",
1638 G_CALLBACK(selection_get), filer_window);
1639 gtk_selection_add_targets(GTK_WIDGET(filer_window->window),
1640 GDK_SELECTION_PRIMARY,
1641 target_table,
1642 sizeof(target_table) / sizeof(*target_table));
1644 g_signal_connect(filer_window->window, "popup-menu",
1645 G_CALLBACK(popup_menu), filer_window);
1646 g_signal_connect(filer_window->window, "key_press_event",
1647 G_CALLBACK(filer_key_press_event), filer_window);
1649 gtk_window_add_accel_group(GTK_WINDOW(filer_window->window),
1650 filer_keys);
1653 static gint clear_scanning_display(FilerWindow *filer_window)
1655 if (filer_exists(filer_window))
1656 filer_set_title(filer_window);
1657 return FALSE;
1660 static void set_scanning_display(FilerWindow *filer_window, gboolean scanning)
1662 if (scanning == filer_window->scanning)
1663 return;
1664 filer_window->scanning = scanning;
1666 if (scanning)
1667 filer_set_title(filer_window);
1668 else
1669 g_timeout_add(300, (GSourceFunc) clear_scanning_display,
1670 filer_window);
1673 /* Note that filer_window may not exist after this call.
1674 * Returns TRUE iff the directory still exists.
1676 gboolean filer_update_dir(FilerWindow *filer_window, gboolean warning)
1678 gboolean still_exists;
1680 still_exists = may_rescan(filer_window, warning);
1682 if (still_exists)
1683 dir_update(filer_window->directory, filer_window->sym_path);
1685 return still_exists;
1688 void filer_update_all(void)
1690 GList *next = all_filer_windows;
1692 while (next)
1694 FilerWindow *filer_window = (FilerWindow *) next->data;
1696 /* Updating directory may remove it from list -- stop sending
1697 * patches to move this line!
1699 next = next->next;
1701 /* Don't trigger a refresh if we're already scanning.
1702 * Otherwise, two views of a single directory will trigger
1703 * two scans.
1705 if (filer_window->directory &&
1706 !filer_window->directory->scanning)
1707 filer_update_dir(filer_window, TRUE);
1711 /* Refresh the various caches even if we don't think we need to */
1712 void full_refresh(void)
1714 mount_update(TRUE);
1715 reread_mime_files(); /* Refreshes all windows */
1718 /* See whether a filer window with a given path already exists
1719 * and is different from diff.
1721 static FilerWindow *find_filer_window(const char *sym_path, FilerWindow *diff)
1723 GList *next;
1725 for (next = all_filer_windows; next; next = next->next)
1727 FilerWindow *filer_window = (FilerWindow *) next->data;
1729 if (filer_window != diff &&
1730 strcmp(sym_path, filer_window->sym_path) == 0)
1731 return filer_window;
1734 return NULL;
1737 /* This path has been mounted/umounted/deleted some files - update all dirs */
1738 void filer_check_mounted(const char *real_path)
1740 GList *next = all_filer_windows;
1741 gchar *parent;
1742 int len;
1743 gboolean resize = o_filer_auto_resize.int_value == RESIZE_ALWAYS;
1745 /* DOS disks, etc, often don't change the mtime of the root directory
1746 * on modification, so force a refresh now.
1748 g_fscache_update(dir_cache, real_path);
1750 len = strlen(real_path);
1752 while (next)
1754 FilerWindow *filer_window = (FilerWindow *) next->data;
1756 next = next->next;
1758 if (strncmp(real_path, filer_window->real_path, len) == 0)
1760 char s = filer_window->real_path[len];
1762 if (s == '/' || s == '\0')
1764 if (filer_update_dir(filer_window, FALSE) &&
1765 resize)
1766 view_autosize(filer_window->view);
1771 parent = g_path_get_dirname(real_path);
1772 refresh_dirs(parent);
1773 g_free(parent);
1775 icons_may_update(real_path);
1778 /* Close all windows displaying 'path' or subdirectories of 'path' */
1779 void filer_close_recursive(const char *path)
1781 GList *next = all_filer_windows;
1782 gchar *real;
1783 int len;
1785 real = pathdup(path);
1786 len = strlen(real);
1788 while (next)
1790 FilerWindow *filer_window = (FilerWindow *) next->data;
1792 next = next->next;
1794 if (strncmp(real, filer_window->real_path, len) == 0)
1796 char s = filer_window->real_path[len];
1798 if (len == 1 || s == '/' || s == '\0')
1799 gtk_widget_destroy(filer_window->window);
1804 /* Like minibuffer_show(), except that:
1805 * - It returns FALSE (to be used from an idle callback)
1806 * - It checks that the filer window still exists.
1808 static gboolean minibuffer_show_cb(FilerWindow *filer_window)
1810 if (filer_exists(filer_window))
1811 minibuffer_show(filer_window, MINI_PATH);
1812 return FALSE;
1815 /* TRUE iff filer_window points to an existing FilerWindow
1816 * structure.
1818 gboolean filer_exists(FilerWindow *filer_window)
1820 GList *next;
1822 for (next = all_filer_windows; next; next = next->next)
1824 FilerWindow *fw = (FilerWindow *) next->data;
1826 if (fw == filer_window)
1827 return TRUE;
1830 return FALSE;
1833 FilerWindow *filer_get_by_id(const char *id)
1835 return g_hash_table_lookup(window_with_id, id);
1838 void filer_set_id(FilerWindow *filer_window, const char *id)
1840 g_return_if_fail(filer_window != NULL);
1842 if (filer_window->window_id)
1844 g_hash_table_remove(window_with_id, filer_window->window_id);
1845 g_free(filer_window->window_id);
1846 filer_window->window_id = NULL;
1849 if (id)
1851 filer_window->window_id = g_strdup(id);
1852 g_hash_table_insert(window_with_id,
1853 filer_window->window_id,
1854 filer_window);
1858 /* Make sure the window title is up-to-date */
1859 void filer_set_title(FilerWindow *filer_window)
1861 gchar *title = NULL;
1862 guchar *flags = "";
1863 gchar *hidden = "";
1865 if (filer_window->scanning ||
1866 filer_window->filter != FILER_SHOW_ALL ||
1867 filer_window->show_hidden || filer_window->show_thumbs)
1869 if (o_short_flag_names.int_value)
1872 switch(filer_window->filter) {
1873 case FILER_SHOW_ALL:
1874 hidden=filer_window->show_hidden? "A": "";
1875 break;
1876 case FILER_SHOW_GLOB: hidden="G"; break;
1877 case FILER_SHOW_REGEXP: hidden="R"; break;
1878 default: break;
1881 flags = g_strconcat(" +",
1882 filer_window->scanning ? _("S") : "",
1883 hidden,
1884 filer_window->show_thumbs ? _("T") : "",
1885 NULL);
1887 else
1889 switch(filer_window->filter) {
1890 case FILER_SHOW_ALL:
1891 hidden=g_strdup(filer_window->show_hidden? "All, ": "");
1892 break;
1893 case FILER_SHOW_GLOB:
1894 hidden=g_strdup_printf("Glob (%s), ",
1895 filer_window->filter_string);
1896 break;
1897 case FILER_SHOW_REGEXP:
1898 hidden=g_strdup_printf("Regexp (%s), ",
1899 filer_window->filter_string);
1900 break;
1901 default:
1902 hidden=g_strdup("");
1903 break;
1905 flags = g_strconcat(" (",
1906 filer_window->scanning ? _("Scanning, ") : "",
1907 hidden,
1908 filer_window->show_thumbs ? _("Thumbs, ") : "",
1909 NULL);
1910 flags[strlen(flags) - 2] = ')';
1911 g_free(hidden);
1915 if (not_local)
1916 title = g_strconcat("//", our_host_name(),
1917 filer_window->sym_path, flags, NULL);
1919 if (!title && home_dir_len > 1 &&
1920 strncmp(filer_window->sym_path, home_dir, home_dir_len) == 0)
1922 guchar sep = filer_window->sym_path[home_dir_len];
1924 if (sep == '\0' || sep == '/')
1925 title = g_strconcat("~",
1926 filer_window->sym_path + home_dir_len,
1927 flags,
1928 NULL);
1931 if (!title)
1932 title = g_strconcat(filer_window->sym_path, flags, NULL);
1934 ensure_utf8(&title);
1936 if (filer_window->directory->error)
1938 gchar *old = title;
1939 title = g_strconcat(old, ": ", filer_window->directory->error,
1940 NULL);
1941 g_free(old);
1944 gtk_window_set_title(GTK_WINDOW(filer_window->window), title);
1946 g_free(title);
1948 if (flags[0] != '\0')
1949 g_free(flags);
1952 /* Reconnect to the same directory (used when the Show Hidden option is
1953 * toggled). This has the side-effect of updating the window title.
1955 void filer_detach_rescan(FilerWindow *filer_window)
1957 Directory *dir = filer_window->directory;
1959 g_object_ref(dir);
1960 detach(filer_window);
1961 filer_window->directory = dir;
1962 attach(filer_window);
1965 /* Puts the filer window into target mode. When an item is chosen,
1966 * fn(filer_window, iter, data) is called. 'reason' will be displayed
1967 * on the toolbar while target mode is active.
1969 * Use fn == NULL to cancel target mode.
1971 void filer_target_mode(FilerWindow *filer_window,
1972 TargetFunc fn,
1973 gpointer data,
1974 const char *reason)
1976 TargetFunc old_fn = filer_window->target_cb;
1978 if (fn != old_fn)
1979 gdk_window_set_cursor(
1980 GTK_WIDGET(filer_window->view)->window,
1981 fn ? crosshair : NULL);
1983 filer_window->target_cb = fn;
1984 filer_window->target_data = data;
1986 if (filer_window->toolbar_text == NULL)
1987 return;
1989 if (fn)
1990 gtk_label_set_text(
1991 GTK_LABEL(filer_window->toolbar_text), reason);
1992 else if (o_toolbar_info.int_value)
1994 if (old_fn)
1995 toolbar_update_info(filer_window);
1997 else
1998 gtk_label_set_text(GTK_LABEL(filer_window->toolbar_text), "");
2001 static void set_selection_state(FilerWindow *filer_window, gboolean normal)
2003 GtkStateType old_state = filer_window->selection_state;
2005 filer_window->selection_state = normal
2006 ? GTK_STATE_SELECTED : GTK_STATE_INSENSITIVE;
2008 if (old_state != filer_window->selection_state
2009 && view_count_selected(filer_window->view))
2010 gtk_widget_queue_draw(GTK_WIDGET(filer_window->view));
2013 void filer_cancel_thumbnails(FilerWindow *filer_window)
2015 gtk_widget_hide(filer_window->thumb_bar);
2017 destroy_glist(&filer_window->thumb_queue);
2018 filer_window->max_thumbs = 0;
2021 /* Generate the next thumb for this window. The window object is
2022 * unref'd when there is nothing more to do.
2023 * If the window no longer has a filer window, nothing is done.
2025 static gboolean filer_next_thumb_real(GObject *window)
2027 FilerWindow *filer_window;
2028 gchar *path;
2029 int done, total;
2031 filer_window = g_object_get_data(window, "filer_window");
2033 if (!filer_window)
2035 g_object_unref(window);
2036 return FALSE;
2039 if (!filer_window->thumb_queue)
2041 filer_cancel_thumbnails(filer_window);
2042 g_object_unref(window);
2043 return FALSE;
2046 total = filer_window->max_thumbs;
2047 done = total - g_list_length(filer_window->thumb_queue);
2049 path = (gchar *) filer_window->thumb_queue->data;
2051 pixmap_background_thumb(path, (GFunc) filer_next_thumb, window);
2053 filer_window->thumb_queue = g_list_remove(filer_window->thumb_queue,
2054 path);
2055 g_free(path);
2057 gtk_progress_bar_set_fraction(
2058 GTK_PROGRESS_BAR(filer_window->thumb_progress),
2059 done / (float) total);
2061 return FALSE;
2064 /* path is the thumb just loaded, if any.
2065 * window is unref'd (eventually).
2067 static void filer_next_thumb(GObject *window, const gchar *path)
2069 if (path)
2070 dir_force_update_path(path);
2072 g_idle_add((GSourceFunc) filer_next_thumb_real, window);
2075 static void start_thumb_scanning(FilerWindow *filer_window)
2077 if (GTK_WIDGET_VISIBLE(filer_window->thumb_bar))
2078 return; /* Already scanning */
2080 gtk_widget_show_all(filer_window->thumb_bar);
2082 g_object_ref(G_OBJECT(filer_window->window));
2083 filer_next_thumb(G_OBJECT(filer_window->window), NULL);
2086 /* Set this image to be loaded some time in the future */
2087 void filer_create_thumb(FilerWindow *filer_window, const gchar *path)
2089 filer_window->max_thumbs++;
2091 filer_window->thumb_queue = g_list_append(filer_window->thumb_queue,
2092 g_strdup(path));
2094 if (filer_window->scanning)
2095 return; /* Will start when scan ends */
2097 start_thumb_scanning(filer_window);
2100 /* If thumbnail display is on, look through all the items in this directory
2101 * and start creating or updating the thumbnails as needed.
2103 void filer_create_thumbs(FilerWindow *filer_window)
2105 DirItem *item;
2106 ViewIter iter;
2108 if (!filer_window->show_thumbs)
2109 return;
2111 view_get_iter(filer_window->view, &iter, 0);
2113 while ((item = iter.next(&iter)))
2115 MaskedPixmap *pixmap;
2116 const guchar *path;
2117 gboolean found;
2119 if (item->base_type != TYPE_FILE)
2120 continue;
2122 /*if (strcmp(item->mime_type->media_type, "image") != 0)
2123 continue;*/
2125 path = make_path(filer_window->real_path, item->leafname);
2127 pixmap = g_fscache_lookup_full(pixmap_cache, path,
2128 FSCACHE_LOOKUP_ONLY_NEW, &found);
2129 if (pixmap)
2130 g_object_unref(pixmap);
2132 /* If we didn't get an image, it could be because:
2134 * - We're loading the image now. found is TRUE,
2135 * and we'll update the item later.
2136 * - We tried to load the image and failed. found
2137 * is TRUE.
2138 * - We haven't tried loading the image. found is
2139 * FALSE, and we start creating the thumb here.
2141 if (!found)
2142 filer_create_thumb(filer_window, path);
2146 static void filer_options_changed(void)
2148 if (o_short_flag_names.has_changed)
2150 GList *next;
2152 for (next = all_filer_windows; next; next = next->next)
2154 FilerWindow *filer_window = (FilerWindow *) next->data;
2156 filer_set_title(filer_window);
2161 /* Append interesting information to this GString */
2162 void filer_add_tip_details(FilerWindow *filer_window,
2163 GString *tip, DirItem *item)
2165 const guchar *fullpath = NULL;
2167 fullpath = make_path(filer_window->real_path, item->leafname);
2169 if (item->flags & ITEM_FLAG_SYMLINK)
2171 char *target;
2173 target = readlink_dup(fullpath);
2174 if (target)
2176 ensure_utf8(&target);
2178 g_string_append(tip, _("Symbolic link to "));
2179 g_string_append(tip, target);
2180 g_string_append_c(tip, '\n');
2181 g_free(target);
2185 if (item->flags & ITEM_FLAG_APPDIR)
2187 XMLwrapper *info;
2188 xmlNode *node;
2190 info = appinfo_get(fullpath, item);
2191 if (info && ((node = xml_get_section(info, NULL, "Summary"))))
2193 guchar *str;
2194 str = xmlNodeListGetString(node->doc,
2195 node->xmlChildrenNode, 1);
2196 if (str)
2198 g_string_append(tip, str);
2199 g_string_append_c(tip, '\n');
2200 g_free(str);
2203 if (info)
2204 g_object_unref(info);
2207 if (!g_utf8_validate(item->leafname, -1, NULL))
2208 g_string_append(tip,
2209 _("This filename is not valid UTF-8. "
2210 "You should rename it.\n"));
2213 /* Return the selection as a text/uri-list.
2214 * g_free() the result.
2216 static guchar *filer_create_uri_list(FilerWindow *filer_window)
2218 GString *string;
2219 GString *leader;
2220 ViewIter iter;
2221 DirItem *item;
2222 guchar *retval;
2224 g_return_val_if_fail(filer_window != NULL, NULL);
2226 string = g_string_new(NULL);
2228 leader = g_string_new(filer_window->sym_path);
2229 if (leader->str[leader->len - 1] != '/')
2230 g_string_append_c(leader, '/');
2232 view_get_iter(filer_window->view, &iter, VIEW_ITER_SELECTED);
2233 while ((item = iter.next(&iter)))
2235 gchar *uri, *path;
2237 path = g_strconcat(leader->str, item->leafname, NULL);
2238 uri = encode_path_as_uri(path);
2239 g_string_append(string, uri);
2240 g_string_append(string, "\r\n");
2241 g_free(path);
2242 g_free(uri);
2245 g_string_free(leader, TRUE);
2246 retval = string->str;
2247 g_string_free(string, FALSE);
2249 return retval;
2252 void filer_perform_action(FilerWindow *filer_window, GdkEventButton *event)
2254 BindAction action;
2255 ViewIface *view = filer_window->view;
2256 DirItem *item = NULL;
2257 gboolean press = event->type == GDK_BUTTON_PRESS;
2258 ViewIter iter;
2259 OpenFlags flags = 0;
2261 if (event->button > 3)
2262 return;
2264 view_get_iter_at_point(view, &iter, event->window, event->x, event->y);
2265 item = iter.peek(&iter);
2267 if (item && event->button == 1 &&
2268 view_get_selected(view, &iter) &&
2269 filer_window->selection_state == GTK_STATE_INSENSITIVE)
2271 /* Possibly a really slow DnD operation? */
2272 filer_window->temp_item_selected = FALSE;
2274 filer_selection_changed(filer_window, event->time);
2275 return;
2278 if (filer_window->target_cb)
2280 dnd_motion_ungrab();
2281 if (item && press && event->button == 1)
2282 filer_window->target_cb(filer_window, &iter,
2283 filer_window->target_data);
2285 filer_target_mode(filer_window, NULL, NULL, NULL);
2287 return;
2290 if (!o_single_click.int_value)
2292 /* Make sure both parts of a double-click fall on
2293 * the same file.
2295 static guchar *first_click = NULL;
2296 static guchar *second_click = NULL;
2298 if (event->type == GDK_BUTTON_PRESS)
2300 g_free(first_click);
2301 first_click = second_click;
2303 if (item)
2304 second_click = g_strdup(item->leafname);
2305 else
2306 second_click = NULL;
2309 if (event->type == GDK_2BUTTON_PRESS)
2311 if (first_click && second_click &&
2312 strcmp(first_click, second_click) != 0)
2313 return;
2314 if ((first_click || second_click) &&
2315 !(first_click && second_click))
2316 return;
2320 action = bind_lookup_bev(
2321 item ? BIND_DIRECTORY_ICON : BIND_DIRECTORY,
2322 event);
2324 switch (action)
2326 case ACT_CLEAR_SELECTION:
2327 view_clear_selection(view);
2328 break;
2329 case ACT_TOGGLE_SELECTED:
2330 view_set_selected(view, &iter,
2331 !view_get_selected(view, &iter));
2332 break;
2333 case ACT_SELECT_EXCL:
2334 view_select_only(view, &iter);
2335 break;
2336 case ACT_EDIT_ITEM:
2337 flags |= OPEN_SHIFT;
2338 /* (no break) */
2339 case ACT_OPEN_ITEM:
2340 if (event->button != 1 || event->state & GDK_MOD1_MASK)
2341 flags |= OPEN_CLOSE_WINDOW;
2342 else
2343 flags |= OPEN_SAME_WINDOW;
2344 if (o_new_button_1.int_value)
2345 flags ^= OPEN_SAME_WINDOW;
2346 if (event->type == GDK_2BUTTON_PRESS)
2347 view_set_selected(view, &iter, FALSE);
2348 dnd_motion_ungrab();
2350 filer_openitem(filer_window, &iter, flags);
2351 break;
2352 case ACT_POPUP_MENU:
2353 dnd_motion_ungrab();
2354 tooltip_show(NULL);
2355 show_filer_menu(filer_window,
2356 (GdkEvent *) event, &iter);
2357 break;
2358 case ACT_PRIME_AND_SELECT:
2359 if (item && !view_get_selected(view, &iter))
2360 view_select_only(view, &iter);
2361 dnd_motion_start(MOTION_READY_FOR_DND);
2362 break;
2363 case ACT_PRIME_AND_TOGGLE:
2364 view_set_selected(view, &iter,
2365 !view_get_selected(view, &iter));
2366 dnd_motion_start(MOTION_READY_FOR_DND);
2367 break;
2368 case ACT_PRIME_FOR_DND:
2369 dnd_motion_start(MOTION_READY_FOR_DND);
2370 break;
2371 case ACT_IGNORE:
2372 if (press && event->button < 4)
2374 if (item)
2375 view_wink_item(view, &iter);
2376 dnd_motion_start(MOTION_NONE);
2378 break;
2379 case ACT_LASSO_CLEAR:
2380 view_clear_selection(view);
2381 /* (no break) */
2382 case ACT_LASSO_MODIFY:
2383 view_start_lasso_box(view, event);
2384 break;
2385 case ACT_RESIZE:
2386 view_autosize(filer_window->view);
2387 break;
2388 default:
2389 g_warning("Unsupported action : %d\n", action);
2390 break;
2394 /* It's time to make the tooltip appear. If we're not over the item any
2395 * more, or the item doesn't need a tooltip, do nothing.
2397 static gboolean tooltip_activate(GtkWidget *window)
2399 FilerWindow *filer_window;
2400 ViewIface *view;
2401 ViewIter iter;
2402 gint x, y;
2403 DirItem *item = NULL;
2404 GString *tip = NULL;
2406 g_return_val_if_fail(tip_item != NULL, 0);
2408 filer_window = g_object_get_data(G_OBJECT(window), "filer_window");
2410 if (!motion_window || !filer_window)
2411 return FALSE; /* Window has been destroyed */
2413 view = filer_window->view;
2415 tooltip_show(NULL);
2417 gdk_window_get_pointer(motion_window, &x, &y, NULL);
2418 view_get_iter_at_point(view, &iter, motion_window, x, y);
2420 item = iter.peek(&iter);
2421 if (item != tip_item)
2422 return FALSE; /* Not still under the pointer */
2424 /* OK, the filer window still exists and the pointer is still
2425 * over the same item. Do we need to show a tip?
2428 tip = g_string_new(NULL);
2430 view_extend_tip(filer_window->view, &iter, tip);
2432 filer_add_tip_details(filer_window, tip, tip_item);
2434 if (tip->len > 1)
2436 g_string_truncate(tip, tip->len - 1);
2438 tooltip_show(tip->str);
2441 g_string_free(tip, TRUE);
2443 return FALSE;
2446 /* Motion detected on the View widget */
2447 gint filer_motion_notify(FilerWindow *filer_window, GdkEventMotion *event)
2449 ViewIface *view = filer_window->view;
2450 ViewIter iter;
2451 DirItem *item;
2453 view_get_iter_at_point(view, &iter, event->window, event->x, event->y);
2454 item = iter.peek(&iter);
2456 if (item)
2458 if (item != tip_item)
2460 tooltip_show(NULL);
2462 tip_item = item;
2463 motion_window = event->window;
2464 tooltip_prime((GtkFunction) tooltip_activate,
2465 G_OBJECT(filer_window->window));
2468 else
2470 tooltip_show(NULL);
2471 tip_item = NULL;
2474 if (motion_state != MOTION_READY_FOR_DND)
2475 return FALSE;
2477 if (!dnd_motion_moved(event))
2478 return FALSE;
2480 view_get_iter_at_point(view, &iter,
2481 event->window,
2482 event->x - (event->x_root - drag_start_x),
2483 event->y - (event->y_root - drag_start_y));
2484 item = iter.peek(&iter);
2485 if (!item)
2486 return FALSE;
2488 view_wink_item(view, NULL);
2490 if (!view_get_selected(view, &iter))
2492 if (event->state & GDK_BUTTON1_MASK)
2494 /* Select just this one */
2495 filer_window->temp_item_selected = TRUE;
2496 view_select_only(view, &iter);
2498 else
2500 if (view_count_selected(view) == 0)
2501 filer_window->temp_item_selected = TRUE;
2502 view_set_selected(view, &iter, TRUE);
2506 g_return_val_if_fail(view_count_selected(view) > 0, TRUE);
2508 if (view_count_selected(view) == 1)
2510 if (item->base_type == TYPE_UNKNOWN)
2511 item = dir_update_item(filer_window->directory,
2512 item->leafname);
2514 if (!item)
2516 report_error(_("Item no longer exists!"));
2517 return FALSE;
2520 drag_one_item(GTK_WIDGET(view), event,
2521 make_path(filer_window->sym_path, item->leafname),
2522 item, di_image(item));
2523 #if 0
2524 /* XXX: Use thumbnail */
2525 item, view ? view->image : NULL);
2526 #endif
2528 else
2530 guchar *uris;
2532 uris = filer_create_uri_list(filer_window);
2533 drag_selection(GTK_WIDGET(view), event, uris);
2534 g_free(uris);
2537 return FALSE;
2540 static void drag_end(GtkWidget *widget, GdkDragContext *context,
2541 FilerWindow *filer_window)
2543 filer_set_autoscroll(filer_window, FALSE);
2545 if (filer_window->temp_item_selected)
2547 view_clear_selection(filer_window->view);
2548 filer_window->temp_item_selected = FALSE;
2552 /* Remove highlights */
2553 static void drag_leave(GtkWidget *widget,
2554 GdkDragContext *context,
2555 guint32 time,
2556 FilerWindow *filer_window)
2558 dnd_spring_abort();
2561 /* Called during the drag when the mouse is in a widget registered
2562 * as a drop target. Returns TRUE if we can accept the drop.
2564 static gboolean drag_motion(GtkWidget *widget,
2565 GdkDragContext *context,
2566 gint x,
2567 gint y,
2568 guint time,
2569 FilerWindow *filer_window)
2571 DirItem *item;
2572 ViewIface *view = filer_window->view;
2573 ViewIter iter;
2574 GdkDragAction action = context->suggested_action;
2575 const guchar *new_path = NULL;
2576 const char *type = NULL;
2577 gboolean retval = FALSE;
2578 gboolean same_window;
2580 if ((context->actions & GDK_ACTION_ASK) && o_dnd_left_menu.int_value)
2582 guint state;
2583 gdk_window_get_pointer(NULL, NULL, NULL, &state);
2584 if (state & GDK_BUTTON1_MASK)
2585 action = GDK_ACTION_ASK;
2588 same_window = gtk_drag_get_source_widget(context) == widget;
2590 filer_set_autoscroll(filer_window, TRUE);
2592 if (filer_window->view_type == VIEW_TYPE_DETAILS)
2594 GdkWindow *bin;
2595 int bin_y;
2596 /* Correct for position of bin window */
2597 bin = gtk_tree_view_get_bin_window(GTK_TREE_VIEW(view));
2598 gdk_window_get_position(bin, NULL, &bin_y);
2599 y -= bin_y;
2602 if (o_dnd_drag_to_icons.int_value)
2604 view_get_iter_at_point(view, &iter, widget->window, x, y);
2605 item = iter.peek(&iter);
2607 else
2608 item = NULL;
2610 if (item && same_window && view_get_selected(view, &iter))
2611 type = NULL;
2612 else
2613 type = dnd_motion_item(context, &item);
2615 if (!type)
2616 item = NULL;
2618 /* Don't allow drops to non-writeable directories. BUT, still
2619 * allow drops on non-writeable SUBdirectories so that we can
2620 * do the spring-open thing.
2622 if (item && type == drop_dest_dir &&
2623 !(item->flags & ITEM_FLAG_APPDIR))
2625 dnd_spring_load(context, filer_window);
2627 else
2628 dnd_spring_abort();
2630 if (item)
2631 view_cursor_to_iter(view, &iter);
2632 else
2634 view_cursor_to_iter(view, NULL);
2636 /* Disallow background drops within a single window */
2637 if (type && same_window)
2638 type = NULL;
2641 if (type)
2643 if (item)
2644 new_path = make_path(filer_window->sym_path,
2645 item->leafname);
2646 else
2647 new_path = filer_window->sym_path;
2650 /* Don't ask about dragging to an application! */
2651 if (type == drop_dest_prog && action == GDK_ACTION_ASK)
2652 action = GDK_ACTION_COPY;
2654 g_dataset_set_data(context, "drop_dest_type", (gpointer) type);
2655 if (type)
2657 gdk_drag_status(context, action, time);
2658 g_dataset_set_data_full(context, "drop_dest_path",
2659 g_strdup(new_path), g_free);
2660 retval = TRUE;
2663 return retval;
2666 static gboolean as_timeout(FilerWindow *filer_window)
2668 gboolean retval;
2670 retval = view_auto_scroll_callback(filer_window->view);
2672 if (!retval)
2673 filer_window->auto_scroll = -1;
2675 return retval;
2678 /* When autoscroll is on, a timer keeps track of the pointer position.
2679 * While it's near the top or bottom of the window, the window scrolls.
2681 * If the mouse buttons are released, the pointer leaves the window, or
2682 * a drag-and-drop operation finishes, auto_scroll is turned off.
2684 void filer_set_autoscroll(FilerWindow *filer_window, gboolean auto_scroll)
2686 g_return_if_fail(filer_window != NULL);
2688 if (auto_scroll)
2690 if (filer_window->auto_scroll != -1)
2691 return; /* Already on! */
2693 filer_window->auto_scroll = g_timeout_add(50,
2694 (GSourceFunc) as_timeout,
2695 filer_window);
2697 else
2699 if (filer_window->auto_scroll == -1)
2700 return; /* Already off! */
2702 g_source_remove(filer_window->auto_scroll);
2703 filer_window->auto_scroll = -1;
2707 #define ZERO_MNT "/uri/0install"
2709 static void refresh_done(FilerWindow *filer_window)
2711 if (filer_exists(filer_window))
2712 filer_update_dir(filer_window, TRUE);
2715 void filer_refresh(FilerWindow *filer_window)
2717 if (!strncmp(ZERO_MNT "/", filer_window->real_path, sizeof(ZERO_MNT)))
2719 /* Try to run 0refresh */
2720 gint pid;
2721 gchar *argv[] = {"0refresh", NULL, NULL};
2722 const char *host = filer_window->real_path + sizeof(ZERO_MNT);
2723 const char *slash;
2725 slash = strchr(host, '/');
2726 if (slash)
2727 argv[1] = g_strndup(host, slash - host);
2728 else
2729 argv[1] = g_strdup(host);
2730 pid = rox_spawn(filer_window->real_path, (const char **) argv);
2731 g_free(argv[1]);
2732 if (pid)
2733 on_child_death(pid, (CallbackFn) refresh_done,
2734 filer_window);
2737 full_refresh();
2740 gboolean filer_match_filter(FilerWindow *filer_window, const gchar *filename)
2742 g_return_val_if_fail(filename != NULL, FALSE);
2744 if(filename[0]=='.' &&
2745 (!filer_window->temp_show_hidden && !filer_window->show_hidden))
2746 return FALSE;
2748 switch(filer_window->filter) {
2749 case FILER_SHOW_GLOB:
2750 return fnmatch(filer_window->filter_string,
2751 filename, 0)==0;
2753 case FILER_SHOW_REGEXP: /* Unimplemented */
2755 case FILER_SHOW_ALL:
2756 default:
2757 break;
2759 return TRUE;
2762 /* Provided to hide the implementation */
2763 void filer_set_hidden(FilerWindow *filer_window, gboolean hidden)
2765 filer_window->show_hidden=hidden;
2768 /* Set the filter type. Returns TRUE if the type has changed
2769 * (must call filer_detach_rescan).
2771 gboolean filer_set_filter(FilerWindow *filer_window, FilterType type,
2772 const gchar *filter_string)
2774 /* Is this new filter the same as the old one? */
2775 if (filer_window->filter == type)
2777 switch(filer_window->filter)
2779 case FILER_SHOW_ALL:
2780 return FALSE;
2781 case FILER_SHOW_GLOB:
2782 case FILER_SHOW_REGEXP:
2783 if (strcmp(filer_window->filter_string,
2784 filter_string) == 0)
2785 return FALSE;
2786 break;
2790 /* Clean up old filter */
2791 if (filer_window->filter_string)
2793 g_free(filer_window->filter_string);
2794 filer_window->filter_string = NULL;
2796 /* Also clean up compiled regexp when implemented */
2798 filer_window->filter = type;
2800 switch(type)
2802 case FILER_SHOW_ALL:
2803 /* No extra work */
2804 break;
2806 case FILER_SHOW_GLOB:
2807 filer_window->filter_string = g_strdup(filter_string);
2808 break;
2810 case FILER_SHOW_REGEXP:
2811 filer_window->filter_string = g_strdup(filter_string);
2812 /* Compile the pattern */
2813 break;
2815 default:
2816 /* oops */
2817 filer_window->filter = FILER_SHOW_ALL;
2818 g_warning("Impossible: filter type %d", type);
2819 break;
2822 return TRUE;
2825 /* Setting stuff */
2826 static Settings *settings_new(const char *path)
2828 Settings *set;
2830 set=g_new(Settings, 1);
2831 memset(set, 0, sizeof(Settings));
2832 if(path)
2833 set->path=g_strdup(path);
2835 return set;
2838 static void settings_free(Settings *set)
2840 g_free(set->path);
2841 if(set->filter)
2842 g_free(set->filter);
2843 g_free(set);
2846 static gboolean free_settings(gpointer key, gpointer value, gpointer data)
2848 if(!data || strcmp(data, key)==0)
2849 settings_free(value);
2851 return TRUE;
2854 static void store_settings(Settings *set)
2856 Settings *old;
2858 old=g_hash_table_lookup(settings_table, set->path);
2859 if(old) {
2860 g_hash_table_foreach_remove(settings_table, free_settings,
2861 set->path);
2864 g_hash_table_insert(settings_table, set->path, set);
2867 /* TODO: use symbolic names in the XML file where possible */
2868 static void load_from_node(Settings *set, xmlDocPtr doc, xmlNodePtr node)
2870 xmlChar *str=NULL;
2872 str=xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
2874 if(strcmp(node->name, "X") == 0) {
2875 set->x=atoi(str);
2876 set->flags|=SET_POSITION;
2877 } else if(strcmp(node->name, "Y") == 0) {
2878 set->y=atoi(str);
2879 set->flags|=SET_POSITION;
2880 } else if(strcmp(node->name, "Width") == 0) {
2881 set->width=atoi(str);
2882 set->flags|=SET_SIZE;
2883 } else if(strcmp(node->name, "Height") == 0) {
2884 set->height=atoi(str);
2885 set->flags|=SET_SIZE;
2886 } else if(strcmp(node->name, "ShowHidden") == 0) {
2887 set->show_hidden=atoi(str);
2888 set->flags|=SET_HIDDEN;
2889 } else if(strcmp(node->name, "ViewType") == 0) {
2890 set->view_type=atoi(str);
2891 set->flags|=SET_DETAILS;
2892 } else if(strcmp(node->name, "DetailsType") == 0) {
2893 set->details_type=atoi(str);
2894 set->flags|=SET_DETAILS;
2895 } else if(strcmp(node->name, "SortType") == 0) {
2896 set->sort_type=atoi(str);
2897 set->flags|=SET_SORT;
2898 } else if(strcmp(node->name, "SortOrder") == 0) {
2899 set->sort_order=atoi(str);
2900 set->flags|=SET_SORT;
2901 } else if(strcmp(node->name, "DisplayStyle") == 0) {
2902 set->display_style=atoi(str);
2903 set->flags|=SET_STYLE;
2904 } else if(strcmp(node->name, "ShowThumbs") == 0) {
2905 set->show_thumbs=atoi(str);
2906 set->flags|=SET_THUMBS;
2907 } else if(strcmp(node->name, "FilterType") == 0) {
2908 set->filter_type=atoi(str);
2909 set->flags|=SET_FILTER;
2910 } else if(strcmp(node->name, "Filter") == 0) {
2911 set->filter=g_strdup(str);
2912 set->flags|=SET_FILTER;
2915 if(str)
2916 xmlFree(str);
2919 static void load_settings(void)
2921 gchar *path;
2922 XMLwrapper *settings_doc=NULL;
2924 path=choices_find_path_load("Settings.xml", "ROX-Filer");
2925 if(path) {
2926 settings_doc=xml_new(path);
2927 g_free(path);
2930 if(!settings_table)
2931 settings_table=g_hash_table_new(g_str_hash, g_str_equal);
2933 if(settings_doc) {
2934 xmlNodePtr node, subnode;
2936 node = xmlDocGetRootElement(settings_doc->doc);
2938 for (node = node->xmlChildrenNode; node; node = node->next)
2940 Settings *set;
2941 xmlChar *path;
2943 if (node->type != XML_ELEMENT_NODE)
2944 continue;
2945 if (strcmp(node->name, "FilerWindow") != 0)
2946 continue;
2948 path=xmlGetProp(node, "path");
2949 set=settings_new(path);
2950 xmlFree(path);
2952 for (subnode=node->xmlChildrenNode; subnode;
2953 subnode=subnode->next) {
2955 if (subnode->type != XML_ELEMENT_NODE)
2956 continue;
2957 load_from_node(set, settings_doc->doc,
2958 subnode);
2961 store_settings(set);
2963 g_object_unref(settings_doc);
2967 static void add_nodes(gpointer key, gpointer value, gpointer data)
2969 xmlNodePtr node=(xmlNodePtr) data;
2970 xmlNodePtr sub;
2971 Settings *set=(Settings *) value;
2972 char *tmp;
2974 sub=xmlNewChild(node, NULL, "FilerWindow", NULL);
2976 xmlSetProp(sub, "path", set->path);
2978 if(set->flags & SET_POSITION) {
2979 tmp=g_strdup_printf("%d", set->x);
2980 xmlNewChild(sub, NULL, "X", tmp);
2981 g_free(tmp);
2982 tmp=g_strdup_printf("%d", set->y);
2983 xmlNewChild(sub, NULL, "Y", tmp);
2984 g_free(tmp);
2986 if(set->flags & SET_SIZE) {
2987 tmp=g_strdup_printf("%d", set->width);
2988 xmlNewChild(sub, NULL, "Width", tmp);
2989 g_free(tmp);
2990 tmp=g_strdup_printf("%d", set->height);
2991 xmlNewChild(sub, NULL, "Height", tmp);
2992 g_free(tmp);
2994 if(set->flags & SET_HIDDEN) {
2995 tmp=g_strdup_printf("%d", set->show_hidden);
2996 xmlNewChild(sub, NULL, "ShowHidden", tmp);
2997 g_free(tmp);
2999 if(set->flags & SET_STYLE) {
3000 tmp=g_strdup_printf("%d", set->display_style);
3001 xmlNewChild(sub, NULL, "DisplayStyle", tmp);
3002 g_free(tmp);
3004 if(set->flags & SET_SORT) {
3005 tmp=g_strdup_printf("%d", set->sort_type);
3006 xmlNewChild(sub, NULL, "SortType", tmp);
3007 g_free(tmp);
3008 tmp=g_strdup_printf("%d", set->sort_order);
3009 xmlNewChild(sub, NULL, "SortOrder", tmp);
3010 g_free(tmp);
3012 if(set->flags & SET_DETAILS) {
3013 tmp=g_strdup_printf("%d", set->view_type);
3014 xmlNewChild(sub, NULL, "ViewType", tmp);
3015 g_free(tmp);
3016 tmp=g_strdup_printf("%d", set->details_type);
3017 xmlNewChild(sub, NULL, "DetailsType", tmp);
3018 g_free(tmp);
3020 if(set->flags & SET_STYLE) {
3021 tmp=g_strdup_printf("%d", set->show_thumbs);
3022 xmlNewChild(sub, NULL, "ShowThumbs", tmp);
3023 g_free(tmp);
3025 if(set->flags & SET_FILTER) {
3026 tmp=g_strdup_printf("%d", set->filter_type);
3027 xmlNewChild(sub, NULL, "FilterType", tmp);
3028 g_free(tmp);
3029 if(set->filter && set->filter[0])
3030 xmlNewChild(sub, NULL, "Filter", set->filter);
3034 static void save_settings(void)
3036 gchar *path;
3038 path=choices_find_path_save("Settings.xml", "ROX-Filer", TRUE);
3039 if(path) {
3040 xmlDocPtr doc = xmlNewDoc("1.0");
3041 xmlDocSetRootElement(doc, xmlNewDocNode(doc, NULL,
3042 "Settings", NULL));
3044 g_hash_table_foreach(settings_table, add_nodes,
3045 xmlDocGetRootElement(doc));
3047 save_xml_file(doc, path);
3049 g_free(path);
3050 xmlFreeDoc(doc);
3055 static void check_settings(FilerWindow *filer_window)
3057 Settings *set;
3059 set=(Settings *) g_hash_table_lookup(settings_table,
3060 filer_window->sym_path);
3062 if(set) {
3063 if(set->flags & SET_POSITION)
3064 gtk_window_move(GTK_WINDOW(filer_window->window),
3065 set->x, set->y);
3066 if(set->flags & SET_SIZE)
3067 filer_window_set_size(filer_window, set->width,
3068 set->height);
3069 if(set->flags & SET_HIDDEN)
3070 filer_set_hidden(filer_window, set->show_hidden);
3072 if(set->flags & (SET_STYLE|SET_DETAILS)) {
3073 DisplayStyle style=filer_window->display_style;
3074 DetailsType details=filer_window->details_type;
3076 if(set->flags & SET_STYLE)
3077 style=set->display_style;
3079 if(set->flags & SET_DETAILS) {
3080 details=set->details_type;
3082 filer_set_view_type(filer_window,
3083 set->view_type);
3086 display_set_layout(filer_window, style,
3087 details, FALSE);
3090 if(set->flags & SET_SORT)
3091 display_set_sort_type(filer_window,
3092 set->sort_type,
3093 set->sort_order);
3095 if(set->flags & SET_THUMBS)
3096 display_set_thumbs(filer_window,
3097 set->show_thumbs);
3099 if(set->flags & SET_FILTER)
3100 display_set_filter(filer_window,
3101 set->filter_type,
3102 set->filter);
3107 typedef struct settings_window {
3108 GtkWidget *window;
3110 GtkWidget *pos, *size, *hidden, *style, *sort, *details,
3111 *thumbs, *filter;
3113 Settings *set;
3114 } SettingsWindow;
3117 static void settings_response(GtkWidget *window, gint response,
3118 SettingsWindow *set_win)
3120 if(response==GTK_RESPONSE_OK) {
3121 gint flags=0;
3123 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win->pos)))
3124 flags|=SET_POSITION;
3125 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win->size)))
3126 flags|=SET_SIZE;
3127 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win->hidden)))
3128 flags|=SET_HIDDEN;
3129 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win->style)))
3130 flags|=SET_STYLE;
3131 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win->sort)))
3132 flags|=SET_SORT;
3133 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win->details)))
3134 flags|=SET_DETAILS;
3135 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win->thumbs)))
3136 flags|=SET_THUMBS;
3137 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win->filter)))
3138 flags|=SET_FILTER;
3140 set_win->set->flags=flags;
3141 store_settings(set_win->set);
3142 save_settings();
3145 gtk_widget_destroy(window);
3148 void filer_save_settings(FilerWindow *fwin)
3150 SettingsWindow *set_win;
3151 GtkWidget *vbox;
3152 GtkWidget *path;
3153 gint x, y;
3155 Settings *set=settings_new(fwin->sym_path);
3157 gtk_window_get_position(GTK_WINDOW(fwin->window),&x, &y);
3158 set->flags|=SET_POSITION;
3159 set->x=x;
3160 set->y=y;
3162 gtk_window_get_size(GTK_WINDOW(fwin->window),&x, &y);
3163 set->flags|=SET_SIZE;
3164 set->width=x;
3165 set->height=y;
3167 set->flags|=SET_HIDDEN;
3168 set->show_hidden=fwin->show_hidden;
3170 set->flags|=SET_STYLE;
3171 set->display_style=fwin->display_style;
3173 set->flags|=SET_SORT;
3174 set->sort_type=fwin->sort_type;
3175 set->sort_order=fwin->sort_order;
3177 set->flags|=SET_DETAILS;
3178 set->view_type=fwin->view_type;
3179 set->details_type=fwin->details_type;
3181 set->flags|=SET_THUMBS;
3182 set->show_thumbs=fwin->show_thumbs;
3184 set->flags|=SET_FILTER;
3185 set->filter_type=fwin->filter;
3186 if(fwin->filter_string)
3187 set->filter=g_strdup(fwin->filter_string);
3189 /* Store other parameters
3191 set_win=g_new(SettingsWindow, 1);
3193 set_win->window=gtk_dialog_new();
3194 number_of_windows++;
3196 gtk_dialog_add_button(GTK_DIALOG(set_win->window),
3197 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
3198 gtk_dialog_add_button(GTK_DIALOG(set_win->window),
3199 GTK_STOCK_OK, GTK_RESPONSE_OK);
3201 g_signal_connect(set_win->window, "destroy",
3202 G_CALLBACK(one_less_window), NULL);
3203 g_signal_connect_swapped(set_win->window, "destroy",
3204 G_CALLBACK(g_free), set_win);
3206 gtk_window_set_title(GTK_WINDOW(set_win->window),
3207 _("Select display properties to save"));
3209 vbox=GTK_DIALOG(set_win->window)->vbox;
3211 path=gtk_label_new(set->path);
3212 gtk_box_pack_start(GTK_BOX(vbox), path, FALSE, FALSE, 2);
3214 set_win->pos=gtk_check_button_new_with_label(_("Position"));
3215 gtk_box_pack_start(GTK_BOX(vbox), set_win->pos, FALSE, FALSE, 2);
3216 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win->pos),
3217 set->flags & SET_POSITION);
3219 set_win->size=gtk_check_button_new_with_label(_("Size"));
3220 gtk_box_pack_start(GTK_BOX(vbox), set_win->size, FALSE, FALSE, 2);
3221 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win->size),
3222 set->flags & SET_SIZE);
3224 set_win->hidden=gtk_check_button_new_with_label(_("Show hidden"));
3225 gtk_box_pack_start(GTK_BOX(vbox), set_win->hidden,
3226 FALSE, FALSE, 2);
3227 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win->hidden),
3228 set->flags & SET_HIDDEN);
3230 set_win->style=gtk_check_button_new_with_label(_("Display style"));
3231 gtk_box_pack_start(GTK_BOX(vbox), set_win->style,
3232 FALSE, FALSE, 2);
3233 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win->style),
3234 set->flags & SET_STYLE);
3236 set_win->sort=gtk_check_button_new_with_label(_("Sort type and order"));
3237 gtk_box_pack_start(GTK_BOX(vbox), set_win->sort, FALSE, FALSE, 2);
3238 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win->sort),
3239 set->flags & SET_SORT);
3241 set_win->details=gtk_check_button_new_with_label(_("Details"));
3242 gtk_box_pack_start(GTK_BOX(vbox), set_win->details, FALSE, FALSE, 2);
3243 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win->details),
3244 set->flags & SET_DETAILS);
3246 set_win->thumbs=gtk_check_button_new_with_label(_("Thumbnails"));
3247 gtk_box_pack_start(GTK_BOX(vbox), set_win->thumbs,
3248 FALSE, FALSE, 2);
3249 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win->thumbs),
3250 set->flags & SET_THUMBS);
3252 set_win->filter=gtk_check_button_new_with_label(_("Filter"));
3253 gtk_box_pack_start(GTK_BOX(vbox), set_win->filter,
3254 FALSE, FALSE, 2);
3255 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win->filter),
3256 set->flags & SET_FILTER);
3258 set_win->set=set;
3259 g_signal_connect(set_win->window, "response",
3260 G_CALLBACK(settings_response), set_win);
3262 gtk_widget_show_all(set_win->window);