mirror of https://github.com/GNOME/gimp.git
1475 lines
44 KiB
C
1475 lines
44 KiB
C
/* LIBGIMP - The GIMP Library
|
|
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
|
|
*
|
|
* Thumbnail handling according to the Thumbnail Managing Standard.
|
|
* https://specifications.freedesktop.org/thumbnail-spec/
|
|
*
|
|
* Copyright (C) 2001-2004 Sven Neumann <sven@gimp.org>
|
|
* Michael Natterer <mitch@gimp.org>
|
|
*
|
|
* This library is free software: you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see
|
|
* <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <glib/gstdio.h>
|
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
|
|
#include "libgimpbase/gimpbase.h"
|
|
|
|
#ifdef G_OS_WIN32
|
|
#include "libgimpbase/gimpwin32-io.h"
|
|
#include <process.h>
|
|
#define _getpid getpid
|
|
#endif
|
|
|
|
#include "gimpthumb-types.h"
|
|
#include "gimpthumb-error.h"
|
|
#include "gimpthumb-utils.h"
|
|
#include "gimpthumbnail.h"
|
|
|
|
#include "libgimp/libgimp-intl.h"
|
|
|
|
|
|
/**
|
|
* SECTION: gimpthumbnail
|
|
* @title: GimpThumbnail
|
|
* @short_description: The GimpThumbnail object
|
|
*
|
|
* The GimpThumbnail object
|
|
**/
|
|
|
|
|
|
/* #define GIMP_THUMB_DEBUG */
|
|
|
|
|
|
#if defined (GIMP_THUMB_DEBUG) && defined (__GNUC__)
|
|
#define GIMP_THUMB_DEBUG_CALL(t) \
|
|
g_printerr ("%s: %s\n", \
|
|
__FUNCTION__, GET_PRIVATE (t)->image_uri ? GET_PRIVATE (t)->image_uri : "(null)")
|
|
#else
|
|
#define GIMP_THUMB_DEBUG_CALL(t) ((void)(0))
|
|
#endif
|
|
|
|
|
|
#define TAG_DESCRIPTION "tEXt::Description"
|
|
#define TAG_SOFTWARE "tEXt::Software"
|
|
#define TAG_THUMB_URI "tEXt::Thumb::URI"
|
|
#define TAG_THUMB_MTIME "tEXt::Thumb::MTime"
|
|
#define TAG_THUMB_FILESIZE "tEXt::Thumb::Size"
|
|
#define TAG_THUMB_MIMETYPE "tEXt::Thumb::Mimetype"
|
|
#define TAG_THUMB_IMAGE_WIDTH "tEXt::Thumb::Image::Width"
|
|
#define TAG_THUMB_IMAGE_HEIGHT "tEXt::Thumb::Image::Height"
|
|
#define TAG_THUMB_GIMP_TYPE "tEXt::Thumb::X-GIMP::Type"
|
|
#define TAG_THUMB_GIMP_LAYERS "tEXt::Thumb::X-GIMP::Layers"
|
|
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_IMAGE_STATE,
|
|
PROP_IMAGE_URI,
|
|
PROP_IMAGE_MTIME,
|
|
PROP_IMAGE_FILESIZE,
|
|
PROP_IMAGE_MIMETYPE,
|
|
PROP_IMAGE_WIDTH,
|
|
PROP_IMAGE_HEIGHT,
|
|
PROP_IMAGE_TYPE,
|
|
PROP_IMAGE_NUM_LAYERS,
|
|
PROP_IMAGE_NOT_FOUND_ERRNO,
|
|
PROP_THUMB_STATE
|
|
};
|
|
|
|
|
|
typedef struct _GimpThumbnailPrivate
|
|
{
|
|
GObject parent_instance;
|
|
|
|
GimpThumbState image_state;
|
|
gchar *image_uri;
|
|
gchar *image_filename;
|
|
gint64 image_filesize;
|
|
gint64 image_mtime;
|
|
gint image_not_found_errno;
|
|
gint image_width;
|
|
gint image_height;
|
|
gchar *image_type;
|
|
gint image_num_layers;
|
|
|
|
GimpThumbState thumb_state;
|
|
GimpThumbSize thumb_size;
|
|
gchar *thumb_filename;
|
|
gint64 thumb_filesize;
|
|
gint64 thumb_mtime;
|
|
|
|
gchar *image_mimetype;
|
|
} GimpThumbnailPrivate;
|
|
|
|
#define GET_PRIVATE(obj) ((GimpThumbnailPrivate *) gimp_thumbnail_get_instance_private ((GimpThumbnail *) (obj)))
|
|
|
|
static void gimp_thumbnail_finalize (GObject *object);
|
|
static void gimp_thumbnail_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
static void gimp_thumbnail_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
static void gimp_thumbnail_reset_info (GimpThumbnail *thumbnail);
|
|
|
|
static void gimp_thumbnail_update_image (GimpThumbnail *thumbnail);
|
|
static void gimp_thumbnail_update_thumb (GimpThumbnail *thumbnail,
|
|
GimpThumbSize size);
|
|
|
|
static gboolean gimp_thumbnail_save (GimpThumbnail *thumbnail,
|
|
GimpThumbSize size,
|
|
const gchar *filename,
|
|
GdkPixbuf *pixbuf,
|
|
const gchar *software,
|
|
GError **error);
|
|
#ifdef GIMP_THUMB_DEBUG
|
|
static void gimp_thumbnail_debug_notify (GObject *object,
|
|
GParamSpec *pspec);
|
|
#endif
|
|
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (GimpThumbnail, gimp_thumbnail, G_TYPE_OBJECT)
|
|
|
|
#define parent_class gimp_thumbnail_parent_class
|
|
|
|
|
|
static void
|
|
gimp_thumbnail_class_init (GimpThumbnailClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->finalize = gimp_thumbnail_finalize;
|
|
object_class->set_property = gimp_thumbnail_set_property;
|
|
object_class->get_property = gimp_thumbnail_get_property;
|
|
|
|
g_object_class_install_property (object_class,
|
|
PROP_IMAGE_STATE,
|
|
g_param_spec_enum ("image-state", NULL,
|
|
"State of the image associated to the thumbnail object",
|
|
GIMP_TYPE_THUMB_STATE,
|
|
GIMP_THUMB_STATE_UNKNOWN,
|
|
GIMP_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_IMAGE_URI,
|
|
g_param_spec_string ("image-uri", NULL,
|
|
"URI of the image file",
|
|
NULL,
|
|
GIMP_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_IMAGE_MTIME,
|
|
g_param_spec_int64 ("image-mtime", NULL,
|
|
"Modification time of the image file in seconds since the Epoch",
|
|
G_MININT64, G_MAXINT64, 0,
|
|
GIMP_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_IMAGE_FILESIZE,
|
|
g_param_spec_int64 ("image-filesize", NULL,
|
|
"Size of the image file in bytes",
|
|
0, G_MAXINT64, 0,
|
|
GIMP_PARAM_READWRITE));
|
|
/**
|
|
* GimpThumbnail::image-mimetype:
|
|
*
|
|
* Image mimetype
|
|
*
|
|
* Since: 2.2
|
|
**/
|
|
g_object_class_install_property (object_class,
|
|
PROP_IMAGE_MIMETYPE,
|
|
g_param_spec_string ("image-mimetype", NULL,
|
|
"Image mimetype",
|
|
NULL,
|
|
GIMP_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_IMAGE_WIDTH,
|
|
g_param_spec_int ("image-width", NULL,
|
|
"Width of the image in pixels",
|
|
0, G_MAXINT, 0,
|
|
GIMP_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_IMAGE_HEIGHT,
|
|
g_param_spec_int ("image-height", NULL,
|
|
"Height of the image in pixels",
|
|
0, G_MAXINT, 0,
|
|
GIMP_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_IMAGE_TYPE,
|
|
g_param_spec_string ("image-type", NULL,
|
|
"String describing the type of the image format",
|
|
NULL,
|
|
GIMP_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_IMAGE_NUM_LAYERS,
|
|
g_param_spec_int ("image-num-layers", NULL,
|
|
"The number of layers in the image",
|
|
0, G_MAXINT, 0,
|
|
GIMP_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (object_class,
|
|
PROP_IMAGE_NOT_FOUND_ERRNO,
|
|
g_param_spec_int ("image-not-found-errno", NULL,
|
|
"Error number if image not found",
|
|
0, G_MAXINT, 0,
|
|
GIMP_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (object_class,
|
|
PROP_THUMB_STATE,
|
|
g_param_spec_enum ("thumb-state", NULL,
|
|
"State of the thumbnail file",
|
|
GIMP_TYPE_THUMB_STATE,
|
|
GIMP_THUMB_STATE_UNKNOWN,
|
|
GIMP_PARAM_READWRITE));
|
|
}
|
|
|
|
static void
|
|
gimp_thumbnail_init (GimpThumbnail *thumbnail)
|
|
{
|
|
GimpThumbnailPrivate *priv = GET_PRIVATE (thumbnail);
|
|
|
|
priv->image_state = GIMP_THUMB_STATE_UNKNOWN;
|
|
priv->image_uri = NULL;
|
|
priv->image_filename = NULL;
|
|
priv->image_mtime = 0;
|
|
priv->image_filesize = 0;
|
|
priv->image_mimetype = NULL;
|
|
priv->image_width = 0;
|
|
priv->image_height = 0;
|
|
priv->image_type = NULL;
|
|
priv->image_num_layers = 0;
|
|
priv->image_not_found_errno = 0;
|
|
|
|
priv->thumb_state = GIMP_THUMB_STATE_UNKNOWN;
|
|
priv->thumb_size = -1;
|
|
priv->thumb_filename = NULL;
|
|
priv->thumb_mtime = 0;
|
|
priv->thumb_filesize = 0;
|
|
|
|
#ifdef GIMP_THUMB_DEBUG
|
|
g_signal_connect (thumbnail, "notify",
|
|
G_CALLBACK (gimp_thumbnail_debug_notify),
|
|
NULL);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
gimp_thumbnail_finalize (GObject *object)
|
|
{
|
|
GimpThumbnailPrivate *priv = GET_PRIVATE (object);
|
|
|
|
g_clear_pointer (&priv->image_uri, g_free);
|
|
g_clear_pointer (&priv->image_filename, g_free);
|
|
g_clear_pointer (&priv->image_mimetype, g_free);
|
|
g_clear_pointer (&priv->image_type, g_free);
|
|
g_clear_pointer (&priv->thumb_filename, g_free);
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
gimp_thumbnail_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GimpThumbnailPrivate *priv = GET_PRIVATE (object);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_IMAGE_STATE:
|
|
priv->image_state = g_value_get_enum (value);
|
|
break;
|
|
case PROP_IMAGE_URI:
|
|
gimp_thumbnail_set_uri (GIMP_THUMBNAIL (object),
|
|
g_value_get_string (value));
|
|
break;
|
|
case PROP_IMAGE_MTIME:
|
|
priv->image_mtime = g_value_get_int64 (value);
|
|
break;
|
|
case PROP_IMAGE_FILESIZE:
|
|
priv->image_filesize = g_value_get_int64 (value);
|
|
break;
|
|
case PROP_IMAGE_MIMETYPE:
|
|
g_free (priv->image_mimetype);
|
|
priv->image_mimetype = g_value_dup_string (value);
|
|
break;
|
|
case PROP_IMAGE_WIDTH:
|
|
priv->image_width = g_value_get_int (value);
|
|
break;
|
|
case PROP_IMAGE_HEIGHT:
|
|
priv->image_height = g_value_get_int (value);
|
|
break;
|
|
case PROP_IMAGE_TYPE:
|
|
g_free (priv->image_type);
|
|
priv->image_type = g_value_dup_string (value);
|
|
break;
|
|
case PROP_IMAGE_NUM_LAYERS:
|
|
priv->image_num_layers = g_value_get_int (value);
|
|
break;
|
|
case PROP_IMAGE_NOT_FOUND_ERRNO:
|
|
priv->image_not_found_errno = g_value_get_int (value);
|
|
break;
|
|
case PROP_THUMB_STATE:
|
|
priv->thumb_state = g_value_get_enum (value);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_thumbnail_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GimpThumbnailPrivate *priv = GET_PRIVATE (object);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_IMAGE_STATE:
|
|
g_value_set_enum (value, priv->image_state);
|
|
break;
|
|
case PROP_IMAGE_URI:
|
|
g_value_set_string (value, priv->image_uri);
|
|
break;
|
|
case PROP_IMAGE_MTIME:
|
|
g_value_set_int64 (value, priv->image_mtime);
|
|
break;
|
|
case PROP_IMAGE_FILESIZE:
|
|
g_value_set_int64 (value, priv->image_filesize);
|
|
break;
|
|
case PROP_IMAGE_MIMETYPE:
|
|
g_value_set_string (value, priv->image_mimetype);
|
|
break;
|
|
case PROP_IMAGE_WIDTH:
|
|
g_value_set_int (value, priv->image_width);
|
|
break;
|
|
case PROP_IMAGE_HEIGHT:
|
|
g_value_set_int (value, priv->image_height);
|
|
break;
|
|
case PROP_IMAGE_TYPE:
|
|
g_value_set_string (value, priv->image_type);
|
|
break;
|
|
case PROP_IMAGE_NUM_LAYERS:
|
|
g_value_set_int (value, priv->image_num_layers);
|
|
break;
|
|
case PROP_IMAGE_NOT_FOUND_ERRNO:
|
|
g_value_set_int (value, priv->image_not_found_errno);
|
|
break;
|
|
|
|
case PROP_THUMB_STATE:
|
|
g_value_set_enum (value, priv->thumb_state);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gimp_thumbnail_new:
|
|
*
|
|
* Creates a new #GimpThumbnail object.
|
|
*
|
|
* Returns: a newly allocated GimpThumbnail object
|
|
**/
|
|
GimpThumbnail *
|
|
gimp_thumbnail_new (void)
|
|
{
|
|
return g_object_new (GIMP_TYPE_THUMBNAIL, NULL);
|
|
}
|
|
|
|
/**
|
|
* gimp_thumbnail_set_uri:
|
|
* @thumbnail: a #GimpThumbnail object
|
|
* @uri: an escaped URI (transfer none)
|
|
*
|
|
* Sets the location of the image file associated with the #thumbnail.
|
|
*
|
|
* Stores duplicate of passed @uri.
|
|
*
|
|
* All information stored in the #GimpThumbnail is reset.
|
|
**/
|
|
void
|
|
gimp_thumbnail_set_uri (GimpThumbnail *thumbnail,
|
|
const gchar *uri)
|
|
{
|
|
GimpThumbnailPrivate *priv;
|
|
|
|
g_return_if_fail (GIMP_IS_THUMBNAIL (thumbnail));
|
|
|
|
GIMP_THUMB_DEBUG_CALL (thumbnail);
|
|
|
|
priv = GET_PRIVATE (thumbnail);
|
|
|
|
if (priv->image_uri)
|
|
g_free (priv->image_uri);
|
|
|
|
priv->image_uri = g_strdup (uri);
|
|
|
|
g_clear_pointer (&priv->image_filename, g_free);
|
|
g_clear_pointer (&priv->thumb_filename, g_free);
|
|
|
|
priv->thumb_size = -1;
|
|
priv->thumb_filesize = 0;
|
|
priv->thumb_mtime = 0;
|
|
|
|
g_object_set (thumbnail,
|
|
"image-state", GIMP_THUMB_STATE_UNKNOWN,
|
|
"image-filesize", (gint64) 0,
|
|
"image-mtime", (gint64) 0,
|
|
"image-mimetype", NULL,
|
|
"image-width", 0,
|
|
"image-height", 0,
|
|
"image-type", NULL,
|
|
"image-num-layers", 0,
|
|
"thumb-state", GIMP_THUMB_STATE_UNKNOWN,
|
|
NULL);
|
|
}
|
|
|
|
/**
|
|
* gimp_thumbnail_set_filename:
|
|
* @thumbnail: a #GimpThumbnail object
|
|
* @filename: a local filename in the encoding of the filesystem
|
|
* @error: return location for possible errors
|
|
*
|
|
* Sets the location of the image file associated with the #thumbnail.
|
|
*
|
|
* Returns: %TRUE if the filename was successfully set,
|
|
* %FALSE otherwise
|
|
**/
|
|
gboolean
|
|
gimp_thumbnail_set_filename (GimpThumbnail *thumbnail,
|
|
const gchar *filename,
|
|
GError **error)
|
|
{
|
|
gchar *uri = NULL;
|
|
|
|
g_return_val_if_fail (GIMP_IS_THUMBNAIL (thumbnail), FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
GIMP_THUMB_DEBUG_CALL (thumbnail);
|
|
|
|
if (filename)
|
|
uri = g_filename_to_uri (filename, NULL, error);
|
|
|
|
gimp_thumbnail_set_uri (thumbnail, uri);
|
|
|
|
g_free (uri);
|
|
|
|
return (!filename || uri);
|
|
}
|
|
|
|
/**
|
|
* gimp_thumbnail_set_from_thumb:
|
|
* @thumbnail: a #GimpThumbnail object
|
|
* @filename: filename of a local thumbnail file
|
|
* @error: return location for possible errors
|
|
*
|
|
* This function tries to load the thumbnail file pointed to by
|
|
* @filename and retrieves the URI of the original image file from
|
|
* it. This allows you to find the image file associated with a
|
|
* thumbnail file.
|
|
*
|
|
* This will only work with thumbnails from the global thumbnail
|
|
* directory that contain a valid Thumb::URI tag.
|
|
*
|
|
* Returns: %TRUE if the pixbuf could be loaded, %FALSE otherwise
|
|
**/
|
|
gboolean
|
|
gimp_thumbnail_set_from_thumb (GimpThumbnail *thumbnail,
|
|
const gchar *filename,
|
|
GError **error)
|
|
{
|
|
GdkPixbuf *pixbuf;
|
|
const gchar *uri;
|
|
|
|
g_return_val_if_fail (GIMP_IS_THUMBNAIL (thumbnail), FALSE);
|
|
g_return_val_if_fail (filename != NULL, FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
GIMP_THUMB_DEBUG_CALL (thumbnail);
|
|
|
|
pixbuf = gdk_pixbuf_new_from_file (filename, error);
|
|
if (! pixbuf)
|
|
return FALSE;
|
|
|
|
uri = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_URI);
|
|
if (! uri)
|
|
{
|
|
g_set_error (error, GIMP_THUMB_ERROR, 0,
|
|
_("Thumbnail contains no Thumb::URI tag"));
|
|
g_object_unref (pixbuf);
|
|
return FALSE;
|
|
}
|
|
|
|
gimp_thumbnail_set_uri (thumbnail, uri);
|
|
g_object_unref (pixbuf);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* gimp_thumbnail_peek_image:
|
|
* @thumbnail: a #GimpThumbnail object
|
|
*
|
|
* Checks the image file associated with the @thumbnail and updates
|
|
* information such as state, filesize and modification time.
|
|
*
|
|
* Returns: the image's #GimpThumbState after the update
|
|
**/
|
|
GimpThumbState
|
|
gimp_thumbnail_peek_image (GimpThumbnail *thumbnail)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_THUMBNAIL (thumbnail),
|
|
GIMP_THUMB_STATE_UNKNOWN);
|
|
|
|
GIMP_THUMB_DEBUG_CALL (thumbnail);
|
|
|
|
g_object_freeze_notify (G_OBJECT (thumbnail));
|
|
|
|
gimp_thumbnail_update_image (thumbnail);
|
|
|
|
g_object_thaw_notify (G_OBJECT (thumbnail));
|
|
|
|
return GET_PRIVATE (thumbnail)->image_state;
|
|
}
|
|
|
|
/**
|
|
* gimp_thumbnail_peek_thumb:
|
|
* @thumbnail: a #GimpThumbnail object
|
|
* @size: the preferred size of the thumbnail image
|
|
*
|
|
* Checks if a thumbnail file for the @thumbnail exists. It doesn't
|
|
* load the thumbnail image and thus cannot check if the thumbnail is
|
|
* valid and uptodate for the image file asosciated with the
|
|
* @thumbnail.
|
|
*
|
|
* If you want to check the thumbnail, either attempt to load it using
|
|
* gimp_thumbnail_load_thumb(), or, if you don't need the resulting
|
|
* thumbnail pixbuf, use gimp_thumbnail_check_thumb().
|
|
*
|
|
* Returns: the thumbnail's #GimpThumbState after the update
|
|
**/
|
|
GimpThumbState
|
|
gimp_thumbnail_peek_thumb (GimpThumbnail *thumbnail,
|
|
GimpThumbSize size)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_THUMBNAIL (thumbnail),
|
|
GIMP_THUMB_STATE_UNKNOWN);
|
|
|
|
GIMP_THUMB_DEBUG_CALL (thumbnail);
|
|
|
|
g_object_freeze_notify (G_OBJECT (thumbnail));
|
|
|
|
gimp_thumbnail_update_image (thumbnail);
|
|
gimp_thumbnail_update_thumb (thumbnail, size);
|
|
|
|
g_object_thaw_notify (G_OBJECT (thumbnail));
|
|
|
|
return GET_PRIVATE (thumbnail)->thumb_state;
|
|
}
|
|
|
|
/**
|
|
* gimp_thumbnail_check_thumb:
|
|
* @thumbnail: a #GimpThumbnail object
|
|
* @size: the preferred size of the thumbnail image
|
|
*
|
|
* Checks if a thumbnail file for the @thumbnail exists, loads it and
|
|
* verifies it is valid and uptodate for the image file asosciated
|
|
* with the @thumbnail.
|
|
*
|
|
* Returns: the thumbnail's #GimpThumbState after the update
|
|
*
|
|
* Since: 2.2
|
|
**/
|
|
GimpThumbState
|
|
gimp_thumbnail_check_thumb (GimpThumbnail *thumbnail,
|
|
GimpThumbSize size)
|
|
{
|
|
GdkPixbuf *pixbuf;
|
|
|
|
g_return_val_if_fail (GIMP_IS_THUMBNAIL (thumbnail), FALSE);
|
|
|
|
GIMP_THUMB_DEBUG_CALL (thumbnail);
|
|
|
|
if (gimp_thumbnail_peek_thumb (thumbnail, size) == GIMP_THUMB_STATE_OK)
|
|
return GIMP_THUMB_STATE_OK;
|
|
|
|
pixbuf = gimp_thumbnail_load_thumb (thumbnail, size, NULL);
|
|
|
|
if (pixbuf)
|
|
g_object_unref (pixbuf);
|
|
|
|
return GET_PRIVATE (thumbnail)->thumb_state;
|
|
}
|
|
|
|
static void
|
|
gimp_thumbnail_update_image (GimpThumbnail *thumbnail)
|
|
{
|
|
GimpThumbnailPrivate *priv = GET_PRIVATE (thumbnail);
|
|
GimpThumbState state;
|
|
gint64 mtime = 0;
|
|
gint64 filesize = 0;
|
|
|
|
if (! priv->image_uri)
|
|
return;
|
|
|
|
state = priv->image_state;
|
|
|
|
switch (state)
|
|
{
|
|
case GIMP_THUMB_STATE_UNKNOWN:
|
|
g_return_if_fail (priv->image_filename == NULL);
|
|
|
|
priv->image_filename =
|
|
_gimp_thumb_filename_from_uri (priv->image_uri);
|
|
|
|
if (! priv->image_filename)
|
|
state = GIMP_THUMB_STATE_REMOTE;
|
|
|
|
break;
|
|
|
|
case GIMP_THUMB_STATE_REMOTE:
|
|
break;
|
|
|
|
default:
|
|
g_return_if_fail (priv->image_filename != NULL);
|
|
break;
|
|
}
|
|
|
|
switch (state)
|
|
{
|
|
case GIMP_THUMB_STATE_REMOTE:
|
|
break;
|
|
|
|
default:
|
|
switch (gimp_thumb_file_test (priv->image_filename,
|
|
&mtime, &filesize,
|
|
&priv->image_not_found_errno))
|
|
{
|
|
case GIMP_THUMB_FILE_TYPE_REGULAR:
|
|
state = GIMP_THUMB_STATE_EXISTS;
|
|
break;
|
|
|
|
case GIMP_THUMB_FILE_TYPE_FOLDER:
|
|
state = GIMP_THUMB_STATE_FOLDER;
|
|
break;
|
|
|
|
case GIMP_THUMB_FILE_TYPE_SPECIAL:
|
|
state = GIMP_THUMB_STATE_SPECIAL;
|
|
break;
|
|
|
|
default:
|
|
state = GIMP_THUMB_STATE_NOT_FOUND;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (state != priv->image_state)
|
|
{
|
|
g_object_set (thumbnail,
|
|
"image-state", state,
|
|
NULL);
|
|
}
|
|
|
|
if (mtime != priv->image_mtime || filesize != priv->image_filesize)
|
|
{
|
|
g_object_set (thumbnail,
|
|
"image-mtime", mtime,
|
|
"image-filesize", filesize,
|
|
NULL);
|
|
|
|
if (priv->thumb_state == GIMP_THUMB_STATE_OK)
|
|
g_object_set (thumbnail,
|
|
"thumb-state", GIMP_THUMB_STATE_OLD,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_thumbnail_update_thumb (GimpThumbnail *thumbnail,
|
|
GimpThumbSize size)
|
|
{
|
|
GimpThumbnailPrivate *priv = GET_PRIVATE (thumbnail);
|
|
gchar *filename;
|
|
GimpThumbState state;
|
|
gint64 filesize = 0;
|
|
gint64 mtime = 0;
|
|
|
|
if (! priv->image_uri)
|
|
return;
|
|
|
|
state = priv->thumb_state;
|
|
|
|
filename = gimp_thumb_find_thumb (priv->image_uri, &size);
|
|
|
|
/* We don't want to clear the GIMP_THUMB_STATE_FAILED state, because
|
|
* it is normal to have no filename if thumbnail creation failed. */
|
|
if (state != GIMP_THUMB_STATE_FAILED && ! filename)
|
|
state = GIMP_THUMB_STATE_NOT_FOUND;
|
|
|
|
switch (state)
|
|
{
|
|
case GIMP_THUMB_STATE_EXISTS:
|
|
case GIMP_THUMB_STATE_OLD:
|
|
case GIMP_THUMB_STATE_OK:
|
|
g_return_if_fail (priv->thumb_filename != NULL);
|
|
|
|
if (priv->thumb_size == size &&
|
|
priv->thumb_filesize == filesize &&
|
|
priv->thumb_mtime == mtime)
|
|
{
|
|
g_free (filename);
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (priv->thumb_filename)
|
|
g_free (priv->thumb_filename);
|
|
|
|
priv->thumb_filename = filename;
|
|
|
|
if (filename)
|
|
state = (size > GIMP_THUMB_SIZE_FAIL ?
|
|
GIMP_THUMB_STATE_EXISTS : GIMP_THUMB_STATE_FAILED);
|
|
|
|
priv->thumb_size = size;
|
|
priv->thumb_filesize = filesize;
|
|
priv->thumb_mtime = mtime;
|
|
|
|
if (state != priv->thumb_state)
|
|
{
|
|
g_object_freeze_notify (G_OBJECT (thumbnail));
|
|
|
|
g_object_set (thumbnail, "thumb-state", state, NULL);
|
|
gimp_thumbnail_reset_info (thumbnail);
|
|
|
|
g_object_thaw_notify (G_OBJECT (thumbnail));
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_thumbnail_reset_info (GimpThumbnail *thumbnail)
|
|
{
|
|
g_object_set (thumbnail,
|
|
"image-width", 0,
|
|
"image-height", 0,
|
|
"image-type", NULL,
|
|
"image-num-layers", 0,
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
gimp_thumbnail_set_info_from_pixbuf (GimpThumbnail *thumbnail,
|
|
GdkPixbuf *pixbuf)
|
|
{
|
|
GimpThumbnailPrivate *priv = GET_PRIVATE (thumbnail);
|
|
const gchar *option;
|
|
gint num;
|
|
|
|
g_object_freeze_notify (G_OBJECT (thumbnail));
|
|
|
|
gimp_thumbnail_reset_info (thumbnail);
|
|
|
|
g_free (priv->image_mimetype);
|
|
priv->image_mimetype =
|
|
g_strdup (gdk_pixbuf_get_option (pixbuf, TAG_THUMB_MIMETYPE));
|
|
|
|
option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_IMAGE_WIDTH);
|
|
if (option && sscanf (option, "%d", &num) == 1)
|
|
priv->image_width = num;
|
|
|
|
option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_IMAGE_HEIGHT);
|
|
if (option && sscanf (option, "%d", &num) == 1)
|
|
priv->image_height = num;
|
|
|
|
priv->image_type =
|
|
g_strdup (gdk_pixbuf_get_option (pixbuf, TAG_THUMB_GIMP_TYPE));
|
|
|
|
option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_GIMP_LAYERS);
|
|
if (option && sscanf (option, "%d", &num) == 1)
|
|
priv->image_num_layers = num;
|
|
|
|
g_object_thaw_notify (G_OBJECT (thumbnail));
|
|
}
|
|
|
|
static gboolean
|
|
gimp_thumbnail_save (GimpThumbnail *thumbnail,
|
|
GimpThumbSize size,
|
|
const gchar *filename,
|
|
GdkPixbuf *pixbuf,
|
|
const gchar *software,
|
|
GError **error)
|
|
{
|
|
GimpThumbnailPrivate *priv = GET_PRIVATE (thumbnail);
|
|
const gchar *keys[12];
|
|
gchar *values[12];
|
|
gchar *basename;
|
|
gchar *dirname;
|
|
gchar *tmpname;
|
|
gboolean success;
|
|
gint i = 0;
|
|
|
|
keys[i] = TAG_DESCRIPTION;
|
|
values[i] = g_strdup_printf ("Thumbnail of %s", priv->image_uri);
|
|
i++;
|
|
|
|
keys[i] = TAG_SOFTWARE;
|
|
values[i] = g_strdup (software);
|
|
i++;
|
|
|
|
keys[i] = TAG_THUMB_URI;
|
|
values[i] = g_strdup (priv->image_uri);
|
|
i++;
|
|
|
|
keys[i] = TAG_THUMB_MTIME;
|
|
values[i] = g_strdup_printf ("%" G_GINT64_FORMAT, priv->image_mtime);
|
|
i++;
|
|
|
|
keys[i] = TAG_THUMB_FILESIZE;
|
|
values[i] = g_strdup_printf ("%" G_GINT64_FORMAT, priv->image_filesize);
|
|
i++;
|
|
|
|
if (priv->image_mimetype)
|
|
{
|
|
keys[i] = TAG_THUMB_MIMETYPE;
|
|
values[i] = g_strdup (priv->image_mimetype);
|
|
i++;
|
|
}
|
|
|
|
if (priv->image_width > 0)
|
|
{
|
|
keys[i] = TAG_THUMB_IMAGE_WIDTH;
|
|
values[i] = g_strdup_printf ("%d", priv->image_width);
|
|
i++;
|
|
}
|
|
|
|
if (priv->image_height > 0)
|
|
{
|
|
keys[i] = TAG_THUMB_IMAGE_HEIGHT;
|
|
values[i] = g_strdup_printf ("%d", priv->image_height);
|
|
i++;
|
|
}
|
|
|
|
if (priv->image_type)
|
|
{
|
|
keys[i] = TAG_THUMB_GIMP_TYPE;
|
|
values[i] = g_strdup (priv->image_type);
|
|
i++;
|
|
}
|
|
|
|
if (priv->image_num_layers > 0)
|
|
{
|
|
keys[i] = TAG_THUMB_GIMP_LAYERS;
|
|
values[i] = g_strdup_printf ("%d", priv->image_num_layers);
|
|
i++;
|
|
}
|
|
|
|
keys[i] = NULL;
|
|
values[i] = NULL;
|
|
|
|
basename = g_path_get_basename (filename);
|
|
dirname = g_path_get_dirname (filename);
|
|
|
|
tmpname = g_strdup_printf ("%s%cgimp-thumb-%d-%.8s",
|
|
dirname, G_DIR_SEPARATOR, getpid (), basename);
|
|
|
|
g_free (dirname);
|
|
g_free (basename);
|
|
|
|
success = gdk_pixbuf_savev (pixbuf, tmpname, "png",
|
|
(gchar **) keys, values,
|
|
error);
|
|
|
|
for (i = 0; keys[i]; i++)
|
|
g_free (values[i]);
|
|
|
|
if (success)
|
|
{
|
|
#ifdef GIMP_THUMB_DEBUG
|
|
g_printerr ("thumbnail saved to temporary file %s\n", tmpname);
|
|
#endif
|
|
|
|
success = (g_rename (tmpname, filename) == 0);
|
|
|
|
if (! success)
|
|
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
|
_("Could not create thumbnail for %s: %s"),
|
|
priv->image_uri, g_strerror (errno));
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
#ifdef GIMP_THUMB_DEBUG
|
|
g_printerr ("temporary thumbnail file renamed to %s\n", filename);
|
|
#endif
|
|
|
|
success = (g_chmod (filename, 0600) == 0);
|
|
|
|
if (! success)
|
|
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
|
"Could not set permissions of thumbnail for %s: %s",
|
|
priv->image_uri, g_strerror (errno));
|
|
|
|
g_object_freeze_notify (G_OBJECT (thumbnail));
|
|
|
|
gimp_thumbnail_update_thumb (thumbnail, size);
|
|
|
|
if (success &&
|
|
priv->thumb_state == GIMP_THUMB_STATE_EXISTS &&
|
|
strcmp (filename, priv->thumb_filename) == 0)
|
|
{
|
|
priv->thumb_state = GIMP_THUMB_STATE_OK;
|
|
}
|
|
|
|
g_object_thaw_notify (G_OBJECT (thumbnail));
|
|
}
|
|
|
|
g_unlink (tmpname);
|
|
g_free (tmpname);
|
|
|
|
return success;
|
|
}
|
|
|
|
#ifdef GIMP_THUMB_DEBUG
|
|
static void
|
|
gimp_thumbnail_debug_notify (GObject *object,
|
|
GParamSpec *pspec)
|
|
{
|
|
GValue value = G_VALUE_INIT;
|
|
gchar *str = NULL;
|
|
const gchar *name;
|
|
|
|
g_value_init (&value, pspec->value_type);
|
|
g_object_get_property (object, pspec->name, &value);
|
|
|
|
if (G_VALUE_HOLDS_STRING (&value))
|
|
{
|
|
str = g_value_dup_string (&value);
|
|
}
|
|
else if (g_value_type_transformable (pspec->value_type, G_TYPE_STRING))
|
|
{
|
|
GValue tmp = G_VALUE_INIT;
|
|
|
|
g_value_init (&tmp, G_TYPE_STRING);
|
|
g_value_transform (&value, &tmp);
|
|
|
|
str = g_value_dup_string (&tmp);
|
|
|
|
g_value_unset (&tmp);
|
|
}
|
|
|
|
g_value_unset (&value);
|
|
|
|
name = GIMP_THUMBNAIL (object)->image_uri;
|
|
|
|
g_printerr (" GimpThumb (%s) %s: %s\n",
|
|
name ? name : "(null)", pspec->name, str);
|
|
|
|
g_free (str);
|
|
}
|
|
#endif
|
|
|
|
|
|
/**
|
|
* gimp_thumbnail_load_thumb:
|
|
* @thumbnail: a #GimpThumbnail object
|
|
* @size: the preferred #GimpThumbSize for the preview
|
|
* @error: return location for possible errors
|
|
*
|
|
* Attempts to load a thumbnail preview for the image associated with
|
|
* @thumbnail. Before you use this function you need need to set an
|
|
* image location using gimp_thumbnail_set_uri() or
|
|
* gimp_thumbnail_set_filename(). You can also peek at the thumb
|
|
* before loading it using gimp_thumbnail_peek_thumb.
|
|
*
|
|
* This function will return the best matching pixbuf for the
|
|
* specified @size. It returns the pixbuf as loaded from disk. It is
|
|
* left to the caller to scale it to the desired size. The returned
|
|
* pixbuf may also represent an outdated preview of the image file.
|
|
* In order to verify if the preview is uptodate, you should check the
|
|
* "thumb_state" property after calling this function.
|
|
*
|
|
* Returns: (nullable) (transfer full): a preview pixbuf or %NULL if no
|
|
* thumbnail was found
|
|
**/
|
|
GdkPixbuf *
|
|
gimp_thumbnail_load_thumb (GimpThumbnail *thumbnail,
|
|
GimpThumbSize size,
|
|
GError **error)
|
|
{
|
|
GimpThumbnailPrivate *priv;
|
|
GimpThumbState state;
|
|
GdkPixbuf *pixbuf;
|
|
const gchar *option;
|
|
gint64 image_mtime;
|
|
gint64 image_size;
|
|
|
|
g_return_val_if_fail (GIMP_IS_THUMBNAIL (thumbnail), NULL);
|
|
|
|
GIMP_THUMB_DEBUG_CALL (thumbnail);
|
|
|
|
priv = GET_PRIVATE (thumbnail);
|
|
|
|
if (! priv->image_uri)
|
|
return NULL;
|
|
|
|
state = gimp_thumbnail_peek_thumb (thumbnail, size);
|
|
|
|
if (state < GIMP_THUMB_STATE_EXISTS || state == GIMP_THUMB_STATE_FAILED)
|
|
return NULL;
|
|
|
|
pixbuf = gdk_pixbuf_new_from_file (priv->thumb_filename, NULL);
|
|
if (! pixbuf)
|
|
return NULL;
|
|
|
|
#ifdef GIMP_THUMB_DEBUG
|
|
g_printerr ("thumbnail loaded from %s\n", priv->thumb_filename);
|
|
#endif
|
|
|
|
g_object_freeze_notify (G_OBJECT (thumbnail));
|
|
|
|
/* URI and mtime from the thumbnail need to match our file */
|
|
option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_URI);
|
|
if (!option)
|
|
goto finish;
|
|
|
|
if (strcmp (option, priv->image_uri))
|
|
{
|
|
/* might be a local thumbnail, try if the local part matches */
|
|
const gchar *baseuri = strrchr (priv->image_uri, '/');
|
|
|
|
if (!baseuri || strcmp (option, baseuri))
|
|
goto finish;
|
|
}
|
|
|
|
state = GIMP_THUMB_STATE_OLD;
|
|
|
|
option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_MTIME);
|
|
if (!option || sscanf (option, "%" G_GINT64_FORMAT, &image_mtime) != 1)
|
|
goto finish;
|
|
|
|
option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_FILESIZE);
|
|
if (option && sscanf (option, "%" G_GINT64_FORMAT, &image_size) != 1)
|
|
goto finish;
|
|
|
|
/* TAG_THUMB_FILESIZE is optional but must match if present */
|
|
if (image_mtime == priv->image_mtime &&
|
|
(option == NULL || image_size == priv->image_filesize))
|
|
{
|
|
if (priv->thumb_size == GIMP_THUMB_SIZE_FAIL)
|
|
state = GIMP_THUMB_STATE_FAILED;
|
|
else
|
|
state = GIMP_THUMB_STATE_OK;
|
|
}
|
|
|
|
if (state == GIMP_THUMB_STATE_FAILED)
|
|
gimp_thumbnail_reset_info (thumbnail);
|
|
else
|
|
gimp_thumbnail_set_info_from_pixbuf (thumbnail, pixbuf);
|
|
|
|
finish:
|
|
if (priv->thumb_size == GIMP_THUMB_SIZE_FAIL ||
|
|
(state != GIMP_THUMB_STATE_OLD && state != GIMP_THUMB_STATE_OK))
|
|
{
|
|
g_object_unref (pixbuf);
|
|
pixbuf = NULL;
|
|
}
|
|
|
|
g_object_set (thumbnail,
|
|
"thumb-state", state,
|
|
NULL);
|
|
|
|
g_object_thaw_notify (G_OBJECT (thumbnail));
|
|
|
|
return pixbuf;
|
|
}
|
|
|
|
/**
|
|
* gimp_thumbnail_save_thumb:
|
|
* @thumbnail: a #GimpThumbnail object
|
|
* @pixbuf: a #GdkPixbuf representing the preview thumbnail
|
|
* @software: a string describing the software saving the thumbnail
|
|
* @error: return location for possible errors
|
|
*
|
|
* Saves a preview thumbnail for the image associated with @thumbnail.
|
|
* to the global thumbnail repository.
|
|
*
|
|
* The caller is responsible for setting the image file location, it's
|
|
* filesize, modification time. One way to set this info is to is to
|
|
* call gimp_thumbnail_set_uri() followed by gimp_thumbnail_peek_image().
|
|
* Since this won't work for remote images, it is left to the user of
|
|
* gimp_thumbnail_save_thumb() to do this or to set the information
|
|
* using the @thumbnail object properties.
|
|
*
|
|
* The image format type and the number of layers can optionally be
|
|
* set in order to be stored with the preview image.
|
|
*
|
|
* Returns: %TRUE if a thumbnail was successfully written,
|
|
* %FALSE otherwise
|
|
**/
|
|
gboolean
|
|
gimp_thumbnail_save_thumb (GimpThumbnail *thumbnail,
|
|
GdkPixbuf *pixbuf,
|
|
const gchar *software,
|
|
GError **error)
|
|
{
|
|
GimpThumbnailPrivate *priv;
|
|
GimpThumbSize size;
|
|
gchar *name;
|
|
gboolean success;
|
|
|
|
g_return_val_if_fail (GIMP_IS_THUMBNAIL (thumbnail), FALSE);
|
|
g_return_val_if_fail (GET_PRIVATE (thumbnail)->image_uri != NULL, FALSE);
|
|
g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), FALSE);
|
|
g_return_val_if_fail (software != NULL, FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
GIMP_THUMB_DEBUG_CALL (thumbnail);
|
|
|
|
priv = GET_PRIVATE (thumbnail);
|
|
|
|
size = MAX (gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf));
|
|
if (size < 1)
|
|
return TRUE;
|
|
|
|
name = gimp_thumb_name_from_uri (priv->image_uri, size);
|
|
if (! name)
|
|
return TRUE;
|
|
|
|
if (! gimp_thumb_ensure_thumb_dir (size, error))
|
|
{
|
|
g_free (name);
|
|
return FALSE;
|
|
}
|
|
|
|
success = gimp_thumbnail_save (thumbnail,
|
|
size, name, pixbuf, software,
|
|
error);
|
|
g_free (name);
|
|
|
|
return success;
|
|
}
|
|
|
|
/**
|
|
* gimp_thumbnail_save_thumb_local:
|
|
* @thumbnail: a #GimpThumbnail object
|
|
* @pixbuf: a #GdkPixbuf representing the preview thumbnail
|
|
* @software: a string describing the software saving the thumbnail
|
|
* @error: return location for possible errors
|
|
*
|
|
* Saves a preview thumbnail for the image associated with @thumbnail
|
|
* to the local thumbnail repository. Local thumbnails have been added
|
|
* with version 0.7 of the spec.
|
|
*
|
|
* Please see also gimp_thumbnail_save_thumb(). The notes made there
|
|
* apply here as well.
|
|
*
|
|
* Returns: %TRUE if a thumbnail was successfully written,
|
|
* %FALSE otherwise
|
|
*
|
|
* Since: 2.2
|
|
**/
|
|
gboolean
|
|
gimp_thumbnail_save_thumb_local (GimpThumbnail *thumbnail,
|
|
GdkPixbuf *pixbuf,
|
|
const gchar *software,
|
|
GError **error)
|
|
{
|
|
GimpThumbnailPrivate *priv;
|
|
GimpThumbSize size;
|
|
gchar *name;
|
|
gchar *filename;
|
|
gchar *dirname;
|
|
gboolean success;
|
|
|
|
g_return_val_if_fail (GIMP_IS_THUMBNAIL (thumbnail), FALSE);
|
|
g_return_val_if_fail (GET_PRIVATE (thumbnail)->image_uri != NULL, FALSE);
|
|
g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), FALSE);
|
|
g_return_val_if_fail (software != NULL, FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
GIMP_THUMB_DEBUG_CALL (thumbnail);
|
|
|
|
priv = GET_PRIVATE (thumbnail);
|
|
|
|
size = MAX (gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf));
|
|
if (size < 1)
|
|
return TRUE;
|
|
|
|
filename = _gimp_thumb_filename_from_uri (priv->image_uri);
|
|
if (! filename)
|
|
return TRUE;
|
|
|
|
dirname = g_path_get_dirname (filename);
|
|
g_free (filename);
|
|
|
|
name = gimp_thumb_name_from_uri_local (priv->image_uri, size);
|
|
if (! name)
|
|
{
|
|
g_free (dirname);
|
|
return TRUE;
|
|
}
|
|
|
|
if (! gimp_thumb_ensure_thumb_dir_local (dirname, size, error))
|
|
{
|
|
g_free (name);
|
|
g_free (dirname);
|
|
return FALSE;
|
|
}
|
|
|
|
g_free (dirname);
|
|
|
|
success = gimp_thumbnail_save (thumbnail,
|
|
size, name, pixbuf, software,
|
|
error);
|
|
g_free (name);
|
|
|
|
return success;
|
|
}
|
|
|
|
/**
|
|
* gimp_thumbnail_save_failure:
|
|
* @thumbnail: a #GimpThumbnail object
|
|
* @software: a string describing the software saving the thumbnail
|
|
* @error: return location for possible errors
|
|
*
|
|
* Saves a failure thumbnail for the image associated with
|
|
* @thumbnail. This is an empty pixbuf that indicates that an attempt
|
|
* to create a preview for the image file failed. It should be used to
|
|
* prevent the software from further attempts to create this thumbnail.
|
|
*
|
|
* Returns: %TRUE if a failure thumbnail was successfully written,
|
|
* %FALSE otherwise
|
|
**/
|
|
gboolean
|
|
gimp_thumbnail_save_failure (GimpThumbnail *thumbnail,
|
|
const gchar *software,
|
|
GError **error)
|
|
{
|
|
GimpThumbnailPrivate *priv;
|
|
GdkPixbuf *pixbuf;
|
|
gchar *name;
|
|
gchar *desc;
|
|
gchar *time_str;
|
|
gchar *size_str;
|
|
gboolean success;
|
|
|
|
g_return_val_if_fail (GIMP_IS_THUMBNAIL (thumbnail), FALSE);
|
|
g_return_val_if_fail (GET_PRIVATE (thumbnail)->image_uri != NULL, FALSE);
|
|
g_return_val_if_fail (software != NULL, FALSE);
|
|
|
|
GIMP_THUMB_DEBUG_CALL (thumbnail);
|
|
|
|
priv = GET_PRIVATE (thumbnail);
|
|
|
|
name = gimp_thumb_name_from_uri (priv->image_uri, GIMP_THUMB_SIZE_FAIL);
|
|
if (! name)
|
|
return TRUE;
|
|
|
|
if (! gimp_thumb_ensure_thumb_dir (GIMP_THUMB_SIZE_FAIL, error))
|
|
{
|
|
g_free (name);
|
|
return FALSE;
|
|
}
|
|
|
|
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 1, 1);
|
|
|
|
desc = g_strdup_printf ("Thumbnail failure for %s", priv->image_uri);
|
|
time_str = g_strdup_printf ("%" G_GINT64_FORMAT, priv->image_mtime);
|
|
size_str = g_strdup_printf ("%" G_GINT64_FORMAT, priv->image_filesize);
|
|
|
|
success = gdk_pixbuf_save (pixbuf, name, "png", error,
|
|
TAG_DESCRIPTION, desc,
|
|
TAG_SOFTWARE, software,
|
|
TAG_THUMB_URI, priv->image_uri,
|
|
TAG_THUMB_MTIME, time_str,
|
|
TAG_THUMB_FILESIZE, size_str,
|
|
NULL);
|
|
if (success)
|
|
{
|
|
success = (g_chmod (name, 0600) == 0);
|
|
|
|
if (success)
|
|
gimp_thumbnail_update_thumb (thumbnail, GIMP_THUMB_SIZE_NORMAL);
|
|
else
|
|
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
|
"Could not set permissions of thumbnail '%s': %s",
|
|
name, g_strerror (errno));
|
|
}
|
|
|
|
g_object_unref (pixbuf);
|
|
|
|
g_free (size_str);
|
|
g_free (time_str);
|
|
g_free (desc);
|
|
g_free (name);
|
|
|
|
return success;
|
|
}
|
|
|
|
/**
|
|
* gimp_thumbnail_delete_failure:
|
|
* @thumbnail: a #GimpThumbnail object
|
|
*
|
|
* Removes a failure thumbnail if one exists. This function should be
|
|
* used after a thumbnail has been successfully created.
|
|
*
|
|
* Since: 2.2
|
|
**/
|
|
void
|
|
gimp_thumbnail_delete_failure (GimpThumbnail *thumbnail)
|
|
{
|
|
GimpThumbnailPrivate *priv;
|
|
gchar *filename;
|
|
|
|
g_return_if_fail (GIMP_IS_THUMBNAIL (thumbnail));
|
|
g_return_if_fail (GET_PRIVATE (thumbnail)->image_uri != NULL);
|
|
|
|
GIMP_THUMB_DEBUG_CALL (thumbnail);
|
|
|
|
priv = GET_PRIVATE (thumbnail);
|
|
|
|
filename = gimp_thumb_name_from_uri (priv->image_uri,
|
|
GIMP_THUMB_SIZE_FAIL);
|
|
if (filename)
|
|
{
|
|
g_unlink (filename);
|
|
g_free (filename);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gimp_thumbnail_delete_others:
|
|
* @thumbnail: a #GimpThumbnail object
|
|
* @size: the thumbnail size which should not be deleted
|
|
*
|
|
* Removes all other thumbnails from the global thumbnail
|
|
* repository. Only the thumbnail for @size is not deleted. This
|
|
* function should be used after a thumbnail has been successfully
|
|
* updated. See the spec for a more detailed description on when to
|
|
* delete thumbnails.
|
|
*
|
|
* Since: 2.2
|
|
**/
|
|
void
|
|
gimp_thumbnail_delete_others (GimpThumbnail *thumbnail,
|
|
GimpThumbSize size)
|
|
{
|
|
g_return_if_fail (GIMP_IS_THUMBNAIL (thumbnail));
|
|
g_return_if_fail (GET_PRIVATE (thumbnail)->image_uri != NULL);
|
|
|
|
GIMP_THUMB_DEBUG_CALL (thumbnail);
|
|
|
|
_gimp_thumbs_delete_others (GET_PRIVATE (thumbnail)->image_uri, size);
|
|
}
|
|
|
|
/**
|
|
* gimp_thumbnail_has_failed:
|
|
* @thumbnail: a #GimpThumbnail object
|
|
*
|
|
* Checks if a valid failure thumbnail for the given thumbnail exists
|
|
* in the global thumbnail repository. This may be the case even if
|
|
* gimp_thumbnail_peek_thumb() doesn't return %GIMP_THUMB_STATE_FAILED
|
|
* since there might be a real thumbnail and a failure thumbnail for
|
|
* the same image file.
|
|
*
|
|
* The application should not attempt to create the thumbnail if a
|
|
* valid failure thumbnail exists.
|
|
*
|
|
* Returns: %TRUE if a failure thumbnail exists or
|
|
*
|
|
* Since: 2.2
|
|
**/
|
|
gboolean
|
|
gimp_thumbnail_has_failed (GimpThumbnail *thumbnail)
|
|
{
|
|
GimpThumbnailPrivate *priv;
|
|
GdkPixbuf *pixbuf;
|
|
const gchar *option;
|
|
gchar *filename;
|
|
gint64 image_mtime;
|
|
gint64 image_size;
|
|
gboolean failed = FALSE;
|
|
|
|
g_return_val_if_fail (GIMP_IS_THUMBNAIL (thumbnail), FALSE);
|
|
g_return_val_if_fail (GET_PRIVATE (thumbnail)->image_uri != NULL, FALSE);
|
|
|
|
GIMP_THUMB_DEBUG_CALL (thumbnail);
|
|
|
|
priv = GET_PRIVATE (thumbnail);
|
|
|
|
filename = gimp_thumb_name_from_uri (priv->image_uri,
|
|
GIMP_THUMB_SIZE_FAIL);
|
|
if (! filename)
|
|
return FALSE;
|
|
|
|
pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
|
|
g_free (filename);
|
|
|
|
if (! pixbuf)
|
|
return FALSE;
|
|
|
|
if (gimp_thumbnail_peek_image (thumbnail) < GIMP_THUMB_STATE_EXISTS)
|
|
goto finish;
|
|
|
|
/* URI and mtime from the thumbnail need to match our file */
|
|
option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_URI);
|
|
if (! option || strcmp (option, priv->image_uri))
|
|
goto finish;
|
|
|
|
option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_MTIME);
|
|
if (!option || sscanf (option, "%" G_GINT64_FORMAT, &image_mtime) != 1)
|
|
goto finish;
|
|
|
|
option = gdk_pixbuf_get_option (pixbuf, TAG_THUMB_FILESIZE);
|
|
if (option && sscanf (option, "%" G_GINT64_FORMAT, &image_size) != 1)
|
|
goto finish;
|
|
|
|
/* TAG_THUMB_FILESIZE is optional but must match if present */
|
|
if (image_mtime == priv->image_mtime &&
|
|
(option == NULL || image_size == priv->image_filesize))
|
|
{
|
|
failed = TRUE;
|
|
}
|
|
|
|
finish:
|
|
g_object_unref (pixbuf);
|
|
|
|
return failed;
|
|
}
|