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 library 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 library 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 Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18  *
19  * Authors: Carlos Garnacho <carlos@imendio.com>
20  */
21 
22 #include "config.h"
23 
24 #include "gtkfilesystem.h"
25 
26 #include <string.h>
27 #include <glib/gi18n-lib.h>
28 
29 #include "gtkfilechooser.h"
30 #include "gtkcssiconthemevalueprivate.h"
31 #include "gtkintl.h"
32 #include "gtkprivate.h"
33 #include "gtkstylecontextprivate.h"
34 
35 /* #define DEBUG_MODE */
36 #ifdef DEBUG_MODE
37 #define DEBUG(x) g_debug (x);
38 #else
39 #define DEBUG(x)
40 #endif
41 
42 #define FILES_PER_QUERY 100
43 
44 /* The pointers we return for a GtkFileSystemVolume are opaque tokens; they are
45  * really pointers to GDrive, GVolume or GMount objects.  We need an extra
46  * token for the fake “File System” volume.  So, we’ll return a pointer to
47  * this particular string.
48  */
49 static const gchar *root_volume_token = N_("File System");
50 #define IS_ROOT_VOLUME(volume) ((gpointer) (volume) == (gpointer) root_volume_token)
51 
52 enum {
53   PROP_0,
54   PROP_FILE,
55   PROP_ENUMERATOR,
56   PROP_ATTRIBUTES
57 };
58 
59 enum {
60   VOLUMES_CHANGED,
61   FS_LAST_SIGNAL
62 };
63 
64 enum {
65   FILES_ADDED,
66   FILES_REMOVED,
67   FILES_CHANGED,
68   FINISHED_LOADING,
69   DELETED,
70   FOLDER_LAST_SIGNAL
71 };
72 
73 static guint fs_signals [FS_LAST_SIGNAL] = { 0, };
74 
75 typedef struct AsyncFuncData AsyncFuncData;
76 
77 struct GtkFileSystemPrivate
78 {
79   GVolumeMonitor *volume_monitor;
80 
81   /* This list contains elements that can be
82    * of type GDrive, GVolume and GMount
83    */
84   GSList *volumes;
85 };
86 
87 struct AsyncFuncData
88 {
89   GtkFileSystem *file_system;
90   GFile *file;
91   GCancellable *cancellable;
92 
93   gpointer callback;
94   gpointer data;
95 };
96 
G_DEFINE_TYPE_WITH_PRIVATE(GtkFileSystem,_gtk_file_system,G_TYPE_OBJECT)97 G_DEFINE_TYPE_WITH_PRIVATE (GtkFileSystem, _gtk_file_system, G_TYPE_OBJECT)
98 
99 
100 /* GtkFileSystem methods */
101 static void
102 volumes_changed (GVolumeMonitor *volume_monitor,
103 		 gpointer        volume,
104 		 gpointer        user_data)
105 {
106   GtkFileSystem *file_system;
107 
108   gdk_threads_enter ();
109 
110   file_system = GTK_FILE_SYSTEM (user_data);
111   g_signal_emit (file_system, fs_signals[VOLUMES_CHANGED], 0, volume);
112   gdk_threads_leave ();
113 }
114 
115 static void
gtk_file_system_dispose(GObject * object)116 gtk_file_system_dispose (GObject *object)
117 {
118   GtkFileSystem *file_system = GTK_FILE_SYSTEM (object);
119   GtkFileSystemPrivate *priv = file_system->priv;
120 
121   DEBUG ("dispose");
122 
123   if (priv->volumes)
124     {
125       g_slist_free_full (priv->volumes, g_object_unref);
126       priv->volumes = NULL;
127     }
128 
129   if (priv->volume_monitor)
130     {
131       g_signal_handlers_disconnect_by_func (priv->volume_monitor, volumes_changed, object);
132       g_object_unref (priv->volume_monitor);
133       priv->volume_monitor = NULL;
134     }
135 
136   G_OBJECT_CLASS (_gtk_file_system_parent_class)->dispose (object);
137 }
138 
139 static void
_gtk_file_system_class_init(GtkFileSystemClass * class)140 _gtk_file_system_class_init (GtkFileSystemClass *class)
141 {
142   GObjectClass *object_class = G_OBJECT_CLASS (class);
143 
144   object_class->dispose = gtk_file_system_dispose;
145 
146   fs_signals[VOLUMES_CHANGED] =
147     g_signal_new (I_("volumes-changed"),
148 		  G_TYPE_FROM_CLASS (object_class),
149 		  G_SIGNAL_RUN_LAST,
150 		  G_STRUCT_OFFSET (GtkFileSystemClass, volumes_changed),
151 		  NULL, NULL,
152 		  NULL,
153 		  G_TYPE_NONE, 0);
154 }
155 
156 static gboolean
mount_referenced_by_volume_activation_root(GList * volumes,GMount * mount)157 mount_referenced_by_volume_activation_root (GList *volumes, GMount *mount)
158 {
159   GList *l;
160   GFile *mount_root;
161   gboolean ret;
162 
163   ret = FALSE;
164 
165   mount_root = g_mount_get_root (mount);
166 
167   for (l = volumes; l != NULL; l = l->next)
168     {
169       GVolume *volume = G_VOLUME (l->data);
170       GFile *volume_activation_root;
171 
172       volume_activation_root = g_volume_get_activation_root (volume);
173       if (volume_activation_root != NULL)
174         {
175           if (g_file_has_prefix (volume_activation_root, mount_root))
176             {
177               ret = TRUE;
178               g_object_unref (volume_activation_root);
179               break;
180             }
181           g_object_unref (volume_activation_root);
182         }
183     }
184 
185   g_object_unref (mount_root);
186   return ret;
187 }
188 
189 static void
get_volumes_list(GtkFileSystem * file_system)190 get_volumes_list (GtkFileSystem *file_system)
191 {
192   GtkFileSystemPrivate *priv = file_system->priv;
193   GList *l, *ll;
194   GList *drives;
195   GList *volumes;
196   GList *mounts;
197   GDrive *drive;
198   GVolume *volume;
199   GMount *mount;
200 
201   if (priv->volumes)
202     {
203       g_slist_free_full (priv->volumes, g_object_unref);
204       priv->volumes = NULL;
205     }
206 
207   /* first go through all connected drives */
208   drives = g_volume_monitor_get_connected_drives (priv->volume_monitor);
209 
210   for (l = drives; l != NULL; l = l->next)
211     {
212       drive = l->data;
213       volumes = g_drive_get_volumes (drive);
214 
215       if (volumes)
216         {
217           for (ll = volumes; ll != NULL; ll = ll->next)
218             {
219               volume = ll->data;
220               mount = g_volume_get_mount (volume);
221 
222               if (mount)
223                 {
224                   /* Show mounted volume */
225                   priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
226                   g_object_unref (mount);
227                 }
228               else
229                 {
230                   /* Do show the unmounted volumes in the sidebar;
231                    * this is so the user can mount it (in case automounting
232                    * is off).
233                    *
234                    * Also, even if automounting is enabled, this gives a visual
235                    * cue that the user should remember to yank out the media if
236                    * he just unmounted it.
237                    */
238                   priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (volume));
239                 }
240 
241 	      g_object_unref (volume);
242             }
243 
244            g_list_free (volumes);
245         }
246       else if (g_drive_is_media_removable (drive) && !g_drive_is_media_check_automatic (drive))
247 	{
248 	  /* If the drive has no mountable volumes and we cannot detect media change.. we
249 	   * display the drive in the sidebar so the user can manually poll the drive by
250 	   * right clicking and selecting "Rescan..."
251 	   *
252 	   * This is mainly for drives like floppies where media detection doesn't
253 	   * work.. but it's also for human beings who like to turn off media detection
254 	   * in the OS to save battery juice.
255 	   */
256 
257 	  priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (drive));
258 	}
259 
260       g_object_unref (drive);
261     }
262 
263   g_list_free (drives);
264 
265   /* add all volumes that is not associated with a drive */
266   volumes = g_volume_monitor_get_volumes (priv->volume_monitor);
267 
268   for (l = volumes; l != NULL; l = l->next)
269     {
270       volume = l->data;
271       drive = g_volume_get_drive (volume);
272 
273       if (drive)
274         {
275           g_object_unref (drive);
276           continue;
277         }
278 
279       mount = g_volume_get_mount (volume);
280 
281       if (mount)
282         {
283           /* show this mount */
284           priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
285           g_object_unref (mount);
286         }
287       else
288         {
289           /* see comment above in why we add an icon for a volume */
290           priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (volume));
291         }
292 
293       g_object_unref (volume);
294     }
295 
296   /* add mounts that has no volume (/etc/mtab mounts, ftp, sftp,...) */
297   mounts = g_volume_monitor_get_mounts (priv->volume_monitor);
298 
299   for (l = mounts; l != NULL; l = l->next)
300     {
301       mount = l->data;
302       volume = g_mount_get_volume (mount);
303 
304       if (volume)
305         {
306           g_object_unref (volume);
307           continue;
308         }
309 
310       /* if there's exists one or more volumes with an activation root inside the mount,
311        * don't display the mount
312        */
313       if (mount_referenced_by_volume_activation_root (volumes, mount))
314         {
315           g_object_unref (mount);
316           continue;
317         }
318 
319       /* show this mount */
320       priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
321       g_object_unref (mount);
322     }
323 
324   g_list_free (volumes);
325 
326   g_list_free (mounts);
327 }
328 
329 static void
_gtk_file_system_init(GtkFileSystem * file_system)330 _gtk_file_system_init (GtkFileSystem *file_system)
331 {
332   GtkFileSystemPrivate *priv;
333 
334   DEBUG ("init");
335 
336   file_system->priv = priv = _gtk_file_system_get_instance_private (file_system);
337 
338   /* Volumes */
339   priv->volume_monitor = g_volume_monitor_get ();
340 
341   g_signal_connect (priv->volume_monitor, "mount-added",
342 		    G_CALLBACK (volumes_changed), file_system);
343   g_signal_connect (priv->volume_monitor, "mount-removed",
344 		    G_CALLBACK (volumes_changed), file_system);
345   g_signal_connect (priv->volume_monitor, "mount-changed",
346 		    G_CALLBACK (volumes_changed), file_system);
347   g_signal_connect (priv->volume_monitor, "volume-added",
348 		    G_CALLBACK (volumes_changed), file_system);
349   g_signal_connect (priv->volume_monitor, "volume-removed",
350 		    G_CALLBACK (volumes_changed), file_system);
351   g_signal_connect (priv->volume_monitor, "volume-changed",
352 		    G_CALLBACK (volumes_changed), file_system);
353   g_signal_connect (priv->volume_monitor, "drive-connected",
354 		    G_CALLBACK (volumes_changed), file_system);
355   g_signal_connect (priv->volume_monitor, "drive-disconnected",
356 		    G_CALLBACK (volumes_changed), file_system);
357   g_signal_connect (priv->volume_monitor, "drive-changed",
358 		    G_CALLBACK (volumes_changed), file_system);
359 }
360 
361 /* GtkFileSystem public methods */
362 GtkFileSystem *
_gtk_file_system_new(void)363 _gtk_file_system_new (void)
364 {
365   return g_object_new (GTK_TYPE_FILE_SYSTEM, NULL);
366 }
367 
368 GSList *
_gtk_file_system_list_volumes(GtkFileSystem * file_system)369 _gtk_file_system_list_volumes (GtkFileSystem *file_system)
370 {
371   GtkFileSystemPrivate *priv = file_system->priv;
372   GSList *list;
373 
374   DEBUG ("list_volumes");
375 
376   get_volumes_list (file_system);
377 
378   list = g_slist_copy (priv->volumes);
379 
380 #ifndef G_OS_WIN32
381   /* Prepend root volume */
382   list = g_slist_prepend (list, (gpointer) root_volume_token);
383 #endif
384 
385   return list;
386 }
387 
388 static void
free_async_data(AsyncFuncData * async_data)389 free_async_data (AsyncFuncData *async_data)
390 {
391   g_object_unref (async_data->file_system);
392   g_object_unref (async_data->file);
393   g_object_unref (async_data->cancellable);
394 
395   g_free (async_data);
396 }
397 
398 static void
query_info_callback(GObject * source_object,GAsyncResult * result,gpointer user_data)399 query_info_callback (GObject      *source_object,
400 		     GAsyncResult *result,
401 		     gpointer      user_data)
402 {
403   AsyncFuncData *async_data;
404   GError *error = NULL;
405   GFileInfo *file_info;
406   GFile *file;
407 
408   DEBUG ("query_info_callback");
409 
410   file = G_FILE (source_object);
411   async_data = (AsyncFuncData *) user_data;
412   file_info = g_file_query_info_finish (file, result, &error);
413 
414   if (async_data->callback)
415     {
416       gdk_threads_enter ();
417       ((GtkFileSystemGetInfoCallback) async_data->callback) (async_data->cancellable,
418 							     file_info, error, async_data->data);
419       gdk_threads_leave ();
420     }
421 
422   if (file_info)
423     g_object_unref (file_info);
424 
425   if (error)
426     g_error_free (error);
427 
428   free_async_data (async_data);
429 }
430 
431 GCancellable *
_gtk_file_system_get_info(GtkFileSystem * file_system,GFile * file,const gchar * attributes,GtkFileSystemGetInfoCallback callback,gpointer data)432 _gtk_file_system_get_info (GtkFileSystem                *file_system,
433 			   GFile                        *file,
434 			   const gchar                  *attributes,
435 			   GtkFileSystemGetInfoCallback  callback,
436 			   gpointer                      data)
437 {
438   GCancellable *cancellable;
439   AsyncFuncData *async_data;
440 
441   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
442   g_return_val_if_fail (G_IS_FILE (file), NULL);
443 
444   cancellable = g_cancellable_new ();
445 
446   async_data = g_new0 (AsyncFuncData, 1);
447   async_data->file_system = g_object_ref (file_system);
448   async_data->file = g_object_ref (file);
449   async_data->cancellable = g_object_ref (cancellable);
450 
451   async_data->callback = callback;
452   async_data->data = data;
453 
454   g_file_query_info_async (file,
455 			   attributes,
456 			   G_FILE_QUERY_INFO_NONE,
457 			   G_PRIORITY_DEFAULT,
458 			   cancellable,
459 			   query_info_callback,
460 			   async_data);
461 
462   return cancellable;
463 }
464 
465 static void
drive_poll_for_media_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)466 drive_poll_for_media_cb (GObject      *source_object,
467                          GAsyncResult *result,
468                          gpointer      user_data)
469 {
470   AsyncFuncData *async_data;
471   GError *error = NULL;
472 
473   g_drive_poll_for_media_finish (G_DRIVE (source_object), result, &error);
474   async_data = (AsyncFuncData *) user_data;
475 
476   gdk_threads_enter ();
477   ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable,
478 							     (GtkFileSystemVolume *) source_object,
479 							     error, async_data->data);
480   gdk_threads_leave ();
481 
482   if (error)
483     g_error_free (error);
484 }
485 
486 static void
volume_mount_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)487 volume_mount_cb (GObject      *source_object,
488 		 GAsyncResult *result,
489 		 gpointer      user_data)
490 {
491   AsyncFuncData *async_data;
492   GError *error = NULL;
493 
494   g_volume_mount_finish (G_VOLUME (source_object), result, &error);
495   async_data = (AsyncFuncData *) user_data;
496 
497   gdk_threads_enter ();
498   ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable,
499 							     (GtkFileSystemVolume *) source_object,
500 							     error, async_data->data);
501   gdk_threads_leave ();
502 
503   if (error)
504     g_error_free (error);
505 }
506 
507 GCancellable *
_gtk_file_system_mount_volume(GtkFileSystem * file_system,GtkFileSystemVolume * volume,GMountOperation * mount_operation,GtkFileSystemVolumeMountCallback callback,gpointer data)508 _gtk_file_system_mount_volume (GtkFileSystem                    *file_system,
509 			       GtkFileSystemVolume              *volume,
510 			       GMountOperation                  *mount_operation,
511 			       GtkFileSystemVolumeMountCallback  callback,
512 			       gpointer                          data)
513 {
514   GCancellable *cancellable;
515   AsyncFuncData *async_data;
516   gboolean handled = FALSE;
517 
518   DEBUG ("volume_mount");
519 
520   cancellable = g_cancellable_new ();
521 
522   async_data = g_new0 (AsyncFuncData, 1);
523   async_data->file_system = g_object_ref (file_system);
524   async_data->cancellable = g_object_ref (cancellable);
525 
526   async_data->callback = callback;
527   async_data->data = data;
528 
529   if (G_IS_DRIVE (volume))
530     {
531       /* this path happens for drives that are not polled by the OS and where the last media
532        * check indicated that no media was available. So the thing to do here is to
533        * invoke poll_for_media() on the drive
534        */
535       g_drive_poll_for_media (G_DRIVE (volume), cancellable, drive_poll_for_media_cb, async_data);
536       handled = TRUE;
537     }
538   else if (G_IS_VOLUME (volume))
539     {
540       g_volume_mount (G_VOLUME (volume), G_MOUNT_MOUNT_NONE, mount_operation, cancellable, volume_mount_cb, async_data);
541       handled = TRUE;
542     }
543 
544   if (!handled)
545     free_async_data (async_data);
546 
547   return cancellable;
548 }
549 
550 static void
enclosing_volume_mount_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)551 enclosing_volume_mount_cb (GObject      *source_object,
552 			   GAsyncResult *result,
553 			   gpointer      user_data)
554 {
555   GtkFileSystemVolume *volume;
556   AsyncFuncData *async_data;
557   GError *error = NULL;
558 
559   async_data = (AsyncFuncData *) user_data;
560   g_file_mount_enclosing_volume_finish (G_FILE (source_object), result, &error);
561   volume = _gtk_file_system_get_volume_for_file (async_data->file_system, G_FILE (source_object));
562 
563   /* Silently drop G_IO_ERROR_ALREADY_MOUNTED error for gvfs backends without visible mounts. */
564   /* Better than doing query_info with additional I/O every time. */
565   if (error && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_ALREADY_MOUNTED))
566     g_clear_error (&error);
567 
568   gdk_threads_enter ();
569   ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable, volume,
570 							     error, async_data->data);
571   gdk_threads_leave ();
572 
573   if (error)
574     g_error_free (error);
575 
576   _gtk_file_system_volume_unref (volume);
577 }
578 
579 GCancellable *
_gtk_file_system_mount_enclosing_volume(GtkFileSystem * file_system,GFile * file,GMountOperation * mount_operation,GtkFileSystemVolumeMountCallback callback,gpointer data)580 _gtk_file_system_mount_enclosing_volume (GtkFileSystem                     *file_system,
581 					 GFile                             *file,
582 					 GMountOperation                   *mount_operation,
583 					 GtkFileSystemVolumeMountCallback   callback,
584 					 gpointer                           data)
585 {
586   GCancellable *cancellable;
587   AsyncFuncData *async_data;
588 
589   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
590   g_return_val_if_fail (G_IS_FILE (file), NULL);
591 
592   DEBUG ("mount_enclosing_volume");
593 
594   cancellable = g_cancellable_new ();
595 
596   async_data = g_new0 (AsyncFuncData, 1);
597   async_data->file_system = g_object_ref (file_system);
598   async_data->file = g_object_ref (file);
599   async_data->cancellable = g_object_ref (cancellable);
600 
601   async_data->callback = callback;
602   async_data->data = data;
603 
604   g_file_mount_enclosing_volume (file,
605 				 G_MOUNT_MOUNT_NONE,
606 				 mount_operation,
607 				 cancellable,
608 				 enclosing_volume_mount_cb,
609 				 async_data);
610   return cancellable;
611 }
612 
613 GtkFileSystemVolume *
_gtk_file_system_get_volume_for_file(GtkFileSystem * file_system,GFile * file)614 _gtk_file_system_get_volume_for_file (GtkFileSystem *file_system,
615 				      GFile         *file)
616 {
617   GMount *mount;
618 
619   DEBUG ("get_volume_for_file");
620 
621   mount = g_file_find_enclosing_mount (file, NULL, NULL);
622 
623   if (!mount && g_file_is_native (file))
624     return (GtkFileSystemVolume *) root_volume_token;
625 
626   return (GtkFileSystemVolume *) mount;
627 }
628 
629 /* GtkFileSystemVolume public methods */
630 gchar *
_gtk_file_system_volume_get_display_name(GtkFileSystemVolume * volume)631 _gtk_file_system_volume_get_display_name (GtkFileSystemVolume *volume)
632 {
633   DEBUG ("volume_get_display_name");
634 
635   if (IS_ROOT_VOLUME (volume))
636     return g_strdup (_(root_volume_token));
637   if (G_IS_DRIVE (volume))
638     return g_drive_get_name (G_DRIVE (volume));
639   else if (G_IS_MOUNT (volume))
640     return g_mount_get_name (G_MOUNT (volume));
641   else if (G_IS_VOLUME (volume))
642     return g_volume_get_name (G_VOLUME (volume));
643 
644   return NULL;
645 }
646 
647 gboolean
_gtk_file_system_volume_is_mounted(GtkFileSystemVolume * volume)648 _gtk_file_system_volume_is_mounted (GtkFileSystemVolume *volume)
649 {
650   gboolean mounted;
651 
652   DEBUG ("volume_is_mounted");
653 
654   if (IS_ROOT_VOLUME (volume))
655     return TRUE;
656 
657   mounted = FALSE;
658 
659   if (G_IS_MOUNT (volume))
660     mounted = TRUE;
661   else if (G_IS_VOLUME (volume))
662     {
663       GMount *mount;
664 
665       mount = g_volume_get_mount (G_VOLUME (volume));
666 
667       if (mount)
668         {
669           mounted = TRUE;
670           g_object_unref (mount);
671         }
672     }
673 
674   return mounted;
675 }
676 
677 GFile *
_gtk_file_system_volume_get_root(GtkFileSystemVolume * volume)678 _gtk_file_system_volume_get_root (GtkFileSystemVolume *volume)
679 {
680   GFile *file = NULL;
681 
682   DEBUG ("volume_get_base");
683 
684   if (IS_ROOT_VOLUME (volume))
685     return g_file_new_for_uri ("file:///");
686 
687   if (G_IS_MOUNT (volume))
688     file = g_mount_get_root (G_MOUNT (volume));
689   else if (G_IS_VOLUME (volume))
690     {
691       GMount *mount;
692 
693       mount = g_volume_get_mount (G_VOLUME (volume));
694 
695       if (mount)
696 	{
697 	  file = g_mount_get_root (mount);
698 	  g_object_unref (mount);
699 	}
700     }
701 
702   return file;
703 }
704 
705 static cairo_surface_t *
get_surface_from_gicon(GIcon * icon,GtkWidget * widget,gint icon_size,GError ** error)706 get_surface_from_gicon (GIcon      *icon,
707 			GtkWidget  *widget,
708 			gint        icon_size,
709 			GError    **error)
710 {
711   GtkStyleContext *context;
712   GtkIconTheme *icon_theme;
713   GtkIconInfo *icon_info;
714   GdkPixbuf *pixbuf;
715   cairo_surface_t *surface;
716 
717   context = gtk_widget_get_style_context (widget);
718   icon_theme = gtk_css_icon_theme_value_get_icon_theme
719     (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ICON_THEME));
720 
721   icon_info = gtk_icon_theme_lookup_by_gicon_for_scale (icon_theme,
722                                                         icon,
723                                                         icon_size,
724                                                         gtk_widget_get_scale_factor (widget),
725                                                         GTK_ICON_LOOKUP_USE_BUILTIN);
726 
727   if (!icon_info)
728     return NULL;
729 
730   pixbuf = gtk_icon_info_load_symbolic_for_context (icon_info,
731                                                     context,
732                                                     NULL,
733                                                     error);
734 
735   g_object_unref (icon_info);
736 
737   if (pixbuf == NULL)
738     return NULL;
739 
740   surface = gdk_cairo_surface_create_from_pixbuf (pixbuf,
741                                                   gtk_widget_get_scale_factor (widget),
742 					          gtk_widget_get_window (widget));
743   g_object_unref (pixbuf);
744 
745   return surface;
746 }
747 
748 cairo_surface_t *
_gtk_file_system_volume_render_icon(GtkFileSystemVolume * volume,GtkWidget * widget,gint icon_size,GError ** error)749 _gtk_file_system_volume_render_icon (GtkFileSystemVolume  *volume,
750 				     GtkWidget            *widget,
751 				     gint                  icon_size,
752 				     GError              **error)
753 {
754   GIcon *icon = NULL;
755   cairo_surface_t *surface;
756 
757   if (IS_ROOT_VOLUME (volume))
758     icon = g_themed_icon_new ("drive-harddisk");
759   else if (G_IS_DRIVE (volume))
760     icon = g_drive_get_icon (G_DRIVE (volume));
761   else if (G_IS_VOLUME (volume))
762     icon = g_volume_get_icon (G_VOLUME (volume));
763   else if (G_IS_MOUNT (volume))
764     icon = g_mount_get_icon (G_MOUNT (volume));
765 
766   if (!icon)
767     return NULL;
768 
769   surface = get_surface_from_gicon (icon, widget, icon_size, error);
770 
771   g_object_unref (icon);
772 
773   return surface;
774 }
775 
776 GIcon *
_gtk_file_system_volume_get_symbolic_icon(GtkFileSystemVolume * volume)777 _gtk_file_system_volume_get_symbolic_icon (GtkFileSystemVolume *volume)
778 {
779   if (IS_ROOT_VOLUME (volume))
780     return g_themed_icon_new ("drive-harddisk-symbolic");
781   else if (G_IS_DRIVE (volume))
782     return g_drive_get_symbolic_icon (G_DRIVE (volume));
783   else if (G_IS_VOLUME (volume))
784     return g_volume_get_symbolic_icon (G_VOLUME (volume));
785   else if (G_IS_MOUNT (volume))
786     return g_mount_get_symbolic_icon (G_MOUNT (volume));
787   else
788     return NULL;
789 }
790 
791 GtkFileSystemVolume *
_gtk_file_system_volume_ref(GtkFileSystemVolume * volume)792 _gtk_file_system_volume_ref (GtkFileSystemVolume *volume)
793 {
794   if (IS_ROOT_VOLUME (volume))
795     return volume;
796 
797   if (G_IS_MOUNT (volume)  ||
798       G_IS_VOLUME (volume) ||
799       G_IS_DRIVE (volume))
800     g_object_ref (volume);
801 
802   return volume;
803 }
804 
805 void
_gtk_file_system_volume_unref(GtkFileSystemVolume * volume)806 _gtk_file_system_volume_unref (GtkFileSystemVolume *volume)
807 {
808   /* Root volume doesn't need to be freed */
809   if (IS_ROOT_VOLUME (volume))
810     return;
811 
812   if (G_IS_MOUNT (volume)  ||
813       G_IS_VOLUME (volume) ||
814       G_IS_DRIVE (volume))
815     g_object_unref (volume);
816 }
817 
818 /* GFileInfo helper functions */
819 static cairo_surface_t *
_gtk_file_info_render_icon_internal(GFileInfo * info,GtkWidget * widget,gint icon_size,gboolean symbolic)820 _gtk_file_info_render_icon_internal (GFileInfo *info,
821 			             GtkWidget *widget,
822 			             gint       icon_size,
823                                      gboolean   symbolic)
824 {
825   GIcon *icon;
826   GdkPixbuf *pixbuf;
827   const gchar *thumbnail_path;
828   cairo_surface_t *surface = NULL;
829   int scale;
830 
831   thumbnail_path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
832 
833   if (thumbnail_path)
834     {
835       scale = gtk_widget_get_scale_factor (widget);
836       pixbuf = gdk_pixbuf_new_from_file_at_size (thumbnail_path,
837 						 icon_size*scale, icon_size*scale,
838 						 NULL);
839 
840       if (pixbuf != NULL)
841         {
842           surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale,
843                                                           gtk_widget_get_window (widget));
844           g_object_unref (pixbuf);
845         }
846     }
847 
848   if (!surface)
849     {
850       if (symbolic)
851         icon = g_file_info_get_symbolic_icon (info);
852       else
853         icon = g_file_info_get_icon (info);
854 
855       if (icon)
856 	surface = get_surface_from_gicon (icon, widget, icon_size, NULL);
857 
858       if (!surface)
859 	{
860 	   /* Use general fallback for all files without icon */
861           if (symbolic)
862 	    icon = g_themed_icon_new ("text-x-generic-symbolic");
863           else
864 	    icon = g_themed_icon_new ("text-x-generic");
865 	  surface = get_surface_from_gicon (icon, widget, icon_size, NULL);
866 	  g_object_unref (icon);
867 	}
868     }
869 
870   return surface;
871 }
872 
873 cairo_surface_t *
_gtk_file_info_render_icon(GFileInfo * info,GtkWidget * widget,gint icon_size)874 _gtk_file_info_render_icon (GFileInfo *info,
875 			    GtkWidget *widget,
876 			    gint       icon_size)
877 {
878   return _gtk_file_info_render_icon_internal (info, widget, icon_size, FALSE);
879 }
880 
881 gboolean
_gtk_file_info_consider_as_directory(GFileInfo * info)882 _gtk_file_info_consider_as_directory (GFileInfo *info)
883 {
884   GFileType type = g_file_info_get_file_type (info);
885 
886   return (type == G_FILE_TYPE_DIRECTORY ||
887           type == G_FILE_TYPE_MOUNTABLE ||
888           type == G_FILE_TYPE_SHORTCUT);
889 }
890 
891 gboolean
_gtk_file_has_native_path(GFile * file)892 _gtk_file_has_native_path (GFile *file)
893 {
894   char *local_file_path;
895   gboolean has_native_path;
896 
897   /* Don't use g_file_is_native(), as we want to support FUSE paths if available */
898   local_file_path = g_file_get_path (file);
899   has_native_path = (local_file_path != NULL);
900   g_free (local_file_path);
901 
902   return has_native_path;
903 }
904 
905 gboolean
_gtk_file_consider_as_remote(GFile * file)906 _gtk_file_consider_as_remote (GFile *file)
907 {
908   GFileInfo *info;
909   gboolean is_remote;
910 
911   info = g_file_query_filesystem_info (file, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, NULL, NULL);
912   if (info)
913     {
914       is_remote = g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE);
915 
916       g_object_unref (info);
917     }
918   else
919     is_remote = FALSE;
920 
921   return is_remote;
922 }
923