]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesystem.c
f4b78d4d546c0e0276134a636f80e905a8e0e648
[~andy/gtk] / gtk / gtkfilesystem.c
1 /* GTK - The GIMP Toolkit
2  * gtkfilesystem.c: Filesystem abstraction functions.
3  * Copyright (C) 2003, Red Hat, Inc.
4  * Copyright (C) 2007-2008 Carlos Garnacho
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
19  *
20  * Authors: Carlos Garnacho <carlos@imendio.com>
21  */
22
23 #include "config.h"
24
25 #include <string.h>
26
27 #include <glib/gi18n-lib.h>
28
29 #include "gtkfilechooser.h"
30 #include "gtkfilesystem.h"
31 #include "gtkicontheme.h"
32 #include "gtkprivate.h"
33
34 #include "gtkalias.h"
35
36 /* #define DEBUG_MODE */
37 #ifdef DEBUG_MODE
38 #define DEBUG(x) g_debug (x);
39 #else
40 #define DEBUG(x)
41 #endif
42
43 #define GTK_FILE_SYSTEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_FILE_SYSTEM, GtkFileSystemPrivate))
44 #define GTK_FOLDER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_FOLDER, GtkFolderPrivate))
45 #define FILES_PER_QUERY 100
46
47 /* The pointers we return for a GtkFileSystemVolume are opaque tokens; they are
48  * really pointers to GDrive, GVolume or GMount objects.  We need an extra
49  * token for the fake "File System" volume.  So, we'll return a pointer to
50  * this particular string.
51  */
52 static const gchar *root_volume_token = N_("File System");
53 #define IS_ROOT_VOLUME(volume) ((gpointer) (volume) == (gpointer) root_volume_token)
54
55 enum {
56   PROP_0,
57   PROP_FILE,
58   PROP_ENUMERATOR,
59   PROP_ATTRIBUTES
60 };
61
62 enum {
63   BOOKMARKS_CHANGED,
64   VOLUMES_CHANGED,
65   FS_LAST_SIGNAL
66 };
67
68 enum {
69   FILES_ADDED,
70   FILES_REMOVED,
71   FILES_CHANGED,
72   FINISHED_LOADING,
73   DELETED,
74   FOLDER_LAST_SIGNAL
75 };
76
77 static guint fs_signals [FS_LAST_SIGNAL] = { 0, };
78 static guint folder_signals [FOLDER_LAST_SIGNAL] = { 0, };
79
80 typedef struct GtkFileSystemPrivate GtkFileSystemPrivate;
81 typedef struct GtkFolderPrivate GtkFolderPrivate;
82 typedef struct AsyncFuncData AsyncFuncData;
83
84 struct GtkFileSystemPrivate
85 {
86   GVolumeMonitor *volume_monitor;
87
88   /* This list contains elements that can be
89    * of type GDrive, GVolume and GMount
90    */
91   GSList *volumes;
92
93   /* This list contains GtkFileSystemBookmark structs */
94   GSList *bookmarks;
95
96   GFileMonitor *bookmarks_monitor;
97 };
98
99 struct GtkFolderPrivate
100 {
101   GFile *folder_file;
102   GHashTable *children;
103   GFileMonitor *directory_monitor;
104   GFileEnumerator *enumerator;
105   GCancellable *cancellable;
106   gchar *attributes;
107
108   guint finished_loading : 1;
109 };
110
111 struct AsyncFuncData
112 {
113   GtkFileSystem *file_system;
114   GFile *file;
115   GtkFolder *folder;
116   GCancellable *cancellable;
117   gchar *attributes;
118
119   gpointer callback;
120   gpointer data;
121 };
122
123 struct GtkFileSystemBookmark
124 {
125   GFile *file;
126   gchar *label;
127 };
128
129 G_DEFINE_TYPE (GtkFileSystem, _gtk_file_system, G_TYPE_OBJECT)
130
131 G_DEFINE_TYPE (GtkFolder, _gtk_folder, G_TYPE_OBJECT)
132
133
134 static void gtk_folder_set_finished_loading (GtkFolder *folder,
135                                              gboolean   finished_loading);
136 static void gtk_folder_add_file             (GtkFolder *folder,
137                                              GFile     *file,
138                                              GFileInfo *info);
139
140
141 /* GtkFileSystemBookmark methods */
142 void
143 _gtk_file_system_bookmark_free (GtkFileSystemBookmark *bookmark)
144 {
145   g_object_unref (bookmark->file);
146   g_free (bookmark->label);
147   g_slice_free (GtkFileSystemBookmark, bookmark);
148 }
149
150 /* GtkFileSystem methods */
151 static void
152 volumes_changed (GVolumeMonitor *volume_monitor,
153                  gpointer        volume,
154                  gpointer        user_data)
155 {
156   GtkFileSystem *file_system;
157
158   gdk_threads_enter ();
159
160   file_system = GTK_FILE_SYSTEM (user_data);
161   g_signal_emit (file_system, fs_signals[VOLUMES_CHANGED], 0, volume);
162   gdk_threads_leave ();
163 }
164
165 static void
166 gtk_file_system_dispose (GObject *object)
167 {
168   GtkFileSystemPrivate *priv;
169
170   DEBUG ("dispose");
171
172   priv = GTK_FILE_SYSTEM_GET_PRIVATE (object);
173
174   if (priv->volumes)
175     {
176       g_slist_foreach (priv->volumes, (GFunc) g_object_unref, NULL);
177       g_slist_free (priv->volumes);
178       priv->volumes = NULL;
179     }
180
181   if (priv->volume_monitor)
182     {
183       g_signal_handlers_disconnect_by_func (priv->volume_monitor, volumes_changed, object);
184       g_object_unref (priv->volume_monitor);
185       priv->volume_monitor = NULL;
186     }
187
188   G_OBJECT_CLASS (_gtk_file_system_parent_class)->dispose (object);
189 }
190
191 static void
192 gtk_file_system_finalize (GObject *object)
193 {
194   GtkFileSystemPrivate *priv;
195
196   DEBUG ("finalize");
197
198   priv = GTK_FILE_SYSTEM_GET_PRIVATE (object);
199
200   if (priv->bookmarks_monitor)
201     g_object_unref (priv->bookmarks_monitor);
202
203   if (priv->bookmarks)
204     {
205       g_slist_foreach (priv->bookmarks, (GFunc) _gtk_file_system_bookmark_free, NULL);
206       g_slist_free (priv->bookmarks);
207     }
208
209   G_OBJECT_CLASS (_gtk_file_system_parent_class)->finalize (object);
210 }
211
212 static void
213 _gtk_file_system_class_init (GtkFileSystemClass *class)
214 {
215   GObjectClass *object_class = G_OBJECT_CLASS (class);
216
217   object_class->dispose = gtk_file_system_dispose;
218   object_class->finalize = gtk_file_system_finalize;
219
220   fs_signals[BOOKMARKS_CHANGED] =
221     g_signal_new ("bookmarks-changed",
222                   G_TYPE_FROM_CLASS (object_class),
223                   G_SIGNAL_RUN_LAST,
224                   G_STRUCT_OFFSET (GtkFileSystemClass, bookmarks_changed),
225                   NULL, NULL,
226                   g_cclosure_marshal_VOID__VOID,
227                   G_TYPE_NONE, 0);
228
229   fs_signals[VOLUMES_CHANGED] =
230     g_signal_new ("volumes-changed",
231                   G_TYPE_FROM_CLASS (object_class),
232                   G_SIGNAL_RUN_LAST,
233                   G_STRUCT_OFFSET (GtkFileSystemClass, volumes_changed),
234                   NULL, NULL,
235                   g_cclosure_marshal_VOID__VOID,
236                   G_TYPE_NONE, 0);
237
238   g_type_class_add_private (object_class, sizeof (GtkFileSystemPrivate));
239 }
240
241 static GFile *
242 get_bookmarks_file (void)
243 {
244   GFile *file;
245   gchar *filename;
246
247   filename = g_build_filename (g_get_home_dir (), ".gtk-bookmarks", NULL);
248   file = g_file_new_for_path (filename);
249   g_free (filename);
250
251   return file;
252 }
253
254 static GSList *
255 read_bookmarks (GFile *file)
256 {
257   gchar *contents;
258   gchar **lines, *space;
259   GSList *bookmarks = NULL;
260   gint i;
261
262   if (!g_file_load_contents (file, NULL, &contents,
263                              NULL, NULL, NULL))
264     return NULL;
265
266   lines = g_strsplit (contents, "\n", -1);
267
268   for (i = 0; lines[i]; i++)
269     {
270       GtkFileSystemBookmark *bookmark;
271
272       if (!*lines[i])
273         continue;
274
275       bookmark = g_slice_new0 (GtkFileSystemBookmark);
276
277       if ((space = strchr (lines[i], ' ')) != NULL)
278         {
279           space[0] = '\0';
280           bookmark->label = g_strdup (space + 1);
281         }
282
283       bookmark->file = g_file_new_for_uri (lines[i]);
284       bookmarks = g_slist_prepend (bookmarks, bookmark);
285     }
286
287   bookmarks = g_slist_reverse (bookmarks);
288   g_strfreev (lines);
289   g_free (contents);
290
291   return bookmarks;
292 }
293
294 static void
295 save_bookmarks (GFile  *bookmarks_file,
296                 GSList *bookmarks)
297 {
298   GError *error = NULL;
299   GString *contents;
300
301   contents = g_string_new ("");
302
303   while (bookmarks)
304     {
305       GtkFileSystemBookmark *bookmark;
306       gchar *uri;
307
308       bookmark = bookmarks->data;
309       uri = g_file_get_uri (bookmark->file);
310       g_string_append (contents, uri);
311
312       if (bookmark->label)
313         g_string_append_printf (contents, " %s", bookmark->label);
314
315       g_string_append_c (contents, '\n');
316       bookmarks = bookmarks->next;
317       g_free (uri);
318     }
319
320   if (!g_file_replace_contents (bookmarks_file,
321                                 contents->str,
322                                 strlen (contents->str),
323                                 NULL, FALSE, 0, NULL,
324                                 NULL, &error))
325     {
326       g_critical ("%s", error->message);
327       g_error_free (error);
328     }
329
330   g_string_free (contents, TRUE);
331 }
332
333 static void
334 bookmarks_file_changed (GFileMonitor      *monitor,
335                         GFile             *file,
336                         GFile             *other_file,
337                         GFileMonitorEvent  event,
338                         gpointer           data)
339 {
340   GtkFileSystemPrivate *priv;
341
342   priv = GTK_FILE_SYSTEM_GET_PRIVATE (data);
343
344   switch (event)
345     {
346     case G_FILE_MONITOR_EVENT_CHANGED:
347     case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
348     case G_FILE_MONITOR_EVENT_CREATED:
349     case G_FILE_MONITOR_EVENT_DELETED:
350       g_slist_foreach (priv->bookmarks, (GFunc) _gtk_file_system_bookmark_free, NULL);
351       g_slist_free (priv->bookmarks);
352
353       priv->bookmarks = read_bookmarks (file);
354
355       gdk_threads_enter ();
356       g_signal_emit (data, fs_signals[BOOKMARKS_CHANGED], 0);
357       gdk_threads_leave ();
358       break;
359     default:
360       /* ignore at the moment */
361       break;
362     }
363 }
364
365 static gboolean
366 mount_referenced_by_volume_activation_root (GList *volumes, GMount *mount)
367 {
368   GList *l;
369   GFile *mount_root;
370   gboolean ret;
371
372   ret = FALSE;
373
374   mount_root = g_mount_get_root (mount);
375
376   for (l = volumes; l != NULL; l = l->next)
377     {
378       GVolume *volume = G_VOLUME (l->data);
379       GFile *volume_activation_root;
380
381       volume_activation_root = g_volume_get_activation_root (volume);
382       if (volume_activation_root != NULL)
383         {
384           if (g_file_has_prefix (volume_activation_root, mount_root))
385             {
386               ret = TRUE;
387               g_object_unref (volume_activation_root);
388               break;
389             }
390           g_object_unref (volume_activation_root);
391         }
392     }
393
394   g_object_unref (mount_root);
395   return ret;
396 }
397
398 static void
399 get_volumes_list (GtkFileSystem *file_system)
400 {
401   GtkFileSystemPrivate *priv;
402   GList *l, *ll;
403   GList *drives;
404   GList *volumes;
405   GList *mounts;
406   GDrive *drive;
407   GVolume *volume;
408   GMount *mount;
409
410   priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
411
412   if (priv->volumes)
413     {
414       g_slist_foreach (priv->volumes, (GFunc) g_object_unref, NULL);
415       g_slist_free (priv->volumes);
416       priv->volumes = NULL;
417     }
418
419   /* first go through all connected drives */
420   drives = g_volume_monitor_get_connected_drives (priv->volume_monitor);
421
422   for (l = drives; l != NULL; l = l->next)
423     {
424       drive = l->data;
425       volumes = g_drive_get_volumes (drive);
426
427       if (volumes)
428         {
429           for (ll = volumes; ll != NULL; ll = ll->next)
430             {
431               volume = ll->data;
432               mount = g_volume_get_mount (volume);
433
434               if (mount)
435                 {
436                   /* Show mounted volume */
437                   priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
438                   g_object_unref (mount);
439                 }
440               else
441                 {
442                   /* Do show the unmounted volumes in the sidebar;
443                    * this is so the user can mount it (in case automounting
444                    * is off).
445                    *
446                    * Also, even if automounting is enabled, this gives a visual
447                    * cue that the user should remember to yank out the media if
448                    * he just unmounted it.
449                    */
450                   priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (volume));
451                 }
452
453               g_object_unref (volume);
454             }
455   
456            g_list_free (volumes);
457         }
458       else if (g_drive_is_media_removable (drive) && !g_drive_is_media_check_automatic (drive))
459         {
460           /* If the drive has no mountable volumes and we cannot detect media change.. we
461            * display the drive in the sidebar so the user can manually poll the drive by
462            * right clicking and selecting "Rescan..."
463            *
464            * This is mainly for drives like floppies where media detection doesn't
465            * work.. but it's also for human beings who like to turn off media detection
466            * in the OS to save battery juice.
467            */
468
469           priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (drive));
470         }
471     }
472
473   g_list_free (drives);
474
475   /* add all volumes that is not associated with a drive */
476   volumes = g_volume_monitor_get_volumes (priv->volume_monitor);
477
478   for (l = volumes; l != NULL; l = l->next)
479     {
480       volume = l->data;
481       drive = g_volume_get_drive (volume);
482
483       if (drive)
484         {
485           g_object_unref (drive);
486           continue;
487         }
488
489       mount = g_volume_get_mount (volume);
490
491       if (mount)
492         {
493           /* show this mount */
494           priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
495           g_object_unref (mount);
496         }
497       else
498         {
499           /* see comment above in why we add an icon for a volume */
500           priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (volume));
501         }
502     }
503
504   /* add mounts that has no volume (/etc/mtab mounts, ftp, sftp,...) */
505   mounts = g_volume_monitor_get_mounts (priv->volume_monitor);
506
507   for (l = mounts; l != NULL; l = l->next)
508     {
509       mount = l->data;
510       volume = g_mount_get_volume (mount);
511
512       if (volume)
513         {
514           g_object_unref (volume);
515           continue;
516         }
517
518       /* if there's exists one or more volumes with an activation root inside the mount,
519        * don't display the mount
520        */
521       if (mount_referenced_by_volume_activation_root (volumes, mount))
522         {
523           continue;
524         }
525
526       /* show this mount */
527       priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
528     }
529
530   g_list_free (volumes);
531
532   g_list_free (mounts);
533 }
534
535 static void
536 _gtk_file_system_init (GtkFileSystem *file_system)
537 {
538   GtkFileSystemPrivate *priv;
539   GFile *bookmarks_file;
540   GError *error = NULL;
541
542   DEBUG ("init");
543
544   priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
545
546   /* Volumes */
547   priv->volume_monitor = g_volume_monitor_get ();
548
549   g_signal_connect (priv->volume_monitor, "mount-added",
550                     G_CALLBACK (volumes_changed), file_system);
551   g_signal_connect (priv->volume_monitor, "mount-removed",
552                     G_CALLBACK (volumes_changed), file_system);
553   g_signal_connect (priv->volume_monitor, "mount-changed",
554                     G_CALLBACK (volumes_changed), file_system);
555   g_signal_connect (priv->volume_monitor, "volume-added",
556                     G_CALLBACK (volumes_changed), file_system);
557   g_signal_connect (priv->volume_monitor, "volume-removed",
558                     G_CALLBACK (volumes_changed), file_system);
559   g_signal_connect (priv->volume_monitor, "volume-changed",
560                     G_CALLBACK (volumes_changed), file_system);
561   g_signal_connect (priv->volume_monitor, "drive-connected",
562                     G_CALLBACK (volumes_changed), file_system);
563   g_signal_connect (priv->volume_monitor, "drive-disconnected",
564                     G_CALLBACK (volumes_changed), file_system);
565   g_signal_connect (priv->volume_monitor, "drive-changed",
566                     G_CALLBACK (volumes_changed), file_system);
567
568   /* Bookmarks */
569   bookmarks_file = get_bookmarks_file ();
570   priv->bookmarks = read_bookmarks (bookmarks_file);
571   priv->bookmarks_monitor = g_file_monitor_file (bookmarks_file,
572                                                  G_FILE_MONITOR_NONE,
573                                                  NULL, &error);
574   if (error)
575     {
576       g_warning ("%s", error->message);
577       g_error_free (error);
578     }
579   else
580     g_signal_connect (priv->bookmarks_monitor, "changed",
581                       G_CALLBACK (bookmarks_file_changed), file_system);
582
583   g_object_unref (bookmarks_file);
584 }
585
586 /* GtkFileSystem public methods */
587 GtkFileSystem *
588 _gtk_file_system_new (void)
589 {
590   return g_object_new (GTK_TYPE_FILE_SYSTEM, NULL);
591 }
592
593 GSList *
594 _gtk_file_system_list_volumes (GtkFileSystem *file_system)
595 {
596   GtkFileSystemPrivate *priv;
597   GSList *list;
598
599   DEBUG ("list_volumes");
600
601   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
602
603   priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
604   get_volumes_list (GTK_FILE_SYSTEM (file_system));
605
606   list = g_slist_copy (priv->volumes);
607
608 #ifndef G_OS_WIN32
609   /* Prepend root volume */
610   list = g_slist_prepend (list, (gpointer) root_volume_token);
611 #endif
612
613   return list;
614 }
615
616 GSList *
617 _gtk_file_system_list_bookmarks (GtkFileSystem *file_system)
618 {
619   GtkFileSystemPrivate *priv;
620   GSList *bookmarks, *files = NULL;
621
622   DEBUG ("list_bookmarks");
623
624   priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
625   bookmarks = priv->bookmarks;
626
627   while (bookmarks)
628     {
629       GtkFileSystemBookmark *bookmark;
630
631       bookmark = bookmarks->data;
632       bookmarks = bookmarks->next;
633
634       files = g_slist_prepend (files, g_object_ref (bookmark->file));
635     }
636
637   return g_slist_reverse (files);
638 }
639
640 gboolean
641 _gtk_file_system_parse (GtkFileSystem     *file_system,
642                         GFile             *base_file,
643                         const gchar       *str,
644                         GFile            **folder,
645                         gchar            **file_part,
646                         GError           **error)
647 {
648   GFile *file;
649   gboolean result = FALSE;
650   gboolean is_dir = FALSE;
651   gchar *last_slash = NULL;
652
653   DEBUG ("parse");
654
655   if (str && *str)
656     is_dir = (str [strlen (str) - 1] == G_DIR_SEPARATOR);
657
658   last_slash = strrchr (str, G_DIR_SEPARATOR);
659
660   if (str[0] == '~')
661     file = g_file_parse_name (str);
662   else
663     file = g_file_resolve_relative_path (base_file, str);
664
665   if (g_file_equal (base_file, file))
666     {
667       /* this is when user types '.', could be the
668        * beginning of a hidden file, ./ or ../
669        */
670       *folder = g_object_ref (file);
671       *file_part = g_strdup (str);
672       result = TRUE;
673     }
674   else if (is_dir)
675     {
676       /* it's a dir, or at least it ends with the dir separator */
677       *folder = g_object_ref (file);
678       *file_part = g_strdup ("");
679       result = TRUE;
680     }
681   else
682     {
683       GFile *parent_file;
684
685       parent_file = g_file_get_parent (file);
686
687       if (!parent_file)
688         {
689           g_set_error (error,
690                        GTK_FILE_CHOOSER_ERROR,
691                        GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
692                        "Could not get parent file");
693           *folder = NULL;
694           *file_part = NULL;
695         }
696       else
697         {
698           *folder = parent_file;
699           result = TRUE;
700
701           if (last_slash)
702             *file_part = g_strdup (last_slash + 1);
703           else
704             *file_part = g_strdup (str);
705         }
706     }
707
708   g_object_unref (file);
709
710   return result;
711 }
712
713 static void
714 free_async_data (AsyncFuncData *async_data)
715 {
716   g_object_unref (async_data->file_system);
717   g_object_unref (async_data->file);
718   g_object_unref (async_data->cancellable);
719
720   if (async_data->folder)
721     g_object_unref (async_data->folder);
722
723   g_free (async_data->attributes);
724   g_free (async_data);
725 }
726
727 static void
728 enumerate_children_callback (GObject      *source_object,
729                              GAsyncResult *result,
730                              gpointer      user_data)
731 {
732   GFileEnumerator *enumerator;
733   AsyncFuncData *async_data;
734   GtkFolder *folder = NULL;
735   GFile *file;
736   GError *error = NULL;
737
738   file = G_FILE (source_object);
739   async_data = (AsyncFuncData *) user_data;
740   enumerator = g_file_enumerate_children_finish (file, result, &error);
741
742   if (enumerator)
743     {
744       folder = g_object_new (GTK_TYPE_FOLDER,
745                              "file", source_object,
746                              "enumerator", enumerator,
747                              "attributes", async_data->attributes,
748                              NULL);
749       g_object_unref (enumerator);
750     }
751
752   gdk_threads_enter ();
753   ((GtkFileSystemGetFolderCallback) async_data->callback) (async_data->cancellable,
754                                                            folder, error, async_data->data);
755   gdk_threads_leave ();
756
757   free_async_data (async_data);
758
759   if (error)
760     g_error_free (error);
761 }
762
763 GCancellable *
764 _gtk_file_system_get_folder (GtkFileSystem                  *file_system,
765                              GFile                          *file,
766                              const gchar                    *attributes,
767                              GtkFileSystemGetFolderCallback  callback,
768                              gpointer                        data)
769 {
770   GCancellable *cancellable;
771   AsyncFuncData *async_data;
772
773   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
774   g_return_val_if_fail (G_IS_FILE (file), NULL);
775
776   cancellable = g_cancellable_new ();
777
778   async_data = g_new0 (AsyncFuncData, 1);
779   async_data->file_system = g_object_ref (file_system);
780   async_data->file = g_object_ref (file);
781   async_data->cancellable = g_object_ref (cancellable);
782   async_data->attributes = g_strdup (attributes);
783
784   async_data->callback = callback;
785   async_data->data = data;
786
787   g_file_enumerate_children_async (file,
788                                    attributes,
789                                    G_FILE_QUERY_INFO_NONE,
790                                    G_PRIORITY_DEFAULT,
791                                    cancellable,
792                                    enumerate_children_callback,
793                                    async_data);
794   return cancellable;
795 }
796
797 static void
798 query_info_callback (GObject      *source_object,
799                      GAsyncResult *result,
800                      gpointer      user_data)
801 {
802   AsyncFuncData *async_data;
803   GError *error = NULL;
804   GFileInfo *file_info;
805   GFile *file;
806
807   DEBUG ("query_info_callback");
808
809   file = G_FILE (source_object);
810   async_data = (AsyncFuncData *) user_data;
811   file_info = g_file_query_info_finish (file, result, &error);
812
813   if (async_data->callback)
814     {
815       gdk_threads_enter ();
816       ((GtkFileSystemGetInfoCallback) async_data->callback) (async_data->cancellable,
817                                                              file_info, error, async_data->data);
818       gdk_threads_leave ();
819     }
820
821   if (file_info)
822     g_object_unref (file_info);
823
824   if (error)
825     g_error_free (error);
826
827   free_async_data (async_data);
828 }
829
830 GCancellable *
831 _gtk_file_system_get_info (GtkFileSystem                *file_system,
832                            GFile                        *file,
833                            const gchar                  *attributes,
834                            GtkFileSystemGetInfoCallback  callback,
835                            gpointer                      data)
836 {
837   GCancellable *cancellable;
838   AsyncFuncData *async_data;
839
840   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
841   g_return_val_if_fail (G_IS_FILE (file), NULL);
842
843   cancellable = g_cancellable_new ();
844
845   async_data = g_new0 (AsyncFuncData, 1);
846   async_data->file_system = g_object_ref (file_system);
847   async_data->file = g_object_ref (file);
848   async_data->cancellable = g_object_ref (cancellable);
849
850   async_data->callback = callback;
851   async_data->data = data;
852
853   g_file_query_info_async (file,
854                            attributes,
855                            G_FILE_QUERY_INFO_NONE,
856                            G_PRIORITY_DEFAULT,
857                            cancellable,
858                            query_info_callback,
859                            async_data);
860
861   return cancellable;
862 }
863
864 static void
865 drive_poll_for_media_cb (GObject      *source_object,
866                          GAsyncResult *result,
867                          gpointer      user_data)
868 {
869   AsyncFuncData *async_data;
870   GError *error = NULL;
871
872   g_drive_poll_for_media_finish (G_DRIVE (source_object), result, &error);
873   async_data = (AsyncFuncData *) user_data;
874
875   gdk_threads_enter ();
876   ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable,
877                                                              (GtkFileSystemVolume *) source_object,
878                                                              error, async_data->data);
879   gdk_threads_leave ();
880
881   if (error)
882     g_error_free (error);
883 }
884
885 static void
886 volume_mount_cb (GObject      *source_object,
887                  GAsyncResult *result,
888                  gpointer      user_data)
889 {
890   AsyncFuncData *async_data;
891   GError *error = NULL;
892
893   g_volume_mount_finish (G_VOLUME (source_object), result, &error);
894   async_data = (AsyncFuncData *) user_data;
895
896   gdk_threads_enter ();
897   ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable,
898                                                              (GtkFileSystemVolume *) source_object,
899                                                              error, async_data->data);
900   gdk_threads_leave ();
901
902   if (error)
903     g_error_free (error);
904 }
905
906 GCancellable *
907 _gtk_file_system_mount_volume (GtkFileSystem                    *file_system,
908                                GtkFileSystemVolume              *volume,
909                                GMountOperation                  *mount_operation,
910                                GtkFileSystemVolumeMountCallback  callback,
911                                gpointer                          data)
912 {
913   GCancellable *cancellable;
914   AsyncFuncData *async_data;
915   gboolean handled = FALSE;
916
917   DEBUG ("volume_mount");
918
919   cancellable = g_cancellable_new ();
920
921   async_data = g_new0 (AsyncFuncData, 1);
922   async_data->file_system = g_object_ref (file_system);
923   async_data->cancellable = g_object_ref (cancellable);
924
925   async_data->callback = callback;
926   async_data->data = data;
927
928   if (G_IS_DRIVE (volume))
929     {
930       /* this path happens for drives that are not polled by the OS and where the last media
931        * check indicated that no media was available. So the thing to do here is to
932        * invoke poll_for_media() on the drive
933        */
934       g_drive_poll_for_media (G_DRIVE (volume), cancellable, drive_poll_for_media_cb, async_data);
935       handled = TRUE;
936     }
937   else if (G_IS_VOLUME (volume))
938     {
939       g_volume_mount (G_VOLUME (volume), G_MOUNT_MOUNT_NONE, mount_operation, cancellable, volume_mount_cb, async_data);
940       handled = TRUE;
941     }
942
943   if (!handled)
944     free_async_data (async_data);
945
946   return cancellable;
947 }
948
949 static void
950 enclosing_volume_mount_cb (GObject      *source_object,
951                            GAsyncResult *result,
952                            gpointer      user_data)
953 {
954   GtkFileSystemVolume *volume;
955   AsyncFuncData *async_data;
956   GError *error = NULL;
957
958   async_data = (AsyncFuncData *) user_data;
959   g_file_mount_enclosing_volume_finish (G_FILE (source_object), result, &error);
960   volume = _gtk_file_system_get_volume_for_file (async_data->file_system, G_FILE (source_object));
961
962   /* Silently drop G_IO_ERROR_ALREADY_MOUNTED error for gvfs backends without visible mounts. */
963   /* Better than doing query_info with additional I/O every time. */
964   if (error && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_ALREADY_MOUNTED))
965     g_clear_error (&error);
966
967   gdk_threads_enter ();
968   ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable, volume,
969                                                              error, async_data->data);
970   gdk_threads_leave ();
971
972   if (error)
973     g_error_free (error);
974 }
975
976 GCancellable *
977 _gtk_file_system_mount_enclosing_volume (GtkFileSystem                     *file_system,
978                                          GFile                             *file,
979                                          GMountOperation                   *mount_operation,
980                                          GtkFileSystemVolumeMountCallback   callback,
981                                          gpointer                           data)
982 {
983   GCancellable *cancellable;
984   AsyncFuncData *async_data;
985
986   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
987   g_return_val_if_fail (G_IS_FILE (file), NULL);
988
989   DEBUG ("mount_enclosing_volume");
990
991   cancellable = g_cancellable_new ();
992
993   async_data = g_new0 (AsyncFuncData, 1);
994   async_data->file_system = g_object_ref (file_system);
995   async_data->file = g_object_ref (file);
996   async_data->cancellable = g_object_ref (cancellable);
997
998   async_data->callback = callback;
999   async_data->data = data;
1000
1001   g_file_mount_enclosing_volume (file,
1002                                  G_MOUNT_MOUNT_NONE,
1003                                  mount_operation,
1004                                  cancellable,
1005                                  enclosing_volume_mount_cb,
1006                                  async_data);
1007   return cancellable;
1008 }
1009
1010 gboolean
1011 _gtk_file_system_insert_bookmark (GtkFileSystem  *file_system,
1012                                   GFile          *file,
1013                                   gint            position,
1014                                   GError        **error)
1015 {
1016   GtkFileSystemPrivate *priv;
1017   GSList *bookmarks;
1018   GtkFileSystemBookmark *bookmark;
1019   gboolean result = TRUE;
1020   GFile *bookmarks_file;
1021
1022   priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
1023   bookmarks = priv->bookmarks;
1024
1025   while (bookmarks)
1026     {
1027       bookmark = bookmarks->data;
1028       bookmarks = bookmarks->next;
1029
1030       if (g_file_equal (bookmark->file, file))
1031         {
1032           /* File is already in bookmarks */
1033           result = FALSE;
1034           break;
1035         }
1036     }
1037
1038   if (!result)
1039     {
1040       gchar *uri = g_file_get_uri (file);
1041
1042       g_set_error (error,
1043                    GTK_FILE_CHOOSER_ERROR,
1044                    GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS,
1045                    "%s already exists in the bookmarks list",
1046                    uri);
1047
1048       g_free (uri);
1049
1050       return FALSE;
1051     }
1052
1053   bookmark = g_slice_new0 (GtkFileSystemBookmark);
1054   bookmark->file = g_object_ref (file);
1055
1056   priv->bookmarks = g_slist_insert (priv->bookmarks, bookmark, position);
1057
1058   bookmarks_file = get_bookmarks_file ();
1059   save_bookmarks (bookmarks_file, priv->bookmarks);
1060   g_object_unref (bookmarks_file);
1061
1062   g_signal_emit (file_system, fs_signals[BOOKMARKS_CHANGED], 0);
1063
1064   return TRUE;
1065 }
1066
1067 gboolean
1068 _gtk_file_system_remove_bookmark (GtkFileSystem  *file_system,
1069                                   GFile          *file,
1070                                   GError        **error)
1071 {
1072   GtkFileSystemPrivate *priv;
1073   GtkFileSystemBookmark *bookmark;
1074   GSList *bookmarks;
1075   gboolean result = FALSE;
1076   GFile *bookmarks_file;
1077
1078   priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
1079
1080   if (!priv->bookmarks)
1081     return FALSE;
1082
1083   bookmarks = priv->bookmarks;
1084
1085   while (bookmarks)
1086     {
1087       bookmark = bookmarks->data;
1088
1089       if (g_file_equal (bookmark->file, file))
1090         {
1091           result = TRUE;
1092           priv->bookmarks = g_slist_remove_link (priv->bookmarks, bookmarks);
1093           _gtk_file_system_bookmark_free (bookmark);
1094           g_slist_free_1 (bookmarks);
1095           break;
1096         }
1097
1098       bookmarks = bookmarks->next;
1099     }
1100
1101   if (!result)
1102     {
1103       gchar *uri = g_file_get_uri (file);
1104
1105       g_set_error (error,
1106                    GTK_FILE_CHOOSER_ERROR,
1107                    GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
1108                    "%s does not exist in the bookmarks list",
1109                    uri);
1110
1111       g_free (uri);
1112
1113       return FALSE;
1114     }
1115
1116   bookmarks_file = get_bookmarks_file ();
1117   save_bookmarks (bookmarks_file, priv->bookmarks);
1118   g_object_unref (bookmarks_file);
1119
1120   g_signal_emit (file_system, fs_signals[BOOKMARKS_CHANGED], 0);
1121
1122   return TRUE;
1123 }
1124
1125 gchar *
1126 _gtk_file_system_get_bookmark_label (GtkFileSystem *file_system,
1127                                      GFile         *file)
1128 {
1129   GtkFileSystemPrivate *priv;
1130   GSList *bookmarks;
1131   gchar *label = NULL;
1132
1133   DEBUG ("get_bookmark_label");
1134
1135   priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
1136   bookmarks = priv->bookmarks;
1137
1138   while (bookmarks)
1139     {
1140       GtkFileSystemBookmark *bookmark;
1141
1142       bookmark = bookmarks->data;
1143       bookmarks = bookmarks->next;
1144
1145       if (g_file_equal (file, bookmark->file))
1146         {
1147           label = g_strdup (bookmark->label);
1148           break;
1149         }
1150     }
1151
1152   return label;
1153 }
1154
1155 void
1156 _gtk_file_system_set_bookmark_label (GtkFileSystem *file_system,
1157                                      GFile         *file,
1158                                      const gchar   *label)
1159 {
1160   GtkFileSystemPrivate *priv;
1161   gboolean changed = FALSE;
1162   GFile *bookmarks_file;
1163   GSList *bookmarks;
1164
1165   DEBUG ("set_bookmark_label");
1166
1167   priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
1168   bookmarks = priv->bookmarks;
1169
1170   while (bookmarks)
1171     {
1172       GtkFileSystemBookmark *bookmark;
1173
1174       bookmark = bookmarks->data;
1175       bookmarks = bookmarks->next;
1176
1177       if (g_file_equal (file, bookmark->file))
1178         {
1179           g_free (bookmark->label);
1180           bookmark->label = g_strdup (label);
1181           changed = TRUE;
1182           break;
1183         }
1184     }
1185
1186   bookmarks_file = get_bookmarks_file ();
1187   save_bookmarks (bookmarks_file, priv->bookmarks);
1188   g_object_unref (bookmarks_file);
1189
1190   if (changed)
1191     g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1192 }
1193
1194 GtkFileSystemVolume *
1195 _gtk_file_system_get_volume_for_file (GtkFileSystem *file_system,
1196                                       GFile         *file)
1197 {
1198   GtkFileSystemPrivate *priv;
1199   GMount *mount;
1200
1201   DEBUG ("get_volume_for_file");
1202
1203   priv = GTK_FILE_SYSTEM_GET_PRIVATE (file_system);
1204   mount = g_file_find_enclosing_mount (file, NULL, NULL);
1205
1206   if (!mount && g_file_is_native (file))
1207     return (GtkFileSystemVolume *) root_volume_token;
1208
1209   return (GtkFileSystemVolume *) mount;
1210 }
1211
1212 /* GtkFolder methods */
1213 static void
1214 gtk_folder_set_property (GObject      *object,
1215                          guint         prop_id,
1216                          const GValue *value,
1217                          GParamSpec   *pspec)
1218 {
1219   GtkFolderPrivate *priv;
1220
1221   priv = GTK_FOLDER_GET_PRIVATE (object);
1222
1223   switch (prop_id)
1224     {
1225     case PROP_FILE:
1226       priv->folder_file = g_value_dup_object (value);
1227       break;
1228     case PROP_ENUMERATOR:
1229       priv->enumerator = g_value_dup_object (value);
1230       break;
1231     case PROP_ATTRIBUTES:
1232       priv->attributes = g_value_dup_string (value);
1233       break;
1234     default:
1235       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1236       break;
1237     }
1238 }
1239
1240 static void
1241 gtk_folder_get_property (GObject    *object,
1242                          guint       prop_id,
1243                          GValue     *value,
1244                          GParamSpec *pspec)
1245 {
1246   GtkFolderPrivate *priv;
1247
1248   priv = GTK_FOLDER_GET_PRIVATE (object);
1249
1250   switch (prop_id)
1251     {
1252     case PROP_FILE:
1253       g_value_set_object (value, priv->folder_file);
1254       break;
1255     case PROP_ENUMERATOR:
1256       g_value_set_object (value, priv->enumerator);
1257       break;
1258     case PROP_ATTRIBUTES:
1259       g_value_set_string (value, priv->attributes);
1260       break;
1261     default:
1262       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1263       break;
1264     }
1265 }
1266
1267 static void
1268 query_created_file_info_callback (GObject      *source_object,
1269                                   GAsyncResult *result,
1270                                   gpointer      user_data)
1271 {
1272   GFile *file = G_FILE (source_object);
1273   GError *error = NULL;
1274   GFileInfo *info;
1275   GtkFolder *folder;
1276   GSList *files;
1277
1278   info = g_file_query_info_finish (file, result, &error);
1279
1280   if (error)
1281     {
1282       g_error_free (error);
1283       return;
1284     }
1285
1286   folder = GTK_FOLDER (user_data);
1287   gtk_folder_add_file (folder, file, info);
1288
1289   files = g_slist_prepend (NULL, file);
1290   g_signal_emit (folder, folder_signals[FILES_ADDED], 0, files);
1291   g_slist_free (files);
1292
1293   g_object_unref (info);
1294 }
1295
1296 static void
1297 directory_monitor_changed (GFileMonitor      *monitor,
1298                            GFile             *file,
1299                            GFile             *other_file,
1300                            GFileMonitorEvent  event,
1301                            gpointer           data)
1302 {
1303   GtkFolderPrivate *priv;
1304   GtkFolder *folder;
1305   GSList *files;
1306
1307   folder = GTK_FOLDER (data);
1308   priv = GTK_FOLDER_GET_PRIVATE (folder);
1309   files = g_slist_prepend (NULL, file);
1310
1311   gdk_threads_enter ();
1312
1313   switch (event)
1314     {
1315     case G_FILE_MONITOR_EVENT_CREATED:
1316       g_file_query_info_async (file,
1317                                priv->attributes,
1318                                G_FILE_QUERY_INFO_NONE,
1319                                G_PRIORITY_DEFAULT,
1320                                priv->cancellable,
1321                                query_created_file_info_callback,
1322                                folder);
1323       break;
1324     case G_FILE_MONITOR_EVENT_DELETED:
1325       if (g_file_equal (file, priv->folder_file))
1326         g_signal_emit (folder, folder_signals[DELETED], 0);
1327       else
1328         g_signal_emit (folder, folder_signals[FILES_REMOVED], 0, files);
1329       break;
1330     default:
1331       break;
1332     }
1333
1334   gdk_threads_leave ();
1335
1336   g_slist_free (files);
1337 }
1338
1339 static void
1340 enumerator_files_callback (GObject      *source_object,
1341                            GAsyncResult *result,
1342                            gpointer      user_data)
1343 {
1344   GFileEnumerator *enumerator;
1345   GtkFolderPrivate *priv;
1346   GtkFolder *folder;
1347   GError *error = NULL;
1348   GSList *files = NULL;
1349   GList *file_infos, *f;
1350
1351   enumerator = G_FILE_ENUMERATOR (source_object);
1352   file_infos = g_file_enumerator_next_files_finish (enumerator, result, &error);
1353
1354   if (error)
1355     {
1356       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1357         g_warning ("%s", error->message);
1358
1359       g_error_free (error);
1360       return;
1361     }
1362
1363   folder = GTK_FOLDER (user_data);
1364   priv = GTK_FOLDER_GET_PRIVATE (folder);
1365
1366   if (!file_infos)
1367     {
1368       g_file_enumerator_close_async (enumerator,
1369                                      G_PRIORITY_DEFAULT,
1370                                      NULL, NULL, NULL);
1371
1372       gtk_folder_set_finished_loading (folder, TRUE);
1373       return;
1374     }
1375
1376   g_file_enumerator_next_files_async (enumerator, FILES_PER_QUERY,
1377                                       G_PRIORITY_DEFAULT,
1378                                       priv->cancellable,
1379                                       enumerator_files_callback,
1380                                       folder);
1381
1382   for (f = file_infos; f; f = f->next)
1383     {
1384       GFileInfo *info;
1385       GFile *child_file;
1386
1387       info = f->data;
1388       child_file = g_file_get_child (priv->folder_file, g_file_info_get_name (info));
1389       gtk_folder_add_file (folder, child_file, info);
1390       files = g_slist_prepend (files, child_file);
1391     }
1392
1393   gdk_threads_enter ();
1394   g_signal_emit (folder, folder_signals[FILES_ADDED], 0, files);
1395   gdk_threads_leave ();
1396
1397   g_list_foreach (file_infos, (GFunc) g_object_unref, NULL);
1398   g_list_free (file_infos);
1399
1400   g_slist_foreach (files, (GFunc) g_object_unref, NULL);
1401   g_slist_free (files);
1402 }
1403
1404 static void
1405 gtk_folder_constructed (GObject *object)
1406 {
1407   GtkFolderPrivate *priv;
1408   GError *error = NULL;
1409
1410   priv = GTK_FOLDER_GET_PRIVATE (object);
1411   priv->directory_monitor = g_file_monitor_directory (priv->folder_file, G_FILE_MONITOR_NONE, NULL, &error);
1412
1413   if (error)
1414     {
1415       g_warning ("%s", error->message);
1416       g_error_free (error);
1417     }
1418   else
1419     g_signal_connect (priv->directory_monitor, "changed",
1420                       G_CALLBACK (directory_monitor_changed), object);
1421
1422   g_file_enumerator_next_files_async (priv->enumerator,
1423                                       FILES_PER_QUERY,
1424                                       G_PRIORITY_DEFAULT,
1425                                       priv->cancellable,
1426                                       enumerator_files_callback,
1427                                       object);
1428   /* This isn't needed anymore */
1429   g_object_unref (priv->enumerator);
1430   priv->enumerator = NULL;
1431 }
1432
1433 static void
1434 gtk_folder_finalize (GObject *object)
1435 {
1436   GtkFolderPrivate *priv;
1437
1438   priv = GTK_FOLDER_GET_PRIVATE (object);
1439
1440   g_hash_table_unref (priv->children);
1441
1442   if (priv->folder_file)
1443     g_object_unref (priv->folder_file);
1444
1445   if (priv->directory_monitor)
1446     g_object_unref (priv->directory_monitor);
1447
1448   g_cancellable_cancel (priv->cancellable);
1449   g_object_unref (priv->cancellable);
1450   g_free (priv->attributes);
1451
1452   G_OBJECT_CLASS (_gtk_folder_parent_class)->finalize (object);
1453 }
1454
1455 static void
1456 _gtk_folder_class_init (GtkFolderClass *class)
1457 {
1458   GObjectClass *object_class = G_OBJECT_CLASS (class);
1459
1460   object_class->set_property = gtk_folder_set_property;
1461   object_class->get_property = gtk_folder_get_property;
1462   object_class->constructed = gtk_folder_constructed;
1463   object_class->finalize = gtk_folder_finalize;
1464
1465   g_object_class_install_property (object_class,
1466                                    PROP_FILE,
1467                                    g_param_spec_object ("file",
1468                                                         "File",
1469                                                         "GFile for the folder",
1470                                                         G_TYPE_FILE,
1471                                                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1472   g_object_class_install_property (object_class,
1473                                    PROP_ENUMERATOR,
1474                                    g_param_spec_object ("enumerator",
1475                                                         "Enumerator",
1476                                                         "GFileEnumerator to list files",
1477                                                         G_TYPE_FILE_ENUMERATOR,
1478                                                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1479   g_object_class_install_property (object_class,
1480                                    PROP_ATTRIBUTES,
1481                                    g_param_spec_string ("attributes",
1482                                                         "Attributes",
1483                                                         "Attributes to query for",
1484                                                         NULL,
1485                                                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1486   folder_signals[FILES_ADDED] =
1487     g_signal_new ("files-added",
1488                   G_TYPE_FROM_CLASS (object_class),
1489                   G_SIGNAL_RUN_LAST,
1490                   G_STRUCT_OFFSET (GtkFolderClass, files_added),
1491                   NULL, NULL,
1492                   g_cclosure_marshal_VOID__POINTER,
1493                   G_TYPE_NONE, 1, G_TYPE_POINTER);
1494   folder_signals[FILES_REMOVED] =
1495     g_signal_new ("files-removed",
1496                   G_TYPE_FROM_CLASS (object_class),
1497                   G_SIGNAL_RUN_LAST,
1498                   G_STRUCT_OFFSET (GtkFolderClass, files_removed),
1499                   NULL, NULL,
1500                   g_cclosure_marshal_VOID__POINTER,
1501                   G_TYPE_NONE, 1, G_TYPE_POINTER);
1502   folder_signals[FILES_CHANGED] =
1503     g_signal_new ("files-changed",
1504                   G_TYPE_FROM_CLASS (object_class),
1505                   G_SIGNAL_RUN_LAST,
1506                   G_STRUCT_OFFSET (GtkFolderClass, files_changed),
1507                   NULL, NULL,
1508                   g_cclosure_marshal_VOID__POINTER,
1509                   G_TYPE_NONE, 1, G_TYPE_POINTER);
1510   folder_signals[FINISHED_LOADING] =
1511     g_signal_new ("finished-loading",
1512                   G_TYPE_FROM_CLASS (object_class),
1513                   G_SIGNAL_RUN_LAST,
1514                   G_STRUCT_OFFSET (GtkFolderClass, finished_loading),
1515                   NULL, NULL,
1516                   g_cclosure_marshal_VOID__VOID,
1517                   G_TYPE_NONE, 0);
1518   folder_signals[DELETED] =
1519     g_signal_new ("deleted",
1520                   G_TYPE_FROM_CLASS (object_class),
1521                   G_SIGNAL_RUN_LAST,
1522                   G_STRUCT_OFFSET (GtkFolderClass, deleted),
1523                   NULL, NULL,
1524                   g_cclosure_marshal_VOID__VOID,
1525                   G_TYPE_NONE, 0);
1526
1527   g_type_class_add_private (object_class, sizeof (GtkFolderPrivate));
1528 }
1529
1530 static void
1531 _gtk_folder_init (GtkFolder *folder)
1532 {
1533   GtkFolderPrivate *priv;
1534
1535   priv = GTK_FOLDER_GET_PRIVATE (folder);
1536
1537   priv->children = g_hash_table_new_full (g_file_hash,
1538                                           (GEqualFunc) g_file_equal,
1539                                           (GDestroyNotify) g_object_unref,
1540                                           (GDestroyNotify) g_object_unref);
1541   priv->cancellable = g_cancellable_new ();
1542 }
1543
1544 static void
1545 gtk_folder_set_finished_loading (GtkFolder *folder,
1546                                  gboolean   finished_loading)
1547 {
1548   GtkFolderPrivate *priv;
1549
1550   priv = GTK_FOLDER_GET_PRIVATE (folder);
1551   priv->finished_loading = (finished_loading == TRUE);
1552
1553   gdk_threads_enter ();
1554   g_signal_emit (folder, folder_signals[FINISHED_LOADING], 0);
1555   gdk_threads_leave ();
1556 }
1557
1558 static void
1559 gtk_folder_add_file (GtkFolder *folder,
1560                      GFile     *file,
1561                      GFileInfo *info)
1562 {
1563   GtkFolderPrivate *priv;
1564
1565   priv = GTK_FOLDER_GET_PRIVATE (folder);
1566
1567   g_hash_table_insert (priv->children,
1568                        g_object_ref (file),
1569                        g_object_ref (info));
1570 }
1571
1572 GSList *
1573 _gtk_folder_list_children (GtkFolder *folder)
1574 {
1575   GtkFolderPrivate *priv;
1576   GList *files, *elem;
1577   GSList *children = NULL;
1578
1579   priv = GTK_FOLDER_GET_PRIVATE (folder);
1580   files = g_hash_table_get_keys (priv->children);
1581   children = NULL;
1582
1583   for (elem = files; elem; elem = elem->next)
1584     children = g_slist_prepend (children, g_object_ref (elem->data));
1585
1586   g_list_free (files);
1587
1588   return children;
1589 }
1590
1591 GFileInfo *
1592 _gtk_folder_get_info (GtkFolder  *folder,
1593                       GFile      *file)
1594 {
1595   GtkFolderPrivate *priv;
1596   GFileInfo *info;
1597
1598   priv = GTK_FOLDER_GET_PRIVATE (folder);
1599   info = g_hash_table_lookup (priv->children, file);
1600
1601   if (!info)
1602     return NULL;
1603
1604   return g_object_ref (info);
1605 }
1606
1607 gboolean
1608 _gtk_folder_is_finished_loading (GtkFolder *folder)
1609 {
1610   GtkFolderPrivate *priv;
1611
1612   priv = GTK_FOLDER_GET_PRIVATE (folder);
1613
1614   return priv->finished_loading;
1615 }
1616
1617 /* GtkFileSystemVolume public methods */
1618 gchar *
1619 _gtk_file_system_volume_get_display_name (GtkFileSystemVolume *volume)
1620 {
1621   DEBUG ("volume_get_display_name");
1622
1623   if (IS_ROOT_VOLUME (volume))
1624     return g_strdup (_(root_volume_token));
1625   if (G_IS_DRIVE (volume))
1626     return g_drive_get_name (G_DRIVE (volume));
1627   else if (G_IS_MOUNT (volume))
1628     return g_mount_get_name (G_MOUNT (volume));
1629   else if (G_IS_VOLUME (volume))
1630     return g_volume_get_name (G_VOLUME (volume));
1631
1632   return NULL;
1633 }
1634
1635 gboolean
1636 _gtk_file_system_volume_is_mounted (GtkFileSystemVolume *volume)
1637 {
1638   gboolean mounted;
1639
1640   DEBUG ("volume_is_mounted");
1641
1642   if (IS_ROOT_VOLUME (volume))
1643     return TRUE;
1644
1645   mounted = FALSE;
1646
1647   if (G_IS_MOUNT (volume))
1648     mounted = TRUE;
1649   else if (G_IS_VOLUME (volume))
1650     {
1651       GMount *mount;
1652
1653       mount = g_volume_get_mount (G_VOLUME (volume));
1654
1655       if (mount)
1656         {
1657           mounted = TRUE;
1658           g_object_unref (mount);
1659         }
1660     }
1661
1662   return mounted;
1663 }
1664
1665 GFile *
1666 _gtk_file_system_volume_get_root (GtkFileSystemVolume *volume)
1667 {
1668   GFile *file = NULL;
1669
1670   DEBUG ("volume_get_base");
1671
1672   if (IS_ROOT_VOLUME (volume))
1673     return g_file_new_for_uri ("file:///");
1674
1675   if (G_IS_MOUNT (volume))
1676     file = g_mount_get_root (G_MOUNT (volume));
1677   else if (G_IS_VOLUME (volume))
1678     {
1679       GMount *mount;
1680
1681       mount = g_volume_get_mount (G_VOLUME (volume));
1682
1683       if (mount)
1684         {
1685           file = g_mount_get_root (mount);
1686           g_object_unref (mount);
1687         }
1688     }
1689
1690   return file;
1691 }
1692
1693 static GdkPixbuf *
1694 get_pixbuf_from_gicon (GIcon      *icon,
1695                        GtkWidget  *widget,
1696                        gint        icon_size,
1697                        GError    **error)
1698 {
1699   GdkScreen *screen;
1700   GtkIconTheme *icon_theme;
1701   GtkIconInfo *icon_info;
1702   GdkPixbuf *pixbuf;
1703
1704   screen = gtk_widget_get_screen (GTK_WIDGET (widget));
1705   icon_theme = gtk_icon_theme_get_for_screen (screen);
1706
1707   icon_info = gtk_icon_theme_lookup_by_gicon (icon_theme,
1708                                               icon,
1709                                               icon_size,
1710                                               GTK_ICON_LOOKUP_USE_BUILTIN);
1711
1712   if (!icon_info)
1713     return NULL;
1714
1715   pixbuf = gtk_icon_info_load_icon (icon_info, error);
1716   gtk_icon_info_free (icon_info);
1717
1718   return pixbuf;
1719 }
1720
1721 GdkPixbuf *
1722 _gtk_file_system_volume_render_icon (GtkFileSystemVolume  *volume,
1723                                      GtkWidget            *widget,
1724                                      gint                  icon_size,
1725                                      GError              **error)
1726 {
1727   GIcon *icon = NULL;
1728   GdkPixbuf *pixbuf;
1729
1730   DEBUG ("volume_get_icon_name");
1731
1732   if (IS_ROOT_VOLUME (volume))
1733     icon = g_themed_icon_new ("drive-harddisk");
1734   else if (G_IS_DRIVE (volume))
1735     icon = g_drive_get_icon (G_DRIVE (volume));
1736   else if (G_IS_VOLUME (volume))
1737     icon = g_volume_get_icon (G_VOLUME (volume));
1738   else if (G_IS_MOUNT (volume))
1739     icon = g_mount_get_icon (G_MOUNT (volume));
1740
1741   if (!icon)
1742     return NULL;
1743
1744   pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, error);
1745
1746   g_object_unref (icon);
1747
1748   return pixbuf;
1749 }
1750
1751 void
1752 _gtk_file_system_volume_free (GtkFileSystemVolume *volume)
1753 {
1754   /* Root volume doesn't need to be freed */
1755   if (IS_ROOT_VOLUME (volume))
1756     return;
1757
1758   if (G_IS_MOUNT (volume)  ||
1759       G_IS_VOLUME (volume) ||
1760       G_IS_DRIVE (volume))
1761     g_object_unref (volume);
1762 }
1763
1764 /* GFileInfo helper functions */
1765 GdkPixbuf *
1766 _gtk_file_info_render_icon (GFileInfo *info,
1767                            GtkWidget *widget,
1768                            gint       icon_size)
1769 {
1770   GIcon *icon;
1771   GdkPixbuf *pixbuf = NULL;
1772   const gchar *thumbnail_path;
1773
1774   thumbnail_path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
1775
1776   if (thumbnail_path)
1777     pixbuf = gdk_pixbuf_new_from_file_at_size (thumbnail_path,
1778                                                icon_size, icon_size,
1779                                                NULL);
1780
1781   if (!pixbuf)
1782     {
1783       icon = g_file_info_get_icon (info);
1784
1785       if (icon)
1786         pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, NULL);
1787
1788       if (!pixbuf)
1789         {
1790            /* Use general fallback for all files without icon */
1791           icon = g_themed_icon_new ("text-x-generic");
1792           pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, NULL);
1793           g_object_unref (icon);
1794         }
1795     }
1796
1797   return pixbuf;
1798 }
1799
1800 gboolean
1801 _gtk_file_info_consider_as_directory (GFileInfo *info)
1802 {
1803   GFileType type = g_file_info_get_file_type (info);
1804   
1805   return (type == G_FILE_TYPE_DIRECTORY ||
1806           type == G_FILE_TYPE_MOUNTABLE);
1807 }
1808