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