~ubuntu-branches/ubuntu/utopic/thunar/utopic-proposed

« back to all changes in this revision

Viewing changes to .pc/menu-icon-tweaks.patch/thunar/thunar-tree-view.c

  • Committer: Package Import Robot
  • Author(s): Jackson Doak
  • Date: 2014-04-05 08:22:40 UTC
  • Revision ID: package-import@ubuntu.com-20140405082240-b3iiyypyyo3uliy3
Tags: 1.6.3-1ubuntu5
* Add git-save-keyboard-shortcuts.patch. LP: #1186846
  - Keyboard shortcuts save when changed rather than when thunar is closed
* Add menu-icon-tweaks.patch. LP: #1271861
  - Don't use generic folder icons for open with default application
  - Add icons for open in new tab, new window
* Add git-force-toolbr-icons.patch
  - Fix for "Home" and "Open Parent" icons going missing from toolbar

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* vi:set et ai sw=2 sts=2 ts=2: */
 
2
/*-
 
3
 * Copyright (c) 2006      Benedikt Meurer <benny@xfce.org>
 
4
 * Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org>
 
5
 *
 
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.
 
10
 *
 
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.
 
15
 *
 
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.
 
20
 */
 
21
 
 
22
#ifdef HAVE_CONFIG_H
 
23
#include <config.h>
 
24
#endif
 
25
 
 
26
#include <gdk/gdkkeysyms.h>
 
27
 
 
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>
 
46
 
 
47
 
 
48
 
 
49
/* the timeout (in ms) until the drag dest row will be expanded */
 
50
#define THUNAR_TREE_VIEW_EXPAND_TIMEOUT (750)
 
51
 
 
52
 
 
53
 
 
54
typedef struct _ThunarTreeViewMountData ThunarTreeViewMountData;
 
55
 
 
56
 
 
57
 
 
58
/* Property identifiers */
 
59
enum
 
60
{
 
61
  PROP_0,
 
62
  PROP_CURRENT_DIRECTORY,
 
63
  PROP_SHOW_HIDDEN,
 
64
};
 
65
 
 
66
/* Signal identifiers */
 
67
enum
 
68
{
 
69
  DELETE_SELECTED_FILES,
 
70
  LAST_SIGNAL,
 
71
};
 
72
 
 
73
/* Identifiers for DnD target types */
 
74
enum
 
75
{
 
76
  TARGET_TEXT_URI_LIST,
 
77
};
 
78
 
 
79
 
 
80
 
 
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,
 
84
                                                                               guint                    prop_id,
 
85
                                                                               GValue                  *value,
 
86
                                                                               GParamSpec              *pspec);
 
87
static void                     thunar_tree_view_set_property                 (GObject                 *object,
 
88
                                                                               guint                    prop_id,
 
89
                                                                               const GValue            *value,
 
90
                                                                               GParamSpec              *pspec);
 
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,
 
102
                                                                               gint                     x,
 
103
                                                                               gint                     y,
 
104
                                                                               GtkSelectionData        *selection_data,
 
105
                                                                               guint                    info,
 
106
                                                                               guint                    time);
 
107
static gboolean                 thunar_tree_view_drag_drop                    (GtkWidget               *widget,
 
108
                                                                               GdkDragContext          *context,
 
109
                                                                               gint                     x,
 
110
                                                                               gint                     y,
 
111
                                                                               guint                    time);
 
112
static gboolean                 thunar_tree_view_drag_motion                  (GtkWidget               *widget,
 
113
                                                                               GdkDragContext          *context,
 
114
                                                                               gint                     x,
 
115
                                                                               gint                     y,
 
116
                                                                               guint                    time);
 
117
static void                     thunar_tree_view_drag_leave                   (GtkWidget               *widget,
 
118
                                                                               GdkDragContext          *context,
 
119
                                                                               guint                    time);
 
120
static gboolean                 thunar_tree_view_popup_menu                   (GtkWidget               *widget);
 
121
static void                     thunar_tree_view_row_activated                (GtkTreeView             *tree_view,
 
122
                                                                               GtkTreePath             *path,
 
123
                                                                               GtkTreeViewColumn       *column);
 
124
static gboolean                 thunar_tree_view_test_expand_row              (GtkTreeView             *tree_view,
 
125
                                                                               GtkTreeIter             *iter,
 
126
                                                                               GtkTreePath             *path);
 
127
static void                     thunar_tree_view_row_collapsed                (GtkTreeView             *tree_view,
 
128
                                                                               GtkTreeIter             *iter,
 
129
                                                                               GtkTreePath             *path);
 
130
static gboolean                 thunar_tree_view_delete_selected_files        (ThunarTreeView          *view);
 
131
static void                     thunar_tree_view_context_menu                 (ThunarTreeView          *view,
 
132
                                                                               GdkEventButton          *event,
 
133
                                                                               GtkTreeModel            *model,
 
134
                                                                               GtkTreeIter             *iter);
 
135
static GdkDragAction            thunar_tree_view_get_dest_actions             (ThunarTreeView          *view,
 
136
                                                                               GdkDragContext          *context,
 
137
                                                                               gint                     x,
 
138
                                                                               gint                     y,
 
139
                                                                               guint                    time,
 
140
                                                                               ThunarFile             **file_return);
 
141
static gboolean                 thunar_tree_view_find_closest_ancestor        (ThunarTreeView          *view,
 
142
                                                                               GtkTreePath             *path,
 
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,
 
158
                                                                               const GError            *error,
 
159
                                                                               gpointer                 user_data);
 
160
static void                     thunar_tree_view_mount                        (ThunarTreeView          *view,
 
161
                                                                               gboolean                 open_after_mounting,
 
162
                                                                               guint                    open_in);
 
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,
 
173
                                                                               GList                   *path_list,
 
174
                                                                               ThunarTreeView          *view);
 
175
static gboolean                 thunar_tree_view_visible_func                 (ThunarTreeModel         *model,
 
176
                                                                               ThunarFile              *file,
 
177
                                                                               gpointer                 user_data);
 
178
static gboolean                 thunar_tree_view_selection_func               (GtkTreeSelection        *selection,
 
179
                                                                               GtkTreeModel            *model,
 
180
                                                                               GtkTreePath             *path,
 
181
                                                                               gboolean                 path_currently_selected,
 
182
                                                                               gpointer                 user_data);
 
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,
 
190
                                                                               GtkTreePath             *path,
 
191
                                                                               gboolean                 open_after_mounting,
 
192
                                                                               guint                    open_in);
 
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);
 
197
 
 
198
 
 
199
 
 
200
struct _ThunarTreeViewClass
 
201
{
 
202
  GtkTreeViewClass __parent__;
 
203
 
 
204
  /* signals */
 
205
  gboolean (*delete_selected_files) (ThunarTreeView *view);
 
206
};
 
207
 
 
208
struct _ThunarTreeView
 
209
{
 
210
  GtkTreeView              __parent__;
 
211
  ThunarClipboardManager *clipboard;
 
212
  ThunarPreferences      *preferences;
 
213
  GtkCellRenderer        *icon_renderer;
 
214
  ThunarFile             *current_directory;
 
215
  ThunarTreeModel        *model;
 
216
 
 
217
  ThunarxProviderFactory *provider_factory;
 
218
 
 
219
  /* whether to display hidden/backup files */
 
220
  guint                   show_hidden : 1;
 
221
 
 
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 */
 
226
 
 
227
  /* the "new-files" closure, which is used to
 
228
   * open newly created directories once done.
 
229
   */
 
230
  GClosure               *new_files_closure;
 
231
 
 
232
  /* the currently pressed mouse button, set in the
 
233
   * button-press-event handler if the associated
 
234
   * button-release-event should activate.
 
235
   */
 
236
  gint                    pressed_button;
 
237
 
 
238
  /* id of the signal used to queue a resize on the
 
239
   * column whenever the shortcuts icon size is changed.
 
240
   */
 
241
  gulong                  queue_resize_signal_id;
 
242
 
 
243
  /* set cursor to current directory idle source */
 
244
  guint                   cursor_idle_id;
 
245
 
 
246
  /* autoscroll during drag timer source */
 
247
  guint                   drag_scroll_timer_id;
 
248
 
 
249
  /* expand drag dest row timer source */
 
250
  guint                   expand_timer_id;
 
251
};
 
252
 
 
253
enum
 
254
{
 
255
  OPEN_IN_VIEW,
 
256
  OPEN_IN_WINDOW,
 
257
  OPEN_IN_TAB
 
258
};
 
259
 
 
260
struct _ThunarTreeViewMountData
 
261
{
 
262
  ThunarTreeView *view;
 
263
  GtkTreePath    *path;
 
264
  gboolean        open_after_mounting;
 
265
  guint           open_in;
 
266
};
 
267
 
 
268
 
 
269
 
 
270
/* Target types for dropping into the tree view */
 
271
static const GtkTargetEntry drop_targets[] = {
 
272
  { "text/uri-list", 0, TARGET_TEXT_URI_LIST },
 
273
};
 
274
 
 
275
 
 
276
 
 
277
static guint tree_view_signals[LAST_SIGNAL];
 
278
 
 
279
 
 
280
 
 
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))
 
283
 
 
284
 
 
285
 
 
286
static void
 
287
thunar_tree_view_class_init (ThunarTreeViewClass *klass)
 
