1 /* Egg Libraries: egg-pixbuf-thumbnail.c
2 *
3 * Copyright (c) 2004 James M. Cape <jcape@ignore-your.tv>
4 * Copyright (c) 2002 Red Hat, Inc.
5 *
6 * Minor changes by AMR for compatibility with GTK+-2.0
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library 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 GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
22 */
23
24 #ifdef HAVE_CONFIG_H
25 # include <config.h>
26 #endif /* HAVE_CONFIG_H */
27
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <errno.h>
31 #include <unistd.h>
32 #include <stdio.h>
33 #include <fcntl.h>
34
35 #include <stdlib.h>
36 #include <string.h>
37
38
39 // AMR - not available with GTK+ <2.4/2.6
40 #if 0
41 #include <glib/gi18n.h>
42 #include <glib/gstdio.h>
43 #else
44 #define _
45 #define g_filename_display_name g_strdup
46 #define g_fopen fopen
47 #endif
48
49 #include <gdk-pixbuf/gdk-pixbuf-io.h>
50
51 #include "eggmd5.h"
52 #include "egg-pixbuf-thumbnail.h"
53
54
55 /* **************** *
56 * Private Macros *
57 * **************** */
58
59 /* 30 days in seconds */
60 #define EXPIRATION_TIME 2592000
61
62 /* Metadata Keys */
63 /* Standard */
64 #define THUMB_URI_KEY "tEXt::Thumb::URI"
65 #define THUMB_MTIME_KEY "tEXt::Thumb::MTime"
66 #define THUMB_MIME_TYPE_KEY "tEXt::Thumb::Mimetype"
67 #define THUMB_FILESIZE_KEY "tEXt::Thumb::Size"
68 #define THUMB_WIDTH_KEY "tEXt::Thumb::Image::Width"
69 #define THUMB_HEIGHT_KEY "tEXt::Thumb::Image::Height"
70 #define THUMB_PAGES_KEY "tEXt::Thumb::Document::Pages"
71 #define THUMB_LENGTH_KEY "tEXt::Thumb::Movie::Length"
72 #define THUMB_DESCRIPTION_KEY "tEXt::Description"
73 #define THUMB_SOFTWARE_KEY "tEXt::Software"
74
75 /* Used by failed creation attempts */
76 #define THUMB_ERROR_DOMAIN_KEY "tEXt::X-GdkPixbuf::FailDomain"
77 #define FILE_ERROR_NAME "file"
78 #define PIXBUF_ERROR_NAME "pixbuf"
79 #define THUMB_ERROR_CODE_KEY "tEXt::X-GdkPixbuf::FailCode"
80 #define THUMB_ERROR_MESSAGE_KEY "tEXt::X-GdkPixbuf::FailMessage"
81
82 /* Misc Strings */
83 #define NORMAL_DIR_NAME "normal"
84 #define LARGE_DIR_NAME "large"
85 #define FAIL_DIR_NAME "fail"
86 #define APP_DIR_NAME "gdk-pixbuf-2"
87 #define THUMB_SOFTWARE_VALUE "GdkPixbuf"
88
89
90 #define THUMBNAIL_DATA_QUARK (thumbnail_data_get_quark ())
91 #define SIZE_TO_DIR(size) ({\
92 const gchar *__r; \
93 if (size == EGG_PIXBUF_THUMBNAIL_NORMAL) \
94 __r = NORMAL_DIR_NAME; \
95 else if (size == EGG_PIXBUF_THUMBNAIL_LARGE) \
96 __r = LARGE_DIR_NAME; \
97 else \
98 __r = NULL; \
99 __r; \
100 })
101
102
103 /* ******************** *
104 * Private Structures *
105 * ******************** */
106
107 typedef struct
108 {
109 /* Required */
110 gint size;
111 gchar *uri;
112 time_t mtime;
113
114 /* Optional Generic */
115 long filesize; // Change by AMR for GTK+-2.0 compatibility
116 gchar *mime_type;
117 gchar *description;
118 gchar *software;
119
120 /* Optional Image */
121 gint image_width;
122 gint image_height;
123
124 /* Optional Document */
125 gint document_pages;
126
127 /* Optional Movie */
128 time_t movie_length;
129 }
130 ThumbnailData;
131
132 typedef struct
133 {
134 gint orig_width;
135 gint orig_height;
136 gint size;
137 }
138 ImageInfo;
139
140
141 /* ************************* *
142 * ThumbnailData Functions *
143 * ************************* */
144
145 static GQuark
thumbnail_data_get_quark(void)146 thumbnail_data_get_quark (void)
147 {
148 static GQuark quark = 0;
149
150 if (G_UNLIKELY (quark == 0))
151 quark = g_quark_from_static_string ("egg-pixbuf-thumbnail-data");
152
153 return quark;
154 }
155
156 static void
thumbnail_data_free(ThumbnailData * data)157 thumbnail_data_free (ThumbnailData *data)
158 {
159 if (!data)
160 return;
161
162 g_free (data->uri);
163 g_free (data->mime_type);
164 g_free (data->description);
165 g_free (data->software);
166 g_free (data);
167 }
168
169 static ThumbnailData *
get_thumbnail_data(GdkPixbuf * thumbnail)170 get_thumbnail_data (GdkPixbuf *thumbnail)
171 {
172 return g_object_get_qdata (G_OBJECT (thumbnail), THUMBNAIL_DATA_QUARK);
173 }
174
175 static ThumbnailData *
ensure_thumbnail_data(GdkPixbuf * thumbnail)176 ensure_thumbnail_data (GdkPixbuf *thumbnail)
177 {
178 ThumbnailData *data;
179
180 data = get_thumbnail_data (thumbnail);
181
182 if (!data)
183 {
184 data = g_new (ThumbnailData, 1);
185 data->uri = NULL;
186 data->mtime = -1;
187 data->size = EGG_PIXBUF_THUMBNAIL_UNKNOWN;
188 data->filesize = -1;
189 data->mime_type = NULL;
190 data->description = NULL;
191 data->software = NULL;
192 data->image_width = -1;
193 data->image_height = -1;
194 data->document_pages = -1;
195 data->movie_length = -1;
196
197 g_object_set_qdata_full (G_OBJECT (thumbnail), THUMBNAIL_DATA_QUARK, data,
198 (GDestroyNotify) thumbnail_data_free);
199 }
200
201 return data;
202 }
203
204 static gboolean
parse_thumbnail_data(GdkPixbuf * thumbnail,EggPixbufThumbnailSize size,GError ** error)205 parse_thumbnail_data (GdkPixbuf *thumbnail,
206 EggPixbufThumbnailSize size,
207 GError **error)
208 {
209 ThumbnailData *data;
210 const gchar *tmp;
211 glong value;
212 guint8 data_required;
213
214 data_required = 2;
215 data = NULL;
216
217 if (error)
218 {
219 GQuark domain;
220
221 tmp = gdk_pixbuf_get_option (thumbnail, THUMB_ERROR_DOMAIN_KEY);
222 if (tmp)
223 domain = g_quark_from_string (tmp);
224 else
225 domain = g_quark_from_static_string ("egg-pixbuf-thumbnail-error");
226
227 tmp = gdk_pixbuf_get_option (thumbnail, THUMB_ERROR_CODE_KEY);
228 if (tmp)
229 value = strtol (tmp, NULL, 10);
230 else
231 value = -1;
232
233 tmp = gdk_pixbuf_get_option (thumbnail, THUMB_ERROR_MESSAGE_KEY);
234
235 *error = g_error_new_literal (domain, value, tmp);
236 }
237
238 tmp = gdk_pixbuf_get_option (thumbnail, THUMB_URI_KEY);
239 if (tmp)
240 {
241 data = ensure_thumbnail_data (thumbnail);
242 data->uri = g_strdup (tmp);
243 data_required--;
244 }
245
246 tmp = gdk_pixbuf_get_option (thumbnail, THUMB_MTIME_KEY);
247 if (tmp)
248 {
249 data = ensure_thumbnail_data (thumbnail);
250 value = strtol (tmp, NULL, 10);
251 if (value != G_MAXINT && value != G_MININT)
252 {
253 data->mtime = value;
254 data_required--;
255 }
256 }
257
258 tmp = gdk_pixbuf_get_option (thumbnail, THUMB_MIME_TYPE_KEY);
259 if (tmp)
260 {
261 data = ensure_thumbnail_data (thumbnail);
262 data->mime_type = g_strdup (tmp);
263 }
264
265 tmp = gdk_pixbuf_get_option (thumbnail, THUMB_DESCRIPTION_KEY);
266 if (tmp)
267 {
268 data = ensure_thumbnail_data (thumbnail);
269 data->description = g_strdup (tmp);
270 }
271
272 tmp = gdk_pixbuf_get_option (thumbnail, THUMB_SOFTWARE_KEY);
273 if (tmp)
274 {
275 data = ensure_thumbnail_data (thumbnail);
276 data->software = g_strdup (tmp);
277 }
278
279 tmp = gdk_pixbuf_get_option (thumbnail, THUMB_FILESIZE_KEY);
280 if (tmp)
281 {
282 data = ensure_thumbnail_data (thumbnail);
283 value = strtol (tmp, NULL, 10);
284 if (value != G_MAXINT && value != G_MININT)
285 data->filesize = value;
286 }
287
288 tmp = gdk_pixbuf_get_option (thumbnail, THUMB_WIDTH_KEY);
289 if (tmp)
290 {
291 data = ensure_thumbnail_data (thumbnail);
292 value = strtol (tmp, NULL, 10);
293 if (value != G_MAXINT && value != G_MININT)
294 data->image_width = value;
295 }
296
297 tmp = gdk_pixbuf_get_option (thumbnail, THUMB_HEIGHT_KEY);
298 if (tmp)
299 {
300 data = ensure_thumbnail_data (thumbnail);
301 value = strtol (tmp, NULL, 10);
302 if (value != G_MAXINT && value != G_MININT)
303 data->image_height = value;
304 }
305
306 tmp = gdk_pixbuf_get_option (thumbnail, THUMB_PAGES_KEY);
307 if (tmp)
308 {
309 data = ensure_thumbnail_data (thumbnail);
310 value = strtol (tmp, NULL, 10);
311 if (value != G_MAXINT && value != G_MININT)
312 data->document_pages = value;
313 }
314
315 tmp = gdk_pixbuf_get_option (thumbnail, THUMB_LENGTH_KEY);
316 if (tmp)
317 {
318 data = ensure_thumbnail_data (thumbnail);
319 value = strtol (tmp, NULL, 10);
320 if (value != G_MAXINT && value != G_MININT)
321 data->movie_length = value;
322 }
323
324 if (!data_required)
325 data->size = size;
326
327 return (!data_required);
328 }
329
330
331 /* *************************************** *
332 * Global Thumbnails Directory Functions *
333 * *************************************** */
334
335 static gboolean
ensure_one_dir(const gchar * path,GError ** error)336 ensure_one_dir (const gchar *path,
337 GError **error)
338 {
339 #ifdef WIN32
340 if (!g_file_test (path, G_FILE_TEST_IS_DIR) && mkdir (path) < 0)
341 #else
342 if (!g_file_test (path, G_FILE_TEST_IS_DIR) && mkdir (path, 0700) < 0)
343 #endif
344 {
345 gchar *utf8_path;
346
347 utf8_path = g_filename_to_utf8 (path, -1, NULL, NULL, NULL);
348 g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
349 _("Error creating directory `%s': %s"), utf8_path,
350 g_strerror (errno));
351 g_free (utf8_path);
352 return FALSE;
353 }
354
355 return TRUE;
356 }
357
358 static gboolean
ensure_thumbnail_dirs(GError ** error)359 ensure_thumbnail_dirs (GError **error)
360 {
361 gchar *path, *tmp;
362
363 path = g_build_filename (g_get_home_dir (), ".thumbnails", NULL);
364 if (!ensure_one_dir (path, error))
365 {
366 g_free (path);
367 return FALSE;
368 }
369
370 tmp = path;
371 path = g_build_filename (tmp, NORMAL_DIR_NAME, NULL);
372 if (!ensure_one_dir (path, error))
373 {
374 g_free (path);
375 g_free (tmp);
376 return FALSE;
377 }
378
379 g_free (path);
380
381 path = g_build_filename (tmp, LARGE_DIR_NAME, NULL);
382 if (!ensure_one_dir (path, error))
383 {
384 g_free (path);
385 g_free (tmp);
386 return FALSE;
387 }
388
389 g_free (path);
390
391 path = g_build_filename (tmp, FAIL_DIR_NAME, NULL);
392 if (!ensure_one_dir (path, error))
393 {
394 g_free (path);
395 g_free (tmp);
396 return FALSE;
397 }
398
399 g_free (path);
400
401 path = g_build_filename (tmp, FAIL_DIR_NAME, APP_DIR_NAME, NULL);
402 if (!ensure_one_dir (path, error))
403 {
404 g_free (path);
405 g_free (tmp);
406 return FALSE;
407 }
408
409 g_free (tmp);
410 g_free (path);
411 return TRUE;
412 }
413
414
415 /* ****************************** *
416 * Thumbnail Creation Functions *
417 * ****************************** */
418
419 static void
loader_size_prepared_cb(GdkPixbufLoader * loader,gint width,gint height,gpointer user_data)420 loader_size_prepared_cb (GdkPixbufLoader *loader,
421 gint width,
422 gint height,
423 gpointer user_data)
424 {
425 ImageInfo *info;
426
427 info = user_data;
428 info->orig_width = width;
429 info->orig_height = height;
430
431 if (info->size > 0 && (width > info->size || height > info->size))
432 {
433 gdouble scale;
434
435 if (width > height)
436 scale = (gdouble) info->size / width;
437 else
438 scale = (gdouble) info->size / height;
439
440 gdk_pixbuf_loader_set_size (loader, width * scale, height * scale);
441 }
442 }
443
444 static GdkPixbuf *
load_image_at_max_size(const gchar * filename,ImageInfo * info,gchar ** mime_type,GError ** error)445 load_image_at_max_size (const gchar *filename,
446 ImageInfo *info,
447 gchar **mime_type,
448 GError **error)
449 {
450 GdkPixbuf *retval;
451 GdkPixbufLoader *loader;
452 FILE *f;
453 gsize result;
454 guchar buffer[8192];
455
456 f = g_fopen (filename, "rb");
457 if (!f)
458 {
459 gchar *display_name;
460
461 display_name = g_filename_display_name (filename);
462 g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
463 _("Error opening `%s': %s"), display_name,
464 g_strerror (errno));
465 g_free (display_name);
466
467 return NULL;
468 }
469
470 loader = gdk_pixbuf_loader_new ();
471 g_signal_connect (loader, "size-prepared",
472 G_CALLBACK (loader_size_prepared_cb), info);
473
474 result = fread (buffer, 1, sizeof (buffer), f);
475 if (!result)
476 {
477 gchar *display_name;
478
479 display_name = g_filename_display_name (filename);
480 g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
481 _("Error reading `%s': file contains no data."),
482 display_name);
483 g_free (display_name);
484 fclose (f);
485 return NULL;
486 }
487
488 fseek (f, 0, SEEK_SET);
489
490 do
491 {
492 result = fread (buffer, 1, sizeof (buffer), f);
493
494 /* Die on read() error. */
495 if (!result && ferror (f))
496 {
497 gchar *display_name;
498
499 gdk_pixbuf_loader_close (loader, NULL);
500 fclose (f);
501 g_object_unref (loader);
502
503 display_name = g_filename_display_name (filename);
504 g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
505 _("Error reading `%s': %s"), display_name,
506 g_strerror (errno));
507 g_free (display_name);
508 return NULL;
509 }
510 /* Die on loader error. */
511 else if (!gdk_pixbuf_loader_write (loader, buffer, result, error))
512 {
513 gdk_pixbuf_loader_close (loader, NULL);
514 fclose (f);
515 g_object_unref (loader);
516 return NULL;
517 }
518 }
519 while (!feof (f));
520
521 fclose (f);
522
523 if (!gdk_pixbuf_loader_close (loader, error))
524 {
525 g_object_unref (loader);
526 return NULL;
527 }
528
529 retval = gdk_pixbuf_loader_get_pixbuf (loader);
530
531 if (!retval)
532 {
533 gchar *display_name;
534
535 display_name = g_filename_display_name (filename);
536
537 g_set_error (error,
538 GDK_PIXBUF_ERROR,
539 GDK_PIXBUF_ERROR_FAILED,
540 _("Failed to load image '%s': reason not known, probably a corrupt image file"),
541 display_name ? display_name : "???");
542 g_free (display_name);
543 }
544 else
545 {
546 g_object_ref (retval);
547
548 if (mime_type)
549 {
550 GdkPixbufFormat *format;
551 gchar **mime_types;
552
553 format = gdk_pixbuf_loader_get_format (loader);
554 mime_types = gdk_pixbuf_format_get_mime_types (format);
555 if (mime_types && mime_types[0])
556 *mime_type = g_strdup (mime_types[0]);
557 g_strfreev (mime_types);
558 }
559 }
560
561 g_object_unref (loader);
562
563 return retval;
564 }
565
566 /**
567 * egg_pixbuf_get_thumbnail_for_file:
568 * @filename: the path to the original file.
569 * @size: the desired size of the thumnail.
570 * @error: a pointer to a location to store errors in.
571 *
572 * Convenience function which first checks if a failure attempt for @filename
573 * exists. If it does, @error will be set to the reason for that failure. If it
574 * does not, this function attempts to load the thumbnail for @filename at
575 * @size. If that load fails, this function will attempt to create a new
576 * thumbnail. If creating a new thumbnail fails, then a new failure attempt
577 * will be saved.
578 *
579 * In other words, this function handles all the intricasies of thumbnailing
580 * local images internally, and you should see if using it makes sense before
581 * trying more complicated schemes.
582 *
583 * Returns: the thumbnail of @filename, or %NULL.
584 *
585 * Since: 2.6
586 **/
587 GdkPixbuf *
egg_pixbuf_get_thumbnail_for_file(const gchar * filename,EggPixbufThumbnailSize size,GError ** error)588 egg_pixbuf_get_thumbnail_for_file (const gchar *filename,
589 EggPixbufThumbnailSize size,
590 GError **error)
591 {
592 GdkPixbuf *retval;
593 gchar *uri;
594 struct stat st;
595
596 #ifdef WIN32
597 // g_return_val_if_fail (filename != NULL && (filename[1] == ':' || filename[1] == '\\'), NULL);
598 #else
599 // g_return_val_if_fail (filename != NULL && filename[0] == '/', NULL);
600 #endif
601 g_return_val_if_fail(g_path_is_absolute(filename),NULL);
602
603 g_return_val_if_fail (size == EGG_PIXBUF_THUMBNAIL_NORMAL ||
604 size == EGG_PIXBUF_THUMBNAIL_LARGE, NULL);
605 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
606
607 if (stat (filename, &st) < 0)
608 {
609 gchar *display_name;
610
611 display_name = g_filename_display_name (filename);
612 g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
613 _("Error verifying `%s': %s"), display_name,
614 g_strerror (errno));
615 g_free (display_name);
616 return NULL;
617 }
618
619 #ifdef WIN32
620 if (!S_ISREG (st.st_mode))
621 #else
622 if (!S_ISREG (st.st_mode) && !S_ISLNK (st.st_mode))
623 #endif
624 {
625 gchar *display_name;
626
627 display_name = g_filename_display_name (filename);
628 g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
629 _("Error reading `%s': file is not a regular file or symbolic link."),
630 display_name);
631 g_free (display_name);
632 return NULL;
633 }
634
635 uri = g_filename_to_uri (filename, NULL, error);
636 if (!uri)
637 {
638 return NULL;
639 }
640
641 if (egg_pixbuf_has_failed_thumbnail (uri, st.st_mtime, error))
642 {
643 g_free (uri);
644 return NULL;
645 }
646
647 retval = egg_pixbuf_load_thumbnail (uri, st.st_mtime, size);
648 if (!retval)
649 {
650 GError *real_error;
651 gchar *mime_type;
652 ImageInfo info;
653
654 info.size = size;
655 mime_type = NULL;
656 real_error = NULL;
657
658 retval = load_image_at_max_size (filename, &info, &mime_type,
659 &real_error);
660
661 if (!retval)
662 {
663 /*
664 * Don't save failures for filetypes not supported by GdkPixbuf.
665 *
666 * I *think* this is the proper way to go, as there's no need to
667 * spill a half-gig of disk space telling the thumbnailing world
668 * that GdkPixbuf doesn't understand "blah/autogen.sh".
669 */
670 if (real_error->domain != GDK_PIXBUF_ERROR ||
671 real_error->code != GDK_PIXBUF_ERROR_UNKNOWN_TYPE)
672 egg_pixbuf_save_failed_thumbnail (uri, st.st_mtime, real_error);
673
674 if (error != NULL)
675 *error = real_error;
676 else
677 g_error_free (real_error);
678 }
679 else
680 {
681 ThumbnailData *data;
682
683 data = ensure_thumbnail_data (retval);
684 data->size = size;
685 data->uri = g_strdup (uri);
686 data->mtime = st.st_mtime;
687 data->mime_type = g_strdup (mime_type);
688 data->description = g_strdup (gdk_pixbuf_get_option (retval,
689 THUMB_DESCRIPTION_KEY));
690 data->mime_type = g_strdup (mime_type);
691 data->image_width = info.orig_width;
692 data->image_height = info.orig_height;
693 data->filesize = st.st_size;
694
695 egg_pixbuf_save_thumbnailv (retval, NULL, NULL, NULL);
696 }
697
698 g_free (mime_type);
699 }
700
701 g_free (uri);
702
703 return retval;
704 }
705
706 /**
707 * egg_pixbuf_load_thumbnail:
708 * @uri: the URI of the thumbnailed file.
709 * @size: the size of the thumbnail to load.
710 * @mtime: the last modified time of @uri, or %-1 if unknown.
711 *
712 * Attempts to load the thumbnail for @uri at @size. If the thumbnail for @uri
713 * at @size does not already exist, %NULL will be returned.
714 *
715 * Returns: the thumbnail pixbuf of @uri at @size which must be un-referenced
716 * with g_object_unref() when no longer needed, or %NULL.
717 *
718 * Since: 2.6
719 **/
720 GdkPixbuf *
egg_pixbuf_load_thumbnail(const gchar * uri,time_t mtime,EggPixbufThumbnailSize size)721 egg_pixbuf_load_thumbnail (const gchar *uri,
722 time_t mtime,
723 EggPixbufThumbnailSize size)
724 {
725 GdkPixbuf *retval;
726 gchar *filename;
727
728 g_return_val_if_fail (uri != NULL && uri[0] != '\0', NULL);
729 g_return_val_if_fail (mtime >= 0, NULL);
730 g_return_val_if_fail (size == EGG_PIXBUF_THUMBNAIL_NORMAL ||
731 size == EGG_PIXBUF_THUMBNAIL_LARGE, NULL);
732
733 filename = egg_pixbuf_get_thumbnail_filename (uri, size);
734 retval = gdk_pixbuf_new_from_file (filename, NULL);
735 g_free (filename);
736
737 if (retval != NULL &&
738 (!parse_thumbnail_data (retval, size, NULL) ||
739 !egg_pixbuf_is_thumbnail (retval, uri, mtime)))
740 {
741 g_object_unref (retval);
742 return NULL;
743 }
744
745 return retval;
746 }
747
748 /**
749 * egg_pixbuf_load_thumbnail_at_size:
750 * @uri: the URI of the thumbnailed file.
751 * @mtime: the last modified time of @uri, or %-1 if unknown.
752 * @pixel_size: the desired pixel size.
753 *
754 * Attempts to load the thumbnail for @uri at @size. If the thumbnail for @uri
755 * at @size does not already exist, %NULL will be returned.
756 *
757 * Returns: the thumbnail pixbuf of @uri at @size which must be un-referenced
758 * with g_object_unref() when no longer needed, or %NULL.
759 *
760 * Since: 2.6
761 **/
762 GdkPixbuf *
egg_pixbuf_load_thumbnail_at_size(const gchar * uri,time_t mtime,gint pixel_size)763 egg_pixbuf_load_thumbnail_at_size (const gchar *uri,
764 time_t mtime,
765 gint pixel_size)
766 {
767 ImageInfo info;
768 GdkPixbuf *retval;
769 gchar *filename;
770
771 g_return_val_if_fail (uri != NULL && uri[0] != '\0', NULL);
772 g_return_val_if_fail (mtime >= 0, NULL);
773
774 if (pixel_size <= EGG_PIXBUF_THUMBNAIL_NORMAL)
775 {
776 info.size = EGG_PIXBUF_THUMBNAIL_NORMAL;
777 filename = egg_pixbuf_get_thumbnail_filename (uri, EGG_PIXBUF_THUMBNAIL_NORMAL);
778 }
779 else if (pixel_size <= EGG_PIXBUF_THUMBNAIL_LARGE)
780 {
781 info.size = EGG_PIXBUF_THUMBNAIL_LARGE;
782 filename = egg_pixbuf_get_thumbnail_filename (uri, EGG_PIXBUF_THUMBNAIL_LARGE);
783 }
784 else if (strncmp (uri, "file://", 7) == 0)
785 {
786 info.size = -1;
787 filename = g_strdup (uri + 7);
788 }
789 else
790 {
791 info.size = -1;
792 filename = egg_pixbuf_get_thumbnail_filename (uri, EGG_PIXBUF_THUMBNAIL_LARGE);
793 }
794
795 retval = load_image_at_max_size (filename, &info, NULL, NULL);
796 g_free (filename);
797
798 if (retval != NULL &&
799 (!parse_thumbnail_data (retval, info.size, NULL) ||
800 !egg_pixbuf_is_thumbnail (retval, uri, mtime)))
801 {
802 g_object_unref (retval);
803 return NULL;
804 }
805
806 return retval;
807 }
808
809
810 /* ****************** *
811 * Saving Functions *
812 * ****************** */
813
814 static void
collect_save_options(va_list opts,gchar *** keys,gchar *** vals)815 collect_save_options (va_list opts,
816 gchar ***keys,
817 gchar ***vals)
818 {
819 gchar *key;
820 gchar *val;
821 gchar *next;
822 gint count;
823
824 count = 0;
825 *keys = NULL;
826 *vals = NULL;
827
828 next = va_arg (opts, gchar*);
829 while (next)
830 {
831 key = next;
832 val = va_arg (opts, gchar*);
833
834 ++count;
835
836 /* woo, slow */
837 *keys = g_realloc (*keys, sizeof(gchar*) * (count + 1));
838 *vals = g_realloc (*vals, sizeof(gchar*) * (count + 1));
839
840 (*keys)[count-1] = g_strdup (key);
841 (*vals)[count-1] = g_strdup (val);
842
843 (*keys)[count] = NULL;
844 (*vals)[count] = NULL;
845
846 next = va_arg (opts, gchar*);
847 }
848 }
849
850 /**
851 * egg_pixbuf_save_thumbnail:
852 * @thumbnail: the valid thumbnail pixbuf to save.
853 * @error: a location to a pointer to store an error.
854 * @Varargs: a %NULL-terminated metadata key/value pair list.
855 *
856 * Saves @thumbnail to it's appropriate file. Note that @thumbnail must have
857 * it's URI, mtime, and size metadata set before this function is called. Any
858 * additional metadata can be saved using the %NULL-terminated
859 * variable-arguments list. If an error occurs while saving the thumbnail, a
860 * failure thumbnail will be automatically created and saved.
861 *
862 * Returns: %TRUE if the thumbnail was successfully saved, %FALSE if it was not.
863 *
864 * Since: 2.6
865 **/
866 gboolean
egg_pixbuf_save_thumbnail(GdkPixbuf * thumbnail,GError ** error,...)867 egg_pixbuf_save_thumbnail (GdkPixbuf *thumbnail,
868 GError **error,
869 ...)
870 {
871 va_list args;
872 gboolean retval;
873 gchar **keys, **values;
874
875 g_return_val_if_fail (egg_pixbuf_is_thumbnail (thumbnail, NULL, -1), FALSE);
876 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
877
878 keys = NULL;
879 values = NULL;
880
881 va_start (args, error);
882 collect_save_options (args, &keys, &values);
883 va_end (args);
884
885 retval = egg_pixbuf_save_thumbnailv (thumbnail, keys, values, error);
886
887 g_strfreev (values);
888 g_strfreev (keys);
889
890 return retval;
891 }
892
893 static gchar **
create_pair_array(const gchar * key,const gchar * value)894 create_pair_array (const gchar *key,
895 const gchar *value)
896 {
897 gchar **retval;
898
899 retval = g_new (gchar *, 2);
900 retval[0] = g_strdup (key);
901 retval[1] = g_strdup (value);
902
903 return retval;
904 }
905
906 static inline void
merge_keys_values_and_thumbnail_data(GdkPixbuf * thumbnail,gchar ** src_keys,gchar ** src_values,gchar *** dest_keys,gchar *** dest_values)907 merge_keys_values_and_thumbnail_data (GdkPixbuf *thumbnail,
908 gchar **src_keys,
909 gchar **src_values,
910 gchar ***dest_keys,
911 gchar ***dest_values)
912 {
913 ThumbnailData *data;
914 GSList *list;
915 guint n_pairs, i;
916 gchar *tmp;
917
918 data = g_object_get_qdata (G_OBJECT (thumbnail), THUMBNAIL_DATA_QUARK);
919 if (!data)
920 {
921 *dest_keys = g_strdupv (src_keys);
922 *dest_values = g_strdupv (src_values);
923 return;
924 }
925
926 list = g_slist_prepend (NULL, create_pair_array (THUMB_SOFTWARE_KEY,
927 THUMB_SOFTWARE_VALUE));
928
929 if (data->uri)
930 list = g_slist_prepend (list, create_pair_array (THUMB_URI_KEY, data->uri));
931
932 if (data->mtime >= 0)
933 {
934 tmp = g_strdup_printf ("%ld", data->mtime);
935 list = g_slist_prepend (list, create_pair_array (THUMB_MTIME_KEY, tmp));
936 g_free (tmp);
937 }
938
939 if (data->description)
940 list = g_slist_prepend (list, create_pair_array (THUMB_DESCRIPTION_KEY,
941 data->description));
942
943 if (data->mime_type)
944 list = g_slist_prepend (list, create_pair_array (THUMB_MIME_TYPE_KEY,
945 data->mime_type));
946
947 if (data->software)
948 list = g_slist_prepend (list, create_pair_array (THUMB_SOFTWARE_KEY,
949 data->software));
950
951 if (data->filesize > 0)
952 {
953 // tmp = g_strdup_printf ("%" G_GSSIZE_FORMAT, data->filesize);
954 // Change by AMR for GTK+-2.0 compatibility
955 tmp = g_strdup_printf ("%ld", data->filesize);
956 list = g_slist_prepend (list, create_pair_array (THUMB_FILESIZE_KEY, tmp));
957 g_free (tmp);
958 }
959
960 if (data->image_width > 0)
961 {
962 tmp = g_strdup_printf ("%d", data->image_width);
963 list = g_slist_prepend (list, create_pair_array (THUMB_WIDTH_KEY, tmp));
964 g_free (tmp);
965 }
966
967 if (data->image_height > 0)
968 {
969 tmp = g_strdup_printf ("%d", data->image_height);
970 list = g_slist_prepend (list, create_pair_array (THUMB_HEIGHT_KEY, tmp));
971 g_free (tmp);
972 }
973
974 if (data->document_pages > 0)
975 {
976 tmp = g_strdup_printf ("%d", data->document_pages);
977 list = g_slist_prepend (list, create_pair_array (THUMB_PAGES_KEY, tmp));
978 g_free (tmp);
979 }
980
981 if (data->movie_length >= 0)
982 {
983 tmp = g_strdup_printf ("%ld", data->movie_length);
984 list = g_slist_prepend (list, create_pair_array (THUMB_MTIME_KEY, tmp));
985 g_free (tmp);
986 }
987
988 /* Get a count of the keys in src_keys */
989 if (src_keys)
990 {
991 for (n_pairs = 0; src_keys[n_pairs] != NULL; n_pairs++);
992 }
993 else
994 n_pairs = 0;
995
996 n_pairs += g_slist_length (list);
997
998 *dest_keys = g_new0 (gchar *, n_pairs + 1);
999 *dest_values = g_new0 (gchar *, n_pairs + 1);
1000
1001 if (src_keys)
1002 {
1003 for (i = 0; src_keys[i] != NULL; i++)
1004 {
1005 (*dest_keys)[i] = g_strdup (src_keys[i]);
1006 (*dest_values)[i] = g_strdup (src_values[i]);
1007 }
1008 }
1009 else
1010 i = 0;
1011
1012 while (list)
1013 {
1014 gchar **pair;
1015
1016 pair = list->data;
1017 (*dest_keys)[i] = pair[0];
1018 (*dest_values)[i] = pair[1];
1019 g_free (pair);
1020 list = g_slist_remove_link (list, list);
1021 i++;
1022 }
1023 }
1024
1025 /**
1026 * egg_pixbuf_save_thumbnailv:
1027 * @thumbnail: the thumbnail to save.
1028 * @keys: a NULL-terminated array of metadata keys.
1029 * @values: a NULL-terminated array of metadata values.
1030 * @error: a pointer to a location to store errors in.
1031 *
1032 * This function is primarily useful to language bindings. Applications should
1033 * use egg_pixbuf_save_thumbnail().
1034 *
1035 * Returns: %TRUE if the thumbnail was successfully saved, %FALSE if it was not.
1036 *
1037 * Since: 2.6
1038 **/
1039 gboolean
egg_pixbuf_save_thumbnailv(GdkPixbuf * thumbnail,gchar ** keys,gchar ** values,GError ** error)1040 egg_pixbuf_save_thumbnailv (GdkPixbuf *thumbnail,
1041 gchar **keys,
1042 gchar **values,
1043 GError **error)
1044 {
1045 const gchar *uri;
1046 gchar *filename, *tmp_filename;
1047 gchar **real_keys, **real_values;
1048 gint fd;
1049 gboolean retval;
1050 GError *real_error;
1051
1052 g_return_val_if_fail (GDK_IS_PIXBUF (thumbnail), FALSE);
1053 g_return_val_if_fail (egg_pixbuf_is_thumbnail (thumbnail, NULL, -1), FALSE);
1054 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1055
1056 if (!ensure_thumbnail_dirs (error))
1057 return FALSE;
1058
1059 uri = egg_pixbuf_get_thumbnail_uri (thumbnail);
1060 filename =
1061 egg_pixbuf_get_thumbnail_filename (uri,
1062 egg_pixbuf_get_thumbnail_size (thumbnail));
1063 tmp_filename = g_strconcat (filename, ".XXXXXX", NULL);
1064
1065 fd = g_mkstemp (tmp_filename);
1066 if (fd < 0)
1067 {
1068 real_error =
1069 g_error_new (G_FILE_ERROR,
1070 g_file_error_from_errno (errno),
1071 _("Error creating temporary thumbnail file for `%s': %s"),
1072 uri, g_strerror (errno));
1073 g_free (tmp_filename);
1074 g_free (filename);
1075
1076 egg_pixbuf_save_failed_thumbnail (egg_pixbuf_get_thumbnail_uri (thumbnail),
1077 egg_pixbuf_get_thumbnail_mtime (thumbnail),
1078 real_error);
1079 if (error != NULL)
1080 *error = real_error;
1081 else
1082 g_error_free (real_error);
1083
1084 return FALSE;
1085 }
1086 else
1087 {
1088 close (fd);
1089 chmod (tmp_filename, 0600);
1090 }
1091
1092 real_error = NULL;
1093 merge_keys_values_and_thumbnail_data (thumbnail, keys, values,
1094 &real_keys, &real_values);
1095 retval = gdk_pixbuf_savev (thumbnail, tmp_filename, "png",
1096 real_keys, real_values, &real_error);
1097 g_strfreev (real_keys);
1098 g_strfreev (real_values);
1099
1100 if (retval)
1101 rename (tmp_filename, filename);
1102 else
1103 {
1104 egg_pixbuf_save_failed_thumbnail (egg_pixbuf_get_thumbnail_uri (thumbnail),
1105 egg_pixbuf_get_thumbnail_mtime (thumbnail),
1106 real_error);
1107
1108 if (error != NULL)
1109 *error = real_error;
1110 else
1111 g_error_free (real_error);
1112 }
1113
1114 g_free (tmp_filename);
1115 g_free (filename);
1116
1117 return retval;
1118 }
1119
1120
1121 /* ******************* *
1122 * Failure Functions *
1123 * ******************* */
1124
1125 /**
1126 * egg_pixbuf_has_failed_thumbnail:
1127 * @uri: the URI of the thumbnailed file to check.
1128 * @mtime: the last modified time of @uri.
1129 * @error: a pointer to a location to store an error.
1130 *
1131 * Checks to see if creating a thumbnail for @uri which was changed on @mtime
1132 * has already been tried and failed. If it has, the error which prevented the
1133 * thumbnail from being created will be stored in @error.
1134 *
1135 * Returns: %TRUE if the thumbnail creation has already failed, %FALSE if it
1136 * has not.
1137 *
1138 * Since: 2.6
1139 **/
1140 gboolean
egg_pixbuf_has_failed_thumbnail(const gchar * uri,time_t mtime,GError ** error)1141 egg_pixbuf_has_failed_thumbnail (const gchar *uri,
1142 time_t mtime,
1143 GError **error)
1144 {
1145 gchar *md5, *basename, *filename;
1146 gboolean retval;
1147 GdkPixbuf *thumb;
1148
1149 g_return_val_if_fail (uri != NULL && uri[0] != '\0', FALSE);
1150 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1151
1152 retval = FALSE;
1153
1154 md5 = egg_str_get_md5_str (uri);
1155 basename = g_strconcat (md5, ".png", NULL);
1156 g_free (md5);
1157 filename = g_build_filename (g_get_home_dir (), ".thumbnails", FAIL_DIR_NAME,
1158 APP_DIR_NAME, basename, NULL);
1159 thumb = gdk_pixbuf_new_from_file (filename, NULL);
1160 g_free (filename);
1161
1162 if (thumb)
1163 {
1164 retval = (parse_thumbnail_data (thumb, EGG_PIXBUF_THUMBNAIL_UNKNOWN,
1165 error) &&
1166 egg_pixbuf_is_thumbnail (thumb, uri, mtime));
1167 g_object_unref (thumb);
1168 }
1169 else
1170 retval = FALSE;
1171
1172 return retval;
1173 }
1174
1175 /**
1176 * egg_pixbuf_save_failed_thumbnail:
1177 * @uri: the URI which the thumbnail creation failed for.
1178 * @mtime: the time that @uri was last modified.
1179 * @error: the error which occurred while trying to create the thumbnail.
1180 *
1181 * Saves a "failure thumbnail" for the @uri with @mtime. This lets other
1182 * applications using the EggPixbufThumbnail API know that a thumbnail attempt
1183 * was tried for @uri when it was modified last at @mtime. The @error parameter
1184 * lets other applications know exactly why the thumbnail creation failed.
1185 *
1186 * <note>@error must be in either the #G_FILE_ERROR or #GDK_PIXBUF_ERROR
1187 * domain.</note>
1188 *
1189 * Since: 2.6
1190 **/
1191 void
egg_pixbuf_save_failed_thumbnail(const gchar * uri,time_t mtime,const GError * error)1192 egg_pixbuf_save_failed_thumbnail (const gchar *uri,
1193 time_t mtime,
1194 const GError *error)
1195 {
1196 GdkPixbuf *pixbuf;
1197 GError *err_ret;
1198 gchar *md5, *basename, *filename, *tmp_filename, *mtime_str;
1199 gboolean saved_ok;
1200 gint fd;
1201
1202 g_return_if_fail (uri != NULL && uri[0] != '\0');
1203 g_return_if_fail (error == NULL ||
1204 error->domain == G_FILE_ERROR ||
1205 error->domain == GDK_PIXBUF_ERROR);
1206
1207 err_ret = NULL;
1208 if (!ensure_thumbnail_dirs (&err_ret))
1209 {
1210 g_warning ("%s", err_ret->message);
1211 g_error_free (err_ret);
1212 return;
1213 }
1214
1215 md5 = egg_str_get_md5_str (uri);
1216 basename = g_strconcat (md5, ".png", NULL);
1217 g_free (md5);
1218 filename = g_build_filename (g_get_home_dir (), ".thumbnails", FAIL_DIR_NAME,
1219 APP_DIR_NAME, basename, NULL);
1220 g_free (basename);
1221
1222 tmp_filename = g_strconcat (filename, ".XXXXXX", NULL);
1223 fd = g_mkstemp (tmp_filename);
1224 if (fd < 0)
1225 {
1226 g_free (tmp_filename);
1227 g_free (filename);
1228 return;
1229 }
1230 else
1231 close (fd);
1232
1233 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 1, 1);
1234
1235 mtime_str = g_strdup_printf ("%ld", mtime);
1236
1237 if (error)
1238 {
1239 gchar *code_str;
1240 const gchar *domain;
1241
1242 if (error->domain == G_FILE_ERROR)
1243 domain = FILE_ERROR_NAME;
1244 else
1245 domain = PIXBUF_ERROR_NAME;
1246
1247 code_str = g_strdup_printf ("%d", error->code);
1248 saved_ok = gdk_pixbuf_save (pixbuf, tmp_filename, "png", &err_ret,
1249 THUMB_URI_KEY, uri,
1250 THUMB_MTIME_KEY, mtime_str,
1251 THUMB_SOFTWARE_KEY, THUMB_SOFTWARE_VALUE,
1252 THUMB_ERROR_DOMAIN_KEY, domain,
1253 THUMB_ERROR_CODE_KEY, code_str,
1254 THUMB_ERROR_MESSAGE_KEY, error->message,
1255 NULL);
1256 g_free (code_str);
1257 }
1258 else
1259 {
1260 saved_ok = gdk_pixbuf_save (pixbuf, tmp_filename, "png", &err_ret,
1261 THUMB_URI_KEY, uri,
1262 THUMB_MTIME_KEY, mtime_str,
1263 THUMB_SOFTWARE_KEY, THUMB_SOFTWARE_VALUE,
1264 NULL);
1265 }
1266
1267 if (!saved_ok)
1268 {
1269 g_message ("Error saving fail thumbnail: %s", err_ret->message);
1270 g_error_free (err_ret);
1271 }
1272 else
1273 {
1274 chmod (tmp_filename, 0600);
1275 rename (tmp_filename, filename);
1276 }
1277
1278 g_free (tmp_filename);
1279 g_free (filename);
1280 g_free (mtime_str);
1281 }
1282
1283
1284 /**
1285 * egg_pixbuf_get_thumbnail_for_pixbuf:
1286 * @pixbuf: the full-sized source pixbuf.
1287 * @size: the size of the thumbnailnail to generate.
1288 * @uri: the URI location of @pixbuf.
1289 * @mtime: the last-modified time of @uri.
1290 *
1291 * Creates a thumbnail of the in-memory @pixbuf at @size, using @uri and
1292 * @mtime for the pre-requisite metadata.
1293 *
1294 * Returns: a new thumbnail of @pixbuf which must be un-referenced with
1295 * g_object_unref() when no longer needed, or %NULL.
1296 *
1297 * Since: 2.6
1298 **/
1299 GdkPixbuf *
egg_pixbuf_get_thumbnail_for_pixbuf(GdkPixbuf * pixbuf,const gchar * uri,time_t mtime,EggPixbufThumbnailSize size)1300 egg_pixbuf_get_thumbnail_for_pixbuf (GdkPixbuf *pixbuf,
1301 const gchar *uri,
1302 time_t mtime,
1303 EggPixbufThumbnailSize size)
1304 {
1305 GdkPixbuf *retval;
1306 gint width, height;
1307
1308 g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
1309 g_return_val_if_fail (size == EGG_PIXBUF_THUMBNAIL_NORMAL ||
1310 size == EGG_PIXBUF_THUMBNAIL_LARGE, NULL);
1311 g_return_val_if_fail (uri != NULL && uri[0] != '\0', NULL);
1312
1313 width = gdk_pixbuf_get_width (pixbuf);
1314 height = gdk_pixbuf_get_height (pixbuf);
1315
1316 if (width > size || height > size)
1317 {
1318 gdouble scale;
1319
1320 if (width > height)
1321 scale = (gdouble) size / width;
1322 else
1323 scale = (gdouble) size / height;
1324
1325 retval = gdk_pixbuf_scale_simple (pixbuf, scale * width, scale *height,
1326 GDK_INTERP_BILINEAR);
1327 }
1328 else
1329 {
1330 retval = gdk_pixbuf_copy (pixbuf);
1331 }
1332
1333 egg_pixbuf_set_thumbnail_uri (retval, uri);
1334 egg_pixbuf_set_thumbnail_mtime (retval, mtime);
1335 egg_pixbuf_set_thumbnail_size (retval, size);
1336 egg_pixbuf_set_thumbnail_description (retval,
1337 gdk_pixbuf_get_option (pixbuf,
1338 THUMB_DESCRIPTION_KEY));
1339
1340 return retval;
1341 }
1342
1343 /* ******************* *
1344 * Testing Functions *
1345 * ******************* */
1346
1347 /**
1348 * egg_pixbuf_is_thumbnail:
1349 * @pixbuf: the pixbuf to test.
1350 * @uri: the source URI of pixbuf (or %NULL to ignore this test).
1351 * @mtime: the modification time of @uri (or %-1 to ignore this test).
1352 *
1353 * This function will always check for thumbnail metadata attached to @pixbuf,
1354 * specifically the existance of a URI value. If @uri is non-%NULL, then the URI
1355 * value on @pixbuf will be compared with it. If @mtime is greater than or equal
1356 * to %0, then the modification time value will also be checked.
1357 *
1358 * See also: egg_pixbuf_set_thumbnail_uri(), egg_pixbuf_set_thumbnail_mtime().
1359 *
1360 * Returns: %TRUE if @pixbuf can be used as a thumbnail, %FALSE if it cannot.
1361 *
1362 * Since: 2.6
1363 **/
1364 gboolean
egg_pixbuf_is_thumbnail(GdkPixbuf * pixbuf,const gchar * uri,time_t mtime)1365 egg_pixbuf_is_thumbnail (GdkPixbuf *pixbuf,
1366 const gchar *uri,
1367 time_t mtime)
1368 {
1369 ThumbnailData *data;
1370
1371 g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), FALSE);
1372 g_return_val_if_fail (uri == NULL || uri[0] != '\0', FALSE);
1373
1374 data = get_thumbnail_data (pixbuf);
1375
1376 /* Must have thumbnail data and matching URIs. */
1377 if (!data || !data->uri || (uri && strcmp (data->uri, uri) != 0))
1378 return FALSE;
1379
1380 /* Thumbnails of local URIs must have matching mtime. */
1381 if (mtime >= 0 && uri && g_ascii_strncasecmp (data->uri, "file:", 5) == 0)
1382 {
1383 if (data->mtime != mtime)
1384 return FALSE;
1385 }
1386 /* Thumbnails of remote URIs expire after 30 days. We could require the user
1387 pass the proper mtime value for remote URIs, but why? */
1388 else if (mtime >= 0)
1389 {
1390 time_t current_time;
1391
1392 current_time = time (NULL);
1393
1394 if (data->mtime + EXPIRATION_TIME < current_time)
1395 return FALSE;
1396 }
1397
1398 return TRUE;
1399 }
1400
1401
1402 /* ***************** *
1403 * Getters/Setters *
1404 * ***************** */
1405
1406 /**
1407 * egg_pixbuf_get_thumbnail_size:
1408 * @thumbnail: the thumbnail to examine.
1409 *
1410 * Retreives the escaped URI that @thumbnail is a preview of.
1411 *
1412 * Returns: a character array which should not be modified or freed.
1413 *
1414 * Since: 2.6
1415 **/
1416 EggPixbufThumbnailSize
egg_pixbuf_get_thumbnail_size(GdkPixbuf * thumbnail)1417 egg_pixbuf_get_thumbnail_size (GdkPixbuf *thumbnail)
1418 {
1419 ThumbnailData *data;
1420
1421 g_return_val_if_fail (GDK_IS_PIXBUF (thumbnail),
1422 EGG_PIXBUF_THUMBNAIL_UNKNOWN);
1423
1424 data = get_thumbnail_data (thumbnail);
1425
1426 return (data ? data->size : EGG_PIXBUF_THUMBNAIL_UNKNOWN);
1427 }
1428
1429 /**
1430 * egg_pixbuf_set_thumbnail_size:
1431 * @thumbnail: the thumbnail to modify.
1432 * @size: the size of @thumbnail.
1433 *
1434 * Sets the size metadata for @thumbnail. This function may only be
1435 * called once for a particular pixbuf.
1436 *
1437 * Since: 2.6
1438 **/
1439 void
egg_pixbuf_set_thumbnail_size(GdkPixbuf * thumbnail,EggPixbufThumbnailSize size)1440 egg_pixbuf_set_thumbnail_size (GdkPixbuf *thumbnail,
1441 EggPixbufThumbnailSize size)
1442 {
1443 ThumbnailData *data;
1444
1445 g_return_if_fail (GDK_IS_PIXBUF (thumbnail));
1446 g_return_if_fail (size == EGG_PIXBUF_THUMBNAIL_NORMAL ||
1447 size == EGG_PIXBUF_THUMBNAIL_LARGE);
1448
1449 data = ensure_thumbnail_data (thumbnail);
1450 data->size = size;
1451 }
1452
1453 /**
1454 * egg_pixbuf_get_thumbnail_uri:
1455 * @thumbnail: the thumbnail to examine.
1456 *
1457 * Retreives the escaped URI that @thumbnail is a preview of.
1458 *
1459 * Returns: a character array which should not be modified or freed.
1460 *
1461 * Since: 2.6
1462 **/
1463 G_CONST_RETURN gchar *
egg_pixbuf_get_thumbnail_uri(GdkPixbuf * thumbnail)1464 egg_pixbuf_get_thumbnail_uri (GdkPixbuf *thumbnail)
1465 {
1466 ThumbnailData *data;
1467
1468 g_return_val_if_fail (GDK_IS_PIXBUF (thumbnail), NULL);
1469
1470 data = get_thumbnail_data (thumbnail);
1471
1472 return (data ? data->uri : NULL);
1473 }
1474
1475 /**
1476 * egg_pixbuf_set_thumbnail_uri:
1477 * @thumbnail: the thumbnail to modify.
1478 * @uri: the escaped URI that @thumbnail is a preview of.
1479 *
1480 * Sets the URI metadata for @thumbnail. This function may only be
1481 * called once for a particular pixbuf.
1482 *
1483 * Since: 2.6
1484 **/
1485 void
egg_pixbuf_set_thumbnail_uri(GdkPixbuf * thumbnail,const gchar * uri)1486 egg_pixbuf_set_thumbnail_uri (GdkPixbuf *thumbnail,
1487 const gchar *uri)
1488 {
1489 ThumbnailData *data;
1490
1491 g_return_if_fail (GDK_IS_PIXBUF (thumbnail));
1492 g_return_if_fail (uri != NULL && uri[0] != '\0');
1493
1494 data = ensure_thumbnail_data (thumbnail);
1495 g_free (data->uri);
1496 data->uri = g_strdup (uri);
1497 }
1498
1499 /**
1500 * egg_pixbuf_get_thumbnail_mime_type:
1501 * @thumbnail: the thumbnail to examine.
1502 *
1503 * Retreives the mime-type of the file that @thumbnail is a preview of.
1504 *
1505 * Returns: a character array which should not be modified or freed.
1506 *
1507 * Since: 2.6
1508 **/
1509 G_CONST_RETURN gchar *
egg_pixbuf_get_thumbnail_mime_type(GdkPixbuf * thumbnail)1510 egg_pixbuf_get_thumbnail_mime_type (GdkPixbuf *thumbnail)
1511 {
1512 ThumbnailData *data;
1513
1514 g_return_val_if_fail (GDK_IS_PIXBUF (thumbnail), NULL);
1515
1516 data = get_thumbnail_data (thumbnail);
1517
1518 return (data ? data->mime_type : NULL);
1519 }
1520
1521 /**
1522 * egg_pixbuf_set_thumbnail_mime_type:
1523 * @thumbnail: the thumbnail to modify.
1524 * @mime_type: the mime type of the file that @thumbnail is a preview of.
1525 *
1526 * Sets the @mime_type metadata for @thumbnail. This function may only be called
1527 * once on a particular pixbuf.
1528 *
1529 * Since: 2.6
1530 **/
1531 void
egg_pixbuf_set_thumbnail_mime_type(GdkPixbuf * thumbnail,const gchar * mime_type)1532 egg_pixbuf_set_thumbnail_mime_type (GdkPixbuf *thumbnail,
1533 const gchar *mime_type)
1534 {
1535 ThumbnailData *data;
1536
1537 g_return_if_fail (GDK_IS_PIXBUF (thumbnail));
1538 g_return_if_fail (mime_type != NULL && mime_type[0] != '\0');
1539
1540 data = ensure_thumbnail_data (thumbnail);
1541 g_free (data->mime_type);
1542 data->mime_type = g_strdup (mime_type);
1543 }
1544
1545 /**
1546 * egg_pixbuf_get_thumbnail_description:
1547 * @thumbnail: the thumbnail to examine.
1548 *
1549 * Retreives the user-specified description (comment) for the file that
1550 * @thumbnail is a preview of. If this metadata was not saved (or does not
1551 * exist for @thumbnail), NULL will be returned.
1552 *
1553 * Returns: a character array which should not be modified or freed.
1554 *
1555 * Since: 2.6
1556 **/
1557 G_CONST_RETURN gchar *
egg_pixbuf_get_thumbnail_description(GdkPixbuf * thumbnail)1558 egg_pixbuf_get_thumbnail_description (GdkPixbuf *thumbnail)
1559 {
1560 ThumbnailData *data;
1561
1562 g_return_val_if_fail (GDK_IS_PIXBUF (thumbnail), NULL);
1563
1564 data = get_thumbnail_data (thumbnail);
1565
1566 return (data ? data->description : NULL);
1567 }
1568
1569 /**
1570 * egg_pixbuf_set_thumbnail_description:
1571 * @thumbnail: the thumbnail to modify.
1572 * @description: the description of the file that @thumbnail is a preview of.
1573 *
1574 * Sets the user-specified @description metadata for @thumbnail. This function
1575 * may only be called once on a particular pixbuf.
1576 *
1577 * Since: 2.6
1578 **/
1579 void
egg_pixbuf_set_thumbnail_description(GdkPixbuf * thumbnail,const gchar * description)1580 egg_pixbuf_set_thumbnail_description (GdkPixbuf *thumbnail,
1581 const gchar *description)
1582 {
1583 ThumbnailData *data;
1584
1585 g_return_if_fail (GDK_IS_PIXBUF (thumbnail));
1586 g_return_if_fail (description != NULL && description[0] != '\0');
1587
1588 data = ensure_thumbnail_data (thumbnail);
1589 g_free (data->description);
1590 data->description = g_strdup (description);
1591 }
1592
1593 /**
1594 * egg_pixbuf_get_thumbnail_mtime:
1595 * @thumbnail: the thumbnail to examine.
1596 *
1597 * Retreives the UNIX time (seconds since the epoch/time_t) the file which
1598 * @thumbnail is a preview of was modified on when the @thumbnail was created.
1599 * If this metadata was not saved with @thumbnail, %-1 will be returned.
1600 *
1601 * Returns: a UNIX seconds-since-epoch time value, or %-1.
1602 *
1603 * Since: 2.6
1604 **/
1605 time_t
egg_pixbuf_get_thumbnail_mtime(GdkPixbuf * thumbnail)1606 egg_pixbuf_get_thumbnail_mtime (GdkPixbuf *thumbnail)
1607 {
1608 ThumbnailData *data;
1609
1610 g_return_val_if_fail (GDK_IS_PIXBUF (thumbnail), -1);
1611
1612 data = get_thumbnail_data (thumbnail);
1613
1614 return (data ? data->mtime : -1);
1615 }
1616
1617 /**
1618 * egg_pixbuf_set_thumbnail_mtime:
1619 * @thumbnail: the thumbnail to modify.
1620 * @mtime: the last-modified time of the file that @thumbnail is a preview of.
1621 *
1622 * Sets the last-modified @mtime metadata for @thumbnail. This function
1623 * may only be called once on a particular pixbuf.
1624 *
1625 * Since: 2.6
1626 **/
1627 void
egg_pixbuf_set_thumbnail_mtime(GdkPixbuf * thumbnail,time_t mtime)1628 egg_pixbuf_set_thumbnail_mtime (GdkPixbuf *thumbnail,
1629 time_t mtime)
1630 {
1631 ThumbnailData *data;
1632
1633 g_return_if_fail (GDK_IS_PIXBUF (thumbnail));
1634
1635 data = ensure_thumbnail_data (thumbnail);
1636 data->mtime = mtime;
1637 }
1638
1639 /**
1640 * egg_pixbuf_get_thumbnail_filesize:
1641 * @thumbnail: the thumbnail to examine.
1642 *
1643 * Retreives the size in bytes of the file which @thumbnail is a preview of. If
1644 * this metadata was not saved with @thumbnail, %-1 will be returned.
1645 *
1646 * Returns: a 64-bit integer.
1647 *
1648 * Since: 2.6
1649 **/
1650 gssize
egg_pixbuf_get_thumbnail_filesize(GdkPixbuf * thumbnail)1651 egg_pixbuf_get_thumbnail_filesize (GdkPixbuf *thumbnail)
1652 {
1653 ThumbnailData *data;
1654
1655 g_return_val_if_fail (GDK_IS_PIXBUF (thumbnail), -1);
1656
1657 data = get_thumbnail_data (thumbnail);
1658
1659 return (data ? data->filesize : -1);
1660 }
1661
1662 /**
1663 * egg_pixbuf_set_thumbnail_filesize:
1664 * @thumbnail: the thumbnail to modify.
1665 * @filesize: the size (in bytes) of the file that @thumbnail is a preview of.
1666 *
1667 * Sets the @filesize metadata for @thumbnail. This function may only be called
1668 * once on a particular pixbuf.
1669 *
1670 * Since: 2.6
1671 **/
1672 void
egg_pixbuf_set_thumbnail_filesize(GdkPixbuf * thumbnail,gssize filesize)1673 egg_pixbuf_set_thumbnail_filesize (GdkPixbuf *thumbnail,
1674 gssize filesize)
1675 {
1676 ThumbnailData *data;
1677
1678 g_return_if_fail (GDK_IS_PIXBUF (thumbnail));
1679
1680 data = ensure_thumbnail_data (thumbnail);
1681 data->filesize = filesize;
1682 }
1683
1684 /**
1685 * egg_pixbuf_get_thumbnail_image_width:
1686 * @thumbnail: the thumbnail to examine.
1687 *
1688 * Retreives the width (in pixels) of the image contained in the file that
1689 * @thumbnail is a preview of. If this metadata was not saved with @thumbnail
1690 * (e.g. if the original file was not an image), %-1 will be returned.
1691 *
1692 * Returns: an integer.
1693 *
1694 * Since: 2.6
1695 **/
1696 gint
egg_pixbuf_get_thumbnail_image_width(GdkPixbuf * thumbnail)1697 egg_pixbuf_get_thumbnail_image_width (GdkPixbuf *thumbnail)
1698 {
1699 ThumbnailData *data;
1700
1701 g_return_val_if_fail (GDK_IS_PIXBUF (thumbnail), -1);
1702
1703 data = get_thumbnail_data (thumbnail);
1704
1705 return (data ? data->image_width : -1);
1706 }
1707
1708 /**
1709 * egg_pixbuf_set_thumbnail_image_width:
1710 * @thumbnail: the thumbnail to modify.
1711 * @image_width: the width (in pixels) of the image file that @thumbnail is a preview of.
1712 *
1713 * Sets the @image_width metadata for @thumbnail. This function may only be
1714 * called once on a particular pixbuf.
1715 *
1716 * Since: 2.6
1717 **/
1718 void
egg_pixbuf_set_thumbnail_image_width(GdkPixbuf * thumbnail,gint image_width)1719 egg_pixbuf_set_thumbnail_image_width (GdkPixbuf *thumbnail,
1720 gint image_width)
1721 {
1722 ThumbnailData *data;
1723
1724 g_return_if_fail (GDK_IS_PIXBUF (thumbnail));
1725
1726 data = ensure_thumbnail_data (thumbnail);
1727 data->image_width = image_width;
1728 }
1729
1730 /**
1731 * egg_pixbuf_get_thumbnail_image_height:
1732 * @thumbnail: the thumbnail to examine.
1733 *
1734 * Retreives the height (in pixels) of the image contained in the file that
1735 * @thumbnail is a preview of. If this metadata was not saved with @thumbnail
1736 * (e.g. if the original file was not an image), %-1 will be returned.
1737 *
1738 * Returns: an integer.
1739 *
1740 * Since: 2.6
1741 **/
1742 gint
egg_pixbuf_get_thumbnail_image_height(GdkPixbuf * thumbnail)1743 egg_pixbuf_get_thumbnail_image_height (GdkPixbuf *thumbnail)
1744 {
1745 ThumbnailData *data;
1746
1747 g_return_val_if_fail (GDK_IS_PIXBUF (thumbnail), -1);
1748
1749 data = get_thumbnail_data (thumbnail);
1750
1751 return (data ? data->image_height : -1);
1752 }
1753
1754 /**
1755 * egg_pixbuf_set_thumbnail_image_height:
1756 * @thumbnail: the thumbnail to modify.
1757 * @image_height: the height (in pixels) of the image file that @thumbnail is a preview of.
1758 *
1759 * Sets the @image_height metadata for @thumbnail. This function may only be
1760 * called once on a particular pixbuf.
1761 *
1762 * Since: 2.6
1763 **/
1764 void
egg_pixbuf_set_thumbnail_image_height(GdkPixbuf * thumbnail,gint image_height)1765 egg_pixbuf_set_thumbnail_image_height (GdkPixbuf *thumbnail,
1766 gint image_height)
1767 {
1768 ThumbnailData *data;
1769
1770 g_return_if_fail (GDK_IS_PIXBUF (thumbnail));
1771
1772 data = ensure_thumbnail_data (thumbnail);
1773 data->image_height = image_height;
1774 }
1775
1776 /**
1777 * egg_pixbuf_get_thumbnail_document_pages:
1778 * @thumbnail: the thumbnail to examine.
1779 *
1780 * Retreives the number of pages in the document contained in the file that
1781 * @thumbnail is a preview of. If this metadata was not saved with @thumbnail
1782 * (e.g. if the original file was not a paged document), %-1 will be returned.
1783 *
1784 * Returns: an integer.
1785 *
1786 * Since: 2.6
1787 **/
1788 gint
egg_pixbuf_get_thumbnail_document_pages(GdkPixbuf * thumbnail)1789 egg_pixbuf_get_thumbnail_document_pages (GdkPixbuf *thumbnail)
1790 {
1791 ThumbnailData *data;
1792
1793 g_return_val_if_fail (GDK_IS_PIXBUF (thumbnail), -1);
1794
1795 data = get_thumbnail_data (thumbnail);
1796
1797 return (data ? data->document_pages : -1);
1798 }
1799
1800 /**
1801 * egg_pixbuf_set_thumbnail_document_pages:
1802 * @thumbnail: the thumbnail to modify.
1803 * @document_pages: the number of pages in the document file that @thumbnail is
1804 * a preview of.
1805 *
1806 * Sets the @document_pages metadata for @thumbnail. This function may only be
1807 * called once on a particular pixbuf.
1808 *
1809 * Since: 2.6
1810 **/
1811 void
egg_pixbuf_set_thumbnail_document_pages(GdkPixbuf * thumbnail,gint document_pages)1812 egg_pixbuf_set_thumbnail_document_pages (GdkPixbuf *thumbnail,
1813 gint document_pages)
1814 {
1815 ThumbnailData *data;
1816
1817 g_return_if_fail (GDK_IS_PIXBUF (thumbnail));
1818
1819 data = ensure_thumbnail_data (thumbnail);
1820 data->document_pages = document_pages;
1821 }
1822
1823 /**
1824 * egg_pixbuf_get_thumbnail_movie_length:
1825 * @thumbnail: the thumbnail to examine.
1826 *
1827 * Retreives the length (in seconds) of the movie contained in the file that
1828 * @thumbnail is a preview of. If this metadata was not saved with @thumbnail
1829 * (e.g. if the original file was not a movie), %-1 will be returned.
1830 *
1831 * Returns: a 64-bit integer.
1832 *
1833 * Since: 2.6
1834 **/
1835 time_t
egg_pixbuf_get_thumbnail_movie_length(GdkPixbuf * thumbnail)1836 egg_pixbuf_get_thumbnail_movie_length (GdkPixbuf *thumbnail)
1837 {
1838 ThumbnailData *data;
1839
1840 g_return_val_if_fail (GDK_IS_PIXBUF (thumbnail), -1);
1841
1842 data = get_thumbnail_data (thumbnail);
1843
1844 return (data ? data->movie_length : -1);
1845 }
1846
1847 /**
1848 * egg_pixbuf_set_thumbnail_movie_length:
1849 * @thumbnail: the thumbnail to modify.
1850 * @movie_length: the length (in seconds) of the movie file that @thumbnail is a preview of.
1851 *
1852 * Sets the @movie_length metadata for @thumbnail. This function may only be
1853 * called once on a particular pixbuf.
1854 *
1855 * Since: 2.6
1856 **/
1857 void
egg_pixbuf_set_thumbnail_movie_length(GdkPixbuf * thumbnail,time_t movie_length)1858 egg_pixbuf_set_thumbnail_movie_length (GdkPixbuf *thumbnail,
1859 time_t movie_length)
1860 {
1861 ThumbnailData *data;
1862
1863 g_return_if_fail (GDK_IS_PIXBUF (thumbnail));
1864
1865 data = ensure_thumbnail_data (thumbnail);
1866 data->movie_length = movie_length;
1867 }
1868
1869 /**
1870 * egg_pixbuf_get_thumbnail_software:
1871 * @thumbnail: the thumbnail to examine.
1872 *
1873 * Retreives the name of the software which originally created @thumbnail. If
1874 * this metadata was not saved (or does not exist for @thumbnail), NULL will be
1875 * returned.
1876 *
1877 * Returns: a character array which should not be modified or freed.
1878 *
1879 * Since: 2.6
1880 **/
1881 G_CONST_RETURN gchar *
egg_pixbuf_get_thumbnail_software(GdkPixbuf * thumbnail)1882 egg_pixbuf_get_thumbnail_software (GdkPixbuf *thumbnail)
1883 {
1884 ThumbnailData *data;
1885
1886 g_return_val_if_fail (GDK_IS_PIXBUF (thumbnail), NULL);
1887
1888 data = get_thumbnail_data (thumbnail);
1889
1890 return (data ? data->software : NULL);
1891 }
1892
1893
1894 /* ******************** *
1895 * Filename Functions *
1896 * ******************** */
1897
1898 /**
1899 * egg_pixbuf_get_thumbnail_filename:
1900 * @uri: the URI of the file to thumbnail.
1901 * @size: the desired size of the thumbnail.
1902 *
1903 * Constructs the global thumbnail filename for @uri at @size. This filename
1904 * can be used to open and save the thumbnail.
1905 *
1906 * Returns: a newly-allocated character array which should be freed with
1907 * g_free() when no longer needed.
1908 *
1909 * Since: 2.6
1910 **/
1911 gchar *
egg_pixbuf_get_thumbnail_filename(const gchar * uri,EggPixbufThumbnailSize size)1912 egg_pixbuf_get_thumbnail_filename (const gchar *uri,
1913 EggPixbufThumbnailSize size)
1914 {
1915 const gchar *home_dir = NULL;
1916 gchar *md5, *basename, *filename;
1917
1918 g_return_val_if_fail (uri != NULL && uri[0] != '\0', NULL);
1919 g_return_val_if_fail (size == EGG_PIXBUF_THUMBNAIL_NORMAL ||
1920 size == EGG_PIXBUF_THUMBNAIL_LARGE, FALSE);
1921
1922 if (home_dir == NULL)
1923 home_dir = g_get_home_dir ();
1924 else
1925 home_dir = g_get_tmp_dir ();
1926
1927 md5 = egg_str_get_md5_str (uri);
1928 basename = g_strconcat (md5, ".png", NULL);
1929 filename = g_build_filename (home_dir, ".thumbnails", SIZE_TO_DIR (size),
1930 basename, NULL);
1931 g_free (basename);
1932
1933 return filename;
1934 }
1935
1936 #if 0
1937
1938 /**
1939 * egg_pixbuf_get_local_thumbnail_uri:
1940 * @uri: the URI of the file to thumbnail.
1941 * @size: the desired size of the thumbnail.
1942 *
1943 * Constructs the correct thumbnail URI for the "local" thumbnail directory,
1944 * intended for removable media.
1945 *
1946 * Returns: a string URI which must be freed with g_free() when no longer
1947 * needed, or %NULL.
1948 *
1949 * Since: 2.6
1950 **/
1951 gchar *
1952 egg_pixbuf_get_local_thumbnail_uri (const gchar *uri,
1953 EggPixbufThumbnailSize size)
1954 {
1955 gchar *retval;
1956
1957 g_return_val_if_fail (uri != NULL && uri[0] != '\0', NULL);
1958 g_return_val_if_fail (size == EGG_PIXBUF_THUMBNAIL_NORMAL ||
1959 size == EGG_PIXBUF_THUMBNAIL_LARGE, FALSE);
1960
1961 retval = NULL;
1962
1963 return retval;
1964 }
1965
1966 #endif /* Disabled */
1967