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