1 /*
2 * pluma-file-browser-store.c - Pluma plugin providing easy file access
3 * from the sidepanel
4 *
5 * Copyright (C) 2006 - Jesse van den Kieboom <jesse@icecrew.nl>
6 * Copyright (C) 2012-2021 MATE Developers
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <string.h>
28 #include <glib/gi18n-lib.h>
29 #include <gio/gio.h>
30
31 #include "pluma-file-browser-store.h"
32 #include "pluma-file-browser-enum-types.h"
33 #include "pluma-file-browser-error.h"
34 #include "pluma-file-browser-utils.h"
35
36 #define NODE_IS_DIR(node) (FILE_IS_DIR((node)->flags))
37 #define NODE_IS_HIDDEN(node) (FILE_IS_HIDDEN((node)->flags))
38 #define NODE_IS_TEXT(node) (FILE_IS_TEXT((node)->flags))
39 #define NODE_LOADED(node) (FILE_LOADED((node)->flags))
40 #define NODE_IS_FILTERED(node) (FILE_IS_FILTERED((node)->flags))
41 #define NODE_IS_DUMMY(node) (FILE_IS_DUMMY((node)->flags))
42
43 #define FILE_BROWSER_NODE_DIR(node) ((FileBrowserNodeDir *)(node))
44
45 #define DIRECTORY_LOAD_ITEMS_PER_CALLBACK 100
46 #define STANDARD_ATTRIBUTE_TYPES G_FILE_ATTRIBUTE_STANDARD_TYPE "," \
47 G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN "," \
48 G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP "," \
49 G_FILE_ATTRIBUTE_STANDARD_NAME "," \
50 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," \
51 G_FILE_ATTRIBUTE_STANDARD_ICON
52
53 typedef struct _FileBrowserNode FileBrowserNode;
54 typedef struct _FileBrowserNodeDir FileBrowserNodeDir;
55 typedef struct _AsyncData AsyncData;
56 typedef struct _AsyncNode AsyncNode;
57
58 typedef gint (*SortFunc) (FileBrowserNode * node1,
59 FileBrowserNode * node2);
60
61 struct _AsyncData
62 {
63 PlumaFileBrowserStore * model;
64 GCancellable * cancellable;
65 gboolean trash;
66 GList * files;
67 GList * iter;
68 gboolean removed;
69 };
70
71 struct _AsyncNode
72 {
73 FileBrowserNodeDir *dir;
74 GCancellable *cancellable;
75 GSList *original_children;
76 };
77
78 typedef struct {
79 PlumaFileBrowserStore * model;
80 gchar * virtual_root;
81 GMountOperation * operation;
82 GCancellable * cancellable;
83 } MountInfo;
84
85 struct _FileBrowserNode
86 {
87 GFile *file;
88 guint flags;
89 gchar *name;
90
91 GdkPixbuf *icon;
92 GdkPixbuf *emblem;
93
94 FileBrowserNode *parent;
95 gint pos;
96 gboolean inserted;
97 };
98
99 struct _FileBrowserNodeDir
100 {
101 FileBrowserNode node;
102 GSList *children;
103
104 GCancellable *cancellable;
105 GFileMonitor *monitor;
106 PlumaFileBrowserStore *model;
107 };
108
109 struct _PlumaFileBrowserStorePrivate
110 {
111 FileBrowserNode *root;
112 FileBrowserNode *virtual_root;
113 GType column_types[PLUMA_FILE_BROWSER_STORE_COLUMN_NUM];
114
115 PlumaFileBrowserStoreFilterMode filter_mode;
116 PlumaFileBrowserStoreFilterFunc filter_func;
117 gpointer filter_user_data;
118
119 SortFunc sort_func;
120
121 GSList *async_handles;
122 MountInfo *mount_info;
123 };
124
125 static FileBrowserNode *model_find_node (PlumaFileBrowserStore *model,
126 FileBrowserNode *node,
127 GFile *uri);
128 static void model_remove_node (PlumaFileBrowserStore * model,
129 FileBrowserNode * node,
130 GtkTreePath * path,
131 gboolean free_nodes);
132
133 static void set_virtual_root_from_node (PlumaFileBrowserStore * model,
134 FileBrowserNode * node);
135
136 static void pluma_file_browser_store_iface_init (GtkTreeModelIface * iface);
137 static GtkTreeModelFlags pluma_file_browser_store_get_flags (GtkTreeModel * tree_model);
138 static gint pluma_file_browser_store_get_n_columns (GtkTreeModel * tree_model);
139 static GType pluma_file_browser_store_get_column_type (GtkTreeModel * tree_model,
140 gint index);
141 static gboolean pluma_file_browser_store_get_iter (GtkTreeModel * tree_model,
142 GtkTreeIter * iter,
143 GtkTreePath * path);
144 static GtkTreePath *pluma_file_browser_store_get_path (GtkTreeModel * tree_model,
145 GtkTreeIter * iter);
146 static void pluma_file_browser_store_get_value (GtkTreeModel * tree_model,
147 GtkTreeIter * iter,
148 gint column,
149 GValue * value);
150 static gboolean pluma_file_browser_store_iter_next (GtkTreeModel * tree_model,
151 GtkTreeIter * iter);
152 static gboolean pluma_file_browser_store_iter_children (GtkTreeModel * tree_model,
153 GtkTreeIter * iter,
154 GtkTreeIter * parent);
155 static gboolean pluma_file_browser_store_iter_has_child (GtkTreeModel * tree_model,
156 GtkTreeIter * iter);
157 static gint pluma_file_browser_store_iter_n_children (GtkTreeModel * tree_model,
158 GtkTreeIter * iter);
159 static gboolean pluma_file_browser_store_iter_nth_child (GtkTreeModel * tree_model,
160 GtkTreeIter * iter,
161 GtkTreeIter * parent,
162 gint n);
163 static gboolean pluma_file_browser_store_iter_parent (GtkTreeModel * tree_model,
164 GtkTreeIter * iter,
165 GtkTreeIter * child);
166 static void pluma_file_browser_store_row_inserted (GtkTreeModel * tree_model,
167 GtkTreePath * path,
168 GtkTreeIter * iter);
169
170 static void pluma_file_browser_store_drag_source_init (GtkTreeDragSourceIface * iface);
171 static gboolean pluma_file_browser_store_row_draggable (GtkTreeDragSource * drag_source,
172 GtkTreePath * path);
173 static gboolean pluma_file_browser_store_drag_data_delete (GtkTreeDragSource * drag_source,
174 GtkTreePath * path);
175 static gboolean pluma_file_browser_store_drag_data_get (GtkTreeDragSource * drag_source,
176 GtkTreePath * path,
177 GtkSelectionData * selection_data);
178
179 static void file_browser_node_free (PlumaFileBrowserStore * model,
180 FileBrowserNode * node);
181 static void model_add_node (PlumaFileBrowserStore * model,
182 FileBrowserNode * child,
183 FileBrowserNode * parent);
184 static void model_clear (PlumaFileBrowserStore * model,
185 gboolean free_nodes);
186 static gint model_sort_default (FileBrowserNode * node1,
187 FileBrowserNode * node2);
188 static void model_check_dummy (PlumaFileBrowserStore * model,
189 FileBrowserNode * node);
190 static void next_files_async (GFileEnumerator * enumerator,
191 AsyncNode * async);
192
193 static void delete_files (AsyncData *data);
194
195 G_DEFINE_DYNAMIC_TYPE_EXTENDED (PlumaFileBrowserStore, pluma_file_browser_store,
196 G_TYPE_OBJECT,
197 0,
198 G_ADD_PRIVATE_DYNAMIC (PlumaFileBrowserStore)
199 G_IMPLEMENT_INTERFACE_DYNAMIC (GTK_TYPE_TREE_MODEL,
200 pluma_file_browser_store_iface_init)
201 G_IMPLEMENT_INTERFACE_DYNAMIC (GTK_TYPE_TREE_DRAG_SOURCE,
202 pluma_file_browser_store_drag_source_init))
203
204 /* Properties */
205 enum {
206 PROP_0,
207
208 PROP_ROOT,
209 PROP_VIRTUAL_ROOT,
210 PROP_FILTER_MODE
211 };
212
213 /* Signals */
214 enum
215 {
216 BEGIN_LOADING,
217 END_LOADING,
218 ERROR,
219 NO_TRASH,
220 RENAME,
221 BEGIN_REFRESH,
222 END_REFRESH,
223 UNLOAD,
224 NUM_SIGNALS
225 };
226
227 static guint model_signals[NUM_SIGNALS] = { 0 };
228
229 static void
cancel_mount_operation(PlumaFileBrowserStore * obj)230 cancel_mount_operation (PlumaFileBrowserStore *obj)
231 {
232 if (obj->priv->mount_info != NULL)
233 {
234 obj->priv->mount_info->model = NULL;
235 g_cancellable_cancel (obj->priv->mount_info->cancellable);
236 obj->priv->mount_info = NULL;
237 }
238 }
239
240 static void
pluma_file_browser_store_finalize(GObject * object)241 pluma_file_browser_store_finalize (GObject * object)
242 {
243 PlumaFileBrowserStore *obj = PLUMA_FILE_BROWSER_STORE (object);
244 GSList *item;
245
246 /* Free all the nodes */
247 file_browser_node_free (obj, obj->priv->root);
248
249 /* Cancel any asynchronous operations */
250 for (item = obj->priv->async_handles; item; item = item->next)
251 {
252 AsyncData *data = (AsyncData *) (item->data);
253 g_cancellable_cancel (data->cancellable);
254
255 data->removed = TRUE;
256 }
257
258 cancel_mount_operation (obj);
259
260 g_slist_free (obj->priv->async_handles);
261 G_OBJECT_CLASS (pluma_file_browser_store_parent_class)->finalize (object);
262 }
263
264 static void
set_gvalue_from_node(GValue * value,FileBrowserNode * node)265 set_gvalue_from_node (GValue *value,
266 FileBrowserNode *node)
267 {
268 gchar * uri;
269
270 if (node == NULL || !node->file) {
271 g_value_set_string (value, NULL);
272 } else {
273 uri = g_file_get_uri (node->file);
274 g_value_take_string (value, uri);
275 }
276 }
277
278 static void
pluma_file_browser_store_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)279 pluma_file_browser_store_get_property (GObject *object,
280 guint prop_id,
281 GValue *value,
282 GParamSpec *pspec)
283 {
284 PlumaFileBrowserStore *obj = PLUMA_FILE_BROWSER_STORE (object);
285
286 switch (prop_id)
287 {
288 case PROP_ROOT:
289 set_gvalue_from_node (value, obj->priv->root);
290 break;
291 case PROP_VIRTUAL_ROOT:
292 set_gvalue_from_node (value, obj->priv->virtual_root);
293 break;
294 case PROP_FILTER_MODE:
295 g_value_set_flags (value, obj->priv->filter_mode);
296 break;
297 default:
298 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
299 break;
300 }
301 }
302
303 static void
pluma_file_browser_store_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)304 pluma_file_browser_store_set_property (GObject *object,
305 guint prop_id,
306 const GValue *value,
307 GParamSpec *pspec)
308 {
309 PlumaFileBrowserStore *obj = PLUMA_FILE_BROWSER_STORE (object);
310
311 switch (prop_id)
312 {
313 case PROP_FILTER_MODE:
314 pluma_file_browser_store_set_filter_mode (obj,
315 g_value_get_flags (value));
316 break;
317 default:
318 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
319 break;
320 }
321 }
322
323 static void
pluma_file_browser_store_class_init(PlumaFileBrowserStoreClass * klass)324 pluma_file_browser_store_class_init (PlumaFileBrowserStoreClass * klass)
325 {
326 GObjectClass *object_class = G_OBJECT_CLASS (klass);
327
328 object_class->finalize = pluma_file_browser_store_finalize;
329
330 object_class->get_property = pluma_file_browser_store_get_property;
331 object_class->set_property = pluma_file_browser_store_set_property;
332
333 g_object_class_install_property (object_class, PROP_ROOT,
334 g_param_spec_string ("root",
335 "Root",
336 "The root uri",
337 NULL,
338 G_PARAM_READABLE));
339
340 g_object_class_install_property (object_class, PROP_VIRTUAL_ROOT,
341 g_param_spec_string ("virtual-root",
342 "Virtual Root",
343 "The virtual root uri",
344 NULL,
345 G_PARAM_READABLE));
346
347 g_object_class_install_property (object_class, PROP_FILTER_MODE,
348 g_param_spec_flags ("filter-mode",
349 "Filter Mode",
350 "The filter mode",
351 PLUMA_TYPE_FILE_BROWSER_STORE_FILTER_MODE,
352 pluma_file_browser_store_filter_mode_get_default (),
353 G_PARAM_READWRITE));
354
355 model_signals[BEGIN_LOADING] =
356 g_signal_new ("begin-loading",
357 G_OBJECT_CLASS_TYPE (object_class),
358 G_SIGNAL_RUN_LAST,
359 G_STRUCT_OFFSET (PlumaFileBrowserStoreClass, begin_loading),
360 NULL, NULL, NULL,
361 G_TYPE_NONE, 1, GTK_TYPE_TREE_ITER);
362 model_signals[END_LOADING] =
363 g_signal_new ("end-loading",
364 G_OBJECT_CLASS_TYPE (object_class),
365 G_SIGNAL_RUN_LAST,
366 G_STRUCT_OFFSET (PlumaFileBrowserStoreClass, end_loading),
367 NULL, NULL, NULL,
368 G_TYPE_NONE, 1,
369 GTK_TYPE_TREE_ITER);
370 model_signals[ERROR] =
371 g_signal_new ("error", G_OBJECT_CLASS_TYPE (object_class),
372 G_SIGNAL_RUN_LAST,
373 G_STRUCT_OFFSET (PlumaFileBrowserStoreClass, error),
374 NULL, NULL, NULL,
375 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
376 model_signals[NO_TRASH] =
377 g_signal_new ("no-trash", G_OBJECT_CLASS_TYPE (object_class),
378 G_SIGNAL_RUN_LAST,
379 G_STRUCT_OFFSET (PlumaFileBrowserStoreClass, no_trash),
380 g_signal_accumulator_true_handled, NULL, NULL,
381 G_TYPE_BOOLEAN, 1, G_TYPE_POINTER);
382 model_signals[RENAME] =
383 g_signal_new ("rename",
384 G_OBJECT_CLASS_TYPE (object_class),
385 G_SIGNAL_RUN_LAST,
386 G_STRUCT_OFFSET (PlumaFileBrowserStoreClass, rename),
387 NULL, NULL, NULL,
388 G_TYPE_NONE, 2,
389 G_TYPE_STRING,
390 G_TYPE_STRING);
391 model_signals[BEGIN_REFRESH] =
392 g_signal_new ("begin-refresh",
393 G_OBJECT_CLASS_TYPE (object_class),
394 G_SIGNAL_RUN_LAST,
395 G_STRUCT_OFFSET (PlumaFileBrowserStoreClass, begin_refresh),
396 NULL, NULL, NULL,
397 G_TYPE_NONE, 0);
398 model_signals[END_REFRESH] =
399 g_signal_new ("end-refresh",
400 G_OBJECT_CLASS_TYPE (object_class),
401 G_SIGNAL_RUN_LAST,
402 G_STRUCT_OFFSET (PlumaFileBrowserStoreClass, end_refresh),
403 NULL, NULL, NULL,
404 G_TYPE_NONE, 0);
405 model_signals[UNLOAD] =
406 g_signal_new ("unload",
407 G_OBJECT_CLASS_TYPE (object_class),
408 G_SIGNAL_RUN_LAST,
409 G_STRUCT_OFFSET (PlumaFileBrowserStoreClass, unload),
410 NULL, NULL, NULL,
411 G_TYPE_NONE, 1,
412 G_TYPE_STRING);
413 }
414
415 static void
pluma_file_browser_store_class_finalize(PlumaFileBrowserStoreClass * klass)416 pluma_file_browser_store_class_finalize (PlumaFileBrowserStoreClass *klass)
417 {
418 /* dummy function - used by G_DEFINE_DYNAMIC_TYPE_EXTENDED */
419 }
420
421 static void
pluma_file_browser_store_iface_init(GtkTreeModelIface * iface)422 pluma_file_browser_store_iface_init (GtkTreeModelIface * iface)
423 {
424 iface->get_flags = pluma_file_browser_store_get_flags;
425 iface->get_n_columns = pluma_file_browser_store_get_n_columns;
426 iface->get_column_type = pluma_file_browser_store_get_column_type;
427 iface->get_iter = pluma_file_browser_store_get_iter;
428 iface->get_path = pluma_file_browser_store_get_path;
429 iface->get_value = pluma_file_browser_store_get_value;
430 iface->iter_next = pluma_file_browser_store_iter_next;
431 iface->iter_children = pluma_file_browser_store_iter_children;
432 iface->iter_has_child = pluma_file_browser_store_iter_has_child;
433 iface->iter_n_children = pluma_file_browser_store_iter_n_children;
434 iface->iter_nth_child = pluma_file_browser_store_iter_nth_child;
435 iface->iter_parent = pluma_file_browser_store_iter_parent;
436 iface->row_inserted = pluma_file_browser_store_row_inserted;
437 }
438
439 static void
pluma_file_browser_store_drag_source_init(GtkTreeDragSourceIface * iface)440 pluma_file_browser_store_drag_source_init (GtkTreeDragSourceIface * iface)
441 {
442 iface->row_draggable = pluma_file_browser_store_row_draggable;
443 iface->drag_data_delete = pluma_file_browser_store_drag_data_delete;
444 iface->drag_data_get = pluma_file_browser_store_drag_data_get;
445 }
446
447 static void
pluma_file_browser_store_init(PlumaFileBrowserStore * obj)448 pluma_file_browser_store_init (PlumaFileBrowserStore * obj)
449 {
450 obj->priv = pluma_file_browser_store_get_instance_private (obj);
451
452 obj->priv->column_types[PLUMA_FILE_BROWSER_STORE_COLUMN_URI] =
453 G_TYPE_STRING;
454 obj->priv->column_types[PLUMA_FILE_BROWSER_STORE_COLUMN_NAME] =
455 G_TYPE_STRING;
456 obj->priv->column_types[PLUMA_FILE_BROWSER_STORE_COLUMN_FLAGS] =
457 G_TYPE_UINT;
458 obj->priv->column_types[PLUMA_FILE_BROWSER_STORE_COLUMN_ICON] =
459 GDK_TYPE_PIXBUF;
460 obj->priv->column_types[PLUMA_FILE_BROWSER_STORE_COLUMN_EMBLEM] =
461 GDK_TYPE_PIXBUF;
462
463 // Default filter mode is hiding the hidden files
464 obj->priv->filter_mode = pluma_file_browser_store_filter_mode_get_default ();
465 obj->priv->sort_func = model_sort_default;
466 }
467
468 static gboolean
node_has_parent(FileBrowserNode * node,FileBrowserNode * parent)469 node_has_parent (FileBrowserNode * node, FileBrowserNode * parent)
470 {
471 if (node->parent == NULL)
472 return FALSE;
473
474 if (node->parent == parent)
475 return TRUE;
476
477 return node_has_parent (node->parent, parent);
478 }
479
480 static gboolean
node_in_tree(PlumaFileBrowserStore * model,FileBrowserNode * node)481 node_in_tree (PlumaFileBrowserStore * model, FileBrowserNode * node)
482 {
483 return node_has_parent (node, model->priv->virtual_root);
484 }
485
486 static gboolean
model_node_visibility(PlumaFileBrowserStore * model,FileBrowserNode * node)487 model_node_visibility (PlumaFileBrowserStore * model,
488 FileBrowserNode * node)
489 {
490 if (node == NULL)
491 return FALSE;
492
493 if (NODE_IS_DUMMY (node))
494 return !NODE_IS_HIDDEN (node);
495
496 if (node == model->priv->virtual_root)
497 return TRUE;
498
499 if (!node_has_parent (node, model->priv->virtual_root))
500 return FALSE;
501
502 return !NODE_IS_FILTERED (node);
503 }
504
505 static gboolean
model_node_inserted(PlumaFileBrowserStore * model,FileBrowserNode * node)506 model_node_inserted (PlumaFileBrowserStore * model,
507 FileBrowserNode * node)
508 {
509 return node == model->priv->virtual_root || (model_node_visibility (model, node) && node->inserted);
510 }
511
512 /* Interface implementation */
513
514 static GtkTreeModelFlags
pluma_file_browser_store_get_flags(GtkTreeModel * tree_model)515 pluma_file_browser_store_get_flags (GtkTreeModel * tree_model)
516 {
517 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (tree_model),
518 (GtkTreeModelFlags) 0);
519
520 return GTK_TREE_MODEL_ITERS_PERSIST;
521 }
522
523 static gint
pluma_file_browser_store_get_n_columns(GtkTreeModel * tree_model)524 pluma_file_browser_store_get_n_columns (GtkTreeModel * tree_model)
525 {
526 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (tree_model), 0);
527
528 return PLUMA_FILE_BROWSER_STORE_COLUMN_NUM;
529 }
530
531 static GType
pluma_file_browser_store_get_column_type(GtkTreeModel * tree_model,gint idx)532 pluma_file_browser_store_get_column_type (GtkTreeModel * tree_model, gint idx)
533 {
534 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (tree_model),
535 G_TYPE_INVALID);
536 g_return_val_if_fail (idx < PLUMA_FILE_BROWSER_STORE_COLUMN_NUM &&
537 idx >= 0, G_TYPE_INVALID);
538
539 return PLUMA_FILE_BROWSER_STORE (tree_model)->priv->column_types[idx];
540 }
541
542 static gboolean
pluma_file_browser_store_get_iter(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreePath * path)543 pluma_file_browser_store_get_iter (GtkTreeModel * tree_model,
544 GtkTreeIter * iter, GtkTreePath * path)
545 {
546 gint * indices, depth, i;
547 FileBrowserNode * node;
548 PlumaFileBrowserStore * model;
549 gint num;
550
551 g_assert (PLUMA_IS_FILE_BROWSER_STORE (tree_model));
552 g_assert (path != NULL);
553
554 model = PLUMA_FILE_BROWSER_STORE (tree_model);
555 indices = gtk_tree_path_get_indices (path);
556 depth = gtk_tree_path_get_depth (path);
557 node = model->priv->virtual_root;
558
559 for (i = 0; i < depth; ++i) {
560 GSList * item;
561
562 if (node == NULL)
563 return FALSE;
564
565 num = 0;
566
567 if (!NODE_IS_DIR (node))
568 return FALSE;
569
570 for (item = FILE_BROWSER_NODE_DIR (node)->children; item; item = item->next) {
571 FileBrowserNode * child;
572
573 child = (FileBrowserNode *) (item->data);
574
575 if (model_node_inserted (model, child)) {
576 if (num == indices[i]) {
577 node = child;
578 break;
579 }
580
581 num++;
582 }
583 }
584
585 if (item == NULL)
586 return FALSE;
587
588 node = (FileBrowserNode *) (item->data);
589 }
590
591 iter->user_data = node;
592 iter->user_data2 = NULL;
593 iter->user_data3 = NULL;
594
595 return node != NULL;
596 }
597
598 static GtkTreePath *
pluma_file_browser_store_get_path_real(PlumaFileBrowserStore * model,FileBrowserNode * node)599 pluma_file_browser_store_get_path_real (PlumaFileBrowserStore * model,
600 FileBrowserNode * node)
601 {
602 GtkTreePath *path;
603 gint num = 0;
604
605 path = gtk_tree_path_new ();
606
607 while (node != model->priv->virtual_root) {
608 GSList *item;
609
610 if (node->parent == NULL) {
611 gtk_tree_path_free (path);
612 return NULL;
613 }
614
615 num = 0;
616
617 for (item = FILE_BROWSER_NODE_DIR (node->parent)->children; item; item = item->next) {
618 FileBrowserNode *check;
619
620 check = (FileBrowserNode *) (item->data);
621
622 if (model_node_visibility (model, check) && (check == node || check->inserted)) {
623 if (check == node) {
624 gtk_tree_path_prepend_index (path,
625 num);
626 break;
627 }
628
629 ++num;
630 } else if (check == node) {
631 if (NODE_IS_DUMMY (node))
632 g_warning ("Dummy not visible???");
633
634 gtk_tree_path_free (path);
635 return NULL;
636 }
637 }
638
639 node = node->parent;
640 }
641
642 return path;
643 }
644
645 static GtkTreePath *
pluma_file_browser_store_get_path(GtkTreeModel * tree_model,GtkTreeIter * iter)646 pluma_file_browser_store_get_path (GtkTreeModel * tree_model,
647 GtkTreeIter * iter)
648 {
649 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (tree_model), NULL);
650 g_return_val_if_fail (iter != NULL, NULL);
651 g_return_val_if_fail (iter->user_data != NULL, NULL);
652
653 return pluma_file_browser_store_get_path_real (PLUMA_FILE_BROWSER_STORE (tree_model),
654 (FileBrowserNode *) (iter->user_data));
655 }
656
657 static void
pluma_file_browser_store_get_value(GtkTreeModel * tree_model,GtkTreeIter * iter,gint column,GValue * value)658 pluma_file_browser_store_get_value (GtkTreeModel * tree_model,
659 GtkTreeIter * iter,
660 gint column,
661 GValue * value)
662 {
663 FileBrowserNode *node;
664
665 g_return_if_fail (PLUMA_IS_FILE_BROWSER_STORE (tree_model));
666 g_return_if_fail (iter != NULL);
667 g_return_if_fail (iter->user_data != NULL);
668
669 node = (FileBrowserNode *) (iter->user_data);
670
671 g_value_init (value, PLUMA_FILE_BROWSER_STORE (tree_model)->priv->column_types[column]);
672
673 switch (column) {
674 case PLUMA_FILE_BROWSER_STORE_COLUMN_URI:
675 set_gvalue_from_node (value, node);
676 break;
677 case PLUMA_FILE_BROWSER_STORE_COLUMN_NAME:
678 g_value_set_string (value, node->name);
679 break;
680 case PLUMA_FILE_BROWSER_STORE_COLUMN_FLAGS:
681 g_value_set_uint (value, node->flags);
682 break;
683 case PLUMA_FILE_BROWSER_STORE_COLUMN_ICON:
684 g_value_set_object (value, node->icon);
685 break;
686 case PLUMA_FILE_BROWSER_STORE_COLUMN_EMBLEM:
687 g_value_set_object (value, node->emblem);
688 break;
689 default:
690 g_return_if_reached ();
691 }
692 }
693
694 static gboolean
pluma_file_browser_store_iter_next(GtkTreeModel * tree_model,GtkTreeIter * iter)695 pluma_file_browser_store_iter_next (GtkTreeModel * tree_model,
696 GtkTreeIter * iter)
697 {
698 PlumaFileBrowserStore * model;
699 FileBrowserNode * node;
700 GSList * item;
701 GSList * first;
702
703 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (tree_model),
704 FALSE);
705 g_return_val_if_fail (iter != NULL, FALSE);
706 g_return_val_if_fail (iter->user_data != NULL, FALSE);
707
708 model = PLUMA_FILE_BROWSER_STORE (tree_model);
709 node = (FileBrowserNode *) (iter->user_data);
710
711 if (node->parent == NULL)
712 return FALSE;
713
714 first = g_slist_next (g_slist_find (FILE_BROWSER_NODE_DIR (node->parent)->children, node));
715
716 for (item = first; item; item = item->next) {
717 if (model_node_inserted (model, (FileBrowserNode *) (item->data))) {
718 iter->user_data = item->data;
719 return TRUE;
720 }
721 }
722
723 return FALSE;
724 }
725
726 static gboolean
pluma_file_browser_store_iter_children(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * parent)727 pluma_file_browser_store_iter_children (GtkTreeModel * tree_model,
728 GtkTreeIter * iter,
729 GtkTreeIter * parent)
730 {
731 FileBrowserNode * node;
732 PlumaFileBrowserStore * model;
733 GSList * item;
734
735 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (tree_model),
736 FALSE);
737 g_return_val_if_fail (parent == NULL
738 || parent->user_data != NULL, FALSE);
739
740 model = PLUMA_FILE_BROWSER_STORE (tree_model);
741
742 if (parent == NULL)
743 node = model->priv->virtual_root;
744 else
745 node = (FileBrowserNode *) (parent->user_data);
746
747 if (node == NULL)
748 return FALSE;
749
750 if (!NODE_IS_DIR (node))
751 return FALSE;
752
753 for (item = FILE_BROWSER_NODE_DIR (node)->children; item; item = item->next) {
754 if (model_node_inserted (model, (FileBrowserNode *) (item->data))) {
755 iter->user_data = item->data;
756 return TRUE;
757 }
758 }
759
760 return FALSE;
761 }
762
763 static gboolean
filter_tree_model_iter_has_child_real(PlumaFileBrowserStore * model,FileBrowserNode * node)764 filter_tree_model_iter_has_child_real (PlumaFileBrowserStore * model,
765 FileBrowserNode * node)
766 {
767 GSList *item;
768
769 if (!NODE_IS_DIR (node))
770 return FALSE;
771
772 for (item = FILE_BROWSER_NODE_DIR (node)->children; item; item = item->next) {
773 if (model_node_inserted (model, (FileBrowserNode *) (item->data)))
774 return TRUE;
775 }
776
777 return FALSE;
778 }
779
780 static gboolean
pluma_file_browser_store_iter_has_child(GtkTreeModel * tree_model,GtkTreeIter * iter)781 pluma_file_browser_store_iter_has_child (GtkTreeModel * tree_model,
782 GtkTreeIter * iter)
783 {
784 FileBrowserNode *node;
785 PlumaFileBrowserStore *model;
786
787 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (tree_model),
788 FALSE);
789 g_return_val_if_fail (iter == NULL
790 || iter->user_data != NULL, FALSE);
791
792 model = PLUMA_FILE_BROWSER_STORE (tree_model);
793
794 if (iter == NULL)
795 node = model->priv->virtual_root;
796 else
797 node = (FileBrowserNode *) (iter->user_data);
798
799 return filter_tree_model_iter_has_child_real (model, node);
800 }
801
802 static gint
pluma_file_browser_store_iter_n_children(GtkTreeModel * tree_model,GtkTreeIter * iter)803 pluma_file_browser_store_iter_n_children (GtkTreeModel * tree_model,
804 GtkTreeIter * iter)
805 {
806 FileBrowserNode *node;
807 PlumaFileBrowserStore *model;
808 GSList *item;
809 gint num = 0;
810
811 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (tree_model),
812 FALSE);
813 g_return_val_if_fail (iter == NULL
814 || iter->user_data != NULL, FALSE);
815
816 model = PLUMA_FILE_BROWSER_STORE (tree_model);
817
818 if (iter == NULL)
819 node = model->priv->virtual_root;
820 else
821 node = (FileBrowserNode *) (iter->user_data);
822
823 if (!NODE_IS_DIR (node))
824 return 0;
825
826 for (item = FILE_BROWSER_NODE_DIR (node)->children; item; item = item->next)
827 if (model_node_inserted (model, (FileBrowserNode *) (item->data)))
828 ++num;
829
830 return num;
831 }
832
833 static gboolean
pluma_file_browser_store_iter_nth_child(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * parent,gint n)834 pluma_file_browser_store_iter_nth_child (GtkTreeModel * tree_model,
835 GtkTreeIter * iter,
836 GtkTreeIter * parent, gint n)
837 {
838 FileBrowserNode *node;
839 PlumaFileBrowserStore *model;
840 GSList *item;
841 gint num = 0;
842
843 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (tree_model),
844 FALSE);
845 g_return_val_if_fail (parent == NULL
846 || parent->user_data != NULL, FALSE);
847
848 model = PLUMA_FILE_BROWSER_STORE (tree_model);
849
850 if (parent == NULL)
851 node = model->priv->virtual_root;
852 else
853 node = (FileBrowserNode *) (parent->user_data);
854
855 if (!NODE_IS_DIR (node))
856 return FALSE;
857
858 for (item = FILE_BROWSER_NODE_DIR (node)->children; item;
859 item = item->next) {
860 if (model_node_inserted (model, (FileBrowserNode *) (item->data))) {
861 if (num == n) {
862 iter->user_data = item->data;
863 return TRUE;
864 }
865
866 ++num;
867 }
868 }
869
870 return FALSE;
871 }
872
873 static gboolean
pluma_file_browser_store_iter_parent(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * child)874 pluma_file_browser_store_iter_parent (GtkTreeModel * tree_model,
875 GtkTreeIter * iter,
876 GtkTreeIter * child)
877 {
878 FileBrowserNode *node;
879 PlumaFileBrowserStore *model;
880
881 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (tree_model), FALSE);
882 g_return_val_if_fail (child != NULL, FALSE);
883 g_return_val_if_fail (child->user_data != NULL, FALSE);
884
885 node = (FileBrowserNode *) (child->user_data);
886 model = PLUMA_FILE_BROWSER_STORE (tree_model);
887
888 if (!node_in_tree (model, node))
889 return FALSE;
890
891 if (node->parent == NULL)
892 return FALSE;
893
894 iter->user_data = node->parent;
895 return TRUE;
896 }
897
898 static void
pluma_file_browser_store_row_inserted(GtkTreeModel * tree_model,GtkTreePath * path,GtkTreeIter * iter)899 pluma_file_browser_store_row_inserted (GtkTreeModel * tree_model,
900 GtkTreePath * path,
901 GtkTreeIter * iter)
902 {
903 FileBrowserNode * node = (FileBrowserNode *)(iter->user_data);
904
905 node->inserted = TRUE;
906 }
907
908 static gboolean
pluma_file_browser_store_row_draggable(GtkTreeDragSource * drag_source,GtkTreePath * path)909 pluma_file_browser_store_row_draggable (GtkTreeDragSource * drag_source,
910 GtkTreePath * path)
911 {
912 GtkTreeIter iter;
913 PlumaFileBrowserStoreFlag flags;
914
915 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (drag_source),
916 &iter, path))
917 {
918 return FALSE;
919 }
920
921 gtk_tree_model_get (GTK_TREE_MODEL (drag_source), &iter,
922 PLUMA_FILE_BROWSER_STORE_COLUMN_FLAGS, &flags,
923 -1);
924
925 return !FILE_IS_DUMMY(flags);
926 }
927
928 static gboolean
pluma_file_browser_store_drag_data_delete(GtkTreeDragSource * drag_source,GtkTreePath * path)929 pluma_file_browser_store_drag_data_delete (GtkTreeDragSource * drag_source,
930 GtkTreePath * path)
931 {
932 return FALSE;
933 }
934
935 static gboolean
pluma_file_browser_store_drag_data_get(GtkTreeDragSource * drag_source,GtkTreePath * path,GtkSelectionData * selection_data)936 pluma_file_browser_store_drag_data_get (GtkTreeDragSource * drag_source,
937 GtkTreePath * path,
938 GtkSelectionData * selection_data)
939 {
940 GtkTreeIter iter;
941 gchar *uri;
942 gchar *uris[2] = {0, };
943 gboolean ret;
944
945 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (drag_source),
946 &iter, path))
947 {
948 return FALSE;
949 }
950
951 gtk_tree_model_get (GTK_TREE_MODEL (drag_source), &iter,
952 PLUMA_FILE_BROWSER_STORE_COLUMN_URI, &uri,
953 -1);
954
955 g_assert (uri);
956
957 uris[0] = uri;
958 ret = gtk_selection_data_set_uris (selection_data, uris);
959
960 g_free (uri);
961
962 return ret;
963 }
964
965 #define FILTER_HIDDEN(mode) (mode & PLUMA_FILE_BROWSER_STORE_FILTER_MODE_HIDE_HIDDEN)
966 #define FILTER_BINARY(mode) (mode & PLUMA_FILE_BROWSER_STORE_FILTER_MODE_HIDE_BINARY)
967
968 /* Private */
969 static void
model_begin_loading(PlumaFileBrowserStore * model,FileBrowserNode * node)970 model_begin_loading (PlumaFileBrowserStore * model, FileBrowserNode * node)
971 {
972 GtkTreeIter iter;
973
974 iter.user_data = node;
975 g_signal_emit (model, model_signals[BEGIN_LOADING], 0, &iter);
976 }
977
978 static void
model_end_loading(PlumaFileBrowserStore * model,FileBrowserNode * node)979 model_end_loading (PlumaFileBrowserStore * model, FileBrowserNode * node)
980 {
981 GtkTreeIter iter;
982
983 iter.user_data = node;
984 g_signal_emit (model, model_signals[END_LOADING], 0, &iter);
985 }
986
987 static void
model_node_update_visibility(PlumaFileBrowserStore * model,FileBrowserNode * node)988 model_node_update_visibility (PlumaFileBrowserStore * model,
989 FileBrowserNode * node)
990 {
991 GtkTreeIter iter;
992
993 node->flags &= ~PLUMA_FILE_BROWSER_STORE_FLAG_IS_FILTERED;
994
995 if (FILTER_HIDDEN (model->priv->filter_mode) &&
996 NODE_IS_HIDDEN (node))
997 node->flags |= PLUMA_FILE_BROWSER_STORE_FLAG_IS_FILTERED;
998 else if (FILTER_BINARY (model->priv->filter_mode) &&
999 (!NODE_IS_TEXT (node) && !NODE_IS_DIR (node)))
1000 node->flags |= PLUMA_FILE_BROWSER_STORE_FLAG_IS_FILTERED;
1001 else if (model->priv->filter_func) {
1002 iter.user_data = node;
1003
1004 if (!model->priv->
1005 filter_func (model, &iter,
1006 model->priv->filter_user_data))
1007 node->flags |=
1008 PLUMA_FILE_BROWSER_STORE_FLAG_IS_FILTERED;
1009 }
1010 }
1011
1012 static gint
collate_nodes(FileBrowserNode * node1,FileBrowserNode * node2)1013 collate_nodes (FileBrowserNode * node1, FileBrowserNode * node2)
1014 {
1015 if (node1->name == NULL)
1016 return -1;
1017 else if (node2->name == NULL)
1018 return 1;
1019 else {
1020 gchar *k1, *k2;
1021 gint result;
1022
1023 k1 = g_utf8_collate_key_for_filename (node1->name, -1);
1024 k2 = g_utf8_collate_key_for_filename (node2->name, -1);
1025
1026 result = strcmp (k1, k2);
1027
1028 g_free (k1);
1029 g_free (k2);
1030
1031 return result;
1032 }
1033 }
1034
1035 static gint
model_sort_default(FileBrowserNode * node1,FileBrowserNode * node2)1036 model_sort_default (FileBrowserNode * node1, FileBrowserNode * node2)
1037 {
1038 gint f1;
1039 gint f2;
1040
1041 f1 = NODE_IS_DUMMY (node1);
1042 f2 = NODE_IS_DUMMY (node2);
1043
1044 if (f1 && f2)
1045 {
1046 return 0;
1047 }
1048 else if (f1 || f2)
1049 {
1050 return f1 ? -1 : 1;
1051 }
1052
1053 f1 = NODE_IS_DIR (node1);
1054 f2 = NODE_IS_DIR (node2);
1055
1056 if (f1 != f2)
1057 {
1058 return f1 ? -1 : 1;
1059 }
1060
1061 f1 = NODE_IS_HIDDEN (node1);
1062 f2 = NODE_IS_HIDDEN (node2);
1063
1064 if (f1 != f2)
1065 {
1066 return f2 ? -1 : 1;
1067 }
1068
1069 return collate_nodes (node1, node2);
1070 }
1071
1072 static void
model_resort_node(PlumaFileBrowserStore * model,FileBrowserNode * node)1073 model_resort_node (PlumaFileBrowserStore * model, FileBrowserNode * node)
1074 {
1075 FileBrowserNodeDir *dir;
1076 GSList *item;
1077 FileBrowserNode *child;
1078 gint pos = 0;
1079 GtkTreeIter iter;
1080 GtkTreePath *path;
1081 gint *neworder;
1082
1083 dir = FILE_BROWSER_NODE_DIR (node->parent);
1084
1085 if (!model_node_visibility (model, node->parent)) {
1086 /* Just sort the children of the parent */
1087 dir->children = g_slist_sort (dir->children,
1088 (GCompareFunc) (model->priv->
1089 sort_func));
1090 } else {
1091 /* Store current positions */
1092 for (item = dir->children; item; item = item->next) {
1093 child = (FileBrowserNode *) (item->data);
1094
1095 if (model_node_visibility (model, child))
1096 child->pos = pos++;
1097 }
1098
1099 dir->children = g_slist_sort (dir->children,
1100 (GCompareFunc) (model->priv->
1101 sort_func));
1102 neworder = g_new (gint, pos);
1103 pos = 0;
1104
1105 /* Store the new positions */
1106 for (item = dir->children; item; item = item->next) {
1107 child = (FileBrowserNode *) (item->data);
1108
1109 if (model_node_visibility (model, child))
1110 neworder[pos++] = child->pos;
1111 }
1112
1113 iter.user_data = node->parent;
1114 path =
1115 pluma_file_browser_store_get_path_real (model,
1116 node->parent);
1117
1118 gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model),
1119 path, &iter, neworder);
1120
1121 g_free (neworder);
1122 gtk_tree_path_free (path);
1123 }
1124 }
1125
1126 static void
row_changed(PlumaFileBrowserStore * model,GtkTreePath ** path,GtkTreeIter * iter)1127 row_changed (PlumaFileBrowserStore * model,
1128 GtkTreePath ** path,
1129 GtkTreeIter * iter)
1130 {
1131 GtkTreeRowReference *ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (model), *path);
1132
1133 /* Insert a copy of the actual path here because the row-inserted
1134 signal may alter the path */
1135 gtk_tree_model_row_changed (GTK_TREE_MODEL(model), *path, iter);
1136 gtk_tree_path_free (*path);
1137
1138 *path = gtk_tree_row_reference_get_path (ref);
1139 gtk_tree_row_reference_free (ref);
1140 }
1141
1142 static void
row_inserted(PlumaFileBrowserStore * model,GtkTreePath ** path,GtkTreeIter * iter)1143 row_inserted (PlumaFileBrowserStore * model,
1144 GtkTreePath ** path,
1145 GtkTreeIter * iter)
1146 {
1147 /* This function creates a row reference for the path because it's
1148 uncertain what might change the actual model/view when we insert
1149 a node, maybe another directory load is triggered for example.
1150 Because functions that use this function rely on the notion that
1151 the path remains pointed towards the inserted node, we use the
1152 reference to keep track. */
1153 GtkTreeRowReference *ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (model), *path);
1154 GtkTreePath * copy = gtk_tree_path_copy (*path);
1155
1156 gtk_tree_model_row_inserted (GTK_TREE_MODEL(model), copy, iter);
1157 gtk_tree_path_free (copy);
1158
1159 if (ref)
1160 {
1161 gtk_tree_path_free (*path);
1162
1163 /* To restore the path, we get the path from the reference. But, since
1164 we inserted a row, the path will be one index further than the
1165 actual path of our node. We therefore call gtk_tree_path_prev */
1166 *path = gtk_tree_row_reference_get_path (ref);
1167 gtk_tree_path_prev (*path);
1168 }
1169
1170 gtk_tree_row_reference_free (ref);
1171 }
1172
1173 static void
row_deleted(PlumaFileBrowserStore * model,const GtkTreePath * path)1174 row_deleted (PlumaFileBrowserStore * model,
1175 const GtkTreePath * path)
1176 {
1177 GtkTreePath *copy = gtk_tree_path_copy (path);
1178
1179 /* Delete a copy of the actual path here because the row-deleted
1180 signal may alter the path */
1181 gtk_tree_model_row_deleted (GTK_TREE_MODEL(model), copy);
1182 gtk_tree_path_free (copy);
1183 }
1184
1185 static void
model_refilter_node(PlumaFileBrowserStore * model,FileBrowserNode * node,GtkTreePath ** path)1186 model_refilter_node (PlumaFileBrowserStore * model,
1187 FileBrowserNode * node,
1188 GtkTreePath ** path)
1189 {
1190 gboolean old_visible;
1191 gboolean new_visible;
1192 FileBrowserNodeDir *dir;
1193 GSList *item;
1194 GtkTreeIter iter;
1195 GtkTreePath *tmppath = NULL;
1196 gboolean in_tree;
1197
1198 if (node == NULL)
1199 return;
1200
1201 old_visible = model_node_visibility (model, node);
1202 model_node_update_visibility (model, node);
1203
1204 in_tree = node_in_tree (model, node);
1205
1206 if (path == NULL)
1207 {
1208 if (in_tree)
1209 tmppath = pluma_file_browser_store_get_path_real (model,
1210 node);
1211 else
1212 tmppath = gtk_tree_path_new_first ();
1213
1214 path = &tmppath;
1215 }
1216
1217 if (NODE_IS_DIR (node)) {
1218 if (in_tree)
1219 gtk_tree_path_down (*path);
1220
1221 dir = FILE_BROWSER_NODE_DIR (node);
1222
1223 for (item = dir->children; item; item = item->next) {
1224 model_refilter_node (model,
1225 (FileBrowserNode *) (item->data),
1226 path);
1227 }
1228
1229 if (in_tree)
1230 gtk_tree_path_up (*path);
1231 }
1232
1233 if (in_tree) {
1234 new_visible = model_node_visibility (model, node);
1235
1236 if (old_visible != new_visible) {
1237 if (old_visible) {
1238 node->inserted = FALSE;
1239 row_deleted (model, *path);
1240 } else {
1241 iter.user_data = node;
1242 row_inserted (model, path, &iter);
1243 gtk_tree_path_next (*path);
1244 }
1245 } else if (old_visible) {
1246 gtk_tree_path_next (*path);
1247 }
1248 }
1249
1250 model_check_dummy (model, node);
1251
1252 if (tmppath)
1253 gtk_tree_path_free (tmppath);
1254 }
1255
1256 static void
model_refilter(PlumaFileBrowserStore * model)1257 model_refilter (PlumaFileBrowserStore * model)
1258 {
1259 model_refilter_node (model, model->priv->root, NULL);
1260 }
1261
1262 static void
file_browser_node_set_name(FileBrowserNode * node)1263 file_browser_node_set_name (FileBrowserNode * node)
1264 {
1265 g_free (node->name);
1266
1267 if (node->file) {
1268 node->name = pluma_file_browser_utils_file_basename (node->file);
1269 } else {
1270 node->name = NULL;
1271 }
1272 }
1273
1274 static void
file_browser_node_init(FileBrowserNode * node,GFile * file,FileBrowserNode * parent)1275 file_browser_node_init (FileBrowserNode * node, GFile * file,
1276 FileBrowserNode * parent)
1277 {
1278 if (file != NULL) {
1279 node->file = g_object_ref (file);
1280 file_browser_node_set_name (node);
1281 }
1282
1283 node->parent = parent;
1284 }
1285
1286 static FileBrowserNode *
file_browser_node_new(GFile * file,FileBrowserNode * parent)1287 file_browser_node_new (GFile * file, FileBrowserNode * parent)
1288 {
1289 FileBrowserNode *node = g_slice_new0 (FileBrowserNode);
1290
1291 file_browser_node_init (node, file, parent);
1292 return node;
1293 }
1294
1295 static FileBrowserNode *
file_browser_node_dir_new(PlumaFileBrowserStore * model,GFile * file,FileBrowserNode * parent)1296 file_browser_node_dir_new (PlumaFileBrowserStore * model,
1297 GFile * file, FileBrowserNode * parent)
1298 {
1299 FileBrowserNode *node =
1300 (FileBrowserNode *) g_slice_new0 (FileBrowserNodeDir);
1301
1302 file_browser_node_init (node, file, parent);
1303
1304 node->flags |= PLUMA_FILE_BROWSER_STORE_FLAG_IS_DIRECTORY;
1305
1306 FILE_BROWSER_NODE_DIR (node)->model = model;
1307
1308 return node;
1309 }
1310
1311 static void
file_browser_node_free_children(PlumaFileBrowserStore * model,FileBrowserNode * node)1312 file_browser_node_free_children (PlumaFileBrowserStore * model,
1313 FileBrowserNode * node)
1314 {
1315 GSList *item;
1316
1317 if (node == NULL)
1318 return;
1319
1320 if (NODE_IS_DIR (node)) {
1321 for (item = FILE_BROWSER_NODE_DIR (node)->children; item;
1322 item = item->next)
1323 file_browser_node_free (model,
1324 (FileBrowserNode *) (item->
1325 data));
1326
1327 g_slist_free (FILE_BROWSER_NODE_DIR (node)->children);
1328 FILE_BROWSER_NODE_DIR (node)->children = NULL;
1329
1330 /* This node is no longer loaded */
1331 node->flags &= ~PLUMA_FILE_BROWSER_STORE_FLAG_LOADED;
1332 }
1333 }
1334
1335 static void
file_browser_node_free(PlumaFileBrowserStore * model,FileBrowserNode * node)1336 file_browser_node_free (PlumaFileBrowserStore * model,
1337 FileBrowserNode * node)
1338 {
1339 gchar *uri;
1340
1341 if (node == NULL)
1342 return;
1343
1344 if (NODE_IS_DIR (node))
1345 {
1346 FileBrowserNodeDir *dir;
1347
1348 dir = FILE_BROWSER_NODE_DIR (node);
1349
1350 if (dir->cancellable) {
1351 g_cancellable_cancel (dir->cancellable);
1352 g_object_unref (dir->cancellable);
1353
1354 model_end_loading (model, node);
1355 }
1356
1357 file_browser_node_free_children (model, node);
1358
1359 if (dir->monitor) {
1360 g_file_monitor_cancel (dir->monitor);
1361 g_object_unref (dir->monitor);
1362 }
1363 }
1364
1365 if (node->file)
1366 {
1367 uri = g_file_get_uri (node->file);
1368 g_signal_emit (model, model_signals[UNLOAD], 0, uri);
1369
1370 g_free (uri);
1371 g_object_unref (node->file);
1372 }
1373
1374 if (node->icon)
1375 g_object_unref (node->icon);
1376
1377 if (node->emblem)
1378 g_object_unref (node->emblem);
1379
1380 g_free (node->name);
1381
1382 if (NODE_IS_DIR (node))
1383 g_slice_free (FileBrowserNodeDir, (FileBrowserNodeDir *)node);
1384 else
1385 g_slice_free (FileBrowserNode, (FileBrowserNode *)node);
1386 }
1387
1388 /**
1389 * model_remove_node_children:
1390 * @model: the #PlumaFileBrowserStore
1391 * @node: the FileBrowserNode to remove
1392 * @path: the path of the node, or NULL to let the path be calculated
1393 * @free_nodes: whether to also remove the nodes from memory
1394 *
1395 * Removes all the children of node from the model. This function is used
1396 * to remove the child nodes from the _model_. Don't use it to just free
1397 * a node.
1398 **/
1399 static void
model_remove_node_children(PlumaFileBrowserStore * model,FileBrowserNode * node,GtkTreePath * path,gboolean free_nodes)1400 model_remove_node_children (PlumaFileBrowserStore * model,
1401 FileBrowserNode * node,
1402 GtkTreePath * path,
1403 gboolean free_nodes)
1404 {
1405 FileBrowserNodeDir *dir;
1406 GtkTreePath *path_child;
1407 GSList *list;
1408 GSList *item;
1409
1410 if (node == NULL || !NODE_IS_DIR (node))
1411 return;
1412
1413 dir = FILE_BROWSER_NODE_DIR (node);
1414
1415 if (dir->children == NULL)
1416 return;
1417
1418 if (!model_node_visibility (model, node)) {
1419 // Node is invisible and therefore the children can just
1420 // be freed
1421 if (free_nodes)
1422 file_browser_node_free_children (model, node);
1423
1424 return;
1425 }
1426
1427 if (path == NULL)
1428 path_child =
1429 pluma_file_browser_store_get_path_real (model, node);
1430 else
1431 path_child = gtk_tree_path_copy (path);
1432
1433 gtk_tree_path_down (path_child);
1434
1435 list = g_slist_copy (dir->children);
1436
1437 for (item = list; item; item = item->next) {
1438 model_remove_node (model, (FileBrowserNode *) (item->data),
1439 path_child, free_nodes);
1440 }
1441
1442 g_slist_free (list);
1443 gtk_tree_path_free (path_child);
1444 }
1445
1446 /**
1447 * model_remove_node:
1448 * @model: the #PlumaFileBrowserStore
1449 * @node: the FileBrowserNode to remove
1450 * @path: the path to use to remove this node, or NULL to use the path
1451 * calculated from the node itself
1452 * @free_nodes: whether to also remove the nodes from memory
1453 *
1454 * Removes this node and all its children from the model. This function is used
1455 * to remove the node from the _model_. Don't use it to just free
1456 * a node.
1457 **/
1458 static void
model_remove_node(PlumaFileBrowserStore * model,FileBrowserNode * node,GtkTreePath * path,gboolean free_nodes)1459 model_remove_node (PlumaFileBrowserStore * model,
1460 FileBrowserNode * node,
1461 GtkTreePath * path,
1462 gboolean free_nodes)
1463 {
1464 gboolean free_path = FALSE;
1465 FileBrowserNode *parent;
1466
1467 if (path == NULL) {
1468 path =
1469 pluma_file_browser_store_get_path_real (model, node);
1470 free_path = TRUE;
1471 }
1472
1473 model_remove_node_children (model, node, path, free_nodes);
1474
1475 /* Only delete if the node is visible in the tree (but only when it's
1476 not the virtual root) */
1477 if (model_node_visibility (model, node) && node != model->priv->virtual_root)
1478 {
1479 node->inserted = FALSE;
1480 row_deleted (model, path);
1481 }
1482
1483 if (free_path)
1484 gtk_tree_path_free (path);
1485
1486 parent = node->parent;
1487
1488 if (free_nodes) {
1489 /* Remove the node from the parents children list */
1490 if (parent)
1491 FILE_BROWSER_NODE_DIR (node->parent)->children =
1492 g_slist_remove (FILE_BROWSER_NODE_DIR
1493 (node->parent)->children,
1494 node);
1495 }
1496
1497 /* If this is the virtual root, than set the parent as the virtual root */
1498 if (node == model->priv->virtual_root)
1499 set_virtual_root_from_node (model, parent);
1500 else if (parent && model_node_visibility (model, parent) && !(free_nodes && NODE_IS_DUMMY(node)))
1501 model_check_dummy (model, parent);
1502
1503 /* Now free the node if necessary */
1504 if (free_nodes)
1505 file_browser_node_free (model, node);
1506 }
1507
1508 /**
1509 * model_clear:
1510 * @model: the #PlumaFileBrowserStore
1511 * @free_nodes: whether to also remove the nodes from memory
1512 *
1513 * Removes all nodes from the model. This function is used
1514 * to remove all the nodes from the _model_. Don't use it to just free the
1515 * nodes in the model.
1516 **/
1517 static void
model_clear(PlumaFileBrowserStore * model,gboolean free_nodes)1518 model_clear (PlumaFileBrowserStore * model, gboolean free_nodes)
1519 {
1520 GtkTreePath *path;
1521 FileBrowserNodeDir *dir;
1522 FileBrowserNode *dummy;
1523
1524 path = gtk_tree_path_new ();
1525 model_remove_node_children (model, model->priv->virtual_root, path,
1526 free_nodes);
1527 gtk_tree_path_free (path);
1528
1529 /* Remove the dummy if there is one */
1530 if (model->priv->virtual_root) {
1531 dir = FILE_BROWSER_NODE_DIR (model->priv->virtual_root);
1532
1533 if (dir->children != NULL) {
1534 dummy = (FileBrowserNode *) (dir->children->data);
1535
1536 if (NODE_IS_DUMMY (dummy)
1537 && model_node_visibility (model, dummy)) {
1538 path = gtk_tree_path_new_first ();
1539
1540 dummy->inserted = FALSE;
1541 row_deleted (model, path);
1542 gtk_tree_path_free (path);
1543 }
1544 }
1545 }
1546 }
1547
1548 static void
file_browser_node_unload(PlumaFileBrowserStore * model,FileBrowserNode * node,gboolean remove_children)1549 file_browser_node_unload (PlumaFileBrowserStore * model,
1550 FileBrowserNode * node, gboolean remove_children)
1551 {
1552 FileBrowserNodeDir *dir;
1553
1554 if (node == NULL)
1555 return;
1556
1557 if (!NODE_IS_DIR (node) || !NODE_LOADED (node))
1558 return;
1559
1560 dir = FILE_BROWSER_NODE_DIR (node);
1561
1562 if (remove_children)
1563 model_remove_node_children (model, node, NULL, TRUE);
1564
1565 if (dir->cancellable) {
1566 g_cancellable_cancel (dir->cancellable);
1567 g_object_unref (dir->cancellable);
1568
1569 model_end_loading (model, node);
1570 dir->cancellable = NULL;
1571 }
1572
1573 if (dir->monitor) {
1574 g_file_monitor_cancel (dir->monitor);
1575 g_object_unref (dir->monitor);
1576
1577 dir->monitor = NULL;
1578 }
1579
1580 node->flags &= ~PLUMA_FILE_BROWSER_STORE_FLAG_LOADED;
1581 }
1582
1583 static void
model_recomposite_icon_real(PlumaFileBrowserStore * tree_model,FileBrowserNode * node,GFileInfo * info)1584 model_recomposite_icon_real (PlumaFileBrowserStore * tree_model,
1585 FileBrowserNode * node,
1586 GFileInfo * info)
1587 {
1588 GdkPixbuf *icon;
1589
1590 g_return_if_fail (PLUMA_IS_FILE_BROWSER_STORE (tree_model));
1591 g_return_if_fail (node != NULL);
1592
1593 if (node->file == NULL)
1594 return;
1595
1596 if (info) {
1597 GIcon *gicon = g_file_info_get_icon (info);
1598 if (gicon != NULL)
1599 icon = pluma_file_browser_utils_pixbuf_from_icon (gicon, GTK_ICON_SIZE_MENU);
1600 else
1601 icon = NULL;
1602 } else {
1603 icon = pluma_file_browser_utils_pixbuf_from_file (node->file, GTK_ICON_SIZE_MENU);
1604 }
1605
1606 if (node->icon)
1607 g_object_unref (node->icon);
1608
1609 if (node->emblem) {
1610 gint icon_size;
1611
1612 gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, NULL, &icon_size);
1613
1614 if (icon == NULL) {
1615 node->icon =
1616 gdk_pixbuf_new (gdk_pixbuf_get_colorspace (node->emblem),
1617 gdk_pixbuf_get_has_alpha (node->emblem),
1618 gdk_pixbuf_get_bits_per_sample (node->emblem),
1619 icon_size,
1620 icon_size);
1621 } else {
1622 node->icon = gdk_pixbuf_copy (icon);
1623 g_object_unref (icon);
1624 }
1625
1626 gdk_pixbuf_composite (node->emblem, node->icon,
1627 icon_size - 10, icon_size - 10, 10,
1628 10, icon_size - 10, icon_size - 10,
1629 1, 1, GDK_INTERP_NEAREST, 255);
1630 } else {
1631 node->icon = icon;
1632 }
1633 }
1634
1635 static void
model_recomposite_icon(PlumaFileBrowserStore * tree_model,GtkTreeIter * iter)1636 model_recomposite_icon (PlumaFileBrowserStore * tree_model,
1637 GtkTreeIter * iter)
1638 {
1639 g_return_if_fail (PLUMA_IS_FILE_BROWSER_STORE (tree_model));
1640 g_return_if_fail (iter != NULL);
1641 g_return_if_fail (iter->user_data != NULL);
1642
1643 model_recomposite_icon_real (tree_model,
1644 (FileBrowserNode *) (iter->user_data),
1645 NULL);
1646 }
1647
1648 static FileBrowserNode *
model_create_dummy_node(PlumaFileBrowserStore * model,FileBrowserNode * parent)1649 model_create_dummy_node (PlumaFileBrowserStore * model,
1650 FileBrowserNode * parent)
1651 {
1652 FileBrowserNode *dummy;
1653
1654 dummy = file_browser_node_new (NULL, parent);
1655 dummy->name = g_strdup (_("(Empty)"));
1656
1657 dummy->flags |= PLUMA_FILE_BROWSER_STORE_FLAG_IS_DUMMY;
1658 dummy->flags |= PLUMA_FILE_BROWSER_STORE_FLAG_IS_HIDDEN;
1659
1660 return dummy;
1661 }
1662
1663 static FileBrowserNode *
model_add_dummy_node(PlumaFileBrowserStore * model,FileBrowserNode * parent)1664 model_add_dummy_node (PlumaFileBrowserStore * model,
1665 FileBrowserNode * parent)
1666 {
1667 FileBrowserNode *dummy;
1668
1669 dummy = model_create_dummy_node (model, parent);
1670
1671 if (model_node_visibility (model, parent))
1672 dummy->flags &= ~PLUMA_FILE_BROWSER_STORE_FLAG_IS_HIDDEN;
1673
1674 model_add_node (model, dummy, parent);
1675
1676 return dummy;
1677 }
1678
1679 static void
model_check_dummy(PlumaFileBrowserStore * model,FileBrowserNode * node)1680 model_check_dummy (PlumaFileBrowserStore * model, FileBrowserNode * node)
1681 {
1682 // Hide the dummy child if needed
1683 if (NODE_IS_DIR (node)) {
1684 FileBrowserNode *dummy;
1685 GtkTreeIter iter;
1686 GtkTreePath *path;
1687 guint flags;
1688 FileBrowserNodeDir *dir;
1689
1690 dir = FILE_BROWSER_NODE_DIR (node);
1691
1692 if (dir->children == NULL) {
1693 model_add_dummy_node (model, node);
1694 return;
1695 }
1696
1697 dummy = (FileBrowserNode *) (dir->children->data);
1698
1699 if (!NODE_IS_DUMMY (dummy)) {
1700 dummy = model_create_dummy_node (model, node);
1701 dir->children = g_slist_prepend (dir->children, dummy);
1702 }
1703
1704 if (!model_node_visibility (model, node)) {
1705 dummy->flags |=
1706 PLUMA_FILE_BROWSER_STORE_FLAG_IS_HIDDEN;
1707 return;
1708 }
1709
1710 /* Temporarily set the node to invisible to check
1711 * for real children */
1712 flags = dummy->flags;
1713 dummy->flags |= PLUMA_FILE_BROWSER_STORE_FLAG_IS_HIDDEN;
1714
1715 if (!filter_tree_model_iter_has_child_real (model, node)) {
1716 dummy->flags &=
1717 ~PLUMA_FILE_BROWSER_STORE_FLAG_IS_HIDDEN;
1718
1719 if (FILE_IS_HIDDEN (flags)) {
1720 // Was hidden, needs to be inserted
1721 iter.user_data = dummy;
1722 path =
1723 pluma_file_browser_store_get_path_real
1724 (model, dummy);
1725
1726 row_inserted (model, &path, &iter);
1727 gtk_tree_path_free (path);
1728 }
1729 } else {
1730 if (!FILE_IS_HIDDEN (flags)) {
1731 // Was shown, needs to be removed
1732
1733 // To get the path we need to set it to visible temporarily
1734 dummy->flags &=
1735 ~PLUMA_FILE_BROWSER_STORE_FLAG_IS_HIDDEN;
1736 path =
1737 pluma_file_browser_store_get_path_real
1738 (model, dummy);
1739 dummy->flags |=
1740 PLUMA_FILE_BROWSER_STORE_FLAG_IS_HIDDEN;
1741
1742 dummy->inserted = FALSE;
1743 row_deleted (model, path);
1744 gtk_tree_path_free (path);
1745 }
1746 }
1747 }
1748 }
1749
1750 static void
insert_node_sorted(PlumaFileBrowserStore * model,FileBrowserNode * child,FileBrowserNode * parent)1751 insert_node_sorted (PlumaFileBrowserStore * model,
1752 FileBrowserNode * child,
1753 FileBrowserNode * parent)
1754 {
1755 FileBrowserNodeDir *dir;
1756
1757 dir = FILE_BROWSER_NODE_DIR (parent);
1758
1759 if (model->priv->sort_func == NULL) {
1760 dir->children = g_slist_append (dir->children, child);
1761 } else {
1762 dir->children =
1763 g_slist_insert_sorted (dir->children, child,
1764 (GCompareFunc) (model->priv->
1765 sort_func));
1766 }
1767 }
1768
1769 static void
model_add_node(PlumaFileBrowserStore * model,FileBrowserNode * child,FileBrowserNode * parent)1770 model_add_node (PlumaFileBrowserStore * model, FileBrowserNode * child,
1771 FileBrowserNode * parent)
1772 {
1773 /* Add child to parents children */
1774 insert_node_sorted (model, child, parent);
1775
1776 if (model_node_visibility (model, parent) &&
1777 model_node_visibility (model, child)) {
1778 GtkTreeIter iter;
1779 GtkTreePath *path;
1780
1781 iter.user_data = child;
1782 path = pluma_file_browser_store_get_path_real (model, child);
1783
1784 /* Emit row inserted */
1785 row_inserted (model, &path, &iter);
1786 gtk_tree_path_free (path);
1787 }
1788
1789 model_check_dummy (model, parent);
1790 model_check_dummy (model, child);
1791 }
1792
1793 static void
model_add_nodes_batch(PlumaFileBrowserStore * model,GSList * children,FileBrowserNode * parent)1794 model_add_nodes_batch (PlumaFileBrowserStore * model,
1795 GSList * children,
1796 FileBrowserNode * parent)
1797 {
1798 GSList *sorted_children;
1799 GSList *child;
1800 GSList *prev;
1801 GSList *l;
1802 FileBrowserNodeDir *dir;
1803
1804 dir = FILE_BROWSER_NODE_DIR (parent);
1805
1806 sorted_children = g_slist_sort (children, (GCompareFunc) model->priv->sort_func);
1807
1808 child = sorted_children;
1809 l = dir->children;
1810 prev = NULL;
1811
1812 model_check_dummy (model, parent);
1813
1814 while (child) {
1815 FileBrowserNode *node = child->data;
1816 GtkTreeIter iter;
1817 GtkTreePath *path;
1818
1819 /* reached the end of the first list, just append the second */
1820 if (l == NULL) {
1821
1822 dir->children = g_slist_concat (dir->children, child);
1823
1824 for (l = child; l; l = l->next) {
1825 if (model_node_visibility (model, parent) &&
1826 model_node_visibility (model, l->data)) {
1827 iter.user_data = l->data;
1828 path = pluma_file_browser_store_get_path_real (model, l->data);
1829
1830 // Emit row inserted
1831 row_inserted (model, &path, &iter);
1832 gtk_tree_path_free (path);
1833 }
1834
1835 model_check_dummy (model, l->data);
1836 }
1837
1838 break;
1839 }
1840
1841 if (model->priv->sort_func (l->data, node) > 0) {
1842 GSList *next_child;
1843
1844 if (prev == NULL) {
1845 /* prepend to the list */
1846 dir->children = g_slist_prepend (dir->children, child);
1847 } else {
1848 prev->next = child;
1849 }
1850
1851 next_child = child->next;
1852 prev = child;
1853 child->next = l;
1854 child = next_child;
1855
1856 if (model_node_visibility (model, parent) &&
1857 model_node_visibility (model, node)) {
1858 iter.user_data = node;
1859 path = pluma_file_browser_store_get_path_real (model, node);
1860
1861 // Emit row inserted
1862 row_inserted (model, &path, &iter);
1863 gtk_tree_path_free (path);
1864 }
1865
1866 model_check_dummy (model, node);
1867
1868 /* try again at the same l position with the
1869 * next child */
1870 } else {
1871
1872 /* Move to the next item in the list */
1873 prev = l;
1874 l = l->next;
1875 }
1876 }
1877 }
1878
1879 static gchar const *
backup_content_type(GFileInfo * info)1880 backup_content_type (GFileInfo * info)
1881 {
1882 gchar const * content;
1883
1884 if (!g_file_info_get_is_backup (info))
1885 return NULL;
1886
1887 content = g_file_info_get_content_type (info);
1888
1889 if (!content || g_content_type_equals (content, "application/x-trash"))
1890 return "text/plain";
1891
1892 return content;
1893 }
1894
1895 static void
file_browser_node_set_from_info(PlumaFileBrowserStore * model,FileBrowserNode * node,GFileInfo * info,gboolean isadded)1896 file_browser_node_set_from_info (PlumaFileBrowserStore * model,
1897 FileBrowserNode * node,
1898 GFileInfo * info,
1899 gboolean isadded)
1900 {
1901 gchar const * content;
1902 gboolean free_info = FALSE;
1903 GtkTreePath * path;
1904 gchar * uri;
1905 GError * error = NULL;
1906
1907 if (info == NULL) {
1908 info = g_file_query_info (node->file,
1909 STANDARD_ATTRIBUTE_TYPES,
1910 G_FILE_QUERY_INFO_NONE,
1911 NULL,
1912 &error);
1913
1914 if (!info) {
1915 if (!(error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_FOUND)) {
1916 uri = g_file_get_uri (node->file);
1917 g_warning ("Could not get info for %s: %s", uri, error->message);
1918 g_free (uri);
1919 }
1920 g_error_free (error);
1921
1922 return;
1923 }
1924
1925 free_info = TRUE;
1926 }
1927
1928 if (g_file_info_get_is_hidden (info) || g_file_info_get_is_backup (info))
1929 node->flags |= PLUMA_FILE_BROWSER_STORE_FLAG_IS_HIDDEN;
1930
1931 if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
1932 node->flags |= PLUMA_FILE_BROWSER_STORE_FLAG_IS_DIRECTORY;
1933 else {
1934 if (!(content = backup_content_type (info)))
1935 content = g_file_info_get_content_type (info);
1936
1937 if (!content ||
1938 g_content_type_is_unknown (content) ||
1939 g_content_type_is_a (content, "text/plain"))
1940 node->flags |= PLUMA_FILE_BROWSER_STORE_FLAG_IS_TEXT;
1941 }
1942
1943 model_recomposite_icon_real (model, node, info);
1944
1945 if (free_info)
1946 g_object_unref (info);
1947
1948 if (isadded) {
1949 path = pluma_file_browser_store_get_path_real (model, node);
1950 model_refilter_node (model, node, &path);
1951 gtk_tree_path_free (path);
1952
1953 model_check_dummy (model, node->parent);
1954 } else {
1955 model_node_update_visibility (model, node);
1956 }
1957 }
1958
1959 static FileBrowserNode *
node_list_contains_file(GSList * children,GFile * file)1960 node_list_contains_file (GSList *children, GFile * file)
1961 {
1962 GSList *item;
1963
1964 for (item = children; item; item = item->next) {
1965 FileBrowserNode *node;
1966
1967 node = (FileBrowserNode *) (item->data);
1968
1969 if (node->file != NULL
1970 && g_file_equal (node->file, file))
1971 return node;
1972 }
1973
1974 return NULL;
1975 }
1976
1977 static FileBrowserNode *
model_add_node_from_file(PlumaFileBrowserStore * model,FileBrowserNode * parent,GFile * file,GFileInfo * info)1978 model_add_node_from_file (PlumaFileBrowserStore * model,
1979 FileBrowserNode * parent,
1980 GFile * file,
1981 GFileInfo * info)
1982 {
1983 FileBrowserNode *node;
1984 gboolean free_info = FALSE;
1985 GError * error = NULL;
1986
1987 if ((node = node_list_contains_file (FILE_BROWSER_NODE_DIR (parent)->children, file)) == NULL) {
1988 if (info == NULL) {
1989 info = g_file_query_info (file,
1990 STANDARD_ATTRIBUTE_TYPES,
1991 G_FILE_QUERY_INFO_NONE,
1992 NULL,
1993 &error);
1994 free_info = TRUE;
1995 }
1996
1997 if (!info) {
1998 g_warning ("Error querying file info: %s", error->message);
1999 g_error_free (error);
2000
2001 /* FIXME: What to do now then... */
2002 node = file_browser_node_new (file, parent);
2003 } else if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
2004 node = file_browser_node_dir_new (model, file, parent);
2005 } else {
2006 node = file_browser_node_new (file, parent);
2007 }
2008
2009 file_browser_node_set_from_info (model, node, info, FALSE);
2010 model_add_node (model, node, parent);
2011
2012 if (info && free_info)
2013 g_object_unref (info);
2014 }
2015
2016 return node;
2017 }
2018
2019 /* We pass in a copy of the list of parent->children so that we do
2020 * not have to check if a file already exists among the ones we just
2021 * added */
2022 static void
model_add_nodes_from_files(PlumaFileBrowserStore * model,FileBrowserNode * parent,GSList * original_children,GList * files)2023 model_add_nodes_from_files (PlumaFileBrowserStore * model,
2024 FileBrowserNode * parent,
2025 GSList * original_children,
2026 GList * files)
2027 {
2028 GList *item;
2029 GSList *nodes = NULL;
2030
2031 for (item = files; item; item = item->next) {
2032 GFileInfo *info = G_FILE_INFO (item->data);
2033 GFileType type;
2034 gchar const * name;
2035 GFile * file;
2036 FileBrowserNode *node;
2037
2038 type = g_file_info_get_file_type (info);
2039
2040 /* Skip all non regular, non directory files */
2041 if (type != G_FILE_TYPE_REGULAR &&
2042 type != G_FILE_TYPE_DIRECTORY &&
2043 type != G_FILE_TYPE_SYMBOLIC_LINK) {
2044 g_object_unref (info);
2045 continue;
2046 }
2047
2048 name = g_file_info_get_name (info);
2049
2050 /* Skip '.' and '..' directories */
2051 if (type == G_FILE_TYPE_DIRECTORY &&
2052 (strcmp (name, ".") == 0 ||
2053 strcmp (name, "..") == 0)) {
2054 continue;
2055 }
2056
2057 file = g_file_get_child (parent->file, name);
2058
2059 if ((node = node_list_contains_file (original_children, file)) == NULL) {
2060
2061 if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
2062 node = file_browser_node_dir_new (model, file, parent);
2063 } else {
2064 node = file_browser_node_new (file, parent);
2065 }
2066
2067 file_browser_node_set_from_info (model, node, info, FALSE);
2068
2069 nodes = g_slist_prepend (nodes, node);
2070 }
2071
2072 g_object_unref (file);
2073 g_object_unref (info);
2074 }
2075
2076 if (nodes)
2077 model_add_nodes_batch (model, nodes, parent);
2078 }
2079
2080 static FileBrowserNode *
model_add_node_from_dir(PlumaFileBrowserStore * model,FileBrowserNode * parent,GFile * file)2081 model_add_node_from_dir (PlumaFileBrowserStore * model,
2082 FileBrowserNode * parent,
2083 GFile * file)
2084 {
2085 FileBrowserNode *node;
2086
2087 /* Check if it already exists */
2088 if ((node = node_list_contains_file (FILE_BROWSER_NODE_DIR (parent)->children, file)) == NULL) {
2089 node = file_browser_node_dir_new (model, file, parent);
2090 file_browser_node_set_from_info (model, node, NULL, FALSE);
2091
2092 if (node->name == NULL) {
2093 file_browser_node_set_name (node);
2094 }
2095
2096 if (node->icon == NULL) {
2097 node->icon = pluma_file_browser_utils_pixbuf_from_theme ("folder", GTK_ICON_SIZE_MENU);
2098 }
2099
2100 model_add_node (model, node, parent);
2101 }
2102
2103 return node;
2104 }
2105
2106 static void
on_directory_monitor_event(GFileMonitor * monitor,GFile * file,GFile * other_file,GFileMonitorEvent event_type,FileBrowserNode * parent)2107 on_directory_monitor_event (GFileMonitor * monitor,
2108 GFile * file,
2109 GFile * other_file,
2110 GFileMonitorEvent event_type,
2111 FileBrowserNode * parent)
2112 {
2113 FileBrowserNode *node;
2114 FileBrowserNodeDir *dir = FILE_BROWSER_NODE_DIR (parent);
2115
2116 switch (event_type) {
2117 case G_FILE_MONITOR_EVENT_DELETED:
2118 node = node_list_contains_file (dir->children, file);
2119
2120 if (node != NULL) {
2121 model_remove_node (dir->model, node, NULL, TRUE);
2122 }
2123 break;
2124 case G_FILE_MONITOR_EVENT_CREATED:
2125 if (g_file_query_exists (file, NULL)) {
2126 model_add_node_from_file (dir->model, parent, file, NULL);
2127 }
2128
2129 break;
2130 default:
2131 break;
2132 }
2133 }
2134
2135 static void
async_node_free(AsyncNode * async)2136 async_node_free (AsyncNode *async)
2137 {
2138 g_object_unref (async->cancellable);
2139 g_slist_free (async->original_children);
2140 g_free (async);
2141 }
2142
2143 static void
model_iterate_next_files_cb(GFileEnumerator * enumerator,GAsyncResult * result,AsyncNode * async)2144 model_iterate_next_files_cb (GFileEnumerator * enumerator,
2145 GAsyncResult * result,
2146 AsyncNode * async)
2147 {
2148 GList * files;
2149 GError * error = NULL;
2150 FileBrowserNodeDir * dir = async->dir;
2151 FileBrowserNode * parent = (FileBrowserNode *)dir;
2152
2153 files = g_file_enumerator_next_files_finish (enumerator, result, &error);
2154
2155 if (files == NULL) {
2156 g_file_enumerator_close (enumerator, NULL, NULL);
2157 async_node_free (async);
2158
2159 if (!error)
2160 {
2161 /* We're done loading */
2162 g_object_unref (dir->cancellable);
2163 dir->cancellable = NULL;
2164
2165 /*
2166 * FIXME: This is temporarly, it is a bug in gio:
2167 * http://bugzilla.gnome.org/show_bug.cgi?id=565924
2168 */
2169 if (g_file_is_native (parent->file) && dir->monitor == NULL) {
2170 dir->monitor = g_file_monitor_directory (parent->file,
2171 G_FILE_MONITOR_NONE,
2172 NULL,
2173 NULL);
2174 if (dir->monitor != NULL)
2175 {
2176 g_signal_connect (dir->monitor,
2177 "changed",
2178 G_CALLBACK (on_directory_monitor_event),
2179 parent);
2180 }
2181 }
2182
2183 model_check_dummy (dir->model, parent);
2184 model_end_loading (dir->model, parent);
2185 } else {
2186 /* Simply return if we were cancelled */
2187 if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_CANCELLED)
2188 return;
2189
2190 /* Otherwise handle the error appropriately */
2191 g_signal_emit (dir->model,
2192 model_signals[ERROR],
2193 0,
2194 PLUMA_FILE_BROWSER_ERROR_LOAD_DIRECTORY,
2195 error->message);
2196
2197 file_browser_node_unload (dir->model, (FileBrowserNode *)parent, TRUE);
2198 g_error_free (error);
2199 }
2200 } else if (g_cancellable_is_cancelled (async->cancellable)) {
2201 /* Check cancel state manually */
2202 g_file_enumerator_close (enumerator, NULL, NULL);
2203 async_node_free (async);
2204 } else {
2205 model_add_nodes_from_files (dir->model, parent, async->original_children, files);
2206
2207 g_list_free (files);
2208 next_files_async (enumerator, async);
2209 }
2210 }
2211
2212 static void
next_files_async(GFileEnumerator * enumerator,AsyncNode * async)2213 next_files_async (GFileEnumerator * enumerator,
2214 AsyncNode * async)
2215 {
2216 g_file_enumerator_next_files_async (enumerator,
2217 DIRECTORY_LOAD_ITEMS_PER_CALLBACK,
2218 G_PRIORITY_DEFAULT,
2219 async->cancellable,
2220 (GAsyncReadyCallback)model_iterate_next_files_cb,
2221 async);
2222 }
2223
2224 static void
model_iterate_children_cb(GFile * file,GAsyncResult * result,AsyncNode * async)2225 model_iterate_children_cb (GFile * file,
2226 GAsyncResult * result,
2227 AsyncNode * async)
2228 {
2229 GError * error = NULL;
2230 GFileEnumerator * enumerator;
2231
2232 if (g_cancellable_is_cancelled (async->cancellable))
2233 {
2234 async_node_free (async);
2235 return;
2236 }
2237
2238 enumerator = g_file_enumerate_children_finish (file, result, &error);
2239
2240 if (enumerator == NULL) {
2241 /* Simply return if we were cancelled or if the dir is not there */
2242 FileBrowserNodeDir *dir = async->dir;
2243
2244 /* Otherwise handle the error appropriately */
2245 g_signal_emit (dir->model,
2246 model_signals[ERROR],
2247 0,
2248 PLUMA_FILE_BROWSER_ERROR_LOAD_DIRECTORY,
2249 error->message);
2250
2251 file_browser_node_unload (dir->model, (FileBrowserNode *)dir, TRUE);
2252 g_error_free (error);
2253 async_node_free (async);
2254 } else {
2255 next_files_async (enumerator, async);
2256 }
2257 }
2258
2259 static void
model_load_directory(PlumaFileBrowserStore * model,FileBrowserNode * node)2260 model_load_directory (PlumaFileBrowserStore * model,
2261 FileBrowserNode * node)
2262 {
2263 FileBrowserNodeDir *dir;
2264 AsyncNode *async;
2265
2266 g_return_if_fail (NODE_IS_DIR (node));
2267
2268 dir = FILE_BROWSER_NODE_DIR (node);
2269
2270 /* Cancel a previous load */
2271 if (dir->cancellable != NULL) {
2272 file_browser_node_unload (dir->model, node, TRUE);
2273 }
2274
2275 node->flags |= PLUMA_FILE_BROWSER_STORE_FLAG_LOADED;
2276 model_begin_loading (model, node);
2277
2278 dir->cancellable = g_cancellable_new ();
2279
2280 async = g_new (AsyncNode, 1);
2281 async->dir = dir;
2282 async->cancellable = g_object_ref (dir->cancellable);
2283 async->original_children = g_slist_copy (dir->children);
2284
2285 /* Start loading async */
2286 g_file_enumerate_children_async (node->file,
2287 STANDARD_ATTRIBUTE_TYPES,
2288 G_FILE_QUERY_INFO_NONE,
2289 G_PRIORITY_DEFAULT,
2290 async->cancellable,
2291 (GAsyncReadyCallback)model_iterate_children_cb,
2292 async);
2293 }
2294
2295 static GList *
get_parent_files(PlumaFileBrowserStore * model,GFile * file)2296 get_parent_files (PlumaFileBrowserStore * model, GFile * file)
2297 {
2298 GList * result = NULL;
2299
2300 result = g_list_prepend (result, g_object_ref (file));
2301
2302 while ((file = g_file_get_parent (file))) {
2303 if (g_file_equal (file, model->priv->root->file)) {
2304 g_object_unref (file);
2305 break;
2306 }
2307
2308 result = g_list_prepend (result, file);
2309 }
2310
2311 return result;
2312 }
2313
2314 static void
model_fill(PlumaFileBrowserStore * model,FileBrowserNode * node,GtkTreePath ** path)2315 model_fill (PlumaFileBrowserStore * model, FileBrowserNode * node,
2316 GtkTreePath ** path)
2317 {
2318 gboolean free_path = FALSE;
2319 GtkTreeIter iter = {0,};
2320 GSList *item;
2321 FileBrowserNode *child;
2322
2323 if (node == NULL) {
2324 node = model->priv->virtual_root;
2325 *path = gtk_tree_path_new ();
2326 free_path = TRUE;
2327 }
2328
2329 if (*path == NULL) {
2330 *path =
2331 pluma_file_browser_store_get_path_real (model, node);
2332 free_path = TRUE;
2333 }
2334
2335 if (!model_node_visibility (model, node)) {
2336 if (free_path)
2337 gtk_tree_path_free (*path);
2338
2339 return;
2340 }
2341
2342 if (node != model->priv->virtual_root) {
2343 /* Insert node */
2344 iter.user_data = node;
2345
2346 row_inserted(model, path, &iter);
2347 }
2348
2349 if (NODE_IS_DIR (node)) {
2350 /* Go to the first child */
2351 gtk_tree_path_down (*path);
2352
2353 for (item = FILE_BROWSER_NODE_DIR (node)->children; item;
2354 item = item->next) {
2355 child = (FileBrowserNode *) (item->data);
2356
2357 if (model_node_visibility (model, child)) {
2358 model_fill (model, child, path);
2359
2360 /* Increase path for next child */
2361 gtk_tree_path_next (*path);
2362 }
2363 }
2364
2365 /* Move back up to node path */
2366 gtk_tree_path_up (*path);
2367 }
2368
2369 model_check_dummy (model, node);
2370
2371 if (free_path)
2372 gtk_tree_path_free (*path);
2373 }
2374
2375 static void
set_virtual_root_from_node(PlumaFileBrowserStore * model,FileBrowserNode * node)2376 set_virtual_root_from_node (PlumaFileBrowserStore * model,
2377 FileBrowserNode * node)
2378 {
2379 FileBrowserNode *next;
2380 FileBrowserNode *prev;
2381 FileBrowserNode *check;
2382 FileBrowserNodeDir *dir;
2383 GSList *item;
2384 GSList *copy;
2385 GtkTreePath *empty = NULL;
2386
2387 g_assert (node != NULL);
2388
2389 prev = node;
2390 next = prev->parent;
2391
2392 /* Free all the nodes below that we don't need in cache */
2393 while (prev != model->priv->root) {
2394 dir = FILE_BROWSER_NODE_DIR (next);
2395 copy = g_slist_copy (dir->children);
2396
2397 for (item = copy; item; item = item->next) {
2398 check = (FileBrowserNode *) (item->data);
2399
2400 if (prev == node) {
2401 /* Only free the children, keeping this depth in cache */
2402 if (check != node) {
2403 file_browser_node_free_children
2404 (model, check);
2405 file_browser_node_unload (model,
2406 check,
2407 FALSE);
2408 }
2409 } else if (check != prev) {
2410 /* Only free when the node is not in the chain */
2411 dir->children =
2412 g_slist_remove (dir->children, check);
2413 file_browser_node_free (model, check);
2414 }
2415 }
2416
2417 if (prev != node)
2418 file_browser_node_unload (model, next, FALSE);
2419
2420 g_slist_free (copy);
2421 prev = next;
2422 next = prev->parent;
2423 }
2424
2425 /* Free all the nodes up that we don't need in cache */
2426 for (item = FILE_BROWSER_NODE_DIR (node)->children; item;
2427 item = item->next) {
2428 check = (FileBrowserNode *) (item->data);
2429
2430 if (NODE_IS_DIR (check)) {
2431 for (copy =
2432 FILE_BROWSER_NODE_DIR (check)->children; copy;
2433 copy = copy->next) {
2434 file_browser_node_free_children (model,
2435 (FileBrowserNode
2436 *)
2437 (copy->
2438 data));
2439 file_browser_node_unload (model,
2440 (FileBrowserNode
2441 *) (copy->data),
2442 FALSE);
2443 }
2444 } else if (NODE_IS_DUMMY (check)) {
2445 check->flags |=
2446 PLUMA_FILE_BROWSER_STORE_FLAG_IS_HIDDEN;
2447 }
2448 }
2449
2450 /* Now finally, set the virtual root, and load it up! */
2451 model->priv->virtual_root = node;
2452
2453 /* Notify that the virtual-root has changed before loading up new nodes so that the
2454 "root_changed" signal can be emitted before any "inserted" signals */
2455 g_object_notify (G_OBJECT (model), "virtual-root");
2456
2457 model_fill (model, NULL, &empty);
2458
2459 if (!NODE_LOADED (node))
2460 model_load_directory (model, node);
2461 }
2462
2463 static void
set_virtual_root_from_file(PlumaFileBrowserStore * model,GFile * file)2464 set_virtual_root_from_file (PlumaFileBrowserStore * model,
2465 GFile * file)
2466 {
2467 GList * files;
2468 GList * item;
2469 FileBrowserNode * parent;
2470 GFile * check;
2471
2472 /* Always clear the model before altering the nodes */
2473 model_clear (model, FALSE);
2474
2475 /* Create the node path, get all the uri's */
2476 files = get_parent_files (model, file);
2477 parent = model->priv->root;
2478
2479 for (item = files; item; item = item->next) {
2480 check = G_FILE (item->data);
2481
2482 parent = model_add_node_from_dir (model, parent, check);
2483 g_object_unref (check);
2484 }
2485
2486 g_list_free (files);
2487 set_virtual_root_from_node (model, parent);
2488 }
2489
2490 static FileBrowserNode *
model_find_node_children(PlumaFileBrowserStore * model,FileBrowserNode * parent,GFile * file)2491 model_find_node_children (PlumaFileBrowserStore * model,
2492 FileBrowserNode * parent,
2493 GFile * file)
2494 {
2495 FileBrowserNodeDir *dir;
2496 FileBrowserNode *child;
2497 FileBrowserNode *result;
2498 GSList *children;
2499
2500 if (!NODE_IS_DIR (parent))
2501 return NULL;
2502
2503 dir = FILE_BROWSER_NODE_DIR (parent);
2504
2505 for (children = dir->children; children; children = children->next) {
2506 child = (FileBrowserNode *)(children->data);
2507
2508 result = model_find_node (model, child, file);
2509
2510 if (result)
2511 return result;
2512 }
2513
2514 return NULL;
2515 }
2516
2517 static FileBrowserNode *
model_find_node(PlumaFileBrowserStore * model,FileBrowserNode * node,GFile * file)2518 model_find_node (PlumaFileBrowserStore * model,
2519 FileBrowserNode * node,
2520 GFile * file)
2521 {
2522 if (node == NULL)
2523 node = model->priv->root;
2524
2525 if (node->file && g_file_equal (node->file, file))
2526 return node;
2527
2528 if (NODE_IS_DIR (node) && g_file_has_prefix (file, node->file))
2529 return model_find_node_children (model, node, file);
2530
2531 return NULL;
2532 }
2533
2534 static GQuark
pluma_file_browser_store_error_quark(void)2535 pluma_file_browser_store_error_quark (void)
2536 {
2537 static GQuark quark = 0;
2538
2539 if (G_UNLIKELY (quark == 0)) {
2540 quark = g_quark_from_string ("pluma_file_browser_store_error");
2541 }
2542
2543 return quark;
2544 }
2545
2546 static GFile *
unique_new_name(GFile * directory,gchar const * name)2547 unique_new_name (GFile * directory, gchar const * name)
2548 {
2549 GFile * newuri = NULL;
2550 guint num = 0;
2551 gchar * newname;
2552
2553 while (newuri == NULL || g_file_query_exists (newuri, NULL)) {
2554 if (newuri != NULL)
2555 g_object_unref (newuri);
2556
2557 if (num == 0)
2558 newname = g_strdup (name);
2559 else
2560 newname = g_strdup_printf ("%s(%d)", name, num);
2561
2562 newuri = g_file_get_child (directory, newname);
2563 g_free (newname);
2564
2565 ++num;
2566 }
2567
2568 return newuri;
2569 }
2570
2571 static PlumaFileBrowserStoreResult
model_root_mounted(PlumaFileBrowserStore * model,gchar const * virtual_root)2572 model_root_mounted (PlumaFileBrowserStore * model, gchar const * virtual_root)
2573 {
2574 model_check_dummy (model, model->priv->root);
2575 g_object_notify (G_OBJECT (model), "root");
2576
2577 if (virtual_root != NULL)
2578 return
2579 pluma_file_browser_store_set_virtual_root_from_string
2580 (model, virtual_root);
2581 else
2582 set_virtual_root_from_node (model,
2583 model->priv->root);
2584
2585 return PLUMA_FILE_BROWSER_STORE_RESULT_OK;
2586 }
2587
2588 static void
handle_root_error(PlumaFileBrowserStore * model,GError * error)2589 handle_root_error (PlumaFileBrowserStore * model, GError *error)
2590 {
2591 FileBrowserNode * root;
2592
2593 g_signal_emit (model,
2594 model_signals[ERROR],
2595 0,
2596 PLUMA_FILE_BROWSER_ERROR_SET_ROOT,
2597 error->message);
2598
2599 /* Set the virtual root to the root */
2600 root = model->priv->root;
2601 model->priv->virtual_root = root;
2602
2603 /* Set the root to be loaded */
2604 root->flags |= PLUMA_FILE_BROWSER_STORE_FLAG_LOADED;
2605
2606 /* Check the dummy */
2607 model_check_dummy (model, root);
2608
2609 g_object_notify (G_OBJECT (model), "root");
2610 g_object_notify (G_OBJECT (model), "virtual-root");
2611 }
2612
2613 static void
mount_cb(GFile * file,GAsyncResult * res,MountInfo * mount_info)2614 mount_cb (GFile * file,
2615 GAsyncResult * res,
2616 MountInfo * mount_info)
2617 {
2618 gboolean mounted;
2619 GError * error = NULL;
2620 PlumaFileBrowserStore * model = mount_info->model;
2621
2622 mounted = g_file_mount_enclosing_volume_finish (file, res, &error);
2623
2624 if (mount_info->model)
2625 {
2626 model->priv->mount_info = NULL;
2627 model_end_loading (model, model->priv->root);
2628 }
2629
2630 if (!mount_info->model || g_cancellable_is_cancelled (mount_info->cancellable))
2631 {
2632 // Reset because it might be reused?
2633 g_cancellable_reset (mount_info->cancellable);
2634 }
2635 else if (mounted)
2636 {
2637 model_root_mounted (model, mount_info->virtual_root);
2638 }
2639 else if (error->code != G_IO_ERROR_CANCELLED)
2640 {
2641 handle_root_error (model, error);
2642 }
2643
2644 if (error)
2645 g_error_free (error);
2646
2647 g_object_unref (mount_info->operation);
2648 g_object_unref (mount_info->cancellable);
2649 g_free (mount_info->virtual_root);
2650
2651 g_free (mount_info);
2652 }
2653
2654 static PlumaFileBrowserStoreResult
model_mount_root(PlumaFileBrowserStore * model,gchar const * virtual_root)2655 model_mount_root (PlumaFileBrowserStore * model, gchar const * virtual_root)
2656 {
2657 GFileInfo * info;
2658 GError * error = NULL;
2659 MountInfo * mount_info;
2660
2661 info = g_file_query_info (model->priv->root->file,
2662 G_FILE_ATTRIBUTE_STANDARD_TYPE,
2663 G_FILE_QUERY_INFO_NONE,
2664 NULL,
2665 &error);
2666
2667 if (!info) {
2668 if (error->code == G_IO_ERROR_NOT_MOUNTED) {
2669 /* Try to mount it */
2670 FILE_BROWSER_NODE_DIR (model->priv->root)->cancellable = g_cancellable_new ();
2671
2672 mount_info = g_new(MountInfo, 1);
2673 mount_info->model = model;
2674 mount_info->virtual_root = g_strdup (virtual_root);
2675
2676 /* FIXME: we should be setting the correct window */
2677 mount_info->operation = gtk_mount_operation_new (NULL);
2678 mount_info->cancellable = g_object_ref (FILE_BROWSER_NODE_DIR (model->priv->root)->cancellable);
2679
2680 model_begin_loading (model, model->priv->root);
2681 g_file_mount_enclosing_volume (model->priv->root->file,
2682 G_MOUNT_MOUNT_NONE,
2683 mount_info->operation,
2684 mount_info->cancellable,
2685 (GAsyncReadyCallback)mount_cb,
2686 mount_info);
2687
2688 model->priv->mount_info = mount_info;
2689 return PLUMA_FILE_BROWSER_STORE_RESULT_MOUNTING;
2690 }
2691 else
2692 {
2693 handle_root_error (model, error);
2694 }
2695
2696 g_error_free (error);
2697 } else {
2698 g_object_unref (info);
2699
2700 return model_root_mounted (model, virtual_root);
2701 }
2702
2703 return PLUMA_FILE_BROWSER_STORE_RESULT_OK;
2704 }
2705
2706 /* Public */
2707 PlumaFileBrowserStore *
pluma_file_browser_store_new(gchar const * root)2708 pluma_file_browser_store_new (gchar const *root)
2709 {
2710 PlumaFileBrowserStore *obj =
2711 PLUMA_FILE_BROWSER_STORE (g_object_new
2712 (PLUMA_TYPE_FILE_BROWSER_STORE,
2713 NULL));
2714
2715 pluma_file_browser_store_set_root (obj, root);
2716 return obj;
2717 }
2718
2719 void
pluma_file_browser_store_set_value(PlumaFileBrowserStore * tree_model,GtkTreeIter * iter,gint column,GValue * value)2720 pluma_file_browser_store_set_value (PlumaFileBrowserStore * tree_model,
2721 GtkTreeIter * iter, gint column,
2722 GValue * value)
2723 {
2724 gpointer data;
2725 FileBrowserNode *node;
2726 GtkTreePath *path;
2727
2728 g_return_if_fail (PLUMA_IS_FILE_BROWSER_STORE (tree_model));
2729 g_return_if_fail (column ==
2730 PLUMA_FILE_BROWSER_STORE_COLUMN_EMBLEM);
2731 g_return_if_fail (G_VALUE_HOLDS_OBJECT (value));
2732 g_return_if_fail (iter != NULL);
2733 g_return_if_fail (iter->user_data != NULL);
2734
2735 data = g_value_get_object (value);
2736
2737 if (data)
2738 g_return_if_fail (GDK_IS_PIXBUF (data));
2739
2740 node = (FileBrowserNode *) (iter->user_data);
2741
2742 if (node->emblem)
2743 g_object_unref (node->emblem);
2744
2745 if (data)
2746 node->emblem = g_object_ref (GDK_PIXBUF (data));
2747 else
2748 node->emblem = NULL;
2749
2750 model_recomposite_icon (tree_model, iter);
2751
2752 if (model_node_visibility (tree_model, node)) {
2753 path = pluma_file_browser_store_get_path (GTK_TREE_MODEL (tree_model),
2754 iter);
2755 row_changed (tree_model, &path, iter);
2756 gtk_tree_path_free (path);
2757 }
2758 }
2759
2760 PlumaFileBrowserStoreResult
pluma_file_browser_store_set_virtual_root(PlumaFileBrowserStore * model,GtkTreeIter * iter)2761 pluma_file_browser_store_set_virtual_root (PlumaFileBrowserStore * model,
2762 GtkTreeIter * iter)
2763 {
2764 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (model),
2765 PLUMA_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
2766 g_return_val_if_fail (iter != NULL,
2767 PLUMA_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
2768 g_return_val_if_fail (iter->user_data != NULL,
2769 PLUMA_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
2770
2771 model_clear (model, FALSE);
2772 set_virtual_root_from_node (model,
2773 (FileBrowserNode *) (iter->user_data));
2774
2775 return TRUE;
2776 }
2777
2778 PlumaFileBrowserStoreResult
pluma_file_browser_store_set_virtual_root_from_string(PlumaFileBrowserStore * model,gchar const * root)2779 pluma_file_browser_store_set_virtual_root_from_string
2780 (PlumaFileBrowserStore * model, gchar const *root) {
2781 GFile *file;
2782
2783 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (model),
2784 PLUMA_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
2785
2786 file = g_file_new_for_uri (root);
2787 if (file == NULL) {
2788 g_warning ("Invalid uri (%s)", root);
2789 return PLUMA_FILE_BROWSER_STORE_RESULT_NO_CHANGE;
2790 }
2791
2792 /* Check if uri is already the virtual root */
2793 if (model->priv->virtual_root &&
2794 g_file_equal (model->priv->virtual_root->file, file)) {
2795 g_object_unref (file);
2796 return PLUMA_FILE_BROWSER_STORE_RESULT_NO_CHANGE;
2797 }
2798
2799 /* Check if uri is the root itself */
2800 if (g_file_equal (model->priv->root->file, file)) {
2801 g_object_unref (file);
2802
2803 /* Always clear the model before altering the nodes */
2804 model_clear (model, FALSE);
2805 set_virtual_root_from_node (model, model->priv->root);
2806 return PLUMA_FILE_BROWSER_STORE_RESULT_OK;
2807 }
2808
2809 if (!g_file_has_prefix (file, model->priv->root->file)) {
2810 gchar *str, *str1;
2811
2812 str = g_file_get_parse_name (model->priv->root->file);
2813 str1 = g_file_get_parse_name (file);
2814
2815 g_warning
2816 ("Virtual root (%s) is not below actual root (%s)",
2817 str1, str);
2818
2819 g_free (str);
2820 g_free (str1);
2821
2822 g_object_unref (file);
2823 return PLUMA_FILE_BROWSER_STORE_RESULT_ERROR;
2824 }
2825
2826 set_virtual_root_from_file (model, file);
2827 g_object_unref (file);
2828
2829 return PLUMA_FILE_BROWSER_STORE_RESULT_OK;
2830 }
2831
2832 PlumaFileBrowserStoreResult
pluma_file_browser_store_set_virtual_root_top(PlumaFileBrowserStore * model)2833 pluma_file_browser_store_set_virtual_root_top (PlumaFileBrowserStore *
2834 model)
2835 {
2836 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (model),
2837 PLUMA_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
2838
2839 if (model->priv->virtual_root == model->priv->root)
2840 return PLUMA_FILE_BROWSER_STORE_RESULT_NO_CHANGE;
2841
2842 model_clear (model, FALSE);
2843 set_virtual_root_from_node (model, model->priv->root);
2844
2845 return PLUMA_FILE_BROWSER_STORE_RESULT_OK;
2846 }
2847
2848 PlumaFileBrowserStoreResult
pluma_file_browser_store_set_virtual_root_up(PlumaFileBrowserStore * model)2849 pluma_file_browser_store_set_virtual_root_up (PlumaFileBrowserStore *
2850 model)
2851 {
2852 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (model),
2853 PLUMA_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
2854
2855 if (model->priv->virtual_root == model->priv->root)
2856 return PLUMA_FILE_BROWSER_STORE_RESULT_NO_CHANGE;
2857
2858 model_clear (model, FALSE);
2859 set_virtual_root_from_node (model,
2860 model->priv->virtual_root->parent);
2861
2862 return PLUMA_FILE_BROWSER_STORE_RESULT_OK;
2863 }
2864
2865 gboolean
pluma_file_browser_store_get_iter_virtual_root(PlumaFileBrowserStore * model,GtkTreeIter * iter)2866 pluma_file_browser_store_get_iter_virtual_root (PlumaFileBrowserStore *
2867 model, GtkTreeIter * iter)
2868 {
2869 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (model), FALSE);
2870 g_return_val_if_fail (iter != NULL, FALSE);
2871
2872 if (model->priv->virtual_root == NULL)
2873 return FALSE;
2874
2875 iter->user_data = model->priv->virtual_root;
2876 return TRUE;
2877 }
2878
2879 gboolean
pluma_file_browser_store_get_iter_root(PlumaFileBrowserStore * model,GtkTreeIter * iter)2880 pluma_file_browser_store_get_iter_root (PlumaFileBrowserStore * model,
2881 GtkTreeIter * iter)
2882 {
2883 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (model), FALSE);
2884 g_return_val_if_fail (iter != NULL, FALSE);
2885
2886 if (model->priv->root == NULL)
2887 return FALSE;
2888
2889 iter->user_data = model->priv->root;
2890 return TRUE;
2891 }
2892
2893 gboolean
pluma_file_browser_store_iter_equal(PlumaFileBrowserStore * model,GtkTreeIter * iter1,GtkTreeIter * iter2)2894 pluma_file_browser_store_iter_equal (PlumaFileBrowserStore * model,
2895 GtkTreeIter * iter1,
2896 GtkTreeIter * iter2)
2897 {
2898 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (model), FALSE);
2899 g_return_val_if_fail (iter1 != NULL, FALSE);
2900 g_return_val_if_fail (iter2 != NULL, FALSE);
2901 g_return_val_if_fail (iter1->user_data != NULL, FALSE);
2902 g_return_val_if_fail (iter2->user_data != NULL, FALSE);
2903
2904 return (iter1->user_data == iter2->user_data);
2905 }
2906
2907 void
pluma_file_browser_store_cancel_mount_operation(PlumaFileBrowserStore * store)2908 pluma_file_browser_store_cancel_mount_operation (PlumaFileBrowserStore *store)
2909 {
2910 g_return_if_fail (PLUMA_IS_FILE_BROWSER_STORE (store));
2911
2912 cancel_mount_operation (store);
2913 }
2914
2915 PlumaFileBrowserStoreResult
pluma_file_browser_store_set_root_and_virtual_root(PlumaFileBrowserStore * model,gchar const * root,gchar const * virtual_root)2916 pluma_file_browser_store_set_root_and_virtual_root (PlumaFileBrowserStore *
2917 model,
2918 gchar const *root,
2919 gchar const *virtual_root)
2920 {
2921 GFile * file = NULL;
2922 GFile * vfile = NULL;
2923 FileBrowserNode * node;
2924 gboolean equal = FALSE;
2925
2926 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (model),
2927 PLUMA_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
2928
2929 if (root == NULL && model->priv->root == NULL)
2930 return PLUMA_FILE_BROWSER_STORE_RESULT_NO_CHANGE;
2931
2932 if (root != NULL) {
2933 file = g_file_new_for_uri (root);
2934 }
2935
2936 if (root != NULL && model->priv->root != NULL) {
2937 equal = g_file_equal (file, model->priv->root->file);
2938
2939 if (equal && virtual_root == NULL) {
2940 g_object_unref (file);
2941 return PLUMA_FILE_BROWSER_STORE_RESULT_NO_CHANGE;
2942 }
2943 }
2944
2945 if (virtual_root) {
2946 vfile = g_file_new_for_uri (virtual_root);
2947
2948 if (equal && g_file_equal (vfile, model->priv->virtual_root->file)) {
2949 if (file)
2950 g_object_unref (file);
2951
2952 g_object_unref (vfile);
2953 return PLUMA_FILE_BROWSER_STORE_RESULT_NO_CHANGE;
2954 }
2955
2956 g_object_unref (vfile);
2957 }
2958
2959 /* make sure to cancel any previous mount operations */
2960 cancel_mount_operation (model);
2961
2962 /* Always clear the model before altering the nodes */
2963 model_clear (model, TRUE);
2964 file_browser_node_free (model, model->priv->root);
2965
2966 model->priv->root = NULL;
2967 model->priv->virtual_root = NULL;
2968
2969 if (file != NULL) {
2970 /* Create the root node */
2971 node = file_browser_node_dir_new (model, file, NULL);
2972
2973 g_object_unref (file);
2974
2975 model->priv->root = node;
2976 return model_mount_root (model, virtual_root);
2977 } else {
2978 g_object_notify (G_OBJECT (model), "root");
2979 g_object_notify (G_OBJECT (model), "virtual-root");
2980 }
2981
2982 return PLUMA_FILE_BROWSER_STORE_RESULT_OK;
2983 }
2984
2985 PlumaFileBrowserStoreResult
pluma_file_browser_store_set_root(PlumaFileBrowserStore * model,gchar const * root)2986 pluma_file_browser_store_set_root (PlumaFileBrowserStore * model,
2987 gchar const *root)
2988 {
2989 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (model),
2990 PLUMA_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
2991 return pluma_file_browser_store_set_root_and_virtual_root (model,
2992 root,
2993 NULL);
2994 }
2995
2996 gchar *
pluma_file_browser_store_get_root(PlumaFileBrowserStore * model)2997 pluma_file_browser_store_get_root (PlumaFileBrowserStore * model)
2998 {
2999 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (model), NULL);
3000
3001 if (model->priv->root == NULL || model->priv->root->file == NULL)
3002 return NULL;
3003 else
3004 return g_file_get_uri (model->priv->root->file);
3005 }
3006
3007 gchar *
pluma_file_browser_store_get_virtual_root(PlumaFileBrowserStore * model)3008 pluma_file_browser_store_get_virtual_root (PlumaFileBrowserStore * model)
3009 {
3010 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (model), NULL);
3011
3012 if (model->priv->virtual_root == NULL || model->priv->virtual_root->file == NULL)
3013 return NULL;
3014 else
3015 return g_file_get_uri (model->priv->virtual_root->file);
3016 }
3017
3018 void
_pluma_file_browser_store_iter_expanded(PlumaFileBrowserStore * model,GtkTreeIter * iter)3019 _pluma_file_browser_store_iter_expanded (PlumaFileBrowserStore * model,
3020 GtkTreeIter * iter)
3021 {
3022 FileBrowserNode *node;
3023
3024 g_return_if_fail (PLUMA_IS_FILE_BROWSER_STORE (model));
3025 g_return_if_fail (iter != NULL);
3026 g_return_if_fail (iter->user_data != NULL);
3027
3028 node = (FileBrowserNode *) (iter->user_data);
3029
3030 if (NODE_IS_DIR (node) && !NODE_LOADED (node)) {
3031 /* Load it now */
3032 model_load_directory (model, node);
3033 }
3034 }
3035
3036 void
_pluma_file_browser_store_iter_collapsed(PlumaFileBrowserStore * model,GtkTreeIter * iter)3037 _pluma_file_browser_store_iter_collapsed (PlumaFileBrowserStore * model,
3038 GtkTreeIter * iter)
3039 {
3040 FileBrowserNode *node;
3041 GSList *item;
3042
3043 g_return_if_fail (PLUMA_IS_FILE_BROWSER_STORE (model));
3044 g_return_if_fail (iter != NULL);
3045 g_return_if_fail (iter->user_data != NULL);
3046
3047 node = (FileBrowserNode *) (iter->user_data);
3048
3049 if (NODE_IS_DIR (node) && NODE_LOADED (node)) {
3050 /* Unload children of the children, keeping 1 depth in cache */
3051
3052 for (item = FILE_BROWSER_NODE_DIR (node)->children; item;
3053 item = item->next) {
3054 node = (FileBrowserNode *) (item->data);
3055
3056 if (NODE_IS_DIR (node) && NODE_LOADED (node)) {
3057 file_browser_node_unload (model, node,
3058 TRUE);
3059 model_check_dummy (model, node);
3060 }
3061 }
3062 }
3063 }
3064
3065 PlumaFileBrowserStoreFilterMode
pluma_file_browser_store_get_filter_mode(PlumaFileBrowserStore * model)3066 pluma_file_browser_store_get_filter_mode (PlumaFileBrowserStore * model)
3067 {
3068 return model->priv->filter_mode;
3069 }
3070
3071 void
pluma_file_browser_store_set_filter_mode(PlumaFileBrowserStore * model,PlumaFileBrowserStoreFilterMode mode)3072 pluma_file_browser_store_set_filter_mode (PlumaFileBrowserStore * model,
3073 PlumaFileBrowserStoreFilterMode
3074 mode)
3075 {
3076 g_return_if_fail (PLUMA_IS_FILE_BROWSER_STORE (model));
3077
3078 if (model->priv->filter_mode == mode)
3079 return;
3080
3081 model->priv->filter_mode = mode;
3082 model_refilter (model);
3083
3084 g_object_notify (G_OBJECT (model), "filter-mode");
3085 }
3086
3087 void
pluma_file_browser_store_set_filter_func(PlumaFileBrowserStore * model,PlumaFileBrowserStoreFilterFunc func,gpointer user_data)3088 pluma_file_browser_store_set_filter_func (PlumaFileBrowserStore * model,
3089 PlumaFileBrowserStoreFilterFunc
3090 func, gpointer user_data)
3091 {
3092 g_return_if_fail (PLUMA_IS_FILE_BROWSER_STORE (model));
3093
3094 model->priv->filter_func = func;
3095 model->priv->filter_user_data = user_data;
3096 model_refilter (model);
3097 }
3098
3099 void
pluma_file_browser_store_refilter(PlumaFileBrowserStore * model)3100 pluma_file_browser_store_refilter (PlumaFileBrowserStore * model)
3101 {
3102 model_refilter (model);
3103 }
3104
3105 PlumaFileBrowserStoreFilterMode
pluma_file_browser_store_filter_mode_get_default(void)3106 pluma_file_browser_store_filter_mode_get_default (void)
3107 {
3108 return PLUMA_FILE_BROWSER_STORE_FILTER_MODE_HIDE_HIDDEN;
3109 }
3110
3111 void
pluma_file_browser_store_refresh(PlumaFileBrowserStore * model)3112 pluma_file_browser_store_refresh (PlumaFileBrowserStore * model)
3113 {
3114 g_return_if_fail (PLUMA_IS_FILE_BROWSER_STORE (model));
3115
3116 if (model->priv->root == NULL || model->priv->virtual_root == NULL)
3117 return;
3118
3119 /* Clear the model */
3120 g_signal_emit (model, model_signals[BEGIN_REFRESH], 0);
3121 file_browser_node_unload (model, model->priv->virtual_root, TRUE);
3122 model_load_directory (model, model->priv->virtual_root);
3123 g_signal_emit (model, model_signals[END_REFRESH], 0);
3124 }
3125
3126 static void
reparent_node(FileBrowserNode * node,gboolean reparent)3127 reparent_node (FileBrowserNode * node, gboolean reparent)
3128 {
3129 FileBrowserNodeDir * dir;
3130 GSList * child;
3131 GFile * parent;
3132 gchar * base;
3133
3134 if (!node->file) {
3135 return;
3136 }
3137
3138 if (reparent) {
3139 parent = node->parent->file;
3140 base = g_file_get_basename (node->file);
3141 g_object_unref (node->file);
3142
3143 node->file = g_file_get_child (parent, base);
3144 g_free (base);
3145 }
3146
3147 if (NODE_IS_DIR (node)) {
3148 dir = FILE_BROWSER_NODE_DIR (node);
3149
3150 for (child = dir->children; child; child = child->next) {
3151 reparent_node ((FileBrowserNode *)child->data, TRUE);
3152 }
3153 }
3154 }
3155
3156 gboolean
pluma_file_browser_store_rename(PlumaFileBrowserStore * model,GtkTreeIter * iter,const gchar * new_name,GError ** error)3157 pluma_file_browser_store_rename (PlumaFileBrowserStore * model,
3158 GtkTreeIter * iter,
3159 const gchar * new_name,
3160 GError ** error)
3161 {
3162 FileBrowserNode *node;
3163 GFile * file;
3164 GFile * parent;
3165 GFile * previous;
3166 GError * err = NULL;
3167 gchar * olduri;
3168 gchar * newuri;
3169 GtkTreePath *path;
3170
3171 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (model), FALSE);
3172 g_return_val_if_fail (iter != NULL, FALSE);
3173 g_return_val_if_fail (iter->user_data != NULL, FALSE);
3174
3175 node = (FileBrowserNode *) (iter->user_data);
3176
3177 parent = g_file_get_parent (node->file);
3178 g_return_val_if_fail (parent != NULL, FALSE);
3179
3180 file = g_file_get_child (parent, new_name);
3181 g_object_unref (parent);
3182
3183 if (g_file_equal (node->file, file)) {
3184 g_object_unref (file);
3185 return TRUE;
3186 }
3187
3188 if (g_file_move (node->file, file, G_FILE_COPY_NONE, NULL, NULL, NULL, &err)) {
3189 previous = node->file;
3190 node->file = file;
3191
3192 /* This makes sure the actual info for the node is requeried */
3193 file_browser_node_set_name (node);
3194 file_browser_node_set_from_info (model, node, NULL, TRUE);
3195
3196 reparent_node (node, FALSE);
3197
3198 if (model_node_visibility (model, node)) {
3199 path = pluma_file_browser_store_get_path_real (model, node);
3200 row_changed (model, &path, iter);
3201 gtk_tree_path_free (path);
3202
3203 /* Reorder this item */
3204 model_resort_node (model, node);
3205 } else {
3206 g_object_unref (previous);
3207
3208 if (error != NULL)
3209 *error = g_error_new_literal (pluma_file_browser_store_error_quark (),
3210 PLUMA_FILE_BROWSER_ERROR_RENAME,
3211 _("The renamed file is currently filtered out. You need to adjust your filter settings to make the file visible"));
3212 return FALSE;
3213 }
3214
3215 olduri = g_file_get_uri (previous);
3216 newuri = g_file_get_uri (node->file);
3217
3218 g_signal_emit (model, model_signals[RENAME], 0, olduri, newuri);
3219
3220 g_object_unref (previous);
3221 g_free (olduri);
3222 g_free (newuri);
3223
3224 return TRUE;
3225 } else {
3226 g_object_unref (file);
3227
3228 if (err) {
3229 if (error != NULL) {
3230 *error =
3231 g_error_new_literal
3232 (pluma_file_browser_store_error_quark (),
3233 PLUMA_FILE_BROWSER_ERROR_RENAME,
3234 err->message);
3235 }
3236
3237 g_error_free (err);
3238 }
3239
3240 return FALSE;
3241 }
3242 }
3243
3244 static void
async_data_free(AsyncData * data)3245 async_data_free (AsyncData * data)
3246 {
3247 g_object_unref (data->cancellable);
3248
3249 g_list_free_full (data->files, g_object_unref);
3250
3251 if (!data->removed)
3252 data->model->priv->async_handles = g_slist_remove (data->model->priv->async_handles, data);
3253
3254 g_free (data);
3255 }
3256
3257 static gboolean
emit_no_trash(AsyncData * data)3258 emit_no_trash (AsyncData * data)
3259 {
3260 /* Emit the no trash error */
3261 gboolean ret;
3262
3263 g_signal_emit (data->model, model_signals[NO_TRASH], 0, data->files, &ret);
3264 return ret;
3265 }
3266
3267 static void
delete_file_finished(GFile * file,GAsyncResult * res,AsyncData * data)3268 delete_file_finished (GFile *file,
3269 GAsyncResult *res,
3270 AsyncData *data)
3271 {
3272 GError * error = NULL;
3273 gboolean ok;
3274
3275 if (data->trash)
3276 {
3277 ok = g_file_trash_finish (file, res, &error);
3278 }
3279 else
3280 {
3281 ok = g_file_delete_finish (file, res, &error);
3282 }
3283
3284 if (ok)
3285 {
3286 /* Remove the file from the model */
3287 FileBrowserNode *node = model_find_node (data->model, NULL, file);
3288
3289 if (node != NULL)
3290 {
3291 model_remove_node (data->model, node, NULL, TRUE);
3292 }
3293
3294 /* Process the next file */
3295 data->iter = data->iter->next;
3296 }
3297 else if (!ok && error != NULL)
3298 {
3299 gint code = error->code;
3300 g_error_free (error);
3301
3302 if (data->trash && code == G_IO_ERROR_NOT_SUPPORTED) {
3303 /* Trash is not supported on this system. Ask the user
3304 * if he wants to delete completely the files instead.
3305 */
3306 if (emit_no_trash (data))
3307 {
3308 /* Changes this into a delete job */
3309 data->trash = FALSE;
3310 data->iter = data->files;
3311 }
3312 else
3313 {
3314 /* End the job */
3315 async_data_free (data);
3316 return;
3317 }
3318 }
3319 else if (code == G_IO_ERROR_CANCELLED)
3320 {
3321 /* Job has been cancelled, end the job */
3322 async_data_free (data);
3323 return;
3324 }
3325 }
3326
3327 /* Continue the job */
3328 delete_files (data);
3329 }
3330
3331 static void
delete_files(AsyncData * data)3332 delete_files (AsyncData *data)
3333 {
3334 GFile *file;
3335
3336 /* Check if our job is done */
3337 if (data->iter == NULL)
3338 {
3339 async_data_free (data);
3340 return;
3341 }
3342
3343 file = G_FILE (data->iter->data);
3344
3345 if (data->trash)
3346 {
3347 g_file_trash_async (file,
3348 G_PRIORITY_DEFAULT,
3349 data->cancellable,
3350 (GAsyncReadyCallback)delete_file_finished,
3351 data);
3352 }
3353 else
3354 {
3355 g_file_delete_async (file,
3356 G_PRIORITY_DEFAULT,
3357 data->cancellable,
3358 (GAsyncReadyCallback)delete_file_finished,
3359 data);
3360 }
3361 }
3362
3363 PlumaFileBrowserStoreResult
pluma_file_browser_store_delete_all(PlumaFileBrowserStore * model,GList * rows,gboolean trash)3364 pluma_file_browser_store_delete_all (PlumaFileBrowserStore *model,
3365 GList *rows, gboolean trash)
3366 {
3367 FileBrowserNode * node;
3368 AsyncData * data;
3369 GList * files = NULL;
3370 GList * row;
3371 GtkTreeIter iter;
3372 GtkTreePath * prev = NULL;
3373 GtkTreePath * path;
3374
3375 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (model), PLUMA_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
3376
3377 if (rows == NULL)
3378 return PLUMA_FILE_BROWSER_STORE_RESULT_NO_CHANGE;
3379
3380 /* First we sort the paths so that we can later on remove any
3381 files/directories that are actually subfiles/directories of
3382 a directory that's also deleted */
3383 rows = g_list_sort (g_list_copy (rows), (GCompareFunc)gtk_tree_path_compare);
3384
3385 for (row = rows; row; row = row->next) {
3386 path = (GtkTreePath *)(row->data);
3387
3388 if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
3389 continue;
3390
3391 /* Skip if the current path is actually a descendant of the
3392 previous path */
3393 if (prev != NULL && gtk_tree_path_is_descendant (path, prev))
3394 continue;
3395
3396 prev = path;
3397 node = (FileBrowserNode *)(iter.user_data);
3398 files = g_list_prepend (files, g_object_ref (node->file));
3399 }
3400
3401 data = g_new (AsyncData, 1);
3402
3403 data->model = model;
3404 data->cancellable = g_cancellable_new ();
3405 data->files = files;
3406 data->trash = trash;
3407 data->iter = files;
3408 data->removed = FALSE;
3409
3410 model->priv->async_handles =
3411 g_slist_prepend (model->priv->async_handles, data);
3412
3413 delete_files (data);
3414 g_list_free (rows);
3415
3416 return PLUMA_FILE_BROWSER_STORE_RESULT_OK;
3417 }
3418
3419 PlumaFileBrowserStoreResult
pluma_file_browser_store_delete(PlumaFileBrowserStore * model,GtkTreeIter * iter,gboolean trash)3420 pluma_file_browser_store_delete (PlumaFileBrowserStore * model,
3421 GtkTreeIter * iter, gboolean trash)
3422 {
3423 FileBrowserNode *node;
3424 GList *rows = NULL;
3425 PlumaFileBrowserStoreResult result;
3426
3427 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (model), PLUMA_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
3428 g_return_val_if_fail (iter != NULL, PLUMA_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
3429 g_return_val_if_fail (iter->user_data != NULL, PLUMA_FILE_BROWSER_STORE_RESULT_NO_CHANGE);
3430
3431 node = (FileBrowserNode *) (iter->user_data);
3432
3433 if (NODE_IS_DUMMY (node))
3434 return PLUMA_FILE_BROWSER_STORE_RESULT_NO_CHANGE;
3435
3436 rows = g_list_append(NULL, pluma_file_browser_store_get_path_real (model, node));
3437 result = pluma_file_browser_store_delete_all (model, rows, trash);
3438
3439 g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free);
3440
3441 return result;
3442 }
3443
3444 gboolean
pluma_file_browser_store_new_file(PlumaFileBrowserStore * model,GtkTreeIter * parent,GtkTreeIter * iter)3445 pluma_file_browser_store_new_file (PlumaFileBrowserStore * model,
3446 GtkTreeIter * parent,
3447 GtkTreeIter * iter)
3448 {
3449 GFile * file;
3450 GFileOutputStream * stream;
3451 FileBrowserNodeDir *parent_node;
3452 gboolean result = FALSE;
3453 FileBrowserNode *node;
3454 GError * error = NULL;
3455
3456 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (model), FALSE);
3457 g_return_val_if_fail (parent != NULL, FALSE);
3458 g_return_val_if_fail (parent->user_data != NULL, FALSE);
3459 g_return_val_if_fail (NODE_IS_DIR
3460 ((FileBrowserNode *) (parent->user_data)),
3461 FALSE);
3462 g_return_val_if_fail (iter != NULL, FALSE);
3463
3464 parent_node = FILE_BROWSER_NODE_DIR (parent->user_data);
3465 /* Translators: This is the default name of new files created by the file browser pane. */
3466 file = unique_new_name (((FileBrowserNode *) parent_node)->file, _("file"));
3467
3468 stream = g_file_create (file, G_FILE_CREATE_NONE, NULL, &error);
3469
3470 if (!stream)
3471 {
3472 g_signal_emit (model, model_signals[ERROR], 0,
3473 PLUMA_FILE_BROWSER_ERROR_NEW_FILE,
3474 error->message);
3475 g_error_free (error);
3476 } else {
3477 g_object_unref (stream);
3478 node = model_add_node_from_file (model,
3479 (FileBrowserNode *)parent_node,
3480 file,
3481 NULL);
3482
3483 if (model_node_visibility (model, node)) {
3484 iter->user_data = node;
3485 result = TRUE;
3486 } else {
3487 g_signal_emit (model, model_signals[ERROR], 0,
3488 PLUMA_FILE_BROWSER_ERROR_NEW_FILE,
3489 _
3490 ("The new file is currently filtered out. You need to adjust your filter settings to make the file visible"));
3491 }
3492 }
3493
3494 g_object_unref (file);
3495 return result;
3496 }
3497
3498 gboolean
pluma_file_browser_store_new_directory(PlumaFileBrowserStore * model,GtkTreeIter * parent,GtkTreeIter * iter)3499 pluma_file_browser_store_new_directory (PlumaFileBrowserStore * model,
3500 GtkTreeIter * parent,
3501 GtkTreeIter * iter)
3502 {
3503 GFile * file;
3504 FileBrowserNodeDir *parent_node;
3505 GError * error = NULL;
3506 FileBrowserNode *node;
3507 gboolean result = FALSE;
3508
3509 g_return_val_if_fail (PLUMA_IS_FILE_BROWSER_STORE (model), FALSE);
3510 g_return_val_if_fail (parent != NULL, FALSE);
3511 g_return_val_if_fail (parent->user_data != NULL, FALSE);
3512 g_return_val_if_fail (NODE_IS_DIR
3513 ((FileBrowserNode *) (parent->user_data)),
3514 FALSE);
3515 g_return_val_if_fail (iter != NULL, FALSE);
3516
3517 parent_node = FILE_BROWSER_NODE_DIR (parent->user_data);
3518 /* Translators: This is the default name of new directories created by the file browser pane. */
3519 file = unique_new_name (((FileBrowserNode *) parent_node)->file, _("directory"));
3520
3521 if (!g_file_make_directory (file, NULL, &error)) {
3522 g_signal_emit (model, model_signals[ERROR], 0,
3523 PLUMA_FILE_BROWSER_ERROR_NEW_DIRECTORY,
3524 error->message);
3525 g_error_free (error);
3526 } else {
3527 node = model_add_node_from_file (model,
3528 (FileBrowserNode *)parent_node,
3529 file,
3530 NULL);
3531
3532 if (model_node_visibility (model, node)) {
3533 iter->user_data = node;
3534 result = TRUE;
3535 } else {
3536 g_signal_emit (model, model_signals[ERROR], 0,
3537 PLUMA_FILE_BROWSER_ERROR_NEW_FILE,
3538 _
3539 ("The new directory is currently filtered out. You need to adjust your filter settings to make the directory visible"));
3540 }
3541 }
3542
3543 g_object_unref (file);
3544 return result;
3545 }
3546
3547 void
_pluma_file_browser_store_register_type(GTypeModule * type_module)3548 _pluma_file_browser_store_register_type (GTypeModule *type_module)
3549 {
3550 pluma_file_browser_store_register_type (type_module);
3551 }
3552
3553 // ex:ts=8:noet:
3554