288
{
 
289
  GtkTreeViewClass *gtktree_view_class;
 
290
  GtkWidgetClass   *gtkwidget_class;
 
291
  GtkBindingSet    *binding_set;
 
292
  GObjectClass     *gobject_class;
 
293
 
 
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;
 
298
 
 
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;
 
309
 
 
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;
 
314
 
 
315
  klass->delete_selected_files = thunar_tree_view_delete_selected_files;
 
316
 
 
317
  /* Override ThunarNavigator's properties */
 
318
  g_object_class_override_property (gobject_class, PROP_CURRENT_DIRECTORY, "current-directory");
 
319
 
 
320
  /**
 
321
   * ThunarTreeView:show-hidden:
 
322
   *
 
323
   * Whether to display hidden and backup folders
 
324
   * in this #ThunarTreeView.
 
325
   **/
 
326
  g_object_class_install_property (gobject_class,
 
327
                                   PROP_SHOW_HIDDEN,
 
328
                                   g_param_spec_boolean ("show-hidden",
 
329
                                                         "show-hidden",
 
330
                                                         "show-hidden",
 
331
                                                         FALSE,
 
332
                                                         EXO_PARAM_READWRITE));
 
333
 
 
334
  /**
 
335
   * ThunarTreeView::delete-selected-files:
 
336
   * @tree_view : a #ThunarTreeView.
 
337
   *
 
338
   * Emitted whenever the user presses the Delete key. This
 
339
   * is an internal signal used to bind the action to keys.
 
340
   **/
 
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,
 
348
                  G_TYPE_BOOLEAN, 0);
 
349
 
 
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);
 
357
}
 
358
 
 
359
 
 
360
 
 
361
static void
 
362
thunar_tree_view_navigator_init (ThunarNavigatorIface *iface)
 
363
{
 
364
  iface->get_current_directory = thunar_tree_view_get_current_directory;
 
365
  iface->set_current_directory = thunar_tree_view_set_current_directory;
 
366
}
 
367
 
 
368
 
 
369
 
 
370
static void
 
371
thunar_tree_view_init (ThunarTreeView *view)
 
372
{
 
373
  GtkTreeViewColumn *column;
 
374
  GtkTreeSelection  *selection;
 
375
  GtkCellRenderer   *renderer;
 
376
 
 
377
  /* grab a reference on the provider factory */
 
378
  view->provider_factory = thunarx_provider_factory_get_default ();
 
379
 
 
380
  /* grab a reference on the preferences; be sure to redraw the view
 
381
   * whenever the "tree-icon-emblems" preference changes.
 
382
   */
 
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);
 
385
 
 
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));
 
390
 
 
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);
 
394
 
 
395
  /* allocate a single column for our renderers */
 
396
  column = g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
 
397
                         "reorderable", FALSE,
 
398
                         "resizable", FALSE,
 
399
                         "sizing", GTK_TREE_VIEW_COLUMN_AUTOSIZE,
 
400
                         "spacing", 2,
 
401
                         NULL);
 
402
  gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
 
403
 
 
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);
 
407
 
 
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,
 
414
                                       NULL);
 
415
 
 
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.
 
418
   */
 
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");
 
421
 
 
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,
 
428
                                       NULL);
 
429
 
 
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);
 
434
 
 
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);
 
438
}
 
439
 
 
440
 
 
441
 
 
442
static void
 
443
thunar_tree_view_finalize (GObject *object)
 
444
{
 
445
  ThunarTreeView *view = THUNAR_TREE_VIEW (object);
 
446
 
 
447
  /* release drop path list (if drag_leave wasn't called) */
 
448
  thunar_g_file_list_free (view->drop_file_list);
 
449
 
 
450
  /* release the provider factory */
 
451
  g_object_unref (G_OBJECT (view->provider_factory));
 
452
 
 
453
  /* disconnect the queue resize signal handler */
 
454
  g_signal_handler_disconnect (G_OBJECT (view->preferences), view->queue_resize_signal_id);
 
455
 
 
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);
 
459
 
 
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);
 
463
 
 
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);
 
467
 
 
468
  /* reset the current-directory property */
 
469
  thunar_navigator_set_current_directory (THUNAR_NAVIGATOR (view), NULL);
 
470
 
 
471
  /* release our reference on the preferences */
 
472
  g_object_unref (G_OBJECT (view->preferences));
 
473
 
 
474
  /* disconnect from the default tree model */
 
475
  g_object_unref (G_OBJECT (view->model));
 
476
 
 
477
  /* drop any existing "new-files" closure */
 
478
  if (G_UNLIKELY (view->new_files_closure != NULL))
 
479
    {
 
480
      g_closure_invalidate (view->new_files_closure);
 
481
      g_closure_unref (view->new_files_closure);
 
482
    }
 
483
 
 
484
  (*G_OBJECT_CLASS (thunar_tree_view_parent_class)->finalize) (object);
 
485
}
 
486
 
 
487
 
 
488
 
 
489
static void
 
490
thunar_tree_view_get_property (GObject    *object,
 
491
                               guint       prop_id,
 
492
                               GValue     *value,
 
493
                               GParamSpec *pspec)
 
494
{
 
495
  switch (prop_id)
 
496
    {
 
497
    case PROP_CURRENT_DIRECTORY:
 
498
      g_value_set_object (value, thunar_navigator_get_current_directory (THUNAR_NAVIGATOR (object)));
 
499
      break;
 
500
 
 
501
    case PROP_SHOW_HIDDEN:
 
502
      g_value_set_boolean (value, thunar_tree_view_get_show_hidden (THUNAR_TREE_VIEW (object)));
 
503
      break;
 
504
 
 
505
    default:
 
506
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
507
      break;
 
508
    }
 
509
}
 
510
 
 
511
 
 
512
 
 
513
static void
 
514
thunar_tree_view_set_property (GObject      *object,
 
515
                               guint         prop_id,
 
516
                               const GValue *value,
 
517
                               GParamSpec   *pspec)
 
518
{
 
519
  switch (prop_id)
 
520
    {
 
521
    case PROP_CURRENT_DIRECTORY:
 
522
      thunar_navigator_set_current_directory (THUNAR_NAVIGATOR (object), g_value_get_object (value));
 
523
      break;
 
524
 
 
525
    case PROP_SHOW_HIDDEN:
 
526
      thunar_tree_view_set_show_hidden (THUNAR_TREE_VIEW (object), g_value_get_boolean (value));
 
527
      break;
 
528
 
 
529
    default:
 
530
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
531
      break;
 
532
    }
 
533
}
 
534
 
 
535
 
 
536
 
 
537
static ThunarFile*
 
538
thunar_tree_view_get_current_directory (ThunarNavigator *navigator)
 
539
{
 
540
  return THUNAR_TREE_VIEW (navigator)->current_directory;
 
541
}
 
542
 
 
543
 
 
544
 
 
545
static void
 
546
thunar_tree_view_set_current_directory (ThunarNavigator *navigator,
 
547
                                        ThunarFile      *current_directory)
 
548
{
 
549
  ThunarTreeView *view = THUNAR_TREE_VIEW (navigator);
 
550
  ThunarFile     *file;
 
551
  ThunarFile     *file_parent;
 
552
  gboolean        needs_refiltering = FALSE;
 
553
 
 
554
  /* check if we already use that directory */
 
555
  if (G_UNLIKELY (view->current_directory == current_directory))
 
556
    return;
 
557
 
 
558
  /* check if we have an active directory */
 
559
  if (G_LIKELY (view->current_directory != NULL))
 
560
    {
 
561
      /* update the filter if the old current directory, or one of it's parents, is hidden */
 
562
      if (!view->show_hidden)
 
563
        {
 
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)
 
566
            {
 
567
              /* check if this file is hidden */
 
568
              if (thunar_file_is_hidden (file))
 
569
                {
 
570
                  /* schedule an update of the filter after the current directory has been changed */
 
571
                  needs_refiltering = TRUE;
 
572
 
 
573
                  /* release the file */
 
574
                  g_object_unref (G_OBJECT (file));
 
575
 
 
576
                  break;
 
577
                }
 
578
 
 
579
              /* get the file parent */
 
580
              file_parent = thunar_file_get_parent (file, NULL);
 
581
 
 
582
              /* release the file */
 
583
              g_object_unref (G_OBJECT (file));
 
584
            }
 
585
        }
 
586
 
 
587
      /* disconnect from the previous current directory */
 
588
      g_object_unref (G_OBJECT (view->current_directory));
 
589
    }
 
590
 
 
591
  /* activate the new current directory */
 
592
  view->current_directory = current_directory;
 
593
 
 
594
  /* connect to the new current directory */
 
595
  if (G_LIKELY (current_directory != NULL))
 
596
    {
 
597
      /* take a reference on the directory */
 
598
      g_object_ref (G_OBJECT (current_directory));
 
599
 
 
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
 
602
       * anyway */
 
603
      if (!needs_refiltering && !view->show_hidden)
 
604
        {
 
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)
 
607
            {
 
608
              /* check if this file is hidden */
 
609
              if (thunar_file_is_hidden (file))
 
610
                {
 
611
                  /* update the filter */
 
612
                  thunar_tree_model_refilter (view->model);
 
613
 
 
614
                  /* release the file */
 
615
                  g_object_unref (G_OBJECT (file));
 
616
 
 
617
                  break;
 
618
                }
 
619
 
 
620
              /* get the file parent */
 
621
              file_parent = thunar_file_get_parent (file, NULL);
 
622
 
 
623
              /* release the file */
 
624
              g_object_unref (G_OBJECT (file));
 
625
            }
 
626
        }
 
627
 
 
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);
 
631
 
 
632
      /* drop any existing "new-files" closure */
 
633
      if (G_UNLIKELY (view->new_files_closure != NULL))
 
634
        {
 
635
          g_closure_invalidate (view->new_files_closure);
 
636
          g_closure_unref (view->new_files_closure);
 
637
          view->new_files_closure = NULL;
 
638
        }
 
639
    }
 
640
 
 
641
  /* refilter the model if necessary */
 
642
  if (needs_refiltering)
 
643
    thunar_tree_model_refilter (view->model);
 
644
 
 
645
  /* notify listeners */
 
646
  g_object_notify (G_OBJECT (view), "current-directory");
 
647
}
 
648
 
 
649
 
 
650
 
 
651
static void
 
652
thunar_tree_view_realize (GtkWidget *widget)
 
653
{
 
654
  ThunarTreeView *view = THUNAR_TREE_VIEW (widget);
 
655
  GdkDisplay     *display;
 
656
 
 
657
  /* let the parent class realize the widget */
 
658
  (*GTK_WIDGET_CLASS (thunar_tree_view_parent_class)->realize) (widget);
 
659
 
 
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);
 
