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