diff options
Diffstat (limited to 'libnautilus-private/nautilus-thumbnails.c')
-rw-r--r-- | libnautilus-private/nautilus-thumbnails.c | 805 |
1 files changed, 112 insertions, 693 deletions
diff --git a/libnautilus-private/nautilus-thumbnails.c b/libnautilus-private/nautilus-thumbnails.c index 53f145f33..fe9a92a94 100644 --- a/libnautilus-private/nautilus-thumbnails.c +++ b/libnautilus-private/nautilus-thumbnails.c @@ -28,6 +28,7 @@ #include "nautilus-directory-notify.h" #include "nautilus-global-preferences.h" #include "nautilus-icon-factory-private.h" +#include "nautilus-icon-factory.h" #include "nautilus-theme.h" #include "nautilus-thumbnails-jpeg.h" #include <eel/eel-gdk-pixbuf-extensions.h> @@ -52,22 +53,6 @@ #define DEBUG_THUMBNAILS #endif -/* The time we allow 'convert' to convert an image, in milliseconds - (1/1000ths of a second). If it hasn't finished by this time, we kill it. */ -#define THUMBNAIL_CONVERT_TIMEOUT 60000 - - -/* permissions for thumbnail directory */ -#define THUMBNAIL_DIR_PERMISSIONS (GNOME_VFS_PERM_USER_ALL \ - | GNOME_VFS_PERM_GROUP_ALL \ - | GNOME_VFS_PERM_OTHER_ALL) - -#define THUMBNAIL_PLACEHOLDER_PERMISSIONS (GNOME_VFS_PERM_USER_READ | \ - GNOME_VFS_PERM_USER_WRITE | \ - GNOME_VFS_PERM_GROUP_READ | \ - GNOME_VFS_PERM_GROUP_WRITE | \ - GNOME_VFS_PERM_OTHER_READ) - /* Should never be a reasonable actual mtime */ #define INVALID_MTIME 0 @@ -94,131 +79,13 @@ static volatile gboolean thumbnail_thread_is_running = FALSE; thumbnails we are making. Lock thumbnails_mutex when accessing this. */ static volatile GList *thumbnails_to_make = NULL; - - -/* utility to test whether a file exists using vfs */ -static gboolean -vfs_file_exists (const char *file_uri) -{ - gboolean result; - GnomeVFSURI *uri; - - uri = gnome_vfs_uri_new (file_uri); - if (uri == NULL) { - return FALSE; - } - - /* FIXME bugzilla.gnome.org 43137: The synchronous I/O here - * means this call is unsuitable for use on anything that - * might be remote. - */ - result = gnome_vfs_uri_exists (uri); - gnome_vfs_uri_unref (uri); - - return result; -} - -static gboolean -uri_is_local (const char *uri) -{ - gboolean is_local; - GnomeVFSURI *vfs_uri; - - vfs_uri = gnome_vfs_uri_new (uri); - is_local = gnome_vfs_uri_is_local (vfs_uri); - gnome_vfs_uri_unref (vfs_uri); - - return is_local; -} - -/* this functions looks for a password in a uri and changes it for 6 'x' */ - -static char * -obfuscate_password (const char *escaped_uri) -{ - const char *passwd_start, *passwd_end; - char *new_uri, *new_uri_temp; - - passwd_start = strchr (escaped_uri, ':'); - g_assert (passwd_start != NULL); - passwd_start = strchr (passwd_start + 1, ':'); /* The fisrt ':' is for the protocol */ - if (passwd_start == NULL) { /* There's no password */ - return g_strdup (escaped_uri); - } - passwd_end = strchr (passwd_start, '@'); - - /* This URL has no valid password */ - if (passwd_end == NULL || passwd_start == NULL || passwd_end <= passwd_start) { - return g_strdup (escaped_uri); - } else { - new_uri_temp = g_strndup (escaped_uri, passwd_start - escaped_uri); - new_uri = g_strdup_printf ("%s:xxxxxx%s", new_uri_temp, passwd_end); - g_free (new_uri_temp); - return new_uri; - } -} - -/* utility routine that, given the uri of an image, constructs the uri to the corresponding thumbnail */ - -static char * -make_thumbnail_uri (const char *image_uri) -{ - char *directory_name, *last_slash; - char *escaped_uri, *protected_uri; - char *thumbnail_path, *thumbnail_base, *thumbnail_uri; - - /* Copy the image uri and change the last '/' character to '\0', - so we have the directory part and the basename part. */ - directory_name = g_strdup (image_uri); - last_slash = strrchr (directory_name, '/'); - *last_slash = '\0'; - - /* Convert '/' characters in the directory part to "%2F", so we can - use one directory to represent the full path, - e.g. "file:///home/damon" becomes "file:%2F%2F%2Fhome%2Fdamon". */ - escaped_uri = gnome_vfs_escape_slashes (directory_name); - - /* Try to obfuscate any password embedded in the uri, so it can't be - spotted by looking the the ~/.nautilus/thumbnails directory. */ - protected_uri = obfuscate_password (escaped_uri); - g_free (escaped_uri); - - /* Create the directory in which the thumbnail file will be stored, - e.g. "/home/damon/.nautilus/thumbnails/file:%2F%2F%2Fhome%2Fdamon". - */ - thumbnail_path = g_strdup_printf ("%s/.nautilus/thumbnails/%s", - g_get_home_dir(), protected_uri); - /* Turn it into a uri, i.e. prefix with 'file:///' and escape any - invalid characters. */ - thumbnail_base = gnome_vfs_get_uri_from_local_path (thumbnail_path); - g_free (thumbnail_path); - g_free (protected_uri); - - /* append the file name, and a .png suffix if necessary. */ - if (eel_istr_has_suffix (image_uri, ".png")) { - thumbnail_uri = g_strdup_printf ("%s/%s", thumbnail_base, - last_slash + 1); - } else { - thumbnail_uri = g_strdup_printf ("%s/%s.png", thumbnail_base, - last_slash + 1); - } - g_free(thumbnail_base); - - g_free (directory_name); - - return thumbnail_uri; -} +static GnomeThumbnailFactory *thumbnail_factory = NULL; static gboolean get_file_mtime (const char *file_uri, time_t* mtime) { GnomeVFSFileInfo *file_info; - if (!uri_is_local (file_uri)) { - *mtime = INVALID_MTIME; - return FALSE; - } - /* gather the info and then compare modification times */ file_info = gnome_vfs_file_info_new (); gnome_vfs_get_file_info (file_uri, file_info, GNOME_VFS_FILE_INFO_FOLLOW_LINKS); @@ -257,43 +124,6 @@ compare_thumbnail_info (gconstpointer a, gconstpointer b) return strcmp (info_a->image_uri, info_b->image_uri) != 0; } -/* utility to create a placeholder thumbnail uri (which indicates that a - * previous thumbnailing attempt has failed) - */ -/* FIXME: A .x extension might exist on a real file, and we might - * recognize it by magic number even if it doesn't have the right - * extension. - */ -static char * -make_invalid_thumbnail_uri (const char *thumbnail_uri) -{ - return g_strconcat (thumbnail_uri, ".x", NULL); -} - -/* return true if there's a placeholder thumbnail present for the passed in - * file, which indicates that a previous thumbnailing attempt failed and - * we should use the mime-type icon instead - */ -gboolean -nautilus_thumbnail_has_invalid_thumbnail (NautilusFile *file) -{ - char *file_uri, *thumbnail_uri, *invalid_thumbnail_uri; - gboolean is_invalid; - - file_uri = nautilus_file_get_uri (file); - - - thumbnail_uri = make_thumbnail_uri (file_uri); - invalid_thumbnail_uri = make_invalid_thumbnail_uri (thumbnail_uri); - - is_invalid = vfs_file_exists (invalid_thumbnail_uri); - - g_free (file_uri); - g_free (thumbnail_uri); - g_free (invalid_thumbnail_uri); - return is_invalid; -} - /* This function is added as a very low priority idle function to start the thread to create any needed thumbnails. It is added with a very low priority so that it doesn't delay showing the directory in the icon/list views. @@ -304,6 +134,11 @@ thumbnail_thread_starter_cb (gpointer data) pthread_attr_t thread_attributes; pthread_t thumbnail_thread; + /* Don't do this in thread, since g_object_ref is not threadsafe */ + if (thumbnail_factory == NULL) { + thumbnail_factory = nautilus_icon_factory_get_thumbnail_factory (); + } + /* We create the thread in the detached state, as we don't need/want to join with it at any point. */ pthread_attr_init (&thread_attributes); @@ -326,236 +161,54 @@ thumbnail_thread_starter_cb (gpointer data) return FALSE; } - -/* Routine that takes a uri of a large image file and returns the uri of its - corresponding thumbnail. If no thumbnail is available, put the image on the - thumbnail queue so one is eventually made, and return NULL. It will call - nautilus_file_changed() when the thumbnail is ready or we know we can't - create one for the image. (Note that this function will probably be called - again at this point, to get the uri of the newly-created thumbnail.) */ -/* FIXME bugzilla.gnome.org 40642: - * Most of this thumbnail machinery belongs in NautilusFile, not here. - */ -char * -nautilus_get_thumbnail_uri (NautilusFile *file) -{ - char *file_uri, *thumbnail_uri; - GnomeVFSFileInfo *file_info; - GnomeVFSResult result; - NautilusThumbnailInfo *info; - time_t file_mtime = INVALID_MTIME; - time_t thumbnail_mtime = INVALID_MTIME; - gboolean remake_thumbnail = FALSE; - - /* We have to check if the thumbnail exists and its mtime matches that - of the file. If it does, we return it. If not, we return NULL - and place the thumbnail in the queue to be made. */ - file_uri = nautilus_file_get_uri (file); - thumbnail_uri = make_thumbnail_uri (file_uri); -#ifdef DEBUG_THUMBNAILS - g_message ("(Main Thread) Checking if thumbnail exists: %s\n", - file_uri); -#endif - - /* Check if the thumbnail file exists and gets its mtime in one go. */ - file_info = gnome_vfs_file_info_new (); - result = gnome_vfs_get_file_info (thumbnail_uri, file_info, - GNOME_VFS_FILE_INFO_FOLLOW_LINKS); - if (result == GNOME_VFS_OK) { - thumbnail_mtime = file_info->mtime; - } else { - /* If the thumbnail file doesn't exist then we need to make - it. */ - remake_thumbnail = TRUE; - } - gnome_vfs_file_info_unref (file_info); - - /* Hopefully the NautilusFile will already have the image file mtime, - so we can just use that. Otherwise we have to get it ourselves. */ - if (file->details->info - && file->details->file_info_is_up_to_date - && file->details->info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_MTIME) { - file_mtime = file->details->info->mtime; - } else { - get_file_mtime (file_uri, &file_mtime); - } - - /* If either mtime is not available for whatever reason, we - don't remake the thumbnail. If both are available and don't - match we do remake the thumbnail. */ -#ifdef DEBUG_THUMBNAILS - g_message ("(Main Thread) file mtime: %li thumbnail mtime: %li\n", - file_mtime, thumbnail_mtime); -#endif - if (file_mtime != INVALID_MTIME && thumbnail_mtime != INVALID_MTIME - && file_mtime != thumbnail_mtime) { - remake_thumbnail = TRUE; - } - - /* If we don't need to make/remake the thumbnail, return the uri. */ - if (!remake_thumbnail) { -#ifdef DEBUG_THUMBNAILS - g_message ("(Main Thread) mtimes match - returning thumbnail uri\n"); -#endif - g_free (file_uri); - return thumbnail_uri; - } - -#ifdef DEBUG_THUMBNAILS - g_message ("(Main Thread) mtimes don't match. Recreating thumbnail\n"); -#endif - nautilus_icon_factory_remove_by_uri (thumbnail_uri); - - info = g_new0 (NautilusThumbnailInfo, 1); - info->image_uri = file_uri; - info->mime_type = nautilus_file_get_mime_type (file); - info->original_file_mtime = file_mtime; - -#ifdef DEBUG_THUMBNAILS - g_message ("(Main Thread) Locking mutex\n"); -#endif - pthread_mutex_lock (&thumbnails_mutex); - - /********************************* - * MUTEX LOCKED - *********************************/ - - /* Check if it is already in the list of thumbnails to make. */ - if (g_list_find_custom ((GList*) thumbnails_to_make, info, - compare_thumbnail_info) == NULL) { - /* Add the thumbnail to the list. */ -#ifdef DEBUG_THUMBNAILS - g_message ("(Main Thread) Adding thumbnail: %s\n", - info->image_uri); -#endif - thumbnails_to_make = g_list_append ((GList*) thumbnails_to_make, info); - - /* If the thumbnail thread isn't running, and we haven't - scheduled an idle function to start it up, do that now. - We don't want to start it until all the other work is done, - so the GUI will be updated as quickly as possible.*/ - if (thumbnail_thread_is_running == FALSE - && thumbnail_thread_starter_id == 0) { - thumbnail_thread_starter_id = g_idle_add_full (G_PRIORITY_LOW, thumbnail_thread_starter_cb, NULL, NULL); - } - } - - /********************************* - * MUTEX UNLOCKED - *********************************/ - -#ifdef DEBUG_THUMBNAILS - g_message ("(Main Thread) Unlocking mutex\n"); -#endif - pthread_mutex_unlock (&thumbnails_mutex); - - g_free (thumbnail_uri); - - return NULL; -} - - -/* Creates the thumbnail directory, and any parent directories, if it doesn't - already exist. Returns TRUE on success. */ -static gboolean -nautilus_thumbnail_create_directory (const char *thumbnail_uri) -{ - GnomeVFSURI *thumbnail_vfs_uri, *thumbnail_directory_uri; - GnomeVFSResult result; - - thumbnail_vfs_uri = gnome_vfs_uri_new (thumbnail_uri); - thumbnail_directory_uri = gnome_vfs_uri_get_parent (thumbnail_vfs_uri); - result = eel_make_directory_and_parents (thumbnail_directory_uri, - THUMBNAIL_DIR_PERMISSIONS); - gnome_vfs_uri_unref (thumbnail_directory_uri); - gnome_vfs_uri_unref (thumbnail_vfs_uri); - - if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_FILE_EXISTS) { -#ifdef DEBUG_THUMBNAILS - g_message ("(Thumbnail Thread) Couldn't create thumbnail directory for: %s\n", thumbnail_uri); -#endif - return FALSE; - } - - return TRUE; -} - void nautilus_update_thumbnail_file_renamed (const char *old_file_uri, const char *new_file_uri) { - char *old_thumbnail_uri, *new_thumbnail_uri; + char *old_thumbnail_path; + GdkPixbuf *pixbuf; + GnomeVFSFileInfo *file_info; + GnomeThumbnailFactory *factory; - old_thumbnail_uri = make_thumbnail_uri (old_file_uri); - if (old_thumbnail_uri != NULL && vfs_file_exists (old_thumbnail_uri)) { - new_thumbnail_uri = make_thumbnail_uri (new_file_uri); - - g_assert (new_thumbnail_uri != NULL); - - if (nautilus_thumbnail_create_directory (new_thumbnail_uri)) - gnome_vfs_move (old_thumbnail_uri, new_thumbnail_uri, - FALSE); - - g_free (new_thumbnail_uri); + old_thumbnail_path = gnome_thumbnail_path_for_uri (old_file_uri, GNOME_THUMBNAIL_SIZE_NORMAL); + if (old_thumbnail_path != NULL && + g_file_test (old_thumbnail_path, G_FILE_TEST_EXISTS)) { + file_info = gnome_vfs_file_info_new (); + if (gnome_vfs_get_file_info (new_file_uri, + file_info, + GNOME_VFS_FILE_INFO_DEFAULT) == GNOME_VFS_OK) { + pixbuf = gdk_pixbuf_new_from_file (old_thumbnail_path, NULL); + + if (pixbuf && gnome_thumbnail_has_uri (pixbuf, old_file_uri)) { + factory = nautilus_icon_factory_get_thumbnail_factory (); + gnome_thumbnail_factory_save_thumbnail (factory, + pixbuf, + new_file_uri, + file_info->mtime); + g_object_unref (factory); + } + + if (pixbuf) { + g_object_unref (pixbuf); + } + + unlink (old_thumbnail_path); + } + gnome_vfs_file_info_unref (file_info); } - g_free (old_thumbnail_uri); + g_free (old_thumbnail_path); } void nautilus_remove_thumbnail_for_file (const char *old_file_uri) { - char *thumbnail_uri; + char *old_thumbnail_path; - thumbnail_uri = make_thumbnail_uri (old_file_uri); - if (thumbnail_uri != NULL && vfs_file_exists (thumbnail_uri)) { - gnome_vfs_unlink (thumbnail_uri); + old_thumbnail_path = gnome_thumbnail_path_for_uri (old_file_uri, GNOME_THUMBNAIL_SIZE_NORMAL); + if (old_thumbnail_path != NULL) { + unlink (old_thumbnail_path); } - - g_free (thumbnail_uri); -} - -/* Here is a heuristic compatibility routine to determine if a pixbuf - * already has a frame around it or not. - * - * This only happens with thumbnails generated by earlier versions of - * Nautilus, which used a fixed frame, so we can test for a few pixels - * to detect it. This is biased toward being quick and saying yes, - * since it's not that big a deal if we're wrong, and it looks better - * to have no frame than two frames. - */ - -static gboolean -pixel_matches_value (const guchar *pixels, guchar value) -{ - g_return_val_if_fail (pixels != NULL, FALSE); - - return pixels[0] == value - && pixels[1] == value - && pixels[2] == value; -} - -static gboolean -pixbuf_is_framed (GdkPixbuf *pixbuf) -{ - const guchar *pixels; - int row_stride; - - g_return_val_if_fail (pixbuf != NULL, FALSE); - - if (gdk_pixbuf_get_height (pixbuf) < 6 - || gdk_pixbuf_get_width (pixbuf) < 6 - || gdk_pixbuf_get_n_channels (pixbuf) != 4) { - return FALSE; - } - - pixels = gdk_pixbuf_get_pixels (pixbuf); - row_stride = gdk_pixbuf_get_rowstride (pixbuf); - - g_assert (row_stride >= 12); - - return pixel_matches_value (pixels, 0xFF) - && pixel_matches_value (pixels + row_stride + 4, 0x00) - && pixel_matches_value (pixels + 2 * row_stride + 8, 0xBB); + g_free (old_thumbnail_path); } /* routine to load an image from the passed-in path, and then embed it in @@ -571,8 +224,8 @@ nautilus_thumbnail_load_framed_image (const char *path) char c; pixbuf = gdk_pixbuf_new_from_file (path, NULL); - if (pixbuf == NULL || pixbuf_is_framed (pixbuf)) { - return pixbuf; + if (pixbuf == NULL) { + return NULL; } /* The pixbuf isn't already framed (i.e., it was not made by @@ -615,152 +268,6 @@ nautilus_thumbnail_load_framed_image (const char *path) * Thumbnail Thread Functions. ***************************************************************************/ -/* This is a timeout function that is run if 'convert' hasn't finished in a - reasonable time. It kills the process with SIGKILL. Note that this is only - a last resort, just in case convert is hanging. */ -static gboolean -thumbnail_thread_convert_timeout_cb (gpointer data) -{ - pid_t *child_process_id = data; - -#ifdef DEBUG_THUMBNAILS - g_message ("Convert is hanging - killing\n"); -#endif - - kill (*child_process_id, SIGKILL); - - return FALSE; -} - - -/* This runs the "convert" program from ImageMagick to try to create a - thumbnail. It is used when gdk-pixbuf and librsvg can't handle the image - format. It does a fork(), exec() and synchronous waitpid(). - We can do everything synchronously since we have our own thread. - It returns TRUE on success, i.e. the thumbnail was created and is valid. */ -static gboolean -thumbnail_thread_run_convert (NautilusThumbnailInfo *info, - char *thumbnail_path) -{ - char *image_path; - pid_t pid; - GdkPixbuf *pixbuf; - GError *error = NULL; - guint timeout_id; - - image_path = gnome_vfs_get_local_path_from_uri (info->image_uri); - if (image_path == NULL) - return FALSE; - - /* Fork a new process to exec "convert". */ - pid = fork (); - - /* If fork() failed, return FALSE. */ - if (pid == -1) - return FALSE; - - /* The child process runs "convert" to convert the image to the - 96x96 PNG thumbnail. */ - if (pid == 0) { -#ifdef DEBUG_THUMBNAILS - g_message ("### Running convert %s -> %s\n", - image_path, thumbnail_path); -#endif - -#if 1 - /* Redirect stdout to the path of the new thumbnail. We do - this because some versions of convert have problems with - the '%' characters that we use in thumbnail paths, so - we can't pass it in as a filename. */ - if (freopen (thumbnail_path, "w", stdout) == NULL) { -#ifdef DEBUG_THUMBNAILS - g_message ("freopen failed!\n"); -#endif - return FALSE; - } - - execlp ("convert", "convert", - "-geometry", "96x96", - image_path, "png:-", - NULL); -#else - /* This was what the old version did, though it didn't work - for me on RedHat 7.1. convert complained about not finding - the files, and seemed confused by the '%' chars. */ - execlp ("convert", "convert", - "-geometry", "96x96", - image_path, thumbnail_path, - NULL); -#endif - - /* We exit() here just in case an error occurred when calling - execlp(). */ - _exit (0); - } - - /* This is the parent process. First add a timeout in the mainloop to - kill the child convert process if it hasn't finished in a reasonable - amount of time. */ - timeout_id = g_timeout_add (THUMBNAIL_CONVERT_TIMEOUT, - thumbnail_thread_convert_timeout_cb, - &pid); - - /* Now wait synchronously until the child exits. */ - for (;;) { - /* We loop around in case we get EINTR. */ - pid_t terminated_pid = waitpid (pid, NULL, 0); - - /* If our child process exited, then we can continue. */ - if (terminated_pid == pid) - break; - - /* If we get any error except EINTR, we shouldn't wait again - so we flag an error and break out of the loop. If waitpid() - returned -1 and errno was EINTR we loop round and call - waitpid() again. */ - if (terminated_pid != -1 || errno != EINTR) { -#ifdef DEBUG_THUMBNAILS - g_message ("convert waitpid failed!\n"); -#endif - return FALSE; - } - } - - /* Remove our timeout, if it still exists. */ - g_source_remove (timeout_id); - -#ifdef DEBUG_THUMBNAILS - g_message ("=== Convert finished\n"); -#endif - - /* Now check if the thumbnail created by convert exists and is valid. - I'm not sure how reliable convert is. For now we try to load the - thumbnail back in to check it is OK. Maybe we could just check for - an empty file. Note that we redirected to stdout, so if an error - occurred an empty file will probably be left there. */ - pixbuf = gdk_pixbuf_new_from_file (thumbnail_path, &error); - if (error) { -#ifdef DEBUG_THUMBNAILS - g_message ("gdk-pixbuf error: %s\n", error->message); -#endif - g_error_free (error); - } - - if (pixbuf != NULL) { -#ifdef DEBUG_THUMBNAILS - g_message ("convert succeeded!\n"); -#endif - g_object_unref (pixbuf); - return TRUE; - } else { -#ifdef DEBUG_THUMBNAILS - g_message ("convert failed: %s -> %s!\n", - image_path, thumbnail_path); -#endif - return FALSE; - } -} - /* This is a one-shot idle callback called from the main loop to call notify_file_changed() for a thumbnail. It frees the uri afterwards. @@ -789,185 +296,78 @@ thumbnail_thread_notify_file_changed (gpointer image_uri) return FALSE; } - -static void -thumbnail_thread_finish_thumbnail (NautilusThumbnailInfo *info, - char *thumbnail_uri, - char *thumbnail_path) +void +nautilus_create_thumbnail (NautilusFile *file) { -#ifdef DEBUG_THUMBNAILS - g_message ("(Thumbnail Thread) Finishing thumbnail\n"); -#endif - - /* Set the mtime of the new thumbnail file to the same as the image - file, so we know when we need to update the thumbnail. */ - if (info->original_file_mtime != INVALID_MTIME) { - GnomeVFSFileInfo *file_info; + time_t file_mtime = 0; + NautilusThumbnailInfo *info; - file_info = gnome_vfs_file_info_new (); - file_info->mtime = info->original_file_mtime; - /* we don't care about atime, but gnome-vfs - * makes us set it along with mtime. - * FIXME if we weren't lame, we would - * perhaps read the old atime and set it back, - * but we're lame. - */ - file_info->atime = info->original_file_mtime; - - gnome_vfs_set_file_info (thumbnail_uri, - file_info, - GNOME_VFS_SET_FILE_INFO_TIME); - - gnome_vfs_file_info_unref (file_info); + info = g_new0 (NautilusThumbnailInfo, 1); + info->image_uri = nautilus_file_get_uri (file); + info->mime_type = nautilus_file_get_mime_type (file); + + /* Hopefully the NautilusFile will already have the image file mtime, + so we can just use that. Otherwise we have to get it ourselves. */ + if (file->details->info + && file->details->file_info_is_up_to_date + && file->details->info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_MTIME) { + file_mtime = file->details->info->mtime; + } else { + get_file_mtime (info->image_uri, &file_mtime); } -} - - -/* Creating the thumbnail failed, so we remove the thumbnail file if it exists, - and create a special file to flag that we failed to create it. */ -static void -thumbnail_thread_cancel_thumbnail (NautilusThumbnailInfo *info, - char *thumbnail_uri, - char *thumbnail_path) -{ - char *invalid_uri; - GnomeVFSResult result; - GnomeVFSHandle *handle; - -#ifdef DEBUG_THUMBNAILS - g_message ("(Thumbnail Thread) Cancelling thumbnail\n"); -#endif + + info->original_file_mtime = file_mtime; - /* Remove any invalid thumbnail that may have been created. */ - if (g_file_test (thumbnail_path, G_FILE_TEST_EXISTS)) { - unlink (thumbnail_path); - } - /* Create a special file to flag that we tried and failed to create - a thumbnail for this image. */ - invalid_uri = make_invalid_thumbnail_uri (thumbnail_uri); - result = gnome_vfs_create (&handle, invalid_uri, GNOME_VFS_OPEN_WRITE, - FALSE, THUMBNAIL_PLACEHOLDER_PERMISSIONS); - if (result == GNOME_VFS_OK) { - gnome_vfs_close (handle); - } else { #ifdef DEBUG_THUMBNAILS - g_message ("(Thumbnail Thread) Error creating invalid thumbnail file: %s\n", invalid_uri); + g_message ("(Main Thread) Locking mutex\n"); #endif - } - g_free (invalid_uri); -} + pthread_mutex_lock (&thumbnails_mutex); + /********************************* + * MUTEX LOCKED + *********************************/ -/* This creates one thumbnail in the thumbnail thread. */ -static void -thumbnail_thread_make_thumbnail (NautilusThumbnailInfo *info) -{ - GdkPixbuf* full_size_image = NULL; - char *thumbnail_uri, *thumbnail_path; - gboolean success = TRUE; - + /* Check if it is already in the list of thumbnails to make. */ + if (g_list_find_custom ((GList*) thumbnails_to_make, info, + compare_thumbnail_info) == NULL) { + /* Add the thumbnail to the list. */ #ifdef DEBUG_THUMBNAILS - g_message ("(Thumbnail Thread) In make_thumbnail: %s\n", - info->image_uri); + g_message ("(Main Thread) Adding thumbnail: %s\n", + info->image_uri); #endif + thumbnails_to_make = g_list_append ((GList*) thumbnails_to_make, info); - /* Create the URI to save the thumbnail icon in, and the corresponding - local path. */ - thumbnail_uri = make_thumbnail_uri (info->image_uri); - if (thumbnail_uri == NULL) { - return; - } - - thumbnail_path = gnome_vfs_get_local_path_from_uri (thumbnail_uri); - if (thumbnail_path == NULL) { - g_free (thumbnail_uri); - return; - } - - /* Create the thumbnail directory, if it doesn't already exist. */ - if (!nautilus_thumbnail_create_directory (thumbnail_uri)) { - /* If we couldn't create the directory, just return. */ - return; - } - - /* For SVG images we use librsvg to create a full-size pixbuf. - For JPEGs we use special, fast code to create a reduced-size pixbuf - though we still need to scale it afterwards. - For other images we try to load them with gdk-pixbuf. If that fails - we try to use the ImageMagick "convert" program to convert them to - png format at the desired size. */ - if (eel_strcasecmp (info->mime_type, "image/svg") == 0) { - char *image_path = gnome_vfs_get_local_path_from_uri (info->image_uri); - if (image_path != NULL) { - full_size_image = rsvg_pixbuf_from_file_at_max_size (image_path, 96, 96, NULL); - g_free (image_path); + /* If the thumbnail thread isn't running, and we haven't + scheduled an idle function to start it up, do that now. + We don't want to start it until all the other work is done, + so the GUI will be updated as quickly as possible.*/ + if (thumbnail_thread_is_running == FALSE + && thumbnail_thread_starter_id == 0) { + thumbnail_thread_starter_id = g_idle_add_full (G_PRIORITY_LOW, thumbnail_thread_starter_cb, NULL, NULL); } -#ifdef HAVE_LIBJPEG - } else if (eel_strcasecmp (info->mime_type, "image/jpeg") == 0) { - full_size_image = nautilus_thumbnail_load_scaled_jpeg - (info->image_uri, 96, 96); -#endif } else { - full_size_image = eel_gdk_pixbuf_load (info->image_uri); + g_free (info->image_uri); + g_free (info->mime_type); + g_free (info); } - - /* If we have managed to create a pixbuf from the image, scale it to - thumbnail size and save it. Otherwise fall back on running - "convert" to create the thumbnail. */ - if (full_size_image != NULL) { - GdkPixbuf *scaled_image; - - /* Scale the image to thumbnail size. */ - scaled_image = eel_gdk_pixbuf_scale_down_to_fit (full_size_image, 96, 96); - g_object_unref (full_size_image); - - /* We trust gdk-pixbuf to save the image correctly. - So if it fails we output a warning. */ + + /********************************* + * MUTEX UNLOCKED + *********************************/ + #ifdef DEBUG_THUMBNAILS - g_message ("Saving thumbnail to: %s\n", thumbnail_path); + g_message ("(Main Thread) Unlocking mutex\n"); #endif - if (!eel_gdk_pixbuf_save_to_file (scaled_image, - thumbnail_path)) { - success = FALSE; - g_warning ("error saving thumbnail %s", - thumbnail_path); - } - g_object_unref (scaled_image); - } else { - success = thumbnail_thread_run_convert (info, thumbnail_path); - } - - /* If we created the thumbnail successfully, set the mtime of the - thumbnail to match the image file, so we know when we need to - remake it. If we failed to create a thumbnail then remove the - thumbnail file and create a special file to flag that we tried - and failed. */ - if (success) { - thumbnail_thread_finish_thumbnail (info, thumbnail_uri, - thumbnail_path); - } else { - thumbnail_thread_cancel_thumbnail (info, thumbnail_uri, - thumbnail_path); - } - - /* We need to call nautilus_file_changed(), but I don't think that is - thread safe. So add an idle handler and do it from the main loop. */ - g_idle_add_full (G_PRIORITY_HIGH_IDLE, - thumbnail_thread_notify_file_changed, - g_strdup (info->image_uri), NULL); - - g_free (thumbnail_path); - g_free (thumbnail_uri); + pthread_mutex_unlock (&thumbnails_mutex); } - - /* thumbnail_thread is invoked as a separate thread to to make thumbnails. */ static gpointer thumbnail_thread_start (gpointer data) { NautilusThumbnailInfo *info = NULL; + GdkPixbuf *pixbuf; /* We loop until there are no more thumbails to make, at which point we exit the thread. */ @@ -1024,6 +424,25 @@ thumbnail_thread_start (gpointer data) g_message ("(Thumbnail Thread) Creating thumbnail: %s\n", info->image_uri); #endif - thumbnail_thread_make_thumbnail (info); + + pixbuf = gnome_thumbnail_factory_generate_thumbnail (thumbnail_factory, + info->image_uri, + info->mime_type); + + if (pixbuf) { + gnome_thumbnail_factory_save_thumbnail (thumbnail_factory, + pixbuf, + info->image_uri, + info->original_file_mtime); + } else { + gnome_thumbnail_factory_create_failed_thumbnail (thumbnail_factory, + info->image_uri, + info->original_file_mtime); + } + /* We need to call nautilus_file_changed(), but I don't think that is + thread safe. So add an idle handler and do it from the main loop. */ + g_idle_add_full (G_PRIORITY_HIGH_IDLE, + thumbnail_thread_notify_file_changed, + g_strdup (info->image_uri), NULL); } } |