663
}
 
664
 
 
665
 
 
666
 
 
667
static void
 
668
thunar_tree_view_unrealize (GtkWidget *widget)
 
669
{
 
670
  ThunarTreeView *view = THUNAR_TREE_VIEW (widget);
 
671
 
 
672
  /* release the clipboard manager reference */
 
673
  g_object_unref (G_OBJECT (view->clipboard));
 
674
  view->clipboard = NULL;
 
675
 
 
676
  /* let the parent class unrealize the widget */
 
677
  (*GTK_WIDGET_CLASS (thunar_tree_view_parent_class)->unrealize) (widget);
 
678
}
 
679
 
 
680
 
 
681
 
 
682
static gboolean
 
683
thunar_tree_view_button_press_event (GtkWidget      *widget,
 
684
                                     GdkEventButton *event)
 
685
{
 
686
  ThunarTreeView *view = THUNAR_TREE_VIEW (widget);
 
687
  GtkTreePath    *path;
 
688
  GtkTreeIter     iter;
 
689
  gboolean        result;
 
690
 
 
691
  /* reset the pressed button state */
 
692
  view->pressed_button = -1;
 
693
 
 
694
  /* completely ignore double middle clicks */
 
695
  if (event->type == GDK_2BUTTON_PRESS && event->button == 2)
 
696
    return TRUE;
 
697
 
 
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);
 
700
 
 
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))
 
703
    {
 
704
      /* check if we should popup the context menu */
 
705
      if (G_UNLIKELY (event->button == 3 && event->type == GDK_BUTTON_PRESS))
 
706
        {
 
707
          /* determine the iterator for the path */
 
708
          if (gtk_tree_model_get_iter (GTK_TREE_MODEL (view->model), &iter, path))
 
709
            {
 
710
              /* popup the context menu */
 
711
              thunar_tree_view_context_menu (view, event, GTK_TREE_MODEL (view->model), &iter);
 
712
 
 
713
              /* we effectively handled the event */
 
714
              result = TRUE;
 
715
            }
 
716
        }
 
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)
 
720
        {
 
721
          /* remember the button as pressed and handled it in the release handler */
 
722
          view->pressed_button = event->button;
 
723
        }
 
724
 
 
725
      /* release the path */
 
726
      gtk_tree_path_free (path);
 
727
    }
 
728
 
 
729
  return result;
 
730
}
 
731
 
 
732
 
 
733
 
 
734
static gboolean
 
735
thunar_tree_view_button_release_event (GtkWidget      *widget,
 
736
                                       GdkEventButton *event)
 
737
{
 
738
  ThunarTreeView *view = THUNAR_TREE_VIEW (widget);
 
739
  gboolean        in_tab;
 
740
 
 
741
  /* check if we have an event matching the pressed button state */
 
742
  if (G_LIKELY (view->pressed_button == (gint) event->button))
 
743
    {
 
744
      /* check if we should simply open or open in new window */
 
745
      if (G_LIKELY (event->button == 1))
 
746
        {
 
747
          thunar_tree_view_action_open (view);
 
748
        }
 
749
      else if (G_UNLIKELY (event->button == 2))
 
750
        {
 
751
          g_object_get (view->preferences, "misc-middle-click-in-tab", &in_tab, NULL);
 
752
          if (in_tab)
 
753
            thunar_tree_view_action_open_in_new_tab (view);
 
754
          else
 
755
            thunar_tree_view_action_open_in_new_window (view);
 
756
        }
 
757
    }
 
758
 
 
759
  /* reset the pressed button state */
 
760
  view->pressed_button = -1;
 
761
 
 
762
  /* call the parent's release event handler */
 
763
  return (*GTK_WIDGET_CLASS (thunar_tree_view_parent_class)->button_release_event) (widget, event);
 
764
}
 
765
 
 
766
 
 
767
 
 
768
static void
 
769
thunar_tree_view_drag_data_received (GtkWidget        *widget,
 
770
                                     GdkDragContext   *context,
 
771
                                     gint              x,
 
772
                                     gint              y,
 
773
                                     GtkSelectionData *selection_data,
 
774
                                     guint             info,
 
775
                                     guint             timestamp)
 
776
{
 
777
  ThunarTreeView *view = THUNAR_TREE_VIEW (widget);
 
778
  GdkDragAction   actions;
 
779
  GdkDragAction   action;
 
780
  ThunarFile     *file;
 
781
  gboolean        succeed = FALSE;
 
782
 
 
783
  /* check if don't already know the drop data */
 
784
  if (G_LIKELY (!view->drop_data_ready))
 
785
    {
 
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);
 
789
 
 
790
      /* reset the state */
 
791
      view->drop_data_ready = TRUE;
 
792
    }
 
793
 
 
794
  /* check if the data was droppped */
 
795
  if (G_UNLIKELY (view->drop_occurred))
 
796
    {
 
797
      /* reset the state */
 
798
      view->drop_occurred = FALSE;
 
799
 
 
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))
 
803
        {
 
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)
 
807
                 : context->action;
 
808
 
 
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);
 
812
        }
 
813
 
 
814
      /* release the file reference */
 
815
      if (G_LIKELY (file != NULL))
 
816
        g_object_unref (G_OBJECT (file));
 
817
 
 
818
      /* tell the peer that we handled the drop */
 
819
      gtk_drag_finish (context, succeed, FALSE, timestamp);
 
820
 
 
821
      /* disable the highlighting and release the drag data */
 
822
      thunar_tree_view_drag_leave (GTK_WIDGET (view), context, timestamp);
 
823
    }
 
824
}
 
825
 
 
826
 
 
827
 
 
828
static gboolean
 
829
thunar_tree_view_drag_drop (GtkWidget      *widget,
 
830
                            GdkDragContext *context,
 
831
                            gint            x,
 
832
                            gint            y,
 
833
                            guint           timestamp)
 
834
{
 
835
  ThunarTreeView *view = THUNAR_TREE_VIEW (widget);
 
836
  GdkAtom         target;
 
837
 
 
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")))
 
841
    {
 
842
      /* set state so the drag-data-received handler
 
843
       * knows that this is really a drop this time.
 
844
       */
 
845
      view->drop_occurred = TRUE;
 
846
 
 
847
      /* request the drag data from the source. */
 
848
      gtk_drag_get_data (widget, context, target, timestamp);
 
849
    }
 
850
  else
 
851
    {
 
852
      /* we cannot handle the drop */
 
853
      return FALSE;
 
854
    }
 
855
 
 
856
  return TRUE;
 
857
}
 
858
 
 
859
 
 
860
 
 
861
static gboolean
 
862
thunar_tree_view_drag_motion (GtkWidget      *widget,
 
863
                              GdkDragContext *context,
 
864
                              gint            x,
 
865
                              gint            y,
 
866
                              guint           timestamp)
 
867
{
 
868
  ThunarTreeView *view = THUNAR_TREE_VIEW (widget);
 
869
  GdkAtom         target;
 
870
 
 
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")))
 
874
    {
 
875
      /* reset the "drop-file" of the icon renderer */
 
876
      g_object_set (G_OBJECT (view->icon_renderer), "drop-file", NULL, NULL);
 
877
 
 
878
      /* we cannot handle the drop */
 
879
      return FALSE;
 
880
    }
 
881
 
 
882
  /* request the drop data on-demand (if we don't have it already) */
 
883
  if (G_UNLIKELY (!view->drop_data_ready))
 
884
    {
 
885
      /* reset the "drop-file" of the icon renderer */
 
886
      g_object_set (G_OBJECT (view->icon_renderer), "drop-file", NULL, NULL);
 
887
 
 
888
      /* request the drag data from the source */
 
889
      gtk_drag_get_data (widget, context, target, timestamp);
 
890
 
 
891
      /* tell Gdk that we don't know whether we can drop */
 
892
      gdk_drag_status (context, 0, timestamp);
 
893
    }
 
894
  else
 
895
    {
 
896
      /* check if we can drop here */
 
897
      thunar_tree_view_get_dest_actions (view, context, x, y, timestamp, NULL);
 
898
    }
 
899
 
 
900
  /* start the drag autoscroll timer if not already running */
 
901
  if (G_UNLIKELY (view->drag_scroll_timer_id == 0))
 
902
    {
 
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);
 
906
    }
 
907
 
 
908
  return TRUE;
 
909
}
 
910
 
 
911
 
 
912
 
 
913
static void
 
914
thunar_tree_view_drag_leave (GtkWidget      *widget,
 
915
                             GdkDragContext *context,
 
916
                             guint           timestamp)
 
917
{
 
918
  ThunarTreeView *view = THUNAR_TREE_VIEW (widget);
 
919
 
 
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);
 
923
 
 
924
  /* reset the "drop-file" of the icon renderer */
 
925
  g_object_set (G_OBJECT (view->icon_renderer), "drop-file", NULL, NULL);
 
926
 
 
927
  /* reset the "drop data ready" status and free the URI list */
 
928
  if (G_LIKELY (view->drop_data_ready))
 
929
    {
 
930
      thunar_g_file_list_free (view->drop_file_list);
 
931
      view->drop_data_ready = FALSE;
 
932
      view->drop_file_list = NULL;
 
933
    }
 
934
 
 
935
  /* call the parent's handler */
 
936
  (*GTK_WIDGET_CLASS (thunar_tree_view_parent_class)->drag_leave) (widget, context, timestamp);
 
937
}
 
938
 
 
939
 
 
940
 
 
941
static gboolean
 
942
thunar_tree_view_popup_menu (GtkWidget *widget)
 
943
{
 
944
  GtkTreeSelection *selection;
 
945
  ThunarTreeView   *view = THUNAR_TREE_VIEW (widget);
 
946
  GtkTreeModel     *model;
 
947
  GtkTreeIter       iter;
 
948
 
 
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))
 
952
    {
 
953
      /* popup the context menu */
 
954
      thunar_tree_view_context_menu (view, NULL, model, &iter);
 
955
      return TRUE;
 
956
    }
 
