changeset 235:ce4405d98b92 am

Added SimpleViewerApplication (working in SDL)
author am@osimis.io
date Tue, 19 Jun 2018 16:02:41 +0200
parents 9afb50d1ac14
children f73d722d98c8
files Applications/IBasicApplication.h Applications/Samples/EmptyApplication.h Applications/Samples/SampleApplicationBase.h Applications/Samples/SampleApplicationContext.cpp Applications/Samples/SampleApplicationContext.h Applications/Samples/SampleMainSdl.cpp Applications/Samples/SimpleViewerApplication.h Applications/Samples/SingleFrameApplication.h Applications/Samples/SingleVolumeApplication.h Applications/Samples/TestPatternApplication.h Applications/Sdl/BasicSdlApplication.cpp Applications/Sdl/BasicSdlApplication.h Framework/Widgets/EmptyWidget.cpp Framework/Widgets/EmptyWidget.h Platforms/Generic/CMakeLists.txt
diffstat 15 files changed, 442 insertions(+), 189 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/IBasicApplication.h	Fri Jun 15 16:36:29 2018 +0200
+++ b/Applications/IBasicApplication.h	Tue Jun 19 16:02:41 2018 +0200
@@ -29,32 +29,6 @@
 {
   class IBasicApplication : public boost::noncopyable
   {
-  protected:
-//    struct StartupOptionValue {
-//      enum Type {
-//        boolean,
-//        string,
-//        integer
-//      };
-//      Type type;
-//      std::string value;
-
-//      int asInt() {return std::stoi(value);}
-//      bool asBool() {return value == "true"; }
-//      std::string asString() {return value; }
-//    };
-
-//    struct StartupOptionDefinition {
-//      std::string name;
-//      std::string helpText;
-//      std::string defaultValue;
-//      StartupOptionValue::Type type;
-//    };
-
-//    typedef std::list<StartupOptionDefinition> StartupOptions;
-
-//    StartupOptions startupOptions_;
-
   public:
     virtual ~IBasicApplication()
     {
@@ -78,10 +52,6 @@
 
     virtual void Finalize() = 0;
 
-//protected:
-//    virtual void DeclareStringStartupOption(const std::string& name, const std::string& defaultValue, const std::string& helpText);
-//    virtual void DeclareIntegerStartupOption(const std::string& name, const int& defaultValue, const std::string& helpText);
-//    virtual void DeclareBoolStartupOption(const std::string& name, bool defaultValue, const std::string& helpText);
   };
 
 }
--- a/Applications/Samples/EmptyApplication.h	Fri Jun 15 16:36:29 2018 +0200
+++ b/Applications/Samples/EmptyApplication.h	Tue Jun 19 16:02:41 2018 +0200
@@ -32,7 +32,7 @@
     class EmptyApplication : public SampleApplicationBase
     {
     public:
-      virtual void DeclareCommandLineOptions(boost::program_options::options_description& options)
+      virtual void DeclareStartupOptions(boost::program_options::options_description& options)
       {
         boost::program_options::options_description generic("Sample options");
         generic.add_options()
@@ -44,15 +44,14 @@
         options.add(generic);    
       }
 
-      virtual void Initialize(BasicApplicationContext& context,
-                              IStatusBar& statusBar,
+      virtual void Initialize(IStatusBar& statusBar,
                               const boost::program_options::variables_map& parameters)
       {
         int red = parameters["red"].as<int>();
         int green = parameters["green"].as<int>();
         int blue = parameters["blue"].as<int>();
 
-        context.SetCentralWidget(new EmptyWidget(red, green, blue));
+        context_->SetCentralWidget(new EmptyWidget(red, green, blue));
       }
     };
   }
--- a/Applications/Samples/SampleApplicationBase.h	Fri Jun 15 16:36:29 2018 +0200
+++ b/Applications/Samples/SampleApplicationBase.h	Tue Jun 19 16:02:41 2018 +0200
@@ -22,6 +22,7 @@
 #pragma once
 
 #include "../../Applications/Sdl/BasicSdlApplication.h"
+#include "../../Framework/Viewport/WidgetViewport.h"
 #include "SampleApplicationContext.h"
 
 namespace OrthancStone
@@ -30,16 +31,18 @@
   {
 
 #ifdef ORTHANC_ENABLE_SDL
-    class SampleSdlApplicationBase : BasicSdlApplication {
-    private:
+    class SampleSdlApplicationBase : public BasicSdlApplication {
+    protected:
       std::unique_ptr<SampleApplicationContext> context_;
+    public:
+      BasicApplicationContext& CreateApplicationContext(Orthanc::WebServiceParameters& orthanc, WidgetViewport* centralViewport) {
+        context_.reset(new SampleApplicationContext(orthanc, centralViewport));
 
-      BasicApplicationContext& CreateApplicationContext(Orthanc::WebServiceParameters& orthanc) {
-        context_.reset(new SampleApplicationContext(orthanc));
+        return *context_;
       }
     };
 
-    typedef SampleApplicationBase_ SampleSdlApplicationBase;
+    typedef SampleSdlApplicationBase SampleApplicationBase_;
 #else
 
 #endif
--- a/Applications/Samples/SampleApplicationContext.cpp	Fri Jun 15 16:36:29 2018 +0200
+++ b/Applications/Samples/SampleApplicationContext.cpp	Tue Jun 19 16:02:41 2018 +0200
@@ -23,8 +23,8 @@
 
 namespace OrthancStone
 {
-  SampleApplicationContext::SampleApplicationContext(Orthanc::WebServiceParameters& orthanc) :
-    BaseApplicationContext(orthanc)
+  SampleApplicationContext::SampleApplicationContext(Orthanc::WebServiceParameters& orthanc, WidgetViewport* centralViewport) :
+    BasicApplicationContext_(orthanc, centralViewport)
   {
   }
 
--- a/Applications/Samples/SampleApplicationContext.h	Fri Jun 15 16:36:29 2018 +0200
+++ b/Applications/Samples/SampleApplicationContext.h	Tue Jun 19 16:02:41 2018 +0200
@@ -32,15 +32,19 @@
 #include <boost/thread.hpp>
 
 
+#if ORTHANC_ENABLE_SDL==1
+#include "../Sdl/BasicSdlApplicationContext.h"
+#else
+#include "../Wasm/BasicWasmApplicationContext.h"
+#endif
+
 namespace OrthancStone
 {
 
-#if ORTHANC_ENABLE_SDL
-#include "../Sdl/BasicSdlApplicationContext.h"
-typedef BasicSdlApplicationContext BasicApplicationContext_;
+#if ORTHANC_ENABLE_SDL==1
+  typedef BasicSdlApplicationContext BasicApplicationContext_;
 #else
-#include "../Wasm/BasicWasmApplicationContext.h"
-typedef BasicWasmApplicationContext BasicApplicationContext_;
+  typedef BasicWasmApplicationContext BasicApplicationContext_;
 #endif
 
   class SampleApplicationContext : public BasicApplicationContext_
@@ -56,7 +60,7 @@
 
   public:
 
-    SampleApplicationContext(Orthanc::WebServiceParameters& orthanc);
+    SampleApplicationContext(Orthanc::WebServiceParameters& orthanc, WidgetViewport* centralViewport);
 
     virtual ~SampleApplicationContext();
 
--- a/Applications/Samples/SampleMainSdl.cpp	Fri Jun 15 16:36:29 2018 +0200
+++ b/Applications/Samples/SampleMainSdl.cpp	Tue Jun 19 16:02:41 2018 +0200
@@ -49,6 +49,10 @@
 #include "LayoutPetCtFusionApplication.h"
 typedef OrthancStone::Samples::LayoutPetCtFusionApplication Application;
 
+#elif ORTHANC_STONE_SAMPLE == 8
+#include "SimpleViewerApplication.h"
+typedef OrthancStone::Samples::SimpleViewerApplication Application;
+
 #else
 #error Please set the ORTHANC_STONE_SAMPLE macro
 #endif
@@ -58,5 +62,5 @@
 {
   Application application;
 
-  return OrthancStone::IBasicApplication::ExecuteWithSdl(application, argc, argv);
+  return OrthancStone::BasicSdlApplication::ExecuteWithSdl(application, argc, argv);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Samples/SimpleViewerApplication.h	Tue Jun 19 16:02:41 2018 +0200
@@ -0,0 +1,302 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2018 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "SampleApplicationBase.h"
+
+#include "../../Framework/Layers/OrthancFrameLayerSource.h"
+#include "../../Framework/Widgets/LayerWidget.h"
+#include "../../Framework/Widgets/LayoutWidget.h"
+
+#include <Core/Logging.h>
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+    class SimpleViewerApplication :
+      public SampleApplicationBase,
+      private ILayerSource::IObserver
+    {
+    private:
+      class Interactor : public IWorldSceneInteractor
+      {
+      private:
+        SimpleViewerApplication&  application_;
+        
+      public:
+        Interactor(SimpleViewerApplication&  application) :
+          application_(application)
+        {
+        }
+        
+        virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
+                                                            const ViewportGeometry& view,
+                                                            MouseButton button,
+                                                            double x,
+                                                            double y,
+                                                            IStatusBar* statusBar)
+        {
+          return NULL;
+        }
+
+        virtual void MouseOver(CairoContext& context,
+                               WorldSceneWidget& widget,
+                               const ViewportGeometry& view,
+                               double x,
+                               double y,
+                               IStatusBar* statusBar)
+        {
+          if (statusBar != NULL)
+          {
+            Vector p = dynamic_cast<LayerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y);
+            
+            char buf[64];
+            sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", 
+                    p[0] / 10.0, p[1] / 10.0, p[2] / 10.0);
+            statusBar->SetMessage(buf);
+          }
+        }
+
+        virtual void MouseWheel(WorldSceneWidget& widget,
+                                MouseWheelDirection direction,
+                                KeyboardModifiers modifiers,
+                                IStatusBar* statusBar)
+        {
+//          int scale = (modifiers & KeyboardModifiers_Control ? 10 : 1);
+          
+//          switch (direction)
+//          {
+//            case MouseWheelDirection_Up:
+//              application_.OffsetSlice(-scale);
+//              break;
+
+//            case MouseWheelDirection_Down:
+//              application_.OffsetSlice(scale);
+//              break;
+
+//            default:
+//              break;
+//          }
+        }
+
+        virtual void KeyPressed(WorldSceneWidget& widget,
+                                char key,
+                                KeyboardModifiers modifiers,
+                                IStatusBar* statusBar)
+        {
+          switch (key)
+          {
+            case 's':
+              widget.SetDefaultView();
+              break;
+
+            default:
+              break;
+          }
+        }
+      };
+
+
+//      void OffsetSlice(int offset)
+//      {
+//        if (source_ != NULL)
+//        {
+//          int slice = static_cast<int>(slice_) + offset;
+
+//          if (slice < 0)
+//          {
+//            slice = 0;
+//          }
+
+//          if (slice >= static_cast<int>(source_->GetSliceCount()))
+//          {
+//            slice = source_->GetSliceCount() - 1;
+//          }
+
+//          if (slice != static_cast<int>(slice_))
+//          {
+//            SetSlice(slice);
+//          }
+//        }
+//      }
+      
+
+//      void SetSlice(size_t index)
+//      {
+//        if (source_ != NULL &&
+//            index < source_->GetSliceCount())
+//        {
+//          slice_ = index;
+          
+//#if 1
+//          widget_->SetSlice(source_->GetSlice(slice_).GetGeometry());
+//#else
+//          // TEST for scene extents - Rotate the axes
+//          double a = 15.0 / 180.0 * M_PI;
+
+//#if 1
+//          Vector x; GeometryToolbox::AssignVector(x, cos(a), sin(a), 0);
+//          Vector y; GeometryToolbox::AssignVector(y, -sin(a), cos(a), 0);
+//#else
+//          // Flip the normal
+//          Vector x; GeometryToolbox::AssignVector(x, cos(a), sin(a), 0);
+//          Vector y; GeometryToolbox::AssignVector(y, sin(a), -cos(a), 0);
+//#endif
+          
+//          SliceGeometry s(source_->GetSlice(slice_).GetGeometry().GetOrigin(), x, y);
+//          widget_->SetSlice(s);
+//#endif
+//        }
+//      }
+        
+      
+      virtual void NotifyGeometryReady(const ILayerSource& source)
+      {
+        // Once the geometry of the series is downloaded from Orthanc,
+        // display its first slice, and adapt the viewport to fit this
+        // slice
+        if (source_ == &source)
+        {
+          //SetSlice(source_->GetSliceCount() / 2);
+        }
+
+        mainLayout_->SetDefaultView();
+      }
+      
+      virtual void NotifyGeometryError(const ILayerSource& source)
+      {
+      }
+      
+      virtual void NotifyContentChange(const ILayerSource& source)
+      {
+      }
+
+      virtual void NotifySliceChange(const ILayerSource& source,
+                                     const Slice& slice)
+      {
+      }
+ 
+      virtual void NotifyLayerReady(std::auto_ptr<ILayerRenderer>& layer,
+                                    const ILayerSource& source,
+                                    const CoordinateSystem3D& slice,
+                                    bool isError)
+      {
+      }
+
+      LayoutWidget*                   mainLayout_;
+      LayoutWidget*                   thumbnailsLayout_;
+      LayerWidget*                    mainViewport_;
+      std::vector<LayerWidget*>       thumbnails_;
+      std::vector<std::string>        instances_;
+      unsigned int                    currentInstanceIndex_;
+
+      OrthancFrameLayerSource*        source_;
+      unsigned int                    slice_;
+      
+    public:
+      SimpleViewerApplication() :
+        mainLayout_(NULL),
+        currentInstanceIndex_(0),
+        source_(NULL),
+        slice_(0)
+      {
+      }
+      
+      virtual void DeclareStartupOptions(boost::program_options::options_description& options)
+      {
+        boost::program_options::options_description generic("Sample options");
+        generic.add_options()
+//          ("study", boost::program_options::value<std::string>(),
+//           "Orthanc ID of the study")
+          ("instance1", boost::program_options::value<std::string>(),
+           "Orthanc ID of the instances")
+            ("instance2", boost::program_options::value<std::string>(),
+             "Orthanc ID of the instances")
+          ;
+
+        options.add(generic);    
+      }
+
+      virtual void Initialize(IStatusBar& statusBar,
+                              const boost::program_options::variables_map& parameters)
+      {
+        using namespace OrthancStone;
+
+        statusBar.SetMessage("Use the key \"s\" to reinitialize the layout");
+
+        if (parameters.count("instance1") < 1)
+        {
+          LOG(ERROR) << "The instance ID is missing";
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+        }
+        if (parameters.count("instance2") < 1)
+        {
+          LOG(ERROR) << "The instance ID is missing";
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+        }
+        instances_.push_back(parameters["instance1"].as<std::string>());
+        instances_.push_back(parameters["instance2"].as<std::string>());
+
+        mainLayout_ = new LayoutWidget();
+        mainLayout_->SetPadding(10);
+        mainLayout_->SetBackgroundCleared(true);
+        mainLayout_->SetBackgroundColor(0, 0, 0);
+        mainLayout_->SetHorizontal();
+
+        thumbnailsLayout_ = new LayoutWidget();
+        thumbnailsLayout_->SetPadding(10);
+        thumbnailsLayout_->SetBackgroundCleared(true);
+        thumbnailsLayout_->SetBackgroundColor(50, 50, 50);
+        thumbnailsLayout_->SetVertical();
+
+        mainViewport_ = new LayerWidget();
+        thumbnails_.push_back(new LayerWidget());
+        thumbnails_.push_back(new LayerWidget());
+
+        // hierarchy
+        mainLayout_->AddWidget(thumbnailsLayout_);
+        mainLayout_->AddWidget(mainViewport_);
+        thumbnailsLayout_->AddWidget(thumbnails_[0]);
+        thumbnailsLayout_->AddWidget(thumbnails_[1]);
+
+        // sources
+        source_ = new OrthancFrameLayerSource(context_->GetWebService());
+        source_->LoadFrame(instances_[currentInstanceIndex_], 0);
+
+        mainViewport_->AddLayer(source_);
+
+        OrthancFrameLayerSource* thumb0 = new OrthancFrameLayerSource(context_->GetWebService());
+        thumb0->LoadFrame(instances_[0], 0);
+        OrthancFrameLayerSource* thumb1 = new OrthancFrameLayerSource(context_->GetWebService());
+        thumb1->LoadFrame(instances_[1], 0);
+
+        thumbnails_[0]->AddLayer(thumb0);
+        thumbnails_[1]->AddLayer(thumb1);
+
+        mainLayout_->SetTransmitMouseOver(true);
+        mainViewport_->SetInteractor(context_->AddInteractor(new Interactor(*this)));
+        context_->SetCentralWidget(mainLayout_);
+      }
+    };
+  }
+}
--- a/Applications/Samples/SingleFrameApplication.h	Fri Jun 15 16:36:29 2018 +0200
+++ b/Applications/Samples/SingleFrameApplication.h	Tue Jun 19 16:02:41 2018 +0200
@@ -214,7 +214,7 @@
       {
       }
       
-      virtual void DeclareCommandLineOptions(boost::program_options::options_description& options)
+      virtual void DeclareStartupOptions(boost::program_options::options_description& options)
       {
         boost::program_options::options_description generic("Sample options");
         generic.add_options()
@@ -229,8 +229,7 @@
         options.add(generic);    
       }
 
-      virtual void Initialize(BasicApplicationContext& context,
-                              IStatusBar& statusBar,
+      virtual void Initialize(IStatusBar& statusBar,
                               const boost::program_options::variables_map& parameters)
       {
         using namespace OrthancStone;
@@ -250,7 +249,7 @@
 
 #if 1
         std::auto_ptr<OrthancFrameLayerSource> layer
-          (new OrthancFrameLayerSource(context.GetWebService()));
+          (new OrthancFrameLayerSource(context_->GetWebService()));
         //layer->SetImageQuality(SliceImageQuality_Jpeg50);
         layer->LoadFrame(instance, frame);
         //layer->LoadSeries("6f1b492a-e181e200-44e51840-ef8db55e-af529ab6");
@@ -271,7 +270,7 @@
         // 0178023P**
         // Extent of the CT layer: (-35.068 -20.368) => (34.932 49.632)
         std::auto_ptr<OrthancFrameLayerSource> ct;
-        ct.reset(new OrthancFrameLayerSource(context.GetWebService()));
+        ct.reset(new OrthancFrameLayerSource(context_->GetWebService()));
         //ct->LoadInstance("c804a1a2-142545c9-33b32fe2-3df4cec0-a2bea6d6", 0);
         //ct->LoadInstance("4bd4304f-47478948-71b24af2-51f4f1bc-275b6c1b", 0);  // BAD SLICE
         //ct->SetImageQuality(SliceImageQuality_Jpeg50);
@@ -281,7 +280,7 @@
         widget->AddLayer(ct.release());
 
         std::auto_ptr<OrthancFrameLayerSource> pet;
-        pet.reset(new OrthancFrameLayerSource(context.GetWebService()));
+        pet.reset(new OrthancFrameLayerSource(context_->GetWebService()));
         //pet->LoadInstance("a1c4dc6b-255d27f0-88069875-8daed730-2f5ee5c6", 0);
         pet->LoadSeries("aabad2e7-80702b5d-e599d26c-4f13398e-38d58a9e");
         pet->Register(*this);
@@ -309,8 +308,8 @@
 
         widget_ = widget.get();
         widget_->SetTransmitMouseOver(true);
-        widget_->SetInteractor(context.AddInteractor(new Interactor(*this)));
-        context.SetCentralWidget(widget.release());
+        widget_->SetInteractor(context_->AddInteractor(new Interactor(*this)));
+        context_->SetCentralWidget(widget.release());
       }
     };
   }
--- a/Applications/Samples/SingleVolumeApplication.h	Fri Jun 15 16:36:29 2018 +0200
+++ b/Applications/Samples/SingleVolumeApplication.h	Tue Jun 19 16:02:41 2018 +0200
@@ -89,7 +89,7 @@
 
 
     public:
-      virtual void DeclareCommandLineOptions(boost::program_options::options_description& options)
+      virtual void DeclareStartupOptions(boost::program_options::options_description& options)
       {
         boost::program_options::options_description generic("Sample options");
         generic.add_options()
@@ -108,8 +108,7 @@
         options.add(generic);    
       }
 
-      virtual void Initialize(BasicApplicationContext& context,
-                              IStatusBar& statusBar,
+      virtual void Initialize(IStatusBar& statusBar,
                               const boost::program_options::variables_map& parameters)
       {
         using namespace OrthancStone;
@@ -147,8 +146,8 @@
           throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
         }
 
-        unsigned int threads = parameters["threads"].as<unsigned int>();
-        bool reverse = parameters["reverse"].as<bool>();
+        //unsigned int threads = parameters["threads"].as<unsigned int>();
+        //bool reverse = parameters["reverse"].as<bool>();
 
         std::string tmp = parameters["projection"].as<std::string>();
         Orthanc::Toolbox::ToLowerCase(tmp);
@@ -175,7 +174,7 @@
         std::auto_ptr<LayerWidget> widget(new LayerWidget);
 
 #if 0
-        std::auto_ptr<OrthancVolumeImage> volume(new OrthancVolumeImage(context.GetWebService(), true));
+        std::auto_ptr<OrthancVolumeImage> volume(new OrthancVolumeImage(context_->GetWebService(), true));
         if (series.empty())
         {
           volume->ScheduleLoadInstance(instance);
@@ -187,8 +186,8 @@
 
         widget->AddLayer(new VolumeImageSource(*volume));
 
-        context.AddInteractor(new Interactor(*volume, *widget, projection, 0));
-        context.AddSlicedVolume(volume.release());
+        context_->AddInteractor(new Interactor(*volume, *widget, projection, 0));
+        context_->AddSlicedVolume(volume.release());
 
         {
           RenderStyle s;
@@ -199,14 +198,14 @@
           widget->SetLayerStyle(0, s);
         }
 #else
-        std::auto_ptr<OrthancVolumeImage> ct(new OrthancVolumeImage(context.GetWebService(), false));
+        std::auto_ptr<OrthancVolumeImage> ct(new OrthancVolumeImage(context_->GetWebService(), false));
         //ct->ScheduleLoadSeries("15a6f44a-ac7b88fe-19c462d9-dddd918e-b01550d8");  // 0178023P
         //ct->ScheduleLoadSeries("dd069910-4f090474-7d2bba07-e5c10783-f9e4fb1d");
         //ct->ScheduleLoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");  // IBA
         //ct->ScheduleLoadSeries("03677739-1d8bca40-db1daf59-d74ff548-7f6fc9c0");  // 0522c0001 TCIA
         ct->ScheduleLoadSeries("295e8a13-dfed1320-ba6aebb2-9a13e20f-1b3eb953");  // Captain
         
-        std::auto_ptr<OrthancVolumeImage> pet(new OrthancVolumeImage(context.GetWebService(), true));
+        std::auto_ptr<OrthancVolumeImage> pet(new OrthancVolumeImage(context_->GetWebService(), true));
         //pet->ScheduleLoadSeries("48d2997f-8e25cd81-dd715b64-bd79cdcc-e8fcee53");  // 0178023P
         //pet->ScheduleLoadSeries("aabad2e7-80702b5d-e599d26c-4f13398e-38d58a9e");
         //pet->ScheduleLoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"); // IBA 1
@@ -216,7 +215,7 @@
         pet->ScheduleLoadInstance("f080888c-0ab7528a-f7d9c28c-84980eb1-ff3b0ae6");  // Captain 1
         //pet->ScheduleLoadInstance("4f78055b-6499a2c5-1e089290-394acc05-3ec781c1");  // Captain 2
 
-        std::auto_ptr<StructureSetLoader> rtStruct(new StructureSetLoader(context.GetWebService()));
+        std::auto_ptr<StructureSetLoader> rtStruct(new StructureSetLoader(context_->GetWebService()));
         //rtStruct->ScheduleLoadInstance("c2ebc17b-6b3548db-5e5da170-b8ecab71-ea03add3");  // 0178023P
         //rtStruct->ScheduleLoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9");  // IBA
         //rtStruct->ScheduleLoadInstance("17cd032b-ad92a438-ca05f06a-f9e96668-7e3e9e20");  // 0522c0001 TCIA
@@ -226,12 +225,12 @@
         widget->AddLayer(new VolumeImageSource(*pet));
         widget->AddLayer(new DicomStructureSetRendererFactory(*rtStruct));
         
-        context.AddInteractor(new Interactor(*pet, *widget, projection, 1));
-        //context.AddInteractor(new VolumeImageInteractor(*ct, *widget, projection));
+        context_->AddInteractor(new Interactor(*pet, *widget, projection, 1));
+        //context_->AddInteractor(new VolumeImageInteractor(*ct, *widget, projection));
 
-        context.AddSlicedVolume(ct.release());
-        context.AddSlicedVolume(pet.release());
-        context.AddVolumeLoader(rtStruct.release());
+        context_->AddSlicedVolume(ct.release());
+        context_->AddSlicedVolume(pet.release());
+        context_->AddVolumeLoader(rtStruct.release());
 
         {
           RenderStyle s;
@@ -263,7 +262,7 @@
         statusBar.SetMessage("Use the keys \"c\" to draw circles");
 
         widget->SetTransmitMouseOver(true);
-        context.SetCentralWidget(widget.release());
+        context_->SetCentralWidget(widget.release());
       }
     };
   }
--- a/Applications/Samples/TestPatternApplication.h	Fri Jun 15 16:36:29 2018 +0200
+++ b/Applications/Samples/TestPatternApplication.h	Tue Jun 19 16:02:41 2018 +0200
@@ -34,7 +34,7 @@
     class TestPatternApplication : public SampleApplicationBase
     {
     public:
-      virtual void DeclareCommandLineOptions(boost::program_options::options_description& options)
+      virtual void DeclareStartupOptions(boost::program_options::options_description& options)
       {
         boost::program_options::options_description generic("Sample options");
         generic.add_options()
@@ -44,8 +44,7 @@
         options.add(generic);    
       }
 
-      virtual void Initialize(BasicApplicationContext& context,
-                              IStatusBar& statusBar,
+      virtual void Initialize(IStatusBar& statusBar,
                               const boost::program_options::variables_map& parameters)
       {
         using namespace OrthancStone;
@@ -56,8 +55,8 @@
         layout->AddWidget(new TestCairoWidget(parameters["animate"].as<bool>()));
         layout->AddWidget(new TestWorldSceneWidget(parameters["animate"].as<bool>()));
 
-        context.SetCentralWidget(layout.release());
-        context.SetUpdateDelay(25);  // If animation, update the content each 25ms
+        context_->SetCentralWidget(layout.release());
+        context_->SetUpdateDelay(25);  // If animation, update the content each 25ms
       }
     };
   }
--- a/Applications/Sdl/BasicSdlApplication.cpp	Fri Jun 15 16:36:29 2018 +0200
+++ b/Applications/Sdl/BasicSdlApplication.cpp	Tue Jun 19 16:02:41 2018 +0200
@@ -78,29 +78,6 @@
     options.add(sdl);
   }
 
