1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 
3 /*
4  *  GThumb
5  *
6  *  Copyright (C) 2001-2010 Free Software Foundation, Inc.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <config.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <signal.h>
28 #include <glib/gstdio.h>
29 #define GDK_PIXBUF_ENABLE_BACKEND
30 #include <gtk/gtk.h>
31 #include <gdk-pixbuf/gdk-pixbuf.h>
32 #include "cairo-scale.h"
33 #include "cairo-utils.h"
34 #include "gio-utils.h"
35 #include "glib-utils.h"
36 #define GNOME_DESKTOP_USE_UNSTABLE_API
37 #include "gnome-desktop-thumbnail.h"
38 #include "gth-error.h"
39 #include "gth-image-loader.h"
40 #include "gth-image-utils.h"
41 #include "gth-main.h"
42 #include "gth-thumb-loader.h"
43 #include "pixbuf-io.h"
44 #include "pixbuf-utils.h"
45 #include "typedefs.h"
46 
47 #define THUMBNAIL_LARGE_SIZE	  256
48 #define THUMBNAIL_NORMAL_SIZE	  128
49 #define THUMBNAIL_DIR_PERMISSIONS 0700
50 #define MAX_THUMBNAILER_LIFETIME  4000   /* kill the thumbnailer after this amount of time*/
51 #define CHECK_CANCELLABLE_DELAY   200
52 
53 
54 enum {
55 	READY,
56 	LAST_SIGNAL
57 };
58 
59 
60 struct _GthThumbLoaderPrivate {
61 	GthImageLoader   *iloader;
62 	GthImageLoader   *tloader;
63 	guint             use_cache : 1;
64 	guint             save_thumbnails : 1;
65 	int               requested_size;
66 	int               cache_max_size;
67 	goffset           max_file_size;         /* If the file size is greater
68 					    	  * than this the thumbnail
69 					    	  * will not be created, for
70 					    	  * functionality reasons. */
71 	GnomeDesktopThumbnailSize
72 			  thumb_size;
73 	GnomeDesktopThumbnailFactory
74 			 *thumb_factory;
75 };
76 
77 
G_DEFINE_TYPE_WITH_CODE(GthThumbLoader,gth_thumb_loader,G_TYPE_OBJECT,G_ADD_PRIVATE (GthThumbLoader))78 G_DEFINE_TYPE_WITH_CODE (GthThumbLoader,
79 			 gth_thumb_loader,
80 			 G_TYPE_OBJECT,
81 			 G_ADD_PRIVATE (GthThumbLoader))
82 
83 
84 static void
85 gth_thumb_loader_finalize (GObject *object)
86 {
87 	GthThumbLoader *self;
88 
89 	self = GTH_THUMB_LOADER (object);
90 	_g_object_unref (self->priv->iloader);
91 	_g_object_unref (self->priv->tloader);
92 
93 	G_OBJECT_CLASS (gth_thumb_loader_parent_class)->finalize (object);
94 }
95 
96 
97 static void
gth_thumb_loader_class_init(GthThumbLoaderClass * class)98 gth_thumb_loader_class_init (GthThumbLoaderClass *class)
99 {
100 	GObjectClass *object_class;
101 
102 	object_class = G_OBJECT_CLASS (class);
103 	object_class->finalize = gth_thumb_loader_finalize;
104 }
105 
106 
107 static void
gth_thumb_loader_init(GthThumbLoader * self)108 gth_thumb_loader_init (GthThumbLoader *self)
109 {
110 	self->priv = gth_thumb_loader_get_instance_private (self);
111 	self->priv->iloader = NULL;
112 	self->priv->tloader = NULL;
113 	self->priv->use_cache = TRUE;
114 	self->priv->save_thumbnails = TRUE;
115 	self->priv->requested_size = 0;
116 	self->priv->cache_max_size = 0;
117 	self->priv->max_file_size = 0;
118 	self->priv->thumb_size = GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL;
119 	self->priv->thumb_factory = NULL;
120 }
121 
122 
123 static GthImage *
generate_thumbnail(GInputStream * istream,GthFileData * file_data,int requested_size,int * original_width,int * original_height,gboolean * loaded_original,gpointer user_data,GCancellable * cancellable,GError ** error)124 generate_thumbnail (GInputStream  *istream,
125 		    GthFileData   *file_data,
126 		    int            requested_size,
127 		    int           *original_width,
128 		    int           *original_height,
129 		    gboolean      *loaded_original,
130 		    gpointer       user_data,
131 		    GCancellable  *cancellable,
132 		    GError       **error)
133 {
134 	GthThumbLoader *self = user_data;
135 	GdkPixbuf      *pixbuf = NULL;
136 	GthImage       *image = NULL;
137 	char           *uri;
138 	const char     *mime_type;
139 
140 	if (file_data == NULL) {
141 		if (error != NULL)
142 			*error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_INVALID_FILENAME, "Could not load file");
143 		return NULL;
144 	}
145 
146 	if (original_width != NULL)
147 		*original_width = -1;
148 
149 	if (original_height != NULL)
150 		*original_height = -1;
151 
152 	mime_type = _g_content_type_get_from_stream (istream, file_data->file, cancellable, error);
153 	if (mime_type == NULL) {
154 		if ((error != NULL) && (*error == NULL))
155 			*error = g_error_new_literal (GTH_ERROR, 0, "Cannot generate the thumbnail: unknown file type");
156 		return NULL;
157 	}
158 
159 	uri = g_file_get_uri (file_data->file);
160 	pixbuf = gnome_desktop_thumbnail_factory_generate_no_script (self->priv->thumb_factory,
161 								     uri,
162 								     mime_type,
163 								     cancellable);
164 
165 	if (g_cancellable_is_cancelled (cancellable)) {
166 		_g_object_unref (pixbuf);
167 		g_free (uri);
168 		if (error != NULL)
169 			*error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, "");
170 		return NULL;
171 	}
172 
173 	if (pixbuf != NULL) {
174 		image = gth_image_new_for_pixbuf (pixbuf);
175 		if (original_width != NULL)
176 			*original_width = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (pixbuf), "gnome-original-width"));
177 		if (original_height != NULL)
178 			*original_height = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (pixbuf), "gnome-original-height"));
179 		if (error != NULL)
180 			g_clear_error (error);
181 
182 		g_object_unref (pixbuf);
183 	}
184 	else {
185 		GthImageLoaderFunc thumbnailer;
186 
187 		/* prefer the GTH_IMAGE_FORMAT_CAIRO_SURFACE format to give
188 		 * priority to the internal loaders. */
189 
190 		thumbnailer = gth_main_get_image_loader_func (mime_type, GTH_IMAGE_FORMAT_CAIRO_SURFACE);
191 		if (thumbnailer != NULL)
192 			image = thumbnailer (istream,
193 					     file_data,
194 					     self->priv->cache_max_size,
195 					     original_width,
196 					     original_height,
197 					     NULL,
198 					     NULL,
199 					     cancellable,
200 					     error);
201 	}
202 
203 	if ((image == NULL) && (error != NULL))
204 		*error = g_error_new_literal (GTH_ERROR, 0, "Could not generate the thumbnail");
205 
206 	g_free (uri);
207 
208 	return image;
209 }
210 
211 
212 static GthImage *
load_cached_thumbnail(GInputStream * istream,GthFileData * file_data,int requested_size,int * original_width,int * original_height,gboolean * loaded_original,gpointer user_data,GCancellable * cancellable,GError ** error)213 load_cached_thumbnail (GInputStream  *istream,
214 		       GthFileData   *file_data,
215 		       int            requested_size,
216 		       int           *original_width,
217 		       int           *original_height,
218 		       gboolean      *loaded_original,
219 		       gpointer       user_data,
220 		       GCancellable  *cancellable,
221 		       GError       **error)
222 {
223 	if (file_data == NULL) {
224 		g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_FILENAME, "Could not load file");
225 		return NULL;
226 	}
227 
228 	return gth_image_new_from_stream (istream,
229 				          requested_size,
230 				          original_width,
231 				          original_height,
232 				          cancellable,
233 				          error);
234 }
235 
236 
237 static void
gth_thumb_loader_construct(GthThumbLoader * self,int requested_size)238 gth_thumb_loader_construct (GthThumbLoader *self,
239 			    int             requested_size)
240 {
241 	gth_thumb_loader_set_requested_size (self, requested_size);
242 	self->priv->tloader = gth_image_loader_new (generate_thumbnail, self);
243 	self->priv->iloader = gth_image_loader_new (load_cached_thumbnail, NULL);
244 }
245 
246 
247 GthThumbLoader *
gth_thumb_loader_new(int requested_size)248 gth_thumb_loader_new (int requested_size)
249 {
250 	GthThumbLoader *self;
251 
252 	self = g_object_new (GTH_TYPE_THUMB_LOADER, NULL);
253 	gth_thumb_loader_construct (self, requested_size);
254 
255 	return self;
256 }
257 
258 
259 void
gth_thumb_loader_set_loader_func(GthThumbLoader * self,GthImageLoaderFunc loader_func)260 gth_thumb_loader_set_loader_func (GthThumbLoader     *self,
261 				  GthImageLoaderFunc  loader_func)
262 {
263 	gth_image_loader_set_loader_func (self->priv->tloader,
264 					  (loader_func != NULL) ? loader_func : generate_thumbnail,
265 					  self);
266 }
267 
268 
269 void
gth_thumb_loader_set_requested_size(GthThumbLoader * self,int size)270 gth_thumb_loader_set_requested_size (GthThumbLoader *self,
271 				     int             size)
272 {
273 	GnomeDesktopThumbnailSize thumb_size;
274 
275 	self->priv->requested_size = size;
276 	if (self->priv->requested_size <= THUMBNAIL_NORMAL_SIZE) {
277 		self->priv->cache_max_size = THUMBNAIL_NORMAL_SIZE;
278 		thumb_size = GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL;
279 	}
280 	else {
281 		self->priv->cache_max_size = THUMBNAIL_LARGE_SIZE;
282 		thumb_size = GNOME_DESKTOP_THUMBNAIL_SIZE_LARGE;
283 	}
284 
285 	if ((self->priv->thumb_size != thumb_size) || (self->priv->thumb_factory == NULL)) {
286 		self->priv->thumb_size = thumb_size;
287 		_g_object_unref (self->priv->thumb_factory);
288 		self->priv->thumb_factory = gnome_desktop_thumbnail_factory_new (self->priv->thumb_size);
289 	}
290 }
291 
292 
293 int
gth_thumb_loader_get_requested_size(GthThumbLoader * self)294 gth_thumb_loader_get_requested_size (GthThumbLoader *self)
295 {
296 	return self->priv->requested_size;
297 }
298 
299 
300 void
gth_thumb_loader_set_use_cache(GthThumbLoader * self,gboolean use)301 gth_thumb_loader_set_use_cache (GthThumbLoader *self,
302 			        gboolean        use)
303 {
304 	g_return_if_fail (self != NULL);
305 	self->priv->use_cache = use;
306 }
307 
308 
309 void
gth_thumb_loader_set_save_thumbnails(GthThumbLoader * self,gboolean save)310 gth_thumb_loader_set_save_thumbnails (GthThumbLoader *self,
311 				      gboolean        save)
312 {
313 	g_return_if_fail (self != NULL);
314 	self->priv->save_thumbnails = save;
315 }
316 
317 
318 void
gth_thumb_loader_set_max_file_size(GthThumbLoader * self,goffset size)319 gth_thumb_loader_set_max_file_size (GthThumbLoader *self,
320 				    goffset         size)
321 {
322 	g_return_if_fail (self != NULL);
323 	self->priv->max_file_size = size;
324 }
325 
326 
327 typedef struct {
328 	GthThumbLoader     *thumb_loader;
329 	GthFileData        *file_data;
330 	int                 requested_size;
331 	GTask              *task;
332 	GCancellable       *cancellable;
333 	char               *thumbnailer_tmpfile;
334 	GPid                thumbnailer_pid;
335 	guint               thumbnailer_watch;
336 	guint               thumbnailer_timeout;
337 	guint               cancellable_watch;
338 	gboolean            script_cancelled;
339 } LoadData;
340 
341 
342 static LoadData *
load_data_new(GthFileData * file_data,int requested_size)343 load_data_new (GthFileData *file_data,
344 	       int          requested_size)
345 {
346 	LoadData *load_data;
347 
348 	load_data = g_new0 (LoadData, 1);
349 	load_data->file_data = g_object_ref (file_data);
350 	load_data->requested_size = requested_size;
351 
352 	return load_data;
353 }
354 
355 
356 static void
load_data_unref(LoadData * load_data)357 load_data_unref (LoadData *load_data)
358 {
359 	g_object_unref (load_data->thumb_loader);
360 	g_object_unref (load_data->file_data);
361 	_g_object_unref (load_data->task);
362 	_g_object_unref (load_data->cancellable);
363 	g_free (load_data->thumbnailer_tmpfile);
364 	g_free (load_data);
365 }
366 
367 
368 typedef struct {
369 	GthFileData     *file_data;
370 	cairo_surface_t *image;
371 } LoadResult;
372 
373 
374 static void
load_result_unref(LoadResult * load_result)375 load_result_unref (LoadResult *load_result)
376 {
377 	g_object_unref (load_result->file_data);
378 	cairo_surface_destroy (load_result->image);
379 	g_free (load_result);
380 }
381 
382 
383 static void
384 original_image_ready_cb (GObject      *source_object,
385 		         GAsyncResult *res,
386 		         gpointer      user_data);
387 
388 
389 static int
normalize_thumb(int * width,int * height,int max_size,int cache_max_size)390 normalize_thumb (int *width,
391 		 int *height,
392 		 int  max_size,
393 		 int  cache_max_size)
394 {
395 	gboolean modified;
396 	float    max_w = max_size;
397 	float    max_h = max_size;
398 	float    w = *width;
399 	float    h = *height;
400 	float    factor;
401 	int      new_width, new_height;
402 
403 	if (max_size > cache_max_size) {
404 		if ((*width < cache_max_size - 1) && (*height < cache_max_size - 1))
405 			return FALSE;
406 	}
407 	else if ((*width < max_size - 1) && (*height < max_size - 1))
408 		return FALSE;
409 
410 	factor = MIN (max_w / w, max_h / h);
411 	new_width  = MAX ((int) (w * factor), 1);
412 	new_height = MAX ((int) (h * factor), 1);
413 
414 	modified = (new_width != *width) || (new_height != *height);
415 
416 	*width = new_width;
417 	*height = new_height;
418 
419 	return modified;
420 }
421 
422 
423 static cairo_surface_t *
_cairo_image_surface_scale_for_thumbnail(cairo_surface_t * image,int new_width,int new_height)424 _cairo_image_surface_scale_for_thumbnail (cairo_surface_t *image,
425 					  int              new_width,
426 					  int              new_height)
427 {
428 	cairo_surface_t *scaled;
429 
430 	scaled = _cairo_image_surface_scale (image, new_width, new_height, SCALE_FILTER_GOOD, NULL);
431 	if (scaled != NULL)
432 		_cairo_image_surface_copy_metadata (image, scaled);
433 
434 	return scaled;
435 }
436 
437 
438 static void
cache_image_ready_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)439 cache_image_ready_cb (GObject      *source_object,
440 		      GAsyncResult *res,
441 		      gpointer      user_data)
442 {
443 	LoadData        *load_data = user_data;
444 	GthThumbLoader  *self = load_data->thumb_loader;
445 	GthImage        *image = NULL;
446 	cairo_surface_t *surface;
447 	int              width;
448 	int              height;
449 	gboolean         modified;
450 	LoadResult      *load_result;
451 
452 	if (! gth_image_loader_load_finish (GTH_IMAGE_LOADER (source_object),
453 					    res,
454 					    &image,
455 					    NULL,
456 					    NULL,
457 					    NULL,
458 					    NULL))
459 	{
460 		/* error loading the thumbnail from the cache, try to generate
461 		 * the thumbnail loading the original image. */
462 
463 		gth_image_loader_load (self->priv->tloader,
464 				       load_data->file_data,
465 				       load_data->requested_size,
466 				       G_PRIORITY_LOW,
467 				       load_data->cancellable,
468 				       original_image_ready_cb,
469 				       load_data);
470 
471 		return;
472 	}
473 
474 	/* Thumbnail correctly loaded from the cache. Scale if the user wants
475 	 * a different size. */
476 
477 	surface = gth_image_get_cairo_surface (image);
478 
479 	g_return_if_fail (surface != NULL);
480 
481 	width = cairo_image_surface_get_width (surface);
482 	height = cairo_image_surface_get_height (surface);
483 	modified = normalize_thumb (&width,
484 				    &height,
485 				    self->priv->requested_size,
486 				    self->priv->cache_max_size);
487 	if (modified) {
488 		cairo_surface_t *tmp = surface;
489 		surface = _cairo_image_surface_scale_for_thumbnail (tmp, width, height);
490 		cairo_surface_destroy (tmp);
491 	}
492 
493 	load_result = g_new0 (LoadResult, 1);
494 	load_result->file_data = g_object_ref (load_data->file_data);
495 	load_result->image = surface;
496 	g_task_return_pointer (load_data->task, load_result, (GDestroyNotify) load_result_unref);
497 
498 	load_data_unref (load_data);
499 	g_object_unref (image);
500 }
501 
502 
503 static gboolean
is_a_cache_file(const char * uri)504 is_a_cache_file (const char *uri)
505 {
506 	char     *filename;
507 	char     *cache_dir_1;
508 	char     *cache_dir_2;
509 	gboolean  result;
510 
511 	filename = g_filename_from_uri (uri, NULL, NULL);
512 	if (filename == NULL)
513 		return FALSE;
514 
515 	cache_dir_1 = g_build_filename (g_get_home_dir (), ".thumbnails", NULL);
516 	cache_dir_2 = g_build_filename (g_get_user_cache_dir (), "thumbnails", NULL);
517 	result = _g_path_is_parent (cache_dir_1, filename) || _g_path_is_parent (cache_dir_2, filename);
518 
519 	g_free (cache_dir_1);
520 	g_free (cache_dir_2);
521 	g_free (filename);
522 
523 	return result;
524 }
525 
526 
527 static gboolean
_gth_thumb_loader_save_to_cache(GthThumbLoader * self,GthFileData * file_data,cairo_surface_t * image,int original_width,int original_height)528 _gth_thumb_loader_save_to_cache (GthThumbLoader  *self,
529 				 GthFileData     *file_data,
530 				 cairo_surface_t *image,
531 				 int              original_width,
532 				 int              original_height)
533 {
534 	char                     *uri;
535 	cairo_surface_metadata_t *metadata;
536 	GdkPixbuf                *pixbuf;
537 
538 	if ((self == NULL) || (image == NULL))
539 		return FALSE;
540 
541 	uri = g_file_get_uri (file_data->file);
542 
543 	/* Do not save thumbnails from the user's thumbnail directory,
544 	 * or an endless loop of thumbnailing may be triggered. */
545 
546 	if (is_a_cache_file (uri)) {
547 		g_free (uri);
548 		return FALSE;
549 	}
550 
551 	if ((original_width > 0) && (original_height > 0)) {
552 		metadata = _cairo_image_surface_get_metadata (image);
553 		metadata->thumbnail.image_width = original_width;
554 		metadata->thumbnail.image_height = original_height;
555 	}
556 	pixbuf = _gdk_pixbuf_new_from_cairo_surface (image);
557 	if (pixbuf == NULL)
558 		return FALSE;
559 
560 	gnome_desktop_thumbnail_factory_save_thumbnail (self->priv->thumb_factory,
561 							pixbuf,
562 							uri,
563 							gth_file_data_get_mtime (file_data));
564 
565 	g_object_unref (pixbuf);
566 
567 	return TRUE;
568 }
569 
570 
571 static void
original_image_loaded_correctly(GthThumbLoader * self,LoadData * load_data,cairo_surface_t * image,int original_width,int original_height)572 original_image_loaded_correctly (GthThumbLoader *self,
573 				 LoadData        *load_data,
574 				 cairo_surface_t *image,
575 				 int              original_width,
576 				 int              original_height)
577 {
578 	cairo_surface_t *local_image;
579 	int              width;
580 	int              height;
581 	gboolean         modified;
582 	LoadResult      *load_result;
583 
584 	local_image = cairo_surface_reference (image);
585 
586 	width = cairo_image_surface_get_width (local_image);
587 	height = cairo_image_surface_get_height (local_image);
588 
589 	if (self->priv->save_thumbnails) {
590 		gboolean modified;
591 
592 		/* Thumbnails are always saved in the cache max size, then
593 		 * scaled a second time if the user requested a different
594 		 * size. */
595 
596 		modified = scale_keeping_ratio (&width,
597 						&height,
598 						self->priv->cache_max_size,
599 						self->priv->cache_max_size,
600 						FALSE);
601 		if (modified) {
602 			cairo_surface_t *tmp = local_image;
603 			local_image = _cairo_image_surface_scale_for_thumbnail (tmp, width, height);
604 			cairo_surface_destroy (tmp);
605 		}
606 
607 		_gth_thumb_loader_save_to_cache (self,
608 						 load_data->file_data,
609 						 local_image,
610 						 original_width,
611 						 original_height);
612 	}
613 
614 	/* Scale if the user wants a different size. */
615 
616 	modified = normalize_thumb (&width,
617 				    &height,
618 				    self->priv->requested_size,
619 				    self->priv->cache_max_size);
620 	if (modified) {
621 		cairo_surface_t *tmp = local_image;
622 		local_image = _cairo_image_surface_scale_for_thumbnail (tmp, width, height);
623 		cairo_surface_destroy (tmp);
624 	}
625 
626 	load_result = g_new0 (LoadResult, 1);
627 	load_result->file_data = g_object_ref (load_data->file_data);
628 	load_result->image = cairo_surface_reference (local_image);
629 	g_task_return_pointer (load_data->task, load_result, (GDestroyNotify) load_result_unref);
630 
631 	cairo_surface_destroy (local_image);
632 }
633 
634 
635 static void
failed_to_load_original_image(GthThumbLoader * self,LoadData * load_data)636 failed_to_load_original_image (GthThumbLoader *self,
637 			       LoadData       *load_data)
638 {
639 	char *uri;
640 
641 	uri = g_file_get_uri (load_data->file_data->file);
642 	gnome_desktop_thumbnail_factory_create_failed_thumbnail (self->priv->thumb_factory,
643 								 uri,
644 								 gth_file_data_get_mtime (load_data->file_data));
645 	g_task_return_error (load_data->task, g_error_new_literal (GTH_ERROR, 0, "failed to generate the thumbnail"));
646 
647 	g_free (uri);
648 }
649 
650 
651 static gboolean
kill_thumbnailer_cb(gpointer user_data)652 kill_thumbnailer_cb (gpointer user_data)
653 {
654 	LoadData *load_data = user_data;
655 
656 	if (load_data->thumbnailer_timeout != 0) {
657 		g_source_remove (load_data->thumbnailer_timeout);
658 		load_data->thumbnailer_timeout = 0;
659 	}
660 
661 	if (load_data->cancellable_watch != 0) {
662 		g_source_remove (load_data->cancellable_watch);
663 		load_data->cancellable_watch = 0;
664 	}
665 
666 	if (load_data->thumbnailer_pid != 0)
667 		kill (load_data->thumbnailer_pid, SIGTERM);
668 
669 	return FALSE;
670 }
671 
672 
673 static gboolean
check_cancellable_cb(gpointer user_data)674 check_cancellable_cb (gpointer user_data)
675 {
676 	LoadData *load_data = user_data;
677 
678 	if (g_cancellable_is_cancelled (load_data->cancellable)) {
679 		load_data->script_cancelled = TRUE;
680 		kill_thumbnailer_cb (user_data);
681 		return FALSE;
682 	}
683 
684 	return TRUE;
685 }
686 
687 
688 static void
watch_thumbnailer_cb(GPid pid,int status,gpointer user_data)689 watch_thumbnailer_cb (GPid     pid,
690 		      int      status,
691 		      gpointer user_data)
692 {
693 	LoadData       *load_data = user_data;
694 	GthThumbLoader *self = load_data->thumb_loader;
695 	GdkPixbuf      *pixbuf;
696 
697 	if (load_data->thumbnailer_timeout != 0) {
698 		g_source_remove (load_data->thumbnailer_timeout);
699 		load_data->thumbnailer_timeout = 0;
700 	}
701 
702 	if (load_data->cancellable_watch != 0) {
703 		g_source_remove (load_data->cancellable_watch);
704 		load_data->cancellable_watch = 0;
705 	}
706 
707 	g_spawn_close_pid (pid);
708 	load_data->thumbnailer_pid = 0;
709 	load_data->thumbnailer_watch = 0;
710 
711 	if (load_data->script_cancelled) {
712 		if (load_data->thumbnailer_tmpfile != NULL) {
713 			g_unlink (load_data->thumbnailer_tmpfile);
714 			g_free (load_data->thumbnailer_tmpfile);
715 			load_data->thumbnailer_tmpfile = NULL;
716 		}
717 
718 		g_task_return_error (load_data->task, g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, "script cancelled"));
719 
720 		return;
721 	}
722 
723 	pixbuf = NULL;
724 	if (status == 0)
725 		pixbuf = gnome_desktop_thumbnail_factory_load_from_tempfile (self->priv->thumb_factory,
726 									     &load_data->thumbnailer_tmpfile);
727 
728 	if (pixbuf != NULL) {
729 		cairo_surface_t *surface;
730 
731 		surface = _cairo_image_surface_create_from_pixbuf (pixbuf);
732 		original_image_loaded_correctly (self, load_data, surface, 0, 0);
733 
734 		cairo_surface_destroy (surface);
735 		g_object_unref (pixbuf);
736 	}
737 	else
738 		failed_to_load_original_image (self, load_data);
739 }
740 
741 
742 static void
original_image_ready_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)743 original_image_ready_cb (GObject      *source_object,
744 		         GAsyncResult *res,
745 		         gpointer      user_data)
746 {
747 	LoadData        *load_data = user_data;
748 	GthThumbLoader  *self = load_data->thumb_loader;
749 	GthImage        *image = NULL;
750 	int              original_width;
751 	int              original_height;
752 	cairo_surface_t *surface = NULL;
753 	GError          *error = NULL;
754 
755 	if (! gth_image_loader_load_finish (GTH_IMAGE_LOADER (source_object),
756 					    res,
757 					    &image,
758 					    &original_width,
759 					    &original_height,
760 					    NULL,
761 					    &error))
762 	{
763 		/* error loading the original image, try with the system
764 		 * thumbnailer */
765 
766 		char *uri;
767 
768 		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
769 			g_task_return_error (load_data->task, error);
770 			return;
771 		}
772 
773 		g_clear_error (&error);
774 
775 		uri = g_file_get_uri (load_data->file_data->file);
776 		if (gnome_desktop_thumbnail_factory_generate_from_script (self->priv->thumb_factory,
777 									  uri,
778 									  gth_file_data_get_mime_type (load_data->file_data),
779 									  &load_data->thumbnailer_pid,
780 									  &load_data->thumbnailer_tmpfile,
781 									  &error))
782 		{
783 			load_data->thumbnailer_watch = g_child_watch_add (load_data->thumbnailer_pid,
784 									  watch_thumbnailer_cb,
785 									  load_data);
786 			load_data->thumbnailer_timeout = g_timeout_add (MAX_THUMBNAILER_LIFETIME,
787 									kill_thumbnailer_cb,
788 									load_data);
789 			load_data->cancellable_watch = g_timeout_add (CHECK_CANCELLABLE_DELAY,
790 								      check_cancellable_cb,
791 								      load_data);
792 		}
793 		else {
794 			g_clear_error (&error);
795 			failed_to_load_original_image (self, load_data);
796 			load_data_unref (load_data);
797 		}
798 
799 		g_free (uri);
800 
801 		return;
802 	}
803 
804 	surface = gth_image_get_cairo_surface (image);
805 	original_image_loaded_correctly (self,
806 					 load_data,
807 					 surface,
808 					 original_width,
809 					 original_height);
810 
811 	cairo_surface_destroy (surface);
812 	g_object_unref (image);
813 	load_data_unref (load_data);
814 }
815 
816 
817 void
gth_thumb_loader_load(GthThumbLoader * self,GthFileData * file_data,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)818 gth_thumb_loader_load (GthThumbLoader      *self,
819 		       GthFileData         *file_data,
820 		       GCancellable        *cancellable,
821 		       GAsyncReadyCallback  callback,
822 		       gpointer             user_data)
823 {
824 	GTask    *task;
825 	char     *cache_path;
826 	char     *uri;
827 	LoadData *load_data;
828 
829 	task = g_task_new (G_OBJECT (self), cancellable, callback, user_data);
830 
831 	cache_path = NULL;
832 
833 	uri = g_file_get_uri (file_data->file);
834 
835 	if (is_a_cache_file (uri)) {
836 		cache_path = g_file_get_path (file_data->file);
837 	}
838 	else if (self->priv->use_cache) {
839 		time_t mtime;
840 
841 		mtime = gth_file_data_get_mtime (file_data);
842 
843 		if (gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail (self->priv->thumb_factory, uri, mtime)) {
844 			g_task_return_error (task, g_error_new_literal (GTH_ERROR, 0, "found a failed thumbnail"));
845 
846 			g_free (uri);
847 			g_object_unref (task);
848 
849 			return;
850 		}
851 
852 		cache_path = gnome_desktop_thumbnail_factory_lookup (self->priv->thumb_factory, uri, mtime);
853 	}
854 
855 	g_free (uri);
856 
857 	if ((cache_path == NULL)
858 	    && (self->priv->max_file_size > 0)
859 	    && (g_file_info_get_size (file_data->info) > self->priv->max_file_size))
860 	{
861 		g_task_return_error (task, g_error_new_literal (GTH_ERROR, 0, "file too big to generate the thumbnail"));
862 		g_object_unref (task);
863 		return;
864 	}
865 
866 	load_data = load_data_new (file_data, self->priv->requested_size);
867 	load_data->thumb_loader = g_object_ref (self);
868 	load_data->cancellable = _g_object_ref (cancellable);
869 	load_data->task = task;
870 
871 	if (cache_path != NULL) {
872 		GFile       *cache_file;
873 		GthFileData *cache_file_data;
874 
875 		cache_file = g_file_new_for_path (cache_path);
876 		cache_file_data = gth_file_data_new (cache_file, NULL);
877 		gth_file_data_set_mime_type (cache_file_data, "image/png");
878 		gth_image_loader_load (self->priv->iloader,
879 				       cache_file_data,
880 				       -1,
881 				       G_PRIORITY_LOW,
882 				       load_data->cancellable,
883 				       cache_image_ready_cb,
884 				       load_data);
885 
886 		g_object_unref (cache_file_data);
887 		g_object_unref (cache_file);
888 		g_free (cache_path);
889 	}
890 	else
891 		gth_image_loader_load (self->priv->tloader,
892 				       file_data,
893 				       self->priv->requested_size,
894 				       G_PRIORITY_LOW,
895 				       load_data->cancellable,
896 				       original_image_ready_cb,
897 				       load_data);
898 }
899 
900 
901 gboolean
gth_thumb_loader_load_finish(GthThumbLoader * self,GAsyncResult * result,cairo_surface_t ** image,GError ** error)902 gth_thumb_loader_load_finish (GthThumbLoader   *self,
903 			      GAsyncResult     *result,
904 			      cairo_surface_t **image,
905 			      GError          **error)
906 {
907 	LoadResult *load_result;
908 
909 	g_return_val_if_fail (g_task_is_valid (G_TASK (result), G_OBJECT (self)), FALSE);
910 
911 	load_result = g_task_propagate_pointer (G_TASK (result), error);
912 	if (load_result == NULL)
913 		return FALSE;
914 
915 	if (image != NULL)
916 		*image = cairo_surface_reference (load_result->image);
917 	load_result_unref (load_result);
918 
919 	return TRUE;
920 }
921 
922 
923 gboolean
gth_thumb_loader_has_valid_thumbnail(GthThumbLoader * self,GthFileData * file_data)924 gth_thumb_loader_has_valid_thumbnail (GthThumbLoader *self,
925 				      GthFileData    *file_data)
926 {
927 	gboolean  valid_thumbnail = FALSE;
928 	char     *uri;
929 	time_t    mtime;
930 	char     *thumbnail_path;
931 
932 	uri = g_file_get_uri (file_data->file);
933 	mtime = gth_file_data_get_mtime (file_data);
934 	thumbnail_path = gnome_desktop_thumbnail_factory_lookup (self->priv->thumb_factory, uri, mtime);
935 	if (thumbnail_path != NULL) {
936 		valid_thumbnail = TRUE;
937 		g_free (thumbnail_path);
938 	}
939 
940 	g_free (uri);
941 
942 	return valid_thumbnail;
943 }
944 
945 
946 gboolean
gth_thumb_loader_has_failed_thumbnail(GthThumbLoader * self,GthFileData * file_data)947 gth_thumb_loader_has_failed_thumbnail (GthThumbLoader *self,
948 				       GthFileData    *file_data)
949 {
950 	char     *uri;
951 	time_t    mtime;
952 	gboolean  valid_thumbnail;
953 
954 	uri = g_file_get_uri (file_data->file);
955 	mtime = gth_file_data_get_mtime (file_data);
956 	valid_thumbnail = gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail (self->priv->thumb_factory, uri, mtime);
957 
958 	g_free (uri);
959 
960 	return valid_thumbnail;
961 }
962