[grilo/wip/hadess/remove-op-flags] core: Add remove operation flags




commit 95dd1c21806c732bb61ad9a79645c51698cdddd4
Author: Bastien Nocera <hadess hadess net>
Date:   Fri Feb 21 16:22:22 2014 +0100

    core: Add remove operation flags
    
    Allow passing remove flags to backends to implement various
    kinds of removals (deletion, moving to trash, marking as
    read/watched, etc.)
    
    https://bugzilla.gnome.org/show_bug.cgi?id=724308

 src/grl-caps.c                   |  35 +++++++++++
 src/grl-caps.h                   |  26 +++++++++
 src/grl-operation-options-priv.h |   1 +
 src/grl-operation-options.c      |  57 ++++++++++++++++++
 src/grl-operation-options.h      |   5 ++
 src/grl-source.c                 | 122 +++++++++++++++++++++++++++++++++++----
 src/grl-source.h                 |  14 ++++-
 tools/grilo-test-ui/main.c       |  35 ++++++++++-
 8 files changed, 282 insertions(+), 13 deletions(-)
---
diff --git a/src/grl-caps.c b/src/grl-caps.c
index 4e9368f..00c185e 100644
--- a/src/grl-caps.c
+++ b/src/grl-caps.c
@@ -56,6 +56,7 @@
 struct _GrlCapsPrivate {
   GHashTable *data;
   GrlTypeFilter type_filter;
+  GrlRemoveFlags remove_flags;
   GList *key_filter;
   GList *key_range_filter;
 };
@@ -90,6 +91,7 @@ grl_caps_init (GrlCaps *self)
   self->priv->type_filter = GRL_TYPE_FILTER_NONE;
   self->priv->key_filter = NULL;
   self->priv->key_range_filter = NULL;
+  self->priv->remove_flags = GRL_REMOVE_FLAG_UNKNOWN;
 }
 
 static void
@@ -194,6 +196,39 @@ grl_caps_set_type_filter (GrlCaps *caps, GrlTypeFilter filter)
   caps->priv->type_filter = filter;
 }
 
+/**
+ * grl_caps_get_remove_flags:
+ * @caps: a #GrlCaps instance
+ *
+ * Returns: the supported #GrlRemoveFlags
+ *
+ * Since: 0.2.0
+ **/
+GrlRemoveFlags
+grl_caps_get_remove_flags (GrlCaps *caps)
+{
+  g_return_val_if_fail (caps != NULL, GRL_REMOVE_FLAG_UNKNOWN);
+
+  return caps->priv->remove_flags;
+}
+
+/**
+ * grl_caps_set_remove_flags:
+ * @caps: a #GrlCaps instance
+ * @remove_flags: a #GrlRemoveFlags
+ *
+ * Sets the supported remove capability.
+ *
+ * Since: 0.2.0
+ **/
+void
+grl_caps_set_remove_flags (GrlCaps *caps, GrlRemoveFlags remove_flags)
+{
+  g_return_if_fail (caps != NULL);
+
+  caps->priv->remove_flags = remove_flags;
+}
+
 /**
  * grl_caps_get_key_filter:
  * @caps: a #GrlCaps instance
diff --git a/src/grl-caps.h b/src/grl-caps.h
index df2e939..5825a0b 100644
--- a/src/grl-caps.h
+++ b/src/grl-caps.h
@@ -84,6 +84,28 @@ typedef enum {
   GRL_TYPE_FILTER_ALL = (GRL_TYPE_FILTER_AUDIO | GRL_TYPE_FILTER_VIDEO | GRL_TYPE_FILTER_IMAGE)
 } GrlTypeFilter;
 
+/**
+ * GrlRemoveFlags:
+ * @GRL_REMOVE_FLAG_UNKNOWN: the #GrlSource did not set any GrlRemoveFlags
+ * @GRL_REMOVE_FLAG_NONE: does not support removals
+ * @GRL_REMOVE_FLAG_DELETE: will unrecoverably delete media
+ * @GRL_REMOVE_FLAG_TRASH: will move media to the trash
+ * @GRL_REMOVE_FLAG_ARCHIVE: will move the media to an archive (which might
+ *   or might not be available through grilo)
+ * @GRL_REMOVE_FLAG_REFERENCE: will remove the reference to the media, not the media.
+ *
+ * Bitwise flags which reflect the kind of removal that a
+ * #GrlSource supports.
+ */
+typedef enum{
+  GRL_REMOVE_FLAG_UNKNOWN    = -1,
+  GRL_REMOVE_FLAG_NONE       = 0,
+  GRL_REMOVE_FLAG_DELETE     = 1,
+  GRL_REMOVE_FLAG_TRASH      = 1 << 1,
+  GRL_REMOVE_FLAG_ARCHIVE    = 1 << 2,
+  GRL_REMOVE_FLAG_REFERENCE  = 1 << 3
+} GrlRemoveFlags;
+
 
 GType grl_caps_get_type (void);
 
