[solang] Improved thumbnail handling



commit ee57ba299afef763283cfdde26a0f47d96298974
Author: Debarshi Ray <rishi gnu org>
Date:   Wed Dec 30 13:26:10 2009 +0200

    Improved thumbnail handling
    
    Do not keep the thumbnail (and other textual data) in
    Application::listStore_. Just keep the serial number and PhotoPtr, and
    use GtkCellLayoutDataFuncs to set the Gtk::CellRenderers in the
    ThumbnailView. New thumbnails are created using
    CellRendererThumbnail::create_thumbnail, which is invoked from
    CellRendererThumbnail::render_vfunc. This is done to reduce the number
    of thumbnails to be created to only those which will actually get
    rendered. As a result of this, BrowserRenderer::generate_thumbnails
    has been replaced with BrowserRenderer::set_thumbnail_size.
    
    The Thumbnailer is now a singleton and a new signal
    Thumbnailer::ready has been added to indicate when it has completed
    processing some photos. Application::on_thumbnailer_ready triggers
    the "row-changed" signal on those rows of Application::listStore_
    which have any of the processed photos.

 src/application/application.cpp              |   47 +++++++-
 src/application/application.h                |    3 +
 src/application/engine.cpp                   |    7 -
 src/application/engine.h                     |    6 -
 src/common/Makefile.am                       |    1 +
 src/common/singleton.h                       |   39 ++++++
 src/common/thumbnailer.cpp                   |   17 +++-
 src/common/thumbnailer.h                     |   16 ++-
 src/renderer/browser-model-column-record.cpp |   32 +-----
 src/renderer/browser-model-column-record.h   |   17 ---
 src/renderer/browser-renderer.cpp            |  168 +++-----------------------
 src/renderer/browser-renderer.h              |    8 +-
 src/renderer/cell-renderer-thumbnail.cpp     |   97 +++++++++++++++-
 src/renderer/cell-renderer-thumbnail.h       |   19 +++
 src/renderer/thumbnail-view.cpp              |   43 ++++++-
 15 files changed, 291 insertions(+), 229 deletions(-)
---
diff --git a/src/application/application.cpp b/src/application/application.cpp
index f3fc401..baf8b55 100644
--- a/src/application/application.cpp
+++ b/src/application/application.cpp
@@ -49,6 +49,7 @@
 #include "search-basket.h"
 #include "slideshow-renderer.h"
 #include "tag-manager.h"
+#include "thumbnailer.h"
 
 namespace Solang
 {
@@ -255,6 +256,11 @@ Application::Application(int & argc, char ** & argv) throw() :
         &Application::show_progress_dialog));
     engine_.photo_import_end().connect(sigc::mem_fun(*this,
         &Application::hide_progress_dialog));
+
+    Thumbnailer & thumbnailer = Thumbnailer::instance();
+    thumbnailer.signal_ready().connect(
+        sigc::mem_fun(*this,
+                      &Application::on_thumbnailer_ready));
 }
 
 Application::~Application() throw()
@@ -420,7 +426,6 @@ Application::add_photo_to_model(const PhotoPtr & photo) throw()
     row[model_column_record.get_column_serial()]
         = static_cast<guint>(model_path.front());
     row[model_column_record.get_column_photo()] = photo;
-    row[model_column_record.get_column_pixbuf()] = PixbufPtr(0);
 }
 
 void
@@ -499,6 +504,46 @@ Application::on_criteria_changed(PhotoSearchCriteriaList & criteria)
 }
 
 void
