diff --git a/ChangeLog b/ChangeLog index 28a86c7bc8..f655156134 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2004-10-23 Sven Neumann + + * libgimpthumb/gimpthumb-utils.[ch] + * libgimpthumb/gimpthumbnail.[ch] + * libgimpthumb/gimpthumb.def: added missing API, mainly for deleting + thumbnails. + + * app/core/gimpimagefile.[ch]: when saving a thumbnail, delete a + failure thumbnail if one exists. Unless the thumbnail was created + explicitely, remove all other thumbnails for this image. + + * app/actions/documents-commands.c: changed accordingly. + + * app/file/file-open.c: only save a thumbnail if there isn't a + valid thumbnail already. + + * app/widgets/gimpthumbbox.c: before attempting to create a new + thumbnail, check if there's an uptodate failure thumbnail. + 2004-10-23 Michael Natterer * app/dialogs/Makefile.am diff --git a/app/actions/documents-commands.c b/app/actions/documents-commands.c index 1a05f50bd0..adecdcafba 100644 --- a/app/actions/documents-commands.c +++ b/app/actions/documents-commands.c @@ -180,16 +180,10 @@ documents_recreate_preview_cmd_callback (GtkAction *action, if (imagefile && gimp_container_have (container, GIMP_OBJECT (imagefile))) { - const gchar *uri = gimp_object_get_name (GIMP_OBJECT (imagefile)); - gint size = imagefile->gimp->config->thumbnail_size; - - if (!size) - return; - - if (uri) - gimp_thumbs_delete_for_uri (uri); - - gimp_imagefile_create_thumbnail (imagefile, context, NULL, size); + gimp_imagefile_create_thumbnail (imagefile, + context, NULL, + imagefile->gimp->config->thumbnail_size, + FALSE); } } diff --git a/app/core/gimpimagefile.c b/app/core/gimpimagefile.c index 05a9577748..1c530b6698 100644 --- a/app/core/gimpimagefile.c +++ b/app/core/gimpimagefile.c @@ -72,6 +72,7 @@ static GdkPixbuf * gimp_imagefile_load_thumb (GimpImagefile *imagefile, static gboolean gimp_imagefile_save_thumb (GimpImagefile *imagefile, GimpImage *gimage, gint size, + gboolean replace, GError **error); static gchar * gimp_imagefile_get_description (GimpViewable *viewable, @@ -230,7 +231,8 @@ void gimp_imagefile_create_thumbnail (GimpImagefile *imagefile, GimpContext *context, GimpProgress *progress, - gint size) + gint size, + gboolean replace) { GimpThumbnail *thumbnail; @@ -276,7 +278,7 @@ gimp_imagefile_create_thumbnail (GimpImagefile *imagefile, NULL); success = gimp_imagefile_save_thumb (imagefile, - gimage, size, &error); + gimage, size, replace, &error); g_object_unref (gimage); } @@ -285,11 +287,12 @@ gimp_imagefile_create_thumbnail (GimpImagefile *imagefile, success = gimp_thumbnail_save_failure (thumbnail, "The GIMP " GIMP_VERSION, &error); + gimp_imagefile_update (imagefile); } g_object_unref (imagefile); - if (!success) + if (! success) { g_message (error->message); g_error_free (error); @@ -300,13 +303,14 @@ gimp_imagefile_create_thumbnail (GimpImagefile *imagefile, /* The weak version doesn't ref the imagefile but deals gracefully * with an imagefile that is destroyed while the thumbnail is * created. Thia allows to use this function w/o the need to block - * the user interface (making it insensitive). + * the user interface. */ void gimp_imagefile_create_thumbnail_weak (GimpImagefile *imagefile, GimpContext *context, GimpProgress *progress, - gint size) + gint size, + gboolean replace) { GimpImagefile *local; const gchar *uri; @@ -327,7 +331,7 @@ gimp_imagefile_create_thumbnail_weak (GimpImagefile *imagefile, g_object_add_weak_pointer (G_OBJECT (imagefile), (gpointer) &imagefile); - gimp_imagefile_create_thumbnail (local, context, progress, size); + gimp_imagefile_create_thumbnail (local, context, progress, size, replace); if (imagefile) { @@ -346,28 +350,54 @@ gimp_imagefile_create_thumbnail_weak (GimpImagefile *imagefile, g_object_unref (local); } +gboolean +gimp_imagefile_check_thumbnail (GimpImagefile *imagefile) +{ + gint size; + + g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), FALSE); + + size = imagefile->gimp->config->thumbnail_size; + + if (size > 0) + { + GimpThumbState state; + + state = gimp_thumbnail_check_thumb (imagefile->thumbnail, size); + + return (state == GIMP_THUMB_STATE_OK); + } + + return TRUE; +} + gboolean gimp_imagefile_save_thumbnail (GimpImagefile *imagefile, GimpImage *gimage) { - gboolean success; - GError *error = NULL; + gint size; + gboolean success = TRUE; + GError *error = NULL; g_return_val_if_fail (GIMP_IS_IMAGEFILE (imagefile), FALSE); g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); - /* peek the thumbnail to make sure that mtime and filesize are set */ - gimp_thumbnail_peek_image (imagefile->thumbnail); + size = imagefile->gimp->config->thumbnail_size; - success = gimp_imagefile_save_thumb (imagefile, - gimage, - gimage->gimp->config->thumbnail_size, - &error); - - if (! success) + if (size > 0) { - g_message (error->message); - g_error_free (error); + /* peek the thumbnail to make sure that mtime and filesize are set */ + gimp_thumbnail_peek_image (imagefile->thumbnail); + + success = gimp_imagefile_save_thumb (imagefile, + gimage, size, FALSE, + &error); + + if (! success) + { + g_message (error->message); + g_error_free (error); + } } return success; @@ -684,6 +714,7 @@ static gboolean gimp_imagefile_save_thumb (GimpImagefile *imagefile, GimpImage *gimage, gint size, + gboolean replace, GError **error) { GimpThumbnail *thumbnail = imagefile->thumbnail; @@ -750,7 +781,14 @@ gimp_imagefile_save_thumb (GimpImagefile *imagefile, g_object_unref (pixbuf); if (success) - gimp_imagefile_update (imagefile); + { + if (replace) + gimp_thumbnail_delete_others (thumbnail, size); + else + gimp_thumbnail_delete_failure (thumbnail); + + gimp_imagefile_update (imagefile); + } return success; } diff --git a/app/core/gimpimagefile.h b/app/core/gimpimagefile.h index 34c2699aa5..02a383b758 100644 --- a/app/core/gimpimagefile.h +++ b/app/core/gimpimagefile.h @@ -68,11 +68,14 @@ void gimp_imagefile_update (GimpImagefile *imagefile); void gimp_imagefile_create_thumbnail (GimpImagefile *imagefile, GimpContext *context, GimpProgress *progress, - gint thumb_size); + gint size, + gboolean replace); void gimp_imagefile_create_thumbnail_weak (GimpImagefile *imagefile, GimpContext *context, GimpProgress *progress, - gint size); + gint size, + gboolean replace); +gboolean gimp_imagefile_check_thumbnail (GimpImagefile *imagefile); gboolean gimp_imagefile_save_thumbnail (GimpImagefile *imagefile, GimpImage *gimage); const gchar * gimp_imagefile_get_desc_string (GimpImagefile *imagefile); diff --git a/app/file/file-open.c b/app/file/file-open.c index 1381f53f34..371f6b9c2b 100644 --- a/app/file/file-open.c +++ b/app/file/file-open.c @@ -247,7 +247,11 @@ file_open_with_proc_and_display (Gimp *gimp, */ if (strcmp (uri, gimp_image_get_uri (gimage)) == 0) { - gimp_imagefile_save_thumbnail (imagefile, gimage); + /* no need to save a thumbnail if there's a good one already */ + if (! gimp_imagefile_check_thumbnail (imagefile)) + { + gimp_imagefile_save_thumbnail (imagefile, gimage); + } } gimp_recent_list_add_uri (uri, mime_type); diff --git a/app/widgets/gimpthumbbox.c b/app/widgets/gimpthumbbox.c index 7759aa2d4b..c97eeeb37e 100644 --- a/app/widgets/gimpthumbbox.c +++ b/app/widgets/gimpthumbbox.c @@ -677,24 +677,26 @@ gimp_thumb_box_create_thumbnail (GimpThumbBox *box, if (filename && g_file_test (filename, G_FILE_TEST_IS_REGULAR)) { - gchar *basename = file_utils_uri_to_utf8_basename (uri); + GimpThumbnail *thumb = box->imagefile->thumbnail; + gchar *basename; + basename = file_utils_uri_to_utf8_basename (uri); gtk_label_set_text (GTK_LABEL (box->filename), basename); g_free (basename); - if (force) - gimp_thumbs_delete_for_uri (uri); - gimp_object_set_name (GIMP_OBJECT (box->imagefile), uri); if (force || - gimp_thumbnail_peek_thumb (box->imagefile->thumbnail, size) - < GIMP_THUMB_STATE_FAILED) + (gimp_thumbnail_peek_thumb (thumb, size) < GIMP_THUMB_STATE_FAILED && + ! gimp_thumbnail_has_failed (thumb))) { + Gimp *gimp = box->imagefile->gimp; + gimp_imagefile_create_thumbnail (box->imagefile, - gimp_get_user_context (box->imagefile->gimp), + gimp_get_user_context (gimp), GIMP_PROGRESS (box), - size); + size, + !force); } } @@ -715,6 +717,7 @@ gimp_thumb_box_auto_thumbnail (GimpThumbBox *box) case GIMP_THUMB_STATE_NOT_FOUND: case GIMP_THUMB_STATE_OLD: if (thumb->image_filesize < gimp->config->thumbnail_filesize_limit && + ! gimp_thumbnail_has_failed (thumb) && file_utils_find_proc_by_extension (gimp->load_procs, uri)) { if (thumb->image_filesize > 0) @@ -740,7 +743,8 @@ gimp_thumb_box_auto_thumbnail (GimpThumbBox *box) gimp_imagefile_create_thumbnail_weak (box->imagefile, gimp_get_user_context (gimp), GIMP_PROGRESS (box), - gimp->config->thumbnail_size); + gimp->config->thumbnail_size, + TRUE); } break; diff --git a/devel-docs/ChangeLog b/devel-docs/ChangeLog index 3803488626..9bb410e634 100644 --- a/devel-docs/ChangeLog +++ b/devel-docs/ChangeLog @@ -1,3 +1,8 @@ +2004-10-23 Sven Neumann + + * libgimpthumb/libgimpthumb-sections.txt + * libgimpthumb/tmpl/gimpthumbnail.sgml: updated. + 2004-10-14 Sven Neumann * libgimpwidgets/libgimpwidgets-sections.txt: added diff --git a/devel-docs/libgimpthumb/libgimpthumb-sections.txt b/devel-docs/libgimpthumb/libgimpthumb-sections.txt index 04084ff271..32da1425ba 100644 --- a/devel-docs/libgimpthumb/libgimpthumb-sections.txt +++ b/devel-docs/libgimpthumb/libgimpthumb-sections.txt @@ -8,10 +8,15 @@ gimp_thumbnail_set_filename gimp_thumbnail_set_from_thumb gimp_thumbnail_peek_image gimp_thumbnail_peek_thumb +gimp_thumbnail_check_thumb gimp_thumbnail_load_thumb gimp_thumbnail_save_thumb -gimp_thumbnail_save_failure gimp_thumbnail_save_thumb_local +gimp_thumbnail_save_failure +gimp_thumbnail_delete_failure +gimp_thumbnail_delete_others +gimp_thumbnail_has_failed + GimpThumbnailClass GIMP_THUMBNAIL diff --git a/devel-docs/libgimpthumb/tmpl/gimpthumbnail.sgml b/devel-docs/libgimpthumb/tmpl/gimpthumbnail.sgml index b604760a6f..0b8640b79d 100644 --- a/devel-docs/libgimpthumb/tmpl/gimpthumbnail.sgml +++ b/devel-docs/libgimpthumb/tmpl/gimpthumbnail.sgml @@ -129,6 +129,16 @@ using object properties. @Returns: + + + + + +@thumbnail: +@size: +@Returns: + + @@ -152,17 +162,6 @@ using object properties. @Returns: - - - - - -@thumbnail: -@software: -@error: -@Returns: - - @@ -175,3 +174,40 @@ using object properties. @Returns: + + + + + +@thumbnail: +@software: +@error: +@Returns: + + + + + + + +@thumbnail: + + + + + + + +@thumbnail: +@size: + + + + + + + +@thumbnail: +@Returns: + + diff --git a/libgimpthumb/gimpthumb-utils.c b/libgimpthumb/gimpthumb-utils.c index 1b63801206..9e3850904c 100644 --- a/libgimpthumb/gimpthumb-utils.c +++ b/libgimpthumb/gimpthumb-utils.c @@ -548,6 +548,30 @@ gimp_thumbs_delete_for_uri_local (const gchar *uri) } } +void +_gimp_thumbs_delete_others (const gchar *uri, + GimpThumbSize size) +{ + gint i; + + g_return_if_fail (gimp_thumb_initialized); + g_return_if_fail (uri != NULL); + + for (i = 0; i < thumb_num_sizes; i++) + { + if (thumb_sizes[i] != size) + { + gchar *filename = gimp_thumb_name_from_uri (uri, thumb_sizes[i]); + + if (filename) + { + unlink (filename); + g_free (filename); + } + } + } +} + static void gimp_thumb_exit (void) { diff --git a/libgimpthumb/gimpthumb-utils.h b/libgimpthumb/gimpthumb-utils.h index 867cb768df..8010d3013d 100644 --- a/libgimpthumb/gimpthumb-utils.h +++ b/libgimpthumb/gimpthumb-utils.h @@ -57,6 +57,10 @@ gboolean gimp_thumb_ensure_thumb_dir_local (const gchar *dirname, void gimp_thumbs_delete_for_uri_local (const gchar *uri); +/* for internal use only */ +void _gimp_thumbs_delete_others (const gchar *uri, + GimpThumbSize size); + G_END_DECLS diff --git a/libgimpthumb/gimpthumb.def b/libgimpthumb/gimpthumb.def index f6918c998e..5c9b94d314 100644 --- a/libgimpthumb/gimpthumb.def +++ b/libgimpthumb/gimpthumb.def @@ -11,7 +11,11 @@ EXPORTS gimp_thumb_name_from_uri_local gimp_thumb_size_get_type gimp_thumb_state_get_type + gimp_thumbnail_check_thumb + gimp_thumbnail_delete_failure + gimp_thumbnail_delete_others gimp_thumbnail_get_type + gimp_thumbnail_has_failed gimp_thumbnail_load_thumb gimp_thumbnail_new gimp_thumbnail_peek_image diff --git a/libgimpthumb/gimpthumbnail.c b/libgimpthumb/gimpthumbnail.c index 8241834aca..371489192a 100644 --- a/libgimpthumb/gimpthumbnail.c +++ b/libgimpthumb/gimpthumbnail.c @@ -561,6 +561,10 @@ gimp_thumbnail_peek_image (GimpThumbnail *thumbnail) * 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(). + * * Return value: the thumbnail's #GimpThumbState after the update **/ GimpThumbState @@ -582,6 +586,40 @@ gimp_thumbnail_peek_thumb (GimpThumbnail *thumbnail, return 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. + * + * Return value: the thumbnail's #GimpThumbState after the update + * + * Since: GIMP 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 thumbnail->thumb_state; +} + static void gimp_thumbnail_update_image (GimpThumbnail *thumbnail) { @@ -657,6 +695,11 @@ gimp_thumbnail_update_image (GimpThumbnail *thumbnail) "image-mtime", mtime, "image-filesize", filesize, NULL); + + if (thumbnail->thumb_state == GIMP_THUMB_STATE_OK) + g_object_set (thumbnail, + "thumb-state", GIMP_THUMB_STATE_OLD, + NULL); } } @@ -706,8 +749,7 @@ gimp_thumbnail_update_thumb (GimpThumbnail *thumbnail, if (filename) state = (size > GIMP_THUMB_SIZE_FAIL ? - GIMP_THUMB_STATE_EXISTS : - GIMP_THUMB_STATE_FAILED); + GIMP_THUMB_STATE_EXISTS : GIMP_THUMB_STATE_FAILED); thumbnail->thumb_size = size; thumbnail->thumb_filesize = filesize; @@ -887,12 +929,23 @@ gimp_thumbnail_save (GimpThumbnail *thumbnail, success = (chmod (filename, 0600) == 0); - if (success) - gimp_thumbnail_update_thumb (thumbnail, size); - else + if (! success) g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), "Could not set permissions of thumbnail for %s: %s", thumbnail->image_uri, g_strerror (errno)); + + g_object_freeze_notify (G_OBJECT (thumbnail)); + + gimp_thumbnail_update_thumb (thumbnail, size); + + if (success && + thumbnail->thumb_state == GIMP_THUMB_STATE_EXISTS && + strcmp (filename, thumbnail->thumb_filename) == 0) + { + thumbnail->thumb_state = GIMP_THUMB_STATE_OK; + } + + g_object_thaw_notify (G_OBJECT (thumbnail)); } unlink (tmpname); @@ -1264,3 +1317,128 @@ gimp_thumbnail_save_failure (GimpThumbnail *thumbnail, 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: GIMP 2.2 + **/ +void +gimp_thumbnail_delete_failure (GimpThumbnail *thumbnail) +{ + gchar *filename; + + g_return_if_fail (GIMP_IS_THUMBNAIL (thumbnail)); + g_return_if_fail (thumbnail->image_uri != NULL); + + GIMP_THUMB_DEBUG_CALL (thumbnail); + + filename = gimp_thumb_name_from_uri (thumbnail->image_uri, + GIMP_THUMB_SIZE_FAIL); + if (filename) + { + 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: GIMP 2.2 + **/ +void +gimp_thumbnail_delete_others (GimpThumbnail *thumbnail, + GimpThumbSize size) +{ + g_return_if_fail (GIMP_IS_THUMBNAIL (thumbnail)); + g_return_if_fail (thumbnail->image_uri != NULL); + + GIMP_THUMB_DEBUG_CALL (thumbnail); + + _gimp_thumbs_delete_others (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. + * + * Return value: %TRUE if a failure thumbnail exists or + * + * Since: GIMP 2.2 + **/ +gboolean +gimp_thumbnail_has_failed (GimpThumbnail *thumbnail) +{ + 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 (thumbnail->image_uri != NULL, FALSE); + + GIMP_THUMB_DEBUG_CALL (thumbnail); + + filename = gimp_thumb_name_from_uri (thumbnail->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, thumbnail->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 == thumbnail->image_mtime && + (option == NULL || image_size == thumbnail->image_filesize)) + { + failed = TRUE; + } + + finish: + g_object_unref (pixbuf); + + return failed; +} diff --git a/libgimpthumb/gimpthumbnail.h b/libgimpthumb/gimpthumbnail.h index 687d572e8f..0228edcbc0 100644 --- a/libgimpthumb/gimpthumbnail.h +++ b/libgimpthumb/gimpthumbnail.h @@ -94,21 +94,31 @@ GimpThumbState gimp_thumbnail_peek_image (GimpThumbnail *thumbnail); GimpThumbState gimp_thumbnail_peek_thumb (GimpThumbnail *thumbnail, GimpThumbSize size); +GimpThumbState gimp_thumbnail_check_thumb (GimpThumbnail *thumbnail, + GimpThumbSize size); + GdkPixbuf * gimp_thumbnail_load_thumb (GimpThumbnail *thumbnail, GimpThumbSize size, GError **error); + gboolean gimp_thumbnail_save_thumb (GimpThumbnail *thumbnail, GdkPixbuf *pixbuf, const gchar *software, GError **error); -gboolean gimp_thumbnail_save_failure (GimpThumbnail *thumbnail, - const gchar *software, - GError **error); gboolean gimp_thumbnail_save_thumb_local (GimpThumbnail *thumbnail, GdkPixbuf *pixbuf, const gchar *software, GError **error); +gboolean gimp_thumbnail_save_failure (GimpThumbnail *thumbnail, + const gchar *software, + GError **error); +void gimp_thumbnail_delete_failure (GimpThumbnail *thumbnail); +void gimp_thumbnail_delete_others (GimpThumbnail *thumbnail, + GimpThumbSize size); + +gboolean gimp_thumbnail_has_failed (GimpThumbnail *thumbnail); + G_END_DECLS