[solang] Overhauled the ProgressObserver and its friends



commit f4257a077c2fd0ad2d790e443e252c1cd2df7f99
Author: Debarshi Ray <rishi gnu org>
Date:   Thu Mar 4 13:39:58 2010 +0200

    Overhauled the ProgressObserver and its friends
    
    The previous ProgressObserver infrastructure was unable to deal with
    two or more simultaneous operations. Moreoever it solely relied on
    Glib::Dispatcher which required the observed operation to be done in a
    separate thread.
    
    A ProgressDialog has been to the Application, and whenever a
    ProgressObserver is created it is associated with this dialog. The
    observer is a Gio::Cancellable, and a template that expects either a
    Glib::Dispatcher or a sigc::slot as the parameter. When the operation
    begins, the observer attaches a ProgressWidget to the ProgressDialog
    and detaches it when cancelled or destructed. The ProgressDialog and
    ProgressWidget were translated to C++ from C code written for
    Nautilus.
    
    A IProgressObserver was added as an interface to the actual templates
    for use by user code:
        + IProgressObsever::is_finished
        + IProgressObsever::progress
        + IProgressObsever::set_description
        + IProgressObsever::set_fraction
        + IProgressObsever::set_total
    
    Construction and destruction of a IProgressObserver, and
    IProgressObserver::set_description are only meant to work from the
    main thread they involve interactions with the GUI.
    
    IPhotoDestination::export_photos has been renamed to
    IPhotoDestination::export_photos_async and it is run in the main
    thread. It is unknown whether the Brasero libraries are thread-safe or
    not, and there was really no pressing need for using a separate
    thread.
    
    The IPhotoDestination::export_photo has been removed as there was no
    real need for it and it was becoming too different for the Brasero and
    directory destinations.
    
    EditablePhoto::pending_ now contains triplets instead of pairs -- a
    ProgressObserverPtr being the new addition. If an ongoing operation is
    cancelled (or the initial Gegl::Buffer could not be created) the
    entire queue is cleared and a new GdkPixbuf is not created.
    
    A Finally was added to mimic Java's finally clause. This is useful for
    invoking of g_object_unref when moving out of the local scope, or
    emitting Glib::Dispatchers to indicate the end of a thread worker.
    
    Regressions:
    
    Removal of the temporary directory used for creating an archive when
    exporting to a directory is now done synchronously in the main thread.

 po/POTFILES.in                         |    1 +
 po/POTFILES.skip                       |    1 +
 src/application/application.cpp        |   37 +----
 src/application/application.h          |   11 +-
 src/application/deletion-queue.cpp     |    8 +-
 src/application/engine.cpp             |  265 ++++++++++++++------------------
 src/application/engine.h               |   99 +++++--------
 src/application/main-window.cpp        |   43 -----
 src/application/main-window.h          |   15 --
 src/common/Makefile.am                 |    7 +-
 src/common/database.cpp                |   44 +-----
 src/common/database.h                  |   13 +-
 src/common/finally.cpp                 |   42 +++++
 src/common/finally.h                   |   44 ++++++
 src/common/i-operation.h               |    3 +-
 src/common/i-photo-destination.h       |    8 +-
 src/common/i-progress-observer.cpp     |   37 +++++
 src/common/i-progress-observer.h       |   59 +++++++
 src/common/non-copyable.cpp            |    2 +-
 src/common/non-copyable.h              |    2 +-
 src/common/operation.cpp               |   32 ++--
 src/common/operation.h                 |    3 +-
 src/common/progress-dialog.cpp         |  122 ++++-----------
 src/common/progress-dialog.h           |   43 ++----
 src/common/progress-observer.cpp       |  126 ---------------
 src/common/progress-observer.h         |  229 +++++++++++++++++++--------
 src/common/progress-widget.cpp         |   99 ++++++++++++
 src/common/progress-widget.h           |   74 +++++++++
 src/common/types.h                     |   15 ++-
 src/editor/editable-photo.cpp          |   60 ++++++--
 src/editor/editable-photo.h            |   24 ++--
 src/editor/editor.cpp                  |   14 +-
 src/exporter/brasero-destination.cpp   |   61 ++------
 src/exporter/brasero-destination.h     |   19 +--
 src/exporter/directory-destination.cpp |  145 +++++++++++++-----
 src/exporter/directory-destination.h   |   26 +++-
 src/exporter/exporter.cpp              |   14 +-
 37 files changed, 1015 insertions(+), 832 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index a859c3b..0711334 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -12,6 +12,7 @@ src/attribute/tag-new-dialog.cpp
 src/common/exif-data.cpp
 src/common/histogram-viewer.cpp
 src/common/progress-dialog.cpp
+src/common/progress-observer.h
 src/editor/editor.cpp
 src/editor/flip-horiz-operation.cpp
 src/editor/flip-operation.cpp
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index f57a79c..b868a38 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -1,6 +1,7 @@
 data/edit-toolbar.ui
 data/save-modified-photos.ui
 data/solang.desktop.in
+src/common/progress-widget.cpp
 src/common/spinner-dialog.cpp
 src/importer/camera-import-widget.cpp
 src/importer/camera-source.cpp
diff --git a/src/application/application.cpp b/src/application/application.cpp
index 734cb5f..f2ee23b 100644
--- a/src/application/application.cpp
+++ b/src/application/application.cpp
@@ -44,7 +44,6 @@
 #include "i-plugin.h"
 //#include "importer.h"
 #include "photo.h"
-#include "progress-observer.h"
 #include "property-manager.h"
 #include "search-manager.h"
 #include "slideshow-renderer.h"
@@ -178,10 +177,9 @@ Application::Application(int & argc, char ** & argv) throw() :
     sigc::trackable(),
     threadPool_(4, false),
     iconFactory_(Gtk::IconFactory::create()),
-    observer_( new ProgressObserver() ),
-    engine_(argc, argv, observer_),
+    engine_(argc, argv),
     mainWindow_(),
-    progressDialog_(engine_.get_default_observer()),
+    progressDialog_(mainWindow_),
     listStore_(Gtk::ListStore::create(BrowserModelColumnRecord())),
     listStoreIter_(),
     plugins_(),
@@ -246,14 +244,6 @@ Application::Application(int & argc, char ** & argv) throw() :
     engine_.signal_criteria_changed().connect(
         sigc::mem_fun(*this,
                       &Application::on_criteria_changed));
-    engine_.photo_export_begin().connect(sigc::mem_fun(*this,
-        &Application::show_progress_dialog));
-    engine_.photo_export_end().connect(sigc::mem_fun(*this,
-        &Application::hide_progress_dialog));
-    engine_.photo_import_begin().connect(sigc::mem_fun(*this,
-        &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(
@@ -343,8 +333,6 @@ Application::init() throw()
 
     mainWindow_.init(*this);
 
-    mainWindow_.connect_progress( engine_.get_default_observer() );
-
     initEnd_.emit(*this);
 }
 
@@ -462,13 +450,6 @@ Application::init_end() throw()
     return initEnd_;
 }
 
-void
-Application::hide_progress_dialog() throw()
-{
-    progressDialog_.hide();
-    engine_.get_default_observer()->reset();
-}
-
 sigc::signal<void, Application &> &
 Application::list_store_change_begin() throw()
 {
@@ -539,14 +520,6 @@ Application::on_thumbnailer_ready(PhotoList & photos) const throw()
     }
 }
 
-void
-Application::show_progress_dialog() throw()
-{
-    progressDialog_.set_transient_for(mainWindow_);
-    progressDialog_.show_all();
-}
-
-
 Glib::ThreadPool &
 Application::get_thread_pool() throw()
 {
@@ -565,6 +538,12 @@ Application::get_main_window() throw()
     return mainWindow_;
 }
 
+ProgressDialog &
+Application::get_progress_dialog() throw()
+{
+    return progressDialog_;
+}
+
 const ListStorePtr &
 Application::get_list_store() throw()
 {
diff --git a/src/application/application.h b/src/application/application.h
index 38d1a06..dcd60be 100644
--- a/src/application/application.h
+++ b/src/application/application.h
@@ -71,6 +71,9 @@ class Application :
         MainWindow &
         get_main_window() throw();
 
+        ProgressDialog &
+        get_progress_dialog() throw();
+
         const ListStorePtr &
         get_list_store() throw();
 
@@ -107,9 +110,6 @@ class Application :
         add_photos_to_model(const PhotoList & photos) throw();
 
         void
-        hide_progress_dialog() throw();
-
-        void
         on_async_search(PhotoList & photos) throw();
 
         void
@@ -119,15 +119,10 @@ class Application :
         void
         on_thumbnailer_ready(PhotoList & photos) const throw();
 
-        void
-        show_progress_dialog() throw();
-
         Glib::ThreadPool threadPool_;
 
         IconFactoryPtr iconFactory_;
 
-        ProgressObserverPtr observer_;
-
         Engine engine_;
 
         MainWindow mainWindow_;
diff --git a/src/application/deletion-queue.cpp b/src/application/deletion-queue.cpp
index 7257bb5..48bea84 100644
--- a/src/application/deletion-queue.cpp
+++ b/src/application/deletion-queue.cpp
@@ -21,7 +21,7 @@
 #endif
 
 #include "deletion-queue.h"
-#include "progress-observer.h"
+#include "i-progress-observer.h"
 
 namespace Solang
 {
@@ -91,8 +91,8 @@ DeletionQueue::execute_actions(
             throw;
         }
     }
-    observer->set_event_description( "Executing delete actions" );
-    observer->set_num_events( actions_.size() );
+    observer->set_description( "Executing delete actions" );
+    observer->set_total( actions_.size() );
     for( DeleteActionList::iterator action = actions_.begin();
                                 action != actions_.end(); action++ )
     {
@@ -106,7 +106,7 @@ DeletionQueue::execute_actions(
             observer->reset();
             throw;
         }
-        observer->receive_event_notifiation();
+        observer->progress();
     }
 }
 
diff --git a/src/application/engine.cpp b/src/application/engine.cpp
index b68e7b7..441b8de 100644
--- a/src/application/engine.cpp
+++ b/src/application/engine.cpp
@@ -28,15 +28,12 @@
 #include "i-photo-destination.h"
 #include "i-photo-source.h"
 #include "photo-tag.h"