+Application::on_thumbnailer_ready(PhotoList & photos) const throw()
+{
+    const Gtk::TreeModel::Children children = listStore_->children();
+
+    // Hoping that keeping the bigger loop within the smaller loop
+    // will result in better optimization. In this case,
+    // children.size() is likely to be greater than photos.size().
+
+    for (PhotoList::const_iterator photo_iter = photos.begin();
+         photos.end() != photo_iter;
+         photo_iter++)
+    {
+        for (Gtk::TreeModel::const_iterator model_iter
+                                                = children.begin();
+             children.end() != model_iter;
+             model_iter++)
+        {
+            Gtk::TreeModel::Row row = *model_iter;
+            BrowserModelColumnRecord model_column_record;
+            const PhotoPtr photo
+                = row[model_column_record.get_column_photo()];
+
+            if (G_UNLIKELY(photo->get_uri()
+                               == (*photo_iter)->get_uri()))
+            {
+                listStore_->row_changed(listStore_->get_path(
+                                            model_iter),
+                                        model_iter);
+                break;
+            }
+        }
+
+        while (true == Gtk::Main::events_pending())
+        {
+            Gtk::Main::iteration();
+        }
+    }
+}
+
+void
 Application::show_progress_dialog() throw()
 {
     progressDialog_.set_transient_for(mainWindow_);
diff --git a/src/application/application.h b/src/application/application.h
index 1ceaec6..b5ca6b6 100644
--- a/src/application/application.h
+++ b/src/application/application.h
@@ -117,6 +117,9 @@ class Application :
                             throw();
 
         void
+        on_thumbnailer_ready(PhotoList & photos) const throw();
+
+        void
         show_progress_dialog() throw();
 
         Glib::ThreadPool threadPool_;
diff --git a/src/application/engine.cpp b/src/application/engine.cpp
index a988224..35f97c7 100644
--- a/src/application/engine.cpp
+++ b/src/application/engine.cpp
@@ -49,7 +49,6 @@ Engine::Engine(int & argc, char ** & argv,
     exportQueue_(),
     photos_(),
     currentStorageSystems_(),
-    thumbnailer_(),
     database_(),
     criterionRepo_(),
     deleteActions_( database_ )
@@ -320,10 +319,4 @@ Engine::get_photos() throw()
     return photos_;
 }
 
-Thumbnailer &
-Engine::get_thumbnailer() throw()
-{
-    return thumbnailer_;
-}
-
 } //namespace Solang
diff --git a/src/application/engine.h b/src/application/engine.h
index 99f29fc..43ba484 100644
--- a/src/application/engine.h
+++ b/src/application/engine.h
@@ -31,7 +31,6 @@
 #include "photo.h"
 #include "photo-search-criteria.h"
 #include "search-criterion-repo.h"
-#include "thumbnailer.h"
 #include "types.h"
 
 namespace Solang
@@ -175,9 +174,6 @@ class Engine :
         inline DeletionQueue &
         get_delete_actions();
 
-        Thumbnailer &
-        get_thumbnailer() throw();
-
     private:
         ProgressObserverPtr observer_;
 
@@ -209,8 +205,6 @@ class Engine :
 
         StorageMap currentStorageSystems_;
 
-        Thumbnailer thumbnailer_;
-
         Database database_;
 
         SearchCriterionRepo criterionRepo_;
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 4285dbd..8d7e75f 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -70,6 +70,7 @@ libcommon_la_SOURCES = \
 	scale-action.h \
 	scale-tool-item.cpp \
 	scale-tool-item.h \
+	singleton.h \
 	spinner.cpp \
 	spinner.h \
 	spinner-dialog.cpp \
diff --git a/src/common/singleton.h b/src/common/singleton.h
new file mode 100644
index 0000000..2ce9236
--- /dev/null
+++ b/src/common/singleton.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * Copyright (C) 2009 Debarshi Ray <rishi gnu org>
+ *
+ * Solang is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Solang 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SOLANG_SINGLETON_H
+#define SOLANG_SINGLETON_H
+
+namespace Solang
+{
+
+template<typename T>
+class Singleton
+{
+    public:
+        static T &
+        instance() throw()
+        {
+            static T t;
+            return t;
+        }
+};
+
+} // namespace Solang
+
+#endif // SOLANG_SINGLETON_H
diff --git a/src/common/thumbnailer.cpp b/src/common/thumbnailer.cpp
index b407551..1ab03cc 100644
--- a/src/common/thumbnailer.cpp
+++ b/src/common/thumbnailer.cpp
@@ -33,7 +33,8 @@ namespace Solang
 Thumbnailer::Thumbnailer() throw() :
     map_(),
     pendingList_(),
