1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * gimpimagefile.c
5  *
6  * Copyright (C) 2001-2004  Sven Neumann <sven@gimp.org>
7  *                          Michael Natterer <mitch@gimp.org>
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
21  */
22 
23 #include "config.h"
24 
25 #include <string.h>
26 
27 #include <gegl.h>
28 #include <gdk-pixbuf/gdk-pixbuf.h>
29 
30 #include "libgimpbase/gimpbase.h"
31 #include "libgimpthumb/gimpthumb.h"
32 
33 #include "core-types.h"
34 
35 #include "config/gimpcoreconfig.h"
36 
37 #include "gegl/gimp-babl.h"
38 #include "gegl/gimp-gegl-utils.h"
39 
40 #include "gimp.h"
41 #include "gimpcontainer.h"
42 #include "gimpcontext.h"
43 #include "gimpimage.h"
44 #include "gimpimagefile.h"
45 #include "gimpmarshal.h"
46 #include "gimppickable.h"
47 #include "gimpprogress.h"
48 
49 #include "file/file-open.h"
50 
51 #include "gimp-intl.h"
52 
53 
54 enum
55 {
56   INFO_CHANGED,
57   LAST_SIGNAL
58 };
59 
60 
61 typedef struct _GimpImagefilePrivate GimpImagefilePrivate;
62 
63 struct _GimpImagefilePrivate
64 {
65   Gimp          *gimp;
66 
67   GFile         *file;
68   GimpThumbnail *thumbnail;
69   GIcon         *icon;
70   GCancellable  *icon_cancellable;
71 
72   gchar         *description;
73   gboolean       static_desc;
74 };
75 
76 #define GET_PRIVATE(imagefile) ((GimpImagefilePrivate *) gimp_imagefile_get_instance_private ((GimpImagefile *) (imagefile)))
77 
78 
79 static void        gimp_imagefile_dispose          (GObject        *object);
80 static void        gimp_imagefile_finalize         (GObject        *object);
81 
82 static void        gimp_imagefile_name_changed     (GimpObject     *object);
83 
84 static GdkPixbuf * gimp_imagefile_get_new_pixbuf   (GimpViewable   *viewable,
85                                                     GimpContext    *context,
86                                                     gint            width,
87                                                     gint            height);
88 static gchar     * gimp_imagefile_get_description  (GimpViewable   *viewable,
89                                                     gchar         **tooltip);
90 
91 static void        gimp_imagefile_info_changed     (GimpImagefile  *imagefile);
92 static void        gimp_imagefile_notify_thumbnail (GimpImagefile  *imagefile,
93                                                     GParamSpec     *pspec);
94 
95 static void        gimp_imagefile_icon_callback    (GObject        *source_object,
96                                                     GAsyncResult   *result,
97                                                     gpointer        data);
98 
99 static GdkPixbuf * gimp_imagefile_load_thumb       (GimpImagefile  *imagefile,
100                                                     gint            width,
101                                                     gint            height);
102 static gboolean    gimp_imagefile_save_thumb       (GimpImagefile  *imagefile,
103                                                     GimpImage      *image,
104                                                     gint            size,
105                                                     gboolean        replace,
106                                                     GError        **error);
107 
108 static void     gimp_thumbnail_set_info_from_image (GimpThumbnail  *thumbnail,
109                                                     const gchar    *mime_type,
110                                                     GimpImage      *image);
111 static void     gimp_thumbnail_set_info            (GimpThumbnail  *thumbnail,
112                                                     const gchar    *mime_type,
113                                                     gint            width,
114                                                     gint            height,
115                                                     const Babl     *format,
116                                                     gint            num_layers);
117 
118 
119 G_DEFINE_TYPE_WITH_PRIVATE (GimpImagefile, gimp_imagefile, GIMP_TYPE_VIEWABLE)
120 
121 #define parent_class gimp_imagefile_parent_class
122 
123 static guint gimp_imagefile_signals[LAST_SIGNAL] = { 0 };
124 
125 
126 static void
gimp_imagefile_class_init(GimpImagefileClass * klass)127 gimp_imagefile_class_init (GimpImagefileClass *klass)
128 {
129   GObjectClass      *object_class      = G_OBJECT_CLASS (klass);
130   GimpObjectClass   *gimp_object_class = GIMP_OBJECT_CLASS (klass);
131   GimpViewableClass *viewable_class    = GIMP_VIEWABLE_CLASS (klass);
132   gchar             *creator;
133 
134   gimp_imagefile_signals[INFO_CHANGED] =
135     g_signal_new ("info-changed",
136                   G_TYPE_FROM_CLASS (klass),
137                   G_SIGNAL_RUN_FIRST,
138                   G_STRUCT_OFFSET (GimpImagefileClass, info_changed),
139                   NULL, NULL,
140                   gimp_marshal_VOID__VOID,
141                   G_TYPE_NONE, 0);
142 
143   object_class->dispose               = gimp_imagefile_dispose;
144   object_class->finalize              = gimp_imagefile_finalize;
145 
146   gimp_object_class->name_changed     = gimp_imagefile_name_changed;
147 
148   viewable_class->name_changed_signal = "info-changed";
149   viewable_class->get_new_pixbuf      = gimp_imagefile_get_new_pixbuf;
150   viewable_class->get_description     = gimp_imagefile_get_description;
151 
152   g_type_class_ref (GIMP_TYPE_IMAGE_TYPE);
153 
154   creator = g_strdup_printf ("gimp-%d.%d",
155                              GIMP_MAJOR_VERSION, GIMP_MINOR_VERSION);
156 
157   gimp_thumb_init (creator, NULL);
158 
159   g_free (creator);
160 }
161 
162 static void
gimp_imagefile_init(GimpImagefile * imagefile)163 gimp_imagefile_init (GimpImagefile *imagefile)
164 {
165   GimpImagefilePrivate *private = GET_PRIVATE (imagefile);
166 
167   private->thumbnail = gimp_thumbnail_new ();
168 
169   g_signal_connect_object (private->thumbnail, "notify",
170                            G_CALLBACK (gimp_imagefile_notify_thumbnail),
171                            imagefile, G_CONNECT_SWAPPED);
172 }
173 
174 static void
gimp_imagefile_dispose(GObject * object)175 gimp_imagefile_dispose (GObject *object)
176 {
177   GimpImagefilePrivate *private = GET_PRIVATE (object);
178 
179   if (private->icon_cancellable)
180     {
181       g_cancellable_cancel (private->icon_cancellable);
182       g_clear_object (&private->icon_cancellable);
183     }
184 
185   G_OBJECT_CLASS (parent_class)->dispose (object);
186 }
187 
188 static void
gimp_imagefile_finalize(GObject * object)189 gimp_imagefile_finalize (GObject *object)
190 {
191   GimpImagefilePrivate *private = GET_PRIVATE (object);
192 
193   if (private->description)
194     {
195       if (! private->static_desc)
196         g_free (private->description);
197 
198       private->description = NULL;
199     }
200 
201   g_clear_object (&private->thumbnail);
202   g_clear_object (&private->icon);
203   g_clear_object (&private->file);
204 
205   G_OBJECT_CLASS (parent_class)->finalize (object);
206 }
207 
208 static void
gimp_imagefile_name_changed(GimpObject * object)209 gimp_imagefile_name_changed (GimpObject *object)
210 {
211   GimpImagefilePrivate *private = GET_PRIVATE (object);
212 
213   if (GIMP_OBJECT_CLASS (parent_class)->name_changed)
214     GIMP_OBJECT_CLASS (parent_class)->name_changed (object);
215 
216   gimp_thumbnail_set_uri (private->thumbnail, gimp_object_get_name (object));
217 
218   g_clear_object (&private->file);
219 
220   if (gimp_object_get_name (object))
221     private->file = g_file_new_for_uri (gimp_object_get_name (object));
222 }
223 
224 static GdkPixbuf *
gimp_imagefile_get_new_pixbuf(GimpViewable * viewable,GimpContext * context,gint width,gint height)225 gimp_imagefile_get_new_pixbuf (GimpViewable *viewable,
226                                GimpContext  *context,
227                                gint          width,
228                                gint          height)
229 {
230   GimpImagefile *imagefile = GIMP_IMAGEFILE (viewable);
231 
232   if (! gimp_object_get_name (imagefile))
233     return NULL;
234 
235   return gimp_imagefile_load_thumb (imagefile, width, height);
236 }
237 
238 static gchar *
gimp_imagefile_get_description(GimpViewable * viewable,gchar ** tooltip)239 gimp_imagefile_get_description (GimpViewable   *viewable,
240                                 gchar         **tooltip)
241 {
242   GimpImagefile        *imagefile = GIMP_IMAGEFILE (viewable);
243   GimpImagefilePrivate *private   = GET_PRIVATE (imagefile);
244   GimpThumbnail        *thumbnail = private->thumbnail;
245   gchar                *basename;
246 
247   if (! private->file)
248     return NULL;
249 
250   if (tooltip)
251     {
252       const gchar *name;
253       const gchar *desc;
254 
255       name = gimp_file_get_utf8_name (private->file);
256       desc = gimp_imagefile_get_desc_string (imagefile);
257 
258       if (desc)
259         *tooltip = g_strdup_printf ("%s\n%s", name, desc);
260       else
261         *tooltip = g_strdup (name);
262     }
263 
264   basename = g_path_get_basename (gimp_file_get_utf8_name (private->file));
265 
266   if (thumbnail->image_width > 0 && thumbnail->image_height > 0)
267     {
268       gchar *tmp = basename;
269 
270       basename = g_strdup_printf ("%s (%d × %d)",
271                                   tmp,
272                                   thumbnail->image_width,
273                                   thumbnail->image_height);
274       g_free (tmp);
275     }
276 
277   return basename;
278 }
279 
280 
281 /*  public functions  */
282 
283 GimpImagefile *
gimp_imagefile_new(Gimp * gimp,GFile * file)284 gimp_imagefile_new (Gimp  *gimp,
285                     GFile *file)
286 {
287   GimpImagefile *imagefile;
288 
289   g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
290   g_return_val_if_fail (file == NULL || G_IS_FILE (file), NULL);
291 
292   imagefile = g_object_new (GIMP_TYPE_IMAGEFILE, NULL);
293 
294   GET_PRIVATE (imagefile)->gimp = gimp;
295 
296   if (file)
297     {
298       gimp_object_take_name (GIMP_OBJECT (imagefile), g_file_get_uri (file));
299 
300       /* file member gets created by gimp_imagefile_name_changed() */
301     }
302 
303   return imagefile;
304 }
305 
306 GFile *
gimp_imagefile_get_file(GimpImagefile * imagefile)307 gimp_imagefile_get_file (GimpImagefile *imagefile)
308 {
309   g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), NULL);
310 
311   return GET_PRIVATE (imagefile)->file;
312 }
313 
314 void
gimp_imagefile_set_file(GimpImagefile * imagefile,GFile * file)315 gimp_imagefile_set_file (GimpImagefile *imagefile,
316                          GFile         *file)
317 {
318   g_return_if_fail (GIMP_IS_IMAGEFILE (imagefile));
319   g_return_if_fail (file == NULL || G_IS_FILE (file));
320 
321   if (GET_PRIVATE (imagefile)->file != file)
322     {
323       gimp_object_take_name (GIMP_OBJECT (imagefile),
324                              file ? g_file_get_uri (file) : NULL);
325     }
326 }
327 
328 GimpThumbnail *
gimp_imagefile_get_thumbnail(GimpImagefile * imagefile)329 gimp_imagefile_get_thumbnail (GimpImagefile *imagefile)
330 {
331   g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), NULL);
332 
333   return GET_PRIVATE (imagefile)->thumbnail;
334 }
335 
336 GIcon *
gimp_imagefile_get_gicon(GimpImagefile * imagefile)337 gimp_imagefile_get_gicon (GimpImagefile *imagefile)
338 {
339   GimpImagefilePrivate *private;
340 
341   g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), NULL);
342 
343   private = GET_PRIVATE (imagefile);
344 
345   if (private->icon)
346     return private->icon;
347 
348   if (private->file && ! private->icon_cancellable)
349     {
350       private->icon_cancellable = g_cancellable_new ();
351 
352       g_file_query_info_async (private->file, "standard::icon",
353                                G_FILE_QUERY_INFO_NONE,
354                                G_PRIORITY_DEFAULT,
355                                private->icon_cancellable,
356                                gimp_imagefile_icon_callback,
357                                imagefile);
358     }
359 
360   return NULL;
361 }
362 
363 void
gimp_imagefile_set_mime_type(GimpImagefile * imagefile,const gchar * mime_type)364 gimp_imagefile_set_mime_type (GimpImagefile *imagefile,
365                               const gchar   *mime_type)
366 {
367   g_return_if_fail (GIMP_IS_IMAGEFILE (imagefile));
368 
369   g_object_set (GET_PRIVATE (imagefile)->thumbnail,
370                 "image-mimetype", mime_type,
371                 NULL);
372 }
373 
374 void
gimp_imagefile_update(GimpImagefile * imagefile)375 gimp_imagefile_update (GimpImagefile *imagefile)
376 {
377   GimpImagefilePrivate *private;
378   gchar                *uri;
379 
380   g_return_if_fail (GIMP_IS_IMAGEFILE (imagefile));
381 
382   private = GET_PRIVATE (imagefile);
383 
384   gimp_viewable_invalidate_preview (GIMP_VIEWABLE (imagefile));
385 
386   g_object_get (private->thumbnail,
387                 "image-uri", &uri,
388                 NULL);
389 
390   if (uri)
391     {
392       GimpImagefile *documents_imagefile = (GimpImagefile *)
393         gimp_container_get_child_by_name (private->gimp->documents, uri);
394 
395       if (documents_imagefile != imagefile &&
396           GIMP_IS_IMAGEFILE (documents_imagefile))
397         gimp_viewable_invalidate_preview (GIMP_VIEWABLE (documents_imagefile));
398 
399       g_free (uri);
400     }
401 }
402 
403 gboolean
gimp_imagefile_create_thumbnail(GimpImagefile * imagefile,GimpContext * context,GimpProgress * progress,gint size,gboolean replace,GError ** error)404 gimp_imagefile_create_thumbnail (GimpImagefile  *imagefile,
405                                  GimpContext    *context,
406                                  GimpProgress   *progress,
407                                  gint            size,
408                                  gboolean        replace,
409                                  GError        **error)
410 {
411   GimpImagefilePrivate *private;
412   GimpThumbnail        *thumbnail;
413   GimpThumbState        image_state;
414 
415   g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), FALSE);
416   g_return_val_if_fail (GIMP_IS_CONTEXT (context), FALSE);
417   g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE);
418   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
419 
420   /* thumbnailing is disabled, we successfully did nothing */
421   if (size < 1)
422     return TRUE;
423 
424   private = GET_PRIVATE (imagefile);
425 
426   thumbnail = private->thumbnail;
427 
428   gimp_thumbnail_set_uri (thumbnail,
429                           gimp_object_get_name (imagefile));
430 
431   image_state = gimp_thumbnail_peek_image (thumbnail);
432 
433   if (image_state == GIMP_THUMB_STATE_REMOTE ||
434       image_state >= GIMP_THUMB_STATE_EXISTS)
435     {
436       GimpImage     *image;
437       gboolean       success;
438       gint           width      = 0;
439       gint           height     = 0;
440       const gchar   *mime_type  = NULL;
441       const Babl    *format     = NULL;
442       gint           num_layers = -1;
443 
444       /*  we only want to attempt thumbnailing on readable, regular files  */
445       if (g_file_is_native (private->file))
446         {
447           GFileInfo *file_info;
448           gboolean   regular;
449           gboolean   readable;
450 
451           file_info = g_file_query_info (private->file,
452                                          G_FILE_ATTRIBUTE_STANDARD_TYPE ","
453                                          G_FILE_ATTRIBUTE_ACCESS_CAN_READ,
454                                          G_FILE_QUERY_INFO_NONE,
455                                          NULL, NULL);
456 
457           regular  = (g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR);
458           readable = g_file_info_get_attribute_boolean (file_info,
459                                                         G_FILE_ATTRIBUTE_ACCESS_CAN_READ);
460 
461           g_object_unref (file_info);
462 
463           if (! (regular && readable))
464             return TRUE;
465         }
466 
467       g_object_ref (imagefile);
468 
469       /* don't pass the error, we're only interested in errors from
470        * actual thumbnail saving
471        */
472       image = file_open_thumbnail (private->gimp, context, progress,
473                                    private->file, size,
474                                    &mime_type, &width, &height,
475                                    &format, &num_layers, NULL);
476 
477       if (image)
478         {
479           gimp_thumbnail_set_info (private->thumbnail,
480                                    mime_type, width, height,
481                                    format, num_layers);
482         }
483       else
484         {
485           GimpPDBStatusType  status;
486 
487           /* don't pass the error, we're only interested in errors
488            * from actual thumbnail saving
489            */
490           image = file_open_image (private->gimp, context, progress,
491                                    private->file,
492                                    private->file,
493                                    FALSE, NULL, GIMP_RUN_NONINTERACTIVE,
494                                    &status, &mime_type, NULL);
495 
496           if (image)
497             gimp_thumbnail_set_info_from_image (private->thumbnail,
498                                                 mime_type, image);
499         }
500 
501       if (image)
502         {
503           success = gimp_imagefile_save_thumb (imagefile,
504                                                image, size, replace,
505                                                error);
506 
507           g_object_unref (image);
508         }
509       else
510         {
511           success = gimp_thumbnail_save_failure (thumbnail,
512                                                  "GIMP " GIMP_VERSION,
513                                                  error);
514           gimp_imagefile_update (imagefile);
515         }
516 
517       g_object_unref (imagefile);
518 
519       if (! success)
520         {
521           g_object_set (thumbnail,
522                         "thumb-state", GIMP_THUMB_STATE_FAILED,
523                         NULL);
524         }
525 
526       return success;
527     }
528 
529   return TRUE;
530 }
531 
532 /*  The weak version doesn't ref the imagefile but deals gracefully
533  *  with an imagefile that is destroyed while the thumbnail is
534  *  created. This allows one to use this function w/o the need to
535  *  block the user interface.
536  */
537 void
gimp_imagefile_create_thumbnail_weak(GimpImagefile * imagefile,GimpContext * context,GimpProgress * progress,gint size,gboolean replace)538 gimp_imagefile_create_thumbnail_weak (GimpImagefile *imagefile,
539                                       GimpContext   *context,
540                                       GimpProgress  *progress,
541                                       gint           size,
542                                       gboolean       replace)
543 {
544   GimpImagefilePrivate *private;
545   GimpImagefile        *local;
546 
547   g_return_if_fail (GIMP_IS_IMAGEFILE (imagefile));
548 
549   if (size < 1)
550     return;
551 
552   private = GET_PRIVATE (imagefile);
553 
554   if (! private->file)
555     return;
556 
557   local = gimp_imagefile_new (private->gimp, private->file);
558 
559   g_object_add_weak_pointer (G_OBJECT (imagefile), (gpointer) &imagefile);
560 
561   if (! gimp_imagefile_create_thumbnail (local, context, progress, size, replace,
562                                          NULL))
563     {
564       /* The weak version works on a local copy so the thumbnail
565        * status of the actual image is not properly updated in case of
566        * creation failure, thus it would end up in a generic
567        * GIMP_THUMB_STATE_NOT_FOUND, which is less informative.
568        */
569       g_object_set (private->thumbnail,
570                     "thumb-state", GIMP_THUMB_STATE_FAILED,
571                     NULL);
572     }
573 
574   if (imagefile)
575     {
576       GFile *file = gimp_imagefile_get_file (imagefile);
577 
578       if (file && g_file_equal (file, gimp_imagefile_get_file (local)))
579         {
580           gimp_imagefile_update (imagefile);
581         }
582 
583       g_object_remove_weak_pointer (G_OBJECT (imagefile),
584                                     (gpointer) &imagefile);
585     }
586 
587   g_object_unref (local);
588 }
589 
590 gboolean
gimp_imagefile_check_thumbnail(GimpImagefile * imagefile)591 gimp_imagefile_check_thumbnail (GimpImagefile *imagefile)
592 {
593   GimpImagefilePrivate *private;
594   gint                  size;
595 
596   g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), FALSE);
597 
598   private = GET_PRIVATE (imagefile);
599 
600   size = private->gimp->config->thumbnail_size;
601 
602   if (size > 0)
603     {
604       GimpThumbState  state;
605 
606       state = gimp_thumbnail_check_thumb (private->thumbnail, size);
607 
608       return (state == GIMP_THUMB_STATE_OK);
609     }
610 
611   return TRUE;
612 }
613 
614 gboolean
gimp_imagefile_save_thumbnail(GimpImagefile * imagefile,const gchar * mime_type,GimpImage * image,GError ** error)615 gimp_imagefile_save_thumbnail (GimpImagefile  *imagefile,
616                                const gchar    *mime_type,
617                                GimpImage      *image,
618                                GError        **error)
619 {
620   GimpImagefilePrivate *private;
621   gint                  size;
622   gboolean              success = TRUE;
623 
624   g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), FALSE);
625   g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
626   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
627 
628   private = GET_PRIVATE (imagefile);
629 
630   size = private->gimp->config->thumbnail_size;
631 
632   if (size > 0)
633     {
634       gimp_thumbnail_set_info_from_image (private->thumbnail,
635                                           mime_type, image);
636 
637       success = gimp_imagefile_save_thumb (imagefile,
638                                            image, size, FALSE,
639                                            error);
640     }
641 
642   return success;
643 }
644 
645 
646 /*  private functions  */
647 
648 static void
gimp_imagefile_info_changed(GimpImagefile * imagefile)649 gimp_imagefile_info_changed (GimpImagefile *imagefile)
650 {
651   GimpImagefilePrivate *private = GET_PRIVATE (imagefile);
652 
653   if (private->description)
654     {
655       if (! private->static_desc)
656         g_free (private->description);
657 
658       private->description = NULL;
659     }
660 
661   g_clear_object (&private->icon);
662 
663   g_signal_emit (imagefile, gimp_imagefile_signals[INFO_CHANGED], 0);
664 }
665 
666 static void
gimp_imagefile_notify_thumbnail(GimpImagefile * imagefile,GParamSpec * pspec)667 gimp_imagefile_notify_thumbnail (GimpImagefile *imagefile,
668                                  GParamSpec    *pspec)
669 {
670   if (strcmp (pspec->name, "image-state") == 0 ||
671       strcmp (pspec->name, "thumb-state") == 0)
672     {
673       gimp_imagefile_info_changed (imagefile);
674     }
675 }
676 
677 static void
gimp_imagefile_icon_callback(GObject * source_object,GAsyncResult * result,gpointer data)678 gimp_imagefile_icon_callback (GObject      *source_object,
679                               GAsyncResult *result,
680                               gpointer      data)
681 {
682   GimpImagefile        *imagefile;
683   GimpImagefilePrivate *private;
684   GFile                *file  = G_FILE (source_object);
685   GError               *error = NULL;
686   GFileInfo            *file_info;
687 
688   file_info = g_file_query_info_finish (file, result, &error);
689 
690   if (error)
691     {
692       /* we were cancelled from dispose() and the imagefile is
693        * long gone, bail out
694        */
695       if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
696         {
697           g_clear_error (&error);
698           return;
699         }
700 
701 #ifdef GIMP_UNSTABLE
702       g_printerr ("%s: %s\n", G_STRFUNC, error->message);
703 #endif
704 
705       g_clear_error (&error);
706     }
707 
708   imagefile = GIMP_IMAGEFILE (data);
709   private   = GET_PRIVATE (imagefile);
710 
711   if (file_info)
712     {
713       private->icon = g_object_ref (g_file_info_get_icon (file_info));
714       g_object_unref (file_info);
715     }
716 
717   g_clear_object (&private->icon_cancellable);
718 
719   if (private->icon)
720     gimp_viewable_invalidate_preview (GIMP_VIEWABLE (imagefile));
721 }
722 
723 const gchar *
gimp_imagefile_get_desc_string(GimpImagefile * imagefile)724 gimp_imagefile_get_desc_string (GimpImagefile *imagefile)
725 {
726   GimpImagefilePrivate *private;
727   GimpThumbnail        *thumbnail;
728 
729   g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), NULL);
730 
731   private = GET_PRIVATE (imagefile);
732 
733   if (private->description)
734     return (const gchar *) private->description;
735 
736   thumbnail = private->thumbnail;
737 
738   switch (thumbnail->image_state)
739     {
740     case GIMP_THUMB_STATE_UNKNOWN:
741       private->description = NULL;
742       private->static_desc = TRUE;
743       break;
744 
745     case GIMP_THUMB_STATE_FOLDER:
746       private->description = (gchar *) _("Folder");
747       private->static_desc = TRUE;
748       break;
749 
750     case GIMP_THUMB_STATE_SPECIAL:
751       private->description = (gchar *) _("Special File");
752       private->static_desc = TRUE;
753       break;
754 
755     case GIMP_THUMB_STATE_NOT_FOUND:
756       private->description =
757         (gchar *) g_strerror (thumbnail->image_not_found_errno);
758       private->static_desc = TRUE;
759       break;
760 
761     default:
762       {
763         GString *str = g_string_new (NULL);
764 
765         if (thumbnail->image_state == GIMP_THUMB_STATE_REMOTE)
766           {
767             g_string_append (str, _("Remote File"));
768           }
769 
770         if (thumbnail->image_filesize > 0)
771           {
772             gchar *size = g_format_size (thumbnail->image_filesize);
773 
774             if (str->len > 0)
775               g_string_append_c (str, '\n');
776 
777             g_string_append (str, size);
778             g_free (size);
779           }
780 
781         switch (thumbnail->thumb_state)
782           {
783           case GIMP_THUMB_STATE_NOT_FOUND:
784             if (str->len > 0)
785               g_string_append_c (str, '\n');
786             g_string_append (str, _("Click to create preview"));
787             break;
788 
789           case GIMP_THUMB_STATE_EXISTS:
790             if (str->len > 0)
791               g_string_append_c (str, '\n');
792             g_string_append (str, _("Loading preview..."));
793             break;
794 
795           case GIMP_THUMB_STATE_OLD:
796             if (str->len > 0)
797               g_string_append_c (str, '\n');
798             g_string_append (str, _("Preview is out of date"));
799             break;
800 
801           case GIMP_THUMB_STATE_FAILED:
802             if (str->len > 0)
803               g_string_append_c (str, '\n');
804             g_string_append (str, _("Cannot create preview"));
805             break;
806 
807           case GIMP_THUMB_STATE_OK:
808             {
809               if (thumbnail->image_state == GIMP_THUMB_STATE_REMOTE)
810                 {
811                   if (str->len > 0)
812                     g_string_append_c (str, '\n');
813 
814                   g_string_append (str, _("(Preview may be out of date)"));
815                 }
816 
817               if (thumbnail->image_width > 0 && thumbnail->image_height > 0)
818                 {
819                   if (str->len > 0)
820                     g_string_append_c (str, '\n');
821 
822                   g_string_append_printf (str,
823                                           ngettext ("%d × %d pixel",
824                                                     "%d × %d pixels",
825                                                     thumbnail->image_height),
826                                           thumbnail->image_width,
827                                           thumbnail->image_height);
828                 }
829 
830               if (thumbnail->image_type)
831                 {
832                   if (str->len > 0)
833                     g_string_append_c (str, '\n');
834 
835                   g_string_append (str, gettext (thumbnail->image_type));
836                 }
837 
838               if (thumbnail->image_num_layers > 0)
839                 {
840                   if (thumbnail->image_type)
841                     g_string_append_len (str, ", ", 2);
842                   else if (str->len > 0)
843                     g_string_append_c (str, '\n');
844 
845                   g_string_append_printf (str,
846                                           ngettext ("%d layer",
847                                                     "%d layers",
848                                                     thumbnail->image_num_layers),
849                                           thumbnail->image_num_layers);
850                 }
851             }
852             break;
853 
854           default:
855             break;
856           }
857 
858         private->description = g_string_free (str, FALSE);
859         private->static_desc = FALSE;
860       }
861     }
862 
863   return (const gchar *) private->description;
864 }
865 
866 static GdkPixbuf *
gimp_imagefile_load_thumb(GimpImagefile * imagefile,gint width,gint height)867 gimp_imagefile_load_thumb (GimpImagefile *imagefile,
868                            gint           width,
869                            gint           height)
870 {
871   GimpImagefilePrivate *private   = GET_PRIVATE (imagefile);
872   GimpThumbnail        *thumbnail = private->thumbnail;
873   GdkPixbuf            *pixbuf    = NULL;
874   GError               *error     = NULL;
875   gint                  size      = MAX (width, height);
876   gint                  pixbuf_width;
877   gint                  pixbuf_height;
878   gint                  preview_width;
879   gint                  preview_height;
880 
881   if (gimp_thumbnail_peek_thumb (thumbnail, size) < GIMP_THUMB_STATE_EXISTS)
882     return NULL;
883 
884   if (thumbnail->image_state == GIMP_THUMB_STATE_NOT_FOUND)
885     return NULL;
886 
887   pixbuf = gimp_thumbnail_load_thumb (thumbnail, size, &error);
888 
889   if (! pixbuf)
890     {
891       if (error)
892         {
893           gimp_message (private->gimp, NULL, GIMP_MESSAGE_ERROR,
894                         _("Could not open thumbnail '%s': %s"),
895                         thumbnail->thumb_filename, error->message);
896           g_clear_error (&error);
897         }
898 
899       return NULL;
900     }
901 
902   pixbuf_width  = gdk_pixbuf_get_width  (pixbuf);
903   pixbuf_height = gdk_pixbuf_get_height (pixbuf);
904 
905   gimp_viewable_calc_preview_size (pixbuf_width,
906                                    pixbuf_height,
907                                    width,
908                                    height,
909                                    TRUE, 1.0, 1.0,
910                                    &preview_width,
911                                    &preview_height,
912                                    NULL);
913 
914   if (preview_width < pixbuf_width || preview_height < pixbuf_height)
915     {
916       GdkPixbuf *scaled = gdk_pixbuf_scale_simple (pixbuf,
917                                                    preview_width,
918                                                    preview_height,
919                                                    GDK_INTERP_BILINEAR);
920       g_object_unref (pixbuf);
921       pixbuf = scaled;
922 
923       pixbuf_width  = preview_width;
924       pixbuf_height = preview_height;
925     }
926 
927   if (gdk_pixbuf_get_n_channels (pixbuf) != 3)
928     {
929       GdkPixbuf *tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
930                                        pixbuf_width, pixbuf_height);
931 
932       gdk_pixbuf_composite_color (pixbuf, tmp,
933                                   0, 0, pixbuf_width, pixbuf_height,
934                                   0.0, 0.0, 1.0, 1.0,
935                                   GDK_INTERP_NEAREST, 255,
936                                   0, 0, GIMP_CHECK_SIZE_SM,
937                                   0x66666666, 0x99999999);
938 
939       g_object_unref (pixbuf);
940       pixbuf = tmp;
941     }
942 
943   return pixbuf;
944 }
945 
946 static gboolean
gimp_imagefile_save_thumb(GimpImagefile * imagefile,GimpImage * image,gint size,gboolean replace,GError ** error)947 gimp_imagefile_save_thumb (GimpImagefile  *imagefile,
948                            GimpImage      *image,
949                            gint            size,
950                            gboolean        replace,
951                            GError        **error)
952 {
953   GimpImagefilePrivate *private   = GET_PRIVATE (imagefile);
954   GimpThumbnail        *thumbnail = private->thumbnail;
955   GdkPixbuf            *pixbuf;
956   gint                  width, height;
957   gboolean              success = FALSE;
958 
959   if (size < 1)
960     return TRUE;
961 
962   if (gimp_image_get_width  (image) <= size &&
963       gimp_image_get_height (image) <= size)
964     {
965       width  = gimp_image_get_width  (image);
966       height = gimp_image_get_height (image);
967 
968       size = MAX (width, height);
969     }
970   else
971     {
972       if (gimp_image_get_width (image) < gimp_image_get_height (image))
973         {
974           height = size;
975           width  = MAX (1, (size * gimp_image_get_width (image) /
976                             gimp_image_get_height (image)));
977         }
978       else
979         {
980           width  = size;
981           height = MAX (1, (size * gimp_image_get_height (image) /
982                             gimp_image_get_width (image)));
983         }
984     }
985 
986   /*  we need the projection constructed NOW, not some time later  */
987   gimp_pickable_flush (GIMP_PICKABLE (image));
988 
989   pixbuf = gimp_viewable_get_new_pixbuf (GIMP_VIEWABLE (image),
990                                          /* random context, unused */
991                                          gimp_get_user_context (image->gimp),
992                                          width, height);
993 
994   /*  when layer previews are disabled, we won't get a pixbuf  */
995   if (! pixbuf)
996     return TRUE;
997 
998   success = gimp_thumbnail_save_thumb (thumbnail,
999                                        pixbuf,
1000                                        "GIMP " GIMP_VERSION,
1001                                        error);
1002 
1003   g_object_unref (pixbuf);
1004 
1005   if (success)
1006     {
1007       if (replace)
1008         gimp_thumbnail_delete_others (thumbnail, size);
1009       else
1010         gimp_thumbnail_delete_failure (thumbnail);
1011 
1012       gimp_imagefile_update (imagefile);
1013     }
1014 
1015   return success;
1016 }
1017 
1018 static void
gimp_thumbnail_set_info_from_image(GimpThumbnail * thumbnail,const gchar * mime_type,GimpImage * image)1019 gimp_thumbnail_set_info_from_image (GimpThumbnail *thumbnail,
1020                                     const gchar   *mime_type,
1021                                     GimpImage     *image)
1022 {
1023   const Babl *format;
1024 
1025   /*  peek the thumbnail to make sure that mtime and filesize are set  */
1026   gimp_thumbnail_peek_image (thumbnail);
1027 
1028   format = gimp_image_get_layer_format (image,
1029                                         gimp_image_has_alpha (image));
1030 
1031   g_object_set (thumbnail,
1032                 "image-mimetype",   mime_type,
1033                 "image-width",      gimp_image_get_width  (image),
1034                 "image-height",     gimp_image_get_height (image),
1035                 "image-type",       gimp_babl_format_get_description (format),
1036                 "image-num-layers", gimp_image_get_n_layers (image),
1037                 NULL);
1038 }
1039 
1040 /**
1041  * gimp_thumbnail_set_info:
1042  * @thumbnail: #GimpThumbnail object
1043  * @mime_type:  MIME type of the image associated with this thumbnail
1044  * @width:      width of the image associated with this thumbnail
1045  * @height:     height of the image associated with this thumbnail
1046  * @format:     format of the image (or NULL if the type is not known)
1047  * @num_layers: number of layers in the image
1048  *              (or -1 if the number of layers is not known)
1049  *
1050  * Set information about the image associated with the @thumbnail object.
1051  */
1052 static void
gimp_thumbnail_set_info(GimpThumbnail * thumbnail,const gchar * mime_type,gint width,gint height,const Babl * format,gint num_layers)1053 gimp_thumbnail_set_info (GimpThumbnail *thumbnail,
1054                          const gchar   *mime_type,
1055                          gint           width,
1056                          gint           height,
1057                          const Babl    *format,
1058                          gint           num_layers)
1059 {
1060   /*  peek the thumbnail to make sure that mtime and filesize are set  */
1061   gimp_thumbnail_peek_image (thumbnail);
1062 
1063   g_object_set (thumbnail,
1064                 "image-mimetype", mime_type,
1065                 "image-width",    width,
1066                 "image-height",   height,
1067                 NULL);
1068 
1069   if (format)
1070     g_object_set (thumbnail,
1071                   "image-type", gimp_babl_format_get_description (format),
1072                   NULL);
1073 
1074   if (num_layers != -1)
1075     g_object_set (thumbnail,
1076                   "image-num-layers", num_layers,
1077                   NULL);
1078 }
1079