[gthumb: 17/40] Fixed other bugs related to image loading cancellation



commit 6e314a9e55faed4523c242d5a1d312b50c7e65ef
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Wed Sep 8 17:38:48 2010 +0200

    Fixed other bugs related to image loading cancellation

 extensions/file_viewer/gth-file-viewer-page.c     |   33 ++-
 extensions/image_print/gth-load-image-info-task.c |    2 -
 extensions/list_tools/gth-script.c                |    1 -
 extensions/webalbums/gth-web-exporter.c           |    2 -
 gthumb/gth-browser.c                              |    8 +-
 gthumb/gth-file-list.c                            |  275 +++++++++++++--------
 gthumb/gth-hook.c                                 |    7 +
 gthumb/gth-image-loader.c                         |   28 +--
 gthumb/gth-image-loader.h                         |    7 +-
 gthumb/gth-image-preloader.c                      |   49 +++--
 gthumb/gth-main.h                                 |    9 +-
 gthumb/gth-overwrite-dialog.c                     |    2 -
 gthumb/gth-thumb-loader.c                         |  101 +++++---
 gthumb/gth-thumb-loader.h                         |    1 -
 gthumb/pixbuf-io.h                                |    7 +
 15 files changed, 340 insertions(+), 192 deletions(-)
---
diff --git a/extensions/file_viewer/gth-file-viewer-page.c b/extensions/file_viewer/gth-file-viewer-page.c
index f2824d7..b90f1bf 100644
--- a/extensions/file_viewer/gth-file-viewer-page.c
+++ b/extensions/file_viewer/gth-file-viewer-page.c
@@ -191,31 +191,46 @@ gth_file_viewer_page_real_can_view (GthViewerPage *base,
 }
 
 
