[gnome-photos] base-item, utils: Handle name collisions while exporting



commit 99357b8fb787e89dcb4b0d3339754e4eb15d80b2
Author: Rafael Fonseca <r4f4rfs gmail com>
Date:   Fri Feb 19 16:49:25 2016 +0100

    base-item, utils: Handle name collisions while exporting
    
    Before this, we would always replace the contents of the exported
    file. Instead, we suffix a copy number to deal with name collisions.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=762318

 src/photos-base-item.c |   36 +++++-----
 src/photos-utils.c     |  176 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/photos-utils.h     |   13 ++++
 3 files changed, 207 insertions(+), 18 deletions(-)
---
diff --git a/src/photos-base-item.c b/src/photos-base-item.c
index 6b145a7..726f9ba 100644
--- a/src/photos-base-item.c
+++ b/src/photos-base-item.c
@@ -133,6 +133,7 @@ typedef struct _PhotosBaseItemSaveData PhotosBaseItemSaveData;
 struct _PhotosBaseItemSaveData
 {
   GFile *dir;
+  GFile *unique_file;
   GeglBuffer *buffer;
   gchar *type;
 };
@@ -168,6 +169,7 @@ static void
 photos_base_item_save_data_free (PhotosBaseItemSaveData *data)
 {
   g_clear_object (&data->dir);
+  g_clear_object (&data->unique_file);
   g_clear_object (&data->buffer);
   g_free (data->type);
   g_slice_free (PhotosBaseItemSaveData, data);
@@ -1147,7 +1149,6 @@ photos_base_item_save_save_metadata (GObject *source_object, GAsyncResult *res,
 {
   PhotosBaseItem *self = PHOTOS_BASE_ITEM (source_object);
   GError *error;
-  GFile *file = NULL;
   GTask *task = G_TASK (user_data);
   PhotosBaseItemSaveData *data;
 
@@ -1160,11 +1161,9 @@ photos_base_item_save_save_metadata (GObject *source_object, GAsyncResult *res,
       goto out;
     }
 
-  file = g_file_get_child (data->dir, self->priv->filename);
-  g_task_return_pointer (task, g_object_ref (file), g_object_unref);
+  g_task_return_pointer (task, g_object_ref (data->unique_file), g_object_unref);
 
  out:
-  g_clear_object (&file);
   g_object_unref (task);
 }
 
@@ -1177,7 +1176,6 @@ photos_base_item_save_save_to_stream (GObject *source_object, GAsyncResult *res,
   PhotosBaseItemSaveData *data;
   GCancellable *cancellable;
   GError *error = NULL;
-  GFile *file = NULL;
 
   self = PHOTOS_BASE_ITEM (g_task_get_source_object (task));
   cancellable = g_task_get_cancellable (task);
@@ -1189,21 +1187,19 @@ photos_base_item_save_save_to_stream (GObject *source_object, GAsyncResult *res,
       goto out;
     }
 
-  file = g_file_get_child (data->dir, self->priv->filename);
   photos_base_item_save_metadata_async (self,
-                                        file,
+                                        data->unique_file,
                                         cancellable,
                                         photos_base_item_save_save_metadata,
                                         g_object_ref (task));
 
  out:
-  g_clear_object (&file);
   g_object_unref (task);
 }
 
 
 static void
-photos_base_item_save_replace (GObject *source_object, GAsyncResult *res, gpointer user_data)
+photos_base_item_save_create (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
   GTask *task = G_TASK (user_data);
   GCancellable *cancellable;
@@ -1212,19 +1208,24 @@ photos_base_item_save_replace (GObject *source_object, GAsyncResult *res, gpoint
   GeglNode *buffer_source;
   GeglNode *graph = NULL;
   GFile *file = G_FILE (source_object);
+  GFile *unique_file = NULL;
   GFileOutputStream *stream = NULL;
   PhotosBaseItemSaveData *data;
 
   cancellable = g_task_get_cancellable (task);
   data = (PhotosBaseItemSaveData *) g_task_get_task_data (task);
 
-  stream = g_file_replace_finish (file, res, &error);
+  stream = photos_utils_file_create_finish (file, res, &unique_file, &error);
   if (error != NULL)
     {
       g_task_return_error (task, error);
       goto out;
     }
 
+  g_assert_null (data->unique_file);
+  g_assert_true (G_IS_FILE (unique_file));
+  data->unique_file = g_object_ref (unique_file);
+
   graph = gegl_node_new ();
   buffer_source = gegl_node_new_child (graph, "operation", "gegl:buffer-source", "buffer", data->buffer, 
NULL);
   pixbuf = photos_utils_create_pixbuf_from_node (buffer_source);
@@ -1260,6 +1261,7 @@ photos_base_item_save_replace (GObject *source_object, GAsyncResult *res, gpoint
   g_clear_object (&graph);
   g_clear_object (&pixbuf);
   g_clear_object (&stream);
+  g_clear_object (&unique_file);
   g_object_unref (task);
 }
 
@@ -1292,14 +1294,12 @@ photos_base_item_save_buffer_zoom (GObject *source_object, GAsyncResult *res, gp
   data->buffer = g_object_ref (buffer_zoomed);
 
   file = g_file_get_child (data->dir, self->priv->filename);
-  g_file_replace_async (file,
-                        NULL,
-                        TRUE,
-                        G_FILE_CREATE_REPLACE_DESTINATION,
-                        G_PRIORITY_DEFAULT,
-                        cancellable,
-                        photos_base_item_save_replace,
-                        g_object_ref (task));
+  photos_utils_file_create_async (file,
+                                  G_FILE_CREATE_NONE,
+                                  G_PRIORITY_DEFAULT,
+                                  cancellable,
+                                  photos_base_item_save_create,
+                                  g_object_ref (task));
 
  out:
   g_clear_object (&buffer_zoomed);
diff --git a/src/photos-utils.c b/src/photos-utils.c
index e85ebd9..64827b7 100644
--- a/src/photos-utils.c
+++ b/src/photos-utils.c
@@ -68,6 +68,18 @@
 
 static const gdouble EPSILON = 1e-5;
 
+typedef struct _PhotosUtilsFileCreateData PhotosUtilsFileCreateData;
+
+struct _PhotosUtilsFileCreateData
+{
+  GFile *dir;
+  GFileCreateFlags flags;
+  gchar *basename;
+  gchar *extension;
+  gint io_priority;
+  guint count;
+};
+
 
 static void
 photos_utils_put_pixel (guchar *p)
@@ -935,6 +947,170 @@ photos_utils_filename_strip_extension (const gchar *filename_with_extension)
 }
 
 
+static void
+photos_utils_file_create_data_free (PhotosUtilsFileCreateData *data)
+{
+  g_object_unref (data->dir);
+  g_free (data->basename);
+  g_free (data->extension);
+  g_slice_free (PhotosUtilsFileCreateData, data);
+}
+
+
+static gchar *
+photos_utils_file_create_data_get_filename (PhotosUtilsFileCreateData *data)
+{
+  gchar *ret_val;
+
+  if (data->count > 0)
+    ret_val = g_strdup_printf ("%s(%u)%s", data->basename, data->count, data->extension);
+  else
+    ret_val = g_strdup_printf ("%s%s", data->basename, data->extension);
+
+  return ret_val;
+}
+
+
+static PhotosUtilsFileCreateData *
+photos_utils_file_create_data_new (GFile *file, GFileCreateFlags flags, gint io_priority)
+{
+  PhotosUtilsFileCreateData *data;
+  gchar *filename;
+
+  data = g_slice_new0 (PhotosUtilsFileCreateData);
+
+  filename = g_file_get_basename (file);
+  data->dir = g_file_get_parent (file);
+  data->basename = photos_utils_filename_strip_extension (filename);
+  data->extension = g_strdup (photos_utils_filename_get_extension_offset (filename));
+  data->count = 0;
+  data->flags = flags;
+  data->io_priority = io_priority;
+
+  g_free (filename);
+
+  return data;
+}
+
+
+static void
+photos_utils_file_create_create (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  GCancellable *cancellable;
+  GError *error = NULL;
+  GFile *file = G_FILE (source_object);
+  GFile *unique_file = NULL;
+  GFileOutputStream *stream = NULL;
+  GTask *task = G_TASK (user_data);
+  PhotosUtilsFileCreateData *data;
+  gchar *filename = NULL;
+
+  cancellable = g_task_get_cancellable (task);
+  data = (PhotosUtilsFileCreateData *) g_task_get_task_data (task);
+
+  stream = g_file_create_finish (file, res, &error);
+  if (error != NULL)
+    {
+
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+        {
+          g_task_return_error (task, error);
+          goto out;
+        }
+
+      if (data->count == G_MAXUINT)
+        {
+          g_task_return_new_error (task, PHOTOS_ERROR, 0, "Exceeded number of copies of a file");
+          goto out;
+        }
+
+      data->count++;
+
+      filename = photos_utils_file_create_data_get_filename (data);
+      unique_file = g_file_get_child (data->dir, filename);
+
+      g_file_create_async (unique_file,
+                           data->flags,
+                           data->io_priority,
+                           cancellable,
+                           photos_utils_file_create_create,
+                           g_object_ref (task));
+
+      goto out;
+    }
+
+  g_task_return_pointer (task, g_object_ref (stream), g_object_unref);
+
+ out:
+  g_free (filename);
+  g_clear_object (&stream);
+  g_clear_object (&unique_file);
+  g_object_unref (task);
+}
+
+
+void
+photos_utils_file_create_async (GFile *file,
+                                GFileCreateFlags flags,
+                                gint io_priority,
+                                GCancellable *cancellable,
+                                GAsyncReadyCallback callback,
+                                gpointer user_data)
+{
+  GTask *task;
+  PhotosUtilsFileCreateData *data;
+
+  task = g_task_new (file, cancellable, callback, user_data);
+  g_task_set_source_tag (task, photos_utils_file_create_async);
+
+  data = photos_utils_file_create_data_new (file, flags, io_priority);
+  g_task_set_task_data (task, data, (GDestroyNotify) photos_utils_file_create_data_free);
+
+  g_file_create_async (file,
+                       data->flags,
+                       data->io_priority,
+                       cancellable,
+                       photos_utils_file_create_create,
+                       g_object_ref (task));
+
+  g_object_unref (task);
+}
+
+
+GFileOutputStream *
+photos_utils_file_create_finish (GFile *file, GAsyncResult *res, GFile **out_unique_file, GError **error)
+{
+  GTask *task = G_TASK (res);
+  GFileOutputStream *ret_val = NULL;
+  PhotosUtilsFileCreateData *data;
+
+  g_return_val_if_fail (g_task_is_valid (res, file), NULL);
+  g_return_val_if_fail (g_task_get_source_tag (task) == photos_utils_file_create_async, NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  data = (PhotosUtilsFileCreateData *) g_task_get_task_data (task);
+  g_return_val_if_fail (data != NULL, NULL);
+
+  ret_val = g_task_propagate_pointer (task, error);
+  if (ret_val == NULL)
+    goto out;
+
+  if (out_unique_file != NULL)
+    {
+      GFile *unique_file;
+      gchar *filename = NULL;
+
+      filename = photos_utils_file_create_data_get_filename (data);
+      unique_file = g_file_get_child (data->dir, filename);
+      *out_unique_file = unique_file;
+      g_free (filename);
+    }
+
+ out:
+  return ret_val;
+}
+
+
 GQuark
 photos_utils_flash_off_quark (void)
 {
diff --git a/src/photos-utils.h b/src/photos-utils.h
index 86b8a90..b788e4d 100644
--- a/src/photos-utils.h
+++ b/src/photos-utils.h
@@ -121,6 +121,18 @@ gdouble          photos_utils_eval_radial_line            (gdouble crop_center_x
                                                            gdouble corner_y,
                                                            gdouble event_x);
 
+void             photos_utils_file_create_async           (GFile *file,
+                                                           GFileCreateFlags flags,
+                                                           gint io_priority,
+                                                           GCancellable *cancellable,
+                                                           GAsyncReadyCallback callback,
+                                                           gpointer user_data);
+
+GFileOutputStream *photos_utils_file_create_finish        (GFile *file,
+                                                           GAsyncResult *res,
+                                                           GFile **out_unique_file,
+                                                           GError **error);
+
 gchar           *photos_utils_filename_strip_extension    (const gchar *filename_with_extension);
 
 GQuark           photos_utils_flash_off_quark             (void);
@@ -129,6 +141,7 @@ GQuark           photos_utils_flash_on_quark              (void);
 
 gchar           *photos_utils_get_extension_from_mime_type (const gchar *mime_type);
 
+
 gint             photos_utils_get_icon_size               (void);
 
 gint             photos_utils_get_icon_size_unscaled      (void);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]