957
  else if (GTK_WIDGET_CLASS (thunar_tree_view_parent_class)->popup_menu != NULL)
 
958
    {
 
959
      /* call the parent's "popup-menu" handler */
 
960
      return (*GTK_WIDGET_CLASS (thunar_tree_view_parent_class)->popup_menu) (widget);
 
961
    }
 
962
 
 
963
  return FALSE;
 
964
}
 
965
 
 
966
 
 
967
 
 
968
static void
 
969
thunar_tree_view_row_activated (GtkTreeView       *tree_view,
 
970
                                GtkTreePath       *path,
 
971
                                GtkTreeViewColumn *column)
 
972
{
 
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);
 
976
 
 
977
  /* toggle the expanded state of the activated row... */
 
978
  if (gtk_tree_view_row_expanded (tree_view, path))
 
979
    {
 
980
      gtk_tree_view_collapse_row (tree_view, path);
 
981
    }
 
982
  else
 
983
    {
 
984
      /* expand the row, but open it if mounted */
 
985
      if (gtk_tree_view_expand_row (tree_view, path, FALSE))
 
986
        {
 
987
          /* ...open the selected folder */
 
988
          thunar_tree_view_action_open (THUNAR_TREE_VIEW (tree_view));
 
989
        }
 
990
    }
 
991
}
 
992
 
 
993
 
 
994
 
 
995
static gboolean
 
996
thunar_tree_view_test_expand_row (GtkTreeView *tree_view,
 
997
                                  GtkTreeIter *iter,
 
998
                                  GtkTreePath *path)
 
999
{
 
1000
  ThunarTreeViewMountData *data;
 
1001
  GMountOperation         *mount_operation;
 
1002
  ThunarTreeView          *view = THUNAR_TREE_VIEW (tree_view);
 
1003
  gboolean                 expandable = TRUE;
 
1004
  ThunarDevice            *device;
 
1005
 
 
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);
 
1008
 
 
1009
  /* check if we have a device */
 
1010
  if (G_UNLIKELY (device != NULL))
 
1011
    {
 
1012
      /* check if we need to mount the device first */
 
1013
      if (!thunar_device_is_mounted (device))
 
1014
        {
 
1015
          /* we need to mount the device before we can expand the row */
 
1016
          expandable = FALSE;
 
1017
 
 
1018
          /* allocate a mount data struct */
 
1019
          data = thunar_tree_view_mount_data_new (view, path, FALSE, OPEN_IN_VIEW);
 
1020
 
 
1021
          /* allocate a GTK+ mount operation */
 
1022
          mount_operation = thunar_gtk_mount_operation_new (GTK_WIDGET (view));
 
1023
 
 
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,
 
1027
                               mount_operation,
 
1028
                               NULL,
 
1029
                               thunar_tree_view_mount_finish,
 
1030
                               data);
 
1031
 
 
1032
          /* release the mount operation */
 
1033
          g_object_unref (mount_operation);
 
1034
        }
 
1035
 
 
1036
      /* release the device */
 
1037
      g_object_unref (G_OBJECT (device));
 
1038
    }
 
1039
 
 
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);
 
1043
 
 
1044
  return !expandable;
 
1045
}
 
1046
 
 
1047
 
 
1048
 
 
1049
static void
 
1050
thunar_tree_view_row_collapsed (GtkTreeView *tree_view,
 
1051
                                GtkTreeIter *iter,
 
1052
                                GtkTreePath *path)
 
1053
{
 
1054
  /* schedule a cleanup of the tree model */
 
1055
  thunar_tree_model_cleanup (THUNAR_TREE_VIEW (tree_view)->model);
 
1056
}
 
1057
 
 
1058
 
 
1059
 
 
1060
static gboolean
 
1061
thunar_tree_view_delete_selected_files (ThunarTreeView *view)
 
1062
{
 
1063
  GtkAccelKey key;
 
1064
 
 
1065
  _thunar_return_val_if_fail (THUNAR_IS_TREE_VIEW (view), FALSE);
 
1066
 
 
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))
 
1072
    return FALSE;
 
1073
 
 
1074
  /* ask the user whether to delete the folder... */
 
1075
  thunar_tree_view_action_move_to_trash (view);
 
1076
 
 
1077
  /* ...and we're done */
 
1078
  return TRUE;
 
1079
}
 
1080
 
 
1081
 
 
1082
 
 
1083
static void
 
1084
thunar_tree_view_context_menu (ThunarTreeView *view,
 
1085
                               GdkEventButton *event,
 
1086
                               GtkTreeModel   *model,
 
1087
                               GtkTreeIter    *iter)
 
1088
{
 
1089
  ThunarDevice *device;
 
1090
  ThunarFile   *parent_file;
 
1091
  ThunarFile   *file;
 
1092
  GtkWidget    *image;
 
1093
  GtkWidget    *menu;
 
1094
  GtkWidget    *item;
 
1095
  GtkWidget    *window;
 
1096
  GList        *providers, *lp;
 
1097
  GList        *actions = NULL, *tmp;
 
1098
 
 
1099
  /* verify that we're connected to the clipboard manager */
 
1100
  if (G_UNLIKELY (view->clipboard == NULL))
 
1101
    return;
 
1102
 
 
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,
 
1107
                      -1);
 
1108
 
 
1109
  /* prepare the popup menu */
 
1110
  menu = gtk_menu_new ();
 
1111
 
 
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);
 
1118
 
 
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);
 
1122
  
 
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);
 
1129
 
 
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);
 
1136
 
 
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);
 
1141
 
 
1142
  if (G_UNLIKELY (device != NULL))
 
1143
    {
 
1144
      if (thunar_device_get_kind (device) == THUNAR_DEVICE_KIND_VOLUME)
 
1145
        {
 
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);
 
1151
 
 
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);
 
1157
 
 
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);
 
1164
        }
 
1165
      else
 
1166
        {
 
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);
 
1173
        }
 
1174
 
 
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);
 
1179
    }
 
1180
  else if (G_UNLIKELY (file != NULL && thunar_file_is_trashed (file) && thunar_file_is_root (file)))
 
1181
    {
 
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);
 
1188
 
 
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);
 
1193
    }
 
1194
 
 
1195
  /* check if we have a non-trashed resource */
 
1196
  if (G_LIKELY (file != NULL && !thunar_file_is_trashed (file)))
 
1197
    {
 
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);
 
1204
 
 
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);
 
1209
    }
 
1210
 
 
1211
  if (G_LIKELY (file != NULL))
 
1212
    {
 
1213
      /* "Cut" and "Copy" don't make much sense for devices */
 
1214
      if (G_LIKELY (device == NULL))
 
1215
        {
 
1216
          /* determine the parent file (required to determine "Cut" sensitivity) */
 
1217
          parent_file = thunar_file_get_parent (file, NULL);
 
1218
 
 
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);
 
1225
 
 
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);
 
1229
 
 
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);
 
1236
 
 
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);
 
1240
 
 
1241
          /* cleanup */
 
1242
          if (G_LIKELY (parent_file != NULL))
 
1243
            g_object_unref (G_OBJECT (parent_file));
 
1244
        }
 
1245
 
 
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);
 
1252
 
 
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);
 
1256
 
 
1257
      /* "Delete" and "Rename" don't make much sense for devices */
 
1258
      if (G_LIKELY (device == NULL))
 
1259
        {
 
1260
          /* determine the parent file (required to determine "Delete" sensitivity) */
 
1261
          parent_file = thunar_file_get_parent (file, NULL);
 
1262
 
 
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);
 
1267
 
 
1268
          if (thunar_g_vfs_is_uri_scheme_supported ("trash")
 
1269
              && thunar_file_can_be_trashed (file))
 
1270
            {
 
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);
 
1277
 
 
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);
 
1281
            }
 
1282
 
 
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);
 
1289
 
 
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);
 
1293
 
 
1294
          /* cleanup */
 
1295
          if (G_LIKELY (parent_file != NULL))
 
1296
            g_object_unref (G_OBJECT (parent_file));
 
1297
 
 
1298
          /* don't show the "Rename" action in the trash */
 
1299
          if (G_LIKELY (!thunar_file_is_trashed (file)))
 
1300
            {
 
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);
 
1305
 
 
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);
 
1312
            }
 
1313
        }
 
1314
 
 
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);
 
1319
 
 
1320
      /* add the providers menu for non-trashed items */
 
1321
      if (G_LIKELY (!thunar_file_is_trashed (file)))
 
1322
        {
 
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))
 
1326
            {
 
1327
              /* determine the toplevel window we belong to */
 
1328
              window = gtk_widget_get_toplevel (GTK_WIDGET (view));
 
1329
 
 
1330
              /* load the actions offered by the menu providers */
 
1331
              for (lp = providers; lp != NULL; lp = lp->next)
 
1332
                {
 
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));
 
1336
                }
 
1337
              g_list_free (providers);
 
1338
 
 
1339
              /* add the actions to the menu */
 
1340
              for (lp = actions; lp != NULL; lp = lp->next)
 
1341
                {
 
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);
 
1345
 
 
1346
                  /* release the reference on the action */
 
1347
                  g_object_unref (G_OBJECT (lp->data));
 
1348
                }
 
1349
 
 
1350
              /* add a separator to the end of the menu */
 
1351
              if (G_LIKELY (lp != actions))
 
1352
                {
 
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);
 
1357
                }
 
1358
 
 
1359
              /* cleanup */
 
1360
              g_list_free (actions);
 
1361
            }
 
1362
        }
 
1363
    }
 
1364
 
 
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);
 
1371
 
 
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);
 
1375
 
 
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 ());
 
1379
 
 
1380
  /* cleanup */
 
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));
 
1385
}
 
1386
 
 
1387
 
 
1388
 
 
1389
static GdkDragAction
 
1390
thunar_tree_view_get_dest_actions (ThunarTreeView *view,
 
1391
                                   GdkDragContext *context,
 
1392
                                   gint            x,
 
1393
                                   gint            y,
 
1394
                                   guint           timestamp,
 
1395
                                   ThunarFile    **file_return)
 