@@ -109,6 +131,10 @@ void grl_caps_set_key_range_filter (GrlCaps *caps, GList *keys);
 
 gboolean grl_caps_is_key_range_filter (GrlCaps *caps, GrlKeyID key);
 
+GrlRemoveFlags grl_caps_get_remove_flags (GrlCaps *caps);
+
+void grl_caps_set_remove_flags (GrlCaps *caps, GrlRemoveFlags remove_flags);
+
 G_END_DECLS
 
 #endif /* _GRL_CAPS_H_ */
diff --git a/src/grl-operation-options-priv.h b/src/grl-operation-options-priv.h
index 49de323..6cd8d3b 100644
--- a/src/grl-operation-options-priv.h
+++ b/src/grl-operation-options-priv.h
@@ -34,6 +34,7 @@
 #define GRL_OPERATION_OPTION_SKIP "skip"
 #define GRL_OPERATION_OPTION_COUNT "count"
 #define GRL_OPERATION_OPTION_RESOLUTION_FLAGS "resolution-flags"
+#define GRL_OPERATION_OPTION_REMOVE_FLAGS "remove-flags"
 #define GRL_OPERATION_OPTION_TYPE_FILTER "type-filter"
 #define GRL_OPERATION_OPTION_KEY_EQUAL_FILTER "key-equal-filter"
 #define GRL_OPERATION_OPTION_KEY_RANGE_FILTER "key-range-filter"