-//  void BasicSdlApplication::DeclareCommandLineOptions(boost::program_options::options_description &options) {
-//    boost::program_options::options_description app("Application specifi options");
-
-//    for (IBasicApplication::StartupOptions::const_iterator it = startupOptions_.begin(); it != startupOptions_.end(); it++) {
-//      switch (it->type) {
-//      case IBasicApplication::StartupOptionValue::boolean:
-//        app.add_options()
-//            (it->name.c_str(), boost::program_options::value<int>()->default_value(std::stoi(it->defaultValue)), it->helpText.c_str());
-//        break;
-//      case IBasicApplication::StartupOptionValue::integer:
-//        app.add_options()
-//            (it->name.c_str(), boost::program_options::value<bool>()->default_value(it->defaultValue == "true"), it->helpText.c_str());
-//        break;
-//      case IBasicApplication::StartupOptionValue::string:
-//        app.add_options()
-//            (it->name.c_str(), boost::program_options::value<std::string>()->default_value(it->defaultValue), it->helpText.c_str());
-//        break;
-//      default:
-//        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-//      }
-//    }
-//    options.add(app);
-//  }
 
   int BasicSdlApplication::ExecuteWithSdl(BasicSdlApplication& application,
                                         int argc,
--- a/Applications/Sdl/BasicSdlApplication.h	Fri Jun 15 16:36:29 2018 +0200
+++ b/Applications/Sdl/BasicSdlApplication.h	Tue Jun 19 16:02:41 2018 +0200
@@ -44,6 +44,8 @@
     static int ExecuteWithSdl(BasicSdlApplication& application,
                               int argc,
                               char* argv[]);
+
+    virtual void Finalize() {}
   };
 
 }