1396
{
 
1397
  GdkDragAction actions = 0;
 
1398
  GdkDragAction action = 0;
 
1399
  GtkTreePath  *path = NULL;
 
1400
  GtkTreeIter   iter;
 
1401
  ThunarFile   *file = NULL;
 
1402
 
 
1403
  /* cancel any previously active expand timer */
 
1404
  if (G_LIKELY (view->expand_timer_id != 0))
 
1405
    g_source_remove (view->expand_timer_id);
 
1406
 
 
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))
 
1409
    {
 
1410
      /* determine the iter for the given path */
 
1411
      if (gtk_tree_model_get_iter (GTK_TREE_MODEL (view->model), &iter, path))
 
1412
        {
 
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))
 
1416
            {
 
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))
 
1420
                {
 
1421
                  /* reset file */
 
1422
                  g_object_unref (G_OBJECT (file));
 
1423
                  file = NULL;
 
1424
                }
 
1425
            }
 
1426
        }
 
1427
    }
 
1428
 
 
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);
 
1431
 
 
1432
  /* check if we have drag dest row */
 
1433
  if (G_LIKELY (path != NULL))
 
1434
    {
 
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);
 
1439
    }
 
1440
 
 
1441
  /* setup the new "drop-file" */
 
1442
  g_object_set (G_OBJECT (view->icon_renderer), "drop-file", file, NULL);
 
1443
 
 
1444
  /* tell Gdk whether we can drop here */
 
1445
  gdk_drag_status (context, action, timestamp);
 
1446
 
 
1447
  /* return the file if requested */
 
1448
  if (G_LIKELY (file_return != NULL))
 
1449
    {
 
1450
      *file_return = file;
 
1451
      file = NULL;
 
1452
    }
 
1453
  else if (G_UNLIKELY (file != NULL))
 
1454
    {
 
1455
      /* release the file */
 
1456
      g_object_unref (G_OBJECT (file));
 
1457
    }
 
1458
 
 
1459
  /* clean up */
 
1460
  if (G_LIKELY (path != NULL))
 
1461
    gtk_tree_path_free (path);
 
1462
 
 
1463
  return actions;
 
1464
}
 
1465
 
 
1466
 
 
1467
 
 
1468
static gboolean
 
1469
thunar_tree_view_find_closest_ancestor (ThunarTreeView *view,
 
1470
                                        GtkTreePath    *path,
 
1471
                                        GtkTreePath   **ancestor_return,
 
1472
                                        gboolean       *exact_return)
 
1473
{
 
1474
  GtkTreeModel *model = GTK_TREE_MODEL (view->model);
 
1475
  GtkTreePath  *child_path;
 
1476
  GtkTreeIter   child_iter;
 
1477
  GtkTreeIter   iter;
 
1478
  ThunarFile   *file;
 
1479
  gboolean      found = FALSE;
 
1480
 
 
1481
  /* determine the iter for the current path */
 
1482
  if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (view->model), &iter, path))
 
1483
    return FALSE;
 
1484
 
 
1485
  /* process all items at this level */
 
1486
  do
 
1487
    {
 
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))
 
1491
        continue;
 
1492
 
 
1493
      /* check if this is the file we're looking for */
 
1494
      if (G_UNLIKELY (file == view->current_directory))
 
1495
        {
 
1496
          /* we found the very best ancestor iter! */
 
1497
          *ancestor_return = gtk_tree_model_get_path (model, &iter);
 
1498
          *exact_return = TRUE;
 
1499
          found = TRUE;
 
1500
        }
 
1501
      else if (thunar_file_is_ancestor (view->current_directory, file))
 
1502
        {
 
1503
          /* check if we can find an even better ancestor below this node */
 
1504
          if (gtk_tree_model_iter_children (model, &child_iter, &iter))
 
1505
            {
 
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);
 
1509
            }
 
1510
 
 
1511
          /* maybe not exact, but still an ancestor */
 
1512
          if (G_UNLIKELY (!found))
 
1513
            {
 
1514
              /* we found an ancestor, not an exact match */
 
1515
              *ancestor_return = gtk_tree_model_get_path (model, &iter);
 
1516
              *exact_return = FALSE;
 
1517
              found = TRUE;
 
1518
            }
 
1519
        }
 
1520
 
 
1521
      /* release the file reference */
 
1522
      g_object_unref (G_OBJECT (file));
 
1523
    }
 
1524
  while (!found && gtk_tree_model_iter_next (model, &iter));
 
1525
 
 
1526
  return found;
 
1527
}
 
1528
 
 
1529
 
 
1530
 
 
1531
static ThunarFile*
 
1532
thunar_tree_view_get_selected_file (ThunarTreeView *view)
 
1533
{
 
1534
  GtkTreeSelection *selection;
 
1535
  GtkTreeModel     *model;
 
1536
  GtkTreeIter       iter;
 
1537
  ThunarFile       *file = NULL;
 
1538
 
 
1539
  /* determine file for the selected row */
 
1540
  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
 
1541
 
 
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))
 
1545
    return NULL;
 
1546
 
 
1547
  if (gtk_tree_selection_get_selected (selection, &model, &iter))
 
1548
    gtk_tree_model_get (model, &iter, THUNAR_TREE_MODEL_COLUMN_FILE, &file, -1);
 
1549
 
 
1550
  return file;
 
1551
}
 
1552
 
 
1553
 
 
1554
 
 
1555
static ThunarDevice*
 
1556
thunar_tree_view_get_selected_device (ThunarTreeView *view)
 
1557
{
 
1558
  GtkTreeSelection *selection;
 
1559
  ThunarDevice     *device = NULL;
 
1560
  GtkTreeModel     *model;
 
1561
  GtkTreeIter       iter;
 
1562
 
 
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);
 
1567
 
 
1568
  return device;
 
1569
}
 
1570
 
 
1571
 
 
1572
 
 
1573
static void
 
1574
thunar_tree_view_action_copy (ThunarTreeView *view)
 
1575
{
 
1576
  ThunarFile *file;
 
1577
  GList       list;
 
1578
 
 
1579
  _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
 
1580
 
 
1581
  /* verify that we're connected to the clipboard */
 
1582
  if (G_UNLIKELY (view->clipboard == NULL))
 
1583
    return;
 
1584
 
 
1585
  /* determine the selected file */
 
1586
  file = thunar_tree_view_get_selected_file (view);
 
1587
  if (G_LIKELY (file != NULL))
 
1588
    {
 
1589
      /* fake a file list */
 
1590
      list.data = file;
 
1591
      list.next = NULL;
 
1592
      list.prev = NULL;
 
1593
 
 
1594
      /* copy the selected to the clipboard */
 
1595
      thunar_clipboard_manager_copy_files (view->clipboard, &list);
 
1596
 
 
1597
      /* release the file reference */
 
1598
      g_object_unref (G_OBJECT (file));
 
1599
    }
 
1600
}
 
1601
 
 
1602
 
 
1603
 
 
1604
static void
 
1605
thunar_tree_view_action_create_folder (ThunarTreeView *view)
 
1606
{
 
1607
  ThunarApplication *application;
 
1608
  ThunarFile        *directory;
 
1609
  GList              path_list;
 
1610
  gchar             *name;
 
1611
 
 
1612
  _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
 
1613
 
 
1614
  /* determine the selected directory */
 
1615
  directory = thunar_tree_view_get_selected_file (view);
 
1616
  if (G_UNLIKELY (directory == NULL))
 
1617
    return;
 
1618
 
 
1619
  /* ask the user to enter a name for the new folder */
 
1620
  name = thunar_show_create_dialog (GTK_WIDGET (view),
 
1621
                                    "inode/directory",
 
1622
                                    _("New Folder"),
 
1623
                                    _("Create New Folder"));
 
1624
  if (G_LIKELY (name != NULL))
 
1625
    {
 
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;
 
1629
 
 
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));
 
1634
 
 
1635
      /* release the path */
 
1636
      g_object_unref (path_list.data);
 
1637
 
 
1638
      /* release the file name */
 
1639
      g_free (name);
 
1640
    }
 
1641
 
 
1642
  /* cleanup */
 
1643
  g_object_unref (G_OBJECT (directory));
 
1644
}
 
1645
 
 
1646
 
 
1647
 
 
1648
static void
 
1649
thunar_tree_view_action_cut (ThunarTreeView *view)
 
1650
{
 
1651
  ThunarFile *file;
 
1652
  GList       list;
 
1653
 
 
1654
  _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
 
1655
 
 
1656
  /* verify that we're connected to the clipboard */
 
1657
  if (G_UNLIKELY (view->clipboard == NULL))
 
1658
    return;
 
1659
 
 
1660
  /* determine the selected file */
 
1661
  file = thunar_tree_view_get_selected_file (view);
 
1662
  if (G_LIKELY (file != NULL))
 
1663
    {
 
1664
      /* fake a file list */
 
1665
      list.data = file;
 
1666
      list.next = NULL;
 
1667
      list.prev = NULL;
 
1668
 
 
1669
      /* cut the selected to the clipboard */
 
1670
      thunar_clipboard_manager_cut_files (view->clipboard, &list);
 
1671
 
 
1672
      /* release the file reference */
 
1673
      g_object_unref (G_OBJECT (file));
 
1674
    }
 
1675
}
 
1676
 
 
1677
 
 
1678
 
 
1679
static void
 
1680
thunar_tree_view_action_move_to_trash (ThunarTreeView *view)
 
1681
{
 
1682
  ThunarApplication *application;
 
1683
  ThunarFile        *file;
 
1684
  GList              file_list;
 
1685
  gboolean           permanently;
 
1686
  GdkModifierType    state;
 
1687
 
 
1688
  _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
 
1689
 
 
1690
  /* determine the selected file */
 
1691
  file = thunar_tree_view_get_selected_file (view);
 
1692
  if (G_LIKELY (file != NULL))
 
1693
    {
 
1694
      /* fake a file list */
 
1695
      file_list.data = file;
 
1696
      file_list.next = NULL;
 
1697
      file_list.prev = NULL;
 
1698
 
 
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);
 
1701
 
 
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));
 