+typedef struct {
+	GthFileViewerPage *self;
+	GthFileData       *file_data;
+} ViewData;
+
+
+static void
+view_data_free (ViewData *view_data)
+{
+	g_object_unref (view_data->file_data);
+	g_object_unref (view_data->self);
+	g_free (view_data);
+}
+
+
 static void
 thumb_loader_ready_cb (GObject      *source_object,
 		       GAsyncResult *result,
 		       gpointer      user_data)
 {
-	GthFileViewerPage *self = user_data;
-	GthFileData       *file_data;
+	ViewData          *view_data = user_data;
+	GthFileViewerPage *self = view_data->self;
 	GdkPixbuf         *pixbuf;
 
 	if (! gth_thumb_loader_load_finish (GTH_THUMB_LOADER (source_object),
 					    result,
-					    &file_data,
 					    &pixbuf,
 					    NULL))
 	{
+		view_data_free (view_data);
 		return;
 	}
 
-	if (g_file_equal (self->priv->file_data->file, file_data->file)) {
+	if (g_file_equal (self->priv->file_data->file, view_data->file_data->file)) {
 		gtk_image_set_from_pixbuf (GTK_IMAGE (self->priv->icon), pixbuf);
 		gth_viewer_page_file_loaded (GTH_VIEWER_PAGE (self), self->priv->file_data, TRUE);
 	}
 
 	g_object_unref (pixbuf);
-	g_object_unref (file_data);
+	view_data_free (view_data);
 }
 
 
@@ -225,6 +240,7 @@ gth_file_viewer_page_real_view (GthViewerPage *base,
 {
 	GthFileViewerPage *self;
 	GIcon             *icon;
+	ViewData          *view_data;
 
 	self = (GthFileViewerPage*) base;
 	g_return_if_fail (file_data != NULL);
@@ -239,11 +255,14 @@ gth_file_viewer_page_real_view (GthViewerPage *base,
 
 	gth_viewer_page_focus (GTH_VIEWER_PAGE (self));
 
+	view_data = g_new0 (ViewData, 1);
+	view_data->self = g_object_ref (self);
+	view_data->file_data = g_object_ref (file_data);
 	gth_thumb_loader_load (self->priv->thumb_loader,
-			       self->priv->file_data,
+			       view_data->file_data,
 			       NULL,
 			       thumb_loader_ready_cb,
-			       self);
+			       view_data);
 }
 
 
diff --git a/extensions/image_print/gth-load-image-info-task.c b/extensions/image_print/gth-load-image-info-task.c
index a6b84c3..fbf7dc5 100644
--- a/extensions/image_print/gth-load-image-info-task.c
+++ b/extensions/image_print/gth-load-image-info-task.c
@@ -114,8 +114,6 @@ image_loader_ready_cb (GObject      *source_object,
 
 	gth_image_loader_load_image_finish (GTH_IMAGE_LOADER (source_object),
 					    result,
-					    NULL,
-					    NULL,
 					    &pixbuf,
 					    NULL,
 					    NULL,
diff --git a/extensions/list_tools/gth-script.c b/extensions/list_tools/gth-script.c
index ec230ac..118a72c 100644
--- a/extensions/list_tools/gth-script.c
+++ b/extensions/list_tools/gth-script.c
@@ -599,7 +599,6 @@ thumb_loader_ready_cb (GObject      *source_object,
 
 	if (! gth_thumb_loader_load_finish (GTH_THUMB_LOADER (source_object),
 		  	 	 	    result,
-		  	 	 	    NULL,
 		  	 	 	    &pixbuf,
 		  	 	 	    NULL))
 	{
diff --git a/extensions/webalbums/gth-web-exporter.c b/extensions/webalbums/gth-web-exporter.c
index f9d6bec..d1eb060 100644
--- a/extensions/webalbums/gth-web-exporter.c
+++ b/extensions/webalbums/gth-web-exporter.c
@@ -2578,8 +2578,6 @@ image_loader_ready_cb (GObject      *source_object,
 
 	if (! gth_image_loader_load_image_finish (GTH_IMAGE_LOADER (source_object),
 						  result,
-						  NULL,
-						  NULL,
 						  &pixbuf,
 						  NULL,
 						  NULL,
diff --git a/gthumb/gth-browser.c b/gthumb/gth-browser.c
index 3635ef4..b22e92a 100644
--- a/gthumb/gth-browser.c
+++ b/gthumb/gth-browser.c
@@ -2044,7 +2044,9 @@ _gth_browser_close_step4 (gpointer user_data)
 {
 	GthBrowser *browser = user_data;
 
-	gth_file_list_cancel (GTH_FILE_LIST (browser->priv->thumbnail_list), _gth_browser_close_final_step, browser);
+	gth_file_list_cancel (GTH_FILE_LIST (browser->priv->thumbnail_list),
+			      _gth_browser_close_final_step,
+			      browser);
 }
 
 
@@ -2053,7 +2055,9 @@ _gth_browser_close_step3 (gpointer user_data)
 {
 	GthBrowser *browser = user_data;
 
-	gth_file_list_cancel (GTH_FILE_LIST (browser->priv->file_list), _gth_browser_close_step4, browser);
+	gth_file_list_cancel (GTH_FILE_LIST (browser->priv->file_list),
+			      _gth_browser_close_step4,
+			      browser);
 }
 
 
diff --git a/gthumb/gth-file-list.c b/gthumb/gth-file-list.c
index 5f34680..b0a4a2b 100644
--- a/gthumb/gth-file-list.c
+++ b/gthumb/gth-file-list.c
@@ -34,11 +34,14 @@
 #include "gth-thumb-loader.h"
 #include "gtk-utils.h"
 
+
 #define DEFAULT_THUMBNAIL_SIZE 112
 #define UPDATE_THUMBNAILS_TIMEOUT 200
 #define N_LOOKAHEAD 50
 #define EMPTY (N_("(Empty)"))
 #define THUMBNAIL_BORDER (8 * 2)
+#define CHECK_JOBS_INTERVAL 100
+
 
 typedef enum {
 	GTH_FILE_LIST_OP_TYPE_SET_FILES,
@@ -107,26 +110,17 @@ struct _GthFileListPrivateData
 	gboolean         ignore_hidden_thumbs;
 	GHashTable      *thumb_data;
 	GthThumbLoader  *thumb_loader;
-	gboolean         update_thumb_in_view;
-	int              thumb_pos;
-	int              n_thumb;
-	GthFileData     *thumb_fd;
 	gboolean         loading_thumbs;
 	gboolean         cancel;
 	gboolean         dirty;
 	guint            dirty_event;
 	guint            restart_thumb_update;
 	GList           *queue; /* list of GthFileListOp */
+	GList           *jobs; /* list of ThumbnailJob */
 	GtkCellRenderer *thumbnail_renderer;
 	GtkCellRenderer *text_renderer;
 	GtkCellRenderer *checkbox_renderer;
-
 	char           **caption_attributes_v;
-
-	gboolean         can_cancel;
-	GCancellable    *cancellable;
-	DataFunc         done_func;
-	gpointer         done_func_data;
 };
 
 
@@ -251,7 +245,6 @@ gth_file_list_finalize (GObject *object)
 	file_list = GTH_FILE_LIST (object);
 
 	if (file_list->priv != NULL) {
-		g_object_unref (file_list->priv->cancellable);
 		g_hash_table_unref (file_list->priv->thumb_data);
 		if (file_list->priv->icon_cache != NULL)
 			gth_icon_cache_free (file_list->priv->icon_cache);
@@ -354,14 +347,11 @@ static void
 gth_file_list_init (GthFileList *file_list)
 {
 	file_list->priv = g_new0 (GthFileListPrivateData, 1);
-
 	file_list->priv->thumb_data = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, (GDestroyNotify) thumb_data_unref);
 	file_list->priv->thumb_size = DEFAULT_THUMBNAIL_SIZE;
 	file_list->priv->ignore_hidden_thumbs = FALSE;
 	file_list->priv->load_thumbs = TRUE;
 	file_list->priv->caption_attributes_v = g_strsplit ("none", ",", -1);
-	file_list->priv->cancellable = g_cancellable_new ();
-	file_list->priv->can_cancel = FALSE;
 }
 
 
@@ -371,8 +361,6 @@ static void _gth_file_list_update_next_thumb (GthFileList *file_list);
 static void
 start_update_next_thumb (GthFileList *file_list)
 {
-	GthFileStore *file_store;
-
 	if (file_list->priv->loading_thumbs)
 		return;
 
@@ -381,8 +369,6 @@ start_update_next_thumb (GthFileList *file_list)
 		return;
 	}
 
-	file_store = (GthFileStore*) gth_file_view_get_model (GTH_FILE_VIEW (file_list->priv->view));
-	file_list->priv->n_thumb = -1;
 	file_list->priv->loading_thumbs = TRUE;
 	_gth_file_list_update_next_thumb (file_list);
 }
@@ -699,39 +685,116 @@ gth_file_list_set_type (GthFileList     *file_list,
 
 
 static void
-_gth_file_list_thumb_cleanup (GthFileList *file_list)
+_gth_file_list_done (GthFileList *file_list)
 {
-	_g_object_unref (file_list->priv->thumb_fd);
-	file_list->priv->thumb_fd = NULL;
+	file_list->priv->loading_thumbs = FALSE;
+	file_list->priv->cancel = FALSE;
 }
 
 
+
+typedef struct {
+	GthFileList    *file_list;
+	GthThumbLoader *loader;
+	GCancellable   *cancellable;
+	GthFileData    *file_data;
+	gboolean        update_in_view;
+} ThumbnailJob;
+
+
 static void
-_gth_file_list_done (GthFileList *file_list)
+thumbnail_job_free (ThumbnailJob *job)
 {
-	_gth_file_list_thumb_cleanup (file_list);
-	file_list->priv->loading_thumbs = FALSE;
-	file_list->priv->cancel = FALSE;
-	g_cancellable_reset (file_list->priv->cancellable);
+	job->file_list->priv->jobs = g_list_remove (job->file_list->priv->jobs, job);
+	_g_object_unref (job->file_data);
+	_g_object_unref (job->cancellable);
+	_g_object_unref (job->loader);
+	_g_object_unref (job->file_list);
+	g_free (job);
 }
 
 
-void
-gth_file_list_cancel (GthFileList *file_list,
-		      DataFunc     done_func,
-		      gpointer     user_data)
+static void
+thumbnail_job_cancel (ThumbnailJob *job)
 {
-	_gth_file_list_clear_queue (file_list);
+	g_cancellable_cancel (job->cancellable);
+}
+
+
+typedef struct {
+	GthFileList *file_list;
+	DataFunc     done_func;
+	gpointer     user_data;
+	guint        check_id;
+} CancelData;
+
+
+static void
+cancel_data_free (CancelData *cancel_data)
+{
+	if (cancel_data->check_id != 0)
+		g_source_remove (cancel_data->check_id);
+	g_object_unref (cancel_data->file_list);
+	g_free (cancel_data);
+}
 
-	file_list->priv->done_func = done_func;
-	file_list->priv->done_func_data = user_data;
 
-	if (file_list->priv->can_cancel) {
-		g_cancellable_cancel (file_list->priv->cancellable);
-		file_list->priv->can_cancel = FALSE;
+static gboolean
+wait_for_jobs_to_finish (gpointer user_data)
+{
+	CancelData *cancel_data = user_data;
+
+	if (cancel_data->file_list->priv->jobs == NULL) {
+		if (cancel_data->check_id != 0) {
+			g_source_remove (cancel_data->check_id);
+			cancel_data->check_id = 0;
+		}
+		if (cancel_data->done_func != NULL)
+			cancel_data->done_func (cancel_data->user_data);
+		cancel_data_free (cancel_data);
+		return FALSE;
 	}
-	else
-		call_when_idle (done_func, user_data);
+
+	return TRUE;
+}
+
+
+static void
+_gth_file_list_cancel_jobs (GthFileList *file_list,
+			    DataFunc     done_func,
+			    gpointer     user_data)
+{
+	CancelData *cancel_data;
+	GList      *scan;
+
+	cancel_data = g_new0 (CancelData, 1);
+	cancel_data->file_list = g_object_ref (file_list);
+	cancel_data->done_func = done_func;
+	cancel_data->user_data = user_data;
+
+	if (file_list->priv->jobs == NULL) {
+		cancel_data->check_id = g_idle_add (wait_for_jobs_to_finish, cancel_data);
+		return;
+	}
+
+	for (scan = file_list->priv->jobs; scan; scan = scan->next) {
+		ThumbnailJob *job = scan->data;
+		thumbnail_job_cancel (job);
+	}
+
+	cancel_data->check_id = g_timeout_add (CHECK_JOBS_INTERVAL,
+					       wait_for_jobs_to_finish,
+					       cancel_data);
+}
+
+
+void
+gth_file_list_cancel (GthFileList    *file_list,
+		      DataFunc        done_func,
+		      gpointer        user_data)
+{
+	_gth_file_list_clear_queue (file_list);
+	_gth_file_list_cancel_jobs (file_list, done_func, user_data);
 }
 
 
@@ -1346,25 +1409,32 @@ set_mime_type_icon (GthFileList *file_list,
 
 
 static void
-thumb_loader_ready_cb (GObject      *source_object,
-		       GAsyncResult *result,
-		       gpointer      user_data)
-{
-	GthFileList *file_list = user_data;
-	ThumbData   *thumb_data;
-	GthFileData *file_data;
-	GdkPixbuf   *pixbuf;
-	GError      *error = NULL;
+thumbnail_job_ready_cb (GObject      *source_object,
+		        GAsyncResult *result,
+		        gpointer      user_data)
+{
+	ThumbnailJob *job = user_data;
+	GthFileList  *file_list = job->file_list;
+	gboolean      success;
+	GdkPixbuf    *pixbuf = NULL;
+	GError       *error = NULL;
+	ThumbData    *thumb_data;
 
-	file_list->priv->can_cancel = FALSE;
+	success = gth_thumb_loader_load_finish (GTH_THUMB_LOADER (source_object),
+						result,
+						&pixbuf,
+						&error);
 
-	if (file_list->priv->thumb_fd == NULL) {
-		_gth_file_list_update_next_thumb (file_list);
+	if (! success && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+		thumbnail_job_free (job);
+		_gth_file_list_done (file_list);
 		return;
 	}
 
-	thumb_data = g_hash_table_lookup (file_list->priv->thumb_data, file_list->priv->thumb_fd->file);
+	thumb_data = g_hash_table_lookup (file_list->priv->thumb_data, job->file_data->file);
 	if (thumb_data == NULL) {
+		_g_object_unref (pixbuf);
+		thumbnail_job_free (job);
 		_gth_file_list_update_next_thumb (file_list);
 		return;
 	}
@@ -1372,39 +1442,46 @@ thumb_loader_ready_cb (GObject      *source_object,
 	_g_object_unref (thumb_data->pixbuf);
 	thumb_data->pixbuf = NULL;
 
-	if (! gth_thumb_loader_load_finish (GTH_THUMB_LOADER (source_object),
-					    result,
-					    &file_data,
-					    &pixbuf,
-					    &error))
-	{
-		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
-			_gth_file_list_done (file_list);
-			if (file_list->priv->done_func)
-				(file_list->priv->done_func) (file_list->priv->done_func_data);
-			return;
-		}
+	if (! success) {
+		thumb_data->thumb_created = FALSE;
+		thumb_data->thumb_loaded = FALSE;
+		if (job->update_in_view)
+			set_mime_type_icon (file_list, job->file_data);
 
 		thumb_data->error = TRUE;
-		thumb_data->thumb_loaded = FALSE;
-		thumb_data->thumb_created = FALSE;
-		if (file_list->priv->update_thumb_in_view)
-			set_mime_type_icon (file_list, file_list->priv->thumb_fd);
 	}
 	else {
-		thumb_data->error = FALSE;
+		thumb_data->pixbuf = g_object_ref (pixbuf);
 		thumb_data->thumb_created = TRUE;
-		thumb_data->pixbuf = pixbuf;
-		if (file_list->priv->update_thumb_in_view) {
+		thumb_data->error = FALSE;
+		if (job->update_in_view) {
 			thumb_data->thumb_loaded = TRUE;
-			update_thumb_in_file_view (file_list, file_list->priv->thumb_fd);
+			update_thumb_in_file_view (file_list, job->file_data);
 		}
 	}
 
+	_g_object_unref (pixbuf);
+	thumbnail_job_free (job);
+
 	_gth_file_list_update_next_thumb (file_list);
 }
 
 
+static gboolean
+start_thumbnail_job (gpointer user_data)
+{
+	ThumbnailJob *job = user_data;
+
+	gth_thumb_loader_load (job->loader,
+			       job->file_data,
+			       job->cancellable,
+			       thumbnail_job_ready_cb,
+			       job);
+
+	return FALSE;
+}
+
+
 static void
 set_loading_icon (GthFileList *file_list,
 		  GthFileData *file_data)
@@ -1434,16 +1511,19 @@ set_loading_icon (GthFileList *file_list,
 
 
 static void
-_gth_file_list_update_current_thumb (GthFileList *file_list)
+_gth_file_list_update_thumb (GthFileList  *file_list,
+			     ThumbnailJob *job)
 {
-	set_loading_icon (file_list, file_list->priv->thumb_fd);
+	GList *scan;
+
+	for (scan = file_list->priv->jobs; scan; scan = scan->next) {
+		ThumbnailJob *job = scan->data;
+		thumbnail_job_cancel (job);
+	}
+	file_list->priv->jobs = g_list_prepend (file_list->priv->jobs, job);
 
-	file_list->priv->can_cancel = TRUE;
-	gth_thumb_loader_load (file_list->priv->thumb_loader,
-			       file_list->priv->thumb_fd,
-			       file_list->priv->cancellable,
-			       thumb_loader_ready_cb,
-			       file_list);
+	set_loading_icon (file_list, job->file_data);
+	g_idle_add (start_thumbnail_job, job);
 }
 
 
@@ -1506,17 +1586,18 @@ can_create_file_thumbnail (GthFileData *file_data,
 static void
 _gth_file_list_update_next_thumb (GthFileList *file_list)
 {
-	GthFileStore *file_store;
-	int           pos;
-	int           first_pos;
-	int           last_pos;
-	int           max_pos;
-	GthFileData  *file_data = NULL;
-	ThumbData    *thumb_data;
-	GList        *list, *scan;
-	int           new_pos = -1;
-	GTimeVal      current_time;
-	gboolean      young_file_found = FALSE;
+	GthFileStore  *file_store;
+	int            pos;
+	int            first_pos;
+	int            last_pos;
+	int            max_pos;
+	GthFileData   *file_data = NULL;
+	ThumbData     *thumb_data;
+	GList         *list, *scan;
+	int            new_pos = -1;
+	GTimeVal       current_time;
+	gboolean       young_file_found = FALSE;
+	ThumbnailJob  *job;
 
 	if (file_list->priv->cancel || (file_list->priv->queue != NULL)) {
 		g_idle_add (update_thumbs_stopped, file_list);
@@ -1620,14 +1701,14 @@ _gth_file_list_update_next_thumb (GthFileList *file_list)
 	   load the visible ones (and N_LOOKAHEAD before and N_LOOKAHEAD after the visible range),
 	   to minimize memory consumption in large folders. */
 
-	file_list->priv->update_thumb_in_view = (new_pos >= (first_pos - N_LOOKAHEAD)) &&
-						(new_pos <= (last_pos + N_LOOKAHEAD));
-	file_list->priv->thumb_pos = new_pos;
-	_g_object_unref (file_list->priv->thumb_fd);
-	file_list->priv->thumb_fd = file_data; /* already ref-ed above */
-	file_list->priv->n_thumb++;
+	job = g_new0 (ThumbnailJob, 1);
+	job->file_list = g_object_ref (file_list);
+	job->loader = gth_thumb_loader_new (file_list->priv->thumb_size);
+	job->cancellable = g_cancellable_new ();
+	job->file_data = file_data; /* already ref-ed above */
+	job->update_in_view = (new_pos >= (first_pos - N_LOOKAHEAD)) && (new_pos <= (last_pos + N_LOOKAHEAD));
 
-	_gth_file_list_update_current_thumb (file_list);
+	_gth_file_list_update_thumb (file_list, job);
 }
 
 
diff --git a/gthumb/gth-hook.c b/gthumb/gth-hook.c
index f9d0e3c..e4ef647 100644
--- a/gthumb/gth-hook.c
+++ b/gthumb/gth-hook.c
@@ -50,6 +50,7 @@ typedef struct {
 typedef struct {
 	GHookList *list;
 	int        n_args;
+	GMutex    *mutex;
 } GthHook;
 
 
@@ -58,6 +59,7 @@ gth_hook_free (GthHook *hook)
 {
 	g_hook_list_clear (hook->list);
 	g_free (hook->list);
+	g_mutex_free (hook->mutex);
 	g_free (hook);
 }
 
@@ -95,6 +97,7 @@ gth_hook_register (const char *name,
 	hook->list = g_new (GHookList, 1);
 	g_hook_list_init (hook->list, sizeof (GthHookCallback));
 	hook->n_args = n_args;
+	hook->mutex = g_mutex_new ();
 
 	g_hash_table_insert (hooks, g_strdup (name), hook);
 }
@@ -261,8 +264,10 @@ gth_hook_invoke (const char *name,
 		break;
 	}
 
+	g_mutex_lock (hook->mutex);
 	if (invoke_marshaller != NULL)
 		g_hook_list_marshal (hook->list, TRUE, invoke_marshaller, marshal_data);
+	g_mutex_unlock (hook->mutex);
 
 	g_free (marshal_data);
 }
@@ -373,8 +378,10 @@ gth_hook_invoke_get (const char *name,
 		break;
 	}
 
+	g_mutex_lock (hook->mutex);
 	if (invoke_marshaller != NULL)
 		g_hook_list_marshal (hook->list, TRUE, invoke_marshaller, marshal_data);
+	g_mutex_unlock (hook->mutex);
 
 	value = marshal_data[hook->n_args];
 
diff --git a/gthumb/gth-image-loader.c b/gthumb/gth-image-loader.c
index c047ddb..8dbf562 100644
--- a/gthumb/gth-image-loader.c
+++ b/gthumb/gth-image-loader.c
@@ -152,8 +152,6 @@ load_data_unref (LoadData *load_data)
 
 
 typedef struct {
-	GthFileData        *file_data;
-	int                 requested_size;
 	GdkPixbufAnimation *animation;
 	int                 original_width;
 	int                 original_height;
@@ -163,7 +161,6 @@ typedef struct {
 static void
 load_result_unref (LoadResult *load_result)
 {
-	g_object_unref (load_result->file_data);
 	if (load_result->animation != NULL)
 		g_object_unref (load_result->animation);
 	g_free (load_result);
@@ -214,8 +211,6 @@ load_pixbuf_thread (GSimpleAsyncResult *result,
 	}
 
 	load_result = g_new0 (LoadResult, 1);
-	load_result->file_data = g_object_ref (load_data->file_data);
-	load_result->requested_size = load_data->requested_size;
 	load_result->animation = animation;
 	load_result->original_width = original_width;
 	load_result->original_height = original_height;
@@ -254,8 +249,6 @@ gth_image_loader_load (GthImageLoader      *loader,
 gboolean
 gth_image_loader_load_animation_finish (GthImageLoader      *loader,
 					GAsyncResult        *result,
-					GthFileData        **file_data,
-					int                 *requested_size,
 					GdkPixbufAnimation **animation,
 					int                 *original_width,
 					int                 *original_height,
@@ -272,10 +265,6 @@ gth_image_loader_load_animation_finish (GthImageLoader      *loader,
 		  return FALSE;
 
 	  load_result = g_simple_async_result_get_op_res_gpointer (simple);
-	  if (file_data != NULL)
-		  *file_data = g_object_ref (load_result->file_data);
-	  if (requested_size != NULL)
-	  	  *requested_size = load_result->requested_size;
 	  if (animation != NULL)
 		  *animation = g_object_ref (load_result->animation);
 	  if (original_width != NULL)
@@ -290,19 +279,16 @@ gth_image_loader_load_animation_finish (GthImageLoader      *loader,
 gboolean
 gth_image_loader_load_image_finish (GthImageLoader  *loader,
 				    GAsyncResult    *res,
-				    GthFileData    **file_data,
-				    int             *requested_size,
 				    GdkPixbuf      **pixbuf,
 				    int             *original_width,
 				    int             *original_height,
 				    GError         **error)
 {
 	GdkPixbufAnimation *animation;
+	GdkPixbuf          *static_image;
 
 	if (! gth_image_loader_load_animation_finish (loader,
 						      res,
-						      file_data,
-						      requested_size,
 						      &animation,
 						      original_width,
 						      original_height,
@@ -311,7 +297,17 @@ gth_image_loader_load_image_finish (GthImageLoader  *loader,
 		return FALSE;
 	}
 
-	*pixbuf = gdk_pixbuf_animation_get_static_image (animation);
+	static_image = gdk_pixbuf_animation_get_static_image (animation);
+	if (static_image != NULL) {
+		*pixbuf = gdk_pixbuf_copy (static_image);
+	}
+	else {
+		*pixbuf = NULL;
+		if (error != NULL)
+			*error = g_error_new_literal (GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, "No image");
+	}
+
+	g_object_unref (animation);
 
 	return TRUE;
 }
diff --git a/gthumb/gth-image-loader.h b/gthumb/gth-image-loader.h
index 31d9c2b..e469177 100644
--- a/gthumb/gth-image-loader.h
+++ b/gthumb/gth-image-loader.h
@@ -24,9 +24,8 @@
 
 #include <glib.h>
 #include <gdk-pixbuf/gdk-pixbuf.h>
-#include "typedefs.h"
 #include "gth-file-data.h"
-#include "gth-main.h"
+#include "pixbuf-io.h"
 
 G_BEGIN_DECLS
 
@@ -64,16 +63,12 @@ void              gth_image_loader_load                   (GthImageLoader
 							   gpointer              user_data);
 gboolean          gth_image_loader_load_animation_finish  (GthImageLoader       *loader,
 							   GAsyncResult         *res,
-							   GthFileData         **file_data,
-							   int                  *requested_size,
 							   GdkPixbufAnimation  **animation,
 							   int                  *original_width,
 							   int                  *original_height,
 							   GError              **error);
 gboolean          gth_image_loader_load_image_finish      (GthImageLoader       *loader,
 						  	   GAsyncResult         *res,
-							   GthFileData         **file_data,
-							   int                  *requested_size,
 							   GdkPixbuf           **pixbuf,
 							   int                  *original_width,
 							   int                  *original_height,
diff --git a/gthumb/gth-image-preloader.c b/gthumb/gth-image-preloader.c
index 314e255..5cfd58a 100644
--- a/gthumb/gth-image-preloader.c
+++ b/gthumb/gth-image-preloader.c
@@ -326,16 +326,30 @@ load_next (gpointer data)
 }
 
 
+typedef struct {
+	Preloader   *preloader;
+	GthFileData *file_data;
+	int          requested_size;
+} LoadRequest;
+
+
+static void
+load_request_free (LoadRequest *load_request)
+{
+	g_object_unref (load_request->file_data);
+	g_free (load_request);
+}
+
+
 static void
 image_loader_ready_cb (GObject      *source_object,
 		       GAsyncResult *result,
 		       gpointer      user_data)
 {
-	Preloader          *preloader = user_data;
+	LoadRequest        *load_request = user_data;
+	Preloader          *preloader = load_request->preloader;
 	GthImagePreloader  *self = preloader->self;
-	GthFileData        *file_data;
-	int                 requested_size;
-	GdkPixbufAnimation *animation;
+	GdkPixbufAnimation *animation = NULL;
 	int                 original_width;
 	int                 original_height;
 	GError             *error = NULL;
@@ -344,18 +358,16 @@ image_loader_ready_cb (GObject      *source_object,
 
 	success = gth_image_loader_load_animation_finish  (GTH_IMAGE_LOADER (source_object),
 							   result,
-							   &file_data,
-							   &requested_size,
 							   &animation,
 							   &original_width,
 							   &original_height,
 							   &error);
 
-	if (! g_file_equal (file_data->file, preloader->file_data->file)
+	if (! g_file_equal (load_request->file_data->file, preloader->file_data->file)
 	    || (preloader->token != self->priv->token))
 	{
-		g_object_unref (file_data);
-		g_object_unref (animation);
+		load_request_free (load_request);
+		_g_object_unref (animation);
 		if (error != NULL)
 			g_error_free (error);
 		return;
@@ -364,14 +376,14 @@ image_loader_ready_cb (GObject      *source_object,
 	interval = NEXT_LOAD_SMALL_TIMEOUT;
 
 	_g_object_unref (preloader->animation);
-	preloader->animation = g_object_ref (animation);
+	preloader->animation = _g_object_ref (animation);
 	preloader->original_width = original_width;
 	preloader->original_height = original_height;
 	preloader->loaded = success;
 	preloader->error  = ! success;
-	preloader->requested_size = requested_size;
+	preloader->requested_size = load_request->requested_size;
 
-	if (_g_file_equal (file_data->file, self->priv->requested_file)) {
+	if (_g_file_equal (load_request->file_data->file, self->priv->requested_file)) {
 #if DEBUG_PRELOADER
 		debug (DEBUG_INFO, "[requested] %s => %s [size: %d]", (error == NULL) ? "ready" : "error", g_file_get_uri (preloader->file_data->file), preloader->requested_size);
 #endif
@@ -400,8 +412,8 @@ image_loader_ready_cb (GObject      *source_object,
 	if (self->priv->load_id == 0)
 		self->priv->load_id = g_timeout_add (interval, load_next, self);
 
-	g_object_unref (file_data);
-	g_object_unref (animation);
+	load_request_free (load_request);
+	_g_object_unref (animation);
 }
 
 
@@ -452,6 +464,8 @@ start_next_loader (GthImagePreloader *self)
 	preloader = current_preloader (self);
 
 	if (preloader != NULL) {
+		LoadRequest *load_request;
+
 #if DEBUG_PRELOADER
 		{
 			char *uri;
@@ -465,13 +479,18 @@ start_next_loader (GthImagePreloader *self)
 		_g_object_unref (preloader->animation);
 		preloader->animation = NULL;
 
+		load_request = g_new0 (LoadRequest, 1);
+		load_request->preloader = preloader;
+		load_request->file_data = g_object_ref (preloader->file_data);
+		load_request->requested_size = preloader->requested_size;
+
 		g_cancellable_reset (preloader->self->priv->cancellable);
 		gth_image_loader_load (preloader->loader,
 				       preloader->file_data,
 				       preloader->requested_size,
 				       preloader->self->priv->cancellable,
 				       image_loader_ready_cb,
-				       preloader);
+				       load_request);
 	}
 #if DEBUG_PRELOADER
 	else
diff --git a/gthumb/gth-main.h b/gthumb/gth-main.h
index f428755..df7458c 100644
--- a/gthumb/gth-main.h
+++ b/gthumb/gth-main.h
@@ -35,6 +35,7 @@
 #include "gth-pixbuf-saver.h"
 #include "gth-tags-file.h"
 #include "gth-test.h"
+#include "pixbuf-io.h"
 
 G_BEGIN_DECLS
 
@@ -49,14 +50,6 @@ typedef struct _GthMain         GthMain;
 typedef struct _GthMainPrivate  GthMainPrivate;
 typedef struct _GthMainClass    GthMainClass;
 
-typedef GdkPixbufAnimation* (*PixbufLoader) (GthFileData   *file_data,
-					     int            requested_size,
-					     int           *original_width,
-					     int           *original_height,
-					     gpointer       user_data,
-					     GCancellable  *cancellable,
-				   	     GError       **error);
-
 struct _GthMain {
 	GObject __parent;
 	GthMainPrivate *priv;
diff --git a/gthumb/gth-overwrite-dialog.c b/gthumb/gth-overwrite-dialog.c
index 9160c82..cc50d3f 100644
--- a/gthumb/gth-overwrite-dialog.c
+++ b/gthumb/gth-overwrite-dialog.c
@@ -129,8 +129,6 @@ image_loader_ready_cb (GObject      *source_object,
 
 	if (! gth_image_loader_load_image_finish (GTH_IMAGE_LOADER (source_object),
 						  result,
-						  NULL,
-						  NULL,
 						  &pixbuf,
 						  NULL,
 						  NULL,
diff --git a/gthumb/gth-thumb-loader.c b/gthumb/gth-thumb-loader.c
index 1f2efb5..790a961 100644
--- a/gthumb/gth-thumb-loader.c
+++ b/gthumb/gth-thumb-loader.c
@@ -44,6 +44,7 @@
 #define THUMBNAIL_NORMAL_SIZE	  128
 #define THUMBNAIL_DIR_PERMISSIONS 0700
 #define MAX_THUMBNAILER_LIFETIME  2000   /* kill the thumbnailer after this amount of time*/
+#define CHECK_CANCELLABLE_DELAY   200
 
 struct _GthThumbLoaderPrivate
 {
@@ -289,6 +290,7 @@ typedef struct {
 	GPid                thumbnailer_pid;
 	guint               thumbnailer_watch;
 	guint               thumbnailer_timeout;
+	guint               cancellable_watch;
 } LoadData;
 
 
@@ -352,8 +354,6 @@ cache_image_ready_cb (GObject      *source_object,
 
 	if (! gth_image_loader_load_image_finish (GTH_IMAGE_LOADER (source_object),
 						  res,
-						  NULL,
-						  NULL,
 						  &pixbuf,
 						  NULL,
 						  NULL,
@@ -375,6 +375,8 @@ cache_image_ready_cb (GObject      *source_object,
 	/* Thumbnail correctly loaded from the cache. Scale if the user wants
 	 * a different size. */
 
+	g_return_if_fail (pixbuf != NULL);
+
 	width = gdk_pixbuf_get_width (pixbuf);
 	height = gdk_pixbuf_get_height (pixbuf);
 	modified = scale_keeping_ratio (&width,
@@ -494,13 +496,16 @@ original_image_loaded_correctly (GthThumbLoader *self,
 				 LoadData        *load_data,
 				 GdkPixbuf       *pixbuf)
 {
+	GdkPixbuf  *local_pixbuf;
 	int         width;
 	int         height;
 	gboolean    modified;
 	LoadResult *load_result;
 
-	width = gdk_pixbuf_get_width (pixbuf);
-	height = gdk_pixbuf_get_height (pixbuf);
+	local_pixbuf = g_object_ref (pixbuf);
+
+	width = gdk_pixbuf_get_width (local_pixbuf);
+	height = gdk_pixbuf_get_height (local_pixbuf);
 
 	if (self->priv->save_thumbnails) {
 		gboolean modified;
@@ -515,12 +520,12 @@ original_image_loaded_correctly (GthThumbLoader *self,
 						self->priv->cache_max_size,
 						FALSE);
 		if (modified) {
-			GdkPixbuf *tmp = pixbuf;
-			pixbuf = _gdk_pixbuf_scale_simple_safe (tmp, width, height, GDK_INTERP_BILINEAR);
+			GdkPixbuf *tmp = local_pixbuf;
+			local_pixbuf = _gdk_pixbuf_scale_simple_safe (tmp, width, height, GDK_INTERP_BILINEAR);
 			g_object_unref (tmp);
 		}
 
-		_gth_thumb_loader_save_to_cache (self, load_data->file_data, pixbuf);
+		_gth_thumb_loader_save_to_cache (self, load_data->file_data, local_pixbuf);
 	}
 
 	/* Scale if the user wants a different size. */
@@ -530,16 +535,18 @@ original_image_loaded_correctly (GthThumbLoader *self,
 				    self->priv->requested_size,
 				    self->priv->cache_max_size);
 	if (modified) {
-		GdkPixbuf *tmp = pixbuf;
-		pixbuf = gdk_pixbuf_scale_simple (tmp, width, height, GDK_INTERP_BILINEAR);
+		GdkPixbuf *tmp = local_pixbuf;
+		local_pixbuf = gdk_pixbuf_scale_simple (tmp, width, height, GDK_INTERP_BILINEAR);
 		g_object_unref (tmp);
 	}
 
 	load_result = g_new0 (LoadResult, 1);
 	load_result->file_data = g_object_ref (load_data->file_data);
-	load_result->pixbuf = g_object_ref (pixbuf);
+	load_result->pixbuf = g_object_ref (local_pixbuf);
 	g_simple_async_result_set_op_res_gpointer (load_data->simple, load_result, (GDestroyNotify) load_result_unref);
 	g_simple_async_result_complete_in_idle (load_data->simple);
+
+	g_object_unref (local_pixbuf);
 }
 
 
@@ -548,7 +555,7 @@ failed_to_load_original_image (GthThumbLoader *self,
 			       LoadData       *load_data)
 {
 	char   *uri;
-	GError *error = NULL;
+	GError *error;
 
 	uri = g_file_get_uri (load_data->file_data->file);
 	gnome_desktop_thumbnail_factory_create_failed_thumbnail (self->priv->thumb_factory,
@@ -574,6 +581,11 @@ kill_thumbnailer_cb (gpointer user_data)
 		load_data->thumbnailer_timeout = 0;
 	}
 
+	if (load_data->cancellable_watch != 0) {
+		g_source_remove (load_data->cancellable_watch);
+		load_data->cancellable_watch = 0;
+	}
+
 	if (load_data->thumbnailer_pid != 0)
 		kill (load_data->thumbnailer_pid, SIGTERM);
 
@@ -581,6 +593,20 @@ kill_thumbnailer_cb (gpointer user_data)
 }
 
 
+static gboolean
+check_cancellable_cb (gpointer user_data)
+{
+	LoadData *load_data = user_data;
+
+	if (g_cancellable_is_cancelled (load_data->cancellable)) {
+		kill_thumbnailer_cb (user_data);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
 static void
 watch_thumbnailer_cb (GPid     pid,
 		      int      status,
@@ -595,6 +621,11 @@ watch_thumbnailer_cb (GPid     pid,
 		load_data->thumbnailer_timeout = 0;
 	}
 
+	if (load_data->cancellable_watch != 0) {
+		g_source_remove (load_data->cancellable_watch);
+		load_data->cancellable_watch = 0;
+	}
+
 	g_spawn_close_pid (pid);
 	load_data->thumbnailer_pid = 0;
 	load_data->thumbnailer_watch = 0;
@@ -620,13 +651,11 @@ original_image_ready_cb (GObject      *source_object,
 {
 	LoadData       *load_data = user_data;
 	GthThumbLoader *self = load_data->thumb_loader;
-	GdkPixbuf      *pixbuf;
+	GdkPixbuf      *pixbuf = NULL;
 	GError         *error = NULL;
 
 	if (! gth_image_loader_load_image_finish (GTH_IMAGE_LOADER (source_object),
 						  res,
-						  NULL,
-						  NULL,
 						  &pixbuf,
 						  NULL,
 						  NULL,
@@ -637,6 +666,12 @@ original_image_ready_cb (GObject      *source_object,
 
 		char *uri;
 
+		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+			g_simple_async_result_set_from_error (load_data->simple, error);
+				g_simple_async_result_complete_in_idle (load_data->simple);
+			return;
+		}
+
 		g_clear_error (&error);
 
 		uri = g_file_get_uri (load_data->file_data->file);
@@ -653,6 +688,9 @@ original_image_ready_cb (GObject      *source_object,
 			load_data->thumbnailer_timeout = g_timeout_add (MAX_THUMBNAILER_LIFETIME,
 									kill_thumbnailer_cb,
 									load_data);
+			load_data->cancellable_watch = g_timeout_add (CHECK_CANCELLABLE_DELAY,
+								      check_cancellable_cb,
+								      load_data);
 		}
 		else
 			failed_to_load_original_image (self, load_data);
@@ -663,7 +701,6 @@ original_image_ready_cb (GObject      *source_object,
 	}
 
 	original_image_loaded_correctly (self, load_data, pixbuf);
-
 	g_object_unref (pixbuf);
 }
 
@@ -691,12 +728,11 @@ gth_thumb_loader_load (GthThumbLoader      *self,
 
 		uri = g_file_get_uri (file_data->file);
 		mtime = gth_file_data_get_mtime (file_data);
-		cache_path = gnome_desktop_thumbnail_factory_lookup (self->priv->thumb_factory, uri, mtime);
 
-		if ((cache_path == NULL) && gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail (self->priv->thumb_factory, uri, mtime)) {
+		if (gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail (self->priv->thumb_factory, uri, mtime)) {
 			GError *error;
 
-			error = g_error_new_literal (GTH_ERROR, 0, "failed thumbnail");
+			error = g_error_new_literal (GTH_ERROR, 0, "found a failed thumbnail");
 			g_simple_async_result_set_from_error (simple, error);
 			g_simple_async_result_complete_in_idle (simple);
 
@@ -706,6 +742,8 @@ gth_thumb_loader_load (GthThumbLoader      *self,
 			return;
 		}
 
+		cache_path = gnome_desktop_thumbnail_factory_lookup (self->priv->thumb_factory, uri, mtime);
+
 		g_free (uri);
 	}
 
@@ -724,7 +762,7 @@ gth_thumb_loader_load (GthThumbLoader      *self,
 		gth_image_loader_load (self->priv->iloader,
 				       cache_file_data,
 				       -1,
-				       cancellable,
+				       load_data->cancellable,
 				       cache_image_ready_cb,
 				       load_data);
 
@@ -747,7 +785,7 @@ gth_thumb_loader_load (GthThumbLoader      *self,
 		gth_image_loader_load (self->priv->tloader,
 				       file_data,
 				       self->priv->requested_size,
-				       cancellable,
+				       load_data->cancellable,
 				       original_image_ready_cb,
 				       load_data);
 }
@@ -756,25 +794,22 @@ gth_thumb_loader_load (GthThumbLoader      *self,
 gboolean
 gth_thumb_loader_load_finish (GthThumbLoader  *self,
 			      GAsyncResult    *result,
-			      GthFileData    **file_data,
 			      GdkPixbuf      **pixbuf,
 			      GError         **error)
 {
-	  GSimpleAsyncResult *simple;
-	  LoadResult         *load_result;
+	GSimpleAsyncResult *simple;
+	LoadResult         *load_result;
 
-	  g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), gth_thumb_loader_load), FALSE);
+	g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), gth_thumb_loader_load), FALSE);
 
-	  simple = G_SIMPLE_ASYNC_RESULT (result);
+	simple = G_SIMPLE_ASYNC_RESULT (result);
 
-	  if (g_simple_async_result_propagate_error (simple, error))
-		  return FALSE;
+	if (g_simple_async_result_propagate_error (simple, error))
+		return FALSE;
 
-	  load_result = g_simple_async_result_get_op_res_gpointer (simple);
-	  if (file_data != NULL)
-  		  *file_data = g_object_ref (load_result->file_data);
-	  if (pixbuf != NULL)
-		  *pixbuf = g_object_ref (load_result->pixbuf);
+	load_result = g_simple_async_result_get_op_res_gpointer (simple);
+	if (pixbuf != NULL)
+		*pixbuf = _g_object_ref (load_result->pixbuf);
 
-	  return TRUE;
+	return TRUE;
 }
diff --git a/gthumb/gth-thumb-loader.h b/gthumb/gth-thumb-loader.h
index 855cac6..a441130 100644
--- a/gthumb/gth-thumb-loader.h
+++ b/gthumb/gth-thumb-loader.h
@@ -72,7 +72,6 @@ void              gth_thumb_loader_load                 (GthThumbLoader       *s
 						         gpointer              user_data);
 gboolean          gth_thumb_loader_load_finish          (GthThumbLoader       *self,
 						  	 GAsyncResult         *res,
-							 GthFileData         **file_data,
 							 GdkPixbuf           **pixbuf,
 							 GError              **error);
 
diff --git a/gthumb/pixbuf-io.h b/gthumb/pixbuf-io.h
index 0fb29d7..8ff6af0 100644
--- a/gthumb/pixbuf-io.h
+++ b/gthumb/pixbuf-io.h
@@ -30,6 +30,13 @@
 
 G_BEGIN_DECLS
 
+typedef GdkPixbufAnimation* (*PixbufLoader) (GthFileData   *file_data,
+					     int            requested_size,
+					     int           *original_width,
+					     int           *original_height,
+					     gpointer       user_data,
+					     GCancellable  *cancellable,
+				   	     GError       **error);
 
 typedef struct {
 	GFile *file;



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