Better caching for document textures and information
Move thumbnail creation into ShellTextureCache. It's now asynchronous, and we cache the result. Create a DocManager class which keeps around the DocInfo objects between invocations. This is also where we ensure we remove thumbnails for recent items not known anymore.
This commit is contained in:
parent
1cb6fc004b
commit
5d067ec718
@ -5,6 +5,8 @@ const Gio = imports.gi.Gio;
|
|||||||
const Gtk = imports.gi.Gtk;
|
const Gtk = imports.gi.Gtk;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Signals = imports.signals;
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
|
|
||||||
const THUMBNAIL_ICON_MARGIN = 2;
|
const THUMBNAIL_ICON_MARGIN = 2;
|
||||||
@ -22,34 +24,19 @@ DocInfo.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
createIcon : function(size) {
|
createIcon : function(size) {
|
||||||
let icon = new Clutter.Texture();
|
let icon = null;
|
||||||
let iconPixbuf;
|
let defaultPixbuf = this._recentInfo.get_icon(size);
|
||||||
|
|
||||||
if (this.uri.match("^file://"))
|
if (this.uri.match("^file://")) {
|
||||||
iconPixbuf = Shell.get_thumbnail(this.uri, this.mimeType);
|
icon = Shell.TextureCache.get_default().load_thumbnail(size, this.uri, this.mimeType,
|
||||||
|
defaultPixbuf);
|
||||||
if (iconPixbuf) {
|
|
||||||
// We calculate the width and height of the texture so as
|
|
||||||
// to preserve the aspect ratio of the thumbnail. Because
|
|
||||||
// the images generated based on thumbnails don't have an
|
|
||||||
// internal padding like system icons do, we create a
|
|
||||||
// slightly smaller texture and then create a group around
|
|
||||||
// it for padding purposes
|
|
||||||
|
|
||||||
let scalingFactor = (size - THUMBNAIL_ICON_MARGIN * 2) / Math.max(iconPixbuf.get_width(), iconPixbuf.get_height());
|
|
||||||
icon.set_width(Math.ceil(iconPixbuf.get_width() * scalingFactor));
|
|
||||||
icon.set_height(Math.ceil(iconPixbuf.get_height() * scalingFactor));
|
|
||||||
Shell.clutter_texture_set_from_pixbuf(icon, iconPixbuf);
|
|
||||||
|
|
||||||
let group = new Clutter.Group({ width: size,
|
|
||||||
height: size });
|
|
||||||
group.add_actor(icon);
|
|
||||||
icon.set_position(THUMBNAIL_ICON_MARGIN, THUMBNAIL_ICON_MARGIN);
|
|
||||||
return group;
|
|
||||||
} else {
|
|
||||||
Shell.clutter_texture_set_from_pixbuf(icon, this._recentInfo.get_icon(size));
|
|
||||||
return icon;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!icon) {
|
||||||
|
icon = new Clutter.Texture({});
|
||||||
|
Shell.clutter_texture_set_from_pixbuf(icon, defaultPixbuf);
|
||||||
|
}
|
||||||
|
return icon;
|
||||||
},
|
},
|
||||||
|
|
||||||
launch : function() {
|
launch : function() {
|
||||||
@ -112,3 +99,58 @@ DocInfo.prototype = {
|
|||||||
return this._recentInfo.get_modified();
|
return this._recentInfo.get_modified();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var docManagerInstance = null;
|
||||||
|
|
||||||
|
function getDocManager(size) {
|
||||||
|
if (docManagerInstance == null)
|
||||||
|
docManagerInstance = new DocManager(size);
|
||||||
|
return docManagerInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DocManager(size) {
|
||||||
|
this._init(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
DocManager.prototype = {
|
||||||
|
_init: function(iconSize) {
|
||||||
|
this._iconSize = iconSize;
|
||||||
|
this._recentManager = Gtk.RecentManager.get_default();
|
||||||
|
this._items = {};
|
||||||
|
this._recentManager.connect('changed', Lang.bind(this, function(recentManager) {
|
||||||
|
this._reload();
|
||||||
|
this.emit('changed');
|
||||||
|
}));
|
||||||
|
this._reload();
|
||||||
|
},
|
||||||
|
|
||||||
|
_reload: function() {
|
||||||
|
let docs = this._recentManager.get_items();
|
||||||
|
let newItems = {};
|
||||||
|
for (let i = 0; i < docs.length; i++) {
|
||||||
|
let recentInfo = docs[i];
|
||||||
|
let docInfo = new DocInfo(recentInfo);
|
||||||
|
|
||||||
|
// we use GtkRecentInfo URI as an item Id
|
||||||
|
newItems[docInfo.uri] = docInfo;
|
||||||
|
}
|
||||||
|
let deleted = {};
|
||||||
|
for (var uri in this._items) {
|
||||||
|
if (!(uri in newItems))
|
||||||
|
deleted[uri] = 1;
|
||||||
|
}
|
||||||
|
/* If we'd cached any thumbnail references that no longer exist,
|
||||||
|
dump them here */
|
||||||
|
let texCache = Shell.TextureCache.get_default();
|
||||||
|
for (var uri in deleted) {
|
||||||
|
texCache.unref_thumbnail(this._iconSize, uri);
|
||||||
|
}
|
||||||
|
this._items = newItems;
|
||||||
|
},
|
||||||
|
|
||||||
|
getItems: function() {
|
||||||
|
return this._items;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Signals.addSignalMethods(DocManager.prototype);
|
||||||
|
@ -77,9 +77,10 @@ DocDisplay.prototype = {
|
|||||||
_init : function(width) {
|
_init : function(width) {
|
||||||
GenericDisplay.GenericDisplay.prototype._init.call(this, width);
|
GenericDisplay.GenericDisplay.prototype._init.call(this, width);
|
||||||
let me = this;
|
let me = this;
|
||||||
this._recentManager = Gtk.RecentManager.get_default();
|
|
||||||
|
this._docManager = DocInfo.getDocManager(GenericDisplay.ITEM_DISPLAY_ICON_SIZE);
|
||||||
this._docsStale = true;
|
this._docsStale = true;
|
||||||
this._recentManager.connect('changed', function(recentManager, userData) {
|
this._docManager.connect('changed', function(mgr, userData) {
|
||||||
me._docsStale = true;
|
me._docsStale = true;
|
||||||
// Changes in local recent files should not happen when we are in the overlay mode,
|
// Changes in local recent files should not happen when we are in the overlay mode,
|
||||||
// but redisplaying right away is cool when we use Zephyr.
|
// but redisplaying right away is cool when we use Zephyr.
|
||||||
@ -96,15 +97,7 @@ DocDisplay.prototype = {
|
|||||||
let me = this;
|
let me = this;
|
||||||
if (!this._docsStale)
|
if (!this._docsStale)
|
||||||
return;
|
return;
|
||||||
this._allItems = {};
|
this._allItems = this._docManager.getItems();
|
||||||
let docs = this._recentManager.get_items();
|
|
||||||
for (let i = 0; i < docs.length; i++) {
|
|
||||||
let recentInfo = docs[i];
|
|
||||||
let docInfo = new DocInfo.DocInfo(recentInfo);
|
|
||||||
|
|
||||||
// we use GtkRecentInfo URI as an item Id
|
|
||||||
this._allItems[docInfo.uri] = docInfo;
|
|
||||||
}
|
|
||||||
this._docsStale = false;
|
this._docsStale = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <dbus/dbus-glib.h>
|
#include <dbus/dbus-glib.h>
|
||||||
#include <libgnomeui/gnome-thumbnail.h>
|
|
||||||
#include <gio/gio.h>
|
#include <gio/gio.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <X11/extensions/Xfixes.h>
|
#include <X11/extensions/Xfixes.h>
|
||||||
@ -339,73 +338,6 @@ shell_clutter_texture_set_from_pixbuf (ClutterTexture *texture,
|
|||||||
0, NULL);
|
0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static GnomeThumbnailFactory *thumbnail_factory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* shell_get_thumbnail:
|
|
||||||
*
|
|
||||||
* @uri: URI of the file to thumbnail
|
|
||||||
*
|
|
||||||
* @mime_type: Mime-Type of the file to thumbnail
|
|
||||||
*
|
|
||||||
* Return value: #GdkPixbuf containing a thumbnail for file @uri
|
|
||||||
* if the thumbnail exists or can be generated, %NULL otherwise
|
|
||||||
*/
|
|
||||||
GdkPixbuf *
|
|
||||||
shell_get_thumbnail(const gchar *uri,
|
|
||||||
const gchar *mime_type)
|
|
||||||
{
|
|
||||||
char *existing_thumbnail;
|
|
||||||
GdkPixbuf *pixbuf = NULL;
|
|
||||||
GError *error = NULL;
|
|
||||||
GFile *file = NULL;
|
|
||||||
GFileInfo *file_info = NULL;
|
|
||||||
GTimeVal mtime_g;
|
|
||||||
time_t mtime = 0;
|
|
||||||
|
|
||||||
file = g_file_new_for_uri (uri);
|
|
||||||
file_info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED, G_FILE_QUERY_INFO_NONE, NULL, NULL);
|
|
||||||
g_object_unref (file);
|
|
||||||
if (file_info) {
|
|
||||||
g_file_info_get_modification_time (file_info, &mtime_g);
|
|
||||||
g_object_unref (file_info);
|
|
||||||
mtime = (time_t) mtime_g.tv_sec;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thumbnail_factory == NULL)
|
|
||||||
thumbnail_factory = gnome_thumbnail_factory_new (GNOME_THUMBNAIL_SIZE_NORMAL);
|
|
||||||
|
|
||||||
existing_thumbnail = gnome_thumbnail_factory_lookup (thumbnail_factory, uri, mtime);
|
|
||||||
|
|
||||||
if (existing_thumbnail != NULL)
|
|
||||||
{
|
|
||||||
pixbuf = gdk_pixbuf_new_from_file(existing_thumbnail, &error);
|
|
||||||
if (error != NULL)
|
|
||||||
{
|
|
||||||
g_warning("Could not generate a pixbuf from file %s: %s", existing_thumbnail, error->message);
|
|
||||||
g_clear_error (&error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (gnome_thumbnail_factory_has_valid_failed_thumbnail (thumbnail_factory, uri, mtime))
|
|
||||||
return NULL;
|
|
||||||
else if (gnome_thumbnail_factory_can_thumbnail (thumbnail_factory, uri, mime_type, mtime))
|
|
||||||
{
|
|
||||||
pixbuf = gnome_thumbnail_factory_generate_thumbnail (thumbnail_factory, uri, mime_type);
|
|
||||||
if (pixbuf)
|
|
||||||
{
|
|
||||||
// we need to save the thumbnail so that we don't need to generate it again in the future
|
|
||||||
gnome_thumbnail_factory_save_thumbnail (thumbnail_factory, pixbuf, uri, mtime);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
g_warning ("Could not generate thumbnail for %s", uri);
|
|
||||||
gnome_thumbnail_factory_create_failed_thumbnail (thumbnail_factory, uri, mtime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pixbuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* shell_get_event_key_symbol:
|
* shell_get_event_key_symbol:
|
||||||
*
|
*
|
||||||
|
@ -36,8 +36,6 @@ GType shell_global_get_type (void) G_GNUC_CONST;
|
|||||||
gboolean shell_clutter_texture_set_from_pixbuf (ClutterTexture *texture,
|
gboolean shell_clutter_texture_set_from_pixbuf (ClutterTexture *texture,
|
||||||
GdkPixbuf *pixbuf);
|
GdkPixbuf *pixbuf);
|
||||||
|
|
||||||
GdkPixbuf *shell_get_thumbnail(const gchar *uri, const gchar *mime_type);
|
|
||||||
|
|
||||||
guint16 shell_get_event_key_symbol(ClutterEvent *event);
|
guint16 shell_get_event_key_symbol(ClutterEvent *event);
|
||||||
|
|
||||||
guint16 shell_get_button_event_click_count(ClutterEvent *event);
|
guint16 shell_get_button_event_click_count(ClutterEvent *event);
|
||||||
|
@ -1,16 +1,20 @@
|
|||||||
#include "shell-texture-cache.h"
|
#include "shell-texture-cache.h"
|
||||||
#include "shell-global.h"
|
#include "shell-global.h"
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
#include <libgnomeui/gnome-thumbnail.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
GIcon *icon;
|
GIcon *icon;
|
||||||
|
gchar *thumbnail_uri;
|
||||||
guint size;
|
guint size;
|
||||||
} CacheKey;
|
} CacheKey;
|
||||||
|
|
||||||
struct _ShellTextureCachePrivate
|
struct _ShellTextureCachePrivate
|
||||||
{
|
{
|
||||||
GHashTable *gicon_cache; /* CacheKey -> CoglTexture* */
|
GHashTable *gicon_cache; /* CacheKey -> CoglTexture* */
|
||||||
|
GnomeThumbnailFactory *thumbnails;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void shell_texture_cache_dispose (GObject *object);
|
static void shell_texture_cache_dispose (GObject *object);
|
||||||
@ -25,6 +29,8 @@ cache_key_hash (gconstpointer a)
|
|||||||
|
|
||||||
if (akey->icon)
|
if (akey->icon)
|
||||||
return g_icon_hash (akey->icon) + 31*akey->size;
|
return g_icon_hash (akey->icon) + 31*akey->size;
|
||||||
|
else if (akey->thumbnail_uri)
|
||||||
|
return g_str_hash (akey->thumbnail_uri) + 31*akey->size;
|
||||||
g_assert_not_reached ();
|
g_assert_not_reached ();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +45,10 @@ cache_key_equal (gconstpointer a,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
if (akey->icon && bkey->icon)
|
if (akey->icon && bkey->icon)
|
||||||
return g_icon_equal (akey->icon, bkey->icon);
|
return g_icon_equal (akey->icon, bkey->icon);
|
||||||
g_assert_not_reached ();
|
else if (akey->thumbnail_uri && bkey->thumbnail_uri)
|
||||||
|
return strcmp (akey->thumbnail_uri, bkey->thumbnail_uri) == 0;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -48,6 +57,8 @@ cache_key_destroy (gpointer a)
|
|||||||
CacheKey *akey = (CacheKey*)a;
|
CacheKey *akey = (CacheKey*)a;
|
||||||
if (akey->icon)
|
if (akey->icon)
|
||||||
g_object_unref (akey->icon);
|
g_object_unref (akey->icon);
|
||||||
|
if (akey->thumbnail_uri)
|
||||||
|
g_free (akey->thumbnail_uri);
|
||||||
g_free (akey);
|
g_free (akey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,6 +77,7 @@ shell_texture_cache_init (ShellTextureCache *self)
|
|||||||
self->priv = g_new0 (ShellTextureCachePrivate, 1);
|
self->priv = g_new0 (ShellTextureCachePrivate, 1);
|
||||||
self->priv->gicon_cache = g_hash_table_new_full (cache_key_hash, cache_key_equal,
|
self->priv->gicon_cache = g_hash_table_new_full (cache_key_hash, cache_key_equal,
|
||||||
cache_key_destroy, cogl_handle_unref);
|
cache_key_destroy, cogl_handle_unref);
|
||||||
|
self->priv->thumbnails = gnome_thumbnail_factory_new (GNOME_THUMBNAIL_SIZE_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -77,6 +89,10 @@ shell_texture_cache_dispose (GObject *object)
|
|||||||
g_hash_table_destroy (self->priv->gicon_cache);
|
g_hash_table_destroy (self->priv->gicon_cache);
|
||||||
self->priv->gicon_cache = NULL;
|
self->priv->gicon_cache = NULL;
|
||||||
|
|
||||||
|
if (self->priv->thumbnails)
|
||||||
|
g_object_unref (self->priv->thumbnails);
|
||||||
|
self->priv->thumbnails = NULL;
|
||||||
|
|
||||||
G_OBJECT_CLASS (shell_texture_cache_parent_class)->dispose (object);
|
G_OBJECT_CLASS (shell_texture_cache_parent_class)->dispose (object);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +110,11 @@ shell_texture_cache_new ()
|
|||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
ShellTextureCache *cache;
|
||||||
char *uri;
|
char *uri;
|
||||||
|
char *mimetype;
|
||||||
|
GdkPixbuf *fallback_pixbuf;
|
||||||
|
gboolean thumbnail;
|
||||||
GIcon *icon;
|
GIcon *icon;
|
||||||
GtkIconInfo *icon_info;
|
GtkIconInfo *icon_info;
|
||||||
gint width;
|
gint width;
|
||||||
@ -102,7 +122,6 @@ typedef struct {
|
|||||||
gpointer user_data;
|
gpointer user_data;
|
||||||
} AsyncIconLookupData;
|
} AsyncIconLookupData;
|
||||||
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
compute_pixbuf_scale (gint width,
|
compute_pixbuf_scale (gint width,
|
||||||
gint height,
|
gint height,
|
||||||
@ -200,6 +219,10 @@ icon_lookup_data_destroy (gpointer p)
|
|||||||
}
|
}
|
||||||
else if (data->uri)
|
else if (data->uri)
|
||||||
g_free (data->uri);
|
g_free (data->uri);
|
||||||
|
if (data->mimetype)
|
||||||
|
g_free (data->mimetype);
|
||||||
|
if (data->fallback_pixbuf)
|
||||||
|
g_object_unref (data->fallback_pixbuf);
|
||||||
|
|
||||||
g_free (data);
|
g_free (data);
|
||||||
}
|
}
|
||||||
@ -328,6 +351,56 @@ out:
|
|||||||
return rotated_pixbuf;
|
return rotated_pixbuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GdkPixbuf *
|
||||||
|
impl_load_thumbnail (ShellTextureCache *cache,
|
||||||
|
const char *uri,
|
||||||
|
const char *mime_type,
|
||||||
|
guint size,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GnomeThumbnailFactory *thumbnail_factory;
|
||||||
|
GdkPixbuf *pixbuf = NULL;
|
||||||
|
GFile *file;
|
||||||
|
GFileInfo *file_info;
|
||||||
|
GTimeVal mtime_g;
|
||||||
|
time_t mtime = 0;
|
||||||
|
char *existing_thumbnail;
|
||||||
|
|
||||||
|
file = g_file_new_for_uri (uri);
|
||||||
|
file_info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED, G_FILE_QUERY_INFO_NONE, NULL, NULL);
|
||||||
|
g_object_unref (file);
|
||||||
|
if (file_info)
|
||||||
|
{
|
||||||
|
g_file_info_get_modification_time (file_info, &mtime_g);
|
||||||
|
g_object_unref (file_info);
|
||||||
|
mtime = (time_t) mtime_g.tv_sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
thumbnail_factory = cache->priv->thumbnails;
|
||||||
|
|
||||||
|
existing_thumbnail = gnome_thumbnail_factory_lookup (thumbnail_factory, uri, mtime);
|
||||||
|
|
||||||
|
if (existing_thumbnail != NULL)
|
||||||
|
pixbuf = gdk_pixbuf_new_from_file_at_size (existing_thumbnail, size, size, error);
|
||||||
|
else if (gnome_thumbnail_factory_has_valid_failed_thumbnail (thumbnail_factory, uri, mtime))
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Has failed thumbnail");
|
||||||
|
else if (gnome_thumbnail_factory_can_thumbnail (thumbnail_factory, uri, mime_type, mtime))
|
||||||
|
{
|
||||||
|
pixbuf = gnome_thumbnail_factory_generate_thumbnail (thumbnail_factory, uri, mime_type);
|
||||||
|
if (pixbuf)
|
||||||
|
{
|
||||||
|
// we need to save the thumbnail so that we don't need to generate it again in the future
|
||||||
|
gnome_thumbnail_factory_save_thumbnail (thumbnail_factory, pixbuf, uri, mtime);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to generate thumbnail");
|
||||||
|
gnome_thumbnail_factory_create_failed_thumbnail (thumbnail_factory, uri, mtime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pixbuf;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
load_pixbuf_thread (GSimpleAsyncResult *result,
|
load_pixbuf_thread (GSimpleAsyncResult *result,
|
||||||
GObject *object,
|
GObject *object,
|
||||||
@ -337,9 +410,19 @@ load_pixbuf_thread (GSimpleAsyncResult *result,
|
|||||||
AsyncIconLookupData *data;
|
AsyncIconLookupData *data;
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
|
|
||||||
data = g_object_get_data (G_OBJECT (result), "load_icon_pixbuf_async");
|
data = g_object_get_data (G_OBJECT (result), "load_pixbuf_async");
|
||||||
|
g_assert (data != NULL);
|
||||||
|
|
||||||
if (data->uri)
|
if (data->thumbnail)
|
||||||
|
{
|
||||||
|
pixbuf = impl_load_thumbnail (data->cache, data->uri, data->mimetype, data->width, &error);
|
||||||
|
if (!pixbuf && data->fallback_pixbuf)
|
||||||
|
{
|
||||||
|
g_clear_error (&error);
|
||||||
|
pixbuf = g_object_ref (data->fallback_pixbuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (data->uri)
|
||||||
pixbuf = impl_load_pixbuf_file (data->uri, data->width, data->height, &error);
|
pixbuf = impl_load_pixbuf_file (data->uri, data->width, data->height, &error);
|
||||||
else if (data->icon)
|
else if (data->icon)
|
||||||
pixbuf = impl_load_pixbuf_gicon (data->icon, data->icon_info, data->width, &error);
|
pixbuf = impl_load_pixbuf_gicon (data->icon, data->icon_info, data->width, &error);
|
||||||
@ -375,6 +458,7 @@ load_icon_pixbuf_async (ShellTextureCache *cache,
|
|||||||
AsyncIconLookupData *data;
|
AsyncIconLookupData *data;
|
||||||
|
|
||||||
data = g_new0 (AsyncIconLookupData, 1);
|
data = g_new0 (AsyncIconLookupData, 1);
|
||||||
|
data->cache = cache;
|
||||||
data->icon = g_object_ref (icon);
|
data->icon = g_object_ref (icon);
|
||||||
data->icon_info = gtk_icon_info_copy (icon_info);
|
data->icon_info = gtk_icon_info_copy (icon_info);
|
||||||
data->width = data->height = size;
|
data->width = data->height = size;
|
||||||
@ -382,7 +466,7 @@ load_icon_pixbuf_async (ShellTextureCache *cache,
|
|||||||
|
|
||||||
result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data, load_icon_pixbuf_async);
|
result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data, load_icon_pixbuf_async);
|
||||||
|
|
||||||
g_object_set_data_full (G_OBJECT (result), "load_icon_pixbuf_async", data, icon_lookup_data_destroy);
|
g_object_set_data_full (G_OBJECT (result), "load_pixbuf_async", data, icon_lookup_data_destroy);
|
||||||
g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, cancellable);
|
g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, cancellable);
|
||||||
|
|
||||||
g_object_unref (result);
|
g_object_unref (result);
|
||||||
@ -401,6 +485,7 @@ load_uri_pixbuf_async (ShellTextureCache *cache,
|
|||||||
AsyncIconLookupData *data;
|
AsyncIconLookupData *data;
|
||||||
|
|
||||||
data = g_new0 (AsyncIconLookupData, 1);
|
data = g_new0 (AsyncIconLookupData, 1);
|
||||||
|
data->cache = cache;
|
||||||
data->uri = g_strdup (uri);
|
data->uri = g_strdup (uri);
|
||||||
data->width = width;
|
data->width = width;
|
||||||
data->height = height;
|
data->height = height;
|
||||||
@ -408,7 +493,38 @@ load_uri_pixbuf_async (ShellTextureCache *cache,
|
|||||||
|
|
||||||
result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data, load_uri_pixbuf_async);
|
result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data, load_uri_pixbuf_async);
|
||||||
|
|
||||||
g_object_set_data_full (G_OBJECT (result), "load_uri_pixbuf_async", data, icon_lookup_data_destroy);
|
g_object_set_data_full (G_OBJECT (result), "load_pixbuf_async", data, icon_lookup_data_destroy);
|
||||||
|
g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, cancellable);
|
||||||
|
|
||||||
|
g_object_unref (result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
load_thumbnail_async (ShellTextureCache *cache,
|
||||||
|
const char *uri,
|
||||||
|
const char *mimetype,
|
||||||
|
guint size,
|
||||||
|
GdkPixbuf *fallback,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GSimpleAsyncResult *result;
|
||||||
|
AsyncIconLookupData *data;
|
||||||
|
|
||||||
|
data = g_new0 (AsyncIconLookupData, 1);
|
||||||
|
data->cache = cache;
|
||||||
|
data->uri = g_strdup (uri);
|
||||||
|
data->mimetype = g_strdup (mimetype);
|
||||||
|
data->fallback_pixbuf = g_object_ref (fallback);
|
||||||
|
data->thumbnail = TRUE;
|
||||||
|
data->width = size;
|
||||||
|
data->height = size;
|
||||||
|
data->user_data = user_data;
|
||||||
|
|
||||||
|
result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data, load_thumbnail_async);
|
||||||
|
|
||||||
|
g_object_set_data_full (G_OBJECT (result), "load_pixbuf_async", data, icon_lookup_data_destroy);
|
||||||
g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, cancellable);
|
g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, cancellable);
|
||||||
|
|
||||||
g_object_unref (result);
|
g_object_unref (result);
|
||||||
@ -425,6 +541,8 @@ load_pixbuf_async_finish (ShellTextureCache *cache, GAsyncResult *result, GError
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *uri;
|
char *uri;
|
||||||
|
gboolean thumbnail;
|
||||||
|
char *mimetype;
|
||||||
GIcon *icon;
|
GIcon *icon;
|
||||||
GtkIconInfo *icon_info;
|
GtkIconInfo *icon_info;
|
||||||
guint width;
|
guint width;
|
||||||
@ -469,12 +587,15 @@ on_pixbuf_loaded (GObject *source,
|
|||||||
|
|
||||||
g_object_unref (pixbuf);
|
g_object_unref (pixbuf);
|
||||||
|
|
||||||
if (data->icon)
|
if (data->icon || data->thumbnail)
|
||||||
{
|
{
|
||||||
gpointer orig_key, value;
|
gpointer orig_key, value;
|
||||||
|
|
||||||
key = g_new0 (CacheKey, 1);
|
key = g_new0 (CacheKey, 1);
|
||||||
key->icon = g_object_ref (data->icon);
|
if (data->icon)
|
||||||
|
key->icon = g_object_ref (data->icon);
|
||||||
|
else
|
||||||
|
key->thumbnail_uri = g_strdup (data->uri);
|
||||||
key->size = data->width;
|
key->size = data->width;
|
||||||
|
|
||||||
if (!g_hash_table_lookup_extended (cache->priv->gicon_cache, key,
|
if (!g_hash_table_lookup_extended (cache->priv->gicon_cache, key,
|
||||||
@ -495,10 +616,15 @@ out:
|
|||||||
}
|
}
|
||||||
else if (data->uri)
|
else if (data->uri)
|
||||||
g_free (data->uri);
|
g_free (data->uri);
|
||||||
|
|
||||||
|
if (data->mimetype)
|
||||||
|
g_free (data->mimetype);
|
||||||
|
|
||||||
/* Alternatively we could weakref and just do nothing if the texture
|
/* Alternatively we could weakref and just do nothing if the texture
|
||||||
is destroyed */
|
is destroyed */
|
||||||
g_object_unref (data->texture);
|
g_object_unref (data->texture);
|
||||||
|
|
||||||
|
g_clear_error (&error);
|
||||||
g_free (data);
|
g_free (data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -522,6 +648,7 @@ shell_texture_cache_load_gicon (ShellTextureCache *cache,
|
|||||||
texture = CLUTTER_TEXTURE (clutter_texture_new ());
|
texture = CLUTTER_TEXTURE (clutter_texture_new ());
|
||||||
clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size);
|
clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size);
|
||||||
|
|
||||||
|
memset (&key, 0, sizeof(key));
|
||||||
key.icon = icon;
|
key.icon = icon;
|
||||||
key.size = size;
|
key.size = size;
|
||||||
texdata = g_hash_table_lookup (cache->priv->gicon_cache, &key);
|
texdata = g_hash_table_lookup (cache->priv->gicon_cache, &key);
|
||||||
@ -630,6 +757,88 @@ shell_texture_cache_load_uri_sync (ShellTextureCache *cache,
|
|||||||
return CLUTTER_ACTOR (texture);
|
return CLUTTER_ACTOR (texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shell_texture_cache_load_thumbnail:
|
||||||
|
* @cache:
|
||||||
|
* @size: Size in pixels to use for thumbnail
|
||||||
|
* @uri: Source URI
|
||||||
|
* @mimetype: Source mime type
|
||||||
|
* @fallback: (allow-none): If no thumbnail is cached, use this pixbuf
|
||||||
|
*
|
||||||
|
* Asynchronously load a thumbnail image of a URI into a texture. The
|
||||||
|
* returned texture object will be a new instance; however, its texture data
|
||||||
|
* may be shared with other objects. This implies the texture data is cached.
|
||||||
|
*
|
||||||
|
* The current caching policy is permanent; to uncache, you must explicitly
|
||||||
|
* call shell_texture_cache_unref_thumbnail().
|
||||||
|
*
|
||||||
|
* A fallback pixbuf may be provided in the case where thumbnailing is
|
||||||
|
* not applicable, or fails.
|
||||||
|
*
|
||||||
|
* Returns: (transfer none): A new #ClutterActor
|
||||||
|
*/
|
||||||
|
ClutterActor *
|
||||||
|
shell_texture_cache_load_thumbnail (ShellTextureCache *cache,
|
||||||
|
int size,
|
||||||
|
const char *uri,
|
||||||
|
const char *mimetype,
|
||||||
|
GdkPixbuf *fallback)
|
||||||
|
{
|
||||||
|
ClutterTexture *texture;
|
||||||
|
AsyncTextureLoadData *data;
|
||||||
|
CacheKey key;
|
||||||
|
CoglHandle texdata;
|
||||||
|
|
||||||
|
texture = CLUTTER_TEXTURE (clutter_texture_new ());
|
||||||
|
clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size);
|
||||||
|
|
||||||
|
memset (&key, 0, sizeof(key));
|
||||||
|
key.size = size;
|
||||||
|
key.thumbnail_uri = (char*)uri;
|
||||||
|
|
||||||
|
texdata = g_hash_table_lookup (cache->priv->gicon_cache, &key);
|
||||||
|
if (!texdata)
|
||||||
|
{
|
||||||
|
data = g_new0 (AsyncTextureLoadData, 1);
|
||||||
|
data->uri = g_strdup (uri);
|
||||||
|
data->mimetype = g_strdup (mimetype);
|
||||||
|
data->thumbnail = TRUE;
|
||||||
|
data->width = size;
|
||||||
|
data->height = size;
|
||||||
|
data->texture = g_object_ref (texture);
|
||||||
|
load_thumbnail_async (cache, uri, mimetype, size, fallback, NULL, on_pixbuf_loaded, data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clutter_texture_set_cogl_texture (texture, texdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CLUTTER_ACTOR (texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shell_texture_cache_unref_thumbnail:
|
||||||
|
* @cache:
|
||||||
|
* @size: Size in pixels
|
||||||
|
* @uri: Source URI
|
||||||
|
*
|
||||||
|
* Removes the reference the shell_texture_cache_load_thumbnail() function
|
||||||
|
* created for a thumbnail.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
shell_texture_cache_unref_thumbnail (ShellTextureCache *cache,
|
||||||
|
int size,
|
||||||
|
const char *uri)
|
||||||
|
{
|
||||||
|
CacheKey key;
|
||||||
|
|
||||||
|
memset (&key, 0, sizeof(key));
|
||||||
|
key.size = size;
|
||||||
|
key.thumbnail_uri = (char*)uri;
|
||||||
|
|
||||||
|
g_hash_table_remove (cache->priv->gicon_cache, &key);
|
||||||
|
}
|
||||||
|
|
||||||
static ShellTextureCache *instance = NULL;
|
static ShellTextureCache *instance = NULL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#define __SHELL_TEXTURE_CACHE_H__
|
#define __SHELL_TEXTURE_CACHE_H__
|
||||||
|
|
||||||
#include <gio/gio.h>
|
#include <gio/gio.h>
|
||||||
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||||
#include <clutter/clutter.h>
|
#include <clutter/clutter.h>
|
||||||
|
|
||||||
#define SHELL_TYPE_TEXTURE_CACHE (shell_texture_cache_get_type ())
|
#define SHELL_TYPE_TEXTURE_CACHE (shell_texture_cache_get_type ())
|
||||||
@ -37,6 +38,16 @@ ClutterActor *shell_texture_cache_load_gicon (ShellTextureCache *cache,
|
|||||||
GIcon *icon,
|
GIcon *icon,
|
||||||
gint size);
|
gint size);
|
||||||
|
|
||||||
|
ClutterActor *shell_texture_cache_load_thumbnail (ShellTextureCache *cache,
|
||||||
|
int size,
|
||||||
|
const char *uri,
|
||||||
|
const char *mimetype,
|
||||||
|
GdkPixbuf *fallback);
|
||||||
|
|
||||||
|
void shell_texture_cache_unref_thumbnail (ShellTextureCache *cache,
|
||||||
|
int size,
|
||||||
|
const char *uri);
|
||||||
|
|
||||||
ClutterActor *shell_texture_cache_load_uri_async (ShellTextureCache *cache,
|
ClutterActor *shell_texture_cache_load_uri_async (ShellTextureCache *cache,
|
||||||
const gchar *filename,
|
const gchar *filename,
|
||||||
int available_width,
|
int available_width,
|
||||||
|
Loading…
Reference in New Issue
Block a user