1706
 
 
1707
      /* release the file */
 
1708
      g_object_unref (G_OBJECT (file));
 
1709
    }
 
1710
}
 
1711
 
 
1712
 
 
1713
 
 
1714
static void
 
1715
thunar_tree_view_action_delete (ThunarTreeView *view)
 
1716
{
 
1717
  ThunarApplication *application;
 
1718
  ThunarFile        *file;
 
1719
  GList              file_list;
 
1720
 
 
1721
  _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
 
1722
 
 
1723
  /* determine the selected file */
 
1724
  file = thunar_tree_view_get_selected_file (view);
 
1725
  if (G_LIKELY (file != NULL))
 
1726
    {
 
1727
      /* fake a file list */
 
1728
      file_list.data = file;
 
1729
      file_list.next = NULL;
 
1730
      file_list.prev = NULL;
 
1731
 
 
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));
 
1736
 
 
1737
      /* release the file */
 
1738
      g_object_unref (G_OBJECT (file));
 
1739
    }
 
1740
}
 
1741
 
 
1742
 
 
1743
 
 
1744
static void
 
1745
thunar_tree_view_rename_error (ExoJob         *job,
 
1746
                               GError         *error,
 
1747
                               ThunarTreeView *view)
 
1748
{
 
1749
  GArray     *param_values;
 
1750
  ThunarFile *file;
 
1751
 
 
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));
 
1755
 
 
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));
 
1758
 
 
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));
 
1762
}
 
1763
 
 
1764
 
 
1765
 
 
1766
static void
 
1767
thunar_tree_view_rename_finished (ExoJob         *job,
 
1768
                                  ThunarTreeView *view)
 
1769
{
 
1770
  _thunar_return_if_fail (EXO_IS_JOB (job));
 
1771
  _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
 
1772
 
 
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);
 
1776
}
 
1777
 
 
1778
 
 
1779
 
 
1780
static void
 
1781
thunar_tree_view_action_rename (ThunarTreeView *view)
 
1782
{
 
1783
  ThunarFile *file;
 
1784
  GtkWidget  *window;
 
1785
  ThunarJob  *job;
 
1786
 
 
1787
  _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
 
1788
 
 
1789
  /* determine the selected file */
 
1790
  file = thunar_tree_view_get_selected_file (view);
 
1791
  if (G_LIKELY (file != NULL))
 
1792
    {
 
1793
      /* get the toplevel window */
 
1794
      window = gtk_widget_get_toplevel (GTK_WIDGET (view));
 
1795
 
 
1796
      /* run the rename dialog */
 
1797
      job = thunar_dialogs_show_rename_file (GTK_WINDOW (window), file);
 
1798
      if (G_LIKELY (job != NULL))
 
1799
        {
 
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);
 
1802
        }
 
1803
 
 
1804
      /* release the file */
 
1805
      g_object_unref (file);
 
1806
    }
 
1807
}
 
1808
 
 
1809
 
 
1810
 
 
1811
static void
 
1812
thunar_tree_view_action_eject_finish (ThunarDevice *device,
 
1813
                                      const GError *error,
 
1814
                                      gpointer      user_data)
 
1815
{
 
1816
  ThunarTreeView *view = THUNAR_TREE_VIEW (user_data);
 
1817
  gchar          *device_name;
 
1818
 
 
1819
  _thunar_return_if_fail (THUNAR_IS_DEVICE (device));
 
1820
  _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
 
1821
 
 
1822
  /* check if there was an error */
 
1823
  if (error != NULL)
 
1824
    {
 
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);
 
1829
    }
 
1830
 
 
1831
  g_object_unref (view);
 
1832
}
 
1833
 
 
1834
 
 
1835
 
 
1836
static void
 
1837
thunar_tree_view_action_eject (ThunarTreeView *view)
 
1838
{
 
1839
  ThunarDevice    *device;
 
1840
  GMountOperation *mount_operation;
 
1841
 
 
1842
  _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
 
1843
 
 
1844
  /* determine the selected device */
 
1845
  device = thunar_tree_view_get_selected_device (view);
 
1846
  if (G_LIKELY (device != NULL))
 
1847
    {
 
1848
      /* prepare a mount operation */
 
1849
      mount_operation = thunar_gtk_mount_operation_new (GTK_WIDGET (view));
 
1850
 
 
1851
      /* eject */
 
1852
      thunar_device_eject (device,
 
1853
                           mount_operation,
 
1854
                           NULL,
 
1855
                           thunar_tree_view_action_eject_finish,
 
1856
                           g_object_ref (view));
 
1857
 
 
1858
      /* release the device */
 
1859
      g_object_unref (device);
 
1860
      g_object_unref (mount_operation);
 
1861
    }
 
1862
}
 
1863
 
 
1864
 
 
1865
 
 
1866
static void
 
1867
thunar_tree_view_action_unmount_finish (ThunarDevice *device,
 
1868
                                        const GError *error,
 
1869
                                        gpointer      user_data)
 
1870
{
 
1871
  ThunarTreeView *view = THUNAR_TREE_VIEW (user_data);
 
1872
  gchar          *device_name;
 
1873
 
 
1874
  _thunar_return_if_fail (THUNAR_IS_DEVICE (device));
 
1875
  _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
 
1876
 
 
1877
  /* check if there was an error */
 
1878
  if (error != NULL)
 
1879
    {
 
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);
 
1884
    }
 
1885
 
 
1886
  g_object_unref (view);
 
1887
}
 
1888
 
 
1889
 
 
1890
 
 
1891
static void
 
1892
thunar_tree_view_action_unmount (ThunarTreeView *view)
 
1893
{
 
1894
  ThunarDevice    *device;
 
1895
  GMountOperation *mount_operation;
 
1896
 
 
1897
  _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
 
1898
 
 
1899
  /* determine the selected device */
 
1900
  device = thunar_tree_view_get_selected_device (view);
 
1901
  if (G_LIKELY (device != NULL))
 
1902
    {
 
1903
      /* prepare a mount operation */
 
1904
      mount_operation = thunar_gtk_mount_operation_new (GTK_WIDGET (view));
 
1905
 
 
1906
      /* eject */
 
1907
      thunar_device_unmount (device,
 
1908
                             mount_operation,
 
1909
                             NULL,
 
1910
                             thunar_tree_view_action_unmount_finish,
 
1911
                             g_object_ref (view));
 
1912
 
 
1913
      /* release the device */
 
1914
      g_object_unref (device);
 
1915
      g_object_unref (mount_operation);
 
1916
    }
 
1917
}
 
1918
 
 
1919
 
 
1920
 
 
1921
static void
 
1922
thunar_tree_view_action_empty_trash (ThunarTreeView *view)
 
1923
{
 
1924
  ThunarApplication *application;
 
1925
 
 
1926
  _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
 
1927
 
 
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));
 
1932
}
 
1933
 
 
1934
 
 
1935
 
 
1936
static void
 
1937
thunar_tree_view_action_mount (ThunarTreeView *view)
 
1938
{
 
1939
  _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
 
1940
  thunar_tree_view_mount (view, FALSE, OPEN_IN_VIEW);
 
1941
}
 
1942
 
 
1943
 
 
1944
 
 
1945
static void
 
1946
thunar_tree_view_mount_finish (ThunarDevice *device,
 
1947
                               const GError *error,
 
1948
                               gpointer      user_data)
 
1949
{
 
1950
  ThunarTreeViewMountData *data = user_data;
 
1951
  gchar                   *device_name;
 
1952
 
 
1953
  _thunar_return_if_fail (THUNAR_IS_DEVICE (device));
 
1954
  _thunar_return_if_fail (data != NULL && THUNAR_IS_TREE_VIEW (data->view));
 
1955
 
 
1956
  if (error != NULL)
 
1957
    {
 
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);
 
1961
    }
 
1962
  else
 
1963
    {
 
1964
      if (G_LIKELY (data->open_after_mounting))
 
1965
        {
 
1966
          switch (data->open_in)
 
1967
            {
 
1968
            case OPEN_IN_WINDOW:
 
1969
              thunar_tree_view_open_selection_in_new_window (data->view);
 
1970
              break;
 
1971
            
 
1972
            case OPEN_IN_TAB:
 
1973
              thunar_tree_view_open_selection_in_new_tab (data->view);
 
1974
              break;
 
1975
            
 
1976
            default:
 
1977
              thunar_tree_view_open_selection (data->view);
 
1978
              break;
 
1979
            }
 
1980
        }
 
1981
      else if (data->path != NULL)
 
1982
        {
 
1983
          gtk_tree_view_expand_row (GTK_TREE_VIEW (data->view), data->path, FALSE);
 
1984
        }
 
1985
    }
 
1986
 
 
1987
  thunar_tree_view_mount_data_free (data);
 
1988
}
 
1989
 
 
1990
 
 
1991
 
 
1992
static void
 
1993
thunar_tree_view_mount (ThunarTreeView *view,
 
1994
                        gboolean        open_after_mounting,
 
1995
                        guint           open_in)
 
1996
{
 
1997
  ThunarTreeViewMountData *data;
 
1998
  GMountOperation         *mount_operation;
 
1999
  ThunarDevice            *device;
 
2000
 
 
2001
  _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
 
2002
 
 
2003
  /* determine the selected device */
 
2004
  device = thunar_tree_view_get_selected_device (view);
 
2005
  if (device != NULL)
 
2006
    {
 
2007
      /* check if we need to mount the device at all */
 
2008
      if (!thunar_device_is_mounted (device))
 
2009
        {
 
2010
          /* allocate mount data */
 
2011
          data = thunar_tree_view_mount_data_new (view, NULL, open_after_mounting, open_in);
 
2012
 
 
2013
          /* allocate a GTK+ mount operation */
 
2014
          mount_operation = thunar_gtk_mount_operation_new (GTK_WIDGET (view));
 
2015
 
 
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,
 
2019
                               mount_operation,
 
2020
                               NULL,
 
2021
                               thunar_tree_view_mount_finish,
 
2022
                               data);
 
2023
 
 
2024
          /* release the mount operation */
 
2025
          g_object_unref (mount_operation);
 
2026
        }
 
2027
 
 
2028
      /* release the device */
 
2029
      g_object_unref (device);
 
2030
    }
 
