summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog136
-rw-r--r--libnautilus-private/Makefile.am8
-rw-r--r--libnautilus-private/gnome-thumbnail-pixbuf-utils.c464
-rw-r--r--libnautilus-private/gnome-thumbnail.c1368
-rw-r--r--libnautilus-private/gnome-thumbnail.h108
-rw-r--r--libnautilus-private/nautilus-bookmark.c85
-rw-r--r--libnautilus-private/nautilus-bookmark.h4
-rw-r--r--libnautilus-private/nautilus-directory-async.c4
-rw-r--r--libnautilus-private/nautilus-file-private.h4
-rw-r--r--libnautilus-private/nautilus-file.c41
-rw-r--r--libnautilus-private/nautilus-file.h3
-rw-r--r--libnautilus-private/nautilus-icon-container.c91
-rw-r--r--libnautilus-private/nautilus-icon-container.h7
-rw-r--r--libnautilus-private/nautilus-icon-factory-private.h8
-rw-r--r--libnautilus-private/nautilus-icon-factory.c2070
-rw-r--r--libnautilus-private/nautilus-icon-factory.h59
-rw-r--r--libnautilus-private/nautilus-icon-private.h1
-rw-r--r--libnautilus-private/nautilus-thumbnails.c805
-rw-r--r--libnautilus-private/nautilus-thumbnails.h3
-rw-r--r--src/file-manager/fm-desktop-icon-view.c23
-rw-r--r--src/file-manager/fm-directory-view.c43
-rw-r--r--src/file-manager/fm-icon-container.c12
-rw-r--r--src/nautilus-bookmark-list.c13
-rw-r--r--src/nautilus-bookmark-parsing.c18
-rw-r--r--src/nautilus-sidebar-title.c7
25 files changed, 2948 insertions, 2437 deletions
diff --git a/ChangeLog b/ChangeLog
index c4d19f625..3c3713303 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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, &current_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