-#include "progress-observer.h"
 #include "tag.h"
 
 namespace Solang
 {
 
-Engine::Engine(int & argc, char ** & argv,
-               const ProgressObserverPtr & observer) throw() :
-    observer_(observer),
+Engine::Engine(int & argc, char ** & argv) throw() :
     photoExportBegin_(),
     photoExportEnd_(),
     photoImportBegin_(),
@@ -67,7 +64,7 @@ Engine::init(Glib::ustring str)
 void
 Engine::final()
 {
-    deleteActions_.execute_actions( observer_ );
+    deleteActions_.execute_actions();
 }
 
 void
@@ -79,116 +76,116 @@ Engine::criteria_changed() throw()
     criteriaChanged_.emit(criteria);
 }
 
-void
-Engine::import(const PhotoPtr & photo,
-               const IPhotoSourcePtr & source,
-               const IStoragePtr & selected_storage,
-               const TagList & tags) throw()
-{
-    import(photo, source, selected_storage, tags, observer_);
-    return;
-}
-
-void
-Engine::import(const PhotoPtr & photo,
-               const IPhotoSourcePtr & source,
-               const IStoragePtr & selected_storage,
-               const TagList & tags,
-               const ProgressObserverPtr & observer) throw()
-{
-    photoImportBegin_.emit();
-#if 0
-    for (TagList::const_iterator it = tags.begin();
-         it != tags.end(); it++)
-    {
-        (*it)->save(database_);
-    }
-#endif
-    PhotoPtr imp_photo = source->import(photo, selected_storage,
-                                    tags, database_, observer);
-    photoImportEnd_.emit();
-
-    PhotoList imp_photos;
-    imp_photos.push_back(imp_photo);
-    {
-        Glib::Mutex::Lock lock(mutex_);
-        photos_ = imp_photos;
-    }
-
-    return;
-}
-
-void
-Engine::import(const PhotoList & photos,
-               const IPhotoSourcePtr & source,
-               const IStoragePtr & selected_storage,
-               const TagList & tags) throw()
-{
-    import(photos, source, selected_storage, tags, observer_);
-    return;
-}
-
-void
-Engine::import(const PhotoList & photos,
-               const IPhotoSourcePtr & source,
-               const IStoragePtr & selected_storage,
-               const TagList & tags,
-               const ProgressObserverPtr & observer) throw()
-{
-    photoImportBegin_.emit();
-#if 0
-    for (TagList::const_iterator it = tags.begin();
-         it != tags.end(); it++)
-    {
-        (*it)->save(database_);
-    }
-#endif
-    PhotoList imp_photos = source->import(photos, selected_storage,
-                                          tags, database_, observer);
-    photoImportEnd_.emit();
-
-    {
-        Glib::Mutex::Lock lock(mutex_);
-        photos_ = imp_photos;
-    }
-
-    return;
-}
-
-void
-Engine::import(const IPhotoSourcePtr & source,
-               const IStoragePtr & selected_storage,
-               const TagList & tags) throw()
-{
-    import(source, selected_storage, tags, observer_);
-    return;
-}
-
-void
-Engine::import(const IPhotoSourcePtr & source,
-               const IStoragePtr & selected_storage,
-               const TagList & tags,
-               const ProgressObserverPtr & observer) throw()
-{
-    photoImportBegin_.emit();
-#if 0
-    for (TagList::const_iterator it = tags.begin();
-         it != tags.end(); it++)
-    {
-        (*it)->save(database_);
-    }
-#endif
-    PhotoList imp_photos = source->import(selected_storage, tags,
-                                          database_, observer);
-    photoImportEnd_.emit();
-
-    {
-        Glib::Mutex::Lock lock(mutex_);
-        photos_ = imp_photos;
-    }
-
-    return;
-}
+/* void */
+/* Engine::import(const PhotoPtr & photo, */
+/*                const IPhotoSourcePtr & source, */
+/*                const IStoragePtr & selected_storage, */
+/*                const TagList & tags) throw() */
+/* { */
+/*     import(photo, source, selected_storage, tags, observer_); */
+/*     return; */
+/* } */
+
+/* void */
+/* Engine::import(const PhotoPtr & photo, */
+/*                const IPhotoSourcePtr & source, */
+/*                const IStoragePtr & selected_storage, */
+/*                const TagList & tags, */
+/*                const ProgressObserverPtr & observer) throw() */
+/* { */
+/*     photoImportBegin_.emit(); */
+/* #if 0 */
+/*     for (TagList::const_iterator it = tags.begin(); */
+/*          it != tags.end(); it++) */
+/*     { */
+/*         (*it)->save(database_); */
+/*     } */
+/* #endif */
+/*     PhotoPtr imp_photo = source->import(photo, selected_storage, */
+/*                                     tags, database_, observer); */
+/*     photoImportEnd_.emit(); */
+
+/*     PhotoList imp_photos; */
+/*     imp_photos.push_back(imp_photo); */
+/*     { */
+/*         Glib::Mutex::Lock lock(mutex_); */
+/*         photos_ = imp_photos; */
+/*     } */
+
+/*     return; */
+/* } */
+
+/* void */
+/* Engine::import(const PhotoList & photos, */
+/*                const IPhotoSourcePtr & source, */
+/*                const IStoragePtr & selected_storage, */
+/*                const TagList & tags) throw() */
+/* { */
+/*     import(photos, source, selected_storage, tags, observer_); */
+/*     return; */
+/* } */
+
+/* void */
+/* Engine::import(const PhotoList & photos, */
+/*                const IPhotoSourcePtr & source, */
+/*                const IStoragePtr & selected_storage, */
+/*                const TagList & tags, */
+/*                const ProgressObserverPtr & observer) throw() */
+/* { */
+/*     photoImportBegin_.emit(); */
+/* #if 0 */
+/*     for (TagList::const_iterator it = tags.begin(); */
+/*          it != tags.end(); it++) */
+/*     { */
+/*         (*it)->save(database_); */
+/*     } */
+/* #endif */
+/*     PhotoList imp_photos = source->import(photos, selected_storage, */
+/*                                           tags, database_, observer); */
+/*     photoImportEnd_.emit(); */
+
+/*     { */
+/*         Glib::Mutex::Lock lock(mutex_); */
+/*         photos_ = imp_photos; */
+/*     } */
+
+/*     return; */
+/* } */
+
+/* void */
+/* Engine::import(const IPhotoSourcePtr & source, */
+/*                const IStoragePtr & selected_storage, */
+/*                const TagList & tags) throw() */
+/* { */
+/*     import(source, selected_storage, tags, observer_); */
+/*     return; */
+/* } */
+
+/* void */
+/* Engine::import(const IPhotoSourcePtr & source, */
+/*                const IStoragePtr & selected_storage, */
+/*                const TagList & tags, */
+/*                const ProgressObserverPtr & observer) throw() */
+/* { */
+/*     photoImportBegin_.emit(); */
+/* #if 0 */
+/*     for (TagList::const_iterator it = tags.begin(); */
+/*          it != tags.end(); it++) */
+/*     { */
+/*         (*it)->save(database_); */
+/*     } */
+/* #endif */
+/*     PhotoList imp_photos = source->import(selected_storage, tags, */
+/*                                           database_, observer); */
+/*     photoImportEnd_.emit(); */
+
+/*     { */
+/*         Glib::Mutex::Lock lock(mutex_); */
+/*         photos_ = imp_photos; */
+/*     } */
+
+/*     return; */
+/* } */
 
 void
 Engine::search_async(const IPhotoSearchCriteriaList & criteria,
@@ -199,37 +196,8 @@ Engine::search_async(const IPhotoSearchCriteriaList & criteria,
 }
 
 void
-Engine::export_photos(const IPhotoDestinationPtr & destination)
-                      throw()
+Engine::erase(const PhotoList & photos)
 {
-    export_photos(destination, observer_);
-    return;
-}
-
-void
-Engine::export_photos(const IPhotoDestinationPtr & destination,
-                      const ProgressObserverPtr & observer) throw()
-{
-    photoExportBegin_.emit();
-    destination->export_photos(exportQueue_, observer);
-    photoExportEnd_.emit();
-
-    return;
-}
-
-
-void
-Engine::erase(const PhotoList & photos,
-              const ProgressObserverPtr & observer)
-{
-    ProgressObserverPtr obs = (observer) ? observer : observer_;
-
-    if (obs)
-    {
-        obs->set_num_events(photos.size());
-        obs->set_event_description("Removing Selected Photos");
-    }
-
     return;
 }
 
@@ -243,20 +211,19 @@ Engine::get_tags_async(const Database::SlotAsyncTags & slot) const
 DatePhotoInfoList
 Engine::get_dates_with_picture_count()
 {
-    return database_.get_dates_with_picture_count( observer_ );
+    return database_.get_dates_with_picture_count( );
 }
 
 DatePhotoInfoList
 Engine::get_dates_with_picture_count( gint year )
 {
-    return database_.get_dates_with_picture_count(year, observer_ );
+    return database_.get_dates_with_picture_count(year);
 }
 
 DatePhotoInfoList
 Engine::get_dates_with_picture_count( gint year, gint month )
 {
-    return database_.get_dates_with_picture_count(year, month,
-                                                  observer_ );
+    return database_.get_dates_with_picture_count(year, month);
 }
 
 Glib::Dispatcher &
diff --git a/src/application/engine.h b/src/application/engine.h
index de35778..69b4f15 100644
--- a/src/application/engine.h
+++ b/src/application/engine.h
@@ -43,9 +43,7 @@ class Engine :
         typedef std::map<Glib::ustring, IStoragePtr> StorageMap;
 
     public:
-        Engine(int & argc, char ** & argv,
-               const ProgressObserverPtr & observer
-                   = ProgressObserverPtr()) throw();
+        Engine(int & argc, char ** & argv) throw();
 
         ~Engine() throw();
 
@@ -58,42 +56,42 @@ class Engine :
         void
         criteria_changed() throw();
 
-        void
-        import(const PhotoPtr & photo,
-               const IPhotoSourcePtr & source,
-               const IStoragePtr & selected_storage,
-               const TagList & tags) throw();
-
-        void
-        import(const PhotoPtr & photo,
-               const IPhotoSourcePtr & source,
-               const IStoragePtr & selected_storage,
-               const TagList & tags,
-               const ProgressObserverPtr & observer) throw();
-
-        void
-        import(const PhotoList & photos,
-               const IPhotoSourcePtr & source, 
-               const IStoragePtr & selected_storage,
-               const TagList & tags) throw();
-
-        void
-        import(const PhotoList & photos,
-               const IPhotoSourcePtr & source, 
-               const IStoragePtr & selected_storage,
-               const TagList & tags,
-               const ProgressObserverPtr & observer) throw();
-
-        void
-        import(const IPhotoSourcePtr & source,
-               const IStoragePtr & selected_storage,
-               const TagList & tags) throw();
-
-        void
-        import(const IPhotoSourcePtr & source,
-               const IStoragePtr & selected_storage,
-               const TagList & tags,
-               const ProgressObserverPtr & observer) throw();
+        /* void */
+        /* import(const PhotoPtr & photo, */
+        /*        const IPhotoSourcePtr & source, */
+        /*        const IStoragePtr & selected_storage, */
+        /*        const TagList & tags) throw(); */
+
+        /* void */
+        /* import(const PhotoPtr & photo, */
+        /*        const IPhotoSourcePtr & source, */
+        /*        const IStoragePtr & selected_storage, */
+        /*        const TagList & tags, */
+        /*        const ProgressObserverPtr & observer) throw(); */
+
+        /* void */
+        /* import(const PhotoList & photos, */
+        /*        const IPhotoSourcePtr & source,  */
+        /*        const IStoragePtr & selected_storage, */
+        /*        const TagList & tags) throw(); */
+
+        /* void */
+        /* import(const PhotoList & photos, */
+        /*        const IPhotoSourcePtr & source,  */
+        /*        const IStoragePtr & selected_storage, */
+        /*        const TagList & tags, */
+        /*        const ProgressObserverPtr & observer) throw(); */
+
+        /* void */
+        /* import(const IPhotoSourcePtr & source, */
+        /*        const IStoragePtr & selected_storage, */
+        /*        const TagList & tags) throw(); */
+
+        /* void */
+        /* import(const IPhotoSourcePtr & source, */
+        /*        const IStoragePtr & selected_storage, */
+        /*        const TagList & tags, */
+        /*        const ProgressObserverPtr & observer) throw(); */
 
         void
         search_async(const IPhotoSearchCriteriaList & criteria,
@@ -101,17 +99,7 @@ class Engine :
                      throw();
 
         void
-        export_photos(const IPhotoDestinationPtr & destination)
-                      throw();
-
-        void
-        export_photos(const IPhotoDestinationPtr & destination,
-                      const ProgressObserverPtr & observer) throw();
-
-        void
-        erase(const PhotoList & photos,
-              const ProgressObserverPtr & observer 
-                  = ProgressObserverPtr());
+        erase(const PhotoList & photos);
 
         void
         get_tags_async(const Database::SlotAsyncTags & slot) const
@@ -165,9 +153,6 @@ class Engine :
         PhotoList
         get_photos() throw();
 
-        inline const ProgressObserverPtr &
-        get_default_observer();
-
         inline SearchCriterionRepo &
         get_criterion_repo();
 
@@ -175,8 +160,6 @@ class Engine :
         get_delete_actions();
 
     private:
-        ProgressObserverPtr observer_;
-
         Glib::Dispatcher photoExportBegin_;
 
         Glib::Dispatcher photoExportEnd_;
@@ -212,12 +195,6 @@ class Engine :
         DeletionQueue deleteActions_;
 };
 
-inline const ProgressObserverPtr &
-Engine::get_default_observer()
-{
-    return observer_;
-}
-
 inline SearchCriterionRepo &
 Engine::get_criterion_repo()
 {
diff --git a/src/application/main-window.cpp b/src/application/main-window.cpp
index b177b96..9402270 100644
--- a/src/application/main-window.cpp
+++ b/src/application/main-window.cpp
@@ -29,7 +29,6 @@
 
 #include "export-queue-operations.h"
 #include "main-window.h"
-#include "progress-observer.h"
 
 namespace Solang
 {
@@ -208,8 +207,6 @@ MainWindow::MainWindow() throw() :
     spinnerToolItem_(),
     hBox_(false, 6),
     statusBar_(),
-    progress_(),
-    observer_(),
     dock_(gdl_dock_new()),
     dockBar_(gdl_dock_bar_new(GDL_DOCK(dock_))),
     layout_(gdl_dock_layout_new(GDL_DOCK(dock_))),
@@ -325,7 +322,6 @@ MainWindow::MainWindow() throw() :
 
     statusBar_.set_has_resize_grip(true);
     vBox_.pack_start(statusBar_, Gtk::PACK_SHRINK, 0);
-    statusBar_.pack_start(progress_, Gtk::PACK_SHRINK, 0);
 
     show_all_children();
 }
@@ -836,43 +832,4 @@ MainWindow::set_busy(bool busy) throw()
     spinnerToolItem_.set_spinning(busy);
 }
 
-void
-MainWindow::connect_progress( const ProgressObserverPtr &observer ) throw()
-{
-    observer_ = observer;
-    progress_.set_fraction( 0.0L );
-
-    observer_->progress().connect(
-        sigc::mem_fun(*this,
-                      &MainWindow::on_progress));
-
-    observer_->dispatcher_reset().connect(
-        sigc::mem_fun(*this,
-                      &MainWindow::on_reset));
-}
-
-void
-MainWindow::on_progress() throw()
-{
-    const guint64 num_events = observer_->get_num_events();
-    if (0 == num_events)
-    {
-        return;
-    }
-
-    const double percentage =
-        static_cast<double>(observer_->get_current_events())
-        / static_cast<double>(num_events);
-
-    progress_.set_fraction( percentage );
-    progress_.show();
-}
-
-void
-MainWindow::on_reset() throw()
-{
-    progress_.hide();
-    progress_.set_fraction(0.0);
-}
-
 } // namespace Solang
