[easytag/wip/musicbrainz-support] Implemented Cancellation of Manual Search using GCancellable



commit efcab260b54d697c889a52a9a47bbb2804d9218c
Author: Abhinav <abhijangda hotmail com>
Date:   Sun May 25 21:31:13 2014 +0530

    Implemented Cancellation of Manual Search using GCancellable

 src/mb_search.c          |  137 ++++++++++++++++++++++++++++++++++++++++------
 src/mb_search.h          |    8 ++-
 src/mbentityview.c       |   83 ++++++++++++++++++++++++++--
 src/musicbrainz_dialog.c |   58 +++++++++++++++++---
 4 files changed, 253 insertions(+), 33 deletions(-)
---
diff --git a/src/mb_search.c b/src/mb_search.c
index 735c7cb..3fd2d46 100644
--- a/src/mb_search.c
+++ b/src/mb_search.c
@@ -21,6 +21,14 @@
 #include "mb_search.h"
 #include "musicbrainz_dialog.h"
 
+void
+et_set_cancel_error (GError **error)
+{
+    g_set_error (error, ET_MB5_SEARCH_ERROR,
+                 ET_MB5_SEARCH_ERROR_CANCELLED,
+                 "Operation cancelled by user");
+}
+
 /*
  * et_mb5_search_error_quark:
  *
@@ -36,19 +44,21 @@ et_mb5_search_error_quark (void)
 
 /*
  * et_musicbrainz_search_in_entity:
- * @string:
- * @child_type:
- * @parent_type:
- * @parent_mbid:
- * @root:
+ * @child_type: Type of the children to get.
+ * @parent_type: Type of the parent.
+ * @parent_mbid: MBID of parent entity.
+ * @root: GNode of parent.
+ * @error: GError
  *
+ * To retrieve children entities of a parent entity.
  *
+ * Returns: TRUE if successfull, FALSE if not.
  */
 gboolean
 et_musicbrainz_search_in_entity (enum MB_ENTITY_TYPE child_type,
                                  enum MB_ENTITY_TYPE parent_type,
                                  gchar *parent_mbid, GNode *root,
-                                 GError **error)
+                                 GError **error, GCancellable *cancellable)
 {
     Mb5Query query;
     Mb5Metadata metadata;
@@ -57,6 +67,15 @@ et_musicbrainz_search_in_entity (enum MB_ENTITY_TYPE child_type,
     char *param_values[1];
     char *param_names[1];
 
+    if (g_cancellable_is_cancelled (cancellable))
+    {
+        g_set_error (error, ET_MB5_SEARCH_ERROR,
+                     ET_MB5_SEARCH_ERROR_CANCELLED,
+                     "Operation cancelled by user");
+        g_assert (error == NULL || *error != NULL);
+        return FALSE;
+    }
+
     param_names [0] = "inc";
     query = mb5_query_new ("easytag", NULL, 0);
 
@@ -77,6 +96,17 @@ et_musicbrainz_search_in_entity (enum MB_ENTITY_TYPE child_type,
                 Mb5Artist artist;
                 gchar *message;
 
+                if (g_cancellable_is_cancelled (cancellable))
+                {
+                    g_set_error (error, ET_MB5_SEARCH_ERROR,
+                                 ET_MB5_SEARCH_ERROR_CANCELLED,
+                                 "Operation cancelled by user");
+                    mb5_query_delete (query);
+                    mb5_metadata_delete (metadata);
+                    g_assert (error == NULL || *error != NULL);
+                    return FALSE;
+                }
+
                 artist = mb5_metadata_get_artist (metadata);
                 list = mb5_artist_get_releaselist (artist);
                 param_values[0] = "artists release-groups";
@@ -96,6 +126,17 @@ et_musicbrainz_search_in_entity (enum MB_ENTITY_TYPE child_type,
                         EtMbEntity *entity;
                         int size;
 
+                        if (g_cancellable_is_cancelled (cancellable))
+                        {
+                            g_set_error (error, ET_MB5_SEARCH_ERROR,
+                                         ET_MB5_SEARCH_ERROR_CANCELLED,
+                                         "Operation cancelled by user");
+                            mb5_query_delete (query);
+                            mb5_metadata_delete (metadata);
+                            g_assert (error == NULL || *error != NULL);
+                            return FALSE;
+                        }
+
                         size = mb5_release_get_title ((Mb5Release)release, buf,
                                                       sizeof (buf));
                         buf [size] = '\0';
@@ -174,6 +215,17 @@ et_musicbrainz_search_in_entity (enum MB_ENTITY_TYPE child_type,
                         {
                             Mb5Recording recording;
 
+                            if (g_cancellable_is_cancelled (cancellable))
+                            {
+                                g_set_error (error, ET_MB5_SEARCH_ERROR,
+                                             ET_MB5_SEARCH_ERROR_CANCELLED,
+                                             "Operation cancelled by user");
+                                mb5_query_delete (query);
+                                mb5_metadata_delete (metadata);
+                                g_assert (error == NULL || *error != NULL);
+                                return FALSE;
+                            }
+
                             recording = mb5_track_get_recording (mb5_track_list_item (track_list, j));
                             size = mb5_recording_get_title (recording, buf,
                                                             sizeof (buf));
@@ -263,15 +315,18 @@ et_musicbrainz_search_in_entity (enum MB_ENTITY_TYPE child_type,
 
 /*
  * et_musicbrainz_search:
- * @string:
- * @type:
- * @root:
+ * @string: String to search in MusicBrainz database.
+ * @type: Type of entity to search.
+ * @root: Root of the mbTree.
+ * @error: GError.
  *
+ * To search for an entity in MusicBrainz Database.
  *
+ * Returns: TRUE if successfull, FALSE if not.
  */
 gboolean
 et_musicbrainz_search (gchar *string, enum MB_ENTITY_TYPE type, GNode *root,
-                       GError **error)
+                       GError **error, GCancellable *cancellable)
 {
     Mb5Query query;
     Mb5Metadata metadata;
@@ -287,12 +342,24 @@ et_musicbrainz_search (gchar *string, enum MB_ENTITY_TYPE type, GNode *root,
     param_values [1] = SEARCH_LIMIT_STR;
     query = mb5_query_new ("easytag", NULL, 0);
 
+    if (g_cancellable_is_cancelled (cancellable))
+    {
+        g_set_error (error, ET_MB5_SEARCH_ERROR,
+                     ET_MB5_SEARCH_ERROR_CANCELLED,
+                     "Operation cancelled by user");
+        mb5_query_delete (query);
+        g_assert (error == NULL || *error != NULL);
+        return FALSE;
+    }
+
     if (type == MB_ENTITY_TYPE_ARTIST)
     {
         param_values [0] = g_strconcat ("artist:", string, NULL);
         metadata = mb5_query_query (query, "artist", "", "", 2, param_names,
                                     param_values);
+        g_free (param_values [0]);
         result = mb5_query_get_lastresult (query);
+
         if (result == eQuery_Success)
         {
             if (metadata)
@@ -304,7 +371,20 @@ et_musicbrainz_search (gchar *string, enum MB_ENTITY_TYPE type, GNode *root,
                 for (i = 0; i < mb5_artist_list_size (list); i++)
                 {
                     Mb5Artist artist;
+
+                    if (g_cancellable_is_cancelled (cancellable))
+                    {
+                        g_set_error (error, ET_MB5_SEARCH_ERROR,
+                                     ET_MB5_SEARCH_ERROR_CANCELLED,
+                                     "Operation cancelled by user");
+                        mb5_query_delete (query);
+                        mb5_metadata_delete (metadata);
+                        g_assert (error == NULL || *error != NULL);
+                        return FALSE;
+                    }
+
                     artist = mb5_artist_list_item (list, i);
+
                     if (artist)
                     {
                         GNode *node;
@@ -319,7 +399,6 @@ et_musicbrainz_search (gchar *string, enum MB_ENTITY_TYPE type, GNode *root,
                 }
             }
 
-            g_free (param_values [0]);
             mb5_metadata_delete (metadata);
         }
         else
@@ -342,20 +421,33 @@ et_musicbrainz_search (gchar *string, enum MB_ENTITY_TYPE type, GNode *root,
             {
                 int i;
                 Mb5ReleaseList list;
+                gchar *message;
+
                 list = mb5_metadata_get_releaselist (metadata);
                 param_names [0] = "inc";
                 param_values [0] = "artists release-groups";
+                message = g_strdup_printf ("Found %d Album(s)",
+                                           mb5_release_list_size (list));
+                et_show_status_msg_in_idle (message);
+                g_free (message);
 
                 for (i = 0; i < mb5_release_list_size (list); i++)
                 {
                     Mb5Release release;
-                    gchar *message;
 
-                    message = g_strdup_printf ("Found %d Album(s)", mb5_release_list_size (list));
-                    et_show_status_msg_in_idle (message);
-                    g_free (message);
+                    if (g_cancellable_is_cancelled (cancellable))
+                    {
+                        g_set_error (error, ET_MB5_SEARCH_ERROR,
+                                     ET_MB5_SEARCH_ERROR_CANCELLED,
+                                     "Operation cancelled by user");
+                        mb5_query_delete (query);
+                        mb5_metadata_delete (metadata);
+                        g_assert (error == NULL || *error != NULL);
+                        return FALSE;
+                    }
 
                     release = mb5_release_list_item (list, i);
+
                     if (release)
                     {
                         Mb5Metadata metadata_release;
@@ -432,6 +524,17 @@ et_musicbrainz_search (gchar *string, enum MB_ENTITY_TYPE type, GNode *root,
                     EtMbEntity *entity;
                     int size;
 
+                    if (g_cancellable_is_cancelled (cancellable))
+                    {
+                        g_set_error (error, ET_MB5_SEARCH_ERROR,
+                                     ET_MB5_SEARCH_ERROR_CANCELLED,
+                                     "Operation cancelled by user");
+                        mb5_query_delete (query);
+                        mb5_metadata_delete (metadata);
+                        g_assert (error == NULL || *error != NULL);
+                        return FALSE;
+                    }
+
                     recording = mb5_recording_list_item (list, i);
                     size = mb5_recording_get_title (recording, buf, sizeof (buf));
                     buf [size] = '\0';
@@ -517,9 +620,9 @@ et_musicbrainz_search (gchar *string, enum MB_ENTITY_TYPE type, GNode *root,
 
 /*
  * free_mb_tree:
- * @node:
- *
+ * @node: Root of the tree to start freeing with.
  *
+ * To free the memory occupied by the tree.
  */
 void
 free_mb_tree (GNode *node)
diff --git a/src/mb_search.h b/src/mb_search.h
index 4f7035f..b937099 100644
--- a/src/mb_search.h
+++ b/src/mb_search.h
@@ -50,6 +50,7 @@ GQuark et_mb5_search_error_quark (void);
  * @ET_MB5_SEARCH_ERROR_FETCH: Cannot Fetch Data
  * @ET_MB5_SEARCH_ERROR_REQUEST: Request to MusicBrainz Server cannot be made
  * @ET_MB5_SEARCH_ERROR_RESOURCE_NOT_FOUND: Resource user is trying to search is not found
+ * @ET_MB5_SEARCH_ERROR_CANCELLED: Operation was cancelled
  *
  * Errors while searching MusicBrainz Server.
  */
@@ -61,6 +62,7 @@ typedef enum
     ET_MB5_SEARCH_ERROR_FETCH,
     ET_MB5_SEARCH_ERROR_REQUEST,
     ET_MB5_SEARCH_ERROR_RESOURCE_NOT_FOUND,
+    ET_MB5_SEARCH_ERROR_CANCELLED,
 } EtMB5SearchError;
 
 enum MB_ENTITY_TYPE
@@ -86,10 +88,12 @@ gboolean
 et_musicbrainz_search_in_entity (enum MB_ENTITY_TYPE child_type,
                                  enum MB_ENTITY_TYPE parent_type,
                                  gchar *parent_mbid, GNode *root,
-                                 GError **error);
+                                 GError **error, GCancellable *cancellable);
 gboolean
 et_musicbrainz_search (gchar *string, enum MB_ENTITY_TYPE type, GNode *root,
-                       GError **error);
+                       GError **error, GCancellable *cancellable);
 void
 free_mb_tree (GNode *node);
+void
+et_set_cancel_error (GError **error);
 #endif /* __MB_SEARCH_H__ */
\ No newline at end of file
diff --git a/src/mbentityview.c b/src/mbentityview.c
index 3a36a2d..bfcb5bf 100644
--- a/src/mbentityview.c
+++ b/src/mbentityview.c
@@ -54,13 +54,17 @@ static GSimpleAsyncResult *async_result;
 /*
  * EtMbEntityViewPrivate:
  * @bread_crumb_box: GtkBox which represents the BreadCrumbWidget
- * @tree_view: GtkTreeView to display the recieved music brainz data
  * @bread_crumb_nodes: Array of GNode being displayed by the GtkToggleButton
+ * @tree_view: GtkTreeView to display the recieved music brainz data
  * @list_store: GtkTreeStore for treeView
  * @scrolled_window: GtkScrolledWindow for treeView
  * @mb_tree_root: Root Node of the Mb5Entity Tree
  * @mb_tree_current_node: Current node being displayed by EtMbEntityView
  * @active_toggle_button: Current active GtkToggleToolButton
+ * @filter: GtkTreeModelFilter to filter rows based on the conditions
+ * @search_or_red: Toggle Red Lines or Search in results
+ * @toggle_red_lines: Display Red Lines or not
+ * @text_to_search_in_results: Text to search in results
  *
  * Private data for EtMbEntityView.
  */
@@ -75,11 +79,19 @@ typedef struct
     GNode *mb_tree_current_node;
     GtkWidget *active_toggle_button;
     GtkTreeModel *filter;
-    gboolean search_or_red;
+    int search_or_red;
     gboolean toggle_red_lines;
     const gchar *text_to_search_in_results;
 } EtMbEntityViewPrivate;
 
+/*
+ * SearchInLevelThreadData:
+ * @entity_view: EtMbEntityView
+ * @child: GNode for which to retrieve children
+ * @iter: Iter to the row of GNode
+ *
+ * Used for passing to Thread Function of Search In Levels.
+ */
 typedef struct
 {
     EtMbEntityView *entity_view;
@@ -113,6 +125,16 @@ tree_filter_visible_func (GtkTreeModel *model, GtkTreeIter *iter,
  * Functions *
  *************/
 
+/*
+ * tree_filter_visible_func:
+ * @model: GtkTreeModel
+ * @iter: GtkTreeIter of current tree row
+ * @data: user data passed
+ *
+ * Filter function of GtkTreeModelFilter
+ *
+ * Returns: TRUE if row should be displayed or FALSE if it shouldn't.
+ */
 static gboolean
 tree_filter_visible_func (GtkTreeModel *model, GtkTreeIter *iter,
                           gpointer data)
@@ -269,7 +291,6 @@ add_iter_to_list_store (GtkListStore *list_store, GNode *node)
         {
             case MB_ENTITY_TYPE_ARTIST:
                 mb5_artist_get_name ((Mb5Artist)entity, name, sizeof (name));
-                //gtk_list_store_append (list_store, &iter);
                 gtk_list_store_insert_with_values (list_store, &iter, -1,
                                         MB_ARTIST_COLUMNS_N, "black", -1);
                 gtk_list_store_set (list_store, &iter, 
@@ -300,7 +321,6 @@ add_iter_to_list_store (GtkListStore *list_store, GNode *node)
             case MB_ENTITY_TYPE_ALBUM:
                 mb5_release_get_title ((Mb5Release)entity, name,
                                        sizeof (name));
-                //gtk_list_store_append (list_store, &iter);
                 gtk_list_store_insert_with_values (list_store, &iter, -1,
                                         MB_ALBUM_COLUMNS_N, "black", -1);
                 gtk_list_store_set (list_store, &iter, 
@@ -554,6 +574,14 @@ toggle_button_clicked (GtkWidget *btn, gpointer user_data)
     show_data_in_entity_view (entity_view);
 }
 
+/*
+ * manual_search_callback:
+ * @source: Source Object
+ * @res: GAsyncResult
+ * @user_data: User data
+ *
+ * Callback function for GAsyncResult for Search In Levels.
+ */
 static void
 search_in_levels_callback (GObject *source, GAsyncResult *res,
                            gpointer user_data)
@@ -566,6 +594,13 @@ search_in_levels_callback (GObject *source, GAsyncResult *res,
     GList *children;
     GList *active_child;
 
+    if (!g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res)))
+    {
+        g_object_unref (res);
+        g_free (user_data);
+        return;
+    }
+
     thread_data = user_data;
     entity_view = thread_data->entity_view;
     priv = ET_MB_ENTITY_VIEW_GET_PRIVATE (entity_view);
@@ -612,6 +647,14 @@ search_in_levels_callback (GObject *source, GAsyncResult *res,
     g_free (thread_data);
 }
 
+/*
+ * manual_search_callback:
+ * @res: GAsyncResult
+ * @obj: Source Object
+ * @cancellable: User data
+ *
+ * Search in Levels Thread Function
+ */
 static void
 search_in_levels_thread_func (GSimpleAsyncResult *res, GObject *obj,
                               GCancellable *cancellable)
@@ -625,6 +668,18 @@ search_in_levels_thread_func (GSimpleAsyncResult *res, GObject *obj,
 
     child_entity_type_str = NULL;
     thread_data = g_async_result_get_user_data (G_ASYNC_RESULT (res));
+    g_simple_async_result_set_op_res_gboolean (res, FALSE);
+
+    if (g_cancellable_is_cancelled (cancellable))
+    {
+        g_set_error (&error, ET_MB5_SEARCH_ERROR,
+                     ET_MB5_SEARCH_ERROR_CANCELLED,
+                     "Operation cancelled by user");
+        g_simple_async_report_gerror_in_idle (NULL,
+                                              mb5_search_error_callback,
+                                              NULL, error);
+        return;
+    }
 
     if (((EtMbEntity *)thread_data->child->data)->type ==
         MB_ENTITY_TYPE_TRACK)
@@ -657,14 +712,29 @@ search_in_levels_thread_func (GSimpleAsyncResult *res, GObject *obj,
     g_free (status_msg);
     g_free (child_entity_type_str);
 
+    if (g_cancellable_is_cancelled (cancellable))
+    {
+        g_set_error (&error, ET_MB5_SEARCH_ERROR,
+                     ET_MB5_SEARCH_ERROR_CANCELLED,
+                     "Operation cancelled by user");
+        g_simple_async_report_gerror_in_idle (NULL,
+                                              mb5_search_error_callback,
+                                              NULL, error);
+        return;
+    }
+
     if (!et_musicbrainz_search_in_entity (((EtMbEntity *)thread_data->child->data)->type + 1,
                                           ((EtMbEntity *)thread_data->child->data)->type,
-                                          mbid, thread_data->child, &error))
+                                          mbid, thread_data->child, &error,
+                                          cancellable))
     {
         g_simple_async_report_gerror_in_idle (NULL,
                                               mb5_search_error_callback,
                                               thread_data, error);
+        return;
     }
+
+    g_simple_async_result_set_op_res_gboolean (res, TRUE);
 }
 
 /*
@@ -721,9 +791,10 @@ tree_view_row_activated (GtkTreeView *tree_view, GtkTreePath *path,
                                               search_in_levels_callback,
                                               thread_data,
                                               tree_view_row_activated);
+    mb5_search_cancellable = g_cancellable_new ();
     g_simple_async_result_run_in_thread (async_result,
                                          search_in_levels_thread_func,
-                                         0, NULL);
+                                         0, mb5_search_cancellable);
 }
 
 /*
diff --git a/src/musicbrainz_dialog.c b/src/musicbrainz_dialog.c
index d36ad78..2287d4b 100644
--- a/src/musicbrainz_dialog.c
+++ b/src/musicbrainz_dialog.c
@@ -62,6 +62,15 @@ manual_search_callback (GObject *source, GAsyncResult *res,
 {
     GtkComboBoxText *combo_box;
 
+    if (!g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res)))
+    {
+        g_object_unref (res);
+        g_free (user_data);
+        free_mb_tree (mb_tree_root);
+        mb_tree_root = g_node_new (NULL);
+        return;
+    }
+
     et_mb_entity_view_set_tree_root (ET_MB_ENTITY_VIEW (entityView),
                                      mb_tree_root);
     gtk_statusbar_push (GTK_STATUSBAR (gtk_builder_get_object (builder, "statusbar")),
@@ -98,7 +107,7 @@ et_show_status_msg_in_idle_cb (GObject *obj, GAsyncResult *res,
  * @res: GAsyncResult
  * @user_data: User data
  *
- * Function to Display StatusBar Message.
+ * Function to Display StatusBar Messages in Main Thread.
  */
 void
 et_show_status_msg_in_idle (gchar *message)
@@ -113,12 +122,12 @@ et_show_status_msg_in_idle (gchar *message)
 }
 
 /*
- * et_show_status_msg_in_idle_cb:
+ * mb5_search_error_callback:
  * @obj: Source Object
  * @res: GAsyncResult
  * @user_data: User data
  *
- * Callback function for Displaying StatusBar Message.
+ * Callback function for displaying errors.
  */
 void
 mb5_search_error_callback (GObject *source, GAsyncResult *res,
@@ -133,7 +142,6 @@ mb5_search_error_callback (GObject *source, GAsyncResult *res,
     gtk_statusbar_push (GTK_STATUSBAR (gtk_builder_get_object (builder, "statusbar")),
                         0, dest->message);
     g_error_free (dest);
-    g_free (user_data);
 }
 
 /*
@@ -153,18 +161,52 @@ manual_search_thread_func (GSimpleAsyncResult *res, GObject *obj,
     gchar *status_msg;
 
     error = NULL;
+    g_simple_async_result_set_op_res_gboolean (res, FALSE);
+
+    if (g_cancellable_is_cancelled (cancellable))
+    {
+        et_set_cancel_error (&error);
+        g_simple_async_report_gerror_in_idle (NULL,
+                                              mb5_search_error_callback,
+                                              NULL, error);
+        return;
+    }
+
     thread_data = (ManualSearchThreadData *)g_async_result_get_user_data (G_ASYNC_RESULT (res));
-    status_msg = g_strconcat ("Searching ", thread_data->text_to_search, NULL);
+    status_msg = g_strconcat ("Searching ", thread_data->text_to_search,
+                              NULL);
     et_show_status_msg_in_idle (status_msg);
     g_free (status_msg);
 
+    if (g_cancellable_is_cancelled (cancellable))
+    {
+        et_set_cancel_error (&error);
+        g_simple_async_report_gerror_in_idle (NULL,
+                                              mb5_search_error_callback,
+                                              thread_data, error);
+        return;
+    }
+
     if (!et_musicbrainz_search (thread_data->text_to_search,
-                                thread_data->type, mb_tree_root, &error))
+                                thread_data->type, mb_tree_root, &error,
+                                cancellable))
+    {
+        g_simple_async_report_gerror_in_idle (NULL,
+                                              mb5_search_error_callback,
+                                              thread_data, error);
+        return;
+    }
+
+    if (g_cancellable_is_cancelled (cancellable))
     {
+        et_set_cancel_error (&error);
         g_simple_async_report_gerror_in_idle (NULL,
                                               mb5_search_error_callback,
                                               thread_data, error);
+        return;
     }
+
+    g_simple_async_result_set_op_res_gboolean (res, TRUE);
 }
 
 /*
@@ -197,7 +239,6 @@ btn_manual_find_clicked (GtkWidget *btn, gpointer user_data)
     thread_data->type = type;
     thread_data->text_to_search = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (cb_manual_search));
     mb5_search_cancellable = g_cancellable_new ();
-    /* TODO: Add Cancellable object */
     gtk_statusbar_push (GTK_STATUSBAR (gtk_builder_get_object (builder, "statusbar")),
                         0, "Starting MusicBrainz Search");
     async_result = g_simple_async_result_new (NULL, manual_search_callback, 
@@ -330,7 +371,8 @@ static void
 entry_tree_view_search_changed (GtkEditable *editable, gpointer user_data)
 {
     et_mb_entity_view_search_in_results (ET_MB_ENTITY_VIEW (entityView),
-                                         gtk_entry_get_text (GTK_ENTRY (gtk_builder_get_object (builder, 
"entryTreeViewSearch"))));
+                                         gtk_entry_get_text (GTK_ENTRY (gtk_builder_get_object (builder,
+                                                                        "entryTreeViewSearch"))));
 }
 
 /*


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