1
/* vi:set et ai sw=2 sts=2 ts=2: */
3
* Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
4
* Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org>
6
* This program is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU General Public License as
8
* published by the Free Software Foundation; either version 2 of
9
* the License, or (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public
17
* License along with this program; if not, write to the Free
18
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19
* Boston, MA 02110-1301, USA.
26
#include <gdk/gdkkeysyms.h>
28
#include <thunar/thunar-application.h>
29
#include <thunar/thunar-clipboard-manager.h>
30
#include <thunar/thunar-create-dialog.h>
31
#include <thunar/thunar-dialogs.h>
32
#include <thunar/thunar-dnd.h>
33
#include <thunar/thunar-gio-extensions.h>
34
#include <thunar/thunar-gtk-extensions.h>
35
#include <thunar/thunar-job.h>
36
#include <thunar/thunar-marshal.h>
37
#include <thunar/thunar-preferences.h>
38
#include <thunar/thunar-private.h>
39
#include <thunar/thunar-stock.h>
40
#include <thunar/thunar-properties-dialog.h>
41
#include <thunar/thunar-shortcuts-icon-renderer.h>
42
#include <thunar/thunar-simple-job.h>
43
#include <thunar/thunar-tree-model.h>
44
#include <thunar/thunar-tree-view.h>
45
#include <thunar/thunar-device.h>
49
/* the timeout (in ms) until the drag dest row will be expanded */
50
#define THUNAR_TREE_VIEW_EXPAND_TIMEOUT (750)
54
typedef struct _ThunarTreeViewMountData ThunarTreeViewMountData;
58
/* Property identifiers */
62
PROP_CURRENT_DIRECTORY,
66
/* Signal identifiers */
69
DELETE_SELECTED_FILES,
73
/* Identifiers for DnD target types */
81
static void thunar_tree_view_navigator_init (ThunarNavigatorIface *iface);
82
static void thunar_tree_view_finalize (GObject *object);
83
static void thunar_tree_view_get_property (GObject *object,
87
static void thunar_tree_view_set_property (GObject *object,
91
static ThunarFile *thunar_tree_view_get_current_directory (ThunarNavigator *navigator);
92
static void thunar_tree_view_set_current_directory (ThunarNavigator *navigator,
93
ThunarFile *current_directory);
94
static void thunar_tree_view_realize (GtkWidget *widget);
95
static void thunar_tree_view_unrealize (GtkWidget *widget);
96
static gboolean thunar_tree_view_button_press_event (GtkWidget *widget,
97
GdkEventButton *event);
98
static gboolean thunar_tree_view_button_release_event (GtkWidget *widget,
99
GdkEventButton *event);
100
static void thunar_tree_view_drag_data_received (GtkWidget *widget,
101
GdkDragContext *context,
104
GtkSelectionData *selection_data,
107
static gboolean thunar_tree_view_drag_drop (GtkWidget *widget,
108
GdkDragContext *context,
112
static gboolean thunar_tree_view_drag_motion (GtkWidget *widget,
113
GdkDragContext *context,
117
static void thunar_tree_view_drag_leave (GtkWidget *widget,
118
GdkDragContext *context,
120
static gboolean thunar_tree_view_popup_menu (GtkWidget *widget);
121
static void thunar_tree_view_row_activated (GtkTreeView *tree_view,
123
GtkTreeViewColumn *column);
124
static gboolean thunar_tree_view_test_expand_row (GtkTreeView *tree_view,
127
static void thunar_tree_view_row_collapsed (GtkTreeView *tree_view,
130
static gboolean thunar_tree_view_delete_selected_files (ThunarTreeView *view);
131
static void thunar_tree_view_context_menu (ThunarTreeView *view,
132
GdkEventButton *event,
135
static GdkDragAction thunar_tree_view_get_dest_actions (ThunarTreeView *view,
136
GdkDragContext *context,
140
ThunarFile **file_return);
141
static gboolean thunar_tree_view_find_closest_ancestor (ThunarTreeView *view,
143
GtkTreePath **ancestor_return,
144
gboolean *exact_return);
145
static ThunarFile *thunar_tree_view_get_selected_file (ThunarTreeView *view);
146
static ThunarDevice *thunar_tree_view_get_selected_device (ThunarTreeView *view);
147
static void thunar_tree_view_action_copy (ThunarTreeView *view);
148
static void thunar_tree_view_action_create_folder (ThunarTreeView *view);
149
static void thunar_tree_view_action_cut (ThunarTreeView *view);
150
static void thunar_tree_view_action_move_to_trash (ThunarTreeView *view);
151
static void thunar_tree_view_action_delete (ThunarTreeView *view);
152
static void thunar_tree_view_action_rename (ThunarTreeView *view);
153
static void thunar_tree_view_action_eject (ThunarTreeView *view);
154
static void thunar_tree_view_action_unmount (ThunarTreeView *view);
155
static void thunar_tree_view_action_empty_trash (ThunarTreeView *view);
156
static void thunar_tree_view_action_mount (ThunarTreeView *view);
157
static void thunar_tree_view_mount_finish (ThunarDevice *device,
160
static void thunar_tree_view_mount (ThunarTreeView *view,
161
gboolean open_after_mounting,
163
static void thunar_tree_view_action_open (ThunarTreeView *view);
164
static void thunar_tree_view_open_selection (ThunarTreeView *view);
165
static void thunar_tree_view_action_open_in_new_window (ThunarTreeView *view);
166
static void thunar_tree_view_action_open_in_new_tab (ThunarTreeView *view);
167
static void thunar_tree_view_open_selection_in_new_window (ThunarTreeView *view);
168
static void thunar_tree_view_open_selection_in_new_tab (ThunarTreeView *view);
169
static void thunar_tree_view_action_paste_into_folder (ThunarTreeView *view);
170
static void thunar_tree_view_action_properties (ThunarTreeView *view);
171
static GClosure *thunar_tree_view_new_files_closure (ThunarTreeView *view);
172
static void thunar_tree_view_new_files (ThunarJob *job,
174
ThunarTreeView *view);
175
static gboolean thunar_tree_view_visible_func (ThunarTreeModel *model,
178
static gboolean thunar_tree_view_selection_func (GtkTreeSelection *selection,
181
gboolean path_currently_selected,
183
static gboolean thunar_tree_view_cursor_idle (gpointer user_data);
184
static void thunar_tree_view_cursor_idle_destroy (gpointer user_data);
185
static gboolean thunar_tree_view_drag_scroll_timer (gpointer user_data);
186
static void thunar_tree_view_drag_scroll_timer_destroy (gpointer user_data);
187
static gboolean thunar_tree_view_expand_timer (gpointer user_data);
188
static void thunar_tree_view_expand_timer_destroy (gpointer user_data);
189
static ThunarTreeViewMountData *thunar_tree_view_mount_data_new (ThunarTreeView *view,
191
gboolean open_after_mounting,
193
static void thunar_tree_view_mount_data_free (ThunarTreeViewMountData *data);
194
static gboolean thunar_tree_view_get_show_hidden (ThunarTreeView *view);
195
static void thunar_tree_view_set_show_hidden (ThunarTreeView *view,
196
gboolean show_hidden);
200
struct _ThunarTreeViewClass
202
GtkTreeViewClass __parent__;
205
gboolean (*delete_selected_files) (ThunarTreeView *view);
208
struct _ThunarTreeView
210
GtkTreeView __parent__;
211
ThunarClipboardManager *clipboard;
212
ThunarPreferences *preferences;
213
GtkCellRenderer *icon_renderer;
214
ThunarFile *current_directory;
215
ThunarTreeModel *model;
217
ThunarxProviderFactory *provider_factory;
219
/* whether to display hidden/backup files */
220
guint show_hidden : 1;
222
/* drop site support */
223
guint drop_data_ready : 1; /* whether the drop data was received already */
224
guint drop_occurred : 1;
225
GList *drop_file_list; /* the list of URIs that are contained in the drop data */
227
/* the "new-files" closure, which is used to
228
* open newly created directories once done.
230
GClosure *new_files_closure;
232
/* the currently pressed mouse button, set in the
233
* button-press-event handler if the associated
234
* button-release-event should activate.
238
/* id of the signal used to queue a resize on the
239
* column whenever the shortcuts icon size is changed.
241
gulong queue_resize_signal_id;
243
/* set cursor to current directory idle source */
244
guint cursor_idle_id;
246
/* autoscroll during drag timer source */
247
guint drag_scroll_timer_id;
249
/* expand drag dest row timer source */
250
guint expand_timer_id;
260
struct _ThunarTreeViewMountData
262
ThunarTreeView *view;
264
gboolean open_after_mounting;
270
/* Target types for dropping into the tree view */
271
static const GtkTargetEntry drop_targets[] = {
272
{ "text/uri-list", 0, TARGET_TEXT_URI_LIST },
277
static guint tree_view_signals[LAST_SIGNAL];
281
G_DEFINE_TYPE_WITH_CODE (ThunarTreeView, thunar_tree_view, GTK_TYPE_TREE_VIEW,
282
G_IMPLEMENT_INTERFACE (THUNAR_TYPE_NAVIGATOR, thunar_tree_view_navigator_init))
287
thunar_tree_view_class_init (ThunarTreeViewClass *klass)
289
GtkTreeViewClass *gtktree_view_class;
290
GtkWidgetClass *gtkwidget_class;
291
GtkBindingSet *binding_set;
292
GObjectClass *gobject_class;
294
gobject_class = G_OBJECT_CLASS (klass);
295
gobject_class->finalize = thunar_tree_view_finalize;
296
gobject_class->get_property = thunar_tree_view_get_property;
297
gobject_class->set_property = thunar_tree_view_set_property;
299
gtkwidget_class = GTK_WIDGET_CLASS (klass);
300
gtkwidget_class->realize = thunar_tree_view_realize;
301
gtkwidget_class->unrealize = thunar_tree_view_unrealize;
302
gtkwidget_class->button_press_event = thunar_tree_view_button_press_event;
303
gtkwidget_class->button_release_event = thunar_tree_view_button_release_event;
304
gtkwidget_class->drag_data_received = thunar_tree_view_drag_data_received;
305
gtkwidget_class->drag_drop = thunar_tree_view_drag_drop;
306
gtkwidget_class->drag_motion = thunar_tree_view_drag_motion;
307
gtkwidget_class->drag_leave = thunar_tree_view_drag_leave;
308
gtkwidget_class->popup_menu = thunar_tree_view_popup_menu;
310
gtktree_view_class = GTK_TREE_VIEW_CLASS (klass);
311
gtktree_view_class->row_activated = thunar_tree_view_row_activated;
312
gtktree_view_class->test_expand_row = thunar_tree_view_test_expand_row;
313
gtktree_view_class->row_collapsed = thunar_tree_view_row_collapsed;
315
klass->delete_selected_files = thunar_tree_view_delete_selected_files;
317
/* Override ThunarNavigator's properties */
318
g_object_class_override_property (gobject_class, PROP_CURRENT_DIRECTORY, "current-directory");
321
* ThunarTreeView:show-hidden:
323
* Whether to display hidden and backup folders
324
* in this #ThunarTreeView.
326
g_object_class_install_property (gobject_class,
328
g_param_spec_boolean ("show-hidden",
332
EXO_PARAM_READWRITE));
335
* ThunarTreeView::delete-selected-files:
336
* @tree_view : a #ThunarTreeView.
338
* Emitted whenever the user presses the Delete key. This
339
* is an internal signal used to bind the action to keys.
341
tree_view_signals[DELETE_SELECTED_FILES] =
342
g_signal_new (I_("delete-selected-files"),
343
G_TYPE_FROM_CLASS (klass),
344
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
345
G_STRUCT_OFFSET (ThunarTreeViewClass, delete_selected_files),
346
g_signal_accumulator_true_handled, NULL,
347
_thunar_marshal_BOOLEAN__VOID,
350
/* setup the key bindings for the tree view */
351
binding_set = gtk_binding_set_by_class (klass);
352
gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_CONTROL_MASK, "delete-selected-files", 0);
353
gtk_binding_entry_add_signal (binding_set, GDK_Delete, 0, "delete-selected-files", 0);
354
gtk_binding_entry_add_signal (binding_set, GDK_Delete, GDK_SHIFT_MASK, "delete-selected-files", 0);
355
gtk_binding_entry_add_signal (binding_set, GDK_KP_Delete, 0, "delete-selected-files", 0);
356
gtk_binding_entry_add_signal (binding_set, GDK_KP_Delete, GDK_SHIFT_MASK, "delete-selected-files", 0);
362
thunar_tree_view_navigator_init (ThunarNavigatorIface *iface)
364
iface->get_current_directory = thunar_tree_view_get_current_directory;
365
iface->set_current_directory = thunar_tree_view_set_current_directory;
371
thunar_tree_view_init (ThunarTreeView *view)
373
GtkTreeViewColumn *column;
374
GtkTreeSelection *selection;
375
GtkCellRenderer *renderer;
377
/* grab a reference on the provider factory */
378
view->provider_factory = thunarx_provider_factory_get_default ();
380
/* grab a reference on the preferences; be sure to redraw the view
381
* whenever the "tree-icon-emblems" preference changes.
383
view->preferences = thunar_preferences_get ();
384
g_signal_connect_swapped (G_OBJECT (view->preferences), "notify::tree-icon-emblems", G_CALLBACK (gtk_widget_queue_draw), view);
386
/* connect to the default tree model */
387
view->model = thunar_tree_model_get_default ();
388
thunar_tree_model_set_visible_func (view->model, thunar_tree_view_visible_func, view);
389
gtk_tree_view_set_model (GTK_TREE_VIEW (view), GTK_TREE_MODEL (view->model));
391
/* configure the tree view */
392
gtk_tree_view_set_enable_search (GTK_TREE_VIEW (view), FALSE);
393
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);
395
/* allocate a single column for our renderers */
396
column = g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
397
"reorderable", FALSE,
399
"sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE,
402
gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
404
/* queue a resize on the column whenever the icon size is changed */
405
view->queue_resize_signal_id = g_signal_connect_swapped (G_OBJECT (view->preferences), "notify::tree-icon-size",
406
G_CALLBACK (gtk_tree_view_column_queue_resize), column);
408
/* allocate the special icon renderer */
409
view->icon_renderer = thunar_shortcuts_icon_renderer_new ();
410
gtk_tree_view_column_pack_start (column, view->icon_renderer, FALSE);
411
gtk_tree_view_column_set_attributes (column, view->icon_renderer,
412
"file", THUNAR_TREE_MODEL_COLUMN_FILE,
413
"device", THUNAR_TREE_MODEL_COLUMN_DEVICE,
416
/* sync the "emblems" property of the icon renderer with the "tree-icon-emblems" preference
417
* and the "size" property of the renderer with the "tree-icon-size" preference.
419
exo_binding_new (G_OBJECT (view->preferences), "tree-icon-size", G_OBJECT (view->icon_renderer), "size");
420
exo_binding_new (G_OBJECT (view->preferences), "tree-icon-emblems", G_OBJECT (view->icon_renderer), "emblems");
422
/* allocate the text renderer */
423
renderer = gtk_cell_renderer_text_new ();
424
gtk_tree_view_column_pack_start (column, renderer, TRUE);
425
gtk_tree_view_column_set_attributes (column, renderer,
426
"attributes", THUNAR_TREE_MODEL_COLUMN_ATTR,
427
"text", THUNAR_TREE_MODEL_COLUMN_NAME,
430
/* setup the tree selection */
431
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
432
gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
433
gtk_tree_selection_set_select_function (selection, thunar_tree_view_selection_func, view, NULL);
435
/* enable drop support for the tree view */
436
gtk_drag_dest_set (GTK_WIDGET (view), 0, drop_targets, G_N_ELEMENTS (drop_targets),
437
GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_MOVE);
443
thunar_tree_view_finalize (GObject *object)
445
ThunarTreeView *view = THUNAR_TREE_VIEW (object);
447
/* release drop path list (if drag_leave wasn't called) */
448
thunar_g_file_list_free (view->drop_file_list);
450
/* release the provider factory */
451
g_object_unref (G_OBJECT (view->provider_factory));
453
/* disconnect the queue resize signal handler */
454
g_signal_handler_disconnect (G_OBJECT (view->preferences), view->queue_resize_signal_id);
456
/* be sure to cancel the cursor idle source */
457
if (G_UNLIKELY (view->cursor_idle_id != 0))
458
g_source_remove (view->cursor_idle_id);
460
/* cancel any running autoscroll timer */
461
if (G_LIKELY (view->drag_scroll_timer_id != 0))
462
g_source_remove (view->drag_scroll_timer_id);
464
/* be sure to cancel the expand timer source */
465
if (G_UNLIKELY (view->expand_timer_id != 0))
466
g_source_remove (view->expand_timer_id);
468
/* reset the current-directory property */
469
thunar_navigator_set_current_directory (THUNAR_NAVIGATOR (view), NULL);
471
/* release our reference on the preferences */
472
g_object_unref (G_OBJECT (view->preferences));
474
/* disconnect from the default tree model */
475
g_object_unref (G_OBJECT (view->model));
477
/* drop any existing "new-files" closure */
478
if (G_UNLIKELY (view->new_files_closure != NULL))
480
g_closure_invalidate (view->new_files_closure);
481
g_closure_unref (view->new_files_closure);
484
(*G_OBJECT_CLASS (thunar_tree_view_parent_class)->finalize) (object);
490
thunar_tree_view_get_property (GObject *object,
497
case PROP_CURRENT_DIRECTORY:
498
g_value_set_object (value, thunar_navigator_get_current_directory (THUNAR_NAVIGATOR (object)));
501
case PROP_SHOW_HIDDEN:
502
g_value_set_boolean (value, thunar_tree_view_get_show_hidden (THUNAR_TREE_VIEW (object)));
506
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
514
thunar_tree_view_set_property (GObject *object,
521
case PROP_CURRENT_DIRECTORY:
522
thunar_navigator_set_current_directory (THUNAR_NAVIGATOR (object), g_value_get_object (value));
525
case PROP_SHOW_HIDDEN:
526
thunar_tree_view_set_show_hidden (THUNAR_TREE_VIEW (object), g_value_get_boolean (value));
530
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
538
thunar_tree_view_get_current_directory (ThunarNavigator *navigator)
540
return THUNAR_TREE_VIEW (navigator)->current_directory;
546
thunar_tree_view_set_current_directory (ThunarNavigator *navigator,
547
ThunarFile *current_directory)
549
ThunarTreeView *view = THUNAR_TREE_VIEW (navigator);
551
ThunarFile *file_parent;
552
gboolean needs_refiltering = FALSE;
554
/* check if we already use that directory */
555
if (G_UNLIKELY (view->current_directory == current_directory))
558
/* check if we have an active directory */
559
if (G_LIKELY (view->current_directory != NULL))
561
/* update the filter if the old current directory, or one of it's parents, is hidden */
562
if (!view->show_hidden)
564
/* look if the file or one of it's parents is hidden */
565
for (file = g_object_ref (G_OBJECT (view->current_directory)); file != NULL; file = file_parent)
567
/* check if this file is hidden */
568
if (thunar_file_is_hidden (file))
570
/* schedule an update of the filter after the current directory has been changed */
571
needs_refiltering = TRUE;
573
/* release the file */
574
g_object_unref (G_OBJECT (file));
579
/* get the file parent */
580
file_parent = thunar_file_get_parent (file, NULL);
582
/* release the file */
583
g_object_unref (G_OBJECT (file));
587
/* disconnect from the previous current directory */
588
g_object_unref (G_OBJECT (view->current_directory));
591
/* activate the new current directory */
592
view->current_directory = current_directory;
594
/* connect to the new current directory */
595
if (G_LIKELY (current_directory != NULL))
597
/* take a reference on the directory */
598
g_object_ref (G_OBJECT (current_directory));
600
/* update the filter if the new current directory, or one of it's parents, is
601
* hidden. we don't have to check this if refiltering needs to be done
603
if (!needs_refiltering && !view->show_hidden)
605
/* look if the file or one of it's parents is hidden */
606
for (file = g_object_ref (G_OBJECT (current_directory)); file != NULL; file = file_parent)
608
/* check if this file is hidden */
609
if (thunar_file_is_hidden (file))
611
/* update the filter */
612
thunar_tree_model_refilter (view->model);
614
/* release the file */
615
g_object_unref (G_OBJECT (file));
620
/* get the file parent */
621
file_parent = thunar_file_get_parent (file, NULL);
623
/* release the file */
624
g_object_unref (G_OBJECT (file));
628
/* schedule an idle source to set the cursor to the current directory */
629
if (G_LIKELY (view->cursor_idle_id == 0))
630
view->cursor_idle_id = g_idle_add_full (G_PRIORITY_LOW, thunar_tree_view_cursor_idle, view, thunar_tree_view_cursor_idle_destroy);
632
/* drop any existing "new-files" closure */
633
if (G_UNLIKELY (view->new_files_closure != NULL))
635
g_closure_invalidate (view->new_files_closure);
636
g_closure_unref (view->new_files_closure);
637
view->new_files_closure = NULL;
641
/* refilter the model if necessary */
642
if (needs_refiltering)
643
thunar_tree_model_refilter (view->model);
645
/* notify listeners */
646
g_object_notify (G_OBJECT (view), "current-directory");
652
thunar_tree_view_realize (GtkWidget *widget)
654
ThunarTreeView *view = THUNAR_TREE_VIEW (widget);
657
/* let the parent class realize the widget */
658
(*GTK_WIDGET_CLASS (thunar_tree_view_parent_class)->realize) (widget);
660
/* query the clipboard manager for the display */
661
display = gtk_widget_get_display (widget);
662
view->clipboard = thunar_clipboard_manager_get_for_display (display);
668
thunar_tree_view_unrealize (GtkWidget *widget)
670
ThunarTreeView *view = THUNAR_TREE_VIEW (widget);
672
/* release the clipboard manager reference */
673
g_object_unref (G_OBJECT (view->clipboard));
674
view->clipboard = NULL;
676
/* let the parent class unrealize the widget */
677
(*GTK_WIDGET_CLASS (thunar_tree_view_parent_class)->unrealize) (widget);
683
thunar_tree_view_button_press_event (GtkWidget *widget,
684
GdkEventButton *event)
686
ThunarTreeView *view = THUNAR_TREE_VIEW (widget);
691
/* reset the pressed button state */
692
view->pressed_button = -1;
694
/* completely ignore double middle clicks */
695
if (event->type == GDK_2BUTTON_PRESS && event->button == 2)
698
/* let the widget process the event first (handles focussing and scrolling) */
699
result = (*GTK_WIDGET_CLASS (thunar_tree_view_parent_class)->button_press_event) (widget, event);
701
/* resolve the path at the cursor position */
702
if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), event->x, event->y, &path, NULL, NULL, NULL))
704
/* check if we should popup the context menu */
705
if (G_UNLIKELY (event->button == 3 && event->type == GDK_BUTTON_PRESS))
707
/* determine the iterator for the path */
708
if (gtk_tree_model_get_iter (GTK_TREE_MODEL (view->model), &iter, path))
710
/* popup the context menu */
711
thunar_tree_view_context_menu (view, event, GTK_TREE_MODEL (view->model), &iter);
713
/* we effectively handled the event */
717
else if ((event->button == 1 || event->button == 2)
718
&& event->type == GDK_BUTTON_PRESS
719
&& (event->state & gtk_accelerator_get_default_mod_mask ()) == 0)
721
/* remember the button as pressed and handled it in the release handler */
722
view->pressed_button = event->button;
725
/* release the path */
726
gtk_tree_path_free (path);
735
thunar_tree_view_button_release_event (GtkWidget *widget,
736
GdkEventButton *event)
738
ThunarTreeView *view = THUNAR_TREE_VIEW (widget);
741
/* check if we have an event matching the pressed button state */
742
if (G_LIKELY (view->pressed_button == (gint) event->button))
744
/* check if we should simply open or open in new window */
745
if (G_LIKELY (event->button == 1))
747
thunar_tree_view_action_open (view);
749
else if (G_UNLIKELY (event->button == 2))
751
g_object_get (view->preferences, "misc-middle-click-in-tab", &in_tab, NULL);
753
thunar_tree_view_action_open_in_new_tab (view);
755
thunar_tree_view_action_open_in_new_window (view);
759
/* reset the pressed button state */
760
view->pressed_button = -1;
762
/* call the parent's release event handler */
763
return (*GTK_WIDGET_CLASS (thunar_tree_view_parent_class)->button_release_event) (widget, event);
769
thunar_tree_view_drag_data_received (GtkWidget *widget,
770
GdkDragContext *context,
773
GtkSelectionData *selection_data,
777
ThunarTreeView *view = THUNAR_TREE_VIEW (widget);
778
GdkDragAction actions;
779
GdkDragAction action;
781
gboolean succeed = FALSE;
783
/* check if don't already know the drop data */
784
if (G_LIKELY (!view->drop_data_ready))
786
/* extract the URI list from the selection data (if valid) */
787
if (info == TARGET_TEXT_URI_LIST && selection_data->format == 8 && selection_data->length > 0)
788
view->drop_file_list = thunar_g_file_list_new_from_string ((const gchar *) selection_data->data);
790
/* reset the state */
791
view->drop_data_ready = TRUE;
794
/* check if the data was droppped */
795
if (G_UNLIKELY (view->drop_occurred))
797
/* reset the state */
798
view->drop_occurred = FALSE;
800
/* determine the drop position */
801
actions = thunar_tree_view_get_dest_actions (view, context, x, y, timestamp, &file);
802
if (G_LIKELY ((actions & (GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK)) != 0))
804
/* ask the user what to do with the drop data */
805
action = (context->action == GDK_ACTION_ASK)
806
? thunar_dnd_ask (GTK_WIDGET (view), file, view->drop_file_list, timestamp, actions)
809
/* perform the requested action */
810
if (G_LIKELY (action != 0))
811
succeed = thunar_dnd_perform (GTK_WIDGET (view), file, view->drop_file_list, action, NULL);
814
/* release the file reference */
815
if (G_LIKELY (file != NULL))
816
g_object_unref (G_OBJECT (file));
818
/* tell the peer that we handled the drop */
819
gtk_drag_finish (context, succeed, FALSE, timestamp);
821
/* disable the highlighting and release the drag data */
822
thunar_tree_view_drag_leave (GTK_WIDGET (view), context, timestamp);
829
thunar_tree_view_drag_drop (GtkWidget *widget,
830
GdkDragContext *context,
835
ThunarTreeView *view = THUNAR_TREE_VIEW (widget);
838
/* determine the drop target */
839
target = gtk_drag_dest_find_target (widget, context, NULL);
840
if (G_LIKELY (target == gdk_atom_intern_static_string ("text/uri-list")))
842
/* set state so the drag-data-received handler
843
* knows that this is really a drop this time.
845
view->drop_occurred = TRUE;
847
/* request the drag data from the source. */
848
gtk_drag_get_data (widget, context, target, timestamp);
852
/* we cannot handle the drop */
862
thunar_tree_view_drag_motion (GtkWidget *widget,
863
GdkDragContext *context,
868
ThunarTreeView *view = THUNAR_TREE_VIEW (widget);
871
/* determine the drop target */
872
target = gtk_drag_dest_find_target (widget, context, NULL);
873
if (G_UNLIKELY (target != gdk_atom_intern_static_string ("text/uri-list")))
875
/* reset the "drop-file" of the icon renderer */
876
g_object_set (G_OBJECT (view->icon_renderer), "drop-file", NULL, NULL);
878
/* we cannot handle the drop */
882
/* request the drop data on-demand (if we don't have it already) */
883
if (G_UNLIKELY (!view->drop_data_ready))
885
/* reset the "drop-file" of the icon renderer */
886
g_object_set (G_OBJECT (view->icon_renderer), "drop-file", NULL, NULL);
888
/* request the drag data from the source */
889
gtk_drag_get_data (widget, context, target, timestamp);
891
/* tell Gdk that we don't know whether we can drop */
892
gdk_drag_status (context, 0, timestamp);
896
/* check if we can drop here */
897
thunar_tree_view_get_dest_actions (view, context, x, y, timestamp, NULL);
900
/* start the drag autoscroll timer if not already running */
901
if (G_UNLIKELY (view->drag_scroll_timer_id == 0))
903
/* schedule the drag autoscroll timer */
904
view->drag_scroll_timer_id = g_timeout_add_full (G_PRIORITY_LOW, 50, thunar_tree_view_drag_scroll_timer,
905
view, thunar_tree_view_drag_scroll_timer_destroy);
914
thunar_tree_view_drag_leave (GtkWidget *widget,
915
GdkDragContext *context,
918
ThunarTreeView *view = THUNAR_TREE_VIEW (widget);
920
/* cancel any running autoscroll timer */
921
if (G_LIKELY (view->drag_scroll_timer_id != 0))
922
g_source_remove (view->drag_scroll_timer_id);
924
/* reset the "drop-file" of the icon renderer */
925
g_object_set (G_OBJECT (view->icon_renderer), "drop-file", NULL, NULL);
927
/* reset the "drop data ready" status and free the URI list */
928
if (G_LIKELY (view->drop_data_ready))
930
thunar_g_file_list_free (view->drop_file_list);
931
view->drop_data_ready = FALSE;
932
view->drop_file_list = NULL;
935
/* call the parent's handler */
936
(*GTK_WIDGET_CLASS (thunar_tree_view_parent_class)->drag_leave) (widget, context, timestamp);
942
thunar_tree_view_popup_menu (GtkWidget *widget)
944
GtkTreeSelection *selection;
945
ThunarTreeView *view = THUNAR_TREE_VIEW (widget);
949
/* determine the selected row */
950
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
951
if (gtk_tree_selection_get_selected (selection, &model, &iter))
953
/* popup the context menu */
954
thunar_tree_view_context_menu (view, NULL, model, &iter);
957
else if (GTK_WIDGET_CLASS (thunar_tree_view_parent_class)->popup_menu != NULL)
959
/* call the parent's "popup-menu" handler */
960
return (*GTK_WIDGET_CLASS (thunar_tree_view_parent_class)->popup_menu) (widget);
969
thunar_tree_view_row_activated (GtkTreeView *tree_view,
971
GtkTreeViewColumn *column)
973
/* call the parent's "row-activated" handler */
974
if (GTK_TREE_VIEW_CLASS (thunar_tree_view_parent_class)->row_activated != NULL)
975
(*GTK_TREE_VIEW_CLASS (thunar_tree_view_parent_class)->row_activated) (tree_view, path, column);
977
/* toggle the expanded state of the activated row... */
978
if (gtk_tree_view_row_expanded (tree_view, path))
980
gtk_tree_view_collapse_row (tree_view, path);
984
/* expand the row, but open it if mounted */
985
if (gtk_tree_view_expand_row (tree_view, path, FALSE))
987
/* ...open the selected folder */
988
thunar_tree_view_action_open (THUNAR_TREE_VIEW (tree_view));
996
thunar_tree_view_test_expand_row (GtkTreeView *tree_view,
1000
ThunarTreeViewMountData *data;
1001
GMountOperation *mount_operation;
1002
ThunarTreeView *view = THUNAR_TREE_VIEW (tree_view);
1003
gboolean expandable = TRUE;
1004
ThunarDevice *device;
1006
/* determine the device for the iterator */
1007
gtk_tree_model_get (GTK_TREE_MODEL (view->model), iter, THUNAR_TREE_MODEL_COLUMN_DEVICE, &device, -1);
1009
/* check if we have a device */
1010
if (G_UNLIKELY (device != NULL))
1012
/* check if we need to mount the device first */
1013
if (!thunar_device_is_mounted (device))
1015
/* we need to mount the device before we can expand the row */
1018
/* allocate a mount data struct */
1019
data = thunar_tree_view_mount_data_new (view, path, FALSE, OPEN_IN_VIEW);
1021
/* allocate a GTK+ mount operation */
1022
mount_operation = thunar_gtk_mount_operation_new (GTK_WIDGET (view));
1024
/* try to mount the device and expand the row on success. the
1025
* data is destroyed in the finish callback */
1026
thunar_device_mount (device,
1029
thunar_tree_view_mount_finish,
1032
/* release the mount operation */
1033
g_object_unref (mount_operation);
1036
/* release the device */
1037
g_object_unref (G_OBJECT (device));
1040
/* cancel the cursor idle source if not expandable */
1041
if (!expandable && view->cursor_idle_id != 0)
1042
g_source_remove (view->cursor_idle_id);
1050
thunar_tree_view_row_collapsed (GtkTreeView *tree_view,
1054
/* schedule a cleanup of the tree model */
1055
thunar_tree_model_cleanup (THUNAR_TREE_VIEW (tree_view)->model);
1061
thunar_tree_view_delete_selected_files (ThunarTreeView *view)
1065
_thunar_return_val_if_fail (THUNAR_IS_TREE_VIEW (view), FALSE);
1067
/* Check if there is a user defined accelerator for the delete action,
1068
* if there is, skip events from the hard-coded keys which are set in
1069
* the class of the standard view. See bug #4173. */
1070
if (gtk_accel_map_lookup_entry ("<Actions>/ThunarStandardView/move-to-trash", &key)
1071
&& (key.accel_key != 0 || key.accel_mods != 0))
1074
/* ask the user whether to delete the folder... */
1075
thunar_tree_view_action_move_to_trash (view);
1077
/* ...and we're done */
1084
thunar_tree_view_context_menu (ThunarTreeView *view,
1085
GdkEventButton *event,
1086
GtkTreeModel *model,
1089
ThunarDevice *device;
1090
ThunarFile *parent_file;
1096
GList *providers, *lp;
1097
GList *actions = NULL, *tmp;
1099
/* verify that we're connected to the clipboard manager */
1100
if (G_UNLIKELY (view->clipboard == NULL))
1103
/* determine the file and device for the given iter */
1104
gtk_tree_model_get (model, iter,
1105
THUNAR_TREE_MODEL_COLUMN_FILE, &file,
1106
THUNAR_TREE_MODEL_COLUMN_DEVICE, &device,
1109
/* prepare the popup menu */
1110
menu = gtk_menu_new ();
1112
/* append the "Open" menu action */
1113
item = gtk_image_menu_item_new_with_mnemonic (_("_Open"));
1114
g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_tree_view_action_open), view);
1115
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1116
gtk_widget_set_sensitive (item, (file != NULL || device != NULL));
1117
gtk_widget_show (item);
1119
/* set the stock icon */
1120
image = gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU);
1121
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
1123
/* append the "Open in New Tab" menu action */
1124
item = gtk_image_menu_item_new_with_mnemonic (_("Open in New _Tab"));
1125
g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_tree_view_action_open_in_new_tab), view);
1126
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1127
gtk_widget_set_sensitive (item, (file != NULL || device != NULL));
1128
gtk_widget_show (item);
1130
/* append the "Open in New Window" menu action */
1131
item = gtk_image_menu_item_new_with_mnemonic (_("Open in New _Window"));
1132
g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_tree_view_action_open_in_new_window), view);
1133
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1134
gtk_widget_set_sensitive (item, (file != NULL || device != NULL));
1135
gtk_widget_show (item);
1137
/* append a separator item */
1138
item = gtk_separator_menu_item_new ();
1139
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1140
gtk_widget_show (item);
1142
if (G_UNLIKELY (device != NULL))
1144
if (thunar_device_get_kind (device) == THUNAR_DEVICE_KIND_VOLUME)
1146
/* append the "Mount" menu action */
1147
item = gtk_image_menu_item_new_with_mnemonic (_("_Mount"));
1148
gtk_widget_set_visible (item, thunar_device_can_mount (device));
1149
g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_tree_view_action_mount), view);
1150
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1152
/* append the "Unmount" menu action */
1153
item = gtk_image_menu_item_new_with_mnemonic (_("_Unmount"));
1154
gtk_widget_set_visible (item, thunar_device_can_unmount (device));
1155
g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_tree_view_action_unmount), view);
1156
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1158
/* append the "Eject" menu action */
1159
item = gtk_image_menu_item_new_with_mnemonic (_("_Eject"));
1160
g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_tree_view_action_eject), view);
1161
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1162
gtk_widget_set_sensitive (item, thunar_device_can_eject (device));
1163
gtk_widget_show (item);
1167
/* append the "Mount Volume" menu action */
1168
item = gtk_image_menu_item_new_with_mnemonic (_("Disconn_ect"));
1169
gtk_widget_set_sensitive (item, thunar_device_can_eject (device));
1170
g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_tree_view_action_eject), view);
1171
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1172
gtk_widget_show (item);
1175
/* append a menu separator */
1176
item = gtk_separator_menu_item_new ();
1177
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1178
gtk_widget_show (item);
1180
else if (G_UNLIKELY (file != NULL && thunar_file_is_trashed (file) && thunar_file_is_root (file)))
1182
/* append the "Empty Trash" menu action */
1183
item = gtk_image_menu_item_new_with_mnemonic (_("_Empty Trash"));
1184
gtk_widget_set_sensitive (item, (thunar_file_get_item_count (file) > 0));
1185
g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_tree_view_action_empty_trash), view);
1186
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1187
gtk_widget_show (item);
1189
/* append a menu separator */
1190
item = gtk_separator_menu_item_new ();
1191
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1192
gtk_widget_show (item);
1195
/* check if we have a non-trashed resource */
1196
if (G_LIKELY (file != NULL && !thunar_file_is_trashed (file)))
1198
/* append the "Create Folder" menu action */
1199
item = gtk_image_menu_item_new_with_mnemonic (_("Create _Folder..."));
1200
gtk_widget_set_sensitive (item, thunar_file_is_writable (file));
1201
g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_tree_view_action_create_folder), view);
1202
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1203
gtk_widget_show (item);
1205
/* append a separator item */
1206
item = gtk_separator_menu_item_new ();
1207
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1208
gtk_widget_show (item);
1211
if (G_LIKELY (file != NULL))
1213
/* "Cut" and "Copy" don't make much sense for devices */
1214
if (G_LIKELY (device == NULL))
1216
/* determine the parent file (required to determine "Cut" sensitivity) */
1217
parent_file = thunar_file_get_parent (file, NULL);
1219
/* append the "Cut" menu action */
1220
item = gtk_image_menu_item_new_with_mnemonic (_("Cu_t"));
1221
g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_tree_view_action_cut), view);
1222
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1223
gtk_widget_set_sensitive (item, (parent_file != NULL && thunar_file_is_writable (parent_file)));
1224
gtk_widget_show (item);
1226
/* set the stock icon */
1227
image = gtk_image_new_from_stock (GTK_STOCK_CUT, GTK_ICON_SIZE_MENU);
1228
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
1230
/* append the "Copy" menu action */
1231
item = gtk_image_menu_item_new_with_mnemonic (_("_Copy"));
1232
g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_tree_view_action_copy), view);
1233
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1234
gtk_widget_set_sensitive (item, (parent_file != NULL));
1235
gtk_widget_show (item);
1237
/* set the stock icon */
1238
image = gtk_image_new_from_stock (GTK_STOCK_COPY, GTK_ICON_SIZE_MENU);
1239
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
1242
if (G_LIKELY (parent_file != NULL))
1243
g_object_unref (G_OBJECT (parent_file));
1246
/* append the "Paste Into Folder" menu action */
1247
item = gtk_image_menu_item_new_with_mnemonic (_("_Paste Into Folder"));
1248
g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_tree_view_action_paste_into_folder), view);
1249
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1250
gtk_widget_set_sensitive (item, (thunar_file_is_writable (file) && thunar_clipboard_manager_get_can_paste (view->clipboard)));
1251
gtk_widget_show (item);
1253
/* set the stock icon */
1254
image = gtk_image_new_from_stock (GTK_STOCK_PASTE, GTK_ICON_SIZE_MENU);
1255
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
1257
/* "Delete" and "Rename" don't make much sense for devices */
1258
if (G_LIKELY (device == NULL))
1260
/* determine the parent file (required to determine "Delete" sensitivity) */
1261
parent_file = thunar_file_get_parent (file, NULL);
1263
/* append a separator item */
1264
item = gtk_separator_menu_item_new ();
1265
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1266
gtk_widget_show (item);
1268
if (thunar_g_vfs_is_uri_scheme_supported ("trash")
1269
&& thunar_file_can_be_trashed (file))
1271
/* append the "Move to Tash" menu action */
1272
item = gtk_image_menu_item_new_with_mnemonic (_("Mo_ve to Trash"));
1273
g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_tree_view_action_move_to_trash), view);
1274
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1275
gtk_widget_set_sensitive (item, (parent_file != NULL && thunar_file_is_writable (parent_file)));
1276
gtk_widget_show (item);
1278
/* set the stock icon */
1279
image = gtk_image_new_from_stock (THUNAR_STOCK_TRASH_FULL, GTK_ICON_SIZE_MENU);
1280
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
1283
/* append the "Delete" menu action */
1284
item = gtk_image_menu_item_new_with_mnemonic (_("_Delete"));
1285
g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_tree_view_action_delete), view);
1286
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1287
gtk_widget_set_sensitive (item, (parent_file != NULL && thunar_file_is_writable (parent_file)));
1288
gtk_widget_show (item);
1290
/* set the stock icon */
1291
image = gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU);
1292
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
1295
if (G_LIKELY (parent_file != NULL))
1296
g_object_unref (G_OBJECT (parent_file));
1298
/* don't show the "Rename" action in the trash */
1299
if (G_LIKELY (!thunar_file_is_trashed (file)))
1301
/* append a separator item */
1302
item = gtk_separator_menu_item_new ();
1303
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1304
gtk_widget_show (item);
1306
/* append the "Rename" menu action */
1307
item = gtk_image_menu_item_new_with_mnemonic (_("_Rename..."));
1308
g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_tree_view_action_rename), view);
1309
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1310
gtk_widget_set_sensitive (item, thunar_file_is_writable (file));
1311
gtk_widget_show (item);
1315
/* append a separator item */
1316
item = gtk_separator_menu_item_new ();
1317
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1318
gtk_widget_show (item);
1320
/* add the providers menu for non-trashed items */
1321
if (G_LIKELY (!thunar_file_is_trashed (file)))
1323
/* load the menu providers from the provider factory */
1324
providers = thunarx_provider_factory_list_providers (view->provider_factory, THUNARX_TYPE_MENU_PROVIDER);
1325
if (G_LIKELY (providers != NULL))
1327
/* determine the toplevel window we belong to */
1328
window = gtk_widget_get_toplevel (GTK_WIDGET (view));
1330
/* load the actions offered by the menu providers */
1331
for (lp = providers; lp != NULL; lp = lp->next)
1333
tmp = thunarx_menu_provider_get_folder_actions (lp->data, window, THUNARX_FILE_INFO (file));
1334
actions = g_list_concat (actions, tmp);
1335
g_object_unref (G_OBJECT (lp->data));
1337
g_list_free (providers);
1339
/* add the actions to the menu */
1340
for (lp = actions; lp != NULL; lp = lp->next)
1342
item = gtk_action_create_menu_item (GTK_ACTION (lp->data));
1343
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1344
gtk_widget_show (item);
1346
/* release the reference on the action */
1347
g_object_unref (G_OBJECT (lp->data));
1350
/* add a separator to the end of the menu */
1351
if (G_LIKELY (lp != actions))
1353
/* append a menu separator */
1354
item = gtk_separator_menu_item_new ();
1355
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1356
gtk_widget_show (item);
1360
g_list_free (actions);
1365
/* append the "Properties" menu action */
1366
item = gtk_image_menu_item_new_with_mnemonic (_("P_roperties..."));
1367
g_signal_connect_swapped (G_OBJECT (item), "activate", G_CALLBACK (thunar_tree_view_action_properties), view);
1368
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1369
gtk_widget_set_sensitive (item, (file != NULL));
1370
gtk_widget_show (item);
1372
/* set the stock icon */
1373
image = gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU);
1374
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
1376
/* run the menu on the view's screen (taking over the floating reference on the menu) */
1377
thunar_gtk_menu_run (GTK_MENU (menu), GTK_WIDGET (view), NULL, NULL, (event != NULL) ? event->button : 0,
1378
(event != NULL) ? event->time : gtk_get_current_event_time ());
1381
if (G_UNLIKELY (device != NULL))
1382
g_object_unref (G_OBJECT (device));
1383
if (G_LIKELY (file != NULL))
1384
g_object_unref (G_OBJECT (file));
1389
static GdkDragAction
1390
thunar_tree_view_get_dest_actions (ThunarTreeView *view,
1391
GdkDragContext *context,
1395
ThunarFile **file_return)
1397
GdkDragAction actions = 0;
1398
GdkDragAction action = 0;
1399
GtkTreePath *path = NULL;
1401
ThunarFile *file = NULL;
1403
/* cancel any previously active expand timer */
1404
if (G_LIKELY (view->expand_timer_id != 0))
1405
g_source_remove (view->expand_timer_id);
1407
/* determine the path for x/y */
1408
if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (view), x, y, &path, NULL, NULL, NULL))
1410
/* determine the iter for the given path */
1411
if (gtk_tree_model_get_iter (GTK_TREE_MODEL (view->model), &iter, path))
1413
/* determine the file for the given path */
1414
gtk_tree_model_get (GTK_TREE_MODEL (view->model), &iter, THUNAR_TREE_MODEL_COLUMN_FILE, &file, -1);
1415
if (G_LIKELY (file != NULL))
1417
/* check if the file accepts the drop */
1418
actions = thunar_file_accepts_drop (file, view->drop_file_list, context, &action);
1419
if (G_UNLIKELY (actions == 0))
1422
g_object_unref (G_OBJECT (file));
1429
/* setup the new drag dest row */
1430
gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (view), path, GTK_TREE_VIEW_DROP_INTO_OR_BEFORE);
1432
/* check if we have drag dest row */
1433
if (G_LIKELY (path != NULL))
1435
/* schedule a new expand timer to expand the drag dest row */
1436
view->expand_timer_id = g_timeout_add_full (G_PRIORITY_LOW, THUNAR_TREE_VIEW_EXPAND_TIMEOUT,
1437
thunar_tree_view_expand_timer, view,
1438
thunar_tree_view_expand_timer_destroy);
1441
/* setup the new "drop-file" */
1442
g_object_set (G_OBJECT (view->icon_renderer), "drop-file", file, NULL);
1444
/* tell Gdk whether we can drop here */
1445
gdk_drag_status (context, action, timestamp);
1447
/* return the file if requested */
1448
if (G_LIKELY (file_return != NULL))
1450
*file_return = file;
1453
else if (G_UNLIKELY (file != NULL))
1455
/* release the file */
1456
g_object_unref (G_OBJECT (file));
1460
if (G_LIKELY (path != NULL))
1461
gtk_tree_path_free (path);
1469
thunar_tree_view_find_closest_ancestor (ThunarTreeView *view,
1471
GtkTreePath **ancestor_return,
1472
gboolean *exact_return)
1474
GtkTreeModel *model = GTK_TREE_MODEL (view->model);
1475
GtkTreePath *child_path;
1476
GtkTreeIter child_iter;
1479
gboolean found = FALSE;
1481
/* determine the iter for the current path */
1482
if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (view->model), &iter, path))
1485
/* process all items at this level */
1488
/* check if the file for iter is an ancestor of the current directory */
1489
gtk_tree_model_get (model, &iter, THUNAR_TREE_MODEL_COLUMN_FILE, &file, -1);
1490
if (G_UNLIKELY (file == NULL))
1493
/* check if this is the file we're looking for */
1494
if (G_UNLIKELY (file == view->current_directory))
1496
/* we found the very best ancestor iter! */
1497
*ancestor_return = gtk_tree_model_get_path (model, &iter);
1498
*exact_return = TRUE;
1501
else if (thunar_file_is_ancestor (view->current_directory, file))
1503
/* check if we can find an even better ancestor below this node */
1504
if (gtk_tree_model_iter_children (model, &child_iter, &iter))
1506
child_path = gtk_tree_model_get_path (model, &child_iter);
1507
found = thunar_tree_view_find_closest_ancestor (view, child_path, ancestor_return, exact_return);
1508
gtk_tree_path_free (child_path);
1511
/* maybe not exact, but still an ancestor */
1512
if (G_UNLIKELY (!found))
1514
/* we found an ancestor, not an exact match */
1515
*ancestor_return = gtk_tree_model_get_path (model, &iter);
1516
*exact_return = FALSE;
1521
/* release the file reference */
1522
g_object_unref (G_OBJECT (file));
1524
while (!found && gtk_tree_model_iter_next (model, &iter));
1532
thunar_tree_view_get_selected_file (ThunarTreeView *view)
1534
GtkTreeSelection *selection;
1535
GtkTreeModel *model;
1537
ThunarFile *file = NULL;
1539
/* determine file for the selected row */
1540
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
1542
/* avoid dealing with invalid selections (may occur when the mount_finish()
1543
* handler is called and the tree view has been hidden already) */
1544
if (!GTK_IS_TREE_SELECTION (selection))
1547
if (gtk_tree_selection_get_selected (selection, &model, &iter))
1548
gtk_tree_model_get (model, &iter, THUNAR_TREE_MODEL_COLUMN_FILE, &file, -1);
1555
static ThunarDevice*
1556
thunar_tree_view_get_selected_device (ThunarTreeView *view)
1558
GtkTreeSelection *selection;
1559
ThunarDevice *device = NULL;
1560
GtkTreeModel *model;
1563
/* determine file for the selected row */
1564
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
1565
if (gtk_tree_selection_get_selected (selection, &model, &iter))
1566
gtk_tree_model_get (model, &iter, THUNAR_TREE_MODEL_COLUMN_DEVICE, &device, -1);
1574
thunar_tree_view_action_copy (ThunarTreeView *view)
1579
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
1581
/* verify that we're connected to the clipboard */
1582
if (G_UNLIKELY (view->clipboard == NULL))
1585
/* determine the selected file */
1586
file = thunar_tree_view_get_selected_file (view);
1587
if (G_LIKELY (file != NULL))
1589
/* fake a file list */
1594
/* copy the selected to the clipboard */
1595
thunar_clipboard_manager_copy_files (view->clipboard, &list);
1597
/* release the file reference */
1598
g_object_unref (G_OBJECT (file));
1605
thunar_tree_view_action_create_folder (ThunarTreeView *view)
1607
ThunarApplication *application;
1608
ThunarFile *directory;
1612
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
1614
/* determine the selected directory */
1615
directory = thunar_tree_view_get_selected_file (view);
1616
if (G_UNLIKELY (directory == NULL))
1619
/* ask the user to enter a name for the new folder */
1620
name = thunar_show_create_dialog (GTK_WIDGET (view),
1623
_("Create New Folder"));
1624
if (G_LIKELY (name != NULL))
1626
/* fake the path list */
1627
path_list.data = g_file_resolve_relative_path (thunar_file_get_file (directory), name);
1628
path_list.next = path_list.prev = NULL;
1630
/* launch the operation */
1631
application = thunar_application_get ();
1632
thunar_application_mkdir (application, GTK_WIDGET (view), &path_list, thunar_tree_view_new_files_closure (view));
1633
g_object_unref (G_OBJECT (application));
1635
/* release the path */
1636
g_object_unref (path_list.data);
1638
/* release the file name */
1643
g_object_unref (G_OBJECT (directory));
1649
thunar_tree_view_action_cut (ThunarTreeView *view)
1654
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
1656
/* verify that we're connected to the clipboard */
1657
if (G_UNLIKELY (view->clipboard == NULL))
1660
/* determine the selected file */
1661
file = thunar_tree_view_get_selected_file (view);
1662
if (G_LIKELY (file != NULL))
1664
/* fake a file list */
1669
/* cut the selected to the clipboard */
1670
thunar_clipboard_manager_cut_files (view->clipboard, &list);
1672
/* release the file reference */
1673
g_object_unref (G_OBJECT (file));
1680
thunar_tree_view_action_move_to_trash (ThunarTreeView *view)
1682
ThunarApplication *application;
1685
gboolean permanently;
1686
GdkModifierType state;
1688
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
1690
/* determine the selected file */
1691
file = thunar_tree_view_get_selected_file (view);
1692
if (G_LIKELY (file != NULL))
1694
/* fake a file list */
1695
file_list.data = file;
1696
file_list.next = NULL;
1697
file_list.prev = NULL;
1699
/* check if we should permanently delete the files (user holds shift) */
1700
permanently = (gtk_get_current_event_state (&state) && (state & GDK_SHIFT_MASK) != 0);
1702
/* delete the file */
1703
application = thunar_application_get ();
1704
thunar_application_unlink_files (application, GTK_WIDGET (view), &file_list, permanently);
1705
g_object_unref (G_OBJECT (application));
1707
/* release the file */
1708
g_object_unref (G_OBJECT (file));
1715
thunar_tree_view_action_delete (ThunarTreeView *view)
1717
ThunarApplication *application;
1721
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
1723
/* determine the selected file */
1724
file = thunar_tree_view_get_selected_file (view);
1725
if (G_LIKELY (file != NULL))
1727
/* fake a file list */
1728
file_list.data = file;
1729
file_list.next = NULL;
1730
file_list.prev = NULL;
1732
/* delete the file */
1733
application = thunar_application_get ();
1734
thunar_application_unlink_files (application, GTK_WIDGET (view), &file_list, TRUE);
1735
g_object_unref (G_OBJECT (application));
1737
/* release the file */
1738
g_object_unref (G_OBJECT (file));
1745
thunar_tree_view_rename_error (ExoJob *job,
1747
ThunarTreeView *view)
1749
GArray *param_values;
1752
_thunar_return_if_fail (EXO_IS_JOB (job));
1753
_thunar_return_if_fail (error != NULL);
1754
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
1756
param_values = thunar_simple_job_get_param_values (THUNAR_SIMPLE_JOB (job));
1757
file = g_value_get_object (&g_array_index (param_values, GValue, 0));
1759
/* display an error message */
1760
thunar_dialogs_show_error (GTK_WIDGET (view), error, _("Failed to rename \"%s\""),
1761
thunar_file_get_display_name (file));
1767
thunar_tree_view_rename_finished (ExoJob *job,
1768
ThunarTreeView *view)
1770
_thunar_return_if_fail (EXO_IS_JOB (job));
1771
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
1773
/* destroy the job */
1774
g_signal_handlers_disconnect_matched (job, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, view);
1775
g_object_unref (job);
1781
thunar_tree_view_action_rename (ThunarTreeView *view)
1787
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
1789
/* determine the selected file */
1790
file = thunar_tree_view_get_selected_file (view);
1791
if (G_LIKELY (file != NULL))
1793
/* get the toplevel window */
1794
window = gtk_widget_get_toplevel (GTK_WIDGET (view));
1796
/* run the rename dialog */
1797
job = thunar_dialogs_show_rename_file (GTK_WINDOW (window), file);
1798
if (G_LIKELY (job != NULL))
1800
g_signal_connect (job, "error", G_CALLBACK (thunar_tree_view_rename_error), view);
1801
g_signal_connect (job, "finished", G_CALLBACK (thunar_tree_view_rename_finished), view);
1804
/* release the file */
1805
g_object_unref (file);
1812
thunar_tree_view_action_eject_finish (ThunarDevice *device,
1813
const GError *error,
1816
ThunarTreeView *view = THUNAR_TREE_VIEW (user_data);
1819
_thunar_return_if_fail (THUNAR_IS_DEVICE (device));
1820
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
1822
/* check if there was an error */
1825
/* display an error dialog to inform the user */
1826
device_name = thunar_device_get_name (device);
1827
thunar_dialogs_show_error (GTK_WIDGET (view), error, _("Failed to eject \"%s\""), device_name);
1828
g_free (device_name);
1831
g_object_unref (view);
1837
thunar_tree_view_action_eject (ThunarTreeView *view)
1839
ThunarDevice *device;
1840
GMountOperation *mount_operation;
1842
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
1844
/* determine the selected device */
1845
device = thunar_tree_view_get_selected_device (view);
1846
if (G_LIKELY (device != NULL))
1848
/* prepare a mount operation */
1849
mount_operation = thunar_gtk_mount_operation_new (GTK_WIDGET (view));
1852
thunar_device_eject (device,
1855
thunar_tree_view_action_eject_finish,
1856
g_object_ref (view));
1858
/* release the device */
1859
g_object_unref (device);
1860
g_object_unref (mount_operation);
1867
thunar_tree_view_action_unmount_finish (ThunarDevice *device,
1868
const GError *error,
1871
ThunarTreeView *view = THUNAR_TREE_VIEW (user_data);
1874
_thunar_return_if_fail (THUNAR_IS_DEVICE (device));
1875
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
1877
/* check if there was an error */
1880
/* display an error dialog to inform the user */
1881
device_name = thunar_device_get_name (device);
1882
thunar_dialogs_show_error (GTK_WIDGET (view), error, _("Failed to unmount \"%s\""), device_name);
1883
g_free (device_name);
1886
g_object_unref (view);
1892
thunar_tree_view_action_unmount (ThunarTreeView *view)
1894
ThunarDevice *device;
1895
GMountOperation *mount_operation;
1897
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
1899
/* determine the selected device */
1900
device = thunar_tree_view_get_selected_device (view);
1901
if (G_LIKELY (device != NULL))
1903
/* prepare a mount operation */
1904
mount_operation = thunar_gtk_mount_operation_new (GTK_WIDGET (view));
1907
thunar_device_unmount (device,
1910
thunar_tree_view_action_unmount_finish,
1911
g_object_ref (view));
1913
/* release the device */
1914
g_object_unref (device);
1915
g_object_unref (mount_operation);
1922
thunar_tree_view_action_empty_trash (ThunarTreeView *view)
1924
ThunarApplication *application;
1926
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
1928
/* empty the trash bin (asking the user first) */
1929
application = thunar_application_get ();
1930
thunar_application_empty_trash (application, GTK_WIDGET (view), NULL);
1931
g_object_unref (G_OBJECT (application));
1937
thunar_tree_view_action_mount (ThunarTreeView *view)
1939
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
1940
thunar_tree_view_mount (view, FALSE, OPEN_IN_VIEW);
1946
thunar_tree_view_mount_finish (ThunarDevice *device,
1947
const GError *error,
1950
ThunarTreeViewMountData *data = user_data;
1953
_thunar_return_if_fail (THUNAR_IS_DEVICE (device));
1954
_thunar_return_if_fail (data != NULL && THUNAR_IS_TREE_VIEW (data->view));
1958
device_name = thunar_device_get_name (device);
1959
thunar_dialogs_show_error (GTK_WIDGET (data->view), error, _("Failed to mount \"%s\""), device_name);
1960
g_free (device_name);
1964
if (G_LIKELY (data->open_after_mounting))
1966
switch (data->open_in)
1968
case OPEN_IN_WINDOW:
1969
thunar_tree_view_open_selection_in_new_window (data->view);
1973
thunar_tree_view_open_selection_in_new_tab (data->view);
1977
thunar_tree_view_open_selection (data->view);
1981
else if (data->path != NULL)
1983
gtk_tree_view_expand_row (GTK_TREE_VIEW (data->view), data->path, FALSE);
1987
thunar_tree_view_mount_data_free (data);
1993
thunar_tree_view_mount (ThunarTreeView *view,
1994
gboolean open_after_mounting,
1997
ThunarTreeViewMountData *data;
1998
GMountOperation *mount_operation;
1999
ThunarDevice *device;
2001
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
2003
/* determine the selected device */
2004
device = thunar_tree_view_get_selected_device (view);
2007
/* check if we need to mount the device at all */
2008
if (!thunar_device_is_mounted (device))
2010
/* allocate mount data */
2011
data = thunar_tree_view_mount_data_new (view, NULL, open_after_mounting, open_in);
2013
/* allocate a GTK+ mount operation */
2014
mount_operation = thunar_gtk_mount_operation_new (GTK_WIDGET (view));
2016
/* try to mount the device and expand the row on success. the
2017
* data is destroyed in the finish callback */
2018
thunar_device_mount (device,
2021
thunar_tree_view_mount_finish,
2024
/* release the mount operation */
2025
g_object_unref (mount_operation);
2028
/* release the device */
2029
g_object_unref (device);
2036
thunar_tree_view_action_open (ThunarTreeView *view)
2039
ThunarDevice *device;
2041
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
2043
/* determine the selected device and file */
2044
device = thunar_tree_view_get_selected_device (view);
2045
file = thunar_tree_view_get_selected_file (view);
2049
if (thunar_device_is_mounted (device))
2050
thunar_tree_view_open_selection (view);
2052
thunar_tree_view_mount (view, TRUE, OPEN_IN_TAB);
2054
g_object_unref (device);
2056
else if (file != NULL)
2058
thunar_tree_view_open_selection (view);
2059
g_object_unref (file);
2066
thunar_tree_view_open_selection (ThunarTreeView *view)
2070
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
2072
/* determine the selected file */
2073
file = thunar_tree_view_get_selected_file (view);
2074
if (G_LIKELY (file != NULL))
2076
/* open that folder in the main view */
2077
thunar_navigator_change_directory (THUNAR_NAVIGATOR (view), file);
2078
g_object_unref (file);
2085
thunar_tree_view_action_open_in_new_window (ThunarTreeView *view)
2088
ThunarDevice *device;
2090
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
2092
/* determine the selected device and file */
2093
device = thunar_tree_view_get_selected_device (view);
2094
file = thunar_tree_view_get_selected_file (view);
2098
if (thunar_device_is_mounted (device))
2099
thunar_tree_view_open_selection_in_new_window (view);
2101
thunar_tree_view_mount (view, TRUE, OPEN_IN_WINDOW);
2103
g_object_unref (device);
2105
else if (file != NULL)
2107
thunar_tree_view_open_selection_in_new_window (view);
2108
g_object_unref (file);
2115
thunar_tree_view_action_open_in_new_tab (ThunarTreeView *view)
2118
ThunarDevice *device;
2120
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
2122
/* determine the selected device and file */
2123
device = thunar_tree_view_get_selected_device (view);
2124
file = thunar_tree_view_get_selected_file (view);
2128
if (thunar_device_is_mounted (device))
2129
thunar_tree_view_open_selection_in_new_tab (view);
2131
thunar_tree_view_mount (view, TRUE, OPEN_IN_TAB);
2133
g_object_unref (device);
2135
else if (file != NULL)
2137
thunar_tree_view_open_selection_in_new_tab (view);
2138
g_object_unref (file);
2145
thunar_tree_view_open_selection_in_new_window (ThunarTreeView *view)
2147
ThunarApplication *application;
2150
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
2152
/* determine the selected file */
2153
file = thunar_tree_view_get_selected_file (view);
2154
if (G_LIKELY (file != NULL))
2156
/* open a new window for the selected folder */
2157
application = thunar_application_get ();
2158
thunar_application_open_window (application, file,
2159
gtk_widget_get_screen (GTK_WIDGET (view)), NULL);
2160
g_object_unref (application);
2161
g_object_unref (file);
2168
thunar_tree_view_open_selection_in_new_tab (ThunarTreeView *view)
2172
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
2174
/* determine the selected file */
2175
file = thunar_tree_view_get_selected_file (view);
2176
if (G_LIKELY (file != NULL))
2178
/* open a new tab for the selected folder */
2179
thunar_navigator_open_new_tab (THUNAR_NAVIGATOR (view), file);
2180
g_object_unref (file);
2187
thunar_tree_view_action_paste_into_folder (ThunarTreeView *view)
2191
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
2193
/* verify that we're connected to the clipboard */
2194
if (G_UNLIKELY (view->clipboard == NULL))
2197
/* determine the selected folder */
2198
file = thunar_tree_view_get_selected_file (view);
2199
if (G_LIKELY (file != NULL))
2201
/* paste the files from the clipboard to the selected folder */
2202
thunar_clipboard_manager_paste_files (view->clipboard, thunar_file_get_file (file), GTK_WIDGET (view), NULL);
2204
/* release the file reference */
2205
g_object_unref (G_OBJECT (file));
2212
thunar_tree_view_action_properties (ThunarTreeView *view)
2216
GtkWidget *toplevel;
2218
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
2220
/* determine the selected file */
2221
file = thunar_tree_view_get_selected_file (view);
2222
if (G_LIKELY (file != NULL))
2224
/* determine the toplevel window */
2225
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view));
2226
if (G_LIKELY (toplevel != NULL && gtk_widget_is_toplevel (toplevel)))
2228
/* popup the properties dialog */
2229
dialog = thunar_properties_dialog_new (GTK_WINDOW (toplevel));
2230
thunar_properties_dialog_set_file (THUNAR_PROPERTIES_DIALOG (dialog), file);
2231
gtk_widget_show (dialog);
2234
/* release the file */
2235
g_object_unref (G_OBJECT (file));
2242
thunar_tree_view_new_files_closure (ThunarTreeView *view)
2244
/* drop any previous "new-files" closure */
2245
if (G_UNLIKELY (view->new_files_closure != NULL))
2247
g_closure_invalidate (view->new_files_closure);
2248
g_closure_unref (view->new_files_closure);
2251
/* allocate a new "new-files" closure */
2252
view->new_files_closure = g_cclosure_new (G_CALLBACK (thunar_tree_view_new_files), view, NULL);
2253
g_closure_ref (view->new_files_closure);
2254
g_closure_sink (view->new_files_closure);
2256
/* and return our new closure */
2257
return view->new_files_closure;
2263
thunar_tree_view_new_files (ThunarJob *job,
2265
ThunarTreeView *view)
2269
/* check if we have exactly one new path */
2270
if (G_UNLIKELY (path_list == NULL || path_list->next != NULL))
2273
/* determine the file for the first path */
2274
file = thunar_file_cache_lookup (path_list->data);
2275
if (G_LIKELY (file != NULL && thunar_file_is_directory (file)))
2277
/* change to the newly created folder */
2278
thunar_navigator_change_directory (THUNAR_NAVIGATOR (view), file);
2285
thunar_tree_view_visible_func (ThunarTreeModel *model,
2289
ThunarTreeView *view;
2290
gboolean visible = TRUE;
2292
_thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
2293
_thunar_return_val_if_fail (THUNAR_IS_TREE_MODEL (model), FALSE);
2294
_thunar_return_val_if_fail (THUNAR_IS_TREE_VIEW (user_data), FALSE);
2296
/* if show_hidden is TRUE, nothing is filtered */
2297
view = THUNAR_TREE_VIEW (user_data);
2298
if (G_LIKELY (!view->show_hidden))
2300
/* we display all non-hidden file and hidden files that are ancestors of the current directory */
2301
visible = !thunar_file_is_hidden (file) || (view->current_directory == file)
2302
|| (view->current_directory != NULL && thunar_file_is_ancestor (view->current_directory, file));
2311
thunar_tree_view_selection_func (GtkTreeSelection *selection,
2312
GtkTreeModel *model,
2314
gboolean path_currently_selected,
2319
gboolean result = FALSE;
2320
ThunarDevice *device;
2322
/* every row may be unselected at any time */
2323
if (path_currently_selected)
2326
/* determine the iterator for the path */
2327
if (gtk_tree_model_get_iter (model, &iter, path))
2329
/* determine the file for the iterator */
2330
gtk_tree_model_get (model, &iter, THUNAR_TREE_MODEL_COLUMN_FILE, &file, -1);
2331
if (G_LIKELY (file != NULL))
2333
/* rows with files can be selected */
2337
g_object_unref (file);
2341
/* but maybe the row has a device */
2342
gtk_tree_model_get (model, &iter, THUNAR_TREE_MODEL_COLUMN_DEVICE, &device, -1);
2343
if (G_LIKELY (device != NULL))
2345
/* rows with devices can also be selected */
2348
/* release device */
2349
g_object_unref (device);
2360
thunar_tree_view_cursor_idle (gpointer user_data)
2362
ThunarTreeView *view = THUNAR_TREE_VIEW (user_data);
2363
GtkTreePath *ancestor = NULL;
2364
GtkTreePath *parent;
2366
gboolean done = TRUE;
2368
GDK_THREADS_ENTER ();
2370
/* verify that we still have a current directory */
2371
if (G_LIKELY (view->current_directory != NULL))
2373
/* determine the current cursor (fallback to root node) */
2374
gtk_tree_view_get_cursor (GTK_TREE_VIEW (view), &path, NULL);
2375
if (G_UNLIKELY (path == NULL))
2376
path = gtk_tree_path_new_first ();
2378
/* look for the closest ancestor in the whole tree, starting from the current path */
2379
for (; gtk_tree_path_get_depth (path) > 0; gtk_tree_path_up (path))
2381
/* be sure to start from the first path at that level */
2382
while (gtk_tree_path_prev (path))
2385
/* try to find the closest ancestor relative to the current path */
2386
if (thunar_tree_view_find_closest_ancestor (view, path, &ancestor, &done))
2388
/* expand to the best ancestor if not an exact match */
2389
if (G_LIKELY (!done))
2391
/* just expand everything up to the row and its children */
2392
gtk_tree_view_expand_to_path (GTK_TREE_VIEW (view), ancestor);
2394
else if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (view), ancestor))
2396
/* just expand everything up to the row, but not the children */
2397
parent = gtk_tree_path_copy (ancestor);
2398
if (gtk_tree_path_up (parent))
2399
gtk_tree_view_expand_to_path (GTK_TREE_VIEW (view), parent);
2400
gtk_tree_path_free (parent);
2403
/* place cursor on the ancestor */
2404
gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), ancestor, NULL, FALSE);
2406
/* release the ancestor path */
2407
gtk_tree_path_free (ancestor);
2414
/* release the tree path */
2415
gtk_tree_path_free (path);
2418
GDK_THREADS_LEAVE ();
2426
thunar_tree_view_cursor_idle_destroy (gpointer user_data)
2428
THUNAR_TREE_VIEW (user_data)->cursor_idle_id = 0;
2434
thunar_tree_view_drag_scroll_timer (gpointer user_data)
2436
ThunarTreeView *view = THUNAR_TREE_VIEW (user_data);
2437
GtkAdjustment *vadjustment;
2438
GtkTreePath *start_path;
2439
GtkTreePath *end_path;
2445
GDK_THREADS_ENTER ();
2447
/* verify that we are realized */
2448
if (gtk_widget_get_realized (GTK_WIDGET (view)))
2450
/* determine pointer location and window geometry */
2451
gdk_window_get_pointer (GTK_WIDGET (view)->window, NULL, &y, NULL);
2452
gdk_window_get_geometry (GTK_WIDGET (view)->window, NULL, NULL, NULL, &h, NULL);
2454
/* check if we are near the edge */
2455
offset = y - (2 * 20);
2456
if (G_UNLIKELY (offset > 0))
2457
offset = MAX (y - (h - 2 * 20), 0);
2459
/* change the vertical adjustment appropriately */
2460
if (G_UNLIKELY (offset != 0))
2462
/* determine the vertical adjustment */
2463
vadjustment = gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (view));
2465
/* determine the new value */
2466
value = CLAMP (vadjustment->value + 2 * offset, vadjustment->lower, vadjustment->upper - vadjustment->page_size);
2468
/* check if we have a new value */
2469
if (G_UNLIKELY (vadjustment->value != value))
2471
/* apply the new value */
2472
gtk_adjustment_set_value (vadjustment, value);
2474
/* drop any pending expand timer source, as its confusing
2475
* if a path is expanded while scrolling through the view.
2476
* reschedule it if the drag dest path is still visible.
2478
if (G_UNLIKELY (view->expand_timer_id != 0))
2480
/* drop the current expand timer source */
2481
g_source_remove (view->expand_timer_id);
2483
/* determine the visible range of the tree view */
2484
if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (view), &start_path, &end_path))
2486
/* determine the drag dest row */
2487
gtk_tree_view_get_drag_dest_row (GTK_TREE_VIEW (view), &path, NULL);
2488
if (G_LIKELY (path != NULL))
2490
/* check if the drag dest row is currently visible */
2491
if (gtk_tree_path_compare (path, start_path) >= 0 && gtk_tree_path_compare (path, end_path) <= 0)
2493
/* schedule a new expand timer to expand the drag dest row */
2494
view->expand_timer_id = g_timeout_add_full (G_PRIORITY_LOW, THUNAR_TREE_VIEW_EXPAND_TIMEOUT,
2495
thunar_tree_view_expand_timer, view,
2496
thunar_tree_view_expand_timer_destroy);
2499
/* release the drag dest row */
2500
gtk_tree_path_free (path);
2503
/* release the start/end paths */
2504
gtk_tree_path_free (start_path);
2505
gtk_tree_path_free (end_path);
2512
GDK_THREADS_LEAVE ();
2520
thunar_tree_view_drag_scroll_timer_destroy (gpointer user_data)
2522
THUNAR_TREE_VIEW (user_data)->drag_scroll_timer_id = 0;
2528
thunar_tree_view_expand_timer (gpointer user_data)
2530
ThunarTreeView *view = THUNAR_TREE_VIEW (user_data);
2533
GDK_THREADS_ENTER ();
2535
/* cancel the drag autoscroll timer when expanding a row */
2536
if (G_UNLIKELY (view->drag_scroll_timer_id != 0))
2537
g_source_remove (view->drag_scroll_timer_id);
2539
/* determine the drag dest row */
2540
gtk_tree_view_get_drag_dest_row (GTK_TREE_VIEW (view), &path, NULL);
2541
if (G_LIKELY (path != NULL))
2543
/* expand the drag dest row */
2544
gtk_tree_view_expand_row (GTK_TREE_VIEW (view), path, FALSE);
2545
gtk_tree_path_free (path);
2548
GDK_THREADS_LEAVE ();
2556
thunar_tree_view_expand_timer_destroy (gpointer user_data)
2558
THUNAR_TREE_VIEW (user_data)->expand_timer_id = 0;
2563
static ThunarTreeViewMountData *
2564
thunar_tree_view_mount_data_new (ThunarTreeView *view,
2566
gboolean open_after_mounting,
2569
ThunarTreeViewMountData *data;
2571
data = g_slice_new0 (ThunarTreeViewMountData);
2572
data->path = path == NULL ? NULL : gtk_tree_path_copy (path);
2573
data->view = g_object_ref (view);
2574
data->open_after_mounting = open_after_mounting;
2575
data->open_in = open_in;
2583
thunar_tree_view_mount_data_free (ThunarTreeViewMountData *data)
2585
_thunar_return_if_fail (data != NULL && THUNAR_IS_TREE_VIEW (data->view));
2587
if (data->path != NULL)
2588
gtk_tree_path_free (data->path);
2589
g_object_unref (data->view);
2590
g_slice_free (ThunarTreeViewMountData, data);
2596
* thunar_tree_view_get_show_hidden:
2597
* @view : a #ThunarTreeView.
2599
* Returns %TRUE if hidden and backup folders are
2602
* Return value: %TRUE if hidden folders are shown.
2605
thunar_tree_view_get_show_hidden (ThunarTreeView *view)
2607
_thunar_return_val_if_fail (THUNAR_IS_TREE_VIEW (view), FALSE);
2608
return view->show_hidden;
2614
* thunar_tree_view_set_show_hidden:
2615
* @view : a #ThunarTreeView.
2616
* @show_hidden : %TRUE to show hidden and backup folders.
2618
* If @show_hidden is %TRUE, @view will shown hidden and
2619
* backup folders. Else, these folders will be hidden.
2622
thunar_tree_view_set_show_hidden (ThunarTreeView *view,
2623
gboolean show_hidden)
2625
_thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
2626
_thunar_return_if_fail (THUNAR_IS_TREE_MODEL (view->model));
2628
/* normalize the value */
2629
show_hidden = !!show_hidden;
2631
/* check if we have a new setting */
2632
if (view->show_hidden != show_hidden)
2634
/* apply the new setting */
2635
view->show_hidden = show_hidden;
2637
/* update the model */
2638
thunar_tree_model_refilter (view->model);
2640
/* notify listeners */
2641
g_object_notify (G_OBJECT (view), "show-hidden");
2648
* thunar_tree_view_new:
2650
* Allocates a new #ThunarTreeView instance.
2652
* Return value: the newly allocated #ThunarTreeView instance.
2655
thunar_tree_view_new (void)
2657
return g_object_new (THUNAR_TYPE_TREE_VIEW, NULL);