2031
}
 
2032
 
 
2033
 
 
2034
 
 
2035
static void
 
2036
thunar_tree_view_action_open (ThunarTreeView *view)
 
2037
{
 
2038
  ThunarFile   *file;
 
2039
  ThunarDevice *device;
 
2040
 
 
2041
  _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
 
2042
 
 
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);
 
2046
 
 
2047
  if (device != NULL)
 
2048
    {
 
2049
      if (thunar_device_is_mounted (device))
 
2050
        thunar_tree_view_open_selection (view);
 
2051
      else
 
2052
        thunar_tree_view_mount (view, TRUE, OPEN_IN_TAB);
 
2053
 
 
2054
      g_object_unref (device);
 
2055
    }
 
2056
  else if (file != NULL)
 
2057
    {
 
2058
      thunar_tree_view_open_selection (view);
 
2059
      g_object_unref (file);
 
2060
    }
 
2061
}
 
2062
 
 
2063
 
 
2064
 
 
2065
static void
 
2066
thunar_tree_view_open_selection (ThunarTreeView *view)
 
2067
{
 
2068
  ThunarFile *file;
 
2069
 
 
2070
  _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
 
2071
 
 
2072
  /* determine the selected file */
 
2073
  file = thunar_tree_view_get_selected_file (view);
 
2074
  if (G_LIKELY (file != NULL))
 
2075
    {
 
2076
      /* open that folder in the main view */
 
2077
      thunar_navigator_change_directory (THUNAR_NAVIGATOR (view), file);
 
2078
      g_object_unref (file);
 
2079
    }
 
2080
}
 
2081
 
 
2082
 
 
2083
 
 
2084
static void
 
2085
thunar_tree_view_action_open_in_new_window (ThunarTreeView *view)
 
2086
{
 
2087
  ThunarFile   *file;
 
2088
  ThunarDevice *device;
 
2089
 
 
2090
  _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
 
2091
 
 
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);
 
2095
 
 
2096
  if (device != NULL)
 
2097
    {
 
2098
      if (thunar_device_is_mounted (device))
 
2099
        thunar_tree_view_open_selection_in_new_window (view);
 
2100
      else
 
2101
        thunar_tree_view_mount (view, TRUE, OPEN_IN_WINDOW);
 
2102
 
 
2103
      g_object_unref (device);
 
2104
    }
 
2105
  else if (file != NULL)
 
2106
    {
 
2107
      thunar_tree_view_open_selection_in_new_window (view);
 
2108
      g_object_unref (file);
 
2109
    }
 
2110
}
 
2111
 
 
2112
 
 
2113
 
 
2114
static void
 
2115
thunar_tree_view_action_open_in_new_tab (ThunarTreeView *view)
 
2116
{
 
2117
  ThunarFile   *file;
 
2118
  ThunarDevice *device;
 
2119
 
 
2120
  _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
 
2121
 
 
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);
 
2125
 
 
2126
  if (device != NULL)
 
2127
    {
 
2128
      if (thunar_device_is_mounted (device))
 
2129
        thunar_tree_view_open_selection_in_new_tab (view);
 
2130
      else
 
2131
        thunar_tree_view_mount (view, TRUE, OPEN_IN_TAB);
 
2132
 
 
2133
      g_object_unref (device);
 
2134
    }
 
2135
  else if (file != NULL)
 
2136
    {
 
2137
      thunar_tree_view_open_selection_in_new_tab (view);
 
2138
      g_object_unref (file);
 
2139
    }
 
2140
}
 
2141
 
 
2142
 
 
2143
 
 
2144
static void
 
2145
thunar_tree_view_open_selection_in_new_window (ThunarTreeView *view)
 
2146
{
 
2147
  ThunarApplication *application;
 
2148
  ThunarFile        *file;
 
2149
 
 
2150
  _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
 
2151
 
 
2152
  /* determine the selected file */
 
2153
  file = thunar_tree_view_get_selected_file (view);
 
2154
  if (G_LIKELY (file != NULL))
 
2155
    {
 
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);
 
2162
    }
 
2163
}
 
2164
 
 
2165
 
 
2166
 
 
2167
static void
 
2168
thunar_tree_view_open_selection_in_new_tab (ThunarTreeView *view)
 
2169
{
 
2170
  ThunarFile *file;
 
2171
 
 
2172
  _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
 
2173
 
 
2174
  /* determine the selected file */
 
2175
  file = thunar_tree_view_get_selected_file (view);
 
2176
  if (G_LIKELY (file != NULL))
 
2177
    {
 
2178
      /* open a new tab for the selected folder */
 
2179
      thunar_navigator_open_new_tab (THUNAR_NAVIGATOR (view), file);
 
2180
      g_object_unref (file);
 
2181
    }
 
2182
}
 
2183
 
 
2184
 
 
2185
 
 
2186
static void
 
2187
thunar_tree_view_action_paste_into_folder (ThunarTreeView *view)
 
2188
{
 
2189
  ThunarFile *file;
 
2190
 
 
2191
  _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
 
2192
 
 
2193
  /* verify that we're connected to the clipboard */
 
2194
  if (G_UNLIKELY (view->clipboard == NULL))
 
2195
    return;
 
2196
 
 
2197
  /* determine the selected folder */
 
2198
  file = thunar_tree_view_get_selected_file (view);
 
2199
  if (G_LIKELY (file != NULL))
 
2200
    {
 
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);
 
2203
 
 
2204
      /* release the file reference */
 
2205
      g_object_unref (G_OBJECT (file));
 
2206
    }
 
2207
}
 
2208
 
 
2209
 
 
2210
 
 
2211
static void
 
2212
thunar_tree_view_action_properties (ThunarTreeView *view)
 
2213
{
 
2214
  ThunarFile *file;
 
2215
  GtkWidget  *dialog;
 
2216
  GtkWidget  *toplevel;
 
2217
 
 
2218
  _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
 
2219
 
 
2220
  /* determine the selected file */
 
2221
  file = thunar_tree_view_get_selected_file (view);
 
2222
  if (G_LIKELY (file != NULL))
 
2223
    {
 
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)))
 
2227
        {
 
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);
 
2232
        }
 
2233
 
 
2234
      /* release the file */
 
2235
      g_object_unref (G_OBJECT (file));
 
2236
    }
 
2237
}
 
2238
 
 
2239
 
 
2240
 
 
2241
static GClosure*
 
2242
thunar_tree_view_new_files_closure (ThunarTreeView *view)
 
2243
{
 
2244
  /* drop any previous "new-files" closure */
 
2245
  if (G_UNLIKELY (view->new_files_closure != NULL))
 
2246
    {
 
2247
      g_closure_invalidate (view->new_files_closure);
 
2248
      g_closure_unref (view->new_files_closure);
 
2249
    }
 
2250
 
 
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);
 
2255
 
 
2256
  /* and return our new closure */
 
2257
  return view->new_files_closure;
 
2258
}
 
2259
 
 
2260
 
 
2261
 
 
2262
static void
 
2263
thunar_tree_view_new_files (ThunarJob      *job,
 
2264
                            GList          *path_list,
 
2265
                            ThunarTreeView *view)
 
2266
{
 
2267
  ThunarFile *file;
 
2268
 
 
2269
  /* check if we have exactly one new path */
 
2270
  if (G_UNLIKELY (path_list == NULL || path_list->next != NULL))
 
2271
    return;
 
2272
 
 
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)))
 
2276
    {
 
2277
      /* change to the newly created folder */
 
2278
      thunar_navigator_change_directory (THUNAR_NAVIGATOR (view), file);
 
2279
    }
 
2280
}
 
2281
 
 
2282
 
 
2283
 
 
2284
static gboolean
 
2285
thunar_tree_view_visible_func (ThunarTreeModel *model,
 
2286
                               ThunarFile      *file,
 
2287
                               gpointer         user_data)
 
2288
{
 
2289
  ThunarTreeView *view;
 
2290
  gboolean        visible = TRUE;
 
2291
 
 
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);
 
2295
 
 
2296
  /* if show_hidden is TRUE, nothing is filtered */
 
2297
  view = THUNAR_TREE_VIEW (user_data);
 
2298
  if (G_LIKELY (!view->show_hidden))
 
2299
    {
 
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));
 
2303
    }
 
2304
 
 
2305
  return visible;
 
2306
}
 
2307
 
 
2308
 
 
2309
 
 
2310
static gboolean
 
2311
thunar_tree_view_selection_func (GtkTreeSelection *selection,
 
2312
                                 GtkTreeModel     *model,
 
2313
                                 GtkTreePath      *path,
 
2314
                                 gboolean          path_currently_selected,
 
2315
                                 gpointer          user_data)
 
2316
{
 
2317
  GtkTreeIter   iter;
 
2318
  ThunarFile   *file;
 
2319
  gboolean      result = FALSE;
 
2320
  ThunarDevice *device;
 
2321
 
 
2322
  /* every row may be unselected at any time */
 
2323
  if (path_currently_selected)
 
2324
    return TRUE;
 
2325
 
 
2326
  /* determine the iterator for the path */
 
2327
  if (gtk_tree_model_get_iter (model, &iter, path))
 
2328
    {
 
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))
 
2332
        {
 
2333
          /* rows with files can be selected */
 
2334
          result = TRUE;
 
2335
 
 
2336
          /* release file */
 
2337
          g_object_unref (file);
 
2338
        }
 
2339
      else
 
2340
        {
 
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))
 
2344
            {
 
2345
              /* rows with devices can also be selected */
 
2346
              result = TRUE;
 
2347
 
 
2348
              /* release device */
 
2349
              g_object_unref (device);
 
2350
            }
 
2351
        }
 
2352
    }
 
2353
 
 
2354
  return result;
 
2355
}
 