diff --git a/src/application/main-window.h b/src/application/main-window.h
index 7275c65..5574010 100644
--- a/src/application/main-window.h
+++ b/src/application/main-window.h
@@ -81,11 +81,6 @@ class MainWindow :
         void
         set_busy(bool busy) throw();
 
-        void
-        connect_progress(
-                const ProgressObserverPtr &observer ) throw();
-
-
     protected:
         std::string
         get_user_layout_file() throw();
@@ -120,12 +115,6 @@ class MainWindow :
         virtual bool
         on_delete_event(GdkEventAny * event);
 
-        void
-        on_progress() throw();
-
-        void
-        on_reset() throw();
-
         ApplicationPtr application_;
 
         ActionGroupPtr actionGroup_;
@@ -142,10 +131,6 @@ class MainWindow :
 
         Gtk::Statusbar statusBar_;
 
-        Gtk::ProgressBar progress_;
-
-        ProgressObserverPtr observer_;
-
         GtkWidget * const dock_;
 
         GtkWidget * const dockBar_;
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 64763be..69dc300 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -16,6 +16,8 @@ libcommon_la_SOURCES = \
 	delete-action.h \
 	db-object.cpp \
 	db-object.h \
+	finally.cpp \
+	finally.h \
 	tracker-client.cpp \
 	tracker-client.h \
 	content-type-repo.cpp \
@@ -47,6 +49,8 @@ libcommon_la_SOURCES = \
 	i-photo-source.h \
 	i-plugin.cpp \
 	i-plugin.h \
+	i-progress-observer.cpp \
+	i-progress-observer.h \
 	i-renderer.cpp \
 	i-renderer.h \
 	i-renderer-selector.cpp \
@@ -65,8 +69,9 @@ libcommon_la_SOURCES = \
 	pixbuf-maker.h \
 	progress-dialog.cpp \
 	progress-dialog.h \
-	progress-observer.cpp \
 	progress-observer.h \
+	progress-widget.cpp \
+	progress-widget.h \
 	renderer-selector.h \
 	scale-action.cpp \
 	scale-action.h \
diff --git a/src/common/database.cpp b/src/common/database.cpp
index a9d5363..d306dd6 100644
--- a/src/common/database.cpp
+++ b/src/common/database.cpp
@@ -31,7 +31,6 @@
 #include "exif-data.h"
 #include "photo.h"
 #include "photo-tag.h"
-#include "progress-observer.h"
 #include "tag.h"
 #include "date-photo-info.h"
 
