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)
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* filer.c - code for handling filer windows */
32 #include <sys/param.h>
37 #include <gdk/gdkkeysyms.h>
46 #include "gui_support.h"
56 #include "minibuffer.h"
63 #include "view_iface.h"
64 #include "view_collection.h"
65 #include "view_details.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
;
95 guint flags
; /* Which parts are valid, see below */
102 GtkSortType sort_order
;
103 gboolean show_thumbs
;
105 DetailsType details_type
;
106 DisplayStyle display_style
;
108 FilterType filter_type
;
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
,
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
,
148 FilerWindow
*filer_window
);
149 static gboolean
drag_motion(GtkWidget
*widget
,
150 GdkDragContext
*context
,
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)
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",
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
,
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
, ':');
207 if (dpyhost
[0] && strcmp(ohost
, dpyhost
) != 0)
209 /* Try the cannonical name for dpyhost (see our_host_name()
214 ent
= gethostbyname(dpyhost
);
215 if (!ent
|| strcmp(ohost
, ent
->h_name
) != 0)
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
;
232 if (strcmp(leafname
, r
[i
]->leafname
) == 0)
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
)
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
))
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(),
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
);
294 gdk_window_resize(gdk_window
, w
, h
);
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 GdkWindow
*win
= filer_window
->window
->window
;
304 XWarpPointer(gdk_x11_drawable_get_xdisplay(win
),
306 gdk_x11_drawable_get_xid(win
),
312 /* Called on a timeout while scanning or when scanning ends
313 * (whichever happens first).
315 static gint
open_filer_window(FilerWindow
*filer_window
)
317 view_style_changed(filer_window
->view
, 0);
319 if (filer_window
->open_timeout
)
321 gtk_timeout_remove(filer_window
->open_timeout
);
322 filer_window
->open_timeout
= 0;
325 if (!GTK_WIDGET_VISIBLE(filer_window
->window
))
327 display_set_actual_size(filer_window
, TRUE
);
328 gtk_widget_show(filer_window
->window
);
334 static void update_display(Directory
*dir
,
337 FilerWindow
*filer_window
)
339 ViewIface
*view
= (ViewIface
*) filer_window
->view
;
344 view_add_items(view
, items
);
345 /* Open and resize if currently hidden */
346 open_filer_window(filer_window
);
349 view_delete_if(view
, if_deleted
, items
);
350 toolbar_update_info(filer_window
);
353 set_scanning_display(filer_window
, TRUE
);
354 toolbar_update_info(filer_window
);
357 if (filer_window
->window
->window
)
358 gdk_window_set_cursor(
359 filer_window
->window
->window
,
361 set_scanning_display(filer_window
, FALSE
);
362 toolbar_update_info(filer_window
);
363 open_filer_window(filer_window
);
365 if (filer_window
->had_cursor
&&
366 !view_cursor_visible(view
))
369 view_get_iter(view
, &start
, 0);
370 if (start
.next(&start
))
371 view_cursor_to_iter(view
, &start
);
372 view_show_cursor(view
);
373 filer_window
->had_cursor
= FALSE
;
375 if (filer_window
->auto_select
)
376 display_set_autoselect(filer_window
,
377 filer_window
->auto_select
);
378 null_g_free(&filer_window
->auto_select
);
380 filer_create_thumbs(filer_window
);
382 if (filer_window
->thumb_queue
)
383 start_thumb_scanning(filer_window
);
386 view_update_items(view
, items
);
388 case DIR_ERROR_CHANGED
:
389 filer_set_title(filer_window
);
394 static void attach(FilerWindow
*filer_window
)
396 gdk_window_set_cursor(filer_window
->window
->window
, busy_cursor
);
397 view_clear(filer_window
->view
);
398 filer_window
->scanning
= TRUE
;
399 dir_attach(filer_window
->directory
, (DirCallback
) update_display
,
401 filer_set_title(filer_window
);
402 bookmarks_add_history(filer_window
->sym_path
);
404 if (filer_window
->directory
->error
)
406 if (spring_in_progress
)
407 g_printerr(_("Error scanning '%s':\n%s\n"),
408 filer_window
->sym_path
,
409 filer_window
->directory
->error
);
411 delayed_error(_("Error scanning '%s':\n%s"),
412 filer_window
->sym_path
,
413 filer_window
->directory
->error
);
417 static void detach(FilerWindow
*filer_window
)
419 g_return_if_fail(filer_window
->directory
!= NULL
);
421 dir_detach(filer_window
->directory
,
422 (DirCallback
) update_display
, filer_window
);
423 g_object_unref(filer_window
->directory
);
424 filer_window
->directory
= NULL
;
427 /* Returns TRUE to prevent closing the window. May offer to unmount a
430 gboolean
filer_window_delete(GtkWidget
*window
,
431 GdkEvent
*unused
, /* (may be NULL) */
432 FilerWindow
*filer_window
)
434 if (mount_is_user_mounted(filer_window
->real_path
))
438 action
= get_choice(PROJECT
,
439 _("Do you want to unmount this device?\n\n"
440 "Unmounting a device makes it safe to remove "
442 GTK_STOCK_CANCEL
, NULL
,
443 GTK_STOCK_CLOSE
, NULL
,
444 ROX_STOCK_MOUNT
, _("Unmount"));
447 return TRUE
; /* Cancel close operation */
453 list
= g_list_prepend(NULL
, filer_window
->sym_path
);
454 action_mount(list
, FALSE
, TRUE
);
464 static void filer_window_destroyed(GtkWidget
*widget
, FilerWindow
*filer_window
)
466 all_filer_windows
= g_list_remove(all_filer_windows
, filer_window
);
468 g_object_set_data(G_OBJECT(widget
), "filer_window", NULL
);
470 if (window_with_primary
== filer_window
)
471 window_with_primary
= NULL
;
473 if (window_with_focus
== filer_window
)
476 window_with_focus
= NULL
;
479 if (filer_window
->directory
)
480 detach(filer_window
);
482 if (filer_window
->open_timeout
)
484 gtk_timeout_remove(filer_window
->open_timeout
);
485 filer_window
->open_timeout
= 0;
488 if (filer_window
->auto_scroll
!= -1)
490 gtk_timeout_remove(filer_window
->auto_scroll
);
491 filer_window
->auto_scroll
= -1;
494 if (filer_window
->thumb_queue
)
495 destroy_glist(&filer_window
->thumb_queue
);
499 filer_set_id(filer_window
, NULL
);
501 if(filer_window
->filter_string
)
502 g_free(filer_window
->filter_string
);
503 if(filer_window
->regexp
)
504 g_free(filer_window
->regexp
);
506 g_free(filer_window
->auto_select
);
507 g_free(filer_window
->real_path
);
508 g_free(filer_window
->sym_path
);
509 g_free(filer_window
);
514 /* Returns TRUE iff the directory still exists. */
515 static gboolean
may_rescan(FilerWindow
*filer_window
, gboolean warning
)
519 g_return_val_if_fail(filer_window
!= NULL
, FALSE
);
521 /* We do a fresh lookup (rather than update) because the inode may
524 dir
= g_fscache_lookup(dir_cache
, filer_window
->real_path
);
528 info_message(_("Directory missing/deleted"));
529 gtk_widget_destroy(filer_window
->window
);
532 if (dir
== filer_window
->directory
)
536 detach(filer_window
);
537 filer_window
->directory
= dir
;
538 attach(filer_window
);
544 /* No items are now selected. This might be because another app claimed
545 * the selection or because the user unselected all the items.
547 void filer_lost_selection(FilerWindow
*filer_window
, guint time
)
549 if (window_with_primary
== filer_window
)
551 window_with_primary
= NULL
;
552 gtk_selection_owner_set(NULL
, GDK_SELECTION_PRIMARY
, time
);
556 /* Another app has claimed the primary selection */
557 static void filer_lost_primary(GtkWidget
*window
,
558 GdkEventSelection
*event
,
561 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
563 if (window_with_primary
&& window_with_primary
== filer_window
)
565 window_with_primary
= NULL
;
566 set_selection_state(filer_window
, FALSE
);
570 /* Someone wants us to send them the selection */
571 static void selection_get(GtkWidget
*widget
,
572 GtkSelectionData
*selection_data
,
577 GString
*reply
, *header
;
578 FilerWindow
*filer_window
= (FilerWindow
*) data
;
582 reply
= g_string_new(NULL
);
583 header
= g_string_new(NULL
);
588 g_string_printf(header
, " %s",
589 make_path(filer_window
->sym_path
, ""));
591 case TARGET_URI_LIST
:
592 g_string_printf(header
, " file://%s%s",
593 our_host_name_for_dnd(),
594 make_path(filer_window
->sym_path
, ""));
598 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
600 while ((item
= iter
.next(&iter
)))
602 g_string_append(reply
, header
->str
);
603 g_string_append(reply
, item
->leafname
);
607 gtk_selection_data_set(selection_data
, xa_string
,
608 8, reply
->str
+ 1, reply
->len
- 1);
611 g_warning("Attempt to paste empty selection!");
612 gtk_selection_data_set(selection_data
, xa_string
, 8, "", 0);
615 g_string_free(reply
, TRUE
);
616 g_string_free(header
, TRUE
);
619 /* Selection has been changed -- try to grab the primary selection
620 * if we don't have it. Also called when clicking on an insensitive selection
622 * Also updates toolbar info.
624 void filer_selection_changed(FilerWindow
*filer_window
, gint time
)
626 toolbar_update_info(filer_window
);
628 if (window_with_primary
== filer_window
)
629 return; /* Already got primary */
631 if (!view_count_selected(filer_window
->view
))
632 return; /* Nothing selected */
634 if (filer_window
->temp_item_selected
== FALSE
&&
635 gtk_selection_owner_set(GTK_WIDGET(filer_window
->window
),
636 GDK_SELECTION_PRIMARY
,
639 window_with_primary
= filer_window
;
640 set_selection_state(filer_window
, TRUE
);
643 set_selection_state(filer_window
, FALSE
);
646 /* Open the item (or add it to the shell command minibuffer) */
647 void filer_openitem(FilerWindow
*filer_window
, ViewIter
*iter
, OpenFlags flags
)
649 gboolean shift
= (flags
& OPEN_SHIFT
) != 0;
650 gboolean close_mini
= flags
& OPEN_FROM_MINI
;
651 gboolean close_window
= (flags
& OPEN_CLOSE_WINDOW
) != 0;
653 const guchar
*full_path
;
654 gboolean wink
= TRUE
;
657 g_return_if_fail(filer_window
!= NULL
&& iter
!= NULL
);
659 item
= iter
->peek(iter
);
661 g_return_if_fail(item
!= NULL
);
663 if (filer_window
->mini_type
== MINI_SHELL
)
665 minibuffer_add(filer_window
, item
->leafname
);
670 dir_update_item(filer_window
->directory
, item
->leafname
);
672 if (item
->base_type
== TYPE_DIRECTORY
)
674 /* Never close a filer window when opening a directory
675 * (click on a dir or click on an app with shift).
677 if (shift
|| !(item
->flags
& ITEM_FLAG_APPDIR
))
678 close_window
= FALSE
;
681 full_path
= make_path(filer_window
->sym_path
, item
->leafname
);
682 if (shift
&& (item
->flags
& ITEM_FLAG_SYMLINK
))
685 old_dir
= filer_window
->directory
;
686 if (run_diritem(full_path
, item
,
687 flags
& OPEN_SAME_WINDOW
? filer_window
: NULL
,
691 if (old_dir
!= filer_window
->directory
)
695 gtk_widget_destroy(filer_window
->window
);
699 view_wink_item(filer_window
->view
, iter
);
701 minibuffer_hide(filer_window
);
706 static gint
pointer_in(GtkWidget
*widget
,
707 GdkEventCrossing
*event
,
708 FilerWindow
*filer_window
)
710 may_rescan(filer_window
, TRUE
);
714 static gint
pointer_out(GtkWidget
*widget
,
715 GdkEventCrossing
*event
,
716 FilerWindow
*filer_window
)
722 /* Move the cursor to the next selected item in direction 'dir'
725 void filer_next_selected(FilerWindow
*filer_window
, int dir
)
727 ViewIter iter
, cursor
;
728 gboolean have_cursor
;
729 ViewIface
*view
= filer_window
->view
;
731 g_return_if_fail(dir
== 1 || dir
== -1);
733 view_get_cursor(view
, &cursor
);
734 have_cursor
= cursor
.peek(&cursor
) != NULL
;
736 view_get_iter(view
, &iter
,
738 (have_cursor
? VIEW_ITER_FROM_CURSOR
: 0) |
739 (dir
< 0 ? VIEW_ITER_BACKWARDS
: 0));
741 if (have_cursor
&& view_get_selected(view
, &cursor
))
742 iter
.next(&iter
); /* Skip the cursor itself */
744 if (iter
.next(&iter
))
745 view_cursor_to_iter(view
, &iter
);
752 static void return_pressed(FilerWindow
*filer_window
, GdkEventKey
*event
)
754 TargetFunc cb
= filer_window
->target_cb
;
755 gpointer data
= filer_window
->target_data
;
759 filer_target_mode(filer_window
, NULL
, NULL
, NULL
);
761 view_get_cursor(filer_window
->view
, &iter
);
762 if (!iter
.peek(&iter
))
767 cb(filer_window
, &iter
, data
);
771 if (event
->state
& GDK_SHIFT_MASK
)
773 if (event
->state
& GDK_MOD1_MASK
)
774 flags
|= OPEN_CLOSE_WINDOW
;
776 flags
|= OPEN_SAME_WINDOW
;
778 filer_openitem(filer_window
, &iter
, flags
);
781 /* Makes sure that 'groups' is up-to-date, reloading from file if it has
782 * changed. If no groups were loaded and there is no file then initialised
783 * groups to an empty document.
784 * Return the node for the 'name' group.
786 static xmlNode
*group_find(char *name
)
791 /* Update the groups, if possible */
792 path
= choices_find_path_load("Groups.xml", PROJECT
);
796 wrapper
= xml_cache_load(path
);
800 g_object_unref(groups
);
809 groups
= xml_new(NULL
);
810 groups
->doc
= xmlNewDoc("1.0");
812 xmlDocSetRootElement(groups
->doc
,
813 xmlNewDocNode(groups
->doc
, NULL
, "groups", NULL
));
817 node
= xmlDocGetRootElement(groups
->doc
);
819 for (node
= node
->xmlChildrenNode
; node
; node
= node
->next
)
823 gid
= xmlGetProp(node
, "name");
828 if (strcmp(name
, gid
) != 0)
839 static void group_save(FilerWindow
*filer_window
, char *name
)
846 group
= group_find(name
);
849 xmlUnlinkNode(group
);
852 group
= xmlNewChild(xmlDocGetRootElement(groups
->doc
),
853 NULL
, "group", NULL
);
854 xmlSetProp(group
, "name", name
);
856 xmlNewTextChild(group
, NULL
, "directory", filer_window
->sym_path
);
858 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
860 while ((item
= iter
.next(&iter
)))
861 xmlNewTextChild(group
, NULL
, "item", item
->leafname
);
863 save_path
= choices_find_path_save("Groups.xml", PROJECT
, TRUE
);
866 save_xml_file(groups
->doc
, save_path
);
871 static gboolean
group_restore_cb(ViewIter
*iter
, gpointer data
)
873 GHashTable
*in_group
= (GHashTable
*) data
;
875 return g_hash_table_lookup(in_group
,
876 iter
->peek(iter
)->leafname
) != NULL
;
879 static void group_restore(FilerWindow
*filer_window
, char *name
)
881 GHashTable
*in_group
;
883 xmlNode
*group
, *node
;
885 group
= group_find(name
);
889 report_error(_("Group %s is not set. Select some files "
890 "and press Ctrl+%s to set the group. Press %s "
891 "on its own to reselect the files later.\n"
892 "Make sure NumLock is on if you use the keypad."),
897 node
= get_subnode(group
, NULL
, "directory");
898 g_return_if_fail(node
!= NULL
);
899 path
= xmlNodeListGetString(groups
->doc
, node
->xmlChildrenNode
, 1);
900 g_return_if_fail(path
!= NULL
);
902 if (strcmp(path
, filer_window
->sym_path
) != 0)
903 filer_change_to(filer_window
, path
, NULL
);
906 in_group
= g_hash_table_new(g_str_hash
, g_str_equal
);
907 for (node
= group
->xmlChildrenNode
; node
; node
= node
->next
)
910 if (node
->type
!= XML_ELEMENT_NODE
)
912 if (strcmp(node
->name
, "item") != 0)
915 leaf
= xmlNodeListGetString(groups
->doc
,
916 node
->xmlChildrenNode
, 1);
918 g_warning("Missing leafname!\n");
920 g_hash_table_insert(in_group
, leaf
, filer_window
);
923 view_select_if(filer_window
->view
, &group_restore_cb
, in_group
);
925 g_hash_table_foreach(in_group
, (GHFunc
) g_free
, NULL
);
926 g_hash_table_destroy(in_group
);
929 static gboolean
popup_menu(GtkWidget
*widget
, FilerWindow
*filer_window
)
933 view_get_cursor(filer_window
->view
, &iter
);
935 show_filer_menu(filer_window
, NULL
, &iter
);
940 void filer_window_toggle_cursor_item_selected(FilerWindow
*filer_window
)
942 ViewIface
*view
= filer_window
->view
;
945 view_get_iter(view
, &iter
, VIEW_ITER_FROM_CURSOR
);
946 if (!iter
.next(&iter
))
947 return; /* No cursor */
949 if (view_get_selected(view
, &iter
))
950 view_set_selected(view
, &iter
, FALSE
);
952 view_set_selected(view
, &iter
, TRUE
);
954 if (iter
.next(&iter
))
955 view_cursor_to_iter(view
, &iter
);
958 gint
filer_key_press_event(GtkWidget
*widget
,
960 FilerWindow
*filer_window
)
962 ViewIface
*view
= filer_window
->view
;
964 GtkWidget
*focus
= GTK_WINDOW(widget
)->focus_widget
;
965 guint key
= event
->keyval
;
968 window_with_focus
= filer_window
;
970 /* Delay setting up the keys until now to speed loading... */
971 if (ensure_filer_menu())
973 /* Gtk updates in an idle-handler, so force a recheck now */
974 g_signal_emit_by_name(widget
, "keys_changed");
977 if (focus
&& focus
== filer_window
->minibuffer
)
978 if (gtk_widget_event(focus
, (GdkEvent
*) event
))
979 return TRUE
; /* Handled */
982 gtk_widget_grab_focus(GTK_WIDGET(view
));
984 view_get_cursor(view
, &cursor
);
985 if (!cursor
.peek(&cursor
) && (key
== GDK_Up
|| key
== GDK_Down
))
988 view_get_iter(view
, &iter
, 0);
989 if (iter
.next(&iter
))
990 view_cursor_to_iter(view
, &iter
);
991 gtk_widget_grab_focus(GTK_WIDGET(view
)); /* Needed? */
998 filer_target_mode(filer_window
, NULL
, NULL
, NULL
);
999 view_cursor_to_iter(filer_window
->view
, NULL
);
1000 view_clear_selection(filer_window
->view
);
1003 return_pressed(filer_window
, event
);
1005 case GDK_ISO_Left_Tab
:
1006 filer_next_selected(filer_window
, -1);
1009 filer_next_selected(filer_window
, 1);
1012 change_to_parent(filer_window
);
1020 view_get_cursor(filer_window
->view
, &iter
);
1021 show_filer_menu(filer_window
,
1022 (GdkEvent
*) event
, &iter
);
1026 filer_window_toggle_cursor_item_selected(filer_window
);
1029 if (key
>= GDK_0
&& key
<= GDK_9
)
1030 group
[0] = key
- GDK_0
+ '0';
1031 else if (key
>= GDK_KP_0
&& key
<= GDK_KP_9
)
1032 group
[0] = key
- GDK_KP_0
+ '0';
1035 if (focus
&& focus
!= widget
&&
1036 gtk_widget_get_toplevel(focus
) == widget
)
1037 if (gtk_widget_event(focus
,
1038 (GdkEvent
*) event
))
1039 return TRUE
; /* Handled */
1043 if (event
->state
& GDK_CONTROL_MASK
)
1044 group_save(filer_window
, group
);
1046 group_restore(filer_window
, group
);
1052 void filer_open_parent(FilerWindow
*filer_window
)
1055 const char *current
= filer_window
->sym_path
;
1057 if (current
[0] == '/' && current
[1] == '\0')
1058 return; /* Already in the root */
1060 dir
= g_path_get_dirname(current
);
1061 filer_opendir(dir
, filer_window
, NULL
);
1065 void change_to_parent(FilerWindow
*filer_window
)
1068 const char *current
= filer_window
->sym_path
;
1070 if (current
[0] == '/' && current
[1] == '\0')
1071 return; /* Already in the root */
1073 dir
= g_path_get_dirname(current
);
1074 filer_change_to(filer_window
, dir
, g_basename(current
));
1078 /* Removes trailing /s from path (modified in place) */
1079 static void tidy_sympath(gchar
*path
)
1083 g_return_if_fail(path
!= NULL
);
1086 while (l
> 1 && path
[l
- 1] == '/')
1093 /* Make filer_window display path. When finished, highlight item 'from', or
1094 * the first item if from is NULL. If there is currently no cursor then
1095 * simply wink 'from' (if not NULL).
1096 * If the cause was a key event and we resize, warp the pointer.
1098 void filer_change_to(FilerWindow
*filer_window
,
1099 const char *path
, const char *from
)
1102 char *sym_path
, *real_path
;
1105 g_return_if_fail(filer_window
!= NULL
);
1107 filer_cancel_thumbnails(filer_window
);
1111 sym_path
= g_strdup(path
);
1112 real_path
= pathdup(path
);
1113 new_dir
= g_fscache_lookup(dir_cache
, real_path
);
1117 delayed_error(_("Directory '%s' is not accessible"),
1124 if (o_unique_filer_windows
.int_value
&& !spring_in_progress
)
1128 fw
= find_filer_window(sym_path
, filer_window
);
1130 gtk_widget_destroy(fw
->window
);
1133 from_dup
= from
&& *from
? g_strdup(from
) : NULL
;
1135 detach(filer_window
);
1136 g_free(filer_window
->real_path
);
1137 g_free(filer_window
->sym_path
);
1138 filer_window
->real_path
= real_path
;
1139 filer_window
->sym_path
= sym_path
;
1140 tidy_sympath(filer_window
->sym_path
);
1142 filer_window
->directory
= new_dir
;
1144 g_free(filer_window
->auto_select
);
1145 filer_window
->auto_select
= from_dup
;
1147 filer_window
->had_cursor
= filer_window
->had_cursor
||
1148 view_cursor_visible(filer_window
->view
);
1150 filer_set_title(filer_window
);
1151 if (filer_window
->window
->window
)
1152 gdk_window_set_role(filer_window
->window
->window
,
1153 filer_window
->sym_path
);
1154 view_cursor_to_iter(filer_window
->view
, NULL
);
1156 attach(filer_window
);
1158 check_settings(filer_window
);
1160 display_set_actual_size(filer_window
, FALSE
);
1162 if (o_filer_auto_resize
.int_value
== RESIZE_ALWAYS
)
1163 view_autosize(filer_window
->view
);
1165 if (filer_window
->mini_type
== MINI_PATH
)
1166 gtk_idle_add((GtkFunction
) minibuffer_show_cb
,
1170 /* Returns a list containing the full (sym) pathname of every selected item.
1171 * You must g_free() each item in the list.
1173 GList
*filer_selected_items(FilerWindow
*filer_window
)
1175 GList
*retval
= NULL
;
1176 guchar
*dir
= filer_window
->sym_path
;
1180 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
1181 while ((item
= iter
.next(&iter
)))
1183 retval
= g_list_prepend(retval
,
1184 g_strdup(make_path(dir
, item
->leafname
)));
1187 return g_list_reverse(retval
);
1190 /* Return the single selected item. Error if nothing is selected. */
1191 DirItem
*filer_selected_item(FilerWindow
*filer_window
)
1196 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
1198 item
= iter
.next(&iter
);
1199 g_return_val_if_fail(item
!= NULL
, NULL
);
1200 g_return_val_if_fail(iter
.next(&iter
) == NULL
, NULL
);
1205 /* Creates and shows a new filer window.
1206 * If src_win != NULL then display options can be taken from that source window.
1207 * Returns the new filer window, or NULL on error.
1208 * Note: if unique windows is in use, may return an existing window.
1210 FilerWindow
*filer_opendir(const char *path
, FilerWindow
*src_win
,
1211 const gchar
*wm_class
)
1213 FilerWindow
*filer_window
;
1215 DisplayStyle dstyle
;
1218 GtkSortType s_order
;
1220 /* Get the real pathname of the directory and copy it */
1221 real_path
= pathdup(path
);
1223 if (o_unique_filer_windows
.int_value
&& !spring_in_progress
)
1225 FilerWindow
*same_dir_window
;
1227 same_dir_window
= find_filer_window(path
, NULL
);
1229 if (same_dir_window
)
1231 gtk_window_present(GTK_WINDOW(same_dir_window
->window
));
1232 return same_dir_window
;
1236 filer_window
= g_new(FilerWindow
, 1);
1237 filer_window
->message
= NULL
;
1238 filer_window
->minibuffer
= NULL
;
1239 filer_window
->minibuffer_label
= NULL
;
1240 filer_window
->minibuffer_area
= NULL
;
1241 filer_window
->temp_show_hidden
= FALSE
;
1242 filer_window
->sym_path
= g_strdup(path
);
1243 filer_window
->real_path
= real_path
;
1244 filer_window
->scanning
= FALSE
;
1245 filer_window
->had_cursor
= FALSE
;
1246 filer_window
->auto_select
= NULL
;
1247 filer_window
->toolbar_text
= NULL
;
1248 filer_window
->target_cb
= NULL
;
1249 filer_window
->mini_type
= MINI_NONE
;
1250 filer_window
->selection_state
= GTK_STATE_INSENSITIVE
;
1251 filer_window
->toolbar
= NULL
;
1252 filer_window
->toplevel_vbox
= NULL
;
1253 filer_window
->view_hbox
= NULL
;
1254 filer_window
->view
= NULL
;
1255 filer_window
->scrollbar
= NULL
;
1256 filer_window
->auto_scroll
= -1;
1257 filer_window
->window_id
= NULL
;
1259 tidy_sympath(filer_window
->sym_path
);
1261 /* Finds the entry for this directory in the dir cache, creating
1262 * a new one if needed. This does not cause a scan to start,
1263 * so if a new entry is created then it will be empty.
1265 filer_window
->directory
= g_fscache_lookup(dir_cache
, real_path
);
1266 if (!filer_window
->directory
)
1268 delayed_error(_("Directory '%s' not found."), path
);
1269 g_free(filer_window
->real_path
);
1270 g_free(filer_window
->sym_path
);
1271 g_free(filer_window
);
1275 filer_window
->temp_item_selected
= FALSE
;
1276 filer_window
->flags
= (FilerFlags
) 0;
1277 filer_window
->details_type
= DETAILS_TIMES
;
1278 filer_window
->display_style
= UNKNOWN_STYLE
;
1279 filer_window
->display_style_wanted
= UNKNOWN_STYLE
;
1280 filer_window
->thumb_queue
= NULL
;
1281 filer_window
->max_thumbs
= 0;
1282 filer_window
->sort_type
= -1;
1284 filer_window
->filter
= FILER_SHOW_ALL
;
1285 filer_window
->filter_string
= NULL
;
1286 filer_window
->regexp
= NULL
;
1288 if (src_win
&& o_display_inherit_options
.int_value
)
1290 s_type
= src_win
->sort_type
;
1291 s_order
= src_win
->sort_order
;
1292 dstyle
= src_win
->display_style_wanted
;
1293 dtype
= src_win
->details_type
;
1294 filer_window
->show_hidden
= src_win
->show_hidden
;
1295 filer_window
->show_thumbs
= src_win
->show_thumbs
;
1296 filer_window
->view_type
= src_win
->view_type
;
1298 filer_set_filter(filer_window
, src_win
->filter
,
1299 src_win
->filter_string
);
1303 s_type
= o_display_sort_by
.int_value
;
1304 s_order
= GTK_SORT_ASCENDING
;
1305 dstyle
= o_display_size
.int_value
;
1306 dtype
= o_display_details
.int_value
;
1307 filer_window
->show_hidden
= o_display_show_hidden
.int_value
;
1308 filer_window
->show_thumbs
= o_display_show_thumbs
.int_value
;
1309 filer_window
->view_type
= o_filer_view_type
.int_value
;
1312 /* Add all the user-interface elements & realise */
1313 filer_add_widgets(filer_window
, wm_class
);
1315 gtk_window_set_position(GTK_WINDOW(filer_window
->window
),
1318 /* Connect to all the signal handlers */
1319 filer_add_signals(filer_window
);
1321 display_set_layout(filer_window
, dstyle
, dtype
, TRUE
);
1322 display_set_sort_type(filer_window
, s_type
, s_order
);
1324 /* Do we have saved settings? */
1325 check_settings(filer_window
);
1327 /* Open the window after a timeout, or when scanning stops.
1328 * Do this before attaching, because attach() might tell us to
1329 * stop scanning (if a scan isn't needed).
1331 filer_window
->open_timeout
= gtk_timeout_add(500,
1332 (GtkFunction
) open_filer_window
,
1335 /* The view is created empty and then attach() is called, which
1336 * links the filer window to the entry in the directory cache we
1337 * looked up / created above.
1339 * The attach() function will immediately callback to the filer window
1340 * to deliver a list of all known entries in the directory (so,
1341 * the number of items will be known after attach() returns).
1343 * If the directory was not in the cache (because it hadn't been
1344 * opened it before) then the types and icons for the entries are
1345 * not know, but the list of names is.
1348 attach(filer_window
);
1350 number_of_windows
++;
1351 all_filer_windows
= g_list_prepend(all_filer_windows
, filer_window
);
1353 return filer_window
;
1356 void filer_set_view_type(FilerWindow
*filer_window
, ViewType type
)
1358 GtkWidget
*view
= NULL
;
1359 Directory
*dir
= NULL
;
1360 GHashTable
*selected
= NULL
;
1362 g_return_if_fail(filer_window
!= NULL
);
1364 motion_window
= NULL
;
1366 if (filer_window
->view
)
1368 /* Save the current selection */
1372 selected
= g_hash_table_new(g_str_hash
, g_str_equal
);
1373 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
1374 while ((item
= iter
.next(&iter
)))
1375 g_hash_table_insert(selected
, item
->leafname
, "yes");
1377 /* Destroy the old view */
1378 gtk_widget_destroy(GTK_WIDGET(filer_window
->view
));
1379 filer_window
->view
= NULL
;
1381 dir
= filer_window
->directory
;
1383 detach(filer_window
);
1388 case VIEW_TYPE_COLLECTION
:
1389 view
= view_collection_new(filer_window
);
1391 case VIEW_TYPE_DETAILS
:
1392 view
= view_details_new(filer_window
);
1396 g_return_if_fail(view
!= NULL
);
1398 filer_window
->view
= VIEW(view
);
1399 filer_window
->view_type
= type
;
1401 gtk_box_pack_start(filer_window
->view_hbox
, view
, TRUE
, TRUE
, 0);
1402 gtk_widget_show(view
);
1404 /* Drag and drop events */
1405 make_drop_target(view
, 0);
1406 g_signal_connect(view
, "drag_motion",
1407 G_CALLBACK(drag_motion
), filer_window
);
1408 g_signal_connect(view
, "drag_leave",
1409 G_CALLBACK(drag_leave
), filer_window
);
1410 g_signal_connect(view
, "drag_end",
1411 G_CALLBACK(drag_end
), filer_window
);
1412 /* Dragging from us... */
1413 g_signal_connect(view
, "drag_data_get",
1414 GTK_SIGNAL_FUNC(drag_data_get
), NULL
);
1418 /* Only when changing type. Otherwise, will attach later. */
1419 filer_window
->directory
= dir
;
1420 attach(filer_window
);
1422 if (o_filer_auto_resize
.int_value
!= RESIZE_NEVER
)
1423 view_autosize(filer_window
->view
);
1431 view_get_iter(filer_window
->view
, &iter
, 0);
1432 while ((item
= iter
.next(&iter
)))
1434 if (g_hash_table_lookup(selected
, item
->leafname
))
1435 view_set_selected(filer_window
->view
,
1438 g_hash_table_destroy(selected
);
1442 /* This adds all the widgets to a new filer window. It is in a separate
1443 * function because filer_opendir() was getting too long...
1445 static void filer_add_widgets(FilerWindow
*filer_window
, const gchar
*wm_class
)
1447 GtkWidget
*hbox
, *vbox
;
1449 /* Create the top-level window widget */
1450 filer_window
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
1451 filer_set_title(filer_window
);
1453 gtk_window_set_wmclass(GTK_WINDOW(filer_window
->window
),
1456 /* This property is cleared when the window is destroyed.
1457 * You can thus ref filer_window->window and use this to see
1458 * if the window no longer exists.
1460 g_object_set_data(G_OBJECT(filer_window
->window
),
1461 "filer_window", filer_window
);
1463 /* Create this now to make the Adjustment before the View */
1464 filer_window
->scrollbar
= gtk_vscrollbar_new(NULL
);
1466 vbox
= gtk_vbox_new(FALSE
, 0);
1467 gtk_container_add(GTK_CONTAINER(filer_window
->window
), vbox
);
1469 filer_window
->toplevel_vbox
= GTK_BOX(vbox
);
1471 /* If there's a message that should be displayed in each window (eg
1472 * 'Running as root'), add it here.
1474 if (show_user_message
)
1476 filer_window
->message
= gtk_label_new(show_user_message
);
1477 gtk_box_pack_start(GTK_BOX(vbox
), filer_window
->message
,
1479 gtk_widget_show(filer_window
->message
);
1482 hbox
= gtk_hbox_new(FALSE
, 0);
1483 filer_window
->view_hbox
= GTK_BOX(hbox
);
1484 gtk_box_pack_start_defaults(GTK_BOX(vbox
), hbox
);
1485 /* Add the main View widget */
1486 filer_set_view_type(filer_window
, filer_window
->view_type
);
1487 /* Put the scrollbar next to the View */
1488 gtk_box_pack_end(GTK_BOX(hbox
),
1489 filer_window
->scrollbar
, FALSE
, TRUE
, 0);
1490 gtk_widget_show(hbox
);
1492 /* If we want a toolbar, create it now */
1493 toolbar_update_toolbar(filer_window
);
1495 /* And the minibuffer (hidden to start with) */
1496 create_minibuffer(filer_window
);
1497 gtk_box_pack_end(GTK_BOX(vbox
), filer_window
->minibuffer_area
,
1500 /* And the thumbnail progress bar (also hidden) */
1504 filer_window
->thumb_bar
= gtk_hbox_new(FALSE
, 2);
1505 gtk_box_pack_end(GTK_BOX(vbox
), filer_window
->thumb_bar
,
1508 filer_window
->thumb_progress
= gtk_progress_bar_new();
1510 gtk_box_pack_start(GTK_BOX(filer_window
->thumb_bar
),
1511 filer_window
->thumb_progress
, TRUE
, TRUE
, 0);
1513 cancel
= gtk_button_new_with_label(_("Cancel"));
1514 GTK_WIDGET_UNSET_FLAGS(cancel
, GTK_CAN_FOCUS
);
1515 gtk_box_pack_start(GTK_BOX(filer_window
->thumb_bar
),
1516 cancel
, FALSE
, TRUE
, 0);
1517 g_signal_connect_swapped(cancel
, "clicked",
1518 G_CALLBACK(filer_cancel_thumbnails
),
1522 gtk_widget_show(vbox
);
1523 gtk_widget_show(filer_window
->scrollbar
);
1525 gtk_widget_realize(filer_window
->window
);
1527 gdk_window_set_role(filer_window
->window
->window
,
1528 filer_window
->sym_path
);
1530 filer_window_set_size(filer_window
, 4, 4);
1533 static void filer_add_signals(FilerWindow
*filer_window
)
1535 GtkTargetEntry target_table
[] =
1537 {"text/uri-list", 0, TARGET_URI_LIST
},
1538 {"STRING", 0, TARGET_STRING
},
1539 {"COMPOUND_TEXT", 0, TARGET_STRING
},/* XXX: Treats as STRING */
1540 {"UTF8_STRING", 0, TARGET_STRING
},
1543 /* Events on the top-level window */
1544 gtk_widget_add_events(filer_window
->window
, GDK_ENTER_NOTIFY
);
1545 g_signal_connect(filer_window
->window
, "enter-notify-event",
1546 G_CALLBACK(pointer_in
), filer_window
);
1547 g_signal_connect(filer_window
->window
, "leave-notify-event",
1548 G_CALLBACK(pointer_out
), filer_window
);
1549 g_signal_connect(filer_window
->window
, "destroy",
1550 G_CALLBACK(filer_window_destroyed
), filer_window
);
1551 g_signal_connect(filer_window
->window
, "delete-event",
1552 G_CALLBACK(filer_window_delete
), filer_window
);
1554 g_signal_connect(filer_window
->window
, "selection_clear_event",
1555 G_CALLBACK(filer_lost_primary
), filer_window
);
1557 g_signal_connect(filer_window
->window
, "selection_get",
1558 G_CALLBACK(selection_get
), filer_window
);
1559 gtk_selection_add_targets(GTK_WIDGET(filer_window
->window
),
1560 GDK_SELECTION_PRIMARY
,
1562 sizeof(target_table
) / sizeof(*target_table
));
1564 g_signal_connect(filer_window
->window
, "popup-menu",
1565 G_CALLBACK(popup_menu
), filer_window
);
1566 g_signal_connect(filer_window
->window
, "key_press_event",
1567 G_CALLBACK(filer_key_press_event
), filer_window
);
1569 gtk_window_add_accel_group(GTK_WINDOW(filer_window
->window
),
1573 static gint
clear_scanning_display(FilerWindow
*filer_window
)
1575 if (filer_exists(filer_window
))
1576 filer_set_title(filer_window
);
1580 static void set_scanning_display(FilerWindow
*filer_window
, gboolean scanning
)
1582 if (scanning
== filer_window
->scanning
)
1584 filer_window
->scanning
= scanning
;
1587 filer_set_title(filer_window
);
1589 gtk_timeout_add(300, (GtkFunction
) clear_scanning_display
,
1593 /* Note that filer_window may not exist after this call.
1594 * Returns TRUE iff the directory still exists.
1596 gboolean
filer_update_dir(FilerWindow
*filer_window
, gboolean warning
)
1598 gboolean still_exists
;
1600 still_exists
= may_rescan(filer_window
, warning
);
1603 dir_update(filer_window
->directory
, filer_window
->sym_path
);
1605 return still_exists
;
1608 void filer_update_all(void)
1610 GList
*next
= all_filer_windows
;
1614 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1616 /* Updating directory may remove it from list -- stop sending
1617 * patches to move this line!
1621 filer_update_dir(filer_window
, TRUE
);
1625 /* Refresh the various caches even if we don't think we need to */
1626 void full_refresh(void)
1629 reread_mime_files(); /* Refreshes all windows */
1632 /* See whether a filer window with a given path already exists
1633 * and is different from diff.
1635 static FilerWindow
*find_filer_window(const char *sym_path
, FilerWindow
*diff
)
1639 for (next
= all_filer_windows
; next
; next
= next
->next
)
1641 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1643 if (filer_window
!= diff
&&
1644 strcmp(sym_path
, filer_window
->sym_path
) == 0)
1645 return filer_window
;
1651 /* This path has been mounted/umounted/deleted some files - update all dirs */
1652 void filer_check_mounted(const char *real_path
)
1654 GList
*next
= all_filer_windows
;
1657 gboolean resize
= o_filer_auto_resize
.int_value
== RESIZE_ALWAYS
;
1659 /* DOS disks, etc, often don't change the mtime of the root directory
1660 * on modification, so force a refresh now.
1662 g_fscache_update(dir_cache
, real_path
);
1664 len
= strlen(real_path
);
1668 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1672 if (strncmp(real_path
, filer_window
->real_path
, len
) == 0)
1674 char s
= filer_window
->real_path
[len
];
1676 if (s
== '/' || s
== '\0')
1678 if (filer_update_dir(filer_window
, FALSE
) &&
1680 view_autosize(filer_window
->view
);
1685 parent
= g_path_get_dirname(real_path
);
1686 refresh_dirs(parent
);
1689 icons_may_update(real_path
);
1692 /* Close all windows displaying 'path' or subdirectories of 'path' */
1693 void filer_close_recursive(const char *path
)
1695 GList
*next
= all_filer_windows
;
1699 real
= pathdup(path
);
1704 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1708 if (strncmp(real
, filer_window
->real_path
, len
) == 0)
1710 char s
= filer_window
->real_path
[len
];
1712 if (len
== 1 || s
== '/' || s
== '\0')
1713 gtk_widget_destroy(filer_window
->window
);
1718 /* Like minibuffer_show(), except that:
1719 * - It returns FALSE (to be used from an idle callback)
1720 * - It checks that the filer window still exists.
1722 static gboolean
minibuffer_show_cb(FilerWindow
*filer_window
)
1724 if (filer_exists(filer_window
))
1725 minibuffer_show(filer_window
, MINI_PATH
);
1729 /* TRUE iff filer_window points to an existing FilerWindow
1732 gboolean
filer_exists(FilerWindow
*filer_window
)
1736 for (next
= all_filer_windows
; next
; next
= next
->next
)
1738 FilerWindow
*fw
= (FilerWindow
*) next
->data
;
1740 if (fw
== filer_window
)
1747 FilerWindow
*filer_get_by_id(const char *id
)
1749 return g_hash_table_lookup(window_with_id
, id
);
1752 void filer_set_id(FilerWindow
*filer_window
, const char *id
)
1754 g_return_if_fail(filer_window
!= NULL
);
1756 if (filer_window
->window_id
)
1758 g_hash_table_remove(window_with_id
, filer_window
->window_id
);
1759 g_free(filer_window
->window_id
);
1760 filer_window
->window_id
= NULL
;
1765 filer_window
->window_id
= g_strdup(id
);
1766 g_hash_table_insert(window_with_id
,
1767 filer_window
->window_id
,
1772 /* Make sure the window title is up-to-date */
1773 void filer_set_title(FilerWindow
*filer_window
)
1775 gchar
*title
= NULL
;
1779 if (filer_window
->scanning
||
1780 filer_window
->filter
!= FILER_SHOW_ALL
||
1781 filer_window
->show_hidden
|| filer_window
->show_thumbs
)
1783 if (o_short_flag_names
.int_value
)
1786 switch(filer_window
->filter
) {
1787 case FILER_SHOW_ALL
:
1788 hidden
=filer_window
->show_hidden
? "A": "";
1790 case FILER_SHOW_GLOB
: hidden
="G"; break;
1791 case FILER_SHOW_REGEXP
: hidden
="R"; break;
1795 flags
= g_strconcat(" +",
1796 filer_window
->scanning
? _("S") : "",
1798 filer_window
->show_thumbs
? _("T") : "",
1803 switch(filer_window
->filter
) {
1804 case FILER_SHOW_ALL
:
1805 hidden
=g_strdup(filer_window
->show_hidden
? "All, ": "");
1807 case FILER_SHOW_GLOB
:
1808 hidden
=g_strdup_printf("Glob (%s), ",
1809 filer_window
->filter_string
);
1811 case FILER_SHOW_REGEXP
:
1812 hidden
=g_strdup_printf("Regexp (%s), ",
1813 filer_window
->filter_string
);
1816 hidden
=g_strdup("");
1819 flags
= g_strconcat(" (",
1820 filer_window
->scanning
? _("Scanning, ") : "",
1822 filer_window
->show_thumbs
? _("Thumbs, ") : "",
1824 flags
[strlen(flags
) - 2] = ')';
1830 title
= g_strconcat("//", our_host_name(),
1831 filer_window
->sym_path
, flags
, NULL
);
1833 if (!title
&& home_dir_len
> 1 &&
1834 strncmp(filer_window
->sym_path
, home_dir
, home_dir_len
) == 0)
1836 guchar sep
= filer_window
->sym_path
[home_dir_len
];
1838 if (sep
== '\0' || sep
== '/')
1839 title
= g_strconcat("~",
1840 filer_window
->sym_path
+ home_dir_len
,
1846 title
= g_strconcat(filer_window
->sym_path
, flags
, NULL
);
1848 ensure_utf8(&title
);
1850 if (filer_window
->directory
->error
)
1853 title
= g_strconcat(old
, ": ", filer_window
->directory
->error
,
1858 gtk_window_set_title(GTK_WINDOW(filer_window
->window
), title
);
1862 if (flags
[0] != '\0')
1866 /* Reconnect to the same directory (used when the Show Hidden option is
1867 * toggled). This has the side-effect of updating the window title.
1869 void filer_detach_rescan(FilerWindow
*filer_window
)
1871 Directory
*dir
= filer_window
->directory
;
1874 detach(filer_window
);
1875 filer_window
->directory
= dir
;
1876 attach(filer_window
);
1879 /* Puts the filer window into target mode. When an item is chosen,
1880 * fn(filer_window, iter, data) is called. 'reason' will be displayed
1881 * on the toolbar while target mode is active.
1883 * Use fn == NULL to cancel target mode.
1885 void filer_target_mode(FilerWindow
*filer_window
,
1890 TargetFunc old_fn
= filer_window
->target_cb
;
1893 gdk_window_set_cursor(
1894 GTK_WIDGET(filer_window
->view
)->window
,
1895 fn
? crosshair
: NULL
);
1897 filer_window
->target_cb
= fn
;
1898 filer_window
->target_data
= data
;
1900 if (filer_window
->toolbar_text
== NULL
)
1905 GTK_LABEL(filer_window
->toolbar_text
), reason
);
1906 else if (o_toolbar_info
.int_value
)
1909 toolbar_update_info(filer_window
);
1912 gtk_label_set_text(GTK_LABEL(filer_window
->toolbar_text
), "");
1915 static void set_selection_state(FilerWindow
*filer_window
, gboolean normal
)
1917 GtkStateType old_state
= filer_window
->selection_state
;
1919 filer_window
->selection_state
= normal
1920 ? GTK_STATE_SELECTED
: GTK_STATE_INSENSITIVE
;
1922 if (old_state
!= filer_window
->selection_state
1923 && view_count_selected(filer_window
->view
))
1924 gtk_widget_queue_draw(GTK_WIDGET(filer_window
->view
));
1927 void filer_cancel_thumbnails(FilerWindow
*filer_window
)
1929 gtk_widget_hide(filer_window
->thumb_bar
);
1931 destroy_glist(&filer_window
->thumb_queue
);
1932 filer_window
->max_thumbs
= 0;
1935 /* Generate the next thumb for this window. The window object is
1936 * unref'd when there is nothing more to do.
1937 * If the window no longer has a filer window, nothing is done.
1939 static gboolean
filer_next_thumb_real(GObject
*window
)
1941 FilerWindow
*filer_window
;
1945 filer_window
= g_object_get_data(window
, "filer_window");
1949 g_object_unref(window
);
1953 if (!filer_window
->thumb_queue
)
1955 filer_cancel_thumbnails(filer_window
);
1956 g_object_unref(window
);
1960 total
= filer_window
->max_thumbs
;
1961 done
= total
- g_list_length(filer_window
->thumb_queue
);
1963 path
= (gchar
*) filer_window
->thumb_queue
->data
;
1965 pixmap_background_thumb(path
, (GFunc
) filer_next_thumb
, window
);
1967 filer_window
->thumb_queue
= g_list_remove(filer_window
->thumb_queue
,
1971 gtk_progress_bar_set_fraction(
1972 GTK_PROGRESS_BAR(filer_window
->thumb_progress
),
1973 done
/ (float) total
);
1978 /* path is the thumb just loaded, if any.
1979 * window is unref'd (eventually).
1981 static void filer_next_thumb(GObject
*window
, const gchar
*path
)
1984 dir_force_update_path(path
);
1986 gtk_idle_add((GtkFunction
) filer_next_thumb_real
, window
);
1989 static void start_thumb_scanning(FilerWindow
*filer_window
)
1991 if (GTK_WIDGET_VISIBLE(filer_window
->thumb_bar
))
1992 return; /* Already scanning */
1994 gtk_widget_show_all(filer_window
->thumb_bar
);
1996 g_object_ref(G_OBJECT(filer_window
->window
));
1997 filer_next_thumb(G_OBJECT(filer_window
->window
), NULL
);
2000 /* Set this image to be loaded some time in the future */
2001 void filer_create_thumb(FilerWindow
*filer_window
, const gchar
*path
)
2003 filer_window
->max_thumbs
++;
2005 filer_window
->thumb_queue
= g_list_append(filer_window
->thumb_queue
,
2008 if (filer_window
->scanning
)
2009 return; /* Will start when scan ends */
2011 start_thumb_scanning(filer_window
);
2014 /* If thumbnail display is on, look through all the items in this directory
2015 * and start creating or updating the thumbnails as needed.
2017 void filer_create_thumbs(FilerWindow
*filer_window
)
2022 if (!filer_window
->show_thumbs
)
2025 view_get_iter(filer_window
->view
, &iter
, 0);
2027 while ((item
= iter
.next(&iter
)))
2029 MaskedPixmap
*pixmap
;
2033 if (item
->base_type
!= TYPE_FILE
)
2036 /*if (strcmp(item->mime_type->media_type, "image") != 0)
2039 path
= make_path(filer_window
->real_path
, item
->leafname
);
2041 pixmap
= g_fscache_lookup_full(pixmap_cache
, path
,
2042 FSCACHE_LOOKUP_ONLY_NEW
, &found
);
2044 g_object_unref(pixmap
);
2046 /* If we didn't get an image, it could be because:
2048 * - We're loading the image now. found is TRUE,
2049 * and we'll update the item later.
2050 * - We tried to load the image and failed. found
2052 * - We haven't tried loading the image. found is
2053 * FALSE, and we start creating the thumb here.
2056 filer_create_thumb(filer_window
, path
);
2060 static void filer_options_changed(void)
2062 if (o_short_flag_names
.has_changed
)
2066 for (next
= all_filer_windows
; next
; next
= next
->next
)
2068 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
2070 filer_set_title(filer_window
);
2075 /* Append interesting information to this GString */
2076 void filer_add_tip_details(FilerWindow
*filer_window
,
2077 GString
*tip
, DirItem
*item
)
2079 const guchar
*fullpath
= NULL
;
2081 fullpath
= make_path(filer_window
->real_path
, item
->leafname
);
2083 if (item
->flags
& ITEM_FLAG_SYMLINK
)
2087 target
= readlink_dup(fullpath
);
2090 ensure_utf8(&target
);
2092 g_string_append(tip
, _("Symbolic link to "));
2093 g_string_append(tip
, target
);
2094 g_string_append_c(tip
, '\n');
2099 if (item
->flags
& ITEM_FLAG_APPDIR
)
2104 info
= appinfo_get(fullpath
, item
);
2105 if (info
&& ((node
= xml_get_section(info
, NULL
, "Summary"))))
2108 str
= xmlNodeListGetString(node
->doc
,
2109 node
->xmlChildrenNode
, 1);
2112 g_string_append(tip
, str
);
2113 g_string_append_c(tip
, '\n');
2118 g_object_unref(info
);
2121 if (!g_utf8_validate(item
->leafname
, -1, NULL
))
2122 g_string_append(tip
,
2123 _("This filename is not valid UTF-8. "
2124 "You should rename it.\n"));
2127 /* Return the selection as a text/uri-list.
2128 * g_free() the result.
2130 static guchar
*filer_create_uri_list(FilerWindow
*filer_window
)
2138 g_return_val_if_fail(filer_window
!= NULL
, NULL
);
2140 string
= g_string_new(NULL
);
2142 leader
= g_string_new(filer_window
->sym_path
);
2143 if (leader
->str
[leader
->len
- 1] != '/')
2144 g_string_append_c(leader
, '/');
2146 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
2147 while ((item
= iter
.next(&iter
)))
2151 path
= g_strconcat(leader
->str
, item
->leafname
, NULL
);
2152 uri
= encode_path_as_uri(path
);
2153 g_string_append(string
, uri
);
2154 g_string_append(string
, "\r\n");
2159 g_string_free(leader
, TRUE
);
2160 retval
= string
->str
;
2161 g_string_free(string
, FALSE
);
2166 void filer_perform_action(FilerWindow
*filer_window
, GdkEventButton
*event
)
2169 ViewIface
*view
= filer_window
->view
;
2170 DirItem
*item
= NULL
;
2171 gboolean press
= event
->type
== GDK_BUTTON_PRESS
;
2173 OpenFlags flags
= 0;
2175 if (event
->button
> 3)
2178 view_get_iter_at_point(view
, &iter
, event
->window
, event
->x
, event
->y
);
2179 item
= iter
.peek(&iter
);
2181 if (item
&& event
->button
== 1 &&
2182 view_get_selected(view
, &iter
) &&
2183 filer_window
->selection_state
== GTK_STATE_INSENSITIVE
)
2185 /* Possibly a really slow DnD operation? */
2186 filer_window
->temp_item_selected
= FALSE
;
2188 filer_selection_changed(filer_window
, event
->time
);
2192 if (filer_window
->target_cb
)
2194 dnd_motion_ungrab();
2195 if (item
&& press
&& event
->button
== 1)
2196 filer_window
->target_cb(filer_window
, &iter
,
2197 filer_window
->target_data
);
2199 filer_target_mode(filer_window
, NULL
, NULL
, NULL
);
2204 if (!o_single_click
.int_value
)
2206 /* Make sure both parts of a double-click fall on
2209 static guchar
*first_click
= NULL
;
2210 static guchar
*second_click
= NULL
;
2212 if (event
->type
== GDK_BUTTON_PRESS
)
2214 g_free(first_click
);
2215 first_click
= second_click
;
2218 second_click
= g_strdup(item
->leafname
);
2220 second_click
= NULL
;
2223 if (event
->type
== GDK_2BUTTON_PRESS
)
2225 if (first_click
&& second_click
&&
2226 strcmp(first_click
, second_click
) != 0)
2228 if ((first_click
|| second_click
) &&
2229 !(first_click
&& second_click
))
2234 action
= bind_lookup_bev(
2235 item
? BIND_DIRECTORY_ICON
: BIND_DIRECTORY
,
2240 case ACT_CLEAR_SELECTION
:
2241 view_clear_selection(view
);
2243 case ACT_TOGGLE_SELECTED
:
2244 view_set_selected(view
, &iter
,
2245 !view_get_selected(view
, &iter
));
2247 case ACT_SELECT_EXCL
:
2248 view_select_only(view
, &iter
);
2251 flags
|= OPEN_SHIFT
;
2254 if (event
->button
!= 1 || event
->state
& GDK_MOD1_MASK
)
2255 flags
|= OPEN_CLOSE_WINDOW
;
2257 flags
|= OPEN_SAME_WINDOW
;
2258 if (o_new_button_1
.int_value
)
2259 flags
^= OPEN_SAME_WINDOW
;
2260 if (event
->type
== GDK_2BUTTON_PRESS
)
2261 view_set_selected(view
, &iter
, FALSE
);
2262 dnd_motion_ungrab();
2264 filer_openitem(filer_window
, &iter
, flags
);
2266 case ACT_POPUP_MENU
:
2267 dnd_motion_ungrab();
2269 show_filer_menu(filer_window
,
2270 (GdkEvent
*) event
, &iter
);
2272 case ACT_PRIME_AND_SELECT
:
2273 if (item
&& !view_get_selected(view
, &iter
))
2274 view_select_only(view
, &iter
);
2275 dnd_motion_start(MOTION_READY_FOR_DND
);
2277 case ACT_PRIME_AND_TOGGLE
:
2278 view_set_selected(view
, &iter
,
2279 !view_get_selected(view
, &iter
));
2280 dnd_motion_start(MOTION_READY_FOR_DND
);
2282 case ACT_PRIME_FOR_DND
:
2283 dnd_motion_start(MOTION_READY_FOR_DND
);
2286 if (press
&& event
->button
< 4)
2289 view_wink_item(view
, &iter
);
2290 dnd_motion_start(MOTION_NONE
);
2293 case ACT_LASSO_CLEAR
:
2294 view_clear_selection(view
);
2296 case ACT_LASSO_MODIFY
:
2297 view_start_lasso_box(view
, event
);
2300 view_autosize(filer_window
->view
);
2303 g_warning("Unsupported action : %d\n", action
);
2308 /* It's time to make the tooltip appear. If we're not over the item any
2309 * more, or the item doesn't need a tooltip, do nothing.
2311 static gboolean
tooltip_activate(GtkWidget
*window
)
2313 FilerWindow
*filer_window
;
2317 DirItem
*item
= NULL
;
2318 GString
*tip
= NULL
;
2320 g_return_val_if_fail(tip_item
!= NULL
, 0);
2322 filer_window
= g_object_get_data(G_OBJECT(window
), "filer_window");
2324 if (!motion_window
|| !filer_window
)
2325 return FALSE
; /* Window has been destroyed */
2327 view
= filer_window
->view
;
2331 gdk_window_get_pointer(motion_window
, &x
, &y
, NULL
);
2332 view_get_iter_at_point(view
, &iter
, motion_window
, x
, y
);
2334 item
= iter
.peek(&iter
);
2335 if (item
!= tip_item
)
2336 return FALSE
; /* Not still under the pointer */
2338 /* OK, the filer window still exists and the pointer is still
2339 * over the same item. Do we need to show a tip?
2342 tip
= g_string_new(NULL
);
2344 view_extend_tip(filer_window
->view
, &iter
, tip
);
2346 filer_add_tip_details(filer_window
, tip
, tip_item
);
2350 g_string_truncate(tip
, tip
->len
- 1);
2352 tooltip_show(tip
->str
);
2355 g_string_free(tip
, TRUE
);
2360 /* Motion detected on the View widget */
2361 gint
filer_motion_notify(FilerWindow
*filer_window
, GdkEventMotion
*event
)
2363 ViewIface
*view
= filer_window
->view
;
2367 view_get_iter_at_point(view
, &iter
, event
->window
, event
->x
, event
->y
);
2368 item
= iter
.peek(&iter
);
2372 if (item
!= tip_item
)
2377 motion_window
= event
->window
;
2378 tooltip_prime((GtkFunction
) tooltip_activate
,
2379 G_OBJECT(filer_window
->window
));
2388 if (motion_state
!= MOTION_READY_FOR_DND
)
2391 if (!dnd_motion_moved(event
))
2394 view_get_iter_at_point(view
, &iter
,
2396 event
->x
- (event
->x_root
- drag_start_x
),
2397 event
->y
- (event
->y_root
- drag_start_y
));
2398 item
= iter
.peek(&iter
);
2402 view_wink_item(view
, NULL
);
2404 if (!view_get_selected(view
, &iter
))
2406 if (event
->state
& GDK_BUTTON1_MASK
)
2408 /* Select just this one */
2409 filer_window
->temp_item_selected
= TRUE
;
2410 view_select_only(view
, &iter
);
2414 if (view_count_selected(view
) == 0)
2415 filer_window
->temp_item_selected
= TRUE
;
2416 view_set_selected(view
, &iter
, TRUE
);
2420 g_return_val_if_fail(view_count_selected(view
) > 0, TRUE
);
2422 if (view_count_selected(view
) == 1)
2425 item
= dir_update_item(filer_window
->directory
,
2430 report_error(_("Item no longer exists!"));
2434 drag_one_item(GTK_WIDGET(view
), event
,
2435 make_path(filer_window
->sym_path
, item
->leafname
),
2438 /* XXX: Use thumbnail */
2439 item
, view
? view
->image
: NULL
);
2446 uris
= filer_create_uri_list(filer_window
);
2447 drag_selection(GTK_WIDGET(view
), event
, uris
);
2454 static void drag_end(GtkWidget
*widget
, GdkDragContext
*context
,
2455 FilerWindow
*filer_window
)
2457 filer_set_autoscroll(filer_window
, FALSE
);
2459 if (filer_window
->temp_item_selected
)
2461 view_clear_selection(filer_window
->view
);
2462 filer_window
->temp_item_selected
= FALSE
;
2466 /* Remove highlights */
2467 static void drag_leave(GtkWidget
*widget
,
2468 GdkDragContext
*context
,
2470 FilerWindow
*filer_window
)
2475 /* Called during the drag when the mouse is in a widget registered
2476 * as a drop target. Returns TRUE if we can accept the drop.
2478 static gboolean
drag_motion(GtkWidget
*widget
,
2479 GdkDragContext
*context
,
2483 FilerWindow
*filer_window
)
2486 ViewIface
*view
= filer_window
->view
;
2488 GdkDragAction action
= context
->suggested_action
;
2489 const guchar
*new_path
= NULL
;
2490 const char *type
= NULL
;
2491 gboolean retval
= FALSE
;
2492 gboolean same_window
;
2494 if ((context
->actions
& GDK_ACTION_ASK
) && o_dnd_left_menu
.int_value
)
2497 gdk_window_get_pointer(NULL
, NULL
, NULL
, &state
);
2498 if (state
& GDK_BUTTON1_MASK
)
2499 action
= GDK_ACTION_ASK
;
2502 same_window
= gtk_drag_get_source_widget(context
) == widget
;
2504 filer_set_autoscroll(filer_window
, TRUE
);
2506 if (filer_window
->view_type
== VIEW_TYPE_DETAILS
)
2510 /* Correct for position of bin window */
2511 bin
= gtk_tree_view_get_bin_window(GTK_TREE_VIEW(view
));
2512 gdk_window_get_position(bin
, NULL
, &bin_y
);
2516 if (o_dnd_drag_to_icons
.int_value
)
2518 view_get_iter_at_point(view
, &iter
, widget
->window
, x
, y
);
2519 item
= iter
.peek(&iter
);
2524 if (item
&& same_window
&& view_get_selected(view
, &iter
))
2527 type
= dnd_motion_item(context
, &item
);
2532 /* Don't allow drops to non-writeable directories. BUT, still
2533 * allow drops on non-writeable SUBdirectories so that we can
2534 * do the spring-open thing.
2536 if (item
&& type
== drop_dest_dir
&&
2537 !(item
->flags
& ITEM_FLAG_APPDIR
))
2539 dnd_spring_load(context
, filer_window
);
2545 view_cursor_to_iter(view
, &iter
);
2548 view_cursor_to_iter(view
, NULL
);
2550 /* Disallow background drops within a single window */
2551 if (type
&& same_window
)
2558 new_path
= make_path(filer_window
->sym_path
,
2561 new_path
= filer_window
->sym_path
;
2564 /* Don't ask about dragging to an application! */
2565 if (type
== drop_dest_prog
&& action
== GDK_ACTION_ASK
)
2566 action
= GDK_ACTION_COPY
;
2568 g_dataset_set_data(context
, "drop_dest_type", (gpointer
) type
);
2571 gdk_drag_status(context
, action
, time
);
2572 g_dataset_set_data_full(context
, "drop_dest_path",
2573 g_strdup(new_path
), g_free
);
2580 static gboolean
as_timeout(FilerWindow
*filer_window
)
2584 retval
= view_auto_scroll_callback(filer_window
->view
);
2587 filer_window
->auto_scroll
= -1;
2592 /* When autoscroll is on, a timer keeps track of the pointer position.
2593 * While it's near the top or bottom of the window, the window scrolls.
2595 * If the mouse buttons are released, the pointer leaves the window, or
2596 * a drag-and-drop operation finishes, auto_scroll is turned off.
2598 void filer_set_autoscroll(FilerWindow
*filer_window
, gboolean auto_scroll
)
2600 g_return_if_fail(filer_window
!= NULL
);
2604 if (filer_window
->auto_scroll
!= -1)
2605 return; /* Already on! */
2607 filer_window
->auto_scroll
= gtk_timeout_add(50,
2608 (GtkFunction
) as_timeout
,
2613 if (filer_window
->auto_scroll
== -1)
2614 return; /* Already off! */
2616 gtk_timeout_remove(filer_window
->auto_scroll
);
2617 filer_window
->auto_scroll
= -1;
2621 #define ZERO_MNT "/uri/0install"
2623 static void refresh_done(FilerWindow
*filer_window
)
2625 if (filer_exists(filer_window
))
2626 filer_update_dir(filer_window
, TRUE
);
2629 void filer_refresh(FilerWindow
*filer_window
)
2631 if (!strncmp(ZERO_MNT
"/", filer_window
->real_path
, sizeof(ZERO_MNT
)))
2633 /* Try to run 0refresh */
2635 gchar
*argv
[] = {"0refresh", NULL
, NULL
};
2636 const char *host
= filer_window
->real_path
+ sizeof(ZERO_MNT
);
2639 slash
= strchr(host
, '/');
2641 argv
[1] = g_strndup(host
, slash
- host
);
2643 argv
[1] = g_strdup(host
);
2644 pid
= rox_spawn(filer_window
->real_path
, (const char **) argv
);
2647 on_child_death(pid
, (CallbackFn
) refresh_done
,
2654 gboolean
filer_match_filter(FilerWindow
*filer_window
, const gchar
*filename
)
2656 if(filename
[0]=='.' &&
2657 (!filer_window
->temp_show_hidden
&& !filer_window
->show_hidden
))
2660 /*printf("%d %s\n", filer_window->filter, filename);*/
2661 switch(filer_window
->filter
) {
2662 case FILER_SHOW_GLOB
:
2663 return fnmatch(filer_window
->filter_string
,
2666 case FILER_SHOW_REGEXP
: /* Unimplemented */
2668 case FILER_SHOW_ALL
:
2675 /* Provided to hide the implementation */
2676 void filer_set_hidden(FilerWindow
*filer_window
, gboolean hidden
)
2678 filer_window
->show_hidden
=hidden
;
2681 void filer_set_filter(FilerWindow
*filer_window
, FilterType type
,
2682 const gchar
*filter_string
)
2684 /*printf("filer_set_filter(%p, %d, %s)\n", filer_window, type,
2685 filter_string? filter_string: "NULL");*/
2686 /* Is this new filter the same as the old one? */
2687 if(filer_window
->filter
==type
) {
2688 switch(filer_window
->filter
) {
2689 case FILER_SHOW_ALL
:
2691 case FILER_SHOW_GLOB
:
2692 case FILER_SHOW_REGEXP
:
2693 if(strcmp(filer_window
->filter_string
,
2700 /* Clean up old filter */
2701 if(filer_window
->filter_string
) {
2702 g_free(filer_window
->filter_string
);
2703 filer_window
->filter_string
=NULL
;
2705 /* Also clean up compiled regexp when implemented */
2707 /*printf("set %d %s\n", type,
2708 filter_string? filter_string: "NULL");*/
2709 filer_window
->filter
=type
;
2711 case FILER_SHOW_ALL
:
2715 case FILER_SHOW_GLOB
:
2716 filer_window
->filter_string
=g_strdup(filter_string
);
2719 case FILER_SHOW_REGEXP
:
2720 filer_window
->filter_string
=g_strdup(filter_string
);
2721 /* Compile the pattern */
2726 filer_window
->filter
=FILER_SHOW_ALL
;
2727 report_error("Impossible: filter type %d", type
);
2733 static Settings
*settings_new(const char *path
)
2737 set
=g_new(Settings
, 1);
2738 memset(set
, 0, sizeof(Settings
));
2740 set
->path
=g_strdup(path
);
2745 static void settings_free(Settings
*set
)
2749 g_free(set
->filter
);
2753 static gboolean
free_settings(gpointer key
, gpointer value
, gpointer data
)
2755 if(!data
|| strcmp(data
, key
)==0)
2756 settings_free(value
);
2761 static void store_settings(Settings
*set
)
2765 old
=g_hash_table_lookup(settings_table
, set
->path
);
2767 g_hash_table_foreach_remove(settings_table
, free_settings
,
2771 g_hash_table_insert(settings_table
, set
->path
, set
);
2774 /* TODO: use symbolic names in the XML file where possible */
2775 static void load_from_node(Settings
*set
, xmlDocPtr doc
, xmlNodePtr node
)
2779 str
=xmlNodeListGetString(doc
, node
->xmlChildrenNode
, 1);
2781 if(strcmp(node
->name
, "X") == 0) {
2783 set
->flags
|=SET_POSITION
;
2784 } else if(strcmp(node
->name
, "Y") == 0) {
2786 set
->flags
|=SET_POSITION
;
2787 } else if(strcmp(node
->name
, "Width") == 0) {
2788 set
->width
=atoi(str
);
2789 set
->flags
|=SET_SIZE
;
2790 } else if(strcmp(node
->name
, "Height") == 0) {
2791 set
->height
=atoi(str
);
2792 set
->flags
|=SET_SIZE
;
2793 } else if(strcmp(node
->name
, "ShowHidden") == 0) {
2794 set
->show_hidden
=atoi(str
);
2795 set
->flags
|=SET_HIDDEN
;
2796 } else if(strcmp(node
->name
, "ViewType") == 0) {
2797 set
->view_type
=atoi(str
);
2798 set
->flags
|=SET_DETAILS
;
2799 } else if(strcmp(node
->name
, "DetailsType") == 0) {
2800 set
->details_type
=atoi(str
);
2801 set
->flags
|=SET_DETAILS
;
2802 } else if(strcmp(node
->name
, "SortType") == 0) {
2803 set
->sort_type
=atoi(str
);
2804 set
->flags
|=SET_SORT
;
2805 } else if(strcmp(node
->name
, "SortOrder") == 0) {
2806 set
->sort_order
=atoi(str
);
2807 set
->flags
|=SET_SORT
;
2808 } else if(strcmp(node
->name
, "DisplayStyle") == 0) {
2809 set
->display_style
=atoi(str
);
2810 set
->flags
|=SET_STYLE
;
2811 } else if(strcmp(node
->name
, "ShowThumbs") == 0) {
2812 set
->show_thumbs
=atoi(str
);
2813 set
->flags
|=SET_THUMBS
;
2814 } else if(strcmp(node
->name
, "FilterType") == 0) {
2815 set
->filter_type
=atoi(str
);
2816 set
->flags
|=SET_FILTER
;
2817 } else if(strcmp(node
->name
, "Filter") == 0) {
2818 set
->filter
=g_strdup(str
);
2819 set
->flags
|=SET_FILTER
;
2826 static void load_settings(void)
2829 XMLwrapper
*settings_doc
=NULL
;
2831 path
=choices_find_path_load("Settings.xml", "ROX-Filer");
2833 settings_doc
=xml_new(path
);
2838 settings_table
=g_hash_table_new(g_str_hash
, g_str_equal
);
2841 xmlNodePtr node
, subnode
;
2843 node
= xmlDocGetRootElement(settings_doc
->doc
);
2845 for (node
= node
->xmlChildrenNode
; node
; node
= node
->next
)
2850 if (node
->type
!= XML_ELEMENT_NODE
)
2852 if (strcmp(node
->name
, "FilerWindow") != 0)
2855 path
=xmlGetProp(node
, "path");
2856 set
=settings_new(path
);
2859 for (subnode
=node
->xmlChildrenNode
; subnode
;
2860 subnode
=subnode
->next
) {
2862 if (subnode
->type
!= XML_ELEMENT_NODE
)
2864 load_from_node(set
, settings_doc
->doc
,
2868 store_settings(set
);
2870 g_object_unref(settings_doc
);
2874 static void add_nodes(gpointer key
, gpointer value
, gpointer data
)
2876 xmlNodePtr node
=(xmlNodePtr
) data
;
2878 Settings
*set
=(Settings
*) value
;
2881 sub
=xmlNewChild(node
, NULL
, "FilerWindow", NULL
);
2883 xmlSetProp(sub
, "path", set
->path
);
2885 if(set
->flags
& SET_POSITION
) {
2886 tmp
=g_strdup_printf("%d", set
->x
);
2887 xmlNewChild(sub
, NULL
, "X", tmp
);
2889 tmp
=g_strdup_printf("%d", set
->y
);
2890 xmlNewChild(sub
, NULL
, "Y", tmp
);
2893 if(set
->flags
& SET_SIZE
) {
2894 tmp
=g_strdup_printf("%d", set
->width
);
2895 xmlNewChild(sub
, NULL
, "Width", tmp
);
2897 tmp
=g_strdup_printf("%d", set
->height
);
2898 xmlNewChild(sub
, NULL
, "Height", tmp
);
2901 if(set
->flags
& SET_HIDDEN
) {
2902 tmp
=g_strdup_printf("%d", set
->show_hidden
);
2903 xmlNewChild(sub
, NULL
, "ShowHidden", tmp
);
2906 if(set
->flags
& SET_STYLE
) {
2907 tmp
=g_strdup_printf("%d", set
->display_style
);
2908 xmlNewChild(sub
, NULL
, "DisplayStyle", tmp
);
2911 if(set
->flags
& SET_SORT
) {
2912 tmp
=g_strdup_printf("%d", set
->sort_type
);
2913 xmlNewChild(sub
, NULL
, "SortType", tmp
);
2915 tmp
=g_strdup_printf("%d", set
->sort_order
);
2916 xmlNewChild(sub
, NULL
, "SortOrder", tmp
);
2919 if(set
->flags
& SET_DETAILS
) {
2920 tmp
=g_strdup_printf("%d", set
->view_type
);
2921 xmlNewChild(sub
, NULL
, "ViewType", tmp
);
2923 tmp
=g_strdup_printf("%d", set
->details_type
);
2924 xmlNewChild(sub
, NULL
, "DetailsType", tmp
);
2927 if(set
->flags
& SET_STYLE
) {
2928 tmp
=g_strdup_printf("%d", set
->show_thumbs
);
2929 xmlNewChild(sub
, NULL
, "ShowThumbs", tmp
);
2932 if(set
->flags
& SET_FILTER
) {
2933 tmp
=g_strdup_printf("%d", set
->filter_type
);
2934 xmlNewChild(sub
, NULL
, "FilterType", tmp
);
2936 if(set
->filter
&& set
->filter
[0])
2937 xmlNewChild(sub
, NULL
, "Filter", set
->filter
);
2941 static void save_settings(void)
2945 path
=choices_find_path_save("Settings.xml", "ROX-Filer", TRUE
);
2947 xmlDocPtr doc
= xmlNewDoc("1.0");
2948 xmlDocSetRootElement(doc
, xmlNewDocNode(doc
, NULL
,
2951 g_hash_table_foreach(settings_table
, add_nodes
,
2952 xmlDocGetRootElement(doc
));
2954 save_xml_file(doc
, path
);
2962 static void check_settings(FilerWindow
*filer_window
)
2966 set
=(Settings
*) g_hash_table_lookup(settings_table
,
2967 filer_window
->sym_path
);
2970 if(set
->flags
& SET_POSITION
)
2971 gtk_window_move(GTK_WINDOW(filer_window
->window
),
2973 if(set
->flags
& SET_SIZE
)
2974 filer_window_set_size(filer_window
, set
->width
,
2976 if(set
->flags
& SET_HIDDEN
)
2977 filer_set_hidden(filer_window
, set
->show_hidden
);
2979 if(set
->flags
& (SET_STYLE
|SET_DETAILS
)) {
2980 DisplayStyle style
=filer_window
->display_style
;
2981 DetailsType details
=filer_window
->details_type
;
2983 if(set
->flags
& SET_STYLE
)
2984 style
=set
->display_style
;
2986 if(set
->flags
& SET_DETAILS
) {
2987 details
=set
->details_type
;
2989 /* This next causes a warning */
2990 filer_set_view_type(filer_window
,
2994 display_set_layout(filer_window
, style
,
2998 if(set
->flags
& SET_SORT
)
2999 display_set_sort_type(filer_window
,
3003 if(set
->flags
& SET_THUMBS
)
3004 display_set_thumbs(filer_window
,
3007 if(set
->flags
& SET_FILTER
)
3008 display_set_filter(filer_window
,
3015 typedef struct settings_window
{
3018 GtkWidget
*pos
, *size
, *hidden
, *style
, *sort
, *details
,
3025 static void settings_response(GtkWidget
*window
, gint response
,
3026 SettingsWindow
*set_win
)
3028 if(response
==GTK_RESPONSE_OK
) {
3031 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->pos
)))
3032 flags
|=SET_POSITION
;
3033 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->size
)))
3035 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->hidden
)))
3037 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->style
)))
3039 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->sort
)))
3041 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->details
)))
3043 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->thumbs
)))
3045 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->filter
)))
3048 set_win
->set
->flags
=flags
;
3049 store_settings(set_win
->set
);
3053 gtk_widget_destroy(window
);
3056 void filer_save_settings(FilerWindow
*fwin
)
3058 SettingsWindow
*set_win
;
3063 Settings
*set
=settings_new(fwin
->sym_path
);
3065 gtk_window_get_position(GTK_WINDOW(fwin
->window
),&x
, &y
);
3066 set
->flags
|=SET_POSITION
;
3070 gtk_window_get_size(GTK_WINDOW(fwin
->window
),&x
, &y
);
3071 set
->flags
|=SET_SIZE
;
3075 set
->flags
|=SET_HIDDEN
;
3076 set
->show_hidden
=fwin
->show_hidden
;
3078 set
->flags
|=SET_STYLE
;
3079 set
->display_style
=fwin
->display_style
;
3081 set
->flags
|=SET_SORT
;
3082 set
->sort_type
=fwin
->sort_type
;
3083 set
->sort_order
=fwin
->sort_order
;
3085 set
->flags
|=SET_DETAILS
;
3086 set
->view_type
=fwin
->view_type
;
3087 set
->details_type
=fwin
->details_type
;
3089 set
->flags
|=SET_THUMBS
;
3090 set
->show_thumbs
=fwin
->show_thumbs
;
3092 set
->flags
|=SET_FILTER
;
3093 set
->filter_type
=fwin
->filter
;
3094 if(fwin
->filter_string
)
3095 set
->filter
=g_strdup(fwin
->filter_string
);
3097 /* Store other parameters
3099 set_win
=g_new(SettingsWindow
, 1);
3101 set_win
->window
=gtk_dialog_new();
3102 number_of_windows
++;
3104 gtk_dialog_add_button(GTK_DIALOG(set_win
->window
),
3105 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
);
3106 gtk_dialog_add_button(GTK_DIALOG(set_win
->window
),
3107 GTK_STOCK_OK
, GTK_RESPONSE_OK
);
3109 g_signal_connect(set_win
->window
, "destroy",
3110 G_CALLBACK(one_less_window
), NULL
);
3111 g_signal_connect_swapped(set_win
->window
, "destroy",
3112 G_CALLBACK(g_free
), set_win
);
3114 gtk_window_set_title(GTK_WINDOW(set_win
->window
),
3115 _("Select display properties to save"));
3117 vbox
=GTK_DIALOG(set_win
->window
)->vbox
;
3119 path
=gtk_label_new(set
->path
);
3120 gtk_box_pack_start(GTK_BOX(vbox
), path
, FALSE
, FALSE
, 2);
3122 set_win
->pos
=gtk_check_button_new_with_label(_("Position"));
3123 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->pos
, FALSE
, FALSE
, 2);
3124 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->pos
),
3125 set
->flags
& SET_POSITION
);
3127 set_win
->size
=gtk_check_button_new_with_label(_("Size"));
3128 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->size
, FALSE
, FALSE
, 2);
3129 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->size
),
3130 set
->flags
& SET_SIZE
);
3132 set_win
->hidden
=gtk_check_button_new_with_label(_("Show hidden"));
3133 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->hidden
,
3135 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->hidden
),
3136 set
->flags
& SET_HIDDEN
);
3138 set_win
->style
=gtk_check_button_new_with_label(_("Display style"));
3139 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->style
,
3141 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->style
),
3142 set
->flags
& SET_STYLE
);
3144 set_win
->sort
=gtk_check_button_new_with_label(_("Sort type and order"));
3145 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->sort
, FALSE
, FALSE
, 2);
3146 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->sort
),
3147 set
->flags
& SET_SORT
);
3149 set_win
->details
=gtk_check_button_new_with_label(_("Details"));
3150 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->details
, FALSE
, FALSE
, 2);
3151 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->details
),
3152 set
->flags
& SET_DETAILS
);
3154 set_win
->thumbs
=gtk_check_button_new_with_label(_("Thumbnails"));
3155 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->thumbs
,
3157 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->thumbs
),
3158 set
->flags
& SET_THUMBS
);
3160 set_win
->filter
=gtk_check_button_new_with_label(_("Filter"));
3161 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->filter
,
3163 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->filter
),
3164 set
->flags
& SET_FILTER
);
3167 g_signal_connect(set_win
->window
, "response",
3168 G_CALLBACK(settings_response
), set_win
);
3170 gtk_widget_show_all(set_win
->window
);