-    thumbnailerProxy_()
+    thumbnailerProxy_(),
+    ready_()
 {
     thumbnailerProxy_.error_connect(
         sigc::mem_fun(*this,
@@ -86,6 +87,7 @@ Thumbnailer::on_signal_ready(guint handle, UStringList & uris)
     }
 
     std::map<Glib::ustring, PhotoPtr> & m = map_[handle];
+    PhotoList photos;
 
     for (UStringList::const_iterator iter = uris.begin();
          uris.end() != iter;
@@ -97,8 +99,13 @@ Thumbnailer::on_signal_ready(guint handle, UStringList & uris)
         {
             continue;
         }
-        m[uri]->set_thumbnail_state(Photo::THUMBNAIL_STATE_READY);
+
+        const PhotoPtr photo = m[uri];
+        photo->set_thumbnail_state(Photo::THUMBNAIL_STATE_READY);
+        photos.push_back(photo);
     }
+
+    ready_.emit(photos);
 }
 
 void
@@ -155,4 +162,10 @@ Thumbnailer::push(const PhotoPtr & photo) throw()
     }
 }
 
+sigc::signal<void, PhotoList &> &
+Thumbnailer::signal_ready() throw()
+{
+    return ready_;
+}
+
 } // namespace Solang
diff --git a/src/common/thumbnailer.h b/src/common/thumbnailer.h
index 998ef2b..a133fbc 100644
--- a/src/common/thumbnailer.h
+++ b/src/common/thumbnailer.h
@@ -24,23 +24,31 @@
 #include <glibmm.h>
 #include <sigc++/sigc++.h>
 
+#include "singleton.h"
 #include "thumbnailer-proxy.h"
 #include "types.h"
 
 namespace Solang
 {
 
-class Thumbnailer
+class Thumbnailer :
+    public Singleton<Thumbnailer>,
+    public sigc::trackable
 {
     public:
-        Thumbnailer() throw();
-
         ~Thumbnailer() throw();
 
         void
         push(const PhotoPtr & photo) throw();
 
+        sigc::signal<void, PhotoList &> &
+        signal_ready() throw();
+
     private:
+        friend class Singleton<Thumbnailer>;
+
+        Thumbnailer() throw();
+
         void
         on_async_queue(guint handle, const PhotoList & photos)
                        throw();
@@ -68,6 +76,8 @@ class Thumbnailer
         ThumbnailerProxy thumbnailerProxy_;
 
         sigc::connection signalTimeout_;
+
+        sigc::signal<void, PhotoList &> ready_;
 };
 
 } // namespace Solang
diff --git a/src/renderer/browser-model-column-record.cpp b/src/renderer/browser-model-column-record.cpp
index b7b5ca8..afd1aeb 100644
--- a/src/renderer/browser-model-column-record.cpp
+++ b/src/renderer/browser-model-column-record.cpp
@@ -28,22 +28,16 @@ enum
 {
     COLUMN_SERIAL = 0,
     COLUMN_PHOTO,
-    COLUMN_PIXBUF,
-    COLUMN_TAG_NAME,
     COLUMN_COUNT
 };
 
 BrowserModelColumnRecord::BrowserModelColumnRecord() throw() :
     Gtk::TreeModelColumnRecord(),
     columnSerial_(),
-    columnPhoto_(),
-    columnPixbuf_(),
-    columnTagName_()
+    columnPhoto_()
 {
     add(columnSerial_);
     add(columnPhoto_);
-    add(columnPixbuf_);
-    add(columnTagName_);
 }
 
 BrowserModelColumnRecord::~BrowserModelColumnRecord() throw()
@@ -74,28 +68,4 @@ BrowserModelColumnRecord::get_column_photo_num() const throw()
     return COLUMN_PHOTO;
 }
 