@@ -179,34 +178,29 @@ Database::search_async(const IPhotoSearchCriteriaList & criteria,
 
 //Group by year
 DatePhotoInfoList
-Database::get_dates_with_picture_count(
-              const ProgressObserverPtr & observer)
+Database::get_dates_with_picture_count()
 {
     Glib::ustring sql
         = "select 0, 0, mod_year, count(*) from photos ";
     sql += "group by mod_year";
-    return get_dates_with_picture_count( sql, observer );
+    return get_dates_with_picture_count( sql );
 }
 
 //Group by year, month
 DatePhotoInfoList
-Database::get_dates_with_picture_count(
-              gint year,
-              const ProgressObserverPtr & observer)
+Database::get_dates_with_picture_count(gint year)
 {
     std::ostringstream sout;
     sout<<"select 0, mod_month, mod_year, count(*) from photos ";
     sout<<"where mod_year="<<year<<" ";
     sout<<"group by mod_year,mod_month ";
 //    sout<<"order by mod_year desc, mod_month desc";
-    return get_dates_with_picture_count( sout.str(), observer );
+    return get_dates_with_picture_count( sout.str() );
 }
 
 //Group by year, month, day
 DatePhotoInfoList
-Database::get_dates_with_picture_count(
-              gint year, gint month,
-              const ProgressObserverPtr & observer)
+Database::get_dates_with_picture_count(gint year, gint month)
 {
     std::ostringstream sout;
     sout<<"select mod_day, mod_month, mod_year, count(*) from photos ";
@@ -214,13 +208,11 @@ Database::get_dates_with_picture_count(
     sout<<"and mod_month="<<month<<" ";
     sout<<"group by mod_year,mod_month,mod_day ";
 //    sout<<"order by mod_year desc, mod_month desc, mod_day desc";
-    return get_dates_with_picture_count( sout.str(), observer );
+    return get_dates_with_picture_count( sout.str() );
 }
 
 DatePhotoInfoList
-Database::get_dates_with_picture_count(
-              const Glib::ustring & sql,
-              const ProgressObserverPtr & observer)
+Database::get_dates_with_picture_count(const Glib::ustring & sql)
 {
     DatePhotoInfoList infos;
 
@@ -233,20 +225,8 @@ Database::get_dates_with_picture_count(
         const gint32 numRows
                          = static_cast<gint32>(model->get_n_rows());
 
-        if (0 != observer)
-        {
-            observer->set_event_description(_("Summarizing photos"));
-            observer->set_num_events(numRows);
-            observer->set_current_events(0);
-        }
-
         for( gint32 row = 0; row < numRows; row++ )
         {
-            if (0 != observer && true == observer->get_stop())
-            {
-                break;
-            }
-
             infos.push_back(
                 DatePhotoInfo(
                     ModificationDate(
@@ -254,16 +234,6 @@ Database::get_dates_with_picture_count(
                         model->get_value_at( 1, row ).get_int(),
                         model->get_value_at( 2, row ).get_int()),
                     model->get_value_at( 3, row ).get_int()));
-
-            if (0 != observer)
-            {
-                observer->receive_event_notifiation();
-            }
-        }
-
-        if (0 != observer)
-        {
-            observer->reset();
         }
     }
     catch (Glib::Error &e)
diff --git a/src/common/database.h b/src/common/database.h
index e10a226..498bf4c 100644
--- a/src/common/database.h
+++ b/src/common/database.h
@@ -91,27 +91,22 @@ class Database
 
         //Group by year
         DatePhotoInfoList
-        get_dates_with_picture_count( const ProgressObserverPtr &);
+        get_dates_with_picture_count( );
 
         //Group by year, month
         DatePhotoInfoList
-        get_dates_with_picture_count( gint year,
-                            const ProgressObserverPtr &);
+        get_dates_with_picture_count( gint year );
 
         //Group by year, month, day
         DatePhotoInfoList
-        get_dates_with_picture_count(
-            gint year, gint month,
-            const ProgressObserverPtr &);
+        get_dates_with_picture_count(gint year, gint month);
 
         void
         get_tags_async(const SlotAsyncTags & slot) const throw();
 
     private:
         DatePhotoInfoList
-        get_dates_with_picture_count(
-            const Glib::ustring & sql,
-            const ProgressObserverPtr &);
+        get_dates_with_picture_count(const Glib::ustring & sql);
 
         void
         on_async_exif_data(std::vector<UStringList> & result,
diff --git a/src/common/finally.cpp b/src/common/finally.cpp
new file mode 100644
index 0000000..7df166f
--- /dev/null
+++ b/src/common/finally.cpp
@@ -0,0 +1,42 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * Copyright (C) 2010 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/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif // HAVE_CONFIG_H
+
+#include "finally.h"
+
+namespace Solang
+{
+
+Finally::Finally(const sigc::slot<void> & s) throw() :
+    NonCopyable(),
+    s_(s)
+{
+}
+
+Finally::~Finally()
+{
+    if (true == s_)
+    {
+        s_();
+    }
+}
+
+} // namespace Solang
diff --git a/src/common/finally.h b/src/common/finally.h
new file mode 100644
index 0000000..100be6b
--- /dev/null
+++ b/src/common/finally.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * Copyright (C) 2010 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_FINALLY_H
+#define SOLANG_FINALLY_H
+
+#include <sigc++/sigc++.h>
+
+#include "non-copyable.h"
+
+namespace Solang
+{
+
+class Finally :
+    public NonCopyable
+{
+    public:
+        Finally(const sigc::slot<void> & s) throw();
+
+        // Calling the slot might lead to an exception.
+        ~Finally();
+
+    private:
+        sigc::slot<void> s_;
+};
+
+} // namespace Solang
+
+#endif // SOLANG_FINALLY_H
diff --git a/src/common/i-operation.h b/src/common/i-operation.h
index b2f9605..672e543 100644
--- a/src/common/i-operation.h
+++ b/src/common/i-operation.h
@@ -47,7 +47,8 @@ class IOperation :
 
         virtual BufferPtr
         apply(const BufferPtr & buffer,
-              const ProgressObserverPtr & observer) throw() = 0;
+              const ProgressObserverPtr & observer)
+              throw(Glib::Thread::Exit) = 0;
 
         virtual Glib::ustring
         get_description() const throw() = 0;
diff --git a/src/common/i-photo-destination.h b/src/common/i-photo-destination.h
index 409aadf..895a92c 100644
--- a/src/common/i-photo-destination.h
+++ b/src/common/i-photo-destination.h
@@ -46,11 +46,9 @@ class IPhotoDestination :
         final(Application & application) throw() = 0;
 
         virtual void
-        export_photo(const PhotoPtr & photo,
-                     const ProgressObserverPtr & observer) throw() = 0;
-        virtual void
-        export_photos(const PhotoList & photos,
-                      const ProgressObserverPtr & observer) throw() = 0;
+        export_photos_async(const PhotoList & photos,
+                            const ProgressObserverPtr & observer)
+                            throw() = 0;
 
         virtual void
         final() throw() = 0;
diff --git a/src/common/i-progress-observer.cpp b/src/common/i-progress-observer.cpp
new file mode 100644
index 0000000..eab350c
--- /dev/null
+++ b/src/common/i-progress-observer.cpp
@@ -0,0 +1,37 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * Copyright (C) 2010 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/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif // HAVE_CONFIG_H
+
+#include "i-progress-observer.h"
+
+namespace Solang
+{
+
+IProgressObserver::IProgressObserver() throw() :
+    Gio::Cancellable()
+{
+}
+
+IProgressObserver::~IProgressObserver() throw()
+{
+}
+
+} // namespace Solang
diff --git a/src/common/i-progress-observer.h b/src/common/i-progress-observer.h
new file mode 100644
index 0000000..946abf3
--- /dev/null
+++ b/src/common/i-progress-observer.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * Copyright (C) 2010 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_I_PROGRESS_OBSERVER_H
+#define SOLANG_I_PROGRESS_OBSERVER_H
+
+#include <giomm.h>
+#include <glibmm.h>
+
+namespace Solang
+{
+
+class IProgressObserver :
+    public Gio::Cancellable
+{
+    public:
+        virtual
+        ~IProgressObserver() throw() = 0;
+
+        virtual bool
+        is_finished() const throw() = 0;
+
+        virtual void
+        progress() throw() = 0;
+
+        virtual void
+        set_description(const Glib::ustring & description)
+                        throw() = 0;
+
+        virtual void
+        set_fraction(gdouble fraction) throw() = 0;
+
+        virtual void
+        set_total(guint64 total) throw() = 0;
+
+    protected:
+        IProgressObserver() throw();
+
+    private:
+};
+
+} // namespace Solang
+
+#endif // SOLANG_I_PROGRESS_OBSERVER_H
diff --git a/src/common/non-copyable.cpp b/src/common/non-copyable.cpp
index a1e8335..0206d71 100644
--- a/src/common/non-copyable.cpp
+++ b/src/common/non-copyable.cpp
@@ -27,7 +27,7 @@ NonCopyable::NonCopyable()
 {
 }
 
-NonCopyable::~NonCopyable() throw()
+NonCopyable::~NonCopyable()
 {
 } 
 
diff --git a/src/common/non-copyable.h b/src/common/non-copyable.h
index d9b47cf..f742ddd 100644
--- a/src/common/non-copyable.h
+++ b/src/common/non-copyable.h
@@ -26,7 +26,7 @@ class NonCopyable
 {
 public:
     virtual
-    ~NonCopyable() throw();
+    ~NonCopyable();
 
 protected:
     NonCopyable();
diff --git a/src/common/operation.cpp b/src/common/operation.cpp
index d8915b2..54a28cb 100644
--- a/src/common/operation.cpp
+++ b/src/common/operation.cpp
@@ -29,8 +29,9 @@ extern "C"
 #include <geglmm/buffer.h>
 #include <geglmm/processor.h>
 
+#include "finally.h"
+#include "i-progress-observer.h"
 #include "operation.h"
-#include "progress-observer.h"
 
 namespace Solang
 {
@@ -46,13 +47,12 @@ Operation::~Operation() throw()
 
 BufferPtr
 Operation::apply(const BufferPtr & buffer,
-                 const ProgressObserverPtr & observer) throw()
+                 const ProgressObserverPtr & observer)
+                 throw(Glib::Thread::Exit)
 {
     if (0 != observer)
     {
-        observer->set_event_description(get_description());
-        observer->set_num_events(100);
-        observer->set_current_events(0);
+        observer->set_description(get_description());
     }
 
     const NodePtr root = Gegl::Node::create();
@@ -69,7 +69,7 @@ Operation::apply(const BufferPtr & buffer,
 
     const NodePtr buffer_sink = root->new_child("operation",
                                                 "gegl:buffer-sink");
-    GeglBuffer * output_buffer;
+    GeglBuffer * output_buffer = 0;
     gegl_node_set(buffer_sink->gobj(),
                   "buffer", &output_buffer,
                   NULL);
@@ -79,25 +79,25 @@ Operation::apply(const BufferPtr & buffer,
     gdouble progress;
     GeglProcessor * const processor = gegl_node_new_processor(
                                           buffer_sink->gobj(), 0);
+    const Finally finally(sigc::bind(sigc::ptr_fun(&g_object_unref),
+                                     processor));
 
     while (true == gegl_processor_work(processor, &progress))
     {
         if (0 != observer)
         {
-            observer->set_current_events(static_cast<guint64>(
-                                             progress * 100.0));
-            if (true == observer->get_stop())
+            observer->set_fraction(progress);
+            if (true == observer->is_cancelled())
             {
-                break;
+                // FIXME: Find a better way to do this.
+                if (0 != output_buffer)
+                {
+                    g_object_unref(output_buffer);
+                }
+                throw Glib::Thread::Exit();
             }
         }
     }
-    g_object_unref(processor);
-
-    if (0 != observer)
-    {
-        observer->reset();
-    }
 
     return Glib::wrap(output_buffer, false);
 }
diff --git a/src/common/operation.h b/src/common/operation.h
index 6af154e..0785ccc 100644
--- a/src/common/operation.h
+++ b/src/common/operation.h
@@ -30,7 +30,8 @@ class Operation :
     public:
         virtual BufferPtr
         apply(const BufferPtr & buffer,
-              const ProgressObserverPtr & observer) throw();
+              const ProgressObserverPtr & observer)
+              throw(Glib::Thread::Exit);
 
     protected:
         virtual
diff --git a/src/common/progress-dialog.cpp b/src/common/progress-dialog.cpp
index 8b45773..e199399 100644
--- a/src/common/progress-dialog.cpp
+++ b/src/common/progress-dialog.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
 /*
- * Copyright (C) 2009 Debarshi Ray <rishi gnu org>
+ * Copyright (C) 2010, 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
@@ -16,52 +16,35 @@
  * with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+/*
+ * Based on code by: Alexander Larsson
+ *
+ * This widget was originally written in C as a part of Nautilus:
+ * libnautilus-private/nautilus-progress-info.c
+ */
+
 #include "config.h"
 
-#include <sstream>
+#include <glibmm/i18n.h>
 
 #include "progress-dialog.h"
-#include "progress-observer.h"
 
 namespace Solang
 {
 
-ProgressDialog::ProgressDialog(const ProgressObserverPtr & observer)
-    throw() :
-    Gtk::Dialog(),
-    observer_(observer),
-    primaryLabel_("", Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false),
-    mainVBox_(false, 3),
-    progressBar_()
+ProgressDialog::ProgressDialog(Gtk::Window & parent) throw() :
+    Gtk::Dialog(_("Pending Operations"), parent, false, false),
+    num_(0)
 {
-    set_border_width(12);
-    set_default_size(408, 108);
-    set_has_separator(false);
-
-    Gtk::VBox * const dialog_vbox = get_vbox();
-    dialog_vbox->set_spacing(12);
-
-    primaryLabel_.set_use_markup(true);
-    dialog_vbox->pack_start(primaryLabel_, Gtk::PACK_SHRINK, 0);
+    Gtk::VBox * const vbox = get_vbox();
+    vbox->set_homogeneous(false);
+    vbox->set_spacing(5);
 
-    dialog_vbox->pack_start(mainVBox_, Gtk::PACK_EXPAND_WIDGET, 0);
-
-    progressBar_.set_orientation(Gtk::PROGRESS_LEFT_TO_RIGHT);
-    mainVBox_.pack_start(progressBar_, Gtk::PACK_SHRINK, 0);
-
-    add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+    set_border_width(10);
+    set_position(Gtk::WIN_POS_CENTER);
+    set_resizable(false);
 
     show_all_children();
-
-    observer_->description_changed().connect(sigc::mem_fun(*this,
-        &ProgressDialog::on_observer_description_changed));
-
-    observer_->progress().connect(sigc::mem_fun(*this,
-        &ProgressDialog::on_progress));
-
-    observer_->dispatcher_reset().connect(
-        sigc::mem_fun(*this,
-                      &ProgressDialog::on_reset));
 }
 
 ProgressDialog::~ProgressDialog() throw()
@@ -69,73 +52,36 @@ ProgressDialog::~ProgressDialog() throw()
 }
 
 void
-ProgressDialog::set_progress_title()
+ProgressDialog::attach(Gtk::Widget & child) throw()
 {
-    const Glib::ustring & description
-                              = observer_->get_event_description();
+    Gtk::VBox * const vbox = get_vbox();
+    vbox->pack_start(child, false, false, 6);
 
-    set_title(description);
-    primaryLabel_.set_markup("<b><big>" + description + "</big></b>");
+    num_++;
+    child.show_all();
+    present();
 }
 
 void
-ProgressDialog::pulse() throw()
+ProgressDialog::detach(Gtk::Widget & child) throw()
 {
-    progressBar_.pulse();
-}
+    Gtk::VBox * const vbox = get_vbox();
+    vbox->remove(child);
 
-void
-ProgressDialog::set_pulse_step(double fraction) throw()
-{
-    progressBar_.set_pulse_step(fraction);
+    num_--;
+    if (0 == num_)
+    {
+        hide();
+    }
 }
 
 bool
 ProgressDialog::on_delete_event(GdkEventAny * event)
 {
-    hide();
-    return false;    
-}
+    const bool return_value = Gtk::Dialog::on_delete_event(event);
 
-void
-ProgressDialog::on_observer_description_changed() throw()
-{
-    set_progress_title();
-}
-
-void
-ProgressDialog::on_progress() throw()
-{
-    const guint64 num_events = observer_->get_num_events();
-    if (0 == num_events)
-    {
-        return;
-    }
-
-    std::ostringstream sout;
-    sout << observer_->get_current_events() << " of "
-         << num_events << " completed";
-
-    progressBar_.set_text(sout.str());
-    progressBar_.set_fraction(
-        static_cast<double>(observer_->get_current_events())
-        / static_cast<double>(num_events));
-}
-
-void
-ProgressDialog::on_response(int response_id)
-{
-    observer_->set_stop(true);
     hide();
-}
-
-void
-ProgressDialog::on_reset() throw()
-{
-    primaryLabel_.set_markup("");
-    progressBar_.set_fraction(0.0);
-    progressBar_.set_text("");
-    set_title("");
+    return return_value;
 }
 
 } // namespace Solang
diff --git a/src/common/progress-dialog.h b/src/common/progress-dialog.h
index edd5d8f..87fe613 100644
--- a/src/common/progress-dialog.h
+++ b/src/common/progress-dialog.h
@@ -1,6 +1,6 @@
 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
 /*
- * Copyright (C) 2009 Debarshi Ray <rishi gnu org>
+ * Copyright (C) 2010, 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
@@ -16,14 +16,18 @@
  * with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+/*
+ * Based on code by: Alexander Larsson
+ *
+ * This widget was originally written in C as a part of Nautilus:
+ * libnautilus-private/nautilus-progress-info.h
+ */
+
 #ifndef SOLANG_PROGRESS_DIALOG_H
 #define SOLANG_PROGRESS_DIALOG_H
 
-#include <glibmm.h>
 #include <gtkmm.h>
 
-#include "types.h"
-
 namespace Solang
 {
 
@@ -31,46 +35,23 @@ class ProgressDialog :
     public Gtk::Dialog 
 {
     public:
-        ProgressDialog(const ProgressObserverPtr & observer) throw();
+        ProgressDialog(Gtk::Window & parent) throw();
 
         virtual
         ~ProgressDialog() throw();
 
         void
-        pulse() throw();
+        attach(Gtk::Widget & child) throw();
 
         void
-        set_pulse_step(double fraction) throw();
-
-        void
-        set_progress_title();
+        detach(Gtk::Widget & child) throw();
 
     protected:
         virtual bool
         on_delete_event(GdkEventAny * event);
 
-        virtual void
-        on_response(int response_id);
-
-        void
-        on_observer_description_changed() throw();
-
-        void
-        on_progress() throw();
-
-        void
-        on_reset() throw();
-
-        ProgressObserverPtr observer_;
-
-        Gtk::Label primaryLabel_;
-
-        Gtk::VBox mainVBox_;
-
-        Gtk::ProgressBar progressBar_;
-
-
     private:
+        guint num_;
 };
 
 } // namespace Solang
diff --git a/src/common/progress-observer.h b/src/common/progress-observer.h
index 2808548..1d0652f 100644
--- a/src/common/progress-observer.h
+++ b/src/common/progress-observer.h
@@ -1,5 +1,6 @@
 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
 /*
+ * Copyright (C) 2010 Debarshi Ray <rishi gnu org>
  * Copyright (C) 2009 Santanu Sinha <santanu sinha gmail com>
  *
  * Solang is free software: you can redistribute it and/or modify it
@@ -19,124 +20,218 @@
 #ifndef SOLANG_PROGRESS_OBSERVER_H
 #define SOLANG_PROGRESS_OBSERVER_H
 
-#include <tr1/memory>
+#include <glibmm/i18n.h>
+#include <sigc++/sigc++.h>
 
-#include <glibmm.h>
+#include "i-progress-observer.h"
+#include "progress-dialog.h"
+#include "progress-widget.h"
+#include "types.h"
+#include "utils.h"
 
 namespace Solang
 {
 
-class ProgressObserver
+template <typename T>
+class ProgressObserver :
+    public IProgressObserver
 {
     public:
-        ProgressObserver() throw();
-
-        ProgressObserver(const ProgressObserver & source) throw();
-
-        ProgressObserver &
-        operator=(const ProgressObserver & source) throw();
+        static ProgressObserverPtr
+        create(ProgressDialog & progress_dialog);
 
+        virtual
         ~ProgressObserver() throw();
 
-        inline guint64
-        get_num_events() const throw();
+        virtual bool
+        is_finished() const throw();
 
-        void
-        set_num_events(guint64 numEvents) throw();
+        virtual void
+        progress() throw();
 
-        inline guint64
-        get_current_events() const throw();
+        virtual void
+        set_description(const Glib::ustring & description) throw();
 
-        void
-        set_current_events(guint64 current_events) throw();
+        virtual void
+        set_fraction(gdouble fraction) throw();
 
-        inline const Glib::ustring &
-        get_event_description() const throw();
+        virtual void
+        set_total(guint64 total) throw();
 
-        void
-        set_event_description(const Glib::ustring & event_description)
-                              throw();
+    protected:
+        ProgressObserver(ProgressDialog & progress_dialog) throw();
 
-        inline bool
-        get_stop() const throw();
+        virtual void
+        on_cancelled();
 
+    private:
         void
-        set_stop(bool value) throw();
+        clean_up() throw();
 
         void
-        receive_event_notifiation() throw();
+        on_fraction_changed() throw();
 
-        inline Glib::Dispatcher &
-        description_changed() throw();
+        T fractionChanged_;
 
-        inline Glib::Dispatcher &
-        progress() throw();
-
-        Glib::Dispatcher &
-        dispatcher_reset() throw();
+        mutable Glib::Mutex mutex_;
 
-        void 
-        reset() throw();
+        gdouble fraction_;
 
-    private:
-        guint64 numEvents_;
+        guint64 current_;
 
-        guint64 currentEvents_;
+        guint64 total_;
 
-        Glib::Dispatcher descriptionChanged_;
+        ProgressDialog & progressDialog_;
 
-        Glib::Dispatcher progress_;
+        ProgressWidget progressWidget_;
 
-        Glib::Dispatcher reset_;
+        sigc::connection connectionFractionChanged_;
+};
 
-        mutable Glib::Mutex mutex_;
+template <typename T>
+ProgressObserver<T>::ProgressObserver(ProgressDialog & progress_dialog)
+                                      throw() :
+    IProgressObserver(),
+    fractionChanged_(),
+    mutex_(),
+    fraction_(0.0),
+    current_(0),
+    total_(0),
+    progressDialog_(progress_dialog),
+    progressWidget_(),
+    connectionFractionChanged_()
+{
+    fractionChanged_.connect(
+        sigc::mem_fun(*this,
+                      &ProgressObserver::on_fraction_changed));
+    progressWidget_.signal_cancelled().connect(
+        sigc::mem_fun(*this,
+                      &ProgressObserver::cancel));
+}
 
-        Glib::ustring eventDescription_;
+template <typename T>
+ProgressObserver<T>::~ProgressObserver() throw()
+{
+    clean_up();
+}
 
-        bool stop_;
-};
+template <typename T>
+void
+ProgressObserver<T>::clean_up() throw()
+{
+    if (0 != progressWidget_.get_parent())
+    {
+        progressDialog_.detach(progressWidget_);
+    }
+}
 
+template <typename T>
+ProgressObserverPtr
+ProgressObserver<T>::create(ProgressDialog & progress_dialog)
+{
+    return ProgressObserverPtr(new ProgressObserver<T>(
+                                       progress_dialog));
+}
 
-inline guint64
-ProgressObserver::get_num_events() const throw()
+template <typename T>
+bool
+ProgressObserver<T>::is_finished() const throw()
 {
     Glib::Mutex::Lock lock(mutex_);
-    return numEvents_;
+    return (0 < total_) ? (current_ == total_)
+                        : is_equal(1.0, fraction_);
 }
 
-inline guint64
-ProgressObserver::get_current_events() const throw()
+template <typename T>
+void
+ProgressObserver<T>::on_cancelled()
 {
-    Glib::Mutex::Lock lock(mutex_);
-    return currentEvents_;
+    clean_up();
+    connectionFractionChanged_.block();
 }
 
-inline const Glib::ustring &
-ProgressObserver::get_event_description() const throw()
+template <typename T>
+void
+ProgressObserver<T>::on_fraction_changed() throw()
 {
-    Glib::Mutex::Lock lock(mutex_);
-    return eventDescription_;
+    {
+        Glib::Mutex::Lock lock(mutex_);
+
+        progressWidget_.set_fraction(fraction_);
+
+        if (0 < total_)
+        {
+            // Translators: Progress bar text. eg., 13 of 42 completed.
+            const Glib::ustring details = Glib::ustring::compose(
+                                              _("%1 of %2 completed"),
+                                              current_,
+                                              total_);
+            progressWidget_.set_details(details);
+        }
+        else
+        {
+            // Translators: Progress bar text. eg., 13% completed.
+            const Glib::ustring details = Glib::ustring::compose(
+                                              _("%1%% complete"),
+                                              fraction_ * 100.0);
+            progressWidget_.set_details(details);
+        }
+    }
+
+    if (0 == progressWidget_.get_parent())
+    {
+        progressDialog_.attach(progressWidget_);
+    }
 }
 
-inline bool
-ProgressObserver::get_stop() const throw()
+template <typename T>
+void
+ProgressObserver<T>::progress() throw()
 {
-    Glib::Mutex::Lock lock(mutex_);
-    return stop_;
+    if (true == is_cancelled())
+    {
+        return;
+    }
+
+    {
+        Glib::Mutex::Lock lock(mutex_);
+        current_++;
+        fraction_ = static_cast<gdouble>(current_)
+                    / static_cast<gdouble>(total_);
+    }
+    fractionChanged_.emit();
 }
 
-inline Glib::Dispatcher &
-ProgressObserver::description_changed() throw()
+template <typename T>
+void
+ProgressObserver<T>::set_description(const Glib::ustring & description)
+                                     throw()
 {
-    Glib::Mutex::Lock lock(mutex_);
-    return descriptionChanged_;
+    progressWidget_.set_status(description);
+}
+
+template <typename T>
+void
+ProgressObserver<T>::set_fraction(gdouble fraction) throw()
+{
+    if (true == is_cancelled())
+    {
+        return;
+    }
+
+    {
+        Glib::Mutex::Lock lock(mutex_);
+        fraction_ = fraction;
+    }
+    fractionChanged_.emit();
 }
 
-inline Glib::Dispatcher &
-ProgressObserver::progress() throw()
+template <typename T>
+void
+ProgressObserver<T>::set_total(guint64 total) throw()
 {
     Glib::Mutex::Lock lock(mutex_);
-    return progress_;
+    total_ = total;
 }
 
 } // namespace Solang
diff --git a/src/common/progress-widget.cpp b/src/common/progress-widget.cpp
new file mode 100644
index 0000000..1a37459
--- /dev/null
+++ b/src/common/progress-widget.cpp
@@ -0,0 +1,99 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * Copyright (C) 2010 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/>.
+ */
+
+/*
+ * Based on code by: Alexander Larsson
+ *
+ * This widget was originally written in C as a part of Nautilus:
+ * libnautilus-private/nautilus-progress-info.c
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif // HAVE_CONFIG_H
+
+#include "progress-widget.h"
+
+namespace Solang
+{
+
+ProgressWidget::ProgressWidget() throw() :
+    Gtk::VBox(false, 5),
+    button_(),
+    hBox_(false, 10),
+    image_(Gtk::Stock::CANCEL, Gtk::ICON_SIZE_BUTTON),
+    detailsLabel_("details", false),
+    statusLabel_("status", false),
+    progressBar_(),
+    vBox_(false, 0)
+{
+    statusLabel_.set_alignment(0.0, 0.5);
+    statusLabel_.set_line_wrap(true);
+    statusLabel_.set_line_wrap_mode(Pango::WRAP_WORD_CHAR);
+    statusLabel_.set_size_request(500, -1);
+    pack_start(statusLabel_, true, false, 0);
+
+    progressBar_.set_pulse_step(0.05);
+    vBox_.pack_start(progressBar_, true, false, 0);
+    hBox_.pack_start(vBox_, true, true, 0);
+
+    button_.add(image_);
+    hBox_.pack_start(button_, false, false, 0);
+
+    pack_start(hBox_, false, false, 0);
+
+    detailsLabel_.set_alignment(0.0, 0.5);
+    detailsLabel_.set_line_wrap(true);
+    detailsLabel_.set_use_markup(true);
+    pack_start(detailsLabel_, true, false, 0);
+
+    show_all_children();
+}
+
+ProgressWidget::~ProgressWidget() throw()
+{
+}
+
+void
+ProgressWidget::set_details(const Glib::ustring & details) throw()
+{
+    const Glib::ustring markup
+        = Glib::ustring::compose("<span size='small'>%1</span>",
+                                 Glib::Markup::escape_text(details));
+    detailsLabel_.set_markup(markup);
+}
+
+void
+ProgressWidget::set_fraction(gdouble fraction) throw()
+{
+    progressBar_.set_fraction(fraction);
+}
+
+void
+ProgressWidget::set_status(const Glib::ustring & status) throw()
+{
+    statusLabel_.set_text(status);
+}
+
+Glib::SignalProxy0<void>
+ProgressWidget::signal_cancelled() throw()
+{
+    return button_.signal_clicked();
+}
+
+} // namespace Solang
diff --git a/src/common/progress-widget.h b/src/common/progress-widget.h
new file mode 100644
index 0000000..5dabe10
--- /dev/null
+++ b/src/common/progress-widget.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * Copyright (C) 2010 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/>.
+ */
+
+/*
+ * Based on code by: Alexander Larsson
+ *
+ * This widget was originally written in C as a part of Nautilus:
+ * libnautilus-private/nautilus-progress-info.h
+ */
+
+#ifndef SOLANG_WIDGET_H
+#define SOLANG_WIDGET_H
+
+#include <glibmm.h>
+#include <gtkmm.h>
+
+namespace Solang
+{
+
+class ProgressWidget :
+    public Gtk::VBox
+{
+    public:
+        ProgressWidget() throw();
+
+        virtual
+        ~ProgressWidget() throw();
+
+        void
+        set_details(const Glib::ustring & details) throw();
+
+        void
+        set_fraction(gdouble fraction) throw();
+
+        void
+        set_status(const Glib::ustring & status) throw();
+
+        Glib::SignalProxy0<void>
+        signal_cancelled() throw();
+
+    private:
+        Gtk::Button button_;
+
+        Gtk::HBox hBox_;
+
+        Gtk::Image image_;
+
+        Gtk::Label detailsLabel_;
+
+        Gtk::Label statusLabel_;
+
+        Gtk::ProgressBar progressBar_;
+
+        Gtk::VBox vBox_;
+};
+
+} // namespace Solang
+
+#endif // SOLANG_WIDGET_H
diff --git a/src/common/types.h b/src/common/types.h
index 837be80..cc05758 100644
--- a/src/common/types.h
+++ b/src/common/types.h
@@ -51,6 +51,7 @@ class Processor;
 namespace Gio
 {
 
+class AsyncResult;
 class DataInputStream;
 class File;
 
@@ -185,6 +186,11 @@ typedef std::tr1::shared_ptr<const IPlugin> ConstIPluginPtr;
 typedef std::tr1::shared_ptr<IPlugin> IPluginPtr;
 typedef std::vector<IPluginPtr> IPluginList;
 
+class IProgressObserver;
+typedef Glib::RefPtr<const IProgressObserver>
+    ConstProgressObserverPtr;
+typedef Glib::RefPtr<IProgressObserver> ProgressObserverPtr;
+
 class IRenderer;
 typedef const IRenderer * ConstIRendererPtr;
 typedef IRenderer * IRendererPtr;
@@ -202,6 +208,7 @@ typedef std::tr1::shared_ptr<const Photo> ConstPhotoPtr;
 typedef std::tr1::shared_ptr<Photo> PhotoPtr;
 typedef Photo * UnrefPhotoPtr;
 typedef std::vector<PhotoPtr> PhotoList;
+typedef std::tr1::shared_ptr<PhotoList> PhotoListPtr;
 
 class IPhotoSearchCriteria;
 typedef std::tr1::shared_ptr<const IPhotoSearchCriteria>
@@ -222,11 +229,6 @@ typedef Glib::RefPtr<ProgressDialog> ProgressDialogPtr;
 
 typedef Glib::RefPtr<Glib::Dispatcher> DispatcherPtr;
 
-class ProgressObserver;
-typedef std::tr1::shared_ptr<const ProgressObserver>
-    ConstProgressObserverPtr;
-typedef std::tr1::shared_ptr<ProgressObserver> ProgressObserverPtr;
-
 class IStorage;
 typedef std::tr1::shared_ptr<const IStorage> ConstIStoragePtr;
 typedef std::tr1::shared_ptr<IStorage> IStoragePtr;
@@ -281,6 +283,9 @@ typedef Glib::RefPtr<Gegl::Node> NodePtr;
 typedef Glib::RefPtr<const Gegl::Processor> ConstProcessorPtr;
 typedef Glib::RefPtr<Gegl::Processor> ProcessorPtr;
 
+typedef Glib::RefPtr<const Gio::AsyncResult> ConstAsyncResultPtr;
+typedef Glib::RefPtr<Gio::AsyncResult> AsyncResultPtr;
+
 typedef Glib::RefPtr<const Gio::DataInputStream>
     ConstDataInputStreamPtr;
 typedef Glib::RefPtr<Gio::DataInputStream> DataInputStreamPtr;
diff --git a/src/editor/editable-photo.cpp b/src/editor/editable-photo.cpp
index 5108586..078293f 100644
--- a/src/editor/editable-photo.cpp
+++ b/src/editor/editable-photo.cpp
@@ -21,25 +21,27 @@
 #include "config.h"
 #endif // HAVE_CONFIG_H
 
+#include <algorithm>
+
 #include <geglmm.h>
 #include <geglmm/buffer.h>
 
 #include "buffer-maker.h"
 #include "buffer-pixbuf-converter.h"
 #include "editable-photo.h"
+#include "finally.h"
 #include "i-operation.h"
+#include "i-progress-observer.h"
 #include "photo.h"
 
 namespace Solang
 {
 
-EditablePhoto::EditablePhoto(const PhotoPtr & photo,
-                             const ProgressObserverPtr & observer) throw() :
+EditablePhoto::EditablePhoto(const PhotoPtr & photo) throw() :
     buffer_(0),
     photo_(photo),
     pixbuf_(0),
     pending_(),
-    observer_(observer),
     applyEnd_(),
     threadPool_(1, false)
 {
@@ -49,15 +51,34 @@ EditablePhoto::EditablePhoto(const PhotoPtr & photo,
 
 EditablePhoto::~EditablePhoto() throw()
 {
+    if (true == pending_.empty())
+    {
+        return;
+    }
+
+    const Triplet & current = pending_.front();
+    const ProgressObserverPtr & observer = current.third;
+
+    if (0 != observer)
+    {
+        observer->cancel();
+    }
 }
 
 void
 EditablePhoto::apply_async(const IOperationPtr & operation,
-                           const SlotAsyncReady & slot) throw()
+                           const SlotAsyncReady & slot,
+                           const ProgressObserverPtr & observer)
+                           throw()
 {
     const SlotAsyncReadyPtr slot_copy(new SlotAsyncReady(slot));
 
-    pending_.push(std::make_pair(operation, slot_copy));
+    Triplet triplet;
+    triplet.first = operation;
+    triplet.second = slot_copy;
+    triplet.third = observer;
+
+    pending_.push(triplet);
     if (1 < pending_.size())
     {
         return;
@@ -70,19 +91,23 @@ EditablePhoto::apply_async(const IOperationPtr & operation,
 void
 EditablePhoto::apply_begin() throw()
 {
-    const std::pair<IOperationPtr, SlotAsyncReadyPtr> & current
-        = pending_.front();
+    const Triplet & current = pending_.front();
 
     threadPool_.push(sigc::bind(
                          sigc::mem_fun(*this,
                                        &EditablePhoto::apply_worker),
-                         current.first));
+                         current.first,
+                         current.third));
 }
 
 void
-EditablePhoto::apply_worker(const IOperationPtr & operation)
+EditablePhoto::apply_worker(const IOperationPtr & operation,
+                            const ProgressObserverPtr & observer)
                             throw(Glib::Thread::Exit)
 {
+    const Finally finally(sigc::mem_fun(applyEnd_,
+                                        &Glib::Dispatcher::emit));
+
     if (0 == buffer_)
     {
         std::string path;
@@ -100,19 +125,17 @@ EditablePhoto::apply_worker(const IOperationPtr & operation)
         buffer_ = buffer_maker(path);
     }
 
-    buffer_ = operation->apply(buffer_, observer_);
+    buffer_ = operation->apply(buffer_, observer);
 g_warning("done op");
     BufferPixbufConverter buffer_pixbuf_converter;
     pixbuf_ = buffer_pixbuf_converter(buffer_);
 g_warning("done pixbuf");
-    applyEnd_.emit();
 }
 
 void
 EditablePhoto::on_apply_end() throw()
 {
-    const std::pair<IOperationPtr, SlotAsyncReadyPtr> current
-        = pending_.front();
+    const Triplet current = pending_.front();
 
     pending_.pop();
     photo_->set_buffer(pixbuf_);
@@ -122,6 +145,17 @@ EditablePhoto::on_apply_end() throw()
         (*current.second)();
     }
 
+    const ProgressObserverPtr & observer = current.third;
+
+    // Clear the pending queue if:
+    // + an operation was cancelled
+    // + the buffer could not be created
+    if (true == observer->is_cancelled() || 0 == buffer_)
+    {
+        PendingOperationQueue tmp;
+        std::swap(pending_, tmp);
+    }
+
     if (true == pending_.empty())
     {
         return;
diff --git a/src/editor/editable-photo.h b/src/editor/editable-photo.h
index 43c45e8..63698e2 100644
--- a/src/editor/editable-photo.h
+++ b/src/editor/editable-photo.h
@@ -40,28 +40,34 @@ class EditablePhoto
         typedef std::tr1::shared_ptr<SlotAsyncReady>
             SlotAsyncReadyPtr;
 
-        typedef std::queue<std::pair<IOperationPtr,
-                                     SlotAsyncReadyPtr> >
-            PendingOperationQueue;
-
-        EditablePhoto(const PhotoPtr & photo,
-                      const ProgressObserverPtr & observer) throw();
+        EditablePhoto(const PhotoPtr & photo) throw();
 
         ~EditablePhoto() throw();
 
         void
         apply_async(const IOperationPtr & operation,
-                    const SlotAsyncReady & slot) throw();
+                    const SlotAsyncReady & slot,
+                    const ProgressObserverPtr & observer) throw();
 
         PhotoPtr &
         get_photo() throw();
 
     private:
+        struct Triplet
+        {
+            IOperationPtr first;
+            SlotAsyncReadyPtr second;
+            ProgressObserverPtr third;
+        };
+
+        typedef std::queue<Triplet> PendingOperationQueue;
+
         void
         apply_begin() throw();
 
         void
-        apply_worker(const IOperationPtr & operation)
+        apply_worker(const IOperationPtr & operation,
+                     const ProgressObserverPtr & observer)
                      throw(Glib::Thread::Exit);
 
         void
@@ -75,8 +81,6 @@ class EditablePhoto
 
         PendingOperationQueue pending_;
 
-        ProgressObserverPtr observer_;
-
         Glib::Dispatcher applyEnd_;
 
         Glib::ThreadPool threadPool_;
diff --git a/src/editor/editor.cpp b/src/editor/editor.cpp
index 15a50fb..610d0de 100644
--- a/src/editor/editor.cpp
+++ b/src/editor/editor.cpp
@@ -31,6 +31,7 @@
 #include "flip-vert-operation.h"
 #include "i-renderer.h"
 #include "non-copyable.h"
+#include "progress-observer.h"
 #include "rotate-clock-operation.h"
 #include "rotate-counter-operation.h"
 
@@ -248,12 +249,8 @@ Editor::apply_async(const IOperationPtr & operation) throw()
 
     if (editablePhotos_.end() == iter)
     {
-        Engine & engine = application_->get_engine();
-        const ProgressObserverPtr & observer = engine.get_default_observer();
-
         editable_photo = EditablePhotoPtr(new EditablePhoto(
-                                              currentPhoto_,
-                                              observer));
+                                              currentPhoto_));
         editablePhotos_.insert(std::make_pair(current_uri,
                                               editable_photo));
     }
@@ -262,11 +259,16 @@ Editor::apply_async(const IOperationPtr & operation) throw()
         editable_photo = iter->second;
     }
 
+    const ProgressObserverPtr observer
+        = ProgressObserver<Glib::Dispatcher>::create(
+              application_->get_progress_dialog());
+
     editable_photo->apply_async(
         operation,
         sigc::bind(sigc::mem_fun(*this,
                                  &Editor::on_action_edit_photo_end),
-                   editable_photo));
+                   editable_photo),
+        observer);
 }
 
 void
diff --git a/src/exporter/brasero-destination.cpp b/src/exporter/brasero-destination.cpp
index 841d5ac..ddcd9a9 100644
--- a/src/exporter/brasero-destination.cpp
+++ b/src/exporter/brasero-destination.cpp
@@ -25,9 +25,9 @@
 #include <glibmm/i18n.h>
 
 #include "brasero-destination.h"
+#include "i-progress-observer.h"
 #include "photo.h"
 #include "photo-destination-enums.h"
-#include "progress-observer.h"
 
 namespace Solang
 {
@@ -38,14 +38,9 @@ BraseroDestination::BraseroDestination() throw() :
     braseroSessionCfg_(0),
     braseroTrackDataCfg_(0),
     braseroDriveSelection_(0),
-    braseroBurnBegin_(),
     initEnd_()
 {
     brasero_burn_library_start(0, 0);
-
-    braseroBurnBegin_.connect(
-        sigc::mem_fun(*this,
-                      &BraseroDestination::on_brasero_burn_begin));
 }
 
 BraseroDestination::~BraseroDestination() throw()
@@ -65,10 +60,9 @@ BraseroDestination::final(Application & application) throw()
 }
 
 void
-BraseroDestination::export_photo(
+BraseroDestination::export_photo_async(
                           const PhotoPtr & photo,
-                          const ProgressObserverPtr & observer)
-                          throw()
+                          const ProgressObserverPtr &) throw()
 {
     brasero_track_data_cfg_add(braseroTrackDataCfg_,
                                photo->get_uri().c_str(),
@@ -76,7 +70,7 @@ BraseroDestination::export_photo(
 }
 
 void
-BraseroDestination::export_photos(
+BraseroDestination::export_photos_async(
                           const PhotoList & photos,
                           const ProgressObserverPtr & observer)
                           throw()
@@ -86,13 +80,6 @@ BraseroDestination::export_photos(
         return;
     }
 
-    if (0 != observer)
-    {
-        observer->set_event_description(_("Exporting photos"));
-        observer->set_num_events(photos.size());
-        observer->set_current_events(0);
-    }
-
     braseroSessionCfg_ = brasero_session_cfg_new();
     brasero_burn_session_set_burner(
         BRASERO_BURN_SESSION(braseroSessionCfg_), braseroDrive_);
@@ -109,23 +96,13 @@ BraseroDestination::export_photos(
 
     for (it = photos.begin(); photos.end() != it; it++)
     {
-        export_photo(*it, observer);
-
-        if (0 != observer)
-        {
-            observer->receive_event_notifiation();
-        }
+        export_photo_async(*it, observer);
     }
 
     g_object_unref(braseroTrackDataCfg_);
     braseroTrackDataCfg_ = 0;
 
-    if (0 != observer)
-    {
-        observer->reset();
-    }
-
-    braseroBurnBegin_.emit();
+    brasero_burn_begin();
 }
 
 void BraseroDestination::final() throw()
@@ -191,6 +168,15 @@ BraseroDestination::set_create_archive(bool value) throw()
 }
 
 void
+BraseroDestination::brasero_burn_begin() throw()
+{
+    Glib::signal_idle().connect_once(
+        sigc::mem_fun(*this,
+                      &BraseroDestination::brasero_burn_begin_idle),
+        Glib::PRIORITY_LOW);
+}
+
+void
 BraseroDestination::brasero_burn_begin_idle() throw()
 {
     GtkWidget * const burn_dialog = brasero_burn_dialog_new();
@@ -205,21 +191,4 @@ BraseroDestination::brasero_burn_begin_idle() throw()
     braseroSessionCfg_ = 0;
 }
 
-void
-BraseroDestination::on_brasero_burn_begin() throw()
-{
-    // FIXME: Sometimes the brasero_burn_dialog_run gets stuck and
-    //        freezes the application. This is a workaround.
-
-    while (true == Gtk::Main::events_pending())
-    {
-        Gtk::Main::iteration();
-    }
-
-    Glib::signal_idle().connect_once(
-        sigc::mem_fun(*this,
-                      &BraseroDestination::brasero_burn_begin_idle),
-        Glib::PRIORITY_LOW);
-}
-
 } // namespace Solang
diff --git a/src/exporter/brasero-destination.h b/src/exporter/brasero-destination.h
index 4d4f53d..5eff35c 100644
--- a/src/exporter/brasero-destination.h
+++ b/src/exporter/brasero-destination.h
@@ -46,12 +46,9 @@ class BraseroDestination :
         final(Application & application) throw();
 
         virtual void
-        export_photo(const PhotoPtr & photo,
-                     const ProgressObserverPtr & observer) throw();
-
-        virtual void
-        export_photos(const PhotoList & photos,
-                      const ProgressObserverPtr & observer) throw();
+        export_photos_async(const PhotoList & photos,
+                            const ProgressObserverPtr & observer)
+                            throw();
 
         virtual void
         final() throw();
@@ -82,10 +79,14 @@ class BraseroDestination :
 
     protected:
         void
-        brasero_burn_begin_idle() throw();
+        export_photo_async(const PhotoPtr & photo,
+                           const ProgressObserverPtr &) throw();
 
         void
-        on_brasero_burn_begin() throw();
+        brasero_burn_begin() throw();
+
+        void
+        brasero_burn_begin_idle() throw();
 
         BraseroDrive * braseroDrive_;
 
@@ -95,8 +96,6 @@ class BraseroDestination :
 
         GtkWidget * braseroDriveSelection_;
 
-        Glib::Dispatcher braseroBurnBegin_;
-
         sigc::signal<void, bool> initEnd_;
 
     private:
diff --git a/src/exporter/directory-destination.cpp b/src/exporter/directory-destination.cpp
index 77eb885..919cd64 100644
--- a/src/exporter/directory-destination.cpp
+++ b/src/exporter/directory-destination.cpp
@@ -28,9 +28,9 @@
 #include <glibmm/i18n.h>
 
 #include "directory-destination.h"
+#include "i-progress-observer.h"
 #include "photo.h"
 #include "photo-destination-enums.h"
-#include "progress-observer.h"
 
 namespace Solang
 {
@@ -60,8 +60,9 @@ DirectoryDestination::final(Application & application) throw()
 }
 
 void
-DirectoryDestination::export_photo(
+DirectoryDestination::export_photo_async(
                           const PhotoPtr & photo,
+                          const PhotoListPtr & pending,
                           const ProgressObserverPtr & observer)
                           throw()
 {
@@ -69,27 +70,30 @@ DirectoryDestination::export_photo(
     const FilePtr dest = Gio::File::create_for_path(
                              filename_ + "/" + file->get_basename());
 
-    try
-    {
-        file->copy(dest, Gio::FILE_COPY_NONE);
-    }
-    catch (const Gio::Error & e)
-    {
-        g_warning("%s", e.what().c_str());
-    }
+    file->copy_async(
+        dest,
+        sigc::slot<void, goffset, goffset>(),
+        sigc::bind(
+            sigc::mem_fun(*this,
+                          &DirectoryDestination::on_async_copy_ready),
+            file,
+            pending,
+            observer),
+        observer,
+        Gio::FILE_COPY_NONE,
+        Glib::PRIORITY_DEFAULT);
 }
 
 void
-DirectoryDestination::export_photos(
+DirectoryDestination::export_photos_async(
                           const PhotoList & photos,
                           const ProgressObserverPtr & observer)
                           throw()
 {
     if (0 != observer)
     {
-        observer->set_event_description(_("Exporting photos"));
-        observer->set_num_events(photos.size());
-        observer->set_current_events(0);
+        observer->set_description(_("Exporting photos"));
+        observer->set_total(photos.size());
     }
 
     if (true == createArchive_)
@@ -116,44 +120,111 @@ DirectoryDestination::export_photos(
         }
     }
 
-    PhotoList::const_iterator it;
+    const PhotoListPtr pending(new PhotoList(photos.begin(),
+                                             photos.end()));
 
-    for (it = photos.begin(); photos.end() != it; it++)
-    {
-        export_photo(*it, observer);
+    export_photo_async(pending->back(), pending, observer);
 
-        if (0 != observer)
-        {
-            observer->receive_event_notifiation();
-        }
-    }
+    return;
+}
 
-    if (true == createArchive_)
+void DirectoryDestination::final() throw()
+{
+}
+
+sigc::signal<void, bool> &
+DirectoryDestination::init_end() throw()
+{
+    return initEnd_;
+}
+
+void
+DirectoryDestination::on_async_copy_ready(
+                          const AsyncResultPtr & async_result,
+                          const FilePtr & file,
+                          const PhotoListPtr & pending,
+                          const ProgressObserverPtr & observer)
+                          throw()
+{
+    try
+    {
+        file->copy_finish(async_result);
+    }
+    catch (const Gio::Error & e)
     {
-        const std::string command_line
-            = "file-roller --add-to=" + filename_ + ".zip"
-              + " --add " + filename_;
+        switch (e.code())
+        {
+        case Gio::Error::CANCELLED:
+            return;
+            break;
 
-        Glib::spawn_command_line_sync(command_line);
-        g_remove(filename_.c_str());
+        default:
+            g_warning("%s", e.what().c_str());
+            break;
+        }
     }
 
     if (0 != observer)
     {
-        observer->reset();
+        observer->progress();
     }
 
-    return;
-}
+    pending->pop_back();
 
-void DirectoryDestination::final() throw()
-{
+    if (true == pending->empty())
+    {
+        if (true == createArchive_)
+        {
+            const std::string command_line
+                = Glib::find_program_in_path("file-roller")
+                  + " --add-to=" + filename_ + ".zip"
+                  + " --add " + filename_;
+
+            GPid pid;
+
+            try
+            {
+                Glib::spawn_async(
+                          "",
+                          Glib::shell_parse_argv(command_line),
+                          static_cast<Glib::SpawnFlags>(0),
+                          sigc::slot<void>(),
+                          &pid);
+            }
+            catch (const Glib::ShellError & e)
+            {
+                g_warning("%s", e.what().c_str());
+                return;
+            }
+            catch (const Glib::SpawnError & e)
+            {
+                g_warning("%s", e.what().c_str());
+                return;
+            }
+
+            Glib::signal_child_watch().connect(
+                sigc::bind(
+                    sigc::mem_fun(
+                        *this,
+                        &DirectoryDestination::on_child_watch),
+                    filename_),
+                pid,
+                Glib::PRIORITY_DEFAULT);
+        }
+    }
+    else
+    {
+        export_photo_async(pending->back(), pending, observer);
+    }
 }
 
-sigc::signal<void, bool> &
-DirectoryDestination::init_end() throw()
+void
+DirectoryDestination::on_child_watch(GPid,
+                                     int,
+                                     const std::string & filename)
+                                     throw()
 {
-    return initEnd_;
+    g_remove(filename.c_str());
 }
 
 void
diff --git a/src/exporter/directory-destination.h b/src/exporter/directory-destination.h
index ca2c332..15d1ea6 100644
--- a/src/exporter/directory-destination.h
+++ b/src/exporter/directory-destination.h
@@ -43,12 +43,9 @@ class DirectoryDestination :
         final(Application & application) throw();
 
         virtual void
-        export_photo(const PhotoPtr & photo,
-                     const ProgressObserverPtr & observer) throw();
-
-        virtual void
-        export_photos(const PhotoList & photos,
-                      const ProgressObserverPtr & observer) throw();
+        export_photos_async(const PhotoList & photos,
+                            const ProgressObserverPtr & observer)
+                            throw();
 
         virtual void
         final() throw();
@@ -78,6 +75,23 @@ class DirectoryDestination :
         set_create_archive(bool value) throw();
 
     protected:
+        void
+        export_photo_async(const PhotoPtr & photo,
+                           const PhotoListPtr & pending,
+                           const ProgressObserverPtr & observer)
+                           throw();
+
+        void
+        on_async_copy_ready(const AsyncResultPtr & async_result,
+                            const FilePtr & file,
+                            const PhotoListPtr & pending,
+                            const ProgressObserverPtr & observer)
+                            throw();
+
+        void
+        on_child_watch(GPid, int, const std::string & filename)
+                       throw();
+
         bool createArchive_;
 
         std::string filename_;
diff --git a/src/exporter/exporter.cpp b/src/exporter/exporter.cpp
index 6cdd967..b1470aa 100644
--- a/src/exporter/exporter.cpp
+++ b/src/exporter/exporter.cpp
@@ -211,14 +211,16 @@ Exporter::on_exporter_dialog_response(
                            = exporter_dialog->get_create_archive();
             photoDestination_->set_create_archive(create_archive);
 
-            Glib::ThreadPool & thread_pool
-                = application_->get_thread_pool();
             Engine & engine = application_->get_engine();
+            const PhotoList & export_queue
+                                  = engine.get_export_queue();
 
-            thread_pool.push(
-                sigc::bind(sigc::mem_fun1(engine,
-                                          &Engine::export_photos),
-                           photoDestination_));
+            const ProgressObserverPtr observer
+                = ProgressObserver<sigc::signal<void> >::create(
+                      application_->get_progress_dialog());
+
+            photoDestination_->export_photos_async(export_queue,
+                                                   observer);
             break;
         }
 



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