summaryrefslogtreecommitdiff
path: root/src/gnome-desktop/gnome-desktop-thumbnail.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gnome-desktop/gnome-desktop-thumbnail.c')
-rw-r--r--src/gnome-desktop/gnome-desktop-thumbnail.c1303
1 files changed, 0 insertions, 1303 deletions
diff --git a/src/gnome-desktop/gnome-desktop-thumbnail.c b/src/gnome-desktop/gnome-desktop-thumbnail.c
deleted file mode 100644
index 566fbeb84..000000000
--- a/src/gnome-desktop/gnome-desktop-thumbnail.c
+++ /dev/null
@@ -1,1303 +0,0 @@
-/*
- * gnome-thumbnail.c: Utilities for handling thumbnails
- *
- * Copyright (C) 2002 Red Hat, Inc.
- * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org>
- *
- * 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., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- *
- * Author: Alexander Larsson <alexl@redhat.com>
- */
-
-/**
- * SECTION:gnome-desktop-thumbnail
- * @short_description: Generates and looks up thumbnails of files and
- * directories
- * @stability: Unstable
- * @include: libgnome-desktop/gnome-desktop-thumbnail.h
- *
- * #GnomeDesktopThumbnailFactory allows generation and loading of thumbnails for
- * local and remote files and directories. It uses a collection of programs
- * called <firstterm>thumbnailers</firstterm>, each one generating thumbnails
- * for a specific set of content-types of files. For example,
- * <application>totem-video-thumbnailer</application> generates thumbnails for
- * video files using GStreamer; <application>evince-thumbnailer</application>
- * generates thumbnails for PDFs and other document files. If no specific
- * thumbnailer exists for a file, or if the thumbnailer fails, gdk-pixbuf is
- * used as a fallback.
- *
- * To generate a thumbnail, an appropriate thumbnailer program is selected then
- * executed, passing it the URI of the file to thumbnail, plus a path to write
- * the thumbnail image to. If thumbnailing succeeds, the thumbnailer should have
- * written the image to disk before terminating; but if thumbnailing fails, no
- * image should be written, and the thumbnailer should return a non-zero exit
- * status. #GnomeDesktopThumbnailFactory will then fall back to using gdk-pixbuf
- * to generate a thumbnail, if possible.
- *
- * Thumbnailers are chosen by examining a series of
- * <filename>.thumbnailer</filename> files in
- * <filename><replaceable>$PREFIX</replaceable>/share/thumbnailers</filename>.
- * Each is in a simple key-file format:
- * <informalexample><programlisting>
- * [Thumbnailer Entry]
- * Exec=evince-thumbnailer -s %s %u %o
- * MimeType=application/pdf;application/x-bzpdf;application/x-gzpdf;
- * </programlisting></informalexample>
- *
- * The <filename>.thumbnailer</filename> format supports three keys:
- * <variablelist>
- * <varlistentry><term><code>Exec</code></term><listitem><para>
- * Required. The command to execute the thumbnailer. It supports a few different
- * parameters which are replaced before calling the thumbnailer:
- * <replaceable>%u</replaceable> is the URI of the file being thumbnailed;
- * <replaceable>%i</replaceable> is its path; <replaceable>%o</replaceable>
- * is the path of the image file to be written to;
- * <replaceable>%s</replaceable> is the maximum desired size of the thumbnail
- * image (the maximum width or height, in pixels); and
- * <replaceable>%%</replaceable> is a literal percent character.
- * </para></listitem></varlistentry>
- * <varlistentry><term><code>MimeType</code></term><listitem><para>
- * Required. A semicolon-separated list of MIME types which the thumbnailer
- * supports generating thumbnails for.
- * </para></listitem></varlistentry>
- * </variablelist>
- *
- * So in the example <filename>.thumbnailer</filename> file above, the command
- * passes the requested thumbnail size, then the input file’s URI, then the
- * path for the output image file to
- * <application>evince-thumbnailer</application>.
- *
- * The code to examine and call a thumbnailer is contained in
- * #GnomeDesktopThumbnailFactory, which handles looking up the right thumbnailer
- * script, building and executing the command for it, and loading the resulting
- * thumbnail image into a #GdkPixbuf.
- *
- * Thumbnail caching is also supported by #GnomeDesktopThumbnailFactory. When
- * calling a thumbnailer, the path passed for the output image file is in
- * <filename><envar>$XDG_CACHE_HOME</envar>/thumbnails/
- * <replaceable>$SIZE</replaceable>/</filename>. The cached image file is given
- * a (probably) unique filename, generated by hashing the original file’s URI,
- * so the thumbnail can be looked up in future. #GnomeDesktopThumbnailFactory
- * supports two sizes of thumbnails: %GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL and
- * %GNOME_DESKTOP_THUMBNAIL_SIZE_LARGE. Normal thumbnails are up to 128×128
- * pixels, whereas large thumbnails are up to 256×256 pixels. Thumbnails which
- * are larger than this are scaled down before being cached, and non-square
- * thumbnails are scaled so their largest dimension is at most 128 or 256
- * pixels.
- *
- * #GnomeDesktopThumbnailFactory also handles failed thumbnails. If a
- * thumbnailer can’t generate a thumbnail for a file (e.g. because the file is
- * corrupt or because the right video codecs aren’t available), it returns a
- * non-zero exit status. The thumbnail factory then writes an entry to
- * <filename><envar>$XDG_CACHE_HOME</envar>/thumbnails/fail/
- * gnome-thumbnail-factory/</filename> which is named after the hash of the
- * input file URI (just like a successful cached thumbnail). For future queries
- * for thumbnails for that file, #GnomeDesktopThumbnailFactory can immediately
- * return an error after looking up the fail entry.
- *
- * If a file changes content, #GnomeDesktopThumbnailFactory will generate a new
- * thumbnail because each cached image has associated metadata (stored as PNG
- * tEXt keys) storing the full URI of the thumbnailed file (to check for hash
- * collisions) and its last modification time at the point of thumbnailing. If
- * the stored modification time doesn’t match the file’s current one, a new
- * thumbnail is generated.
- *
- * Since: 2.2
- */
-
-#include <config.h>
-
-#include <glib.h>
-#include <glib/gstdio.h>
-#include <gdk-pixbuf/gdk-pixbuf.h>
-#include <string.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#define GNOME_DESKTOP_USE_UNSTABLE_API
-#include "gnome-desktop-thumbnail.h"
-#include "gnome-desktop-thumbnail-script.h"
-
-static void
-thumbnailers_directory_changed (GFileMonitor *monitor,
- GFile *file,
- GFile *other_file,
- GFileMonitorEvent event_type,
- GnomeDesktopThumbnailFactory *factory);
-
-struct _GnomeDesktopThumbnailFactoryPrivate {
- GnomeDesktopThumbnailSize size;
-
- GMutex lock;
-
- GList *thumbnailers;
- GHashTable *mime_types_map;
- GList *monitors;
-
- GSettings *settings;
- gboolean loaded : 1;
- gboolean disabled : 1;
- gchar **disabled_types;
-};
-
-static const char *appname = "gnome-thumbnail-factory";
-
-G_DEFINE_TYPE (GnomeDesktopThumbnailFactory,
- gnome_desktop_thumbnail_factory,
- G_TYPE_OBJECT)
-#define parent_class gnome_desktop_thumbnail_factory_parent_class
-
-#define GNOME_DESKTOP_THUMBNAIL_FACTORY_GET_PRIVATE(object) \
- (G_TYPE_INSTANCE_GET_PRIVATE ((object), GNOME_DESKTOP_TYPE_THUMBNAIL_FACTORY, GnomeDesktopThumbnailFactoryPrivate))
-
-#define THUMBNAILER_ENTRY_GROUP "Thumbnailer Entry"
-#define THUMBNAILER_EXTENSION ".thumbnailer"
-
-typedef struct {
- volatile gint ref_count;
-
- gchar *path;
-
- gchar *command;
- gchar **mime_types;
-} Thumbnailer;
-
-static Thumbnailer *
-thumbnailer_ref (Thumbnailer *thumb)
-{
- g_return_val_if_fail (thumb != NULL, NULL);
- g_return_val_if_fail (thumb->ref_count > 0, NULL);
-
- g_atomic_int_inc (&thumb->ref_count);
- return thumb;
-}
-
-static void
-thumbnailer_unref (Thumbnailer *thumb)
-{
- g_return_if_fail (thumb != NULL);
- g_return_if_fail (thumb->ref_count > 0);
-
- if (g_atomic_int_dec_and_test (&thumb->ref_count))
- {
- g_free (thumb->path);
- g_free (thumb->command);
- g_strfreev (thumb->mime_types);
-
- g_slice_free (Thumbnailer, thumb);
- }
-}
-
-static Thumbnailer *
-thumbnailer_load (Thumbnailer *thumb)
-{
- GKeyFile *key_file;
- GError *error = NULL;
-
- key_file = g_key_file_new ();
- if (!g_key_file_load_from_file (key_file, thumb->path, 0, &error))
- {
- g_warning ("Failed to load thumbnailer from \"%s\": %s\n", thumb->path, error->message);
- g_error_free (error);
- thumbnailer_unref (thumb);
- g_key_file_free (key_file);
-
- return NULL;
- }
-
- if (!g_key_file_has_group (key_file, THUMBNAILER_ENTRY_GROUP))
- {
- g_warning ("Invalid thumbnailer: missing group \"%s\"\n", THUMBNAILER_ENTRY_GROUP);
- thumbnailer_unref (thumb);
- g_key_file_free (key_file);
-
- return NULL;
- }
-
- thumb->command = g_key_file_get_string (key_file, THUMBNAILER_ENTRY_GROUP, "Exec", NULL);
- if (!thumb->command)
- {
- g_warning ("Invalid thumbnailer: missing Exec key\n");
- thumbnailer_unref (thumb);
- g_key_file_free (key_file);
-
- return NULL;
- }
-
- thumb->mime_types = g_key_file_get_string_list (key_file, THUMBNAILER_ENTRY_GROUP, "MimeType", NULL, NULL);
- if (!thumb->mime_types)
- {
- g_warning ("Invalid thumbnailer: missing MimeType key\n");
- thumbnailer_unref (thumb);
- g_key_file_free (key_file);
-
- return NULL;
- }
-
- g_key_file_free (key_file);
-
- return thumb;
-}
-
-static Thumbnailer *
-thumbnailer_reload (Thumbnailer *thumb)
-{
- g_return_val_if_fail (thumb != NULL, NULL);
-
- g_free (thumb->command);
- thumb->command = NULL;
- g_strfreev (thumb->mime_types);
- thumb->mime_types = NULL;
-
- return thumbnailer_load (thumb);
-}
-
-static Thumbnailer *
-thumbnailer_new (const gchar *path)
-{
- Thumbnailer *thumb;
-
- thumb = g_slice_new0 (Thumbnailer);
- thumb->ref_count = 1;
- thumb->path = g_strdup (path);
-
- return thumbnailer_load (thumb);
-}
-
-static gpointer
-init_thumbnailers_dirs (gpointer data)
-{
- const gchar * const *data_dirs;
- GPtrArray *thumbs_dirs;
- guint i;
-
- data_dirs = g_get_system_data_dirs ();
- thumbs_dirs = g_ptr_array_new ();
-
- g_ptr_array_add (thumbs_dirs, g_build_filename (g_get_user_data_dir (), "thumbnailers", NULL));
- for (i = 0; data_dirs[i] != NULL; i++)
- g_ptr_array_add (thumbs_dirs, g_build_filename (data_dirs[i], "thumbnailers", NULL));
- g_ptr_array_add (thumbs_dirs, NULL);
-
- return g_ptr_array_free (thumbs_dirs, FALSE);
-}
-
-static const gchar * const *
-get_thumbnailers_dirs (void)
-{
- static GOnce once_init = G_ONCE_INIT;
- return g_once (&once_init, init_thumbnailers_dirs, NULL);
-}
-
-/* These should be called with the lock held */
-static void
-gnome_desktop_thumbnail_factory_register_mime_types (GnomeDesktopThumbnailFactory *factory,
- Thumbnailer *thumb)
-{
- GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv;
- gint i;
-
- for (i = 0; thumb->mime_types[i]; i++)
- {
- if (!g_hash_table_lookup (priv->mime_types_map, thumb->mime_types[i]))
- g_hash_table_insert (priv->mime_types_map,
- g_strdup (thumb->mime_types[i]),
- thumbnailer_ref (thumb));
- }
-}
-
-static void
-gnome_desktop_thumbnail_factory_add_thumbnailer (GnomeDesktopThumbnailFactory *factory,
- Thumbnailer *thumb)
-{
- GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv;
-
- gnome_desktop_thumbnail_factory_register_mime_types (factory, thumb);
- priv->thumbnailers = g_list_prepend (priv->thumbnailers, thumb);
-}
-
-static gboolean
-gnome_desktop_thumbnail_factory_is_disabled (GnomeDesktopThumbnailFactory *factory,
- const gchar *mime_type)
-{
- GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv;
- guint i;
-
- if (priv->disabled)
- return TRUE;
-
- if (!priv->disabled_types)
- return FALSE;
-
- for (i = 0; priv->disabled_types[i]; i++)
- {
- if (g_strcmp0 (priv->disabled_types[i], mime_type) == 0)
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gboolean
-remove_thumbnailer_from_mime_type_map (gchar *key,
- Thumbnailer *value,
- gchar *path)
-{
- return (strcmp (value->path, path) == 0);
-}
-
-static void
-update_or_create_thumbnailer (GnomeDesktopThumbnailFactory *factory,
- const gchar *path)
-{
- GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv;
- GList *l;
- Thumbnailer *thumb;
- gboolean found = FALSE;
-
- g_mutex_lock (&priv->lock);
-
- for (l = priv->thumbnailers; l && !found; l = g_list_next (l))
- {
- thumb = (Thumbnailer *)l->data;
-
- if (strcmp (thumb->path, path) == 0)
- {
- found = TRUE;
-
- /* First remove the mime_types associated to this thumbnailer */
- g_hash_table_foreach_remove (priv->mime_types_map,
- (GHRFunc)remove_thumbnailer_from_mime_type_map,
- (gpointer)path);
- if (!thumbnailer_reload (thumb))
- priv->thumbnailers = g_list_delete_link (priv->thumbnailers, l);
- else
- gnome_desktop_thumbnail_factory_register_mime_types (factory, thumb);
- }
- }
-
- if (!found)
- {
- thumb = thumbnailer_new (path);
- if (thumb)
- gnome_desktop_thumbnail_factory_add_thumbnailer (factory, thumb);
- }
-
- g_mutex_unlock (&priv->lock);
-}
-
-static void
-remove_thumbnailer (GnomeDesktopThumbnailFactory *factory,
- const gchar *path)
-{
- GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv;
- GList *l;
- Thumbnailer *thumb;
-
- g_mutex_lock (&priv->lock);
-
- for (l = priv->thumbnailers; l; l = g_list_next (l))
- {
- thumb = (Thumbnailer *)l->data;
-
- if (strcmp (thumb->path, path) == 0)
- {
- priv->thumbnailers = g_list_delete_link (priv->thumbnailers, l);
- g_hash_table_foreach_remove (priv->mime_types_map,
- (GHRFunc)remove_thumbnailer_from_mime_type_map,
- (gpointer)path);
- thumbnailer_unref (thumb);
-
- break;
- }
- }
-
- g_mutex_unlock (&priv->lock);
-}
-
-static void
-remove_thumbnailers_for_dir (GnomeDesktopThumbnailFactory *factory,
- const gchar *thumbnailer_dir,
- GFileMonitor *monitor)
-{
- GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv;
- GList *l;
- Thumbnailer *thumb;
-
- g_mutex_lock (&priv->lock);
-
- /* Remove all the thumbnailers inside this @thumbnailer_dir. */
- for (l = priv->thumbnailers; l; l = g_list_next (l))
- {
- thumb = (Thumbnailer *)l->data;
-
- if (g_str_has_prefix (thumb->path, thumbnailer_dir) == TRUE)
- {
- priv->thumbnailers = g_list_delete_link (priv->thumbnailers, l);
- g_hash_table_foreach_remove (priv->mime_types_map,
- (GHRFunc)remove_thumbnailer_from_mime_type_map,
- (gpointer)thumb->path);
- thumbnailer_unref (thumb);
-
- break;
- }
- }
-
- /* Remove the monitor for @thumbnailer_dir. */
- priv->monitors = g_list_remove (priv->monitors, monitor);
- g_signal_handlers_disconnect_by_func (monitor, thumbnailers_directory_changed, factory);
-
- g_mutex_unlock (&priv->lock);
-}
-
-static void
-gnome_desktop_thumbnail_factory_load_thumbnailers_for_dir (GnomeDesktopThumbnailFactory *factory,
- const gchar *path)
-{
- GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv;
- GDir *dir;
- GFile *dir_file;
- GFileMonitor *monitor;
- const gchar *dirent;
-
- dir = g_dir_open (path, 0, NULL);
- if (!dir)
- return;
-
- /* Monitor dir */
- dir_file = g_file_new_for_path (path);
- monitor = g_file_monitor_directory (dir_file,
- G_FILE_MONITOR_NONE,
- NULL, NULL);
- if (monitor)
- {
- g_signal_connect (monitor, "changed",
- G_CALLBACK (thumbnailers_directory_changed),
- factory);
- priv->monitors = g_list_prepend (priv->monitors, monitor);
- }
- g_object_unref (dir_file);
-
- while ((dirent = g_dir_read_name (dir)))
- {
- Thumbnailer *thumb;
- gchar *filename;
-
- if (!g_str_has_suffix (dirent, THUMBNAILER_EXTENSION))
- continue;
-
- filename = g_build_filename (path, dirent, NULL);
- thumb = thumbnailer_new (filename);
- g_free (filename);
-
- if (thumb)
- gnome_desktop_thumbnail_factory_add_thumbnailer (factory, thumb);
- }
-
- g_dir_close (dir);
-}
-
-static void
-thumbnailers_directory_changed (GFileMonitor *monitor,
- GFile *file,
- GFile *other_file,
- GFileMonitorEvent event_type,
- GnomeDesktopThumbnailFactory *factory)
-{
- gchar *path;
-
- switch (event_type)
- {
- case G_FILE_MONITOR_EVENT_CREATED:
- case G_FILE_MONITOR_EVENT_CHANGED:
- case G_FILE_MONITOR_EVENT_DELETED:
- path = g_file_get_path (file);
- if (!g_str_has_suffix (path, THUMBNAILER_EXTENSION))
- {
- g_free (path);
- return;
- }
-
- if (event_type == G_FILE_MONITOR_EVENT_DELETED)
- remove_thumbnailer (factory, path);
- else
- update_or_create_thumbnailer (factory, path);
-
- g_free (path);
- break;
- case G_FILE_MONITOR_EVENT_UNMOUNTED:
- case G_FILE_MONITOR_EVENT_MOVED:
- path = g_file_get_path (file);
- remove_thumbnailers_for_dir (factory, path, monitor);
-
- if (event_type == G_FILE_MONITOR_EVENT_MOVED)
- gnome_desktop_thumbnail_factory_load_thumbnailers_for_dir (factory, path);
-
- g_free (path);
- break;
- case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
- case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
- case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
- case G_FILE_MONITOR_EVENT_RENAMED:
- case G_FILE_MONITOR_EVENT_MOVED_IN:
- case G_FILE_MONITOR_EVENT_MOVED_OUT:
- default:
- break;
- }
-}
-
-static void
-gnome_desktop_thumbnail_factory_load_thumbnailers (GnomeDesktopThumbnailFactory *factory)
-{
- GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv;
- const gchar * const *dirs;
- guint i;
-
- if (priv->loaded)
- return;
-
- dirs = get_thumbnailers_dirs ();
- for (i = 0; dirs[i]; i++)
- {
- gnome_desktop_thumbnail_factory_load_thumbnailers_for_dir (factory, dirs[i]);
- }
-
- priv->loaded = TRUE;
-}
-
-static void
-external_thumbnailers_disabled_all_changed_cb (GSettings *settings,
- const gchar *key,
- GnomeDesktopThumbnailFactory *factory)
-{
- GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv;
-
- g_mutex_lock (&priv->lock);
-
- priv->disabled = g_settings_get_boolean (priv->settings, "disable-all");
- if (priv->disabled)
- {
- g_strfreev (priv->disabled_types);
- priv->disabled_types = NULL;
- }
- else
- {
- priv->disabled_types = g_settings_get_strv (priv->settings, "disable");
- gnome_desktop_thumbnail_factory_load_thumbnailers (factory);
- }
-
- g_mutex_unlock (&priv->lock);
-}
-
-static void
-external_thumbnailers_disabled_changed_cb (GSettings *settings,
- const gchar *key,
- GnomeDesktopThumbnailFactory *factory)
-{
- GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv;
-
- g_mutex_lock (&priv->lock);
-
- if (!priv->disabled)
- {
- g_strfreev (priv->disabled_types);
- priv->disabled_types = g_settings_get_strv (priv->settings, "disable");
- }
-
- g_mutex_unlock (&priv->lock);
-}
-
-static void
-gnome_desktop_thumbnail_factory_init (GnomeDesktopThumbnailFactory *factory)
-{
- GnomeDesktopThumbnailFactoryPrivate *priv;
-
- factory->priv = GNOME_DESKTOP_THUMBNAIL_FACTORY_GET_PRIVATE (factory);
-
- priv = factory->priv;
-
- priv->size = GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL;
-
- priv->mime_types_map = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- (GDestroyNotify)g_free,
- (GDestroyNotify)thumbnailer_unref);
-
- g_mutex_init (&priv->lock);
-
- priv->settings = g_settings_new ("org.gnome.desktop.thumbnailers");
- priv->disabled = g_settings_get_boolean (priv->settings, "disable-all");
- if (!priv->disabled)
- priv->disabled_types = g_settings_get_strv (priv->settings, "disable");
- g_signal_connect (priv->settings, "changed::disable-all",
- G_CALLBACK (external_thumbnailers_disabled_all_changed_cb),
- factory);
- g_signal_connect (priv->settings, "changed::disable",
- G_CALLBACK (external_thumbnailers_disabled_changed_cb),
- factory);
-
- if (!priv->disabled)
- gnome_desktop_thumbnail_factory_load_thumbnailers (factory);
-}
-
-static void
-gnome_desktop_thumbnail_factory_finalize (GObject *object)
-{
- GnomeDesktopThumbnailFactory *factory;
- GnomeDesktopThumbnailFactoryPrivate *priv;
-
- factory = GNOME_DESKTOP_THUMBNAIL_FACTORY (object);
-
- priv = factory->priv;
-
- if (priv->thumbnailers)
- {
- g_list_free_full (priv->thumbnailers, (GDestroyNotify)thumbnailer_unref);
- priv->thumbnailers = NULL;
- }
-
- g_clear_pointer (&priv->mime_types_map, g_hash_table_destroy);
-
- if (priv->monitors)
- {
- g_list_free_full (priv->monitors, (GDestroyNotify)g_object_unref);
- priv->monitors = NULL;
- }
-
- g_mutex_clear (&priv->lock);
-
- g_clear_pointer (&priv->disabled_types, g_strfreev);
-
- if (priv->settings)
- {
- g_signal_handlers_disconnect_by_func (priv->settings,
- external_thumbnailers_disabled_all_changed_cb,
- factory);
- g_signal_handlers_disconnect_by_func (priv->settings,
- external_thumbnailers_disabled_changed_cb,
- factory);
- g_clear_object (&priv->settings);
- }
-
- if (G_OBJECT_CLASS (parent_class)->finalize)
- (* G_OBJECT_CLASS (parent_class)->finalize) (object);
-}
-
-static void
-gnome_desktop_thumbnail_factory_class_init (GnomeDesktopThumbnailFactoryClass *class)
-{
- GObjectClass *gobject_class;
-
- gobject_class = G_OBJECT_CLASS (class);
-
- gobject_class->finalize = gnome_desktop_thumbnail_factory_finalize;
-
- g_type_class_add_private (class, sizeof (GnomeDesktopThumbnailFactoryPrivate));
-}
-
-/**
- * gnome_desktop_thumbnail_factory_new:
- * @size: The thumbnail size to use
- *
- * Creates a new #GnomeDesktopThumbnailFactory.
- *
- * This function must be called on the main thread and is non-blocking.
- *
- * Return value: a new #GnomeDesktopThumbnailFactory
- *
- * Since: 2.2
- **/
-GnomeDesktopThumbnailFactory *
-gnome_desktop_thumbnail_factory_new (GnomeDesktopThumbnailSize size)
-{
- GnomeDesktopThumbnailFactory *factory;
-
- factory = g_object_new (GNOME_DESKTOP_TYPE_THUMBNAIL_FACTORY, NULL);
-
- factory->priv->size = size;
-
- return factory;
-}
-
-static char *
-thumbnail_filename (const char *uri)
-{
- GChecksum *checksum;
- guint8 digest[16];
- gsize digest_len = sizeof (digest);
- char *file;
-
- checksum = g_checksum_new (G_CHECKSUM_MD5);
- g_checksum_update (checksum, (const guchar *) uri, strlen (uri));
-
- g_checksum_get_digest (checksum, digest, &digest_len);
- g_assert (digest_len == 16);
-
- file = g_strconcat (g_checksum_get_string (checksum), ".png", NULL);
-
- g_checksum_free (checksum);
-
- return file;
-}
-
-static char *
-thumbnail_path (const char *uri,
- GnomeDesktopThumbnailSize size)
-{
- char *path, *file;
-
- file = thumbnail_filename (uri);
- path = g_build_filename (g_get_user_cache_dir (),
- "thumbnails",
- size == GNOME_DESKTOP_THUMBNAIL_SIZE_LARGE ? "large" : "normal",
- file,
- NULL);
- g_free (file);
- return path;
-}
-
-static char *
-thumbnail_failed_path (const char *uri)
-{
- char *path, *file;
-
- file = thumbnail_filename (uri);
- /* XXX: appname is only used for failed thumbnails. Is this a mistake? */
- path = g_build_filename (g_get_user_cache_dir (),
- "thumbnails",
- "fail",
- appname,
- file,
- NULL);
- g_free (file);
- return path;
-}
-
-static char *
-validate_thumbnail_path (char *path,
- const char *uri,
- time_t mtime,
- GnomeDesktopThumbnailSize size)
-{
- GdkPixbuf *pixbuf;
-
- pixbuf = gdk_pixbuf_new_from_file (path, NULL);
- if (pixbuf == NULL ||
- !gnome_desktop_thumbnail_is_valid (pixbuf, uri, mtime)) {
- g_free (path);
- return NULL;
- }
-
- g_clear_object (&pixbuf);
-
- return path;
-}
-
-static char *
-lookup_thumbnail_path (const char *uri,
- time_t mtime,
- GnomeDesktopThumbnailSize size)
-{
- char *path = thumbnail_path (uri, size);
- return validate_thumbnail_path (path, uri, mtime, size);
-}
-
-static char *
-lookup_failed_thumbnail_path (const char *uri,
- time_t mtime,
- GnomeDesktopThumbnailSize size)
-{
- char *path = thumbnail_failed_path (uri);
- return validate_thumbnail_path (path, uri, mtime, size);
-}
-
-/**
- * gnome_desktop_thumbnail_factory_lookup:
- * @factory: a #GnomeDesktopThumbnailFactory
- * @uri: the uri of a file
- * @mtime: the mtime of the file
- *
- * Tries to locate an existing thumbnail for the file specified.
- *
- * Usage of this function is threadsafe and does blocking I/O.
- *
- * Return value: The absolute path of the thumbnail, or %NULL if none exist.
- *
- * Since: 2.2
- **/
-char *
-gnome_desktop_thumbnail_factory_lookup (GnomeDesktopThumbnailFactory *factory,
- const char *uri,
- time_t mtime)
-{
- GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv;
-
- g_return_val_if_fail (uri != NULL, NULL);
-
- return lookup_thumbnail_path (uri, mtime, priv->size);
-}
-
-/**
- * gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail:
- * @factory: a #GnomeDesktopThumbnailFactory
- * @uri: the uri of a file
- * @mtime: the mtime of the file
- *
- * Tries to locate an failed thumbnail for the file specified. Writing
- * and looking for failed thumbnails is important to avoid to try to
- * thumbnail e.g. broken images several times.
- *
- * Usage of this function is threadsafe and does blocking I/O.
- *
- * Return value: TRUE if there is a failed thumbnail for the file.
- *
- * Since: 2.2
- **/
-gboolean
-gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail (GnomeDesktopThumbnailFactory *factory,
- const char *uri,
- time_t mtime)
-{
- char *path;
-
- g_return_val_if_fail (uri != NULL, FALSE);
-
- path = lookup_failed_thumbnail_path (uri, mtime, factory->priv->size);
- if (path == NULL)
- return FALSE;
-
- g_free (path);
-
- return TRUE;
-}
-
-/**
- * gnome_desktop_thumbnail_factory_can_thumbnail:
- * @factory: a #GnomeDesktopThumbnailFactory
- * @uri: the uri of a file
- * @mime_type: the mime type of the file
- * @mtime: the mtime of the file
- *
- * Returns TRUE if this GnomeDesktopThumbnailFactory can (at least try) to thumbnail
- * this file. Thumbnails or files with failed thumbnails won't be thumbnailed.
- *
- * Usage of this function is threadsafe and does blocking I/O.
- *
- * Return value: TRUE if the file can be thumbnailed.
- *
- * Since: 2.2
- **/
-gboolean
-gnome_desktop_thumbnail_factory_can_thumbnail (GnomeDesktopThumbnailFactory *factory,
- const char *uri,
- const char *mime_type,
- time_t mtime)
-{
- gboolean have_script = FALSE;
-
- /* Don't thumbnail thumbnails */
- if (uri &&
- strncmp (uri, "file:/", 6) == 0 &&
- strstr (uri, "/thumbnails/") != NULL)
- return FALSE;
-
- if (!mime_type)
- return FALSE;
-
- g_mutex_lock (&factory->priv->lock);
- if (!gnome_desktop_thumbnail_factory_is_disabled (factory, mime_type))
- {
- Thumbnailer *thumb;
-
- thumb = g_hash_table_lookup (factory->priv->mime_types_map, mime_type);
- have_script = (thumb != NULL);
- }
- g_mutex_unlock (&factory->priv->lock);
-
- if (have_script)
- {
- return !gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail (factory,
- uri,
- mtime);
- }
-
- return FALSE;
-}
-
-static GdkPixbuf *
-get_preview_thumbnail (const char *uri,
- int size)
-{
- GdkPixbuf *pixbuf;
- GFile *file;
- GFileInfo *file_info;
- GInputStream *input_stream;
- GObject *object;
-
- g_return_val_if_fail (uri != NULL, NULL);
-
- input_stream = NULL;
-
- file = g_file_new_for_uri (uri);
-
- /* First see if we can get an input stream via preview::icon */
- file_info = g_file_query_info (file,
- G_FILE_ATTRIBUTE_PREVIEW_ICON,
- G_FILE_QUERY_INFO_NONE,
- NULL, /* GCancellable */
- NULL); /* return location for GError */
- g_object_unref (file);
-
- if (file_info == NULL)
- return NULL;
-
- object = g_file_info_get_attribute_object (file_info,
- G_FILE_ATTRIBUTE_PREVIEW_ICON);
- if (object)
- g_object_ref (object);
- g_object_unref (file_info);
-
- if (!object)
- return NULL;
- if (!G_IS_LOADABLE_ICON (object)) {
- g_object_unref (object);
- return NULL;
- }
-
- input_stream = g_loadable_icon_load (G_LOADABLE_ICON (object),
- 0, /* size */
- NULL, /* return location for type */
- NULL, /* GCancellable */
- NULL); /* return location for GError */
- g_object_unref (object);
-
- if (!input_stream)
- return NULL;
-
- pixbuf = gdk_pixbuf_new_from_stream_at_scale (input_stream,
- size, size,
- TRUE, NULL, NULL);
- g_object_unref (input_stream);
-
- return pixbuf;
-}
-
-static GdkPixbuf *
-pixbuf_new_from_bytes (GBytes *bytes,
- GError **error)
-{
- g_autoptr(GdkPixbufLoader) loader = NULL;
-
- loader = gdk_pixbuf_loader_new_with_mime_type ("image/png", error);
- if (!loader)
- return NULL;
-
- if (!gdk_pixbuf_loader_write (loader,
- g_bytes_get_data (bytes, NULL),
- g_bytes_get_size (bytes),
- error))
- {
- return NULL;
- }
-
- if (!gdk_pixbuf_loader_close (loader, error))
- return NULL;
-
- return g_object_ref (gdk_pixbuf_loader_get_pixbuf (loader));
-}
-
-/**
- * gnome_desktop_thumbnail_factory_generate_thumbnail:
- * @factory: a #GnomeDesktopThumbnailFactory
- * @uri: the uri of a file
- * @mime_type: the mime type of the file
- *
- * Tries to generate a thumbnail for the specified file. If it succeeds
- * it returns a pixbuf that can be used as a thumbnail.
- *
- * Usage of this function is threadsafe and does blocking I/O.
- *
- * Return value: (transfer full): thumbnail pixbuf if thumbnailing succeeded, %NULL otherwise.
- *
- * Since: 2.2
- **/
-GdkPixbuf *
-gnome_desktop_thumbnail_factory_generate_thumbnail (GnomeDesktopThumbnailFactory *factory,
- const char *uri,
- const char *mime_type)
-{
- GdkPixbuf *pixbuf;
- char *script;
- int size;
-
- g_return_val_if_fail (uri != NULL, NULL);
- g_return_val_if_fail (mime_type != NULL, NULL);
-
- /* Doesn't access any volatile fields in factory, so it's threadsafe */
-
- size = 128;
- if (factory->priv->size == GNOME_DESKTOP_THUMBNAIL_SIZE_LARGE)
- size = 256;
-
- pixbuf = get_preview_thumbnail (uri, size);
- if (pixbuf != NULL)
- return pixbuf;
-
- script = NULL;
- g_mutex_lock (&factory->priv->lock);
- if (!gnome_desktop_thumbnail_factory_is_disabled (factory, mime_type))
- {
- Thumbnailer *thumb;
-
- thumb = g_hash_table_lookup (factory->priv->mime_types_map, mime_type);
- if (thumb)
- script = g_strdup (thumb->command);
- }
- g_mutex_unlock (&factory->priv->lock);
-
- if (script)
- {
- GBytes *data;
- GError *error = NULL;
-
- data = gnome_desktop_thumbnail_script_exec (script, size, uri, &error);
- if (data)
- {
- pixbuf = pixbuf_new_from_bytes (data, &error);
- if (!pixbuf)
- {
- g_debug ("Could not load thumbnail pixbuf: %s", error->message);
- g_error_free (error);
- }
- g_bytes_unref (data);
- }
- else
- {
- g_debug ("Thumbnail script ('%s') failed for '%s': %s",
- script, uri, error ? error->message : "no details");
- g_clear_error (&error);
- }
- }
- else
- {
- g_debug ("Could not find thumbnailer for mime-type '%s'",
- mime_type);
- }
-
- g_free (script);
-
- return pixbuf;
-}
-
-static gboolean
-save_thumbnail (GdkPixbuf *pixbuf,
- char *path,
- const char *uri,
- time_t mtime)
-{
- char *dirname;
- char *tmp_path = NULL;
- int tmp_fd;
- char mtime_str[21];
- gboolean ret = FALSE;
- GError *error = NULL;
- const char *width, *height;
-
- if (pixbuf == NULL)
- return FALSE;
-
- dirname = g_path_get_dirname (path);
-
- if (g_mkdir_with_parents (dirname, 0700) != 0)
- goto out;
-
- tmp_path = g_strconcat (path, ".XXXXXX", NULL);
- tmp_fd = g_mkstemp (tmp_path);
-
- if (tmp_fd == -1)
- goto out;
- close (tmp_fd);
-
- g_snprintf (mtime_str, 21, "%" G_GINT64_FORMAT, (gint64) mtime);
- width = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::Image::Width");
- height = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::Image::Height");
-
- error = NULL;
- if (width != NULL && height != NULL)
- ret = gdk_pixbuf_save (pixbuf,
- tmp_path,
- "png", &error,
- "tEXt::Thumb::Image::Width", width,
- "tEXt::Thumb::Image::Height", height,
- "tEXt::Thumb::URI", uri,
- "tEXt::Thumb::MTime", mtime_str,
- "tEXt::Software", "GNOME::ThumbnailFactory",
- NULL);
- else
- ret = gdk_pixbuf_save (pixbuf,
- tmp_path,
- "png", &error,
- "tEXt::Thumb::URI", uri,
- "tEXt::Thumb::MTime", mtime_str,
- "tEXt::Software", "GNOME::ThumbnailFactory",
- NULL);
-
- if (!ret)
- goto out;
-
- g_chmod (tmp_path, 0600);
- g_rename (tmp_path, path);
-
- out:
- if (error != NULL)
- {
- g_warning ("Failed to create thumbnail %s: %s", tmp_path, error->message);
- g_error_free (error);
- }
- g_unlink (tmp_path);
- g_free (tmp_path);
- g_free (dirname);
- return ret;
-}
-
-static GdkPixbuf *
-make_failed_thumbnail (void)
-{
- GdkPixbuf *pixbuf;
-
- pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 1, 1);
- gdk_pixbuf_fill (pixbuf, 0x00000000);
- return pixbuf;
-}
-
-/**
- * gnome_desktop_thumbnail_factory_save_thumbnail:
- * @factory: a #GnomeDesktopThumbnailFactory
- * @thumbnail: the thumbnail as a pixbuf
- * @uri: the uri of a file
- * @original_mtime: the modification time of the original file
- *
- * Saves @thumbnail at the right place. If the save fails a
- * failed thumbnail is written.
- *
- * Usage of this function is threadsafe and does blocking I/O.
- *
- * Since: 2.2
- **/
-void
-gnome_desktop_thumbnail_factory_save_thumbnail (GnomeDesktopThumbnailFactory *factory,
- GdkPixbuf *thumbnail,
- const char *uri,
- time_t original_mtime)
-{
- char *path;
-
- path = thumbnail_path (uri, factory->priv->size);
- if (!save_thumbnail (thumbnail, path, uri, original_mtime))
- {
- thumbnail = make_failed_thumbnail ();
- g_free (path);
- path = thumbnail_failed_path (uri);
- save_thumbnail (thumbnail, path, uri, original_mtime);
- g_object_unref (thumbnail);
- }
- g_free (path);
-}
-
-/**
- * gnome_desktop_thumbnail_factory_create_failed_thumbnail:
- * @factory: a #GnomeDesktopThumbnailFactory
- * @uri: the uri of a file
- * @mtime: the modification time of the file
- *
- * Creates a failed thumbnail for the file so that we don't try
- * to re-thumbnail the file later.
- *
- * Usage of this function is threadsafe and does blocking I/O.
- *
- * Since: 2.2
- **/
-void
-gnome_desktop_thumbnail_factory_create_failed_thumbnail (GnomeDesktopThumbnailFactory *factory,
- const char *uri,
- time_t mtime)
-{
- char *path;
- GdkPixbuf *pixbuf;
-
- path = thumbnail_failed_path (uri);
- pixbuf = make_failed_thumbnail ();
- save_thumbnail (pixbuf, path, uri, mtime);
- g_free (path);
- g_object_unref (pixbuf);
-}
-
-/**
- * gnome_desktop_thumbnail_path_for_uri:
- * @uri: an uri
- * @size: a thumbnail size
- *
- * Returns the filename that a thumbnail of size @size for @uri would have.
- * This function is threadsafe and does no blocking I/O.
- *
- * Return value: an absolute filename
- *
- * Since: 2.2
- **/
-char *
-gnome_desktop_thumbnail_path_for_uri (const char *uri,
- GnomeDesktopThumbnailSize size)
-{
- return thumbnail_path (uri, size);
-}
-
-/**
- * gnome_desktop_thumbnail_is_valid:
- * @pixbuf: an loaded thumbnail #GdkPixbuf
- * @uri: a uri
- * @mtime: the mtime
- *
- * Returns whether the thumbnail has the correct uri and mtime embedded in the
- * png options. This function is threadsafe and does no blocking I/O.
- *
- * Return value: TRUE if the thumbnail has the right @uri and @mtime
- *
- * Since: 2.2
- **/
-gboolean
-gnome_desktop_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 (g_strcmp0 (uri, thumb_uri) != 0)
- return FALSE;
-
- thumb_mtime_str = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::MTime");
- if (!thumb_mtime_str)
- return FALSE;
- thumb_mtime = atol (thumb_mtime_str);
- if (mtime != thumb_mtime)
- return FALSE;
-
- return TRUE;
-}