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