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