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