4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2005, 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
);
159 static char *tip_from_desktop_file(const char *full_path
);
161 static GdkCursor
*busy_cursor
= NULL
;
162 static GdkCursor
*crosshair
= NULL
;
164 /* Indicates whether the filer's display is different to the machine it
165 * is actually running on.
167 static gboolean not_local
= FALSE
;
169 static Option o_short_flag_names
;
170 static Option o_filer_view_type
;
171 Option o_filer_auto_resize
, o_unique_filer_windows
;
172 Option o_filer_size_limit
;
174 void filer_init(void)
178 gchar
*dpyhost
, *tmp
;
180 option_add_int(&o_filer_size_limit
, "filer_size_limit", 75);
181 option_add_int(&o_filer_auto_resize
, "filer_auto_resize",
183 option_add_int(&o_unique_filer_windows
, "filer_unique_windows", 0);
185 option_add_int(&o_short_flag_names
, "filer_short_flag_names", FALSE
);
187 option_add_int(&o_filer_view_type
, "filer_view_type",
188 VIEW_TYPE_COLLECTION
);
190 option_add_notify(filer_options_changed
);
192 busy_cursor
= gdk_cursor_new(GDK_WATCH
);
193 crosshair
= gdk_cursor_new(GDK_CROSSHAIR
);
195 window_with_id
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
198 /* Is the display on the local machine, or are we being
199 * run remotely? See filer_set_title().
201 ohost
= our_host_name();
202 dpy
= gdk_get_display();
203 dpyhost
= g_strdup(dpy
);
204 tmp
= strchr(dpyhost
, ':');
208 if (dpyhost
[0] && strcmp(ohost
, dpyhost
) != 0)
210 /* Try the cannonical name for dpyhost (see our_host_name()
215 ent
= gethostbyname(dpyhost
);
216 if (!ent
|| strcmp(ohost
, ent
->h_name
) != 0)
225 static gboolean
if_deleted(gpointer item
, gpointer removed
)
227 int i
= ((GPtrArray
*) removed
)->len
;
228 DirItem
**r
= (DirItem
**) ((GPtrArray
*) removed
)->pdata
;
229 char *leafname
= ((DirItem
*) item
)->leafname
;
233 if (strcmp(leafname
, r
[i
]->leafname
) == 0)
240 #define DECOR_BORDER 32
242 /* Resize the filer window to w x h pixels, plus border (not clamped).
243 * If triggered by a key event, warp the pointer (for SloppyFocus users).
245 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
;
268 w
= MAX(req
->width
, w
);
269 h
= MAX(req
->height
, h
);
270 gdk_window_get_pointer(NULL
, &x
, &y
, NULL
);
271 m
= gdk_screen_get_monitor_at_point(gdk_screen_get_default(),
273 gdk_window_get_position(gdk_window
, &x
, &y
);
275 if (x
+ w
+ DECOR_BORDER
>
276 monitor_geom
[m
].x
+ monitor_geom
[m
].width
||
277 y
+ h
+ DECOR_BORDER
>
278 monitor_geom
[m
].y
+ monitor_geom
[m
].height
)
280 if (x
+ w
+ DECOR_BORDER
>
281 monitor_geom
[m
].x
+ monitor_geom
[m
].width
)
283 x
= monitor_geom
[m
].x
+ monitor_geom
[m
].width
-
284 w
- 4 - DECOR_BORDER
;
286 if (y
+ h
+ DECOR_BORDER
>
287 monitor_geom
[m
].y
+ monitor_geom
[m
].height
)
289 y
= monitor_geom
[m
].y
+ monitor_geom
[m
].height
-
290 h
- 4 - DECOR_BORDER
;
292 gdk_window_move_resize(gdk_window
, x
, y
, w
, h
);
295 gdk_window_resize(gdk_window
, w
, h
);
297 /* If the resize was triggered by a key press, keep
298 * the pointer inside the window so that it doesn't
299 * lose focus when using pointer-follows-mouse.
301 event
= gtk_get_current_event();
302 if (event
&& event
->type
== GDK_KEY_PRESS
)
307 GdkWindow
*win
= filer_window
->window
->window
;
309 gdk_window_get_pointer(filer_window
->window
->window
,
312 nx
= CLAMP(x
, 4, w
- 4);
313 ny
= CLAMP(y
, 4, h
- 4);
315 if (nx
!= x
|| ny
!= y
)
317 XWarpPointer(gdk_x11_drawable_get_xdisplay(win
),
319 gdk_x11_drawable_get_xid(win
),
325 gdk_event_free(event
);
328 gtk_window_set_default_size(GTK_WINDOW(window
), w
, h
);
331 /* Called on a timeout while scanning or when scanning ends
332 * (whichever happens first).
334 static gint
open_filer_window(FilerWindow
*filer_window
)
336 Settings
*dir_settings
;
337 gboolean force_resize
;
339 dir_settings
= (Settings
*) g_hash_table_lookup(settings_table
,
340 filer_window
->sym_path
);
342 force_resize
= !(o_filer_auto_resize
.int_value
== RESIZE_NEVER
&&
343 dir_settings
&& dir_settings
->flags
& SET_POSITION
);
345 view_style_changed(filer_window
->view
, 0);
347 if (filer_window
->open_timeout
)
349 g_source_remove(filer_window
->open_timeout
);
350 filer_window
->open_timeout
= 0;
353 if (!GTK_WIDGET_VISIBLE(filer_window
->window
))
355 display_set_actual_size(filer_window
, force_resize
);
356 gtk_widget_show(filer_window
->window
);
362 /* Look through all items we want to display, and queue a recheck on any
365 static void queue_interesting(FilerWindow
*filer_window
)
370 view_get_iter(filer_window
->view
, &iter
, 0);
371 while ((item
= iter
.next(&iter
)))
373 if (item
->flags
& ITEM_FLAG_NEED_RESCAN_QUEUE
)
374 dir_queue_recheck(filer_window
->directory
, item
);
378 static void update_display(Directory
*dir
,
381 FilerWindow
*filer_window
)
383 ViewIface
*view
= (ViewIface
*) filer_window
->view
;
388 view_add_items(view
, items
);
389 /* Open and resize if currently hidden */
390 open_filer_window(filer_window
);
393 view_delete_if(view
, if_deleted
, items
);
394 toolbar_update_info(filer_window
);
397 set_scanning_display(filer_window
, TRUE
);
398 toolbar_update_info(filer_window
);
401 if (filer_window
->window
->window
)
402 gdk_window_set_cursor(
403 filer_window
->window
->window
,
405 set_scanning_display(filer_window
, FALSE
);
406 toolbar_update_info(filer_window
);
407 open_filer_window(filer_window
);
409 if (filer_window
->had_cursor
&&
410 !view_cursor_visible(view
))
413 view_get_iter(view
, &start
, 0);
414 if (start
.next(&start
))
415 view_cursor_to_iter(view
, &start
);
416 view_show_cursor(view
);
417 filer_window
->had_cursor
= FALSE
;
419 if (filer_window
->auto_select
)
420 display_set_autoselect(filer_window
,
421 filer_window
->auto_select
);
422 null_g_free(&filer_window
->auto_select
);
424 filer_create_thumbs(filer_window
);
426 if (filer_window
->thumb_queue
)
427 start_thumb_scanning(filer_window
);
430 view_update_items(view
, items
);
432 case DIR_ERROR_CHANGED
:
433 filer_set_title(filer_window
);
435 case DIR_QUEUE_INTERESTING
:
436 queue_interesting(filer_window
);
441 static void attach(FilerWindow
*filer_window
)
443 gdk_window_set_cursor(filer_window
->window
->window
, busy_cursor
);
444 view_clear(filer_window
->view
);
445 filer_window
->scanning
= TRUE
;
446 dir_attach(filer_window
->directory
, (DirCallback
) update_display
,
448 filer_set_title(filer_window
);
449 bookmarks_add_history(filer_window
->sym_path
);
451 if (filer_window
->directory
->error
)
453 if (spring_in_progress
)
454 g_printerr(_("Error scanning '%s':\n%s\n"),
455 filer_window
->sym_path
,
456 filer_window
->directory
->error
);
458 delayed_error(_("Error scanning '%s':\n%s"),
459 filer_window
->sym_path
,
460 filer_window
->directory
->error
);
464 static void detach(FilerWindow
*filer_window
)
466 g_return_if_fail(filer_window
->directory
!= NULL
);
468 dir_detach(filer_window
->directory
,
469 (DirCallback
) update_display
, filer_window
);
470 g_object_unref(filer_window
->directory
);
471 filer_window
->directory
= NULL
;
474 /* If 'start' was mounted by ROX-Filer, return it. Otherwise, try the
475 * parents up the tree.
476 * NULL if we're not in a user mount point.
477 * g_free() the result.
479 static char *get_ancestor_user_mount_point(const char *start
)
483 path
= strdup(start
);
489 if (mount_is_user_mounted(path
))
498 slash
= strrchr(path
+ 1, '/');
505 static void umount_dialog_response(GtkWidget
*dialog
, int response
, char *mount
)
507 if (response
== GTK_RESPONSE_OK
)
511 list
= g_list_prepend(NULL
, mount
);
512 action_mount(list
, FALSE
, FALSE
, TRUE
);
518 gtk_widget_destroy(dialog
);
523 /* 'filer_window' shows a directory under 'mount'. If no other window also
524 * shows a directory under it, display a non-modal dialog offering to
525 * unmount the directory.
526 * 'mount' is freed by this function, either directly, or after the dialog
529 static void may_offer_unmount(FilerWindow
*filer_window
, char *mount
)
531 GtkWidget
*dialog
, *button
;
537 for (next
= all_filer_windows
; next
; next
= next
->next
)
539 FilerWindow
*other
= (FilerWindow
*) next
->data
;
541 if (other
== filer_window
)
544 if (strncmp(filer_window
->real_path
, other
->real_path
,
549 filer_window
->real_path
[len
] != '/' ||
550 filer_window
->real_path
[len
] != '\0');
552 if (other
->real_path
[len
] != '/' &&
553 other
->real_path
[len
] != '\0')
556 /* Found another window. Don't offer to unmount. */
561 dialog
= gtk_message_dialog_new(NULL
, 0, GTK_MESSAGE_QUESTION
,
563 _("Do you want to unmount this device?\n\n"
564 "Unmounting a device makes it safe to remove "
565 "the disk."), mount
);
567 button
= button_new_mixed(ROX_STOCK_MOUNTED
, _("No change"));
568 GTK_WIDGET_SET_FLAGS(button
, GTK_CAN_DEFAULT
);
569 gtk_dialog_add_action_widget(GTK_DIALOG(dialog
), button
,
570 GTK_RESPONSE_CANCEL
);
571 gtk_widget_show(button
);
573 button
= button_new_mixed(ROX_STOCK_MOUNT
, _("Unmount"));
574 GTK_WIDGET_SET_FLAGS(button
, GTK_CAN_DEFAULT
);
575 gtk_dialog_add_action_widget(GTK_DIALOG(dialog
), button
,
577 gtk_widget_show(button
);
579 g_signal_connect(G_OBJECT(dialog
), "response",
580 G_CALLBACK(umount_dialog_response
), mount
);
582 gtk_dialog_set_default_response(GTK_DIALOG(dialog
),
586 gtk_widget_show(dialog
);
589 /* Returns TRUE to prevent closing the window. May offer to unmount a
592 gboolean
filer_window_delete(GtkWidget
*window
,
593 GdkEvent
*unused
, /* (may be NULL) */
594 FilerWindow
*filer_window
)
598 mount
= get_ancestor_user_mount_point(filer_window
->real_path
);
601 may_offer_unmount(filer_window
, mount
);
606 static void filer_window_destroyed(GtkWidget
*widget
, FilerWindow
*filer_window
)
608 all_filer_windows
= g_list_remove(all_filer_windows
, filer_window
);
610 g_object_set_data(G_OBJECT(widget
), "filer_window", NULL
);
612 if (window_with_primary
== filer_window
)
613 window_with_primary
= NULL
;
615 if (window_with_focus
== filer_window
)
618 window_with_focus
= NULL
;
621 if (filer_window
->directory
)
622 detach(filer_window
);
624 if (filer_window
->open_timeout
)
626 g_source_remove(filer_window
->open_timeout
);
627 filer_window
->open_timeout
= 0;
630 if (filer_window
->auto_scroll
!= -1)
632 g_source_remove(filer_window
->auto_scroll
);
633 filer_window
->auto_scroll
= -1;
636 if (filer_window
->thumb_queue
)
637 destroy_glist(&filer_window
->thumb_queue
);
641 filer_set_id(filer_window
, NULL
);
643 if(filer_window
->filter_string
)
644 g_free(filer_window
->filter_string
);
645 if(filer_window
->regexp
)
646 g_free(filer_window
->regexp
);
648 g_free(filer_window
->auto_select
);
649 g_free(filer_window
->real_path
);
650 g_free(filer_window
->sym_path
);
651 g_free(filer_window
);
656 /* Returns TRUE iff the directory still exists. */
657 static gboolean
may_rescan(FilerWindow
*filer_window
, gboolean warning
)
661 g_return_val_if_fail(filer_window
!= NULL
, FALSE
);
663 /* We do a fresh lookup (rather than update) because the inode may
666 dir
= g_fscache_lookup(dir_cache
, filer_window
->real_path
);
670 info_message(_("Directory missing/deleted"));
671 gtk_widget_destroy(filer_window
->window
);
674 if (dir
== filer_window
->directory
)
678 detach(filer_window
);
679 filer_window
->directory
= dir
;
680 attach(filer_window
);
686 /* No items are now selected. This might be because another app claimed
687 * the selection or because the user unselected all the items.
689 void filer_lost_selection(FilerWindow
*filer_window
, guint time
)
691 if (window_with_primary
== filer_window
)
693 window_with_primary
= NULL
;
694 gtk_selection_owner_set(NULL
, GDK_SELECTION_PRIMARY
, time
);
698 /* Another app has claimed the primary selection */
699 static void filer_lost_primary(GtkWidget
*window
,
700 GdkEventSelection
*event
,
703 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
705 if (window_with_primary
&& window_with_primary
== filer_window
)
707 window_with_primary
= NULL
;
708 set_selection_state(filer_window
, FALSE
);
712 /* Someone wants us to send them the selection */
713 static void selection_get(GtkWidget
*widget
,
714 GtkSelectionData
*selection_data
,
719 GString
*reply
, *header
;
720 FilerWindow
*filer_window
= (FilerWindow
*) data
;
724 reply
= g_string_new(NULL
);
725 header
= g_string_new(NULL
);
730 g_string_printf(header
, " %s",
731 make_path(filer_window
->sym_path
, ""));
733 case TARGET_URI_LIST
:
734 g_string_printf(header
, " file://%s%s",
735 our_host_name_for_dnd(),
736 make_path(filer_window
->sym_path
, ""));
740 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
742 while ((item
= iter
.next(&iter
)))
744 g_string_append(reply
, header
->str
);
745 g_string_append(reply
, item
->leafname
);
749 gtk_selection_data_set_text(selection_data
,
750 reply
->str
+ 1, reply
->len
- 1);
753 g_warning("Attempt to paste empty selection!");
754 gtk_selection_data_set_text(selection_data
, "", 0);
757 g_string_free(reply
, TRUE
);
758 g_string_free(header
, TRUE
);
761 /* Selection has been changed -- try to grab the primary selection
762 * if we don't have it. Also called when clicking on an insensitive selection
764 * Also updates toolbar info.
766 void filer_selection_changed(FilerWindow
*filer_window
, gint time
)
768 g_return_if_fail(filer_window
!= NULL
);
770 toolbar_update_info(filer_window
);
772 if (window_with_primary
== filer_window
)
773 return; /* Already got primary */
775 if (!view_count_selected(filer_window
->view
))
776 return; /* Nothing selected */
778 if (filer_window
->temp_item_selected
== FALSE
&&
779 gtk_selection_owner_set(GTK_WIDGET(filer_window
->window
),
780 GDK_SELECTION_PRIMARY
,
783 window_with_primary
= filer_window
;
784 set_selection_state(filer_window
, TRUE
);
787 set_selection_state(filer_window
, FALSE
);
790 /* Open the item (or add it to the shell command minibuffer) */
791 void filer_openitem(FilerWindow
*filer_window
, ViewIter
*iter
, OpenFlags flags
)
793 gboolean shift
= (flags
& OPEN_SHIFT
) != 0;
794 gboolean close_mini
= flags
& OPEN_FROM_MINI
;
795 gboolean close_window
= (flags
& OPEN_CLOSE_WINDOW
) != 0;
797 const guchar
*full_path
;
798 gboolean wink
= TRUE
;
801 g_return_if_fail(filer_window
!= NULL
&& iter
!= NULL
);
803 item
= iter
->peek(iter
);
805 g_return_if_fail(item
!= NULL
);
807 if (filer_window
->mini_type
== MINI_SHELL
)
809 minibuffer_add(filer_window
, item
->leafname
);
813 if (item
->base_type
== TYPE_UNKNOWN
)
814 dir_update_item(filer_window
->directory
, item
->leafname
);
816 if (item
->base_type
== TYPE_DIRECTORY
)
818 /* Never close a filer window when opening a directory
819 * (click on a dir or click on an app with shift).
821 if (shift
|| !(item
->flags
& ITEM_FLAG_APPDIR
))
822 close_window
= FALSE
;
825 full_path
= make_path(filer_window
->sym_path
, item
->leafname
);
826 if (shift
&& (item
->flags
& ITEM_FLAG_SYMLINK
))
829 old_dir
= filer_window
->directory
;
830 if (run_diritem(full_path
, item
,
831 flags
& OPEN_SAME_WINDOW
? filer_window
: NULL
,
835 if (old_dir
!= filer_window
->directory
)
839 gtk_widget_destroy(filer_window
->window
);
843 view_wink_item(filer_window
->view
, iter
);
845 minibuffer_hide(filer_window
);
850 static gint
pointer_in(GtkWidget
*widget
,
851 GdkEventCrossing
*event
,
852 FilerWindow
*filer_window
)
854 may_rescan(filer_window
, TRUE
);
858 static gint
pointer_out(GtkWidget
*widget
,
859 GdkEventCrossing
*event
,
860 FilerWindow
*filer_window
)
866 /* Move the cursor to the next selected item in direction 'dir'
869 void filer_next_selected(FilerWindow
*filer_window
, int dir
)
871 ViewIter iter
, cursor
;
872 gboolean have_cursor
;
873 ViewIface
*view
= filer_window
->view
;
875 g_return_if_fail(dir
== 1 || dir
== -1);
877 view_get_cursor(view
, &cursor
);
878 have_cursor
= cursor
.peek(&cursor
) != NULL
;
880 view_get_iter(view
, &iter
,
882 (have_cursor
? VIEW_ITER_FROM_CURSOR
: 0) |
883 (dir
< 0 ? VIEW_ITER_BACKWARDS
: 0));
885 if (have_cursor
&& view_get_selected(view
, &cursor
))
886 iter
.next(&iter
); /* Skip the cursor itself */
888 if (iter
.next(&iter
))
889 view_cursor_to_iter(view
, &iter
);
896 static void return_pressed(FilerWindow
*filer_window
, GdkEventKey
*event
)
898 TargetFunc cb
= filer_window
->target_cb
;
899 gpointer data
= filer_window
->target_data
;
903 filer_target_mode(filer_window
, NULL
, NULL
, NULL
);
905 view_get_cursor(filer_window
->view
, &iter
);
906 if (!iter
.peek(&iter
))
911 cb(filer_window
, &iter
, data
);
915 if (event
->state
& GDK_SHIFT_MASK
)
917 if (event
->state
& GDK_MOD1_MASK
)
918 flags
|= OPEN_CLOSE_WINDOW
;
920 flags
|= OPEN_SAME_WINDOW
;
922 filer_openitem(filer_window
, &iter
, flags
);
925 /* Makes sure that 'groups' is up-to-date, reloading from file if it has
926 * changed. If no groups were loaded and there is no file then initialised
927 * groups to an empty document.
928 * Return the node for the 'name' group.
930 static xmlNode
*group_find(char *name
)
935 /* Update the groups, if possible */
936 path
= choices_find_xdg_path_load("Groups.xml", PROJECT
, SITE
);
940 wrapper
= xml_cache_load(path
);
944 g_object_unref(groups
);
953 groups
= xml_new(NULL
);
954 groups
->doc
= xmlNewDoc("1.0");
956 xmlDocSetRootElement(groups
->doc
,
957 xmlNewDocNode(groups
->doc
, NULL
, "groups", NULL
));
961 node
= xmlDocGetRootElement(groups
->doc
);
963 for (node
= node
->xmlChildrenNode
; node
; node
= node
->next
)
967 gid
= xmlGetProp(node
, "name");
972 if (strcmp(name
, gid
) != 0)
983 static void group_save(FilerWindow
*filer_window
, char *name
)
990 group
= group_find(name
);
993 xmlUnlinkNode(group
);
996 group
= xmlNewChild(xmlDocGetRootElement(groups
->doc
),
997 NULL
, "group", NULL
);
998 xmlSetProp(group
, "name", name
);
1000 xmlNewTextChild(group
, NULL
, "directory", filer_window
->sym_path
);
1002 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
1004 while ((item
= iter
.next(&iter
)))
1005 xmlNewTextChild(group
, NULL
, "item", item
->leafname
);
1007 save_path
= choices_find_xdg_path_save("Groups.xml", PROJECT
, SITE
,
1011 save_xml_file(groups
->doc
, save_path
);
1016 static gboolean
group_restore_cb(ViewIter
*iter
, gpointer data
)
1018 GHashTable
*in_group
= (GHashTable
*) data
;
1020 return g_hash_table_lookup(in_group
,
1021 iter
->peek(iter
)->leafname
) != NULL
;
1024 static void group_restore(FilerWindow
*filer_window
, char *name
)
1026 GHashTable
*in_group
;
1028 xmlNode
*group
, *node
;
1030 group
= group_find(name
);
1034 report_error(_("Group %s is not set. Select some files "
1035 "and press Ctrl+%s to set the group. Press %s "
1036 "on its own to reselect the files later.\n"
1037 "Make sure NumLock is on if you use the keypad."),
1042 node
= get_subnode(group
, NULL
, "directory");
1043 g_return_if_fail(node
!= NULL
);
1044 path
= xmlNodeListGetString(groups
->doc
, node
->xmlChildrenNode
, 1);
1045 g_return_if_fail(path
!= NULL
);
1047 if (strcmp(path
, filer_window
->sym_path
) != 0)
1048 filer_change_to(filer_window
, path
, NULL
);
1051 in_group
= g_hash_table_new(g_str_hash
, g_str_equal
);
1052 for (node
= group
->xmlChildrenNode
; node
; node
= node
->next
)
1055 if (node
->type
!= XML_ELEMENT_NODE
)
1057 if (strcmp(node
->name
, "item") != 0)
1060 leaf
= xmlNodeListGetString(groups
->doc
,
1061 node
->xmlChildrenNode
, 1);
1063 g_warning("Missing leafname!\n");
1065 g_hash_table_insert(in_group
, leaf
, filer_window
);
1068 view_select_if(filer_window
->view
, &group_restore_cb
, in_group
);
1070 g_hash_table_foreach(in_group
, (GHFunc
) g_free
, NULL
);
1071 g_hash_table_destroy(in_group
);
1074 static gboolean
popup_menu(GtkWidget
*widget
, FilerWindow
*filer_window
)
1079 view_get_cursor(filer_window
->view
, &iter
);
1081 event
= gtk_get_current_event();
1082 show_filer_menu(filer_window
, event
, &iter
);
1084 gdk_event_free(event
);
1089 void filer_window_toggle_cursor_item_selected(FilerWindow
*filer_window
)
1091 ViewIface
*view
= filer_window
->view
;
1094 view_get_iter(view
, &iter
, VIEW_ITER_FROM_CURSOR
);
1095 if (!iter
.next(&iter
))
1096 return; /* No cursor */
1098 if (view_get_selected(view
, &iter
))
1099 view_set_selected(view
, &iter
, FALSE
);
1101 view_set_selected(view
, &iter
, TRUE
);
1103 if (iter
.next(&iter
))
1104 view_cursor_to_iter(view
, &iter
);
1107 gint
filer_key_press_event(GtkWidget
*widget
,
1109 FilerWindow
*filer_window
)
1111 ViewIface
*view
= filer_window
->view
;
1113 GtkWidget
*focus
= GTK_WINDOW(widget
)->focus_widget
;
1114 guint key
= event
->keyval
;
1115 char group
[2] = "1";
1117 window_with_focus
= filer_window
;
1119 /* Delay setting up the keys until now to speed loading... */
1120 if (ensure_filer_menu())
1122 /* Gtk updates in an idle-handler, so force a recheck now */
1123 g_signal_emit_by_name(widget
, "keys_changed");
1126 if (focus
&& focus
== filer_window
->minibuffer
)
1127 if (gtk_widget_event(focus
, (GdkEvent
*) event
))
1128 return TRUE
; /* Handled */
1131 gtk_widget_grab_focus(GTK_WIDGET(view
));
1133 view_get_cursor(view
, &cursor
);
1134 if (!cursor
.peek(&cursor
) && (key
== GDK_Up
|| key
== GDK_Down
))
1137 view_get_iter(view
, &iter
, 0);
1138 if (iter
.next(&iter
))
1139 view_cursor_to_iter(view
, &iter
);
1140 gtk_widget_grab_focus(GTK_WIDGET(view
)); /* Needed? */
1147 filer_target_mode(filer_window
, NULL
, NULL
, NULL
);
1148 view_cursor_to_iter(filer_window
->view
, NULL
);
1149 view_clear_selection(filer_window
->view
);
1152 return_pressed(filer_window
, event
);
1154 case GDK_ISO_Left_Tab
:
1155 filer_next_selected(filer_window
, -1);
1158 filer_next_selected(filer_window
, 1);
1161 change_to_parent(filer_window
);
1169 view_get_cursor(filer_window
->view
, &iter
);
1170 show_filer_menu(filer_window
,
1171 (GdkEvent
*) event
, &iter
);
1175 filer_window_toggle_cursor_item_selected(filer_window
);
1178 if (key
>= GDK_0
&& key
<= GDK_9
)
1179 group
[0] = key
- GDK_0
+ '0';
1180 else if (key
>= GDK_KP_0
&& key
<= GDK_KP_9
)
1181 group
[0] = key
- GDK_KP_0
+ '0';
1184 if (focus
&& focus
!= widget
&&
1185 gtk_widget_get_toplevel(focus
) == widget
)
1186 if (gtk_widget_event(focus
,
1187 (GdkEvent
*) event
))
1188 return TRUE
; /* Handled */
1192 if (event
->state
& GDK_CONTROL_MASK
)
1193 group_save(filer_window
, group
);
1195 group_restore(filer_window
, group
);
1201 void filer_open_parent(FilerWindow
*filer_window
)
1204 const char *current
= filer_window
->sym_path
;
1206 if (current
[0] == '/' && current
[1] == '\0')
1207 return; /* Already in the root */
1209 dir
= g_path_get_dirname(current
);
1210 filer_opendir(dir
, filer_window
, NULL
);
1214 void change_to_parent(FilerWindow
*filer_window
)
1217 const char *current
= filer_window
->sym_path
;
1219 if (current
[0] == '/' && current
[1] == '\0')
1220 return; /* Already in the root */
1222 if (mount_is_user_mounted(filer_window
->real_path
))
1223 may_offer_unmount(filer_window
,
1224 g_strdup(filer_window
->real_path
));
1226 dir
= g_path_get_dirname(current
);
1227 filer_change_to(filer_window
, dir
, g_basename(current
));
1231 /* Removes trailing /s from path (modified in place) */
1232 static void tidy_sympath(gchar
*path
)
1236 g_return_if_fail(path
!= NULL
);
1239 while (l
> 1 && path
[l
- 1] == '/')
1246 /* Make filer_window display path. When finished, highlight item 'from', or
1247 * the first item if from is NULL. If there is currently no cursor then
1248 * simply wink 'from' (if not NULL).
1249 * If the cause was a key event and we resize, warp the pointer.
1251 void filer_change_to(FilerWindow
*filer_window
,
1252 const char *path
, const char *from
)
1255 char *sym_path
, *real_path
;
1258 g_return_if_fail(filer_window
!= NULL
);
1260 filer_cancel_thumbnails(filer_window
);
1264 sym_path
= g_strdup(path
);
1265 real_path
= pathdup(path
);
1266 new_dir
= g_fscache_lookup(dir_cache
, real_path
);
1270 delayed_error(_("Directory '%s' is not accessible"),
1277 if (o_unique_filer_windows
.int_value
&& !spring_in_progress
)
1281 fw
= find_filer_window(sym_path
, filer_window
);
1283 gtk_widget_destroy(fw
->window
);
1286 from_dup
= from
&& *from
? g_strdup(from
) : NULL
;
1288 detach(filer_window
);
1289 g_free(filer_window
->real_path
);
1290 g_free(filer_window
->sym_path
);
1291 filer_window
->real_path
= real_path
;
1292 filer_window
->sym_path
= sym_path
;
1293 tidy_sympath(filer_window
->sym_path
);
1295 filer_window
->directory
= new_dir
;
1297 g_free(filer_window
->auto_select
);
1298 filer_window
->auto_select
= from_dup
;
1300 filer_window
->had_cursor
= filer_window
->had_cursor
||
1301 view_cursor_visible(filer_window
->view
);
1303 filer_set_title(filer_window
);
1304 if (filer_window
->window
->window
)
1305 gdk_window_set_role(filer_window
->window
->window
,
1306 filer_window
->sym_path
);
1307 view_cursor_to_iter(filer_window
->view
, NULL
);
1309 attach(filer_window
);
1311 check_settings(filer_window
);
1313 display_set_actual_size(filer_window
, FALSE
);
1315 if (o_filer_auto_resize
.int_value
== RESIZE_ALWAYS
)
1316 view_autosize(filer_window
->view
);
1318 if (filer_window
->mini_type
== MINI_PATH
)
1319 g_idle_add((GSourceFunc
) minibuffer_show_cb
, filer_window
);
1322 /* Returns a list containing the full (sym) pathname of every selected item.
1323 * You must g_free() each item in the list.
1325 GList
*filer_selected_items(FilerWindow
*filer_window
)
1327 GList
*retval
= NULL
;
1328 guchar
*dir
= filer_window
->sym_path
;
1332 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
1333 while ((item
= iter
.next(&iter
)))
1335 retval
= g_list_prepend(retval
,
1336 g_strdup(make_path(dir
, item
->leafname
)));
1339 return g_list_reverse(retval
);
1342 /* Return the single selected item. Error if nothing is selected. */
1343 DirItem
*filer_selected_item(FilerWindow
*filer_window
)
1348 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
1350 item
= iter
.next(&iter
);
1351 g_return_val_if_fail(item
!= NULL
, NULL
);
1352 g_return_val_if_fail(iter
.next(&iter
) == NULL
, NULL
);
1357 /* Creates and shows a new filer window.
1358 * If src_win != NULL then display options can be taken from that source window.
1359 * Returns the new filer window, or NULL on error.
1360 * Note: if unique windows is in use, may return an existing window.
1362 FilerWindow
*filer_opendir(const char *path
, FilerWindow
*src_win
,
1363 const gchar
*wm_class
)
1365 FilerWindow
*filer_window
;
1367 DisplayStyle dstyle
;
1370 GtkSortType s_order
;
1371 Settings
*dir_settings
= NULL
;
1372 gboolean force_resize
= TRUE
;
1374 /* Get the real pathname of the directory and copy it */
1375 real_path
= pathdup(path
);
1377 if (o_unique_filer_windows
.int_value
&& !spring_in_progress
)
1379 FilerWindow
*same_dir_window
;
1381 same_dir_window
= find_filer_window(path
, NULL
);
1383 if (same_dir_window
)
1385 gtk_window_present(GTK_WINDOW(same_dir_window
->window
));
1386 return same_dir_window
;
1390 filer_window
= g_new(FilerWindow
, 1);
1391 filer_window
->message
= NULL
;
1392 filer_window
->minibuffer
= NULL
;
1393 filer_window
->minibuffer_label
= NULL
;
1394 filer_window
->minibuffer_area
= NULL
;
1395 filer_window
->temp_show_hidden
= FALSE
;
1396 filer_window
->sym_path
= g_strdup(path
);
1397 filer_window
->real_path
= real_path
;
1398 filer_window
->scanning
= FALSE
;
1399 filer_window
->had_cursor
= FALSE
;
1400 filer_window
->auto_select
= NULL
;
1401 filer_window
->toolbar_text
= NULL
;
1402 filer_window
->target_cb
= NULL
;
1403 filer_window
->mini_type
= MINI_NONE
;
1404 filer_window
->selection_state
= GTK_STATE_INSENSITIVE
;
1405 filer_window
->toolbar
= NULL
;
1406 filer_window
->toplevel_vbox
= NULL
;
1407 filer_window
->view_hbox
= NULL
;
1408 filer_window
->view
= NULL
;
1409 filer_window
->scrollbar
= NULL
;
1410 filer_window
->auto_scroll
= -1;
1411 filer_window
->window_id
= NULL
;
1413 tidy_sympath(filer_window
->sym_path
);
1415 /* Finds the entry for this directory in the dir cache, creating
1416 * a new one if needed. This does not cause a scan to start,
1417 * so if a new entry is created then it will be empty.
1419 filer_window
->directory
= g_fscache_lookup(dir_cache
, real_path
);
1420 if (!filer_window
->directory
)
1422 delayed_error(_("Directory '%s' not found."), path
);
1423 g_free(filer_window
->real_path
);
1424 g_free(filer_window
->sym_path
);
1425 g_free(filer_window
);
1429 filer_window
->temp_item_selected
= FALSE
;
1430 filer_window
->flags
= (FilerFlags
) 0;
1431 filer_window
->details_type
= DETAILS_TIMES
;
1432 filer_window
->display_style
= UNKNOWN_STYLE
;
1433 filer_window
->display_style_wanted
= UNKNOWN_STYLE
;
1434 filer_window
->thumb_queue
= NULL
;
1435 filer_window
->max_thumbs
= 0;
1436 filer_window
->sort_type
= -1;
1438 filer_window
->filter
= FILER_SHOW_ALL
;
1439 filer_window
->filter_string
= NULL
;
1440 filer_window
->regexp
= NULL
;
1442 if (src_win
&& o_display_inherit_options
.int_value
)
1444 s_type
= src_win
->sort_type
;
1445 s_order
= src_win
->sort_order
;
1446 dstyle
= src_win
->display_style_wanted
;
1447 dtype
= src_win
->details_type
;
1448 filer_window
->show_hidden
= src_win
->show_hidden
;
1449 filer_window
->show_thumbs
= src_win
->show_thumbs
;
1450 filer_window
->view_type
= src_win
->view_type
;
1452 filer_set_filter(filer_window
, src_win
->filter
,
1453 src_win
->filter_string
);
1457 s_type
= o_display_sort_by
.int_value
;
1458 s_order
= GTK_SORT_ASCENDING
;
1459 dstyle
= o_display_size
.int_value
;
1460 dtype
= o_display_details
.int_value
;
1461 filer_window
->show_hidden
= o_display_show_hidden
.int_value
;
1462 filer_window
->show_thumbs
= o_display_show_thumbs
.int_value
;
1463 filer_window
->view_type
= o_filer_view_type
.int_value
;
1466 dir_settings
= (Settings
*) g_hash_table_lookup(settings_table
,
1467 filer_window
->sym_path
);
1470 /* Override the current defaults with the per-directory
1473 if (dir_settings
->flags
& SET_HIDDEN
)
1474 filer_window
->show_hidden
= dir_settings
->show_hidden
;
1476 if (dir_settings
->flags
& SET_STYLE
)
1477 dstyle
= dir_settings
->display_style
;
1479 if (dir_settings
->flags
& SET_DETAILS
)
1481 dtype
= dir_settings
->details_type
;
1482 filer_window
->view_type
= dir_settings
->view_type
;
1485 if (dir_settings
->flags
& SET_SORT
)
1487 s_type
= dir_settings
->sort_type
;
1488 s_order
= dir_settings
->sort_order
;
1491 if (dir_settings
->flags
& SET_THUMBS
)
1492 filer_window
->show_thumbs
= dir_settings
->show_thumbs
;
1494 if (dir_settings
->flags
& SET_FILTER
)
1495 filer_set_filter(filer_window
,
1496 dir_settings
->filter_type
,
1497 dir_settings
->filter
);
1500 /* Add all the user-interface elements & realise */
1501 filer_add_widgets(filer_window
, wm_class
);
1503 gtk_window_set_position(GTK_WINDOW(filer_window
->window
),
1508 if (dir_settings
->flags
& SET_POSITION
)
1510 gtk_window_move(GTK_WINDOW(filer_window
->window
),
1511 dir_settings
->x
, dir_settings
->y
);
1513 if (dir_settings
->flags
& SET_SIZE
)
1515 filer_window_set_size(filer_window
,
1516 dir_settings
->width
,
1517 dir_settings
->height
);
1518 force_resize
= o_filer_auto_resize
.int_value
!= RESIZE_NEVER
;
1522 /* Connect to all the signal handlers */
1523 filer_add_signals(filer_window
);
1525 display_set_layout(filer_window
, dstyle
, dtype
, force_resize
);
1526 display_set_sort_type(filer_window
, s_type
, s_order
);
1528 /* Open the window after a timeout, or when scanning stops.
1529 * Do this before attaching, because attach() might tell us to
1530 * stop scanning (if a scan isn't needed).
1532 filer_window
->open_timeout
= g_timeout_add(500,
1533 (GSourceFunc
) open_filer_window
,
1536 /* The view is created empty and then attach() is called, which
1537 * links the filer window to the entry in the directory cache we
1538 * looked up / created above.
1540 * The attach() function will immediately callback to the filer window
1541 * to deliver a list of all known entries in the directory (so,
1542 * the number of items will be known after attach() returns).
1544 * If the directory was not in the cache (because it hadn't been
1545 * opened it before) then the types and icons for the entries are
1546 * not know, but the list of names is.
1549 attach(filer_window
);
1551 number_of_windows
++;
1552 all_filer_windows
= g_list_prepend(all_filer_windows
, filer_window
);
1554 return filer_window
;
1557 void filer_set_view_type(FilerWindow
*filer_window
, ViewType type
)
1559 GtkWidget
*view
= NULL
;
1560 Directory
*dir
= NULL
;
1561 GHashTable
*selected
= NULL
;
1563 g_return_if_fail(filer_window
!= NULL
);
1565 motion_window
= NULL
;
1567 if (filer_window
->view
)
1569 /* Save the current selection */
1573 selected
= g_hash_table_new(g_str_hash
, g_str_equal
);
1574 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
1575 while ((item
= iter
.next(&iter
)))
1576 g_hash_table_insert(selected
, item
->leafname
, "yes");
1578 /* Destroy the old view */
1579 gtk_widget_destroy(GTK_WIDGET(filer_window
->view
));
1580 filer_window
->view
= NULL
;
1582 dir
= filer_window
->directory
;
1584 detach(filer_window
);
1589 case VIEW_TYPE_COLLECTION
:
1590 view
= view_collection_new(filer_window
);
1592 case VIEW_TYPE_DETAILS
:
1593 view
= view_details_new(filer_window
);
1597 g_return_if_fail(view
!= NULL
);
1599 filer_window
->view
= VIEW(view
);
1600 filer_window
->view_type
= type
;
1602 gtk_box_pack_start(filer_window
->view_hbox
, view
, TRUE
, TRUE
, 0);
1603 gtk_widget_show(view
);
1605 /* Drag and drop events */
1606 make_drop_target(view
, 0);
1607 g_signal_connect(view
, "drag_motion",
1608 G_CALLBACK(drag_motion
), filer_window
);
1609 g_signal_connect(view
, "drag_leave",
1610 G_CALLBACK(drag_leave
), filer_window
);
1611 g_signal_connect(view
, "drag_end",
1612 G_CALLBACK(drag_end
), filer_window
);
1613 /* Dragging from us... */
1614 g_signal_connect(view
, "drag_data_get",
1615 GTK_SIGNAL_FUNC(drag_data_get
), NULL
);
1619 /* Only when changing type. Otherwise, will attach later. */
1620 filer_window
->directory
= dir
;
1621 attach(filer_window
);
1623 if (o_filer_auto_resize
.int_value
!= RESIZE_NEVER
)
1624 view_autosize(filer_window
->view
);
1632 view_get_iter(filer_window
->view
, &iter
, 0);
1633 while ((item
= iter
.next(&iter
)))
1635 if (g_hash_table_lookup(selected
, item
->leafname
))
1636 view_set_selected(filer_window
->view
,
1639 g_hash_table_destroy(selected
);
1643 /* This adds all the widgets to a new filer window. It is in a separate
1644 * function because filer_opendir() was getting too long...
1646 static void filer_add_widgets(FilerWindow
*filer_window
, const gchar
*wm_class
)
1648 GtkWidget
*hbox
, *vbox
;
1650 /* Create the top-level window widget */
1651 filer_window
->window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
1652 filer_set_title(filer_window
);
1653 gtk_widget_set_name(filer_window
->window
, "rox-filer");
1656 gtk_window_set_wmclass(GTK_WINDOW(filer_window
->window
),
1659 /* This property is cleared when the window is destroyed.
1660 * You can thus ref filer_window->window and use this to see
1661 * if the window no longer exists.
1663 g_object_set_data(G_OBJECT(filer_window
->window
),
1664 "filer_window", filer_window
);
1666 /* Create this now to make the Adjustment before the View */
1667 filer_window
->scrollbar
= gtk_vscrollbar_new(NULL
);
1669 vbox
= gtk_vbox_new(FALSE
, 0);
1670 gtk_container_add(GTK_CONTAINER(filer_window
->window
), vbox
);
1672 filer_window
->toplevel_vbox
= GTK_BOX(vbox
);
1674 /* If there's a message that should be displayed in each window (eg
1675 * 'Running as root'), add it here.
1677 if (show_user_message
)
1679 filer_window
->message
= gtk_label_new(show_user_message
);
1680 gtk_box_pack_start(GTK_BOX(vbox
), filer_window
->message
,
1682 gtk_widget_show(filer_window
->message
);
1685 hbox
= gtk_hbox_new(FALSE
, 0);
1686 filer_window
->view_hbox
= GTK_BOX(hbox
);
1687 gtk_box_pack_start_defaults(GTK_BOX(vbox
), hbox
);
1688 /* Add the main View widget */
1689 filer_set_view_type(filer_window
, filer_window
->view_type
);
1690 /* Put the scrollbar next to the View */
1691 gtk_box_pack_end(GTK_BOX(hbox
),
1692 filer_window
->scrollbar
, FALSE
, TRUE
, 0);
1693 gtk_widget_show(hbox
);
1695 /* If we want a toolbar, create it now */
1696 toolbar_update_toolbar(filer_window
);
1698 /* And the minibuffer (hidden to start with) */
1699 create_minibuffer(filer_window
);
1700 gtk_box_pack_end(GTK_BOX(vbox
), filer_window
->minibuffer_area
,
1703 /* And the thumbnail progress bar (also hidden) */
1707 filer_window
->thumb_bar
= gtk_hbox_new(FALSE
, 2);
1708 gtk_box_pack_end(GTK_BOX(vbox
), filer_window
->thumb_bar
,
1711 filer_window
->thumb_progress
= gtk_progress_bar_new();
1713 gtk_box_pack_start(GTK_BOX(filer_window
->thumb_bar
),
1714 filer_window
->thumb_progress
, TRUE
, TRUE
, 0);
1716 cancel
= gtk_button_new_with_label(_("Cancel"));
1717 GTK_WIDGET_UNSET_FLAGS(cancel
, GTK_CAN_FOCUS
);
1718 gtk_box_pack_start(GTK_BOX(filer_window
->thumb_bar
),
1719 cancel
, FALSE
, TRUE
, 0);
1720 g_signal_connect_swapped(cancel
, "clicked",
1721 G_CALLBACK(filer_cancel_thumbnails
),
1725 gtk_widget_show(vbox
);
1726 gtk_widget_show(filer_window
->scrollbar
);
1728 gtk_widget_realize(filer_window
->window
);
1730 gdk_window_set_role(filer_window
->window
->window
,
1731 filer_window
->sym_path
);
1733 filer_window_set_size(filer_window
, 4, 4);
1736 static void filer_add_signals(FilerWindow
*filer_window
)
1738 GtkTargetEntry target_table
[] =
1740 {"text/uri-list", 0, TARGET_URI_LIST
},
1741 {"UTF8_STRING", 0, TARGET_STRING
},
1742 {"STRING", 0, TARGET_STRING
},
1743 {"COMPOUND_TEXT", 0, TARGET_STRING
},/* XXX: Treats as STRING */
1746 /* Events on the top-level window */
1747 gtk_widget_add_events(filer_window
->window
, GDK_ENTER_NOTIFY
);
1748 g_signal_connect(filer_window
->window
, "enter-notify-event",
1749 G_CALLBACK(pointer_in
), filer_window
);
1750 g_signal_connect(filer_window
->window
, "leave-notify-event",
1751 G_CALLBACK(pointer_out
), filer_window
);
1752 g_signal_connect(filer_window
->window
, "destroy",
1753 G_CALLBACK(filer_window_destroyed
), filer_window
);
1754 g_signal_connect(filer_window
->window
, "delete-event",
1755 G_CALLBACK(filer_window_delete
), filer_window
);
1757 g_signal_connect(filer_window
->window
, "selection_clear_event",
1758 G_CALLBACK(filer_lost_primary
), filer_window
);
1760 g_signal_connect(filer_window
->window
, "selection_get",
1761 G_CALLBACK(selection_get
), filer_window
);
1762 gtk_selection_add_targets(GTK_WIDGET(filer_window
->window
),
1763 GDK_SELECTION_PRIMARY
,
1765 sizeof(target_table
) / sizeof(*target_table
));
1767 g_signal_connect(filer_window
->window
, "popup-menu",
1768 G_CALLBACK(popup_menu
), filer_window
);
1769 g_signal_connect(filer_window
->window
, "key_press_event",
1770 G_CALLBACK(filer_key_press_event
), filer_window
);
1772 gtk_window_add_accel_group(GTK_WINDOW(filer_window
->window
),
1776 static gint
clear_scanning_display(FilerWindow
*filer_window
)
1778 if (filer_exists(filer_window
))
1779 filer_set_title(filer_window
);
1783 static void set_scanning_display(FilerWindow
*filer_window
, gboolean scanning
)
1785 if (scanning
== filer_window
->scanning
)
1787 filer_window
->scanning
= scanning
;
1790 filer_set_title(filer_window
);
1792 g_timeout_add(300, (GSourceFunc
) clear_scanning_display
,
1796 /* Note that filer_window may not exist after this call.
1797 * Returns TRUE iff the directory still exists.
1799 gboolean
filer_update_dir(FilerWindow
*filer_window
, gboolean warning
)
1801 gboolean still_exists
;
1803 still_exists
= may_rescan(filer_window
, warning
);
1806 dir_update(filer_window
->directory
, filer_window
->sym_path
);
1808 return still_exists
;
1811 void filer_update_all(void)
1813 GList
*next
= all_filer_windows
;
1817 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1819 /* Updating directory may remove it from list -- stop sending
1820 * patches to move this line!
1824 /* Don't trigger a refresh if we're already scanning.
1825 * Otherwise, two views of a single directory will trigger
1828 if (filer_window
->directory
&&
1829 !filer_window
->directory
->scanning
)
1830 filer_update_dir(filer_window
, TRUE
);
1834 /* Refresh the various caches even if we don't think we need to */
1835 void full_refresh(void)
1838 reread_mime_files(); /* Refreshes all windows */
1841 /* See whether a filer window with a given path already exists
1842 * and is different from diff.
1844 static FilerWindow
*find_filer_window(const char *sym_path
, FilerWindow
*diff
)
1848 for (next
= all_filer_windows
; next
; next
= next
->next
)
1850 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1852 if (filer_window
!= diff
&&
1853 strcmp(sym_path
, filer_window
->sym_path
) == 0)
1854 return filer_window
;
1860 /* This path has been mounted/umounted/deleted some files - update all dirs */
1861 void filer_check_mounted(const char *real_path
)
1863 GList
*next
= all_filer_windows
;
1866 gboolean resize
= o_filer_auto_resize
.int_value
== RESIZE_ALWAYS
;
1868 /* DOS disks, etc, often don't change the mtime of the root directory
1869 * on modification, so force a refresh now.
1871 g_fscache_update(dir_cache
, real_path
);
1873 len
= strlen(real_path
);
1877 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1881 if (strncmp(real_path
, filer_window
->real_path
, len
) == 0)
1883 char s
= filer_window
->real_path
[len
];
1885 if (s
== '/' || s
== '\0')
1887 if (filer_update_dir(filer_window
, FALSE
) &&
1889 view_autosize(filer_window
->view
);
1894 parent
= g_path_get_dirname(real_path
);
1895 refresh_dirs(parent
);
1898 icons_may_update(real_path
);
1901 /* Close all windows displaying 'path' or subdirectories of 'path' */
1902 void filer_close_recursive(const char *path
)
1904 GList
*next
= all_filer_windows
;
1908 real
= pathdup(path
);
1913 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
1917 if (strncmp(real
, filer_window
->real_path
, len
) == 0)
1919 char s
= filer_window
->real_path
[len
];
1921 if (len
== 1 || s
== '/' || s
== '\0')
1922 gtk_widget_destroy(filer_window
->window
);
1927 /* Like minibuffer_show(), except that:
1928 * - It returns FALSE (to be used from an idle callback)
1929 * - It checks that the filer window still exists.
1931 static gboolean
minibuffer_show_cb(FilerWindow
*filer_window
)
1933 if (filer_exists(filer_window
))
1934 minibuffer_show(filer_window
, MINI_PATH
);
1938 /* TRUE iff filer_window points to an existing FilerWindow
1941 gboolean
filer_exists(FilerWindow
*filer_window
)
1945 for (next
= all_filer_windows
; next
; next
= next
->next
)
1947 FilerWindow
*fw
= (FilerWindow
*) next
->data
;
1949 if (fw
== filer_window
)
1956 FilerWindow
*filer_get_by_id(const char *id
)
1958 return g_hash_table_lookup(window_with_id
, id
);
1961 void filer_set_id(FilerWindow
*filer_window
, const char *id
)
1963 g_return_if_fail(filer_window
!= NULL
);
1965 if (filer_window
->window_id
)
1967 g_hash_table_remove(window_with_id
, filer_window
->window_id
);
1968 g_free(filer_window
->window_id
);
1969 filer_window
->window_id
= NULL
;
1974 filer_window
->window_id
= g_strdup(id
);
1975 g_hash_table_insert(window_with_id
,
1976 filer_window
->window_id
,
1981 /* Make sure the window title is up-to-date */
1982 void filer_set_title(FilerWindow
*filer_window
)
1984 gchar
*title
= NULL
;
1987 if (filer_window
->scanning
||
1988 filer_window
->filter
!= FILER_SHOW_ALL
||
1989 filer_window
->show_hidden
|| filer_window
->show_thumbs
)
1991 if (o_short_flag_names
.int_value
)
1993 const gchar
*hidden
= "";
1995 switch(filer_window
->filter
) {
1996 case FILER_SHOW_ALL
:
1997 hidden
=filer_window
->show_hidden
? _("A") : "";
1999 case FILER_SHOW_GLOB
: hidden
= _("G"); break;
2003 flags
= g_strconcat(" +",
2004 filer_window
->scanning
? _("S") : "",
2006 filer_window
->show_thumbs
? _("T") : "",
2011 gchar
*hidden
= NULL
;
2013 switch(filer_window
->filter
) {
2014 case FILER_SHOW_ALL
:
2015 hidden
= g_strdup(filer_window
->show_hidden
2018 case FILER_SHOW_GLOB
:
2019 hidden
= g_strdup_printf(_("Glob (%s), "),
2020 filer_window
->filter_string
);
2023 hidden
=g_strdup("");
2026 flags
= g_strconcat(" (",
2027 filer_window
->scanning
? _("Scanning, ") : "",
2029 filer_window
->show_thumbs
? _("Thumbs, ") : "",
2031 flags
[strlen(flags
) - 2] = ')';
2037 title
= g_strconcat("//", our_host_name(),
2038 filer_window
->sym_path
, flags
, NULL
);
2040 if (!title
&& home_dir_len
> 1 &&
2041 strncmp(filer_window
->sym_path
, home_dir
, home_dir_len
) == 0)
2043 guchar sep
= filer_window
->sym_path
[home_dir_len
];
2045 if (sep
== '\0' || sep
== '/')
2046 title
= g_strconcat("~",
2047 filer_window
->sym_path
+ home_dir_len
,
2053 title
= g_strconcat(filer_window
->sym_path
, flags
, NULL
);
2055 ensure_utf8(&title
);
2057 if (filer_window
->directory
->error
)
2060 title
= g_strconcat(old
, ": ", filer_window
->directory
->error
,
2065 gtk_window_set_title(GTK_WINDOW(filer_window
->window
), title
);
2069 if (flags
[0] != '\0')
2073 /* Reconnect to the same directory (used when the Show Hidden option is
2074 * toggled). This has the side-effect of updating the window title.
2076 void filer_detach_rescan(FilerWindow
*filer_window
)
2078 Directory
*dir
= filer_window
->directory
;
2081 detach(filer_window
);
2082 filer_window
->directory
= dir
;
2083 attach(filer_window
);
2086 /* Puts the filer window into target mode. When an item is chosen,
2087 * fn(filer_window, iter, data) is called. 'reason' will be displayed
2088 * on the toolbar while target mode is active.
2090 * Use fn == NULL to cancel target mode.
2092 void filer_target_mode(FilerWindow
*filer_window
,
2097 TargetFunc old_fn
= filer_window
->target_cb
;
2100 gdk_window_set_cursor(
2101 GTK_WIDGET(filer_window
->view
)->window
,
2102 fn
? crosshair
: NULL
);
2104 filer_window
->target_cb
= fn
;
2105 filer_window
->target_data
= data
;
2107 if (filer_window
->toolbar_text
== NULL
)
2112 GTK_LABEL(filer_window
->toolbar_text
), reason
);
2113 else if (o_toolbar_info
.int_value
)
2116 toolbar_update_info(filer_window
);
2119 gtk_label_set_text(GTK_LABEL(filer_window
->toolbar_text
), "");
2122 static void set_selection_state(FilerWindow
*filer_window
, gboolean normal
)
2124 GtkStateType old_state
= filer_window
->selection_state
;
2126 filer_window
->selection_state
= normal
2127 ? GTK_STATE_SELECTED
: GTK_STATE_INSENSITIVE
;
2129 if (old_state
!= filer_window
->selection_state
2130 && view_count_selected(filer_window
->view
))
2131 gtk_widget_queue_draw(GTK_WIDGET(filer_window
->view
));
2134 void filer_cancel_thumbnails(FilerWindow
*filer_window
)
2136 gtk_widget_hide(filer_window
->thumb_bar
);
2138 destroy_glist(&filer_window
->thumb_queue
);
2139 filer_window
->max_thumbs
= 0;
2142 /* Generate the next thumb for this window. The window object is
2143 * unref'd when there is nothing more to do.
2144 * If the window no longer has a filer window, nothing is done.
2146 static gboolean
filer_next_thumb_real(GObject
*window
)
2148 FilerWindow
*filer_window
;
2152 filer_window
= g_object_get_data(window
, "filer_window");
2156 g_object_unref(window
);
2160 if (!filer_window
->thumb_queue
)
2162 filer_cancel_thumbnails(filer_window
);
2163 g_object_unref(window
);
2167 total
= filer_window
->max_thumbs
;
2168 done
= total
- g_list_length(filer_window
->thumb_queue
);
2170 path
= (gchar
*) filer_window
->thumb_queue
->data
;
2172 pixmap_background_thumb(path
, (GFunc
) filer_next_thumb
, window
);
2174 filer_window
->thumb_queue
= g_list_remove(filer_window
->thumb_queue
,
2178 gtk_progress_bar_set_fraction(
2179 GTK_PROGRESS_BAR(filer_window
->thumb_progress
),
2180 done
/ (float) total
);
2185 /* path is the thumb just loaded, if any.
2186 * window is unref'd (eventually).
2188 static void filer_next_thumb(GObject
*window
, const gchar
*path
)
2191 dir_force_update_path(path
);
2193 g_idle_add((GSourceFunc
) filer_next_thumb_real
, window
);
2196 static void start_thumb_scanning(FilerWindow
*filer_window
)
2198 if (GTK_WIDGET_VISIBLE(filer_window
->thumb_bar
))
2199 return; /* Already scanning */
2201 gtk_widget_show_all(filer_window
->thumb_bar
);
2203 g_object_ref(G_OBJECT(filer_window
->window
));
2204 filer_next_thumb(G_OBJECT(filer_window
->window
), NULL
);
2207 /* Set this image to be loaded some time in the future */
2208 void filer_create_thumb(FilerWindow
*filer_window
, const gchar
*path
)
2210 if (g_list_find_custom(filer_window
->thumb_queue
, path
,
2211 (GCompareFunc
) strcmp
))
2214 if (!filer_window
->thumb_queue
)
2215 filer_window
->max_thumbs
=0;
2216 filer_window
->max_thumbs
++;
2218 filer_window
->thumb_queue
= g_list_append(filer_window
->thumb_queue
,
2221 if (filer_window
->scanning
)
2222 return; /* Will start when scan ends */
2224 start_thumb_scanning(filer_window
);
2227 /* If thumbnail display is on, look through all the items in this directory
2228 * and start creating or updating the thumbnails as needed.
2230 void filer_create_thumbs(FilerWindow
*filer_window
)
2235 if (!filer_window
->show_thumbs
)
2238 view_get_iter(filer_window
->view
, &iter
, 0);
2240 while ((item
= iter
.next(&iter
)))
2242 MaskedPixmap
*pixmap
;
2246 if (item
->base_type
!= TYPE_FILE
)
2249 /*if (strcmp(item->mime_type->media_type, "image") != 0)
2252 path
= make_path(filer_window
->real_path
, item
->leafname
);
2254 pixmap
= g_fscache_lookup_full(pixmap_cache
, path
,
2255 FSCACHE_LOOKUP_ONLY_NEW
, &found
);
2257 g_object_unref(pixmap
);
2259 /* If we didn't get an image, it could be because:
2261 * - We're loading the image now. found is TRUE,
2262 * and we'll update the item later.
2263 * - We tried to load the image and failed. found
2265 * - We haven't tried loading the image. found is
2266 * FALSE, and we start creating the thumb here.
2269 filer_create_thumb(filer_window
, path
);
2273 static void filer_options_changed(void)
2275 if (o_short_flag_names
.has_changed
)
2279 for (next
= all_filer_windows
; next
; next
= next
->next
)
2281 FilerWindow
*filer_window
= (FilerWindow
*) next
->data
;
2283 filer_set_title(filer_window
);
2288 /* Append interesting information to this GString */
2289 void filer_add_tip_details(FilerWindow
*filer_window
,
2290 GString
*tip
, DirItem
*item
)
2292 const guchar
*fullpath
= NULL
;
2294 fullpath
= make_path(filer_window
->real_path
, item
->leafname
);
2296 if (item
->flags
& ITEM_FLAG_SYMLINK
)
2300 target
= readlink_dup(fullpath
);
2303 ensure_utf8(&target
);
2305 g_string_append(tip
, _("Symbolic link to "));
2306 g_string_append(tip
, target
);
2307 g_string_append_c(tip
, '\n');
2312 if (item
->flags
& ITEM_FLAG_APPDIR
)
2317 info
= appinfo_get(fullpath
, item
);
2318 if (info
&& ((node
= xml_get_section(info
, NULL
, "Summary"))))
2321 str
= xmlNodeListGetString(node
->doc
,
2322 node
->xmlChildrenNode
, 1);
2325 g_string_append(tip
, str
);
2326 g_string_append_c(tip
, '\n');
2331 g_object_unref(info
);
2333 else if (item
->mime_type
== application_x_desktop
)
2336 summary
= tip_from_desktop_file(fullpath
);
2339 g_string_append(tip
, summary
);
2340 g_string_append_c(tip
, '\n');
2345 if (!g_utf8_validate(item
->leafname
, -1, NULL
))
2346 g_string_append(tip
,
2347 _("This filename is not valid UTF-8. "
2348 "You should rename it.\n"));
2351 /* Return the selection as a text/uri-list.
2352 * g_free() the result.
2354 static guchar
*filer_create_uri_list(FilerWindow
*filer_window
)
2362 g_return_val_if_fail(filer_window
!= NULL
, NULL
);
2364 string
= g_string_new(NULL
);
2366 leader
= g_string_new(filer_window
->sym_path
);
2367 if (leader
->str
[leader
->len
- 1] != '/')
2368 g_string_append_c(leader
, '/');
2370 view_get_iter(filer_window
->view
, &iter
, VIEW_ITER_SELECTED
);
2371 while ((item
= iter
.next(&iter
)))
2376 path
= g_strconcat(leader
->str
, item
->leafname
, NULL
);
2377 uri
= encode_path_as_uri(path
);
2378 g_string_append(string
, (char *) uri
);
2379 g_string_append(string
, "\r\n");
2384 g_string_free(leader
, TRUE
);
2385 retval
= string
->str
;
2386 g_string_free(string
, FALSE
);
2391 void filer_perform_action(FilerWindow
*filer_window
, GdkEventButton
*event
)
2394 ViewIface
*view
= filer_window
->view
;
2395 DirItem
*item
= NULL
;
2396 gboolean press
= event
->type
== GDK_BUTTON_PRESS
;
2398 OpenFlags flags
= 0;
2400 if (event
->button
> 3)
2403 view_get_iter_at_point(view
, &iter
, event
->window
, event
->x
, event
->y
);
2404 item
= iter
.peek(&iter
);
2406 if (item
&& view_cursor_visible(view
))
2407 view_cursor_to_iter(view
, &iter
);
2409 if (item
&& event
->button
== 1 &&
2410 view_get_selected(view
, &iter
) &&
2411 filer_window
->selection_state
== GTK_STATE_INSENSITIVE
)
2413 /* Possibly a really slow DnD operation? */
2414 filer_window
->temp_item_selected
= FALSE
;
2416 filer_selection_changed(filer_window
, event
->time
);
2420 if (filer_window
->target_cb
)
2422 dnd_motion_ungrab();
2423 if (item
&& press
&& event
->button
== 1)
2424 filer_window
->target_cb(filer_window
, &iter
,
2425 filer_window
->target_data
);
2427 filer_target_mode(filer_window
, NULL
, NULL
, NULL
);
2432 if (!o_single_click
.int_value
)
2434 /* Make sure both parts of a double-click fall on
2437 static guchar
*first_click
= NULL
;
2438 static guchar
*second_click
= NULL
;
2440 if (event
->type
== GDK_BUTTON_PRESS
)
2442 g_free(first_click
);
2443 first_click
= second_click
;
2446 second_click
= g_strdup(item
->leafname
);
2448 second_click
= NULL
;
2451 if (event
->type
== GDK_2BUTTON_PRESS
)
2453 if (first_click
&& second_click
&&
2454 strcmp(first_click
, second_click
) != 0)
2456 if ((first_click
|| second_click
) &&
2457 !(first_click
&& second_click
))
2462 action
= bind_lookup_bev(
2463 item
? BIND_DIRECTORY_ICON
: BIND_DIRECTORY
,
2468 case ACT_CLEAR_SELECTION
:
2469 view_clear_selection(view
);
2471 case ACT_TOGGLE_SELECTED
:
2472 view_set_selected(view
, &iter
,
2473 !view_get_selected(view
, &iter
));
2475 case ACT_SELECT_EXCL
:
2476 view_select_only(view
, &iter
);
2479 flags
|= OPEN_SHIFT
;
2482 if (event
->button
!= 1 || event
->state
& GDK_MOD1_MASK
)
2483 flags
|= OPEN_CLOSE_WINDOW
;
2485 flags
|= OPEN_SAME_WINDOW
;
2486 if (o_new_button_1
.int_value
)
2487 flags
^= OPEN_SAME_WINDOW
;
2488 if (event
->type
== GDK_2BUTTON_PRESS
)
2489 view_set_selected(view
, &iter
, FALSE
);
2490 dnd_motion_ungrab();
2492 filer_openitem(filer_window
, &iter
, flags
);
2494 case ACT_POPUP_MENU
:
2495 dnd_motion_ungrab();
2497 show_filer_menu(filer_window
,
2498 (GdkEvent
*) event
, &iter
);
2500 case ACT_PRIME_AND_SELECT
:
2501 if (item
&& !view_get_selected(view
, &iter
))
2502 view_select_only(view
, &iter
);
2503 dnd_motion_start(MOTION_READY_FOR_DND
);
2505 case ACT_PRIME_AND_TOGGLE
:
2506 view_set_selected(view
, &iter
,
2507 !view_get_selected(view
, &iter
));
2508 dnd_motion_start(MOTION_READY_FOR_DND
);
2510 case ACT_PRIME_FOR_DND
:
2511 dnd_motion_start(MOTION_READY_FOR_DND
);
2514 if (press
&& event
->button
< 4)
2517 view_wink_item(view
, &iter
);
2518 dnd_motion_start(MOTION_NONE
);
2521 case ACT_LASSO_CLEAR
:
2522 view_clear_selection(view
);
2524 case ACT_LASSO_MODIFY
:
2525 view_start_lasso_box(view
, event
);
2528 view_autosize(filer_window
->view
);
2531 g_warning("Unsupported action : %d\n", action
);
2536 /* It's time to make the tooltip appear. If we're not over the item any
2537 * more, or the item doesn't need a tooltip, do nothing.
2539 static gboolean
tooltip_activate(GtkWidget
*window
)
2541 FilerWindow
*filer_window
;
2545 DirItem
*item
= NULL
;
2546 GString
*tip
= NULL
;
2548 g_return_val_if_fail(tip_item
!= NULL
, 0);
2550 filer_window
= g_object_get_data(G_OBJECT(window
), "filer_window");
2552 if (!motion_window
|| !filer_window
)
2553 return FALSE
; /* Window has been destroyed */
2555 view
= filer_window
->view
;
2559 gdk_window_get_pointer(motion_window
, &x
, &y
, NULL
);
2560 view_get_iter_at_point(view
, &iter
, motion_window
, x
, y
);
2562 item
= iter
.peek(&iter
);
2563 if (item
!= tip_item
)
2564 return FALSE
; /* Not still under the pointer */
2566 /* OK, the filer window still exists and the pointer is still
2567 * over the same item. Do we need to show a tip?
2570 tip
= g_string_new(NULL
);
2572 view_extend_tip(filer_window
->view
, &iter
, tip
);
2574 filer_add_tip_details(filer_window
, tip
, tip_item
);
2578 g_string_truncate(tip
, tip
->len
- 1);
2580 tooltip_show(tip
->str
);
2583 g_string_free(tip
, TRUE
);
2588 /* Motion detected on the View widget */
2589 gint
filer_motion_notify(FilerWindow
*filer_window
, GdkEventMotion
*event
)
2591 ViewIface
*view
= filer_window
->view
;
2595 view_get_iter_at_point(view
, &iter
, event
->window
, event
->x
, event
->y
);
2596 item
= iter
.peek(&iter
);
2600 if (item
!= tip_item
)
2605 motion_window
= event
->window
;
2606 tooltip_prime((GtkFunction
) tooltip_activate
,
2607 G_OBJECT(filer_window
->window
));
2616 if (motion_state
!= MOTION_READY_FOR_DND
)
2619 if (!dnd_motion_moved(event
))
2622 view_get_iter_at_point(view
, &iter
,
2624 event
->x
- (event
->x_root
- drag_start_x
),
2625 event
->y
- (event
->y_root
- drag_start_y
));
2626 item
= iter
.peek(&iter
);
2630 view_wink_item(view
, NULL
);
2632 if (!view_get_selected(view
, &iter
))
2634 if (event
->state
& GDK_BUTTON1_MASK
)
2636 /* Select just this one */
2637 filer_window
->temp_item_selected
= TRUE
;
2638 view_select_only(view
, &iter
);
2642 if (view_count_selected(view
) == 0)
2643 filer_window
->temp_item_selected
= TRUE
;
2644 view_set_selected(view
, &iter
, TRUE
);
2648 g_return_val_if_fail(view_count_selected(view
) > 0, TRUE
);
2650 if (view_count_selected(view
) == 1)
2652 if (item
->base_type
== TYPE_UNKNOWN
)
2653 item
= dir_update_item(filer_window
->directory
,
2658 report_error(_("Item no longer exists!"));
2662 drag_one_item(GTK_WIDGET(view
), event
,
2663 make_path(filer_window
->sym_path
, item
->leafname
),
2664 item
, di_image(item
));
2666 /* XXX: Use thumbnail */
2667 item
, view
? view
->image
: NULL
);
2674 uris
= filer_create_uri_list(filer_window
);
2675 drag_selection(GTK_WIDGET(view
), event
, uris
);
2682 static void drag_end(GtkWidget
*widget
, GdkDragContext
*context
,
2683 FilerWindow
*filer_window
)
2685 filer_set_autoscroll(filer_window
, FALSE
);
2687 if (filer_window
->temp_item_selected
)
2689 view_clear_selection(filer_window
->view
);
2690 filer_window
->temp_item_selected
= FALSE
;
2694 /* Remove highlights */
2695 static void drag_leave(GtkWidget
*widget
,
2696 GdkDragContext
*context
,
2698 FilerWindow
*filer_window
)
2703 /* Called during the drag when the mouse is in a widget registered
2704 * as a drop target. Returns TRUE if we can accept the drop.
2706 static gboolean
drag_motion(GtkWidget
*widget
,
2707 GdkDragContext
*context
,
2711 FilerWindow
*filer_window
)
2714 ViewIface
*view
= filer_window
->view
;
2716 GdkDragAction action
= context
->suggested_action
;
2717 const guchar
*new_path
= NULL
;
2718 const char *type
= NULL
;
2719 gboolean retval
= FALSE
;
2720 gboolean same_window
;
2722 if ((context
->actions
& GDK_ACTION_ASK
) && o_dnd_left_menu
.int_value
)
2725 gdk_window_get_pointer(NULL
, NULL
, NULL
, &state
);
2726 if (state
& GDK_BUTTON1_MASK
)
2727 action
= GDK_ACTION_ASK
;
2730 same_window
= gtk_drag_get_source_widget(context
) == widget
;
2732 filer_set_autoscroll(filer_window
, TRUE
);
2734 if (filer_window
->view_type
== VIEW_TYPE_DETAILS
)
2738 /* Correct for position of bin window */
2739 bin
= gtk_tree_view_get_bin_window(GTK_TREE_VIEW(view
));
2740 gdk_window_get_position(bin
, NULL
, &bin_y
);
2744 if (o_dnd_drag_to_icons
.int_value
)
2746 view_get_iter_at_point(view
, &iter
, widget
->window
, x
, y
);
2747 item
= iter
.peek(&iter
);
2752 if (item
&& same_window
&& view_get_selected(view
, &iter
))
2755 type
= dnd_motion_item(context
, &item
);
2760 /* Don't allow drops to non-writeable directories. BUT, still
2761 * allow drops on non-writeable SUBdirectories so that we can
2762 * do the spring-open thing.
2764 if (item
&& type
== drop_dest_dir
&&
2765 !(item
->flags
& ITEM_FLAG_APPDIR
))
2767 dnd_spring_load(context
, filer_window
);
2773 view_cursor_to_iter(view
, &iter
);
2776 view_cursor_to_iter(view
, NULL
);
2778 /* Disallow background drops within a single window */
2779 if (type
&& same_window
)
2786 new_path
= make_path(filer_window
->sym_path
,
2789 new_path
= filer_window
->sym_path
;
2792 /* Don't ask about dragging to an application! */
2793 if (type
== drop_dest_prog
&& action
== GDK_ACTION_ASK
)
2794 action
= GDK_ACTION_COPY
;
2796 g_dataset_set_data(context
, "drop_dest_type", (gpointer
) type
);
2799 gdk_drag_status(context
, action
, time
);
2800 g_dataset_set_data_full(context
, "drop_dest_path",
2801 g_strdup(new_path
), g_free
);
2808 static gboolean
as_timeout(FilerWindow
*filer_window
)
2812 retval
= view_auto_scroll_callback(filer_window
->view
);
2815 filer_window
->auto_scroll
= -1;
2820 /* When autoscroll is on, a timer keeps track of the pointer position.
2821 * While it's near the top or bottom of the window, the window scrolls.
2823 * If the mouse buttons are released, the pointer leaves the window, or
2824 * a drag-and-drop operation finishes, auto_scroll is turned off.
2826 void filer_set_autoscroll(FilerWindow
*filer_window
, gboolean auto_scroll
)
2828 g_return_if_fail(filer_window
!= NULL
);
2832 if (filer_window
->auto_scroll
!= -1)
2833 return; /* Already on! */
2835 filer_window
->auto_scroll
= g_timeout_add(50,
2836 (GSourceFunc
) as_timeout
,
2841 if (filer_window
->auto_scroll
== -1)
2842 return; /* Already off! */
2844 g_source_remove(filer_window
->auto_scroll
);
2845 filer_window
->auto_scroll
= -1;
2849 #define ZERO_MNT "/uri/0install"
2851 static void refresh_done(FilerWindow
*filer_window
)
2853 if (filer_exists(filer_window
))
2854 filer_update_dir(filer_window
, TRUE
);
2857 void filer_refresh(FilerWindow
*filer_window
)
2859 if (!strncmp(ZERO_MNT
"/", filer_window
->real_path
, sizeof(ZERO_MNT
)))
2861 /* Try to run 0refresh */
2863 gchar
*argv
[] = {"0refresh", NULL
, NULL
};
2864 const char *host
= filer_window
->real_path
+ sizeof(ZERO_MNT
);
2867 slash
= strchr(host
, '/');
2869 argv
[1] = g_strndup(host
, slash
- host
);
2871 argv
[1] = g_strdup(host
);
2872 pid
= rox_spawn(filer_window
->real_path
, (const char **) argv
);
2875 on_child_death(pid
, (CallbackFn
) refresh_done
,
2882 gboolean
filer_match_filter(FilerWindow
*filer_window
, const gchar
*filename
)
2884 g_return_val_if_fail(filename
!= NULL
, FALSE
);
2886 if(filename
[0]=='.' &&
2887 (!filer_window
->temp_show_hidden
&& !filer_window
->show_hidden
))
2890 switch(filer_window
->filter
) {
2891 case FILER_SHOW_GLOB
:
2892 return fnmatch(filer_window
->filter_string
,
2895 case FILER_SHOW_ALL
:
2902 /* Provided to hide the implementation */
2903 void filer_set_hidden(FilerWindow
*filer_window
, gboolean hidden
)
2905 filer_window
->show_hidden
=hidden
;
2908 /* Set the filter type. Returns TRUE if the type has changed
2909 * (must call filer_detach_rescan).
2911 gboolean
filer_set_filter(FilerWindow
*filer_window
, FilterType type
,
2912 const gchar
*filter_string
)
2914 /* Is this new filter the same as the old one? */
2915 if (filer_window
->filter
== type
)
2917 switch(filer_window
->filter
)
2919 case FILER_SHOW_ALL
:
2921 case FILER_SHOW_GLOB
:
2922 if (strcmp(filer_window
->filter_string
,
2923 filter_string
) == 0)
2929 /* Clean up old filter */
2930 if (filer_window
->filter_string
)
2932 g_free(filer_window
->filter_string
);
2933 filer_window
->filter_string
= NULL
;
2935 /* Also clean up compiled regexp when implemented */
2937 filer_window
->filter
= type
;
2941 case FILER_SHOW_ALL
:
2945 case FILER_SHOW_GLOB
:
2946 filer_window
->filter_string
= g_strdup(filter_string
);
2951 filer_window
->filter
= FILER_SHOW_ALL
;
2952 g_warning("Impossible: filter type %d", type
);
2960 static Settings
*settings_new(const char *path
)
2964 set
=g_new(Settings
, 1);
2965 memset(set
, 0, sizeof(Settings
));
2967 set
->path
=g_strdup(path
);
2972 static void settings_free(Settings
*set
)
2976 g_free(set
->filter
);
2980 static void store_settings(Settings
*set
)
2984 old
=g_hash_table_lookup(settings_table
, set
->path
);
2987 g_hash_table_remove(settings_table
, set
->path
);
2991 g_hash_table_insert(settings_table
, set
->path
, set
);
2994 /* TODO: use symbolic names in the XML file where possible */
2995 static void load_from_node(Settings
*set
, xmlDocPtr doc
, xmlNodePtr node
)
2999 str
=xmlNodeListGetString(doc
, node
->xmlChildrenNode
, 1);
3001 if(strcmp(node
->name
, "X") == 0) {
3003 set
->flags
|=SET_POSITION
;
3004 } else if(strcmp(node
->name
, "Y") == 0) {
3006 set
->flags
|=SET_POSITION
;
3007 } else if(strcmp(node
->name
, "Width") == 0) {
3008 set
->width
=atoi(str
);
3009 set
->flags
|=SET_SIZE
;
3010 } else if(strcmp(node
->name
, "Height") == 0) {
3011 set
->height
=atoi(str
);
3012 set
->flags
|=SET_SIZE
;
3013 } else if(strcmp(node
->name
, "ShowHidden") == 0) {
3014 set
->show_hidden
=atoi(str
);
3015 set
->flags
|=SET_HIDDEN
;
3016 } else if(strcmp(node
->name
, "ViewType") == 0) {
3017 set
->view_type
=atoi(str
);
3018 set
->flags
|=SET_DETAILS
;
3019 } else if(strcmp(node
->name
, "DetailsType") == 0) {
3020 set
->details_type
=atoi(str
);
3021 set
->flags
|=SET_DETAILS
;
3022 } else if(strcmp(node
->name
, "SortType") == 0) {
3023 set
->sort_type
=atoi(str
);
3024 set
->flags
|=SET_SORT
;
3025 } else if(strcmp(node
->name
, "SortOrder") == 0) {
3026 set
->sort_order
=atoi(str
);
3027 set
->flags
|=SET_SORT
;
3028 } else if(strcmp(node
->name
, "DisplayStyle") == 0) {
3029 set
->display_style
=atoi(str
);
3030 set
->flags
|=SET_STYLE
;
3031 } else if(strcmp(node
->name
, "ShowThumbs") == 0) {
3032 set
->show_thumbs
=atoi(str
);
3033 set
->flags
|=SET_THUMBS
;
3034 } else if(strcmp(node
->name
, "FilterType") == 0) {
3035 set
->filter_type
=atoi(str
);
3036 set
->flags
|=SET_FILTER
;
3037 } else if(strcmp(node
->name
, "Filter") == 0) {
3038 set
->filter
=g_strdup(str
);
3039 set
->flags
|=SET_FILTER
;
3046 static void load_settings(void)
3049 XMLwrapper
*settings_doc
=NULL
;
3051 path
=choices_find_xdg_path_load("Settings.xml", PROJECT
, SITE
);
3053 settings_doc
=xml_new(path
);
3058 settings_table
=g_hash_table_new(g_str_hash
, g_str_equal
);
3061 xmlNodePtr node
, subnode
;
3063 node
= xmlDocGetRootElement(settings_doc
->doc
);
3065 for (node
= node
->xmlChildrenNode
; node
; node
= node
->next
)
3070 if (node
->type
!= XML_ELEMENT_NODE
)
3072 if (strcmp(node
->name
, "FilerWindow") != 0)
3075 path
=xmlGetProp(node
, "path");
3076 set
=settings_new(path
);
3079 for (subnode
=node
->xmlChildrenNode
; subnode
;
3080 subnode
=subnode
->next
) {
3082 if (subnode
->type
!= XML_ELEMENT_NODE
)
3084 load_from_node(set
, settings_doc
->doc
,
3088 store_settings(set
);
3090 g_object_unref(settings_doc
);
3094 static void add_nodes(gpointer key
, gpointer value
, gpointer data
)
3096 xmlNodePtr node
=(xmlNodePtr
) data
;
3098 Settings
*set
=(Settings
*) value
;
3101 sub
=xmlNewChild(node
, NULL
, "FilerWindow", NULL
);
3103 xmlSetProp(sub
, "path", set
->path
);
3105 if(set
->flags
& SET_POSITION
) {
3106 tmp
=g_strdup_printf("%d", set
->x
);
3107 xmlNewChild(sub
, NULL
, "X", tmp
);
3109 tmp
=g_strdup_printf("%d", set
->y
);
3110 xmlNewChild(sub
, NULL
, "Y", tmp
);
3113 if(set
->flags
& SET_SIZE
) {
3114 tmp
=g_strdup_printf("%d", set
->width
);
3115 xmlNewChild(sub
, NULL
, "Width", tmp
);
3117 tmp
=g_strdup_printf("%d", set
->height
);
3118 xmlNewChild(sub
, NULL
, "Height", tmp
);
3121 if(set
->flags
& SET_HIDDEN
) {
3122 tmp
=g_strdup_printf("%d", set
->show_hidden
);
3123 xmlNewChild(sub
, NULL
, "ShowHidden", tmp
);
3126 if(set
->flags
& SET_STYLE
) {
3127 tmp
=g_strdup_printf("%d", set
->display_style
);
3128 xmlNewChild(sub
, NULL
, "DisplayStyle", tmp
);
3131 if(set
->flags
& SET_SORT
) {
3132 tmp
=g_strdup_printf("%d", set
->sort_type
);
3133 xmlNewChild(sub
, NULL
, "SortType", tmp
);
3135 tmp
=g_strdup_printf("%d", set
->sort_order
);
3136 xmlNewChild(sub
, NULL
, "SortOrder", tmp
);
3139 if(set
->flags
& SET_DETAILS
) {
3140 tmp
=g_strdup_printf("%d", set
->view_type
);
3141 xmlNewChild(sub
, NULL
, "ViewType", tmp
);
3143 tmp
=g_strdup_printf("%d", set
->details_type
);
3144 xmlNewChild(sub
, NULL
, "DetailsType", tmp
);
3147 if(set
->flags
& SET_STYLE
) {
3148 tmp
=g_strdup_printf("%d", set
->show_thumbs
);
3149 xmlNewChild(sub
, NULL
, "ShowThumbs", tmp
);
3152 if(set
->flags
& SET_FILTER
) {
3153 tmp
=g_strdup_printf("%d", set
->filter_type
);
3154 xmlNewChild(sub
, NULL
, "FilterType", tmp
);
3156 if(set
->filter
&& set
->filter
[0])
3157 xmlNewChild(sub
, NULL
, "Filter", set
->filter
);
3161 static void save_settings(void)
3165 path
=choices_find_xdg_path_save("Settings.xml", PROJECT
, SITE
, TRUE
);
3167 xmlDocPtr doc
= xmlNewDoc("1.0");
3168 xmlDocSetRootElement(doc
, xmlNewDocNode(doc
, NULL
,
3171 g_hash_table_foreach(settings_table
, add_nodes
,
3172 xmlDocGetRootElement(doc
));
3174 save_xml_file(doc
, path
);
3182 static void check_settings(FilerWindow
*filer_window
)
3186 set
=(Settings
*) g_hash_table_lookup(settings_table
,
3187 filer_window
->sym_path
);
3190 if(set
->flags
& SET_POSITION
)
3191 gtk_window_move(GTK_WINDOW(filer_window
->window
),
3193 if(set
->flags
& SET_SIZE
)
3194 filer_window_set_size(filer_window
, set
->width
,
3196 if(set
->flags
& SET_HIDDEN
)
3197 filer_set_hidden(filer_window
, set
->show_hidden
);
3199 if(set
->flags
& (SET_STYLE
|SET_DETAILS
)) {
3200 DisplayStyle style
=filer_window
->display_style
;
3201 DetailsType details
=filer_window
->details_type
;
3203 if(set
->flags
& SET_STYLE
)
3204 style
=set
->display_style
;
3206 if(set
->flags
& SET_DETAILS
) {
3207 details
=set
->details_type
;
3209 filer_set_view_type(filer_window
,
3213 display_set_layout(filer_window
, style
,
3217 if(set
->flags
& SET_SORT
)
3218 display_set_sort_type(filer_window
,
3222 if(set
->flags
& SET_THUMBS
)
3223 display_set_thumbs(filer_window
,
3226 if(set
->flags
& SET_FILTER
)
3227 display_set_filter(filer_window
,
3234 typedef struct settings_window
{
3237 GtkWidget
*pos
, *size
, *hidden
, *style
, *sort
, *details
,
3244 static void settings_response(GtkWidget
*window
, gint response
,
3245 SettingsWindow
*set_win
)
3247 if(response
==GTK_RESPONSE_OK
) {
3250 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->pos
)))
3251 flags
|=SET_POSITION
;
3252 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->size
)))
3254 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->hidden
)))
3256 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->style
)))
3258 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->sort
)))
3260 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->details
)))
3262 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->thumbs
)))
3264 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(set_win
->filter
)))
3267 set_win
->set
->flags
=flags
;
3268 store_settings(set_win
->set
);
3272 gtk_widget_destroy(window
);
3275 void filer_save_settings(FilerWindow
*fwin
)
3277 SettingsWindow
*set_win
;
3282 Settings
*set
=settings_new(fwin
->sym_path
);
3284 gtk_window_get_position(GTK_WINDOW(fwin
->window
),&x
, &y
);
3285 set
->flags
|=SET_POSITION
;
3289 gtk_window_get_size(GTK_WINDOW(fwin
->window
),&x
, &y
);
3290 set
->flags
|=SET_SIZE
;
3294 set
->flags
|=SET_HIDDEN
;
3295 set
->show_hidden
=fwin
->show_hidden
;
3297 set
->flags
|=SET_STYLE
;
3298 set
->display_style
=fwin
->display_style
;
3300 set
->flags
|=SET_SORT
;
3301 set
->sort_type
=fwin
->sort_type
;
3302 set
->sort_order
=fwin
->sort_order
;
3304 set
->flags
|=SET_DETAILS
;
3305 set
->view_type
=fwin
->view_type
;
3306 set
->details_type
=fwin
->details_type
;
3308 set
->flags
|=SET_THUMBS
;
3309 set
->show_thumbs
=fwin
->show_thumbs
;
3311 set
->flags
|=SET_FILTER
;
3312 set
->filter_type
=fwin
->filter
;
3313 if(fwin
->filter_string
)
3314 set
->filter
=g_strdup(fwin
->filter_string
);
3316 /* Store other parameters
3318 set_win
=g_new(SettingsWindow
, 1);
3320 set_win
->window
=gtk_dialog_new();
3321 number_of_windows
++;
3323 gtk_dialog_add_button(GTK_DIALOG(set_win
->window
),
3324 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
);
3325 gtk_dialog_add_button(GTK_DIALOG(set_win
->window
),
3326 GTK_STOCK_OK
, GTK_RESPONSE_OK
);
3328 g_signal_connect(set_win
->window
, "destroy",
3329 G_CALLBACK(one_less_window
), NULL
);
3330 g_signal_connect_swapped(set_win
->window
, "destroy",
3331 G_CALLBACK(g_free
), set_win
);
3333 gtk_window_set_title(GTK_WINDOW(set_win
->window
),
3334 _("Select display properties to save"));
3336 vbox
=GTK_DIALOG(set_win
->window
)->vbox
;
3338 path
=gtk_label_new(set
->path
);
3339 gtk_box_pack_start(GTK_BOX(vbox
), path
, FALSE
, FALSE
, 2);
3341 set_win
->pos
=gtk_check_button_new_with_label(_("Position"));
3342 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->pos
, FALSE
, FALSE
, 2);
3343 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->pos
),
3344 set
->flags
& SET_POSITION
);
3346 set_win
->size
=gtk_check_button_new_with_label(_("Size"));
3347 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->size
, FALSE
, FALSE
, 2);
3348 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->size
),
3349 set
->flags
& SET_SIZE
);
3351 set_win
->hidden
=gtk_check_button_new_with_label(_("Show hidden"));
3352 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->hidden
,
3354 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->hidden
),
3355 set
->flags
& SET_HIDDEN
);
3357 set_win
->style
=gtk_check_button_new_with_label(_("Display style"));
3358 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->style
,
3360 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->style
),
3361 set
->flags
& SET_STYLE
);
3363 set_win
->sort
=gtk_check_button_new_with_label(_("Sort type and order"));
3364 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->sort
, FALSE
, FALSE
, 2);
3365 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->sort
),
3366 set
->flags
& SET_SORT
);
3368 set_win
->details
=gtk_check_button_new_with_label(_("Details"));
3369 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->details
, FALSE
, FALSE
, 2);
3370 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->details
),
3371 set
->flags
& SET_DETAILS
);
3373 set_win
->thumbs
=gtk_check_button_new_with_label(_("Thumbnails"));
3374 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->thumbs
,
3376 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->thumbs
),
3377 set
->flags
& SET_THUMBS
);
3379 set_win
->filter
=gtk_check_button_new_with_label(_("Filter"));
3380 gtk_box_pack_start(GTK_BOX(vbox
), set_win
->filter
,
3382 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_win
->filter
),
3383 set
->flags
& SET_FILTER
);
3386 g_signal_connect(set_win
->window
, "response",
3387 G_CALLBACK(settings_response
), set_win
);
3389 gtk_widget_show_all(set_win
->window
);
3392 static char *tip_from_desktop_file(const char *full_path
)
3394 GError
*error
= NULL
;
3395 char *comment
= NULL
;
3397 comment
= get_value_from_desktop_file(full_path
,
3398 "Desktop Entry", "Comment", &error
);
3401 delayed_error("Failed to parse .desktop file '%s':\n%s",
3402 full_path
, error
->message
);
3408 g_error_free(error
);