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