-const Gtk::TreeModelColumn<PixbufPtr> &
-BrowserModelColumnRecord::get_column_pixbuf() const throw()
-{
-    return columnPixbuf_;
-}
-
-gint
-BrowserModelColumnRecord::get_column_pixbuf_num() const throw()
-{
-    return COLUMN_PIXBUF;
-}
-
-const Gtk::TreeModelColumn<Glib::ustring> &
-BrowserModelColumnRecord::get_column_tag_name() const throw()
-{
-    return columnTagName_;
-}
-
-gint
-BrowserModelColumnRecord::get_column_tag_name_num() const throw()
-{
-    return COLUMN_TAG_NAME;
-}
-
 } // namespace Solang
diff --git a/src/renderer/browser-model-column-record.h b/src/renderer/browser-model-column-record.h
index 0cd7ff2..f3311f9 100644
--- a/src/renderer/browser-model-column-record.h
+++ b/src/renderer/browser-model-column-record.h
@@ -19,7 +19,6 @@
 #ifndef SOLANG_BROWSER_MODEL_COLUMN_RECORD_H
 #define SOLANG_BROWSER_MODEL_COLUMN_RECORD_H
 
-#include <gdkmm.h>
 #include <gtkmm.h>
 
 #include "types.h"
@@ -48,27 +47,11 @@ class BrowserModelColumnRecord :
         gint
         get_column_photo_num() const throw();
 
-        const Gtk::TreeModelColumn<PixbufPtr> &
-        get_column_pixbuf() const throw();
-
-        gint
-        get_column_pixbuf_num() const throw();
-
-        const Gtk::TreeModelColumn<Glib::ustring> &
-        get_column_tag_name() const throw();
-
-        gint
-        get_column_tag_name_num() const throw();
-
     protected:
         Gtk::TreeModelColumn<guint> columnSerial_;
 
         Gtk::TreeModelColumn<PhotoPtr> columnPhoto_;
 
-        Gtk::TreeModelColumn<PixbufPtr> columnPixbuf_;
-
-        Gtk::TreeModelColumn<Glib::ustring> columnTagName_;
-
     private:
 };
 
diff --git a/src/renderer/browser-renderer.cpp b/src/renderer/browser-renderer.cpp
index 439d831..a2bf331 100644
--- a/src/renderer/browser-renderer.cpp
+++ b/src/renderer/browser-renderer.cpp
@@ -21,8 +21,6 @@
 #include "config.h"
 #endif // HAVE_CONFIG_H
 
-#include <iostream>
-#include <sstream>
 #include <vector>
 
 #include <glibmm/i18n.h>
@@ -38,8 +36,6 @@
 #include "photo.h"
 #include "photo-search-criteria.h"
 #include "scale-action.h"
-#include "thumbbuf-maker.h"
-#include "thumbnailer.h"
 
 namespace Solang
 {
@@ -88,7 +84,6 @@ BrowserRenderer::BrowserRenderer() throw() :
     paginationBar_(),
     dummyLabel_(),
     scrolledWindow_(),
-    imageLoading_(0),
     treeModelFilter_(),
     thumbnailView_(thumbnailRendererWidth, thumbnailRendererHeight),
     zoomValue_(initialZoomValue),
@@ -371,13 +366,11 @@ BrowserRenderer::clear_thumbnails() throw()
         BrowserModelColumnRecord model_column_record;
         Gtk::TreeModel::Row row = *iter;
 
-        row[model_column_record.get_column_pixbuf()] = PixbufPtr(0);
         PhotoPtr photo = row[model_column_record.get_column_photo()];
         if( !photo->get_has_unsaved_data() )
         {
             photo->set_thumbnail_buffer( PixbufPtr(0) );
         }
-        row[model_column_record.get_column_tag_name()] = "";
 
         while (true == Gtk::Main::events_pending())
         {
@@ -386,146 +379,6 @@ BrowserRenderer::clear_thumbnails() throw()
     }
 }
 
