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