 ae42f286bb
			
		
	
	ae42f286bb
	
	
	
		
			
			This lets us do proper completion in GtkFileChooserEntry even when no base folder has been set. Completion for relative paths won't work, as usual, as expected.
		
			
				
	
	
		
			1868 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1868 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* GTK - The GIMP Toolkit
 | |
|  * gtkfilesystem.c: Filesystem abstraction functions.
 | |
|  * Copyright (C) 2003, Red Hat, Inc.
 | |
|  * Copyright (C) 2007-2008 Carlos Garnacho
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU Lesser General Public License as
 | |
|  * published by the Free Software Foundation; either version 2 of the
 | |
|  * License, or (at your option) any later version.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|  * GNU General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with this program; if not, write to the Free Software
 | |
|  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 | |
|  *
 | |
|  * Authors: Carlos Garnacho <carlos@imendio.com>
 | |
|  */
 | |
| 
 | |
| #include "config.h"
 | |
| 
 | |
| #include <string.h>
 | |
| 
 | |
| #include <glib/gi18n-lib.h>
 | |
| 
 | |
| #include "gtkfilechooser.h"
 | |
| #include "gtkfilesystem.h"
 | |
| #include "gtkicontheme.h"
 | |
| #include "gtkprivate.h"
 | |
| 
 | |
| /* #define DEBUG_MODE */
 | |
| #ifdef DEBUG_MODE
 | |
| #define DEBUG(x) g_debug (x);
 | |
| #else
 | |
| #define DEBUG(x)
 | |
| #endif
 | |
| 
 | |
| #define FILES_PER_QUERY 100
 | |
| 
 | |
| /* The pointers we return for a GtkFileSystemVolume are opaque tokens; they are
 | |
|  * really pointers to GDrive, GVolume or GMount objects.  We need an extra
 | |
|  * token for the fake "File System" volume.  So, we'll return a pointer to
 | |
|  * this particular string.
 | |
|  */
 | |
| static const gchar *root_volume_token = N_("File System");
 | |
| #define IS_ROOT_VOLUME(volume) ((gpointer) (volume) == (gpointer) root_volume_token)
 | |
| 
 | |
| enum {
 | |
|   PROP_0,
 | |
|   PROP_FILE,
 | |
|   PROP_ENUMERATOR,
 | |
|   PROP_ATTRIBUTES
 | |
| };
 | |
| 
 | |
| enum {
 | |
|   BOOKMARKS_CHANGED,
 | |
|   VOLUMES_CHANGED,
 | |
|   FS_LAST_SIGNAL
 | |
| };
 | |
| 
 | |
| enum {
 | |
|   FILES_ADDED,
 | |
|   FILES_REMOVED,
 | |
|   FILES_CHANGED,
 | |
|   FINISHED_LOADING,
 | |
|   DELETED,
 | |
|   FOLDER_LAST_SIGNAL
 | |
| };
 | |
| 
 | |
| static guint fs_signals [FS_LAST_SIGNAL] = { 0, };
 | |
| static guint folder_signals [FOLDER_LAST_SIGNAL] = { 0, };
 | |
| 
 | |
| typedef struct AsyncFuncData AsyncFuncData;
 | |
| 
 | |
| struct GtkFileSystemPrivate
 | |
| {
 | |
|   GVolumeMonitor *volume_monitor;
 | |
| 
 | |
|   /* This list contains elements that can be
 | |
|    * of type GDrive, GVolume and GMount
 | |
|    */
 | |
|   GSList *volumes;
 | |
| 
 | |
|   /* This list contains GtkFileSystemBookmark structs */
 | |
|   GSList *bookmarks;
 | |
| 
 | |
|   GFileMonitor *bookmarks_monitor;
 | |
| };
 | |
| 
 | |
| struct GtkFolderPrivate
 | |
| {
 | |
|   GFile *folder_file;
 | |
|   GHashTable *children;
 | |
|   GFileMonitor *directory_monitor;
 | |
|   GFileEnumerator *enumerator;
 | |
|   GCancellable *cancellable;
 | |
|   gchar *attributes;
 | |
| 
 | |
|   guint finished_loading : 1;
 | |
| };
 | |
| 
 | |
| struct AsyncFuncData
 | |
| {
 | |
|   GtkFileSystem *file_system;
 | |
|   GFile *file;
 | |
|   GtkFolder *folder;
 | |
|   GCancellable *cancellable;
 | |
|   gchar *attributes;
 | |
| 
 | |
|   gpointer callback;
 | |
|   gpointer data;
 | |
| };
 | |
| 
 | |
| struct GtkFileSystemBookmark
 | |
| {
 | |
|   GFile *file;
 | |
|   gchar *label;
 | |
| };
 | |
| 
 | |
| G_DEFINE_TYPE (GtkFileSystem, _gtk_file_system, G_TYPE_OBJECT)
 | |
| 
 | |
| G_DEFINE_TYPE (GtkFolder, _gtk_folder, G_TYPE_OBJECT)
 | |
| 
 | |
| 
 | |
| static void gtk_folder_set_finished_loading (GtkFolder *folder,
 | |
| 					     gboolean   finished_loading);
 | |
| static void gtk_folder_add_file             (GtkFolder *folder,
 | |
| 					     GFile     *file,
 | |
| 					     GFileInfo *info);
 | |
| 
 | |
| 
 | |
| /* GtkFileSystemBookmark methods */
 | |
| void
 | |
| _gtk_file_system_bookmark_free (GtkFileSystemBookmark *bookmark)
 | |
| {
 | |
|   g_object_unref (bookmark->file);
 | |
|   g_free (bookmark->label);
 | |
|   g_slice_free (GtkFileSystemBookmark, bookmark);
 | |
| }
 | |
| 
 | |
| /* GtkFileSystem methods */
 | |
| static void
 | |
| volumes_changed (GVolumeMonitor *volume_monitor,
 | |
| 		 gpointer        volume,
 | |
| 		 gpointer        user_data)
 | |