-void
-BrowserRenderer::generate_thumbnails() throw()
-{
-    std::ostringstream fsout;
-    fsout << paginationBar_.get_lower_limit();
-
-    const Gtk::TreeModel::Path first_path(fsout.str());
-
-    if (true == first_path.empty())
-    {
-        return;
-    }
-
-    const Gtk::TreeModel::iterator first_iter
-        = treeModelFilter_->get_model()->get_iter(first_path);
-
-    if (false == first_iter)
-    {
-        return;
-    }
-
-    std::ostringstream lsout;
-    lsout << paginationBar_.get_upper_limit() - 1;
-
-    const Gtk::TreeModel::Path last_path(lsout.str());
-
-    if (true == last_path.empty())
-    {
-        return;
-    }
-
-    const Gtk::TreeModel::iterator last_iter
-        = treeModelFilter_->get_model()->get_iter(last_path);
-
-    if (false == last_iter)
-    {
-        return;
-    }
-
-    const guint thumbnail_width
-                    = static_cast<guint>(
-                          ratioWidth
-                          * static_cast<double>(zoomValue_));
-    const guint thumbnail_height
-                    = static_cast<guint>(
-                          ratioHeight
-                          * static_cast<double>(zoomValue_));
-
-    thumbnailView_.set_thumbnail_width(thumbnail_width + 12);
-    thumbnailView_.set_thumbnail_height(thumbnail_height + 12);
-
-    if (0 == imageLoading_)
-    {
-        const IconThemePtr icon_theme = Gtk::IconTheme::get_default();
-        try
-        {
-            imageLoading_ = icon_theme->load_icon(
-                                "image-loading",
-                                thumbnail_height,
-                                static_cast<Gtk::IconLookupFlags>(0));
-        }
-        catch (const Glib::Error & e)
-        {
-            std::cerr << G_STRLOC << ", " << G_STRFUNC << ": "
-                      << e.what() << std::endl;
-        }
-    }
-
-    Gtk::TreeModel::iterator iter;
-
-    // operator<= is not defined.
-    for (iter = first_iter; ; iter++)
-    {
-        BrowserModelColumnRecord model_column_record;
-        Gtk::TreeModel::Row row = *iter;
-
-        const PhotoPtr & photo
-            = row[model_column_record.get_column_photo()];
-        const Photo::ThumbnailState thumbnail_state
-            = photo->get_thumbnail_state();
-
-        if (Photo::THUMBNAIL_STATE_NONE == thumbnail_state)
-        {
-            ThumbbufMaker thumbbuf_maker(thumbnail_width,
-                                         thumbnail_height,
-                                         false);
-
-            PixbufPtr pixbuf;
-            try
-            {
-                pixbuf = thumbbuf_maker(photo);
-                photo->set_thumbnail_buffer(pixbuf);
-                photo->set_thumbnail_state(
-                           Photo::THUMBNAIL_STATE_READY);
-            }
-            catch (const Gdk::PixbufError & e)
-            {
-                Engine & engine = application_->get_engine();
-                Thumbnailer & thumbnailer = engine.get_thumbnailer();
-                thumbnailer.push(photo);
-
-                pixbuf = imageLoading_;
-                photo->set_thumbnail_state(
-                           Photo::THUMBNAIL_STATE_LOADING);
-            }
-            catch (const Glib::FileError & e)
-            {
-                Engine & engine = application_->get_engine();
-                Thumbnailer & thumbnailer = engine.get_thumbnailer();
-                thumbnailer.push(photo);
-
-                pixbuf = imageLoading_;
-                photo->set_thumbnail_state(
-                           Photo::THUMBNAIL_STATE_LOADING);
-            }
-
-            row[model_column_record.get_column_pixbuf()] = pixbuf;
-
-//            row[model_column_record.get_column_tag_name()]
-//                = photo->get_exif_data().get_picture_taken_time();
-        }
-        else if (Photo::THUMBNAIL_STATE_LOADING == thumbnail_state)
-        {
-            row[model_column_record.get_column_pixbuf()]
-                = imageLoading_;
-        }
-        else if (Photo::THUMBNAIL_STATE_READY == thumbnail_state)
-        {
-            row[model_column_record.get_column_pixbuf()]
-                = photo->get_thumbnail_buffer();
-        }
-
-        // operator<= is not defined.
-        if (last_iter == iter)
-        {
-            break;
-        }
-    }
-}
-
 PhotoList
 BrowserRenderer::get_current_selection() throw()
 {
@@ -678,7 +531,7 @@ BrowserRenderer::on_item_edit() throw()
 void
 BrowserRenderer::on_limits_changed() throw()
 {
-    generate_thumbnails();
+    set_thumbnail_size();
     treeModelFilter_->refilter();
 }
 
@@ -715,7 +568,6 @@ BrowserRenderer::on_action_view_zoom_changed(
                      const ScaleActionPtr & scale_action) throw()
 {
     zoomValue_ = scale_action->get_value();
-    imageLoading_.reset();
 
     static sigc::connection connection;
 
@@ -830,9 +682,25 @@ BrowserRenderer::on_visible(
 void
 BrowserRenderer::reload() throw()
 {
+    set_thumbnail_size();
     clear_thumbnails();
-    generate_thumbnails();
     treeModelFilter_->refilter();
 }
 
+void
+BrowserRenderer::set_thumbnail_size() throw()
+{
+    const guint thumbnail_width
+                    = static_cast<guint>(
+                          ratioWidth
+                          * static_cast<double>(zoomValue_));
+    const guint thumbnail_height
+                    = static_cast<guint>(
+                          ratioHeight
+                          * static_cast<double>(zoomValue_));
+
+    thumbnailView_.set_thumbnail_width(thumbnail_width + 12);
+    thumbnailView_.set_thumbnail_height(thumbnail_height + 12);
+}
+
 } // namespace Solang
diff --git a/src/renderer/browser-renderer.h b/src/renderer/browser-renderer.h
index ad9c778..e185e3b 100644
--- a/src/renderer/browser-renderer.h
+++ b/src/renderer/browser-renderer.h
@@ -81,9 +81,6 @@ class BrowserRenderer :
         clear_thumbnails() throw();
 
         void
-        generate_thumbnails() throw();
-
-        void
         on_action_add_to_export_queue() throw();
 
         void
@@ -127,6 +124,9 @@ class BrowserRenderer :
         void
         reload() throw();
 
+        void
+        set_thumbnail_size() throw();
+
         ApplicationPtr application_;
 
         IconFactoryPtr iconFactory_;
@@ -155,8 +155,6 @@ class BrowserRenderer :
 
         Gtk::ScrolledWindow scrolledWindow_;
 
-        PixbufPtr imageLoading_;
-
         TreeModelFilterPtr treeModelFilter_;
 
         ThumbnailView thumbnailView_;
diff --git a/src/renderer/cell-renderer-thumbnail.cpp b/src/renderer/cell-renderer-thumbnail.cpp
index 77fede6..bea44df 100644
--- a/src/renderer/cell-renderer-thumbnail.cpp
+++ b/src/renderer/cell-renderer-thumbnail.cpp
@@ -20,21 +20,34 @@
 #include "config.h"
 #endif // HAVE_CONFIG_H
 
+#include <iostream>
 #include <cmath>
 
 #include <cairomm/cairomm.h>
 
 #include "cell-renderer-thumbnail.h"
+#include "photo.h"
+#include "thumbbuf-maker.h"
+#include "thumbnailer.h"
 
 namespace Solang
 {
 
 CellRendererThumbnail::CellRendererThumbnail() throw() :
     Gtk::CellRendererPixbuf(),
-    extraHeight_(0)
+    extraHeight_(0),
+    imageLoading_(0),
+    photo_()
 {
     property_xalign().set_value(0.5);
     property_yalign().set_value(0.5);
+
+    const IconThemePtr icon_theme = Gtk::IconTheme::get_default();
+    icon_theme->signal_changed().connect(
+        sigc::mem_fun(*this,
+                      &CellRendererThumbnail::on_icon_theme_changed));
+
+    load_icons();
 }
 
 CellRendererThumbnail::~CellRendererThumbnail() throw()
@@ -42,6 +55,76 @@ CellRendererThumbnail::~CellRendererThumbnail() throw()
 }
 
 void
+CellRendererThumbnail::create_thumbnail(gint thumbnail_height,
+                                        gint thumbnail_width) throw()
+{
+    if (thumbnail_height < -1 || thumbnail_width < -1)
+    {
+        return;
+    }
+
+    if (Photo::THUMBNAIL_STATE_LOADING
+            == photo_->get_thumbnail_state())
+    {
+        property_pixbuf().set_value(imageLoading_);
+        return;
+    }
+
+    ThumbbufMaker thumbbuf_maker(thumbnail_width,
+                                 thumbnail_height,
+                                 false);
+    PixbufPtr pixbuf;
+    try
+    {
+        pixbuf = thumbbuf_maker(photo_);
+        photo_->set_thumbnail_buffer(pixbuf);
+        photo_->set_thumbnail_state(Photo::THUMBNAIL_STATE_READY);
+    }
+    catch (const Gdk::PixbufError & e)
+    {
+        Thumbnailer & thumbnailer = Thumbnailer::instance();
+        thumbnailer.push(photo_);
+
+        pixbuf = imageLoading_;
+        photo_->set_thumbnail_state(Photo::THUMBNAIL_STATE_LOADING);
+    }
+    catch (const Glib::FileError & e)
+    {
+        Thumbnailer & thumbnailer = Thumbnailer::instance();
+        thumbnailer.push(photo_);
+
+        pixbuf = imageLoading_;
+        photo_->set_thumbnail_state(Photo::THUMBNAIL_STATE_LOADING);
+    }
+
+    property_pixbuf().set_value(pixbuf);
+}
+
+void
+CellRendererThumbnail::load_icons() throw()
+{
+    const IconThemePtr icon_theme = Gtk::IconTheme::get_default();
+    try
+    {
+        imageLoading_ = icon_theme->load_icon(
+                            "image-loading",
+                            96,
+                            static_cast<Gtk::IconLookupFlags>(0));
+    }
+    catch (const Glib::Error & e)
+    {
+        std::cerr << G_STRLOC << ", " << G_STRFUNC << ": "
+                  << e.what() << std::endl;
+    }
+}
+
+void
+CellRendererThumbnail::on_icon_theme_changed() throw()
+{
+    load_icons();
+}
+
+void
 CellRendererThumbnail::render_vfunc(
     const Glib::RefPtr<Gdk::Drawable> & window,
     Gtk::Widget & widget,
@@ -50,6 +133,12 @@ CellRendererThumbnail::render_vfunc(
     const Gdk::Rectangle & expose_area,
     Gtk::CellRendererState flags)
 {
+    if (0 == property_pixbuf().get_value())
+    {
+        create_thumbnail(cell_area.get_height() - 12,
+                         cell_area.get_width() - 12);
+    }
+
     const gint height = background_area.get_height() + extraHeight_;
     const gint width = background_area.get_width();
 
@@ -128,4 +217,10 @@ CellRendererThumbnail::set_extra_height(guint height) throw()
     extraHeight_ = height;
 }
 
+void
+CellRendererThumbnail::set_photo(const PhotoPtr & photo) throw()
+{
+    photo_ = photo;
+}
+
 } // namespace Solang
diff --git a/src/renderer/cell-renderer-thumbnail.h b/src/renderer/cell-renderer-thumbnail.h
index d60deb7..173568f 100644
--- a/src/renderer/cell-renderer-thumbnail.h
+++ b/src/renderer/cell-renderer-thumbnail.h
@@ -23,6 +23,8 @@
 #include <glibmm.h>
 #include <gtkmm.h>
 
+#include "types.h"
+
 namespace Solang
 {
 
@@ -38,6 +40,9 @@ class CellRendererThumbnail :
         void
         set_extra_height(guint height) throw();
 
+        void
+        set_photo(const PhotoPtr & photo) throw();
+
     protected:
         virtual void
         render_vfunc(const Glib::RefPtr<Gdk::Drawable> & window,
@@ -47,8 +52,22 @@ class CellRendererThumbnail :
                      const Gdk::Rectangle & expose_area,
                      Gtk::CellRendererState flags);
 
+        void
+        create_thumbnail(gint thumbnail_height,
+                         gint thumbnail_width) throw();
+
+        void
+        load_icons() throw();
+
+        void
+        on_icon_theme_changed() throw();
+
         guint extraHeight_;
 
+        PhotoPtr photo_;
+
+        PixbufPtr imageLoading_;
+
     private:
 };
 
diff --git a/src/renderer/thumbnail-view.cpp b/src/renderer/thumbnail-view.cpp
index 19dc038..4e80b38 100644
--- a/src/renderer/thumbnail-view.cpp
+++ b/src/renderer/thumbnail-view.cpp
@@ -34,6 +34,37 @@ static const std::string uiFile
     = PACKAGE_DATA_DIR"/"PACKAGE_TARNAME"/ui/"
           PACKAGE_TARNAME"-thumbnail-popup.ui";
 
+static void
+thumbnail_view_set_renderer_thumbnail_data(
+    GtkCellLayout * cell_layout,
+    GtkCellRenderer * cell_renderer,
+    GtkTreeModel * tree_model,
+    GtkTreeIter * tree_iter,
+    gpointer user_data)
+{
+    CellRendererThumbnail * const renderer_thumbnail
+        = dynamic_cast<CellRendererThumbnail *>(Glib::wrap(
+                                                    cell_renderer,
+                                                    false));
+    const TreeModelPtr list_store = Glib::wrap(tree_model, true);
+
+    // Can not convert a GtkTreeIter * to a Gtk::TreeModel::iterator *
+    // directly.
+    const Gtk::TreePath tree_path(gtk_tree_model_get_path(tree_model,
+                                                          tree_iter),
+                                  false);
+    const Gtk::TreeModel::const_iterator iter = list_store->get_iter(
+                                                    tree_path);
+
+    Gtk::TreeModel::Row row = *iter;
+    BrowserModelColumnRecord model_column_record;
+    const PhotoPtr photo = row[model_column_record.get_column_photo()];
+    PixbufPtr pixbuf = photo->get_thumbnail_buffer();
+
+    renderer_thumbnail->property_pixbuf().set_value(pixbuf);
+    renderer_thumbnail->set_photo(photo);
+}
+
 ThumbnailView::ThumbnailView(gint thumbnail_renderer_width,
                              gint thumbnail_renderer_height) throw() :
     Gtk::IconView(),
@@ -102,14 +133,14 @@ ThumbnailView::configure(gint thumbnail_renderer_width,
         = GTK_CELL_RENDERER(rendererText_.gobj());
 
     gtk_cell_layout_pack_start(self, renderer_thumbnail, FALSE);
-    gtk_cell_layout_set_attributes(self, renderer_thumbnail,
-        "pixbuf", BrowserModelColumnRecord().get_column_pixbuf_num(),
-        NULL);
+    gtk_cell_layout_set_cell_data_func(
+        self,
+        renderer_thumbnail,
+        thumbnail_view_set_renderer_thumbnail_data,
+        0,
+        0);
 
     gtk_cell_layout_pack_start(self, renderer_info, FALSE);
-    gtk_cell_layout_set_attributes(self, renderer_info,
-        "text", BrowserModelColumnRecord().get_column_tag_name_num(),
-        NULL);
     
     rendererThumbnail_.property_width().set_value(
                            thumbnail_renderer_width);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]