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