| {
 | |
|   GtkFileSystem *file_system;
 | |
| 
 | |
|   gdk_threads_enter ();
 | |
| 
 | |
|   file_system = GTK_FILE_SYSTEM (user_data);
 | |
|   g_signal_emit (file_system, fs_signals[VOLUMES_CHANGED], 0, volume);
 | |
|   gdk_threads_leave ();
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_file_system_dispose (GObject *object)
 | |
| {
 | |
|   GtkFileSystem *file_system = GTK_FILE_SYSTEM (object);
 | |
|   GtkFileSystemPrivate *priv = file_system->priv;
 | |
| 
 | |
|   DEBUG ("dispose");
 | |
| 
 | |
|   if (priv->volumes)
 | |
|     {
 | |
|       g_slist_foreach (priv->volumes, (GFunc) g_object_unref, NULL);
 | |
|       g_slist_free (priv->volumes);
 | |
|       priv->volumes = NULL;
 | |
|     }
 | |
| 
 | |
|   if (priv->volume_monitor)
 | |
|     {
 | |
|       g_signal_handlers_disconnect_by_func (priv->volume_monitor, volumes_changed, object);
 | |
|       g_object_unref (priv->volume_monitor);
 | |
|       priv->volume_monitor = NULL;
 | |
|     }
 | |
| 
 | |
|   G_OBJECT_CLASS (_gtk_file_system_parent_class)->dispose (object);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_file_system_finalize (GObject *object)
 | |
| {
 | |
|   GtkFileSystem *file_system = GTK_FILE_SYSTEM (object);
 | |
|   GtkFileSystemPrivate *priv = file_system->priv;
 | |
| 
 | |
|   DEBUG ("finalize");
 | |
| 
 | |
|   if (priv->bookmarks_monitor)
 | |
|     g_object_unref (priv->bookmarks_monitor);
 | |
| 
 | |
|   if (priv->bookmarks)
 | |
|     {
 | |
|       g_slist_foreach (priv->bookmarks, (GFunc) _gtk_file_system_bookmark_free, NULL);
 | |
|       g_slist_free (priv->bookmarks);
 | |
|     }
 | |
| 
 | |
|   G_OBJECT_CLASS (_gtk_file_system_parent_class)->finalize (object);
 | |
| }
 | |
| 
 | |
| static void
 | |
| _gtk_file_system_class_init (GtkFileSystemClass *class)
 | |
| {
 | |
|   GObjectClass *object_class = G_OBJECT_CLASS (class);
 | |
| 
 | |
|   object_class->dispose = gtk_file_system_dispose;
 | |
|   object_class->finalize = gtk_file_system_finalize;
 | |
| 
 | |
|   fs_signals[BOOKMARKS_CHANGED] =
 | |
|     g_signal_new ("bookmarks-changed",
 | |
| 		  G_TYPE_FROM_CLASS (object_class),
 | |
| 		  G_SIGNAL_RUN_LAST,
 | |
| 		  G_STRUCT_OFFSET (GtkFileSystemClass, bookmarks_changed),
 | |
| 		  NULL, NULL,
 | |
| 		  g_cclosure_marshal_VOID__VOID,
 | |
| 		  G_TYPE_NONE, 0);
 | |
| 
 | |
|   fs_signals[VOLUMES_CHANGED] =
 | |
|     g_signal_new ("volumes-changed",
 | |
| 		  G_TYPE_FROM_CLASS (object_class),
 | |
| 		  G_SIGNAL_RUN_LAST,
 | |
| 		  G_STRUCT_OFFSET (GtkFileSystemClass, volumes_changed),
 | |
| 		  NULL, NULL,
 | |
| 		  g_cclosure_marshal_VOID__VOID,
 | |
| 		  G_TYPE_NONE, 0);
 | |
| 
 | |
|   g_type_class_add_private (object_class, sizeof (GtkFileSystemPrivate));
 | |
| }
 | |
| 
 | |
| static GFile *
 | |
| get_bookmarks_file (void)
 | |
| {
 | |
|   GFile *file;
 | |
|   gchar *filename;
 | |
| 
 | |
|   filename = g_build_filename (g_get_home_dir (), ".gtk-bookmarks", NULL);
 | |
|   file = g_file_new_for_path (filename);
 | |
|   g_free (filename);
 | |
| 
 | |
|   return file;
 | |
| }
 | |
| 
 | |
| static GSList *
 | |
| read_bookmarks (GFile *file)
 | |
| {
 | |
|   gchar *contents;
 | |
|   gchar **lines, *space;
 | |
|   GSList *bookmarks = NULL;
 | |
|   gint i;
 | |
| 
 | |
|   if (!g_file_load_contents (file, NULL, &contents,
 | |
| 			     NULL, NULL, NULL))
 | |
|     return NULL;
 | |
| 
 | |
|   lines = g_strsplit (contents, "\n", -1);
 | |
| 
 | |
|   for (i = 0; lines[i]; i++)
 | |
|     {
 | |
|       GtkFileSystemBookmark *bookmark;
 | |
| 
 | |
|       if (!*lines[i])
 | |
| 	continue;
 | |
| 
 | |
|       bookmark = g_slice_new0 (GtkFileSystemBookmark);
 | |
| 
 | |
|       if ((space = strchr (lines[i], ' ')) != NULL)
 | |
| 	{
 | |
| 	  space[0] = '\0';
 | |
| 	  bookmark->label = g_strdup (space + 1);
 | |
| 	}
 | |
| 
 | |
|       bookmark->file = g_file_new_for_uri (lines[i]);
 | |
|       bookmarks = g_slist_prepend (bookmarks, bookmark);
 | |
|     }
 | |
| 
 | |
|   bookmarks = g_slist_reverse (bookmarks);
 | |
|   g_strfreev (lines);
 | |
|   g_free (contents);
 | |
| 
 | |
|   return bookmarks;
 | |
| }
 | |
| 
 | |
| static void
 | |
| save_bookmarks (GFile  *bookmarks_file,
 | |
| 		GSList *bookmarks)
 | |
| {
 | |
|   GError *error = NULL;
 | |
|   GString *contents;
 | |
| 
 | |
|   contents = g_string_new ("");
 | |
| 
 | |
|   while (bookmarks)
 | |
|     {
 | |
|       GtkFileSystemBookmark *bookmark;
 | |
|       gchar *uri;
 | |
| 
 | |
|       bookmark = bookmarks->data;
 | |
|       uri = g_file_get_uri (bookmark->file);
 | |
|       g_string_append (contents, uri);
 | |
| 
 | |
|       if (bookmark->label)
 | |
| 	g_string_append_printf (contents, " %s", bookmark->label);
 | |
| 
 | |
|       g_string_append_c (contents, '\n');
 | |
|       bookmarks = bookmarks->next;
 | |
|       g_free (uri);
 | |
|     }
 | |
| 
 | |
|   if (!g_file_replace_contents (bookmarks_file,
 | |
| 				contents->str,
 | |
| 				strlen (contents->str),
 | |
| 				NULL, FALSE, 0, NULL,
 | |
| 				NULL, &error))
 | |
|     {
 | |
|       g_critical ("%s", error->message);
 | |
|       g_error_free (error);
 | |
|     }
 | |
| 
 | |
|   g_string_free (contents, TRUE);
 | |
| }
 | |
| 
 | |
| static void
 | |
| bookmarks_file_changed (GFileMonitor      *monitor,
 | |
| 			GFile             *file,
 | |
| 			GFile             *other_file,
 | |
| 			GFileMonitorEvent  event,
 | |
| 			gpointer           data)
 | |
| {
 | |
|   GtkFileSystem *file_system = GTK_FILE_SYSTEM (data);
 | |
|   GtkFileSystemPrivate *priv = file_system->priv;
 | |
| 
 | |
|   switch (event)
 | |
|     {
 | |
|     case G_FILE_MONITOR_EVENT_CHANGED:
 | |
|     case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
 | |
|     case G_FILE_MONITOR_EVENT_CREATED:
 | |
|     case G_FILE_MONITOR_EVENT_DELETED:
 | |
|       g_slist_foreach (priv->bookmarks, (GFunc) _gtk_file_system_bookmark_free, NULL);
 | |
|       g_slist_free (priv->bookmarks);
 | |
| 
 | |
|       priv->bookmarks = read_bookmarks (file);
 | |
| 
 | |
|       gdk_threads_enter ();
 | |
|       g_signal_emit (data, fs_signals[BOOKMARKS_CHANGED], 0);
 | |
|       gdk_threads_leave ();
 | |
|       break;
 | |
|     default:
 | |
|       /* ignore at the moment */
 | |
|       break;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| mount_referenced_by_volume_activation_root (GList *volumes, GMount *mount)
 | |
| {
 | |
|   GList *l;
 | |
|   GFile *mount_root;
 | |
|   gboolean ret;
 | |
| 
 | |
|   ret = FALSE;
 | |
| 
 | |
|   mount_root = g_mount_get_root (mount);
 | |
| 
 | |
|   for (l = volumes; l != NULL; l = l->next)
 | |
|     {
 | |
|       GVolume *volume = G_VOLUME (l->data);
 | |
|       GFile *volume_activation_root;
 | |
| 
 | |
|       volume_activation_root = g_volume_get_activation_root (volume);
 | |
|       if (volume_activation_root != NULL)
 | |
|         {
 | |
|           if (g_file_has_prefix (volume_activation_root, mount_root))
 | |
|             {
 | |
|               ret = TRUE;
 | |
|               g_object_unref (volume_activation_root);
 | |
|               break;
 | |
|             }
 | |
|           g_object_unref (volume_activation_root);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   g_object_unref (mount_root);
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| static void
 | |
| get_volumes_list (GtkFileSystem *file_system)
 | |
| {
 | |
|   GtkFileSystemPrivate *priv = file_system->priv;
 | |
|   GList *l, *ll;
 | |
|   GList *drives;
 | |
|   GList *volumes;
 | |
|   GList *mounts;
 | |
|   GDrive *drive;
 | |
|   GVolume *volume;
 | |
|   GMount *mount;
 | |
| 
 | |
|   if (priv->volumes)
 | |
|     {
 | |
|       g_slist_foreach (priv->volumes, (GFunc) g_object_unref, NULL);
 | |
|       g_slist_free (priv->volumes);
 | |
|       priv->volumes = NULL;
 | |
|     }
 | |
| 
 | |
|   /* first go through all connected drives */
 | |
|   drives = g_volume_monitor_get_connected_drives (priv->volume_monitor);
 | |
| 
 | |
|   for (l = drives; l != NULL; l = l->next)
 | |
|     {
 | |
|       drive = l->data;
 | |
|       volumes = g_drive_get_volumes (drive);
 | |
| 
 | |
|       if (volumes)
 | |
|         {
 | |
|           for (ll = volumes; ll != NULL; ll = ll->next)
 | |
|             {
 | |
|               volume = ll->data;
 | |
|               mount = g_volume_get_mount (volume);
 | |
| 
 | |
|               if (mount)
 | |
|                 {
 | |
|                   /* Show mounted volume */
 | |
|                   priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
 | |
|                   g_object_unref (mount);
 | |
|                 }
 | |
|               else
 | |
|                 {
 | |
|                   /* Do show the unmounted volumes in the sidebar;
 | |
|                    * this is so the user can mount it (in case automounting
 | |
|                    * is off).
 | |
|                    *
 | |
|                    * Also, even if automounting is enabled, this gives a visual
 | |
|                    * cue that the user should remember to yank out the media if
 | |
|                    * he just unmounted it.
 | |
|                    */
 | |
|                   priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (volume));
 | |
|                 }
 | |
| 
 | |
| 	      g_object_unref (volume);
 | |
|             }
 | |
|   
 | |
|            g_list_free (volumes);
 | |
|         }
 | |
|       else if (g_drive_is_media_removable (drive) && !g_drive_is_media_check_automatic (drive))
 | |
| 	{
 | |
| 	  /* If the drive has no mountable volumes and we cannot detect media change.. we
 | |
| 	   * display the drive in the sidebar so the user can manually poll the drive by
 | |
| 	   * right clicking and selecting "Rescan..."
 | |
| 	   *
 | |
| 	   * This is mainly for drives like floppies where media detection doesn't
 | |
| 	   * work.. but it's also for human beings who like to turn off media detection
 | |
| 	   * in the OS to save battery juice.
 | |
| 	   */
 | |
| 
 | |
| 	  priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (drive));
 | |
| 	}
 | |
| 
 | |
|       g_object_unref (drive);
 | |
|     }
 | |
| 
 | |
|   g_list_free (drives);
 | |
| 
 | |
|   /* add all volumes that is not associated with a drive */
 | |
|   volumes = g_volume_monitor_get_volumes (priv->volume_monitor);
 | |
| 
 | |
|   for (l = volumes; l != NULL; l = l->next)
 | |
|     {
 | |
|       volume = l->data;
 | |
|       drive = g_volume_get_drive (volume);
 | |
| 
 | |
|       if (drive)
 | |
|         {
 | |
|           g_object_unref (drive);
 | |
|           continue;
 | |
|         }
 | |
| 
 | |
|       mount = g_volume_get_mount (volume);
 | |
| 
 | |
|       if (mount)
 | |
|         {
 | |
|           /* show this mount */
 | |
|           priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
 | |
|           g_object_unref (mount);
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           /* see comment above in why we add an icon for a volume */
 | |
|           priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (volume));
 | |
|         }
 | |
| 
 | |
|       g_object_unref (volume);
 | |
|     }
 | |
| 
 | |
|   /* add mounts that has no volume (/etc/mtab mounts, ftp, sftp,...) */
 | |
|   mounts = g_volume_monitor_get_mounts (priv->volume_monitor);
 | |
| 
 | |
|   for (l = mounts; l != NULL; l = l->next)
 | |
|     {
 | |
|       mount = l->data;
 | |
|       volume = g_mount_get_volume (mount);
 | |
| 
 | |
|       if (volume)
 | |
|         {
 | |
|           g_object_unref (volume);
 | |
|           continue;
 | |
|         }
 | |
| 
 | |
|       /* if there's exists one or more volumes with an activation root inside the mount,
 | |
|        * don't display the mount
 | |
|        */
 | |
|       if (mount_referenced_by_volume_activation_root (volumes, mount))
 | |
|         {
 | |
|           g_object_unref (mount);
 | |
|           continue;
 | |
|         }
 | |
| 
 | |
|       /* show this mount */
 | |
|       priv->volumes = g_slist_prepend (priv->volumes, g_object_ref (mount));
 | |
|       g_object_unref (mount);
 | |
|     }
 | |
| 
 | |
|   g_list_free (volumes);
 | |
| 
 | |
|   g_list_free (mounts);
 | |
| }
 | |
| 
 | |
| static void
 | |
| _gtk_file_system_init (GtkFileSystem *file_system)
 | |
| {
 | |
|   GtkFileSystemPrivate *priv;
 | |
|   GFile *bookmarks_file;
 | |
|   GError *error = NULL;
 | |
| 
 | |
|   DEBUG ("init");
 | |
| 
 | |
|   file_system->priv = G_TYPE_INSTANCE_GET_PRIVATE (file_system,
 | |
|                                                    GTK_TYPE_FILE_SYSTEM,
 | |
|                                                    GtkFileSystemPrivate);
 | |
|   priv = file_system->priv;
 | |
| 
 | |
|   /* Volumes */
 | |
|   priv->volume_monitor = g_volume_monitor_get ();
 | |
| 
 | |
|   g_signal_connect (priv->volume_monitor, "mount-added",
 | |
| 		    G_CALLBACK (volumes_changed), file_system);
 | |
|   g_signal_connect (priv->volume_monitor, "mount-removed",
 | |
| 		    G_CALLBACK (volumes_changed), file_system);
 | |
|   g_signal_connect (priv->volume_monitor, "mount-changed",
 | |
| 		    G_CALLBACK (volumes_changed), file_system);
 | |
|   g_signal_connect (priv->volume_monitor, "volume-added",
 | |
| 		    G_CALLBACK (volumes_changed), file_system);
 | |
|   g_signal_connect (priv->volume_monitor, "volume-removed",
 | |
| 		    G_CALLBACK (volumes_changed), file_system);
 | |
|   g_signal_connect (priv->volume_monitor, "volume-changed",
 | |
| 		    G_CALLBACK (volumes_changed), file_system);
 | |
|   g_signal_connect (priv->volume_monitor, "drive-connected",
 | |
| 		    G_CALLBACK (volumes_changed), file_system);
 | |
|   g_signal_connect (priv->volume_monitor, "drive-disconnected",
 | |
| 		    G_CALLBACK (volumes_changed), file_system);
 | |
|   g_signal_connect (priv->volume_monitor, "drive-changed",
 | |
| 		    G_CALLBACK (volumes_changed), file_system);
 | |
| 
 | |
|   /* Bookmarks */
 | |
|   bookmarks_file = get_bookmarks_file ();
 | |
|   priv->bookmarks = read_bookmarks (bookmarks_file);
 | |
|   priv->bookmarks_monitor = g_file_monitor_file (bookmarks_file,
 | |
| 						 G_FILE_MONITOR_NONE,
 | |
| 						 NULL, &error);
 | |
|   if (error)
 | |
|     {
 | |
|       g_warning ("%s", error->message);
 | |
|       g_error_free (error);
 | |
|     }
 | |
|   else
 | |
|     g_signal_connect (priv->bookmarks_monitor, "changed",
 | |
| 		      G_CALLBACK (bookmarks_file_changed), file_system);
 | |
| 
 | |
|   g_object_unref (bookmarks_file);
 | |
| }
 | |
| 
 | |
| /* GtkFileSystem public methods */
 | |
| GtkFileSystem *
 | |
| _gtk_file_system_new (void)
 | |
| {
 | |
|   return g_object_new (GTK_TYPE_FILE_SYSTEM, NULL);
 | |
| }
 | |
| 
 | |
| GSList *
 | |
| _gtk_file_system_list_volumes (GtkFileSystem *file_system)
 | |
| {
 | |
|   GtkFileSystemPrivate *priv = file_system->priv;
 | |
|   GSList *list;
 | |
| 
 | |
|   DEBUG ("list_volumes");
 | |
| 
 | |
|   get_volumes_list (file_system);
 | |
| 
 | |
|   list = g_slist_copy (priv->volumes);
 | |
| 
 | |
| #ifndef G_OS_WIN32
 | |
|   /* Prepend root volume */
 | |
|   list = g_slist_prepend (list, (gpointer) root_volume_token);
 | |
| #endif
 | |
| 
 | |
|   return list;
 | |
| }
 | |
| 
 | |
| GSList *
 | |
| _gtk_file_system_list_bookmarks (GtkFileSystem *file_system)
 | |
| {
 | |
|   GtkFileSystemPrivate *priv = file_system->priv;
 | |
|   GSList *bookmarks, *files = NULL;
 | |
| 
 | |
|   DEBUG ("list_bookmarks");
 | |
| 
 | |
|   bookmarks = priv->bookmarks;
 | |
| 
 | |
|   while (bookmarks)
 | |
|     {
 | |
|       GtkFileSystemBookmark *bookmark;
 | |
| 
 | |
|       bookmark = bookmarks->data;
 | |
|       bookmarks = bookmarks->next;
 | |
| 
 | |
|       files = g_slist_prepend (files, g_object_ref (bookmark->file));
 | |
|     }
 | |
| 
 | |
|   return g_slist_reverse (files);
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| is_valid_scheme_character (char c)
 | |
| {
 | |
|   return g_ascii_isalnum (c) || c == '+' || c == '-' || c == '.';
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| has_uri_scheme (const char *str)
 | |
| {
 | |
|   const char *p;
 | |
| 
 | |
|   p = str;
 | |
| 
 | |
|   if (!is_valid_scheme_character (*p))
 | |
|     return FALSE;
 | |
| 
 | |
|   do
 | |
|     p++;
 | |
|   while (is_valid_scheme_character (*p));
 | |
| 
 | |
|   return (strncmp (p, "://", 3) == 0);
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| _gtk_file_system_parse (GtkFileSystem     *file_system,
 | |
| 		        GFile             *base_file,
 | |
| 		        const gchar       *str,
 | |
| 		        GFile            **folder,
 | |
| 		        gchar            **file_part,
 | |
| 		        GError           **error)
 | |
| {
 | |
|   GFile *file;
 | |
|   gboolean result = FALSE;
 | |
|   gboolean is_dir = FALSE;
 | |
|   gchar *last_slash = NULL;
 | |
|   gboolean is_uri;
 | |
| 
 | |
|   DEBUG ("parse");
 | |
| 
 | |
|   if (str && *str)
 | |
|     is_dir = (str [strlen (str) - 1] == G_DIR_SEPARATOR);
 | |
| 
 | |
|   last_slash = strrchr (str, G_DIR_SEPARATOR);
 | |
| 
 | |
|   is_uri = has_uri_scheme (str);
 | |
| 
 | |
|   if (is_uri)
 | |
|     {
 | |
|       const char *colon;
 | |
|       const char *slash_after_hostname;
 | |
| 
 | |
|       colon = strchr (str, ':');
 | |
|       g_assert (colon != NULL);
 | |
|       g_assert (strncmp (colon, "://", 3) == 0);
 | |
| 
 | |
|       slash_after_hostname = strchr (colon + 3, '/');
 | |
| 
 | |
|       if (slash_after_hostname == NULL)
 | |
| 	{
 | |
| 	  /* We don't have a full hostname yet.  So, don't switch the folder
 | |
| 	   * until we have seen a full hostname.  Otherwise, completion will
 | |
| 	   * happen for every character the user types for the hostname.
 | |
| 	   */
 | |
| 
 | |
| 	  *folder = NULL;
 | |
| 	  *file_part = NULL;
 | |
| 	  g_set_error (error,
 | |
| 		       GTK_FILE_CHOOSER_ERROR,
 | |
| 		       GTK_FILE_CHOOSER_ERROR_INCOMPLETE_HOSTNAME,
 | |
| 		       "Incomplete hostname");
 | |
| 	  return FALSE;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   if (str[0] == '~' || g_path_is_absolute (str) || is_uri)
 | |
|     file = g_file_parse_name (str);
 | |
|   else
 | |
|     {
 | |
|       if (base_file)
 | |
| 	file = g_file_resolve_relative_path (base_file, str);
 | |
|       else
 | |
| 	{
 | |
| 	  *folder = NULL;
 | |
| 	  *file_part = NULL;
 | |
| 	  g_set_error (error,
 | |
| 		       GTK_FILE_CHOOSER_ERROR,
 | |
| 		       GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
 | |
| 		       _("Invalid path"));
 | |
| 	  return FALSE;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   if (base_file && g_file_equal (base_file, file))
 | |
|     {
 | |
|       /* this is when user types '.', could be the
 | |
|        * beginning of a hidden file, ./ or ../
 | |
|        */
 | |
|       *folder = g_object_ref (file);
 | |
|       *file_part = g_strdup (str);
 | |
|       result = TRUE;
 | |
|     }
 | |
|   else if (is_dir)
 | |
|     {
 | |
|       /* it's a dir, or at least it ends with the dir separator */
 | |
|       *folder = g_object_ref (file);
 | |
|       *file_part = g_strdup ("");
 | |
|       result = TRUE;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       GFile *parent_file;
 | |
| 
 | |
|       parent_file = g_file_get_parent (file);
 | |
| 
 | |
|       if (!parent_file)
 | |
| 	{
 | |
| 	  g_set_error (error,
 | |
| 		       GTK_FILE_CHOOSER_ERROR,
 | |
| 		       GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
 | |
| 		       "Could not get parent file");
 | |
| 	  *folder = NULL;
 | |
| 	  *file_part = NULL;
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  *folder = parent_file;
 | |
| 	  result = TRUE;
 | |
| 
 | |
| 	  if (last_slash)
 | |
| 	    *file_part = g_strdup (last_slash + 1);
 | |
| 	  else
 | |
| 	    *file_part = g_strdup (str);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   g_object_unref (file);
 | |
| 
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| static void
 | |
| free_async_data (AsyncFuncData *async_data)
 | |
| {
 | |
|   g_object_unref (async_data->file_system);
 | |
|   g_object_unref (async_data->file);
 | |
|   g_object_unref (async_data->cancellable);
 | |
| 
 | |
|   if (async_data->folder)
 | |
|     g_object_unref (async_data->folder);
 | |
| 
 | |
|   g_free (async_data->attributes);
 | |
|   g_free (async_data);
 | |
| }
 | |
| 
 | |
| static void
 | |
| enumerate_children_callback (GObject      *source_object,
 | |
| 			     GAsyncResult *result,
 | |
| 			     gpointer      user_data)
 | |
| {
 | |
|   GFileEnumerator *enumerator;
 | |
|   AsyncFuncData *async_data;
 | |
|   GtkFolder *folder = NULL;
 | |
|   GFile *file;
 | |
|   GError *error = NULL;
 | |
| 
 | |
|   file = G_FILE (source_object);
 | |
|   async_data = (AsyncFuncData *) user_data;
 | |
|   enumerator = g_file_enumerate_children_finish (file, result, &error);
 | |
| 
 | |
|   if (enumerator)
 | |
|     {
 | |
|       folder = g_object_new (GTK_TYPE_FOLDER,
 | |
| 			     "file", source_object,
 | |
| 			     "enumerator", enumerator,
 | |
| 			     "attributes", async_data->attributes,
 | |
| 			     NULL);
 | |
|       g_object_unref (enumerator);
 | |
|     }
 | |
| 
 | |
|   gdk_threads_enter ();
 | |
|   ((GtkFileSystemGetFolderCallback) async_data->callback) (async_data->cancellable,
 | |
| 							   folder, error, async_data->data);
 | |
|   gdk_threads_leave ();
 | |
| 
 | |
|   free_async_data (async_data);
 | |
| 
 | |
|   if (error)
 | |
|     g_error_free (error);
 | |
| }
 | |
| 
 | |
| GCancellable *
 | |
| _gtk_file_system_get_folder (GtkFileSystem                  *file_system,
 | |
| 			     GFile                          *file,
 | |
| 			     const gchar                    *attributes,
 | |
| 			     GtkFileSystemGetFolderCallback  callback,
 | |
| 			     gpointer                        data)
 | |
| {
 | |
|   GCancellable *cancellable;
 | |
|   AsyncFuncData *async_data;
 | |
| 
 | |
|   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
 | |
|   g_return_val_if_fail (G_IS_FILE (file), NULL);
 | |
| 
 | |
|   cancellable = g_cancellable_new ();
 | |
| 
 | |
|   async_data = g_new0 (AsyncFuncData, 1);
 | |
|   async_data->file_system = g_object_ref (file_system);
 | |
|   async_data->file = g_object_ref (file);
 | |
|   async_data->cancellable = g_object_ref (cancellable);
 | |
|   async_data->attributes = g_strdup (attributes);
 | |
| 
 | |
|   async_data->callback = callback;
 | |
|   async_data->data = data;
 | |
| 
 | |
|   g_file_enumerate_children_async (file,
 | |
| 				   attributes,
 | |
| 				   G_FILE_QUERY_INFO_NONE,
 | |
| 				   G_PRIORITY_DEFAULT,
 | |
| 				   cancellable,
 | |
| 				   enumerate_children_callback,
 | |
| 				   async_data);
 | |
|   return cancellable;
 | |
| }
 | |
| 
 | |
| static void
 | |
| query_info_callback (GObject      *source_object,
 | |
| 		     GAsyncResult *result,
 | |
| 		     gpointer      user_data)
 | |
| {
 | |
|   AsyncFuncData *async_data;
 | |
|   GError *error = NULL;
 | |
|   GFileInfo *file_info;
 | |
|   GFile *file;
 | |
| 
 | |
|   DEBUG ("query_info_callback");
 | |
| 
 | |
|   file = G_FILE (source_object);
 | |
|   async_data = (AsyncFuncData *) user_data;
 | |
|   file_info = g_file_query_info_finish (file, result, &error);
 | |
| 
 | |
|   if (async_data->callback)
 | |
|     {
 | |
|       gdk_threads_enter ();
 | |
|       ((GtkFileSystemGetInfoCallback) async_data->callback) (async_data->cancellable,
 | |
| 							     file_info, error, async_data->data);
 | |
|       gdk_threads_leave ();
 | |
|     }
 | |
| 
 | |
|   if (file_info)
 | |
|     g_object_unref (file_info);
 | |
| 
 | |
|   if (error)
 | |
|     g_error_free (error);
 | |
| 
 | |
|   free_async_data (async_data);
 | |
| }
 | |
| 
 | |
| GCancellable *
 | |
| _gtk_file_system_get_info (GtkFileSystem                *file_system,
 | |
| 			   GFile                        *file,
 | |
| 			   const gchar                  *attributes,
 | |
| 			   GtkFileSystemGetInfoCallback  callback,
 | |
| 			   gpointer                      data)
 | |
| {
 | |
|   GCancellable *cancellable;
 | |
|   AsyncFuncData *async_data;
 | |
| 
 | |
|   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
 | |
|   g_return_val_if_fail (G_IS_FILE (file), NULL);
 | |
| 
 | |
|   cancellable = g_cancellable_new ();
 | |
| 
 | |
|   async_data = g_new0 (AsyncFuncData, 1);
 | |
|   async_data->file_system = g_object_ref (file_system);
 | |
|   async_data->file = g_object_ref (file);
 | |
|   async_data->cancellable = g_object_ref (cancellable);
 | |
| 
 | |
|   async_data->callback = callback;
 | |
|   async_data->data = data;
 | |
| 
 | |
|   g_file_query_info_async (file,
 | |
| 			   attributes,
 | |
| 			   G_FILE_QUERY_INFO_NONE,
 | |
| 			   G_PRIORITY_DEFAULT,
 | |
| 			   cancellable,
 | |
| 			   query_info_callback,
 | |
| 			   async_data);
 | |
| 
 | |
|   return cancellable;
 | |
| }
 | |
| 
 | |
| static void
 | |
| drive_poll_for_media_cb (GObject      *source_object,
 | |
|                          GAsyncResult *result,
 | |
|                          gpointer      user_data)
 | |
| {
 | |
|   AsyncFuncData *async_data;
 | |
|   GError *error = NULL;
 | |
| 
 | |
|   g_drive_poll_for_media_finish (G_DRIVE (source_object), result, &error);
 | |
|   async_data = (AsyncFuncData *) user_data;
 | |
| 
 | |
|   gdk_threads_enter ();
 | |
|   ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable,
 | |
| 							     (GtkFileSystemVolume *) source_object,
 | |
| 							     error, async_data->data);
 | |
|   gdk_threads_leave ();
 | |
| 
 | |
|   if (error)
 | |
|     g_error_free (error);
 | |
| }
 | |
| 
 | |
| static void
 | |
| volume_mount_cb (GObject      *source_object,
 | |
| 		 GAsyncResult *result,
 | |
| 		 gpointer      user_data)
 | |
| {
 | |
|   AsyncFuncData *async_data;
 | |
|   GError *error = NULL;
 | |
| 
 | |
|   g_volume_mount_finish (G_VOLUME (source_object), result, &error);
 | |
|   async_data = (AsyncFuncData *) user_data;
 | |
| 
 | |
|   gdk_threads_enter ();
 | |
|   ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable,
 | |
| 							     (GtkFileSystemVolume *) source_object,
 | |
| 							     error, async_data->data);
 | |
|   gdk_threads_leave ();
 | |
| 
 | |
|   if (error)
 | |
|     g_error_free (error);
 | |
| }
 | |
| 
 | |
| GCancellable *
 | |
| _gtk_file_system_mount_volume (GtkFileSystem                    *file_system,
 | |
| 			       GtkFileSystemVolume              *volume,
 | |
| 			       GMountOperation                  *mount_operation,
 | |
| 			       GtkFileSystemVolumeMountCallback  callback,
 | |
| 			       gpointer                          data)
 | |
| {
 | |
|   GCancellable *cancellable;
 | |
|   AsyncFuncData *async_data;
 | |
|   gboolean handled = FALSE;
 | |
| 
 | |
|   DEBUG ("volume_mount");
 | |
| 
 | |
|   cancellable = g_cancellable_new ();
 | |
| 
 | |
|   async_data = g_new0 (AsyncFuncData, 1);
 | |
|   async_data->file_system = g_object_ref (file_system);
 | |
|   async_data->cancellable = g_object_ref (cancellable);
 | |
| 
 | |
|   async_data->callback = callback;
 | |
|   async_data->data = data;
 | |
| 
 | |
|   if (G_IS_DRIVE (volume))
 | |
|     {
 | |
|       /* this path happens for drives that are not polled by the OS and where the last media
 | |
|        * check indicated that no media was available. So the thing to do here is to
 | |
|        * invoke poll_for_media() on the drive
 | |
|        */
 | |
|       g_drive_poll_for_media (G_DRIVE (volume), cancellable, drive_poll_for_media_cb, async_data);
 | |
|       handled = TRUE;
 | |
|     }
 | |
|   else if (G_IS_VOLUME (volume))
 | |
|     {
 | |
|       g_volume_mount (G_VOLUME (volume), G_MOUNT_MOUNT_NONE, mount_operation, cancellable, volume_mount_cb, async_data);
 | |
|       handled = TRUE;
 | |
|     }
 | |
| 
 | |
|   if (!handled)
 | |
|     free_async_data (async_data);
 | |
| 
 | |
|   return cancellable;
 | |
| }
 | |
| 
 | |
| static void
 | |
| enclosing_volume_mount_cb (GObject      *source_object,
 | |
| 			   GAsyncResult *result,
 | |
| 			   gpointer      user_data)
 | |
| {
 | |
|   GtkFileSystemVolume *volume;
 | |
|   AsyncFuncData *async_data;
 | |
|   GError *error = NULL;
 | |
| 
 | |
|   async_data = (AsyncFuncData *) user_data;
 | |
|   g_file_mount_enclosing_volume_finish (G_FILE (source_object), result, &error);
 | |
|   volume = _gtk_file_system_get_volume_for_file (async_data->file_system, G_FILE (source_object));
 | |
| 
 | |
|   /* Silently drop G_IO_ERROR_ALREADY_MOUNTED error for gvfs backends without visible mounts. */
 | |
|   /* Better than doing query_info with additional I/O every time. */
 | |
|   if (error && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_ALREADY_MOUNTED))
 | |
|     g_clear_error (&error);
 | |
| 
 | |
|   gdk_threads_enter ();
 | |
|   ((GtkFileSystemVolumeMountCallback) async_data->callback) (async_data->cancellable, volume,
 | |
| 							     error, async_data->data);
 | |
|   gdk_threads_leave ();
 | |
| 
 | |
|   if (error)
 | |
|     g_error_free (error);
 | |
| 
 | |
|   _gtk_file_system_volume_unref (volume);
 | |
| }
 | |
| 
 | |
| GCancellable *
 | |
| _gtk_file_system_mount_enclosing_volume (GtkFileSystem                     *file_system,
 | |
| 					 GFile                             *file,
 | |
| 					 GMountOperation                   *mount_operation,
 | |
| 					 GtkFileSystemVolumeMountCallback   callback,
 | |
| 					 gpointer                           data)
 | |
| {
 | |
|   GCancellable *cancellable;
 | |
|   AsyncFuncData *async_data;
 | |
| 
 | |
|   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
 | |
|   g_return_val_if_fail (G_IS_FILE (file), NULL);
 | |
| 
 | |
|   DEBUG ("mount_enclosing_volume");
 | |
| 
 | |
|   cancellable = g_cancellable_new ();
 | |
| 
 | |
|   async_data = g_new0 (AsyncFuncData, 1);
 | |
|   async_data->file_system = g_object_ref (file_system);
 | |
|   async_data->file = g_object_ref (file);
 | |
|   async_data->cancellable = g_object_ref (cancellable);
 | |
| 
 | |
|   async_data->callback = callback;
 | |
|   async_data->data = data;
 | |
| 
 | |
|   g_file_mount_enclosing_volume (file,
 | |
| 				 G_MOUNT_MOUNT_NONE,
 | |
| 				 mount_operation,
 | |
| 				 cancellable,
 | |
| 				 enclosing_volume_mount_cb,
 | |
| 				 async_data);
 | |
|   return cancellable;
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| _gtk_file_system_insert_bookmark (GtkFileSystem  *file_system,
 | |
| 				  GFile          *file,
 | |
| 				  gint            position,
 | |
| 				  GError        **error)
 | |
| {
 | |
|   GtkFileSystemPrivate *priv = file_system->priv;
 | |
|   GSList *bookmarks;
 | |
|   GtkFileSystemBookmark *bookmark;
 | |
|   gboolean result = TRUE;
 | |
|   GFile *bookmarks_file;
 | |
| 
 | |
|   bookmarks = priv->bookmarks;
 | |
| 
 | |
|   while (bookmarks)
 | |
|     {
 | |
|       bookmark = bookmarks->data;
 | |
|       bookmarks = bookmarks->next;
 | |
| 
 | |
|       if (g_file_equal (bookmark->file, file))
 | |
| 	{
 | |
| 	  /* File is already in bookmarks */
 | |
| 	  result = FALSE;
 | |
| 	  break;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   if (!result)
 | |
|     {
 | |
|       gchar *uri = g_file_get_uri (file);
 | |
| 
 | |
|       g_set_error (error,
 | |
| 		   GTK_FILE_CHOOSER_ERROR,
 | |
| 		   GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS,
 | |
| 		   "%s already exists in the bookmarks list",
 | |
| 		   uri);
 | |
| 
 | |
|       g_free (uri);
 | |
| 
 | |
|       return FALSE;
 | |
|     }
 | |
| 
 | |
|   bookmark = g_slice_new0 (GtkFileSystemBookmark);
 | |
|   bookmark->file = g_object_ref (file);
 | |
| 
 | |
|   priv->bookmarks = g_slist_insert (priv->bookmarks, bookmark, position);
 | |
| 
 | |
|   bookmarks_file = get_bookmarks_file ();
 | |
|   save_bookmarks (bookmarks_file, priv->bookmarks);
 | |
|   g_object_unref (bookmarks_file);
 | |
| 
 | |
|   g_signal_emit (file_system, fs_signals[BOOKMARKS_CHANGED], 0);
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| _gtk_file_system_remove_bookmark (GtkFileSystem  *file_system,
 | |
| 				  GFile          *file,
 | |
| 				  GError        **error)
 | |
| {
 | |
|   GtkFileSystemPrivate *priv = file_system->priv;
 | |
|   GtkFileSystemBookmark *bookmark;
 | |
|   GSList *bookmarks;
 | |
|   gboolean result = FALSE;
 | |
|   GFile *bookmarks_file;
 | |
| 
 | |
|   if (!priv->bookmarks)
 | |
|     return FALSE;
 | |
| 
 | |
|   bookmarks = priv->bookmarks;
 | |
| 
 | |
|   while (bookmarks)
 | |
|     {
 | |
|       bookmark = bookmarks->data;
 | |
| 
 | |
|       if (g_file_equal (bookmark->file, file))
 | |
| 	{
 | |
| 	  result = TRUE;
 | |
| 	  priv->bookmarks = g_slist_remove_link (priv->bookmarks, bookmarks);
 | |
| 	  _gtk_file_system_bookmark_free (bookmark);
 | |
| 	  g_slist_free_1 (bookmarks);
 | |
| 	  break;
 | |
| 	}
 | |
| 
 | |
|       bookmarks = bookmarks->next;
 | |
|     }
 | |
| 
 | |
|   if (!result)
 | |
|     {
 | |
|       gchar *uri = g_file_get_uri (file);
 | |
| 
 | |
|       g_set_error (error,
 | |
| 		   GTK_FILE_CHOOSER_ERROR,
 | |
| 		   GTK_FILE_CHOOSER_ERROR_NONEXISTENT,
 | |
| 		   "%s does not exist in the bookmarks list",
 | |
| 		   uri);
 | |
| 
 | |
|       g_free (uri);
 | |
| 
 | |
|       return FALSE;
 | |
|     }
 | |
| 
 | |
|   bookmarks_file = get_bookmarks_file ();
 | |
|   save_bookmarks (bookmarks_file, priv->bookmarks);
 | |
|   g_object_unref (bookmarks_file);
 | |
| 
 | |
|   g_signal_emit (file_system, fs_signals[BOOKMARKS_CHANGED], 0);
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| gchar *
 | |
| _gtk_file_system_get_bookmark_label (GtkFileSystem *file_system,
 | |
| 				     GFile         *file)
 | |
| {
 | |
|   GtkFileSystemPrivate *priv = file_system->priv;
 | |
|   GSList *bookmarks;
 | |
|   gchar *label = NULL;
 | |
| 
 | |
|   DEBUG ("get_bookmark_label");
 | |
| 
 | |
|   bookmarks = priv->bookmarks;
 | |
| 
 | |
|   while (bookmarks)
 | |
|     {
 | |
|       GtkFileSystemBookmark *bookmark;
 | |
| 
 | |
|       bookmark = bookmarks->data;
 | |
|       bookmarks = bookmarks->next;
 | |
| 
 | |
|       if (g_file_equal (file, bookmark->file))
 | |
| 	{
 | |
| 	  label = g_strdup (bookmark->label);
 | |
| 	  break;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   return label;
 | |
| }
 | |
| 
 | |
| void
 | |
| _gtk_file_system_set_bookmark_label (GtkFileSystem *file_system,
 | |
| 				     GFile         *file,
 | |
| 				     const gchar   *label)
 | |
| {
 | |
|   GtkFileSystemPrivate *priv = file_system->priv;
 | |
|   gboolean changed = FALSE;
 | |
|   GFile *bookmarks_file;
 | |
|   GSList *bookmarks;
 | |
| 
 | |
|   DEBUG ("set_bookmark_label");
 | |
| 
 | |
|   bookmarks = priv->bookmarks;
 | |
| 
 | |
|   while (bookmarks)
 | |
|     {
 | |
|       GtkFileSystemBookmark *bookmark;
 | |
| 
 | |
|       bookmark = bookmarks->data;
 | |
|       bookmarks = bookmarks->next;
 | |
| 
 | |
|       if (g_file_equal (file, bookmark->file))
 | |
| 	{
 | |
|           g_free (bookmark->label);
 | |
| 	  bookmark->label = g_strdup (label);
 | |
| 	  changed = TRUE;
 | |
| 	  break;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   bookmarks_file = get_bookmarks_file ();
 | |
|   save_bookmarks (bookmarks_file, priv->bookmarks);
 | |
|   g_object_unref (bookmarks_file);
 | |
| 
 | |
|   if (changed)
 | |
|     g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
 | |
| }
 | |
| 
 | |
| GtkFileSystemVolume *
 | |
| _gtk_file_system_get_volume_for_file (GtkFileSystem *file_system,
 | |
| 				      GFile         *file)
 | |
| {
 | |
|   GMount *mount;
 | |
| 
 | |
|   DEBUG ("get_volume_for_file");
 | |
| 
 | |
|   mount = g_file_find_enclosing_mount (file, NULL, NULL);
 | |
| 
 | |
|   if (!mount && g_file_is_native (file))
 | |
|     return (GtkFileSystemVolume *) root_volume_token;
 | |
| 
 | |
|   return (GtkFileSystemVolume *) mount;
 | |
| }
 | |
| 
 | |
| /* GtkFolder methods */
 | |
| static void
 | |
| gtk_folder_set_property (GObject      *object,
 | |
| 			 guint         prop_id,
 | |
| 			 const GValue *value,
 | |
| 			 GParamSpec   *pspec)
 | |
| {
 | |
|   GtkFolder *folder = GTK_FOLDER (object);
 | |
|   GtkFolderPrivate *priv = folder->priv;
 | |
| 
 | |
|   switch (prop_id)
 | |
|     {
 | |
|     case PROP_FILE:
 | |
|       priv->folder_file = g_value_dup_object (value);
 | |
|       break;
 | |
|     case PROP_ENUMERATOR:
 | |
|       priv->enumerator = g_value_dup_object (value);
 | |
|       break;
 | |
|     case PROP_ATTRIBUTES:
 | |
|       priv->attributes = g_value_dup_string (value);
 | |
|       break;
 | |
|     default:
 | |
|       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 | |
|       break;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_folder_get_property (GObject    *object,
 | |
| 			 guint       prop_id,
 | |
| 			 GValue     *value,
 | |
| 			 GParamSpec *pspec)
 | |
| {
 | |
|   GtkFolder *folder = GTK_FOLDER (object);
 | |
|   GtkFolderPrivate *priv = folder->priv;
 | |
| 
 | |
|   switch (prop_id)
 | |
|     {
 | |
|     case PROP_FILE:
 | |
|       g_value_set_object (value, priv->folder_file);
 | |
|       break;
 | |
|     case PROP_ENUMERATOR:
 | |
|       g_value_set_object (value, priv->enumerator);
 | |
|       break;
 | |
|     case PROP_ATTRIBUTES:
 | |
|       g_value_set_string (value, priv->attributes);
 | |
|       break;
 | |
|     default:
 | |
|       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 | |
|       break;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| query_created_file_info_callback (GObject      *source_object,
 | |
| 				  GAsyncResult *result,
 | |
| 				  gpointer      user_data)
 | |
| {
 | |
|   GFile *file = G_FILE (source_object);
 | |
|   GError *error = NULL;
 | |
|   GFileInfo *info;
 | |
|   GtkFolder *folder;
 | |
|   GSList *files;
 | |
| 
 | |
|   info = g_file_query_info_finish (file, result, &error);
 | |
| 
 | |
|   if (error)
 | |
|     {
 | |
|       g_error_free (error);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   gdk_threads_enter ();
 | |
| 
 | |
|   folder = GTK_FOLDER (user_data);
 | |
|   gtk_folder_add_file (folder, file, info);
 | |
| 
 | |
|   files = g_slist_prepend (NULL, file);
 | |
|   g_signal_emit (folder, folder_signals[FILES_ADDED], 0, files);
 | |
|   g_slist_free (files);
 | |
| 
 | |
|   g_object_unref (info);
 | |
|   gdk_threads_leave ();
 | |
| }
 | |
| 
 | |
| static void
 | |
| directory_monitor_changed (GFileMonitor      *monitor,
 | |
| 			   GFile             *file,
 | |
| 			   GFile             *other_file,
 | |
| 			   GFileMonitorEvent  event,
 | |
| 			   gpointer           data)
 | |
| {
 | |
|   GtkFolder *folder = GTK_FOLDER (data);
 | |
|   GtkFolderPrivate *priv = folder->priv;
 | |
|   GSList *files;
 | |
| 
 | |
|   files = g_slist_prepend (NULL, file);
 | |
| 
 | |
|   gdk_threads_enter ();
 | |
| 
 | |
|   switch (event)
 | |
|     {
 | |
|     case G_FILE_MONITOR_EVENT_CREATED:
 | |
|       g_file_query_info_async (file,
 | |
| 			       priv->attributes,
 | |
| 			       G_FILE_QUERY_INFO_NONE,
 | |
| 			       G_PRIORITY_DEFAULT,
 | |
| 			       priv->cancellable,
 | |
| 			       query_created_file_info_callback,
 | |
| 			       folder);
 | |
|       break;
 | |
|     case G_FILE_MONITOR_EVENT_DELETED:
 | |
|       if (g_file_equal (file, priv->folder_file))
 | |
| 	g_signal_emit (folder, folder_signals[DELETED], 0);
 | |
|       else
 | |
| 	g_signal_emit (folder, folder_signals[FILES_REMOVED], 0, files);
 | |
|       break;
 | |
|     default:
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|   gdk_threads_leave ();
 | |
| 
 | |
|   g_slist_free (files);
 | |
| }
 | |
| 
 | |
| static void
 | |
| enumerator_files_callback (GObject      *source_object,
 | |
| 			   GAsyncResult *result,
 | |
| 			   gpointer      user_data)
 | |
| {
 | |
|   GtkFolder *folder = GTK_FOLDER (user_data);
 | |
|   GtkFolderPrivate *priv = folder->priv;
 | |
|   GFileEnumerator *enumerator;
 | |
|   GError *error = NULL;
 | |
|   GSList *files = NULL;
 | |
|   GList *file_infos, *f;
 | |
| 
 | |
|   enumerator = G_FILE_ENUMERATOR (source_object);
 | |
|   file_infos = g_file_enumerator_next_files_finish (enumerator, result, &error);
 | |
| 
 | |
|   if (error)
 | |
|     {
 | |
|       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
 | |
|         g_warning ("%s", error->message);
 | |
| 
 | |
|       g_error_free (error);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   if (!file_infos)
 | |
|     {
 | |
|       g_file_enumerator_close_async (enumerator,
 | |
| 				     G_PRIORITY_DEFAULT,
 | |
| 				     NULL, NULL, NULL);
 | |
| 
 | |
|       gtk_folder_set_finished_loading (folder, TRUE);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   g_file_enumerator_next_files_async (enumerator, FILES_PER_QUERY,
 | |
| 				      G_PRIORITY_DEFAULT,
 | |
| 				      priv->cancellable,
 | |
| 				      enumerator_files_callback,
 | |
| 				      folder);
 | |
| 
 | |
|   for (f = file_infos; f; f = f->next)
 | |
|     {
 | |
|       GFileInfo *info;
 | |
|       GFile *child_file;
 | |
| 
 | |
|       info = f->data;
 | |
|       child_file = g_file_get_child (priv->folder_file, g_file_info_get_name (info));
 | |
|       gtk_folder_add_file (folder, child_file, info);
 | |
|       files = g_slist_prepend (files, child_file);
 | |
|     }
 | |
| 
 | |
|   gdk_threads_enter ();
 | |
|   g_signal_emit (folder, folder_signals[FILES_ADDED], 0, files);
 | |
|   gdk_threads_leave ();
 | |
| 
 | |
|   g_list_foreach (file_infos, (GFunc) g_object_unref, NULL);
 | |
|   g_list_free (file_infos);
 | |
| 
 | |
|   g_slist_foreach (files, (GFunc) g_object_unref, NULL);
 | |
|   g_slist_free (files);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_folder_constructed (GObject *object)
 | |
| {
 | |
|   GtkFolder *folder = GTK_FOLDER (object);
 | |
|   GtkFolderPrivate *priv = folder->priv;
 | |
|   GError *error = NULL;
 | |
| 
 | |
|   priv->directory_monitor = g_file_monitor_directory (priv->folder_file, G_FILE_MONITOR_NONE, NULL, &error);
 | |
| 
 | |
|   if (error)
 | |
|     {
 | |
|       g_warning ("%s", error->message);
 | |
|       g_error_free (error);
 | |
|     }
 | |
|   else
 | |
|     g_signal_connect (priv->directory_monitor, "changed",
 | |
| 		      G_CALLBACK (directory_monitor_changed), object);
 | |
| 
 | |
|   g_file_enumerator_next_files_async (priv->enumerator,
 | |
| 				      FILES_PER_QUERY,
 | |
| 				      G_PRIORITY_DEFAULT,
 | |
| 				      priv->cancellable,
 | |
| 				      enumerator_files_callback,
 | |
| 				      object);
 | |
|   /* This isn't needed anymore */
 | |
|   g_object_unref (priv->enumerator);
 | |
|   priv->enumerator = NULL;
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_folder_finalize (GObject *object)
 | |
| {
 | |
|   GtkFolder *folder = GTK_FOLDER (object);
 | |
|   GtkFolderPrivate *priv = folder->priv;
 | |
| 
 | |
|   g_hash_table_unref (priv->children);
 | |
| 
 | |
|   if (priv->folder_file)
 | |
|     g_object_unref (priv->folder_file);
 | |
| 
 | |
|   if (priv->directory_monitor)
 | |
|     g_object_unref (priv->directory_monitor);
 | |
| 
 | |
|   g_cancellable_cancel (priv->cancellable);
 | |
|   g_object_unref (priv->cancellable);
 | |
|   g_free (priv->attributes);
 | |
| 
 | |
|   G_OBJECT_CLASS (_gtk_folder_parent_class)->finalize (object);
 | |
| }
 | |
| 
 | |
| static void
 | |
| _gtk_folder_class_init (GtkFolderClass *class)
 | |
| {
 | |
|   GObjectClass *object_class = G_OBJECT_CLASS (class);
 | |
| 
 | |
|   object_class->set_property = gtk_folder_set_property;
 | |
|   object_class->get_property = gtk_folder_get_property;
 | |
|   object_class->constructed = gtk_folder_constructed;
 | |
|   object_class->finalize = gtk_folder_finalize;
 | |
| 
 | |
|   g_object_class_install_property (object_class,
 | |
| 				   PROP_FILE,
 | |
| 				   g_param_spec_object ("file",
 | |
| 							"File",
 | |
| 							"GFile for the folder",
 | |
| 							G_TYPE_FILE,
 | |
| 							GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 | |
|   g_object_class_install_property (object_class,
 | |
| 				   PROP_ENUMERATOR,
 | |
| 				   g_param_spec_object ("enumerator",
 | |
| 							"Enumerator",
 | |
| 							"GFileEnumerator to list files",
 | |
| 							G_TYPE_FILE_ENUMERATOR,
 | |
| 							GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 | |
|   g_object_class_install_property (object_class,
 | |
| 				   PROP_ATTRIBUTES,
 | |
| 				   g_param_spec_string ("attributes",
 | |
| 							"Attributes",
 | |
| 							"Attributes to query for",
 | |
| 							NULL,
 | |
| 							GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 | |
|   folder_signals[FILES_ADDED] =
 | |
|     g_signal_new ("files-added",
 | |
| 		  G_TYPE_FROM_CLASS (object_class),
 | |
| 		  G_SIGNAL_RUN_LAST,
 | |
| 		  G_STRUCT_OFFSET (GtkFolderClass, files_added),
 | |
| 		  NULL, NULL,
 | |
| 		  g_cclosure_marshal_VOID__POINTER,
 | |
| 		  G_TYPE_NONE, 1, G_TYPE_POINTER);
 | |
|   folder_signals[FILES_REMOVED] =
 | |
|     g_signal_new ("files-removed",
 | |
| 		  G_TYPE_FROM_CLASS (object_class),
 | |
| 		  G_SIGNAL_RUN_LAST,
 | |
| 		  G_STRUCT_OFFSET (GtkFolderClass, files_removed),
 | |
| 		  NULL, NULL,
 | |
| 		  g_cclosure_marshal_VOID__POINTER,
 | |
| 		  G_TYPE_NONE, 1, G_TYPE_POINTER);
 | |
|   folder_signals[FILES_CHANGED] =
 | |
|     g_signal_new ("files-changed",
 | |
| 		  G_TYPE_FROM_CLASS (object_class),
 | |
| 		  G_SIGNAL_RUN_LAST,
 | |
| 		  G_STRUCT_OFFSET (GtkFolderClass, files_changed),
 | |
| 		  NULL, NULL,
 | |
| 		  g_cclosure_marshal_VOID__POINTER,
 | |
| 		  G_TYPE_NONE, 1, G_TYPE_POINTER);
 | |
|   folder_signals[FINISHED_LOADING] =
 | |
|     g_signal_new ("finished-loading",
 | |
| 		  G_TYPE_FROM_CLASS (object_class),
 | |
| 		  G_SIGNAL_RUN_LAST,
 | |
| 		  G_STRUCT_OFFSET (GtkFolderClass, finished_loading),
 | |
| 		  NULL, NULL,
 | |
| 		  g_cclosure_marshal_VOID__VOID,
 | |
| 		  G_TYPE_NONE, 0);
 | |
|   folder_signals[DELETED] =
 | |
|     g_signal_new ("deleted",
 | |
| 		  G_TYPE_FROM_CLASS (object_class),
 | |
| 		  G_SIGNAL_RUN_LAST,
 | |
| 		  G_STRUCT_OFFSET (GtkFolderClass, deleted),
 | |
| 		  NULL, NULL,
 | |
| 		  g_cclosure_marshal_VOID__VOID,
 | |
| 		  G_TYPE_NONE, 0);
 | |
| 
 | |
|   g_type_class_add_private (object_class, sizeof (GtkFolderPrivate));
 | |
| }
 | |
| 
 | |
| static void
 | |
| _gtk_folder_init (GtkFolder *folder)
 | |
| {
 | |
|   GtkFolderPrivate *priv;
 | |
| 
 | |
|   folder->priv = G_TYPE_INSTANCE_GET_PRIVATE (folder,
 | |
|                                               GTK_TYPE_FOLDER,
 | |
|                                               GtkFolderPrivate);
 | |
|   priv = folder->priv;
 | |
|   priv->children = g_hash_table_new_full (g_file_hash,
 | |
| 					  (GEqualFunc) g_file_equal,
 | |
| 					  (GDestroyNotify) g_object_unref,
 | |
| 					  (GDestroyNotify) g_object_unref);
 | |
|   priv->cancellable = g_cancellable_new ();
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_folder_set_finished_loading (GtkFolder *folder,
 | |
| 				 gboolean   finished_loading)
 | |
| {
 | |
|   GtkFolderPrivate *priv = folder->priv;
 | |
| 
 | |
|   priv->finished_loading = (finished_loading == TRUE);
 | |
| 
 | |
|   gdk_threads_enter ();
 | |
|   g_signal_emit (folder, folder_signals[FINISHED_LOADING], 0);
 | |
|   gdk_threads_leave ();
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_folder_add_file (GtkFolder *folder,
 | |
| 		     GFile     *file,
 | |
| 		     GFileInfo *info)
 | |
| {
 | |
|   GtkFolderPrivate *priv = folder->priv;
 | |
| 
 | |
|   g_hash_table_insert (priv->children,
 | |
| 		       g_object_ref (file),
 | |
| 		       g_object_ref (info));
 | |
| }
 | |
| 
 | |
| GSList *
 | |
| _gtk_folder_list_children (GtkFolder *folder)
 | |
| {
 | |
|   GtkFolderPrivate *priv = folder->priv;
 | |
|   GList *files, *elem;
 | |
|   GSList *children = NULL;
 | |
| 
 | |
|   files = g_hash_table_get_keys (priv->children);
 | |
|   children = NULL;
 | |
| 
 | |
|   for (elem = files; elem; elem = elem->next)
 | |
|     children = g_slist_prepend (children, g_object_ref (elem->data));
 | |
| 
 | |
|   g_list_free (files);
 | |
| 
 | |
|   return children;
 | |
| }
 | |
| 
 | |
| GFileInfo *
 | |
| _gtk_folder_get_info (GtkFolder  *folder,
 | |
| 		      GFile      *file)
 | |
| {
 | |
|   GtkFolderPrivate *priv = folder->priv;
 | |
|   GFileInfo *info;
 | |
| 
 | |
|   info = g_hash_table_lookup (priv->children, file);
 | |
| 
 | |
|   if (!info)
 | |
|     return NULL;
 | |
| 
 | |
|   return g_object_ref (info);
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| _gtk_folder_is_finished_loading (GtkFolder *folder)
 | |
| {
 | |
|   return folder->priv->finished_loading;
 | |
| }
 | |
| 
 | |
| /* GtkFileSystemVolume public methods */
 | |
| gchar *
 | |
| _gtk_file_system_volume_get_display_name (GtkFileSystemVolume *volume)
 | |
| {
 | |
|   DEBUG ("volume_get_display_name");
 | |
| 
 | |
|   if (IS_ROOT_VOLUME (volume))
 | |
|     return g_strdup (_(root_volume_token));
 | |
|   if (G_IS_DRIVE (volume))
 | |
|     return g_drive_get_name (G_DRIVE (volume));
 | |
|   else if (G_IS_MOUNT (volume))
 | |
|     return g_mount_get_name (G_MOUNT (volume));
 | |
|   else if (G_IS_VOLUME (volume))
 | |
|     return g_volume_get_name (G_VOLUME (volume));
 | |
| 
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| _gtk_file_system_volume_is_mounted (GtkFileSystemVolume *volume)
 | |
| {
 | |
|   gboolean mounted;
 | |
| 
 | |
|   DEBUG ("volume_is_mounted");
 | |
| 
 | |
|   if (IS_ROOT_VOLUME (volume))
 | |
|     return TRUE;
 | |
| 
 | |
|   mounted = FALSE;
 | |
| 
 | |
|   if (G_IS_MOUNT (volume))
 | |
|     mounted = TRUE;
 | |
|   else if (G_IS_VOLUME (volume))
 | |
|     {
 | |
|       GMount *mount;
 | |
| 
 | |
|       mount = g_volume_get_mount (G_VOLUME (volume));
 | |
| 
 | |
|       if (mount)
 | |
|         {
 | |
|           mounted = TRUE;
 | |
|           g_object_unref (mount);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   return mounted;
 | |
| }
 | |
| 
 | |
| GFile *
 | |
| _gtk_file_system_volume_get_root (GtkFileSystemVolume *volume)
 | |
| {
 | |
|   GFile *file = NULL;
 | |
| 
 | |
|   DEBUG ("volume_get_base");
 | |
| 
 | |
|   if (IS_ROOT_VOLUME (volume))
 | |
|     return g_file_new_for_uri ("file:///");
 | |
| 
 | |
|   if (G_IS_MOUNT (volume))
 | |
|     file = g_mount_get_root (G_MOUNT (volume));
 | |
|   else if (G_IS_VOLUME (volume))
 | |
|     {
 | |
|       GMount *mount;
 | |
| 
 | |
|       mount = g_volume_get_mount (G_VOLUME (volume));
 | |
| 
 | |
|       if (mount)
 | |
| 	{
 | |
| 	  file = g_mount_get_root (mount);
 | |
| 	  g_object_unref (mount);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   return file;
 | |
| }
 | |
| 
 | |
| static GdkPixbuf *
 | |
| get_pixbuf_from_gicon (GIcon      *icon,
 | |
| 		       GtkWidget  *widget,
 | |
| 		       gint        icon_size,
 | |
| 		       GError    **error)
 | |
| {
 | |
|   GdkScreen *screen;
 | |
|   GtkIconTheme *icon_theme;
 | |
|   GtkIconInfo *icon_info;
 | |
|   GdkPixbuf *pixbuf;
 | |
| 
 | |
|   screen = gtk_widget_get_screen (GTK_WIDGET (widget));
 | |
|   icon_theme = gtk_icon_theme_get_for_screen (screen);
 | |
| 
 | |
|   icon_info = gtk_icon_theme_lookup_by_gicon (icon_theme,
 | |
| 					      icon,
 | |
| 					      icon_size,
 | |
| 					      GTK_ICON_LOOKUP_USE_BUILTIN);
 | |
| 
 | |
|   if (!icon_info)
 | |
|     return NULL;
 | |
| 
 | |
|   pixbuf = gtk_icon_info_load_icon (icon_info, error);
 | |
|   gtk_icon_info_free (icon_info);
 | |
| 
 | |
|   return pixbuf;
 | |
| }
 | |
| 
 | |
| GdkPixbuf *
 | |
| _gtk_file_system_volume_render_icon (GtkFileSystemVolume  *volume,
 | |
| 				     GtkWidget            *widget,
 | |
| 				     gint                  icon_size,
 | |
| 				     GError              **error)
 | |
| {
 | |
|   GIcon *icon = NULL;
 | |
|   GdkPixbuf *pixbuf;
 | |
| 
 | |
|   DEBUG ("volume_get_icon_name");
 | |
| 
 | |
|   if (IS_ROOT_VOLUME (volume))
 | |
|     icon = g_themed_icon_new ("drive-harddisk");
 | |
|   else if (G_IS_DRIVE (volume))
 | |
|     icon = g_drive_get_icon (G_DRIVE (volume));
 | |
|   else if (G_IS_VOLUME (volume))
 | |
|     icon = g_volume_get_icon (G_VOLUME (volume));
 | |
|   else if (G_IS_MOUNT (volume))
 | |
|     icon = g_mount_get_icon (G_MOUNT (volume));
 | |
| 
 | |
|   if (!icon)
 | |
|     return NULL;
 | |
| 
 | |
|   pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, error);
 | |
| 
 | |
|   g_object_unref (icon);
 | |
| 
 | |
|   return pixbuf;
 | |
| }
 | |
| 
 | |
| GtkFileSystemVolume *
 | |
| _gtk_file_system_volume_ref (GtkFileSystemVolume *volume)
 | |
| {
 | |
|   if (IS_ROOT_VOLUME (volume))
 | |
|     return volume;
 | |
| 
 | |
|   if (G_IS_MOUNT (volume)  ||
 | |
|       G_IS_VOLUME (volume) ||
 | |
|       G_IS_DRIVE (volume))
 | |
|     g_object_ref (volume);
 | |
| 
 | |
|   return volume;
 | |
| }
 | |
| 
 | |
| void
 | |
| _gtk_file_system_volume_unref (GtkFileSystemVolume *volume)
 | |
| {
 | |
|   /* Root volume doesn't need to be freed */
 | |
|   if (IS_ROOT_VOLUME (volume))
 | |
|     return;
 | |
| 
 | |
|   if (G_IS_MOUNT (volume)  ||
 | |
|       G_IS_VOLUME (volume) ||
 | |
|       G_IS_DRIVE (volume))
 | |
|     g_object_unref (volume);
 | |
| }
 | |
| 
 | |
| /* GFileInfo helper functions */
 | |
| GdkPixbuf *
 | |
| _gtk_file_info_render_icon (GFileInfo *info,
 | |
| 			   GtkWidget *widget,
 | |
| 			   gint       icon_size)
 | |
| {
 | |
|   GIcon *icon;
 | |
|   GdkPixbuf *pixbuf = NULL;
 | |
|   const gchar *thumbnail_path;
 | |
| 
 | |
|   thumbnail_path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
 | |
| 
 | |
|   if (thumbnail_path)
 | |
|     pixbuf = gdk_pixbuf_new_from_file_at_size (thumbnail_path,
 | |
| 					       icon_size, icon_size,
 | |
| 					       NULL);
 | |
| 
 | |
|   if (!pixbuf)
 | |
|     {
 | |
|       icon = g_file_info_get_icon (info);
 | |
| 
 | |
|       if (icon)
 | |
| 	pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, NULL);
 | |
| 
 | |
|       if (!pixbuf)
 | |
| 	{
 | |
| 	   /* Use general fallback for all files without icon */
 | |
| 	  icon = g_themed_icon_new ("text-x-generic");
 | |
| 	  pixbuf = get_pixbuf_from_gicon (icon, widget, icon_size, NULL);
 | |
| 	  g_object_unref (icon);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   return pixbuf;
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| _gtk_file_info_consider_as_directory (GFileInfo *info)
 | |
| {
 | |
|   GFileType type = g_file_info_get_file_type (info);
 | |
|   
 | |
|   return (type == G_FILE_TYPE_DIRECTORY ||
 | |
|           type == G_FILE_TYPE_MOUNTABLE ||
 | |
|           type == G_FILE_TYPE_SHORTCUT);
 | |
| }
 | |
| 
 |