--- a/Framework/Widgets/EmptyWidget.cpp	Fri Jun 15 16:36:29 2018 +0200
+++ b/Framework/Widgets/EmptyWidget.cpp	Tue Jun 19 16:02:41 2018 +0200
@@ -13,7 +13,7 @@
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Affero General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  **/
@@ -26,19 +26,16 @@
 
 namespace OrthancStone
 {
-  namespace Samples
+  bool EmptyWidget::Render(Orthanc::ImageAccessor& surface)
   {
-    bool EmptyWidget::Render(Orthanc::ImageAccessor& surface)
-    {
-      // Note: This call is slow
-      Orthanc::ImageProcessing::Set(surface, red_, green_, blue_, 255);
-      return true;
-    }
+    // Note: This call is slow
+    Orthanc::ImageProcessing::Set(surface, red_, green_, blue_, 255);
+    return true;
+  }
 
-  
-    void EmptyWidget::UpdateContent()
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-    }
+
+  void EmptyWidget::UpdateContent()
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
   }
 }
--- a/Framework/Widgets/EmptyWidget.h	Fri Jun 15 16:36:29 2018 +0200
+++ b/Framework/Widgets/EmptyWidget.h	Tue Jun 19 16:02:41 2018 +0200
@@ -13,7 +13,7 @@
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Affero General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  **/
@@ -25,93 +25,90 @@
 
 namespace OrthancStone
 {
-  namespace Samples
-  {
-    /**
+  /**
      * This is a test widget that simply fills its surface with an
      * uniform color.
      **/
-    class EmptyWidget : public IWidget
-    {
-    private:
-      uint8_t  red_;
-      uint8_t  green_;
-      uint8_t  blue_;
+  class EmptyWidget : public IWidget
+  {
+  private:
+    uint8_t  red_;
+    uint8_t  green_;
+    uint8_t  blue_;
 
-    public:
-      EmptyWidget(uint8_t red,
-                  uint8_t green,
-                  uint8_t blue) :
-        red_(red),
-        green_(green),
-        blue_(blue)
-      {
-      }
+  public:
+    EmptyWidget(uint8_t red,
+                uint8_t green,
+                uint8_t blue) :
+      red_(red),
+      green_(green),
+      blue_(blue)
+    {
+    }
+
+    virtual void SetDefaultView()
+    {
+    }
 
-      virtual void SetDefaultView()
-      {
-      }
-  
-      virtual void SetParent(OrthancStone::IWidget& widget)
-      {
-      }
-    
-      virtual void SetViewport(IViewport& viewport)
-      {
-      }
+    virtual void SetParent(IWidget& widget)
+    {
+    }
+
+    virtual void SetViewport(IViewport& viewport)
+    {
+    }
 
-      virtual void NotifyChange()
-      {
-      }
+    virtual void NotifyChange()
+    {
+    }
 
-      virtual void SetStatusBar(IStatusBar& statusBar)
-      {
-      }
+    virtual void SetStatusBar(IStatusBar& statusBar)
+    {
+    }
+
+    virtual void SetSize(unsigned int width,
+                         unsigned int height)
+    {
+    }
 
-      virtual void SetSize(unsigned int width, 
-                           unsigned int height)
-      {
-      }
- 
-      virtual bool Render(Orthanc::ImageAccessor& surface);
+    virtual bool Render(Orthanc::ImageAccessor& surface);
 
-      virtual IMouseTracker* CreateMouseTracker(MouseButton button,
-                                                int x,
-                                                int y,
-                                                KeyboardModifiers modifiers)
-      {
-        return NULL;
-      }
+    virtual IMouseTracker* CreateMouseTracker(MouseButton button,
+                                              int x,
+                                              int y,
+                                              KeyboardModifiers modifiers)
+    {
+      return NULL;
+    }
 
-      virtual void RenderMouseOver(Orthanc::ImageAccessor& target,
-                                   int x,
-                                   int y)
-      {
-      }
+    virtual void RenderMouseOver(Orthanc::ImageAccessor& target,
+                                 int x,
+                                 int y)
+    {
+    }
 
-      virtual void MouseWheel(MouseWheelDirection direction,
-                              int x,
-                              int y,
-                              KeyboardModifiers modifiers)
-      {
-      }
+    virtual void MouseWheel(MouseWheelDirection direction,
+                            int x,
+                            int y,
+                            KeyboardModifiers modifiers)
+    {
+    }
 
-      virtual void KeyPressed(char key,
-                              KeyboardModifiers modifiers)
-      {
-      }
+    virtual void KeyPressed(char key,
+                            KeyboardModifiers modifiers)
+    {
+    }
 
-      virtual bool HasUpdateContent() const
-      {
-        return false;
-      }
+    virtual bool HasUpdateContent() const
+    {
+      return false;
+    }
 
-      virtual void UpdateContent();
+    virtual void UpdateContent();
 
-      virtual bool HasRenderMouseOver()
-      {
-        return false;
-      }
-    };
-  }
+    virtual bool HasRenderMouseOver()
+    {
+      return false;
+    }
+  };
 }
--- a/Platforms/Generic/CMakeLists.txt	Fri Jun 15 16:36:29 2018 +0200
+++ b/Platforms/Generic/CMakeLists.txt	Tue Jun 19 16:02:41 2018 +0200
@@ -68,6 +68,7 @@
 #BuildSample(OrthancStoneBasicPetCtFusion 5)
 #BuildSample(OrthancStoneSynchronizedSeries 6)
 #BuildSample(OrthancStoneLayoutPetCtFusion 7)
+BuildSample(OrthancStoneSimpleViewer SimpleViewerApplication.h 8)
 
 
 #####################################################################