diff options
25 files changed, 2948 insertions, 2437 deletions
@@ -1,3 +1,139 @@ +2002-09-18 Alexander Larsson <alexl@redhat.com> + + Merge the new-icon-factory-branch branch to HEAD. + +2002-09-18 Alexander Larsson <alexl@redhat.com> + + * libnautilus-private/gnome-icon-lookup.c: + * libnautilus-private/nautilus-bookmark.c: + * src/file-manager/fm-desktop-icon-view.c: + Final icon naming. + +2002-09-18 Alexander Larsson <alexl@redhat.com> + + * libnautilus-private/nautilus-icon-factory.c: + Compile fixes for gnome-desktop head. + +2002-09-17 Alexander Larsson <alexl@redhat.com> + + * libnautilus-private/gnome-icon-lookup.c: + Also look for generic mimetypes. + +2002-09-17 Alexander Larsson <alexl@redhat.com> + + * libnautilus-private/gnome-icon-lookup.c: + Clean up #defines + +2002-09-17 Alexander Larsson <alexl@redhat.com> + + * libnautilus-private/gnome-icon-lookup.c (gnome_icon_lookup): + Look up mime icons before file types. + +2002-09-17 Alexander Larsson <alexl@redhat.com> + + * libnautilus-private/Makefile.am: + Added SYSCONFDIR. + Removed nautilus-find-icon-image.[ch] + + * libnautilus-private/gnome-icon-lookup.c: + Add gif to gdk-pixbuf supported list. + + * libnautilus-private/gnome-thumbnail.c: + Add gif to gdk-pixbuf supported list. + Thumbnailing scripts + Fix failed thumbnail directory creation + + * libnautilus-private/nautilus-directory-async.c: + * libnautilus-private/nautilus-file-private.h: + * libnautilus-private/nautilus-file.c: + * libnautilus-private/nautilus-file.h: + * libnautilus-private/nautilus-icon-factory.c: + custom_icon_uri -> custom_icon + + * libnautilus-private/nautilus-thumbnails.c: + Write failed files on thumbnail failure. + +2002-09-13 Alexander Larsson <alexl@redhat.com> + + * libnautilus-private/gnome-thumbnail.[ch]: + const cleanups + Add gnome_thumbnail_has_uri. + + * libnautilus-private/nautilus-icon-container.c: + * libnautilus-private/nautilus-icon-private.h: + Clean up TODOs + + * libnautilus-private/nautilus-icon-factory-private.h: + Remove nautilus_icon_factory_remove_by_uri + + * libnautilus-private/nautilus-icon-factory.c: + Disable self checks by default + Fix memory management of cache and fallback_icon + Remove nautilus_icon_factory_remove_by_uri + Clean up old code leftovers + Use "loading" icon while thumbnailing. + + * libnautilus-private/nautilus-thumbnails.c: + Remove some old code + Reimplement nautilus_update_thumbnail_file_renamed and + nautilus_remove_thumbnail_for_file + +2002-09-12 Alexander Larsson <alexl@redhat.com> + + * libnautilus-private/gnome-thumbnail-pixbuf-utils.c: + Add fast jpeg loading code. + + * libnautilus-private/gnome-icon-lookup.[ch]: + Add GNOME_ICON_LOOKUP_FLAGS_SHOW_SMALL_IMAGES_AS_THEMSELVES flag. + + * libnautilus-private/gnome-thumbnail.c: + Threadsafe GnomeThumbnailFactory + Use fast jpeg loader + cleanup + Thumbnail more image formats. + + * libnautilus-private/nautilus-icon-container.c: + Don't scale wide but not very tall icons + + * libnautilus-private/nautilus-icon-factory.[ch]: + Better handling of scaling with unknown base_size + Frame thumbnails + Add NAUTILUS_ICON_SIZE_THUMBNAIL + + * libnautilus-private/nautilus-thumbnails.c: + Don't look for old framed thumbnails. + We changed thumbnailing system anyway. + +2002-09-11 Alexander Larsson <alexl@redhat.com> + + * libnautilus-private/Makefile.am: + * libnautilus-private/gnome-icon-lookup.c: + * libnautilus-private/gnome-icon-lookup.h: + * libnautilus-private/gnome-thumbnail-pixbuf-utils.c: + * libnautilus-private/gnome-thumbnail.c: + * libnautilus-private/gnome-thumbnail.h: + New files. Destined for libgnomeui eventually. + + * libnautilus-private/nautilus-icon-factory.c: + * libnautilus-private/nautilus-icon-factory.h: + * libnautilus-private/nautilus-thumbnails.c: + * libnautilus-private/nautilus-thumbnails.h: + Major rewrite of icon factory and thumbnailing code. + + * libnautilus-private/nautilus-bookmark.c: + * libnautilus-private/nautilus-bookmark.h: + * libnautilus-private/nautilus-file-private.h: + * libnautilus-private/nautilus-file.c: + * libnautilus-private/nautilus-file.h: + * libnautilus-private/nautilus-icon-container.c: + * libnautilus-private/nautilus-icon-container.h: + * libnautilus-private/nautilus-icon-private.h: + * src/file-manager/fm-icon-container.c: + * src/nautilus-bookmark-list.c: + * src/nautilus-bookmark-parsing.c: + * src/nautilus-sidebar-title.c: + Use the new APIs + 2002-09-15 Dave Camp <dave@ximian.com> Fixes bug #88596. diff --git a/libnautilus-private/Makefile.am b/libnautilus-private/Makefile.am index 3ac922548..e13bda83e 100644 --- a/libnautilus-private/Makefile.am +++ b/libnautilus-private/Makefile.am @@ -8,6 +8,7 @@ INCLUDES = \ $(CORE_CFLAGS) \ $(DISABLE_DEPRECATED_CFLAGS) \ -DDATADIR=\""$(datadir)"\" \ + -DSYSCONFDIR=\""$(sysconfdir)"\" \ -DNAUTILUS_DATADIR=\""$(datadir)/nautilus"\" \ $(NULL) @@ -38,6 +39,11 @@ libnautilus_private_la_SOURCES = \ $(nautilus_metafile_server_idl_sources) \ eggtreemultidnd.c \ eggtreemultidnd.h \ + gnome-icon-lookup.c \ + gnome-icon-lookup.h \ + gnome-thumbnail.c \ + gnome-thumbnail.h \ + gnome-thumbnail-pixbuf-utils.c \ nautilus-audio-player.c \ nautilus-audio-player.h \ nautilus-authn-manager.c \ @@ -85,8 +91,6 @@ libnautilus_private_la_SOURCES = \ nautilus-file-utilities.h \ nautilus-file.c \ nautilus-file.h \ - nautilus-find-icon-image.c \ - nautilus-find-icon-image.h \ nautilus-global-preferences.c \ nautilus-global-preferences.h \ nautilus-horizontal-splitter.c \ diff --git a/libnautilus-private/gnome-thumbnail-pixbuf-utils.c b/libnautilus-private/gnome-thumbnail-pixbuf-utils.c new file mode 100644 index 000000000..86efafb5f --- /dev/null +++ b/libnautilus-private/gnome-thumbnail-pixbuf-utils.c @@ -0,0 +1,464 @@ +/* + * gnome-thumbnail-pixbuf-utils.c: Utilities for handling pixbufs when thumbnailing + * + * Copyright (C) 2002 Red Hat, Inc. + * + * This file is part of the Gnome Library. + * + * The Gnome Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * The Gnome 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the Gnome Library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Alexander Larsson <alexl@redhat.com> + */ + +#include <config.h> + +#include <stdlib.h> +#include <string.h> +#include <glib.h> +#include <libgnomevfs/gnome-vfs-ops.h> +#include "gnome-thumbnail.h" + +#define LOAD_BUFFER_SIZE 65536 + +GdkPixbuf * _gnome_thumbnail_load_scaled_jpeg (const char *uri, + int target_width, + int target_height); + + +GdkPixbuf * +gnome_thumbnail_load_pixbuf (const char *uri) +{ + GnomeVFSResult result; + GnomeVFSHandle *handle; + char buffer[LOAD_BUFFER_SIZE]; + GnomeVFSFileSize bytes_read; + GdkPixbufLoader *loader; + GdkPixbuf *pixbuf; + + g_return_val_if_fail (uri != NULL, NULL); + + result = gnome_vfs_open (&handle, + uri, + GNOME_VFS_OPEN_READ); + if (result != GNOME_VFS_OK) { + return NULL; + } + + loader = gdk_pixbuf_loader_new (); + while (1) { + result = gnome_vfs_read (handle, + buffer, + sizeof (buffer), + &bytes_read); + if (result != GNOME_VFS_OK) { + break; + } + if (bytes_read == 0) { + break; + } + if (!gdk_pixbuf_loader_write (loader, + buffer, + bytes_read, + NULL)) { + result = GNOME_VFS_ERROR_WRONG_FORMAT; + break; + } + } + + if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_EOF) { + gdk_pixbuf_loader_close (loader, NULL); + g_object_unref (loader); + gnome_vfs_close (handle); + return NULL; + } + + gnome_vfs_close (handle); + gdk_pixbuf_loader_close (loader, NULL); + + pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + if (pixbuf != NULL) { + g_object_ref (pixbuf); + } + g_object_unref (loader); + + return pixbuf; +} + +GdkPixbuf * +gnome_thumbnail_scale_down_pixbuf (GdkPixbuf *pixbuf, + int dest_width, + int dest_height) +{ + int source_width, source_height; + int s_x1, s_y1, s_x2, s_y2; + int s_xfrac, s_yfrac; + int dx, dx_frac, dy, dy_frac; + div_t ddx, ddy; + int x, y; + int r, g, b, a; + int n_pixels; + gboolean has_alpha; + guchar *dest, *src, *xsrc, *src_pixels; + GdkPixbuf *dest_pixbuf; + int pixel_stride; + int source_rowstride, dest_rowstride; + + if (dest_width == 0 || dest_height == 0) { + return NULL; + } + + source_width = gdk_pixbuf_get_width (pixbuf); + source_height = gdk_pixbuf_get_height (pixbuf); + + g_assert (source_width >= dest_width); + g_assert (source_height >= dest_height); + + ddx = div (source_width, dest_width); + dx = ddx.quot; + dx_frac = ddx.rem; + + ddy = div (source_height, dest_height); + dy = ddy.quot; + dy_frac = ddy.rem; + + has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); + source_rowstride = gdk_pixbuf_get_rowstride (pixbuf); + src_pixels = gdk_pixbuf_get_pixels (pixbuf); + + dest_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, has_alpha, 8, + dest_width, dest_height); + dest = gdk_pixbuf_get_pixels (dest_pixbuf); + dest_rowstride = gdk_pixbuf_get_rowstride (dest_pixbuf); + + pixel_stride = (has_alpha)?4:3; + + s_y1 = 0; + s_yfrac = -dest_height/2; + while (s_y1 < source_height) { + s_y2 = s_y1 + dy; + s_yfrac += dy_frac; + if (s_yfrac > 0) { + s_y2++; + s_yfrac -= dest_height; + } + + s_x1 = 0; + s_xfrac = -dest_width/2; + while (s_x1 < source_width) { + s_x2 = s_x1 + dx; + s_xfrac += dx_frac; + if (s_xfrac > 0) { + s_x2++; + s_xfrac -= dest_width; + } + + /* Average block of [x1,x2[ x [y1,y2[ and store in dest */ + r = g = b = a = 0; + n_pixels = 0; + + src = src_pixels + s_y1 * source_rowstride + s_x1 * pixel_stride; + for (y = s_y1; y < s_y2; y++) { + xsrc = src; + if (has_alpha) { + for (x = 0; x < s_x2-s_x1; x++) { + n_pixels++; + + r += xsrc[3] * xsrc[0]; + g += xsrc[3] * xsrc[1]; + b += xsrc[3] * xsrc[2]; + a += xsrc[3]; + xsrc += 4; + } + } else { + for (x = 0; x < s_x2-s_x1; x++) { + n_pixels++; + r += *xsrc++; + g += *xsrc++; + b += *xsrc++; + } + } + src += source_rowstride; + } + + if (has_alpha) { + if (a != 0) { + *dest++ = r / a; + *dest++ = g / a; + *dest++ = b / a; + *dest++ = a / n_pixels; + } else { + *dest++ = 0; + *dest++ = 0; + *dest++ = 0; + *dest++ = 0; + } + } else { + *dest++ = r / n_pixels; + *dest++ = g / n_pixels; + *dest++ = b / n_pixels; + } + + s_x1 = s_x2; + } + s_y1 = s_y2; + dest += dest_rowstride - dest_width * pixel_stride; + } + + return dest_pixbuf; +} + + + +#ifdef HAVE_LIBJPEG + +#include <setjmp.h> + +#include <stdio.h> + +/* Workaround broken libjpeg defining these that may + * collide w/ the ones in config.h + */ +#undef HAVE_STDDEF_H +#undef HAVE_STDLIB_H +#include <jpeglib.h> + +#include <libgnomevfs/gnome-vfs-types.h> +#include <libgnomevfs/gnome-vfs-ops.h> +#include <libgnomevfs/gnome-vfs-utils.h> + +#define BUFFER_SIZE 16384 + +typedef struct { + struct jpeg_source_mgr pub; /* public fields */ + GnomeVFSHandle *handle; + JOCTET buffer[BUFFER_SIZE]; +} Source; + +typedef struct { + struct jpeg_error_mgr pub; + jmp_buf setjmp_buffer; +} ErrorHandlerData; + +static void +fatal_error_handler (j_common_ptr cinfo) +{ + ErrorHandlerData *data; + + data = (ErrorHandlerData *) cinfo->err; + longjmp (data->setjmp_buffer, 1); +} + +static void +output_message_handler (j_common_ptr cinfo) +{ + /* If we don't supply this handler, libjpeg reports errors + * directly to stderr. + */ +} + +static void +init_source (j_decompress_ptr cinfo) +{ +} + +static gboolean +fill_input_buffer (j_decompress_ptr cinfo) +{ + Source *src; + GnomeVFSFileSize nbytes; + GnomeVFSResult result; + + src = (Source *) cinfo->src; + result = gnome_vfs_read (src->handle, + src->buffer, + G_N_ELEMENTS (src->buffer), + &nbytes); + + if (result != GNOME_VFS_OK || nbytes == 0) { + /* return a fake EOI marker so we will eventually terminate */ + src->buffer[0] = (JOCTET) 0xFF; + src->buffer[1] = (JOCTET) JPEG_EOI; + nbytes = 2; + } + + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = nbytes; + + return TRUE; +} + +static void +skip_input_data (j_decompress_ptr cinfo, long num_bytes) +{ + Source *src; + + src = (Source *) cinfo->src; + if (num_bytes > 0) { + while (num_bytes > (long) src->pub.bytes_in_buffer) { + num_bytes -= (long) src->pub.bytes_in_buffer; + fill_input_buffer (cinfo); + } + src->pub.next_input_byte += (size_t) num_bytes; + src->pub.bytes_in_buffer -= (size_t) num_bytes; + } +} + +static void +term_source (j_decompress_ptr cinfo) +{ +} + +static void +vfs_src (j_decompress_ptr cinfo, GnomeVFSHandle *handle) +{ + Source *src; + + if (cinfo->src == NULL) { /* first time for this JPEG object? */ + cinfo->src = &(g_new (Source, 1))->pub; + } + + src = (Source *) cinfo->src; + src->pub.init_source = init_source; + src->pub.fill_input_buffer = fill_input_buffer; + src->pub.skip_input_data = skip_input_data; + src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ + src->pub.term_source = term_source; + src->handle = handle; + src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ + src->pub.next_input_byte = NULL; /* until buffer loaded */ +} + +static int +calculate_divisor (int width, + int height, + int target_width, + int target_height) +{ + if (width/8 > target_width && height/8 > target_height) { + return 8; + } + if (width/4 > target_width && height/4 > target_height) { + return 4; + } + if (width/2 > target_width && height/2 > target_height) { + return 2; + } + return 1; +} + +static void +free_buffer (guchar *pixels, gpointer data) +{ + g_free (pixels); +} + +GdkPixbuf * +_gnome_thumbnail_load_scaled_jpeg (const char *uri, + int target_width, + int target_height) +{ + struct jpeg_decompress_struct cinfo; + ErrorHandlerData jerr; + GnomeVFSHandle *handle; + unsigned char *lines[1]; + guchar * volatile buffer; + guchar * volatile pixels; + guchar *ptr; + GnomeVFSResult result; + unsigned int i; + + result = gnome_vfs_open (&handle, + uri, + GNOME_VFS_OPEN_READ); + if (result != GNOME_VFS_OK) { + return NULL; + } + + cinfo.err = jpeg_std_error (&jerr.pub); + jerr.pub.error_exit = fatal_error_handler; + jerr.pub.output_message = output_message_handler; + + buffer = NULL; + pixels = NULL; + if (setjmp (jerr.setjmp_buffer)) { + /* Handle a JPEG error. */ + jpeg_destroy_decompress (&cinfo); + gnome_vfs_close (handle); + g_free (buffer); + g_free (pixels); + return NULL; + } + + jpeg_create_decompress (&cinfo); + + vfs_src (&cinfo, handle); + + jpeg_read_header (&cinfo, TRUE); + + cinfo.scale_num = 1; + cinfo.scale_denom = calculate_divisor (cinfo.image_width, + cinfo.image_height, + target_width, + target_height); + cinfo.dct_method = JDCT_FASTEST; + cinfo.do_fancy_upsampling = FALSE; + + jpeg_start_decompress (&cinfo); + + pixels = g_malloc (cinfo.output_width * cinfo.output_height * 3); + + ptr = pixels; + if (cinfo.num_components == 1) { + /* Allocate extra buffer for grayscale data */ + buffer = g_malloc (cinfo.output_width); + lines[0] = buffer; + } else { + lines[0] = pixels; + } + + while (cinfo.output_scanline < cinfo.output_height) { + jpeg_read_scanlines (&cinfo, lines, 1); + + if (cinfo.num_components == 1) { + /* Convert grayscale to rgb */ + for (i = 0; i < cinfo.output_width; i++) { + ptr[i*3] = buffer[i]; + ptr[i*3+1] = buffer[i]; + ptr[i*3+2] = buffer[i]; + } + ptr += cinfo.output_width * 3; + } else { + lines[0] += cinfo.output_width * 3; + } + } + + g_free (buffer); + buffer = NULL; + + jpeg_finish_decompress (&cinfo); + jpeg_destroy_decompress (&cinfo); + + gnome_vfs_close (handle); + + return gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, FALSE, 8, + cinfo.output_width, + cinfo.output_height, + cinfo.output_width * 3, + free_buffer, NULL); +} + +#endif /* HAVE_LIBJPEG */ + diff --git a/libnautilus-private/gnome-thumbnail.c b/libnautilus-private/gnome-thumbnail.c new file mode 100644 index 000000000..86b7bc200 --- /dev/null +++ b/libnautilus-private/gnome-thumbnail.c @@ -0,0 +1,1368 @@ +/* + * gnome-thumbnail.c: Utilities for handling thumbnails + * + * Copyright (C) 2002 Red Hat, Inc. + * + * This file is part of the Gnome Library. + * + * The Gnome Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * The Gnome 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the Gnome Library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Alexander Larsson <alexl@redhat.com> + */ + +#include <config.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <unistd.h> +#include <stdlib.h> +#include <dirent.h> +#include <time.h> +#include <math.h> +#include <string.h> +#include <glib.h> +#include <stdio.h> +#include <pthread.h> +#include <libgnome/gnome-macros.h> +#include <libgnome/gnome-init.h> +#include <libgnomevfs/gnome-vfs-utils.h> +#include "gnome-thumbnail.h" + +#ifdef HAVE_LIBJPEG +GdkPixbuf * _gnome_thumbnail_load_scaled_jpeg (const char *uri, + int target_width, + int target_height); +#endif + +#define SECONDS_BETWEEN_STATS 10 + +struct ThumbMD5Context { + guint32 buf[4]; + guint32 bits[2]; + unsigned char in[64]; +}; + +static void thumb_md5 (const char *string, unsigned char digest[16]); + +struct _GnomeThumbnailFactoryPrivate { + char *application; + GnomeThumbnailSize size; + + GHashTable *existing_thumbs; + time_t read_existing_mtime; + long last_existing_time; + + GHashTable *failed_thumbs; + time_t read_failed_mtime; + long last_failed_time; + + pthread_mutex_t lock; + + GHashTable *scripts_hash; +}; + +struct ThumbnailInfo { + time_t mtime; + char *uri; +}; + +GNOME_CLASS_BOILERPLATE (GnomeThumbnailFactory, + gnome_thumbnail_factory, + GObject, G_TYPE_OBJECT); + +static void gnome_thumbnail_factory_instance_init (GnomeThumbnailFactory *factory); +static void gnome_thumbnail_factory_class_init (GnomeThumbnailFactoryClass *class); + + +static void +thumbnail_info_free (gpointer data) +{ + struct ThumbnailInfo *info = data; + + if (info) + { + g_free (info->uri); + g_free (info); + } +} + +static void +gnome_thumbnail_factory_finalize (GObject *object) +{ + GnomeThumbnailFactory *factory; + GnomeThumbnailFactoryPrivate *priv; + + factory = GNOME_THUMBNAIL_FACTORY (object); + + priv = factory->priv; + + g_free (priv->application); + priv->application = NULL; + + if (priv->existing_thumbs) + { + g_hash_table_destroy (priv->existing_thumbs); + priv->existing_thumbs = NULL; + } + + if (priv->failed_thumbs) + { + g_hash_table_destroy (priv->failed_thumbs); + priv->failed_thumbs = NULL; + } + + if (priv->scripts_hash) + { + g_hash_table_destroy (priv->scripts_hash); + priv->scripts_hash = NULL; + } + + g_free (priv); + factory->priv = NULL; + + if (G_OBJECT_CLASS (parent_class)->finalize) + (* G_OBJECT_CLASS (parent_class)->finalize) (object); +} + +static guint +md5_hash (gconstpointer key) +{ + const char *digest = key; + + return *(guint *)digest; +} + +static gboolean +md5_equal (gconstpointer a, + gconstpointer b) +{ + const char *digest_a = a; + const char *digest_b = b; + int i; + + for (i = 0; i < 16; i++) + { + if (digest_a[i] != digest_b[i]) + return FALSE; + } + + return TRUE; +} + +static void +read_scripts_file (GnomeThumbnailFactory *factory, const char *file) +{ + FILE *f; + char buf[1024]; + char *p; + gchar **mime_types; + int i; + + f = fopen (file, "r"); + + if (f) + { + while (fgets (buf, 1024, f) != NULL) + { + if (buf[0] == '#') + continue; + + p = strchr (buf, ':'); + + if (p == NULL) + continue; + + *p++ = 0; + while (g_ascii_isspace (*p)) + p++; + + mime_types = g_strsplit (buf, ",", 0); + + for (i = 0; mime_types[i] != NULL; i++) + g_hash_table_insert (factory->priv->scripts_hash, + mime_types[i], g_strdup (p)); + + /* The mimetype strings are owned by the hash table now */ + g_free (mime_types); + } + fclose (f); + } +} + +static void +read_scripts (GnomeThumbnailFactory *factory) +{ + char *file; + + read_scripts_file (factory, SYSCONFDIR "/gnome/thumbnailrc"); + + file = g_build_filename (g_get_home_dir (), + GNOME_DOT_GNOME, + "thumbnailrc", + NULL); + read_scripts_file (factory, file); + g_free (file); +} + +static void +gnome_thumbnail_factory_instance_init (GnomeThumbnailFactory *factory) +{ + factory->priv = g_new0 (GnomeThumbnailFactoryPrivate, 1); + + factory->priv->size = GNOME_THUMBNAIL_SIZE_NORMAL; + factory->priv->application = g_strdup ("gnome-thumbnail-factory"); + + factory->priv->existing_thumbs = g_hash_table_new_full (md5_hash, + md5_equal, + g_free, thumbnail_info_free); + factory->priv->failed_thumbs = g_hash_table_new_full (md5_hash, + md5_equal, + g_free, NULL); + + factory->priv->scripts_hash = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, g_free); + + + read_scripts (factory); + + pthread_mutex_init (&factory->priv->lock, NULL); +} + +static void +gnome_thumbnail_factory_class_init (GnomeThumbnailFactoryClass *class) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (class); + + gobject_class->finalize = gnome_thumbnail_factory_finalize; +} + +GnomeThumbnailFactory * +gnome_thumbnail_factory_new (GnomeThumbnailSize size) +{ + GnomeThumbnailFactory *factory; + + factory = g_object_new (GNOME_TYPE_THUMBNAIL_FACTORY, NULL); + + factory->priv->size = size; + + return factory; +} + +static char * +thumb_digest_to_ascii (unsigned char digest[16]) +{ + static char hex_digits[] = "0123456789abcdef"; + unsigned char *res; + int i; + + res = g_malloc (33); + + for (i = 0; i < 16; i++) { + res[2*i] = hex_digits[digest[i] >> 4]; + res[2*i+1] = hex_digits[digest[i] & 0xf]; + } + + res[32] = 0; + + return res; +} + +static void +thumb_digest_from_ascii (unsigned char *ascii, unsigned char digest[16]) +{ + int i; + + for (i = 0; i < 16; i++) + { + digest[i] = + g_ascii_xdigit_value (ascii[2*i]) << 4 | + g_ascii_xdigit_value (ascii[2*i + 1]); + } +} + +static gboolean +remove_all (void) +{ + return TRUE; +} + +static void +read_md5_dir (const char *path, GHashTable *hash_table) +{ + DIR *dir; + struct dirent *dirent; + char *digest; + + /* Remove all current thumbs */ + g_hash_table_foreach_remove (hash_table, + (GHRFunc) remove_all, + NULL); + + dir = opendir (path); + + if (dir) + { + while ((dirent = readdir (dir)) != NULL) + { + if (strlen (dirent->d_name) == 36 && + strcmp (dirent->d_name + 32, ".png") == 0) + { + digest = g_malloc (16); + thumb_digest_from_ascii (dirent->d_name, digest); + g_hash_table_insert (hash_table, digest, NULL); + } + } + closedir (dir); + } +} + +static void +gnome_thumbnail_factory_ensure_uptodate (GnomeThumbnailFactory *factory) +{ + char *path; + struct timeval tv; + struct stat statbuf; + GnomeThumbnailFactoryPrivate *priv = factory->priv; + + if (priv->last_existing_time != 0) + { + gettimeofday (&tv, NULL); + + if (tv.tv_sec >= priv->last_existing_time && + tv.tv_sec < priv->last_existing_time + SECONDS_BETWEEN_STATS) + return; + } + + path = g_build_filename (g_get_home_dir (), + ".thumbnails", + (priv->size == GNOME_THUMBNAIL_SIZE_NORMAL)?"normal":"large", + NULL); + + if (stat(path, &statbuf) != 0) + { + g_free (path); + return; + } + + if (statbuf.st_mtime == priv->read_existing_mtime) + { + g_free (path); + return; + } + + priv->read_existing_mtime = statbuf.st_mtime; + priv->last_existing_time = tv.tv_sec; + + read_md5_dir (path, priv->existing_thumbs); + + g_free (path); +} + +static struct ThumbnailInfo * +load_thumbnail_info (const char *path) +{ + struct ThumbnailInfo *info; + GdkPixbuf *pixbuf; + const char *thumb_uri, *thumb_mtime_str; + + pixbuf = gdk_pixbuf_new_from_file (path, NULL); + + if (pixbuf == NULL) + return NULL; + + info = g_new0 (struct ThumbnailInfo, 1); + + thumb_uri = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::URI"); + info->uri = g_strdup (thumb_uri); + + thumb_mtime_str = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::MTime"); + info->mtime = atol (thumb_mtime_str); + + g_object_unref (pixbuf); + + return info; +} + +static void +gnome_thumbnail_factory_ensure_failed_uptodate (GnomeThumbnailFactory *factory) +{ + char *path; + struct timeval tv; + struct stat statbuf; + GnomeThumbnailFactoryPrivate *priv = factory->priv; + + if (priv->last_failed_time != 0) + { + gettimeofday (&tv, NULL); + + if (tv.tv_sec >= priv->last_failed_time && + tv.tv_sec < priv->last_failed_time + SECONDS_BETWEEN_STATS) + return; + } + + path = g_build_filename (g_get_home_dir (), + ".thumbnails/fail/", + factory->priv->application, + NULL); + + if (stat(path, &statbuf) != 0) + { + g_free (path); + return; + } + + if (statbuf.st_mtime == priv->read_failed_mtime) + { + g_free (path); + return; + } + + priv->read_failed_mtime = statbuf.st_mtime; + priv->last_failed_time = tv.tv_sec; + + read_md5_dir (path, priv->failed_thumbs); + + g_free (path); +} + +char * +gnome_thumbnail_factory_lookup (GnomeThumbnailFactory *factory, + const char *uri, + time_t mtime) +{ + GnomeThumbnailFactoryPrivate *priv = factory->priv; + unsigned char digest[16]; + char *path, *md5, *file; + gpointer value; + struct ThumbnailInfo *info; + + pthread_mutex_lock (&priv->lock); + + gnome_thumbnail_factory_ensure_uptodate (factory); + + thumb_md5 (uri, digest); + + if (g_hash_table_lookup_extended (priv->existing_thumbs, + digest, NULL, &value)) + { + md5 = thumb_digest_to_ascii (digest); + file = g_strconcat (md5, ".png", NULL); + g_free (md5); + + path = g_build_filename (g_get_home_dir (), + ".thumbnails", + (priv->size == GNOME_THUMBNAIL_SIZE_NORMAL)?"normal":"large", + file, + NULL); + g_free (file); + + if (value == NULL) + { + info = load_thumbnail_info (path); + if (info) + { + unsigned char *key; + key = g_malloc (16); + memcpy (key, digest, 16); + g_hash_table_insert (priv->existing_thumbs, key, info); + } + } + else + info = value; + + if (info && + info->mtime == mtime && + strcmp (info->uri, uri) == 0) + { + pthread_mutex_unlock (&priv->lock); + return path; + } + + g_free (path); + } + + pthread_mutex_unlock (&priv->lock); + + return NULL; +} + +gboolean +gnome_thumbnail_factory_has_valid_failed_thumbnail (GnomeThumbnailFactory *factory, + const char *uri, + time_t mtime) +{ + GnomeThumbnailFactoryPrivate *priv = factory->priv; + unsigned char digest[16]; + char *path, *file, *md5; + GdkPixbuf *pixbuf; + gboolean res; + + res = FALSE; + + pthread_mutex_lock (&priv->lock); + + gnome_thumbnail_factory_ensure_failed_uptodate (factory); + + thumb_md5 (uri, digest); + + if (g_hash_table_lookup_extended (factory->priv->failed_thumbs, + digest, NULL, NULL)) + { + md5 = thumb_digest_to_ascii (digest); + file = g_strconcat (md5, ".png", NULL); + g_free (md5); + + path = g_build_filename (g_get_home_dir (), + ".thumbnails/fail", + factory->priv->application, + file, + NULL); + g_free (file); + + pixbuf = gdk_pixbuf_new_from_file (path, NULL); + g_free (path); + + if (pixbuf) + { + res = gnome_thumbnail_is_valid (pixbuf, uri, mtime); + g_object_unref (pixbuf); + } + } + + pthread_mutex_unlock (&priv->lock); + + return res; +} + +static gboolean +mimetype_supported_by_gdk_pixbuf (const char *mime_type) +{ + guint i; + static GHashTable *formats = NULL; + static const char *types [] = { + "image/x-bmp", "image/x-ico", "image/jpeg", "image/gif", + "image/png", "image/pnm", "image/ras", "image/tga", + "image/tiff", "image/wbmp", "image/x-xbitmap", + "image/x-xpixmap" + }; + + if (!formats) { + formats = g_hash_table_new (g_str_hash, g_str_equal); + + for (i = 0; i < G_N_ELEMENTS (types); i++) + g_hash_table_insert (formats, + (gpointer) types [i], + GUINT_TO_POINTER (1)); + } + + if (g_hash_table_lookup (formats, mime_type)) + return TRUE; + + return FALSE; +} + + +gboolean +gnome_thumbnail_factory_can_thumbnail (GnomeThumbnailFactory *factory, + const char *uri, + const char *mime_type, + time_t mtime) +{ + /* Don't thumbnail thumbnails */ + if (uri && + strncmp (uri, "file:/", 6) == 0 && + strstr (uri, "/.thumbnails/") != NULL) + return FALSE; + + /* TODO: Replace with generic system */ + if (mime_type != NULL && + (mimetype_supported_by_gdk_pixbuf (mime_type) || + g_hash_table_lookup (factory->priv->scripts_hash, mime_type))) + { + return !gnome_thumbnail_factory_has_valid_failed_thumbnail (factory, + uri, + mtime); + } + + return FALSE; +} + +static char * +expand_thumbnailing_script (const char *script, + const char *inuri, + const char *outfile) +{ + GString *str; + const char *p, *last; + char *localfile, *quoted; + gboolean got_in; + + str = g_string_new (NULL); + + got_in = FALSE; + last = script; + while ((p = strchr (last, '%')) != NULL) + { + g_string_append_len (str, last, p - last); + p++; + + switch (*p) { + case 'u': + quoted = g_shell_quote (inuri); + g_string_append (str, quoted); + g_free (quoted); + got_in = TRUE; + p++; + break; + case 'i': + localfile = gnome_vfs_get_local_path_from_uri (inuri); + if (localfile) + { + quoted = g_shell_quote (localfile); + g_string_append (str, quoted); + got_in = TRUE; + g_free (quoted); + g_free (localfile); + } + p++; + break; + case 'o': + quoted = g_shell_quote (outfile); + g_string_append (str, quoted); + g_free (quoted); + p++; + break; + case 's': + g_string_append (str, "128"); + p++; + break; + case '%': + g_string_append_c (str, '%'); + p++; + break; + case 0: + default: + break; + } + last = p; + } + g_string_append (str, last); + + if (got_in) + return g_string_free (str, FALSE); + + g_string_free (str, TRUE); + return NULL; +} + + +GdkPixbuf * +gnome_thumbnail_factory_generate_thumbnail (GnomeThumbnailFactory *factory, + const char *uri, + const char *mime_type) +{ + GdkPixbuf *pixbuf, *scaled; + char *script, *expanded_script; + int width, height, size; + double scale; + int exit_status; + char tmpname[50]; + + /* Doesn't access any volatile fields in factory, so it's threadsafe */ + + size = 128; + if (factory->priv->size == GNOME_THUMBNAIL_SIZE_LARGE) + size = 256; + + pixbuf = NULL; + + script = g_hash_table_lookup (factory->priv->scripts_hash, mime_type); + if (script) + { + int fd; + + strcpy (tmpname, "/tmp/.gnome_thumbnail.XXXXXX"); + + fd = mkstemp(tmpname); + + if (fd) + { + close (fd); + + expanded_script = expand_thumbnailing_script (script, uri, tmpname); + if (expanded_script != NULL && + g_spawn_command_line_sync (expanded_script, + NULL, NULL, &exit_status, NULL) && + exit_status == 0) + { + pixbuf = gdk_pixbuf_new_from_file (tmpname, NULL); + g_free (expanded_script); + } + + unlink(tmpname); + } + } + + /* Fall back to gdk-pixbuf */ + if (pixbuf == NULL) + { +#ifdef HAVE_LIBJPEG + if (strcmp (mime_type, "image/jpeg") == 0) + pixbuf = _gnome_thumbnail_load_scaled_jpeg (uri, size, size); + else +#endif + pixbuf = gnome_thumbnail_load_pixbuf (uri); + } + + if (pixbuf == NULL) + return NULL; + + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + + if (width > size || height > size) + { + scale = (double)size / MAX (width, height); + + scaled = gnome_thumbnail_scale_down_pixbuf (pixbuf, + floor (width * scale + 0.5), + floor (height * scale + 0.5)); + + g_object_unref (pixbuf); + pixbuf = scaled; + } + + return pixbuf; +} + +static gboolean +make_thumbnail_dirs (GnomeThumbnailFactory *factory) +{ + char *thumbnail_dir; + char *image_dir; + gboolean res; + + res = FALSE; + + thumbnail_dir = g_build_filename (g_get_home_dir (), + ".thumbnails", + NULL); + if (!g_file_test (thumbnail_dir, G_FILE_TEST_IS_DIR)) + { + mkdir (thumbnail_dir, 0700); + res = TRUE; + } + + image_dir = g_build_filename (thumbnail_dir, + (factory->priv->size == GNOME_THUMBNAIL_SIZE_NORMAL)?"normal":"large", + NULL); + if (!g_file_test (image_dir, G_FILE_TEST_IS_DIR)) + { + mkdir (image_dir, 0700); + res = TRUE; + } + + g_free (thumbnail_dir); + g_free (image_dir); + + return res; +} + +static gboolean +make_thumbnail_fail_dirs (GnomeThumbnailFactory *factory) +{ + char *thumbnail_dir; + char *fail_dir; + char *app_dir; + gboolean res; + + res = FALSE; + + thumbnail_dir = g_build_filename (g_get_home_dir (), + ".thumbnails", + NULL); + if (!g_file_test (thumbnail_dir, G_FILE_TEST_IS_DIR)) + { + mkdir (thumbnail_dir, 0700); + res = TRUE; + } + + fail_dir = g_build_filename (thumbnail_dir, + "fail", + NULL); + if (!g_file_test (fail_dir, G_FILE_TEST_IS_DIR)) + { + mkdir (fail_dir, 0700); + res = TRUE; + } + + app_dir = g_build_filename (fail_dir, + factory->priv->application, + NULL); + if (!g_file_test (app_dir, G_FILE_TEST_IS_DIR)) + { + mkdir (app_dir, 0700); + res = TRUE; + } + + g_free (thumbnail_dir); + g_free (fail_dir); + g_free (app_dir); + + return res; +} + + +void +gnome_thumbnail_factory_save_thumbnail (GnomeThumbnailFactory *factory, + GdkPixbuf *thumbnail, + const char *uri, + time_t original_mtime) +{ + GnomeThumbnailFactoryPrivate *priv = factory->priv; + unsigned char *digest; + char *path, *md5, *file, *dir; + char *tmp_path; + int tmp_fd; + char mtime_str[21]; + gboolean saved_ok; + struct stat statbuf; + struct ThumbnailInfo *info; + + pthread_mutex_lock (&priv->lock); + + gnome_thumbnail_factory_ensure_uptodate (factory); + + pthread_mutex_unlock (&priv->lock); + + digest = g_malloc (16); + thumb_md5 (uri, digest); + + md5 = thumb_digest_to_ascii (digest); + file = g_strconcat (md5, ".png", NULL); + g_free (md5); + + dir = g_build_filename (g_get_home_dir (), + ".thumbnails", + (priv->size == GNOME_THUMBNAIL_SIZE_NORMAL)?"normal":"large", + NULL); + + path = g_build_filename (dir, + file, + NULL); + g_free (file); + + tmp_path = g_strconcat (path, ".XXXXXX", NULL); + + tmp_fd = mkstemp (tmp_path); + if (tmp_fd == -1 && + make_thumbnail_dirs (factory)) + { + g_free (tmp_path); + tmp_path = g_strconcat (path, ".XXXXXX", NULL); + tmp_fd = mkstemp (tmp_path); + } + + if (tmp_fd == -1) + { + gnome_thumbnail_factory_create_failed_thumbnail (factory, uri, original_mtime); + g_free (dir); + g_free (tmp_path); + g_free (path); + g_free (digest); + return; + } + close (tmp_fd); + + g_snprintf (mtime_str, 21, "%lu", original_mtime); + saved_ok = gdk_pixbuf_save (thumbnail, + tmp_path, + "png", NULL, + "tEXt::Thumb::URI", uri, + "tEXt::Thumb::MTime", mtime_str, + "tEXt::Software", "GNOME::ThumbnailFactory", + NULL); + if (saved_ok) + { + chmod (tmp_path, 0600); + rename(tmp_path, path); + + info = g_new (struct ThumbnailInfo, 1); + info->mtime = original_mtime; + info->uri = g_strdup (uri); + + pthread_mutex_lock (&priv->lock); + + g_hash_table_insert (factory->priv->existing_thumbs, digest, info); + /* Make sure we don't re-read the directory. We should be uptodate + * with all previous changes du to the ensure_uptodate above. + * There is still a small window here where we might miss exisiting + * thumbnails, but that shouldn't matter. (we would just redo them or + * catch them later). + */ + if (stat(dir, &statbuf) == 0) + factory->priv->read_existing_mtime = statbuf.st_mtime; + + pthread_mutex_unlock (&priv->lock); + } + else + { + g_free (digest); + gnome_thumbnail_factory_create_failed_thumbnail (factory, uri, original_mtime); + } + + g_free (dir); + g_free (path); + g_free (tmp_path); +} + +void +gnome_thumbnail_factory_create_failed_thumbnail (GnomeThumbnailFactory *factory, + const char *uri, + time_t mtime) +{ + GnomeThumbnailFactoryPrivate *priv = factory->priv; + unsigned char *digest; + char *path, *md5, *file, *dir; + char *tmp_path; + int tmp_fd; + char mtime_str[21]; + gboolean saved_ok; + struct stat statbuf; + GdkPixbuf *pixbuf; + + pthread_mutex_lock (&priv->lock); + + gnome_thumbnail_factory_ensure_failed_uptodate (factory); + + pthread_mutex_unlock (&priv->lock); + + digest = g_malloc (16); + thumb_md5 (uri, digest); + + md5 = thumb_digest_to_ascii (digest); + file = g_strconcat (md5, ".png", NULL); + g_free (md5); + + dir = g_build_filename (g_get_home_dir (), + ".thumbnails/fail", + factory->priv->application, + NULL); + + path = g_build_filename (dir, + file, + NULL); + g_free (file); + + tmp_path = g_strconcat (path, ".XXXXXX", NULL); + + tmp_fd = mkstemp (tmp_path); + if (tmp_fd == -1 && + make_thumbnail_fail_dirs (factory)) + { + g_free (tmp_path); + tmp_path = g_strconcat (path, ".XXXXXX", NULL); + tmp_fd = mkstemp (tmp_path); + } + + if (tmp_fd == -1) + { + g_free (dir); + g_free (tmp_path); + g_free (path); + g_free (digest); + return; + } + close (tmp_fd); + + g_snprintf (mtime_str, 21, "%lu", mtime); + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 1, 1); + saved_ok = gdk_pixbuf_save (pixbuf, + tmp_path, + "png", NULL, + "tEXt::Thumb::URI", uri, + "tEXt::Thumb::MTime", mtime_str, + "tEXt::Software", "GNOME::ThumbnailFactory", + NULL); + g_object_unref (pixbuf); + if (saved_ok) + { + chmod (tmp_path, 0600); + rename(tmp_path, path); + + pthread_mutex_lock (&priv->lock); + + g_hash_table_insert (factory->priv->failed_thumbs, digest, NULL); + /* Make sure we don't re-read the directory. We should be uptodate + * with all previous changes du to the ensure_uptodate above. + * There is still a small window here where we might miss exisiting + * thumbnails, but that shouldn't matter. (we would just redo them or + * catch them later). + */ + if (stat(dir, &statbuf) == 0) + factory->priv->read_failed_mtime = statbuf.st_mtime; + + pthread_mutex_unlock (&priv->lock); + } + else + g_free (digest); + + g_free (dir); + g_free (path); + g_free (tmp_path); +} + +char * +gnome_thumbnail_md5 (const char *uri) +{ + unsigned char digest[16]; + + thumb_md5 (uri, digest); + return thumb_digest_to_ascii (digest); +} + +char * +gnome_thumbnail_path_for_uri (const char *uri, + GnomeThumbnailSize size) +{ + char *md5; + char *file; + char *path; + + md5 = gnome_thumbnail_md5 (uri); + file = g_strconcat (md5, ".png", NULL); + g_free (md5); + + path = g_build_filename (g_get_home_dir (), + ".thumbnails", + (size == GNOME_THUMBNAIL_SIZE_NORMAL)?"normal":"large", + file, + NULL); + + g_free (file); + + return path; +} + +gboolean +gnome_thumbnail_has_uri (GdkPixbuf *pixbuf, + const char *uri) +{ + const char *thumb_uri; + + thumb_uri = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::URI"); + return strcmp (uri, thumb_uri) == 0; +} + +gboolean +gnome_thumbnail_is_valid (GdkPixbuf *pixbuf, + const char *uri, + time_t mtime) +{ + const char *thumb_uri, *thumb_mtime_str; + time_t thumb_mtime; + + thumb_uri = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::URI"); + if (strcmp (uri, thumb_uri) != 0) + return FALSE; + + thumb_mtime_str = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::MTime"); + thumb_mtime = atol (thumb_mtime_str); + if (mtime != thumb_mtime) + return FALSE; + + return TRUE; +} + + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * ThumbMD5Context structure, pass it to thumb_md5_init, call + * thumb_md5_update as needed on buffers full of bytes, and then call + * thumb_md5_final, which will fill a supplied 32-byte array with the + * digest in ascii form. + * + */ + +static void thumb_md5_init (struct ThumbMD5Context *context); +static void thumb_md5_update (struct ThumbMD5Context *context, + unsigned char const *buf, + unsigned len); +static void thumb_md5_final (unsigned char digest[16], + struct ThumbMD5Context *context); +static void thumb_md5_transform (guint32 buf[4], + guint32 const in[16]); + + +static void +thumb_md5 (const char *string, unsigned char digest[16]) +{ + struct ThumbMD5Context md5_context; + + thumb_md5_init (&md5_context); + thumb_md5_update (&md5_context, string, strlen (string)); + thumb_md5_final (digest, &md5_context); +} + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN +#define byteReverse(buf, len) /* Nothing */ +#else + +/* + * Note: this code is harmless on little-endian machines. + */ +static void +byteReverse(unsigned char *buf, unsigned longs) +{ + guint32 t; + do { + t = (guint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(guint32 *) buf = t; + buf += 4; + } while (--longs); +} + +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +static void +thumb_md5_init (struct ThumbMD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +static void +thumb_md5_update (struct ThumbMD5Context *ctx, + unsigned char const *buf, + unsigned len) +{ + guint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((guint32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memcpy (p, buf, len); + return; + } + memcpy (p, buf, t); + byteReverse (ctx->in, 16); + thumb_md5_transform (ctx->buf, (guint32 *) ctx->in); + buf += t; + len -= t; + } + + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy (ctx->in, buf, 64); + byteReverse (ctx->in, 16); + thumb_md5_transform (ctx->buf, (guint32 *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +static void +thumb_md5_final (unsigned char digest[16], struct ThumbMD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset (p, 0, count); + byteReverse (ctx->in, 16); + thumb_md5_transform (ctx->buf, (guint32 *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((guint32 *) ctx->in)[14] = ctx->bits[0]; + ((guint32 *) ctx->in)[15] = ctx->bits[1]; + + thumb_md5_transform (ctx->buf, (guint32 *) ctx->in); + byteReverse ((unsigned char *) ctx->buf, 4); + memcpy (digest, ctx->buf, 16); + memset (ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + + +/* The four core functions - F1 is optimized somewhat */ + +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1 (z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define thumb_md5_step(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. ThumbMD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void +thumb_md5_transform (guint32 buf[4], guint32 const in[16]) +{ + register guint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + thumb_md5_step(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + thumb_md5_step(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + thumb_md5_step(F1, c, d, a, b, in[2] + 0x242070db, 17); + thumb_md5_step(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + thumb_md5_step(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + thumb_md5_step(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + thumb_md5_step(F1, c, d, a, b, in[6] + 0xa8304613, 17); + thumb_md5_step(F1, b, c, d, a, in[7] + 0xfd469501, 22); + thumb_md5_step(F1, a, b, c, d, in[8] + 0x698098d8, 7); + thumb_md5_step(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + thumb_md5_step(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + thumb_md5_step(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + thumb_md5_step(F1, a, b, c, d, in[12] + 0x6b901122, 7); + thumb_md5_step(F1, d, a, b, c, in[13] + 0xfd987193, 12); + thumb_md5_step(F1, c, d, a, b, in[14] + 0xa679438e, 17); + thumb_md5_step(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + thumb_md5_step(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + thumb_md5_step(F2, d, a, b, c, in[6] + 0xc040b340, 9); + thumb_md5_step(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + thumb_md5_step(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + thumb_md5_step(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + thumb_md5_step(F2, d, a, b, c, in[10] + 0x02441453, 9); + thumb_md5_step(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + thumb_md5_step(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + thumb_md5_step(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + thumb_md5_step(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + thumb_md5_step(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + thumb_md5_step(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + thumb_md5_step(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + thumb_md5_step(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + thumb_md5_step(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + thumb_md5_step(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + thumb_md5_step(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + thumb_md5_step(F3, d, a, b, c, in[8] + 0x8771f681, 11); + thumb_md5_step(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + thumb_md5_step(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + thumb_md5_step(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + thumb_md5_step(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + thumb_md5_step(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + thumb_md5_step(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + thumb_md5_step(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + thumb_md5_step(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + thumb_md5_step(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + thumb_md5_step(F3, b, c, d, a, in[6] + 0x04881d05, 23); + thumb_md5_step(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + thumb_md5_step(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + thumb_md5_step(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + thumb_md5_step(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + thumb_md5_step(F4, a, b, c, d, in[0] + 0xf4292244, 6); + thumb_md5_step(F4, d, a, b, c, in[7] + 0x432aff97, 10); + thumb_md5_step(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + thumb_md5_step(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + thumb_md5_step(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + thumb_md5_step(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + thumb_md5_step(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + thumb_md5_step(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + thumb_md5_step(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + thumb_md5_step(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + thumb_md5_step(F4, c, d, a, b, in[6] + 0xa3014314, 15); + thumb_md5_step(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + thumb_md5_step(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + thumb_md5_step(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + thumb_md5_step(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + thumb_md5_step(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + diff --git a/libnautilus-private/gnome-thumbnail.h b/libnautilus-private/gnome-thumbnail.h new file mode 100644 index 000000000..097554413 --- /dev/null +++ b/libnautilus-private/gnome-thumbnail.h @@ -0,0 +1,108 @@ +/* + * gnome-thumbnail.h: Utilities for handling thumbnails + * + * Copyright (C) 2002 Red Hat, Inc. + * + * This file is part of the Gnome Library. + * + * The Gnome Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * The Gnome 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the Gnome Library; see the file COPYING.LIB. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Alexander Larsson <alexl@redhat.com> + */ + +#ifndef GNOME_THUMBNAIL_H +#define GNOME_THUMBNAIL_H + +#include <glib.h> +#include <glib-object.h> +#include <time.h> +#include <gdk-pixbuf/gdk-pixbuf.h> + +G_BEGIN_DECLS + +typedef enum { + GNOME_THUMBNAIL_SIZE_NORMAL, + GNOME_THUMBNAIL_SIZE_LARGE, +} GnomeThumbnailSize; + +#define GNOME_TYPE_THUMBNAIL_FACTORY (gnome_thumbnail_factory_get_type ()) +#define GNOME_THUMBNAIL_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_THUMBNAIL_FACTORY, GnomeThumbnailFactory)) +#define GNOME_THUMBNAIL_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_THUMBNAIL_FACTORY, GnomeThumbnailFactoryClass)) +#define GNOME_IS_THUMBNAIL_FACTORY(obj) (G_TYPE_INSTANCE_CHECK_TYPE ((obj), GNOME_TYPE_THUMBNAIL_FACTORY)) +#define GNOME_IS_THUMBNAIL_FACTORY_CLASS(klass) (G_TYPE_CLASS_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_THUMBNAIL_FACTORY)) + +typedef struct _GnomeThumbnailFactory GnomeThumbnailFactory; +typedef struct _GnomeThumbnailFactoryClass GnomeThumbnailFactoryClass; +typedef struct _GnomeThumbnailFactoryPrivate GnomeThumbnailFactoryPrivate; + +struct _GnomeThumbnailFactory { + GObject parent; + + GnomeThumbnailFactoryPrivate *priv; +}; + +struct _GnomeThumbnailFactoryClass { + GObjectClass parent; +}; + +GType gnome_thumbnail_factory_get_type (void); +GnomeThumbnailFactory *gnome_thumbnail_factory_new (GnomeThumbnailSize size); + +char * gnome_thumbnail_factory_lookup (GnomeThumbnailFactory *factory, + const char *uri, + time_t mtime); + +gboolean gnome_thumbnail_factory_has_valid_failed_thumbnail (GnomeThumbnailFactory *factory, + const char *uri, + time_t mtime); +gboolean gnome_thumbnail_factory_can_thumbnail (GnomeThumbnailFactory *factory, + const char *uri, + const char *mime_type, + time_t mtime); +GdkPixbuf * gnome_thumbnail_factory_generate_thumbnail (GnomeThumbnailFactory *factory, + const char *uri, + const char *mime_type); +void gnome_thumbnail_factory_save_thumbnail (GnomeThumbnailFactory *factory, + GdkPixbuf *thumbnail, + const char *uri, + time_t original_mtime); +void gnome_thumbnail_factory_create_failed_thumbnail (GnomeThumbnailFactory *factory, + const char *uri, + time_t mtime); + + +/* Thumbnailing utils: */ +gboolean gnome_thumbnail_has_uri (GdkPixbuf *pixbuf, + const char *uri); +gboolean gnome_thumbnail_is_valid (GdkPixbuf *pixbuf, + const char *uri, + time_t mtime); +char * gnome_thumbnail_md5 (const char *uri); +char * gnome_thumbnail_path_for_uri (const char *uri, + GnomeThumbnailSize size); + + +/* Pixbuf utils */ + +GdkPixbuf *gnome_thumbnail_scale_down_pixbuf (GdkPixbuf *pixbuf, + int dest_width, + int dest_height); + +GdkPixbuf *gnome_thumbnail_load_pixbuf (const char *uri); + +G_END_DECLS + +#endif /* GNOME_THUMBNAIL_H */ diff --git a/libnautilus-private/nautilus-bookmark.c b/libnautilus-private/nautilus-bookmark.c index b07976966..71402baae 100644 --- a/libnautilus-private/nautilus-bookmark.c +++ b/libnautilus-private/nautilus-bookmark.c @@ -47,8 +47,8 @@ enum { LAST_SIGNAL }; -#define GENERIC_BOOKMARK_ICON_NAME "i-bookmark" -#define MISSING_BOOKMARK_ICON_NAME "i-bookmark-missing" +#define GENERIC_BOOKMARK_ICON_NAME "gnome-fs-bookmark" +#define MISSING_BOOKMARK_ICON_NAME "gnome-fs-bookmark-missing" static guint signals[LAST_SIGNAL]; @@ -56,7 +56,7 @@ struct NautilusBookmarkDetails { char *name; char *uri; - NautilusScalableIcon *icon; + char *icon; NautilusFile *file; }; @@ -81,6 +81,7 @@ nautilus_bookmark_finalize (GObject *object) g_free (bookmark->details->name); g_free (bookmark->details->uri); + g_free (bookmark->details->icon); g_free (bookmark->details); G_OBJECT_CLASS (parent_class)->finalize (object); @@ -205,7 +206,7 @@ nautilus_bookmark_get_pixbuf (NautilusBookmark *bookmark, gboolean optimize_for_anti_aliasing) { GdkPixbuf *result; - NautilusScalableIcon *icon; + char *icon; g_return_val_if_fail (NAUTILUS_IS_BOOKMARK (bookmark), NULL); @@ -215,15 +216,15 @@ nautilus_bookmark_get_pixbuf (NautilusBookmark *bookmark, } result = nautilus_icon_factory_get_pixbuf_for_icon - (icon, - icon_size, icon_size, icon_size, icon_size, - NULL, TRUE); - nautilus_scalable_icon_unref (icon); + (icon, NULL, NULL, + icon_size, NULL, TRUE); + + g_free (icon); return result; } -NautilusScalableIcon * +char * nautilus_bookmark_get_icon (NautilusBookmark *bookmark) { g_return_val_if_fail (NAUTILUS_IS_BOOKMARK (bookmark), NULL); @@ -231,10 +232,7 @@ nautilus_bookmark_get_icon (NautilusBookmark *bookmark) /* Try to connect a file in case file exists now but didn't earlier. */ nautilus_bookmark_connect_file (bookmark); - if (bookmark->details->icon != NULL) { - nautilus_scalable_icon_ref (bookmark->details->icon); - } - return bookmark->details->icon; + return g_strdup (bookmark->details->icon); } char * @@ -282,41 +280,12 @@ nautilus_bookmark_set_name (NautilusBookmark *bookmark, const char *new_name) static gboolean nautilus_bookmark_icon_is_different (NautilusBookmark *bookmark, - NautilusScalableIcon *new_icon) + char *new_icon) { - char *new_uri, *new_mime_type, *new_name; - char *old_uri, *old_mime_type, *old_name; - gboolean result; - g_assert (NAUTILUS_IS_BOOKMARK (bookmark)); g_assert (new_icon != NULL); - /* Bookmarks don't store the modifier or embedded text. */ - nautilus_scalable_icon_get_text_pieces - (new_icon, &new_uri, &new_mime_type, &new_name, NULL, NULL); - - if (bookmark->details->icon == NULL) { - result = !eel_str_is_empty (new_uri) - || !eel_str_is_empty (new_mime_type) - || !eel_str_is_empty (new_name); - } else { - nautilus_scalable_icon_get_text_pieces - (bookmark->details->icon, &old_uri, &old_mime_type, &old_name, NULL, NULL); - - result = eel_strcmp (old_uri, new_uri) != 0 - || eel_strcmp (old_mime_type, new_mime_type) != 0 - || eel_strcmp (old_name, new_name) != 0; - - g_free (old_uri); - g_free (old_mime_type); - g_free (old_name); - } - - g_free (new_uri); - g_free (new_mime_type); - g_free (new_name); - - return result; + return eel_strcmp (bookmark->details->icon, new_icon) != 0; } /** @@ -326,7 +295,7 @@ nautilus_bookmark_icon_is_different (NautilusBookmark *bookmark, static gboolean nautilus_bookmark_update_icon (NautilusBookmark *bookmark) { - NautilusScalableIcon *new_icon; + char *new_icon; g_assert (NAUTILUS_IS_BOOKMARK (bookmark)); @@ -335,15 +304,13 @@ nautilus_bookmark_update_icon (NautilusBookmark *bookmark) } if (nautilus_icon_factory_is_icon_ready_for_file (bookmark->details->file)) { - new_icon = nautilus_icon_factory_get_icon_for_file (bookmark->details->file, NULL); + new_icon = nautilus_icon_factory_get_icon_for_file (bookmark->details->file); if (nautilus_bookmark_icon_is_different (bookmark, new_icon)) { - if (bookmark->details->icon != NULL) { - nautilus_scalable_icon_unref (bookmark->details->icon); - } + g_free (bookmark->details->icon); bookmark->details->icon = new_icon; return TRUE; } - nautilus_scalable_icon_unref (new_icon); + g_free (new_icon); } return FALSE; @@ -409,17 +376,16 @@ nautilus_bookmark_set_icon_to_default (NautilusBookmark *bookmark) { const char *icon_name; - if (bookmark->details->icon != NULL) { - nautilus_scalable_icon_unref (bookmark->details->icon); - } + + g_free (bookmark->details->icon); if (nautilus_bookmark_uri_known_not_to_exist (bookmark)) { icon_name = MISSING_BOOKMARK_ICON_NAME; } else { icon_name = GENERIC_BOOKMARK_ICON_NAME; } - bookmark->details->icon = nautilus_scalable_icon_new_from_text_pieces - (NULL, NULL, icon_name, NULL, NULL); + + bookmark->details->icon = g_strdup (icon_name); } /** @@ -456,7 +422,7 @@ nautilus_bookmark_disconnect_file (NautilusBookmark *bookmark) } if (bookmark->details->icon != NULL) { - nautilus_scalable_icon_unref (bookmark->details->icon); + g_free (bookmark->details->icon); bookmark->details->icon = NULL; } } @@ -490,7 +456,7 @@ nautilus_bookmark_connect_file (NautilusBookmark *bookmark) NautilusBookmark * nautilus_bookmark_new_with_icon (const char *uri, const char *name, - NautilusScalableIcon *icon) + const char *icon) { NautilusBookmark *new_bookmark; @@ -501,10 +467,7 @@ nautilus_bookmark_new_with_icon (const char *uri, const char *name, new_bookmark->details->name = g_strdup (name); new_bookmark->details->uri = g_strdup (uri); - if (icon != NULL) { - nautilus_scalable_icon_ref (icon); - } - new_bookmark->details->icon = icon; + new_bookmark->details->icon = g_strdup (icon); nautilus_bookmark_connect_file (new_bookmark); diff --git a/libnautilus-private/nautilus-bookmark.h b/libnautilus-private/nautilus-bookmark.h index 01dc0af18..d5963ad20 100644 --- a/libnautilus-private/nautilus-bookmark.h +++ b/libnautilus-private/nautilus-bookmark.h @@ -71,11 +71,11 @@ NautilusBookmark * nautilus_bookmark_new (const char const char *name); NautilusBookmark * nautilus_bookmark_new_with_icon (const char *uri, const char *name, - NautilusScalableIcon *icon); + const char *icon); NautilusBookmark * nautilus_bookmark_copy (NautilusBookmark *bookmark); char * nautilus_bookmark_get_name (NautilusBookmark *bookmark); char * nautilus_bookmark_get_uri (NautilusBookmark *bookmark); -NautilusScalableIcon *nautilus_bookmark_get_icon (NautilusBookmark *bookmark); +char * nautilus_bookmark_get_icon (NautilusBookmark *bookmark); gboolean nautilus_bookmark_set_name (NautilusBookmark *bookmark, const char *new_name); gboolean nautilus_bookmark_uri_known_not_to_exist (NautilusBookmark *bookmark); diff --git a/libnautilus-private/nautilus-directory-async.c b/libnautilus-private/nautilus-directory-async.c index 03681ae02..2d5c86263 100644 --- a/libnautilus-private/nautilus-directory-async.c +++ b/libnautilus-private/nautilus-directory-async.c @@ -2825,10 +2825,10 @@ link_info_done (NautilusDirectory *directory, file->details->got_link_info = TRUE; g_free (file->details->activation_uri); g_free (file->details->display_name); - g_free (file->details->custom_icon_uri); + g_free (file->details->custom_icon); file->details->activation_uri = g_strdup (uri); file->details->display_name = g_strdup (name); - file->details->custom_icon_uri = g_strdup (icon); + file->details->custom_icon = g_strdup (icon); nautilus_file_clear_cached_display_name (file); nautilus_directory_async_state_changed (directory); diff --git a/libnautilus-private/nautilus-file-private.h b/libnautilus-private/nautilus-file-private.h index 9be02878c..4d1adbc50 100644 --- a/libnautilus-private/nautilus-file-private.h +++ b/libnautilus-private/nautilus-file-private.h @@ -80,7 +80,7 @@ struct NautilusFileDetails /* Info you might get from a link (.desktop, .directory or nautilus link) */ char *display_name; - char *custom_icon_uri; + char *custom_icon; char *activation_uri; /* The following is for file operations in progress. Since @@ -168,4 +168,6 @@ gboolean nautilus_file_is_self_owned (NautilusFile void nautilus_file_invalidate_count_and_mime_list (NautilusFile *file); gboolean nautilus_file_rename_in_progress (NautilusFile *file); +GnomeVFSFileInfo *nautilus_file_peek_vfs_file_info (NautilusFile *file); + #endif diff --git a/libnautilus-private/nautilus-file.c b/libnautilus-private/nautilus-file.c index ee5f02b95..b1cbf3fec 100644 --- a/libnautilus-private/nautilus-file.c +++ b/libnautilus-private/nautilus-file.c @@ -423,7 +423,7 @@ finalize (GObject *object) } g_free (file->details->top_left_text); g_free (file->details->display_name); - g_free (file->details->custom_icon_uri); + g_free (file->details->custom_icon); g_free (file->details->activation_uri); g_free (file->details->compare_by_emblem_cache); @@ -2400,7 +2400,7 @@ nautilus_file_get_drop_target_uri (NautilusFile *file) } char * -nautilus_file_get_custom_icon_uri (NautilusFile *file) +nautilus_file_get_custom_icon (NautilusFile *file) { char *uri; @@ -2412,7 +2412,7 @@ nautilus_file_get_custom_icon_uri (NautilusFile *file) uri = nautilus_file_get_metadata (file, NAUTILUS_METADATA_KEY_CUSTOM_ICON, NULL); if (uri == NULL && file->details->got_link_info) { - uri = g_strdup (file->details->custom_icon_uri); + uri = g_strdup (file->details->custom_icon); } return uri; @@ -2905,6 +2905,13 @@ nautilus_file_recompute_deep_counts (NautilusFile *file) } } +GnomeVFSFileInfo * +nautilus_file_peek_vfs_file_info (NautilusFile *file) +{ + return file->details->info; +} + + /** * nautilus_file_get_directory_item_mime_types * @@ -4650,16 +4657,18 @@ nautilus_file_is_executable (NautilusFile *file) } /** - * nautilus_file_get_top_left_text + * nautilus_file_peek_top_left_text * - * Get the text from the top left of the file. + * Peek at the text from the top left of the file. * @file: NautilusFile representing the file in question. * * Returns: NULL if there is no text readable, otherwise, the text. + * This string is owned by the file object and should not + * be kept around or freed. * **/ char * -nautilus_file_get_top_left_text (NautilusFile *file) +nautilus_file_peek_top_left_text (NautilusFile *file) { g_return_val_if_fail (NAUTILUS_IS_FILE (file), NULL); @@ -4670,15 +4679,31 @@ nautilus_file_get_top_left_text (NautilusFile *file) /* Show " ..." in the file until we read the contents in. */ if (!file->details->got_top_left_text) { if (nautilus_file_contains_text (file)) { - return g_strdup (" ..."); + return " ..."; } return NULL; } /* Show what we read in. */ - return g_strdup (file->details->top_left_text); + return file->details->top_left_text; +} + +/** + * nautilus_file_get_top_left_text + * + * Get the text from the top left of the file. + * @file: NautilusFile representing the file in question. + * + * Returns: NULL if there is no text readable, otherwise, the text. + * + **/ +char * +nautilus_file_get_top_left_text (NautilusFile *file) +{ + return g_strdup (nautilus_file_peek_top_left_text (file)); } + void nautilus_file_mark_gone (NautilusFile *file) { diff --git a/libnautilus-private/nautilus-file.h b/libnautilus-private/nautilus-file.h index 6b88c53a9..63ec8708e 100644 --- a/libnautilus-private/nautilus-file.h +++ b/libnautilus-private/nautilus-file.h @@ -159,6 +159,7 @@ void nautilus_file_set_keywords (Nautilu GList *keywords); GList * nautilus_file_get_emblem_names (NautilusFile *file); char * nautilus_file_get_top_left_text (NautilusFile *file); +char * nautilus_file_peek_top_left_text (NautilusFile *file); gboolean nautilus_file_get_directory_item_mime_types (NautilusFile *file, GList **mime_list); @@ -294,7 +295,7 @@ char * nautilus_file_get_activation_uri (Nautilu char * nautilus_file_get_drop_target_uri (NautilusFile *file); /* Get custom icon (if specified by metadata or link contents) */ -char * nautilus_file_get_custom_icon_uri (NautilusFile *file); +char * nautilus_file_get_custom_icon (NautilusFile *file); /* Convenience functions for dealing with a list of NautilusFile objects that each have a ref. * These are just convenient names for functions that work on lists of GtkObject *. diff --git a/libnautilus-private/nautilus-icon-container.c b/libnautilus-private/nautilus-icon-container.c index 5da8bfe8d..46730f726 100644 --- a/libnautilus-private/nautilus-icon-container.c +++ b/libnautilus-private/nautilus-icon-container.c @@ -304,16 +304,15 @@ icon_set_position (NautilusIcon *icon, static void icon_get_size (NautilusIconContainer *container, NautilusIcon *icon, - guint *size_x, guint *size_y) + guint *size) { - if (size_x != NULL) { - *size_x = MAX (nautilus_get_icon_size_for_zoom_level (container->details->zoom_level) + g_assert (fabs (icon->scale_x - icon->scale_y) <= 0.001); + + /* ALEX TODO: Bogus. Should only have one scale, not _x and _y */ + if (size != NULL) { + *size = MAX (nautilus_get_icon_size_for_zoom_level (container->details->zoom_level) * icon->scale_x, NAUTILUS_ICON_SIZE_SMALLEST); } - if (size_y != NULL) { - *size_y = MAX (nautilus_get_icon_size_for_zoom_level (container->details->zoom_level) - * icon->scale_y, NAUTILUS_ICON_SIZE_SMALLEST); - } } /* The icon_set_size function is used by the stretching user @@ -327,11 +326,11 @@ icon_set_size (NautilusIconContainer *container, guint icon_size, gboolean update_position) { - guint old_size_x, old_size_y; + guint old_size; double scale; - icon_get_size (container, icon, &old_size_x, &old_size_y); - if (icon_size == old_size_x && icon_size == old_size_y) { + icon_get_size (container, icon, &old_size); + if (icon_size == old_size) { return; } @@ -2656,7 +2655,7 @@ start_stretching (NautilusIconContainer *container) &details->stretch_start.icon_x, &details->stretch_start.icon_y); icon_get_size (container, icon, - &details->stretch_start.icon_size, NULL); + &details->stretch_start.icon_size); gnome_canvas_item_grab (GNOME_CANVAS_ITEM (icon->item), (GDK_POINTER_MOTION_MASK @@ -3793,18 +3792,18 @@ get_icon_being_renamed (NautilusIconContainer *container) return rename_icon; } -static NautilusScalableIcon * +static char * nautilus_icon_container_get_icon_images (NautilusIconContainer *container, NautilusIconData *data, - const char *modifier, - GList **emblem_icons) + GList **emblem_icons, + char **embedded_text) { NautilusIconContainerClass *klass; klass = NAUTILUS_ICON_CONTAINER_GET_CLASS (container); g_return_val_if_fail (klass->get_icon_images != NULL, NULL); - return klass->get_icon_images (container, data, modifier, emblem_icons); + return klass->get_icon_images (container, data, emblem_icons, embedded_text); } @@ -3827,16 +3826,17 @@ nautilus_icon_container_update_icon (NautilusIconContainer *container, NautilusIcon *icon) { NautilusIconContainerDetails *details; - guint icon_size_x, icon_size_y; + guint icon_size; guint min_image_size, max_image_size; guint width, height, scaled_width, scaled_height; double scale_factor; - NautilusScalableIcon *scalable_icon; + char *icon_name; NautilusEmblemAttachPoints attach_points; GdkPixbuf *pixbuf, *emblem_pixbuf, *saved_pixbuf; - GList *emblem_scalable_icons, *emblem_pixbufs, *p; + GList *emblem_icon_names, *emblem_pixbufs, *p; char *editable_text, *additional_text; - + char *embedded_text; + if (icon == NULL) { return; } @@ -3844,33 +3844,38 @@ nautilus_icon_container_update_icon (NautilusIconContainer *container, details = container->details; /* Get the icons. */ - emblem_scalable_icons = NULL; - scalable_icon = nautilus_icon_container_get_icon_images ( + emblem_icon_names = NULL; + embedded_text = NULL; + icon_name = nautilus_icon_container_get_icon_images ( container, icon->data, - (icon == details->drop_target) ? "accept" : "", - &emblem_scalable_icons); + &emblem_icon_names, + &embedded_text); /* compute the maximum size based on the scale factor */ min_image_size = MINIMUM_IMAGE_SIZE * GNOME_CANVAS (container)->pixels_per_unit; - max_image_size = MAXIMUM_IMAGE_SIZE * GNOME_CANVAS (container)->pixels_per_unit; + max_image_size = MAX (MAXIMUM_IMAGE_SIZE * GNOME_CANVAS (container)->pixels_per_unit, NAUTILUS_ICON_MAXIMUM_SIZE); /* Get the appropriate images for the file. */ - icon_get_size (container, icon, &icon_size_x, &icon_size_y); + icon_get_size (container, icon, &icon_size); + + icon_size = MAX (icon_size, min_image_size); + icon_size = MIN (icon_size, max_image_size); + pixbuf = nautilus_icon_factory_get_pixbuf_for_icon - (scalable_icon, - icon_size_x, - icon_size_y, - max_image_size * icon->scale_x, - max_image_size * icon->scale_y, + (icon_name, + (icon == details->drop_target) ? "accept" : NULL, + embedded_text, + icon_size, &attach_points, TRUE); - nautilus_scalable_icon_unref (scalable_icon); + g_free (icon_name); /* in the rare case an image is too small, scale it up */ width = gdk_pixbuf_get_width (pixbuf); height = gdk_pixbuf_get_height (pixbuf); - if (width < min_image_size || height < min_image_size) { + if (width < min_image_size && height < min_image_size) { + g_print ("to small (%dx%d, scaling up\n", width, height); scale_factor = MAX (min_image_size / (double) width, min_image_size / (double) height); /* don't let it exceed the maximum width in the other dimension */ scale_factor = MIN (scale_factor, max_image_size / width); @@ -3885,17 +3890,14 @@ nautilus_icon_container_update_icon (NautilusIconContainer *container, emblem_pixbufs = NULL; - icon_size_x = MAX (nautilus_get_icon_size_for_zoom_level (container->details->zoom_level) + icon_size = MAX (nautilus_get_icon_size_for_zoom_level (container->details->zoom_level) * icon->scale_x, NAUTILUS_ICON_SIZE_SMALLEST); - icon_size_y = MAX (nautilus_get_icon_size_for_zoom_level (container->details->zoom_level) - * icon->scale_y, NAUTILUS_ICON_SIZE_SMALLEST); - for (p = emblem_scalable_icons; p != NULL; p = p->next) { + for (p = emblem_icon_names; p != NULL; p = p->next) { emblem_pixbuf = nautilus_icon_factory_get_pixbuf_for_icon (p->data, - icon_size_x, - icon_size_y, - MAXIMUM_EMBLEM_SIZE, - MAXIMUM_EMBLEM_SIZE, + NULL, + NULL, + MIN (icon_size, MAXIMUM_EMBLEM_SIZE), NULL, FALSE); if (emblem_pixbuf != NULL) { @@ -3904,7 +3906,8 @@ nautilus_icon_container_update_icon (NautilusIconContainer *container, } } emblem_pixbufs = g_list_reverse (emblem_pixbufs); - nautilus_scalable_icon_list_free (emblem_scalable_icons); + + eel_g_list_free_deep (emblem_icon_names); nautilus_icon_container_get_icon_text (container, icon->data, @@ -4503,7 +4506,7 @@ nautilus_icon_container_show_stretch_handles (NautilusIconContainer *container) { NautilusIconContainerDetails *details; NautilusIcon *icon; - int initial_size_x, initial_size_y; + int initial_size; icon = get_first_selected_icon (container); if (icon == NULL) { @@ -4526,12 +4529,12 @@ nautilus_icon_container_show_stretch_handles (NautilusIconContainer *container) nautilus_icon_canvas_item_set_show_stretch_handles (icon->item, TRUE); details->stretch_icon = icon; - icon_get_size (container, icon, &initial_size_x, &initial_size_y); + icon_get_size (container, icon, &initial_size); /* only need to keep size in one dimension, since they are constrained to be the same */ container->details->stretch_initial_x = icon->x; container->details->stretch_initial_y = icon->y; - container->details->stretch_initial_size = initial_size_x; + container->details->stretch_initial_size = initial_size; emit_stretch_started (container, icon); } diff --git a/libnautilus-private/nautilus-icon-container.h b/libnautilus-private/nautilus-icon-container.h index 172f49f9f..d6b78a8d0 100644 --- a/libnautilus-private/nautilus-icon-container.h +++ b/libnautilus-private/nautilus-icon-container.h @@ -105,11 +105,10 @@ typedef struct { * These must be implemented. The default "do nothing" is not * good enough, these are _not_ signals. */ - NautilusScalableIcon * - (* get_icon_images) (NautilusIconContainer *container, + char * (* get_icon_images) (NautilusIconContainer *container, NautilusIconData *data, - const char *modifier, - GList **emblem_icons); + GList **emblem_icons, + char **embedded_text); void (* get_icon_text) (NautilusIconContainer *container, NautilusIconData *data, char **editable_text, diff --git a/libnautilus-private/nautilus-icon-factory-private.h b/libnautilus-private/nautilus-icon-factory-private.h index 4e4dd7dd8..f47a3757f 100644 --- a/libnautilus-private/nautilus-icon-factory-private.h +++ b/libnautilus-private/nautilus-icon-factory-private.h @@ -28,14 +28,6 @@ #include <gdk-pixbuf/gdk-pixbuf.h> -/* For now, images are used themselves as thumbnails when they are - * below this threshold size. Later we might have to have a more - * complex rule about when to use an image for itself. - */ -#define SELF_THUMBNAIL_SIZE_THRESHOLD 16384 - -void nautilus_icon_factory_remove_by_uri (const char *uri); - /* Convenience routine to return the appropriate thumbnail frame. */ GdkPixbuf *nautilus_icon_factory_get_thumbnail_frame (void); diff --git a/libnautilus-private/nautilus-icon-factory.c b/libnautilus-private/nautilus-icon-factory.c index 0b3fa7e4f..3c95a3d09 100644 --- a/libnautilus-private/nautilus-icon-factory.c +++ b/libnautilus-private/nautilus-icon-factory.c @@ -27,11 +27,12 @@ #include <config.h> #include "nautilus-icon-factory.h" +#include "gnome-icon-lookup.h" #include "nautilus-default-file-icon.h" #include "nautilus-file-attributes.h" +#include "nautilus-file-private.h" #include "nautilus-file-utilities.h" -#include "nautilus-find-icon-image.h" #include "nautilus-global-preferences.h" #include "nautilus-icon-factory-private.h" #include "nautilus-lib-self-check-functions.h" @@ -52,6 +53,7 @@ #include <gtk/gtksignal.h> #include <libgnome/gnome-i18n.h> #include <libgnome/gnome-util.h> +#include <libgnome/gnome-macros.h> #include <libgnomevfs/gnome-vfs-file-info.h> #include <libgnomevfs/gnome-vfs-mime-handlers.h> #include <libgnomevfs/gnome-vfs-mime-monitor.h> @@ -59,32 +61,14 @@ #include <libgnomevfs/gnome-vfs-types.h> #include <libgnomevfs/gnome-vfs-utils.h> #include <librsvg/rsvg.h> +#include <math.h> #include <stdio.h> #include <string.h> -#undef IMAGE_CACHE_DEBUG +#define CACHE_SELF_CHECKS 0 -#ifdef IMAGE_CACHE_DEBUG -# include <gtk/gtkwindow.h> -# include <gtk/gtkscrolledwindow.h> -# include <eel/eel-image-table.h> -# include <eel/eel-labeled-image.h> -#endif - -#define ICON_NAME_BLOCK_DEVICE "i-blockdev" -#define ICON_NAME_BROKEN_SYMBOLIC_LINK "i-symlink" -#define ICON_NAME_CHARACTER_DEVICE "i-chardev" -#define ICON_NAME_DIRECTORY "i-directory" -#define ICON_NAME_EXECUTABLE "i-executable" -#define ICON_NAME_FIFO "i-fifo" -#define ICON_NAME_REGULAR "i-regular" -#define ICON_NAME_SEARCH_RESULTS "i-search" -#define ICON_NAME_SOCKET "i-sock" #define ICON_NAME_THUMBNAIL_LOADING "loading" -#define ICON_NAME_TRASH_EMPTY "trash-empty" -#define ICON_NAME_TRASH_NOT_EMPTY "trash-full" - -#define EMBLEM_SCALE_FACTOR 0.75 +#define NAUTILUS_EMBLEM_NAME_PREFIX "emblem-" /* This used to be called ICON_CACHE_MAX_ENTRIES, but it's misleading * to call it that, since we can have any number of entries in the @@ -98,17 +82,6 @@ */ #define ICON_CACHE_SWEEP_TIMEOUT (10 * 1000) -/* Images are used themselves as thumbnails when they are below this - * threshold size. - */ -/* FIXME bugzilla.gnome.org 45081: Later we might have to have a more - * complex rule about when to use an image for itself. - */ -#define SELF_THUMBNAIL_SIZE_THRESHOLD 16384 - -/* Maximum size for either dimension at the standard zoom level. */ -#define MAXIMUM_ICON_SIZE 96 - /* Embedded text font size and text line settings */ /* FIXME; Hard coded font size */ @@ -128,28 +101,31 @@ struct CircularList { CircularList *prev; }; +/* The key to a hash table that holds CacheIcons. */ +typedef struct { + char *name; /* Icon name or absolute filename */ + char *modifier; + char *embedded_text; + guint nominal_size; +} CacheKey; + +/* The value in the same table. */ +typedef struct { + guint ref_count; + + GdkPixbuf *pixbuf; + GnomeIconData *icon_data; + + CircularList recently_used_node; +} CacheIcon; + /* The icon factory. * These are just globals, but they're in an object so we can * connect signals and have multiple icon factories some day * if we want to. */ typedef struct { - GtkObject object; - - NautilusIconThemeSpecifications theme; - - /* A hash table so we pass out the same scalable icon pointer - * every time someone asks for the same icon. Scalable icons - * are removed from this hash table when they are destroyed. - */ - GHashTable *scalable_icons; - -#ifdef IMAGE_CACHE_DEBUG - /* A hash table so we can find a cached icon's data structure - * from the pixbuf - used for debugging only [!] - */ - GHashTable *cache_icons; -#endif + GObject object; /* A hash table that contains the icons. A circular list of * the most recently used icons is kept around, and we don't @@ -157,22 +133,25 @@ typedef struct { */ GHashTable *icon_cache; - CircularList recently_used_dummy_head; - guint recently_used_count; - guint sweep_timer; - /* frames to use for thumbnail icons */ GdkPixbuf *thumbnail_frame; /* Used for icon themes according to the freedesktop icon spec. */ GnomeIconLoader *icon_loader; + GnomeThumbnailFactory *thumbnail_factory; + + CircularList recently_used_dummy_head; + guint recently_used_count; + guint sweep_timer; + + CacheIcon *fallback_icon; } NautilusIconFactory; #define NAUTILUS_ICON_FACTORY(obj) \ GTK_CHECK_CAST (obj, nautilus_icon_factory_get_type (), NautilusIconFactory) typedef struct { - GtkObjectClass parent_class; + GObjectClass parent_class; } NautilusIconFactoryClass; enum { @@ -181,101 +160,42 @@ enum { }; static guint signals[LAST_SIGNAL]; -/* A scalable icon, which is basically the name and path of an icon, - * before we load the actual pixels of the icons's pixbuf. - */ -struct NautilusScalableIcon { - guint ref_count; - - char *uri; - char *mime_type; - char *name; - char *modifier; - char *embedded_text; -}; - -typedef enum { - REQUEST_NORMAL, - REQUEST_PICKY_CUSTOM_ONLY, - REQUEST_PICKY_BY_NAME_FIRST_CHOICE, - REQUEST_PICKY_BY_NAME_SECOND_CHOICE -} IconRequest; - -/* A request for an icon of a particular size. */ -typedef struct { - guint nominal_width; - guint nominal_height; - guint maximum_width; - guint maximum_height; -} IconSizeRequest; - -/* The key to a hash table that holds CacheIcons. */ -typedef struct { - NautilusScalableIcon *scalable_icon; - IconSizeRequest size; -} CacheKey; - -/* The value in the same table. */ -typedef struct { - GdkPixbuf *pixbuf; - NautilusIconDetails details; - - /* Number of internal clients with refs to the pixbuf. */ - guint internal_ref_count; - - /* Used to decide when to kick icons out of the cache. */ - CircularList recently_used_node; - /* Used to know when to make a new thumbnail. */ - time_t cache_time; - GnomeVFSFileSize cache_size; - - /* Type of icon. */ - IconRequest request; - gboolean scaled; - gboolean is_fallback; -#ifdef IMAGE_CACHE_DEBUG - GtkWidget *image; -#endif -} CacheIcon; - -static CacheIcon *fallback_icon; static int cached_thumbnail_limit; /* forward declarations */ static GType nautilus_icon_factory_get_type (void); -static void nautilus_icon_factory_class_init (NautilusIconFactoryClass *class); -static void nautilus_icon_factory_init (NautilusIconFactory *factory); -static void nautilus_icon_factory_destroy (GtkObject *object); -static void icon_theme_changed_callback (gpointer user_data); +static void nautilus_icon_factory_class_init (NautilusIconFactoryClass *class); +static void nautilus_icon_factory_instance_init (NautilusIconFactory *factory); +static void nautilus_icon_factory_finalize (GObject *object); static void thumbnail_limit_changed_callback (gpointer user_data); static void mime_type_data_changed_callback (GnomeVFSMIMEMonitor *monitor, gpointer user_data); -static guint nautilus_scalable_icon_hash (gconstpointer p); -static gboolean nautilus_scalable_icon_equal (gconstpointer a, - gconstpointer b); static guint cache_key_hash (gconstpointer p); static gboolean cache_key_equal (gconstpointer a, gconstpointer b); -static CacheIcon *get_icon_from_cache (NautilusScalableIcon *scalable_icon, - const IconSizeRequest *size, - IconRequest type); -static CacheIcon *load_icon_with_embedded_text (NautilusScalableIcon *scalable_icon, - const IconSizeRequest *size); - -EEL_CLASS_BOILERPLATE (NautilusIconFactory, - nautilus_icon_factory, - GTK_TYPE_OBJECT) +static void cache_key_destroy (CacheKey *key); +static void cache_icon_unref (CacheIcon *icon); +static CacheIcon *cache_icon_new (GdkPixbuf *pixbuf, + GnomeIconData *icon_data); +static CacheIcon *get_icon_from_cache (const char *icon, + const char *modifier, + const char *embedded_text, + guint nominal_size); +static GdkPixbuf *embed_text (GdkPixbuf *pixbuf_without_text, + GnomeIconData *icon_data, + const char *text); + +GNOME_CLASS_BOILERPLATE (NautilusIconFactory, + nautilus_icon_factory, + GObject, G_TYPE_OBJECT); static NautilusIconFactory *global_icon_factory = NULL; static void destroy_icon_factory (void) { - eel_preferences_remove_callback (NAUTILUS_PREFERENCES_THEME, - icon_theme_changed_callback, - NULL); eel_preferences_remove_callback (NAUTILUS_PREFERENCES_IMAGE_FILE_THUMBNAIL_LIMIT, thumbnail_limit_changed_callback, NULL); @@ -291,13 +211,6 @@ get_icon_factory (void) global_icon_factory = NAUTILUS_ICON_FACTORY (g_object_new (nautilus_icon_factory_get_type (), NULL)); - g_object_ref (global_icon_factory); - gtk_object_sink (GTK_OBJECT (global_icon_factory)); - - icon_theme_changed_callback (NULL); - eel_preferences_add_callback (NAUTILUS_PREFERENCES_THEME, - icon_theme_changed_callback, - NULL); thumbnail_limit_changed_callback (NULL); eel_preferences_add_callback (NAUTILUS_PREFERENCES_IMAGE_FILE_THUMBNAIL_LIMIT, @@ -314,10 +227,10 @@ get_icon_factory (void) return global_icon_factory; } -GtkObject * +GObject * nautilus_icon_factory_get (void) { - return GTK_OBJECT (get_icon_factory ()); + return G_OBJECT (get_icon_factory ()); } static void @@ -339,20 +252,24 @@ nautilus_icon_factory_get_icon_loader (void) factory = get_icon_factory (); - if (factory->icon_loader == NULL) { - factory->icon_loader = gnome_icon_loader_new (); - g_signal_connect_object (factory->icon_loader, - "changed", - G_CALLBACK (icon_loader_changed_callback), - factory, 0); - } - return g_object_ref (factory->icon_loader); } +GnomeThumbnailFactory * +nautilus_icon_factory_get_thumbnail_factory () +{ + NautilusIconFactory *factory; + + factory = get_icon_factory (); + + return g_object_ref (factory->thumbnail_factory); +} + + static void check_recently_used_list (void) { +#if CACHE_SELF_CHECKS NautilusIconFactory *factory; CircularList *head, *node, *next; guint count; @@ -379,8 +296,10 @@ check_recently_used_list (void) } g_assert (count == factory->recently_used_count); +#endif } + /* load the thumbnail frame */ static void load_thumbnail_frame (NautilusIconFactory *factory) @@ -396,32 +315,49 @@ load_thumbnail_frame (NautilusIconFactory *factory) } static void -nautilus_icon_factory_init (NautilusIconFactory *factory) +nautilus_icon_factory_instance_init (NautilusIconFactory *factory) { - factory->scalable_icons = g_hash_table_new (nautilus_scalable_icon_hash, - nautilus_scalable_icon_equal); -#ifdef IMAGE_CACHE_DEBUG - factory->cache_icons = g_hash_table_new (NULL, NULL); -#endif - factory->icon_cache = g_hash_table_new (cache_key_hash, - cache_key_equal); + GdkPixbuf *pixbuf; - /* Empty out the recently-used list. */ - factory->recently_used_dummy_head.next = &factory->recently_used_dummy_head; - factory->recently_used_dummy_head.prev = &factory->recently_used_dummy_head; + factory->icon_cache = g_hash_table_new_full (cache_key_hash, + cache_key_equal, + (GDestroyNotify)cache_key_destroy, + (GDestroyNotify)cache_icon_unref); + + factory->icon_loader = gnome_icon_loader_new (); + gnome_icon_loader_set_allow_svg (factory->icon_loader, TRUE); + g_signal_connect_object (factory->icon_loader, + "changed", + G_CALLBACK (icon_loader_changed_callback), + factory, 0); - factory->theme.current = nautilus_icon_theme_new (); - factory->theme.fallback = nautilus_icon_theme_new (); + factory->thumbnail_factory = gnome_thumbnail_factory_new (GNOME_THUMBNAIL_SIZE_NORMAL); load_thumbnail_frame (factory); + + /* Empty out the recently-used list. */ + factory->recently_used_dummy_head.next = &factory->recently_used_dummy_head; + factory->recently_used_dummy_head.prev = &factory->recently_used_dummy_head; + + pixbuf = gdk_pixbuf_new_from_data (nautilus_default_file_icon, + GDK_COLORSPACE_RGB, + TRUE, + 8, + nautilus_default_file_icon_width, + nautilus_default_file_icon_height, + nautilus_default_file_icon_width * 4, /* stride */ + NULL, /* don't destroy data */ + NULL); + + factory->fallback_icon = cache_icon_new (pixbuf, NULL); } static void nautilus_icon_factory_class_init (NautilusIconFactoryClass *class) { - GtkObjectClass *object_class; + GObjectClass *object_class; - object_class = GTK_OBJECT_CLASS (class); + object_class = G_OBJECT_CLASS (class); signals[ICONS_CHANGED] = g_signal_new ("icons_changed", @@ -432,137 +368,75 @@ nautilus_icon_factory_class_init (NautilusIconFactoryClass *class) g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - object_class->destroy = nautilus_icon_factory_destroy; + object_class->finalize = nautilus_icon_factory_finalize; } static void cache_key_destroy (CacheKey *key) { - nautilus_scalable_icon_unref (key->scalable_icon); + g_free (key->name); + g_free (key->embedded_text); g_free (key); } -#ifdef IMAGE_CACHE_DEBUG -static GtkContainer * -get_image_cache_view (void) -{ - static GtkWidget *view = NULL; - - if (!view) { - GtkWidget *window; - GtkWidget *scroll; - - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - gtk_window_set_default_size ( - GTK_WINDOW (window), 800, 600); - gtk_window_set_title (GTK_WINDOW (window), - "Debug icon cache display"); - scroll = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy ( - GTK_SCROLLED_WINDOW (scroll), - GTK_POLICY_ALWAYS, - GTK_POLICY_ALWAYS); - view = eel_image_table_new (FALSE); - gtk_scrolled_window_add_with_viewport ( - GTK_SCROLLED_WINDOW (scroll), view); - gtk_container_add (GTK_CONTAINER (window), scroll); - gtk_widget_show_all (window); - } - - return GTK_CONTAINER (view); -} -#endif - static CacheIcon * -cache_icon_new (GdkPixbuf *pixbuf, - IconRequest request, - gboolean scaled, - const NautilusIconDetails *details) +cache_icon_new (GdkPixbuf *pixbuf, + GnomeIconData *icon_data) { - NautilusIconFactory *factory; CacheIcon *icon; - factory = get_icon_factory (); - /* Grab the pixbuf since we are keeping it. */ g_object_ref (pixbuf); /* Make the icon. */ icon = g_new0 (CacheIcon, 1); + icon->ref_count = 1; icon->pixbuf = pixbuf; - icon->internal_ref_count = 1; - icon->request = request; - icon->scaled = scaled; - icon->is_fallback = FALSE; - - if (details != NULL) { - icon->details = *details; - } - -#ifdef IMAGE_CACHE_DEBUG - /* Just a check to see this is not reusing a pixbuf. */ - g_assert (g_hash_table_lookup (factory->cache_icons, pixbuf) == NULL); - /* Put it into the hash table. */ - g_hash_table_insert (factory->cache_icons, pixbuf, icon); - - icon->image = eel_labeled_image_new ( - "cache image", icon->pixbuf); - gtk_widget_show (icon->image); - gtk_container_add (get_image_cache_view (), icon->image); -#endif + icon->icon_data = icon_data; return icon; } -static void +static void cache_icon_ref (CacheIcon *icon) { - NautilusIconFactory *factory; - - factory = get_icon_factory (); - g_assert (icon != NULL); - g_assert (icon->internal_ref_count >= 1 - || (icon->internal_ref_count == 0 && icon == fallback_icon)); -#ifdef IMAGE_CACHE_DEBUG - g_assert (g_hash_table_lookup (factory->cache_icons, icon->pixbuf) == icon); -#endif + g_assert (icon->ref_count >= 1); - icon->internal_ref_count++; + icon->ref_count++; } static void cache_icon_unref (CacheIcon *icon) { - NautilusIconFactory *factory; CircularList *node; - - factory = get_icon_factory (); - + NautilusIconFactory *factory; + g_assert (icon != NULL); - g_assert (icon->internal_ref_count >= 1); -#ifdef IMAGE_CACHE_DEBUG - g_assert (g_hash_table_lookup (factory->cache_icons, icon->pixbuf) == icon); -#endif + g_assert (icon->ref_count >= 1); - if (icon->internal_ref_count > 1) { - icon->internal_ref_count--; + if (icon->ref_count > 1) { + icon->ref_count--; return; } - icon->internal_ref_count = 0; + + icon->ref_count = 0; + factory = get_icon_factory (); + check_recently_used_list (); /* If it's in the recently used list, free it from there */ node = &icon->recently_used_node; if (node->next != NULL) { +#if CACHE_SELF_CHECKS g_assert (factory->recently_used_count >= 1); g_assert (node->next->prev == node); g_assert (node->prev->next == node); g_assert (node->next != node); g_assert (node->prev != node); - +#endif node->next->prev = node->prev; node->prev->next = node->next; @@ -571,86 +445,19 @@ cache_icon_unref (CacheIcon *icon) factory->recently_used_count -= 1; } - + check_recently_used_list (); - /* The fallback icon has life after death. */ - if (icon == fallback_icon) { - return; - } - -#ifdef IMAGE_CACHE_DEBUG - /* Remove from the cache icons table. */ - g_hash_table_remove (factory->cache_icons, icon->pixbuf); - gtk_widget_destroy (icon->image); -#endif - g_object_unref (icon->pixbuf); + + if (icon->icon_data) { + g_free (icon->icon_data->display_name); + g_free (icon->icon_data); + } g_free (icon); } -/* Destroy one pixbuf in the cache. */ -static gboolean -nautilus_icon_factory_destroy_cached_icon (gpointer key, gpointer value, gpointer user_data) -{ - cache_key_destroy (key); - cache_icon_unref (value); - - /* Tell the caller to remove the hash table entry. */ - return TRUE; -} - -/* Reset the cache to the default state. */ -static void -nautilus_icon_factory_clear (void) -{ - NautilusIconFactory *factory; - CircularList *head; - - factory = get_icon_factory (); - - g_hash_table_foreach_remove (factory->icon_cache, - nautilus_icon_factory_destroy_cached_icon, - NULL); - - /* Empty out the recently-used list. */ - head = &factory->recently_used_dummy_head; - g_assert (factory->recently_used_count == 0); - g_assert (head->next == head); - g_assert (head->prev == head); -} - -static void -nautilus_icon_factory_destroy (GtkObject *object) -{ - NautilusIconFactory *factory; - - factory = NAUTILUS_ICON_FACTORY (object); - - nautilus_icon_factory_clear (); - - if (g_hash_table_size (factory->scalable_icons) != 0) { - g_warning ("%d scalable icons still left when destroying icon factory", - g_hash_table_size (factory->scalable_icons)); - } - if (g_hash_table_size (factory->icon_cache) != 0) { - g_warning ("%d icon cache entries still left when destroying icon factory", - g_hash_table_size (factory->icon_cache)); - } - - g_hash_table_destroy (factory->scalable_icons); - g_hash_table_destroy (factory->icon_cache); - - if (factory->thumbnail_frame != NULL) { - g_object_unref (factory->thumbnail_frame); - } - - nautilus_icon_theme_destroy (factory->theme.current); - nautilus_icon_theme_destroy (factory->theme.fallback); - - EEL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object)); -} static gboolean nautilus_icon_factory_possibly_free_cached_icon (gpointer key, @@ -658,9 +465,9 @@ nautilus_icon_factory_possibly_free_cached_icon (gpointer key, gpointer user_data) { CacheIcon *icon; - + icon = value; - + /* Don't free a cache entry that is in the recently used list. */ if (icon->recently_used_node.next != NULL) { return FALSE; @@ -672,32 +479,9 @@ nautilus_icon_factory_possibly_free_cached_icon (gpointer key, } /* Free the item. */ - return nautilus_icon_factory_destroy_cached_icon (key, value, NULL); + return TRUE; } -/* Remove icons whose URI field matches the passed-in URI. */ -static gboolean -nautilus_icon_factory_remove_if_uri_matches (gpointer key, - gpointer value, - gpointer user_data) -{ - char *image_uri; - CacheKey *cache_key; - CacheIcon *icon; - - cache_key = key; - icon = value; - image_uri = user_data; - - /* See if the the uri's match - if not, just return. */ - if (cache_key->scalable_icon->uri != NULL - && strcmp (cache_key->scalable_icon->uri, image_uri)) { - return FALSE; - } - - /* Free the item. */ - return nautilus_icon_factory_destroy_cached_icon (key, value, NULL); -} /* Sweep the cache, freeing any icons that are not in use and are * also not recently used. @@ -713,19 +497,14 @@ nautilus_icon_factory_sweep (gpointer user_data) nautilus_icon_factory_possibly_free_cached_icon, NULL); - factory->sweep_timer = 0; - - return FALSE; + factory->sweep_timer = 0; + return FALSE; } /* Schedule a timer to do a sweep. */ static void -nautilus_icon_factory_schedule_sweep (void) +nautilus_icon_factory_schedule_sweep (NautilusIconFactory *factory) { - NautilusIconFactory *factory; - - factory = get_icon_factory (); - if (factory->sweep_timer != 0) { return; } @@ -735,155 +514,109 @@ nautilus_icon_factory_schedule_sweep (void) factory); } -/* Clear a specific icon from the cache. */ -void -nautilus_icon_factory_remove_by_uri (const char *image_uri) +/* Move this item to the head of the recently-used list, + * bumping the last item off that list if necessary. + */ +static void +mark_recently_used (CircularList *node) { NautilusIconFactory *factory; + CircularList *head, *last_node; - /* build the key and look it up in the icon cache */ + check_recently_used_list (); factory = get_icon_factory (); - g_hash_table_foreach_remove (factory->icon_cache, - nautilus_icon_factory_remove_if_uri_matches, - (gpointer) image_uri); -} + head = &factory->recently_used_dummy_head; -static char * -get_mime_type_icon_without_suffix (const char *mime_type) -{ - return nautilus_remove_icon_file_name_suffix (gnome_vfs_mime_get_icon (mime_type)); -} + /* Move the node to the start of the list. */ + if (node->prev != head) { + if (node->next != NULL) { + /* Remove the node from its current position in the list. */ + node->next->prev = node->prev; + node->prev->next = node->next; + } else { + /* Node was not already in the list, so add it. + * If the list is already full, remove the last node. + */ + if (factory->recently_used_count < ICON_CACHE_COUNT) { + factory->recently_used_count += 1; + } else { + /* Remove the last node. */ + last_node = head->prev; -static char * -make_icon_name_from_mime_type (const char *mime_type) -{ - char *mime_type_without_slashes, *icon_name; +#if CACHE_SELF_CHECKS + g_assert (last_node != head); + g_assert (last_node != node); +#endif + + head->prev = last_node->prev; + last_node->prev->next = head; - if (mime_type == NULL) { - return NULL; + last_node->prev = NULL; + last_node->next = NULL; + } + } + + /* Insert the node at the head of the list. */ + node->prev = head; + node->next = head->next; + node->next->prev = node; + head->next = node; } - mime_type_without_slashes = eel_str_replace_substring - (mime_type, "/", "-"); - icon_name = g_strconcat ("gnome-", mime_type_without_slashes, NULL); - g_free (mime_type_without_slashes); - return icon_name; + check_recently_used_list (); } -/* Use the MIME type to get the icon name. */ -static char * -get_icon_name_for_regular_file (NautilusFile *file) +static gboolean +remove_all (gpointer key, gpointer value, gpointer user_data) { - const char *icon_name; - char *mime_type, *uri; - - mime_type = nautilus_file_get_mime_type (file); - uri = nautilus_file_get_uri (file); - - /* don't use the executable icon for text files, since it's more useful to display - *embedded text - */ - if (nautilus_file_is_executable (file) && eel_strcasecmp (mime_type, "text/plain") != 0) { - icon_name = ICON_NAME_EXECUTABLE; - } else { - icon_name = ICON_NAME_REGULAR; - } - - g_free (uri); - g_free (mime_type); - - return g_strdup (icon_name); + /* Tell the caller to remove the hash table entry. */ + return TRUE; } -/* Use the MIME type to get the icon name. */ -static char * -get_icon_name_for_directory (NautilusFile *file) +/* Reset the cache to the default state. */ +static void +nautilus_icon_factory_clear (void) { - const char *icon_name; - char *mime_type; - - mime_type = nautilus_file_get_mime_type (file); - - if (eel_strcasecmp (mime_type, "x-directory/search") == 0) { - icon_name = ICON_NAME_SEARCH_RESULTS; - } else { - icon_name = ICON_NAME_DIRECTORY; - } + NautilusIconFactory *factory; + CircularList *head; - g_free (mime_type); - - return g_strdup (icon_name); -} - + factory = get_icon_factory (); -/* Get the icon name for a file. */ -static char * -get_icon_name_for_file (NautilusFile *file) -{ - char *uri; + g_hash_table_foreach_remove (factory->icon_cache, + remove_all, + NULL); - uri = nautilus_file_get_uri (file); - if (strcmp (uri, EEL_TRASH_URI) == 0) { - g_free (uri); - return g_strdup (nautilus_trash_monitor_is_empty () - ? ICON_NAME_TRASH_EMPTY : ICON_NAME_TRASH_NOT_EMPTY); - } - g_free (uri); - - /* Get an icon name based on the file's type. */ - switch (nautilus_file_get_file_type (file)) { - case GNOME_VFS_FILE_TYPE_DIRECTORY: - return get_icon_name_for_directory (file); - case GNOME_VFS_FILE_TYPE_FIFO: - return g_strdup (ICON_NAME_FIFO); - case GNOME_VFS_FILE_TYPE_SOCKET: - return g_strdup (ICON_NAME_SOCKET); - case GNOME_VFS_FILE_TYPE_CHARACTER_DEVICE: - return g_strdup (ICON_NAME_CHARACTER_DEVICE); - case GNOME_VFS_FILE_TYPE_BLOCK_DEVICE: - return g_strdup (ICON_NAME_BLOCK_DEVICE); - case GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK: - /* Non-broken symbolic links return the target's type. */ - return g_strdup (ICON_NAME_BROKEN_SYMBOLIC_LINK); - case GNOME_VFS_FILE_TYPE_REGULAR: - case GNOME_VFS_FILE_TYPE_UNKNOWN: - default: - return get_icon_name_for_regular_file (file); - } + /* Empty out the recently-used list. */ + head = &factory->recently_used_dummy_head; + g_assert (factory->recently_used_count == 0); + g_assert (head->next == head); + g_assert (head->prev == head); } static void -icon_theme_changed_callback (gpointer user_data) +nautilus_icon_factory_finalize (GObject *object) { - char *icon_theme, *icon_fallback_theme; NautilusIconFactory *factory; - gboolean changed; - icon_theme = nautilus_theme_get_theme_data ("icons", "icon_theme"); - if (icon_theme == NULL) { - icon_theme = eel_preferences_get (NAUTILUS_PREFERENCES_THEME); - } - icon_fallback_theme = nautilus_theme_get_theme_data ("icon-images", "default_theme"); + factory = NAUTILUS_ICON_FACTORY (object); - if (icon_fallback_theme == NULL) { - icon_fallback_theme = g_strdup ("default"); + if (factory->icon_cache) { + g_hash_table_destroy (factory->icon_cache); + factory->icon_cache = NULL; } - - factory = get_icon_factory (); - - changed = nautilus_icon_theme_set_names (factory->theme.current, icon_theme); - changed |= nautilus_icon_theme_set_names (factory->theme.fallback, icon_fallback_theme); - - if (changed) { - nautilus_icon_factory_clear (); - load_thumbnail_frame (factory); - g_signal_emit (factory, - signals[ICONS_CHANGED], 0); + + if (factory->thumbnail_frame != NULL) { + g_object_unref (factory->thumbnail_frame); + factory->thumbnail_frame = NULL; } - g_free (icon_theme); - g_free (icon_fallback_theme); + if (factory->fallback_icon) { + g_assert (factory->fallback_icon->ref_count == 1); + cache_icon_unref (factory->fallback_icon); + } + + EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); } static void @@ -912,348 +645,99 @@ mime_type_data_changed_callback (GnomeVFSMIMEMonitor *monitor, gpointer user_dat signals[ICONS_CHANGED], 0); } -/* Decompose a scalable icon into its text pieces. */ -void -nautilus_scalable_icon_get_text_pieces (NautilusScalableIcon *icon, - char **uri_return, - char **mime_type_return, - char **name_return, - char **modifier_return, - char **embedded_text_return) -{ - g_return_if_fail (icon != NULL); - - if (uri_return != NULL) { - *uri_return = g_strdup (icon->uri); - } - if (mime_type_return != NULL) { - *mime_type_return = g_strdup (icon->mime_type); - } - if (name_return != NULL) { - *name_return = g_strdup (icon->name); - } - if (modifier_return != NULL) { - *modifier_return = g_strdup (icon->modifier); - } - if (embedded_text_return != NULL) { - *embedded_text_return = g_strdup (icon->embedded_text); - } -} - -/* Get or create a scalable icon from text pieces. */ -NautilusScalableIcon * -nautilus_scalable_icon_new_from_text_pieces (const char *uri, - const char *mime_type, - const char *name, - const char *modifier, - const char *embedded_text) -{ - GHashTable *hash_table; - NautilusScalableIcon cache_key, *icon; - NautilusIconFactory *factory; - - factory = get_icon_factory (); - /* Make empty strings canonical. */ - if (uri != NULL && uri[0] == '\0') { - uri = NULL; - } - if (mime_type != NULL && mime_type[0] == '\0') { - mime_type = NULL; - } - if (name != NULL && name[0] == '\0') { - name = NULL; - } - if (modifier != NULL && modifier[0] == '\0') { - modifier = NULL; - } - if (embedded_text != NULL && embedded_text[0] == '\0') { - embedded_text = NULL; - } - - /* Get at the hash table. */ - hash_table = factory->scalable_icons; - - /* Check to see if it's already in the table. */ - cache_key.uri = (char *) uri; - cache_key.mime_type = (char *) mime_type; - cache_key.name = (char *) name; - cache_key.modifier = (char *) modifier; - cache_key.embedded_text = (char *) embedded_text; - - icon = g_hash_table_lookup (hash_table, &cache_key); - if (icon == NULL) { - /* Not in the table, so create it and put it in. */ - icon = g_new0 (NautilusScalableIcon, 1); - icon->uri = g_strdup (uri); - icon->mime_type = g_strdup (mime_type); - icon->name = g_strdup (name); - icon->modifier = g_strdup (modifier); - icon->embedded_text = g_strdup (embedded_text); - g_hash_table_insert (hash_table, icon, icon); - } - - /* Grab a reference and return it. */ - nautilus_scalable_icon_ref (icon); - return icon; -} - -void -nautilus_scalable_icon_ref (NautilusScalableIcon *icon) -{ - g_return_if_fail (icon != NULL); - - icon->ref_count++; -} - -void -nautilus_scalable_icon_unref (NautilusScalableIcon *icon) -{ - GHashTable *hash_table; - - g_return_if_fail (icon != NULL); - g_return_if_fail (icon->ref_count != 0); - - if (--icon->ref_count != 0) { - return; - } - - hash_table = get_icon_factory ()->scalable_icons; - g_hash_table_remove (hash_table, icon); - - g_free (icon->uri); - g_free (icon->mime_type); - g_free (icon->name); - g_free (icon->modifier); - g_free (icon->embedded_text); - g_free (icon); -} - -static guint -nautilus_scalable_icon_hash (gconstpointer p) -{ - const NautilusScalableIcon *icon; - guint hash; - - icon = p; - hash = 0; - - if (icon->uri != NULL) { - hash = g_str_hash (icon->uri); - } - - hash <<= 4; - if (icon->mime_type != NULL) { - hash ^= g_str_hash (icon->mime_type); - } - - hash <<= 4; - if (icon->name != NULL) { - hash ^= g_str_hash (icon->name); - } - - hash <<= 4; - if (icon->modifier != NULL) { - hash ^= g_str_hash (icon->modifier); - } - - hash <<= 4; - if (icon->embedded_text != NULL) { - hash ^= g_str_hash (icon->embedded_text); - } - - return hash; -} - -static gboolean -nautilus_scalable_icon_equal (gconstpointer a, - gconstpointer b) -{ - const NautilusScalableIcon *icon_a, *icon_b; - - icon_a = a; - icon_b = b; - - return eel_strcmp (icon_a->uri, icon_b->uri) == 0 - && eel_strcmp (icon_a->mime_type, icon_b->mime_type) == 0 - && eel_strcmp (icon_a->name, icon_b->name) == 0 - && eel_strcmp (icon_a->modifier, icon_b->modifier) == 0 - && eel_strcmp (icon_a->embedded_text, icon_b->embedded_text) == 0; -} - -static gboolean -should_display_image_file_as_itself (NautilusFile *file) +static char * +nautilus_remove_icon_file_name_suffix (const char *icon_name) { - static int show_thumbnails_auto_value; - static gboolean show_thumbnail_auto_value_registered; + guint i; + const char *suffix; + static const char *icon_file_name_suffixes[] = { ".svg", ".png", ".jpg" }; - if (!show_thumbnail_auto_value_registered) { - eel_preferences_add_auto_enum (NAUTILUS_PREFERENCES_SHOW_IMAGE_FILE_THUMBNAILS, - &show_thumbnails_auto_value); - show_thumbnail_auto_value_registered = TRUE; - } - - /* see if there's a proxy thumbnail to indicate that thumbnailing - * failed, in which case we shouldn't use the thumbnail. - */ - if (nautilus_thumbnail_has_invalid_thumbnail (file)) { - return FALSE; - } - - /* if we don't have read permissions for the file, don't show as itself for security reasons */ - if (!nautilus_file_can_read (file)) { - return FALSE; - } - - if (show_thumbnails_auto_value == NAUTILUS_SPEED_TRADEOFF_ALWAYS) { - return TRUE; - } - - if (show_thumbnails_auto_value == NAUTILUS_SPEED_TRADEOFF_NEVER) { - return FALSE; + for (i = 0; i < G_N_ELEMENTS (icon_file_name_suffixes); i++) { + suffix = icon_file_name_suffixes[i]; + if (eel_str_has_suffix (icon_name, suffix)) { + return eel_str_strip_trailing_str (icon_name, suffix); + } } - - g_assert (show_thumbnails_auto_value == NAUTILUS_SPEED_TRADEOFF_LOCAL_ONLY); - return nautilus_file_is_local (file); -} - -/* return TRUE if the passed-in mime-type is one that we can thumbnail. It's - * used to exclude ones that we know will generate errors if we tried them. - */ -static gboolean -is_supported_mime_type (const char *mime_type) -{ - /* exclude xfig images, since we can't handle them */ - if (eel_strcmp (mime_type, "image/x-xfig") == 0) { - return FALSE; - } - return TRUE; + return g_strdup (icon_name); } -static void -image_uri_to_name_or_uri (const char *image_uri, - char **icon_name, - char **uri) +static char * +image_uri_to_name_or_uri (const char *image_uri) { char *icon_path; - if (image_uri == NULL) { - return; - } - - /* FIXME bugzilla.eazel.com 2564: All custom icons must be in file:. */ icon_path = gnome_vfs_get_local_path_from_uri (image_uri); if (icon_path == NULL && image_uri[0] == '/') { icon_path = g_strdup (image_uri); } if (icon_path != NULL) { - if (*uri == NULL) { - *uri = gnome_vfs_get_uri_from_local_path (icon_path); - } - g_free (icon_path); + return icon_path; } else if (strpbrk (image_uri, ":/") == NULL) { - *icon_name = nautilus_remove_icon_file_name_suffix (image_uri); - } -} -static gboolean -nautilus_gdk_pixbuf_supported (char *mime_type) -{ - guint i; - static GHashTable *formats = NULL; - static const char *types [] = { - "image/x-bmp", "image/x-ico", "image/jpeg", - "image/png", "image/pnm", "image/ras", "image/tga", - "image/tiff", "image/wbmp", "image/x-xbitmap", - "image/x-xpixmap" - }; - - if (!formats) { - formats = g_hash_table_new (g_str_hash, g_str_equal); - - for (i = 0; i < G_N_ELEMENTS (types); i++) - g_hash_table_insert (formats, - (gpointer) types [i], - GUINT_TO_POINTER (1)); + return nautilus_remove_icon_file_name_suffix (image_uri); } - - if (g_hash_table_lookup (formats, mime_type)) - return TRUE; - - return FALSE; + return NULL; } -/* key routine to get the scalable icon for a file */ -NautilusScalableIcon * -nautilus_icon_factory_get_icon_for_file (NautilusFile *file, const char *modifier) +/* key routine to get the icon for a file */ +char * +nautilus_icon_factory_get_icon_for_file (NautilusFile *file) { - char *uri, *custom_uri, *file_uri, *icon_name, *mime_type, *top_left_text; - int file_size; + char *custom_uri, *file_uri, *icon_name, *mime_type, *custom_icon; gboolean is_local; - NautilusScalableIcon *scalable_icon; - + NautilusIconFactory *factory; + GnomeIconLookupResultFlags lookup_result; + GnomeVFSFileInfo *file_info; + if (file == NULL) { return NULL; } - icon_name = NULL; - uri = NULL; + factory = get_icon_factory (); + + custom_icon = NULL; /* if there is a custom image in the metadata or link info, use that. */ - custom_uri = nautilus_file_get_custom_icon_uri (file); - image_uri_to_name_or_uri (custom_uri, &icon_name, &uri); + custom_uri = nautilus_file_get_custom_icon (file); + if (custom_uri) { + custom_icon = image_uri_to_name_or_uri (custom_uri); + } g_free (custom_uri); file_uri = nautilus_file_get_uri (file); is_local = nautilus_file_is_local (file); mime_type = nautilus_file_get_mime_type (file); - /* if the file is an image, either use the image itself as the icon if it's small enough, - or use a thumbnail if one exists. If it's too large, don't try to thumbnail it at all. - If a thumbnail is required, but does not yet exist, put an entry on the thumbnail queue so we - eventually make one */ - - /* also, dont make thumbnails for images in the thumbnails directory */ - if (uri == NULL) { - file_size = nautilus_file_get_size (file); - if (eel_istr_has_prefix (mime_type, "image/") - && is_supported_mime_type (mime_type) - && should_display_image_file_as_itself (file)) { - if (file_size < SELF_THUMBNAIL_SIZE_THRESHOLD && is_local - && nautilus_gdk_pixbuf_supported (mime_type)) - uri = nautilus_file_get_uri (file); - - if (uri == NULL && /* handle SVG files */ - !strcmp (mime_type, "image/svg")) { - uri = g_strdup (file_uri); - } - - if (uri == NULL && strstr (file_uri, "/.thumbnails/") == NULL - && file_size < cached_thumbnail_limit) { - uri = nautilus_get_thumbnail_uri (file); - if (uri == NULL) { - icon_name = g_strdup (ICON_NAME_THUMBNAIL_LOADING); - } - } - } + file_info = nautilus_file_peek_vfs_file_info (file); + + icon_name = gnome_icon_lookup (factory->icon_loader, + factory->thumbnail_factory, + file_uri, + custom_icon, + nautilus_file_peek_vfs_file_info (file), + mime_type, + GNOME_ICON_LOOKUP_FLAGS_EMBEDDING_TEXT | + GNOME_ICON_LOOKUP_FLAGS_SHOW_SMALL_IMAGES_AS_THEMSELVES, + &lookup_result); + + + /* Create thumbnails if we can, and if the looked up icon isn't a thumbnail + or an absolute pathname (custom icon or image as itself) */ + if (!(lookup_result & GNOME_ICON_LOOKUP_RESULT_FLAGS_THUMBNAIL) && + icon_name[0] != '/' && file_info && + gnome_thumbnail_factory_can_thumbnail (factory->thumbnail_factory, + file_uri, + mime_type, + file_info->mtime)) { + nautilus_create_thumbnail (file); + g_free (icon_name); + icon_name = g_strdup (ICON_NAME_THUMBNAIL_LOADING); } - /* Get the generic icon set for this file. */ g_free (file_uri); - if (icon_name == NULL) { - icon_name = get_icon_name_for_file (file); - } - - top_left_text = nautilus_file_get_top_left_text (file); - - /* Create the icon or find it in the cache if it's already there. */ - scalable_icon = nautilus_scalable_icon_new_from_text_pieces - (uri, mime_type, icon_name, modifier, top_left_text); - - g_free (uri); + g_free (custom_icon); g_free (mime_type); - g_free (icon_name); - g_free (top_left_text); - return scalable_icon; + return icon_name; } /** @@ -1337,18 +821,14 @@ nautilus_icon_factory_is_basic_icon_ready_for_file (NautilusFile *file) return result; } -NautilusScalableIcon * +char * nautilus_icon_factory_get_emblem_icon_by_name (const char *emblem_name) { - NautilusScalableIcon *scalable_icon; char *name_with_prefix; name_with_prefix = g_strconcat (NAUTILUS_EMBLEM_NAME_PREFIX, emblem_name, NULL); - scalable_icon = nautilus_scalable_icon_new_from_text_pieces - (NULL, NULL, name_with_prefix, NULL, NULL); - g_free (name_with_prefix); - return scalable_icon; + return name_with_prefix; } GList * @@ -1357,7 +837,7 @@ nautilus_icon_factory_get_emblem_icons_for_file (NautilusFile *file, { GList *icons, *emblem_names, *node; char *uri, *name; - NautilusScalableIcon *icon; + char *icon; gboolean file_is_trash; /* Leave out the trash emblem for the trash itself, since @@ -1441,84 +921,59 @@ get_smaller_icon_size (guint size) return NAUTILUS_ICON_SIZE_SMALLEST; } -/* Return true if there is another size to try. - * Set the size pointed to by @current_size to 0 to start. - */ -static gboolean -get_next_icon_size_to_try (guint target_size, guint *current_size) -{ - guint size; - /* Get next larger size. */ - size = *current_size; - if (size == 0 || size >= target_size) { - if (size == 0 && target_size != 0) { - size = target_size - 1; - } - if (size < NAUTILUS_ICON_SIZE_LARGEST) { - *current_size = get_larger_icon_size (size); - return TRUE; - } - size = target_size; +static void +scale_icon_data (GnomeIconData *icon_data, + double scale_x, + double scale_y) +{ + int num_points, i; + + if (icon_data->has_embedded_rect) { + icon_data->x0 = icon_data->x0 * scale_x; + icon_data->y0 = icon_data->y0 * scale_y; + icon_data->x1 = icon_data->x1 * scale_x; + icon_data->y1 = icon_data->y1 * scale_y; } - - /* Already hit the largest size, get the next smaller size instead. */ - if (size > NAUTILUS_ICON_SIZE_SMALLEST) { - *current_size = get_smaller_icon_size (size); - return TRUE; + + num_points = icon_data->n_attach_points; + for (i = 0; i < num_points; i++) { + icon_data->attach_points[i].x = icon_data->attach_points[i].x * scale_x; + icon_data->attach_points[i].y = icon_data->attach_points[i].y * scale_y; } - - /* Tried them all. */ - return FALSE; } + /* This loads an SVG image, scaling it to the appropriate size. */ static GdkPixbuf * -load_pixbuf_svg (const char *path, guint size_in_pixels, guint max_size_in_pixels, gboolean is_emblem, - NautilusIconDetails *details, NautilusIconDetails *scalable_details) - +load_pixbuf_svg (const char *path, + guint size_in_pixels, + guint base_size, + GnomeIconData *icon_data) { - double actual_size_in_pixels, zoom; - int rect_width, rect_height, num_points, i; + double zoom; int width, height; GdkPixbuf *pixbuf; - - /* FIXME: the nominal size of .svg emblems is too large, so we scale it - * down here if the file is an emblem. This code should be removed - * when we scale all the emblems properly. - */ - if (is_emblem) { - actual_size_in_pixels = size_in_pixels * EMBLEM_SCALE_FACTOR; + + if (base_size != 0) { + zoom = (double)size_in_pixels / base_size; + + pixbuf = rsvg_pixbuf_from_file_at_zoom_with_max (path, zoom, zoom, NAUTILUS_ICON_MAXIMUM_SIZE, NAUTILUS_ICON_MAXIMUM_SIZE, NULL); } else { - actual_size_in_pixels = size_in_pixels; + pixbuf = rsvg_pixbuf_from_file_at_max_size (path, + size_in_pixels, + size_in_pixels, + NULL); } - zoom = actual_size_in_pixels / NAUTILUS_ICON_SIZE_STANDARD; - - pixbuf = rsvg_pixbuf_from_file_at_zoom_with_max (path, zoom, zoom, max_size_in_pixels, max_size_in_pixels, NULL); if (pixbuf == NULL) { return NULL; } - if (details && scalable_details) { + if (icon_data != NULL) { width = gdk_pixbuf_get_width (pixbuf); height = gdk_pixbuf_get_height (pixbuf); - if (scalable_details->text_rect.x0 != scalable_details->text_rect.x1) { - rect_width = (scalable_details->text_rect.x1 - scalable_details->text_rect.x0); - rect_height = (scalable_details->text_rect.y1 - scalable_details->text_rect.y0); - - details->text_rect.x0 = scalable_details->text_rect.x0 * width / 1000; - details->text_rect.y0 = scalable_details->text_rect.y0 * height / 1000; - details->text_rect.x1 = details->text_rect.x0 + rect_width * width / 1000; - details->text_rect.y1 = details->text_rect.y0 + rect_height * height / 1000; - } - - num_points = scalable_details->attach_points.num_points; - details->attach_points.num_points = num_points; - for (i = 0; i < num_points; i++) { - details->attach_points.points[i].x = scalable_details->attach_points.points[i].x * width / 1000; - details->attach_points.points[i].y = scalable_details->attach_points.points[i].y * height / 1000; - } + scale_icon_data (icon_data, width / 1000.0, height / 1000.0); } return pixbuf; } @@ -1534,495 +989,213 @@ path_represents_svg_image (const char *path) return path != NULL && strstr (path, ".svg") != NULL; } -/* Returns GNOME_VFS_ERROR_NOT_SUPPORTED for icons that are not files. */ -static GnomeVFSResult -get_cache_time_and_size (const char *file_uri, - time_t *cache_time, - GnomeVFSFileSize *cache_size) -{ - GnomeVFSURI *vfs_uri; - GnomeVFSFileInfo *file_info; - GnomeVFSResult result; - gboolean is_local; - - /* If there's no specific file, simply return. */ - if (file_uri == NULL) { - return GNOME_VFS_ERROR_NOT_SUPPORTED; - } - - /* FIXME bugzilla.gnome.org 42566: if the URI is remote, assume - * it's valid to match logic below. - */ - vfs_uri = gnome_vfs_uri_new (file_uri); - is_local = gnome_vfs_uri_is_local (vfs_uri); - gnome_vfs_uri_unref (vfs_uri); - if (!is_local) { - return GNOME_VFS_ERROR_NOT_SUPPORTED; - } - - /* Gather the info and then compare modification times. */ - file_info = gnome_vfs_file_info_new (); - result = gnome_vfs_get_file_info (file_uri, file_info, GNOME_VFS_FILE_INFO_FOLLOW_LINKS); - if (result == GNOME_VFS_OK) { - *cache_time = file_info->mtime; - *cache_size = file_info->size; - } - gnome_vfs_file_info_unref (file_info); - - return result; -} - static GdkPixbuf * -load_icon_from_path (const char *path, - guint size_in_pixels, - guint max_size_in_pixels, - gboolean custom, - gboolean is_emblem /* for emblem scaling hack only */, - NautilusIconDetails *details, - NautilusIconDetails *scalable_details) +scale_icon (GdkPixbuf *pixbuf, + double *scale) { - /* Get the icon. */ - if (path == NULL) { - return NULL; - } - if (path_represents_svg_image (path)) { - return load_pixbuf_svg (path, size_in_pixels, max_size_in_pixels, is_emblem, - details, scalable_details); - } - - /* Custom non-svg icons exist at one size. - * Non-custom icons have their size encoded in their path. - */ - if (custom && size_in_pixels != NAUTILUS_ICON_SIZE_STANDARD) { - return NULL; - } - - /* if it's a thumbnail, frame it if necessary */ - if (strstr (path, "/.nautilus/thumbnails/") != NULL) { - return nautilus_thumbnail_load_framed_image (path); - } - - return gdk_pixbuf_new_from_file (path, NULL); -} - -static GdkPixbuf * -load_named_icon (const char *name, - const char *modifier, - guint size_in_pixels, - guint max_size_in_pixels, - NautilusIconDetails *details) -{ - char *path; - GdkPixbuf *pixbuf; - NautilusIconDetails scalable_details; - - memset (&scalable_details, 0, sizeof (scalable_details)); - - path = nautilus_get_icon_file_name (&get_icon_factory ()->theme, - name, modifier, size_in_pixels, - details, &scalable_details); - - pixbuf = load_icon_from_path (path, size_in_pixels, max_size_in_pixels, FALSE, - eel_str_has_prefix (name, NAUTILUS_EMBLEM_NAME_PREFIX), - details, &scalable_details); + guint width, height; - g_free (path); + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); - if (pixbuf == NULL) { - memset (&details, 0, sizeof (details)); + if ((int) (width * *scale) > NAUTILUS_ICON_MAXIMUM_SIZE || + (int) (height * *scale) > NAUTILUS_ICON_MAXIMUM_SIZE) { + *scale = MIN ((double) NAUTILUS_ICON_MAXIMUM_SIZE / width, + (double) NAUTILUS_ICON_MAXIMUM_SIZE / height); } - return pixbuf; -} -static gboolean -is_generic_icon_name (const char *name) -{ - return eel_strcmp (name, ICON_NAME_EXECUTABLE) == 0 - || eel_strcmp (name, ICON_NAME_REGULAR) == 0; + width = floor (width * *scale + 0.5); + height = floor (height * *scale + 0.5); + + return gdk_pixbuf_scale_simple (pixbuf, width, height, GDK_INTERP_BILINEAR); } -/* This load function returns NULL if the icon is not available at - * this size. - */ -static CacheIcon * -load_specific_icon (NautilusScalableIcon *scalable_icon, - guint size_in_pixels, - guint max_size_in_pixels, - IconRequest type) -{ - NautilusIconDetails details; - GdkPixbuf *pixbuf; - char *mime_type_icon_name, *path; - const char *first_choice_name, *second_choice_name, *real_name; - CacheIcon *icon; - - memset (&details, 0, sizeof (details)); - pixbuf = NULL; - - /* Get the path. */ - if (type == REQUEST_PICKY_CUSTOM_ONLY) { - if (scalable_icon->uri == NULL) - return NULL; - /* We don't support custom icons that are not local here. */ - path = gnome_vfs_get_local_path_from_uri (scalable_icon->uri); - pixbuf = load_icon_from_path (path, size_in_pixels, max_size_in_pixels, TRUE, FALSE, NULL, NULL); - g_free (path); +static GdkPixbuf * +load_icon_file (char *filename, + guint base_size, + guint nominal_size, + GnomeIconData *icon_data) +{ + GdkPixbuf *pixbuf, *scaled_pixbuf; + int width, height, size; + double scale; + gboolean is_thumbnail; + + if (path_represents_svg_image (filename)) { + pixbuf = load_pixbuf_svg (filename, + nominal_size, + base_size, + icon_data); } else { - mime_type_icon_name = get_mime_type_icon_without_suffix (scalable_icon->mime_type); - if (mime_type_icon_name == NULL) { - mime_type_icon_name = make_icon_name_from_mime_type (scalable_icon->mime_type); - } - if (is_generic_icon_name (scalable_icon->name)) { - first_choice_name = mime_type_icon_name; - second_choice_name = scalable_icon->name; + is_thumbnail = strstr (filename, "/.thumbnails/") != NULL; + + /* FIXME: Maybe we shouldn't have to load the file each time + * Not sure if that is important */ + if (is_thumbnail) { + pixbuf = nautilus_thumbnail_load_framed_image (filename); } else { - first_choice_name = scalable_icon->name; - second_choice_name = mime_type_icon_name; + pixbuf = gdk_pixbuf_new_from_file (filename, NULL); } - real_name = (type == REQUEST_PICKY_BY_NAME_FIRST_CHOICE - ? first_choice_name : second_choice_name); - - pixbuf = load_named_icon - (real_name, - scalable_icon->modifier, - size_in_pixels, max_size_in_pixels, - &details); - g_free (mime_type_icon_name); - } - - if (pixbuf == NULL) { - return NULL; - } - - /* Since we got something, we can create a cache icon. */ - icon = cache_icon_new (pixbuf, type, FALSE, &details); - get_cache_time_and_size (scalable_icon->uri, &icon->cache_time, &icon->cache_size); - g_object_unref (pixbuf); - - return icon; -} - -static void -destroy_fallback_icon (void) -{ -#if 0 - CacheIcon *icon; - - icon = fallback_icon; - g_assert (icon->internal_ref_count == 0); - cache_icon_ref (icon); - fallback_icon = NULL; - cache_icon_unref (icon); -#endif -} - -/* This load function is not allowed to return NULL. */ -static CacheIcon * -load_icon_for_scaling (NautilusScalableIcon *scalable_icon, - const IconSizeRequest *size, - guint *actual_size_result) -{ - CacheIcon *icon; - guint actual_size; - IconSizeRequest size_request; - GdkPixbuf *pixbuf; - guint i; - guint requested_size; - const IconRequest requests[] = { - REQUEST_PICKY_CUSTOM_ONLY, - REQUEST_PICKY_BY_NAME_FIRST_CHOICE, - REQUEST_PICKY_BY_NAME_SECOND_CHOICE - }; - - requested_size = size->nominal_width; - size_request.maximum_width = MAXIMUM_ICON_SIZE * requested_size / NAUTILUS_ICON_SIZE_STANDARD; - size_request.maximum_height = size_request.maximum_width; - - for (i = 0; i < G_N_ELEMENTS (requests); i++) { - /* Try an exact lookup. This will handle the case where - * there is an SVG icon and we want to render it - * immediately at the correct size - */ - icon = get_icon_from_cache (scalable_icon, size, requests[i]); - if (icon != NULL) { - *actual_size_result = size->nominal_width; - return icon; + if (pixbuf == NULL) { + return NULL; } - actual_size = 0; - while (get_next_icon_size_to_try (requested_size, &actual_size)) { - size_request.nominal_width = actual_size; - size_request.nominal_height = actual_size; - - icon = get_icon_from_cache (scalable_icon, &size_request, requests[i]); - if (icon != NULL) { - *actual_size_result = actual_size; - return icon; + if (base_size == 0) { + if (is_thumbnail) { + base_size = 128 * NAUTILUS_ICON_SIZE_STANDARD / NAUTILUS_ICON_SIZE_THUMBNAIL; + } else { + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + size = MAX (width, height); + if (size > NAUTILUS_ICON_SIZE_STANDARD + 5) { + base_size = size; + } else { + /* Don't scale up small icons */ + base_size = NAUTILUS_ICON_SIZE_STANDARD; + } } } + + if (base_size != nominal_size) { + scale = (double)nominal_size/base_size; + scaled_pixbuf = scale_icon (pixbuf, &scale); + if (icon_data != NULL) { + scale_icon_data (icon_data, scale, scale); + } + g_object_unref (pixbuf); + pixbuf = scaled_pixbuf; + } } - /* Fall back on the hard-coded image. */ - if (fallback_icon != NULL) { - cache_icon_ref (fallback_icon); - } else { - pixbuf = gdk_pixbuf_new_from_data - (nautilus_default_file_icon, - GDK_COLORSPACE_RGB, - TRUE, - 8, - nautilus_default_file_icon_width, - nautilus_default_file_icon_height, - nautilus_default_file_icon_width * 4, /* stride */ - NULL, /* don't destroy data */ - NULL); - fallback_icon = cache_icon_new (pixbuf, FALSE, FALSE, NULL); - fallback_icon->is_fallback = TRUE; - eel_debug_call_at_shutdown (destroy_fallback_icon); - } - - *actual_size_result = NAUTILUS_ICON_SIZE_STANDARD; - return fallback_icon; + return pixbuf; } -/* Consumes the icon and returns a scaled one if the pixbuf is too big. - * Note that this does an unref on the icon and returns a new one. - */ static CacheIcon * -scale_icon (CacheIcon *icon, - double scale_x, - double scale_y) +create_embedded_text_cache_icon (CacheIcon *base_icon, + const char *embedded_text) { - int width, height; - int rect_width, rect_height; - int i, num_points; - GdkPixbuf *scaled_pixbuf; - NautilusIconDetails scaled_details; - CacheIcon *scaled_icon; - - g_assert (!icon->scaled); - - width = gdk_pixbuf_get_width (icon->pixbuf); - height = gdk_pixbuf_get_height (icon->pixbuf); - - /* Check for no-scaling case. */ - if ((int) (width * scale_x) == width - && (int) (height * scale_y) == height) { - return NULL; - } - - width *= scale_x; - if (width < 1) { - width = 1; - } - height *= scale_y; - if (height < 1) { - height = 1; - } - - scaled_pixbuf = gdk_pixbuf_scale_simple - (icon->pixbuf, width, height, GDK_INTERP_BILINEAR); - - rect_width = (icon->details.text_rect.x1 - icon->details.text_rect.x0) * scale_x; - rect_height = (icon->details.text_rect.y1 - icon->details.text_rect.y0) * scale_y; - - scaled_details.text_rect.x0 = icon->details.text_rect.x0 * scale_x; - scaled_details.text_rect.y0 = icon->details.text_rect.y0 * scale_y; - scaled_details.text_rect.x1 = scaled_details.text_rect.x0 + rect_width; - scaled_details.text_rect.y1 = scaled_details.text_rect.y0 + rect_height; - - num_points = icon->details.attach_points.num_points; - scaled_details.attach_points.num_points = num_points; - for (i = 0; i < num_points; i++) { - scaled_details.attach_points.points[i].x = icon->details.attach_points.points[i].x * scale_x; - scaled_details.attach_points.points[i].y = icon->details.attach_points.points[i].y * scale_y; - } - - scaled_icon = cache_icon_new (scaled_pixbuf, - icon->request, - TRUE, - &scaled_details); - scaled_icon->is_fallback = icon->is_fallback; - scaled_icon->cache_time = icon->cache_time; - scaled_icon->cache_size = icon->cache_size; - g_object_unref (scaled_pixbuf); - return scaled_icon; -} + GdkPixbuf *pixbuf; + GnomeIconData *icon_data; -static void -revise_scale_factors_if_too_big (GdkPixbuf *pixbuf, - const IconSizeRequest *size, - double *scale_x, - double *scale_y) -{ - guint width, height; - double y_distortion; + pixbuf = embed_text (base_icon->pixbuf, + base_icon->icon_data, + embedded_text); - width = gdk_pixbuf_get_width (pixbuf); - height = gdk_pixbuf_get_height (pixbuf); - - if ((int) (width * *scale_x) <= (int) size->maximum_width - && (int) (height * *scale_y) <= (int) size->maximum_height) { - return; + if (pixbuf) { + icon_data = NULL; + if (base_icon->icon_data) { + icon_data = g_new (GnomeIconData, 1); + *icon_data = *base_icon->icon_data; + } + return cache_icon_new (pixbuf, icon_data); + } else { + cache_icon_ref (base_icon); + return base_icon; } - - y_distortion = *scale_y / *scale_x; - - *scale_x = MIN ((double) size->maximum_width / width, - (double) size->maximum_height / (height / y_distortion)); - *scale_y = *scale_x * y_distortion; } -/* Returns a scaled icon if this one is too big. */ -static CacheIcon * -scale_down_if_too_big (CacheIcon *icon, - const IconSizeRequest *size) -{ - double scale_x, scale_y; - - scale_x = 1.0; - scale_y = 1.0; - revise_scale_factors_if_too_big (icon->pixbuf, size, &scale_x, &scale_y); - return scale_icon (icon, scale_x, scale_y); -} -/* This load function is not allowed to return NULL. */ static CacheIcon * -load_icon_scale_if_necessary (NautilusScalableIcon *scalable_icon, - const IconSizeRequest *size) -{ - CacheIcon *icon, *scaled_icon; - guint nominal_actual_size; - double scale_x, scale_y; - - /* Load the icon that's closest in size to what we want. */ - icon = load_icon_for_scaling (scalable_icon, - size, - &nominal_actual_size); - - /* Scale the pixbuf to the size we want. */ - scale_x = (double) size->nominal_width / nominal_actual_size; - scale_y = (double) size->nominal_height / nominal_actual_size; - revise_scale_factors_if_too_big (icon->pixbuf, size, &scale_x, &scale_y); - scaled_icon = scale_icon (icon, scale_x, scale_y); - if (scaled_icon == NULL) { - return icon; - } - - /* Mark this icon as scaled, too. */ - cache_icon_unref (icon); - g_assert (scaled_icon->scaled); - return scaled_icon; -} - -/* Move this item to the head of the recently-used list, - * bumping the last item off that list if necessary. - */ -static void -mark_recently_used (CircularList *node) +create_normal_cache_icon (const char *icon, + const char *modifier, + guint nominal_size) { NautilusIconFactory *factory; - CircularList *head, *last_node; - - check_recently_used_list (); - + char *filename; + char *name_with_modifier; + const GnomeIconData *src_icon_data; + GnomeIconData *icon_data; + CacheIcon *cache_icon; + GdkPixbuf *pixbuf; + int base_size; + factory = get_icon_factory (); - head = &factory->recently_used_dummy_head; - /* Move the node to the start of the list. */ - if (node->prev != head) { - if (node->next != NULL) { - /* Remove the node from its current position in the list. */ - node->next->prev = node->prev; - node->prev->next = node->next; + icon_data = NULL; + filename = NULL; + + base_size = 0; + if (icon[0] == '/') { + /* FIXME: maybe we should add modifier to the filename + * before the extension */ + if (g_file_test (icon, G_FILE_TEST_IS_REGULAR)) { + filename = g_strdup (icon); + } + } else { + if (modifier) { + name_with_modifier = g_strconcat (icon, "-", modifier, NULL); } else { - /* Node was not already in the list, so add it. - * If the list is already full, remove the last node. - */ - if (factory->recently_used_count < ICON_CACHE_COUNT) { - factory->recently_used_count += 1; - } else { - /* Remove the last node. */ - last_node = head->prev; + name_with_modifier = (char *)icon; + } - g_assert (last_node != head); - g_assert (last_node != node); - - head->prev = last_node->prev; - last_node->prev->next = head; + filename = gnome_icon_loader_lookup_icon_extended (factory->icon_loader, + name_with_modifier, + nominal_size, + &src_icon_data, + &base_size); + if (name_with_modifier != icon) { + g_free (name_with_modifier); + } - last_node->prev = NULL; - last_node->next = NULL; - } + /* Make a copy of the icon data */ + icon_data = NULL; + if (src_icon_data) { + icon_data = g_memdup (src_icon_data, sizeof (GnomeIconData)); + icon_data->display_name = g_strdup (src_icon_data->display_name); } - - /* Insert the node at the head of the list. */ - node->prev = head; - node->next = head->next; - node->next->prev = node; - head->next = node; } - check_recently_used_list (); -} - -/* Utility routine that checks if a cached thumbnail-type icon has - * changed since it was cached. Returns TRUE after removing the icon - * from the cache if the icon has changed. - */ -static gboolean -remove_icons_if_file_changed (const char *file_uri, time_t cached_time, GnomeVFSFileSize cached_size) -{ - GnomeVFSResult result; - time_t new_time; - GnomeVFSFileSize new_size; - - /* Get the time from the file. */ - result = get_cache_time_and_size (file_uri, &new_time, &new_size); - - /* Do nothing for cases where a time doesn't apply. */ - if (result == GNOME_VFS_ERROR_NOT_SUPPORTED) { - return FALSE; + if (filename == NULL) { + return NULL; } - /* Do nothing if the file is still the same as before. */ - if (result == GNOME_VFS_OK && new_time == cached_time && new_size == cached_size) { - return FALSE; + pixbuf = load_icon_file (filename, + base_size, + nominal_size, + icon_data); + + if (pixbuf == NULL) { + return NULL; } + + cache_icon = cache_icon_new (pixbuf, icon_data); - /* Remove the icon from the cache and inform the caller. */ - nautilus_icon_factory_remove_by_uri (file_uri); - return TRUE; + return cache_icon; } + /* Get the icon, handling the caching. * If @picky is true, then only an unscaled icon is acceptable. * Also, if @picky is true, the icon must be a custom icon if * @custom is true or a standard icon is @custom is false. */ static CacheIcon * -get_icon_from_cache (NautilusScalableIcon *scalable_icon, - const IconSizeRequest *size, - IconRequest type) +get_icon_from_cache (const char *icon, + const char *modifier, + const char *embedded_text, + guint nominal_size) { NautilusIconFactory *factory; GHashTable *hash_table; - CacheKey lookup_key, *key; - CacheIcon *icon, *scaled_icon; + CacheKey lookup_key; + CacheKey *key; + CacheIcon *cached_icon, *base_cached_icon; gpointer key_in_table, value; - g_return_val_if_fail (scalable_icon != NULL, NULL); + g_return_val_if_fail (icon != NULL, NULL); key = NULL; - icon = NULL; + cached_icon = NULL; factory = get_icon_factory (); hash_table = factory->icon_cache; /* Check to see if it's already in the table. */ - lookup_key.scalable_icon = scalable_icon; - lookup_key.size = *size; + lookup_key.name = (char *)icon; + lookup_key.modifier = (char *)modifier; + lookup_key.embedded_text = (char *)embedded_text; + lookup_key.nominal_size = nominal_size; if (g_hash_table_lookup_extended (hash_table, &lookup_key, &key_in_table, &value)) { @@ -2030,128 +1203,110 @@ get_icon_from_cache (NautilusScalableIcon *scalable_icon, g_assert (key_in_table != NULL); g_assert (value != NULL); key = key_in_table; - icon = value; - - /* If we're going to be picky, then don't accept anything - * other than exactly what we are looking for. - */ - if (type != REQUEST_NORMAL - && (icon->scaled || type != icon->request)) { - return NULL; - } - - /* Check if the cached image is good before using it. */ - if (remove_icons_if_file_changed (scalable_icon->uri, - icon->cache_time, - icon->cache_size)) { - icon = NULL; - } + cached_icon = value; } - if (icon == NULL) { + if (cached_icon == NULL) { /* Not in the table, so load the image. */ - /* If we're picky, then we want the image only if this exact - * nominal size is available. - */ - if (type != REQUEST_NORMAL) { - g_assert (scalable_icon->embedded_text == NULL); - - /* Actual icons have nominal sizes that are square! */ - if (size->nominal_width != size->nominal_height) { - return NULL; - } - - /* Get the image. */ - icon = load_specific_icon (scalable_icon, - size->nominal_width, - size->maximum_width, - type); - if (icon == NULL) { - return NULL; - } - - /* Now we have the image, but is it bigger - * than the maximum size? If so we scale it, - * but we don't call it "scaled" for caching - * purposese. - */ - scaled_icon = scale_down_if_too_big (icon, size); - if (scaled_icon != NULL) { - scaled_icon->scaled = FALSE; - cache_icon_unref (icon); - icon = scaled_icon; - } + + /* + g_print ("cache miss for %s:%s:%s:%d\n", + icon, modifier?modifier:"", embedded_text?"<tl>":"", nominal_size); + */ + + if (embedded_text) { + base_cached_icon = get_icon_from_cache (icon, + modifier, + NULL, + nominal_size); + cached_icon = create_embedded_text_cache_icon (base_cached_icon, + embedded_text); + cache_icon_unref (base_cached_icon); + } else { - if (scalable_icon->embedded_text != NULL) { - icon = load_icon_with_embedded_text (scalable_icon, size); - } else { - icon = load_icon_scale_if_necessary (scalable_icon, size); + cached_icon = create_normal_cache_icon (icon, + modifier, + nominal_size); + /* Try to fallback without modifier */ + if (cached_icon == NULL && modifier != NULL) { + cached_icon = create_normal_cache_icon (icon, + NULL, + nominal_size); + } + + if (cached_icon == NULL) { + cached_icon = factory->fallback_icon; + cache_icon_ref (cached_icon); } - g_assert (icon != NULL); } - + + + /* Create the key and icon for the hash table. */ key = g_new (CacheKey, 1); - key->scalable_icon = scalable_icon; - key->size = *size; - - /* Recursive get_icon_from_cache might already have placed icon - * in hash table if there was an exact match. - */ - if (g_hash_table_lookup (hash_table, key) != NULL) { - g_free (key); - return icon; - } - - /* Add the item to the hash table. */ - nautilus_scalable_icon_ref (scalable_icon); - g_hash_table_insert (hash_table, key, icon); + key->name = g_strdup (icon); + key->modifier = g_strdup (modifier); + key->embedded_text = g_strdup (embedded_text); + key->nominal_size = nominal_size; + + g_hash_table_insert (hash_table, key, cached_icon); } /* Hand back a ref to the caller. */ - cache_icon_ref (icon); + cache_icon_ref (cached_icon); /* Since this item was used, keep it in the cache longer. */ - mark_recently_used (&icon->recently_used_node); + mark_recently_used (&cached_icon->recently_used_node); /* Come back later and sweep the cache. */ - nautilus_icon_factory_schedule_sweep (); - - return icon; + nautilus_icon_factory_schedule_sweep (factory); + + return cached_icon; } GdkPixbuf * -nautilus_icon_factory_get_pixbuf_for_icon (NautilusScalableIcon *scalable_icon, - guint nominal_width, - guint nominal_height, - guint maximum_width, - guint maximum_height, - NautilusEmblemAttachPoints *attach_points, - gboolean wants_default) +nautilus_icon_factory_get_pixbuf_for_icon (const char *icon, + const char *modifier, + const char *embedded_text, + guint nominal_size, + NautilusEmblemAttachPoints *attach_points, + gboolean wants_default) { - IconSizeRequest size; - CacheIcon *icon; + NautilusIconFactory *factory; + CacheIcon *cached_icon; + GnomeIconData *icon_data; GdkPixbuf *pixbuf; + int i; - size.nominal_width = nominal_width; - size.nominal_height = nominal_width; - size.maximum_width = maximum_width; - size.maximum_height = maximum_height; - icon = get_icon_from_cache (scalable_icon, &size, REQUEST_NORMAL); + factory = get_icon_factory (); + cached_icon = get_icon_from_cache (icon, + modifier, + embedded_text, + nominal_size); if (attach_points != NULL) { - *attach_points = icon->details.attach_points; + if (cached_icon->icon_data != NULL) { + icon_data = cached_icon->icon_data; + attach_points->num_points = MAX (icon_data->n_attach_points, + MAX_ATTACH_POINTS); + for (i = 0; i < attach_points->num_points; i++) { + attach_points->points[i].x = icon_data->attach_points[i].x; + attach_points->points[i].y = icon_data->attach_points[i].y; + } + } else { + attach_points->num_points = 0; + } } /* if we don't want a default icon and one is returned, return NULL instead */ - if (!wants_default && icon->is_fallback) { - cache_icon_unref (icon); + if (!wants_default && cached_icon == factory->fallback_icon) { + cache_icon_unref (cached_icon); return NULL; } - pixbuf = icon->pixbuf; + pixbuf = cached_icon->pixbuf; g_object_ref (pixbuf); - cache_icon_unref (icon); + cache_icon_unref (cached_icon); return pixbuf; } @@ -2160,13 +1315,22 @@ static guint cache_key_hash (gconstpointer p) { const CacheKey *key; + guint hash; key = p; - return ((((((((GPOINTER_TO_UINT (key->scalable_icon) << 4) - ^ key->size.nominal_width) << 4) - ^ key->size.nominal_height) << 4) - ^ key->size.maximum_width) << 4) - ^ key->size.maximum_height); + + hash = g_str_hash (key->name) ^ + (key->nominal_size << 4); + + if (key->modifier) { + hash ^= g_str_hash (key->modifier); + } + + if (key->embedded_text) { + hash ^= g_str_hash (key->embedded_text); + } + + return hash; } static gboolean @@ -2177,11 +1341,10 @@ cache_key_equal (gconstpointer a, gconstpointer b) key_a = a; key_b = b; - return key_a->scalable_icon == key_b->scalable_icon - && key_a->size.nominal_width == key_b->size.nominal_width - && key_a->size.nominal_height == key_b->size.nominal_height - && key_a->size.maximum_width == key_b->size.maximum_width - && key_a->size.maximum_height == key_b->size.maximum_height; + return eel_strcmp (key_a->name, key_b->name) == 0 && + key_a->nominal_size == key_b->nominal_size && + eel_strcmp (key_a->modifier, key_b->modifier) == 0 && + eel_strcmp (key_a->embedded_text, key_b->embedded_text) == 0; } /* Return nominal icon size for given zoom level. @@ -2217,23 +1380,27 @@ nautilus_get_icon_size_for_zoom_level (NautilusZoomLevel zoom_level) */ GdkPixbuf * nautilus_icon_factory_get_pixbuf_for_file (NautilusFile *file, - const char *modifer, + const char *modifier, guint size_in_pixels) { - NautilusScalableIcon *icon; + char *icon; GdkPixbuf *pixbuf; + char *embedded_text; + /* Get the pixbuf for this file. */ - icon = nautilus_icon_factory_get_icon_for_file (file, modifer); + icon = nautilus_icon_factory_get_icon_for_file (file); if (icon == NULL) { return NULL; } - pixbuf = nautilus_icon_factory_get_pixbuf_for_icon - (icon, - size_in_pixels, size_in_pixels, - size_in_pixels, size_in_pixels, - NULL, TRUE); - nautilus_scalable_icon_unref (icon); + embedded_text = nautilus_file_peek_top_left_text (file); + + pixbuf = nautilus_icon_factory_get_pixbuf_for_icon (icon, modifier, + embedded_text, + size_in_pixels, + NULL, TRUE); + + g_free (icon); return pixbuf; } @@ -2244,16 +1411,10 @@ nautilus_icon_factory_get_pixbuf_from_name (const char *icon_name, const char *modifier, guint size_in_pixels) { - GdkPixbuf *pixbuf; - NautilusScalableIcon *icon; - - icon = nautilus_scalable_icon_new_from_text_pieces (NULL, NULL, icon_name, modifier, NULL); - pixbuf = nautilus_icon_factory_get_pixbuf_for_icon (icon, - size_in_pixels, size_in_pixels, - size_in_pixels, size_in_pixels, - NULL, TRUE); - nautilus_scalable_icon_unref (icon); - return pixbuf; + return nautilus_icon_factory_get_pixbuf_for_icon (icon_name, modifier, + NULL, + size_in_pixels, + NULL, TRUE); } GdkPixbuf * @@ -2263,16 +1424,14 @@ nautilus_icon_factory_get_thumbnail_frame (void) } static gboolean -embedded_text_rect_usable (ArtIRect embedded_text_rect) +embedded_text_rect_usable (GnomeIconData *icon_data) { - if (art_irect_empty (&embedded_text_rect)) { + if (icon_data == NULL || !icon_data->has_embedded_rect) { return FALSE; } - if (embedded_text_rect.x1 - embedded_text_rect.x0 - < MINIMUM_EMBEDDED_TEXT_RECT_WIDTH || - embedded_text_rect.y1 - embedded_text_rect.y0 - < MINIMUM_EMBEDDED_TEXT_RECT_HEIGHT) { + if (icon_data->x1 - icon_data->x0 < MINIMUM_EMBEDDED_TEXT_RECT_WIDTH || + icon_data->y1 - icon_data->y0 < MINIMUM_EMBEDDED_TEXT_RECT_HEIGHT) { return FALSE; } @@ -2318,20 +1477,21 @@ embedded_text_font_changed_callback (GtkSettings *settings, static GdkPixbuf * embed_text (GdkPixbuf *pixbuf_without_text, - ArtIRect text_rect, + GnomeIconData *icon_data, const char *text) { GdkPixbuf *pixbuf_with_text; PangoLayout *layout; static PangoContext *context; GtkSettings *settings; + ArtIRect clip_rect; g_return_val_if_fail (pixbuf_without_text != NULL, NULL); /* Quick out for the case where there's no place to embed the * text or the place is too small or there's no text. */ - if (!embedded_text_rect_usable (text_rect) || eel_strlen (text) == 0) { + if (!embedded_text_rect_usable (icon_data) || eel_strlen (text) == 0) { return NULL; } @@ -2349,83 +1509,27 @@ embed_text (GdkPixbuf *pixbuf_without_text, context = eel_pango_ft2_get_context (); eel_debug_call_at_shutdown_with_data (g_object_unref, context); } + layout = pango_layout_new (context); pango_layout_set_font_description (layout, embedded_text_font); pango_layout_set_text (layout, text, -1); pixbuf_with_text = gdk_pixbuf_copy (pixbuf_without_text); + clip_rect.x0 = icon_data->x0; + clip_rect.y0 = icon_data->y0; + clip_rect.x1 = icon_data->x1; + clip_rect.y1 = icon_data->y1; + eel_gdk_pixbuf_draw_layout_clipped (pixbuf_with_text, - text_rect, + clip_rect, EEL_RGB_COLOR_BLACK, layout); g_object_unref (layout); return pixbuf_with_text; } -static CacheIcon * -load_icon_with_embedded_text (NautilusScalableIcon *scalable_icon, - const IconSizeRequest *size) -{ - NautilusScalableIcon *scalable_icon_without_text; - CacheIcon *icon_without_text, *icon_with_text; - GdkPixbuf *pixbuf_with_text; - NautilusIconDetails details; - - g_assert (scalable_icon->embedded_text != NULL); - - /* Get the icon without text. */ - scalable_icon_without_text = nautilus_scalable_icon_new_from_text_pieces - (scalable_icon->uri, - scalable_icon->mime_type, - scalable_icon->name, - scalable_icon->modifier, - NULL); - icon_without_text = get_icon_from_cache - (scalable_icon_without_text, size, REQUEST_NORMAL); - nautilus_scalable_icon_unref (scalable_icon_without_text); - - /* Create a pixbuf with the text in it. */ - pixbuf_with_text = embed_text (icon_without_text->pixbuf, - icon_without_text->details.text_rect, - scalable_icon->embedded_text); - if (pixbuf_with_text == NULL) { - return icon_without_text; - } - - /* Create an icon from the new pixbuf. */ - details = icon_without_text->details; - memset (&details.text_rect, 0, sizeof (details.text_rect)); - icon_with_text = cache_icon_new (pixbuf_with_text, - icon_without_text->request, - icon_without_text->scaled, - &details); - icon_with_text->cache_time = icon_without_text->cache_time; - icon_with_text->cache_size = icon_without_text->cache_size; - cache_icon_unref (icon_without_text); - g_object_unref (pixbuf_with_text); - - return icon_with_text; -} - -/* Convenience function for unrefing and then freeing an entire list. */ -void -nautilus_scalable_icon_list_free (GList *icon_list) -{ - eel_g_list_free_deep_custom - (icon_list, (GFunc) nautilus_scalable_icon_unref, NULL); -} - #if ! defined (NAUTILUS_OMIT_SELF_CHECK) -static char * -self_test_next_icon_size_to_try (guint start_size, guint current_size) -{ - gboolean got_next_size; - - got_next_size = get_next_icon_size_to_try (start_size, ¤t_size); - return g_strdup_printf ("%s,%d", got_next_size ? "TRUE" : "FALSE", current_size); -} - void nautilus_self_check_icon_factory (void) { @@ -2476,46 +1580,6 @@ nautilus_self_check_icon_factory (void) EEL_CHECK_INTEGER_RESULT (get_smaller_icon_size (192), 96); EEL_CHECK_INTEGER_RESULT (get_smaller_icon_size (193), 192); EEL_CHECK_INTEGER_RESULT (get_smaller_icon_size (0xFFFFFFFF), 192); - - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (0, 0), "TRUE,12"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (0, 12), "TRUE,20"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (0, 20), "TRUE,24"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (0, 24), "TRUE,36"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (0, 36), "TRUE,48"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (0, 48), "TRUE,72"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (0, 72), "TRUE,96"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (0, 96), "TRUE,192"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (0, 192), "FALSE,192"); - - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (36, 0), "TRUE,36"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (36, 36), "TRUE,48"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (36, 48), "TRUE,72"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (36, 72), "TRUE,96"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (36, 96), "TRUE,192"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (36, 192), "TRUE,24"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (36, 24), "TRUE,20"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (36, 20), "TRUE,12"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (36, 12), "FALSE,12"); - - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (37, 0), "TRUE,48"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (37, 48), "TRUE,72"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (37, 72), "TRUE,96"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (37, 96), "TRUE,192"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (37, 192), "TRUE,36"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (37, 36), "TRUE,24"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (37, 24), "TRUE,20"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (37, 20), "TRUE,12"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (37, 12), "FALSE,12"); - - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (0xFFFFFFFF, 0), "TRUE,192"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (0xFFFFFFFF, 192), "TRUE,96"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (0xFFFFFFFF, 96), "TRUE,72"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (0xFFFFFFFF, 72), "TRUE,48"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (0xFFFFFFFF, 48), "TRUE,36"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (0xFFFFFFFF, 36), "TRUE,24"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (0xFFFFFFFF, 24), "TRUE,20"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (0xFFFFFFFF, 20), "TRUE,12"); - EEL_CHECK_STRING_RESULT (self_test_next_icon_size_to_try (0xFFFFFFFF, 12), "FALSE,12"); } #endif /* ! NAUTILUS_OMIT_SELF_CHECK */ diff --git a/libnautilus-private/nautilus-icon-factory.h b/libnautilus-private/nautilus-icon-factory.h index c77b439e2..21b7d0121 100644 --- a/libnautilus-private/nautilus-icon-factory.h +++ b/libnautilus-private/nautilus-icon-factory.h @@ -32,6 +32,7 @@ #include <gtk/gtkobject.h> #include <libnautilus-private/nautilus-file.h> #include <libgnome/gnome-icon-loader.h> +#include "gnome-thumbnail.h" /* NautilusIconFactory is a class that knows how to hand out icons to be * used for representing files and some other objects. It was designed @@ -71,17 +72,20 @@ typedef enum { #define NAUTILUS_ICON_SIZE_LARGER 96 #define NAUTILUS_ICON_SIZE_LARGEST 192 +#define NAUTILUS_ICON_SIZE_THUMBNAIL 96 + +/* Maximum size of an icon that the icon factory will ever produce */ +#define NAUTILUS_ICON_MAXIMUM_SIZE 320 + /* Icon size to use for menus. NAUTILUS_ICON_SIZE_SMALLEST * is a little too small and NAUTILUS_ICON_SIZE_SMALLER is * a little too big. */ #define NAUTILUS_ICON_SIZE_FOR_MENUS 20 -typedef struct NautilusScalableIcon NautilusScalableIcon; - /* here's a structure to hold the emblem attach points */ -#define MAX_ATTACH_POINTS 8 +#define MAX_ATTACH_POINTS 12 typedef struct { int num_points; @@ -97,14 +101,13 @@ typedef struct { /* There's a single NautilusIconFactory object. * The only thing you need it for is to connect to its signals. */ -GtkObject * nautilus_icon_factory_get (void); +GObject * nautilus_icon_factory_get (void); /* Relationship between zoom levels and icons sizes. */ guint nautilus_get_icon_size_for_zoom_level (NautilusZoomLevel zoom_level); /* Choose the appropriate icon, but don't render it yet. */ -NautilusScalableIcon *nautilus_icon_factory_get_icon_for_file (NautilusFile *file, - const char *modifier); +char * nautilus_icon_factory_get_icon_for_file (NautilusFile *file); gboolean nautilus_icon_factory_is_icon_ready_for_file (NautilusFile *file); GList * nautilus_icon_factory_get_required_file_attributes (void); @@ -114,7 +117,7 @@ GList * nautilus_icon_factory_get_basic_file_attributes (void); GList * nautilus_icon_factory_get_emblem_icons_for_file (NautilusFile *file, EelStringList *exclude); -NautilusScalableIcon *nautilus_icon_factory_get_emblem_icon_by_name (const char *emblem_name); +char * nautilus_icon_factory_get_emblem_icon_by_name (const char *emblem_name); /* Render an icon to a particular size. * Ownership of a ref. count in this pixbuf comes with the deal. @@ -124,11 +127,10 @@ NautilusScalableIcon *nautilus_icon_factory_get_emblem_icon_by_name (const * If the wants_default boolean is set, return a default icon instead * of NULL if we can't find anything */ -GdkPixbuf * nautilus_icon_factory_get_pixbuf_for_icon (NautilusScalableIcon *scalable_icon, - guint nominal_size_in_pixels_x, - guint nominal_size_in_pixels_y, - guint maximum_size_in_pixels_x, - guint maximum_size_in_pixels_y, +GdkPixbuf * nautilus_icon_factory_get_pixbuf_for_icon (const char *icon, + const char *modifier, + const char *embedded_text, + guint nominal_size, NautilusEmblemAttachPoints *attach_points, gboolean wants_default); @@ -144,38 +146,9 @@ GdkPixbuf * nautilus_icon_factory_get_pixbuf_for_file (Nautil GdkPixbuf * nautilus_icon_factory_get_pixbuf_from_name (const char *icon_name, const char *modifer, guint size_in_pixels); -/* Manage a scalable icon. - * Since the factory always passes out references to the same scalable - * icon, you can compare two scalable icons to see if they are the same - * with ==. - */ -void nautilus_scalable_icon_ref (NautilusScalableIcon *scalable_icon); -void nautilus_scalable_icon_unref (NautilusScalableIcon *scalable_icon); -/* A scalable icon can be decomposed into text and reconstituted later - * using nautilus_scalable_icon_new_from_text_pieces. This is the way - * to store scalable icons in metadata or other files. - */ -void nautilus_scalable_icon_get_text_pieces (NautilusScalableIcon *scalable_icon, - char **uri_return, - char **mime_type_return, - char **name_return, - char **modifier_return, - char **embedded_text_return); -/* Get a scalable icon using the earlier results of - * nautilus_scalable_icon_get_text_pieces. - */ -NautilusScalableIcon *nautilus_scalable_icon_new_from_text_pieces (const char *uri, - const char *mime_type, - const char *name, - const char *modifier, - const char *embedded_text); - -/* Convenience function for freeing a list of scalable icons. - * Unrefs all the icons before freeing the list. - */ -void nautilus_scalable_icon_list_free (GList *scalable_icon_list); +GnomeIconLoader * nautilus_icon_factory_get_icon_loader (void); +GnomeThumbnailFactory *nautilus_icon_factory_get_thumbnail_factory (void); #endif /* NAUTILUS_ICON_FACTORY_H */ -GnomeIconLoader * nautilus_icon_factory_get_icon_loader (void); diff --git a/libnautilus-private/nautilus-icon-private.h b/libnautilus-private/nautilus-icon-private.h index efad28272..469f86e0d 100644 --- a/libnautilus-private/nautilus-icon-private.h +++ b/libnautilus-private/nautilus-icon-private.h @@ -45,6 +45,7 @@ typedef struct { double x, y; /* Scale factor (stretches icon). */ + /* ALEX TODO: only allow once scale */ double scale_x, scale_y; /* Whether this item is selected. */ 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); } } diff --git a/libnautilus-private/nautilus-thumbnails.h b/libnautilus-private/nautilus-thumbnails.h index fcfa47d20..ff013a01d 100644 --- a/libnautilus-private/nautilus-thumbnails.h +++ b/libnautilus-private/nautilus-thumbnails.h @@ -29,8 +29,7 @@ #include <libnautilus-private/nautilus-file.h> /* Returns NULL if there's no thumbnail yet. */ -char * nautilus_get_thumbnail_uri (NautilusFile *file); -gboolean nautilus_thumbnail_has_invalid_thumbnail (NautilusFile *file); +void nautilus_create_thumbnail (NautilusFile *file); GdkPixbuf *nautilus_thumbnail_load_framed_image (const char *path); void nautilus_update_thumbnail_file_renamed (const char *old_file_uri, const char *new_file_uri); diff --git a/src/file-manager/fm-desktop-icon-view.c b/src/file-manager/fm-desktop-icon-view.c index 2a77dddb0..ca610c494 100644 --- a/src/file-manager/fm-desktop-icon-view.c +++ b/src/file-manager/fm-desktop-icon-view.c @@ -435,35 +435,35 @@ create_mount_link (FMDesktopIconView *icon_view, } /* Get icon type */ - icon_name = "i-blockdev"; + icon_name = "gnome-dev-harddisk"; switch (nautilus_volume_get_device_type (volume)) { case NAUTILUS_DEVICE_AUDIO_CD: case NAUTILUS_DEVICE_CDROM_DRIVE: - icon_name = "i-cdrom"; + icon_name = "gnome-dev-cdrom"; break; case NAUTILUS_DEVICE_FLOPPY_DRIVE: - icon_name = "i-floppy"; + icon_name = "gnome-dev-floppy"; break; case NAUTILUS_DEVICE_JAZ_DRIVE: - icon_name = "i-zipdisk2"; + icon_name = "gnome-dev-jazdisk"; break; case NAUTILUS_DEVICE_MEMORY_STICK: - icon_name = "gnome-ccperiph"; + icon_name = "gnome-dev-memory"; break; case NAUTILUS_DEVICE_NFS: - icon_name = "i-nfs"; + icon_name = "gnome-fs-nfs"; break; case NAUTILUS_DEVICE_SMB: - icon_name = "i-smb"; + icon_name = "gnome-fs-smb"; break; case NAUTILUS_DEVICE_ZIP_DRIVE: - icon_name = "i-zipdisk"; + icon_name = "gome-fs-zipdisk"; break; case NAUTILUS_DEVICE_APPLE: @@ -1061,7 +1061,7 @@ fm_desktop_icon_view_trash_state_changed_callback (NautilusTrashMonitor *trash_m path = g_build_filename (desktop_directory, TRASH_LINK_NAME, NULL); - nautilus_link_local_set_icon (path, state ? "trash-empty" : "trash-full"); + nautilus_link_local_set_icon (path, state ? "gnome-fs-trash-empty" : "gnome-fs-trash-full"); g_free (path); } @@ -1538,6 +1538,7 @@ update_desktop_directory (UpdateType type) if (!found_home_link && nautilus_link_local_is_utf8 (link_path, info)) { nautilus_link_local_set_link_uri (link_path, home_uri); + nautilus_link_local_set_icon (link_path, "gnome-fs-home"); found_home_link = TRUE; } else { unlink_and_notify (link_path); /* kill duplicates */ @@ -1564,7 +1565,7 @@ update_desktop_directory (UpdateType type) !eel_preferences_get_boolean (NAUTILUS_PREFERENCES_DESKTOP_IS_HOME_DIR)) { nautilus_link_local_create (desktop_directory, home_link_name, - "desktop-home", + "gnome-fs-home", home_uri, NULL, NAUTILUS_LINK_HOME); @@ -1574,7 +1575,7 @@ update_desktop_directory (UpdateType type) if (!found_trash_link) { nautilus_link_local_create (desktop_directory, TRASH_LINK_NAME, - "trash-empty", + "gnome-fs-trash-empty", EEL_TRASH_URI, NULL, NAUTILUS_LINK_TRASH); diff --git a/src/file-manager/fm-directory-view.c b/src/file-manager/fm-directory-view.c index 84e75d684..c74c224af 100644 --- a/src/file-manager/fm-directory-view.c +++ b/src/file-manager/fm-directory-view.c @@ -33,6 +33,7 @@ #include "fm-desktop-icon-view.h" #include "fm-error-reporting.h" #include "fm-properties-window.h" +#include <libgnome/gnome-url.h> #include <bonobo/bonobo-control.h> #include <bonobo/bonobo-window.h> #include <bonobo/bonobo-zoomable.h> @@ -4745,24 +4746,32 @@ activate_callback (NautilusFile *file, gpointer callback_data) } if (action == ACTIVATION_ACTION_DISPLAY) { - if (nautilus_mime_get_default_action_type_for_file (file) - == GNOME_VFS_MIME_ACTION_TYPE_APPLICATION) { - application = nautilus_mime_get_default_application_for_file (file); + /* BADHACK(tm) to make desktop web links work */ + if (nautilus_file_is_nautilus_link (file) && + uri != NULL && + (eel_str_has_prefix (uri, "http:") || + eel_str_has_prefix (uri, "https:"))) { + gnome_url_show (uri, NULL); } else { - /* If the action type is unspecified, treat it like - * the component case. This is most likely to happen - * (only happens?) when there are no registered - * viewers or apps, or there are errors in the - * mime.keys files. - */ - application = NULL; - } - - if (application != NULL) { - fm_directory_view_launch_application (application, file, view); - gnome_vfs_mime_application_free (application); - } else { - open_location (view, uri, parameters->choice); + if (nautilus_mime_get_default_action_type_for_file (file) + == GNOME_VFS_MIME_ACTION_TYPE_APPLICATION) { + application = nautilus_mime_get_default_application_for_file (file); + } else { + /* If the action type is unspecified, treat it like + * the component case. This is most likely to happen + * (only happens?) when there are no registered + * viewers or apps, or there are errors in the + * mime.keys files. + */ + application = NULL; + } + + if (application != NULL) { + fm_directory_view_launch_application (application, file, view); + gnome_vfs_mime_application_free (application); + } else { + open_location (view, uri, parameters->choice); + } } } diff --git a/src/file-manager/fm-icon-container.c b/src/file-manager/fm-icon-container.c index 527ca2cdd..b5a01423b 100644 --- a/src/file-manager/fm-icon-container.c +++ b/src/file-manager/fm-icon-container.c @@ -47,11 +47,11 @@ get_icon_view (NautilusIconContainer *container) return FM_ICON_CONTAINER (container)->view; } -static NautilusScalableIcon * +static char * fm_icon_container_get_icon_images (NautilusIconContainer *container, NautilusIconData *data, - const char *modifier, - GList **emblem_icons) + GList **emblem_icons, + char **embedded_text) { FMIconView *icon_view; EelStringList *emblems_to_ignore; @@ -63,6 +63,10 @@ fm_icon_container_get_icon_images (NautilusIconContainer *container, icon_view = get_icon_view (container); g_return_val_if_fail (icon_view != NULL, NULL); + if (embedded_text) { + *embedded_text = nautilus_file_peek_top_left_text (file); + } + if (emblem_icons != NULL) { emblems_to_ignore = fm_directory_view_get_emblem_names_to_exclude (FM_DIRECTORY_VIEW (icon_view)); @@ -71,7 +75,7 @@ fm_icon_container_get_icon_images (NautilusIconContainer *container, eel_string_list_free (emblems_to_ignore); } - return nautilus_icon_factory_get_icon_for_file (file, modifier); + return nautilus_icon_factory_get_icon_for_file (file); } /* diff --git a/src/nautilus-bookmark-list.c b/src/nautilus-bookmark-list.c index 95de6ce51..3f67f7f8c 100644 --- a/src/nautilus-bookmark-list.c +++ b/src/nautilus-bookmark-list.c @@ -125,9 +125,8 @@ append_bookmark_node (gpointer data, gpointer user_data) { xmlNodePtr root_node, bookmark_node; NautilusBookmark *bookmark; - NautilusScalableIcon *icon; + char *icon; char *bookmark_uri, *bookmark_name; - char *icon_uri, *icon_mime_type, *icon_name; g_assert (NAUTILUS_IS_BOOKMARK (data)); @@ -147,15 +146,7 @@ append_bookmark_node (gpointer data, gpointer user_data) icon = nautilus_bookmark_get_icon (bookmark); if (icon != NULL) { /* Don't bother storing modifier or embedded text for bookmarks. */ - nautilus_scalable_icon_get_text_pieces (icon, &icon_uri, &icon_mime_type, &icon_name, - NULL, NULL); - xmlSetProp (bookmark_node, "icon_uri", icon_uri); - xmlSetProp (bookmark_node, "icon_mime_type", icon_mime_type); - xmlSetProp (bookmark_node, "icon_name", icon_name); - nautilus_scalable_icon_unref (icon); - g_free (icon_uri); - g_free (icon_mime_type); - g_free (icon_name); + xmlSetProp (bookmark_node, "icon_name", icon); } } diff --git a/src/nautilus-bookmark-parsing.c b/src/nautilus-bookmark-parsing.c index 8cbd055b4..e1c1914b3 100644 --- a/src/nautilus-bookmark-parsing.c +++ b/src/nautilus-bookmark-parsing.c @@ -37,33 +37,19 @@ NautilusBookmark * nautilus_bookmark_new_from_node (xmlNodePtr node) { xmlChar *name, *uri; - xmlChar *icon_uri, *icon_mime_type, *icon_name; - NautilusScalableIcon *icon; + xmlChar *icon_name; NautilusBookmark *new_bookmark; /* Maybe should only accept bookmarks with both a name and uri? */ name = eel_xml_get_property_translated (node, "name"); uri = xmlGetProp (node, "uri"); - icon_uri = xmlGetProp (node, "icon_uri"); - icon_mime_type = xmlGetProp (node, "icon_mime_type"); icon_name = xmlGetProp (node, "icon_name"); - if (icon_uri == NULL && icon_name == NULL) { - icon = NULL; - } else { - icon = nautilus_scalable_icon_new_from_text_pieces - (icon_uri, icon_mime_type, icon_name, NULL, NULL); - } - new_bookmark = nautilus_bookmark_new_with_icon (uri, name, icon); - if (icon != NULL) { - nautilus_scalable_icon_unref (icon); - } + new_bookmark = nautilus_bookmark_new_with_icon (uri, name, icon_name); xmlFree (name); xmlFree (uri); - xmlFree (icon_uri); xmlFree (icon_name); - xmlFree (icon_mime_type); return new_bookmark; } diff --git a/src/nautilus-sidebar-title.c b/src/nautilus-sidebar-title.c index 47a9503cd..e72475a6b 100644 --- a/src/nautilus-sidebar-title.c +++ b/src/nautilus-sidebar-title.c @@ -575,9 +575,8 @@ update_emblems (NautilusSidebarTitle *sidebar_title) /* loop through the list of emblems, installing them in the box */ for (p = icons; p != NULL; p = p->next) { pixbuf = nautilus_icon_factory_get_pixbuf_for_icon - (p->data, - NAUTILUS_ICON_SIZE_STANDARD, NAUTILUS_ICON_SIZE_STANDARD, - NAUTILUS_ICON_SIZE_STANDARD, NAUTILUS_ICON_SIZE_STANDARD, + (p->data, NULL, NULL, + NAUTILUS_ICON_SIZE_STANDARD, NULL, FALSE); if (pixbuf != NULL) { add_emblem (sidebar_title, pixbuf); @@ -585,7 +584,7 @@ update_emblems (NautilusSidebarTitle *sidebar_title) } } - nautilus_scalable_icon_list_free (icons); + eel_g_list_free_deep (icons); } static void |