diff --git a/src/grl-operation-options.c b/src/grl-operation-options.c
index ec50d65..c1c9791 100644
--- a/src/grl-operation-options.c
+++ b/src/grl-operation-options.c
@@ -49,6 +49,7 @@ G_DEFINE_TYPE_WITH_PRIVATE (GrlOperationOptions, grl_operation_options, G_TYPE_O
 #define SKIP_DEFAULT 0;
 #define COUNT_DEFAULT GRL_COUNT_INFINITY;
 #define RESOLUTION_FLAGS_DEFAULT GRL_RESOLVE_NORMAL;
+#define REMOVE_FLAGS_DEFAULT GRL_REMOVE_FLAG_UNKNOWN
 #define TYPE_FILTER_DEFAULT GRL_TYPE_FILTER_ALL;
 
 static void
@@ -208,6 +209,7 @@ grl_operation_options_obey_caps (GrlOperationOptions *options,
     copy_option (options, *supported_options, GRL_OPERATION_OPTION_SKIP);
     copy_option (options, *supported_options, GRL_OPERATION_OPTION_COUNT);
     copy_option (options, *supported_options, GRL_OPERATION_OPTION_RESOLUTION_FLAGS);
+    copy_option (options, *supported_options, GRL_OPERATION_OPTION_REMOVE_FLAGS);
   }
 
   if (unsupported_options)
@@ -279,6 +281,7 @@ grl_operation_options_copy (GrlOperationOptions *options)
   copy_option (options, copy, GRL_OPERATION_OPTION_SKIP);
   copy_option (options, copy, GRL_OPERATION_OPTION_COUNT);
   copy_option (options, copy, GRL_OPERATION_OPTION_RESOLUTION_FLAGS);
+  copy_option (options, copy, GRL_OPERATION_OPTION_REMOVE_FLAGS);
   copy_option (options, copy, GRL_OPERATION_OPTION_TYPE_FILTER);
 
   g_hash_table_foreach (options->priv->key_filter,
@@ -465,6 +468,60 @@ grl_operation_options_get_resolution_flags (GrlOperationOptions *options)
   return RESOLUTION_FLAGS_DEFAULT;
 }
 
+/**
+ * grl_operation_options_set_remove_flags:
+ * @options: a #GrlOperationOptions instance
+ * @flags: the remove flags to be set for an operation. See
+ * #GrlRemoveFlags for possible values.
+ *
+ * Set the remove flags for a remove operation. Will only succeed if @flags obey
+ * to the inherent capabilities of @options.
+ *
+ * Returns: %TRUE if @flags could be set, %FALSE otherwise.
+ *
+ * Since: 0.2.0
+ */
+gboolean
+grl_operation_options_set_remove_flags (GrlOperationOptions *options,
+                                        GrlRemoveFlags flags)
+{
+  GValue value = { 0, };
+
+  /* FIXME: I think we should use mk_enum to have a GType for
+   * GrlRemoveFlags */
+  g_value_init (&value, G_TYPE_UINT);
+  g_value_set_uint (&value, flags);
+  set_value (options, GRL_OPERATION_OPTION_REMOVE_FLAGS, &value);
+  g_value_unset (&value);
+
+  return TRUE;
+}
+
+/**
+ * grl_operation_options_get_remove_flags:
+ * @options: a #GrlOperationOptions instance
+ *
+ * Returns: remove flags of @options.
+ *
+ * Since: 0.2.0
+ */
+GrlRemoveFlags
+grl_operation_options_get_remove_flags (GrlOperationOptions *options)
+{
+  const GValue *value;
+
+  if (options)
+    value = g_hash_table_lookup (options->priv->data,
+                                 GRL_OPERATION_OPTION_REMOVE_FLAGS);
+  else
+    value = NULL;
+
+  if (value)
+    return g_value_get_uint (value);
+
+  return REMOVE_FLAGS_DEFAULT;
+}
+
 /**
  * grl_operation_options_set_type_filter:
  * @options: a #GrlOperationOptions instance
diff --git a/src/grl-operation-options.h b/src/grl-operation-options.h
index 60bb10d..65a88db 100644
--- a/src/grl-operation-options.h
+++ b/src/grl-operation-options.h
@@ -117,6 +117,11 @@ gboolean grl_operation_options_set_resolution_flags (GrlOperationOptions *option
 GrlResolutionFlags
     grl_operation_options_get_resolution_flags (GrlOperationOptions *options);
 
+gboolean grl_operation_options_set_remove_flags (GrlOperationOptions *options,
+                                                 GrlRemoveFlags flags);
+GrlRemoveFlags
+    grl_operation_options_get_remove_flags (GrlOperationOptions *options);
+
 gboolean grl_operation_options_set_type_filter (GrlOperationOptions *options,
                                                 GrlTypeFilter filter);
 
diff --git a/src/grl-source.c b/src/grl-source.c
index b368c88..fb340b3 100644
--- a/src/grl-source.c
+++ b/src/grl-source.c
@@ -1208,6 +1208,7 @@ remove_relay_free (struct RemoveRelayCb *rrc)
   if (rrc->spec) {
     g_object_unref (rrc->spec->source);
     g_object_unref (rrc->spec->media);
+    g_object_unref (rrc->spec->options);
     g_free (rrc->spec->media_id);
     g_free (rrc->spec);
   }
@@ -4158,9 +4159,43 @@ grl_source_query_sync (GrlSource *source,
   return result;
 }
 
+static gboolean
+remove_flags_supported (GrlSource *source,
+                        GrlOperationOptions *options)
+{
+  GrlRemoveFlags flags, source_flags;
+  guint i, num_flags;
+  GrlCaps *caps;
+
+  flags = grl_operation_options_get_remove_flags (options);
+
+  /* 1) Check that no more than one flag is set */
+  if (flags == GRL_REMOVE_FLAG_UNKNOWN)
+    return TRUE;
+  if (flags == GRL_REMOVE_FLAG_NONE)
+    return FALSE;
+
+  num_flags = 0;
+  for (i = 1; i <= 4; i++) {
+    if (flags & (1 << i))
+      num_flags++;
+  }
+  if (num_flags > 1)
+    return FALSE;
+
+  /* 2) Check that it matches what the source supports */
+  caps = grl_source_get_caps (source, GRL_OP_REMOVE);
+  source_flags = grl_caps_get_remove_flags (caps);
+  if (!(source_flags & flags))
+    return FALSE;
+
+  return TRUE;
+}
+
 static gboolean
 grl_source_store_remove_impl (GrlSource *source,
                               GrlMedia *media,
+                              GrlOperationOptions *options,
                               GrlSourceRemoveCb callback,
                               gpointer user_data)
 {
@@ -4183,6 +4218,14 @@ grl_source_store_remove_impl (GrlSource *source,
   rrc->user_callback = callback;
   rrc->user_data = user_data;
 
+  if (!remove_flags_supported (source, options)) {
+    rrc->error = g_error_new (GRL_CORE_ERROR,
+                              GRL_CORE_ERROR_REMOVE_FAILED,
+                              _("Removal type not supported, cannot remove"));
+    rrc->spec = NULL;
+    goto end;
+  }
+
   /* Check that we have the minimum information we need */
   id = grl_media_get_id (media);
   if (!id) {
@@ -4190,17 +4233,20 @@ grl_source_store_remove_impl (GrlSource *source,
                               GRL_CORE_ERROR_REMOVE_FAILED,
                               _("Media has no “id”, cannot remove"));
     rrc->spec = NULL;
-  } else {
-    rrc->error = NULL;
-    rs = g_new0 (GrlSourceRemoveSpec, 1);
-    rs->source = g_object_ref (source);
-    rs->media_id = g_strdup (id);
-    rs->media = g_object_ref (media);
-    rs->callback = remove_result_relay_cb;
-    rs->user_data = rrc;
-    rrc->spec = rs;
+    goto end;
   }
 
+  rrc->error = NULL;
+  rs = g_new0 (GrlSourceRemoveSpec, 1);
+  rs->source = g_object_ref (source);
+  rs->media_id = g_strdup (id);
+  rs->media = g_object_ref (media);
+  rs->options = options ? g_object_ref (options) : NULL;
+  rs->callback = remove_result_relay_cb;
+  rs->user_data = rrc;
+  rrc->spec = rs;
+
+end:
   tag_id = g_idle_add (remove_idle, rrc);
   g_source_set_name_by_id (tag_id, "[grilo] remove_idle");
 
@@ -4226,7 +4272,7 @@ grl_source_remove (GrlSource *source,
                    GrlSourceRemoveCb callback,
                    gpointer user_data)
 {
-  grl_source_store_remove_impl (source, media, callback, user_data);
+  grl_source_store_remove_impl (source, media, NULL, callback, user_data);
 }
 
 /**
@@ -4245,6 +4291,52 @@ void
 grl_source_remove_sync (GrlSource *source,
                         GrlMedia *media,
                         GError **error)
+{
+  grl_source_remove_with_options_sync (source, media, NULL, error);
+}
+
+/**
+ * grl_source_remove_with_options:
+ * @source: a source
+ * @media: a data transfer object
+ * @options: options to pass to this operation
+ * @callback: (scope notified): the user defined callback
+ * @user_data: the user data to pass in the callback
+ *
+ * Remove a @media from the @source repository.
+ *
+ * This method is asynchronous.
+ *
+ * Since: 0.2.12
+ */
+void
+grl_source_remove_with_options (GrlSource *source,
+                                GrlMedia *media,
+                                GrlOperationOptions *options,
+                                GrlSourceRemoveCb callback,
+                                gpointer user_data)
+{
+  grl_source_store_remove_impl (source, media, options, callback, user_data);
+}
+
+/**
+ * grl_source_remove_with_options_sync:
+ * @source: a source
+ * @media: a data transfer object
+ * @options: options to pass to this operation
+ * @error: a #GError, or @NULL
+ *
+ * Remove a @media from the @source repository.
+ *
+ * This method is synchronous.
+ *
+ * Since: 0.2.12
+ */
+void
+grl_source_remove_with_options_sync (GrlSource *source,
+                                     GrlMedia *media,
+                                     GrlOperationOptions *options,
+                                     GError **error)
 {
   GrlDataSync *ds;
 
@@ -4252,6 +4344,7 @@ grl_source_remove_sync (GrlSource *source,
 
   if (grl_source_store_remove_impl (source,
                                     media,
+                                    options,
                                     remove_async_cb,
                                     ds))
     grl_wait_for_async_operation_complete (ds);
@@ -4664,6 +4757,7 @@ grl_source_get_caps (GrlSource *source,
                      GrlSupportedOps operation)
 {
   static GrlCaps *default_caps = NULL;
+  static GrlCaps *default_caps_no_remove = NULL;
   GrlSourceClass *klass = GRL_SOURCE_GET_CLASS (source);
 
   if (klass->get_caps)
@@ -4671,6 +4765,12 @@ grl_source_get_caps (GrlSource *source,
 
   if (!default_caps)
     default_caps = grl_caps_new ();
+  if (!default_caps_no_remove) {
+    default_caps_no_remove = grl_caps_new ();
+    grl_caps_set_remove_flags (default_caps_no_remove, GRL_REMOVE_FLAG_NONE);
+  }
 
-  return default_caps;
+  if (grl_source_supported_operations (source) & GRL_OP_REMOVE)
+    return default_caps;
+  return default_caps_no_remove;
 }
diff --git a/src/grl-source.h b/src/grl-source.h
index 574d608..97b6fb5 100644
--- a/src/grl-source.h
+++ b/src/grl-source.h
@@ -360,9 +360,10 @@ typedef struct {
   GrlMedia *media;
   GrlSourceRemoveCb callback;
   gpointer user_data;
+  GrlOperationOptions *options;
 
   /*< private >*/
-  gpointer _grl_reserved[GRL_PADDING];
+  gpointer _grl_reserved[GRL_PADDING - 1];
 } GrlSourceRemoveSpec;
 
 /**
@@ -597,6 +598,17 @@ void grl_source_remove_sync (GrlSource *source,
                              GrlMedia *media,
                              GError **error);
 
+void grl_source_remove_with_options (GrlSource *source,
+                                     GrlMedia *media,
+                                     GrlOperationOptions *options,
+                                     GrlSourceRemoveCb callback,
+                                     gpointer user_data);
+
+void grl_source_remove_with_options_sync (GrlSource *source,
+                                          GrlMedia *media,
+                                          GrlOperationOptions *options,
+                                          GError **error);
+
 void grl_source_store (GrlSource *source,
                        GrlMedia *parent,
                        GrlMedia *media,
diff --git a/tools/grilo-test-ui/main.c b/tools/grilo-test-ui/main.c
index 17e5389..8976b91 100644
--- a/tools/grilo-test-ui/main.c
+++ b/tools/grilo-test-ui/main.c
@@ -1001,6 +1001,33 @@ tags_to_str (char **tags)
   return g_string_free (s, FALSE);
 }
 
+static char *
+remove_flags_to_str (GrlCaps *caps)
+{
+  GrlRemoveFlags flags;
+  GString *str;
+
+  if (caps == NULL)
+    return NULL;
+  flags = grl_caps_get_remove_flags (caps);
+  if (flags == GRL_REMOVE_FLAG_UNKNOWN)
+    return g_strdup ("unknown");
+  if (flags == GRL_REMOVE_FLAG_NONE)
+    return g_strdup ("none");
+  str = g_string_new ("");
+  if (flags & GRL_REMOVE_FLAG_DELETE)
+    g_string_append (str, "delete, ");
+  if (flags & GRL_REMOVE_FLAG_TRASH)
+    g_string_append (str, "trash, ");
+  if (flags & GRL_REMOVE_FLAG_ARCHIVE)
+    g_string_append (str, "archive, ");
+  if (flags & GRL_REMOVE_FLAG_REFERENCE)
+    g_string_append (str, "reference, ");
+  if (str->len > 0)
+    g_string_set_size (str, str->len - 2);
+  return g_string_free (str, FALSE);
+}
+
 static void
 populate_source_metadata (GrlSource *source)
 {
@@ -1025,6 +1052,7 @@ populate_source_metadata (GrlSource *source)
     GrlSupportedMedia supported_media;
     GIcon *icon;
     char **tags;
+    GrlCaps *caps;
 
     for (i = 0; i < G_N_ELEMENTS (str_props); i++) {
       g_object_get (G_OBJECT (source), str_props[i], &str, NULL);
@@ -1063,6 +1091,11 @@ populate_source_metadata (GrlSource *source)
     add_source_metadata (view->metadata_model, "source-tags", str);
     g_free (str);
     g_strfreev (tags);
+
+    caps = grl_source_get_caps (source, GRL_OP_REMOVE);
+    str = remove_flags_to_str (caps);
+    add_source_metadata (view->metadata_model, "remove-flags", str);
+    g_free (str);
   }
 
   gtk_widget_set_sensitive (view->show_btn, FALSE);
@@ -1349,7 +1382,7 @@ remove_btn_clicked_cb (GtkButton *btn, gpointer user_data)
                      BROWSER_MODEL_CONTENT, &media,
                      -1);
 
-  grl_source_remove (source, media, remove_cb, NULL);
+  grl_source_remove_with_options (source, media, NULL, remove_cb, NULL);
 
   g_clear_object (&source);
   g_clear_object (&media);


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