2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA
20 /* filer.c - code for handling filer windows */
30 #include <sys/param.h>
35 #include <gdk/gdkkeysyms.h>
44 #include "gui_support.h"
54 #include "minibuffer.h"
61 #include "view_iface.h"
62 #include "view_collection.h"
63 #include "view_details.h"
65 #include "bookmarks.h"
68 static XMLwrapper
*groups
= NULL
;
70 /* Item we are about to display a tooltip for */
71 static DirItem
*tip_item
= NULL
;
73 /* The window which the motion event for the tooltip came from. Use this
74 * to get the correct widget for finding the item under the pointer.
76 static GdkWindow
*motion_window
= NULL
;
78 /* This is rather badly named. It's actually the filer window which received
79 * the last key press or Menu click event.
81 FilerWindow
*window_with_focus
= NULL
;
83 GList
*all_filer_windows
= NULL
;
85 static GHashTable
*window_with_id
= NULL
;
87 static FilerWindow
*window_with_primary
= NULL
;
89 static GHashTable
*settings_table
=NULL
;
94 guint flags
; /* Which parts are valid, see below */
101 GtkSortType sort_order
;
102 gboolean show_thumbs
;
104 DetailsType details_type
;
105 DisplayStyle display_style
;
107 FilterType filter_type
;
109 gboolean filter_directories
;
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 GHashTable
*unmount_prompt_actions
= NULL
;
125 /* Static prototypes */
126 static void attach(FilerWindow
*filer_window
);
127 static void detach(FilerWindow
*filer_window
);
128 static void filer_window_destroyed(GtkWidget
*widget
,
129 FilerWindow
*filer_window
);
130 static void update_display(Directory
*dir
,
133 FilerWindow
*filer_window
);
134 static void set_scanning_display(FilerWindow
*filer_window
, gboolean scanning
);
135 static gboolean
may_rescan(FilerWindow
*filer_window
, gboolean warning
);
136 static gboolean
minibuffer_show_cb(FilerWindow
*filer_window
);
137 static FilerWindow
*find_filer_window(const char *sym_path
, FilerWindow
*diff
);
138 static void filer_add_widgets(FilerWindow
*filer_window
, const gchar
*wm_class
);
139 static void filer_add_signals(FilerWindow
*filer_window
);
141 static void set_selection_state(FilerWindow
*filer_window
, gboolean normal
);
142 static void filer_next_thumb(GObject
*window
, const gchar
*path
);
143 static void start_thumb_scanning(FilerWindow
*filer_window
);
144 static void filer_options_changed(void);
145 static void drag_end(GtkWidget
*widget
, GdkDragContext
*context
,
146 FilerWindow
*filer_window
);
147 static void drag_leave(GtkWidget
*widget
,
148 GdkDragContext
*context
,
150 FilerWindow
*filer_window
);
151 static gboolean
drag_motion(GtkWidget
*widget
,
152 GdkDragContext
*context
,
156 FilerWindow
*filer_window
);
158 static void load_learnt_mounts(void);
159 static void save_learnt_mounts(void);
160 static void load_settings(void);
161 static void save_settings(void);
162 static void check_settings(FilerWindow
*filer_window
);
163 static char *tip_from_desktop_file(const char *full_path
);
165 static GdkCursor
*busy_cursor
= NULL
;
166 static GdkCursor
*crosshair
= NULL
;
168 /* Indicates whether the filer's display is different to the machine it
169 * is actually running on.
171 static gboolean not_local
= FALSE
;
173 static Option o_short_flag_names
;
174 static Option o_filer_view_type
;
175 Option o_filer_auto_resize
, o_unique_filer_windows
;
176 Option o_filer_size_limit
;
178 #define ROX_RESPONSE_EJECT 99 /**< User clicked on Eject button */
180 void filer_init(void)
184 gchar
*dpyhost
, *tmp
;
186 option_add_int(&o_filer_size_limit
, "filer_size_limit", 75);
187 option_add_int(&o_filer_auto_resize
, "filer_auto_resize",
189 option_add_int(&o_unique_filer_windows
, "filer_unique_windows", 0);
191 option_add_int(&o_short_flag_names
, "filer_short_flag_names", FALSE
);
193 option_add_int(&o_filer_view_type
, "filer_view_type",
194 VIEW_TYPE_COLLECTION
);
196 option_add_notify(filer_options_changed
);
198 busy_cursor
= gdk_cursor_new(GDK_WATCH
);
199 crosshair
= gdk_cursor_new(GDK_CROSSHAIR
);
201 window_with_id
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
204 /* Is the display on the local machine, or are we being
205 * run remotely? See filer_set_title().
207 ohost
= our_host_name();
208 dpy
= gdk_get_display();
209 dpyhost
= g_strdup(dpy
);
210 tmp
= strchr(dpyhost
, ':');
214 if (dpyhost
[0] && strcmp(ohost
, dpyhost
) != 0)
216 /* Try the cannonical name for dpyhost (see our_host_name()
221 ent
= gethostbyname(dpyhost
);
222 if (!ent
|| strcmp(ohost
, ent
->h_name
) != 0)
229 load_learnt_mounts();
232 static gboolean
if_deleted(gpointer item
, gpointer removed
)
234 int i
= ((GPtrArray
*) removed
)->len
;
235 DirItem
**r
= (DirItem
**) ((GPtrArray
*) removed
)->pdata
;
236 char *leafname
= ((DirItem
*) item
)->leafname
;
240 if (strcmp(leafname
, r
[i
]->leafname
) == 0)
247 #define DECOR_BORDER 32
249 /* Resize the filer window to w x h pixels, plus border (not clamped).
250 * If triggered by a key event, warp the pointer (for SloppyFocus users).
252 void filer_window_set_size(FilerWindow
*filer_window
, int w
, int h
)
256 g_return_if_fail(filer_window
!= NULL
);
258 if (filer_window
->scrollbar
)
259 w
+= filer_window
->scrollbar
->allocation
.width
;
261 if (o_toolbar
.int_value
!= TOOLBAR_NONE
)
262 h
+= filer_window
->toolbar
->allocation
.height
;
263 if (filer_window
->message
)
264 h
+= filer_window
->message
->allocation
.height
;
266 window
= filer_window
->window
;
268 if (GTK_WIDGET_VISIBLE(window
))
271 GtkRequisition
*req
= &window
->requisition
;
272 GdkWindow
*gdk_window
= window
->window
;
275 w
= MAX(req
->width
, w
);
276 h
= MAX(req
->height
, h
);
277 gdk_window_get_pointer(NULL
, &x
, &y
, NULL
);
278 m
= gdk_screen_get_monitor_at_point(gdk_screen_get_default(),
280 gdk_window_get_position(gdk_window
, &x
, &y
);
282 if (x
+ w
+ DECOR_BORDER
>
283 monitor_geom
[m
].x
+ monitor_geom
[m
].width
||
284 y
+ h
+ DECOR_BORDER
>
285 monitor_geom
[m
].y
+ monitor_geom
[m
].height
)
287 if (x
+ w
+ DECOR_BORDER
>
288 monitor_geom
[m
].x
+ monitor_geom
[m
].width
)
290 x
= monitor_geom
[m
].x
+ monitor_geom
[m
].width
-
291 w
- 4 - DECOR_BORDER
;
293 if (y
+ h
+ DECOR_BORDER
>
294 monitor_geom
[m
].y
+ monitor_geom
[m
].height
)
296 y
= monitor_geom
[m
].y
+ monitor_geom
[m
].height
-
297 h
- 4 - DECOR_BORDER
;
299 gdk_window_move_resize(gdk_window
, x
, y
, w
, h
);
302 gdk_window_resize(gdk_window
, w
, h
);
304 /* If the resize was triggered by a key press, keep
305 * the pointer inside the window so that it doesn't
306 * lose focus when using pointer-follows-mouse.
308 event
= gtk_get_current_event();
309 if (event
&& event
->type
== GDK_KEY_PRESS
)
314 GdkWindow
*win
= filer_window
->window
->window
;
316 gdk_window_get_pointer(filer_window
->window
->window
,
319 nx
= CLAMP(x
, 4, w
- 4);
320 ny
= CLAMP(y
, 4, h
- 4);
322 if (nx
!= x
|| ny
!= y
)
324 XWarpPointer(gdk_x11_drawable_get_xdisplay(win
),
326 gdk_x11_drawable_get_xid(win
),
332 gdk_event_free(event
);
335 gtk_window_set_default_size(GTK_WINDOW(window
), w
, h
);
338 /* Called on a timeout while scanning or when scanning ends
339 * (whichever happens first).
341 static gint
open_filer_window(FilerWindow
*filer_window
)
343 Settings
*dir_settings
;
344 gboolean force_resize
;
346 dir_settings
= (Settings
*) g_hash_table_lookup(settings_table
,
347 filer_window
->sym_path
);
349 force_resize
= !(o_filer_auto_resize
.int_value
== RESIZE_NEVER
&&
350 dir_settings
&& dir_settings
->flags
& SET_POSITION
);
352 view_style_changed(filer_window
->view
, 0);
354 if (filer_window
->open_timeout
)
356 g_source_remove(filer_window
->open_timeout
);
357 filer_window
->open_timeout
= 0;
360 if (!GTK_WIDGET_VISIBLE(filer_window
->window
))
362 display_set_actual_size(filer_window
, force_resize
);
363 gtk_widget_show(filer_window
->window
);
369 /* Look through all items we want to display, and queue a recheck on any
372 static void queue_interesting(FilerWindow
*filer_window
)
377 view_get_iter(filer_window
->view
, &iter
, 0);
378 while ((item
= iter
.next(&iter
)))
380 if (item
->flags
& ITEM_FLAG_NEED_RESCAN_QUEUE
)
381 dir_queue_recheck(filer_window
->directory
, item
);
385 static void update_display(Directory
*dir
,
388 FilerWindow
*filer_window
)
390 ViewIface
*view
= (ViewIface
*) filer_window
->view
;
395 view_add_items(view
, items
);
396 /* Open and resize if currently hidden */
397 open_filer_window(filer_window
);
400 view_delete_if(view
, if_deleted
, items
);
401 toolbar_update_info(filer_window
);
404 set_scanning_display(filer_window
, TRUE
);
405 toolbar_update_info(filer_window
);
408 if (filer_window
->window
->window
)
409 gdk_window_set_cursor(
410 filer_window
->window
->window
,
412 set_scanning_display(filer_window
, FALSE
);
413 toolbar_update_info(filer_window
);
414 open_filer_window(filer_window
);
416 if (filer_window
->had_cursor
&&
417 !view_cursor_visible(view
))
420 view_get_iter(view
, &start
, 0);
421 if (start
.next(&start
))
422 view_cursor_to_iter(view
, &start
);
423 view_show_cursor(view
);
424 filer_window
->had_cursor
= FALSE
;
426 if (filer_window
->auto_select
)
427 display_set_autoselect(filer_window
,
428 filer_window
->auto_select
);
429 null_g_free(&filer_window
->auto_select
);
431 filer_create_thumbs(filer_window
);
433 if (filer_window
->thumb_queue
)
434 start_thumb_scanning(filer_window
);
437 view_update_items(view
, items
);
439 case DIR_ERROR_CHANGED
:
440 filer_set_title(filer_window
);
442 case DIR_QUEUE_INTERESTING
:
443 queue_interesting(filer_window
);
448 static void attach(FilerWindow
*filer_window
)
450 gdk_window_set_cursor(filer_window
->window
->window
, busy_cursor
);
451 view_clear(filer_window
->view
);
452 filer_window
->scanning
= TRUE
;
453 dir_attach(filer_window
->directory
, (DirCallback
) update_display
,
455 filer_set_title(filer_window
);
456 bookmarks_add_history(filer_window
->sym_path
);
458 if (filer_window
->directory
->error
)
460 if (spring_in_progress
)
461 g_printerr(_("Error scanning '%s':\n%s\n"),
462 filer_window
->sym_path
,
463 filer_window
->directory
->error
);
465 delayed_error(_("Error scanning '%s':\n%s"),
466 filer_window
->sym_path
,
467 filer_window
->directory
->error
);
471 static void detach(FilerWindow
*filer_window
)
473 g_return_if_fail(filer_window
->directory
!= NULL
);
475 dir_detach(filer_window
->directory
,
476 (DirCallback
) update_display
, filer_window
);
477 g_object_unref(filer_window
->directory
);
478 filer_window
->directory
= NULL
;
481 /* If 'start' was mounted by ROX-Filer, return it. Otherwise, try the
482 * parents up the tree.
483 * NULL if we're not in a user mount point.
484 * g_free() the result.
486 static char *get_ancestor_user_mount_point(const char *start
)
490 path
= strdup(start
);
496 if (mount_is_user_mounted(path
))
505 slash
= strrchr(path
+ 1, '/');
512 static void unmount_dialog_response(GtkWidget
*dialog
,
513 int response
, char *mount
)
516 UnmountPrompt prompt_val
= 0;
520 case GTK_RESPONSE_OK
:
521 list
= g_list_prepend(NULL
, mount
);
522 action_mount(list
, FALSE
, FALSE
, TRUE
);
523 prompt_val
= UNMOUNT_PROMPT_UNMOUNT
;
526 case ROX_RESPONSE_EJECT
:
527 list
= g_list_prepend(NULL
, mount
);
529 prompt_val
= UNMOUNT_PROMPT_EJECT
;
533 prompt_val
= UNMOUNT_PROMPT_NO_CHANGE
;
539 if (gtk_toggle_button_get_active(g_object_get_data(G_OBJECT(dialog
),
542 filer_set_unmount_action(mount
, prompt_val
);
547 gtk_widget_destroy(dialog
);
552 /* 'filer_window' shows a directory under 'mount'. If no other window also
553 * shows a directory under it, display a non-modal dialog offering to
554 * unmount the directory.
555 * 'mount' is freed by this function, either directly, or after the dialog
558 static void may_offer_unmount(FilerWindow
*filer_window
, char *mount
)
560 GtkWidget
*dialog
, *button
, *unmount_mem_btn
;
566 for (next
= all_filer_windows
; next
; next
= next
->next
)
568 FilerWindow
*other
= (FilerWindow
*) next
->data
;
570 if (other
== filer_window
)
573 if (strncmp(filer_window
->real_path
, other
->real_path
,
578 filer_window
->real_path
[len
] != '/' ||
579 filer_window
->real_path
[len
] != '\0');
581 if (other
->real_path
[len
] != '/' &&
582 other
->real_path
[len
] != '\0')
585 /* Found another window. Don't offer to unmount. */
590 if (unmount_prompt_actions
)
593 UnmountPrompt unmount_val
= filer_get_unmount_action(mount
);
597 case UNMOUNT_PROMPT_UNMOUNT
:
598 list
= g_list_prepend(NULL
, mount
);
599 action_mount(list
, FALSE
, FALSE
, TRUE
);
602 case UNMOUNT_PROMPT_EJECT
:
603 list
= g_list_prepend(NULL
, mount
);
619 dialog
= gtk_message_dialog_new(NULL
, 0, GTK_MESSAGE_QUESTION
,
621 _("Do you want to unmount this device?\n\n"
622 "Unmounting a device makes it safe to remove "
625 unmount_mem_btn
= gtk_check_button_new_with_label(
626 _("Perform the same action in future for this mount point"));
627 gtk_widget_show(unmount_mem_btn
);
628 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog
)->vbox
), unmount_mem_btn
,
630 g_object_set_data(G_OBJECT(dialog
), "unmount_mem_btn",
633 button
= button_new_mixed(ROX_STOCK_MOUNTED
, _("No change"));
634 GTK_WIDGET_SET_FLAGS(button
, GTK_CAN_DEFAULT
);
635 gtk_dialog_add_action_widget(GTK_DIALOG(dialog
), button
,
636 GTK_RESPONSE_CANCEL
);
637 gtk_widget_show(button
);
639 button
= button_new_mixed(ROX_STOCK_MOUNT
, _("Unmount"));
640 GTK_WIDGET_SET_FLAGS(button
, GTK_CAN_DEFAULT
);
641 gtk_dialog_add_action_widget(GTK_DIALOG(dialog
), button
,
643 gtk_widget_show(button
);
645 /* We need a better icon, but I can't draw */
646 button
= button_new_mixed(GTK_STOCK_UNDO
, _("Eject"));
647 GTK_WIDGET_SET_FLAGS(button
, GTK_CAN_DEFAULT
);
648 gtk_dialog_add_action_widget(GTK_DIALOG(dialog
), button
,
650 gtk_widget_show(button
);
652 g_signal_connect(G_OBJECT(dialog
), "response",
653 G_CALLBACK(unmount_dialog_response
), mount
);
655 gtk_dialog_set_default_response(GTK_DIALOG(dialog
),
659 gtk_widget_show(dialog
);
662 /* Returns TRUE to prevent closing the window. May offer to unmount a
665 gboolean
filer_window_delete(GtkWidget
*window
,
666 GdkEvent
*unused
, /* (may be NULL) */
667 FilerWindow
*filer_window
)
671 mount
= get_ancestor_user_mount_point(filer_window
->real_path
);
674 may_offer_unmount(filer_window
, mount
);
679 static void filer_window_destroyed(GtkWidget
*widget
, FilerWindow
*filer_window
)
681 all_filer_windows
= g_list_remove(all_filer_windows
, filer_window
);
683 g_object_set_data(G_OBJECT(widget
), "filer_window", NULL
);
685 if (window_with_primary
== filer_window
)
686 window_with_primary
= NULL
;
688 if (window_with_focus
== filer_window
)
691 window_with_focus
= NULL
;
694 if (filer_window
->directory
)
695 detach(filer_window
);
697 if (filer_window
->open_timeout
)
699 g_source_remove(filer_window
->open_timeout
);
700 filer_window
->open_timeout
= 0;
703 if (filer_window
->auto_scroll
!= -1)
705 g_source_remove(filer_window
->auto_scroll
);
706 filer_window
->auto_scroll
= -1;
709 if (filer_window
->thumb_queue
)
710 destroy_glist(&filer_window
->thumb_queue
);
714 filer_set_id(filer_window
, NULL
);
716 if(filer_window
->filter_string
)
717 g_free(filer_window
->filter_string
);
718 if(filer_window
->regexp
)
719 g_free(filer_window
->regexp
);
721 g_free(filer_window
->auto_select
);
722 g_free(filer_window
->real_path
);
723 g_free(filer_window
->sym_path
);
724 g_free(filer_window
);
729 /* Returns TRUE iff the directory still exists. */
730 static gboolean
may_rescan(FilerWindow
*filer_window
, gboolean warning
)
734 g_return_val_if_fail(filer_window
!= NULL
, FALSE
);
736 /* We do a fresh lookup (rather than update) because the inode may
739 dir
= g_fscache_lookup(dir_cache
, filer_window
->real_path
);
743 info_message(_("Directory missing/deleted"));
744 gtk_widget_destroy(filer_window
->window
);
747 if (dir
== filer_window
->directory
)
751 detach(filer_window
);
752 filer_window
->directory
= dir
;
753 attach(filer_window
);
759 /* No items are now selected. This might be because another app claimed
760 * the selection or because the user unselected all the items.
762 void filer_lost_selection(FilerWindow
*filer_window
, guint time
)
764 if (window_with_primary
== filer_window
)
766 window_with_primary
= NULL
;
767 gtk_selection_owner_set(NULL
, GDK_SELECTION_PRIMARY
, time
);
771 /* Another app has claimed the primary selection */
772 static void filer_lost_primary(GtkWidget
*window
,
773 GdkEventSelection
*event
,
776 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
778 if (window_with_primary
&& window_with_primary
== filer_window
)
780 window_with_primary
= NULL
;
781 set_selection_state(filer_window
, FALSE
);
785 /* Someone wants us to send them the selection */
786 static void selection_get(GtkWidget
*widget
,
787 GtkSelectionData
*selection_data
,
792 GString
*reply
, *header
;
793 FilerWindow
*filer_window
= (FilerWindow
*) data
;
797 reply
= g_string_new(NULL
);
798 header
= g_string_new(NULL
);
803 g_string_printf(header
, " %s",
804 make_path(filer_window
->sym_path
, ""));
806 case TARGET_URI_LIST
:
807 g_string_printf(header
, " file://%s%s",
808 our_host_name_for_dnd(),
809 make_path(filer_window
->sym_path
, ""));
813 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
815 while ((item
= iter
.next(&iter
)))
817 g_string_append(reply
, header
->str
);
818 g_string_append(reply
, item
->leafname
);
822 gtk_selection_data_set_text(selection_data
,
823 reply
->str
+ 1, reply
->len
- 1);
826 g_warning("Attempt to paste empty selection!");
827 gtk_selection_data_set_text(selection_data
, "", 0);
830 g_string_free(reply
, TRUE
);
831 g_string_free(header
, TRUE
);
834 /* Selection has been changed -- try to grab the primary selection
835 * if we don't have it. Also called when clicking on an insensitive selection
837 * Also updates toolbar info.
839 void filer_selection_changed(FilerWindow
*filer_window
, gint time
)
841 g_return_if_fail(filer_window
!= NULL
);
843 toolbar_update_info(filer_window
);
845 if (window_with_primary
== filer_window
)
846 return; /* Already got primary */
848 if (!view_count_selected(filer_window
->view
))
849 return; /* Nothing selected */
851 if (filer_window
->temp_item_selected
== FALSE
&&
852 gtk_selection_owner_set(GTK_WIDGET(filer_window
->window
),
853 GDK_SELECTION_PRIMARY
,
856 window_with_primary
= filer_window
;
857 set_selection_state(filer_window
, TRUE
);
860 set_selection_state(filer_window
, FALSE
);
863 /* Open the item (or add it to the shell command minibuffer) */
864 void filer_openitem(FilerWindow
*filer_window
, ViewIter
*iter
, OpenFlags flags
)
866 gboolean shift
= (flags
& OPEN_SHIFT
) != 0;
867 gboolean close_mini
= flags
& OPEN_FROM_MINI
;
868 gboolean close_window
= (flags
& OPEN_CLOSE_WINDOW
) != 0;
870 const guchar
*full_path
;
871 gboolean wink
= TRUE
;
874 g_return_if_fail(filer_window
!= NULL
&& iter
!= NULL
);
876 item
= iter
->peek(iter
);
878 g_return_if_fail(item
!= NULL
);
880 if (filer_window
->mini_type
== MINI_SHELL
)
882 minibuffer_add(filer_window
, item
->leafname
);
886 if (item
->base_type
== TYPE_UNKNOWN
)
887 dir_update_item(filer_window
->directory
, item
->leafname
);
889 if (item
->base_type
== TYPE_DIRECTORY
)
891 /* Never close a filer window when opening a directory
892 * (click on a dir or click on an app with shift).
894 if (shift
|| !(item
->flags
& ITEM_FLAG_APPDIR
))
895 close_window
= FALSE
;
898 full_path
= make_path(filer_window
->sym_path
, item
->leafname
);
899 if (shift
&& (item
->flags
& ITEM_FLAG_SYMLINK
))
902 old_dir
= filer_window
->directory
;
903 if (run_diritem(full_path
, item
,
904 flags
& OPEN_SAME_WINDOW
? filer_window
: NULL
,
908 if (old_dir
!= filer_window
->directory
)
912 gtk_widget_destroy(filer_window
->window
);
916 view_wink_item(filer_window
->view
, iter
);
918 minibuffer_hide(filer_window
);
923 static gint
pointer_in(GtkWidget
*widget
,
924 GdkEventCrossing
*event
,
925 FilerWindow
*filer_window
)
927 may_rescan(filer_window
, TRUE
);
931 static gint
pointer_out(GtkWidget
*widget
,
932 GdkEventCrossing
*event
,
933 FilerWindow
*filer_window
)
939 /* Move the cursor to the next selected item in direction 'dir'
942 void filer_next_selected(FilerWindow
*filer_window
, int dir
)
944 ViewIter iter
, cursor
;
945 gboolean have_cursor
;
946 ViewIface
*view
= filer_window
->view
;
948 g_return_if_fail(dir
== 1 || dir
== -1);
950 view_get_cursor(view
, &cursor
);
951 have_cursor
= cursor
.peek(&cursor
) != NULL
;
953 view_get_iter(view
, &iter
,
955 (have_cursor
? VIEW_ITER_FROM_CURSOR
: 0) |
956 (dir
< 0 ? VIEW_ITER_BACKWARDS
: 0));
958 if (have_cursor
&& view_get_selected(view
, &cursor
))
959 iter
.next(&iter
); /* Skip the cursor itself */
961 if (iter
.next(&iter
))
962 view_cursor_to_iter(view
, &iter
);
969 static void return_pressed(FilerWindow
*filer_window
, GdkEventKey
*event
)
971 TargetFunc cb
= filer_window
->target_cb
;
972 gpointer data
= filer_window
->target_data
;
976 filer_target_mode(filer_window
, NULL
, NULL
, NULL
);
978 view_get_cursor(filer_window
->view
, &iter
);
979 if (!iter
.peek(&iter
))
984 cb(filer_window
, &iter
, data
);
988 if (event
->state
& GDK_SHIFT_MASK
)
990 if (event
->state
& GDK_MOD1_MASK
)
991 flags
|= OPEN_CLOSE_WINDOW
;
993 flags
|= OPEN_SAME_WINDOW
;
995 filer_openitem(filer_window
, &iter
, flags
);
998 /* Makes sure that 'groups' is up-to-date, reloading from file if it has
999 * changed. If no groups were loaded and there is no file then initialised
1000 * groups to an empty document.
1001 * Return the node for the 'name' group.
1003 static xmlNode
*group_find(char *name
)
1008 /* Update the groups, if possible */
1009 path
= choices_find_xdg_path_load("Groups.xml", PROJECT
, SITE
);
1012 XMLwrapper
*wrapper
;
1013 wrapper
= xml_cache_load(path
);
1017 g_object_unref(groups
);
1026 groups
= xml_new(NULL
);
1027 groups
->doc
= xmlNewDoc("1.0");
1029 xmlDocSetRootElement(groups
->doc
,
1030 xmlNewDocNode(groups
->doc
, NULL
, "groups", NULL
));
1034 node
= xmlDocGetRootElement(groups
->doc
);
1036 for (node
= node
->xmlChildrenNode
; node
; node
= node
->next
)
1040 gid
= xmlGetProp(node
, "name");
1045 if (strcmp(name
, gid
) != 0)
1056 static void group_save(FilerWindow
*filer_window
, char *name
)
1063 group
= group_find(name
);
1066 xmlUnlinkNode(group
);
1069 group
= xmlNewChild(xmlDocGetRootElement(groups
->doc
),
1070 NULL
, "group", NULL
);
1071 xmlSetProp(group
, "name", name
);
1073 xmlNewTextChild(group
, NULL
, "directory", filer_window
->sym_path
);
1075 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
1077 while ((item
= iter
.next(&iter
)))
1078 xmlNewTextChild(group
, NULL
, "item", item
->leafname
);
1080 save_path
= choices_find_xdg_path_save("Groups.xml", PROJECT
, SITE
,
1084 save_xml_file(groups
->doc
, save_path
);
1089 static gboolean
group_restore_cb(ViewIter
*iter
, gpointer data
)
1091 GHashTable
*in_group
= (GHashTable
*) data
;
1093 return g_hash_table_lookup(in_group
,
1094 iter
->peek(iter
)->leafname
) != NULL
;
1097 static void group_restore(FilerWindow
*filer_window
, char *name
)
1099 GHashTable
*in_group
;
1101 xmlNode
*group
, *node
;
1103 group
= group_find(name
);
1107 report_error(_("Group %s is not set. Select some files "
1108 "and press Ctrl+%s to set the group. Press %s "
1109 "on its own to reselect the files later.\n"
1110 "Make sure NumLock is on if you use the keypad."),
1115 node
= get_subnode(group
, NULL
, "directory");
1116 g_return_if_fail(node
!= NULL
);
1117 path
= xmlNodeListGetString(groups
->doc
, node
->xmlChildrenNode
, 1);
1118 g_return_if_fail(path
!= NULL
);
1120 if (strcmp(path
, filer_window
->sym_path
) != 0)
1121 filer_change_to(filer_window
, path
, NULL
);
1124 in_group
= g_hash_table_new(g_str_hash
, g_str_equal
);
1125 for (node
= group
->xmlChildrenNode
; node
; node
= node
->next
)
1128 if (node
->type
!= XML_ELEMENT_NODE
)
1130 if (strcmp(node
->name
, "item") != 0)
1133 leaf
= xmlNodeListGetString(groups
->doc
,
1134 node
->xmlChildrenNode
, 1);
1136 g_warning("Missing leafname!\n");
1138 g_hash_table_insert(in_group
, leaf
, filer_window
);
1141 view_select_if(filer_window
->view
, &group_restore_cb
, in_group
);
1143 g_hash_table_foreach(in_group
, (GHFunc
) g_free
, NULL
);
1144 g_hash_table_destroy(in_group
);
1147 static gboolean
popup_menu(GtkWidget
*widget
, FilerWindow
*filer_window
)
1152 view_get_cursor(filer_window
->view
, &iter
);
1154 event
= gtk_get_current_event();
1155 show_filer_menu(filer_window
, event
, &iter
);
1157 gdk_event_free(event
);
1162 void filer_window_toggle_cursor_item_selected(FilerWindow
*filer_window
)
1164 ViewIface
*view
= filer_window
->view
;
1167 view_get_iter(view
, &iter
, VIEW_ITER_FROM_CURSOR
);
1168 if (!iter
.next(&iter
))
1169 return; /* No cursor */
1171 if (view_get_selected(view
, &iter
))
1172 view_set_selected(view
, &iter
, FALSE
);
1174 view_set_selected(view
, &iter
, TRUE
);
1176 if (iter
.next(&iter
))
1177 view_cursor_to_iter(view
, &iter
);
1180 gint
filer_key_press_event(GtkWidget
*widget
,
1182 FilerWindow
*filer_window
)
1184 ViewIface
*view
= filer_window
->view
;
1186 GtkWidget
*focus
= GTK_WINDOW(widget
)->focus_widget
;
1187 guint key
= event
->keyval
;
1188 char group
[2] = "1";
1190 window_with_focus
= filer_window
;
1192 /* Delay setting up the keys until now to speed loading... */
1193 if (ensure_filer_menu())
1195 /* Gtk updates in an idle-handler, so force a recheck now */
1196 g_signal_emit_by_name(widget
, "keys_changed");
1199 if (focus
&& focus
== filer_window
->minibuffer
)
1200 if (gtk_widget_event(focus
, (GdkEvent
*) event
))
1201 return TRUE
; /* Handled */
1204 gtk_widget_grab_focus(GTK_WIDGET(view
));
1206 view_get_cursor(view
, &cursor
);
1207 if (!cursor
.peek(&cursor
) && (key
== GDK_Up
|| key
== GDK_Down
))
1210 view_get_iter(view
, &iter
, 0);
1211 if (iter
.next(&iter
))
1212 view_cursor_to_iter(view
, &iter
);
1213 gtk_widget_grab_focus(GTK_WIDGET(view
)); /* Needed? */
1220 filer_target_mode(filer_window
, NULL
, NULL
, NULL
);
1221 view_cursor_to_iter(filer_window
->view
, NULL
);
1222 view_clear_selection(filer_window
->view
);
1225 return_pressed(filer_window
, event
);
1227 case GDK_ISO_Left_Tab
:
1228 filer_next_selected(filer_window
, -1);
1231 filer_next_selected(filer_window
, 1);
1234 change_to_parent(filer_window
);
1242 view_get_cursor(filer_window
->view
, &iter
);
1243 show_filer_menu(filer_window
,
1244 (GdkEvent
*) event
, &iter
);
1248 filer_window_toggle_cursor_item_selected(filer_window
);
1251 if (key
>= GDK_0
&& key
<= GDK_9
)
1252 group
[0] = key
- GDK_0
+ '0';
1253 else if (key
>= GDK_KP_0
&& key
<= GDK_KP_9
)
1254 group
[0] = key
- GDK_KP_0
+ '0';
1257 if (focus
&& focus
!= widget
&&
1258 gtk_widget_get_toplevel(focus
) == widget
)
1259 if (gtk_widget_event(focus
,
1260 (GdkEvent
*) event
))
1261 return TRUE
; /* Handled */
1265 if (event
->state
& GDK_CONTROL_MASK
)
1266 group_save(filer_window
, group
);
1268 group_restore(filer_window
, group
);
1274 void filer_open_parent(FilerWindow
*filer_window
)
1277 const char *current
= filer_window
->sym_path
;
1279 if (current
[0] == '/' && current
[1] == '\0')
1280 return; /* Already in the root */
1282 dir
= g_path_get_dirname(current
);
1283 filer_opendir(dir
, filer_window
, NULL
);
1287 void change_to_parent(FilerWindow
*filer_window
)
1290 const char *current
= filer_window
->sym_path
;
1292 if (current
[0] == '/' && current
[1] == '\0')
1293 return; /* Already in the root */
1295 if (mount_is_user_mounted(filer_window
->real_path
))
1296 may_offer_unmount(filer_window
,
1297 g_strdup(filer_window
->real_path
));
1299 dir
= g_path_get_dirname(current
);
1300 filer_change_to(filer_window
, dir
, g_basename(current
));
1304 /* Removes trailing /s from path (modified in place) */
1305 static void tidy_sympath(gchar
*path
)
1309 g_return_if_fail(path
!= NULL
);
1312 while (l
> 1 && path
[l
- 1] == '/')
1319 /* Make filer_window display path. When finished, highlight item 'from', or
1320 * the first item if from is NULL. If there is currently no cursor then
1321 * simply wink 'from' (if not NULL).
1322 * If the cause was a key event and we resize, warp the pointer.
1324 void filer_change_to(FilerWindow
*filer_window
,
1325 const char *path
, const char *from
)
1328 char *sym_path
, *real_path
;
1331 g_return_if_fail(filer_window
!= NULL
);
1333 filer_cancel_thumbnails(filer_window
);
1337 sym_path
= g_strdup(path
);
1338 real_path
= pathdup(path
);
1339 new_dir
= g_fscache_lookup(dir_cache
, real_path
);
1343 delayed_error(_("Directory '%s' is not accessible"),
1350 if (o_unique_filer_windows
.int_value
&& !spring_in_progress
)
1354 fw
= find_filer_window(sym_path
, filer_window
);
1356 gtk_widget_destroy(fw
->window
);
1359 from_dup
= from
&& *from
? g_strdup(from
) : NULL
;
1361 detach(filer_window
);
1362 g_free(filer_window
->real_path
);
1363 g_free(filer_window
->sym_path
);
1364 filer_window
->real_path
= real_path
;
1365 filer_window
->sym_path
= sym_path
;
1366 tidy_sympath(filer_window
->sym_path
);
1368 filer_window
->directory
= new_dir
;
1370 g_free(filer_window
->auto_select
);
1371 filer_window
->auto_select
= from_dup
;
1373 filer_window
->had_cursor
= filer_window
->had_cursor
||
1374 view_cursor_visible(filer_window
->view
);
1376 filer_set_title(filer_window
);
1377 if (filer_window
->window
->window
)
1378 gdk_window_set_role(filer_window
->window
->window
,
1379 filer_window
->sym_path
);
1380 view_cursor_to_iter(filer_window
->view
, NULL
);
1382 attach(filer_window
);
1384 check_settings(filer_window
);
1386 display_set_actual_size(filer_window
, FALSE
);
1388 if (o_filer_auto_resize
.int_value
== RESIZE_ALWAYS
)
1389 view_autosize(filer_window
->view
);
1391 if (filer_window
->mini_type
== MINI_PATH
)
1392 g_idle_add((GSourceFunc
) minibuffer_show_cb
, filer_window
);
1395 /* Returns a list containing the full (sym) pathname of every selected item.
1396 * You must g_free() each item in the list.
1398 GList
*filer_selected_items(FilerWindow
*filer_window
)
1400 GList
*retval
= NULL
;
1401 guchar
*dir
= filer_window
->sym_path
;
1405 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
1406 while ((item
= iter
.next(&iter
)))
1408 retval
= g_list_prepend(retval
,
1409 g_strdup(make_path(dir
, item
->leafname
)));
1412 return g_list_reverse(retval
);
1415 /* Return the single selected item. Error if nothing is selected. */
1416 DirItem
*filer_selected_item(FilerWindow
*filer_window
)
1421 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
1423 item
= iter
.next(&iter
);
1424 g_return_val_if_fail(item
!= NULL
, NULL
);
1425 g_return_val_if_fail(iter
.next(&iter
) == NULL
, NULL
);
1430 /* Creates and shows a new filer window.
1431 * If src_win != NULL then display options can be taken from that source window.
1432 * Returns the new filer window, or NULL on error.
1433 * Note: if unique windows is in use, may return an existing window.
1435 FilerWindow
*filer_opendir(const char *path
, FilerWindow
*src_win
,
1436 const gchar
*wm_class
)
1438 FilerWindow
*filer_window
;
1440 DisplayStyle dstyle
;
1443 GtkSortType s_order
;
1444 Settings
*dir_settings
= NULL
;
1445 gboolean force_resize
= TRUE
;
1447 /* Get the real pathname of the directory and copy it */
1448 real_path
= pathdup(path
);
1450 if (o_unique_filer_windows
.int_value
&& !spring_in_progress
)
1452 FilerWindow
*same_dir_window
;
1454 same_dir_window
= find_filer_window(path
, NULL
);
1456 if (same_dir_window
)
1458 gtk_window_present(GTK_WINDOW(same_dir_window
->window
));
1459 return same_dir_window
;
1463 filer_window
= g_new(FilerWindow
, 1);
1464 filer_window
->message
= NULL
;
1465 filer_window
->minibuffer
= NULL
;
1466 filer_window
->minibuffer_label
= NULL
;
1467 filer_window
->minibuffer_area
= NULL
;
1468 filer_window
->temp_show_hidden
= FALSE
;
1469 filer_window
->sym_path
= g_strdup(path
);
1470 filer_window
->real_path
= real_path
;
1471 filer_window
->scanning
= FALSE
;
1472 filer_window
->had_cursor
= FALSE
;
1473 filer_window
->auto_select
= NULL
;
1474 filer_window
->toolbar_text
= NULL
;
1475 filer_window
->target_cb
= NULL
;
1476 filer_window
->mini_type
= MINI_NONE
;
1477 filer_window
->selection_state
= GTK_STATE_INSENSITIVE
;
1478 filer_window
->toolbar
= NULL
;
1479 filer_window
->toplevel_vbox
= NULL
;
1480 filer_window
->view_hbox
= NULL
;
1481 filer_window
->view
= NULL
;
1482 filer_window
->scrollbar
= NULL
;
1483 filer_window
->auto_scroll
= -1;
1484 filer_window
->window_id
= NULL
;
1486 tidy_sympath(filer_window
->sym_path
);
1488 /* Finds the entry for this directory in the dir cache, creating
1489 * a new one if needed. This does not cause a scan to start,
1490 * so if a new entry is created then it will be empty.
1492 filer_window
->directory
= g_fscache_lookup(dir_cache
, real_path
);
1493 if (!filer_window
->directory
)
1495 delayed_error(_("Directory '%s' not found."), path
);
1496 g_free(filer_window
->real_path
);
1497 g_free(filer_window
->sym_path
);
1498 g_free(filer_window
);
1502 filer_window
->temp_item_selected
= FALSE
;
1503 filer_window
->flags
= (FilerFlags
) 0;
1504 filer_window
->details_type
= DETAILS_TIMES
;
1505 filer_window
->display_style
= UNKNOWN_STYLE
;
1506 filer_window
->display_style_wanted
= UNKNOWN_STYLE
;
1507 filer_window
->thumb_queue
= NULL
;
1508 filer_window
->max_thumbs
= 0;
1509 filer_window
->sort_type
= -1;
1511 filer_window
->filter
= FILER_SHOW_ALL
;
1512 filer_window
->filter_string
= NULL
;
1513 filer_window
->regexp
= NULL
;
1514 filer_window
->filter_directories
= FALSE
;
1516 if (src_win
&& o_display_inherit_options
.int_value
)
1518 s_type
= src_win
->sort_type
;
1519 s_order
= src_win
->sort_order
;
1520 dstyle
= src_win
->display_style_wanted
;
1521 dtype
= src_win
->details_type
;
1522 filer_window
->show_hidden
= src_win
->show_hidden
;
1523 filer_window
->show_thumbs
= src_win
->show_thumbs
;
1524 filer_window
->view_type
= src_win
->view_type
;
1526 filer_window
->filter_directories
= src_win
->filter_directories
;
1527 filer_set_filter(filer_window
, src_win
->filter
,
1528 src_win
->filter_string
);
1532 s_type
= o_display_sort_by
.int_value
;
1533 s_order
= GTK_SORT_ASCENDING
;
1534 dstyle
= o_display_size
.int_value
;
1535 dtype
= o_display_details
.int_value
;
1536 filer_window
->show_hidden
= o_display_show_hidden
.int_value
;
1537 filer_window
->show_thumbs
= o_display_show_thumbs
.int_value
;
1538 filer_window
->view_type
= o_filer_view_type
.int_value
;
1541 dir_settings
= (Settings
*) g_hash_table_lookup(settings_table
,
1542 filer_window
->sym_path
);
1545 /* Override the current defaults with the per-directory
1548 if (dir_settings
->flags
& SET_HIDDEN
)
1549 filer_window
->show_hidden
= dir_settings
->show_hidden
;
1551 if (dir_settings
->flags
& SET_STYLE
)
1552 dstyle
= dir_settings
->display_style
;
1554 if (dir_settings
->flags
& SET_DETAILS
)
1556 dtype
= dir_settings
->details_type
;
1557 filer_window
->view_type
= dir_settings
->view_type
;
1560 if (dir_settings
->flags
& SET_SORT
)
1562 s_type
= dir_settings
->sort_type
;
1563 s_order
= dir_settings
->sort_order
;
1566 if (dir_settings
->flags
& SET_THUMBS
)
1567 filer_window
->show_thumbs
= dir_settings
->show_thumbs
;
1569 if (dir_settings
->flags
& SET_FILTER
)
1571 filer_set_filter(filer_window
,
1572 dir_settings
->filter_type
,
1573 dir_settings
->filter
);
1574 filer_set_filter_directories(filer_window
,
1575 dir_settings
->filter_directories
);
1579 /* Add all the user-interface elements & realise */
1580 filer_add_widgets(filer_window
, wm_class
);
1582 gtk_window_set_position(GTK_WINDOW(filer_window
->window
),
1587 if (dir_settings
->flags
& SET_POSITION
)
1589 gtk_window_move(GTK_WINDOW(filer_window
->window
),
1590 dir_settings
->x
, dir_settings
->y
);
1592 if (dir_settings
->flags
& SET_SIZE
)
1594 filer_window_set_size(filer_window
,
1595 dir_settings
->width
,
1596 dir_settings
->height
);
1597 force_resize
= o_filer_auto_resize
.int_value
!= RESIZE_NEVER
;
1601 /* Connect to all the signal handlers */
1602 filer_add_signals(filer_window
);
1604 display_set_layout(filer_window
, dstyle
, dtype
, force_resize
);
1605 display_set_sort_type(filer_window
, s_type
, s_order
);
1607 /* Open the window after a timeout, or when scanning stops.
1608 * Do this before attaching, because attach() might tell us to
1609 * stop scanning (if a scan isn't needed).
1611 filer_window
->open_timeout
= g_timeout_add(500,
1612 (GSourceFunc
) open_filer_window
,
1615 /* The view is created empty and then attach() is called, which
1616 * links the filer window to the entry in the directory cache we
1617 * looked up / created above.
1619 * The attach() function will immediately callback to the filer window
1620 * to deliver a list of all known entries in the directory (so,
1621 * the number of items will be known after attach() returns).
1623 * If the directory was not in the cache (because it hadn't been
1624 * opened it before) then the types and icons for the entries are
1625 * not know, but the list of names is.
1628 attach(filer_window
);
1630 number_of_windows
++;
1631 all_filer_windows
= g_list_prepend(all_filer_windows
, filer_window
);
1633 return filer_window
;
1636 void filer_set_view_type(FilerWindow
*filer_window
, ViewType type
)
1638 GtkWidget
*view
= NULL
;
1639 Directory
*dir
= NULL
;
1640 GHashTable
*selected
= NULL
;
1642 g_return_if_fail(filer_window
!= NULL
);
1644 motion_window
= NULL
;
1646 if (filer_window
->view
)
1648 /* Save the current selection */
1652 selected
= g_hash_table_new(g_str_hash
, g_str_equal
);
1653 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
1654 while ((item
= iter
.next(&iter
)))
1655 g_hash_table_insert(selected
, item
->leafname
, "yes");
1657 /* Destroy the old view */
1658 gtk_widget_destroy(GTK_WIDGET(filer_window
->view
));
1659 filer_window
->view
= NULL
;
1661 dir
= filer_window
->directory
;
1663 detach(filer_window
);
1668 case VIEW_TYPE_COLLECTION
:
1669 view
= view_collection_new(filer_window
);
1671 case VIEW_TYPE_DETAILS
:
1672 view
= view_details_new(filer_window
);
1676 g_return_if_fail(view
!= NULL
);
1678 filer_window
->view
= VIEW(view
);
1679 filer_window
->view_type
= type
;
1681 gtk_box_pack_start(filer_window
->view_hbox
, view
, TRUE
, TRUE
, 0);
1682 gtk_widget_show(view
);
1684 /* Drag and drop events */
1685 make_drop_target(view
, 0);
1686 g_signal_connect(view
, "drag_motion",
1687 G_CALLBACK(drag_motion
), filer_window
);
1688 g_signal_connect(view
, "drag_leave",
1689 G_CALLBACK(drag_leave
), filer_window
);
1690 g_signal_connect(view
, "drag_end",
1691 G_CALLBACK(drag_end
), filer_window
);
1692 /* Dragging from us... */
1693 g_signal_connect(view
, "drag_data_get",
1694 GTK_SIGNAL_FUNC(drag_data_get
), NULL
);
1698 /* Only when changing type. Otherwise, will attach later. */
1699 filer_window
->directory
= dir
;
1700 attach(filer_window
);
1702 if (o_filer_auto_resize
.int_value
!= RESIZE_NEVER
)
1703 view_autosize(filer_window
->view
);
1711 view_get_iter(filer_window
->view
, &iter
, 0);
1712 while ((item
= iter
.next(&iter
)))
1714 if (g_hash_table_lookup(selected
, item
->leafname
))
1715 view_set_selected(filer_window
->view
,
1718 g_hash_table_destroy(selected
);
1722 /* This adds all the widgets to a new filer window. It is in a separate
1723 * function because filer_opendir() was getting too long...
1725 static void filer_add_widgets(FilerWindow
*filer_window
, const gchar
*wm_class
)
1727 GtkWidget
*hbox
, *vbox
;
1729 /* Create the top-level window widget */
1730 filer_window
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
1731 filer_set_title(filer_window
);
1732 gtk_widget_set_name(filer_window
->window
, "rox-filer");
1735 gtk_window_set_wmclass(GTK_WINDOW(filer_window
->window
),
1738 /* This property is cleared when the window is destroyed.
1739 * You can thus ref filer_window->window and use this to see
1740 * if the window no longer exists.
1742 g_object_set_data(G_OBJECT(filer_window
->window
),
1743 "filer_window", filer_window
);
1745 /* Create this now to make the Adjustment before the View */
1746 filer_window
->scrollbar
= gtk_vscrollbar_new(NULL
);
1748 vbox
= gtk_vbox_new(FALSE
, 0);
1749 gtk_container_add(GTK_CONTAINER(filer_window
->window
), vbox
);
1751 filer_window
->toplevel_vbox
= GTK_BOX(vbox
);
1753 /* If there's a message that should be displayed in each window (eg
1754 * 'Running as root'), add it here.
1756 if (show_user_message
)
1758 filer_window
->message
= gtk_label_new(show_user_message
);
1759 gtk_box_pack_start(GTK_BOX(vbox
), filer_window
->message
,
1761 gtk_widget_show(filer_window
->message
);
1764 hbox
= gtk_hbox_new(FALSE
, 0);
1765 filer_window
->view_hbox
= GTK_BOX(hbox
);
1766 gtk_box_pack_start_defaults(GTK_BOX(vbox
), hbox
);
1767 /* Add the main View widget */
1768 filer_set_view_type(filer_window
, filer_window
->view_type
);
1769 /* Put the scrollbar next to the View */
1770 gtk_box_pack_end(GTK_BOX(hbox
),
1771 filer_window
->scrollbar
, FALSE
, TRUE
, 0);
1772 gtk_widget_show(hbox
);
1774 /* If we want a toolbar, create it now */
1775 toolbar_update_toolbar(filer_window
);
1777 /* And the minibuffer (hidden to start with) */
1778 create_minibuffer(filer_window
);
1779 gtk_box_pack_end(GTK_BOX(vbox
), filer_window
->minibuffer_area
,
1782 /* And the thumbnail progress bar (also hidden) */
1786 filer_window
->thumb_bar
= gtk_hbox_new(FALSE
, 2);
1787 gtk_box_pack_end(GTK_BOX(vbox
), filer_window
->thumb_bar
,
1790 filer_window
->thumb_progress
= gtk_progress_bar_new();
1792 gtk_box_pack_start(GTK_BOX(filer_window
->thumb_bar
),
1793 filer_window
->thumb_progress
, TRUE
, TRUE
, 0);
1795 cancel
= gtk_button_new_with_label(_("Cancel"));
1796 GTK_WIDGET_UNSET_FLAGS(cancel
, GTK_CAN_FOCUS
);
1797 gtk_box_pack_start(GTK_BOX(filer_window
->thumb_bar
),
1798 cancel
, FALSE
, TRUE
, 0);
1799 g_signal_connect_swapped(cancel
, "clicked",
1800 G_CALLBACK(filer_cancel_thumbnails
),
1804 gtk_widget_show(vbox
);
1805 gtk_widget_show(filer_window
->scrollbar
);
1807 gtk_widget_realize(filer_window
->window
);
1809 gdk_window_set_role(filer_window
->window
->window
,
1810 filer_window
->sym_path
);
1812 filer_window_set_size(filer_window
, 4, 4);
1815 static void filer_add_signals(FilerWindow
*filer_window
)
1817 GtkTargetEntry target_table
[] =
1819 {"text/uri-list", 0, TARGET_URI_LIST
},
1820 {"UTF8_STRING", 0, TARGET_STRING
},
1821 {"STRING", 0, TARGET_STRING
},
1822 {"COMPOUND_TEXT", 0, TARGET_STRING
},/* XXX: Treats as STRING */
1825 /* Events on the top-level window */
1826 gtk_widget_add_events(filer_window
->window
, GDK_ENTER_NOTIFY
);
1827 g_signal_connect(filer_window
->window
, "enter-notify-event",
1828 G_CALLBACK(pointer_in
), filer_window
);
1829 g_signal_connect(filer_window
->window
, "leave-notify-event",
1830 G_CALLBACK(pointer_out
), filer_window
);
1831 g_signal_connect(filer_window
->window
, "destroy",
1832 G_CALLBACK(filer_window_destroyed
), filer_window
);
1833 g_signal_connect(filer_window
->window
, "delete-event",
1834 G_CALLBACK(filer_window_delete
), filer_window
);
1836 g_signal_connect(filer_window
->window
, "selection_clear_event",
1837 G_CALLBACK(filer_lost_primary
), filer_window
);
1839 g_signal_connect(filer_window
->window
, "selection_get",
1840 G_CALLBACK(selection_get
), filer_window
);
1841 gtk_selection_add_targets(GTK_WIDGET(filer_window
->window
),
1842 GDK_SELECTION_PRIMARY
,
1844 sizeof(target_table
) / sizeof(*target_table
));
1846 g_signal_connect(filer_window
->window
, "popup-menu",
1847 G_CALLBACK(popup_menu
), filer_window
);
1848 g_signal_connect(filer_window
->window
, "key_press_event",
1849 G_CALLBACK(filer_key_press_event
), filer_window
);
1851 gtk_window_add_accel_group(GTK_WINDOW(filer_window
->window
),
1855 static gint
clear_scanning_display(FilerWindow
*filer_window
)
1857 if (filer_exists(filer_window
))
1858 filer_set_title(filer_window
);
1862 static void set_scanning_display(FilerWindow
*filer_window
, gboolean scanning
)
1864 if (scanning
== filer_window
->scanning
)
1866 filer_window
->scanning
= scanning
;
1869 filer_set_title(filer_window
);
1871 g_timeout_add(300, (GSourceFunc
) clear_scanning_display
,
1875 /* Note that filer_window may not exist after this call.
1876 * Returns TRUE iff the directory still exists.
1878 gboolean
filer_update_dir(FilerWindow
*filer_window
, gboolean warning
)
1880 gboolean still_exists
;
1882 still_exists
= may_rescan(filer_window
, warning
);
1885 dir_update(filer_window
->directory
, filer_window
->sym_path
);
1887 return still_exists
;
1890 void filer_update_all(void)
1892 GList
*next
= all_filer_windows
;
1896 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1898 /* Updating directory may remove it from list -- stop sending
1899 * patches to move this line!
1903 /* Don't trigger a refresh if we're already scanning.
1904 * Otherwise, two views of a single directory will trigger
1907 if (filer_window
->directory
&&
1908 !filer_window
->directory
->scanning
)
1909 filer_update_dir(filer_window
, TRUE
);
1913 /* Refresh the various caches even if we don't think we need to */
1914 void full_refresh(void)
1917 reread_mime_files(); /* Refreshes all windows */
1920 /* See whether a filer window with a given path already exists
1921 * and is different from diff.
1923 static FilerWindow
*find_filer_window(const char *sym_path
, FilerWindow
*diff
)
1927 for (next
= all_filer_windows
; next
; next
= next
->next
)
1929 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1931 if (filer_window
!= diff
&&
1932 strcmp(sym_path
, filer_window
->sym_path
) == 0)
1933 return filer_window
;
1939 /* This path has been mounted/umounted/deleted some files - update all dirs */
1940 void filer_check_mounted(const char *real_path
)
1942 GList
*next
= all_filer_windows
;
1945 gboolean resize
= o_filer_auto_resize
.int_value
== RESIZE_ALWAYS
;
1947 /* DOS disks, etc, often don't change the mtime of the root directory
1948 * on modification, so force a refresh now.
1950 g_fscache_update(dir_cache
, real_path
);
1952 len
= strlen(real_path
);
1956 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1960 if (strncmp(real_path
, filer_window
->real_path
, len
) == 0)
1962 char s
= filer_window
->real_path
[len
];
1964 if (s
== '/' || s
== '\0')
1966 if (filer_update_dir(filer_window
, FALSE
) &&
1968 view_autosize(filer_window
->view
);
1973 parent
= g_path_get_dirname(real_path
);
1974 refresh_dirs(parent
);
1977 icons_may_update(real_path
);
1980 /* Close all windows displaying 'path' or subdirectories of 'path' */
1981 void filer_close_recursive(const char *path
)
1983 GList
*next
= all_filer_windows
;
1987 real
= pathdup(path
);
1992 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1996 if (strncmp(real
, filer_window
->real_path
, len
) == 0)
1998 char s
= filer_window
->real_path
[len
];
2000 if (len
== 1 || s
== '/' || s
== '\0')
2001 gtk_widget_destroy(filer_window
->window
);
2006 /* Like minibuffer_show(), except that:
2007 * - It returns FALSE (to be used from an idle callback)
2008 * - It checks that the filer window still exists.
2010 static gboolean
minibuffer_show_cb(FilerWindow
*filer_window
)
2012 if (filer_exists(filer_window
))
2013 minibuffer_show(filer_window
, MINI_PATH
);
2017 /* TRUE iff filer_window points to an existing FilerWindow
2020 gboolean
filer_exists(FilerWindow
*filer_window
)
2024 for (next
= all_filer_windows
; next
; next
= next
->next
)
2026 FilerWindow
*fw
= (FilerWindow
*) next
->data
;
2028 if (fw
== filer_window
)
2035 FilerWindow
*filer_get_by_id(const char *id
)
2037 return g_hash_table_lookup(window_with_id
, id
);
2040 void filer_set_id(FilerWindow
*filer_window
, const char *id
)
2042 g_return_if_fail(filer_window
!= NULL
);
2044 if (filer_window
->window_id
)
2046 g_hash_table_remove(window_with_id
, filer_window
->window_id
);
2047 g_free(filer_window
->window_id
);
2048 filer_window
->window_id
= NULL
;
2053 filer_window
->window_id
= g_strdup(id
);
2054 g_hash_table_insert(window_with_id
,
2055 filer_window
->window_id
,
2060 /* Make sure the window title is up-to-date */
2061 void filer_set_title(FilerWindow
*filer_window
)
2063 gchar
*title
= NULL
;
2066 if (filer_window
->scanning
||
2067 filer_window
->filter
!= FILER_SHOW_ALL
||
2068 filer_window
->show_hidden
|| filer_window
->show_thumbs
)
2070 if (o_short_flag_names
.int_value
)
2072 const gchar
*hidden
= "";
2074 switch(filer_window
->filter
) {
2075 case FILER_SHOW_ALL
:
2076 hidden
=filer_window
->show_hidden
? _("A") : "";
2078 case FILER_SHOW_GLOB
: hidden
= _("G"); break;
2082 flags
= g_strconcat(" +",
2083 filer_window
->scanning
? _("S") : "",
2085 filer_window
->show_thumbs
? _("T") : "",
2090 gchar
*hidden
= NULL
;
2092 switch(filer_window
->filter
) {
2093 case FILER_SHOW_ALL
:
2094 hidden
= g_strdup(filer_window
->show_hidden
2097 case FILER_SHOW_GLOB
:
2098 hidden
= g_strdup_printf(_("Glob (%s), "),
2099 filer_window
->filter_string
);
2102 hidden
=g_strdup("");
2105 flags
= g_strconcat(" (",
2106 filer_window
->scanning
? _("Scanning, ") : "",
2108 filer_window
->show_thumbs
? _("Thumbs, ") : "",
2110 flags
[strlen(flags
) - 2] = ')';
2116 title
= g_strconcat("//", our_host_name(),
2117 filer_window
->sym_path
, flags
, NULL
);
2119 if (!title
&& home_dir_len
> 1 &&
2120 strncmp(filer_window
->sym_path
, home_dir
, home_dir_len
) == 0)
2122 guchar sep
= filer_window
->sym_path
[home_dir_len
];
2124 if (sep
== '\0' || sep
== '/')
2125 title
= g_strconcat("~",
2126 filer_window
->sym_path
+ home_dir_len
,
2132 title
= g_strconcat(filer_window
->sym_path
, flags
, NULL
);
2134 ensure_utf8(&title
);
2136 if (filer_window
->directory
->error
)
2139 title
= g_strconcat(old
, ": ", filer_window
->directory
->error
,
2144 gtk_window_set_title(GTK_WINDOW(filer_window
->window
), title
);
2148 if (flags
[0] != '\0')
2152 /* Reconnect to the same directory (used when the Show Hidden option is
2153 * toggled). This has the side-effect of updating the window title.
2155 void filer_detach_rescan(FilerWindow
*filer_window
)
2157 Directory
*dir
= filer_window
->directory
;
2160 detach(filer_window
);
2161 filer_window
->directory
= dir
;
2162 attach(filer_window
);
2165 /* Puts the filer window into target mode. When an item is chosen,
2166 * fn(filer_window, iter, data) is called. 'reason' will be displayed
2167 * on the toolbar while target mode is active.
2169 * Use fn == NULL to cancel target mode.
2171 void filer_target_mode(FilerWindow
*filer_window
,
2176 TargetFunc old_fn
= filer_window
->target_cb
;
2179 gdk_window_set_cursor(
2180 GTK_WIDGET(filer_window
->view
)->window
,
2181 fn
? crosshair
: NULL
);
2183 filer_window
->target_cb
= fn
;
2184 filer_window
->target_data
= data
;
2186 if (filer_window
->toolbar_text
== NULL
)
2191 GTK_LABEL(filer_window
->toolbar_text
), reason
);
2192 else if (o_toolbar_info
.int_value
)
2195 toolbar_update_info(filer_window
);
2198 gtk_label_set_text(GTK_LABEL(filer_window
->toolbar_text
), "");
2201 static void set_selection_state(FilerWindow
*filer_window
, gboolean normal
)
2203 GtkStateType old_state
= filer_window
->selection_state
;
2205 filer_window
->selection_state
= normal
2206 ? GTK_STATE_SELECTED
: GTK_STATE_INSENSITIVE
;
2208 if (old_state
!= filer_window
->selection_state
2209 && view_count_selected(filer_window
->view
))
2210 gtk_widget_queue_draw(GTK_WIDGET(filer_window
->view
));
2213 void filer_cancel_thumbnails(FilerWindow
*filer_window
)
2215 gtk_widget_hide(filer_window
->thumb_bar
);
2217 destroy_glist(&filer_window
->thumb_queue
);
2218 filer_window
->max_thumbs
= 0;
2221 /* Generate the next thumb for this window. The window object is
2222 * unref'd when there is nothing more to do.
2223 * If the window no longer has a filer window, nothing is done.
2225 static gboolean
filer_next_thumb_real(GObject
*window
)
2227 FilerWindow
*filer_window
;
2231 filer_window
= g_object_get_data(window
, "filer_window");
2235 g_object_unref(window
);
2239 if (!filer_window
->thumb_queue
)
2241 filer_cancel_thumbnails(filer_window
);
2242 g_object_unref(window
);
2246 total
= filer_window
->max_thumbs
;
2247 done
= total
- g_list_length(filer_window
->thumb_queue
);
2249 path
= (gchar
*) filer_window
->thumb_queue
->data
;
2251 pixmap_background_thumb(path
, (GFunc
) filer_next_thumb
, window
);
2253 filer_window
->thumb_queue
= g_list_remove(filer_window
->thumb_queue
,
2257 gtk_progress_bar_set_fraction(
2258 GTK_PROGRESS_BAR(filer_window
->thumb_progress
),
2259 done
/ (float) total
);
2264 /* path is the thumb just loaded, if any.
2265 * window is unref'd (eventually).
2267 static void filer_next_thumb(GObject
*window
, const gchar
*path
)
2270 dir_force_update_path(path
);
2272 g_idle_add((GSourceFunc
) filer_next_thumb_real
, window
);
2275 static void start_thumb_scanning(FilerWindow
*filer_window
)
2277 if (GTK_WIDGET_VISIBLE(filer_window
->thumb_bar
))
2278 return; /* Already scanning */
2280 gtk_widget_show_all(filer_window
->thumb_bar
);
2282 g_object_ref(G_OBJECT(filer_window
->window
));
2283 filer_next_thumb(G_OBJECT(filer_window
->window
), NULL
);
2286 /* Set this image to be loaded some time in the future */
2287 void filer_create_thumb(FilerWindow
*filer_window
, const gchar
*path
)
2289 if (g_list_find_custom(filer_window
->thumb_queue
, path
,
2290 (GCompareFunc
) strcmp
))
2293 if (!filer_window
->thumb_queue
)
2294 filer_window
->max_thumbs
=0;
2295 filer_window
->max_thumbs
++;
2297 filer_window
->thumb_queue
= g_list_append(filer_window
->thumb_queue
,
2300 if (filer_window
->scanning
)
2301 return; /* Will start when scan ends */
2303 start_thumb_scanning(filer_window
);
2306 /* If thumbnail display is on, look through all the items in this directory
2307 * and start creating or updating the thumbnails as needed.
2309 void filer_create_thumbs(FilerWindow
*filer_window
)
2314 if (!filer_window
->show_thumbs
)
2317 view_get_iter(filer_window
->view
, &iter
, 0);
2319 while ((item
= iter
.next(&iter
)))
2321 MaskedPixmap
*pixmap
;
2325 if (item
->base_type
!= TYPE_FILE
)
2328 /*if (strcmp(item->mime_type->media_type, "image") != 0)
2331 path
= make_path(filer_window
->real_path
, item
->leafname
);
2333 pixmap
= g_fscache_lookup_full(pixmap_cache
, path
,
2334 FSCACHE_LOOKUP_ONLY_NEW
, &found
);
2336 g_object_unref(pixmap
);
2338 /* If we didn't get an image, it could be because:
2340 * - We're loading the image now. found is TRUE,
2341 * and we'll update the item later.
2342 * - We tried to load the image and failed. found
2344 * - We haven't tried loading the image. found is
2345 * FALSE, and we start creating the thumb here.
2348 filer_create_thumb(filer_window
, path
);
2352 static void filer_options_changed(void)
2354 if (o_short_flag_names
.has_changed
)
2358 for (next
= all_filer_windows
; next
; next
= next
->next
)
2360 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
2362 filer_set_title(filer_window
);
2367 /* Append interesting information to this GString */
2368 void filer_add_tip_details(FilerWindow
*filer_window
,
2369 GString
*tip
, DirItem
*item
)
2371 const guchar
*fullpath
= NULL
;
2373 fullpath
= make_path(filer_window
->real_path
, item
->leafname
);
2375 if (item
->flags
& ITEM_FLAG_SYMLINK
)
2379 target
= readlink_dup(fullpath
);
2382 ensure_utf8(&target
);
2384 g_string_append(tip
, _("Symbolic link to "));
2385 g_string_append(tip
, target
);
2386 g_string_append_c(tip
, '\n');
2391 if (item
->flags
& ITEM_FLAG_APPDIR
)
2396 info
= appinfo_get(fullpath
, item
);
2397 if (info
&& ((node
= xml_get_section(info
, NULL
, "Summary"))))
2400 str
= xmlNodeListGetString(node
->doc
,
2401 node
->xmlChildrenNode
, 1);
2404 g_string_append(tip
, str
);
2405 g_string_append_c(tip
, '\n');
2410 g_object_unref(info
);
2412 else if (item
->mime_type
== application_x_desktop
)
2415 summary
= tip_from_desktop_file(fullpath
);
2418 g_string_append(tip
, summary
);
2419 g_string_append_c(tip
, '\n');
2424 if (!g_utf8_validate(item
->leafname
, -1, NULL
))
2425 g_string_append(tip
,
2426 _("This filename is not valid UTF-8. "
2427 "You should rename it.\n"));
2430 /* Return the selection as a text/uri-list.
2431 * g_free() the result.
2433 static guchar
*filer_create_uri_list(FilerWindow
*filer_window
)
2441 g_return_val_if_fail(filer_window
!= NULL
, NULL
);
2443 string
= g_string_new(NULL
);
2445 leader
= g_string_new(filer_window
->sym_path
);
2446 if (leader
->str
[leader
->len
- 1] != '/')
2447 g_string_append_c(leader
, '/');
2449 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
2450 while ((item
= iter
.next(&iter
)))
2455 path
= g_strconcat(leader
->str
, item
->leafname
, NULL
);
2456 uri
= encode_path_as_uri(path
);
2457 g_string_append(string
, (char *) uri
);
2458 g_string_append(string
, "\r\n");
2463 g_string_free(leader
, TRUE
);
2464 retval
= string
->str
;
2465 g_string_free(string
, FALSE
);
2470 void filer_perform_action(FilerWindow
*filer_window
, GdkEventButton
*event
)
2473 ViewIface
*view
= filer_window
->view
;
2474 DirItem
*item
= NULL
;
2475 gboolean press
= event
->type
== GDK_BUTTON_PRESS
;
2477 OpenFlags flags
= 0;
2479 if (event
->button
> 3)
2482 view_get_iter_at_point(view
, &iter
, event
->window
, event
->x
, event
->y
);
2483 item
= iter
.peek(&iter
);
2485 if (item
&& view_cursor_visible(view
))
2486 view_cursor_to_iter(view
, &iter
);
2488 if (item
&& event
->button
== 1 &&
2489 view_get_selected(view
, &iter
) &&
2490 filer_window
->selection_state
== GTK_STATE_INSENSITIVE
)
2492 /* Possibly a really slow DnD operation? */
2493 filer_window
->temp_item_selected
= FALSE
;
2495 filer_selection_changed(filer_window
, event
->time
);
2499 if (filer_window
->target_cb
)
2501 dnd_motion_ungrab();
2502 if (item
&& press
&& event
->button
== 1)
2503 filer_window
->target_cb(filer_window
, &iter
,
2504 filer_window
->target_data
);
2506 filer_target_mode(filer_window
, NULL
, NULL
, NULL
);
2511 if (!o_single_click
.int_value
)
2513 /* Make sure both parts of a double-click fall on
2516 static guchar
*first_click
= NULL
;
2517 static guchar
*second_click
= NULL
;
2519 if (event
->type
== GDK_BUTTON_PRESS
)
2521 g_free(first_click
);
2522 first_click
= second_click
;
2525 second_click
= g_strdup(item
->leafname
);
2527 second_click
= NULL
;
2530 if (event
->type
== GDK_2BUTTON_PRESS
)
2532 if (first_click
&& second_click
&&
2533 strcmp(first_click
, second_click
) != 0)
2535 if ((first_click
|| second_click
) &&
2536 !(first_click
&& second_click
))
2541 action
= bind_lookup_bev(
2542 item
? BIND_DIRECTORY_ICON
: BIND_DIRECTORY
,
2547 case ACT_CLEAR_SELECTION
:
2548 view_clear_selection(view
);
2550 case ACT_TOGGLE_SELECTED
:
2551 view_set_selected(view
, &iter
,
2552 !view_get_selected(view
, &iter
));
2554 case ACT_SELECT_EXCL
:
2555 view_select_only(view
, &iter
);
2558 flags
|= OPEN_SHIFT
;
2561 if (event
->button
!= 1 || event
->state
& GDK_MOD1_MASK
)
2562 flags
|= OPEN_CLOSE_WINDOW
;
2564 flags
|= OPEN_SAME_WINDOW
;
2565 if (o_new_button_1
.int_value
)
2566 flags
^= OPEN_SAME_WINDOW
;
2567 if (event
->type
== GDK_2BUTTON_PRESS
)
2568 view_set_selected(view
, &iter
, FALSE
);
2569 dnd_motion_ungrab();
2571 filer_openitem(filer_window
, &iter
, flags
);
2573 case ACT_POPUP_MENU
:
2574 dnd_motion_ungrab();
2576 show_filer_menu(filer_window
,
2577 (GdkEvent
*) event
, &iter
);
2579 case ACT_PRIME_AND_SELECT
:
2580 if (item
&& !view_get_selected(view
, &iter
))
2581 view_select_only(view
, &iter
);
2582 dnd_motion_start(MOTION_READY_FOR_DND
);
2584 case ACT_PRIME_AND_TOGGLE
:
2585 view_set_selected(view
, &iter
,
2586 !view_get_selected(view
, &iter
));
2587 dnd_motion_start(MOTION_READY_FOR_DND
);
2589 case ACT_PRIME_FOR_DND
:
2590 dnd_motion_start(MOTION_READY_FOR_DND
);
2593 if (press
&& event
->button
< 4)
2596 view_wink_item(view
, &iter
);
2597 dnd_motion_start(MOTION_NONE
);
2600 case ACT_LASSO_CLEAR
:
2601 view_clear_selection(view
);
2603 case ACT_LASSO_MODIFY
:
2604 view_start_lasso_box(view
, event
);
2607 view_autosize(filer_window
->view
);
2610 g_warning("Unsupported action : %d\n", action
);
2615 /* It's time to make the tooltip appear. If we're not over the item any
2616 * more, or the item doesn't need a tooltip, do nothing.
2618 static gboolean
tooltip_activate(GtkWidget
*window
)
2620 FilerWindow
*filer_window
;
2624 DirItem
*item
= NULL
;
2625 GString
*tip
= NULL
;
2627 g_return_val_if_fail(tip_item
!= NULL
, 0);
2629 filer_window
= g_object_get_data(G_OBJECT(window
), "filer_window");
2631 if (!motion_window
|| !filer_window
)
2632 return FALSE
; /* Window has been destroyed */
2634 view
= filer_window
->view
;
2638 gdk_window_get_pointer(motion_window
, &x
, &y
, NULL
);
2639 view_get_iter_at_point(view
, &iter
, motion_window
, x
, y
);
2641 item
= iter
.peek(&iter
);
2642 if (item
!= tip_item
)
2643 return FALSE
; /* Not still under the pointer */
2645 /* OK, the filer window still exists and the pointer is still
2646 * over the same item. Do we need to show a tip?
2649 tip
= g_string_new(NULL
);
2651 view_extend_tip(filer_window
->view
, &iter
, tip
);
2653 filer_add_tip_details(filer_window
, tip
, tip_item
);
2657 g_string_truncate(tip
, tip
->len
- 1);
2659 tooltip_show(tip
->str
);
2662 g_string_free(tip
, TRUE
);
2667 /* Motion detected on the View widget */
2668 gint
filer_motion_notify(FilerWindow
*filer_window
, GdkEventMotion
*event
)
2670 ViewIface
*view
= filer_window
->view
;
2674 view_get_iter_at_point(view
, &iter
, event
->window
, event
->x
, event
->y
);
2675 item
= iter
.peek(&iter
);
2679 if (item
!= tip_item
)
2684 motion_window
= event
->window
;
2685 tooltip_prime((GtkFunction
) tooltip_activate
,
2686 G_OBJECT(filer_window
->window
));
2695 if (motion_state
!= MOTION_READY_FOR_DND
)
2698 if (!dnd_motion_moved(event
))
2701 view_get_iter_at_point(view
, &iter
,
2703 event
->x
- (event
->x_root
- drag_start_x
),
2704 event
->y
- (event
->y_root
- drag_start_y
));
2705 item
= iter
.peek(&iter
);
2709 view_wink_item(view
, NULL
);
2711 if (!view_get_selected(view
, &iter
))
2713 /* If we drag an unselected item, select it only.
2714 * Unless we're also holding down Ctrl, in which case
2715 * it's probably unselected only because we
2716 * mis-interpreted the click as toggle-selected.
2718 if ((event
->state
& GDK_BUTTON1_MASK
) &&
2719 !(event
->state
& GDK_CONTROL_MASK
))
2721 /* Select just this one */
2722 filer_window
->temp_item_selected
= TRUE
;
2723 view_select_only(view
, &iter
);
2727 if (view_count_selected(view
) == 0)
2728 filer_window
->temp_item_selected
= TRUE
;
2729 view_set_selected(view
, &iter
, TRUE
);
2733 g_return_val_if_fail(view_count_selected(view
) > 0, TRUE
);
2735 if (view_count_selected(view
) == 1)
2737 if (item
->base_type
== TYPE_UNKNOWN
)
2738 item
= dir_update_item(filer_window
->directory
,
2743 report_error(_("Item no longer exists!"));
2747 drag_one_item(GTK_WIDGET(view
), event
,
2748 make_path(filer_window
->sym_path
, item
->leafname
),
2749 item
, di_image(item
));
2751 /* XXX: Use thumbnail */
2752 item
, view
? view
->image
: NULL
);
2759 uris
= filer_create_uri_list(filer_window
);
2760 drag_selection(GTK_WIDGET(view
), event
, uris
);
2767 static void drag_end(GtkWidget
*widget
, GdkDragContext
*context
,
2768 FilerWindow
*filer_window
)
2770 filer_set_autoscroll(filer_window
, FALSE
);
2772 if (filer_window
->temp_item_selected
)
2774 view_clear_selection(filer_window
->view
);
2775 filer_window
->temp_item_selected
= FALSE
;
2779 /* Remove highlights */
2780 static void drag_leave(GtkWidget
*widget
,
2781 GdkDragContext
*context
,
2783 FilerWindow
*filer_window
)
2788 /* Called during the drag when the mouse is in a widget registered
2789 * as a drop target. Returns TRUE if we can accept the drop.
2791 static gboolean
drag_motion(GtkWidget
*widget
,
2792 GdkDragContext
*context
,
2796 FilerWindow
*filer_window
)
2799 ViewIface
*view
= filer_window
->view
;
2801 GdkDragAction action
= context
->suggested_action
;
2802 const guchar
*new_path
= NULL
;
2803 const char *type
= NULL
;
2804 gboolean retval
= FALSE
;
2805 gboolean same_window
;
2807 if ((context
->actions
& GDK_ACTION_ASK
) && o_dnd_left_menu
.int_value
)
2810 gdk_window_get_pointer(NULL
, NULL
, NULL
, &state
);
2811 if (state
& GDK_BUTTON1_MASK
)
2812 action
= GDK_ACTION_ASK
;
2815 same_window
= gtk_drag_get_source_widget(context
) == widget
;
2817 filer_set_autoscroll(filer_window
, TRUE
);
2819 if (filer_window
->view_type
== VIEW_TYPE_DETAILS
)
2823 /* Correct for position of bin window */
2824 bin
= gtk_tree_view_get_bin_window(GTK_TREE_VIEW(view
));
2825 gdk_window_get_position(bin
, NULL
, &bin_y
);
2829 if (o_dnd_drag_to_icons
.int_value
)
2831 view_get_iter_at_point(view
, &iter
, widget
->window
, x
, y
);
2832 item
= iter
.peek(&iter
);
2837 if (item
&& same_window
&& view_get_selected(view
, &iter
))
2840 type
= dnd_motion_item(context
, &item
);
2845 /* Don't allow drops to non-writeable directories. BUT, still
2846 * allow drops on non-writeable SUBdirectories so that we can
2847 * do the spring-open thing.
2849 if (item
&& type
== drop_dest_dir
&&
2850 !(item
->flags
& ITEM_FLAG_APPDIR
))
2852 dnd_spring_load(context
, filer_window
);
2858 view_cursor_to_iter(view
, &iter
);
2861 view_cursor_to_iter(view
, NULL
);
2863 /* Disallow background drops within a single window */
2864 if (type
&& same_window
)
2871 new_path
= make_path(filer_window
->sym_path
,
2874 new_path
= filer_window
->sym_path
;
2877 /* Don't ask about dragging to an application! */
2878 if (type
== drop_dest_prog
&& action
== GDK_ACTION_ASK
)
2879 action
= GDK_ACTION_COPY
;
2881 g_dataset_set_data(context
, "drop_dest_type", (gpointer
) type
);
2884 gdk_drag_status(context
, action
, time
);
2885 g_dataset_set_data_full(context
, "drop_dest_path",
2886 g_strdup(new_path
), g_free
);
2893 static gboolean
as_timeout(FilerWindow
*filer_window
)
2897 retval
= view_auto_scroll_callback(filer_window
->view
);
2900 filer_window
->auto_scroll
= -1;
2905 /* When autoscroll is on, a timer keeps track of the pointer position.
2906 * While it's near the top or bottom of the window, the window scrolls.
2908 * If the mouse buttons are released, the pointer leaves the window, or
2909 * a drag-and-drop operation finishes, auto_scroll is turned off.
2911 void filer_set_autoscroll(FilerWindow
*filer_window
, gboolean auto_scroll
)
2913 g_return_if_fail(filer_window
!= NULL
);
2917 if (filer_window
->auto_scroll
!= -1)
2918 return; /* Already on! */
2920 filer_window
->auto_scroll
= g_timeout_add(50,
2921 (GSourceFunc
) as_timeout
,
2926 if (filer_window
->auto_scroll
== -1)
2927 return; /* Already off! */
2929 g_source_remove(filer_window
->auto_scroll
);
2930 filer_window
->auto_scroll
= -1;
2934 #define ZERO_MNT "/uri/0install"
2936 static void refresh_done(FilerWindow
*filer_window
)
2938 if (filer_exists(filer_window
))
2939 filer_update_dir(filer_window
, TRUE
);
2942 void filer_refresh(FilerWindow
*filer_window
)
2944 if (!strncmp(ZERO_MNT
"/", filer_window
->real_path
, sizeof(ZERO_MNT
)))
2946 /* Try to run 0refresh */
2948 gchar
*argv
[] = {"0refresh", NULL
, NULL
};
2949 const char *host
= filer_window
->real_path
+ sizeof(ZERO_MNT
);
2952 slash
= strchr(host
, '/');
2954 argv
[1] = g_strndup(host
, slash
- host
);
2956 argv
[1] = g_strdup(host
);
2957 pid
= rox_spawn(filer_window
->real_path
, (const char **) argv
);
2960 on_child_death(pid
, (CallbackFn
) refresh_done
,
2967 static inline gboolean
is_hidden(const char *dir
, DirItem
*item
)
2969 /* If the leaf name starts with '.' then the item is hidden */
2970 if(item
->leafname
[0]=='.')
2973 /*** Test disabled for now. The flags aren't set on the first pass...
2976 /* Most files will not have extended attributes, so this should
2978 if(!o_xattr_ignore
.int_value
&& (item
->flags
& ITEM_FLAG_HAS_XATTR
)) {
2981 gboolean hidden
=FALSE
;
2983 path
=g_build_filename(dir
, item
->leafname
, NULL
);
2984 val
=xattr_get(path
, XATTR_HIDDEN
, &len
);
2986 hidden
=atoi(val
) || (strcmp(val
, "true")==0);
2996 /* Otherwise not hidden */
3000 gboolean
filer_match_filter(FilerWindow
*filer_window
, DirItem
*item
)
3002 g_return_val_if_fail(item
!= NULL
, FALSE
);
3004 if(is_hidden(filer_window
->real_path
, item
) &&
3005 (!filer_window
->temp_show_hidden
&& !filer_window
->show_hidden
))
3008 switch(filer_window
->filter
) {
3009 case FILER_SHOW_GLOB
:
3010 return fnmatch(filer_window
->filter_string
,
3011 item
->leafname
, 0)==0 ||
3012 (item
->base_type
==TYPE_DIRECTORY
&&
3013 !filer_window
->filter_directories
);
3015 case FILER_SHOW_ALL
:
3022 /* Provided to hide the implementation */
3023 void filer_set_hidden(FilerWindow
*filer_window
, gboolean hidden
)
3025 filer_window
->show_hidden
=hidden
;
3028 /* Provided to hide the implementation */
3029 void filer_set_filter_directories(FilerWindow
*filer_window
,
3030 gboolean filter_directories
)
3032 filer_window
->filter_directories
=filter_directories
;
3035 /* Set the filter type. Returns TRUE if the type has changed
3036 * (must call filer_detach_rescan).
3038 gboolean
filer_set_filter(FilerWindow
*filer_window
, FilterType type
,
3039 const gchar
*filter_string
)
3041 /* Is this new filter the same as the old one? */
3042 if (filer_window
->filter
== type
)
3044 switch(filer_window
->filter
)
3046 case FILER_SHOW_ALL
:
3048 case FILER_SHOW_GLOB
:
3049 if (strcmp(filer_window
->filter_string
,
3050 filter_string
) == 0)
3056 /* Clean up old filter */
3057 if (filer_window
->filter_string
)
3059 g_free(filer_window
->filter_string
);
3060 filer_window
->filter_string
= NULL
;
3062 /* Also clean up compiled regexp when implemented */
3064 filer_window
->filter
= type
;
3068 case FILER_SHOW_ALL
:
3072 case FILER_SHOW_GLOB
:
3073 filer_window
->filter_string
= g_strdup(filter_string
);
3078 filer_window
->filter
= FILER_SHOW_ALL
;
3079 g_warning("Impossible: filter type %d", type
);
3087 static Settings
*settings_new(const char *path
)
3091 set
=g_new(Settings
, 1);
3092 memset(set
, 0, sizeof(Settings
));
3094 set
->path
=g_strdup(path
);
3099 static void settings_free(Settings
*set
)
3103 g_free(set
->filter
);
3107 static void store_settings(Settings
*set
)
3111 old
=g_hash_table_lookup(settings_table
, set
->path
);
3114 g_hash_table_remove(settings_table
, set
->path
);
3118 g_hash_table_insert(settings_table
, set
->path
, set
);
3121 /* TODO: use symbolic names in the XML file where possible */
3122 static void load_from_node(Settings
*set
, xmlDocPtr doc
, xmlNodePtr node
)
3126 str
=xmlNodeListGetString(doc
, node
->xmlChildrenNode
, 1);
3128 if(strcmp(node
->name
, "X") == 0) {
3130 set
->flags
|=SET_POSITION
;
3131 } else if(strcmp(node
->name
, "Y") == 0) {
3133 set
->flags
|=SET_POSITION
;
3134 } else if(strcmp(node
->name
, "Width") == 0) {
3135 set
->width
=atoi(str
);
3136 set
->flags
|=SET_SIZE
;
3137 } else if(strcmp(node
->name
, "Height") == 0) {
3138 set
->height
=atoi(str
);
3139 set
->flags
|=SET_SIZE
;
3140 } else if(strcmp(node
->name
, "ShowHidden") == 0) {
3141 set
->show_hidden
=atoi(str
);
3142 set
->flags
|=SET_HIDDEN
;
3143 } else if(strcmp(node
->name
, "ViewType") == 0) {
3144 set
->view_type
=atoi(str
);
3145 set
->flags
|=SET_DETAILS
;
3146 } else if(strcmp(node
->name
, "DetailsType") == 0) {
3147 set
->details_type
=atoi(str
);
3148 set
->flags
|=SET_DETAILS
;
3149 } else if(strcmp(node
->name
, "SortType") == 0) {
3150 set
->sort_type
=atoi(str
);
3151 set
->flags
|=SET_SORT
;
3152 } else if(strcmp(node
->name
, "SortOrder") == 0) {
3153 set
->sort_order
=atoi(str
);
3154 set
->flags
|=SET_SORT
;
3155 } else if(strcmp(node
->name
, "DisplayStyle") == 0) {
3156 set
->display_style
=atoi(str
);
3157 set
->flags
|=SET_STYLE
;
3158 } else if(strcmp(node
->name
, "ShowThumbs") == 0) {
3159 set
->show_thumbs
=atoi(str
);
3160 set
->flags
|=SET_THUMBS
;
3161 } else if(strcmp(node
->name
, "FilterType") == 0) {
3162 set
->filter_type
=atoi(str
);
3163 set
->flags
|=SET_FILTER
;
3164 } else if(strcmp(node
->name
, "Filter") == 0) {
3165 set
->filter
=g_strdup(str
);
3166 set
->flags
|=SET_FILTER
;
3167 } else if(strcmp(node
->name
, "FilterDirectories") == 0) {
3168 set
->filter_directories
=atoi(str
);
3169 set
->flags
|=SET_FILTER
;
3176 static void load_learnt_mounts(void)
3179 gchar
*buffer
= NULL
;
3184 unmount_prompt_actions
= g_hash_table_new_full(g_str_hash
,
3185 g_str_equal
, g_free
, NULL
);
3187 path
= choices_find_xdg_path_load("Mounts", PROJECT
, SITE
);
3190 if (!g_file_get_contents(path
, &buffer
, &len
, NULL
))
3198 buffer
[len
- 1] = 0;
3206 entries
= g_strsplit(buffer
, "\n", 0);
3208 for (n
= 0; entries
[n
]; ++n
)
3210 buffer
= entries
[n
];
3211 len
= strlen(buffer
);
3214 buffer
[len
- 2] = 0;
3215 g_hash_table_insert(unmount_prompt_actions
, g_strdup(buffer
),
3216 GINT_TO_POINTER(buffer
[len
- 1] - '0'));
3219 g_strfreev(entries
);
3222 static void save_mount(char *path
, gpointer value
, FILE **pfp
)
3226 gchar
*spath
= choices_find_xdg_path_save("Mounts",
3227 PROJECT
, SITE
, TRUE
);
3231 *pfp
= fopen(spath
, "w");
3236 fprintf(*pfp
, "%s %d\n", path
, GPOINTER_TO_INT(value
));
3239 static void save_learnt_mounts(void)
3243 /* A GHashTableIter would be easier, but it's a relatively new feature */
3244 if (unmount_prompt_actions
)
3245 g_hash_table_foreach(unmount_prompt_actions
, (GHFunc
) save_mount
, &fp
);
3250 static void load_settings(void)
3253 XMLwrapper
*settings_doc
=NULL
;
3255 path
=choices_find_xdg_path_load("Settings.xml", PROJECT
, SITE
);
3257 settings_doc
=xml_new(path
);
3262 settings_table
=g_hash_table_new(g_str_hash
, g_str_equal
);
3265 xmlNodePtr node
, subnode
;
3267 node
= xmlDocGetRootElement(settings_doc
->doc
);
3269 for (node
= node
->xmlChildrenNode
; node
; node
= node
->next
)
3274 if (node
->type
!= XML_ELEMENT_NODE
)
3276 if (strcmp(node
->name
, "FilerWindow") != 0)
3279 path
=xmlGetProp(node
, "path");
3280 set
=settings_new(path
);
3283 for (subnode
=node
->xmlChildrenNode
; subnode
;
3284 subnode
=subnode
->next
) {
3286 if (subnode
->type
!= XML_ELEMENT_NODE
)
3288 load_from_node(set
, settings_doc
->doc
,
3292 store_settings(set
);
3294 g_object_unref(settings_doc
);
3298 static void add_nodes(gpointer key
, gpointer value
, gpointer data
)
3300 xmlNodePtr node
=(xmlNodePtr
) data
;
3302 Settings
*set
=(Settings
*) value
;
3305 sub
=xmlNewChild(node
, NULL
, "FilerWindow", NULL
);
3307 xmlSetProp(sub
, "path", set
->path
);
3309 if(set
->flags
& SET_POSITION
) {
3310 tmp
=g_strdup_printf("%d", set
->x
);
3311 xmlNewChild(sub
, NULL
, "X", tmp
);
3313 tmp
=g_strdup_printf("%d", set
->y
);
3314 xmlNewChild(sub
, NULL
, "Y", tmp
);
3317 if(set
->flags
& SET_SIZE
) {
3318 tmp
=g_strdup_printf("%d", set
->width
);
3319 xmlNewChild(sub
, NULL
, "Width", tmp
);
3321 tmp
=g_strdup_printf("%d", set
->height
);
3322 xmlNewChild(sub
, NULL
, "Height", tmp
);
3325 if(set
->flags
& SET_HIDDEN
) {
3326 tmp
=g_strdup_printf("%d", set
->show_hidden
);
3327 xmlNewChild(sub
, NULL
, "ShowHidden", tmp
);
3330 if(set
->flags
& SET_STYLE
) {
3331 tmp
=g_strdup_printf("%d", set
->display_style
);
3332 xmlNewChild(sub
, NULL
, "DisplayStyle", tmp
);
3335 if(set
->flags
& SET_SORT
) {
3336 tmp
=g_strdup_printf("%d", set
->sort_type
);
3337 xmlNewChild(sub
, NULL
, "SortType", tmp
);
3339 tmp
=g_strdup_printf("%d", set
->sort_order
);
3340 xmlNewChild(sub
, NULL
, "SortOrder", tmp
);
3343 if(set
->flags
& SET_DETAILS
) {
3344 tmp
=g_strdup_printf("%d", set
->view_type
);
3345 xmlNewChild(sub
, NULL
, "ViewType", tmp
);
3347 tmp
=g_strdup_printf("%d", set
->details_type
);
3348 xmlNewChild(sub
, NULL
, "DetailsType", tmp
);
3351 if(set
->flags
& SET_STYLE
) {
3352 tmp
=g_strdup_printf("%d", set
->show_thumbs
);
3353 xmlNewChild(sub
, NULL
, "ShowThumbs", tmp
);
3356 if(set
->flags
& SET_FILTER
) {
3357 tmp
=g_strdup_printf("%d", set
->filter_type
);
3358 xmlNewChild(sub
, NULL
, "FilterType", tmp
);
3360 if(set
->filter
&& set
->filter
[0])
3361 xmlNewChild(sub
, NULL
, "Filter", set
->filter
);
3362 tmp
=g_strdup_printf("%d", set
->filter_directories
);
3363 xmlNewChild(sub
, NULL
, "FilterDirectories", tmp
);
3367 static void save_settings(void)
3371 path
=choices_find_xdg_path_save("Settings.xml", PROJECT
, SITE
, TRUE
);
3373 xmlDocPtr doc
= xmlNewDoc("1.0");
3374 xmlDocSetRootElement(doc
, xmlNewDocNode(doc
, NULL
,
3377 g_hash_table_foreach(settings_table
, add_nodes
,
3378 xmlDocGetRootElement(doc
));
3380 save_xml_file(doc
, path
);
3388 static void check_settings(FilerWindow
*filer_window
)
3392 set
=(Settings
*) g_hash_table_lookup(settings_table
,
3393 filer_window
->sym_path
);
3396 if(set
->flags
& SET_POSITION
)
3397 gtk_window_move(GTK_WINDOW(filer_window
->window
),
3399 if(set
->flags
& SET_SIZE
)
3400 filer_window_set_size(filer_window
, set
->width
,
3402 if(set
->flags
& SET_HIDDEN
)
3403 filer_set_hidden(filer_window
, set
->show_hidden
);
3405 if(set
->flags
& (SET_STYLE
|SET_DETAILS
)) {
3406 DisplayStyle style
=filer_window
->display_style
;
3407 DetailsType details
=filer_window
->details_type
;
3409 if(set
->flags
& SET_STYLE
)
3410 style
=set
->display_style
;
3412 if(set
->flags
& SET_DETAILS
) {
3413 details
=set
->details_type
;
3415 filer_set_view_type(filer_window
,
3419 display_set_layout(filer_window
, style
,
3423 if(set
->flags
& SET_SORT
)
3424 display_set_sort_type(filer_window
,
3428 if(set
->flags
& SET_THUMBS
)
3429 display_set_thumbs(filer_window
,
3432 if(set
->flags
& SET_FILTER
)
3434 display_set_filter(filer_window
,
3437 display_set_filter_directories(filer_window
,
3438 set
->filter_directories
);
3444 typedef struct settings_window
{
3447 GtkWidget
*pos
, *size
, *hidden
, *style
, *sort
, *details
,
3454 static void settings_response(GtkWidget
*window
, gint response
,
3455 SettingsWindow
*set_win
)
3457 if(response
==GTK_RESPONSE_OK
) {
3460 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->pos
)))
3461 flags
|=SET_POSITION
;
3462 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->size
)))
3464 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->hidden
)))
3466 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->style
)))
3468 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->sort
)))
3470 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->details
)))
3472 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->thumbs
)))
3474 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->filter
)))
3477 set_win
->set
->flags
=flags
;
3478 store_settings(set_win
->set
);
3482 gtk_widget_destroy(window
);
3485 void filer_save_settings(FilerWindow
*fwin
)
3487 SettingsWindow
*set_win
;
3488 GtkWidget
*vbox
, *frame
;
3489 GtkWidget
*path
, *lbl
;
3492 Settings
*set
=settings_new(fwin
->sym_path
);
3494 gtk_window_get_position(GTK_WINDOW(fwin
->window
),&x
, &y
);
3495 set
->flags
|=SET_POSITION
;
3499 gtk_window_get_size(GTK_WINDOW(fwin
->window
),&x
, &y
);
3500 set
->flags
|=SET_SIZE
;
3504 set
->flags
|=SET_HIDDEN
;
3505 set
->show_hidden
=fwin
->show_hidden
;
3507 set
->flags
|=SET_STYLE
;
3508 set
->display_style
=fwin
->display_style
;
3510 set
->flags
|=SET_SORT
;
3511 set
->sort_type
=fwin
->sort_type
;
3512 set
->sort_order
=fwin
->sort_order
;
3514 set
->flags
|=SET_DETAILS
;
3515 set
->view_type
=fwin
->view_type
;
3516 set
->details_type
=fwin
->details_type
;
3518 set
->flags
|=SET_THUMBS
;
3519 set
->show_thumbs
=fwin
->show_thumbs
;
3521 set
->flags
|=SET_FILTER
;
3522 set
->filter_type
=fwin
->filter
;
3523 if(fwin
->filter_string
)
3524 set
->filter
=g_strdup(fwin
->filter_string
);
3525 set
->filter_directories
=fwin
->filter_directories
;
3527 /* Store other parameters
3529 set_win
=g_new(SettingsWindow
, 1);
3531 set_win
->window
=gtk_dialog_new();
3532 number_of_windows
++;
3534 gtk_dialog_add_button(GTK_DIALOG(set_win
->window
),
3535 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
);
3536 gtk_dialog_add_button(GTK_DIALOG(set_win
->window
),
3537 GTK_STOCK_OK
, GTK_RESPONSE_OK
);
3539 g_signal_connect(set_win
->window
, "destroy",
3540 G_CALLBACK(one_less_window
), NULL
);
3541 g_signal_connect_swapped(set_win
->window
, "destroy",
3542 G_CALLBACK(g_free
), set_win
);
3544 gtk_window_set_title(GTK_WINDOW(set_win
->window
),
3545 _("Select display properties to save"));
3547 vbox
=GTK_DIALOG(set_win
->window
)->vbox
;
3549 lbl
=gtk_label_new(_("<b>Save display settings for directory</b>"));
3550 gtk_label_set_use_markup(GTK_LABEL(lbl
), TRUE
);
3551 gtk_box_pack_start(GTK_BOX(vbox
), lbl
, FALSE
, FALSE
, 2);
3553 path
=gtk_label_new(set
->path
);
3554 gtk_box_pack_start(GTK_BOX(vbox
), path
, FALSE
, FALSE
, 2);
3556 frame
=gtk_frame_new(_("Select settings to save"));
3557 gtk_box_pack_start(GTK_BOX(vbox
), frame
, TRUE
, TRUE
, 2);
3559 /*Make new vbox to go in the frame */
3560 vbox
=gtk_vbox_new(FALSE
, 2);
3561 gtk_container_add(GTK_CONTAINER(frame
), vbox
);
3563 set_win
->pos
=gtk_check_button_new_with_label(_("Position"));
3564 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->pos
, FALSE
, FALSE
, 2);
3565 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->pos
),
3566 set
->flags
& SET_POSITION
);
3568 set_win
->size
=gtk_check_button_new_with_label(_("Size"));
3569 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->size
, FALSE
, FALSE
, 2);
3570 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->size
),
3571 set
->flags
& SET_SIZE
);
3573 set_win
->hidden
=gtk_check_button_new_with_label(_("Show hidden"));
3574 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->hidden
,
3576 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->hidden
),
3577 set
->flags
& SET_HIDDEN
);
3579 set_win
->style
=gtk_check_button_new_with_label(_("Display style"));
3580 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->style
,
3582 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->style
),
3583 set
->flags
& SET_STYLE
);
3585 set_win
->sort
=gtk_check_button_new_with_label(_("Sort type and order"));
3586 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->sort
, FALSE
, FALSE
, 2);
3587 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->sort
),
3588 set
->flags
& SET_SORT
);
3590 set_win
->details
=gtk_check_button_new_with_label(_("Details"));
3591 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->details
, FALSE
, FALSE
, 2);
3592 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->details
),
3593 set
->flags
& SET_DETAILS
);
3595 set_win
->thumbs
=gtk_check_button_new_with_label(_("Thumbnails"));
3596 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->thumbs
,
3598 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->thumbs
),
3599 set
->flags
& SET_THUMBS
);
3601 set_win
->filter
=gtk_check_button_new_with_label(_("Filter"));
3602 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->filter
,
3604 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->filter
),
3605 set
->flags
& SET_FILTER
);
3608 g_signal_connect(set_win
->window
, "response",
3609 G_CALLBACK(settings_response
), set_win
);
3611 gtk_widget_show_all(set_win
->window
);
3614 static char *tip_from_desktop_file(const char *full_path
)
3616 GError
*error
= NULL
;
3617 char *comment
= NULL
;
3619 comment
= get_value_from_desktop_file(full_path
,
3620 "Desktop Entry", "Comment", &error
);
3623 delayed_error("Failed to parse .desktop file '%s':\n%s",
3624 full_path
, error
->message
);
3630 g_error_free(error
);
3635 UnmountPrompt
filer_get_unmount_action(const char *path
)
3637 return GPOINTER_TO_INT(g_hash_table_lookup(unmount_prompt_actions
, path
));
3640 void filer_set_unmount_action(const char *path
, UnmountPrompt action
)
3642 g_hash_table_insert(unmount_prompt_actions
, g_strdup(path
),
3643 GINT_TO_POINTER(action
));
3644 save_learnt_mounts();