From 849ddbd3f68d8e535e48285dd34bd783e210faaf Mon Sep 17 00:00:00 2001 From: Marina Zhurakhinskaya Date: Fri, 9 Jan 2009 01:09:35 +0000 Subject: [PATCH] Display thumbnails of recent files in the overlay mode by using GnomeThumbnailFactory to get them. Fall back to the system icon for the file type provided by GtkRecentInfo when a thumbnail is not available. svn path=/trunk/; revision=143 --- configure.ac | 4 +++ js/ui/appDisplay.js | 6 +++-- js/ui/docDisplay.js | 34 +++++++++++++++++++------ js/ui/genericDisplay.js | 15 +++++------ js/ui/overlay.js | 2 +- src/Makefile.am | 2 ++ src/shell-global.c | 55 ++++++++++++++++++++++++++++++++++++++++- src/shell-global.h | 8 +++--- 8 files changed, 105 insertions(+), 21 deletions(-) diff --git a/configure.ac b/configure.ac index 76ab4bcbb..17a6a4d23 100644 --- a/configure.ac +++ b/configure.ac @@ -20,6 +20,10 @@ PKG_CHECK_MODULES(MUTTER_PLUGIN, gtk+-2.0 metacity-plugins gjs-gi-1.0) PKG_CHECK_MODULES(TIDY, clutter-0.8) PKG_CHECK_MODULES(BIG, clutter-cairo-0.8 gtk+-2.0 librsvg-2.0) PKG_CHECK_MODULES(TRAY, gtk+-2.0) +# We require libgnomeui for generating thumbnails for recent files with GnomeThumbnailFactory. +# We'll switch to using GnomeDesktopThumbnailFactory once the branch of gnome-desktop that contains +# it becomes stable. +PKG_CHECK_MODULES(LIBGNOMEUI, libgnomeui-2.0) # Sets GLIB_GENMARSHAL and GLIB_MKENUMS AM_PATH_GLIB_2_0() diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index e3345da76..02c59f169 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -56,11 +56,11 @@ AppDisplayItem.prototype = { let iconTheme = Gtk.IconTheme.get_default(); - let icon = new Clutter.Texture({ width: 48, height: 48}); + let icon = new Clutter.Texture({ width: GenericDisplay.ITEM_DISPLAY_ICON_SIZE, height: GenericDisplay.ITEM_DISPLAY_ICON_SIZE}); let gicon = appInfo.get_icon(); let path = null; if (gicon != null) { - let iconinfo = iconTheme.lookup_by_gicon(gicon, 48, Gtk.IconLookupFlags.NO_SVG); + let iconinfo = iconTheme.lookup_by_gicon(gicon, GenericDisplay.ITEM_DISPLAY_ICON_SIZE, Gtk.IconLookupFlags.NO_SVG); if (iconinfo) path = iconinfo.get_filename(); } @@ -68,6 +68,8 @@ AppDisplayItem.prototype = { if (path) { try { icon.set_from_file(path); + icon.x = GenericDisplay.ITEM_DISPLAY_PADDING; + icon.y = GenericDisplay.ITEM_DISPLAY_PADDING; } catch (e) { // we can get an error here if the file path doesn't exist on the system log('Error loading AppDisplayItem icon ' + e); diff --git a/js/ui/docDisplay.js b/js/ui/docDisplay.js index e115276ca..badce3010 100644 --- a/js/ui/docDisplay.js +++ b/js/ui/docDisplay.js @@ -9,6 +9,8 @@ const Shell = imports.gi.Shell; const GenericDisplay = imports.ui.genericDisplay; +const ITEM_DISPLAY_ICON_MARGIN = 2; + /* This class represents a single display item containing information about a document. * * docInfo - GtkRecentInfo object containing information about the document @@ -30,9 +32,24 @@ DocDisplayItem.prototype = { // we can possibly display tags in the space for description in the future let description = ""; - let icon = new Clutter.Texture({width: 48, height: 48}); - Shell.clutter_texture_set_from_pixbuf(icon, docInfo.get_icon(48)); - + let icon = new Clutter.Texture(); + let pixbuf = Shell.get_thumbnail_for_recent_info(docInfo); + if (pixbuf) { + // 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 use extra margin when positioning it. + let scalingFactor = (GenericDisplay.ITEM_DISPLAY_ICON_SIZE - ITEM_DISPLAY_ICON_MARGIN * 2) / Math.max(pixbuf.get_width(), pixbuf.get_height()); + icon.set_width(Math.ceil(pixbuf.get_width() * scalingFactor)); + icon.set_height(Math.ceil(pixbuf.get_height() * scalingFactor)); + Shell.clutter_texture_set_from_pixbuf(icon, pixbuf); + icon.x = GenericDisplay.ITEM_DISPLAY_PADDING + ITEM_DISPLAY_ICON_MARGIN; + icon.y = GenericDisplay.ITEM_DISPLAY_PADDING + ITEM_DISPLAY_ICON_MARGIN; + } else { + Shell.clutter_texture_set_from_pixbuf(icon, docInfo.get_icon(GenericDisplay.ITEM_DISPLAY_ICON_SIZE)); + icon.x = GenericDisplay.ITEM_DISPLAY_PADDING; + icon.y = GenericDisplay.ITEM_DISPLAY_PADDING; + } + this._setItemInfo(name, description, icon); }, @@ -124,10 +141,13 @@ DocDisplay.prototype = { // While this._allItems associative array seems to always be ordered by last added, // as the results of this._recentManager.get_items() based on which it is constructed are, // we should do the sorting manually because we want the order to be based on last visited. - // TODO: would it be better to store an additional array of doc ids as they are - // returned by this._recentManager.get_items() to avoid having to do this sorting? - // This function is called each time the search string is set back to '', so we are - // doing the sorting over the same items multiple times. + // + // This function is called each time the search string is set back to '' or we display + // the overlay, so we are doing the sorting over the same items multiple times if the list + // of recent items didn't change. We could store an additional array of doc ids and sort + // them once when they are returned by this._recentManager.get_items() to avoid having to do + // this sorting each time, but the sorting seems to be very fast anyway, so there is no need + // to introduce an additional class variable. let docIds = []; for (docId in this._allItems) { docIds.push(docId); diff --git a/js/ui/genericDisplay.js b/js/ui/genericDisplay.js index 82dd209f6..d9713adc1 100644 --- a/js/ui/genericDisplay.js +++ b/js/ui/genericDisplay.js @@ -20,7 +20,9 @@ const ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR = new Clutter.Color(); ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR.from_pixel(0x00ff0055); const ITEM_DISPLAY_HEIGHT = 50; -const ITEM_DISPLAY_PADDING = 4; +const ITEM_DISPLAY_ICON_SIZE = 48; +const ITEM_DISPLAY_PADDING = 1; +const ITEM_DISPLAY_MARGIN = 4; /* This is a virtual class that represents a single display item containing * a name, a description, and an icon. It allows selecting an item and represents @@ -106,18 +108,16 @@ GenericDisplayItem.prototype = { } this._icon = iconActor; - this._icon.x = 0; - this._icon.y = 0; this._group.add_actor(this._icon); - let text_width = this._availableWidth - (this._icon.width + 4); + let text_width = this._availableWidth - (ITEM_DISPLAY_ICON_SIZE + 4); this._name = new Clutter.Label({ color: ITEM_DISPLAY_NAME_COLOR, font_name: "Sans 14px", width: text_width, ellipsize: Pango.EllipsizeMode.END, text: nameText, - x: this._icon.width + 4, - y: 0}); + x: ITEM_DISPLAY_ICON_SIZE + 4, + y: ITEM_DISPLAY_PADDING}); this._group.add_actor(this._name); this._description = new Clutter.Label({ color: ITEM_DISPLAY_DESCRIPTION_COLOR, font_name: "Sans 12px", @@ -157,7 +157,8 @@ GenericDisplay.prototype = { this._activatedItem = null; this._selectedIndex = -1; this._keepDisplayCurrent = false; - this._maxItems = this._height / (ITEM_DISPLAY_HEIGHT + ITEM_DISPLAY_PADDING); + // TODO: this should be Math.floor, but right now we get too few items if we apply it + this._maxItems = this._height / (ITEM_DISPLAY_HEIGHT + ITEM_DISPLAY_MARGIN); this.actor = this._grid; }, diff --git a/js/ui/overlay.js b/js/ui/overlay.js index a1699e875..2dc0139b5 100644 --- a/js/ui/overlay.js +++ b/js/ui/overlay.js @@ -19,7 +19,7 @@ const OVERLAY_BACKGROUND_COLOR = new Clutter.Color(); OVERLAY_BACKGROUND_COLOR.from_pixel(0x000000ff); const SIDESHOW_PAD = 6; -const SIDESHOW_PAD_BOTTOM = 60; +const SIDESHOW_PAD_BOTTOM = 40; const SIDESHOW_MIN_WIDTH = 250; const SIDESHOW_SECTION_PAD = 10; const SIDESHOW_SECTION_LABEL_PAD_BOTTOM = 6; diff --git a/src/Makefile.am b/src/Makefile.am index ebb7fa138..c3269b233 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,6 +10,7 @@ include Makefile-tray.am gnome_shell_cflags = \ $(MUTTER_PLUGIN_CFLAGS) \ + $(LIBGNOMEUI_CFLAGS) \ -Itray \ -DGETTEXT_PACKAGE=gnome-shell \ -DGNOME_SHELL_DATADIR=\"$(pkgdatadir)\" \ @@ -65,6 +66,7 @@ shell-marshal.c: Makefile shell-marshal.list libgnome_shell_la_LDFLAGS = -avoid-version -module libgnome_shell_la_LIBADD = \ $(MUTTER_PLUGIN_LIBS) \ + $(LIBGNOMEUI_LIBS) \ libbig-1.0.la \ libtidy-1.0.la \ libtray.la diff --git a/src/shell-global.c b/src/shell-global.c index d7281285a..ce1094452 100644 --- a/src/shell-global.c +++ b/src/shell-global.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include struct _ShellGlobal { GObject parent; @@ -240,6 +242,57 @@ shell_clutter_texture_set_from_pixbuf (ClutterTexture *texture, 0, NULL); } +static GnomeThumbnailFactory *thumbnail_factory; + +/** + * shell_get_thumbnail_for_recent_info: + * + * @recent_info: #GtkRecentInfo for which to return a thumbnail + * + * Return value: #GdkPixbuf containing a thumbnail for the file described by #GtkRecentInfo + * if the thumbnail exists or can be generated, %NULL otherwise + */ +GdkPixbuf * +shell_get_thumbnail_for_recent_info(GtkRecentInfo *recent_info) +{ + char *existing_thumbnail; + GdkPixbuf *pixbuf = NULL; + const gchar *uri = gtk_recent_info_get_uri (recent_info); + time_t mtime = gtk_recent_info_get_modified (recent_info); + const gchar *mime_type = gtk_recent_info_get_mime_type (recent_info); + GError *error = NULL; + + 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_can_thumbnail (thumbnail_factory, uri, mime_type, mtime)) + { + pixbuf = gnome_thumbnail_factory_generate_thumbnail (thumbnail_factory, uri, mime_type); + if (pixbuf == NULL) + { + g_warning ("Could not generate thumbnail for %s", uri); + } + else + { + // 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); + } + } + + return pixbuf; +} + /** * shell_global_get: * @@ -402,4 +455,4 @@ shell_global_reexec_self (ShellGlobal *global) execvp (arr->pdata[0], (char**)arr->pdata); g_warning ("failed to reexec: %s", g_strerror (errno)); g_ptr_array_free (arr, TRUE); -} \ No newline at end of file +} diff --git a/src/shell-global.h b/src/shell-global.h index 8888cebec..06afd79fe 100644 --- a/src/shell-global.h +++ b/src/shell-global.h @@ -5,6 +5,7 @@ #include #include #include +#include G_BEGIN_DECLS @@ -30,9 +31,10 @@ struct _ShellGlobalClass GType shell_global_get_type (void) G_GNUC_CONST; -gboolean -shell_clutter_texture_set_from_pixbuf (ClutterTexture *texture, - GdkPixbuf *pixbuf); +gboolean shell_clutter_texture_set_from_pixbuf (ClutterTexture *texture, + GdkPixbuf *pixbuf); + +GdkPixbuf *shell_get_thumbnail_for_recent_info(GtkRecentInfo *recent_info); ShellGlobal *shell_global_get (void);