2356
 
 
2357
 
 
2358
 
 
2359
static gboolean
 
2360
thunar_tree_view_cursor_idle (gpointer user_data)
 
2361
{
 
2362
  ThunarTreeView *view = THUNAR_TREE_VIEW (user_data);
 
2363
  GtkTreePath    *ancestor = NULL;
 
2364
  GtkTreePath    *parent;
 
2365
  GtkTreePath    *path;
 
2366
  gboolean        done = TRUE;
 
2367
 
 
2368
  GDK_THREADS_ENTER ();
 
2369
 
 
2370
  /* verify that we still have a current directory */
 
2371
  if (G_LIKELY (view->current_directory != NULL))
 
2372
    {
 
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 ();
 
2377
 
 
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))
 
2380
        {
 
2381
          /* be sure to start from the first path at that level */
 
2382
          while (gtk_tree_path_prev (path))
 
2383
            ;
 
2384
 
 
2385
          /* try to find the closest ancestor relative to the current path */
 
2386
          if (thunar_tree_view_find_closest_ancestor (view, path, &ancestor, &done))
 
2387
            {
 
2388
              /* expand to the best ancestor if not an exact match */
 
2389
              if (G_LIKELY (!done))
 
2390
                {
 
2391
                  /* just expand everything up to the row and its children */
 
2392
                  gtk_tree_view_expand_to_path (GTK_TREE_VIEW (view), ancestor);
 
2393
                }
 
2394
              else if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (view), ancestor))
 
2395
                {
 
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);
 
2401
                }
 
2402
 
 
2403
              /* place cursor on the ancestor */
 
2404
              gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), ancestor, NULL, FALSE);
 
2405
 
 
2406
              /* release the ancestor path */
 
2407
              gtk_tree_path_free (ancestor);
 
2408
 
 
2409
              /* we did it */
 
2410
              break;
 
2411
            }
 
2412
        }
 
2413
 
 
2414
      /* release the tree path */
 
2415
      gtk_tree_path_free (path);
 
2416
    }
 
2417
 
 
2418
  GDK_THREADS_LEAVE ();
 
2419
 
 
2420
  return !done;
 
2421
}
 
2422
 
 
2423
 
 
2424
 
 
2425
static void
 
2426
thunar_tree_view_cursor_idle_destroy (gpointer user_data)
 
2427
{
 
2428
  THUNAR_TREE_VIEW (user_data)->cursor_idle_id = 0;
 
2429
}
 
2430
 
 
2431
 
 
2432
 
 
2433
static gboolean
 
2434
thunar_tree_view_drag_scroll_timer (gpointer user_data)
 
2435
{
 
2436
  ThunarTreeView *view = THUNAR_TREE_VIEW (user_data);
 
2437
  GtkAdjustment  *vadjustment;
 
2438
  GtkTreePath    *start_path;
 
2439
  GtkTreePath    *end_path;
 
2440
  GtkTreePath    *path;
 
2441
  gfloat          value;
 
2442
  gint            offset;
 
2443
  gint            y, h;
 
2444
 
 
2445
  GDK_THREADS_ENTER ();
 
2446
 
 
2447
  /* verify that we are realized */
 
2448
  if (gtk_widget_get_realized (GTK_WIDGET (view)))
 
2449
    {
 
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);
 
2453
 
 
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);
 
2458
 
 
2459
      /* change the vertical adjustment appropriately */
 
2460
      if (G_UNLIKELY (offset != 0))
 
2461
        {
 
2462
          /* determine the vertical adjustment */
 
2463
          vadjustment = gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (view));
 
2464
 
 
2465
          /* determine the new value */
 
2466
          value = CLAMP (vadjustment->value + 2 * offset, vadjustment->lower, vadjustment->upper - vadjustment->page_size);
 
2467
 
 
2468
          /* check if we have a new value */
 
2469
          if (G_UNLIKELY (vadjustment->value != value))
 
2470
            {
 
2471
              /* apply the new value */
 
2472
              gtk_adjustment_set_value (vadjustment, value);
 
2473
 
 
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.
 
2477
               */
 
2478
              if (G_UNLIKELY (view->expand_timer_id != 0))
 
2479
                {
 
2480
                  /* drop the current expand timer source */
 
2481
                  g_source_remove (view->expand_timer_id);
 
2482
 
 
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))
 
2485
                    {
 
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))
 
2489
                        {
 
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)
 
2492
                            {
 
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);
 
2497
                            }
 
2498
 
 
2499
                          /* release the drag dest row */
 
2500
                          gtk_tree_path_free (path);
 
2501
                        }
 
2502
 
 
2503
                      /* release the start/end paths */
 
2504
                      gtk_tree_path_free (start_path);
 
2505
                      gtk_tree_path_free (end_path);
 
2506
                    }
 
2507
                }
 
2508
            }
 
2509
        }
 
2510
    }
 
2511
 
 
2512
  GDK_THREADS_LEAVE ();
 
2513
 
 
2514
  return TRUE;
 
2515
}
 
2516
 
 
2517
 
 
2518
 
 
2519
static void
 
2520
thunar_tree_view_drag_scroll_timer_destroy (gpointer user_data)
 
2521
{
 
2522
  THUNAR_TREE_VIEW (user_data)->drag_scroll_timer_id = 0;
 
2523
}
 
2524
 
 
2525
 
 
2526
 
 
2527
static gboolean
 
2528
thunar_tree_view_expand_timer (gpointer user_data)
 
2529
{
 
2530
  ThunarTreeView *view = THUNAR_TREE_VIEW (user_data);
 
2531
  GtkTreePath    *path;
 
2532
 
 
2533
  GDK_THREADS_ENTER ();
 
2534
 
 
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);
 
2538
 
 
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))
 
2542
    {
 
2543
      /* expand the drag dest row */
 
2544
      gtk_tree_view_expand_row (GTK_TREE_VIEW (view), path, FALSE);
 
2545
      gtk_tree_path_free (path);
 
2546
    }
 
2547
 
 
2548
  GDK_THREADS_LEAVE ();
 
2549
 
 
2550
  return FALSE;
 
2551
}
 
2552
 
 
2553
 
 
2554
 
 
2555
static void
 
2556
thunar_tree_view_expand_timer_destroy (gpointer user_data)
 
2557
{
 
2558
  THUNAR_TREE_VIEW (user_data)->expand_timer_id = 0;
 
2559
}
 
2560
 
 
2561
 
 
2562
 
 
2563
static ThunarTreeViewMountData *
 
2564
thunar_tree_view_mount_data_new (ThunarTreeView *view,
 
2565
                                 GtkTreePath    *path,
 
2566
                                 gboolean        open_after_mounting,
 
2567
                                 guint           open_in)
 
2568
{
 
2569
  ThunarTreeViewMountData *data;
 
2570
 
 
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;
 
2576
 
 
2577
  return data;
 
2578
}
 
2579
 
 
2580
 
 
2581
 
 
2582
static void
 
2583
thunar_tree_view_mount_data_free (ThunarTreeViewMountData *data)
 
2584
{
 
2585
  _thunar_return_if_fail (data != NULL && THUNAR_IS_TREE_VIEW (data->view));
 
2586
 
 
2587
  if (data->path != NULL)
 
2588
    gtk_tree_path_free (data->path);
 
2589
  g_object_unref (data->view);
 
2590
  g_slice_free (ThunarTreeViewMountData, data);
 
2591
}
 
2592
 
 
2593
 
 
2594
 
 
2595
/**
 
2596
 * thunar_tree_view_get_show_hidden:
 
2597
 * @view : a #ThunarTreeView.
 
2598
 *
 
2599
 * Returns %TRUE if hidden and backup folders are
 
2600
 * shown in @view.
 
2601
 *
 
2602
 * Return value: %TRUE if hidden folders are shown.
 
2603
 **/
 
2604
static gboolean
 
2605
thunar_tree_view_get_show_hidden (ThunarTreeView *view)
 
2606
{
 
2607
  _thunar_return_val_if_fail (THUNAR_IS_TREE_VIEW (view), FALSE);
 
2608
  return view->show_hidden;
 
2609
}
 
2610
 
 
2611
 
 
2612
 
 
2613
/**
 
2614
 * thunar_tree_view_set_show_hidden:
 
2615
 * @view        : a #ThunarTreeView.
 
2616
 * @show_hidden : %TRUE to show hidden and backup folders.
 
2617
 *
 
2618
 * If @show_hidden is %TRUE, @view will shown hidden and
 
2619
 * backup folders. Else, these folders will be hidden.
 
2620
 **/
 
2621
static void
 
2622
thunar_tree_view_set_show_hidden (ThunarTreeView *view,
 
2623
                                  gboolean        show_hidden)
 
2624
{
 
2625
  _thunar_return_if_fail (THUNAR_IS_TREE_VIEW (view));
 
2626
  _thunar_return_if_fail (THUNAR_IS_TREE_MODEL (view->model));
 
2627
 
 
2628
  /* normalize the value */
 
2629
  show_hidden = !!show_hidden;
 
2630
 
 
2631
  /* check if we have a new setting */
 
2632
  if (view->show_hidden != show_hidden)
 
2633
    {
 
2634
      /* apply the new setting */
 
2635
      view->show_hidden = show_hidden;
 
2636
 
 
2637
      /* update the model */
 
2638
      thunar_tree_model_refilter (view->model);
 
2639
 
 
2640
      /* notify listeners */
 
2641
      g_object_notify (G_OBJECT (view), "show-hidden");
 
2642
    }
 
2643
}
 
2644
 
 
2645
 
 
2646
 
 
2647
/**
 
2648
 * thunar_tree_view_new:
 
2649
 *
 
2650
 * Allocates a new #ThunarTreeView instance.
 
2651
 *
 
2652
 * Return value: the newly allocated #ThunarTreeView instance.
 
2653
 **/
 
2654
GtkWidget*
 
2655
thunar_tree_view_new (void)
 
2656
{
 
2657
  return g_object_new (THUNAR_TYPE_TREE_VIEW, NULL);
 
2658
}