changeset 1308:adf234ecaa00 broker

Merge
author Benjamin Golinvaux <bgo@osimis.io>
date Wed, 04 Mar 2020 10:21:54 +0100
parents 8a28a9bf8876 (diff) d6d56df61715 (current diff)
children 1f877e0846fe
files Framework/Deprecated/Loaders/OrthancSeriesVolumeProgressiveLoader.h Framework/Scene2DViewport/LayerHolder.cpp Framework/Scene2DViewport/ViewportController.h Framework/Viewport/SdlViewport.h Framework/Viewport/WebAssemblyViewport.cpp Resources/CMake/OrthancStoneConfiguration.cmake
diffstat 52 files changed, 706 insertions(+), 509 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Deprecated/Loaders/OrthancMultiframeVolumeLoader.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Deprecated/Loaders/OrthancMultiframeVolumeLoader.h	Wed Mar 04 10:21:54 2020 +0100
@@ -23,6 +23,7 @@
 
 #include "LoaderStateMachine.h"
 #include "../../Volumes/DicomVolumeImage.h"
+#include "../Volumes/IGeometryProvider.h"
 
 #include <boost/shared_ptr.hpp>
 
@@ -30,7 +31,8 @@
 {
   class OrthancMultiframeVolumeLoader :
     public LoaderStateMachine,
-    public OrthancStone::IObservable
+    public OrthancStone::IObservable,
+    public IGeometryProvider
   {
   private:
     class LoadRTDoseGeometry;
--- a/Framework/Deprecated/Loaders/OrthancSeriesVolumeProgressiveLoader.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Deprecated/Loaders/OrthancSeriesVolumeProgressiveLoader.h	Wed Mar 04 10:21:54 2020 +0100
@@ -33,6 +33,9 @@
 #include "../../Volumes/DicomVolumeImage.h"
 #include "../../Volumes/IVolumeSlicer.h"
 
+#include "../Volumes/IGeometryProvider.h"
+
+
 #include <boost/shared_ptr.hpp>
 
 namespace Deprecated
@@ -44,7 +47,8 @@
   class OrthancSeriesVolumeProgressiveLoader : 
     public OrthancStone::ObserverBase<OrthancSeriesVolumeProgressiveLoader>,
     public OrthancStone::IObservable,
-    public OrthancStone::IVolumeSlicer
+    public OrthancStone::IVolumeSlicer,
+    public IGeometryProvider
   {
   private:
     static const unsigned int LOW_QUALITY = 0;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Volumes/IGeometryProvider.h	Wed Mar 04 10:21:54 2020 +0100
@@ -0,0 +1,35 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 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 "../../Volumes/VolumeImageGeometry.h"
+
+namespace Deprecated
+{
+  class IGeometryProvider
+  {
+  public:
+    virtual ~IGeometryProvider() {}
+    virtual bool HasGeometry() const = 0;
+    virtual const OrthancStone::VolumeImageGeometry& GetImageGeometry() const = 0;
+  };
+}
--- a/Framework/Loaders/LoadedDicomResources.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Loaders/LoadedDicomResources.h	Wed Mar 04 10:21:54 2020 +0100
@@ -26,6 +26,10 @@
 
 namespace OrthancStone
 {
+  /**
+    Stores an indexed collection of DicomMap objects. The index is a 
+    user-specified DicomTag.
+  */
   class LoadedDicomResources : public boost::noncopyable
   {
   private:
--- a/Framework/Radiography/RadiographyTextLayer.cpp	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Radiography/RadiographyTextLayer.cpp	Wed Mar 04 10:21:54 2020 +0100
@@ -22,7 +22,7 @@
 
 #include "Core/OrthancException.h"
 #include "RadiographyScene.h"
-#include "Framework/Toolbox/TextRenderer.h"
+#include "../Toolbox/TextRenderer.h"
 
 namespace OrthancStone
 {
--- a/Framework/Scene2D/Internals/FixedPointAligner.cpp	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2D/Internals/FixedPointAligner.cpp	Wed Mar 04 10:21:54 2020 +0100
@@ -25,26 +25,28 @@
 {
   namespace Internals
   {
-    FixedPointAligner::FixedPointAligner(boost::weak_ptr<ViewportController> controllerW,
+    FixedPointAligner::FixedPointAligner(IViewport& viewport,
                                          const ScenePoint2D& p) 
-      : controllerW_(controllerW)
+      : viewport_(viewport)
       , canvas_(p)
     {
-      boost::shared_ptr<ViewportController> controller = controllerW_.lock();
-      pivot_ = canvas_.Apply(controller->GetCanvasToSceneTransform());
+      std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+      pivot_ = canvas_.Apply(lock->GetController().GetCanvasToSceneTransform());
     }
 
     
     void FixedPointAligner::Apply()
     {
-      boost::shared_ptr<ViewportController> controller = controllerW_.lock();
-      ScenePoint2D p = canvas_.Apply(controller->GetCanvasToSceneTransform());
+      std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+      ScenePoint2D p = canvas_.Apply(
+        lock->GetController().GetCanvasToSceneTransform());
 
-      controller->SetSceneToCanvasTransform(
+      lock->GetController().SetSceneToCanvasTransform(
         AffineTransform2D::Combine(
-          controller->GetSceneToCanvasTransform(),
+          lock->GetController().GetSceneToCanvasTransform(),
           AffineTransform2D::CreateOffset(p.GetX() - pivot_.GetX(),
                                           p.GetY() - pivot_.GetY())));
+      lock->Invalidate();
     }
   }
 }
--- a/Framework/Scene2D/Internals/FixedPointAligner.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2D/Internals/FixedPointAligner.h	Wed Mar 04 10:21:54 2020 +0100
@@ -22,6 +22,9 @@
 
 #include "../../Scene2DViewport/PredeclaredTypes.h"
 #include "../../Scene2D/ScenePoint2D.h"
+#include "../../Viewport/IViewport.h"
+
+#include <boost/weak_ptr.hpp>
 
 namespace OrthancStone
 {
@@ -32,12 +35,12 @@
     class FixedPointAligner : public boost::noncopyable
     {
     private:
-      boost::weak_ptr<ViewportController> controllerW_;
+      IViewport& viewport_;
       ScenePoint2D           pivot_;
       ScenePoint2D           canvas_;
 
     public:
-      FixedPointAligner(boost::weak_ptr<ViewportController> controllerW,
+      FixedPointAligner(IViewport& viewport,
                         const ScenePoint2D& p);
 
       void Apply();
--- a/Framework/Scene2D/PanSceneTracker.cpp	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2D/PanSceneTracker.cpp	Wed Mar 04 10:21:54 2020 +0100
@@ -20,16 +20,23 @@
 
 
 #include "PanSceneTracker.h"
+#include "../Viewport/IViewport.h"
 #include "../Scene2DViewport/ViewportController.h"
 
+#include <memory>
+
 namespace OrthancStone
 {
-  PanSceneTracker::PanSceneTracker(boost::weak_ptr<ViewportController> controllerW,
+  PanSceneTracker::PanSceneTracker(IViewport& viewport,
                                    const PointerEvent& event)
-    : OneGesturePointerTracker(controllerW)
-    , originalSceneToCanvas_(GetController()->GetSceneToCanvasTransform())
-    , originalCanvasToScene_(GetController()->GetCanvasToSceneTransform())
+    : OneGesturePointerTracker(viewport)
   {
+    
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+
+    originalSceneToCanvas_ = lock->GetController().GetSceneToCanvasTransform();
+    originalCanvasToScene_ = lock->GetController().GetCanvasToSceneTransform();
+
     pivot_ = event.GetMainPosition().Apply(originalCanvasToScene_);
   }
 
@@ -38,25 +45,19 @@
   {
     ScenePoint2D p = event.GetMainPosition().Apply(originalCanvasToScene_);
 
-      // The controller is a weak pointer. It could be deleted when the
-      // tracker is still alive (for instance, because of a lost WebGL
-      // context)
-      if(GetController().get() != NULL)
-      {
-      GetController()->SetSceneToCanvasTransform(
-        AffineTransform2D::Combine(
-          originalSceneToCanvas_,
-          AffineTransform2D::CreateOffset(p.GetX() - pivot_.GetX(),
-                                          p.GetY() - pivot_.GetY())));
-      }
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+
+    lock->GetController().SetSceneToCanvasTransform(
+      AffineTransform2D::Combine(
+        originalSceneToCanvas_,
+        AffineTransform2D::CreateOffset(p.GetX() - pivot_.GetX(),
+                                        p.GetY() - pivot_.GetY())));
+    lock->Invalidate();
   }
 
   void PanSceneTracker::Cancel()
   {
-      if(GetController().get() != NULL)
-      {
-        GetController()->SetSceneToCanvasTransform(originalSceneToCanvas_);
-      }
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+    lock->GetController().SetSceneToCanvasTransform(originalSceneToCanvas_);
   }
-
 }
--- a/Framework/Scene2D/PanSceneTracker.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2D/PanSceneTracker.h	Wed Mar 04 10:21:54 2020 +0100
@@ -28,14 +28,13 @@
   class PanSceneTracker : public OneGesturePointerTracker
   {
   public:
-    PanSceneTracker(boost::weak_ptr<ViewportController> controllerW,
+    PanSceneTracker(IViewport& viewport,
                     const PointerEvent& event);
 
     virtual void PointerMove(const PointerEvent& event) ORTHANC_OVERRIDE;
     virtual void Cancel() ORTHANC_OVERRIDE;
 
   private:
-    boost::weak_ptr<ViewportController> controllerW_;
     ScenePoint2D           pivot_;
     AffineTransform2D      originalSceneToCanvas_;
     AffineTransform2D      originalCanvasToScene_;
--- a/Framework/Scene2D/RotateSceneTracker.cpp	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2D/RotateSceneTracker.cpp	Wed Mar 04 10:21:54 2020 +0100
@@ -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/>.
  **/
@@ -23,23 +23,25 @@
 
 namespace OrthancStone
 {
-  RotateSceneTracker::RotateSceneTracker(boost::weak_ptr<ViewportController> controllerW,
+  RotateSceneTracker::RotateSceneTracker(IViewport& viewport,
                                          const PointerEvent& event)
-    : OneGesturePointerTracker(controllerW)
+    : OneGesturePointerTracker(viewport)
     , click_(event.GetMainPosition())
-    , aligner_(controllerW, click_)
+    , aligner_(viewport, click_)
     , isFirst_(true)
-    , originalSceneToCanvas_(GetController()->GetSceneToCanvasTransform())
   {
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+    originalSceneToCanvas_ = lock->GetController().GetSceneToCanvasTransform();
+
   }
-  
+
   void RotateSceneTracker::PointerMove(const PointerEvent& event)
   {
     ScenePoint2D p = event.GetMainPosition();
     double dx = p.GetX() - click_.GetX();
     double dy = p.GetY() - click_.GetY();
 
-    if (std::abs(dx) > 5.0 || 
+    if (std::abs(dx) > 5.0 ||
         std::abs(dy) > 5.0)
     {
       double a = atan2(dy, dx);
@@ -50,28 +52,22 @@
         isFirst_ = false;
       }
 
-      // The controller is a weak pointer. It could be deleted when the
-      // tracker is still alive (for instance, because of a lost WebGL
-      // context that triggers a recreation of the viewport)
-      if(GetController().get() != NULL)
-      {
-        GetController()->SetSceneToCanvasTransform(
-          AffineTransform2D::Combine(
-            AffineTransform2D::CreateRotation(a - referenceAngle_),
-            originalSceneToCanvas_));
-        
-        aligner_.Apply();
-      }
+      std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+
+      lock->GetController().SetSceneToCanvasTransform(
+        AffineTransform2D::Combine(
+          AffineTransform2D::CreateRotation(a - referenceAngle_),
+          originalSceneToCanvas_));
+      aligner_.Apply();
+      lock->Invalidate();
     }
   }
 
   void RotateSceneTracker::Cancel()
   {
-      // See remark above
-      if(GetController().get() != NULL)
-      {
-        GetController()->SetSceneToCanvasTransform(originalSceneToCanvas_);
-      }
+    // See remark above
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+    lock->GetController().SetSceneToCanvasTransform(originalSceneToCanvas_);
+    lock->Invalidate();
   }
-
 }
--- a/Framework/Scene2D/RotateSceneTracker.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2D/RotateSceneTracker.h	Wed Mar 04 10:21:54 2020 +0100
@@ -30,7 +30,7 @@
   class RotateSceneTracker : public OneGesturePointerTracker
   {
   public:
-    RotateSceneTracker(boost::weak_ptr<ViewportController> controllerW,
+    RotateSceneTracker(IViewport& viewport,
                        const PointerEvent& event);
 
     virtual void PointerMove(const PointerEvent& event) ORTHANC_OVERRIDE;
--- a/Framework/Scene2D/ZoomSceneTracker.cpp	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2D/ZoomSceneTracker.cpp	Wed Mar 04 10:21:54 2020 +0100
@@ -22,19 +22,19 @@
 #include "ZoomSceneTracker.h"
 #include "../Scene2DViewport/ViewportController.h"
 
-using boost::weak_ptr;
-using boost::shared_ptr;
-
 namespace OrthancStone
 {
-  ZoomSceneTracker::ZoomSceneTracker(weak_ptr<ViewportController> controllerW,
+  ZoomSceneTracker::ZoomSceneTracker(IViewport& viewport,
                                      const PointerEvent& event,
                                      unsigned int canvasHeight)
-    : OneGesturePointerTracker(controllerW)
+    : OneGesturePointerTracker(viewport)
     , clickY_(event.GetMainPosition().GetY())
-    , aligner_(controllerW, event.GetMainPosition())
-    , originalSceneToCanvas_(GetController()->GetSceneToCanvasTransform())
+    , aligner_(viewport, event.GetMainPosition())
   {
+    
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+    originalSceneToCanvas_ = lock->GetController().GetSceneToCanvasTransform();
+
     if (canvasHeight <= 3)
     {
       active_ = false;
@@ -76,26 +76,20 @@
 
       double zoom = pow(2.0, z);
 
-      // The controller is a weak pointer. It could be deleted when the
-      // tracker is still alive (for instance, because of a lost WebGL
-      // context)
-      if(GetController().get() != NULL)
-      {
-        GetController()->SetSceneToCanvasTransform(
-          AffineTransform2D::Combine(
-            AffineTransform2D::CreateScaling(zoom, zoom),
-            originalSceneToCanvas_));
-
-        aligner_.Apply();
-      }
+      std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+      lock->GetController().SetSceneToCanvasTransform(
+        AffineTransform2D::Combine(
+          AffineTransform2D::CreateScaling(zoom, zoom),
+          originalSceneToCanvas_));
+      aligner_.Apply();
+      lock->Invalidate();
     }
   }
 
   void ZoomSceneTracker::Cancel()
   {
-      if(GetController().get() != NULL)
-      {
-        GetController()->SetSceneToCanvasTransform(originalSceneToCanvas_);
-      }
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+    lock->GetController().SetSceneToCanvasTransform(originalSceneToCanvas_);
+    lock->Invalidate();
   }
 }
--- a/Framework/Scene2D/ZoomSceneTracker.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2D/ZoomSceneTracker.h	Wed Mar 04 10:21:54 2020 +0100
@@ -23,14 +23,17 @@
 
 
 #include "../Scene2DViewport/OneGesturePointerTracker.h"
+#include "../Viewport/IViewport.h"
 #include "Internals/FixedPointAligner.h"
 
+#include <boost/weak_ptr.hpp>
+
 namespace OrthancStone
 {
   class ZoomSceneTracker : public OneGesturePointerTracker
   {
   public:
-    ZoomSceneTracker(boost::weak_ptr<ViewportController> controllerW,
+    ZoomSceneTracker(IViewport& viewport,
                      const PointerEvent& event,
                      unsigned int canvasHeight);
 
--- a/Framework/Scene2DViewport/AngleMeasureTool.cpp	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/AngleMeasureTool.cpp	Wed Mar 04 10:21:54 2020 +0100
@@ -42,12 +42,12 @@
   // the params in the LayerHolder ctor specify the number of polyline and text
   // layers
   AngleMeasureTool::AngleMeasureTool(
-    boost::weak_ptr<ViewportController> controllerW)
-    : MeasureTool(controllerW)
+    IViewport& viewport)
+    : MeasureTool(viewport)
 #if ORTHANC_STONE_ENABLE_OUTLINED_TEXT == 1
-    , layerHolder_(boost::make_shared<LayerHolder>(controllerW,1,5))
+    , layerHolder_(boost::make_shared<LayerHolder>(viewport,1,5))
 #else
-    , layerHolder_(boost::make_shared<LayerHolder>(controllerW, 1, 1))
+    , layerHolder_(boost::make_shared<LayerHolder>(viewport, 1, 1))
 #endif
     , angleHighlightArea_(AngleHighlightArea_None)
   {
@@ -108,7 +108,9 @@
   
   void AngleMeasureTool::SetMemento(boost::shared_ptr<MeasureToolMemento> mementoBase)
   {
-    boost::shared_ptr<AngleMeasureToolMemento> memento = boost::dynamic_pointer_cast<AngleMeasureToolMemento>(mementoBase);
+    boost::shared_ptr<AngleMeasureToolMemento> memento = 
+      boost::dynamic_pointer_cast<AngleMeasureToolMemento>(mementoBase);
+
     ORTHANC_ASSERT(memento.get() != NULL, "Internal error: wrong (or bad) memento");
     center_   = memento->center_;
     side1End_ = memento->side1End_;
@@ -119,7 +121,8 @@
   std::string AngleMeasureTool::GetDescription()
   {
     std::stringstream ss;
-    ss << "AngleMeasureTool. Center = " << center_ << " Side1End = " << side1End_ << " Side2End = " << side2End_;
+    ss << "AngleMeasureTool. Center = " << center_ << " Side1End = " 
+       << side1End_ << " Side2End = " << side2End_;
     return ss.str();
   }
 
@@ -131,36 +134,51 @@
 
   AngleMeasureTool::AngleHighlightArea AngleMeasureTool::AngleHitTest(ScenePoint2D p) const
   {
-    const double pixelToScene = GetController()->GetScene().GetCanvasToSceneTransform().ComputeZoom();
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+    ViewportController& controller = lock->GetController();
+    Scene2D& scene = controller.GetScene();
 
-    const double SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD = pixelToScene * HIT_TEST_MAX_DISTANCE_CANVAS_COORD * pixelToScene * HIT_TEST_MAX_DISTANCE_CANVAS_COORD;
+    const double pixelToScene = scene.GetCanvasToSceneTransform().ComputeZoom();
+
+    const double SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD = 
+      pixelToScene * HIT_TEST_MAX_DISTANCE_CANVAS_COORD * 
+      pixelToScene * HIT_TEST_MAX_DISTANCE_CANVAS_COORD;
 
     {
-      const double sqDistanceFromSide1End = ScenePoint2D::SquaredDistancePtPt(p, side1End_);
+      const double sqDistanceFromSide1End = 
+        ScenePoint2D::SquaredDistancePtPt(p, side1End_);
+
       if (sqDistanceFromSide1End <= SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD)
         return AngleHighlightArea_Side1End;
     }
 
     {
-      const double sqDistanceFromSide2End = ScenePoint2D::SquaredDistancePtPt(p, side2End_);
+      const double sqDistanceFromSide2End = 
+        ScenePoint2D::SquaredDistancePtPt(p, side2End_);
+
       if (sqDistanceFromSide2End <= SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD)
         return AngleHighlightArea_Side2End;
     }
 
     {
-      const double sqDistanceFromCenter = ScenePoint2D::SquaredDistancePtPt(p, center_);
+      const double sqDistanceFromCenter = 
+        ScenePoint2D::SquaredDistancePtPt(p, center_);
       if (sqDistanceFromCenter <= SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD)
         return AngleHighlightArea_Center;
     }
 
     {
-      const double sqDistanceFromSide1 = ScenePoint2D::SquaredDistancePtSegment(center_, side1End_, p);
+      const double sqDistanceFromSide1 = 
+        ScenePoint2D::SquaredDistancePtSegment(center_, side1End_, p);
+
       if (sqDistanceFromSide1 <= SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD)
         return AngleHighlightArea_Side1;
     }
 
     {
-      const double sqDistanceFromSide2 = ScenePoint2D::SquaredDistancePtSegment(center_, side2End_, p);
+      const double sqDistanceFromSide2 = 
+        ScenePoint2D::SquaredDistancePtSegment(center_, side2End_, p);
+
       if (sqDistanceFromSide2 <= SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD)
         return AngleHighlightArea_Side2;
     }
@@ -168,7 +186,7 @@
     return AngleHighlightArea_None;
   }
 
-  bool AngleMeasureTool::HitTest(ScenePoint2D p) const
+  bool AngleMeasureTool::HitTest(ScenePoint2D p)
   {
     return AngleHitTest(p) != AngleHighlightArea_None;
   }
@@ -176,8 +194,12 @@
 
   boost::shared_ptr<IFlexiblePointerTracker> AngleMeasureTool::CreateEditionTracker(const PointerEvent& e)
   {
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+    ViewportController& controller = lock->GetController();
+    Scene2D& scene = controller.GetScene();
+
     ScenePoint2D scenePos = e.GetMainPosition().Apply(
-      GetController()->GetScene().GetCanvasToSceneTransform());
+      scene.GetCanvasToSceneTransform());
 
     if (!HitTest(scenePos))
       return boost::shared_ptr<IFlexiblePointerTracker>();
@@ -186,12 +208,12 @@
       new EditLineMeasureTracker(
         boost::shared_ptr<LineMeasureTool> measureTool;
         MessageBroker & broker,
-        boost::weak_ptr<ViewportController>          controllerW,
+        IViewport&          viewport,
         const PointerEvent & e);
     */
 
     boost::shared_ptr<EditAngleMeasureTracker> editAngleMeasureTracker(
-      new EditAngleMeasureTracker(shared_from_this(), GetController(), e));
+      new EditAngleMeasureTracker(shared_from_this(), viewport_, e));
     return editAngleMeasureTracker;
   }
 
@@ -205,7 +227,10 @@
   {
     if (IsSceneAlive())
     {
-      boost::shared_ptr<ViewportController> controller = GetController();
+      std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+      ViewportController& controller = lock->GetController();
+      Scene2D& scene = controller.GetScene();
+
       if (IsEnabled())
       {
         layerHolder_->CreateLayersIfNeeded();
@@ -217,8 +242,13 @@
           {
             polylineLayer->ClearAllChains();
 
-            const Color color(TOOL_ANGLE_LINES_COLOR_RED, TOOL_ANGLE_LINES_COLOR_GREEN, TOOL_ANGLE_LINES_COLOR_BLUE);
-            const Color highlightColor(TOOL_ANGLE_LINES_HL_COLOR_RED, TOOL_ANGLE_LINES_HL_COLOR_GREEN, TOOL_ANGLE_LINES_HL_COLOR_BLUE);
+            const Color color(TOOL_ANGLE_LINES_COLOR_RED, 
+                              TOOL_ANGLE_LINES_COLOR_GREEN, 
+                              TOOL_ANGLE_LINES_COLOR_BLUE);
+
+            const Color highlightColor(TOOL_ANGLE_LINES_HL_COLOR_RED, 
+                                       TOOL_ANGLE_LINES_HL_COLOR_GREEN, 
+                                       TOOL_ANGLE_LINES_HL_COLOR_BLUE);
 
             // sides
             {
@@ -227,19 +257,29 @@
                 chain.push_back(side1End_);
                 chain.push_back(center_);
 
-                if ((angleHighlightArea_ == AngleHighlightArea_Side1) || (angleHighlightArea_ == AngleHighlightArea_Side2))
+                if ((angleHighlightArea_ == AngleHighlightArea_Side1) ||
+                    (angleHighlightArea_ == AngleHighlightArea_Side2))
+                {
                   polylineLayer->AddChain(chain, false, highlightColor);
+                } 
                 else
+                {
                   polylineLayer->AddChain(chain, false, color);
+                }
               }
               {
                 PolylineSceneLayer::Chain chain;
                 chain.push_back(side2End_);
                 chain.push_back(center_);
-                if ((angleHighlightArea_ == AngleHighlightArea_Side1) || (angleHighlightArea_ == AngleHighlightArea_Side2))
+                if ((angleHighlightArea_ == AngleHighlightArea_Side1) ||
+                  (angleHighlightArea_ == AngleHighlightArea_Side2))
+                {
                   polylineLayer->AddChain(chain, false, highlightColor);
+                }
                 else
+                {
                   polylineLayer->AddChain(chain, false, color);
+                }
               }
             }
 
@@ -248,8 +288,8 @@
               {
                 PolylineSceneLayer::Chain chain;
                 //TODO: take DPI into account
-                AddSquare(chain, controller->GetScene(), side1End_, 
-                          GetController()->GetHandleSideLengthS());
+                AddSquare(chain, controller.GetScene(), side1End_, 
+                          controller.GetHandleSideLengthS());
               
                 if (angleHighlightArea_ == AngleHighlightArea_Side1End)
                   polylineLayer->AddChain(chain, true, highlightColor);
@@ -260,8 +300,8 @@
               {
                 PolylineSceneLayer::Chain chain;
                 //TODO: take DPI into account
-                AddSquare(chain, controller->GetScene(), side2End_, 
-                          GetController()->GetHandleSideLengthS());
+                AddSquare(chain, controller.GetScene(), side2End_, 
+                          controller.GetHandleSideLengthS());
 
                 if (angleHighlightArea_ == AngleHighlightArea_Side2End)
                   polylineLayer->AddChain(chain, true, highlightColor);
@@ -275,7 +315,7 @@
               PolylineSceneLayer::Chain chain;
 
               AddShortestArc(chain, side1End_, center_, side2End_,
-                             controller->GetAngleToolArcRadiusS());
+                             controller.GetAngleToolArcRadiusS());
               if (angleHighlightArea_ == AngleHighlightArea_Center)
                 polylineLayer->AddChain(chain, false, highlightColor);
               else
@@ -297,8 +337,8 @@
           double delta = NormalizeAngle(p2cAngle - p1cAngle);
           double theta = p1cAngle + delta / 2;
 
-          double ox = controller->GetAngleTopTextLabelDistanceS() * cos(theta);
-          double oy = controller->GetAngleTopTextLabelDistanceS() * sin(theta);
+          double ox = controller.GetAngleTopTextLabelDistanceS() * cos(theta);
+          double oy = controller.GetAngleTopTextLabelDistanceS() * sin(theta);
 
           double pointX = center_.GetX() + ox;
           double pointY = center_.GetY() + oy;
@@ -311,10 +351,10 @@
 
 #if ORTHANC_STONE_ENABLE_OUTLINED_TEXT == 1
           SetTextLayerOutlineProperties(
-            controller->GetScene(), layerHolder_, buf, ScenePoint2D(pointX, pointY), 0);
+            scene, layerHolder_, buf, ScenePoint2D(pointX, pointY), 0);
 #else
           SetTextLayerProperties(
-            controller->GetScene(), layerHolder_, buf, ScenePoint2D(pointX, pointY) , 0);
+            scene, layerHolder_, buf, ScenePoint2D(pointX, pointY) , 0);
 #endif
 
 #if 0
@@ -374,6 +414,7 @@
       {
         RemoveFromScene();
       }
+      lock->Invalidate();
     }
   }
 }
--- a/Framework/Scene2DViewport/AngleMeasureTool.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/AngleMeasureTool.h	Wed Mar 04 10:21:54 2020 +0100
@@ -40,7 +40,7 @@
   class AngleMeasureTool : public MeasureTool
   {
   public:
-    AngleMeasureTool(boost::weak_ptr<ViewportController> controllerW);
+    AngleMeasureTool(IViewport& viewport);
 
     ~AngleMeasureTool();
 
@@ -48,7 +48,7 @@
     void SetCenter(ScenePoint2D start);
     void SetSide2End(ScenePoint2D start);
 
-    virtual bool HitTest(ScenePoint2D p) const ORTHANC_OVERRIDE;
+    virtual bool HitTest(ScenePoint2D p) ORTHANC_OVERRIDE;
     virtual void Highlight(ScenePoint2D p) ORTHANC_OVERRIDE;
     virtual void ResetHighlightState() ORTHANC_OVERRIDE;
     virtual boost::shared_ptr<IFlexiblePointerTracker> CreateEditionTracker(const PointerEvent& e) ORTHANC_OVERRIDE;
--- a/Framework/Scene2DViewport/CreateAngleMeasureCommand.cpp	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/CreateAngleMeasureCommand.cpp	Wed Mar 04 10:21:54 2020 +0100
@@ -26,13 +26,17 @@
 namespace OrthancStone
 {
   CreateAngleMeasureCommand::CreateAngleMeasureCommand(
-    boost::weak_ptr<ViewportController> controllerW,
+    IViewport& viewport,
     ScenePoint2D           point)
-    : CreateMeasureCommand(controllerW)
+    : CreateMeasureCommand(viewport)
     , measureTool_(
-      boost::make_shared<AngleMeasureTool>(controllerW))
+      boost::make_shared<AngleMeasureTool>(viewport))
   {
-    GetController()->AddMeasureTool(measureTool_);
+    
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+    ViewportController& controller = lock->GetController();
+
+    controller.AddMeasureTool(measureTool_);
     measureTool_->SetSide1End(point);
     measureTool_->SetCenter(point);
     measureTool_->SetSide2End(point);
--- a/Framework/Scene2DViewport/CreateAngleMeasureCommand.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/CreateAngleMeasureCommand.h	Wed Mar 04 10:21:54 2020 +0100
@@ -28,7 +28,7 @@
   public:
     /** Ctor sets end of side 1*/
     CreateAngleMeasureCommand(
-      boost::weak_ptr<ViewportController> controllerW,
+      IViewport& viewport,
       ScenePoint2D           point);
 
     /** This method sets center*/
--- a/Framework/Scene2DViewport/CreateAngleMeasureTracker.cpp	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/CreateAngleMeasureTracker.cpp	Wed Mar 04 10:21:54 2020 +0100
@@ -26,22 +26,18 @@
 namespace OrthancStone
 {
   CreateAngleMeasureTracker::CreateAngleMeasureTracker(
-    boost::weak_ptr<ViewportController>          controllerW,
-    const PointerEvent&             e)
-    : CreateMeasureTracker(controllerW)
+    IViewport& viewport,
+    const PointerEvent& e)
+    : CreateMeasureTracker(viewport)
     , state_(CreatingSide1)
   {
     ScenePoint2D point = e.GetMainPosition();
-    
-    {
-      boost::shared_ptr<ViewportController> controller = controllerW.lock();
-      if (controller)
-      {
-        point = e.GetMainPosition().Apply(controller->GetScene().GetCanvasToSceneTransform());
-      }
+    {    
+      std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+      Scene2D& scene = lock->GetController().GetScene();
+      point = e.GetMainPosition().Apply(scene.GetCanvasToSceneTransform());
     }
-    
-    command_.reset(new CreateAngleMeasureCommand(controllerW, point));
+    command_.reset(new CreateAngleMeasureCommand(viewport, point));
   }
 
   CreateAngleMeasureTracker::~CreateAngleMeasureTracker()
@@ -57,11 +53,13 @@
         "PointerMove: active_ == false");
     }
 
-    boost::shared_ptr<ViewportController> controller = controllerW_.lock();
-    if (controller)
+    
     {
+      std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+      ViewportController& controller = lock->GetController();
+
       ScenePoint2D scenePos = event.GetMainPosition().Apply(
-        controller->GetScene().GetCanvasToSceneTransform());
+        controller.GetScene().GetCanvasToSceneTransform());
 
       switch (state_)
       {
@@ -78,6 +76,7 @@
       }
       //LOG(TRACE) << "scenePos.GetX() = " << scenePos.GetX() << "     " <<
       //  "scenePos.GetY() = " << scenePos.GetY();
+      lock->Invalidate();
     }
   }
 
--- a/Framework/Scene2DViewport/CreateAngleMeasureTracker.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/CreateAngleMeasureTracker.h	Wed Mar 04 10:21:54 2020 +0100
@@ -38,7 +38,7 @@
     must be supplied, too
     */
     CreateAngleMeasureTracker(
-      boost::weak_ptr<ViewportController>          controllerW,
+      IViewport&          viewport,
       const PointerEvent&             e);
 
     ~CreateAngleMeasureTracker();
--- a/Framework/Scene2DViewport/CreateLineMeasureCommand.cpp	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/CreateLineMeasureCommand.cpp	Wed Mar 04 10:21:54 2020 +0100
@@ -26,14 +26,18 @@
 namespace OrthancStone
 {
   CreateLineMeasureCommand::CreateLineMeasureCommand(
-    boost::weak_ptr<ViewportController> controllerW,
+    IViewport& viewport,
     ScenePoint2D           point)
-    : CreateMeasureCommand(controllerW)
+    : CreateMeasureCommand(viewport)
     , measureTool_(
-      boost::make_shared<LineMeasureTool>(controllerW))
+      boost::make_shared<LineMeasureTool>(viewport))
   {
-    GetController()->AddMeasureTool(measureTool_);
+    
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+    ViewportController& controller = lock->GetController();
+    controller.AddMeasureTool(measureTool_);
     measureTool_->Set(point, point);
+    lock->Invalidate();
   }
 
   void CreateLineMeasureCommand::SetEnd(ScenePoint2D scenePos)
--- a/Framework/Scene2DViewport/CreateLineMeasureCommand.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/CreateLineMeasureCommand.h	Wed Mar 04 10:21:54 2020 +0100
@@ -27,7 +27,7 @@
   {
   public:
     CreateLineMeasureCommand(
-      boost::weak_ptr<ViewportController> controllerW,
+      IViewport& viewport,
       ScenePoint2D           point);
 
     // the starting position is set in the ctor
--- a/Framework/Scene2DViewport/CreateLineMeasureTracker.cpp	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/CreateLineMeasureTracker.cpp	Wed Mar 04 10:21:54 2020 +0100
@@ -26,21 +26,17 @@
 namespace OrthancStone
 {
   CreateLineMeasureTracker::CreateLineMeasureTracker(
-    boost::weak_ptr<ViewportController>          controllerW,
+    IViewport&          viewport,
     const PointerEvent&             e)
-    : CreateMeasureTracker(controllerW)
+    : CreateMeasureTracker(viewport)
   {
     ScenePoint2D point = e.GetMainPosition();
-    
     {
-      boost::shared_ptr<ViewportController> controller = controllerW.lock();
-      if (controller)
-      {
-        point = e.GetMainPosition().Apply(controller->GetScene().GetCanvasToSceneTransform());
-      }
+      std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+      ViewportController& controller = lock->GetController();
+      point = e.GetMainPosition().Apply(controller.GetScene().GetCanvasToSceneTransform());
     }
-
-    command_.reset(new CreateLineMeasureCommand(controllerW, point));
+    command_.reset(new CreateLineMeasureCommand(viewport, point));
   }
 
   CreateLineMeasureTracker::~CreateLineMeasureTracker()
@@ -57,20 +53,19 @@
         "PointerMove: active_ == false");
     }
 
-    boost::shared_ptr<ViewportController> controller = controllerW_.lock();
-    if (controller)
-    {
-      ScenePoint2D scenePos = event.GetMainPosition().Apply(
-        controller->GetScene().GetCanvasToSceneTransform());
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+    ViewportController& controller = lock->GetController();
+
+    ScenePoint2D scenePos = event.GetMainPosition().Apply(
+      controller.GetScene().GetCanvasToSceneTransform());
       
-      //LOG(TRACE) << "scenePos.GetX() = " << scenePos.GetX() << "     " <<
-      //  "scenePos.GetY() = " << scenePos.GetY();
+    //LOG(TRACE) << "scenePos.GetX() = " << scenePos.GetX() << "     " <<
+    //  "scenePos.GetY() = " << scenePos.GetY();
       
-      CreateLineMeasureTracker* concreteThis =
-        dynamic_cast<CreateLineMeasureTracker*>(this);
-      assert(concreteThis != NULL);
-      GetCommand()->SetEnd(scenePos);
-    }
+    CreateLineMeasureTracker* concreteThis =
+      dynamic_cast<CreateLineMeasureTracker*>(this);
+    assert(concreteThis != NULL);
+    GetCommand()->SetEnd(scenePos);
   }
 
   void CreateLineMeasureTracker::PointerUp(const PointerEvent& e)
--- a/Framework/Scene2DViewport/CreateLineMeasureTracker.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/CreateLineMeasureTracker.h	Wed Mar 04 10:21:54 2020 +0100
@@ -38,7 +38,7 @@
     must be supplied, too
     */
     CreateLineMeasureTracker(
-      boost::weak_ptr<ViewportController>          controllerW,
+      IViewport&          viewport,
       const PointerEvent&             e);
 
     ~CreateLineMeasureTracker();
--- a/Framework/Scene2DViewport/EditAngleMeasureCommand.cpp	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/EditAngleMeasureCommand.cpp	Wed Mar 04 10:21:54 2020 +0100
@@ -24,8 +24,8 @@
 {
   EditAngleMeasureCommand::EditAngleMeasureCommand(
     boost::shared_ptr<MeasureTool>  measureTool,
-    boost::weak_ptr<ViewportController> controllerW)
-    : EditMeasureCommand(measureTool, controllerW)
+    IViewport& viewport)
+    : EditMeasureCommand(measureTool, viewport)
     , measureTool_(measureTool)
   {
   }
--- a/Framework/Scene2DViewport/EditAngleMeasureCommand.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/EditAngleMeasureCommand.h	Wed Mar 04 10:21:54 2020 +0100
@@ -29,7 +29,7 @@
     /** Ctor sets end of side 1*/
     EditAngleMeasureCommand(
       boost::shared_ptr<MeasureTool>  measureTool,
-      boost::weak_ptr<ViewportController> controllerW);
+      IViewport& viewport);
 
     /** This method sets center*/
     void SetCenter(ScenePoint2D scenePos);
--- a/Framework/Scene2DViewport/EditAngleMeasureTracker.cpp	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/EditAngleMeasureTracker.cpp	Wed Mar 04 10:21:54 2020 +0100
@@ -27,21 +27,18 @@
 {
   EditAngleMeasureTracker::EditAngleMeasureTracker(
     boost::shared_ptr<MeasureTool>  measureTool,
-    boost::weak_ptr<ViewportController> controllerW,
+    IViewport& viewport,
     const PointerEvent& e)
-    : EditMeasureTracker(controllerW, e)
+    : EditMeasureTracker(viewport, e)
   {
     ScenePoint2D scenePos = e.GetMainPosition();
-
-    boost::shared_ptr<ViewportController> controller = controllerW_.lock();
-    if (controller)
     {
-      scenePos = e.GetMainPosition().Apply(controller->GetScene().GetCanvasToSceneTransform());
+      std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+      ViewportController& controller = lock->GetController();
+      scenePos = e.GetMainPosition().Apply(controller.GetScene().GetCanvasToSceneTransform());
     }
-
     modifiedZone_ = dynamic_cast<AngleMeasureTool&>(*measureTool).AngleHitTest(scenePos);
-
-    command_.reset(new EditAngleMeasureCommand(measureTool, controllerW));
+    command_.reset(new EditAngleMeasureCommand(measureTool, viewport));
   }
 
   EditAngleMeasureTracker::~EditAngleMeasureTracker()
@@ -51,54 +48,54 @@
 
   void EditAngleMeasureTracker::PointerMove(const PointerEvent& e)
   {
-    boost::shared_ptr<ViewportController> controller = controllerW_.lock();
-    if (controller)
-    {
-      ScenePoint2D scenePos = e.GetMainPosition().Apply(
-        controller->GetScene().GetCanvasToSceneTransform());
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+    ViewportController& controller = lock->GetController();
+    Scene2D& scene = controller.GetScene();
+
+    ScenePoint2D scenePos = e.GetMainPosition().Apply(
+      scene.GetCanvasToSceneTransform());
 
-      ScenePoint2D delta = scenePos - GetOriginalClickPosition();
+    ScenePoint2D delta = scenePos - GetOriginalClickPosition();
+
+    boost::shared_ptr<AngleMeasureToolMemento> memento =
+      boost::dynamic_pointer_cast<AngleMeasureToolMemento>(command_->mementoOriginal_);
+
+    ORTHANC_ASSERT(memento.get() != NULL);
 
-      boost::shared_ptr<AngleMeasureToolMemento> memento =
-        boost::dynamic_pointer_cast<AngleMeasureToolMemento>(command_->mementoOriginal_);
-
-      ORTHANC_ASSERT(memento.get() != NULL);
-
-      switch (modifiedZone_)
+    switch (modifiedZone_)
+    {
+      case AngleMeasureTool::AngleHighlightArea_Center:
+      {
+        ScenePoint2D newCenter = memento->center_ + delta;
+        GetCommand()->SetCenter(newCenter);
+      }
+      break;
+      case AngleMeasureTool::AngleHighlightArea_Side1:
+      case AngleMeasureTool::AngleHighlightArea_Side2:
       {
-        case AngleMeasureTool::AngleHighlightArea_Center:
-        {
-          ScenePoint2D newCenter = memento->center_ + delta;
-          GetCommand()->SetCenter(newCenter);
-        }
+        ScenePoint2D newCenter = memento->center_ + delta;
+        ScenePoint2D newSide1End = memento->side1End_ + delta;
+        ScenePoint2D newSide2End = memento->side2End_ + delta;
+        GetCommand()->SetCenter(newCenter);
+        GetCommand()->SetSide1End(newSide1End);
+        GetCommand()->SetSide2End(newSide2End);
+      }
+      break;
+      case AngleMeasureTool::AngleHighlightArea_Side1End:
+      {
+        ScenePoint2D newSide1End = memento->side1End_ + delta;
+        GetCommand()->SetSide1End(newSide1End);
+      }
+      break;
+      case AngleMeasureTool::AngleHighlightArea_Side2End:
+      {
+        ScenePoint2D newSide2End = memento->side2End_ + delta;
+        GetCommand()->SetSide2End(newSide2End);
+      }
+      break;
+      default:
+        LOG(WARNING) << "Warning: please retry the measuring tool editing operation!";
         break;
-        case AngleMeasureTool::AngleHighlightArea_Side1:
-        case AngleMeasureTool::AngleHighlightArea_Side2:
-        {
-          ScenePoint2D newCenter = memento->center_ + delta;
-          ScenePoint2D newSide1End = memento->side1End_ + delta;
-          ScenePoint2D newSide2End = memento->side2End_ + delta;
-          GetCommand()->SetCenter(newCenter);
-          GetCommand()->SetSide1End(newSide1End);
-          GetCommand()->SetSide2End(newSide2End);
-        }
-        break;
-        case AngleMeasureTool::AngleHighlightArea_Side1End:
-        {
-          ScenePoint2D newSide1End = memento->side1End_ + delta;
-          GetCommand()->SetSide1End(newSide1End);
-        }
-        break;
-        case AngleMeasureTool::AngleHighlightArea_Side2End:
-        {
-          ScenePoint2D newSide2End = memento->side2End_ + delta;
-          GetCommand()->SetSide2End(newSide2End);
-        }
-        break;
-        default:
-          LOG(WARNING) << "Warning: please retry the measuring tool editing operation!";
-          break;
-      }
     }
   }
 
@@ -119,5 +116,4 @@
     ORTHANC_ASSERT(ret.get() != NULL, "Internal error in EditAngleMeasureTracker::GetCommand()");
     return ret;
   }
-
 }
--- a/Framework/Scene2DViewport/EditAngleMeasureTracker.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/EditAngleMeasureTracker.h	Wed Mar 04 10:21:54 2020 +0100
@@ -38,7 +38,7 @@
     */
     EditAngleMeasureTracker(
       boost::shared_ptr<MeasureTool>  measureTool,
-      boost::weak_ptr<ViewportController> controllerW,
+      IViewport& viewport,
       const PointerEvent& e);
 
     ~EditAngleMeasureTracker();
--- a/Framework/Scene2DViewport/EditLineMeasureCommand.cpp	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/EditLineMeasureCommand.cpp	Wed Mar 04 10:21:54 2020 +0100
@@ -24,8 +24,8 @@
 {
   EditLineMeasureCommand::EditLineMeasureCommand(
     boost::shared_ptr<MeasureTool>  measureTool,
-    boost::weak_ptr<ViewportController> controllerW)
-    : EditMeasureCommand(measureTool, controllerW)
+    IViewport& viewport)
+    : EditMeasureCommand(measureTool, viewport)
     , measureTool_(measureTool)
   {
   }
--- a/Framework/Scene2DViewport/EditLineMeasureCommand.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/EditLineMeasureCommand.h	Wed Mar 04 10:21:54 2020 +0100
@@ -28,7 +28,7 @@
   public:
     EditLineMeasureCommand(
       boost::shared_ptr<MeasureTool>  measureTool,
-      boost::weak_ptr<ViewportController> controllerW);
+      IViewport& viewport);
 
     void SetStart(ScenePoint2D scenePos);
     void SetEnd(ScenePoint2D scenePos);
--- a/Framework/Scene2DViewport/EditLineMeasureTracker.cpp	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/EditLineMeasureTracker.cpp	Wed Mar 04 10:21:54 2020 +0100
@@ -28,23 +28,18 @@
 {
   EditLineMeasureTracker::EditLineMeasureTracker(
     boost::shared_ptr<MeasureTool>  measureTool,
-    boost::weak_ptr<ViewportController> controllerW,
-    const PointerEvent& e) 
-    : EditMeasureTracker(controllerW, e)
+    IViewport& viewport,
+    const PointerEvent& e)
+    : EditMeasureTracker(viewport, e)
   {
     ScenePoint2D scenePos = e.GetMainPosition();
-
     {
-      boost::shared_ptr<ViewportController> controller = controllerW.lock();
-      if (controller)
-      {
-        scenePos = e.GetMainPosition().Apply(controller->GetScene().GetCanvasToSceneTransform());
-      }
+      std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+      Scene2D& scene = lock->GetController().GetScene();
+      scenePos = e.GetMainPosition().Apply(scene.GetCanvasToSceneTransform());
     }
-
     modifiedZone_ = dynamic_cast<LineMeasureTool&>(*measureTool).LineHitTest(scenePos);
-
-    command_.reset(new EditLineMeasureCommand(measureTool, controllerW));
+    command_.reset(new EditLineMeasureCommand(measureTool, viewport));
   }
 
   EditLineMeasureTracker::~EditLineMeasureTracker()
@@ -54,48 +49,48 @@
 
   void EditLineMeasureTracker::PointerMove(const PointerEvent& e)
   {
-    boost::shared_ptr<ViewportController> controller = controllerW_.lock();
-    if (controller)
-    {
-      ScenePoint2D scenePos = e.GetMainPosition().Apply(
-        controller->GetScene().GetCanvasToSceneTransform());
-      
-      ScenePoint2D delta = scenePos - GetOriginalClickPosition();
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+    ViewportController& controller = lock->GetController();
+    Scene2D& scene = controller.GetScene();
+
+    ScenePoint2D scenePos = e.GetMainPosition().Apply(
+      scene.GetCanvasToSceneTransform());
 
-      boost::shared_ptr<LineMeasureToolMemento> memento =
-        boost::dynamic_pointer_cast<LineMeasureToolMemento>(command_->mementoOriginal_);
+    ScenePoint2D delta = scenePos - GetOriginalClickPosition();
 
-      ORTHANC_ASSERT(memento.get() != NULL);
+    boost::shared_ptr<LineMeasureToolMemento> memento =
+      boost::dynamic_pointer_cast<LineMeasureToolMemento>(command_->mementoOriginal_);
+
+    ORTHANC_ASSERT(memento.get() != NULL);
 
-      switch (modifiedZone_)
-      {
-        case LineMeasureTool::LineHighlightArea_Start:
-        {
-          ScenePoint2D newStart = memento->start_ + delta;
-          GetCommand()->SetStart(newStart);
-        }
-        break;
-        case LineMeasureTool::LineHighlightArea_End:
-        {
-          ScenePoint2D newEnd = memento->end_ + delta;
-          GetCommand()->SetEnd(newEnd);
-        }
-        break;
-        case LineMeasureTool::LineHighlightArea_Segment:
-        {
-          ScenePoint2D newStart = memento->start_ + delta;
-          ScenePoint2D newEnd = memento->end_ + delta;
-          GetCommand()->SetStart(newStart);
-          GetCommand()->SetEnd(newEnd);
-        }
-        break;
-        default:
-          LOG(WARNING) << "Warning: please retry the measuring tool editing operation!";
-          break;
-      }
+    switch (modifiedZone_)
+    {
+    case LineMeasureTool::LineHighlightArea_Start:
+    {
+      ScenePoint2D newStart = memento->start_ + delta;
+      GetCommand()->SetStart(newStart);
+    }
+    break;
+    case LineMeasureTool::LineHighlightArea_End:
+    {
+      ScenePoint2D newEnd = memento->end_ + delta;
+      GetCommand()->SetEnd(newEnd);
+    }
+    break;
+    case LineMeasureTool::LineHighlightArea_Segment:
+    {
+      ScenePoint2D newStart = memento->start_ + delta;
+      ScenePoint2D newEnd = memento->end_ + delta;
+      GetCommand()->SetStart(newStart);
+      GetCommand()->SetEnd(newEnd);
+    }
+    break;
+    default:
+      LOG(WARNING) << "Warning: please retry the measuring tool editing operation!";
+      break;
     }
   }
-  
+
   void EditLineMeasureTracker::PointerUp(const PointerEvent& e)
   {
     alive_ = false;
--- a/Framework/Scene2DViewport/EditLineMeasureTracker.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/EditLineMeasureTracker.h	Wed Mar 04 10:21:54 2020 +0100
@@ -38,7 +38,7 @@
     */
     EditLineMeasureTracker(
       boost::shared_ptr<MeasureTool>  measureTool,
-      boost::weak_ptr<ViewportController> controllerW,
+      IViewport& viewport,
       const PointerEvent&                 e);
 
     ~EditLineMeasureTracker();
--- a/Framework/Scene2DViewport/LayerHolder.cpp	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/LayerHolder.cpp	Wed Mar 04 10:21:54 2020 +0100
@@ -22,20 +22,20 @@
 #include "../Scene2D/TextSceneLayer.h"
 #include "../Scene2D/PolylineSceneLayer.h"
 #include "../Scene2D/Scene2D.h"
-#include "../Scene2DViewport/ViewportController.h"
+#include "../Viewport/IViewport.h"
 #include "../StoneException.h"
 
 namespace OrthancStone
 {
   LayerHolder::LayerHolder(
-    boost::weak_ptr<ViewportController> controllerW,
-    int                    polylineLayerCount,
-    int                    textLayerCount,
-    int                    infoTextCount)
+    IViewport& viewport,
+    int        polylineLayerCount,
+    int        textLayerCount,
+    int        infoTextCount)
     : textLayerCount_(textLayerCount)
     , polylineLayerCount_(polylineLayerCount)
     , infoTextCount_(infoTextCount)
-    , controllerW_(controllerW)
+    , viewport_(viewport)
     , baseLayerIndex_(-1)
   {
 
@@ -43,28 +43,26 @@
 
   void LayerHolder::CreateLayers()
   {
-    boost::shared_ptr<ViewportController> controller = controllerW_.lock();
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+    ViewportController& controller = lock->GetController();
+    Scene2D& scene = controller.GetScene();
 
-    if (controller)
-    {
-      assert(baseLayerIndex_ == -1);
+    assert(baseLayerIndex_ == -1);
 
-      baseLayerIndex_ = controller->GetScene().GetMaxDepth() + 100;
+    baseLayerIndex_ = scene.GetMaxDepth() + 100;
 
-      for (int i = 0; i < polylineLayerCount_; ++i)
-      {
-        std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer());
-        controller->GetScene().SetLayer(baseLayerIndex_ + i, layer.release());
-      }
+    for (int i = 0; i < polylineLayerCount_; ++i)
+    {
+      std::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer());
+      scene.SetLayer(baseLayerIndex_ + i, layer.release());
+    }
 
-      for (int i = 0; i < textLayerCount_; ++i)
-      {
-        std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer());
-        controller->GetScene().SetLayer(
-          baseLayerIndex_ + polylineLayerCount_ + i,
-          layer.release());
-      }
+    for (int i = 0; i < textLayerCount_; ++i)
+    {
+      std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer());
+      scene.SetLayer(baseLayerIndex_ + polylineLayerCount_ + i, layer.release());
     }
+    lock->Invalidate();
   }
 
   void LayerHolder::CreateLayersIfNeeded()
@@ -86,65 +84,50 @@
   
   void LayerHolder::DeleteLayers()
   {
-    boost::shared_ptr<ViewportController> controller = controllerW_.lock();
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+    Scene2D& scene = lock->GetController().GetScene();
 
-    if (controller)
+    for (int i = 0; i < textLayerCount_ + polylineLayerCount_; ++i)
     {
-      for (int i = 0; i < textLayerCount_ + polylineLayerCount_; ++i)
-      {
-        ORTHANC_ASSERT(controller->GetScene().HasLayer(baseLayerIndex_ + i), "No layer");
-        controller->GetScene().DeleteLayer(baseLayerIndex_ + i);
-      }
-      baseLayerIndex_ = -1;
+      ORTHANC_ASSERT(scene.HasLayer(baseLayerIndex_ + i), "No layer");
+      scene.DeleteLayer(baseLayerIndex_ + i);
     }
+    baseLayerIndex_ = -1;
+    lock->Invalidate();
   }
   
   PolylineSceneLayer* LayerHolder::GetPolylineLayer(int index /*= 0*/)
   {
-    boost::shared_ptr<ViewportController> controller = controllerW_.lock();
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+    Scene2D& scene = lock->GetController().GetScene();
 
-    if (controller)
-    {
-      using namespace Orthanc;
-      ORTHANC_ASSERT(baseLayerIndex_ != -1);
-      ORTHANC_ASSERT(controller->GetScene().HasLayer(GetPolylineLayerIndex(index)));
-      ISceneLayer* layer =
-        &(controller->GetScene().GetLayer(GetPolylineLayerIndex(index)));
+    using namespace Orthanc;
+    ORTHANC_ASSERT(baseLayerIndex_ != -1);
+    ORTHANC_ASSERT(scene.HasLayer(GetPolylineLayerIndex(index)));
+    ISceneLayer* layer = &(scene.GetLayer(GetPolylineLayerIndex(index)));
       
-      PolylineSceneLayer* concreteLayer =
-        dynamic_cast<PolylineSceneLayer*>(layer);
+    PolylineSceneLayer* concreteLayer =
+      dynamic_cast<PolylineSceneLayer*>(layer);
       
-      ORTHANC_ASSERT(concreteLayer != NULL);
-      return concreteLayer;
-    }
-    else
-    {
-      return NULL; // TODO
-    }
+    ORTHANC_ASSERT(concreteLayer != NULL);
+    return concreteLayer;
   }
 
   TextSceneLayer* LayerHolder::GetTextLayer(int index /*= 0*/)
   {
-    boost::shared_ptr<ViewportController> controller = controllerW_.lock();
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+    Scene2D& scene = lock->GetController().GetScene();
 
-    if (controller)
-    {
-      using namespace Orthanc;
-      ORTHANC_ASSERT(baseLayerIndex_ != -1);
-      ORTHANC_ASSERT(controller->GetScene().HasLayer(GetTextLayerIndex(index)));
-      ISceneLayer* layer =
-        &(controller->GetScene().GetLayer(GetTextLayerIndex(index)));
+    using namespace Orthanc;
+    ORTHANC_ASSERT(baseLayerIndex_ != -1);
+    ORTHANC_ASSERT(scene.HasLayer(GetTextLayerIndex(index)));
+    ISceneLayer* layer = &(scene.GetLayer(GetTextLayerIndex(index)));
       
-      TextSceneLayer* concreteLayer =
-        dynamic_cast<TextSceneLayer*>(layer);
+    TextSceneLayer* concreteLayer =
+      dynamic_cast<TextSceneLayer*>(layer);
       
-      ORTHANC_ASSERT(concreteLayer != NULL);
-      return concreteLayer;
-    }
-    else
-    {
-      return NULL; // TODO
-    }
+    ORTHANC_ASSERT(concreteLayer != NULL);
+    return concreteLayer;
   }
 
   int LayerHolder::GetPolylineLayerIndex(int index /*= 0*/)
@@ -153,8 +136,7 @@
     ORTHANC_ASSERT(index < polylineLayerCount_);
     return baseLayerIndex_ + index;
   }
-
-
+  
   int LayerHolder::GetTextLayerIndex(int index /*= 0*/)
   {
     using namespace Orthanc;
--- a/Framework/Scene2DViewport/LayerHolder.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/LayerHolder.h	Wed Mar 04 10:21:54 2020 +0100
@@ -43,7 +43,7 @@
     performed at this time
     */
     LayerHolder(
-      boost::weak_ptr<ViewportController> controllerW,
+      IViewport& viewport,
       int polylineLayerCount, int textLayerCount, int infoTextCount = 0);
 
     /**
@@ -101,7 +101,7 @@
     int textLayerCount_;
     int polylineLayerCount_;
     int infoTextCount_;
-    boost::weak_ptr<ViewportController> controllerW_;
+    IViewport& viewport_;
     int baseLayerIndex_;
   };
 }
--- a/Framework/Scene2DViewport/LineMeasureTool.cpp	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/LineMeasureTool.cpp	Wed Mar 04 10:21:54 2020 +0100
@@ -32,12 +32,12 @@
 {
 
   LineMeasureTool::LineMeasureTool(
-    boost::weak_ptr<ViewportController> controllerW)
-    : MeasureTool(controllerW)
+    IViewport& viewport)
+    : MeasureTool(viewport)
 #if ORTHANC_STONE_ENABLE_OUTLINED_TEXT == 1
-    , layerHolder_(boost::make_shared<LayerHolder>(controllerW, 1, 5))
+    , layerHolder_(boost::make_shared<LayerHolder>(viewport, 1, 5))
 #else
-    , layerHolder_(boost::make_shared<LayerHolder>(controllerW, 1, 1))
+    , layerHolder_(boost::make_shared<LayerHolder>(viewport, 1, 1))
 #endif
     , lineHighlightArea_(LineHighlightArea_None)
   {
@@ -106,36 +106,50 @@
     SetLineHighlightArea(lineHighlightArea);
   }
 
-  LineMeasureTool::LineHighlightArea LineMeasureTool::LineHitTest(ScenePoint2D p) const
+  LineMeasureTool::LineHighlightArea LineMeasureTool::LineHitTest(ScenePoint2D p)
   {
-    const double pixelToScene =
-      GetController()->GetScene().GetCanvasToSceneTransform().ComputeZoom();
-    const double SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD = pixelToScene * HIT_TEST_MAX_DISTANCE_CANVAS_COORD * pixelToScene * HIT_TEST_MAX_DISTANCE_CANVAS_COORD;
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+    ViewportController& controller = lock->GetController();
+    Scene2D& scene = controller.GetScene();
 
-    const double sqDistanceFromStart = ScenePoint2D::SquaredDistancePtPt(p, start_);
+    const double pixelToScene = scene.GetCanvasToSceneTransform().ComputeZoom();
+    const double SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD = 
+      pixelToScene * HIT_TEST_MAX_DISTANCE_CANVAS_COORD * 
+      pixelToScene * HIT_TEST_MAX_DISTANCE_CANVAS_COORD;
+
+    const double sqDistanceFromStart = 
+      ScenePoint2D::SquaredDistancePtPt(p, start_);
+    
     if (sqDistanceFromStart <= SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD)
       return LineHighlightArea_Start;
     
     const double sqDistanceFromEnd = ScenePoint2D::SquaredDistancePtPt(p, end_);
+
     if (sqDistanceFromEnd <= SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD)
       return LineHighlightArea_End;
 
-    const double sqDistanceFromPtSegment = ScenePoint2D::SquaredDistancePtSegment(start_, end_, p);
+    const double sqDistanceFromPtSegment = 
+      ScenePoint2D::SquaredDistancePtSegment(start_, end_, p);
+    
     if (sqDistanceFromPtSegment <= SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD)
       return LineHighlightArea_Segment;
 
     return LineHighlightArea_None;
   }
 
-  bool LineMeasureTool::HitTest(ScenePoint2D p) const
+  bool LineMeasureTool::HitTest(ScenePoint2D p)
   {
     return LineHitTest(p) != LineHighlightArea_None;
   }
 
   boost::shared_ptr<IFlexiblePointerTracker> LineMeasureTool::CreateEditionTracker(const PointerEvent& e)
   {
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+    ViewportController& controller = lock->GetController();
+    Scene2D& scene = controller.GetScene();
+
     ScenePoint2D scenePos = e.GetMainPosition().Apply(
-      GetController()->GetScene().GetCanvasToSceneTransform());
+      scene.GetCanvasToSceneTransform());
 
     if (!HitTest(scenePos))
       return boost::shared_ptr<IFlexiblePointerTracker>();
@@ -144,15 +158,14 @@
       new EditLineMeasureTracker(
         boost::shared_ptr<LineMeasureTool> measureTool;
         MessageBroker & broker,
-        boost::weak_ptr<ViewportController>          controllerW,
+        IViewport&          viewport,
         const PointerEvent & e);
     */
     boost::shared_ptr<EditLineMeasureTracker> editLineMeasureTracker(
-      new EditLineMeasureTracker(shared_from_this(), GetController(), e));
+      new EditLineMeasureTracker(shared_from_this(), viewport_, e));
     return editLineMeasureTracker;
   }
 
-
   boost::shared_ptr<MeasureToolMemento> LineMeasureTool::GetMemento() const
   {
     boost::shared_ptr<LineMeasureToolMemento> memento(new LineMeasureToolMemento());
@@ -161,10 +174,14 @@
     return memento;
   }
 
-  void LineMeasureTool::SetMemento(boost::shared_ptr<MeasureToolMemento> mementoBase)
+  void LineMeasureTool::SetMemento(
+    boost::shared_ptr<MeasureToolMemento> mementoBase)
   {
-    boost::shared_ptr<LineMeasureToolMemento> memento = boost::dynamic_pointer_cast<LineMeasureToolMemento>(mementoBase);
+    boost::shared_ptr<LineMeasureToolMemento> memento = 
+      boost::dynamic_pointer_cast<LineMeasureToolMemento>(mementoBase);
+    
     ORTHANC_ASSERT(memento.get() != NULL, "Internal error: wrong (or bad) memento");
+    
     start_ = memento->start_;
     end_ = memento->end_;
     RefreshScene();
@@ -176,8 +193,12 @@
     {
       if (IsEnabled())
       {
+        
+        std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+        ViewportController& controller = lock->GetController();
+        Scene2D& scene = controller.GetScene();
+
         layerHolder_->CreateLayersIfNeeded();
-
         {
           // Fill the polyline layer with the measurement line
 
@@ -210,8 +231,8 @@
                 PolylineSceneLayer::Chain chain;
               
                 //TODO: take DPI into account
-                AddSquare(chain, GetController()->GetScene(), start_, 
-                          GetController()->GetHandleSideLengthS());
+                AddSquare(chain, controller.GetScene(), start_, 
+                          controller.GetHandleSideLengthS());
               
                 if (lineHighlightArea_ == LineHighlightArea_Start)
                   polylineLayer->AddChain(chain, true, highlightColor);
@@ -223,8 +244,8 @@
                 PolylineSceneLayer::Chain chain;
               
                 //TODO: take DPI into account
-                AddSquare(chain, GetController()->GetScene(), end_, 
-                          GetController()->GetHandleSideLengthS());
+                AddSquare(chain, controller.GetScene(), end_, 
+                          controller.GetHandleSideLengthS());
               
                 if (lineHighlightArea_ == LineHighlightArea_End)
                   polylineLayer->AddChain(chain, true, highlightColor);
@@ -248,14 +269,19 @@
           double midX = 0.5 * (end_.GetX() + start_.GetX());
           double midY = 0.5 * (end_.GetY() + start_.GetY());
 
+          {
+
 #if ORTHANC_STONE_ENABLE_OUTLINED_TEXT == 1
-          SetTextLayerOutlineProperties(
-            GetController()->GetScene(), layerHolder_, buf, ScenePoint2D(midX, midY), 0);
+            SetTextLayerOutlineProperties(
+              scene, layerHolder_, buf, ScenePoint2D(midX, midY), 0);
 #else
-          SetTextLayerProperties(
-            GetController()->GetScene(), layerHolder_, buf, ScenePoint2D(midX, midY), 0);
+            SetTextLayerProperties(
+              scene, layerHolder_, buf, ScenePoint2D(midX, midY), 0);
 #endif
+            lock->Invalidate();
+          }
         }
+        lock->Invalidate();
       }
       else
       {
--- a/Framework/Scene2DViewport/LineMeasureTool.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/LineMeasureTool.h	Wed Mar 04 10:21:54 2020 +0100
@@ -38,7 +38,7 @@
   class LineMeasureTool : public MeasureTool
   {
   public:
-    LineMeasureTool(boost::weak_ptr<ViewportController> controllerW);
+    LineMeasureTool(IViewport& viewport);
 
     ~LineMeasureTool();
 
@@ -47,7 +47,7 @@
     void Set(ScenePoint2D start, ScenePoint2D end);
 
 
-    virtual bool HitTest(ScenePoint2D p) const ORTHANC_OVERRIDE;
+    virtual bool HitTest(ScenePoint2D p) ORTHANC_OVERRIDE;
     virtual void Highlight(ScenePoint2D p) ORTHANC_OVERRIDE;
     virtual void ResetHighlightState() ORTHANC_OVERRIDE;
     virtual boost::shared_ptr<IFlexiblePointerTracker> CreateEditionTracker(const PointerEvent& e) ORTHANC_OVERRIDE;
@@ -64,7 +64,7 @@
     };
 
 
-    LineHighlightArea LineHitTest(ScenePoint2D p) const;
+    LineHighlightArea LineHitTest(ScenePoint2D p);
 
   private:
     virtual void        RefreshScene() ORTHANC_OVERRIDE;
--- a/Framework/Scene2DViewport/MeasureCommands.cpp	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/MeasureCommands.cpp	Wed Mar 04 10:21:54 2020 +0100
@@ -20,6 +20,8 @@
 
 #include "MeasureCommands.h"
 
+#include <memory>
+
 #include <boost/make_shared.hpp>
 #include <boost/ref.hpp>
 
@@ -27,19 +29,21 @@
 {
   void CreateMeasureCommand::Undo()
   {
+    std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_.Lock());
     // simply disable the measure tool upon undo
     GetMeasureTool()->Disable();
-    GetController()->RemoveMeasureTool(GetMeasureTool());
+    lock->GetController().RemoveMeasureTool(GetMeasureTool());
   }
 
   void CreateMeasureCommand::Redo()
   {
+    std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_.Lock());
     GetMeasureTool()->Enable();
-    GetController()->AddMeasureTool(GetMeasureTool());
+    lock->GetController().AddMeasureTool(GetMeasureTool());
   }
 
-  CreateMeasureCommand::CreateMeasureCommand(boost::weak_ptr<ViewportController> controllerW)
-    : MeasureCommand(controllerW)
+  CreateMeasureCommand::CreateMeasureCommand(IViewport& viewport)
+    : MeasureCommand(viewport)
   {
 
   }
@@ -52,15 +56,17 @@
 
   void DeleteMeasureCommand::Redo()
   {
+    std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_.Lock());
     // simply disable the measure tool upon undo
     GetMeasureTool()->Disable();
-    GetController()->RemoveMeasureTool(GetMeasureTool());
+    lock->GetController().RemoveMeasureTool(GetMeasureTool());
   }
 
   void DeleteMeasureCommand::Undo()
   {
+    std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_.Lock());
     GetMeasureTool()->Enable();
-    GetController()->AddMeasureTool(GetMeasureTool());
+    lock->GetController().AddMeasureTool(GetMeasureTool());
   }
 
   DeleteMeasureCommand::~DeleteMeasureCommand()
@@ -69,18 +75,19 @@
     // we thus leave it as is
   }
 
-  DeleteMeasureCommand::DeleteMeasureCommand(boost::shared_ptr<MeasureTool> measureTool, boost::weak_ptr<ViewportController> controllerW)
-    : MeasureCommand(controllerW)
+  DeleteMeasureCommand::DeleteMeasureCommand(boost::shared_ptr<MeasureTool> measureTool, IViewport& viewport)
+    : MeasureCommand(viewport)
     , mementoOriginal_(measureTool->GetMemento())
     , measureTool_(measureTool)
     , mementoModified_(measureTool->GetMemento())
   {
+    std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_.Lock());
     GetMeasureTool()->Disable();
-    GetController()->RemoveMeasureTool(GetMeasureTool());
+    lock->GetController().RemoveMeasureTool(GetMeasureTool());
   }
 
-  EditMeasureCommand::EditMeasureCommand(boost::shared_ptr<MeasureTool> measureTool, boost::weak_ptr<ViewportController> controllerW)
-    : MeasureCommand(controllerW)
+  EditMeasureCommand::EditMeasureCommand(boost::shared_ptr<MeasureTool> measureTool, IViewport& viewport)
+    : MeasureCommand(viewport)
     , mementoOriginal_(measureTool->GetMemento())
     , mementoModified_(measureTool->GetMemento())
   {
@@ -102,11 +109,4 @@
   {
     GetMeasureTool()->SetMemento(mementoModified_);
   }
-
-  boost::shared_ptr<ViewportController> MeasureCommand::GetController()
-  {
-    boost::shared_ptr<ViewportController> controller = controllerW_.lock();
-    assert(controller); // accessing dead object?
-    return controller;
-  }
 }
--- a/Framework/Scene2DViewport/MeasureCommands.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/MeasureCommands.h	Wed Mar 04 10:21:54 2020 +0100
@@ -19,7 +19,7 @@
  **/
 #pragma once
 
-#include "../Scene2D/Scene2D.h"
+#include "../Viewport/IViewport.h"
 
 // to be moved into Stone
 #include "PredeclaredTypes.h"
@@ -35,27 +35,21 @@
   class MeasureCommand : public boost::noncopyable
   {
   public:
-    MeasureCommand(boost::weak_ptr<ViewportController> controllerW) 
-      : controllerW_(controllerW)
-    {
-
-    }
+    MeasureCommand(IViewport& viewport) : viewport_(viewport)
+    {}
     virtual void Undo() = 0;
     virtual void Redo() = 0;
     
     virtual ~MeasureCommand() {};
 
   protected:
-    boost::shared_ptr<ViewportController>  GetController();
-
-  private:
-    boost::weak_ptr<ViewportController> controllerW_;
+    IViewport& viewport_;
   };
 
   class CreateMeasureCommand : public MeasureCommand
   {
   public:
-    CreateMeasureCommand(boost::weak_ptr<ViewportController> controllerW);
+    CreateMeasureCommand(IViewport& viewport);
     virtual ~CreateMeasureCommand();
     virtual void Undo() ORTHANC_OVERRIDE;
     virtual void Redo() ORTHANC_OVERRIDE;
@@ -67,7 +61,7 @@
   class EditMeasureCommand : public MeasureCommand
   {
   public:
-    EditMeasureCommand(boost::shared_ptr<MeasureTool> measureTool, boost::weak_ptr<ViewportController> controllerW);
+    EditMeasureCommand(boost::shared_ptr<MeasureTool> measureTool, IViewport& viewport);
     virtual ~EditMeasureCommand();
     virtual void Undo() ORTHANC_OVERRIDE;
     virtual void Redo() ORTHANC_OVERRIDE;
@@ -88,7 +82,7 @@
   class DeleteMeasureCommand : public MeasureCommand
   {
   public:
-    DeleteMeasureCommand(boost::shared_ptr<MeasureTool> measureTool, boost::weak_ptr<ViewportController> controllerW);
+    DeleteMeasureCommand(boost::shared_ptr<MeasureTool> measureTool, IViewport& viewport);
     virtual ~DeleteMeasureCommand();
     virtual void Undo() ORTHANC_OVERRIDE;
     virtual void Redo() ORTHANC_OVERRIDE;
--- a/Framework/Scene2DViewport/MeasureTool.cpp	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/MeasureTool.cpp	Wed Mar 04 10:21:54 2020 +0100
@@ -26,6 +26,8 @@
 
 #include <boost/math/constants/constants.hpp>
 
+#include "../Viewport/IViewport.h"
+
 namespace OrthancStone
 {
   void MeasureTool::Enable()
@@ -45,46 +47,26 @@
     return enabled_;
   }
 
-
-  boost::shared_ptr<const ViewportController> MeasureTool::GetController() const
-  {
-    boost::shared_ptr<const ViewportController> controller = controllerW_.lock();
-    if (!controller)
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
-        "Using dead ViewportController object!");
-    return controller;
-  }
-
-  boost::shared_ptr<ViewportController> MeasureTool::GetController()
-  {
-#if 1
-    return boost::const_pointer_cast<ViewportController>
-      (const_cast<const MeasureTool*>(this)->GetController());
-    //return boost::const_<boost::shared_ptr<ViewportController>>
-    //  (const_cast<const MeasureTool*>(this)->GetController());
-#else
-    boost::shared_ptr<ViewportController> controller = controllerW_.lock();
-    if (!controller)
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, 
-        "Using dead ViewportController object!");
-    return controller;
-#endif
-  }
-
   MeasureTool::MeasureTool(
-    boost::weak_ptr<ViewportController> controllerW)
-    : controllerW_(controllerW)
+    IViewport& viewport)
+    : viewport_(viewport)
     , enabled_(true)
   {
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+    ViewportController& controller = lock->GetController();
+
     // TODO => Move this out of constructor
-    Register<ViewportController::SceneTransformChanged>(*GetController(), &MeasureTool::OnSceneTransformChanged);
+    Register<ViewportController::SceneTransformChanged>(
+      controller, 
+      &MeasureTool::OnSceneTransformChanged);
   }
 
-
   bool MeasureTool::IsSceneAlive() const
   {
-    boost::shared_ptr<ViewportController> controller = controllerW_.lock();
-    return (controller.get() != NULL);
+    // since the lifetimes of the viewport, viewportcontroller (and the
+    // measuring tools inside it) are linked, the scene is always alive as 
+    // long as "this" is alive
+    return true;
   }
 
   void MeasureTool::OnSceneTransformChanged(
--- a/Framework/Scene2DViewport/MeasureTool.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/MeasureTool.h	Wed Mar 04 10:21:54 2020 +0100
@@ -75,7 +75,7 @@
     true, then a click at that position will return a tracker to edit the 
     measuring tool
     */
-    virtual bool HitTest(ScenePoint2D p) const = 0;
+    virtual bool HitTest(ScenePoint2D p) = 0;
 
     /**
     This method must return a memento the captures the tool state (not including
@@ -113,7 +113,7 @@
     virtual std::string GetDescription() = 0;
 
   protected:
-    MeasureTool(boost::weak_ptr<ViewportController> controllerW);
+    MeasureTool(IViewport& viewport);
 
     /**
     The measuring tool may exist in a standalone fashion, without any available
@@ -129,17 +129,20 @@
     */
     virtual void RefreshScene() = 0;
 
-    boost::shared_ptr<const ViewportController> GetController() const;
-    boost::shared_ptr<ViewportController>      GetController();
-
     /**
     enabled_ is not accessible by subclasses because there is a state machine
     that we do not wanna mess with
     */
     bool IsEnabled() const;
 
+    /**
+    Protected to allow sub-classes to use this weak pointer in factory methods
+    (pass them to created objects)
+    */
+    IViewport& viewport_;
+
+
   private:
-    boost::weak_ptr<ViewportController> controllerW_;
     bool     enabled_;
   };
 
--- a/Framework/Scene2DViewport/MeasureTrackers.cpp	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/MeasureTrackers.cpp	Wed Mar 04 10:21:54 2020 +0100
@@ -24,8 +24,8 @@
 namespace OrthancStone
 {
 
-  CreateMeasureTracker::CreateMeasureTracker(boost::weak_ptr<ViewportController> controllerW)
-    : controllerW_(controllerW)
+  CreateMeasureTracker::CreateMeasureTracker(IViewport& viewport)
+    : viewport_(viewport)
     , alive_(true)
     , commitResult_(true)
   {
@@ -47,23 +47,28 @@
     // if the tracker completes successfully, we add the command
     // to the undo stack
     // otherwise, we simply undo it
+
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+    ViewportController& controller = lock->GetController();
+
     if (commitResult_)
-      controllerW_.lock()->PushCommand(command_);
+      lock->GetController().PushCommand(command_);
     else
       command_->Undo();
+
+    lock->Invalidate();
   }
 
-  EditMeasureTracker::EditMeasureTracker(boost::weak_ptr<ViewportController> controllerW, const PointerEvent& e)
-    : controllerW_(controllerW)
+  EditMeasureTracker::EditMeasureTracker(IViewport& viewport, const PointerEvent& e)
+    : viewport_(viewport)
     , alive_(true)
     , commitResult_(true)
   {
-    boost::shared_ptr<ViewportController> controller = controllerW.lock();
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+    ViewportController& controller = lock->GetController();
 
-    if (controller)
-    {
-      originalClickPosition_ = e.GetMainPosition().Apply(controller->GetScene().GetCanvasToSceneTransform());
-    }
+    originalClickPosition_ = e.GetMainPosition().Apply(
+      controller.GetScene().GetCanvasToSceneTransform());
   }
 
   void EditMeasureTracker::Cancel()
@@ -82,10 +87,16 @@
     // if the tracker completes successfully, we add the command
     // to the undo stack
     // otherwise, we simply undo it
+
+    std::unique_ptr<IViewport::ILock> lock(viewport_.Lock());
+    ViewportController& controller = lock->GetController();
+
     if (commitResult_)
-      controllerW_.lock()->PushCommand(command_);
+      lock->GetController().PushCommand(command_);
     else
       command_->Undo();
+
+    lock->Invalidate();
   }
 }
 
--- a/Framework/Scene2DViewport/MeasureTrackers.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/MeasureTrackers.h	Wed Mar 04 10:21:54 2020 +0100
@@ -40,13 +40,13 @@
     virtual void Cancel() ORTHANC_OVERRIDE;
     virtual bool IsAlive() const ORTHANC_OVERRIDE;
   protected:
-    CreateMeasureTracker(boost::weak_ptr<ViewportController> controllerW);
+    CreateMeasureTracker(IViewport& viewport);
 
     ~CreateMeasureTracker();
   
   protected:
     boost::shared_ptr<CreateMeasureCommand>         command_;
-    boost::weak_ptr<ViewportController>          controllerW_;
+    IViewport&          viewport_;
     bool                            alive_;
 
   private:
@@ -59,13 +59,13 @@
     virtual void Cancel() ORTHANC_OVERRIDE;
     virtual bool IsAlive() const ORTHANC_OVERRIDE;
   protected:
-    EditMeasureTracker(boost::weak_ptr<ViewportController> controllerW, const PointerEvent& e);
+    EditMeasureTracker(IViewport& viewport, const PointerEvent& e);
 
     ~EditMeasureTracker();
 
   protected:
     boost::shared_ptr<EditMeasureCommand> command_;
-    boost::weak_ptr<ViewportController>   controllerW_;
+    IViewport&   viewport_;
     bool                                  alive_;
     
     ScenePoint2D                          GetOriginalClickPosition() const
--- a/Framework/Scene2DViewport/OneGesturePointerTracker.cpp	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/OneGesturePointerTracker.cpp	Wed Mar 04 10:21:54 2020 +0100
@@ -28,8 +28,8 @@
 namespace OrthancStone
 {
   OneGesturePointerTracker::OneGesturePointerTracker(
-    boost::weak_ptr<ViewportController> controllerW)
-    : controllerW_(controllerW)
+    IViewport& viewport)
+    : viewport_(viewport)
     , alive_(true)
     , currentTouchCount_(1)
   {
@@ -60,7 +60,7 @@
      * 2019-12-06 (SJO): Patch to have consistent behavior when mouse
      * leaves the canvas while the tracker is still active, then
      * button is released while out-of-canvas. Such an event is not
-     * catched (at least in WebAssembly), so we delete the tracker on
+     * caught (at least in WebAssembly), so we delete the tracker on
      * the next click inside the canvas.
      **/
     alive_ = false;
@@ -70,9 +70,4 @@
   {
     return alive_;
   }
-
-  boost::shared_ptr<ViewportController> OneGesturePointerTracker::GetController()
-  {
-    return controllerW_.lock();
-  }
 }
--- a/Framework/Scene2DViewport/OneGesturePointerTracker.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/OneGesturePointerTracker.h	Wed Mar 04 10:21:54 2020 +0100
@@ -22,6 +22,11 @@
 
 #include "IFlexiblePointerTracker.h"
 
+#include "../Viewport/IViewport.h"
+
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+
 namespace OrthancStone
 {
   /**
@@ -39,16 +44,15 @@
   class OneGesturePointerTracker : public IFlexiblePointerTracker
   {
   public:
-    OneGesturePointerTracker(boost::weak_ptr<ViewportController> controllerW);
+    OneGesturePointerTracker(IViewport& viewport);
     virtual void PointerUp(const PointerEvent& event) ORTHANC_OVERRIDE;
     virtual void PointerDown(const PointerEvent& event) ORTHANC_OVERRIDE;
     virtual bool IsAlive() const ORTHANC_OVERRIDE;
   
   protected:
-    boost::shared_ptr<ViewportController>  GetController();
+    IViewport& viewport_;
 
   private:
-    boost::weak_ptr<ViewportController> controllerW_;
     bool                   alive_;
     int                    currentTouchCount_;
   };
--- a/Framework/Scene2DViewport/PredeclaredTypes.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/PredeclaredTypes.h	Wed Mar 04 10:21:54 2020 +0100
@@ -38,4 +38,5 @@
   class MeasureCommand;
   class ViewportController;
   class LayerHolder;
+  class IViewport;
 }
--- a/Framework/Scene2DViewport/ViewportController.cpp	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/ViewportController.cpp	Wed Mar 04 10:21:54 2020 +0100
@@ -33,24 +33,24 @@
 namespace OrthancStone
 {
   IFlexiblePointerTracker* DefaultViewportInteractor::CreateTracker(
-    boost::shared_ptr<ViewportController> controller,
+    IViewport&          viewport,
     const PointerEvent& event,
-    unsigned int viewportWidth,
-    unsigned int viewportHeight)
+    unsigned int        viewportWidth,
+    unsigned int        viewportHeight)
   {
     switch (event.GetMouseButton())
     {
       case MouseButton_Left:
-        return new RotateSceneTracker(controller, event);
+        return new RotateSceneTracker(viewport, event);
 
       case MouseButton_Middle:
-        return new PanSceneTracker(controller, event);
+        return new PanSceneTracker(viewport, event);
       
       case MouseButton_Right:
       {
         if (viewportWidth != 0)
         {
-          return new ZoomSceneTracker(controller, event, viewportWidth);
+          return new ZoomSceneTracker(viewport, event, viewportWidth);
         }
         else
         {
@@ -64,24 +64,29 @@
   }
 
 
-  ViewportController::ViewportController() :
-    undoStackW_(boost::make_shared<OrthancStone::UndoStack>()),
-    scene_(new Scene2D),
-    canvasToSceneFactor_(1)
+  ViewportController::ViewportController(IViewport& viewport) 
+    : viewport_(viewport)
+    , undoStackW_(boost::make_shared<OrthancStone::UndoStack>())
+    , scene_(new Scene2D)
+    , canvasToSceneFactor_(1)
   {
   }
 
-  ViewportController::ViewportController(const Scene2D& scene) : 
-    undoStackW_(boost::make_shared<OrthancStone::UndoStack>()),
-    scene_(scene.Clone()),
-    canvasToSceneFactor_(1)
+  ViewportController::ViewportController(IViewport& viewport,
+                                         const Scene2D& scene)
+    : viewport_(viewport)
+    , undoStackW_(boost::make_shared<OrthancStone::UndoStack>())
+    , scene_(scene.Clone())
+    , canvasToSceneFactor_(1)
   {
   }
 
-  ViewportController::ViewportController(boost::weak_ptr<UndoStack> undoStackW) :
-    undoStackW_(undoStackW),
-    scene_(new Scene2D),
-    canvasToSceneFactor_(1)
+  ViewportController::ViewportController(IViewport& viewport, 
+                                         boost::weak_ptr<UndoStack> undoStackW)
+    : viewport_(viewport)
+    , undoStackW_(undoStackW)
+    , scene_(new Scene2D)
+    , canvasToSceneFactor_(1)
   {
   }
  
@@ -274,7 +279,7 @@
       }
 
       // No measure tool, create new tracker from the interactor
-      activeTracker_.reset(interactor.CreateTracker(shared_from_this(), event, viewportWidth, viewportHeight));
+      activeTracker_.reset(interactor.CreateTracker(viewport_, event, viewportWidth, viewportHeight));
     }
   }
 
--- a/Framework/Scene2DViewport/ViewportController.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Scene2DViewport/ViewportController.h	Wed Mar 04 10:21:54 2020 +0100
@@ -41,7 +41,7 @@
     {
     }
 
-    virtual IFlexiblePointerTracker* CreateTracker(boost::shared_ptr<ViewportController> controller,
+    virtual IFlexiblePointerTracker* CreateTracker(IViewport& viewport,
                                                    const PointerEvent& event,
                                                    unsigned int viewportWidth,
                                                    unsigned int viewportHeight) = 0;
@@ -52,7 +52,7 @@
   class DefaultViewportInteractor : public IViewportInteractor
   {
   public:
-    virtual IFlexiblePointerTracker* CreateTracker(boost::shared_ptr<ViewportController> controller,
+    virtual IFlexiblePointerTracker* CreateTracker(IViewport& viewport,
                                                    const PointerEvent& event,
                                                    unsigned int viewportWidth,
                                                    unsigned int viewportHeight) ORTHANC_OVERRIDE;
@@ -111,11 +111,11 @@
     ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, \
                                         SceneTransformChanged, ViewportController);
 
-    ViewportController();
+    ViewportController(IViewport& viewport);
 
-    ViewportController(const Scene2D& scene /* will be cloned */);
+    ViewportController(IViewport& viewport, const Scene2D& scene /* will be cloned */);
 
-    ViewportController(boost::weak_ptr<UndoStack> undoStackW);
+    ViewportController(IViewport& viewport, boost::weak_ptr<UndoStack> undoStackW);
 
     ~ViewportController();
 
@@ -231,6 +231,7 @@
   private:
     double GetCanvasToSceneFactor() const;
 
+    IViewport&                                    viewport_;
     boost::weak_ptr<UndoStack>                    undoStackW_;  // Global stack, possibly shared by all viewports
     std::vector<boost::shared_ptr<MeasureTool> >  measureTools_;
     boost::shared_ptr<IFlexiblePointerTracker>    activeTracker_;  // TODO - Couldn't this be a "std::unique_ptr"?
--- a/Framework/Toolbox/GenericToolbox.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Toolbox/GenericToolbox.h	Wed Mar 04 10:21:54 2020 +0100
@@ -29,6 +29,12 @@
 {
   namespace GenericToolbox
   {
+    /**
+    Fast floating point string validation.
+    No trimming applied, so the input must match regex 
+    /^[-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/
+    The following are allowed as edge cases: "" and "-"
+    */
     inline bool LegitDoubleString(const char* text)
     {
       const char* p = text;
@@ -70,6 +76,11 @@
       return true;
     }
 
+    /**
+    Fast integer string validation.
+    No trimming applied, so the input must match regex /^-?[0-9]*$/
+    The following are allowed as edge cases: "" and "-"
+    */
     inline bool LegitIntegerString(const char* text)
     {
       const char* p = text;
@@ -86,6 +97,9 @@
     }
 
     /*
+      Fast string --> double conversion.
+      Must pass the LegitDoubleString test
+
       String to doubles with at most 18 digits
     */
     inline bool StringToDouble(double& r, const char* text)
@@ -210,10 +224,17 @@
       return StringToDouble(r, text.c_str());
     }
 
+    /**
+    Fast string to integer conversion. Leading zeroes and minus are accepted,
+    but a leading + sign is NOT.
+    Must pass the LegitIntegerString function test.
+    In addition, an empty string (or lone minus sign) yields 0.
+    */
+
     template<typename T>
     inline bool StringToInteger(T& r, const char* text)
     {
-      if (!LegitDoubleString(text))
+      if (!LegitIntegerString(text))
         return false;
 
       r = 0;
@@ -244,21 +265,27 @@
     }
 
     /**
-    "rgb(12,23,255)"  --> red, green, blue and returns true
-    "everything else" --> returns false (other values left untouched)
+    if input is "rgb(12,23,255)"  --> function fills `red`, `green` and `blue` and returns true
+    else ("everything else")      --> function returns false and leaves all values untouched
     */
     bool GetRgbValuesFromString(uint8_t& red, uint8_t& green, uint8_t& blue, const char* text);
 
     /**
-    See other overload
+    See main overload
     */
     inline bool GetRgbValuesFromString(uint8_t& red, uint8_t& green, uint8_t& blue, const std::string& text)
     {
       return GetRgbValuesFromString(red, green, blue, text.c_str());
     }
 
+    /**
+    Same as GetRgbValuesFromString
+    */
     bool GetRgbaValuesFromString(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha, const char* text);
 
+    /**
+    Same as GetRgbValuesFromString
+    */
     inline bool GetRgbaValuesFromString(uint8_t& red, uint8_t& green, uint8_t& blue, uint8_t& alpha, const std::string& text)
     {
       return GetRgbaValuesFromString(red, green, blue, alpha, text.c_str());
--- a/Framework/Viewport/IViewport.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Viewport/IViewport.h	Wed Mar 04 10:21:54 2020 +0100
@@ -48,8 +48,16 @@
 
       virtual bool HasCompositor() const = 0;
 
+      /**
+      Do not store the result! Only access the compositor interface through
+      the lock.
+      */
       virtual ICompositor& GetCompositor() = 0;
 
+      /**
+      Do not store the result! Only access the compositor interface through
+      the lock.
+      */
       virtual ViewportController& GetController() = 0;
 
       virtual void Invalidate() = 0;
--- a/Framework/Viewport/SdlViewport.cpp	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Viewport/SdlViewport.cpp	Wed Mar 04 10:21:54 2020 +0100
@@ -48,18 +48,27 @@
     compositor_.reset(compositor);
   }
 
-
   SdlViewport::SdlViewport() :
-    controller_(new ViewportController)
+    controller_(new ViewportController(*this))
   {
     refreshEvent_ = SDL_RegisterEvents(1);
-    
+
     if (refreshEvent_ == static_cast<uint32_t>(-1))
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
     }
   }
 
+  SdlViewport::SdlViewport(boost::weak_ptr<UndoStack> undoStackW) :
+    controller_(new ViewportController(*this,undoStackW))
+  {
+    refreshEvent_ = SDL_RegisterEvents(1);
+
+    if (refreshEvent_ == static_cast<uint32_t>(-1))
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+  }
 
   void SdlViewport::SendRefreshEvent()
   {
@@ -79,6 +88,16 @@
     AcquireCompositor(new OpenGLCompositor(context_));  // (*)
   }
 
+  SdlOpenGLViewport::SdlOpenGLViewport(const char* title,
+                                       boost::weak_ptr<UndoStack> undoStackW,
+                                       unsigned int width,
+                                       unsigned int height,
+                                       bool allowDpiScaling) :
+    SdlViewport(undoStackW),
+    context_(title, width, height, allowDpiScaling)
+  {
+    AcquireCompositor(new OpenGLCompositor(context_));  // (*)
+  }
 
   SdlOpenGLViewport::~SdlOpenGLViewport()
   {
--- a/Framework/Viewport/SdlViewport.h	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Viewport/SdlViewport.h	Wed Mar 04 10:21:54 2020 +0100
@@ -43,12 +43,20 @@
 
 #include <SDL_events.h>
 
+// TODO: required for UndoStack injection
+// I don't like it either :)
+#include <boost/weak_ptr.hpp>
+
+#include <boost/thread/recursive_mutex.hpp>
+
 namespace OrthancStone
 {
+  class UndoStack;
+
   class SdlViewport : public IViewport
   {
   private:
-    boost::mutex                           mutex_;
+    boost::recursive_mutex                 mutex_;
     uint32_t                               refreshEvent_;
     boost::shared_ptr<ViewportController>  controller_;
     std::unique_ptr<ICompositor>             compositor_;
@@ -59,8 +67,8 @@
     class SdlLock : public ILock
     {
     private:
-      SdlViewport&                that_;
-      boost::mutex::scoped_lock   lock_;
+      SdlViewport&                        that_;
+      boost::recursive_mutex::scoped_lock lock_;
 
     public:
       SdlLock(SdlViewport& that) :
@@ -96,6 +104,7 @@
 
   public:
     SdlViewport();
+    SdlViewport(boost::weak_ptr<UndoStack> undoStackW);
 
     bool IsRefreshEvent(const SDL_Event& event) const
     {
@@ -128,6 +137,12 @@
                       unsigned int height,
                       bool allowDpiScaling = true);
 
+    SdlOpenGLViewport(const char* title,
+                      boost::weak_ptr<UndoStack> undoStackW,
+                      unsigned int width,
+                      unsigned int height,
+                      bool allowDpiScaling = true);
+
     virtual ~SdlOpenGLViewport();
 
     virtual void Paint() ORTHANC_OVERRIDE;
--- a/Framework/Viewport/WebAssemblyViewport.cpp	Mon Mar 02 18:30:04 2020 +0100
+++ b/Framework/Viewport/WebAssemblyViewport.cpp	Wed Mar 04 10:21:54 2020 +0100
@@ -213,11 +213,11 @@
   {
     if (scene == NULL)
     {
-      controller_ = boost::make_shared<ViewportController>();
+      controller_ = boost::make_shared<ViewportController>(*this);
     }
     else
     {
-      controller_ = boost::make_shared<ViewportController>(*scene);
+      controller_ = boost::make_shared<ViewportController>(*this,*scene);
     }
 
     LOG(INFO) << "Initializing Stone viewport on HTML canvas: " << canvasId;
--- a/Resources/CMake/OrthancStoneConfiguration.cmake	Mon Mar 02 18:30:04 2020 +0100
+++ b/Resources/CMake/OrthancStoneConfiguration.cmake	Wed Mar 04 10:21:54 2020 +0100
@@ -308,6 +308,7 @@
     list(APPEND PLATFORM_SOURCES
       ${ORTHANC_STONE_ROOT}/Platforms/Generic/OracleWebService.cpp
       ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Viewport/CairoFont.cpp
+      ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Viewport/CairoFont.h
       )
   endif()
 
@@ -350,17 +351,36 @@
   list(APPEND ORTHANC_STONE_SOURCES
     ${ORTHANC_STONE_ROOT}/Applications/IStoneApplication.h
     ${ORTHANC_STONE_ROOT}/Applications/StoneApplicationContext.cpp
+
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/dev.h
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/SmartLoader.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/SmartLoader.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/CircleMeasureTracker.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/CircleMeasureTracker.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/ColorFrameRenderer.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/ColorFrameRenderer.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/DicomSeriesVolumeSlicer.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/DicomSeriesVolumeSlicer.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/DicomStructureSetSlicer.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/DicomStructureSetSlicer.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/FrameRenderer.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/FrameRenderer.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/GrayscaleFrameRenderer.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/GrayscaleFrameRenderer.h
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/ILayerRenderer.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/IVolumeSlicer.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/LineLayerRenderer.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/LineLayerRenderer.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/LineMeasureTracker.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/LineMeasureTracker.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/RenderStyle.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/RenderStyle.h
+    # ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/SeriesFrameRendererFactory.cpp
+    # ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/SeriesFrameRendererFactory.h
+    # ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/SingleFrameRendererFactory.cpp
+    # ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/SingleFrameRendererFactory.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/SliceOutlineRenderer.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/SliceOutlineRenderer.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Loaders/DicomStructureSetLoader.cpp
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Loaders/DicomStructureSetLoader.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Loaders/DicomStructureSetLoader2.cpp
@@ -373,39 +393,66 @@
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Loaders/OrthancMultiframeVolumeLoader.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Loaders/OrthancSeriesVolumeProgressiveLoader.cpp
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Loaders/OrthancSeriesVolumeProgressiveLoader.h
-    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/SmartLoader.cpp
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/BaseWebService.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/BaseWebService.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/DicomFrameConverter.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/DicomFrameConverter.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/DownloadStack.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/DownloadStack.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/IDelayedCallExecutor.h
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/ISeriesLoader.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/IWebService.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/IWebService.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/MessagingToolbox.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/MessagingToolbox.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/OrthancApiClient.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/OrthancApiClient.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/OrthancSlicesLoader.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/OrthancSlicesLoader.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/ParallelSlices.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/ParallelSlices.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/ParallelSlicesCursor.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/ParallelSlicesCursor.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/Slice.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/Slice.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/ViewportGeometry.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/ViewportGeometry.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Viewport/IMouseTracker.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Viewport/IStatusBar.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Viewport/IViewport.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Viewport/WidgetViewport.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Viewport/WidgetViewport.h
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Volumes/IGeometryProvider.h
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Volumes/ISlicedVolume.h
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Volumes/IVolumeLoader.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Volumes/StructureSetLoader.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Volumes/StructureSetLoader.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/CairoWidget.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/CairoWidget.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/EmptyWidget.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/EmptyWidget.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/IWidget.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/IWorldSceneInteractor.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/IWorldSceneMouseTracker.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/LayoutWidget.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/LayoutWidget.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/PanMouseTracker.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/PanMouseTracker.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/PanZoomMouseTracker.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/PanZoomMouseTracker.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/SliceViewerWidget.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/SliceViewerWidget.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/TestCairoWidget.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/TestCairoWidget.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/TestWorldSceneWidget.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/TestWorldSceneWidget.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/WidgetBase.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/WidgetBase.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/WorldSceneWidget.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/WorldSceneWidget.h
     ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/ZoomMouseTracker.cpp
-    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/dev.h
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/ZoomMouseTracker.h
+
     ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyAlphaLayer.cpp
     ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyDicomLayer.cpp
     ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyLayer.cpp